quiver-cli 0.7.0 → 0.8.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/dist/cli.js +190 -44
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -205,7 +205,13 @@ var init_node_guard = __esm({
|
|
|
205
205
|
"src/version/node-guard.ts"() {
|
|
206
206
|
"use strict";
|
|
207
207
|
MIN_NODE = "20.12.0";
|
|
208
|
-
INTERACTIVE_COMMANDS = /* @__PURE__ */ new Set([
|
|
208
|
+
INTERACTIVE_COMMANDS = /* @__PURE__ */ new Set([
|
|
209
|
+
"init",
|
|
210
|
+
"add",
|
|
211
|
+
"remove",
|
|
212
|
+
"rm",
|
|
213
|
+
"providers"
|
|
214
|
+
]);
|
|
209
215
|
parse = (v) => {
|
|
210
216
|
const [major = 0, minor = 0, patch = 0] = v.replace(/^v/, "").split(".").map((p) => Number.parseInt(p, 10) || 0);
|
|
211
217
|
return [major, minor, patch];
|
|
@@ -927,6 +933,62 @@ var init_io = __esm({
|
|
|
927
933
|
}
|
|
928
934
|
});
|
|
929
935
|
|
|
936
|
+
// src/providers/resolve.ts
|
|
937
|
+
var validateProviders, sameProviders, resolveProviders, selectProviders;
|
|
938
|
+
var init_resolve2 = __esm({
|
|
939
|
+
"src/providers/resolve.ts"() {
|
|
940
|
+
"use strict";
|
|
941
|
+
init_schema();
|
|
942
|
+
init_prompts();
|
|
943
|
+
validateProviders = (values) => {
|
|
944
|
+
const invalid = values.filter((v) => !isProvider(v));
|
|
945
|
+
if (invalid.length) return { invalid };
|
|
946
|
+
const seen = /* @__PURE__ */ new Set();
|
|
947
|
+
const providers2 = [];
|
|
948
|
+
for (const v of values) {
|
|
949
|
+
if (isProvider(v) && !seen.has(v)) {
|
|
950
|
+
seen.add(v);
|
|
951
|
+
providers2.push(v);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
return { providers: providers2 };
|
|
955
|
+
};
|
|
956
|
+
sameProviders = (a, b) => a.length === b.length && (/* @__PURE__ */ new Set([...a, ...b])).size === a.length;
|
|
957
|
+
resolveProviders = async (options) => {
|
|
958
|
+
if (options.providers) {
|
|
959
|
+
const { providers: providers2, invalid } = validateProviders(options.providers);
|
|
960
|
+
if (invalid) {
|
|
961
|
+
await error(
|
|
962
|
+
`Unknown provider(s): ${invalid.join(", ")}. Valid: ${PROVIDERS.join(", ")}.`
|
|
963
|
+
);
|
|
964
|
+
process.exitCode = 1;
|
|
965
|
+
return null;
|
|
966
|
+
}
|
|
967
|
+
return providers2;
|
|
968
|
+
}
|
|
969
|
+
if (options.all || !process.stdin.isTTY) return [...PROVIDERS];
|
|
970
|
+
return selectProviders([...PROVIDERS]);
|
|
971
|
+
};
|
|
972
|
+
selectProviders = async (initialValues) => {
|
|
973
|
+
const picked = await selectGrouped({
|
|
974
|
+
message: "Generate configs for (space toggles, enter confirms)",
|
|
975
|
+
groups: [
|
|
976
|
+
{
|
|
977
|
+
name: "providers",
|
|
978
|
+
items: [
|
|
979
|
+
{ value: "claude", label: "Claude Code" },
|
|
980
|
+
{ value: "opencode", label: "opencode" },
|
|
981
|
+
{ value: "codex", label: "Codex" }
|
|
982
|
+
]
|
|
983
|
+
}
|
|
984
|
+
],
|
|
985
|
+
initialValues
|
|
986
|
+
});
|
|
987
|
+
return picked.length ? picked : initialValues;
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
|
|
930
992
|
// src/providers/claude.ts
|
|
931
993
|
import { resolve as resolve9 } from "path";
|
|
932
994
|
var formatMcpJson, planClaude;
|
|
@@ -1273,8 +1335,8 @@ var init_selection = __esm({
|
|
|
1273
1335
|
|
|
1274
1336
|
// src/providers/write.ts
|
|
1275
1337
|
import { existsSync as existsSync9, lstatSync as lstatSync2 } from "fs";
|
|
1276
|
-
import { basename, resolve as resolve13 } from "path";
|
|
1277
|
-
var isLinkable, buildPlan, writeProviders, checkProviders;
|
|
1338
|
+
import { basename, relative as relative5, resolve as resolve13 } from "path";
|
|
1339
|
+
var isLinkable, buildPlan, writeProviders, formatWriteResult, checkProviders;
|
|
1278
1340
|
var init_write = __esm({
|
|
1279
1341
|
"src/providers/write.ts"() {
|
|
1280
1342
|
"use strict";
|
|
@@ -1371,6 +1433,21 @@ var init_write = __esm({
|
|
|
1371
1433
|
}
|
|
1372
1434
|
return result;
|
|
1373
1435
|
};
|
|
1436
|
+
formatWriteResult = (targetRoot, result) => {
|
|
1437
|
+
const rel = (p) => relative5(targetRoot, p) || p;
|
|
1438
|
+
const lines = [];
|
|
1439
|
+
const groups = [
|
|
1440
|
+
["generated", result.generated],
|
|
1441
|
+
["linked", result.linked],
|
|
1442
|
+
["removed", result.removed]
|
|
1443
|
+
];
|
|
1444
|
+
for (const [label, paths] of groups) {
|
|
1445
|
+
if (!paths.length) continue;
|
|
1446
|
+
lines.push(` ${label}:`);
|
|
1447
|
+
for (const p of [...paths].map(rel).sort()) lines.push(` - ${p}`);
|
|
1448
|
+
}
|
|
1449
|
+
return lines;
|
|
1450
|
+
};
|
|
1374
1451
|
checkProviders = (targetRoot, catalog, lock) => {
|
|
1375
1452
|
const plan = buildPlan(targetRoot, catalog, lock, () => {
|
|
1376
1453
|
});
|
|
@@ -1397,11 +1474,11 @@ var init_gitignore = __esm({
|
|
|
1397
1474
|
};
|
|
1398
1475
|
SHARED_ENTRIES = ["AGENTS.md", "CLAUDE.md"];
|
|
1399
1476
|
SECRET_ENTRIES = [".env.local"];
|
|
1400
|
-
patchGitignore = (targetRoot,
|
|
1477
|
+
patchGitignore = (targetRoot, providers2) => {
|
|
1401
1478
|
const path = resolve14(targetRoot, ".gitignore");
|
|
1402
1479
|
const current = existsSync10(path) ? readFileSync7(path, "utf8") : "";
|
|
1403
1480
|
const lines = new Set(current.split("\n").map((l) => l.trim()));
|
|
1404
|
-
const active =
|
|
1481
|
+
const active = providers2?.length ? providers2 : [...PROVIDERS];
|
|
1405
1482
|
const shimEntries = [
|
|
1406
1483
|
...active.flatMap((p) => PROVIDER_ENTRIES[p]),
|
|
1407
1484
|
...SHARED_ENTRIES
|
|
@@ -1528,7 +1605,7 @@ var init_exports = {};
|
|
|
1528
1605
|
__export(init_exports, {
|
|
1529
1606
|
init: () => init
|
|
1530
1607
|
});
|
|
1531
|
-
var init
|
|
1608
|
+
var init;
|
|
1532
1609
|
var init_init = __esm({
|
|
1533
1610
|
"src/commands/init.ts"() {
|
|
1534
1611
|
"use strict";
|
|
@@ -1538,6 +1615,7 @@ var init_init = __esm({
|
|
|
1538
1615
|
init_resolve();
|
|
1539
1616
|
init_io();
|
|
1540
1617
|
init_schema();
|
|
1618
|
+
init_resolve2();
|
|
1541
1619
|
init_write();
|
|
1542
1620
|
init_interpolate();
|
|
1543
1621
|
init_prompts();
|
|
@@ -1562,8 +1640,8 @@ var init_init = __esm({
|
|
|
1562
1640
|
const selection = await selectFromCatalog(sourceCatalog, {
|
|
1563
1641
|
interactive: !options.all
|
|
1564
1642
|
});
|
|
1565
|
-
const
|
|
1566
|
-
if (
|
|
1643
|
+
const providers2 = await resolveProviders(options);
|
|
1644
|
+
if (providers2 === null) return;
|
|
1567
1645
|
const resolved = materializeCatalog(
|
|
1568
1646
|
options.targetRoot,
|
|
1569
1647
|
source,
|
|
@@ -1575,7 +1653,7 @@ var init_init = __esm({
|
|
|
1575
1653
|
ref: source.ref ?? null,
|
|
1576
1654
|
resolved: source.resolved ?? null
|
|
1577
1655
|
});
|
|
1578
|
-
lock.providers =
|
|
1656
|
+
lock.providers = providers2;
|
|
1579
1657
|
for (const skill of catalog.skills) {
|
|
1580
1658
|
if (selection.skills.includes(skill.name)) {
|
|
1581
1659
|
lock.entries[entryId("skill", skill.name)] = skillToEntry(skill);
|
|
@@ -1601,7 +1679,7 @@ var init_init = __esm({
|
|
|
1601
1679
|
`Provider configs: ${result.generated.length} generated, ${result.linked.length} linked`
|
|
1602
1680
|
);
|
|
1603
1681
|
}
|
|
1604
|
-
if (patchGitignore(options.targetRoot,
|
|
1682
|
+
if (patchGitignore(options.targetRoot, providers2)) {
|
|
1605
1683
|
await step("Updated .gitignore (generated shims + .env.local ignored)");
|
|
1606
1684
|
}
|
|
1607
1685
|
const ignored = ignoredSourcePaths(options.targetRoot);
|
|
@@ -1623,35 +1701,6 @@ var init_init = __esm({
|
|
|
1623
1701
|
"Done. Commit .agents/ and quiver.lock; restart your AI tool to load the config."
|
|
1624
1702
|
);
|
|
1625
1703
|
};
|
|
1626
|
-
resolveProviders = async (options) => {
|
|
1627
|
-
if (options.providers) {
|
|
1628
|
-
const invalid = options.providers.filter((p) => !isProvider(p));
|
|
1629
|
-
if (invalid.length) {
|
|
1630
|
-
await error(
|
|
1631
|
-
`Unknown provider(s): ${invalid.join(", ")}. Valid: ${PROVIDERS.join(", ")}.`
|
|
1632
|
-
);
|
|
1633
|
-
process.exitCode = 1;
|
|
1634
|
-
return null;
|
|
1635
|
-
}
|
|
1636
|
-
return options.providers.filter(isProvider);
|
|
1637
|
-
}
|
|
1638
|
-
if (options.all || !process.stdin.isTTY) return [...PROVIDERS];
|
|
1639
|
-
const picked = await selectGrouped({
|
|
1640
|
-
message: "Generate configs for (space toggles, enter confirms)",
|
|
1641
|
-
groups: [
|
|
1642
|
-
{
|
|
1643
|
-
name: "providers",
|
|
1644
|
-
items: [
|
|
1645
|
-
{ value: "claude", label: "Claude Code" },
|
|
1646
|
-
{ value: "opencode", label: "opencode" },
|
|
1647
|
-
{ value: "codex", label: "Codex" }
|
|
1648
|
-
]
|
|
1649
|
-
}
|
|
1650
|
-
],
|
|
1651
|
-
initialValues: [...PROVIDERS]
|
|
1652
|
-
});
|
|
1653
|
-
return picked.length ? picked : [...PROVIDERS];
|
|
1654
|
-
};
|
|
1655
1704
|
}
|
|
1656
1705
|
});
|
|
1657
1706
|
|
|
@@ -1888,6 +1937,7 @@ var init_sync = __esm({
|
|
|
1888
1937
|
init_repo();
|
|
1889
1938
|
init_io();
|
|
1890
1939
|
init_schema();
|
|
1940
|
+
init_resolve2();
|
|
1891
1941
|
init_write();
|
|
1892
1942
|
init_prompts();
|
|
1893
1943
|
init_gitignore();
|
|
@@ -1904,6 +1954,23 @@ var init_sync = __esm({
|
|
|
1904
1954
|
process.exitCode = 1;
|
|
1905
1955
|
return;
|
|
1906
1956
|
}
|
|
1957
|
+
if (options.providers) {
|
|
1958
|
+
const { providers: valid, invalid } = validateProviders(options.providers);
|
|
1959
|
+
if (invalid) {
|
|
1960
|
+
await error(
|
|
1961
|
+
`Unknown provider(s): ${invalid.join(", ")}. Valid: ${PROVIDERS.join(", ")}.`
|
|
1962
|
+
);
|
|
1963
|
+
process.exitCode = 1;
|
|
1964
|
+
return;
|
|
1965
|
+
}
|
|
1966
|
+
if (!valid.length) {
|
|
1967
|
+
await error("At least one provider is required.");
|
|
1968
|
+
process.exitCode = 1;
|
|
1969
|
+
return;
|
|
1970
|
+
}
|
|
1971
|
+
lock.providers = valid;
|
|
1972
|
+
await step(`Providers set to: ${valid.join(", ")}`);
|
|
1973
|
+
}
|
|
1907
1974
|
const { catalog } = loadRepoCatalog(options.targetRoot, lock.catalog.source);
|
|
1908
1975
|
const haveSkills = new Set(catalog.skills.map((s) => s.name));
|
|
1909
1976
|
const haveCommands = new Set(catalog.commands.map((c) => c.name));
|
|
@@ -1936,6 +2003,79 @@ var init_sync = __esm({
|
|
|
1936
2003
|
await success(
|
|
1937
2004
|
`Synced: ${result.generated.length} generated, ${result.linked.length} linked, ${result.removed.length} removed`
|
|
1938
2005
|
);
|
|
2006
|
+
const detail = formatWriteResult(options.targetRoot, result);
|
|
2007
|
+
if (detail.length) block(detail);
|
|
2008
|
+
};
|
|
2009
|
+
}
|
|
2010
|
+
});
|
|
2011
|
+
|
|
2012
|
+
// src/commands/providers.ts
|
|
2013
|
+
var providers_exports = {};
|
|
2014
|
+
__export(providers_exports, {
|
|
2015
|
+
providers: () => providers
|
|
2016
|
+
});
|
|
2017
|
+
var providers;
|
|
2018
|
+
var init_providers = __esm({
|
|
2019
|
+
"src/commands/providers.ts"() {
|
|
2020
|
+
"use strict";
|
|
2021
|
+
init_repo();
|
|
2022
|
+
init_io();
|
|
2023
|
+
init_schema();
|
|
2024
|
+
init_resolve2();
|
|
2025
|
+
init_write();
|
|
2026
|
+
init_prompts();
|
|
2027
|
+
providers = async (options) => {
|
|
2028
|
+
const lock = readLockfile(options.targetRoot);
|
|
2029
|
+
if (!lock) {
|
|
2030
|
+
await error("No quiver.lock found. Run `quiver-cli init` first.");
|
|
2031
|
+
process.exitCode = 1;
|
|
2032
|
+
return;
|
|
2033
|
+
}
|
|
2034
|
+
if (!repoCatalogExists(options.targetRoot)) {
|
|
2035
|
+
await error("No .agents/ directory found. Run `quiver-cli init` first.");
|
|
2036
|
+
process.exitCode = 1;
|
|
2037
|
+
return;
|
|
2038
|
+
}
|
|
2039
|
+
const current = lock.providers?.length ? lock.providers : [...PROVIDERS];
|
|
2040
|
+
const explicit = options.providers ?? (options.positionals[0] ? options.positionals[0].split(",").map((p) => p.trim()).filter(Boolean) : null);
|
|
2041
|
+
let next;
|
|
2042
|
+
if (explicit) {
|
|
2043
|
+
const { providers: valid, invalid } = validateProviders(explicit);
|
|
2044
|
+
if (invalid) {
|
|
2045
|
+
await error(
|
|
2046
|
+
`Unknown provider(s): ${invalid.join(", ")}. Valid: ${PROVIDERS.join(", ")}.`
|
|
2047
|
+
);
|
|
2048
|
+
process.exitCode = 1;
|
|
2049
|
+
return;
|
|
2050
|
+
}
|
|
2051
|
+
next = valid;
|
|
2052
|
+
} else if (process.stdin.isTTY && !options.all) {
|
|
2053
|
+
next = await selectProviders(current);
|
|
2054
|
+
} else {
|
|
2055
|
+
await error(
|
|
2056
|
+
`No providers given. Usage: quiver-cli providers <${PROVIDERS.join(",")}>`
|
|
2057
|
+
);
|
|
2058
|
+
process.exitCode = 1;
|
|
2059
|
+
return;
|
|
2060
|
+
}
|
|
2061
|
+
if (!next.length) {
|
|
2062
|
+
await error("At least one provider is required.");
|
|
2063
|
+
process.exitCode = 1;
|
|
2064
|
+
return;
|
|
2065
|
+
}
|
|
2066
|
+
if (sameProviders(next, current)) {
|
|
2067
|
+
await info(`Providers already set to: ${current.join(", ")}.`);
|
|
2068
|
+
return;
|
|
2069
|
+
}
|
|
2070
|
+
lock.providers = next;
|
|
2071
|
+
writeLockfile(options.targetRoot, lock);
|
|
2072
|
+
const { catalog } = loadRepoCatalog(options.targetRoot, lock.catalog.source);
|
|
2073
|
+
const result = writeProviders(options.targetRoot, catalog, lock);
|
|
2074
|
+
await success(
|
|
2075
|
+
`Providers set to ${next.join(", ")}: ${result.generated.length} generated, ${result.linked.length} linked, ${result.removed.length} removed`
|
|
2076
|
+
);
|
|
2077
|
+
const detail = formatWriteResult(options.targetRoot, result);
|
|
2078
|
+
if (detail.length) block(detail);
|
|
1939
2079
|
};
|
|
1940
2080
|
}
|
|
1941
2081
|
});
|
|
@@ -2230,12 +2370,12 @@ var init_list = __esm({
|
|
|
2230
2370
|
);
|
|
2231
2371
|
}
|
|
2232
2372
|
}
|
|
2233
|
-
const
|
|
2373
|
+
const providers2 = lock.providers?.length ? lock.providers.join(", ") : "claude, opencode, codex";
|
|
2234
2374
|
lines.push(
|
|
2235
2375
|
"",
|
|
2236
2376
|
` ${c.bold(
|
|
2237
2377
|
`${skills.length} skills \xB7 ${commands.length} commands \xB7 ${mcp.length} MCP servers`
|
|
2238
|
-
)} ${c.dim(`providers: ${
|
|
2378
|
+
)} ${c.dim(`providers: ${providers2}`)}`
|
|
2239
2379
|
);
|
|
2240
2380
|
if (missingTools) {
|
|
2241
2381
|
lines.push(` ${c.dim("run 'quiver-cli check' to populate tool counts")}`);
|
|
@@ -3221,6 +3361,7 @@ Commands:
|
|
|
3221
3361
|
add <id> Add a single catalog entry (skill:<name>, command:<name>, mcp:<name>)
|
|
3222
3362
|
remove <id> Remove a single entry; keep lockfile + configs consistent
|
|
3223
3363
|
sync Regenerate provider configs from .agents/ (warns on drift)
|
|
3364
|
+
providers [a,b] Change which tools get configs (claude, opencode, codex)
|
|
3224
3365
|
update [id] Pull newer catalog content into .agents/ (all or one entry)
|
|
3225
3366
|
list Show installed entries (skills, commands, MCP tool counts)
|
|
3226
3367
|
status Diff the lockfile against what is actually in the repo
|
|
@@ -3239,7 +3380,7 @@ Options:
|
|
|
3239
3380
|
--json Machine-readable output (status/check/upstream/list)
|
|
3240
3381
|
-V, --verbose Show full tool lists and description diffs (check)
|
|
3241
3382
|
--accept Record the current MCP tool snapshots as the new baseline (check)
|
|
3242
|
-
--providers=a,b Generate configs only for these tools (
|
|
3383
|
+
--providers=a,b Generate configs only for these tools (init, sync, providers)
|
|
3243
3384
|
--catalog=<source> Catalog source for init (e.g. github:owner/repo[/path][#ref])
|
|
3244
3385
|
--introspect-stdio Allow introspecting stdio MCP servers (runs foreign code)
|
|
3245
3386
|
-v, --version Show the quiver-cli version
|
|
@@ -3249,7 +3390,7 @@ var parse2 = (argv) => {
|
|
|
3249
3390
|
const flags = new Set(rest.filter((a) => a.startsWith("-")));
|
|
3250
3391
|
const positionals = rest.filter((a) => !a.startsWith("-"));
|
|
3251
3392
|
const providersFlag = rest.find((a) => a.startsWith("--providers="));
|
|
3252
|
-
const
|
|
3393
|
+
const providers2 = providersFlag ? providersFlag.slice("--providers=".length).split(",").map((p) => p.trim()).filter(Boolean) : null;
|
|
3253
3394
|
const catalogFlag = rest.find((a) => a.startsWith("--catalog="));
|
|
3254
3395
|
const catalog = catalogFlag ? catalogFlag.slice("--catalog=".length).trim() || null : null;
|
|
3255
3396
|
return {
|
|
@@ -3262,7 +3403,7 @@ var parse2 = (argv) => {
|
|
|
3262
3403
|
verbose: flags.has("--verbose") || flags.has("-V"),
|
|
3263
3404
|
accept: flags.has("--accept"),
|
|
3264
3405
|
introspectStdio: flags.has("--introspect-stdio"),
|
|
3265
|
-
providers,
|
|
3406
|
+
providers: providers2,
|
|
3266
3407
|
catalog,
|
|
3267
3408
|
positionals
|
|
3268
3409
|
}
|
|
@@ -3301,6 +3442,11 @@ var run = async () => {
|
|
|
3301
3442
|
await sync2(options);
|
|
3302
3443
|
break;
|
|
3303
3444
|
}
|
|
3445
|
+
case "providers": {
|
|
3446
|
+
const { providers: providers2 } = await Promise.resolve().then(() => (init_providers(), providers_exports));
|
|
3447
|
+
await providers2(options);
|
|
3448
|
+
break;
|
|
3449
|
+
}
|
|
3304
3450
|
case "update": {
|
|
3305
3451
|
const { update: update2 } = await Promise.resolve().then(() => (init_update(), update_exports));
|
|
3306
3452
|
await update2(options);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quiver-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Compose a selected subset of skills, commands & MCP servers from a central catalog into any repo as native configs for opencode, Claude Code and Codex - with lockfile-based drift awareness.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|