@geonosis/ledger 1.0.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.
@@ -0,0 +1,339 @@
1
+ import {
2
+ appendFallback,
3
+ captureProof,
4
+ checkArchitecture,
5
+ checkCommitMessage,
6
+ checkDelivery,
7
+ checkPlans,
8
+ formatProve,
9
+ formatStatus,
10
+ loadLedgerConfig,
11
+ messageOf,
12
+ newPlan,
13
+ parseArgs,
14
+ readStatus,
15
+ rejectUnknownSwitches,
16
+ required,
17
+ runDecide,
18
+ runHandoff,
19
+ runProve,
20
+ runSync,
21
+ runTick
22
+ } from "./chunk-WI3N5B3J.js";
23
+
24
+ // src/ledger-cli.ts
25
+ import { readFileSync } from "fs";
26
+ import { resolve } from "path";
27
+ var USAGE = `geonosis-ledger <command> [--root <dir>]
28
+
29
+ The tick ledger: it validates, it refuses, and it appends one row. It authors nothing else.
30
+
31
+ plan check [<file>|<dir>] [--since <NNN>]
32
+ A plan needs a status header, an EARS criterion UNDER its acceptance-criteria
33
+ heading, and a verification section. Prints every violation; exits 1 on any.
34
+ --since exempts lower-numbered plans, reported as legacy.
35
+
36
+ plan new <slug>
37
+ Writes plans/<next NNN>-<slug>.md: the header, the two headings, and a placeholder
38
+ that plan check REFUSES until it is replaced. The skeleton is deliberately red.
39
+
40
+ tick --plan <NNN> --commit <hash> --proof <path> [--note "\u2026"]
41
+ Four refusals: the hash is not a commit \xB7 the plan does not pass plan check \xB7 the
42
+ proof file is not there \xB7 the commit's diff holds no runtime code (G0). On success
43
+ it appends ONE row to the progress file, and that append is its only write.
44
+
45
+ proof capture <slug> --command "<cmd>" [--url <url>]
46
+ Runs the command; writes proofs/<next NNN>-<slug>.md with the HEAD hash, the
47
+ command, the exit code and the output, bounded by ledger.maxLines. The exit code
48
+ is the COMMAND's \u2014 a failing proof is recorded and still fails.
49
+
50
+ journal --kind stub|mock|guess|skip --where <path:line> --why "\u2026"
51
+ Appends a dated row to the fallbacks journal. An unknown kind is refused.
52
+
53
+ decide --id D-NNN --text "\u2026" --why "\u2026" --evidence "\u2026" --revisit "\u2026" [--status "\u2026"]
54
+ Appends a row to the decisions register, each value under the column that names
55
+ it. Refuses without a revisit trigger, and refuses a register whose header has no
56
+ column for one \u2014 printing the header to add. It never rewrites that header itself.
57
+
58
+ handoff [--dry-run]
59
+ Rewrites the SESSION CLOSE block of the handoff file between its markers, from the
60
+ tree alone: HEAD, uncommitted files, the last tick, the last gate report, the open
61
+ plans. Deterministic \u2014 the same tree gives byte-identical output.
62
+
63
+ status [--json]
64
+ The digest: six lines, always the same six, whatever the size of the repo.
65
+
66
+ architecture check [--no-workspace]
67
+ The architecture file has to carry "| tag | packages | may depend on |"; every
68
+ package cell has to name a workspace directory and every edge a declared tag.
69
+ Exit 1 listing the violations. Edges it cannot enforce are printed as notes.
70
+
71
+ sync --target turbo|layer-walls|restricted-imports [--write <file>] [--check <file>]
72
+ One table, three spellings (C8), all generated from it. --write splices only the
73
+ named fragment; --check exits 1 with what is committed beside what is generated.
74
+
75
+ sync --target agents-md|cursor [--write <path>] [--check <path>]
76
+ The same law, in the vocabulary of the other tools. agents-md writes AGENTS.md \u2014
77
+ the law's rules section, one line per skill, and what each scoped rule binds to.
78
+ cursor writes one .cursor/rules/geonosis-<area>.mdc per scoped rule, with Cursor's
79
+ comma-separated globs (a rule scoped to ** becomes alwaysApply). --write takes the
80
+ FILE for agents-md and the DIRECTORY for cursor; --check the same, and a generated
81
+ file that is not there at all is drift.
82
+
83
+ commit-msg <file> | --message "<text>" | --commit <hash>
84
+ Refuses a message longer than ledger.commitMessageLines (default 4 = subject + 3).
85
+ Blank lines, the "#" lines git puts in the editor file, everything past the
86
+ --verbose scissors, and a trailing block of trailers are not counted. Made for a
87
+ commit-msg hook, which is handed the path to the file.
88
+
89
+ delivery check --cached | --commit <hash>
90
+ Refuses a diff whose every changed line is a comment or whitespace. The comment
91
+ tokens are per language, by extension. Deleting comments counts too: a sweep rides
92
+ along with runtime code, the way G0 lets docs ride along.
93
+
94
+ --prove
95
+ Plants a 40-line message and a comments-only diff and requires both to be refused.
96
+ A gate that has never been shown refusing is a claim, not a gate.
97
+
98
+ Every path and pattern is a key of the "ledger" block of geonosis.json. Exit 1 is a refusal with
99
+ its reason on stdout; exit 2 is a run that could not be made at all, with the reason on stderr.`;
100
+ var VALUED = /* @__PURE__ */ new Set([
101
+ "--command",
102
+ "--check",
103
+ "--commit",
104
+ "--evidence",
105
+ "--id",
106
+ "--kind",
107
+ "--message",
108
+ "--note",
109
+ "--plan",
110
+ "--proof",
111
+ "--revisit",
112
+ "--package",
113
+ "--root",
114
+ "--since",
115
+ "--status",
116
+ "--target",
117
+ "--text",
118
+ "--url",
119
+ "--where",
120
+ "--why",
121
+ "--write"
122
+ ]);
123
+ var KNOWN_SWITCHES = /* @__PURE__ */ new Set(["--cached", "--dry-run", "--help", "--json", "--no-workspace"]);
124
+ var contextOf = (argv, cwd) => {
125
+ const args = parseArgs(argv, VALUED);
126
+ rejectUnknownSwitches(args, KNOWN_SWITCHES);
127
+ const root = resolve(cwd, args.flags["--root"] ?? cwd);
128
+ return { args, config: loadLedgerConfig(root), root };
129
+ };
130
+ var planCommand = ({ args, config, root }, verb) => {
131
+ if (verb === "check") {
132
+ const since = args.flags["--since"];
133
+ const report = checkPlans({
134
+ config,
135
+ root,
136
+ ...since === void 0 ? {} : { since: Number(since) },
137
+ ...args.positional[0] === void 0 ? {} : { target: args.positional[0] }
138
+ });
139
+ const lines = [
140
+ ...report.legacy.map((file) => `${file}: legacy`),
141
+ ...report.violations,
142
+ report.violations.length === 0 ? `plan check PASS \u2014 ${report.checked} plan(s), ${report.legacy.length} legacy` : `plan check FAIL \u2014 ${report.violations.length} violation(s) over ${report.checked} plan(s)`
143
+ ];
144
+ return { code: report.violations.length === 0 ? 0 : 1, out: `${lines.join("\n")}
145
+ ` };
146
+ }
147
+ if (verb === "new") {
148
+ const slug = args.positional[0];
149
+ if (slug === void 0) throw new Error("plan new needs a slug");
150
+ return { code: 0, out: `wrote ${newPlan({ config, root, slug })}
151
+ ` };
152
+ }
153
+ throw new Error(`unknown plan command "${verb ?? ""}"`);
154
+ };
155
+ var tickCommand = ({ args, config, root }) => {
156
+ const note = args.flags["--note"];
157
+ const done = runTick({
158
+ commit: required(args, "--commit"),
159
+ config,
160
+ ...note === void 0 ? {} : { note },
161
+ now: /* @__PURE__ */ new Date(),
162
+ plan: required(args, "--plan"),
163
+ proof: required(args, "--proof"),
164
+ root
165
+ });
166
+ if (done.refusal !== void 0) return { code: 1, out: `tick REFUSED \u2014 ${done.refusal}
167
+ ` };
168
+ return { code: 0, out: `tick ${done.number} recorded in ${config.progress}
169
+ ${done.row ?? ""}` };
170
+ };
171
+ var proofCommand = ({ args, config, root }, verb) => {
172
+ if (verb !== "capture") throw new Error(`unknown proof command "${verb ?? ""}"`);
173
+ const slug = args.positional[0];
174
+ if (slug === void 0) throw new Error("proof capture needs a slug");
175
+ const url = args.flags["--url"];
176
+ const done = captureProof({
177
+ command: required(args, "--command"),
178
+ config,
179
+ root,
180
+ slug,
181
+ ...url === void 0 ? {} : { url }
182
+ });
183
+ return { code: done.exitCode, out: `wrote ${done.file} \u2014 exit ${done.exitCode}
184
+ ` };
185
+ };
186
+ var journalCommand = ({ args, config, root }) => {
187
+ const row = appendFallback({
188
+ config,
189
+ kind: required(args, "--kind"),
190
+ now: /* @__PURE__ */ new Date(),
191
+ root,
192
+ where: required(args, "--where"),
193
+ why: required(args, "--why")
194
+ });
195
+ return { code: 0, out: `recorded in ${config.journal}
196
+ ${row}` };
197
+ };
198
+ var decideCommand = ({ args, config, root }) => {
199
+ const status = args.flags["--status"];
200
+ const done = runDecide({
201
+ config,
202
+ evidence: required(args, "--evidence"),
203
+ id: required(args, "--id"),
204
+ revisit: args.flags["--revisit"] ?? "",
205
+ root,
206
+ ...status === void 0 ? {} : { status },
207
+ text: required(args, "--text"),
208
+ why: required(args, "--why")
209
+ });
210
+ if (done.refusal !== void 0) return { code: 1, out: `decide REFUSED \u2014 ${done.refusal}
211
+ ` };
212
+ return { code: 0, out: `recorded in ${config.decisions}
213
+ ${done.row ?? ""}
214
+ ` };
215
+ };
216
+ var handoffCommand = ({ args, config, root }) => {
217
+ const done = runHandoff({ config, root, write: !args.switches.has("--dry-run") });
218
+ return {
219
+ code: 0,
220
+ out: done.written ? `rewrote the SESSION CLOSE block of ${config.handoff}
221
+ ` : `${done.block}
222
+ `
223
+ };
224
+ };
225
+ var statusCommand = ({ args, config, root }) => {
226
+ const digest = readStatus({ config, root });
227
+ return {
228
+ code: 0,
229
+ out: args.switches.has("--json") ? `${JSON.stringify(digest, void 0, 2)}
230
+ ` : `${formatStatus(digest)}
231
+ `
232
+ };
233
+ };
234
+ var architectureCommand = ({ args, config, root }, verb) => {
235
+ if (verb !== "check") throw new Error(`unknown architecture command "${verb ?? ""}"`);
236
+ const report = checkArchitecture({
237
+ config,
238
+ root,
239
+ workspace: !args.switches.has("--no-workspace")
240
+ });
241
+ const lines = [
242
+ ...report.notes.map((note) => `note: ${note}`),
243
+ ...report.violations,
244
+ report.violations.length === 0 ? `architecture check PASS \u2014 ${report.edges.length} tag(s)` : `architecture check FAIL \u2014 ${report.violations.length} violation(s)`
245
+ ];
246
+ return { code: report.violations.length === 0 ? 0 : 1, out: `${lines.join("\n")}
247
+ ` };
248
+ };
249
+ var syncCommand = ({ args, config, root }) => {
250
+ const check = args.flags["--check"];
251
+ const packageName = args.flags["--package"];
252
+ const write = args.flags["--write"];
253
+ const target = required(args, "--target");
254
+ const done = runSync({
255
+ ...check === void 0 ? {} : { check },
256
+ config,
257
+ ...packageName === void 0 ? {} : { packageName },
258
+ root,
259
+ target,
260
+ ...write === void 0 ? {} : { write }
261
+ });
262
+ if (done.written !== void 0) return { code: 0, out: `wrote ${done.written}
263
+ ` };
264
+ if (check !== void 0) {
265
+ return done.ok ? { code: 0, out: `sync check PASS \u2014 ${check} is what ${target} generates
266
+ ` } : { code: 1, out: `sync check FAIL
267
+ ${done.diff ?? ""}
268
+ ` };
269
+ }
270
+ return { code: 0, out: `${done.generated}
271
+ ` };
272
+ };
273
+ var messageFrom = ({ args, root }) => {
274
+ const given = args.flags["--message"];
275
+ if (given !== void 0) return given;
276
+ const named = args.flags["--commit"];
277
+ if (named !== void 0) {
278
+ const found = messageOf(named, root);
279
+ if (found === void 0) throw new Error(`${named} is not a commit`);
280
+ return found;
281
+ }
282
+ const file = args.positional[0];
283
+ if (file === void 0) throw new Error("commit-msg needs a file, --message or --commit");
284
+ return readFileSync(resolve(root, file), "utf8");
285
+ };
286
+ var commitMessageCommand = (context) => {
287
+ const done = checkCommitMessage(messageFrom(context), context.config);
288
+ return done.refusal === void 0 ? { code: 0, out: "commit-msg PASS\n" } : { code: 1, out: `commit-msg REFUSED \u2014 ${done.refusal}
289
+ ` };
290
+ };
291
+ var deliveryCommand = ({ args, root }, verb) => {
292
+ if (verb !== "check") throw new Error(`unknown delivery command "${verb ?? ""}"`);
293
+ const named = args.flags["--commit"];
294
+ const done = checkDelivery({
295
+ ...named === void 0 ? {} : { commit: named },
296
+ root,
297
+ staged: args.switches.has("--cached") || named === void 0
298
+ });
299
+ return done.refusal === void 0 ? { code: 0, out: "delivery check PASS\n" } : { code: 1, out: `delivery REFUSED \u2014 ${done.refusal}
300
+ ` };
301
+ };
302
+ var runLedger = (argv, cwd) => {
303
+ const command = argv[0];
304
+ if (command === void 0 || command === "--help" || command === "help") {
305
+ return { code: command === void 0 ? 2 : 0, out: `${USAGE}
306
+ ` };
307
+ }
308
+ if (command === "plan") return planCommand(contextOf(argv.slice(2), cwd), argv[1]);
309
+ if (command === "proof") return proofCommand(contextOf(argv.slice(2), cwd), argv[1]);
310
+ if (command === "tick") return tickCommand(contextOf(argv.slice(1), cwd));
311
+ if (command === "journal") return journalCommand(contextOf(argv.slice(1), cwd));
312
+ if (command === "decide") return decideCommand(contextOf(argv.slice(1), cwd));
313
+ if (command === "handoff") return handoffCommand(contextOf(argv.slice(1), cwd));
314
+ if (command === "status") return statusCommand(contextOf(argv.slice(1), cwd));
315
+ if (command === "architecture") return architectureCommand(contextOf(argv.slice(2), cwd), argv[1]);
316
+ if (command === "sync") return syncCommand(contextOf(argv.slice(1), cwd));
317
+ if (command === "commit-msg") return commitMessageCommand(contextOf(argv.slice(1), cwd));
318
+ if (command === "delivery") return deliveryCommand(contextOf(argv.slice(2), cwd), argv[1]);
319
+ if (command === "--prove") {
320
+ const report = runProve(contextOf(argv.slice(1), cwd).config);
321
+ return { code: report.ok ? 0 : 2, out: formatProve(report) };
322
+ }
323
+ throw new Error(`unknown command "${command}"`);
324
+ };
325
+ var main = () => {
326
+ try {
327
+ const { code, out } = runLedger(process.argv.slice(2), process.cwd());
328
+ process.stdout.write(out);
329
+ return code;
330
+ } catch (error) {
331
+ process.stderr.write(`geonosis-ledger: ${error.message}
332
+ `);
333
+ return 2;
334
+ }
335
+ };
336
+ process.exitCode = main();
337
+ export {
338
+ runLedger
339
+ };
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@geonosis/ledger",
3
+ "version": "1.0.0",
4
+ "description": "The tick ledger — validate and refuse, never author: no criteria, no proof, no runtime code, no revisit trigger, no delivery.",
5
+ "keywords": [
6
+ "ledger",
7
+ "gate",
8
+ "plan",
9
+ "proof",
10
+ "ci",
11
+ "monorepo",
12
+ "architecture"
13
+ ],
14
+ "homepage": "https://github.com/microcompanies/geonosis/tree/main/packages/ledger",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/microcompanies/geonosis.git",
18
+ "directory": "packages/ledger"
19
+ },
20
+ "license": "Apache-2.0",
21
+ "type": "module",
22
+ "main": "dist/index.js",
23
+ "bin": {
24
+ "geonosis-ledger": "bin/geonosis-ledger.mjs"
25
+ },
26
+ "exports": {
27
+ ".": "./dist/index.js"
28
+ },
29
+ "files": [
30
+ "bin",
31
+ "dist"
32
+ ],
33
+ "engines": {
34
+ "node": ">=22"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "scripts": {
40
+ "build": "tsup",
41
+ "typecheck": "tsc --noEmit"
42
+ }
43
+ }