@michaelfromyeg/loom-cli 0.1.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 +21 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +650 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Michael DeMarco
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createPrivateKey, createPublicKey } from 'crypto';
|
|
3
|
+
import { mkdirSync, writeFileSync, existsSync, readFileSync } from 'fs';
|
|
4
|
+
import { join, resolve as resolve$1, basename, relative } from 'path';
|
|
5
|
+
import { lint, hasMarketplaceManifest, buildMarketplace, build, install, readLock, update, generateSigningKeys, signLock, verifyArtifacts, importNativePlugin, uninstall, LOOM_VERSION, CompileError, AdapterRegistry } from '@michaelfromyeg/loom-core';
|
|
6
|
+
import { discoverEvals, runEval, drivers } from '@michaelfromyeg/loom-eval';
|
|
7
|
+
import { publishCheck, indexFromPluginDirs, fetchMcpRegistry, federate, serializeIndex } from '@michaelfromyeg/loom-index';
|
|
8
|
+
import { defineCommand, runMain, renderUsage } from 'citty';
|
|
9
|
+
import claudeAdapter from '@michaelfromyeg/loom-adapter-claude';
|
|
10
|
+
import codexAdapter from '@michaelfromyeg/loom-adapter-codex';
|
|
11
|
+
import copilotAdapter from '@michaelfromyeg/loom-adapter-copilot';
|
|
12
|
+
import cursorAdapter from '@michaelfromyeg/loom-adapter-cursor';
|
|
13
|
+
import opencodeAdapter from '@michaelfromyeg/loom-adapter-opencode';
|
|
14
|
+
|
|
15
|
+
async function resolve(r) {
|
|
16
|
+
return typeof r === "function" ? await r() : r;
|
|
17
|
+
}
|
|
18
|
+
async function renderCliReference(main2) {
|
|
19
|
+
const out = [
|
|
20
|
+
"# Loom CLI reference",
|
|
21
|
+
"",
|
|
22
|
+
"_Generated from the CLI definition by `loom docs` -- do not edit by hand._",
|
|
23
|
+
"",
|
|
24
|
+
"## `loom`",
|
|
25
|
+
"",
|
|
26
|
+
"```",
|
|
27
|
+
(await renderUsage(main2)).trim(),
|
|
28
|
+
"```",
|
|
29
|
+
""
|
|
30
|
+
];
|
|
31
|
+
const subs = main2.subCommands ? await resolve(main2.subCommands) : {};
|
|
32
|
+
for (const name of Object.keys(subs).sort()) {
|
|
33
|
+
const sub = await resolve(subs[name]);
|
|
34
|
+
const meta = await resolve(sub.meta);
|
|
35
|
+
out.push(`## \`loom ${name}\``, "");
|
|
36
|
+
if (meta?.description) out.push(meta.description, "");
|
|
37
|
+
out.push("```", (await renderUsage(sub, main2)).trim(), "```", "");
|
|
38
|
+
}
|
|
39
|
+
return `${out.join("\n")}
|
|
40
|
+
`;
|
|
41
|
+
}
|
|
42
|
+
function buildRegistry() {
|
|
43
|
+
return new AdapterRegistry().register(claudeAdapter).register(codexAdapter).register(cursorAdapter).register(copilotAdapter).register(opencodeAdapter);
|
|
44
|
+
}
|
|
45
|
+
function allDrivers() {
|
|
46
|
+
return drivers;
|
|
47
|
+
}
|
|
48
|
+
function parseList(value) {
|
|
49
|
+
if (!value) return void 0;
|
|
50
|
+
const items = value.split(",").map((t) => t.trim()).filter(Boolean);
|
|
51
|
+
return items.length > 0 ? items : void 0;
|
|
52
|
+
}
|
|
53
|
+
function parseTargets(value) {
|
|
54
|
+
return parseList(value);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/report.ts
|
|
58
|
+
function formatDiagnostic(d) {
|
|
59
|
+
const tag = d.severity === "error" ? "error" : d.severity === "warning" ? "warn" : "info";
|
|
60
|
+
const where = d.where ? `${d.where}: ` : "";
|
|
61
|
+
return ` ${tag.padEnd(5)} ${where}${d.message}`;
|
|
62
|
+
}
|
|
63
|
+
function printDiagnostics(items) {
|
|
64
|
+
for (const d of items) {
|
|
65
|
+
const line = formatDiagnostic(d);
|
|
66
|
+
if (d.severity === "error") console.error(line);
|
|
67
|
+
else console.warn(line);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function printTrustSummary(result) {
|
|
71
|
+
const { plugin } = result.fb;
|
|
72
|
+
const components = result.components;
|
|
73
|
+
console.log(`
|
|
74
|
+
Trust summary for ${result.id}@${plugin.version}`);
|
|
75
|
+
console.log(` publisher: ${plugin.owner.name} <${plugin.owner.namespace}> (unverified)`);
|
|
76
|
+
const counts = /* @__PURE__ */ new Map();
|
|
77
|
+
for (const c of components) counts.set(c.kind, (counts.get(c.kind) ?? 0) + 1);
|
|
78
|
+
const summary = [...counts.entries()].map(([k, n]) => `${n} ${k}`).join(", ");
|
|
79
|
+
console.log(` components: ${summary || "none"}`);
|
|
80
|
+
const executables = result.targets.flatMap(
|
|
81
|
+
(t) => t.artifacts.filter((p) => p.artifact.executable).map((p) => `${t.target}:${p.artifact.relPath}`)
|
|
82
|
+
);
|
|
83
|
+
console.log(
|
|
84
|
+
executables.length > 0 ? ` executables (placed DISABLED): ${executables.join(", ")}` : " executables: none"
|
|
85
|
+
);
|
|
86
|
+
console.log(` mcp servers that will run: ${counts.get("mcp") ?? 0}`);
|
|
87
|
+
console.log(" badges: valid (computed) | signed/verified/scanned: not yet\n");
|
|
88
|
+
}
|
|
89
|
+
function printEvalReport(report) {
|
|
90
|
+
console.log(`
|
|
91
|
+
Eval: ${report.component}`);
|
|
92
|
+
for (const h of report.harnesses) {
|
|
93
|
+
if (h.status === "untested") {
|
|
94
|
+
console.log(` ${h.harness}: UNTESTED (${h.reason})`);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
console.log(` ${h.harness}: ${h.pass ? "PASS" : "FAIL"}`);
|
|
98
|
+
for (const c of h.cases) {
|
|
99
|
+
console.log(` - ${c.name}: ${c.pass ? "pass" : "fail"}`);
|
|
100
|
+
for (const a of c.assertions) {
|
|
101
|
+
console.log(` ${a.kind}: ${a.status} (${a.detail})`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function kebab(s) {
|
|
107
|
+
return s.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") || "my-plugin";
|
|
108
|
+
}
|
|
109
|
+
function scaffoldPlugin(opts) {
|
|
110
|
+
const dir = resolve$1(opts.dir);
|
|
111
|
+
mkdirSync(dir, { recursive: true });
|
|
112
|
+
const manifestPath = join(dir, "loom.yaml");
|
|
113
|
+
if (existsSync(manifestPath)) {
|
|
114
|
+
throw new Error(`a plugin already exists at ${manifestPath}`);
|
|
115
|
+
}
|
|
116
|
+
const name = kebab(opts.name ?? basename(dir));
|
|
117
|
+
const namespace = opts.namespace ?? "com.example";
|
|
118
|
+
const files = [];
|
|
119
|
+
const loomYaml = `name: ${name}
|
|
120
|
+
version: 0.1.0
|
|
121
|
+
owner:
|
|
122
|
+
name: Your Name
|
|
123
|
+
namespace: ${namespace}
|
|
124
|
+
description: A Loom plugin.
|
|
125
|
+
components:
|
|
126
|
+
- skill: skills/hello
|
|
127
|
+
`;
|
|
128
|
+
writeFileSync(manifestPath, loomYaml);
|
|
129
|
+
files.push(relative(dir, manifestPath));
|
|
130
|
+
const skillDir = join(dir, "skills", "hello");
|
|
131
|
+
mkdirSync(skillDir, { recursive: true });
|
|
132
|
+
const skillMd = `---
|
|
133
|
+
name: hello
|
|
134
|
+
description: Greet the user and explain what this skill does.
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
When invoked, greet the user warmly and summarize the task at hand.
|
|
138
|
+
`;
|
|
139
|
+
const skillPath = join(skillDir, "SKILL.md");
|
|
140
|
+
writeFileSync(skillPath, skillMd);
|
|
141
|
+
files.push(relative(dir, skillPath));
|
|
142
|
+
return { dir, name, files };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/index.ts
|
|
146
|
+
async function detectPresent(requested) {
|
|
147
|
+
const drivers2 = allDrivers();
|
|
148
|
+
const present = [];
|
|
149
|
+
const missing = [];
|
|
150
|
+
for (const t of requested) {
|
|
151
|
+
if (await drivers2[t]?.available()) present.push(t);
|
|
152
|
+
else missing.push(t);
|
|
153
|
+
}
|
|
154
|
+
return { present, missing };
|
|
155
|
+
}
|
|
156
|
+
function countByTarget(written) {
|
|
157
|
+
const counts = /* @__PURE__ */ new Map();
|
|
158
|
+
for (const w of written) counts.set(w.target, (counts.get(w.target) ?? 0) + 1);
|
|
159
|
+
return counts;
|
|
160
|
+
}
|
|
161
|
+
function fail(err) {
|
|
162
|
+
if (err instanceof CompileError) {
|
|
163
|
+
console.error(`
|
|
164
|
+
${err.message}:`);
|
|
165
|
+
printDiagnostics(err.diagnostics);
|
|
166
|
+
} else {
|
|
167
|
+
console.error(`
|
|
168
|
+
${err.message}`);
|
|
169
|
+
}
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
var initCmd = defineCommand({
|
|
173
|
+
meta: { name: "init", description: "Scaffold a new plugin (loom.yaml + a sample skill)" },
|
|
174
|
+
args: {
|
|
175
|
+
dir: { type: "positional", required: false, default: ".", description: "Target directory" },
|
|
176
|
+
name: { type: "string", description: "Plugin name (kebab-case)" },
|
|
177
|
+
namespace: { type: "string", description: "Reverse-DNS namespace, e.g. com.acme" }
|
|
178
|
+
},
|
|
179
|
+
run({ args }) {
|
|
180
|
+
const created = scaffoldPlugin({
|
|
181
|
+
dir: args.dir,
|
|
182
|
+
name: args.name,
|
|
183
|
+
namespace: args.namespace
|
|
184
|
+
});
|
|
185
|
+
console.log(`Scaffolded plugin "${created.name}" in ${created.dir}`);
|
|
186
|
+
for (const f of created.files) console.log(` + ${f}`);
|
|
187
|
+
console.log(`
|
|
188
|
+
Next: loom validate ${args.dir} && loom build ${args.dir}`);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
var validateCmd = defineCommand({
|
|
192
|
+
meta: { name: "validate", description: "Statically validate a plugin (the valid badge)" },
|
|
193
|
+
args: {
|
|
194
|
+
dir: { type: "positional", required: false, default: ".", description: "Plugin directory" }
|
|
195
|
+
},
|
|
196
|
+
run({ args }) {
|
|
197
|
+
try {
|
|
198
|
+
const result = lint(args.dir);
|
|
199
|
+
const { items, hasErrors } = result.diagnostics;
|
|
200
|
+
if (items.length > 0) printDiagnostics(items);
|
|
201
|
+
if (hasErrors) {
|
|
202
|
+
console.error(`
|
|
203
|
+
${result.id}: invalid`);
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
console.log(`${result.id}: valid (${result.plugin.components.length} components)`);
|
|
207
|
+
} catch (err) {
|
|
208
|
+
fail(err);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
var buildCmd = defineCommand({
|
|
213
|
+
meta: {
|
|
214
|
+
name: "build",
|
|
215
|
+
description: "Compile a plugin (or a marketplace of plugins) to harness manifests"
|
|
216
|
+
},
|
|
217
|
+
args: {
|
|
218
|
+
dir: {
|
|
219
|
+
type: "positional",
|
|
220
|
+
required: false,
|
|
221
|
+
default: ".",
|
|
222
|
+
description: "Plugin or marketplace directory"
|
|
223
|
+
},
|
|
224
|
+
out: { type: "string", default: ".loom-out", description: "Output directory" },
|
|
225
|
+
target: { type: "string", description: "Comma-separated targets (default: all registered)" }
|
|
226
|
+
},
|
|
227
|
+
async run({ args }) {
|
|
228
|
+
try {
|
|
229
|
+
const registry = buildRegistry();
|
|
230
|
+
const targets = parseTargets(args.target);
|
|
231
|
+
if (hasMarketplaceManifest(args.dir)) {
|
|
232
|
+
const { marketplace, plugins, written: written2 } = await buildMarketplace({
|
|
233
|
+
marketplaceDir: args.dir,
|
|
234
|
+
outDir: args.out,
|
|
235
|
+
registry,
|
|
236
|
+
targets
|
|
237
|
+
});
|
|
238
|
+
console.log(
|
|
239
|
+
`Built marketplace ${marketplace.name} (${plugins.length} plugins) -> ${args.out}/`
|
|
240
|
+
);
|
|
241
|
+
for (const [t, n] of countByTarget(written2)) console.log(` ${t}: ${n} files`);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const { result, written } = await build({
|
|
245
|
+
pluginDir: args.dir,
|
|
246
|
+
outDir: args.out,
|
|
247
|
+
registry,
|
|
248
|
+
targets
|
|
249
|
+
});
|
|
250
|
+
if (result.diagnostics.items.length > 0) printDiagnostics(result.diagnostics.items);
|
|
251
|
+
console.log(`Built ${result.id} -> ${args.out}/`);
|
|
252
|
+
for (const [t, n] of countByTarget(written)) {
|
|
253
|
+
console.log(` ${t}: ${n} files (catalog at ${args.out}/${t})`);
|
|
254
|
+
}
|
|
255
|
+
} catch (err) {
|
|
256
|
+
fail(err);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
var installCmd = defineCommand({
|
|
261
|
+
meta: {
|
|
262
|
+
name: "install",
|
|
263
|
+
description: "Compile + place a plugin into a harness scope, write loom.lock"
|
|
264
|
+
},
|
|
265
|
+
args: {
|
|
266
|
+
dir: { type: "positional", required: false, default: ".", description: "Plugin directory" },
|
|
267
|
+
scope: { type: "string", default: "project", description: "user | project" },
|
|
268
|
+
target: { type: "string", description: "Comma-separated targets (default: all registered)" },
|
|
269
|
+
only: {
|
|
270
|
+
type: "string",
|
|
271
|
+
description: "Comma-separated component names to install piecemeal (e.g. one skill)"
|
|
272
|
+
},
|
|
273
|
+
all: {
|
|
274
|
+
type: "boolean",
|
|
275
|
+
description: "Install to requested targets even if the harness is not detected"
|
|
276
|
+
},
|
|
277
|
+
managed: {
|
|
278
|
+
type: "string",
|
|
279
|
+
description: "Managed mode: only allow these namespaces (comma-separated allowlist)"
|
|
280
|
+
},
|
|
281
|
+
cwd: { type: "string", description: "Project root for project-scope placement (default: cwd)" }
|
|
282
|
+
},
|
|
283
|
+
async run({ args }) {
|
|
284
|
+
try {
|
|
285
|
+
const registry = buildRegistry();
|
|
286
|
+
const scope = args.scope === "user" ? "user" : "project";
|
|
287
|
+
const requested = parseTargets(args.target) ?? registry.targets;
|
|
288
|
+
const allow = parseList(args.managed);
|
|
289
|
+
let targets = requested;
|
|
290
|
+
if (!args.all) {
|
|
291
|
+
const { present, missing } = await detectPresent(requested);
|
|
292
|
+
for (const t of missing) console.log(` skipped ${t}: harness not detected`);
|
|
293
|
+
if (present.length === 0) {
|
|
294
|
+
console.error("No requested harness is installed. Use --all to place anyway.");
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
targets = present;
|
|
298
|
+
}
|
|
299
|
+
const result = await install({
|
|
300
|
+
pluginDir: args.dir,
|
|
301
|
+
scope,
|
|
302
|
+
cwd: args.cwd ?? process.cwd(),
|
|
303
|
+
registry,
|
|
304
|
+
targets,
|
|
305
|
+
only: parseList(args.only),
|
|
306
|
+
...allow ? { managed: { allowNamespaces: allow } } : {}
|
|
307
|
+
});
|
|
308
|
+
printTrustSummary(result.result);
|
|
309
|
+
console.log(
|
|
310
|
+
`Installed ${result.lockfile.plugin.id}@${result.lockfile.plugin.version} (${scope})`
|
|
311
|
+
);
|
|
312
|
+
console.log(` ${result.lockfile.artifacts.length} artifacts placed`);
|
|
313
|
+
if (result.secrets.resolved.length > 0) {
|
|
314
|
+
const missing = result.secrets.resolved.filter((r) => r.source === "missing");
|
|
315
|
+
console.log(
|
|
316
|
+
` config: ${result.secrets.resolved.length} declared` + (result.secrets.path ? ` -> ${result.secrets.path} (gitignored)` : "") + (missing.length > 0 ? `; missing: ${missing.map((m) => m.env).join(", ")}` : "")
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
console.log(` lockfile: ${result.lockPath}`);
|
|
320
|
+
} catch (err) {
|
|
321
|
+
fail(err);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
var updateCmd = defineCommand({
|
|
326
|
+
meta: {
|
|
327
|
+
name: "update",
|
|
328
|
+
description: "Re-resolve refs, recompile, and re-place only artifacts whose hash changed"
|
|
329
|
+
},
|
|
330
|
+
args: {
|
|
331
|
+
dir: { type: "positional", required: false, default: ".", description: "Plugin directory" },
|
|
332
|
+
scope: { type: "string", default: "project", description: "user | project" },
|
|
333
|
+
target: { type: "string", description: "Comma-separated targets (default: all registered)" },
|
|
334
|
+
cwd: { type: "string", description: "Project root for project-scope placement (default: cwd)" }
|
|
335
|
+
},
|
|
336
|
+
async run({ args }) {
|
|
337
|
+
try {
|
|
338
|
+
const scope = args.scope === "user" ? "user" : "project";
|
|
339
|
+
const lock = readLock(args.dir);
|
|
340
|
+
const lockedTargets = lock ? [...new Set(lock.artifacts.map((a) => a.target))] : void 0;
|
|
341
|
+
const result = await update({
|
|
342
|
+
pluginDir: args.dir,
|
|
343
|
+
scope,
|
|
344
|
+
cwd: args.cwd ?? process.cwd(),
|
|
345
|
+
registry: buildRegistry(),
|
|
346
|
+
targets: parseTargets(args.target) ?? lockedTargets
|
|
347
|
+
});
|
|
348
|
+
console.log(
|
|
349
|
+
`Updated ${result.lockfile.plugin.id}: ${result.changed.length} artifact(s) changed`
|
|
350
|
+
);
|
|
351
|
+
for (const p of result.changed) console.log(` ~ ${p}`);
|
|
352
|
+
} catch (err) {
|
|
353
|
+
fail(err);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
var evalCmd = defineCommand({
|
|
358
|
+
meta: {
|
|
359
|
+
name: "eval",
|
|
360
|
+
description: "Run a component's evals against the real harnesses (reports UNTESTED honestly)"
|
|
361
|
+
},
|
|
362
|
+
args: {
|
|
363
|
+
dir: { type: "positional", required: false, default: ".", description: "Plugin directory" },
|
|
364
|
+
component: { type: "string", description: "Only eval this component leaf name" },
|
|
365
|
+
harness: { type: "string", description: "Restrict to these harnesses (comma-separated)" }
|
|
366
|
+
},
|
|
367
|
+
async run({ args }) {
|
|
368
|
+
try {
|
|
369
|
+
const registry = buildRegistry();
|
|
370
|
+
const drivers2 = allDrivers();
|
|
371
|
+
const discovered = discoverEvals(args.dir).filter(
|
|
372
|
+
(d) => !args.component || d.componentLeaf === args.component
|
|
373
|
+
);
|
|
374
|
+
if (discovered.length === 0) {
|
|
375
|
+
console.log("No evals found (a component adds them with an `evals:` file).");
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const onlyHarness = parseTargets(args.harness);
|
|
379
|
+
let anyFail = false;
|
|
380
|
+
for (const d of discovered) {
|
|
381
|
+
const evalFile = onlyHarness ? {
|
|
382
|
+
...d.evalFile,
|
|
383
|
+
harnesses: d.evalFile.harnesses.filter((h) => onlyHarness.includes(h))
|
|
384
|
+
} : d.evalFile;
|
|
385
|
+
const report = await runEval({
|
|
386
|
+
evalFile,
|
|
387
|
+
pluginDir: args.dir,
|
|
388
|
+
componentLeaf: d.componentLeaf,
|
|
389
|
+
registry,
|
|
390
|
+
drivers: drivers2
|
|
391
|
+
});
|
|
392
|
+
printEvalReport(report);
|
|
393
|
+
if (report.harnesses.some((h) => h.status === "tested" && !h.pass)) anyFail = true;
|
|
394
|
+
}
|
|
395
|
+
if (anyFail) process.exit(1);
|
|
396
|
+
} catch (err) {
|
|
397
|
+
fail(err);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
var publishCmd = defineCommand({
|
|
402
|
+
meta: {
|
|
403
|
+
name: "publish",
|
|
404
|
+
description: "Run the deterministic publish gate (static valid + trace/output evals)"
|
|
405
|
+
},
|
|
406
|
+
args: {
|
|
407
|
+
dir: { type: "positional", required: false, default: ".", description: "Plugin directory" },
|
|
408
|
+
snapshot: {
|
|
409
|
+
type: "boolean",
|
|
410
|
+
description: "Snapshot eval scores into evals/.baselines/ for the next release"
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
async run({ args }) {
|
|
414
|
+
try {
|
|
415
|
+
const res = await publishCheck(args.dir, {
|
|
416
|
+
registry: buildRegistry(),
|
|
417
|
+
drivers: allDrivers(),
|
|
418
|
+
snapshot: Boolean(args.snapshot)
|
|
419
|
+
});
|
|
420
|
+
console.log(`Publish check: ${res.id}@${res.version}`);
|
|
421
|
+
if (res.diagnostics.length > 0) printDiagnostics(res.diagnostics);
|
|
422
|
+
console.log(` valid: ${res.validPassed ? "yes" : "NO"}`);
|
|
423
|
+
console.log(` scan: ${res.scan.clean ? "clean" : `${res.scan.findings.length} finding(s)`}`);
|
|
424
|
+
console.log(` badges: ${res.badges.join(", ") || "none"}`);
|
|
425
|
+
console.log(` harness coverage: ${res.harnessCoverage.join(", ") || "none"}`);
|
|
426
|
+
for (const r of res.evalReports) printEvalReport(r);
|
|
427
|
+
if (!res.ok) {
|
|
428
|
+
console.error("\nPublish BLOCKED: the deterministic tier failed.");
|
|
429
|
+
process.exit(1);
|
|
430
|
+
}
|
|
431
|
+
console.log("\nPublish gate passed.");
|
|
432
|
+
} catch (err) {
|
|
433
|
+
fail(err);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
var signCmd = defineCommand({
|
|
438
|
+
meta: {
|
|
439
|
+
name: "sign",
|
|
440
|
+
description: "Sign loom.lock's artifact set (ed25519) -> loom.sig + loom.pub (the signed badge)"
|
|
441
|
+
},
|
|
442
|
+
args: {
|
|
443
|
+
dir: {
|
|
444
|
+
type: "positional",
|
|
445
|
+
required: false,
|
|
446
|
+
default: ".",
|
|
447
|
+
description: "Plugin dir with loom.lock"
|
|
448
|
+
}
|
|
449
|
+
},
|
|
450
|
+
run({ args }) {
|
|
451
|
+
try {
|
|
452
|
+
const lock = readLock(args.dir);
|
|
453
|
+
if (!lock) {
|
|
454
|
+
console.error("no loom.lock found (run `loom install` first)");
|
|
455
|
+
process.exit(1);
|
|
456
|
+
}
|
|
457
|
+
const loomDir = join(args.dir, ".loom");
|
|
458
|
+
mkdirSync(loomDir, { recursive: true });
|
|
459
|
+
writeFileSync(join(loomDir, ".gitignore"), "*\n");
|
|
460
|
+
const keyPath = join(loomDir, "signing.key");
|
|
461
|
+
let privateKey;
|
|
462
|
+
if (existsSync(keyPath)) {
|
|
463
|
+
privateKey = createPrivateKey(readFileSync(keyPath));
|
|
464
|
+
} else {
|
|
465
|
+
const keys = generateSigningKeys();
|
|
466
|
+
privateKey = keys.privateKey;
|
|
467
|
+
writeFileSync(keyPath, keys.privateKey.export({ type: "pkcs8", format: "pem" }));
|
|
468
|
+
writeFileSync(
|
|
469
|
+
join(args.dir, "loom.pub"),
|
|
470
|
+
keys.publicKey.export({ type: "spki", format: "pem" })
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
writeFileSync(join(args.dir, "loom.sig"), `${signLock(lock, privateKey)}
|
|
474
|
+
`);
|
|
475
|
+
console.log(
|
|
476
|
+
`Signed ${lock.artifacts.length} artifacts -> loom.sig (key kept in .loom/, public key in loom.pub)`
|
|
477
|
+
);
|
|
478
|
+
} catch (err) {
|
|
479
|
+
fail(err);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
var verifyCmd = defineCommand({
|
|
484
|
+
meta: {
|
|
485
|
+
name: "verify",
|
|
486
|
+
description: "Verify loom.sig against loom.lock and the on-disk artifacts"
|
|
487
|
+
},
|
|
488
|
+
args: {
|
|
489
|
+
dir: {
|
|
490
|
+
type: "positional",
|
|
491
|
+
required: false,
|
|
492
|
+
default: ".",
|
|
493
|
+
description: "Plugin dir with loom.lock"
|
|
494
|
+
}
|
|
495
|
+
},
|
|
496
|
+
run({ args }) {
|
|
497
|
+
try {
|
|
498
|
+
const lock = readLock(args.dir);
|
|
499
|
+
if (!lock) {
|
|
500
|
+
console.error("no loom.lock found");
|
|
501
|
+
process.exit(1);
|
|
502
|
+
}
|
|
503
|
+
const sig = readFileSync(join(args.dir, "loom.sig"), "utf8").trim();
|
|
504
|
+
const publicKey = createPublicKey(readFileSync(join(args.dir, "loom.pub")));
|
|
505
|
+
const res = verifyArtifacts(lock, publicKey, sig);
|
|
506
|
+
console.log(`signature: ${res.signatureValid ? "valid" : "INVALID"}`);
|
|
507
|
+
console.log(`tampered artifacts: ${res.tampered.length}`);
|
|
508
|
+
for (const p of res.tampered) console.log(` ! ${p}`);
|
|
509
|
+
if (!res.signatureValid || res.tampered.length > 0) process.exit(1);
|
|
510
|
+
console.log("signed badge verified.");
|
|
511
|
+
} catch (err) {
|
|
512
|
+
fail(err);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
var indexCmd = defineCommand({
|
|
517
|
+
meta: {
|
|
518
|
+
name: "index",
|
|
519
|
+
description: "Build a metadata index from plugin dirs (optionally federating the MCP Registry)"
|
|
520
|
+
},
|
|
521
|
+
args: {
|
|
522
|
+
dir: { type: "positional", required: false, default: ".", description: "Plugin directories" },
|
|
523
|
+
out: { type: "string", default: "index.json", description: "Output index file" },
|
|
524
|
+
federate: { type: "boolean", description: "Ingest the MCP Registry (GET /v0.1/servers)" }
|
|
525
|
+
},
|
|
526
|
+
async run({ args }) {
|
|
527
|
+
try {
|
|
528
|
+
const positionals = args._ ?? [];
|
|
529
|
+
const dirs = positionals.length > 0 ? positionals : ["."];
|
|
530
|
+
let index = await indexFromPluginDirs(dirs);
|
|
531
|
+
if (args.federate) {
|
|
532
|
+
const servers = await fetchMcpRegistry({ limit: 30 });
|
|
533
|
+
index = federate(index, servers, (/* @__PURE__ */ new Date()).toISOString());
|
|
534
|
+
}
|
|
535
|
+
writeFileSync(args.out, serializeIndex(index));
|
|
536
|
+
const fed = args.federate ? `, federated ${index.federated?.length ?? 0} source(s)` : "";
|
|
537
|
+
console.log(`Wrote index (${index.plugins.length} plugins${fed}) -> ${args.out}`);
|
|
538
|
+
} catch (err) {
|
|
539
|
+
fail(err);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
var importCmd = defineCommand({
|
|
544
|
+
meta: {
|
|
545
|
+
name: "import",
|
|
546
|
+
description: "Reverse-compile an existing native plugin/marketplace into a Loom plugin"
|
|
547
|
+
},
|
|
548
|
+
args: {
|
|
549
|
+
dir: {
|
|
550
|
+
type: "positional",
|
|
551
|
+
required: false,
|
|
552
|
+
default: ".",
|
|
553
|
+
description: "Dir with an existing native plugin or marketplace"
|
|
554
|
+
},
|
|
555
|
+
from: { type: "string", default: "claude", description: "Source harness format" },
|
|
556
|
+
out: { type: "string", default: "imported", description: "Output directory" },
|
|
557
|
+
namespace: {
|
|
558
|
+
type: "string",
|
|
559
|
+
description: "Reverse-DNS namespace to assign (default com.imported)"
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
run({ args }) {
|
|
563
|
+
try {
|
|
564
|
+
const adapter = buildRegistry().get(args.from);
|
|
565
|
+
if (!adapter) {
|
|
566
|
+
console.error(`unknown source harness "${args.from}"`);
|
|
567
|
+
process.exit(1);
|
|
568
|
+
}
|
|
569
|
+
const res = importNativePlugin({
|
|
570
|
+
dir: args.dir,
|
|
571
|
+
adapter,
|
|
572
|
+
outDir: args.out,
|
|
573
|
+
...args.namespace ? { namespace: args.namespace } : {}
|
|
574
|
+
});
|
|
575
|
+
console.log(`Imported ${res.kind} "${res.name}" -> ${res.manifestPath}`);
|
|
576
|
+
if (res.kind === "plugin") console.log(` ${res.fileCount} component file(s)`);
|
|
577
|
+
console.log(` next: loom build ${res.outDir}`);
|
|
578
|
+
} catch (err) {
|
|
579
|
+
fail(err);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
var uninstallCmd = defineCommand({
|
|
584
|
+
meta: {
|
|
585
|
+
name: "uninstall",
|
|
586
|
+
description: "Remove everything install placed (from loom.lock) and delete the lockfile"
|
|
587
|
+
},
|
|
588
|
+
args: {
|
|
589
|
+
dir: {
|
|
590
|
+
type: "positional",
|
|
591
|
+
required: false,
|
|
592
|
+
default: ".",
|
|
593
|
+
description: "Plugin dir with loom.lock"
|
|
594
|
+
}
|
|
595
|
+
},
|
|
596
|
+
run({ args }) {
|
|
597
|
+
try {
|
|
598
|
+
const res = uninstall({ pluginDir: args.dir });
|
|
599
|
+
console.log(`Uninstalled ${res.removed.length} artifact(s)`);
|
|
600
|
+
} catch (err) {
|
|
601
|
+
fail(err);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
});
|
|
605
|
+
var docsCmd = defineCommand({
|
|
606
|
+
meta: {
|
|
607
|
+
name: "docs",
|
|
608
|
+
description: "Print the full CLI reference (a CLI map), generated from the command tree"
|
|
609
|
+
},
|
|
610
|
+
args: {
|
|
611
|
+
out: {
|
|
612
|
+
type: "string",
|
|
613
|
+
description: "Write the Markdown reference to this file instead of stdout"
|
|
614
|
+
}
|
|
615
|
+
},
|
|
616
|
+
async run({ args }) {
|
|
617
|
+
const md = await renderCliReference(main);
|
|
618
|
+
if (args.out) {
|
|
619
|
+
writeFileSync(args.out, md);
|
|
620
|
+
console.log(`Wrote CLI reference to ${args.out}`);
|
|
621
|
+
} else {
|
|
622
|
+
process.stdout.write(md);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
var main = defineCommand({
|
|
627
|
+
meta: {
|
|
628
|
+
name: "loom",
|
|
629
|
+
version: LOOM_VERSION,
|
|
630
|
+
description: "Author once, compile to every coding-agent harness."
|
|
631
|
+
},
|
|
632
|
+
subCommands: {
|
|
633
|
+
init: initCmd,
|
|
634
|
+
validate: validateCmd,
|
|
635
|
+
build: buildCmd,
|
|
636
|
+
install: installCmd,
|
|
637
|
+
uninstall: uninstallCmd,
|
|
638
|
+
update: updateCmd,
|
|
639
|
+
import: importCmd,
|
|
640
|
+
eval: evalCmd,
|
|
641
|
+
publish: publishCmd,
|
|
642
|
+
sign: signCmd,
|
|
643
|
+
verify: verifyCmd,
|
|
644
|
+
index: indexCmd,
|
|
645
|
+
docs: docsCmd
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
runMain(main);
|
|
649
|
+
//# sourceMappingURL=index.js.map
|
|
650
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli-docs.ts","../src/registry.ts","../src/report.ts","../src/scaffold.ts","../src/index.ts"],"names":["main","resolve","drivers","written","join","mkdirSync","writeFileSync","existsSync"],"mappings":"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","file":"index.js","sourcesContent":["import { type CommandDef, renderUsage } from \"citty\";\n\ntype Resolvable<T> = T | (() => T | Promise<T>);\n\nasync function resolve<T>(r: Resolvable<T>): Promise<T> {\n return typeof r === \"function\" ? await (r as () => T | Promise<T>)() : r;\n}\n\n/**\n * Render a full Markdown CLI reference straight from the citty command tree, so\n * the docs can never drift from the actual commands -- the same single-source\n * principle Loom applies to manifests, applied to its own CLI.\n */\nexport async function renderCliReference(main: CommandDef): Promise<string> {\n const out: string[] = [\n \"# Loom CLI reference\",\n \"\",\n \"_Generated from the CLI definition by `loom docs` -- do not edit by hand._\",\n \"\",\n \"## `loom`\",\n \"\",\n \"```\",\n (await renderUsage(main)).trim(),\n \"```\",\n \"\",\n ];\n\n const subs = main.subCommands ? await resolve(main.subCommands) : {};\n for (const name of Object.keys(subs).sort()) {\n const sub = await resolve(subs[name] as Resolvable<CommandDef>);\n const meta = await resolve(sub.meta);\n out.push(`## \\`loom ${name}\\``, \"\");\n if (meta?.description) out.push(meta.description as string, \"\");\n out.push(\"```\", (await renderUsage(sub, main)).trim(), \"```\", \"\");\n }\n\n return `${out.join(\"\\n\")}\\n`;\n}\n","import claudeAdapter from \"@michaelfromyeg/loom-adapter-claude\";\nimport codexAdapter from \"@michaelfromyeg/loom-adapter-codex\";\nimport copilotAdapter from \"@michaelfromyeg/loom-adapter-copilot\";\nimport cursorAdapter from \"@michaelfromyeg/loom-adapter-cursor\";\nimport type { HarnessDriver } from \"@michaelfromyeg/loom-adapter-kit\";\nimport opencodeAdapter from \"@michaelfromyeg/loom-adapter-opencode\";\nimport { AdapterRegistry } from \"@michaelfromyeg/loom-core\";\nimport { drivers } from \"@michaelfromyeg/loom-eval\";\nimport type { Target } from \"@michaelfromyeg/loom-schema\";\n\n/**\n * Wire every concrete adapter into a registry. The CLI sits at the top of the\n * dependency graph, so it -- not core -- knows the full adapter set. Community\n * adapters are added the same way.\n */\nexport function buildRegistry(): AdapterRegistry {\n return new AdapterRegistry()\n .register(claudeAdapter)\n .register(codexAdapter)\n .register(cursorAdapter)\n .register(copilotAdapter)\n .register(opencodeAdapter);\n}\n\n/** The headless eval drivers, keyed by Target (from @michaelfromyeg/loom-eval). */\nexport function allDrivers(): Record<Target, HarnessDriver> {\n return drivers;\n}\n\n/** Parse a comma-separated CLI value into a trimmed list, or undefined when empty. */\nexport function parseList(value: string | undefined): string[] | undefined {\n if (!value) return undefined;\n const items = value\n .split(\",\")\n .map((t) => t.trim())\n .filter(Boolean);\n return items.length > 0 ? items : undefined;\n}\n\n/** Parse a comma-separated --target value into a typed list, or undefined for all. */\nexport function parseTargets(value: string | undefined): Target[] | undefined {\n return parseList(value) as Target[] | undefined;\n}\n","import type { CompileResult, Diagnostic } from \"@michaelfromyeg/loom-core\";\nimport type { EvalReport } from \"@michaelfromyeg/loom-eval\";\n\nexport function formatDiagnostic(d: Diagnostic): string {\n const tag = d.severity === \"error\" ? \"error\" : d.severity === \"warning\" ? \"warn\" : \"info\";\n const where = d.where ? `${d.where}: ` : \"\";\n return ` ${tag.padEnd(5)} ${where}${d.message}`;\n}\n\nexport function printDiagnostics(items: Diagnostic[]): void {\n for (const d of items) {\n const line = formatDiagnostic(d);\n if (d.severity === \"error\") console.error(line);\n else console.warn(line);\n }\n}\n\n/**\n * The trust summary required before first install (spec §11): what runs and on\n * whose authority. Lists components by kind, every executable artifact, every MCP\n * server, and the publisher-verification state.\n */\nexport function printTrustSummary(result: CompileResult): void {\n const { plugin } = result.fb;\n // Count from the components actually installed (accurate for piecemeal installs).\n const components = result.components;\n console.log(`\\nTrust summary for ${result.id}@${plugin.version}`);\n console.log(` publisher: ${plugin.owner.name} <${plugin.owner.namespace}> (unverified)`);\n\n const counts = new Map<string, number>();\n for (const c of components) counts.set(c.kind, (counts.get(c.kind) ?? 0) + 1);\n const summary = [...counts.entries()].map(([k, n]) => `${n} ${k}`).join(\", \");\n console.log(` components: ${summary || \"none\"}`);\n\n const executables = result.targets.flatMap((t) =>\n t.artifacts\n .filter((p) => p.artifact.executable)\n .map((p) => `${t.target}:${p.artifact.relPath}`),\n );\n console.log(\n executables.length > 0\n ? ` executables (placed DISABLED): ${executables.join(\", \")}`\n : \" executables: none\",\n );\n\n console.log(` mcp servers that will run: ${counts.get(\"mcp\") ?? 0}`);\n console.log(\" badges: valid (computed) | signed/verified/scanned: not yet\\n\");\n}\n\n/** Render an eval report: per-harness PASS/FAIL/UNTESTED with per-assertion status. */\nexport function printEvalReport(report: EvalReport): void {\n console.log(`\\nEval: ${report.component}`);\n for (const h of report.harnesses) {\n if (h.status === \"untested\") {\n console.log(` ${h.harness}: UNTESTED (${h.reason})`);\n continue;\n }\n console.log(` ${h.harness}: ${h.pass ? \"PASS\" : \"FAIL\"}`);\n for (const c of h.cases) {\n console.log(` - ${c.name}: ${c.pass ? \"pass\" : \"fail\"}`);\n for (const a of c.assertions) {\n console.log(` ${a.kind}: ${a.status} (${a.detail})`);\n }\n }\n }\n}\n","import { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { basename, join, relative, resolve } from \"node:path\";\n\nexport interface ScaffoldOptions {\n dir: string;\n name?: string;\n namespace?: string;\n}\n\nexport interface ScaffoldResult {\n dir: string;\n name: string;\n files: string[];\n}\n\nfunction kebab(s: string): string {\n return (\n s\n .toLowerCase()\n .replace(/[^a-z0-9-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\") || \"my-plugin\"\n );\n}\n\n/** Create a minimal, valid plugin: loom.yaml + one sample skill. Never clobbers. */\nexport function scaffoldPlugin(opts: ScaffoldOptions): ScaffoldResult {\n const dir = resolve(opts.dir);\n mkdirSync(dir, { recursive: true });\n\n const manifestPath = join(dir, \"loom.yaml\");\n if (existsSync(manifestPath)) {\n throw new Error(`a plugin already exists at ${manifestPath}`);\n }\n\n const name = kebab(opts.name ?? basename(dir));\n const namespace = opts.namespace ?? \"com.example\";\n const files: string[] = [];\n\n const loomYaml = `name: ${name}\nversion: 0.1.0\nowner:\n name: Your Name\n namespace: ${namespace}\ndescription: A Loom plugin.\ncomponents:\n - skill: skills/hello\n`;\n writeFileSync(manifestPath, loomYaml);\n files.push(relative(dir, manifestPath));\n\n const skillDir = join(dir, \"skills\", \"hello\");\n mkdirSync(skillDir, { recursive: true });\n const skillMd = `---\nname: hello\ndescription: Greet the user and explain what this skill does.\n---\n\nWhen invoked, greet the user warmly and summarize the task at hand.\n`;\n const skillPath = join(skillDir, \"SKILL.md\");\n writeFileSync(skillPath, skillMd);\n files.push(relative(dir, skillPath));\n\n return { dir, name, files };\n}\n","import { createPrivateKey, createPublicKey } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n build,\n buildMarketplace,\n CompileError,\n generateSigningKeys,\n hasMarketplaceManifest,\n importNativePlugin,\n install,\n LOOM_VERSION,\n lint,\n readLock,\n signLock,\n uninstall,\n update,\n verifyArtifacts,\n} from \"@michaelfromyeg/loom-core\";\nimport { discoverEvals, runEval } from \"@michaelfromyeg/loom-eval\";\nimport {\n federate,\n fetchMcpRegistry,\n indexFromPluginDirs,\n publishCheck,\n serializeIndex,\n} from \"@michaelfromyeg/loom-index\";\nimport type { Scope, Target } from \"@michaelfromyeg/loom-schema\";\nimport { defineCommand, runMain } from \"citty\";\nimport { renderCliReference } from \"./cli-docs\";\nimport { allDrivers, buildRegistry, parseList, parseTargets } from \"./registry\";\nimport { printDiagnostics, printEvalReport, printTrustSummary } from \"./report\";\nimport { scaffoldPlugin } from \"./scaffold\";\n\n/** Filter requested targets to harnesses actually present on this machine. */\nasync function detectPresent(\n requested: Target[],\n): Promise<{ present: Target[]; missing: Target[] }> {\n const drivers = allDrivers();\n const present: Target[] = [];\n const missing: Target[] = [];\n for (const t of requested) {\n if (await drivers[t]?.available()) present.push(t);\n else missing.push(t);\n }\n return { present, missing };\n}\n\nfunction countByTarget(written: { target: string }[]): Map<string, number> {\n const counts = new Map<string, number>();\n for (const w of written) counts.set(w.target, (counts.get(w.target) ?? 0) + 1);\n return counts;\n}\n\nfunction fail(err: unknown): never {\n if (err instanceof CompileError) {\n console.error(`\\n${err.message}:`);\n printDiagnostics(err.diagnostics);\n } else {\n console.error(`\\n${(err as Error).message}`);\n }\n process.exit(1);\n}\n\nconst initCmd = defineCommand({\n meta: { name: \"init\", description: \"Scaffold a new plugin (loom.yaml + a sample skill)\" },\n args: {\n dir: { type: \"positional\", required: false, default: \".\", description: \"Target directory\" },\n name: { type: \"string\", description: \"Plugin name (kebab-case)\" },\n namespace: { type: \"string\", description: \"Reverse-DNS namespace, e.g. com.acme\" },\n },\n run({ args }) {\n const created = scaffoldPlugin({\n dir: args.dir,\n name: args.name,\n namespace: args.namespace,\n });\n console.log(`Scaffolded plugin \"${created.name}\" in ${created.dir}`);\n for (const f of created.files) console.log(` + ${f}`);\n console.log(`\\nNext: loom validate ${args.dir} && loom build ${args.dir}`);\n },\n});\n\nconst validateCmd = defineCommand({\n meta: { name: \"validate\", description: \"Statically validate a plugin (the valid badge)\" },\n args: {\n dir: { type: \"positional\", required: false, default: \".\", description: \"Plugin directory\" },\n },\n run({ args }) {\n try {\n const result = lint(args.dir);\n const { items, hasErrors } = result.diagnostics;\n if (items.length > 0) printDiagnostics(items);\n if (hasErrors) {\n console.error(`\\n${result.id}: invalid`);\n process.exit(1);\n }\n console.log(`${result.id}: valid (${result.plugin.components.length} components)`);\n } catch (err) {\n fail(err);\n }\n },\n});\n\nconst buildCmd = defineCommand({\n meta: {\n name: \"build\",\n description: \"Compile a plugin (or a marketplace of plugins) to harness manifests\",\n },\n args: {\n dir: {\n type: \"positional\",\n required: false,\n default: \".\",\n description: \"Plugin or marketplace directory\",\n },\n out: { type: \"string\", default: \".loom-out\", description: \"Output directory\" },\n target: { type: \"string\", description: \"Comma-separated targets (default: all registered)\" },\n },\n async run({ args }) {\n try {\n const registry = buildRegistry();\n const targets = parseTargets(args.target);\n\n // A marketplace.yaml packages many plugins into one catalog.\n if (hasMarketplaceManifest(args.dir)) {\n const { marketplace, plugins, written } = await buildMarketplace({\n marketplaceDir: args.dir,\n outDir: args.out,\n registry,\n targets,\n });\n console.log(\n `Built marketplace ${marketplace.name} (${plugins.length} plugins) -> ${args.out}/`,\n );\n for (const [t, n] of countByTarget(written)) console.log(` ${t}: ${n} files`);\n return;\n }\n\n const { result, written } = await build({\n pluginDir: args.dir,\n outDir: args.out,\n registry,\n targets,\n });\n if (result.diagnostics.items.length > 0) printDiagnostics(result.diagnostics.items);\n console.log(`Built ${result.id} -> ${args.out}/`);\n for (const [t, n] of countByTarget(written)) {\n console.log(` ${t}: ${n} files (catalog at ${args.out}/${t})`);\n }\n } catch (err) {\n fail(err);\n }\n },\n});\n\nconst installCmd = defineCommand({\n meta: {\n name: \"install\",\n description: \"Compile + place a plugin into a harness scope, write loom.lock\",\n },\n args: {\n dir: { type: \"positional\", required: false, default: \".\", description: \"Plugin directory\" },\n scope: { type: \"string\", default: \"project\", description: \"user | project\" },\n target: { type: \"string\", description: \"Comma-separated targets (default: all registered)\" },\n only: {\n type: \"string\",\n description: \"Comma-separated component names to install piecemeal (e.g. one skill)\",\n },\n all: {\n type: \"boolean\",\n description: \"Install to requested targets even if the harness is not detected\",\n },\n managed: {\n type: \"string\",\n description: \"Managed mode: only allow these namespaces (comma-separated allowlist)\",\n },\n cwd: { type: \"string\", description: \"Project root for project-scope placement (default: cwd)\" },\n },\n async run({ args }) {\n try {\n const registry = buildRegistry();\n const scope = (args.scope === \"user\" ? \"user\" : \"project\") as Scope;\n const requested = parseTargets(args.target) ?? registry.targets;\n const allow = parseList(args.managed);\n\n // Skip targets whose harness isn't on this machine, and say so (spec §9.2).\n let targets = requested;\n if (!args.all) {\n const { present, missing } = await detectPresent(requested);\n for (const t of missing) console.log(` skipped ${t}: harness not detected`);\n if (present.length === 0) {\n console.error(\"No requested harness is installed. Use --all to place anyway.\");\n process.exit(1);\n }\n targets = present;\n }\n\n const result = await install({\n pluginDir: args.dir,\n scope,\n cwd: args.cwd ?? process.cwd(),\n registry,\n targets,\n only: parseList(args.only),\n ...(allow ? { managed: { allowNamespaces: allow } } : {}),\n });\n printTrustSummary(result.result);\n console.log(\n `Installed ${result.lockfile.plugin.id}@${result.lockfile.plugin.version} (${scope})`,\n );\n console.log(` ${result.lockfile.artifacts.length} artifacts placed`);\n if (result.secrets.resolved.length > 0) {\n const missing = result.secrets.resolved.filter((r) => r.source === \"missing\");\n console.log(\n ` config: ${result.secrets.resolved.length} declared` +\n (result.secrets.path ? ` -> ${result.secrets.path} (gitignored)` : \"\") +\n (missing.length > 0 ? `; missing: ${missing.map((m) => m.env).join(\", \")}` : \"\"),\n );\n }\n console.log(` lockfile: ${result.lockPath}`);\n } catch (err) {\n fail(err);\n }\n },\n});\n\nconst updateCmd = defineCommand({\n meta: {\n name: \"update\",\n description: \"Re-resolve refs, recompile, and re-place only artifacts whose hash changed\",\n },\n args: {\n dir: { type: \"positional\", required: false, default: \".\", description: \"Plugin directory\" },\n scope: { type: \"string\", default: \"project\", description: \"user | project\" },\n target: { type: \"string\", description: \"Comma-separated targets (default: all registered)\" },\n cwd: { type: \"string\", description: \"Project root for project-scope placement (default: cwd)\" },\n },\n async run({ args }) {\n try {\n const scope = (args.scope === \"user\" ? \"user\" : \"project\") as Scope;\n // Default to the targets already in the lockfile, so update matches what\n // install placed (not every registered adapter).\n const lock = readLock(args.dir);\n const lockedTargets = lock\n ? ([...new Set(lock.artifacts.map((a) => a.target))] as Target[])\n : undefined;\n const result = await update({\n pluginDir: args.dir,\n scope,\n cwd: args.cwd ?? process.cwd(),\n registry: buildRegistry(),\n targets: parseTargets(args.target) ?? lockedTargets,\n });\n console.log(\n `Updated ${result.lockfile.plugin.id}: ${result.changed.length} artifact(s) changed`,\n );\n for (const p of result.changed) console.log(` ~ ${p}`);\n } catch (err) {\n fail(err);\n }\n },\n});\n\nconst evalCmd = defineCommand({\n meta: {\n name: \"eval\",\n description: \"Run a component's evals against the real harnesses (reports UNTESTED honestly)\",\n },\n args: {\n dir: { type: \"positional\", required: false, default: \".\", description: \"Plugin directory\" },\n component: { type: \"string\", description: \"Only eval this component leaf name\" },\n harness: { type: \"string\", description: \"Restrict to these harnesses (comma-separated)\" },\n },\n async run({ args }) {\n try {\n const registry = buildRegistry();\n const drivers = allDrivers();\n const discovered = discoverEvals(args.dir).filter(\n (d) => !args.component || d.componentLeaf === args.component,\n );\n if (discovered.length === 0) {\n console.log(\"No evals found (a component adds them with an `evals:` file).\");\n return;\n }\n const onlyHarness = parseTargets(args.harness);\n let anyFail = false;\n for (const d of discovered) {\n const evalFile = onlyHarness\n ? {\n ...d.evalFile,\n harnesses: d.evalFile.harnesses.filter((h) => onlyHarness.includes(h)),\n }\n : d.evalFile;\n const report = await runEval({\n evalFile,\n pluginDir: args.dir,\n componentLeaf: d.componentLeaf,\n registry,\n drivers,\n });\n printEvalReport(report);\n if (report.harnesses.some((h) => h.status === \"tested\" && !h.pass)) anyFail = true;\n }\n if (anyFail) process.exit(1);\n } catch (err) {\n fail(err);\n }\n },\n});\n\nconst publishCmd = defineCommand({\n meta: {\n name: \"publish\",\n description: \"Run the deterministic publish gate (static valid + trace/output evals)\",\n },\n args: {\n dir: { type: \"positional\", required: false, default: \".\", description: \"Plugin directory\" },\n snapshot: {\n type: \"boolean\",\n description: \"Snapshot eval scores into evals/.baselines/ for the next release\",\n },\n },\n async run({ args }) {\n try {\n const res = await publishCheck(args.dir, {\n registry: buildRegistry(),\n drivers: allDrivers(),\n snapshot: Boolean(args.snapshot),\n });\n console.log(`Publish check: ${res.id}@${res.version}`);\n if (res.diagnostics.length > 0) printDiagnostics(res.diagnostics);\n console.log(` valid: ${res.validPassed ? \"yes\" : \"NO\"}`);\n console.log(` scan: ${res.scan.clean ? \"clean\" : `${res.scan.findings.length} finding(s)`}`);\n console.log(` badges: ${res.badges.join(\", \") || \"none\"}`);\n console.log(` harness coverage: ${res.harnessCoverage.join(\", \") || \"none\"}`);\n for (const r of res.evalReports) printEvalReport(r);\n if (!res.ok) {\n console.error(\"\\nPublish BLOCKED: the deterministic tier failed.\");\n process.exit(1);\n }\n console.log(\"\\nPublish gate passed.\");\n } catch (err) {\n fail(err);\n }\n },\n});\n\nconst signCmd = defineCommand({\n meta: {\n name: \"sign\",\n description:\n \"Sign loom.lock's artifact set (ed25519) -> loom.sig + loom.pub (the signed badge)\",\n },\n args: {\n dir: {\n type: \"positional\",\n required: false,\n default: \".\",\n description: \"Plugin dir with loom.lock\",\n },\n },\n run({ args }) {\n try {\n const lock = readLock(args.dir);\n if (!lock) {\n console.error(\"no loom.lock found (run `loom install` first)\");\n process.exit(1);\n }\n const loomDir = join(args.dir, \".loom\");\n mkdirSync(loomDir, { recursive: true });\n writeFileSync(join(loomDir, \".gitignore\"), \"*\\n\");\n const keyPath = join(loomDir, \"signing.key\");\n\n let privateKey: ReturnType<typeof createPrivateKey>;\n if (existsSync(keyPath)) {\n privateKey = createPrivateKey(readFileSync(keyPath));\n } else {\n const keys = generateSigningKeys();\n privateKey = keys.privateKey;\n writeFileSync(keyPath, keys.privateKey.export({ type: \"pkcs8\", format: \"pem\" }));\n writeFileSync(\n join(args.dir, \"loom.pub\"),\n keys.publicKey.export({ type: \"spki\", format: \"pem\" }),\n );\n }\n writeFileSync(join(args.dir, \"loom.sig\"), `${signLock(lock, privateKey)}\\n`);\n console.log(\n `Signed ${lock.artifacts.length} artifacts -> loom.sig (key kept in .loom/, public key in loom.pub)`,\n );\n } catch (err) {\n fail(err);\n }\n },\n});\n\nconst verifyCmd = defineCommand({\n meta: {\n name: \"verify\",\n description: \"Verify loom.sig against loom.lock and the on-disk artifacts\",\n },\n args: {\n dir: {\n type: \"positional\",\n required: false,\n default: \".\",\n description: \"Plugin dir with loom.lock\",\n },\n },\n run({ args }) {\n try {\n const lock = readLock(args.dir);\n if (!lock) {\n console.error(\"no loom.lock found\");\n process.exit(1);\n }\n const sig = readFileSync(join(args.dir, \"loom.sig\"), \"utf8\").trim();\n const publicKey = createPublicKey(readFileSync(join(args.dir, \"loom.pub\")));\n const res = verifyArtifacts(lock, publicKey, sig);\n console.log(`signature: ${res.signatureValid ? \"valid\" : \"INVALID\"}`);\n console.log(`tampered artifacts: ${res.tampered.length}`);\n for (const p of res.tampered) console.log(` ! ${p}`);\n if (!res.signatureValid || res.tampered.length > 0) process.exit(1);\n console.log(\"signed badge verified.\");\n } catch (err) {\n fail(err);\n }\n },\n});\n\nconst indexCmd = defineCommand({\n meta: {\n name: \"index\",\n description: \"Build a metadata index from plugin dirs (optionally federating the MCP Registry)\",\n },\n args: {\n dir: { type: \"positional\", required: false, default: \".\", description: \"Plugin directories\" },\n out: { type: \"string\", default: \"index.json\", description: \"Output index file\" },\n federate: { type: \"boolean\", description: \"Ingest the MCP Registry (GET /v0.1/servers)\" },\n },\n async run({ args }) {\n try {\n // citty puts every positional in `_`; default to the current dir when none.\n const positionals = (args._ as string[] | undefined) ?? [];\n const dirs = positionals.length > 0 ? positionals : [\".\"];\n let index = await indexFromPluginDirs(dirs);\n if (args.federate) {\n const servers = await fetchMcpRegistry({ limit: 30 });\n index = federate(index, servers, new Date().toISOString());\n }\n writeFileSync(args.out, serializeIndex(index));\n const fed = args.federate ? `, federated ${index.federated?.length ?? 0} source(s)` : \"\";\n console.log(`Wrote index (${index.plugins.length} plugins${fed}) -> ${args.out}`);\n } catch (err) {\n fail(err);\n }\n },\n});\n\nconst importCmd = defineCommand({\n meta: {\n name: \"import\",\n description: \"Reverse-compile an existing native plugin/marketplace into a Loom plugin\",\n },\n args: {\n dir: {\n type: \"positional\",\n required: false,\n default: \".\",\n description: \"Dir with an existing native plugin or marketplace\",\n },\n from: { type: \"string\", default: \"claude\", description: \"Source harness format\" },\n out: { type: \"string\", default: \"imported\", description: \"Output directory\" },\n namespace: {\n type: \"string\",\n description: \"Reverse-DNS namespace to assign (default com.imported)\",\n },\n },\n run({ args }) {\n try {\n const adapter = buildRegistry().get(args.from as Target);\n if (!adapter) {\n console.error(`unknown source harness \"${args.from}\"`);\n process.exit(1);\n }\n const res = importNativePlugin({\n dir: args.dir,\n adapter,\n outDir: args.out,\n ...(args.namespace ? { namespace: args.namespace } : {}),\n });\n console.log(`Imported ${res.kind} \"${res.name}\" -> ${res.manifestPath}`);\n if (res.kind === \"plugin\") console.log(` ${res.fileCount} component file(s)`);\n console.log(` next: loom build ${res.outDir}`);\n } catch (err) {\n fail(err);\n }\n },\n});\n\nconst uninstallCmd = defineCommand({\n meta: {\n name: \"uninstall\",\n description: \"Remove everything install placed (from loom.lock) and delete the lockfile\",\n },\n args: {\n dir: {\n type: \"positional\",\n required: false,\n default: \".\",\n description: \"Plugin dir with loom.lock\",\n },\n },\n run({ args }) {\n try {\n const res = uninstall({ pluginDir: args.dir });\n console.log(`Uninstalled ${res.removed.length} artifact(s)`);\n } catch (err) {\n fail(err);\n }\n },\n});\n\nconst docsCmd = defineCommand({\n meta: {\n name: \"docs\",\n description: \"Print the full CLI reference (a CLI map), generated from the command tree\",\n },\n args: {\n out: {\n type: \"string\",\n description: \"Write the Markdown reference to this file instead of stdout\",\n },\n },\n async run({ args }) {\n const md = await renderCliReference(main);\n if (args.out) {\n writeFileSync(args.out, md);\n console.log(`Wrote CLI reference to ${args.out}`);\n } else {\n process.stdout.write(md);\n }\n },\n});\n\nconst main = defineCommand({\n meta: {\n name: \"loom\",\n version: LOOM_VERSION,\n description: \"Author once, compile to every coding-agent harness.\",\n },\n subCommands: {\n init: initCmd,\n validate: validateCmd,\n build: buildCmd,\n install: installCmd,\n uninstall: uninstallCmd,\n update: updateCmd,\n import: importCmd,\n eval: evalCmd,\n publish: publishCmd,\n sign: signCmd,\n verify: verifyCmd,\n index: indexCmd,\n docs: docsCmd,\n },\n});\n\nrunMain(main);\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@michaelfromyeg/loom-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The `loom` CLI: a thin shell over @michaelfromyeg/loom-core and the harness adapters.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"loom": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"citty": "^0.1.6",
|
|
15
|
+
"@michaelfromyeg/loom-adapter-claude": "0.1.0",
|
|
16
|
+
"@michaelfromyeg/loom-adapter-codex": "0.1.0",
|
|
17
|
+
"@michaelfromyeg/loom-adapter-copilot": "0.1.0",
|
|
18
|
+
"@michaelfromyeg/loom-adapter-cursor": "0.1.0",
|
|
19
|
+
"@michaelfromyeg/loom-adapter-kit": "0.1.0",
|
|
20
|
+
"@michaelfromyeg/loom-adapter-opencode": "0.1.0",
|
|
21
|
+
"@michaelfromyeg/loom-core": "0.1.0",
|
|
22
|
+
"@michaelfromyeg/loom-schema": "0.1.0",
|
|
23
|
+
"@michaelfromyeg/loom-index": "0.1.0",
|
|
24
|
+
"@michaelfromyeg/loom-eval": "0.1.0"
|
|
25
|
+
},
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"author": "Michael DeMarco",
|
|
28
|
+
"homepage": "https://github.com/michaelfromyeg/loom#readme",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/michaelfromyeg/loom.git",
|
|
32
|
+
"directory": "packages/cli"
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/michaelfromyeg/loom/issues"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"loom",
|
|
39
|
+
"coding-agent",
|
|
40
|
+
"ai-agent",
|
|
41
|
+
"claude-code",
|
|
42
|
+
"codex",
|
|
43
|
+
"cursor",
|
|
44
|
+
"copilot",
|
|
45
|
+
"opencode",
|
|
46
|
+
"mcp",
|
|
47
|
+
"plugin",
|
|
48
|
+
"skill",
|
|
49
|
+
"compiler"
|
|
50
|
+
],
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsup"
|
|
56
|
+
}
|
|
57
|
+
}
|