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.
Files changed (2) hide show
  1. package/dist/cli.js +190 -44
  2. 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(["init", "add", "remove", "rm"]);
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, providers) => {
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 = providers?.length ? providers : [...PROVIDERS];
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, resolveProviders;
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 providers = await resolveProviders(options);
1566
- if (providers === null) return;
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 = 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, providers)) {
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 providers = lock.providers?.length ? lock.providers.join(", ") : "claude, opencode, codex";
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: ${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 (claude, opencode, codex)
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 providers = providersFlag ? providersFlag.slice("--providers=".length).split(",").map((p) => p.trim()).filter(Boolean) : null;
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.7.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": {