quiver-cli 0.6.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 (97) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cli.js +314 -86
  3. package/package.json +1 -1
  4. package/template/.agents/skills/agent-browser/SKILL.md +4 -9
  5. package/template/.agents/skills/apps/skybridge/SKILL.md +1 -1
  6. package/template/.agents/skills/code/improve/SKILL.md +9 -5
  7. package/template/.agents/skills/code/improve/references/audit-playbook.md +10 -10
  8. package/template/.agents/skills/code/improve/references/closing-the-loop.md +4 -3
  9. package/template/.agents/skills/code/improve/references/plan-template.md +5 -0
  10. package/template/.agents/skills/design/impeccable/SKILL.md +14 -24
  11. package/template/.agents/skills/design/impeccable/reference/animate.md +1 -1
  12. package/template/.agents/skills/design/impeccable/reference/bolder.md +1 -1
  13. package/template/.agents/skills/design/impeccable/reference/brand.md +2 -2
  14. package/template/.agents/skills/design/impeccable/reference/colorize.md +1 -1
  15. package/template/.agents/skills/design/impeccable/reference/critique.md +6 -6
  16. package/template/.agents/skills/design/impeccable/reference/delight.md +1 -1
  17. package/template/.agents/skills/design/impeccable/reference/distill.md +1 -1
  18. package/template/.agents/skills/design/impeccable/reference/document.md +1 -1
  19. package/template/.agents/skills/design/impeccable/reference/extract.md +1 -1
  20. package/template/.agents/skills/design/impeccable/reference/hooks.md +90 -0
  21. package/template/.agents/skills/design/impeccable/reference/init.md +5 -5
  22. package/template/.agents/skills/design/impeccable/reference/live.md +16 -16
  23. package/template/.agents/skills/design/impeccable/reference/overdrive.md +1 -1
  24. package/template/.agents/skills/design/impeccable/reference/polish.md +2 -2
  25. package/template/.agents/skills/design/impeccable/reference/quieter.md +1 -1
  26. package/template/.agents/skills/design/impeccable/reference/shape.md +2 -2
  27. package/template/.agents/skills/design/impeccable/scripts/context-signals.mjs +1 -1
  28. package/template/.agents/skills/design/impeccable/scripts/context.mjs +724 -33
  29. package/template/.agents/skills/design/impeccable/scripts/critique-storage.mjs +1 -1
  30. package/template/.agents/skills/design/impeccable/scripts/detector/browser/injected/index.mjs +204 -0
  31. package/template/.agents/skills/design/impeccable/scripts/detector/cli/main.mjs +57 -11
  32. package/template/.agents/skills/design/impeccable/scripts/detector/design-system.mjs +750 -0
  33. package/template/.agents/skills/design/impeccable/scripts/detector/detect-antipatterns-browser.js +633 -46
  34. package/template/.agents/skills/design/impeccable/scripts/detector/detect-antipatterns.mjs +7 -0
  35. package/template/.agents/skills/design/impeccable/scripts/detector/engines/browser/detect-url.mjs +29 -4
  36. package/template/.agents/skills/design/impeccable/scripts/detector/engines/regex/detect-text.mjs +43 -10
  37. package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/css-cascade.mjs +29 -0
  38. package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/detect-html.mjs +27 -1
  39. package/template/.agents/skills/design/impeccable/scripts/detector/node/file-system.mjs +1 -1
  40. package/template/.agents/skills/design/impeccable/scripts/detector/registry/antipatterns.mjs +29 -0
  41. package/template/.agents/skills/design/impeccable/scripts/detector/rules/checks.mjs +401 -46
  42. package/template/.agents/skills/design/impeccable/scripts/detector/shared/inline-ignores.mjs +148 -0
  43. package/template/.agents/skills/design/impeccable/scripts/hook-admin.mjs +661 -0
  44. package/template/.agents/skills/design/impeccable/scripts/hook-before-edit.mjs +476 -0
  45. package/template/.agents/skills/design/impeccable/scripts/hook-lib.mjs +1632 -0
  46. package/template/.agents/skills/design/impeccable/scripts/hook.mjs +61 -0
  47. package/template/.agents/skills/design/impeccable/scripts/{design-parser.mjs → lib/design-parser.mjs} +8 -1
  48. package/template/.agents/skills/design/impeccable/scripts/lib/impeccable-config.mjs +638 -0
  49. package/template/.agents/skills/design/impeccable/scripts/lib/impeccable-paths.mjs +128 -0
  50. package/template/.agents/skills/design/impeccable/scripts/lib/target-args.mjs +42 -0
  51. package/template/.agents/skills/design/impeccable/scripts/live/browser-script-parts.mjs +49 -0
  52. package/template/.agents/skills/design/impeccable/scripts/{live-event-validation.mjs → live/event-validation.mjs} +6 -5
  53. package/template/.agents/skills/design/impeccable/scripts/live/manual-apply.mjs +939 -0
  54. package/template/.agents/skills/design/impeccable/scripts/live/manual-edit-routes.mjs +357 -0
  55. package/template/.agents/skills/design/impeccable/scripts/{live-manual-edits-buffer.mjs → live/manual-edits-buffer.mjs} +1 -1
  56. package/template/.agents/skills/design/impeccable/scripts/{live-session-store.mjs → live/session-store.mjs} +1 -1
  57. package/template/.agents/skills/design/impeccable/scripts/{live-ui-core.mjs → live/ui-core.mjs} +2 -1
  58. package/template/.agents/skills/design/impeccable/scripts/live/vocabulary.mjs +36 -0
  59. package/template/.agents/skills/design/impeccable/scripts/live-accept.mjs +3 -3
  60. package/template/.agents/skills/design/impeccable/scripts/live-browser-dom.js +146 -0
  61. package/template/.agents/skills/design/impeccable/scripts/live-browser.js +1456 -599
  62. package/template/.agents/skills/design/impeccable/scripts/live-commit-manual-edits.mjs +2 -2
  63. package/template/.agents/skills/design/impeccable/scripts/live-complete.mjs +2 -2
  64. package/template/.agents/skills/design/impeccable/scripts/live-discard-manual-edits.mjs +1 -1
  65. package/template/.agents/skills/design/impeccable/scripts/live-inject.mjs +35 -9
  66. package/template/.agents/skills/design/impeccable/scripts/live-insert.mjs +2 -2
  67. package/template/.agents/skills/design/impeccable/scripts/live-manual-edit-evidence.mjs +2 -2
  68. package/template/.agents/skills/design/impeccable/scripts/live-poll.mjs +18 -13
  69. package/template/.agents/skills/design/impeccable/scripts/live-resume.mjs +1 -1
  70. package/template/.agents/skills/design/impeccable/scripts/live-server.mjs +77 -1264
  71. package/template/.agents/skills/design/impeccable/scripts/live-status.mjs +2 -2
  72. package/template/.agents/skills/design/impeccable/scripts/live-target.mjs +30 -0
  73. package/template/.agents/skills/design/impeccable/scripts/live-wrap.mjs +4 -4
  74. package/template/.agents/skills/design/impeccable/scripts/live.mjs +73 -22
  75. package/template/.agents/skills/writing/humanizer/SKILL.md +621 -0
  76. package/template/.agents/upstreams.json +17 -8
  77. package/template/.agents/skills/apps/skybridge/references/architecture.md +0 -175
  78. package/template/.agents/skills/apps/skybridge/references/copy-template.md +0 -24
  79. package/template/.agents/skills/apps/skybridge/references/csp.md +0 -33
  80. package/template/.agents/skills/apps/skybridge/references/deploy.md +0 -33
  81. package/template/.agents/skills/apps/skybridge/references/discover.md +0 -84
  82. package/template/.agents/skills/apps/skybridge/references/download-file.md +0 -77
  83. package/template/.agents/skills/apps/skybridge/references/fetch-and-render-data.md +0 -151
  84. package/template/.agents/skills/apps/skybridge/references/oauth.md +0 -115
  85. package/template/.agents/skills/apps/skybridge/references/open-external-links.md +0 -71
  86. package/template/.agents/skills/apps/skybridge/references/prompt-llm.md +0 -20
  87. package/template/.agents/skills/apps/skybridge/references/publish.md +0 -19
  88. package/template/.agents/skills/apps/skybridge/references/run-locally.md +0 -51
  89. package/template/.agents/skills/apps/skybridge/references/state-and-context.md +0 -151
  90. package/template/.agents/skills/apps/skybridge/references/ui-guidelines.md +0 -205
  91. package/template/.agents/skills/design/impeccable/scripts/cleanup-deprecated.mjs +0 -284
  92. package/template/.agents/skills/design/impeccable/scripts/impeccable-paths.mjs +0 -126
  93. /package/template/.agents/skills/design/impeccable/scripts/{is-generated.mjs → lib/is-generated.mjs} +0 -0
  94. /package/template/.agents/skills/design/impeccable/scripts/{live-completion.mjs → live/completion.mjs} +0 -0
  95. /package/template/.agents/skills/design/impeccable/scripts/{live-insert-ui.mjs → live/insert-ui.mjs} +0 -0
  96. /package/template/.agents/skills/design/impeccable/scripts/{live-svelte-component.mjs → live/svelte-component.mjs} +0 -0
  97. /package/template/.agents/skills/design/impeccable/scripts/{live-sveltekit-adapter.mjs → live/sveltekit-adapter.mjs} +0 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 nbialk
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/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
  });
