@geonosis/cli 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.
- package/LICENSE +202 -0
- package/README.md +62 -0
- package/bin/geonosis.mjs +4 -0
- package/dist/chunk-U3TBZWD3.js +773 -0
- package/dist/geonosis-cli.js +62 -0
- package/dist/index.d.ts +229 -0
- package/dist/index.js +64 -0
- package/package.json +61 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {
|
|
2
|
+
COMMANDS,
|
|
3
|
+
commandById,
|
|
4
|
+
inputFor
|
|
5
|
+
} from "./chunk-U3TBZWD3.js";
|
|
6
|
+
|
|
7
|
+
// src/geonosis-cli.ts
|
|
8
|
+
var REFUSED = 2;
|
|
9
|
+
var usage = () => [
|
|
10
|
+
"geonosis [--json] <command> [args\u2026]",
|
|
11
|
+
"",
|
|
12
|
+
"One declarative registry, many doors. Every command below is the same command the MCP server",
|
|
13
|
+
"serves and the plugin calls; the ones that front a bin spawn it and pass its exit code through",
|
|
14
|
+
"unchanged \u2014 0 clean, 1 a finding or a refusal, 2 a run that could not be made at all.",
|
|
15
|
+
"",
|
|
16
|
+
...COMMANDS.map((one) => ` ${one.id.padEnd(12)} ${one.kind.padEnd(6)} ${one.description}`),
|
|
17
|
+
"",
|
|
18
|
+
" --json print { command, ok, exitCode, output } instead of the command\u2019s own bytes",
|
|
19
|
+
"",
|
|
20
|
+
" A flag written BEFORE the command is this door\u2019s; everything after the command is the",
|
|
21
|
+
" command\u2019s own, --help included.",
|
|
22
|
+
""
|
|
23
|
+
].join("\n");
|
|
24
|
+
var DOOR_FLAGS = /* @__PURE__ */ new Set(["--help", "--json", "-h"]);
|
|
25
|
+
var main = async () => {
|
|
26
|
+
const argv = process.argv.slice(2);
|
|
27
|
+
const at = argv.findIndex((one) => !one.startsWith("-"));
|
|
28
|
+
const mine = at === -1 ? argv : argv.slice(0, at);
|
|
29
|
+
const unknown = mine.find((one) => !DOOR_FLAGS.has(one));
|
|
30
|
+
if (unknown !== void 0) {
|
|
31
|
+
process.stderr.write(`geonosis: ${unknown} is not a flag of this command \u2014 ${usage()}`);
|
|
32
|
+
return REFUSED;
|
|
33
|
+
}
|
|
34
|
+
const id = at === -1 ? void 0 : argv[at];
|
|
35
|
+
if (id === void 0) {
|
|
36
|
+
process.stdout.write(usage());
|
|
37
|
+
return mine.some((one) => one === "--help" || one === "-h") ? 0 : REFUSED;
|
|
38
|
+
}
|
|
39
|
+
const json = mine.includes("--json");
|
|
40
|
+
const rest = argv.slice(at);
|
|
41
|
+
let command;
|
|
42
|
+
try {
|
|
43
|
+
command = commandById(id);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
process.stderr.write(`geonosis: ${error.message}
|
|
46
|
+
`);
|
|
47
|
+
return REFUSED;
|
|
48
|
+
}
|
|
49
|
+
const result = await command.run(inputFor(id, rest.slice(1), process.cwd()));
|
|
50
|
+
process.stdout.write(
|
|
51
|
+
json ? `${JSON.stringify({ command: id, exitCode: result.exitCode, ok: result.ok, output: result.output }, void 0, 2)}
|
|
52
|
+
` : result.output
|
|
53
|
+
);
|
|
54
|
+
return result.exitCode;
|
|
55
|
+
};
|
|
56
|
+
try {
|
|
57
|
+
process.exit(await main());
|
|
58
|
+
} catch (error) {
|
|
59
|
+
process.stderr.write(`geonosis: ${error.message}
|
|
60
|
+
`);
|
|
61
|
+
process.exit(REFUSED);
|
|
62
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { ZodType } from 'zod';
|
|
2
|
+
|
|
3
|
+
type Parsed = {
|
|
4
|
+
flags: Record<string, string>;
|
|
5
|
+
positional: string[];
|
|
6
|
+
switches: Set<string>;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* `--flag value`, `--flag=value` and bare `--switch`, told apart by a declared set rather than by
|
|
10
|
+
* guessing from the next token — the ledger learned this the hard way, where `--note --plan 3`
|
|
11
|
+
* would otherwise eat the next flag as a note.
|
|
12
|
+
*/
|
|
13
|
+
declare const parseArgv: (argv: readonly string[], valued: ReadonlySet<string>) => Parsed;
|
|
14
|
+
/**
|
|
15
|
+
* argv into the input the registry's schema accepts. Turning one door's spelling into the
|
|
16
|
+
* registry's shape is exactly what a door is for; the registry never learns what a flag looks like.
|
|
17
|
+
*/
|
|
18
|
+
declare const inputFor: (id: string, argv: readonly string[], cwd: string) => unknown;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* What every command answers, whatever door asked. `exitCode` is the child bin's own, never a
|
|
22
|
+
* translation: the nine bins agree that 0 is clean, 1 is a finding or a refusal and 2 is a run that
|
|
23
|
+
* could not be made at all (D-030), and a wrapper that normalised a 2 into a 1 would have invented
|
|
24
|
+
* a finding out of a tool that never ran.
|
|
25
|
+
*/
|
|
26
|
+
type CommandResult = {
|
|
27
|
+
exitCode: number;
|
|
28
|
+
ok: boolean;
|
|
29
|
+
output: string;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Whether the command can change something outside this process that a reviewer would want to have
|
|
33
|
+
* approved — a file the repo TRACKS, or a send to a third party. It is the fact the MCP door builds
|
|
34
|
+
* its preview→confirm gate out of, and it is recorded here so that every door reads one answer:
|
|
35
|
+
* `verify` is a write because the tiers a repo declares include the ratchet, which rewrites the
|
|
36
|
+
* baseline when a number shrinks.
|
|
37
|
+
*/
|
|
38
|
+
type CommandKind = 'read' | 'write';
|
|
39
|
+
/**
|
|
40
|
+
* One declarative registry, many doors — musa's `tools/registry.ts` behind an npm embed, an HTTP
|
|
41
|
+
* server, an MCP and an editor; Midday's 103 tools behind one `McpServer` that the in-app assistant
|
|
42
|
+
* reaches over `InMemoryTransport` rather than re-registering. A door that carried its own list of
|
|
43
|
+
* commands would be the second list, and the second list is the one that goes stale.
|
|
44
|
+
*/
|
|
45
|
+
type Command = {
|
|
46
|
+
description: string;
|
|
47
|
+
id: string;
|
|
48
|
+
inputSchema: ZodType;
|
|
49
|
+
kind: CommandKind;
|
|
50
|
+
run: (input: unknown) => Promise<CommandResult>;
|
|
51
|
+
};
|
|
52
|
+
declare const resultOf: (exitCode: number, output: string) => CommandResult;
|
|
53
|
+
/** A refusal: the command could not be made at all. Never a finding. */
|
|
54
|
+
declare const refused: (output: string) => CommandResult;
|
|
55
|
+
|
|
56
|
+
type Candidate = {
|
|
57
|
+
count: number;
|
|
58
|
+
rule: string;
|
|
59
|
+
sites: string[];
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* The findings a repo's own config already produces, grouped by rule. Agent OS mines standards out
|
|
63
|
+
* of code; this mines `fixShape` candidates out of the enforcement already running — the rule, how
|
|
64
|
+
* loud it is, and the first three places to read. Loudest first, because the shape worth writing
|
|
65
|
+
* down is the one the tree keeps producing.
|
|
66
|
+
*/
|
|
67
|
+
declare const candidatesOf: (findings: readonly string[]) => Candidate[];
|
|
68
|
+
declare const formatCandidates: (candidates: readonly Candidate[]) => string;
|
|
69
|
+
type DiscoverInput = {
|
|
70
|
+
config?: string;
|
|
71
|
+
cwd: string;
|
|
72
|
+
paths?: string[];
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Read-only by construction: it runs the linter and prints. The scored agent never writes the
|
|
76
|
+
* scoreboard, and a tool that edited the standards it had just mined would be exactly that.
|
|
77
|
+
*/
|
|
78
|
+
declare const runDiscover: ({ config, cwd, paths, }: DiscoverInput) => Promise<CommandResult>;
|
|
79
|
+
|
|
80
|
+
type Runtime = 'bun' | 'pnpm';
|
|
81
|
+
type InitInput = {
|
|
82
|
+
brand?: string;
|
|
83
|
+
cwd: string;
|
|
84
|
+
presets: string[];
|
|
85
|
+
runtime?: Runtime;
|
|
86
|
+
};
|
|
87
|
+
declare const runtimeOf: (cwd: string) => Runtime | undefined;
|
|
88
|
+
declare const countersFor: (runtime: Runtime) => Record<string, unknown>[];
|
|
89
|
+
type Rules = {
|
|
90
|
+
rules: Record<string, unknown>;
|
|
91
|
+
unanswered: [string, string][];
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* The rules of the named presets, with the options the caller has answers for.
|
|
95
|
+
*
|
|
96
|
+
* A rule whose option nobody answered is left OUT and reported, never written with a placeholder:
|
|
97
|
+
* an enabled rule without its options refuses the whole oxlint run at load (D-031), so a config
|
|
98
|
+
* that guessed would be one oxlint will not read. Which rules need an answer is measured, not
|
|
99
|
+
* listed — `optionsNeededBy` asks the rules themselves.
|
|
100
|
+
*/
|
|
101
|
+
declare const rulesFor: (presets: string[], answers?: Record<string, unknown[]>) => Rules;
|
|
102
|
+
/**
|
|
103
|
+
* The four files an existing repo needs before any gate can run, written once and never over the
|
|
104
|
+
* top of anything. A tool that overwrote a config would be a tool nobody dares run twice, and the
|
|
105
|
+
* one thing worse than a repo without a baseline is a repo whose baseline was silently replaced.
|
|
106
|
+
*/
|
|
107
|
+
declare const runInit: ({ brand, cwd, presets, runtime, }: InitInput) => Promise<CommandResult>;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Which rules refuse to run without options, MEASURED by asking them.
|
|
111
|
+
*
|
|
112
|
+
* D-031 makes an enabled rule without its options a config error that stops the whole run, so a
|
|
113
|
+
* skeleton that wrote one would hand a repo an `.oxlintrc.json` oxlint will not read. The list of
|
|
114
|
+
* which rules those are is a fact about the rules, and a second copy of it in this package would be
|
|
115
|
+
* the copy that goes stale the next time one is added — so it is not copied. Each rule is
|
|
116
|
+
* constructed with no options, and the ones that refuse say so, naming what they wanted.
|
|
117
|
+
*/
|
|
118
|
+
declare const optionsNeededBy: () => Record<string, string>;
|
|
119
|
+
|
|
120
|
+
type Wrapped = {
|
|
121
|
+
bin: string;
|
|
122
|
+
description: string;
|
|
123
|
+
id: string;
|
|
124
|
+
kind: CommandKind;
|
|
125
|
+
lead?: string[];
|
|
126
|
+
package: string;
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* Every bin the kit ships, wrapped and never re-implemented. A second implementation of a gate is a
|
|
130
|
+
* second answer to the question the gate exists to settle, and the two disagree the first time one
|
|
131
|
+
* of them is fixed.
|
|
132
|
+
*
|
|
133
|
+
* `kind` is whether the command can change something outside this process that a reviewer would
|
|
134
|
+
* want to have approved: a file the repo TRACKS, or a send to a third party. `verify` is a write
|
|
135
|
+
* because the tiers a repo declares include the ratchet, which rewrites the baseline down when a
|
|
136
|
+
* number shrank; `observability` is one because a real sink is somebody else's project.
|
|
137
|
+
*/
|
|
138
|
+
declare const WRAPPED: Wrapped[];
|
|
139
|
+
/** The one list every door reads. */
|
|
140
|
+
declare const COMMANDS: Command[];
|
|
141
|
+
declare const commandById: (id: string) => Command;
|
|
142
|
+
|
|
143
|
+
declare const LOCK_FILE = "skills-lock.json";
|
|
144
|
+
declare const SKILL_FILE = "SKILL.md";
|
|
145
|
+
type LockEntry = {
|
|
146
|
+
computedHash: string;
|
|
147
|
+
skillPath: string;
|
|
148
|
+
source: string;
|
|
149
|
+
sourceType: string;
|
|
150
|
+
};
|
|
151
|
+
type Lock = {
|
|
152
|
+
skills: Record<string, LockEntry>;
|
|
153
|
+
version: number;
|
|
154
|
+
};
|
|
155
|
+
type FoundSkill = {
|
|
156
|
+
dir: string;
|
|
157
|
+
hash: string;
|
|
158
|
+
name: string;
|
|
159
|
+
skillPath: string;
|
|
160
|
+
};
|
|
161
|
+
declare const hashOf: (bytes: Buffer) => string;
|
|
162
|
+
declare const rootsIn: (cwd: string) => string[];
|
|
163
|
+
/**
|
|
164
|
+
* Every skill directory under every root, by the hash of the one file a lock pins. Sorted, so two
|
|
165
|
+
* runs over one tree write the same bytes and a lock diff is a change and never an ordering.
|
|
166
|
+
*/
|
|
167
|
+
declare const skillsIn: (cwd: string) => FoundSkill[];
|
|
168
|
+
/**
|
|
169
|
+
* Files a markdown lock cannot see. Vercel disclosed the class in one sentence — "a skill that
|
|
170
|
+
* looked completely clean at the markdown level but included a Python file that opened a remote
|
|
171
|
+
* shell on install" — and a hash over `SKILL.md` is blind to every byte beside it.
|
|
172
|
+
*/
|
|
173
|
+
declare const executablesIn: (dir: string, cwd: string) => string[];
|
|
174
|
+
declare const lockFor: (cwd: string) => Lock;
|
|
175
|
+
type SkillsInput = {
|
|
176
|
+
cwd: string;
|
|
177
|
+
verb: string;
|
|
178
|
+
};
|
|
179
|
+
declare const runSkills: ({ cwd, verb }: SkillsInput) => Promise<CommandResult>;
|
|
180
|
+
|
|
181
|
+
type BinRef = {
|
|
182
|
+
bin: string;
|
|
183
|
+
package: string;
|
|
184
|
+
};
|
|
185
|
+
type Found = {
|
|
186
|
+
note: string;
|
|
187
|
+
path: string;
|
|
188
|
+
};
|
|
189
|
+
/**
|
|
190
|
+
* The repo's copy first, this CLI's own second — and it SAYS when it fell back.
|
|
191
|
+
*
|
|
192
|
+
* Running the CLI's own bundled ratchet over a repo that pins a different one is the declared ≠
|
|
193
|
+
* loaded failure `geonosis-doctor` exists to catch, one level up. It is still better than refusing
|
|
194
|
+
* to run at all, so it happens and it is printed: a fallback nobody can see is the version skew
|
|
195
|
+
* nobody can see.
|
|
196
|
+
*/
|
|
197
|
+
declare const findBin: (cwd: string, ref: BinRef) => Found;
|
|
198
|
+
/**
|
|
199
|
+
* The child's whole answer, and the child's own status. stdout and stderr are joined because a
|
|
200
|
+
* caller reading one of them is a caller who will miss the refusal that was written to the other.
|
|
201
|
+
*/
|
|
202
|
+
declare const runBin: (cwd: string, ref: BinRef, args: readonly string[]) => Promise<CommandResult>;
|
|
203
|
+
|
|
204
|
+
declare const LAW_FILE = "CLAUDE.md";
|
|
205
|
+
declare const AGENTS_FILE = "AGENTS.md";
|
|
206
|
+
declare const CURSOR_RULES = ".cursor/rules";
|
|
207
|
+
type Generated = Record<string, string>;
|
|
208
|
+
/**
|
|
209
|
+
* One law, every tool (D-002).
|
|
210
|
+
*
|
|
211
|
+
* The law is written once, for Claude Code, and every other tool's file is DERIVED from it —
|
|
212
|
+
* agents.md for Codex and the rest, `.cursor/rules/*.mdc` for Cursor. A hand-maintained AGENTS.md
|
|
213
|
+
* beside a CLAUDE.md is two laws, and the second one is the stale one by the end of the week.
|
|
214
|
+
*/
|
|
215
|
+
declare const generate: (cwd: string) => Generated;
|
|
216
|
+
type SyncInput = {
|
|
217
|
+
check?: boolean;
|
|
218
|
+
cwd: string;
|
|
219
|
+
into?: string;
|
|
220
|
+
target?: string;
|
|
221
|
+
write?: boolean;
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* `--target` is the edges table, and the edges table is the ledger's. Handing it over — exit code
|
|
225
|
+
* and all — is what keeps there from being a second reader of one table.
|
|
226
|
+
*/
|
|
227
|
+
declare const runSync: (input: SyncInput) => Promise<CommandResult>;
|
|
228
|
+
|
|
229
|
+
export { AGENTS_FILE, type BinRef, COMMANDS, CURSOR_RULES, type Candidate, type Command, type CommandKind, type CommandResult, type DiscoverInput, type FoundSkill, type Generated, type InitInput, LAW_FILE, LOCK_FILE, type Lock, type LockEntry, type Parsed, type Runtime, SKILL_FILE, type SkillsInput, type SyncInput, WRAPPED, type Wrapped, candidatesOf, commandById, countersFor, executablesIn, findBin, formatCandidates, generate, hashOf, inputFor, lockFor, optionsNeededBy, parseArgv, refused, resultOf, rootsIn, rulesFor, runBin, runDiscover, runInit, runSkills, runSync, runtimeOf, skillsIn };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AGENTS_FILE,
|
|
3
|
+
COMMANDS,
|
|
4
|
+
CURSOR_RULES,
|
|
5
|
+
LAW_FILE,
|
|
6
|
+
LOCK_FILE,
|
|
7
|
+
SKILL_FILE,
|
|
8
|
+
WRAPPED,
|
|
9
|
+
candidatesOf,
|
|
10
|
+
commandById,
|
|
11
|
+
countersFor,
|
|
12
|
+
executablesIn,
|
|
13
|
+
findBin,
|
|
14
|
+
formatCandidates,
|
|
15
|
+
generate,
|
|
16
|
+
hashOf,
|
|
17
|
+
inputFor,
|
|
18
|
+
lockFor,
|
|
19
|
+
optionsNeededBy,
|
|
20
|
+
parseArgv,
|
|
21
|
+
refused,
|
|
22
|
+
resultOf,
|
|
23
|
+
rootsIn,
|
|
24
|
+
rulesFor,
|
|
25
|
+
runBin,
|
|
26
|
+
runDiscover,
|
|
27
|
+
runInit,
|
|
28
|
+
runSkills,
|
|
29
|
+
runSync,
|
|
30
|
+
runtimeOf,
|
|
31
|
+
skillsIn
|
|
32
|
+
} from "./chunk-U3TBZWD3.js";
|
|
33
|
+
export {
|
|
34
|
+
AGENTS_FILE,
|
|
35
|
+
COMMANDS,
|
|
36
|
+
CURSOR_RULES,
|
|
37
|
+
LAW_FILE,
|
|
38
|
+
LOCK_FILE,
|
|
39
|
+
SKILL_FILE,
|
|
40
|
+
WRAPPED,
|
|
41
|
+
candidatesOf,
|
|
42
|
+
commandById,
|
|
43
|
+
countersFor,
|
|
44
|
+
executablesIn,
|
|
45
|
+
findBin,
|
|
46
|
+
formatCandidates,
|
|
47
|
+
generate,
|
|
48
|
+
hashOf,
|
|
49
|
+
inputFor,
|
|
50
|
+
lockFor,
|
|
51
|
+
optionsNeededBy,
|
|
52
|
+
parseArgv,
|
|
53
|
+
refused,
|
|
54
|
+
resultOf,
|
|
55
|
+
rootsIn,
|
|
56
|
+
rulesFor,
|
|
57
|
+
runBin,
|
|
58
|
+
runDiscover,
|
|
59
|
+
runInit,
|
|
60
|
+
runSkills,
|
|
61
|
+
runSync,
|
|
62
|
+
runtimeOf,
|
|
63
|
+
skillsIn
|
|
64
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@geonosis/cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "One declarative command registry over every geonosis bin — the CLI door onto it, plus init, sync, discover and skills.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cli",
|
|
7
|
+
"registry",
|
|
8
|
+
"monorepo",
|
|
9
|
+
"gates",
|
|
10
|
+
"ratchet",
|
|
11
|
+
"scaffold",
|
|
12
|
+
"skills"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/microcompanies/geonosis/tree/main/packages/cli",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/microcompanies/geonosis.git",
|
|
18
|
+
"directory": "packages/cli"
|
|
19
|
+
},
|
|
20
|
+
"license": "Apache-2.0",
|
|
21
|
+
"type": "module",
|
|
22
|
+
"main": "dist/index.js",
|
|
23
|
+
"bin": {
|
|
24
|
+
"geonosis": "bin/geonosis.mjs"
|
|
25
|
+
},
|
|
26
|
+
"exports": {
|
|
27
|
+
".": "./dist/index.js"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"bin",
|
|
31
|
+
"dist"
|
|
32
|
+
],
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"zod": "^4.0.0",
|
|
35
|
+
"@geonosis/doctor": "1.0.0",
|
|
36
|
+
"@geonosis/lint-parity": "1.0.0",
|
|
37
|
+
"@geonosis/observability": "1.0.0",
|
|
38
|
+
"@geonosis/ledger": "1.0.0",
|
|
39
|
+
"@geonosis/oxlint-plugin-biological-architecture": "1.0.0",
|
|
40
|
+
"@geonosis/testbed": "1.0.0",
|
|
41
|
+
"@geonosis/verify": "1.0.0",
|
|
42
|
+
"@geonosis/verify-arch": "1.0.0",
|
|
43
|
+
"@geonosis/ratchet": "1.0.0",
|
|
44
|
+
"@geonosis/review": "1.0.0",
|
|
45
|
+
"@geonosis/visual-diff": "1.0.0",
|
|
46
|
+
"@geonosis/walk": "1.0.0"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"oxlint": ">=1.77"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=22"
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "tsup",
|
|
59
|
+
"typecheck": "tsc --noEmit"
|
|
60
|
+
}
|
|
61
|
+
}
|