@themoltnet/legreffier 0.31.0 → 0.32.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 +6 -3
- package/dist/index.js +376 -391
- package/package.json +5 -6
package/README.md
CHANGED
|
@@ -36,16 +36,19 @@ and across agents.
|
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
38
|
# Run directly (no install needed)
|
|
39
|
-
npx @themoltnet/legreffier --name my-agent
|
|
39
|
+
npx @themoltnet/legreffier init --name my-agent
|
|
40
40
|
|
|
41
41
|
# Or install globally
|
|
42
42
|
npm install -g @themoltnet/legreffier
|
|
43
|
-
legreffier --name my-agent
|
|
43
|
+
legreffier init --name my-agent
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
Every invocation requires an explicit subcommand (`init`, `setup`, `port`,
|
|
47
|
+
or `github`). Run `legreffier --help` to see them all.
|
|
48
|
+
|
|
46
49
|
### Subcommands
|
|
47
50
|
|
|
48
|
-
#### `legreffier init`
|
|
51
|
+
#### `legreffier init`
|
|
49
52
|
|
|
50
53
|
Full onboarding: identity, GitHub App, git signing, agent setup.
|
|
51
54
|
|
package/dist/index.js
CHANGED
|
@@ -1,263 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { parseArgs, parseEnv } from "node:util";
|
|
4
|
-
import { Box, Text, render, useApp, useInput } from "ink";
|
|
2
|
+
import { defineCommand, runMain } from "citty";
|
|
5
3
|
import { execFileSync, execSync } from "node:child_process";
|
|
6
4
|
import { basename, dirname, isAbsolute, join } from "node:path";
|
|
5
|
+
import { Box, Text, render, useApp, useInput } from "ink";
|
|
7
6
|
import { useEffect, useReducer, useRef, useState } from "react";
|
|
8
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
-
import figlet from "figlet";
|
|
10
8
|
import { createSign } from "node:crypto";
|
|
11
9
|
import { createHash, randomBytes } from "crypto";
|
|
12
10
|
import { access, chmod, copyFile, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
13
11
|
import { homedir } from "node:os";
|
|
14
12
|
import { parse, stringify } from "smol-toml";
|
|
13
|
+
import { parseEnv } from "node:util";
|
|
15
14
|
import open from "open";
|
|
16
|
-
|
|
17
|
-
function formatFlag(flag) {
|
|
18
|
-
const head = flag.short ? `${flag.name}, ${flag.short}` : flag.name;
|
|
19
|
-
const value = flag.value ? ` ${flag.value}` : "";
|
|
20
|
-
const suffixParts = [];
|
|
21
|
-
if (flag.required) suffixParts.push("(required)");
|
|
22
|
-
if (flag.default !== void 0) suffixParts.push(`default: ${flag.default}`);
|
|
23
|
-
return ` ${head}${value}${suffixParts.length > 0 ? ` [${suffixParts.join(", ")}]` : ""}\n ${flag.description}`;
|
|
24
|
-
}
|
|
25
|
-
function printCommandHelp(help) {
|
|
26
|
-
const lines = [];
|
|
27
|
-
lines.push(`${help.command} — ${help.summary}`);
|
|
28
|
-
lines.push("");
|
|
29
|
-
lines.push(`Usage: ${help.usage}`);
|
|
30
|
-
lines.push("");
|
|
31
|
-
lines.push(help.description);
|
|
32
|
-
if (help.flags.length > 0) {
|
|
33
|
-
lines.push("");
|
|
34
|
-
lines.push("Flags:");
|
|
35
|
-
for (const flag of help.flags) lines.push(formatFlag(flag));
|
|
36
|
-
}
|
|
37
|
-
if (help.examples.length > 0) {
|
|
38
|
-
lines.push("");
|
|
39
|
-
lines.push("Examples:");
|
|
40
|
-
for (const ex of help.examples) {
|
|
41
|
-
lines.push(` # ${ex.description}`);
|
|
42
|
-
lines.push(` ${ex.command}`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
if (help.notes && help.notes.length > 0) {
|
|
46
|
-
lines.push("");
|
|
47
|
-
lines.push("Notes:");
|
|
48
|
-
for (const note of help.notes) lines.push(` - ${note}`);
|
|
49
|
-
}
|
|
50
|
-
lines.push("");
|
|
51
|
-
process.stdout.write(lines.join("\n"));
|
|
52
|
-
}
|
|
53
|
-
function printRootHelp(commands) {
|
|
54
|
-
const lines = [];
|
|
55
|
-
lines.push("legreffier — LeGreffier CLI");
|
|
56
|
-
lines.push("");
|
|
57
|
-
lines.push("Usage: legreffier <command> [flags]");
|
|
58
|
-
lines.push("");
|
|
59
|
-
lines.push("Commands:");
|
|
60
|
-
const pad = Math.max(...commands.map((c) => c.command.length)) + 2;
|
|
61
|
-
for (const cmd of commands) lines.push(` ${cmd.command.padEnd(pad)}${cmd.summary}`);
|
|
62
|
-
lines.push("");
|
|
63
|
-
lines.push("Run `legreffier <command> --help` for command-specific help.");
|
|
64
|
-
lines.push("");
|
|
65
|
-
process.stdout.write(lines.join("\n"));
|
|
66
|
-
}
|
|
67
|
-
var COMMANDS = [
|
|
68
|
-
{
|
|
69
|
-
command: "init",
|
|
70
|
-
summary: "Create a new agent identity and wire it into this repository",
|
|
71
|
-
usage: "legreffier init --name <agent-name> [flags]",
|
|
72
|
-
description: "Runs the full 5-phase onboarding: generates an Ed25519 keypair, registers the agent on MoltNet, creates a GitHub App via manifest flow, writes the gitconfig with SSH signing, installs the GitHub App on selected repos, and writes the MCP config for your chosen agent clients.",
|
|
73
|
-
flags: [
|
|
74
|
-
{
|
|
75
|
-
name: "--name",
|
|
76
|
-
short: "-n",
|
|
77
|
-
value: "<agent-name>",
|
|
78
|
-
description: "Agent name (2-39 lowercase alphanumerics/hyphens, e.g. `jobi`)",
|
|
79
|
-
required: true
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
name: "--agent",
|
|
83
|
-
short: "-a",
|
|
84
|
-
value: "claude|codex",
|
|
85
|
-
description: "Agent client to configure (repeatable). Default: no client config written."
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
name: "--api-url",
|
|
89
|
-
value: "<url>",
|
|
90
|
-
description: "MoltNet API base URL",
|
|
91
|
-
default: "https://api.themolt.net"
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
name: "--dir",
|
|
95
|
-
value: "<path>",
|
|
96
|
-
description: "Target repository root",
|
|
97
|
-
default: "current working directory"
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
name: "--org",
|
|
101
|
-
short: "-o",
|
|
102
|
-
value: "<github-org>",
|
|
103
|
-
description: "GitHub organization to install the App on (optional)"
|
|
104
|
-
}
|
|
105
|
-
],
|
|
106
|
-
examples: [{
|
|
107
|
-
description: "Basic init for a new agent named `jobi`",
|
|
108
|
-
command: "legreffier init --name jobi --agent claude"
|
|
109
|
-
}, {
|
|
110
|
-
description: "Init against a local API",
|
|
111
|
-
command: "legreffier init --name jobi --agent claude --api-url http://localhost:3000"
|
|
112
|
-
}]
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
command: "setup",
|
|
116
|
-
summary: "Install LeGreffier skills and MCP config into an existing repo",
|
|
117
|
-
usage: "legreffier setup --name <agent-name> [flags]",
|
|
118
|
-
description: "For a repository that already has `.moltnet/<agent-name>/` credentials (e.g. after running `init` elsewhere), `setup` writes the MCP config, downloads skills, and configures your agent clients. Does not touch identity, keys, or GitHub App state.",
|
|
119
|
-
flags: [
|
|
120
|
-
{
|
|
121
|
-
name: "--name",
|
|
122
|
-
short: "-n",
|
|
123
|
-
value: "<agent-name>",
|
|
124
|
-
description: "Agent name (must already exist under `.moltnet/`)",
|
|
125
|
-
required: true
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
name: "--agent",
|
|
129
|
-
short: "-a",
|
|
130
|
-
value: "claude|codex",
|
|
131
|
-
description: "Agent client to configure (repeatable)"
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
name: "--dir",
|
|
135
|
-
value: "<path>",
|
|
136
|
-
description: "Target repository root",
|
|
137
|
-
default: "current working directory"
|
|
138
|
-
}
|
|
139
|
-
],
|
|
140
|
-
examples: [{
|
|
141
|
-
description: "Install skills and MCP config for both Claude and Codex",
|
|
142
|
-
command: "legreffier setup --name jobi --agent claude --agent codex"
|
|
143
|
-
}]
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
command: "port",
|
|
147
|
-
summary: "Copy an existing agent from another repository into this one",
|
|
148
|
-
usage: "legreffier port --name <agent-name> --from <repo-root>/.moltnet/<agent-name> [flags]",
|
|
149
|
-
description: "Ports an existing agent identity (keypair, moltnet.json, gitconfig, GitHub App credentials) from a source repository into the current one. `--from` is strict: it must point to the exact `<repo-root>/.moltnet/<agent-name>` directory. The source repo is not modified.",
|
|
150
|
-
flags: [
|
|
151
|
-
{
|
|
152
|
-
name: "--name",
|
|
153
|
-
short: "-n",
|
|
154
|
-
value: "<agent-name>",
|
|
155
|
-
description: "Agent name to port (must exist under `--from`)",
|
|
156
|
-
required: true
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
name: "--from",
|
|
160
|
-
value: "<repo-root>/.moltnet/<agent-name>",
|
|
161
|
-
description: "Absolute path to the source agent directory. Strict format: must be `<repo-root>/.moltnet/<agent-name>` and contain moltnet.json + gitconfig.",
|
|
162
|
-
required: true
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
name: "--agent",
|
|
166
|
-
short: "-a",
|
|
167
|
-
value: "claude|codex",
|
|
168
|
-
description: "Agent client to configure in the target repo (repeatable)",
|
|
169
|
-
default: "claude"
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
name: "--dir",
|
|
173
|
-
value: "<path>",
|
|
174
|
-
description: "Target repository root",
|
|
175
|
-
default: "current working directory"
|
|
176
|
-
},
|
|
177
|
-
{
|
|
178
|
-
name: "--diary",
|
|
179
|
-
value: "new|reuse|skip",
|
|
180
|
-
description: "How to handle the diary in the new repo: `new` creates a fresh diary, `reuse` reuses the source diary ID, `skip` leaves MOLTNET_DIARY_ID unset",
|
|
181
|
-
default: "new"
|
|
182
|
-
}
|
|
183
|
-
],
|
|
184
|
-
examples: [{
|
|
185
|
-
description: "Port agent `jobi` from a sibling repo",
|
|
186
|
-
command: "legreffier port --name jobi --from /Users/me/code/other-repo/.moltnet/jobi"
|
|
187
|
-
}, {
|
|
188
|
-
description: "Port and reuse the existing diary",
|
|
189
|
-
command: "legreffier port --name jobi --from /Users/me/code/other-repo/.moltnet/jobi --diary reuse"
|
|
190
|
-
}],
|
|
191
|
-
notes: ["The source repo is read-only; nothing there is modified.", "`--from` does not accept relative paths, `~`, or repo-name shorthands. Provide the full `.moltnet/<agent>` directory path."]
|
|
192
|
-
}
|
|
193
|
-
];
|
|
194
|
-
//#endregion
|
|
195
|
-
//#region src/commands/resolveCommand.ts
|
|
196
|
-
/**
|
|
197
|
-
* Long-form option names that consume the next argument as a value.
|
|
198
|
-
* Used by `resolveHelpCommand` to skip over option values when scanning
|
|
199
|
-
* for the first positional subcommand.
|
|
200
|
-
*/
|
|
201
|
-
var VALUE_OPTIONS_LONG = new Set([
|
|
202
|
-
"--name",
|
|
203
|
-
"--agent",
|
|
204
|
-
"--api-url",
|
|
205
|
-
"--dir",
|
|
206
|
-
"--org",
|
|
207
|
-
"--from",
|
|
208
|
-
"--diary"
|
|
209
|
-
]);
|
|
210
|
-
/**
|
|
211
|
-
* Short-form option names that consume the next argument as a value.
|
|
212
|
-
* Kept in sync with `parseArgs` options in index.tsx.
|
|
213
|
-
*/
|
|
214
|
-
var VALUE_OPTIONS_SHORT = new Set([
|
|
215
|
-
"-n",
|
|
216
|
-
"-a",
|
|
217
|
-
"-o"
|
|
218
|
-
]);
|
|
219
|
-
function isValueOption(arg) {
|
|
220
|
-
if (VALUE_OPTIONS_LONG.has(arg)) return true;
|
|
221
|
-
if (VALUE_OPTIONS_SHORT.has(arg)) return true;
|
|
222
|
-
return false;
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Resolve which command's help to print for `legreffier <...> --help`.
|
|
226
|
-
*
|
|
227
|
-
* Scans `rawArgs` linearly, skipping option flags and their values, and
|
|
228
|
-
* returns the first positional argument that matches a known command. If
|
|
229
|
-
* no positional is found (or the positional is not a known command),
|
|
230
|
-
* returns `null` so the caller prints root help.
|
|
231
|
-
*
|
|
232
|
-
* Unlike `rawArgs.find((a) => !a.startsWith('-'))`, this correctly handles
|
|
233
|
-
* flags-before-subcommand orderings like:
|
|
234
|
-
*
|
|
235
|
-
* legreffier --name jobi port --help
|
|
236
|
-
*
|
|
237
|
-
* where the naive scan would return "jobi" instead of "port".
|
|
238
|
-
*
|
|
239
|
-
* Unknown positionals (typos, etc.) fall back to root help rather than
|
|
240
|
-
* silently matching nothing, so users see the full command list.
|
|
241
|
-
*/
|
|
242
|
-
function resolveHelpCommand(rawArgs, commands) {
|
|
243
|
-
for (let i = 0; i < rawArgs.length; i++) {
|
|
244
|
-
const arg = rawArgs[i];
|
|
245
|
-
if (arg === void 0) continue;
|
|
246
|
-
if (arg === "--help" || arg === "-h") continue;
|
|
247
|
-
if (arg.startsWith("--") && arg.includes("=")) continue;
|
|
248
|
-
if (arg.startsWith("--")) {
|
|
249
|
-
if (isValueOption(arg)) i++;
|
|
250
|
-
continue;
|
|
251
|
-
}
|
|
252
|
-
if (arg.startsWith("-") && arg.length > 1) {
|
|
253
|
-
if (isValueOption(arg)) i++;
|
|
254
|
-
continue;
|
|
255
|
-
}
|
|
256
|
-
return commands.find((c) => c.command === arg) ?? null;
|
|
257
|
-
}
|
|
258
|
-
return null;
|
|
259
|
-
}
|
|
260
|
-
//#endregion
|
|
15
|
+
import { statSync } from "node:fs";
|
|
261
16
|
//#region src/github-token.ts
|
|
262
17
|
var MOLTNET_GITCONFIG_RE = /\.moltnet\/([^/]+)\/gitconfig$/;
|
|
263
18
|
function resolveAgentName(nameFlag, gitConfigGlobal) {
|
|
@@ -289,6 +44,146 @@ function printGitHubToken(agentName, dir) {
|
|
|
289
44
|
process.stdout.write(token);
|
|
290
45
|
}
|
|
291
46
|
//#endregion
|
|
47
|
+
//#region src/ui/types.ts
|
|
48
|
+
var SUPPORTED_AGENTS = ["claude", "codex"];
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/commands/shared.ts
|
|
51
|
+
/**
|
|
52
|
+
* Regex for valid agent names: 2-39 chars, lowercase alphanumerics and
|
|
53
|
+
* hyphens, must start and end with an alphanumeric. Matches the server-side
|
|
54
|
+
* validation so a client-side failure gives immediate feedback rather than
|
|
55
|
+
* a REST 400 after a keypair has been generated.
|
|
56
|
+
*/
|
|
57
|
+
var AGENT_NAME_RE = /^[a-z0-9][a-z0-9-]{0,37}[a-z0-9]$/;
|
|
58
|
+
var DEFAULT_API_URL = "https://api.themolt.net";
|
|
59
|
+
/**
|
|
60
|
+
* Common argument definitions shared across subcommands. Kept as plain
|
|
61
|
+
* objects (not a merged record) so individual commands can pick the flags
|
|
62
|
+
* they actually accept instead of inheriting every flag.
|
|
63
|
+
*/
|
|
64
|
+
var commonArgs = {
|
|
65
|
+
name: {
|
|
66
|
+
type: "string",
|
|
67
|
+
description: "Agent name (2-39 lowercase alphanumerics or hyphens, e.g. `jobi`)",
|
|
68
|
+
alias: "n",
|
|
69
|
+
valueHint: "agent-name"
|
|
70
|
+
},
|
|
71
|
+
agent: {
|
|
72
|
+
type: "string",
|
|
73
|
+
description: "Agent client to configure (repeatable: --agent claude --agent codex). Accepts: claude, codex.",
|
|
74
|
+
alias: "a",
|
|
75
|
+
valueHint: "claude|codex"
|
|
76
|
+
},
|
|
77
|
+
"api-url": {
|
|
78
|
+
type: "string",
|
|
79
|
+
description: "MoltNet API base URL (default: $MOLTNET_API_URL or https://api.themolt.net)",
|
|
80
|
+
valueHint: "url"
|
|
81
|
+
},
|
|
82
|
+
dir: {
|
|
83
|
+
type: "string",
|
|
84
|
+
description: "Target repository root (default: current working directory)",
|
|
85
|
+
valueHint: "path"
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Collect repeated `--agent` / `-a` values from rawArgs.
|
|
90
|
+
*
|
|
91
|
+
* Citty wraps Node's `parseArgs` without `multiple: true`, which means
|
|
92
|
+
* repeating `--agent claude --agent codex` keeps only the last value. The
|
|
93
|
+
* hand-rolled CLI supported repeats and users' docs rely on that shape, so
|
|
94
|
+
* we walk `rawArgs` ourselves to rebuild the full list before validating it
|
|
95
|
+
* against the supported-agent allowlist.
|
|
96
|
+
*/
|
|
97
|
+
function collectAgents(rawArgs) {
|
|
98
|
+
const out = [];
|
|
99
|
+
for (let i = 0; i < rawArgs.length; i++) {
|
|
100
|
+
const token = rawArgs[i];
|
|
101
|
+
if (token === "--") break;
|
|
102
|
+
let value;
|
|
103
|
+
if (token === "--agent" || token === "-a") {
|
|
104
|
+
value = rawArgs[i + 1];
|
|
105
|
+
i++;
|
|
106
|
+
} else if (token.startsWith("--agent=")) value = token.slice(8);
|
|
107
|
+
else if (token.startsWith("-a=")) value = token.slice(3);
|
|
108
|
+
if (value === void 0) continue;
|
|
109
|
+
if (!SUPPORTED_AGENTS.includes(value)) throw new CliValidationError(`Unsupported agent: ${value}. Supported: ${SUPPORTED_AGENTS.join(", ")}`);
|
|
110
|
+
out.push(value);
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Validate a `--name` arg. Throws with the same user-facing message as the
|
|
116
|
+
* previous hand-rolled CLI so scripts and docs referencing it keep working.
|
|
117
|
+
*/
|
|
118
|
+
function requireAgentName(name) {
|
|
119
|
+
if (typeof name !== "string" || name.length === 0) throw new CliValidationError("--name is required");
|
|
120
|
+
if (!AGENT_NAME_RE.test(name)) throw new CliValidationError(`Invalid agent name: "${name}". Must be 2-39 lowercase alphanumeric characters or hyphens, starting and ending with a letter or digit.`);
|
|
121
|
+
return name;
|
|
122
|
+
}
|
|
123
|
+
/** Resolve the target repo dir, defaulting to CWD. */
|
|
124
|
+
function resolveDir(dir) {
|
|
125
|
+
if (typeof dir === "string" && dir.length > 0) return dir;
|
|
126
|
+
return process.cwd();
|
|
127
|
+
}
|
|
128
|
+
/** Resolve the API URL: --api-url flag > $MOLTNET_API_URL > default. */
|
|
129
|
+
function resolveApiUrl(apiUrl) {
|
|
130
|
+
if (typeof apiUrl === "string" && apiUrl.length > 0) return apiUrl;
|
|
131
|
+
return process.env["MOLTNET_API_URL"] ?? DEFAULT_API_URL;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Thrown by shared validators (`requireAgentName`, `collectAgents`,
|
|
135
|
+
* `validatePortFromArg` adapters, etc.) when the user passed a bad flag
|
|
136
|
+
* value. `withCleanErrors` catches these and prints a single-line
|
|
137
|
+
* "Error: <msg>" on stderr + exit 1, instead of letting citty dump the
|
|
138
|
+
* full stack via its default `console.error(error, "\n")` handler.
|
|
139
|
+
*/
|
|
140
|
+
var CliValidationError = class extends Error {
|
|
141
|
+
name = "CliValidationError";
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* Wrap a command handler so `CliValidationError`s print a single clean
|
|
145
|
+
* line and exit 1. Unexpected errors (bugs, TypeErrors, network failures)
|
|
146
|
+
* still bubble up with their full stack so we can debug them.
|
|
147
|
+
*/
|
|
148
|
+
function withCleanErrors(handler) {
|
|
149
|
+
return async (ctx) => {
|
|
150
|
+
try {
|
|
151
|
+
await handler(ctx);
|
|
152
|
+
} catch (err) {
|
|
153
|
+
if (err instanceof CliValidationError) {
|
|
154
|
+
process.stderr.write(`Error: ${err.message}\n`);
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
throw err;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
var githubCommand = defineCommand({
|
|
162
|
+
meta: {
|
|
163
|
+
name: "github",
|
|
164
|
+
description: "GitHub-related helpers (token minting)"
|
|
165
|
+
},
|
|
166
|
+
subCommands: { token: defineCommand({
|
|
167
|
+
meta: {
|
|
168
|
+
name: "token",
|
|
169
|
+
description: "Print a short-lived installation token for the agent GitHub App (reads .moltnet/<agent>/moltnet.json)."
|
|
170
|
+
},
|
|
171
|
+
args: {
|
|
172
|
+
name: commonArgs.name,
|
|
173
|
+
dir: commonArgs.dir
|
|
174
|
+
},
|
|
175
|
+
run: withCleanErrors(({ args }) => {
|
|
176
|
+
let agentName;
|
|
177
|
+
try {
|
|
178
|
+
agentName = resolveAgentName(typeof args.name === "string" ? args.name : void 0, process.env["GIT_CONFIG_GLOBAL"]);
|
|
179
|
+
} catch (err) {
|
|
180
|
+
throw new CliValidationError(err instanceof Error ? err.message : String(err));
|
|
181
|
+
}
|
|
182
|
+
printGitHubToken(agentName, resolveDir(args.dir));
|
|
183
|
+
})
|
|
184
|
+
}) }
|
|
185
|
+
});
|
|
186
|
+
//#endregion
|
|
292
187
|
//#region ../../libs/design-system/src/tokens.ts
|
|
293
188
|
/**
|
|
294
189
|
* MoltNet Design Tokens
|
|
@@ -572,7 +467,7 @@ var QUILL_LINES = [
|
|
|
572
467
|
" ╲───────╲───────╲",
|
|
573
468
|
" ◆"
|
|
574
469
|
];
|
|
575
|
-
var WORDMARK
|
|
470
|
+
var WORDMARK = [
|
|
576
471
|
" __ __ ___ _ _____ _ _ ___ _____",
|
|
577
472
|
"| \\/ |/ _ \\| ||_ _|| \\| | __|_ _|",
|
|
578
473
|
"| |\\/| | (_) | |__| | | .` | _| | | ",
|
|
@@ -619,7 +514,7 @@ function CliHero({ animated = false }) {
|
|
|
619
514
|
color: glowColor,
|
|
620
515
|
children: HALO_TOP
|
|
621
516
|
}),
|
|
622
|
-
WORDMARK
|
|
517
|
+
WORDMARK.map((line, i) => /* @__PURE__ */ jsxs(Text, { children: [
|
|
623
518
|
/* @__PURE__ */ jsx(Text, {
|
|
624
519
|
color: glowColor,
|
|
625
520
|
children: " · · │ "
|
|
@@ -680,7 +575,6 @@ function CliHero({ animated = false }) {
|
|
|
680
575
|
})
|
|
681
576
|
});
|
|
682
577
|
}
|
|
683
|
-
figlet.textSync("MOLTNET", { font: "slant" });
|
|
684
578
|
//#endregion
|
|
685
579
|
//#region ../../libs/design-system/src/cli/CliSpinner.tsx
|
|
686
580
|
var FRAMES = [
|
|
@@ -8189,6 +8083,63 @@ async function writeEnvFile(opts) {
|
|
|
8189
8083
|
}
|
|
8190
8084
|
await writeFile(envPath, outputLines.join("\n") + "\n", "utf-8");
|
|
8191
8085
|
}
|
|
8086
|
+
/**
|
|
8087
|
+
* Resolve the human operator's git identity from global git config.
|
|
8088
|
+
* Must be called BEFORE GIT_CONFIG_GLOBAL is set (so it reads the
|
|
8089
|
+
* human's config, not the agent's).
|
|
8090
|
+
*
|
|
8091
|
+
* Returns `"Name <email>"` or `null` if either is missing.
|
|
8092
|
+
*/
|
|
8093
|
+
function resolveHumanGitIdentity() {
|
|
8094
|
+
try {
|
|
8095
|
+
const name = execFileSync("git", [
|
|
8096
|
+
"config",
|
|
8097
|
+
"--global",
|
|
8098
|
+
"user.name"
|
|
8099
|
+
], {
|
|
8100
|
+
encoding: "utf-8",
|
|
8101
|
+
env: {
|
|
8102
|
+
...process.env,
|
|
8103
|
+
GIT_CONFIG_GLOBAL: void 0
|
|
8104
|
+
}
|
|
8105
|
+
}).trim();
|
|
8106
|
+
const email = execFileSync("git", [
|
|
8107
|
+
"config",
|
|
8108
|
+
"--global",
|
|
8109
|
+
"user.email"
|
|
8110
|
+
], {
|
|
8111
|
+
encoding: "utf-8",
|
|
8112
|
+
env: {
|
|
8113
|
+
...process.env,
|
|
8114
|
+
GIT_CONFIG_GLOBAL: void 0
|
|
8115
|
+
}
|
|
8116
|
+
}).trim();
|
|
8117
|
+
return name && email ? `${name} <${email}>` : null;
|
|
8118
|
+
} catch {
|
|
8119
|
+
return null;
|
|
8120
|
+
}
|
|
8121
|
+
}
|
|
8122
|
+
/**
|
|
8123
|
+
* Append MOLTNET_HUMAN_GIT_IDENTITY and optionally MOLTNET_COMMIT_AUTHORSHIP
|
|
8124
|
+
* to an existing env file if not already present.
|
|
8125
|
+
* Preserves existing content — only appends new vars.
|
|
8126
|
+
*/
|
|
8127
|
+
async function appendAuthorshipVars(envDir, humanGitIdentity, commitAuthorship) {
|
|
8128
|
+
const envPath = join(envDir, "env");
|
|
8129
|
+
let existing = "";
|
|
8130
|
+
try {
|
|
8131
|
+
existing = await readFile(envPath, "utf-8");
|
|
8132
|
+
} catch {
|
|
8133
|
+
return;
|
|
8134
|
+
}
|
|
8135
|
+
const lines = [];
|
|
8136
|
+
const hasVar = (content, key) => new RegExp(`^${key}=`, "m").test(content);
|
|
8137
|
+
if (humanGitIdentity && !hasVar(existing, "MOLTNET_HUMAN_GIT_IDENTITY")) lines.push(`MOLTNET_HUMAN_GIT_IDENTITY=${q(humanGitIdentity)}`);
|
|
8138
|
+
if (commitAuthorship && !hasVar(existing, "MOLTNET_COMMIT_AUTHORSHIP")) lines.push(`MOLTNET_COMMIT_AUTHORSHIP=${q(commitAuthorship)}`);
|
|
8139
|
+
if (lines.length === 0) return;
|
|
8140
|
+
const suffix = lines.join("\n") + "\n";
|
|
8141
|
+
await writeFile(envPath, existing.endsWith("\n") ? existing + suffix : existing + "\n" + suffix, "utf-8");
|
|
8142
|
+
}
|
|
8192
8143
|
//#endregion
|
|
8193
8144
|
//#region src/state.ts
|
|
8194
8145
|
function getStatePath(configDir) {
|
|
@@ -8293,6 +8244,7 @@ async function runAgentSetupPhase(opts) {
|
|
|
8293
8244
|
pemPath,
|
|
8294
8245
|
installationId
|
|
8295
8246
|
});
|
|
8247
|
+
await appendAuthorshipVars(configDir, opts.humanGitIdentity ?? resolveHumanGitIdentity(), opts.commitAuthorship);
|
|
8296
8248
|
await clearState(configDir);
|
|
8297
8249
|
}
|
|
8298
8250
|
//#endregion
|
|
@@ -8851,9 +8803,6 @@ async function runInstallationPhase(opts) {
|
|
|
8851
8803
|
};
|
|
8852
8804
|
}
|
|
8853
8805
|
//#endregion
|
|
8854
|
-
//#region src/ui/types.ts
|
|
8855
|
-
var SUPPORTED_AGENTS = ["claude", "codex"];
|
|
8856
|
-
//#endregion
|
|
8857
8806
|
//#region src/ui/AgentSelect.tsx
|
|
8858
8807
|
var AGENTS = [
|
|
8859
8808
|
{
|
|
@@ -9093,12 +9042,26 @@ function ProgressPhase({ state, name, showManifestFallback, showInstallFallback
|
|
|
9093
9042
|
flexDirection: "column",
|
|
9094
9043
|
children: steps.githubApp === "running" ? /* @__PURE__ */ jsxs(Box, {
|
|
9095
9044
|
flexDirection: "column",
|
|
9096
|
-
children: [/* @__PURE__ */ jsx(CliSpinner, { label: githubAppSpinnerLabel }),
|
|
9097
|
-
|
|
9098
|
-
children: [
|
|
9099
|
-
|
|
9100
|
-
|
|
9101
|
-
|
|
9045
|
+
children: [/* @__PURE__ */ jsx(CliSpinner, { label: githubAppSpinnerLabel }), manifestFormUrl ? /* @__PURE__ */ jsxs(Box, {
|
|
9046
|
+
flexDirection: "column",
|
|
9047
|
+
children: [
|
|
9048
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
9049
|
+
color: cliTheme.color.text,
|
|
9050
|
+
children: [" ", "Confirm the GitHub App creation in your browser:"]
|
|
9051
|
+
}),
|
|
9052
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
9053
|
+
color: cliTheme.color.accent,
|
|
9054
|
+
children: [
|
|
9055
|
+
" ",
|
|
9056
|
+
"→ ",
|
|
9057
|
+
manifestFormUrl
|
|
9058
|
+
]
|
|
9059
|
+
}),
|
|
9060
|
+
showManifestFallback ? /* @__PURE__ */ jsxs(Text, {
|
|
9061
|
+
color: cliTheme.color.muted,
|
|
9062
|
+
children: [" ", "Browser didn't open? Copy the link above."]
|
|
9063
|
+
}) : null
|
|
9064
|
+
]
|
|
9102
9065
|
}) : null]
|
|
9103
9066
|
}) : /* @__PURE__ */ jsx(CliStatusLine, {
|
|
9104
9067
|
label: "Create GitHub App",
|
|
@@ -9125,12 +9088,26 @@ function ProgressPhase({ state, name, showManifestFallback, showInstallFallback
|
|
|
9125
9088
|
children: [
|
|
9126
9089
|
steps.installation === "running" ? /* @__PURE__ */ jsxs(Box, {
|
|
9127
9090
|
flexDirection: "column",
|
|
9128
|
-
children: [/* @__PURE__ */ jsx(CliSpinner, { label: installationSpinnerLabel }),
|
|
9129
|
-
|
|
9130
|
-
children: [
|
|
9131
|
-
|
|
9132
|
-
|
|
9133
|
-
|
|
9091
|
+
children: [/* @__PURE__ */ jsx(CliSpinner, { label: installationSpinnerLabel }), installationUrl ? /* @__PURE__ */ jsxs(Box, {
|
|
9092
|
+
flexDirection: "column",
|
|
9093
|
+
children: [
|
|
9094
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
9095
|
+
color: cliTheme.color.text,
|
|
9096
|
+
children: [" ", "Install the GitHub App on your account/org:"]
|
|
9097
|
+
}),
|
|
9098
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
9099
|
+
color: cliTheme.color.accent,
|
|
9100
|
+
children: [
|
|
9101
|
+
" ",
|
|
9102
|
+
"→ ",
|
|
9103
|
+
installationUrl
|
|
9104
|
+
]
|
|
9105
|
+
}),
|
|
9106
|
+
showInstallFallback ? /* @__PURE__ */ jsxs(Text, {
|
|
9107
|
+
color: cliTheme.color.muted,
|
|
9108
|
+
children: [" ", "Browser didn't open? Copy the link above."]
|
|
9109
|
+
}) : null
|
|
9110
|
+
]
|
|
9134
9111
|
}) : null]
|
|
9135
9112
|
}) : /* @__PURE__ */ jsx(CliStatusLine, {
|
|
9136
9113
|
label: "GitHub App installation",
|
|
@@ -9288,6 +9265,43 @@ function InitApp({ name, agents: agentsProp, apiUrl, dir = process.cwd(), org })
|
|
|
9288
9265
|
});
|
|
9289
9266
|
}
|
|
9290
9267
|
//#endregion
|
|
9268
|
+
//#region src/commands/init.tsx
|
|
9269
|
+
var initCommand = defineCommand({
|
|
9270
|
+
meta: {
|
|
9271
|
+
name: "init",
|
|
9272
|
+
description: "Create a new agent identity and wire it into this repository"
|
|
9273
|
+
},
|
|
9274
|
+
args: {
|
|
9275
|
+
name: {
|
|
9276
|
+
...commonArgs.name,
|
|
9277
|
+
required: true
|
|
9278
|
+
},
|
|
9279
|
+
agent: commonArgs.agent,
|
|
9280
|
+
"api-url": commonArgs["api-url"],
|
|
9281
|
+
dir: commonArgs.dir,
|
|
9282
|
+
org: {
|
|
9283
|
+
type: "string",
|
|
9284
|
+
description: "GitHub organization to install the App on (optional)",
|
|
9285
|
+
alias: "o",
|
|
9286
|
+
valueHint: "github-org"
|
|
9287
|
+
}
|
|
9288
|
+
},
|
|
9289
|
+
run: withCleanErrors(({ args, rawArgs }) => {
|
|
9290
|
+
const name = requireAgentName(args.name);
|
|
9291
|
+
const agents = collectAgents(rawArgs);
|
|
9292
|
+
const apiUrl = resolveApiUrl(args["api-url"]);
|
|
9293
|
+
const dir = resolveDir(args.dir);
|
|
9294
|
+
const org = typeof args.org === "string" ? args.org : void 0;
|
|
9295
|
+
render(/* @__PURE__ */ jsx(InitApp, {
|
|
9296
|
+
name,
|
|
9297
|
+
agents: agents.length > 0 ? agents : void 0,
|
|
9298
|
+
apiUrl,
|
|
9299
|
+
dir,
|
|
9300
|
+
org
|
|
9301
|
+
}));
|
|
9302
|
+
})
|
|
9303
|
+
});
|
|
9304
|
+
//#endregion
|
|
9291
9305
|
//#region src/phases/portArgs.ts
|
|
9292
9306
|
/**
|
|
9293
9307
|
* Validate the raw `--from` argument passed to `legreffier port` before
|
|
@@ -9602,6 +9616,7 @@ async function runPortRewritePhase(opts) {
|
|
|
9602
9616
|
pemPath: newPem,
|
|
9603
9617
|
installationId: config.github.installation_id
|
|
9604
9618
|
});
|
|
9619
|
+
await appendAuthorshipVars(targetDir, resolveHumanGitIdentity());
|
|
9605
9620
|
return {
|
|
9606
9621
|
configPath: join(targetDir, "moltnet.json"),
|
|
9607
9622
|
rewrittenFields,
|
|
@@ -9894,6 +9909,68 @@ function PortApp({ name, agents, sourceDir, targetRepoDir, diaryMode, apiUrl })
|
|
|
9894
9909
|
});
|
|
9895
9910
|
}
|
|
9896
9911
|
//#endregion
|
|
9912
|
+
//#region src/commands/port.tsx
|
|
9913
|
+
var DIARY_MODES = [
|
|
9914
|
+
"new",
|
|
9915
|
+
"reuse",
|
|
9916
|
+
"skip"
|
|
9917
|
+
];
|
|
9918
|
+
function assertDirectory(label, path) {
|
|
9919
|
+
try {
|
|
9920
|
+
if (!statSync(path).isDirectory()) throw new CliValidationError(`${label} "${path}" is not a directory`);
|
|
9921
|
+
} catch (err) {
|
|
9922
|
+
if (err instanceof CliValidationError) throw err;
|
|
9923
|
+
throw new CliValidationError(`${label} "${path}" does not exist`);
|
|
9924
|
+
}
|
|
9925
|
+
}
|
|
9926
|
+
var portCommand = defineCommand({
|
|
9927
|
+
meta: {
|
|
9928
|
+
name: "port",
|
|
9929
|
+
description: "Copy an existing agent from another repository into this one"
|
|
9930
|
+
},
|
|
9931
|
+
args: {
|
|
9932
|
+
name: {
|
|
9933
|
+
...commonArgs.name,
|
|
9934
|
+
required: true
|
|
9935
|
+
},
|
|
9936
|
+
from: {
|
|
9937
|
+
type: "string",
|
|
9938
|
+
description: "Absolute path to the source agent directory (must be `<repo-root>/.moltnet/<agent-name>` and contain moltnet.json + gitconfig)",
|
|
9939
|
+
required: true,
|
|
9940
|
+
valueHint: "repo-root/.moltnet/agent-name"
|
|
9941
|
+
},
|
|
9942
|
+
agent: commonArgs.agent,
|
|
9943
|
+
"api-url": commonArgs["api-url"],
|
|
9944
|
+
dir: commonArgs.dir,
|
|
9945
|
+
diary: {
|
|
9946
|
+
type: "enum",
|
|
9947
|
+
description: "How to handle the diary in the new repo: `new` creates a fresh diary, `reuse` reuses the source diary ID, `skip` leaves MOLTNET_DIARY_ID unset",
|
|
9948
|
+
options: [...DIARY_MODES],
|
|
9949
|
+
default: "new",
|
|
9950
|
+
valueHint: "new|reuse|skip"
|
|
9951
|
+
}
|
|
9952
|
+
},
|
|
9953
|
+
run: withCleanErrors(({ args, rawArgs }) => {
|
|
9954
|
+
const name = requireAgentName(args.name);
|
|
9955
|
+
const agents = collectAgents(rawArgs);
|
|
9956
|
+
const apiUrl = resolveApiUrl(args["api-url"]);
|
|
9957
|
+
const dir = resolveDir(args.dir);
|
|
9958
|
+
const fromValidation = validatePortFromArg(args.from);
|
|
9959
|
+
if (!fromValidation.ok) throw new CliValidationError(fromValidation.error);
|
|
9960
|
+
const absoluteFromDir = args.from;
|
|
9961
|
+
assertDirectory("--dir", dir);
|
|
9962
|
+
assertDirectory("--from", absoluteFromDir);
|
|
9963
|
+
render(/* @__PURE__ */ jsx(PortApp, {
|
|
9964
|
+
name,
|
|
9965
|
+
agents: agents.length > 0 ? agents : ["claude"],
|
|
9966
|
+
sourceDir: absoluteFromDir,
|
|
9967
|
+
targetRepoDir: dir,
|
|
9968
|
+
diaryMode: args.diary,
|
|
9969
|
+
apiUrl
|
|
9970
|
+
}));
|
|
9971
|
+
})
|
|
9972
|
+
});
|
|
9973
|
+
//#endregion
|
|
9897
9974
|
//#region src/SetupApp.tsx
|
|
9898
9975
|
function SetupApp({ name, agents: agentsProp, apiUrl, dir }) {
|
|
9899
9976
|
const { exit } = useApp();
|
|
@@ -10009,131 +10086,39 @@ function SetupApp({ name, agents: agentsProp, apiUrl, dir }) {
|
|
|
10009
10086
|
}
|
|
10010
10087
|
//#endregion
|
|
10011
10088
|
//#region src/index.tsx
|
|
10012
|
-
|
|
10013
|
-
|
|
10014
|
-
|
|
10015
|
-
|
|
10016
|
-
|
|
10017
|
-
|
|
10018
|
-
|
|
10019
|
-
|
|
10020
|
-
|
|
10021
|
-
|
|
10022
|
-
|
|
10023
|
-
|
|
10024
|
-
|
|
10025
|
-
|
|
10026
|
-
|
|
10027
|
-
|
|
10028
|
-
|
|
10029
|
-
|
|
10030
|
-
|
|
10031
|
-
|
|
10032
|
-
|
|
10033
|
-
|
|
10034
|
-
|
|
10035
|
-
|
|
10036
|
-
|
|
10037
|
-
|
|
10038
|
-
|
|
10039
|
-
|
|
10040
|
-
|
|
10041
|
-
},
|
|
10042
|
-
|
|
10043
|
-
|
|
10089
|
+
runMain(defineCommand({
|
|
10090
|
+
meta: {
|
|
10091
|
+
name: "legreffier",
|
|
10092
|
+
description: "LeGreffier — attribution and measured memory for AI coding agents"
|
|
10093
|
+
},
|
|
10094
|
+
subCommands: {
|
|
10095
|
+
init: initCommand,
|
|
10096
|
+
setup: defineCommand({
|
|
10097
|
+
meta: {
|
|
10098
|
+
name: "setup",
|
|
10099
|
+
description: "Install LeGreffier skills and MCP config into an existing repo"
|
|
10100
|
+
},
|
|
10101
|
+
args: {
|
|
10102
|
+
name: {
|
|
10103
|
+
...commonArgs.name,
|
|
10104
|
+
required: true
|
|
10105
|
+
},
|
|
10106
|
+
agent: commonArgs.agent,
|
|
10107
|
+
"api-url": commonArgs["api-url"],
|
|
10108
|
+
dir: commonArgs.dir
|
|
10109
|
+
},
|
|
10110
|
+
run: withCleanErrors(({ args, rawArgs }) => {
|
|
10111
|
+
render(/* @__PURE__ */ jsx(SetupApp, {
|
|
10112
|
+
name: requireAgentName(args.name),
|
|
10113
|
+
agents: collectAgents(rawArgs),
|
|
10114
|
+
apiUrl: resolveApiUrl(args["api-url"]),
|
|
10115
|
+
dir: resolveDir(args.dir)
|
|
10116
|
+
}));
|
|
10117
|
+
})
|
|
10118
|
+
}),
|
|
10119
|
+
port: portCommand,
|
|
10120
|
+
github: githubCommand
|
|
10044
10121
|
}
|
|
10045
|
-
});
|
|
10046
|
-
var subcommand = positionals[0] ?? "init";
|
|
10047
|
-
var name = values["name"];
|
|
10048
|
-
var agentFlags = values["agent"] ?? [];
|
|
10049
|
-
var apiUrl = values["api-url"] ?? process.env.MOLTNET_API_URL ?? "https://api.themolt.net";
|
|
10050
|
-
var dir = values["dir"] ?? process.cwd();
|
|
10051
|
-
var org = values["org"];
|
|
10052
|
-
var fromDir = values["from"];
|
|
10053
|
-
var diaryModeArg = values["diary"];
|
|
10054
|
-
if (diaryModeArg !== void 0 && subcommand !== "port") {
|
|
10055
|
-
process.stderr.write(`Error: --diary is only valid for \`legreffier port\` (got subcommand "${subcommand}")\n`);
|
|
10056
|
-
process.exit(1);
|
|
10057
|
-
}
|
|
10058
|
-
if (subcommand === "github" && positionals[1] === "token") try {
|
|
10059
|
-
printGitHubToken(resolveAgentName(name, process.env.GIT_CONFIG_GLOBAL), dir);
|
|
10060
|
-
process.exit(0);
|
|
10061
|
-
} catch (err) {
|
|
10062
|
-
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
10063
|
-
process.exit(1);
|
|
10064
|
-
}
|
|
10065
|
-
if (!name) {
|
|
10066
|
-
const help = COMMANDS.find((c) => c.command === subcommand);
|
|
10067
|
-
process.stderr.write(`Error: --name is required.\n\nRun \`legreffier ${help ? help.command : "<command>"} --help\` for details.\n`);
|
|
10068
|
-
process.exit(1);
|
|
10069
|
-
}
|
|
10070
|
-
if (!/^[a-z0-9][a-z0-9-]{0,37}[a-z0-9]$/.test(name)) {
|
|
10071
|
-
process.stderr.write(`Invalid agent name: "${name}". Must be 2-39 lowercase alphanumeric characters or hyphens, starting and ending with a letter or digit.\n`);
|
|
10072
|
-
process.exit(1);
|
|
10073
|
-
}
|
|
10074
|
-
for (const a of agentFlags) if (!SUPPORTED_AGENTS.includes(a)) {
|
|
10075
|
-
process.stderr.write(`Unsupported agent: ${a}. Supported: ${SUPPORTED_AGENTS.join(", ")}\n`);
|
|
10076
|
-
process.exit(1);
|
|
10077
|
-
}
|
|
10078
|
-
var agents = agentFlags;
|
|
10079
|
-
if (subcommand === "setup") render(/* @__PURE__ */ jsx(SetupApp, {
|
|
10080
|
-
name,
|
|
10081
|
-
agents,
|
|
10082
|
-
apiUrl,
|
|
10083
|
-
dir
|
|
10084
10122
|
}));
|
|
10085
|
-
else if (subcommand === "init") render(/* @__PURE__ */ jsx(InitApp, {
|
|
10086
|
-
name,
|
|
10087
|
-
agents: agents.length > 0 ? agents : void 0,
|
|
10088
|
-
apiUrl,
|
|
10089
|
-
dir,
|
|
10090
|
-
org
|
|
10091
|
-
}));
|
|
10092
|
-
else if (subcommand === "port") {
|
|
10093
|
-
const fromValidation = validatePortFromArg(fromDir);
|
|
10094
|
-
if (!fromValidation.ok) {
|
|
10095
|
-
process.stderr.write(`Error: ${fromValidation.error}\n`);
|
|
10096
|
-
process.exit(1);
|
|
10097
|
-
}
|
|
10098
|
-
const absoluteFromDir = fromDir;
|
|
10099
|
-
const resolvedDiaryMode = diaryModeArg ?? "new";
|
|
10100
|
-
if (![
|
|
10101
|
-
"new",
|
|
10102
|
-
"reuse",
|
|
10103
|
-
"skip"
|
|
10104
|
-
].includes(resolvedDiaryMode)) {
|
|
10105
|
-
process.stderr.write(`Error: --diary must be one of: new, reuse, skip (got "${resolvedDiaryMode}")\n`);
|
|
10106
|
-
process.exit(1);
|
|
10107
|
-
}
|
|
10108
|
-
try {
|
|
10109
|
-
if (!statSync(dir).isDirectory()) {
|
|
10110
|
-
process.stderr.write(`Error: --dir "${dir}" is not a directory\n`);
|
|
10111
|
-
process.exit(1);
|
|
10112
|
-
}
|
|
10113
|
-
} catch {
|
|
10114
|
-
process.stderr.write(`Error: --dir "${dir}" does not exist\n`);
|
|
10115
|
-
process.exit(1);
|
|
10116
|
-
}
|
|
10117
|
-
try {
|
|
10118
|
-
if (!statSync(absoluteFromDir).isDirectory()) {
|
|
10119
|
-
process.stderr.write(`Error: --from "${absoluteFromDir}" is not a directory\n`);
|
|
10120
|
-
process.exit(1);
|
|
10121
|
-
}
|
|
10122
|
-
} catch {
|
|
10123
|
-
process.stderr.write(`Error: --from "${absoluteFromDir}" does not exist\n`);
|
|
10124
|
-
process.exit(1);
|
|
10125
|
-
}
|
|
10126
|
-
render(/* @__PURE__ */ jsx(PortApp, {
|
|
10127
|
-
name,
|
|
10128
|
-
agents: agents.length > 0 ? agents : ["claude"],
|
|
10129
|
-
sourceDir: absoluteFromDir,
|
|
10130
|
-
targetRepoDir: dir,
|
|
10131
|
-
diaryMode: resolvedDiaryMode,
|
|
10132
|
-
apiUrl
|
|
10133
|
-
}));
|
|
10134
|
-
} else {
|
|
10135
|
-
process.stderr.write(`Unknown subcommand: ${subcommand}. Use "init", "setup", or "port".\n`);
|
|
10136
|
-
process.exit(1);
|
|
10137
|
-
}
|
|
10138
10123
|
//#endregion
|
|
10139
10124
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@themoltnet/legreffier",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.0",
|
|
4
4
|
"description": "LeGreffier — attribution and measured memory for AI coding agents.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"type": "module",
|
|
@@ -20,24 +20,23 @@
|
|
|
20
20
|
"node": ">=22"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"
|
|
23
|
+
"citty": "^0.2.2",
|
|
24
24
|
"ink": "^6.8.0",
|
|
25
25
|
"open": "^10.1.2",
|
|
26
26
|
"react": "^19.0.0",
|
|
27
27
|
"smol-toml": "^1.6.1"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@types/figlet": "^1.7.0",
|
|
31
30
|
"@types/node": "^20.11.0",
|
|
32
31
|
"@types/react": "^19.0.0",
|
|
33
32
|
"typescript": "^5.3.3",
|
|
34
33
|
"vite": "^8.0.0",
|
|
35
34
|
"vitest": "^3.0.0",
|
|
35
|
+
"@moltnet/crypto-service": "0.1.0",
|
|
36
36
|
"@moltnet/api-client": "0.1.0",
|
|
37
|
-
"@themoltnet/design-system": "0.
|
|
37
|
+
"@themoltnet/design-system": "0.5.1",
|
|
38
38
|
"@themoltnet/github-agent": "0.23.0",
|
|
39
|
-
"@themoltnet/sdk": "0.89.0"
|
|
40
|
-
"@moltnet/crypto-service": "0.1.0"
|
|
39
|
+
"@themoltnet/sdk": "0.89.0"
|
|
41
40
|
},
|
|
42
41
|
"scripts": {
|
|
43
42
|
"dev": "vite build --watch",
|