@@ -2112,7 +2252,7 @@ var list_exports = {};
2112
2252
  __export(list_exports, {
2113
2253
  list: () => list
2114
2254
  });
2115
- var truncate, list;
2255
+ var truncate, padCell, list;
2116
2256
  var init_list = __esm({
2117
2257
  "src/commands/list.ts"() {
2118
2258
  "use strict";
@@ -2120,10 +2260,12 @@ var init_list = __esm({
2120
2260
  init_io();
2121
2261
  init_schema();
2122
2262
  init_prompts();
2123
- truncate = (s, max = 60) => {
2263
+ truncate = (s, max) => {
2124
2264
  const flat = s.replace(/\s+/g, " ").trim();
2265
+ if (max < 1) return "";
2125
2266
  return flat.length > max ? flat.slice(0, max - 1) + "\u2026" : flat;
2126
2267
  };
2268
+ padCell = (text, width, color) => color(text.padEnd(width));
2127
2269
  list = async (options) => {
2128
2270
  const lock = readLockfile(options.targetRoot);
2129
2271
  if (!lock) {
@@ -2180,17 +2322,22 @@ var init_list = __esm({
2180
2322
  return;
2181
2323
  }
2182
2324
  const c = palette();
2325
+ const term = process.stdout.columns ?? 80;
2183
2326
  const lines = [""];
2184
- const width = Math.max(
2185
- 0,
2186
- ...[...skills, ...commands, ...mcp].map((e) => e.name.length + 1)
2187
- );
2188
2327
  if (skills.length) {
2328
+ const nameW = Math.max(...skills.map((e) => e.name.length));
2329
+ const verW = Math.max(
2330
+ 0,
2331
+ ...skills.map(
2332
+ (e) => e.entry.frontmatter.version ? e.entry.frontmatter.version.length + 1 : 0
2333
+ )
2334
+ );
2335
+ const descMax = term - (4 + nameW + 1 + verW + 1) - 1;
2189
2336
  lines.push(` ${c.bold("skills")}`);
2190
2337
  for (const { name, entry } of skills) {
2191
- const version = entry.frontmatter.version ? c.cyan(`v${entry.frontmatter.version} `) : "";
2192
- const desc = entry.frontmatter.description ? c.dim(truncate(entry.frontmatter.description)) : "";
2193
- lines.push(` ${name.padEnd(width)} ${version}${desc}`);
2338
+ const ver = entry.frontmatter.version ? padCell(`v${entry.frontmatter.version}`, verW, c.cyan) : " ".repeat(verW);
2339
+ const desc = entry.frontmatter.description ? c.dim(truncate(entry.frontmatter.description, descMax)) : "";
2340
+ lines.push(` ${name.padEnd(nameW)} ${ver} ${desc}`.trimEnd());
2194
2341
  }
2195
2342
  }
2196
2343
  if (commands.length) {
@@ -2199,24 +2346,41 @@ var init_list = __esm({
2199
2346
  lines.push(` /${name}`);
2200
2347
  }
2201
2348
  }
2349
+ let missingTools = false;
2202
2350
  if (mcp.length) {
2351
+ const nameW = Math.max(...mcp.map((e) => e.name.length));
2352
+ const toolW = Math.max(
2353
+ ...mcp.map((e) => {
2354
+ const n = e.entry.tools ? Object.keys(e.entry.tools).length : null;
2355
+ return `${n ?? "?"} tools`.length;
2356
+ })
2357
+ );
2203
2358
  lines.push("", ` ${c.bold("mcp servers")}`);
2204
2359
  for (const { name, entry } of mcp) {
2205
- const tools = entry.tools ? c.green(`${Object.keys(entry.tools).length} tools`) : c.dim("tools: ? (run check)");
2360
+ const count = entry.tools ? Object.keys(entry.tools).length : null;
2361
+ if (count === null) missingTools = true;
2362
+ const tools = padCell(
2363
+ `${count ?? "?"} tools`,
2364
+ toolW,
2365
+ count === null ? c.dim : c.green
2366
+ );
2206
2367
  const detail = serverDetail2.get(name);
2207
2368
  lines.push(
2208
- ` ${name.padEnd(width)} ${entry.transport.padEnd(5)} ${tools}` + (detail ? ` ${c.dim(detail)}` : "")
2369
+ ` ${name.padEnd(nameW)} ${entry.transport.padEnd(5)} ${tools}` + (detail ? ` ${c.dim(detail)}` : "")
2209
2370
  );
2210
2371
  }
2211
2372
  }
2212
- const providers = lock.providers?.length ? lock.providers.join(", ") : "claude, opencode, codex";
2373
+ const providers2 = lock.providers?.length ? lock.providers.join(", ") : "claude, opencode, codex";
2213
2374
  lines.push(
2214
2375
  "",
2215
2376
  ` ${c.bold(
2216
2377
  `${skills.length} skills \xB7 ${commands.length} commands \xB7 ${mcp.length} MCP servers`
2217
- )} ${c.dim(`providers: ${providers}`)}`,
2218
- ""
2378
+ )} ${c.dim(`providers: ${providers2}`)}`
2219
2379
  );
2380
+ if (missingTools) {
2381
+ lines.push(` ${c.dim("run 'quiver-cli check' to populate tool counts")}`);
2382
+ }
2383
+ lines.push("");
2220
2384
  block(lines);
2221
2385
  };
2222
2386
  }
@@ -2436,7 +2600,7 @@ __export(check_exports, {
2436
2600
  check: () => check,
2437
2601
  summarize: () => summarize
2438
2602
  });
2439
- var check, report2, summarize, truncate2, fail;
2603
+ var check, report2, driftLines, list2, recommend, summarize, truncate2, fail;
2440
2604
  var init_check = __esm({
2441
2605
  "src/commands/check.ts"() {
2442
2606
  "use strict";
@@ -2503,6 +2667,11 @@ var init_check = __esm({
2503
2667
  const diff = diffSnapshots(mcpEntry.tools, current);
2504
2668
  if (isEmptyDiff(diff)) {
2505
2669
  mcpReports.push({ id, status: "ok" });
2670
+ } else if (options.accept) {
2671
+ mcpEntry.tools = current;
2672
+ mcpEntry.toolsFetchedAt = (/* @__PURE__ */ new Date()).toISOString();
2673
+ lockChanged = true;
2674
+ mcpReports.push({ id, status: "accepted", diff });
2506
2675
  } else {
2507
2676
  mcpReports.push({ id, status: "drift", diff });
2508
2677
  }
@@ -2520,47 +2689,96 @@ var init_check = __esm({
2520
2689
  if (hasDrift) process.exitCode = 1;
2521
2690
  return;
2522
2691
  }
2523
- await report2(skillDrift, mcpReports, checked);
2692
+ await report2(skillDrift, mcpReports, checked, options);
2524
2693
  if (hasDrift) process.exitCode = 1;
2525
2694
  };
2526
- report2 = async (skillDrift, mcpReports, checked) => {
2695
+ report2 = async (skillDrift, mcpReports, checked, options) => {
2527
2696
  if (skillDrift.length) {
2528
2697
  await warn(
2529
2698
  `Skill/command content changed since lockfile:
2530
2699
  - ${skillDrift.map((s) => s.id).join("\n - ")}`
2531
2700
  );
2532
2701
  }
2533
- for (const r of mcpReports) {
2534
- if (r.status === "ok") {
2535
- await success(`${r.id}: no tool changes`);
2536
- } else if (r.status === "baseline") {
2537
- await info(`${r.id}: recorded tool baseline`);
2538
- } else if (r.status === "skipped") {
2539
- await info(`${r.id}: skipped (${r.reason})`);
2540
- } else if (r.status === "drift" && r.diff) {
2541
- const lines = [];
2542
- if (r.diff.added.length) lines.push(`new tools: ${r.diff.added.join(", ")}`);
2543
- if (r.diff.removed.length)
2544
- lines.push(`removed tools: ${r.diff.removed.join(", ")}`);
2545
- if (r.diff.schemaChanged.length)
2546
- lines.push(`schema changed: ${r.diff.schemaChanged.join(", ")}`);
2547
- for (const dc of r.diff.descriptionChanged) {
2702
+ const skipped = mcpReports.filter((r) => r.status === "skipped");
2703
+ if (skipped.length) {
2704
+ const names = skipped.map((r) => parseEntryId(r.id)?.name ?? r.id);
2705
+ await info(
2706
+ `skipped ${skipped.length} server${skipped.length === 1 ? "" : "s"}: ${names.join(", ")}` + (options.verbose ? "\n - " + skipped.map((r) => `${r.id}: ${r.reason}`).join("\n - ") : "")
2707
+ );
2708
+ }
2709
+ const baselined = mcpReports.filter((r) => r.status === "baseline");
2710
+ if (baselined.length) {
2711
+ await info(
2712
+ `recorded tool baseline: ${baselined.map((r) => r.id).join(", ")}`
2713
+ );
2714
+ }
2715
+ const accepted = mcpReports.filter((r) => r.status === "accepted");
2716
+ for (const r of accepted) {
2717
+ await success(`${r.id}: accepted new tool baseline`);
2718
+ }
2719
+ const drifted = mcpReports.filter((r) => r.status === "drift");
2720
+ for (const r of drifted) {
2721
+ if (!r.diff) continue;
2722
+ await warn(`${r.id}: tool drift
2723
+ - ${driftLines(r.diff, options.verbose).join("\n - ")}`);
2724
+ }
2725
+ const summary = summarize(checked);
2726
+ const hasDrift = skillDrift.length > 0 || drifted.length > 0;
2727
+ if (!hasDrift) {
2728
+ await success(`check passed: ${summary}, no drift detected.`);
2729
+ } else {
2730
+ await info(`checked ${summary}, drift detected.`);
2731
+ await recommend(skillDrift, drifted);
2732
+ }
2733
+ };
2734
+ driftLines = (diff, verbose) => {
2735
+ const lines = [];
2736
+ if (diff.added.length) lines.push(`new tools: ${list2(diff.added, verbose)}`);
2737
+ if (diff.removed.length)
2738
+ lines.push(`removed tools: ${list2(diff.removed, verbose)}`);
2739
+ if (diff.schemaChanged.length)
2740
+ lines.push(`schema changed: ${list2(diff.schemaChanged, verbose)}`);
2741
+ if (diff.descriptionChanged.length) {
2742
+ const dc = diff.descriptionChanged;
2743
+ lines.push(
2744
+ `description changed (possible poisoning): ${list2(dc.map((d) => d.name), verbose)}`
2745
+ );
2746
+ if (verbose) {
2747
+ for (const d of dc) {
2548
2748
  lines.push(
2549
- `DESCRIPTION CHANGED (possible poisoning) "${dc.name}":
2550
- before: ${truncate2(dc.before)}
2551
- after: ${truncate2(dc.after)}`
2749
+ ` "${d.name}":
2750
+ before: ${truncate2(d.before)}
2751
+ after: ${truncate2(d.after)}`
2552
2752
  );
2553
2753
  }
2554
- await warn(`${r.id}: tool drift
2555
- - ${lines.join("\n - ")}`);
2556
2754
  }
2557
2755
  }
2558
- const summary = summarize(checked);
2559
- if (!skillDrift.length && !mcpReports.some((r) => r.status === "drift")) {
2560
- await success(`check passed: ${summary}, no drift detected.`);
2561
- } else {
2562
- await info(`checked ${summary}.`);
2756
+ return lines;
2757
+ };
2758
+ list2 = (names, verbose, sample = 3) => {
2759
+ if (verbose || names.length <= sample) {
2760
+ return `${names.length} ${names.length === 1 ? "tool" : "tools"} (${names.join(", ")})`;
2761
+ }
2762
+ const rest = names.length - sample;
2763
+ return `${names.length} tools (${names.slice(0, sample).join(", ")}, \u2026 +${rest} more)`;
2764
+ };
2765
+ recommend = async (skillDrift, drifted) => {
2766
+ const c = palette();
2767
+ const lines = ["to update the lockfile baseline:"];
2768
+ if (drifted.length) {
2769
+ lines.push(` ${c.cyan("quiver-cli check --accept")} accept the new MCP tool snapshots`);
2563
2770
  }
2771
+ if (skillDrift.length) {
2772
+ const ids = skillDrift.map((s) => s.id);
2773
+ const one = ids.length === 1 ? ids[0] : "<id>";
2774
+ lines.push(
2775
+ ` ${c.cyan(`quiver-cli update ${one}`)} pull catalog content for the changed skill/command`
2776
+ );
2777
+ if (ids.length > 1) {
2778
+ lines.push(` ${c.dim(`changed: ${ids.join(", ")}`)}`);
2779
+ }
2780
+ }
2781
+ block(["", ...lines]);
2564
2782
  };
2565
2783
  summarize = (c) => {
2566
2784
  const plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
@@ -3143,6 +3361,7 @@ Commands:
3143
3361
  add <id> Add a single catalog entry (skill:<name>, command:<name>, mcp:<name>)
3144
3362
  remove <id> Remove a single entry; keep lockfile + configs consistent
3145
3363
  sync Regenerate provider configs from .agents/ (warns on drift)
3364
+ providers [a,b] Change which tools get configs (claude, opencode, codex)
3146
3365
  update [id] Pull newer catalog content into .agents/ (all or one entry)
3147
3366
  list Show installed entries (skills, commands, MCP tool counts)
3148
3367
  status Diff the lockfile against what is actually in the repo
@@ -3159,7 +3378,9 @@ Options:
3159
3378
  -f, --force Overwrite existing files
3160
3379
  --all, -y Keep everything without prompting (non-interactive)
3161
3380
  --json Machine-readable output (status/check/upstream/list)
3162
- --providers=a,b Generate configs only for these tools (claude, opencode, codex)
3381
+ -V, --verbose Show full tool lists and description diffs (check)
3382
+ --accept Record the current MCP tool snapshots as the new baseline (check)
3383
+ --providers=a,b Generate configs only for these tools (init, sync, providers)
3163
3384
  --catalog=<source> Catalog source for init (e.g. github:owner/repo[/path][#ref])
3164
3385
  --introspect-stdio Allow introspecting stdio MCP servers (runs foreign code)
3165
3386
  -v, --version Show the quiver-cli version
@@ -3169,7 +3390,7 @@ var parse2 = (argv) => {
3169
3390
  const flags = new Set(rest.filter((a) => a.startsWith("-")));
3170
3391
  const positionals = rest.filter((a) => !a.startsWith("-"));
3171
3392
  const providersFlag = rest.find((a) => a.startsWith("--providers="));
3172
- 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;
3173
3394
  const catalogFlag = rest.find((a) => a.startsWith("--catalog="));
3174
3395
  const catalog = catalogFlag ? catalogFlag.slice("--catalog=".length).trim() || null : null;
3175
3396
  return {
@@ -3179,8 +3400,10 @@ var parse2 = (argv) => {
3179
3400
  force: flags.has("--force") || flags.has("-f"),
3180
3401
  all: flags.has("--all") || flags.has("--yes") || flags.has("-y"),
3181
3402
  json: flags.has("--json"),
3403
+ verbose: flags.has("--verbose") || flags.has("-V"),
3404
+ accept: flags.has("--accept"),
3182
3405
  introspectStdio: flags.has("--introspect-stdio"),
3183
- providers,
3406
+ providers: providers2,
3184
3407
  catalog,
3185
3408
  positionals
3186
3409
  }
@@ -3219,6 +3442,11 @@ var run = async () => {
3219
3442
  await sync2(options);
3220
3443
  break;
3221
3444
  }
3445
+ case "providers": {
3446
+ const { providers: providers2 } = await Promise.resolve().then(() => (init_providers(), providers_exports));
3447
+ await providers2(options);
3448
+ break;
3449
+ }
3222
3450
  case "update": {
3223
3451
  const { update: update2 } = await Promise.resolve().then(() => (init_update(), update_exports));
3224
3452
  await update2(options);
@@ -3226,8 +3454,8 @@ var run = async () => {
3226
3454
  }
3227
3455
  case "list":
3228
3456
  case "ls": {
3229
- const { list: list2 } = await Promise.resolve().then(() => (init_list(), list_exports));
3230
- await list2(options);
3457
+ const { list: list3 } = await Promise.resolve().then(() => (init_list(), list_exports));
3458
+ await list3(options);
3231
3459
  break;
3232
3460
  }
3233
3461
  case "status": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quiver-cli",
3
- "version": "0.6.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": {