@themoltnet/legreffier 0.29.0 → 0.30.0
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 +88 -27
- package/package.json +5 -5
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: [
|
|
@@ -596,6 +596,32 @@ function CliSummaryBox({ agentName, fingerprint, appSlug, apiUrl, mcpUrl }) {
|
|
|
596
596
|
}),
|
|
597
597
|
" in any repo where the app is installed."
|
|
598
598
|
]
|
|
599
|
+
}),
|
|
600
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
601
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
602
|
+
color: cliTheme.color.text,
|
|
603
|
+
children: [
|
|
604
|
+
" ",
|
|
605
|
+
/* @__PURE__ */ jsx(Text, {
|
|
606
|
+
color: cliTheme.color.primary,
|
|
607
|
+
children: "Next step:"
|
|
608
|
+
}),
|
|
609
|
+
" run ",
|
|
610
|
+
/* @__PURE__ */ jsx(Text, {
|
|
611
|
+
color: cliTheme.color.accent,
|
|
612
|
+
children: "/legreffier-onboarding"
|
|
613
|
+
}),
|
|
614
|
+
" or ",
|
|
615
|
+
/* @__PURE__ */ jsx(Text, {
|
|
616
|
+
color: cliTheme.color.accent,
|
|
617
|
+
children: "$legreffier-onboarding"
|
|
618
|
+
}),
|
|
619
|
+
" in your next session"
|
|
620
|
+
]
|
|
621
|
+
}),
|
|
622
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
623
|
+
color: cliTheme.color.muted,
|
|
624
|
+
children: [" ", "to connect your team diary and start capturing knowledge."]
|
|
599
625
|
})
|
|
600
626
|
]
|
|
601
627
|
})
|
|
@@ -7485,6 +7511,10 @@ var SKILLS = [
|
|
|
7485
7511
|
{
|
|
7486
7512
|
name: "legreffier-explore",
|
|
7487
7513
|
files: ["SKILL.md", "references/exploration-pack-plan.yaml"]
|
|
7514
|
+
},
|
|
7515
|
+
{
|
|
7516
|
+
name: "legreffier-onboarding",
|
|
7517
|
+
files: ["SKILL.md", "references/onboarding-guide.md"]
|
|
7488
7518
|
}
|
|
7489
7519
|
];
|
|
7490
7520
|
async function downloadSkillFiles(skill) {
|
|
@@ -7532,33 +7562,60 @@ async function downloadSkills(repoDir, skillDir) {
|
|
|
7532
7562
|
}
|
|
7533
7563
|
}
|
|
7534
7564
|
}
|
|
7535
|
-
function buildGhTokenRule(
|
|
7565
|
+
function buildGhTokenRule() {
|
|
7536
7566
|
return [
|
|
7537
|
-
"# GitHub CLI Authentication (
|
|
7567
|
+
"# GitHub CLI Authentication (MoltNet agents)",
|
|
7538
7568
|
"",
|
|
7539
|
-
|
|
7540
|
-
"
|
|
7569
|
+
"> **STRICT RULE — read this before every `gh` call.**",
|
|
7570
|
+
">",
|
|
7571
|
+
"> When `GIT_CONFIG_GLOBAL` is set (matches `.moltnet/<agent>/gitconfig`), you",
|
|
7572
|
+
"> **MUST NOT** run bare `gh <command>`. You **MUST** prefix every `gh` call",
|
|
7573
|
+
"> with a `GH_TOKEN` resolved from an **absolute path** to `moltnet.json`.",
|
|
7574
|
+
"> Running bare `gh` silently falls back to the human personal token and",
|
|
7575
|
+
"> attributes the action to the wrong identity — this is a correctness bug,",
|
|
7576
|
+
"> not a warning.",
|
|
7577
|
+
"",
|
|
7578
|
+
"## The only correct form",
|
|
7541
7579
|
"",
|
|
7542
7580
|
"```bash",
|
|
7543
|
-
"
|
|
7581
|
+
"# 1. Resolve credentials to an ABSOLUTE path (never trust $GIT_CONFIG_GLOBAL as-is).",
|
|
7582
|
+
"CREDS=\"$(cd \"$(dirname \"$GIT_CONFIG_GLOBAL\")\" 2>/dev/null && pwd)/moltnet.json\"",
|
|
7583
|
+
"",
|
|
7584
|
+
"# 2. Refuse to proceed if the file does not exist at that absolute path.",
|
|
7585
|
+
"[ -f \"$CREDS\" ] || { echo \"FATAL: moltnet.json not found at $CREDS\" >&2; exit 1; }",
|
|
7586
|
+
"",
|
|
7587
|
+
"# 3. Call gh with GH_TOKEN inlined.",
|
|
7588
|
+
"GH_TOKEN=$(npx @themoltnet/cli github token --credentials \"$CREDS\") gh <command>",
|
|
7544
7589
|
"```",
|
|
7545
7590
|
"",
|
|
7546
|
-
"The
|
|
7591
|
+
"The credentials file (`moltnet.json`) always lives next to the `gitconfig`",
|
|
7592
|
+
"inside the same `.moltnet/<agent>/` directory, regardless of which agent is",
|
|
7593
|
+
"active. The token is cached locally (~1 hour lifetime, 5-min expiry buffer),",
|
|
7547
7594
|
"so repeated calls are fast after the first API hit.",
|
|
7548
7595
|
"",
|
|
7549
|
-
"##
|
|
7596
|
+
"## Why absolute paths are mandatory",
|
|
7550
7597
|
"",
|
|
7551
|
-
|
|
7552
|
-
"
|
|
7553
|
-
"
|
|
7554
|
-
"
|
|
7598
|
+
"`GIT_CONFIG_GLOBAL` is almost always a **relative path** (e.g. `.moltnet/<agent>/gitconfig`).",
|
|
7599
|
+
"Every git worktree has a different CWD from the main worktree root, so",
|
|
7600
|
+
"`$(dirname \"$GIT_CONFIG_GLOBAL\")` resolves differently depending on where you are.",
|
|
7601
|
+
"When it resolves to a non-existent directory:",
|
|
7555
7602
|
"",
|
|
7556
|
-
"
|
|
7603
|
+
"- `npx @themoltnet/cli github token` prints `no credentials found` to stderr,",
|
|
7604
|
+
"- the command substitution yields an empty `GH_TOKEN`,",
|
|
7605
|
+
"- `gh` silently falls back to your personal token,",
|
|
7606
|
+
"- the resulting API call is attributed to the **human**, not the agent.",
|
|
7557
7607
|
"",
|
|
7558
|
-
"
|
|
7559
|
-
"
|
|
7560
|
-
"
|
|
7561
|
-
"
|
|
7608
|
+
"This failure is invisible in normal output. The `cd ... && pwd` dance in step 1",
|
|
7609
|
+
"is the only reliable way to get an absolute path that works across worktrees.",
|
|
7610
|
+
"",
|
|
7611
|
+
"## Forbidden patterns",
|
|
7612
|
+
"",
|
|
7613
|
+
"- `gh <command>` — bare, no `GH_TOKEN`. **Never.**",
|
|
7614
|
+
"- `GH_TOKEN=$(... --credentials \"$(dirname \"$GIT_CONFIG_GLOBAL\")/moltnet.json\") gh ...`",
|
|
7615
|
+
" — uses the raw relative path. Breaks in worktrees.",
|
|
7616
|
+
"- `GH_TOKEN=$(... --credentials \"./moltnet.json\") gh ...` — relative. Breaks.",
|
|
7617
|
+
"- `GH_TOKEN=$(... --credentials \"~/.moltnet/...\") gh ...` — `~` is not expanded",
|
|
7618
|
+
" inside double quotes; use `$HOME` or the literal absolute path.",
|
|
7562
7619
|
"",
|
|
7563
7620
|
"## Allowed `gh` subcommands",
|
|
7564
7621
|
"",
|
|
@@ -7780,7 +7837,7 @@ var ClaudeAdapter = class {
|
|
|
7780
7837
|
async writeRules(opts) {
|
|
7781
7838
|
const dir = join(opts.repoDir, ".claude", "rules");
|
|
7782
7839
|
await mkdir(dir, { recursive: true });
|
|
7783
|
-
await writeFile(join(dir, "legreffier-gh.md"), buildGhTokenRule(
|
|
7840
|
+
await writeFile(join(dir, "legreffier-gh.md"), buildGhTokenRule(), "utf-8");
|
|
7784
7841
|
}
|
|
7785
7842
|
};
|
|
7786
7843
|
//#endregion
|
|
@@ -8057,17 +8114,21 @@ async function writePem(pem, appSlug, configDir) {
|
|
|
8057
8114
|
/**
|
|
8058
8115
|
* Write a standalone gitconfig file to <configDir>/gitconfig and return
|
|
8059
8116
|
* its path. The config sets user.name/email and enables SSH commit signing
|
|
8060
|
-
* using the agent's SSH key.
|
|
8117
|
+
* using the agent's SSH public key.
|
|
8118
|
+
*
|
|
8119
|
+
* **Important:** `signingkey` must live under `[user]`, not `[gpg "ssh"]`.
|
|
8120
|
+
* Git only reads `user.signingkey`; a key declared as `gpg.ssh.signingkey`
|
|
8121
|
+
* is silently ignored and `git commit -S` fails with
|
|
8122
|
+
* `fatal: either user.signingkey or gpg.ssh.defaultKeyCommand needs to be configured`.
|
|
8061
8123
|
*/
|
|
8062
|
-
async function writeGitConfig({ configDir, name, email,
|
|
8124
|
+
async function writeGitConfig({ configDir, name, email, sshPublicKeyPath }) {
|
|
8063
8125
|
const content = [
|
|
8064
8126
|
"[user]",
|
|
8065
8127
|
`\tname = ${name}`,
|
|
8066
8128
|
`\temail = ${email}`,
|
|
8129
|
+
`\tsigningkey = ${sshPublicKeyPath}`,
|
|
8067
8130
|
"[gpg]",
|
|
8068
8131
|
" format = ssh",
|
|
8069
|
-
"[gpg \"ssh\"]",
|
|
8070
|
-
`\tsigningKey = ${sshKeyPath}`,
|
|
8071
8132
|
"[commit]",
|
|
8072
8133
|
" gpgsign = true",
|
|
8073
8134
|
""
|
|
@@ -8322,7 +8383,7 @@ async function runGitSetupPhase(opts) {
|
|
|
8322
8383
|
key: "gitSetup",
|
|
8323
8384
|
status: "running"
|
|
8324
8385
|
});
|
|
8325
|
-
const {
|
|
8386
|
+
const { publicPath } = await exportSSHKey({ configDir });
|
|
8326
8387
|
const email = buildBotEmail((await lookupBotUser(appSlug, { maxRetries: 5 })).id, appSlug);
|
|
8327
8388
|
await updateConfigSection("git", {
|
|
8328
8389
|
name: agentName,
|
|
@@ -8332,7 +8393,7 @@ async function runGitSetupPhase(opts) {
|
|
|
8332
8393
|
configDir,
|
|
8333
8394
|
name: agentName,
|
|
8334
8395
|
email,
|
|
8335
|
-
|
|
8396
|
+
sshPublicKeyPath: publicPath
|
|
8336
8397
|
})
|
|
8337
8398
|
}, configDir);
|
|
8338
8399
|
dispatch({
|
|
@@ -9252,7 +9313,7 @@ async function runPortRewritePhase(opts) {
|
|
|
9252
9313
|
configDir: targetDir,
|
|
9253
9314
|
name: config.git.name,
|
|
9254
9315
|
email: config.git.email,
|
|
9255
|
-
|
|
9316
|
+
sshPublicKeyPath: newSshPub
|
|
9256
9317
|
});
|
|
9257
9318
|
await writeEnvFile({
|
|
9258
9319
|
envDir: targetDir,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@themoltnet/legreffier",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "LeGreffier —
|
|
3
|
+
"version": "0.30.0",
|
|
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": {
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"vite": "^8.0.0",
|
|
35
35
|
"vitest": "^3.0.0",
|
|
36
36
|
"@moltnet/api-client": "0.1.0",
|
|
37
|
-
"@themoltnet/design-system": "0.3.2",
|
|
38
37
|
"@moltnet/crypto-service": "0.1.0",
|
|
39
|
-
"@themoltnet/
|
|
40
|
-
"@themoltnet/sdk": "0.
|
|
38
|
+
"@themoltnet/design-system": "0.4.0",
|
|
39
|
+
"@themoltnet/sdk": "0.89.0",
|
|
40
|
+
"@themoltnet/github-agent": "0.23.0"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
43
|
"dev": "vite build --watch",
|