quiver-cli 0.8.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -45
- package/bin/quiver-cli.mjs +3 -1
- package/dist/cli.js +546 -458
- package/package.json +2 -2
- package/template/.agents/AGENTS.md +4 -3
- package/template/.agents/config.json +7 -0
- package/template/.agents/plugins/opencode/rtk.ts +34 -0
- package/template/.agents/skills/agent-browser/SKILL.md +1 -0
- package/template/.agents/skills/apps/skybridge/SKILL.md +4 -0
- package/template/.agents/skills/design/impeccable/SKILL.md +36 -118
- package/template/.agents/skills/design/impeccable/reference/adapt.md +1 -0
- package/template/.agents/skills/design/impeccable/reference/adapt.native.md +58 -0
- package/template/.agents/skills/design/impeccable/reference/android.md +40 -0
- package/template/.agents/skills/design/impeccable/reference/animate.md +73 -188
- package/template/.agents/skills/design/impeccable/reference/audit.md +12 -9
- package/template/.agents/skills/design/impeccable/reference/audit.native.md +139 -0
- package/template/.agents/skills/design/impeccable/reference/bolder.md +19 -101
- package/template/.agents/skills/design/impeccable/reference/clarify.md +59 -253
- package/template/.agents/skills/design/impeccable/reference/colorize.md +51 -222
- package/template/.agents/skills/design/impeccable/reference/craft-floor.md +42 -0
- package/template/.agents/skills/design/impeccable/reference/craft.md +3 -121
- package/template/.agents/skills/design/impeccable/reference/critique.md +44 -23
- package/template/.agents/skills/design/impeccable/reference/degraded/asset-producer.md +90 -0
- package/template/.agents/skills/design/impeccable/reference/degraded/documenter.md +24 -0
- package/template/.agents/skills/design/impeccable/reference/degraded/finish-reviewer.md +37 -0
- package/template/.agents/skills/design/impeccable/reference/degraded/manual-edit-applier.md +92 -0
- package/template/.agents/skills/design/impeccable/reference/delight.md +47 -279
- package/template/.agents/skills/design/impeccable/reference/distill.md +2 -2
- package/template/.agents/skills/design/impeccable/reference/doctor.md +53 -0
- package/template/.agents/skills/design/impeccable/reference/document.md +60 -73
- package/template/.agents/skills/design/impeccable/reference/harden.md +1 -12
- package/template/.agents/skills/design/impeccable/reference/hooks.md +20 -5
- package/template/.agents/skills/design/impeccable/reference/init.md +72 -119
- package/template/.agents/skills/design/impeccable/reference/ios.md +45 -0
- package/template/.agents/skills/design/impeccable/reference/layout.md +54 -131
- package/template/.agents/skills/design/impeccable/reference/live-setup.md +102 -0
- package/template/.agents/skills/design/impeccable/reference/live.md +116 -511
- package/template/.agents/skills/design/impeccable/reference/new-work.md +105 -0
- package/template/.agents/skills/design/impeccable/reference/{product.md → operate.md} +6 -5
- package/template/.agents/skills/design/impeccable/reference/optimize.md +4 -4
- package/template/.agents/skills/design/impeccable/reference/overdrive.md +1 -4
- package/template/.agents/skills/design/impeccable/reference/polish.md +68 -212
- package/template/.agents/skills/design/impeccable/reference/quieter.md +3 -3
- package/template/.agents/skills/design/impeccable/reference/routing.md +18 -0
- package/template/.agents/skills/design/impeccable/reference/shape.md +38 -144
- package/template/.agents/skills/design/impeccable/reference/typeset.md +51 -250
- package/template/.agents/skills/design/impeccable/reference/visualize.md +47 -0
- package/template/.agents/skills/design/impeccable/scripts/command-metadata.json +1 -1
- package/template/.agents/skills/design/impeccable/scripts/concept-seed.mjs +558 -0
- package/template/.agents/skills/design/impeccable/scripts/context-signals.mjs +119 -10
- package/template/.agents/skills/design/impeccable/scripts/context.mjs +534 -45
- package/template/.agents/skills/design/impeccable/scripts/critique-storage.mjs +18 -47
- package/template/.agents/skills/design/impeccable/scripts/detector/browser/injected/index.mjs +96 -10
- package/template/.agents/skills/design/impeccable/scripts/detector/cli/main.mjs +174 -26
- package/template/.agents/skills/design/impeccable/scripts/detector/design-system.mjs +233 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/detect-antipatterns-browser.js +3348 -203
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/browser/detect-url.mjs +102 -7
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/regex/detect-text.mjs +297 -97
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/css-cascade.mjs +187 -16
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/detect-html.mjs +41 -11
- package/template/.agents/skills/design/impeccable/scripts/detector/findings.mjs +7 -1
- package/template/.agents/skills/design/impeccable/scripts/detector/node/file-system.mjs +16 -2
- package/template/.agents/skills/design/impeccable/scripts/detector/registry/antipatterns.mjs +207 -38
- package/template/.agents/skills/design/impeccable/scripts/detector/rules/checks.mjs +3082 -173
- package/template/.agents/skills/design/impeccable/scripts/detector/shared/constants.mjs +11 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/shared/fonts.mjs +30 -0
- package/template/.agents/skills/design/impeccable/scripts/doctor.mjs +336 -0
- package/template/.agents/skills/design/impeccable/scripts/embed-prompt.mjs +133 -0
- package/template/.agents/skills/design/impeccable/scripts/generate-image.mjs +240 -0
- package/template/.agents/skills/design/impeccable/scripts/hook-admin.mjs +98 -18
- package/template/.agents/skills/design/impeccable/scripts/hook-before-edit.mjs +46 -6
- package/template/.agents/skills/design/impeccable/scripts/hook-lib.mjs +518 -50
- package/template/.agents/skills/design/impeccable/scripts/hook.mjs +25 -8
- package/template/.agents/skills/design/impeccable/scripts/lib/artifact-schema.mjs +93 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/composition-catalog.mjs +200 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/concept-catalog.mjs +357 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/impeccable-config.mjs +27 -7
- package/template/.agents/skills/design/impeccable/scripts/lib/impeccable-paths.mjs +17 -8
- package/template/.agents/skills/design/impeccable/scripts/lib/provider.mjs +5 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/roll-selection.mjs +362 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/staleness-deep.mjs +457 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/staleness-notice.mjs +169 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/staleness.mjs +457 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/surface-briefs.mjs +151 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/target-slug.mjs +33 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/template-extensions.mjs +146 -0
- package/template/.agents/skills/design/impeccable/scripts/live/accept-css.mjs +617 -0
- package/template/.agents/skills/design/impeccable/scripts/live/accept-verify.mjs +60 -0
- package/template/.agents/skills/design/impeccable/scripts/live/browser-script-parts.mjs +7 -1
- package/template/.agents/skills/design/impeccable/scripts/live/completion.mjs +10 -1
- package/template/.agents/skills/design/impeccable/scripts/live/event-validation.mjs +67 -5
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/astro.mjs +47 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/detect-utils.mjs +73 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/index.mjs +143 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/journal.mjs +197 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/nextjs.mjs +49 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/nuxt.mjs +161 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/script-src.mjs +17 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/static-html.mjs +26 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/sveltekit.mjs +71 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/tag-strategy.mjs +247 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/tanstack-start.mjs +70 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/vite-generic.mjs +42 -0
- package/template/.agents/skills/design/impeccable/scripts/live/generation-preflight.mjs +149 -0
- package/template/.agents/skills/design/impeccable/scripts/live/instructions.mjs +142 -0
- package/template/.agents/skills/design/impeccable/scripts/live/poll-lanes.mjs +14 -0
- package/template/.agents/skills/design/impeccable/scripts/live/roots.mjs +508 -0
- package/template/.agents/skills/design/impeccable/scripts/live/session-store.mjs +324 -50
- package/template/.agents/skills/design/impeccable/scripts/live/source-lock.mjs +105 -0
- package/template/.agents/skills/design/impeccable/scripts/live/source-search.mjs +105 -0
- package/template/.agents/skills/design/impeccable/scripts/live/svelte-ast.mjs +961 -0
- package/template/.agents/skills/design/impeccable/scripts/live/svelte-component.mjs +588 -72
- package/template/.agents/skills/design/impeccable/scripts/live/sveltekit-adapter.mjs +59 -17
- package/template/.agents/skills/design/impeccable/scripts/live/tanstack-adapter.mjs +280 -0
- package/template/.agents/skills/design/impeccable/scripts/live/vocabulary.mjs +135 -0
- package/template/.agents/skills/design/impeccable/scripts/live-accept.mjs +210 -68
- package/template/.agents/skills/design/impeccable/scripts/live-browser.js +1562 -223
- package/template/.agents/skills/design/impeccable/scripts/live-commit-manual-edits.mjs +3 -0
- package/template/.agents/skills/design/impeccable/scripts/live-complete.mjs +33 -1
- package/template/.agents/skills/design/impeccable/scripts/live-inject.mjs +175 -255
- package/template/.agents/skills/design/impeccable/scripts/live-insert.mjs +26 -6
- package/template/.agents/skills/design/impeccable/scripts/live-manual-edit-evidence.mjs +6 -1
- package/template/.agents/skills/design/impeccable/scripts/live-poll.mjs +61 -16
- package/template/.agents/skills/design/impeccable/scripts/live-resume.mjs +39 -10
- package/template/.agents/skills/design/impeccable/scripts/live-server.mjs +573 -47
- package/template/.agents/skills/design/impeccable/scripts/live-status.mjs +17 -7
- package/template/.agents/skills/design/impeccable/scripts/live-wrap.mjs +124 -91
- package/template/.agents/skills/design/impeccable/scripts/live.mjs +88 -26
- package/template/.agents/skills/design/impeccable/scripts/palette.mjs +76 -81
- package/template/.agents/skills/design/impeccable/scripts/pin.mjs +18 -11
- package/template/.agents/skills/design/impeccable/scripts/serve-question.mjs +932 -0
- package/template/.agents/skills/design/impeccable/scripts/surface-brief.mjs +74 -0
- package/template/.agents/skills/design/shadcn/SKILL.md +46 -11
- package/template/.agents/skills/design/shadcn/cli.md +49 -16
- package/template/.agents/skills/design/shadcn/customization.md +14 -7
- package/template/.agents/skills/design/shadcn/evals/evals.json +30 -0
- package/template/.agents/skills/design/shadcn/mcp.md +27 -16
- package/template/.agents/skills/design/shadcn/registry.md +277 -0
- package/template/.agents/skills/design/shadcn/rules/chat.md +224 -0
- package/template/.agents/skills/design/shadcn/rules/composition.md +20 -2
- package/template/.agents/skills/design/shadcn/rules/styling.md +23 -0
- package/template/.agents/skills/find-skills/SKILL.md +2 -3
- package/template/.agents/skills/hono/SKILL.md +579 -0
- package/template/.agents/skills/integrations/langfuse/SKILL.md +10 -6
- package/template/.agents/skills/integrations/langfuse/references/ci-cd.md +41 -0
- package/template/.agents/skills/integrations/langfuse/references/cli.md +8 -0
- package/template/.agents/skills/integrations/langfuse/references/error-analysis.md +15 -27
- package/template/.agents/skills/integrations/langfuse/references/instrumentation.md +39 -52
- package/template/.agents/skills/integrations/langfuse/references/judge-calibration.md +5 -2
- package/template/.agents/skills/integrations/langfuse/references/prompt-engineering.md +35 -0
- package/template/.agents/skills/integrations/langfuse/references/prompt-migration.md +41 -196
- package/template/.agents/skills/integrations/langfuse/references/skill-feedback.md +3 -0
- package/template/.agents/skills/integrations/langfuse/references/trace-evaluator-upgrade.md +76 -0
- package/template/.agents/skills/integrations/langfuse/references/user-feedback.md +4 -0
- package/template/.agents/skills/integrations/langfuse/references/v4-project-migration.md +73 -0
- package/template/.agents/skills/supabase/CHANGELOG.md +71 -0
- package/template/.agents/skills/supabase/SKILL.md +145 -0
- package/template/.agents/skills/supabase/assets/feedback-issue-template.md +17 -0
- package/template/.agents/skills/supabase/references/skill-feedback.md +17 -0
- package/template/.agents/skills/supabase-postgres-best-practices/CHANGELOG.md +73 -0
- package/template/.agents/skills/supabase-postgres-best-practices/SKILL.md +64 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/_contributing.md +170 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/_sections.md +39 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/_template.md +34 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/advanced-full-text-search.md +55 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/advanced-jsonb-indexing.md +49 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/conn-idle-timeout.md +46 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/conn-limits.md +44 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/conn-pooling.md +41 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/conn-prepared-statements.md +46 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/data-batch-inserts.md +54 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/data-n-plus-one.md +53 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/data-pagination.md +50 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/data-upsert.md +50 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/lock-advisory.md +56 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/lock-deadlock-prevention.md +68 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/lock-short-transactions.md +50 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/lock-skip-locked.md +54 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/monitor-explain-analyze.md +45 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/monitor-pg-stat-statements.md +55 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/monitor-vacuum-analyze.md +55 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/query-composite-indexes.md +44 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/query-covering-indexes.md +40 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/query-index-types.md +48 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/query-missing-indexes.md +43 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/query-partial-indexes.md +45 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/schema-constraints.md +80 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/schema-data-types.md +46 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md +59 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md +55 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/schema-partitioning.md +55 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/schema-primary-keys.md +61 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/security-privileges.md +54 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/security-rls-basics.md +50 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/security-rls-performance.md +63 -0
- package/template/.agents/upstreams.json +34 -12
- package/template/.agents/skills/design/impeccable/reference/brand.md +0 -108
- package/template/.agents/skills/design/impeccable/reference/codex.md +0 -105
- package/template/.agents/skills/design/impeccable/reference/interaction-design.md +0 -189
- package/template/.agents/skills/integrations/langfuse/references/sdk-upgrade.md +0 -175
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ var __export = (target, all) => {
|
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
// src/ui/prompts.ts
|
|
12
|
-
var clackPromise, warnedClackUnavailable, loadClack, QUIVER_ART, TAGLINE, colorEnabled, palette, block, banner, outro, step, info, success, warn, error,
|
|
12
|
+
var clackPromise, warnedClackUnavailable, loadClack, QUIVER_ART, TAGLINE, colorEnabled, palette, block, banner, outro, step, info, success, warn, error, selectGrouped, selectGroupedText;
|
|
13
13
|
var init_prompts = __esm({
|
|
14
14
|
"src/ui/prompts.ts"() {
|
|
15
15
|
"use strict";
|
|
@@ -96,23 +96,6 @@ ${message}
|
|
|
96
96
|
if (clack) clack.log.error(message);
|
|
97
97
|
else console.error(message);
|
|
98
98
|
};
|
|
99
|
-
password = async (message) => {
|
|
100
|
-
const clack = await loadClack();
|
|
101
|
-
if (clack) {
|
|
102
|
-
const value = await clack.password({ message });
|
|
103
|
-
if (clack.isCancel(value)) return null;
|
|
104
|
-
return value;
|
|
105
|
-
}
|
|
106
|
-
const { createInterface } = await import("readline");
|
|
107
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
108
|
-
const answer = await new Promise((res) => {
|
|
109
|
-
rl.question(`${message}: `, (a) => {
|
|
110
|
-
rl.close();
|
|
111
|
-
res(a);
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
return answer.trim() || null;
|
|
115
|
-
};
|
|
116
99
|
selectGrouped = async ({
|
|
117
100
|
message,
|
|
118
101
|
groups,
|
|
@@ -326,7 +309,7 @@ var init_frontmatter = __esm({
|
|
|
326
309
|
// src/catalog/discover.ts
|
|
327
310
|
import { existsSync, readdirSync as readdirSync2, readFileSync as readFileSync2 } from "fs";
|
|
328
311
|
import { relative as relative2, resolve as resolve2 } from "path";
|
|
329
|
-
var readConfig, discoverSkills, discoverCommands, discoverMcp, loadCatalog;
|
|
312
|
+
var readConfig, discoverSkills, discoverCommands, discoverMcp, discoverPlugins, loadCatalog;
|
|
330
313
|
var init_discover = __esm({
|
|
331
314
|
"src/catalog/discover.ts"() {
|
|
332
315
|
"use strict";
|
|
@@ -400,6 +383,20 @@ var init_discover = __esm({
|
|
|
400
383
|
configDigest: jsonDigest(servers[name])
|
|
401
384
|
}));
|
|
402
385
|
};
|
|
386
|
+
discoverPlugins = (root, config) => Object.entries(config.plugins ?? {}).sort(([a], [b]) => a.localeCompare(b)).map(([name, plugin]) => {
|
|
387
|
+
const absPath = resolve2(root, plugin.sourcePath);
|
|
388
|
+
if (!existsSync(absPath)) {
|
|
389
|
+
throw new Error(`Plugin "${name}" source not found: ${absPath}`);
|
|
390
|
+
}
|
|
391
|
+
return {
|
|
392
|
+
name,
|
|
393
|
+
provider: plugin.provider,
|
|
394
|
+
sourcePath: plugin.sourcePath,
|
|
395
|
+
absPath,
|
|
396
|
+
digest: jsonDigest({ config: plugin, content: fileDigest(absPath) }),
|
|
397
|
+
requires: plugin.requires ?? []
|
|
398
|
+
};
|
|
399
|
+
});
|
|
403
400
|
loadCatalog = (catalog) => {
|
|
404
401
|
const { config, path } = readConfig(catalog.root);
|
|
405
402
|
return {
|
|
@@ -407,14 +404,15 @@ var init_discover = __esm({
|
|
|
407
404
|
configPath: path,
|
|
408
405
|
skills: discoverSkills(catalog.root),
|
|
409
406
|
commands: discoverCommands(catalog.root),
|
|
410
|
-
mcp: discoverMcp(config)
|
|
407
|
+
mcp: discoverMcp(config),
|
|
408
|
+
plugins: discoverPlugins(catalog.root, config)
|
|
411
409
|
};
|
|
412
410
|
};
|
|
413
411
|
}
|
|
414
412
|
});
|
|
415
413
|
|
|
416
414
|
// src/catalog/entries.ts
|
|
417
|
-
var skillToEntry, commandToEntry, mcpToEntry;
|
|
415
|
+
var skillToEntry, commandToEntry, mcpToEntry, pluginToEntry;
|
|
418
416
|
var init_entries = __esm({
|
|
419
417
|
"src/catalog/entries.ts"() {
|
|
420
418
|
"use strict";
|
|
@@ -437,6 +435,13 @@ var init_entries = __esm({
|
|
|
437
435
|
tools: null,
|
|
438
436
|
toolsFetchedAt: null
|
|
439
437
|
});
|
|
438
|
+
pluginToEntry = (plugin) => ({
|
|
439
|
+
type: "plugin",
|
|
440
|
+
provider: plugin.provider,
|
|
441
|
+
sourcePath: plugin.sourcePath,
|
|
442
|
+
digest: plugin.digest,
|
|
443
|
+
requires: plugin.requires
|
|
444
|
+
});
|
|
440
445
|
}
|
|
441
446
|
});
|
|
442
447
|
|
|
@@ -526,6 +531,12 @@ var init_materialize = __esm({
|
|
|
526
531
|
mkdirSync(dirname(dest), { recursive: true });
|
|
527
532
|
cpSync(command.absPath, dest, { force: true });
|
|
528
533
|
}
|
|
534
|
+
for (const plugin of catalog.plugins) {
|
|
535
|
+
if (!selection.plugins.includes(plugin.name)) continue;
|
|
536
|
+
const dest = resolve4(destRoot, plugin.sourcePath);
|
|
537
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
538
|
+
cpSync(plugin.absPath, dest, { force: true });
|
|
539
|
+
}
|
|
529
540
|
const filteredConfig = {};
|
|
530
541
|
if (catalog.config.shared) filteredConfig["shared"] = catalog.config.shared;
|
|
531
542
|
const mcpServers = {};
|
|
@@ -533,6 +544,14 @@ var init_materialize = __esm({
|
|
|
533
544
|
if (selection.mcp.includes(mcp.name)) mcpServers[mcp.name] = mcp.server;
|
|
534
545
|
}
|
|
535
546
|
if (Object.keys(mcpServers).length) filteredConfig["mcpServers"] = mcpServers;
|
|
547
|
+
const plugins = {};
|
|
548
|
+
for (const plugin of catalog.plugins) {
|
|
549
|
+
if (!selection.plugins.includes(plugin.name)) continue;
|
|
550
|
+
plugins[plugin.name] = catalog.config.plugins?.[plugin.name];
|
|
551
|
+
}
|
|
552
|
+
if (Object.keys(plugins).length) filteredConfig["plugins"] = plugins;
|
|
553
|
+
if (catalog.config.opencode) filteredConfig["opencode"] = catalog.config.opencode;
|
|
554
|
+
if (catalog.config.tui) filteredConfig["tui"] = catalog.config.tui;
|
|
536
555
|
if (catalog.config.claude) filteredConfig["claude"] = catalog.config.claude;
|
|
537
556
|
writeFileSync(
|
|
538
557
|
resolve4(destRoot, "config.json"),
|
|
@@ -568,53 +587,10 @@ var init_materialize = __esm({
|
|
|
568
587
|
|
|
569
588
|
// src/github/auth.ts
|
|
570
589
|
import { execFileSync } from "child_process";
|
|
571
|
-
|
|
572
|
-
chmodSync,
|
|
573
|
-
existsSync as existsSync4,
|
|
574
|
-
mkdirSync as mkdirSync2,
|
|
575
|
-
readFileSync as readFileSync4,
|
|
576
|
-
rmSync as rmSync2,
|
|
577
|
-
writeFileSync as writeFileSync2
|
|
578
|
-
} from "fs";
|
|
579
|
-
import { homedir } from "os";
|
|
580
|
-
import { resolve as resolve5 } from "path";
|
|
581
|
-
var configDir, authFilePath, readAuthFile, readStoredToken, writeStoredToken, deleteStoredToken, cachedToken, ghToken, resolveGithubToken, resetTokenCache, validateToken;
|
|
590
|
+
var cachedToken, ghToken, resolveGithubToken;
|
|
582
591
|
var init_auth = __esm({
|
|
583
592
|
"src/github/auth.ts"() {
|
|
584
593
|
"use strict";
|
|
585
|
-
configDir = () => {
|
|
586
|
-
const base = process.env["XDG_CONFIG_HOME"] || resolve5(homedir(), ".config");
|
|
587
|
-
return resolve5(base, "quiver");
|
|
588
|
-
};
|
|
589
|
-
authFilePath = () => resolve5(configDir(), "auth.json");
|
|
590
|
-
readAuthFile = () => {
|
|
591
|
-
const path = authFilePath();
|
|
592
|
-
if (!existsSync4(path)) return null;
|
|
593
|
-
try {
|
|
594
|
-
return JSON.parse(readFileSync4(path, "utf8"));
|
|
595
|
-
} catch {
|
|
596
|
-
return null;
|
|
597
|
-
}
|
|
598
|
-
};
|
|
599
|
-
readStoredToken = () => readAuthFile()?.github?.token ?? null;
|
|
600
|
-
writeStoredToken = (token, login2) => {
|
|
601
|
-
mkdirSync2(configDir(), { recursive: true });
|
|
602
|
-
const file = {
|
|
603
|
-
github: { token, login: login2, createdAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
604
|
-
};
|
|
605
|
-
writeFileSync2(authFilePath(), JSON.stringify(file, null, 2) + "\n", {
|
|
606
|
-
mode: 384
|
|
607
|
-
});
|
|
608
|
-
chmodSync(authFilePath(), 384);
|
|
609
|
-
resetTokenCache();
|
|
610
|
-
};
|
|
611
|
-
deleteStoredToken = () => {
|
|
612
|
-
const path = authFilePath();
|
|
613
|
-
if (!existsSync4(path)) return false;
|
|
614
|
-
rmSync2(path);
|
|
615
|
-
resetTokenCache();
|
|
616
|
-
return true;
|
|
617
|
-
};
|
|
618
594
|
ghToken = () => {
|
|
619
595
|
try {
|
|
620
596
|
const out = execFileSync("gh", ["auth", "token"], {
|
|
@@ -629,38 +605,14 @@ var init_auth = __esm({
|
|
|
629
605
|
};
|
|
630
606
|
resolveGithubToken = () => {
|
|
631
607
|
if (cachedToken !== void 0) return cachedToken;
|
|
632
|
-
cachedToken = process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"] ??
|
|
608
|
+
cachedToken = process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"] ?? ghToken();
|
|
633
609
|
return cachedToken;
|
|
634
610
|
};
|
|
635
|
-
resetTokenCache = () => {
|
|
636
|
-
cachedToken = void 0;
|
|
637
|
-
};
|
|
638
|
-
validateToken = async (token) => {
|
|
639
|
-
let res;
|
|
640
|
-
try {
|
|
641
|
-
res = await fetch("https://api.github.com/user", {
|
|
642
|
-
headers: {
|
|
643
|
-
Accept: "application/vnd.github+json",
|
|
644
|
-
"User-Agent": "quiver-cli",
|
|
645
|
-
Authorization: `Bearer ${token}`
|
|
646
|
-
}
|
|
647
|
-
});
|
|
648
|
-
} catch (err) {
|
|
649
|
-
return {
|
|
650
|
-
ok: false,
|
|
651
|
-
reason: err instanceof Error ? err.message : "fetch failed"
|
|
652
|
-
};
|
|
653
|
-
}
|
|
654
|
-
if (res.status === 401) return { ok: false, reason: "token is invalid or expired" };
|
|
655
|
-
if (!res.ok) return { ok: false, reason: `HTTP ${res.status}` };
|
|
656
|
-
const body = await res.json();
|
|
657
|
-
return { ok: true, login: body.login ?? "unknown" };
|
|
658
|
-
};
|
|
659
611
|
}
|
|
660
612
|
});
|
|
661
613
|
|
|
662
614
|
// src/github/api.ts
|
|
663
|
-
import { mkdirSync as
|
|
615
|
+
import { mkdirSync as mkdirSync2 } from "fs";
|
|
664
616
|
import { Readable } from "stream";
|
|
665
617
|
import { pipeline } from "stream/promises";
|
|
666
618
|
import * as tar from "tar";
|
|
@@ -678,13 +630,14 @@ var init_api = __esm({
|
|
|
678
630
|
if (token) h["Authorization"] = `Bearer ${token}`;
|
|
679
631
|
return h;
|
|
680
632
|
};
|
|
681
|
-
failureReason = (
|
|
682
|
-
if (
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
633
|
+
failureReason = (status) => {
|
|
634
|
+
if (status === 401)
|
|
635
|
+
return "authentication failed - set GITHUB_TOKEN or log in with the gh CLI";
|
|
636
|
+
if (status === 403 || status === 429)
|
|
637
|
+
return "rate-limited or access denied (set GITHUB_TOKEN or log in with the gh CLI)";
|
|
638
|
+
if (status === 404)
|
|
639
|
+
return "repo or ref not found (private repo? set GITHUB_TOKEN or log in with the gh CLI)";
|
|
640
|
+
return `HTTP ${status}`;
|
|
688
641
|
};
|
|
689
642
|
apiFetch = async (url) => {
|
|
690
643
|
let res;
|
|
@@ -721,7 +674,7 @@ var init_api = __esm({
|
|
|
721
674
|
);
|
|
722
675
|
if (!res.ok) return res;
|
|
723
676
|
if (!res.value.body) return { ok: false, reason: "empty tarball response" };
|
|
724
|
-
|
|
677
|
+
mkdirSync2(destDir, { recursive: true });
|
|
725
678
|
try {
|
|
726
679
|
await pipeline(
|
|
727
680
|
Readable.fromWeb(res.value.body),
|
|
@@ -746,9 +699,9 @@ __export(remote_exports, {
|
|
|
746
699
|
fetchRemoteCatalog: () => fetchRemoteCatalog,
|
|
747
700
|
parseGithubSource: () => parseGithubSource
|
|
748
701
|
});
|
|
749
|
-
import { existsSync as
|
|
750
|
-
import { homedir
|
|
751
|
-
import { dirname as dirname2, resolve as
|
|
702
|
+
import { existsSync as existsSync4, mkdtempSync, renameSync, rmSync as rmSync2, mkdirSync as mkdirSync3 } from "fs";
|
|
703
|
+
import { homedir } from "os";
|
|
704
|
+
import { dirname as dirname2, resolve as resolve5 } from "path";
|
|
752
705
|
var parseGithubSource, catalogCacheRoot, catalogCacheDir, fetchRemoteCatalog;
|
|
753
706
|
var init_remote = __esm({
|
|
754
707
|
"src/catalog/remote.ts"() {
|
|
@@ -771,10 +724,10 @@ var init_remote = __esm({
|
|
|
771
724
|
return { repo: `${owner}/${repo}`, path: pathParts.join("/"), ref };
|
|
772
725
|
};
|
|
773
726
|
catalogCacheRoot = () => {
|
|
774
|
-
const base = process.env["XDG_CACHE_HOME"] ||
|
|
775
|
-
return
|
|
727
|
+
const base = process.env["XDG_CACHE_HOME"] || resolve5(homedir(), ".cache");
|
|
728
|
+
return resolve5(base, "quiver", "catalogs");
|
|
776
729
|
};
|
|
777
|
-
catalogCacheDir = (repo, sha) =>
|
|
730
|
+
catalogCacheDir = (repo, sha) => resolve5(catalogCacheRoot(), `${repo.replace("/", "-")}-${sha.slice(0, 12)}`);
|
|
778
731
|
fetchRemoteCatalog = async (source, options = {}) => {
|
|
779
732
|
const spec = parseGithubSource(source);
|
|
780
733
|
let ref = spec.ref;
|
|
@@ -794,22 +747,22 @@ var init_remote = __esm({
|
|
|
794
747
|
sha = commit.value;
|
|
795
748
|
}
|
|
796
749
|
const cacheDir = catalogCacheDir(spec.repo, sha);
|
|
797
|
-
if (!
|
|
798
|
-
|
|
750
|
+
if (!existsSync4(cacheDir)) {
|
|
751
|
+
mkdirSync3(dirname2(cacheDir), { recursive: true });
|
|
799
752
|
const tmp = mkdtempSync(`${cacheDir}.tmp-`);
|
|
800
753
|
const result = await downloadTarball(spec.repo, sha, tmp);
|
|
801
754
|
if (!result.ok) {
|
|
802
|
-
|
|
755
|
+
rmSync2(tmp, { recursive: true, force: true });
|
|
803
756
|
throw new Error(`Cannot download ${spec.repo}@${sha.slice(0, 12)}: ${result.reason}`);
|
|
804
757
|
}
|
|
805
758
|
try {
|
|
806
759
|
renameSync(tmp, cacheDir);
|
|
807
760
|
} catch {
|
|
808
|
-
|
|
761
|
+
rmSync2(tmp, { recursive: true, force: true });
|
|
809
762
|
}
|
|
810
763
|
}
|
|
811
|
-
const root = spec.path ?
|
|
812
|
-
if (!
|
|
764
|
+
const root = spec.path ? resolve5(cacheDir, spec.path) : cacheDir;
|
|
765
|
+
if (!existsSync4(root)) {
|
|
813
766
|
throw new Error(
|
|
814
767
|
`Catalog path "${spec.path}" not found in ${spec.repo}@${sha.slice(0, 12)}.`
|
|
815
768
|
);
|
|
@@ -820,21 +773,21 @@ var init_remote = __esm({
|
|
|
820
773
|
});
|
|
821
774
|
|
|
822
775
|
// src/catalog/resolve.ts
|
|
823
|
-
import { accessSync, constants, existsSync as
|
|
824
|
-
import { dirname as dirname3, relative as relative3, resolve as
|
|
776
|
+
import { accessSync, constants, existsSync as existsSync5 } from "fs";
|
|
777
|
+
import { dirname as dirname3, relative as relative3, resolve as resolve6, sep } from "path";
|
|
825
778
|
import { fileURLToPath } from "url";
|
|
826
779
|
var packageRoot, DEFAULT_CATALOG_SOURCE, resolveCatalog, isInstalledPackage, isBundledCatalog, isCatalogWritable;
|
|
827
780
|
var init_resolve = __esm({
|
|
828
781
|
"src/catalog/resolve.ts"() {
|
|
829
782
|
"use strict";
|
|
830
|
-
packageRoot =
|
|
783
|
+
packageRoot = resolve6(dirname3(fileURLToPath(import.meta.url)), "..");
|
|
831
784
|
DEFAULT_CATALOG_SOURCE = "local:template/.agents";
|
|
832
785
|
resolveCatalog = async (source = DEFAULT_CATALOG_SOURCE, options = {}) => {
|
|
833
786
|
const [scheme, ...rest] = source.split(":");
|
|
834
787
|
const spec = rest.join(":");
|
|
835
788
|
if (scheme === "local") {
|
|
836
|
-
const root =
|
|
837
|
-
if (!
|
|
789
|
+
const root = resolve6(packageRoot, spec);
|
|
790
|
+
if (!existsSync5(root)) {
|
|
838
791
|
throw new Error(`Catalog not found at ${root} (source: ${source}).`);
|
|
839
792
|
}
|
|
840
793
|
return { source, root };
|
|
@@ -879,7 +832,9 @@ var init_schema = __esm({
|
|
|
879
832
|
if (idx === -1) return null;
|
|
880
833
|
const type = id.slice(0, idx);
|
|
881
834
|
const name = id.slice(idx + 1);
|
|
882
|
-
if (type !== "skill" && type !== "command" && type !== "mcp")
|
|
835
|
+
if (type !== "skill" && type !== "command" && type !== "mcp" && type !== "plugin") {
|
|
836
|
+
return null;
|
|
837
|
+
}
|
|
883
838
|
if (!name) return null;
|
|
884
839
|
return { type, name };
|
|
885
840
|
};
|
|
@@ -887,19 +842,19 @@ var init_schema = __esm({
|
|
|
887
842
|
});
|
|
888
843
|
|
|
889
844
|
// src/lockfile/io.ts
|
|
890
|
-
import { existsSync as
|
|
891
|
-
import { resolve as
|
|
845
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
846
|
+
import { resolve as resolve7 } from "path";
|
|
892
847
|
var lockfilePath, lockfileExists, readLockfile, writeLockfile, emptyLockfile;
|
|
893
848
|
var init_io = __esm({
|
|
894
849
|
"src/lockfile/io.ts"() {
|
|
895
850
|
"use strict";
|
|
896
851
|
init_schema();
|
|
897
|
-
lockfilePath = (targetRoot) =>
|
|
898
|
-
lockfileExists = (targetRoot) =>
|
|
852
|
+
lockfilePath = (targetRoot) => resolve7(targetRoot, LOCKFILE_NAME);
|
|
853
|
+
lockfileExists = (targetRoot) => existsSync6(lockfilePath(targetRoot));
|
|
899
854
|
readLockfile = (targetRoot) => {
|
|
900
855
|
const path = lockfilePath(targetRoot);
|
|
901
|
-
if (!
|
|
902
|
-
const parsed = JSON.parse(
|
|
856
|
+
if (!existsSync6(path)) return null;
|
|
857
|
+
const parsed = JSON.parse(readFileSync4(path, "utf8"));
|
|
903
858
|
if (parsed.version !== LOCKFILE_VERSION) {
|
|
904
859
|
throw new Error(
|
|
905
860
|
`Unsupported ${LOCKFILE_NAME} version ${parsed.version}; expected ${LOCKFILE_VERSION}.`
|
|
@@ -918,7 +873,7 @@ var init_io = __esm({
|
|
|
918
873
|
...lock.providers !== void 0 ? { providers: lock.providers } : {},
|
|
919
874
|
entries: sortedEntries
|
|
920
875
|
};
|
|
921
|
-
|
|
876
|
+
writeFileSync2(lockfilePath(targetRoot), JSON.stringify(ordered, null, 2) + "\n");
|
|
922
877
|
};
|
|
923
878
|
emptyLockfile = (catalogSource, remote = {}) => ({
|
|
924
879
|
version: LOCKFILE_VERSION,
|
|
@@ -990,7 +945,7 @@ var init_resolve2 = __esm({
|
|
|
990
945
|
});
|
|
991
946
|
|
|
992
947
|
// src/providers/claude.ts
|
|
993
|
-
import { resolve as
|
|
948
|
+
import { resolve as resolve8 } from "path";
|
|
994
949
|
var formatMcpJson, planClaude;
|
|
995
950
|
var init_claude = __esm({
|
|
996
951
|
"src/providers/claude.ts"() {
|
|
@@ -1005,33 +960,33 @@ var init_claude = __esm({
|
|
|
1005
960
|
const removeFiles = [];
|
|
1006
961
|
if (claudeSettings) {
|
|
1007
962
|
files.push({
|
|
1008
|
-
path:
|
|
963
|
+
path: resolve8(targetRoot, ".claude/settings.json"),
|
|
1009
964
|
content: JSON.stringify(claudeSettings, null, 2) + "\n"
|
|
1010
965
|
});
|
|
1011
966
|
}
|
|
1012
967
|
const mcp = formatMcpJson(mcpServers);
|
|
1013
|
-
const mcpPath =
|
|
968
|
+
const mcpPath = resolve8(targetRoot, ".mcp.json");
|
|
1014
969
|
if (mcp) files.push({ path: mcpPath, content: mcp });
|
|
1015
970
|
else removeFiles.push(mcpPath);
|
|
1016
971
|
const skillNames = selected.skills.map((s) => s.name);
|
|
1017
972
|
const commandFiles = selected.commands.map((c) => `${c.name}.md`);
|
|
1018
973
|
const symlinks = [
|
|
1019
974
|
...selected.skills.map((s) => ({
|
|
1020
|
-
path:
|
|
975
|
+
path: resolve8(targetRoot, ".claude/skills", s.name),
|
|
1021
976
|
target: s.absDir
|
|
1022
977
|
})),
|
|
1023
978
|
...selected.commands.map((c) => ({
|
|
1024
|
-
path:
|
|
979
|
+
path: resolve8(targetRoot, ".claude/commands", `${c.name}.md`),
|
|
1025
980
|
target: c.absPath
|
|
1026
981
|
}))
|
|
1027
982
|
];
|
|
1028
983
|
const managedDirs = [
|
|
1029
984
|
{
|
|
1030
|
-
path:
|
|
985
|
+
path: resolve8(targetRoot, ".claude/skills"),
|
|
1031
986
|
expected: new Set(skillNames)
|
|
1032
987
|
},
|
|
1033
988
|
{
|
|
1034
|
-
path:
|
|
989
|
+
path: resolve8(targetRoot, ".claude/commands"),
|
|
1035
990
|
expected: new Set(commandFiles)
|
|
1036
991
|
}
|
|
1037
992
|
];
|
|
@@ -1041,7 +996,7 @@ var init_claude = __esm({
|
|
|
1041
996
|
});
|
|
1042
997
|
|
|
1043
998
|
// src/providers/codex.ts
|
|
1044
|
-
import { resolve as
|
|
999
|
+
import { resolve as resolve9 } from "path";
|
|
1045
1000
|
import TOML from "@iarna/toml";
|
|
1046
1001
|
var buildConfig, planCodex;
|
|
1047
1002
|
var init_codex = __esm({
|
|
@@ -1085,7 +1040,7 @@ var init_codex = __esm({
|
|
|
1085
1040
|
const files = [];
|
|
1086
1041
|
const removeFiles = [];
|
|
1087
1042
|
const config = buildConfig(rawMcpServers);
|
|
1088
|
-
const path =
|
|
1043
|
+
const path = resolve9(targetRoot, ".codex/config.toml");
|
|
1089
1044
|
if (config) {
|
|
1090
1045
|
files.push({
|
|
1091
1046
|
path,
|
|
@@ -1101,19 +1056,19 @@ var init_codex = __esm({
|
|
|
1101
1056
|
|
|
1102
1057
|
// src/providers/fsops.ts
|
|
1103
1058
|
import {
|
|
1104
|
-
existsSync as
|
|
1059
|
+
existsSync as existsSync7,
|
|
1105
1060
|
lstatSync,
|
|
1106
|
-
mkdirSync as
|
|
1061
|
+
mkdirSync as mkdirSync4,
|
|
1107
1062
|
readdirSync as readdirSync3,
|
|
1108
|
-
readFileSync as
|
|
1063
|
+
readFileSync as readFileSync5,
|
|
1109
1064
|
readlinkSync,
|
|
1110
1065
|
rmdirSync,
|
|
1111
|
-
rmSync as
|
|
1066
|
+
rmSync as rmSync3,
|
|
1112
1067
|
symlinkSync,
|
|
1113
1068
|
unlinkSync,
|
|
1114
|
-
writeFileSync as
|
|
1069
|
+
writeFileSync as writeFileSync3
|
|
1115
1070
|
} from "fs";
|
|
1116
|
-
import { dirname as dirname4, relative as relative4, resolve as
|
|
1071
|
+
import { dirname as dirname4, relative as relative4, resolve as resolve10 } from "path";
|
|
1117
1072
|
var isENOENT, isMatchingSymlink, removePath, applyOutputs, checkOutputs;
|
|
1118
1073
|
var init_fsops = __esm({
|
|
1119
1074
|
"src/providers/fsops.ts"() {
|
|
@@ -1122,7 +1077,7 @@ var init_fsops = __esm({
|
|
|
1122
1077
|
isMatchingSymlink = (path, target) => {
|
|
1123
1078
|
const stats = lstatSync(path);
|
|
1124
1079
|
if (!stats.isSymbolicLink()) return false;
|
|
1125
|
-
return
|
|
1080
|
+
return resolve10(dirname4(path), readlinkSync(path)) === target;
|
|
1126
1081
|
};
|
|
1127
1082
|
removePath = (path) => {
|
|
1128
1083
|
let stats;
|
|
@@ -1133,7 +1088,7 @@ var init_fsops = __esm({
|
|
|
1133
1088
|
throw e;
|
|
1134
1089
|
}
|
|
1135
1090
|
if (stats.isSymbolicLink()) unlinkSync(path);
|
|
1136
|
-
else
|
|
1091
|
+
else rmSync3(path, { force: true, recursive: true });
|
|
1137
1092
|
};
|
|
1138
1093
|
applyOutputs = ({
|
|
1139
1094
|
files,
|
|
@@ -1146,22 +1101,22 @@ var init_fsops = __esm({
|
|
|
1146
1101
|
for (const out of files) {
|
|
1147
1102
|
let current = null;
|
|
1148
1103
|
try {
|
|
1149
|
-
current =
|
|
1104
|
+
current = readFileSync5(out.path, "utf8");
|
|
1150
1105
|
} catch (e) {
|
|
1151
1106
|
if (!isENOENT(e)) throw e;
|
|
1152
1107
|
}
|
|
1153
1108
|
if (current === out.content) continue;
|
|
1154
|
-
|
|
1155
|
-
|
|
1109
|
+
mkdirSync4(dirname4(out.path), { recursive: true });
|
|
1110
|
+
writeFileSync3(out.path, out.content);
|
|
1156
1111
|
result.generated.push(out.path);
|
|
1157
1112
|
}
|
|
1158
1113
|
for (const path of removeFiles) {
|
|
1159
|
-
if (!
|
|
1160
|
-
|
|
1114
|
+
if (!existsSync7(path)) continue;
|
|
1115
|
+
rmSync3(path, { force: true });
|
|
1161
1116
|
result.removed.push(path);
|
|
1162
1117
|
}
|
|
1163
1118
|
for (const path of removeDirs) {
|
|
1164
|
-
if (!
|
|
1119
|
+
if (!existsSync7(path)) continue;
|
|
1165
1120
|
removePath(path);
|
|
1166
1121
|
result.removed.push(path);
|
|
1167
1122
|
}
|
|
@@ -1172,7 +1127,7 @@ var init_fsops = __esm({
|
|
|
1172
1127
|
}
|
|
1173
1128
|
}
|
|
1174
1129
|
for (const link of symlinks) {
|
|
1175
|
-
|
|
1130
|
+
mkdirSync4(dirname4(link.path), { recursive: true });
|
|
1176
1131
|
try {
|
|
1177
1132
|
if (isMatchingSymlink(link.path, link.target)) continue;
|
|
1178
1133
|
} catch (e) {
|
|
@@ -1187,10 +1142,10 @@ var init_fsops = __esm({
|
|
|
1187
1142
|
result.linked.push(link.path);
|
|
1188
1143
|
}
|
|
1189
1144
|
for (const dir of managedDirs) {
|
|
1190
|
-
if (!
|
|
1145
|
+
if (!existsSync7(dir.path)) continue;
|
|
1191
1146
|
for (const child of readdirSync3(dir.path)) {
|
|
1192
1147
|
if (dir.expected.has(child)) continue;
|
|
1193
|
-
const childPath =
|
|
1148
|
+
const childPath = resolve10(dir.path, child);
|
|
1194
1149
|
removePath(childPath);
|
|
1195
1150
|
result.removed.push(childPath);
|
|
1196
1151
|
}
|
|
@@ -1207,7 +1162,7 @@ var init_fsops = __esm({
|
|
|
1207
1162
|
const problems = [];
|
|
1208
1163
|
for (const out of files) {
|
|
1209
1164
|
try {
|
|
1210
|
-
if (
|
|
1165
|
+
if (readFileSync5(out.path, "utf8") !== out.content) {
|
|
1211
1166
|
problems.push(`out of sync: ${out.path}`);
|
|
1212
1167
|
}
|
|
1213
1168
|
} catch (e) {
|
|
@@ -1216,10 +1171,10 @@ var init_fsops = __esm({
|
|
|
1216
1171
|
}
|
|
1217
1172
|
}
|
|
1218
1173
|
for (const path of removeFiles) {
|
|
1219
|
-
if (
|
|
1174
|
+
if (existsSync7(path)) problems.push(`stale (should be removed): ${path}`);
|
|
1220
1175
|
}
|
|
1221
1176
|
for (const path of removeDirs) {
|
|
1222
|
-
if (
|
|
1177
|
+
if (existsSync7(path)) problems.push(`stale (should be removed): ${path}`);
|
|
1223
1178
|
}
|
|
1224
1179
|
for (const link of symlinks) {
|
|
1225
1180
|
try {
|
|
@@ -1232,10 +1187,10 @@ var init_fsops = __esm({
|
|
|
1232
1187
|
}
|
|
1233
1188
|
}
|
|
1234
1189
|
for (const dir of managedDirs) {
|
|
1235
|
-
if (!
|
|
1190
|
+
if (!existsSync7(dir.path)) continue;
|
|
1236
1191
|
for (const child of readdirSync3(dir.path)) {
|
|
1237
1192
|
if (!dir.expected.has(child)) {
|
|
1238
|
-
problems.push(`unexpected shim: ${
|
|
1193
|
+
problems.push(`unexpected shim: ${resolve10(dir.path, child)}`);
|
|
1239
1194
|
}
|
|
1240
1195
|
}
|
|
1241
1196
|
}
|
|
@@ -1245,61 +1200,111 @@ var init_fsops = __esm({
|
|
|
1245
1200
|
});
|
|
1246
1201
|
|
|
1247
1202
|
// src/providers/opencode.ts
|
|
1248
|
-
import {
|
|
1249
|
-
|
|
1203
|
+
import { existsSync as existsSync8 } from "fs";
|
|
1204
|
+
import { resolve as resolve11 } from "path";
|
|
1205
|
+
var formatOpenCodeJson, preserveEnvReference, preserveEnvReferences, planOpenCode;
|
|
1250
1206
|
var init_opencode = __esm({
|
|
1251
1207
|
"src/providers/opencode.ts"() {
|
|
1252
1208
|
"use strict";
|
|
1253
|
-
formatOpenCodeJson = (servers) => {
|
|
1254
|
-
if (Object.keys(servers).length === 0) return null;
|
|
1209
|
+
formatOpenCodeJson = (servers, overlay) => {
|
|
1210
|
+
if (Object.keys(servers).length === 0 && !overlay) return null;
|
|
1255
1211
|
const mcp = {};
|
|
1256
1212
|
for (const [name, server] of Object.entries(servers)) {
|
|
1257
1213
|
if (server.transport === "http") {
|
|
1258
1214
|
mcp[name] = {
|
|
1259
1215
|
type: "remote",
|
|
1260
|
-
url: server.url,
|
|
1261
|
-
...server.headers ? { headers: server.headers } : {}
|
|
1216
|
+
url: preserveEnvReference(server.url),
|
|
1217
|
+
...server.headers ? { headers: preserveEnvReferences(server.headers) } : {}
|
|
1262
1218
|
};
|
|
1263
1219
|
} else {
|
|
1264
1220
|
mcp[name] = {
|
|
1265
1221
|
type: "local",
|
|
1266
|
-
command: [server.command, ...server.args ?? []]
|
|
1267
|
-
|
|
1222
|
+
command: [server.command, ...server.args ?? []].map(
|
|
1223
|
+
preserveEnvReference
|
|
1224
|
+
),
|
|
1225
|
+
...server.env ? { environment: preserveEnvReferences(server.env) } : {}
|
|
1268
1226
|
};
|
|
1269
1227
|
}
|
|
1270
1228
|
}
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1229
|
+
const overlayMcp = overlay?.["mcp"] && typeof overlay["mcp"] === "object" ? overlay["mcp"] : {};
|
|
1230
|
+
const mergedMcp = { ...overlayMcp, ...mcp };
|
|
1231
|
+
const config = {
|
|
1232
|
+
...overlay,
|
|
1233
|
+
$schema: "https://opencode.ai/config.json",
|
|
1234
|
+
...Object.keys(mergedMcp).length ? { mcp: mergedMcp } : {}
|
|
1235
|
+
};
|
|
1236
|
+
return JSON.stringify(config, null, 2) + "\n";
|
|
1237
|
+
};
|
|
1238
|
+
preserveEnvReference = (value) => value.replace(/\$\{([^}]+)\}/g, "{env:$1}");
|
|
1239
|
+
preserveEnvReferences = (values) => Object.fromEntries(
|
|
1240
|
+
Object.entries(values).map(([key, value]) => [
|
|
1241
|
+
key,
|
|
1242
|
+
preserveEnvReference(value)
|
|
1243
|
+
])
|
|
1244
|
+
);
|
|
1277
1245
|
planOpenCode = (inputs) => {
|
|
1278
|
-
const {
|
|
1246
|
+
const {
|
|
1247
|
+
targetRoot,
|
|
1248
|
+
selected,
|
|
1249
|
+
rawMcpServers,
|
|
1250
|
+
opencodeConfig,
|
|
1251
|
+
tuiConfig
|
|
1252
|
+
} = inputs;
|
|
1279
1253
|
const files = [];
|
|
1280
1254
|
const removeFiles = [];
|
|
1281
|
-
const json = formatOpenCodeJson(
|
|
1282
|
-
const jsonPath =
|
|
1255
|
+
const json = formatOpenCodeJson(rawMcpServers, opencodeConfig);
|
|
1256
|
+
const jsonPath = resolve11(targetRoot, "opencode.json");
|
|
1283
1257
|
if (json) files.push({ path: jsonPath, content: json });
|
|
1284
1258
|
else removeFiles.push(jsonPath);
|
|
1259
|
+
const tuiPath = resolve11(targetRoot, ".opencode/tui.json");
|
|
1260
|
+
const tuiMarkerPath = resolve11(targetRoot, ".opencode/.quiver-tui");
|
|
1261
|
+
if (tuiConfig) {
|
|
1262
|
+
files.push({
|
|
1263
|
+
path: tuiPath,
|
|
1264
|
+
content: JSON.stringify(
|
|
1265
|
+
{ ...tuiConfig, $schema: "https://opencode.ai/tui.json" },
|
|
1266
|
+
null,
|
|
1267
|
+
2
|
|
1268
|
+
) + "\n"
|
|
1269
|
+
});
|
|
1270
|
+
files.push({ path: tuiMarkerPath, content: "managed by quiver\n" });
|
|
1271
|
+
} else if (existsSync8(tuiMarkerPath)) {
|
|
1272
|
+
removeFiles.push(tuiPath, tuiMarkerPath);
|
|
1273
|
+
}
|
|
1285
1274
|
const symlinks = [
|
|
1286
1275
|
...selected.skills.map((s) => ({
|
|
1287
|
-
path:
|
|
1276
|
+
path: resolve11(targetRoot, ".opencode/skills", s.name),
|
|
1288
1277
|
target: s.absDir
|
|
1289
1278
|
})),
|
|
1290
1279
|
...selected.commands.map((c) => ({
|
|
1291
|
-
path:
|
|
1280
|
+
path: resolve11(targetRoot, ".opencode/commands", `${c.name}.md`),
|
|
1292
1281
|
target: c.absPath
|
|
1282
|
+
})),
|
|
1283
|
+
...selected.plugins.map((plugin) => ({
|
|
1284
|
+
path: resolve11(
|
|
1285
|
+
targetRoot,
|
|
1286
|
+
".opencode/plugins",
|
|
1287
|
+
`${plugin.name}${plugin.absPath.endsWith(".js") ? ".js" : ".ts"}`
|
|
1288
|
+
),
|
|
1289
|
+
target: plugin.absPath
|
|
1293
1290
|
}))
|
|
1294
1291
|
];
|
|
1295
1292
|
const managedDirs = [
|
|
1296
1293
|
{
|
|
1297
|
-
path:
|
|
1294
|
+
path: resolve11(targetRoot, ".opencode/skills"),
|
|
1298
1295
|
expected: new Set(selected.skills.map((s) => s.name))
|
|
1299
1296
|
},
|
|
1300
1297
|
{
|
|
1301
|
-
path:
|
|
1298
|
+
path: resolve11(targetRoot, ".opencode/commands"),
|
|
1302
1299
|
expected: new Set(selected.commands.map((c) => `${c.name}.md`))
|
|
1300
|
+
},
|
|
1301
|
+
{
|
|
1302
|
+
path: resolve11(targetRoot, ".opencode/plugins"),
|
|
1303
|
+
expected: new Set(
|
|
1304
|
+
selected.plugins.map(
|
|
1305
|
+
(plugin) => `${plugin.name}${plugin.absPath.endsWith(".js") ? ".js" : ".ts"}`
|
|
1306
|
+
)
|
|
1307
|
+
)
|
|
1303
1308
|
}
|
|
1304
1309
|
];
|
|
1305
1310
|
return { files, removeFiles, symlinks, managedDirs };
|
|
@@ -1317,17 +1322,20 @@ var init_selection = __esm({
|
|
|
1317
1322
|
const skillNames = /* @__PURE__ */ new Set();
|
|
1318
1323
|
const commandNames = /* @__PURE__ */ new Set();
|
|
1319
1324
|
const mcpNames = /* @__PURE__ */ new Set();
|
|
1325
|
+
const pluginNames = /* @__PURE__ */ new Set();
|
|
1320
1326
|
for (const id of Object.keys(lock.entries)) {
|
|
1321
1327
|
const parsed = parseEntryId(id);
|
|
1322
1328
|
if (!parsed) continue;
|
|
1323
1329
|
if (parsed.type === "skill") skillNames.add(parsed.name);
|
|
1324
1330
|
else if (parsed.type === "command") commandNames.add(parsed.name);
|
|
1325
1331
|
else if (parsed.type === "mcp") mcpNames.add(parsed.name);
|
|
1332
|
+
else if (parsed.type === "plugin") pluginNames.add(parsed.name);
|
|
1326
1333
|
}
|
|
1327
1334
|
return {
|
|
1328
1335
|
skills: catalog.skills.filter((s) => skillNames.has(s.name)),
|
|
1329
1336
|
commands: catalog.commands.filter((c) => commandNames.has(c.name)),
|
|
1330
|
-
mcp: catalog.mcp.filter((m) => mcpNames.has(m.name))
|
|
1337
|
+
mcp: catalog.mcp.filter((m) => mcpNames.has(m.name)),
|
|
1338
|
+
plugins: catalog.plugins.filter((p) => pluginNames.has(p.name))
|
|
1331
1339
|
};
|
|
1332
1340
|
};
|
|
1333
1341
|
}
|
|
@@ -1335,7 +1343,7 @@ var init_selection = __esm({
|
|
|
1335
1343
|
|
|
1336
1344
|
// src/providers/write.ts
|
|
1337
1345
|
import { existsSync as existsSync9, lstatSync as lstatSync2 } from "fs";
|
|
1338
|
-
import { basename, relative as relative5, resolve as
|
|
1346
|
+
import { basename, relative as relative5, resolve as resolve12 } from "path";
|
|
1339
1347
|
var isLinkable, buildPlan, writeProviders, formatWriteResult, checkProviders;
|
|
1340
1348
|
var init_write = __esm({
|
|
1341
1349
|
"src/providers/write.ts"() {
|
|
@@ -1360,14 +1368,16 @@ var init_write = __esm({
|
|
|
1360
1368
|
const rawMcpServers = {};
|
|
1361
1369
|
for (const mcp of selected.mcp) rawMcpServers[mcp.name] = mcp.server;
|
|
1362
1370
|
const mcpServers = interpolateEnvVars(rawMcpServers, onMissingEnv);
|
|
1363
|
-
const agentsRoot =
|
|
1371
|
+
const agentsRoot = resolve12(targetRoot, ".agents");
|
|
1364
1372
|
const inputs = {
|
|
1365
1373
|
targetRoot,
|
|
1366
1374
|
agentsRoot,
|
|
1367
1375
|
selected,
|
|
1368
1376
|
mcpServers,
|
|
1369
1377
|
rawMcpServers,
|
|
1370
|
-
claudeSettings: catalog.config.claude?.settings ?? null
|
|
1378
|
+
claudeSettings: catalog.config.claude?.settings ?? null,
|
|
1379
|
+
opencodeConfig: catalog.config.opencode ?? null,
|
|
1380
|
+
tuiConfig: catalog.config.tui ?? null
|
|
1371
1381
|
};
|
|
1372
1382
|
const active = lock.providers?.length ? lock.providers : [...PROVIDERS];
|
|
1373
1383
|
const planByProvider = {
|
|
@@ -1391,12 +1401,12 @@ var init_write = __esm({
|
|
|
1391
1401
|
}
|
|
1392
1402
|
}
|
|
1393
1403
|
const rootSymlinks = [];
|
|
1394
|
-
const agentsMd =
|
|
1404
|
+
const agentsMd = resolve12(agentsRoot, "AGENTS.md");
|
|
1395
1405
|
if (existsSync9(agentsMd)) {
|
|
1396
|
-
const rootAgents =
|
|
1406
|
+
const rootAgents = resolve12(targetRoot, "AGENTS.md");
|
|
1397
1407
|
for (const link of [
|
|
1398
1408
|
{ path: rootAgents, target: agentsMd },
|
|
1399
|
-
{ path:
|
|
1409
|
+
{ path: resolve12(targetRoot, "CLAUDE.md"), target: rootAgents }
|
|
1400
1410
|
]) {
|
|
1401
1411
|
if (isLinkable(link.path)) rootSymlinks.push(link);
|
|
1402
1412
|
else onSkippedRootFile?.(basename(link.path));
|
|
@@ -1458,8 +1468,8 @@ var init_write = __esm({
|
|
|
1458
1468
|
|
|
1459
1469
|
// src/commands/gitignore.ts
|
|
1460
1470
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
1461
|
-
import { existsSync as existsSync10, readFileSync as
|
|
1462
|
-
import { resolve as
|
|
1471
|
+
import { existsSync as existsSync10, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
1472
|
+
import { resolve as resolve13 } from "path";
|
|
1463
1473
|
var HEADER, SECRETS_COMMENT, PROVIDER_ENTRIES, SHARED_ENTRIES, SECRET_ENTRIES, patchGitignore, ignoredSourcePaths;
|
|
1464
1474
|
var init_gitignore = __esm({
|
|
1465
1475
|
"src/commands/gitignore.ts"() {
|
|
@@ -1475,8 +1485,8 @@ var init_gitignore = __esm({
|
|
|
1475
1485
|
SHARED_ENTRIES = ["AGENTS.md", "CLAUDE.md"];
|
|
1476
1486
|
SECRET_ENTRIES = [".env.local"];
|
|
1477
1487
|
patchGitignore = (targetRoot, providers2) => {
|
|
1478
|
-
const path =
|
|
1479
|
-
const current = existsSync10(path) ?
|
|
1488
|
+
const path = resolve13(targetRoot, ".gitignore");
|
|
1489
|
+
const current = existsSync10(path) ? readFileSync6(path, "utf8") : "";
|
|
1480
1490
|
const lines = new Set(current.split("\n").map((l) => l.trim()));
|
|
1481
1491
|
const active = providers2?.length ? providers2 : [...PROVIDERS];
|
|
1482
1492
|
const shimEntries = [
|
|
@@ -1490,7 +1500,7 @@ var init_gitignore = __esm({
|
|
|
1490
1500
|
if (missingShims.length) block2.push(HEADER, ...missingShims);
|
|
1491
1501
|
if (missingSecrets.length) block2.push(SECRETS_COMMENT, ...missingSecrets);
|
|
1492
1502
|
const prefix = current && !current.endsWith("\n") ? "\n" : "";
|
|
1493
|
-
|
|
1503
|
+
writeFileSync4(
|
|
1494
1504
|
path,
|
|
1495
1505
|
current + prefix + (current ? "\n" : "") + block2.join("\n") + "\n"
|
|
1496
1506
|
);
|
|
@@ -1535,12 +1545,24 @@ var init_select = __esm({
|
|
|
1535
1545
|
const s = entry.server;
|
|
1536
1546
|
return s.transport === "http" ? s.url : [s.command, ...s.args ?? []].filter(Boolean).join(" ");
|
|
1537
1547
|
};
|
|
1538
|
-
selectFromCatalog = async (catalog, {
|
|
1548
|
+
selectFromCatalog = async (catalog, {
|
|
1549
|
+
interactive,
|
|
1550
|
+
providers: providers2
|
|
1551
|
+
}) => {
|
|
1539
1552
|
const allSkills = catalog.skills.map((s) => s.name);
|
|
1540
1553
|
const allCommands = catalog.commands.map((c) => c.name);
|
|
1541
1554
|
const allMcp = catalog.mcp.map((m) => m.name);
|
|
1555
|
+
const availablePlugins = catalog.plugins.filter(
|
|
1556
|
+
(plugin) => providers2.includes(plugin.provider)
|
|
1557
|
+
);
|
|
1558
|
+
const allPlugins = availablePlugins.map((p) => p.name);
|
|
1542
1559
|
if (!interactive || !process.stdin.isTTY) {
|
|
1543
|
-
return {
|
|
1560
|
+
return {
|
|
1561
|
+
skills: allSkills,
|
|
1562
|
+
commands: allCommands,
|
|
1563
|
+
mcp: allMcp,
|
|
1564
|
+
plugins: allPlugins
|
|
1565
|
+
};
|
|
1544
1566
|
}
|
|
1545
1567
|
const groupNames = [...new Set(catalog.skills.map((s) => s.group))].sort(
|
|
1546
1568
|
(a, b) => a === "general" ? -1 : b === "general" ? 1 : a.localeCompare(b)
|
|
@@ -1595,7 +1617,21 @@ var init_select = __esm({
|
|
|
1595
1617
|
groups: mcpGroups,
|
|
1596
1618
|
initialValues: []
|
|
1597
1619
|
}) : [];
|
|
1598
|
-
|
|
1620
|
+
const plugins = availablePlugins.length ? await selectGrouped({
|
|
1621
|
+
message: "Select plugins (space toggles, a all, enter confirms)",
|
|
1622
|
+
groups: [
|
|
1623
|
+
{
|
|
1624
|
+
name: "opencode",
|
|
1625
|
+
items: availablePlugins.map((plugin) => ({
|
|
1626
|
+
value: plugin.name,
|
|
1627
|
+
label: plugin.name,
|
|
1628
|
+
hint: plugin.requires.length ? `requires: ${plugin.requires.join(", ")}` : void 0
|
|
1629
|
+
}))
|
|
1630
|
+
}
|
|
1631
|
+
],
|
|
1632
|
+
initialValues: []
|
|
1633
|
+
}) : [];
|
|
1634
|
+
return { skills, commands, mcp, plugins };
|
|
1599
1635
|
};
|
|
1600
1636
|
}
|
|
1601
1637
|
});
|
|
@@ -1637,11 +1673,12 @@ var init_init = __esm({
|
|
|
1637
1673
|
);
|
|
1638
1674
|
}
|
|
1639
1675
|
const sourceCatalog = loadCatalog(source);
|
|
1640
|
-
const selection = await selectFromCatalog(sourceCatalog, {
|
|
1641
|
-
interactive: !options.all
|
|
1642
|
-
});
|
|
1643
1676
|
const providers2 = await resolveProviders(options);
|
|
1644
1677
|
if (providers2 === null) return;
|
|
1678
|
+
const selection = await selectFromCatalog(sourceCatalog, {
|
|
1679
|
+
interactive: !options.all,
|
|
1680
|
+
providers: providers2
|
|
1681
|
+
});
|
|
1645
1682
|
const resolved = materializeCatalog(
|
|
1646
1683
|
options.targetRoot,
|
|
1647
1684
|
source,
|
|
@@ -1669,9 +1706,14 @@ var init_init = __esm({
|
|
|
1669
1706
|
lock.entries[entryId("mcp", mcp.name)] = mcpToEntry(mcp);
|
|
1670
1707
|
}
|
|
1671
1708
|
}
|
|
1709
|
+
for (const plugin of catalog.plugins) {
|
|
1710
|
+
if (selection.plugins.includes(plugin.name)) {
|
|
1711
|
+
lock.entries[entryId("plugin", plugin.name)] = pluginToEntry(plugin);
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1672
1714
|
writeLockfile(options.targetRoot, lock);
|
|
1673
1715
|
await step(
|
|
1674
|
-
`Wrote quiver.lock (${selection.skills.length} skills, ${selection.commands.length} commands, ${selection.mcp.length} MCP servers)`
|
|
1716
|
+
`Wrote quiver.lock (${selection.skills.length} skills, ${selection.commands.length} commands, ${selection.mcp.length} MCP servers, ${selection.plugins.length} plugins)`
|
|
1675
1717
|
);
|
|
1676
1718
|
const result = writeProviders(options.targetRoot, catalog, lock);
|
|
1677
1719
|
if (result.generated.length || result.linked.length) {
|
|
@@ -1706,13 +1748,13 @@ var init_init = __esm({
|
|
|
1706
1748
|
|
|
1707
1749
|
// src/catalog/repo.ts
|
|
1708
1750
|
import { existsSync as existsSync11 } from "fs";
|
|
1709
|
-
import { resolve as
|
|
1751
|
+
import { resolve as resolve14 } from "path";
|
|
1710
1752
|
var repoCatalogRoot, repoCatalogExists, loadRepoCatalog;
|
|
1711
1753
|
var init_repo = __esm({
|
|
1712
1754
|
"src/catalog/repo.ts"() {
|
|
1713
1755
|
"use strict";
|
|
1714
1756
|
init_discover();
|
|
1715
|
-
repoCatalogRoot = (targetRoot) =>
|
|
1757
|
+
repoCatalogRoot = (targetRoot) => resolve14(targetRoot, ".agents");
|
|
1716
1758
|
repoCatalogExists = (targetRoot) => existsSync11(repoCatalogRoot(targetRoot));
|
|
1717
1759
|
loadRepoCatalog = (targetRoot, source) => {
|
|
1718
1760
|
const resolved = {
|
|
@@ -1745,13 +1787,17 @@ var init_add = __esm({
|
|
|
1745
1787
|
add = async (options) => {
|
|
1746
1788
|
const id = options.positionals[0];
|
|
1747
1789
|
if (!id) {
|
|
1748
|
-
await error(
|
|
1790
|
+
await error(
|
|
1791
|
+
"Usage: quiver-cli add <skill:name|command:name|mcp:name|plugin:name>"
|
|
1792
|
+
);
|
|
1749
1793
|
process.exitCode = 1;
|
|
1750
1794
|
return;
|
|
1751
1795
|
}
|
|
1752
1796
|
const parsed = parseEntryId(id);
|
|
1753
1797
|
if (!parsed) {
|
|
1754
|
-
await error(
|
|
1798
|
+
await error(
|
|
1799
|
+
`Invalid id "${id}". Expected skill:<name>, command:<name>, mcp:<name> or plugin:<name>.`
|
|
1800
|
+
);
|
|
1755
1801
|
process.exitCode = 1;
|
|
1756
1802
|
return;
|
|
1757
1803
|
}
|
|
@@ -1772,16 +1818,25 @@ var init_add = __esm({
|
|
|
1772
1818
|
const skill = sourceCatalog.skills.find((s) => s.name === parsed.name);
|
|
1773
1819
|
const command = sourceCatalog.commands.find((c) => c.name === parsed.name);
|
|
1774
1820
|
const mcp = sourceCatalog.mcp.find((m) => m.name === parsed.name);
|
|
1775
|
-
const
|
|
1821
|
+
const plugin = sourceCatalog.plugins.find((p) => p.name === parsed.name);
|
|
1822
|
+
const entry = parsed.type === "skill" && skill ? skillToEntry(skill) : parsed.type === "command" && command ? commandToEntry(command) : parsed.type === "mcp" && mcp ? mcpToEntry(mcp) : parsed.type === "plugin" && plugin ? pluginToEntry(plugin) : null;
|
|
1776
1823
|
if (!entry) {
|
|
1777
1824
|
await error(`${id} not found in catalog.`);
|
|
1778
1825
|
process.exitCode = 1;
|
|
1779
1826
|
return;
|
|
1780
1827
|
}
|
|
1828
|
+
if (entry.type === "plugin" && lock.providers?.length && !lock.providers.includes(entry.provider)) {
|
|
1829
|
+
await error(
|
|
1830
|
+
`${id} requires the ${entry.provider} provider. Enable it with \`quiver-cli providers ${entry.provider}\` first.`
|
|
1831
|
+
);
|
|
1832
|
+
process.exitCode = 1;
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
1781
1835
|
const selection = selectionFromLock(lock);
|
|
1782
1836
|
if (parsed.type === "skill") selection.skills.push(parsed.name);
|
|
1783
1837
|
if (parsed.type === "command") selection.commands.push(parsed.name);
|
|
1784
1838
|
if (parsed.type === "mcp") selection.mcp.push(parsed.name);
|
|
1839
|
+
if (parsed.type === "plugin") selection.plugins.push(parsed.name);
|
|
1785
1840
|
materializeCatalog(options.targetRoot, source, sourceCatalog, selection);
|
|
1786
1841
|
lock.entries[id] = entry;
|
|
1787
1842
|
writeLockfile(options.targetRoot, lock);
|
|
@@ -1790,13 +1845,19 @@ var init_add = __esm({
|
|
|
1790
1845
|
await success(`Added ${id}.`);
|
|
1791
1846
|
};
|
|
1792
1847
|
selectionFromLock = (lock) => {
|
|
1793
|
-
const selection = {
|
|
1848
|
+
const selection = {
|
|
1849
|
+
skills: [],
|
|
1850
|
+
commands: [],
|
|
1851
|
+
mcp: [],
|
|
1852
|
+
plugins: []
|
|
1853
|
+
};
|
|
1794
1854
|
for (const lid of Object.keys(lock.entries)) {
|
|
1795
1855
|
const p = parseEntryId(lid);
|
|
1796
1856
|
if (!p) continue;
|
|
1797
1857
|
if (p.type === "skill") selection.skills.push(p.name);
|
|
1798
1858
|
else if (p.type === "command") selection.commands.push(p.name);
|
|
1799
1859
|
else if (p.type === "mcp") selection.mcp.push(p.name);
|
|
1860
|
+
else if (p.type === "plugin") selection.plugins.push(p.name);
|
|
1800
1861
|
}
|
|
1801
1862
|
return selection;
|
|
1802
1863
|
};
|
|
@@ -1808,9 +1869,9 @@ var remove_exports = {};
|
|
|
1808
1869
|
__export(remove_exports, {
|
|
1809
1870
|
remove: () => remove
|
|
1810
1871
|
});
|
|
1811
|
-
import { existsSync as existsSync12, readdirSync as readdirSync4, readFileSync as
|
|
1812
|
-
import { resolve as
|
|
1813
|
-
var remove, cleanupEmptyGroups, rewriteRepoMcp;
|
|
1872
|
+
import { existsSync as existsSync12, readdirSync as readdirSync4, readFileSync as readFileSync7, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
1873
|
+
import { resolve as resolve15 } from "path";
|
|
1874
|
+
var remove, rewriteRepoPlugins, cleanupEmptyGroups, rewriteRepoMcp;
|
|
1814
1875
|
var init_remove = __esm({
|
|
1815
1876
|
"src/commands/remove.ts"() {
|
|
1816
1877
|
"use strict";
|
|
@@ -1823,7 +1884,9 @@ var init_remove = __esm({
|
|
|
1823
1884
|
remove = async (options) => {
|
|
1824
1885
|
const id = options.positionals[0];
|
|
1825
1886
|
if (!id) {
|
|
1826
|
-
await error(
|
|
1887
|
+
await error(
|
|
1888
|
+
"Usage: quiver remove <skill:name|command:name|mcp:name|plugin:name>"
|
|
1889
|
+
);
|
|
1827
1890
|
process.exitCode = 1;
|
|
1828
1891
|
return;
|
|
1829
1892
|
}
|
|
@@ -1844,31 +1907,46 @@ var init_remove = __esm({
|
|
|
1844
1907
|
await info(`${id} is not installed.`);
|
|
1845
1908
|
return;
|
|
1846
1909
|
}
|
|
1847
|
-
if (entry.type === "skill" || entry.type === "command") {
|
|
1910
|
+
if (entry.type === "skill" || entry.type === "command" || entry.type === "plugin") {
|
|
1848
1911
|
removeArtifact(options.targetRoot, entry.sourcePath);
|
|
1849
1912
|
cleanupEmptyGroups(options.targetRoot);
|
|
1850
1913
|
}
|
|
1851
1914
|
delete lock.entries[id];
|
|
1852
1915
|
writeLockfile(options.targetRoot, lock);
|
|
1853
1916
|
rewriteRepoMcp(options.targetRoot, lock);
|
|
1917
|
+
rewriteRepoPlugins(options.targetRoot, lock);
|
|
1854
1918
|
const { catalog } = loadRepoCatalog(options.targetRoot, lock.catalog.source);
|
|
1855
1919
|
writeProviders(options.targetRoot, catalog, lock);
|
|
1856
1920
|
await success(`Removed ${id}.`);
|
|
1857
1921
|
};
|
|
1922
|
+
rewriteRepoPlugins = (targetRoot, lock) => {
|
|
1923
|
+
const configPath = resolve15(targetRoot, ".agents/config.json");
|
|
1924
|
+
if (!existsSync12(configPath)) return;
|
|
1925
|
+
const config = JSON.parse(readFileSync7(configPath, "utf8"));
|
|
1926
|
+
if (!config.plugins) return;
|
|
1927
|
+
const keep = new Set(
|
|
1928
|
+
Object.keys(lock.entries).map(parseEntryId).filter((p) => p?.type === "plugin").map((p) => p.name)
|
|
1929
|
+
);
|
|
1930
|
+
config.plugins = Object.fromEntries(
|
|
1931
|
+
Object.entries(config.plugins).filter(([name]) => keep.has(name))
|
|
1932
|
+
);
|
|
1933
|
+
if (!Object.keys(config.plugins).length) delete config.plugins;
|
|
1934
|
+
writeFileSync5(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
1935
|
+
};
|
|
1858
1936
|
cleanupEmptyGroups = (targetRoot) => {
|
|
1859
|
-
const skillsRoot =
|
|
1937
|
+
const skillsRoot = resolve15(targetRoot, ".agents/skills");
|
|
1860
1938
|
if (!existsSync12(skillsRoot)) return;
|
|
1861
1939
|
for (const entry of readdirSync4(skillsRoot, { withFileTypes: true })) {
|
|
1862
1940
|
if (!entry.isDirectory()) continue;
|
|
1863
|
-
const dir =
|
|
1864
|
-
if (existsSync12(
|
|
1865
|
-
if (readdirSync4(dir).length === 0)
|
|
1941
|
+
const dir = resolve15(skillsRoot, entry.name);
|
|
1942
|
+
if (existsSync12(resolve15(dir, "SKILL.md"))) continue;
|
|
1943
|
+
if (readdirSync4(dir).length === 0) rmSync4(dir, { recursive: true, force: true });
|
|
1866
1944
|
}
|
|
1867
1945
|
};
|
|
1868
1946
|
rewriteRepoMcp = (targetRoot, lock) => {
|
|
1869
|
-
const configPath =
|
|
1947
|
+
const configPath = resolve15(targetRoot, ".agents/config.json");
|
|
1870
1948
|
if (!existsSync12(configPath)) return;
|
|
1871
|
-
const config = JSON.parse(
|
|
1949
|
+
const config = JSON.parse(readFileSync7(configPath, "utf8"));
|
|
1872
1950
|
if (!config.mcpServers) return;
|
|
1873
1951
|
const keep = new Set(
|
|
1874
1952
|
Object.keys(lock.entries).map(parseEntryId).filter((p) => p?.type === "mcp").map((p) => p.name)
|
|
@@ -1879,7 +1957,7 @@ var init_remove = __esm({
|
|
|
1879
1957
|
}
|
|
1880
1958
|
if (Object.keys(kept).length) config.mcpServers = kept;
|
|
1881
1959
|
else delete config.mcpServers;
|
|
1882
|
-
|
|
1960
|
+
writeFileSync5(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
1883
1961
|
};
|
|
1884
1962
|
}
|
|
1885
1963
|
});
|
|
@@ -1894,6 +1972,7 @@ var init_locksync = __esm({
|
|
|
1894
1972
|
const skillByName = new Map(catalog.skills.map((s) => [s.name, s]));
|
|
1895
1973
|
const commandByName = new Map(catalog.commands.map((c) => [c.name, c]));
|
|
1896
1974
|
const mcpByName = new Map(catalog.mcp.map((m) => [m.name, m]));
|
|
1975
|
+
const pluginByName = new Map(catalog.plugins.map((p) => [p.name, p]));
|
|
1897
1976
|
for (const [id, entry] of Object.entries(lock.entries)) {
|
|
1898
1977
|
if (entry.type === "skill") {
|
|
1899
1978
|
const cat = skillByName.get(id.slice("skill:".length));
|
|
@@ -1918,6 +1997,15 @@ var init_locksync = __esm({
|
|
|
1918
1997
|
entry.configDigest = cat.configDigest;
|
|
1919
1998
|
entry.transport = cat.server.transport;
|
|
1920
1999
|
}
|
|
2000
|
+
} else if (entry.type === "plugin") {
|
|
2001
|
+
const cat = pluginByName.get(id.slice("plugin:".length));
|
|
2002
|
+
if (!cat) continue;
|
|
2003
|
+
if (cat.digest !== entry.digest) {
|
|
2004
|
+
drift.push(`${id}: plugin content changed`);
|
|
2005
|
+
entry.digest = cat.digest;
|
|
2006
|
+
entry.sourcePath = cat.sourcePath;
|
|
2007
|
+
entry.requires = cat.requires;
|
|
2008
|
+
}
|
|
1921
2009
|
}
|
|
1922
2010
|
}
|
|
1923
2011
|
return drift;
|
|
@@ -1975,11 +2063,12 @@ var init_sync = __esm({
|
|
|
1975
2063
|
const haveSkills = new Set(catalog.skills.map((s) => s.name));
|
|
1976
2064
|
const haveCommands = new Set(catalog.commands.map((c) => c.name));
|
|
1977
2065
|
const haveMcp = new Set(catalog.mcp.map((m) => m.name));
|
|
2066
|
+
const havePlugins = new Set(catalog.plugins.map((p) => p.name));
|
|
1978
2067
|
const orphans = [];
|
|
1979
2068
|
for (const id of Object.keys(lock.entries)) {
|
|
1980
2069
|
const p = parseEntryId(id);
|
|
1981
2070
|
if (!p) continue;
|
|
1982
|
-
const present = p.type === "skill" && haveSkills.has(p.name) || p.type === "command" && haveCommands.has(p.name) || p.type === "mcp" && haveMcp.has(p.name);
|
|
2071
|
+
const present = p.type === "skill" && haveSkills.has(p.name) || p.type === "command" && haveCommands.has(p.name) || p.type === "mcp" && haveMcp.has(p.name) || p.type === "plugin" && havePlugins.has(p.name);
|
|
1983
2072
|
if (!present) orphans.push(id);
|
|
1984
2073
|
}
|
|
1985
2074
|
if (orphans.length) {
|
|
@@ -2085,8 +2174,8 @@ var update_exports = {};
|
|
|
2085
2174
|
__export(update_exports, {
|
|
2086
2175
|
update: () => update
|
|
2087
2176
|
});
|
|
2088
|
-
import { cpSync as cpSync2, mkdirSync as
|
|
2089
|
-
import { dirname as dirname5, resolve as
|
|
2177
|
+
import { cpSync as cpSync2, mkdirSync as mkdirSync5, readFileSync as readFileSync8, rmSync as rmSync5, writeFileSync as writeFileSync6 } from "fs";
|
|
2178
|
+
import { dirname as dirname5, resolve as resolve16 } from "path";
|
|
2090
2179
|
var update, applyUpdate, report;
|
|
2091
2180
|
var init_update = __esm({
|
|
2092
2181
|
"src/commands/update.ts"() {
|
|
@@ -2142,21 +2231,27 @@ var init_update = __esm({
|
|
|
2142
2231
|
sourceCatalog,
|
|
2143
2232
|
repoCatalog,
|
|
2144
2233
|
lock,
|
|
2145
|
-
options.force
|
|
2234
|
+
options.force,
|
|
2235
|
+
options.dryRun
|
|
2146
2236
|
);
|
|
2147
2237
|
if (report4.status === "updated") changed = true;
|
|
2148
2238
|
reports.push(report4);
|
|
2149
2239
|
}
|
|
2150
|
-
if (
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2240
|
+
if (!options.dryRun) {
|
|
2241
|
+
if (changed || catalogMoved) {
|
|
2242
|
+
writeLockfile(options.targetRoot, lock);
|
|
2243
|
+
}
|
|
2244
|
+
if (changed) {
|
|
2245
|
+
const { catalog } = loadRepoCatalog(
|
|
2246
|
+
options.targetRoot,
|
|
2247
|
+
lock.catalog.source
|
|
2248
|
+
);
|
|
2249
|
+
writeProviders(options.targetRoot, catalog, lock);
|
|
2250
|
+
}
|
|
2156
2251
|
}
|
|
2157
2252
|
report(reports, options);
|
|
2158
2253
|
};
|
|
2159
|
-
applyUpdate = (targetRoot, parsed, entry, sourceCatalog, repoCatalog, lock, force) => {
|
|
2254
|
+
applyUpdate = (targetRoot, parsed, entry, sourceCatalog, repoCatalog, lock, force, dryRun) => {
|
|
2160
2255
|
const id = `${parsed.type}:${parsed.name}`;
|
|
2161
2256
|
if (entry.type === "skill") {
|
|
2162
2257
|
const src2 = sourceCatalog.skills.find((s) => s.name === parsed.name);
|
|
@@ -2166,9 +2261,10 @@ var init_update = __esm({
|
|
|
2166
2261
|
if (repo2 && repo2.digest !== entry.digest && !force) {
|
|
2167
2262
|
return { id, status: "local-changes" };
|
|
2168
2263
|
}
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2264
|
+
if (dryRun) return { id, status: "updated" };
|
|
2265
|
+
const dest = resolve16(targetRoot, ".agents", src2.sourcePath);
|
|
2266
|
+
rmSync5(dest, { recursive: true, force: true });
|
|
2267
|
+
mkdirSync5(dirname5(dest), { recursive: true });
|
|
2172
2268
|
cpSync2(src2.absDir, dest, { recursive: true });
|
|
2173
2269
|
entry.digest = src2.digest;
|
|
2174
2270
|
entry.frontmatter = src2.frontmatter;
|
|
@@ -2183,13 +2279,38 @@ var init_update = __esm({
|
|
|
2183
2279
|
if (repo2 && repo2.digest !== entry.digest && !force) {
|
|
2184
2280
|
return { id, status: "local-changes" };
|
|
2185
2281
|
}
|
|
2186
|
-
|
|
2187
|
-
|
|
2282
|
+
if (dryRun) return { id, status: "updated" };
|
|
2283
|
+
const dest = resolve16(targetRoot, ".agents", src2.sourcePath);
|
|
2284
|
+
mkdirSync5(dirname5(dest), { recursive: true });
|
|
2188
2285
|
cpSync2(src2.absPath, dest, { force: true });
|
|
2189
2286
|
entry.digest = src2.digest;
|
|
2190
2287
|
entry.sourcePath = src2.sourcePath;
|
|
2191
2288
|
return { id, status: "updated" };
|
|
2192
2289
|
}
|
|
2290
|
+
if (entry.type === "plugin") {
|
|
2291
|
+
const src2 = sourceCatalog.plugins.find((p) => p.name === parsed.name);
|
|
2292
|
+
if (!src2) return { id, status: "not-in-catalog" };
|
|
2293
|
+
if (src2.digest === entry.digest) return { id, status: "up-to-date" };
|
|
2294
|
+
const repo2 = repoCatalog.plugins.find((p) => p.name === parsed.name);
|
|
2295
|
+
if (repo2 && repo2.digest !== entry.digest && !force) {
|
|
2296
|
+
return { id, status: "local-changes" };
|
|
2297
|
+
}
|
|
2298
|
+
if (dryRun) return { id, status: "updated" };
|
|
2299
|
+
const dest = resolve16(targetRoot, ".agents", src2.sourcePath);
|
|
2300
|
+
mkdirSync5(dirname5(dest), { recursive: true });
|
|
2301
|
+
cpSync2(src2.absPath, dest, { force: true });
|
|
2302
|
+
const configPath2 = resolve16(targetRoot, ".agents/config.json");
|
|
2303
|
+
const config2 = JSON.parse(readFileSync8(configPath2, "utf8"));
|
|
2304
|
+
config2.plugins = {
|
|
2305
|
+
...config2.plugins,
|
|
2306
|
+
[parsed.name]: sourceCatalog.config.plugins?.[parsed.name]
|
|
2307
|
+
};
|
|
2308
|
+
writeFileSync6(configPath2, JSON.stringify(config2, null, 2) + "\n");
|
|
2309
|
+
entry.digest = src2.digest;
|
|
2310
|
+
entry.sourcePath = src2.sourcePath;
|
|
2311
|
+
entry.requires = src2.requires;
|
|
2312
|
+
return { id, status: "updated" };
|
|
2313
|
+
}
|
|
2193
2314
|
const src = sourceCatalog.mcp.find((m) => m.name === parsed.name);
|
|
2194
2315
|
if (!src) return { id, status: "not-in-catalog" };
|
|
2195
2316
|
if (src.configDigest === entry.configDigest) return { id, status: "up-to-date" };
|
|
@@ -2197,10 +2318,11 @@ var init_update = __esm({
|
|
|
2197
2318
|
if (repo && repo.configDigest !== entry.configDigest && !force) {
|
|
2198
2319
|
return { id, status: "local-changes" };
|
|
2199
2320
|
}
|
|
2200
|
-
|
|
2201
|
-
const
|
|
2321
|
+
if (dryRun) return { id, status: "updated" };
|
|
2322
|
+
const configPath = resolve16(targetRoot, ".agents", "config.json");
|
|
2323
|
+
const config = JSON.parse(readFileSync8(configPath, "utf8"));
|
|
2202
2324
|
config.mcpServers = { ...config.mcpServers, [parsed.name]: src.server };
|
|
2203
|
-
|
|
2325
|
+
writeFileSync6(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
2204
2326
|
entry.configDigest = src.configDigest;
|
|
2205
2327
|
entry.transport = src.server.transport;
|
|
2206
2328
|
entry.tools = null;
|
|
@@ -2214,6 +2336,7 @@ var init_update = __esm({
|
|
|
2214
2336
|
JSON.stringify(
|
|
2215
2337
|
{
|
|
2216
2338
|
ok: true,
|
|
2339
|
+
dryRun: options.dryRun,
|
|
2217
2340
|
updated: by("updated").map((r) => r.id),
|
|
2218
2341
|
upToDate: by("up-to-date").map((r) => r.id),
|
|
2219
2342
|
localChanges: by("local-changes").map((r) => r.id),
|
|
@@ -2227,7 +2350,8 @@ var init_update = __esm({
|
|
|
2227
2350
|
}
|
|
2228
2351
|
const c = palette();
|
|
2229
2352
|
const lines = [""];
|
|
2230
|
-
|
|
2353
|
+
const verb = options.dryRun ? "would update" : "updated";
|
|
2354
|
+
for (const r of by("updated")) lines.push(` ${c.cyan("\u2191")} ${r.id} ${verb}`);
|
|
2231
2355
|
for (const r of by("local-changes"))
|
|
2232
2356
|
lines.push(
|
|
2233
2357
|
` ${c.yellow("\u25B2")} ${r.id} ${c.yellow("local changes - skipped (use --force)")}`
|
|
@@ -2239,7 +2363,11 @@ var init_update = __esm({
|
|
|
2239
2363
|
const updated = by("updated").length;
|
|
2240
2364
|
lines.push(
|
|
2241
2365
|
"",
|
|
2242
|
-
` ${updated ?
|
|
2366
|
+
` ${updated ? options.dryRun ? c.cyan(
|
|
2367
|
+
`\u2191 ${updated} would be updated - run without --dry-run to apply`
|
|
2368
|
+
) : c.cyan(
|
|
2369
|
+
`\u2191 ${updated} updated - review and commit .agents/ + quiver.lock`
|
|
2370
|
+
) : c.green("\u2713 everything up to date with the catalog")}`,
|
|
2243
2371
|
""
|
|
2244
2372
|
);
|
|
2245
2373
|
block(lines);
|
|
@@ -2287,14 +2415,16 @@ var init_list = __esm({
|
|
|
2287
2415
|
const skills = [];
|
|
2288
2416
|
const commands = [];
|
|
2289
2417
|
const mcp = [];
|
|
2418
|
+
const plugins = [];
|
|
2290
2419
|
for (const [id, entry] of Object.entries(lock.entries)) {
|
|
2291
2420
|
const p = parseEntryId(id);
|
|
2292
2421
|
if (!p) continue;
|
|
2293
2422
|
if (entry.type === "skill") skills.push({ name: p.name, entry });
|
|
2294
2423
|
else if (entry.type === "command") commands.push({ name: p.name, entry });
|
|
2295
2424
|
else if (entry.type === "mcp") mcp.push({ name: p.name, entry });
|
|
2425
|
+
else if (entry.type === "plugin") plugins.push({ name: p.name, entry });
|
|
2296
2426
|
}
|
|
2297
|
-
for (const group of [skills, commands, mcp]) {
|
|
2427
|
+
for (const group of [skills, commands, mcp, plugins]) {
|
|
2298
2428
|
group.sort((a, b) => a.name.localeCompare(b.name));
|
|
2299
2429
|
}
|
|
2300
2430
|
if (options.json) {
|
|
@@ -2313,6 +2443,11 @@ var init_list = __esm({
|
|
|
2313
2443
|
transport: entry.transport,
|
|
2314
2444
|
detail: serverDetail2.get(name) ?? null,
|
|
2315
2445
|
toolCount: entry.tools ? Object.keys(entry.tools).length : null
|
|
2446
|
+
})),
|
|
2447
|
+
plugins: plugins.map(({ name, entry }) => ({
|
|
2448
|
+
name,
|
|
2449
|
+
provider: entry.provider,
|
|
2450
|
+
requires: entry.requires
|
|
2316
2451
|
}))
|
|
2317
2452
|
},
|
|
2318
2453
|
null,
|
|
@@ -2370,11 +2505,18 @@ var init_list = __esm({
|
|
|
2370
2505
|
);
|
|
2371
2506
|
}
|
|
2372
2507
|
}
|
|
2508
|
+
if (plugins.length) {
|
|
2509
|
+
lines.push("", ` ${c.bold("plugins")}`);
|
|
2510
|
+
for (const { name, entry } of plugins) {
|
|
2511
|
+
const requires = entry.requires.length ? ` ${c.dim(`requires: ${entry.requires.join(", ")}`)}` : "";
|
|
2512
|
+
lines.push(` ${name} ${c.dim(entry.provider)}${requires}`);
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2373
2515
|
const providers2 = lock.providers?.length ? lock.providers.join(", ") : "claude, opencode, codex";
|
|
2374
2516
|
lines.push(
|
|
2375
2517
|
"",
|
|
2376
2518
|
` ${c.bold(
|
|
2377
|
-
`${skills.length} skills \xB7 ${commands.length} commands \xB7 ${mcp.length} MCP servers`
|
|
2519
|
+
`${skills.length} skills \xB7 ${commands.length} commands \xB7 ${mcp.length} MCP servers \xB7 ${plugins.length} plugins`
|
|
2378
2520
|
)} ${c.dim(`providers: ${providers2}`)}`
|
|
2379
2521
|
);
|
|
2380
2522
|
if (missingTools) {
|
|
@@ -2386,84 +2528,6 @@ var init_list = __esm({
|
|
|
2386
2528
|
}
|
|
2387
2529
|
});
|
|
2388
2530
|
|
|
2389
|
-
// src/commands/status.ts
|
|
2390
|
-
var status_exports = {};
|
|
2391
|
-
__export(status_exports, {
|
|
2392
|
-
status: () => status
|
|
2393
|
-
});
|
|
2394
|
-
var status;
|
|
2395
|
-
var init_status = __esm({
|
|
2396
|
-
"src/commands/status.ts"() {
|
|
2397
|
-
"use strict";
|
|
2398
|
-
init_repo();
|
|
2399
|
-
init_io();
|
|
2400
|
-
init_schema();
|
|
2401
|
-
init_write();
|
|
2402
|
-
init_prompts();
|
|
2403
|
-
status = async (options) => {
|
|
2404
|
-
const lock = readLockfile(options.targetRoot);
|
|
2405
|
-
if (!lock) {
|
|
2406
|
-
if (options.json) console.log(JSON.stringify({ ok: false, error: "no-lockfile" }));
|
|
2407
|
-
else await error("No quiver.lock found. Run `quiver-cli init` first.");
|
|
2408
|
-
process.exitCode = 1;
|
|
2409
|
-
return;
|
|
2410
|
-
}
|
|
2411
|
-
if (!repoCatalogExists(options.targetRoot)) {
|
|
2412
|
-
if (options.json) console.log(JSON.stringify({ ok: false, error: "no-agents" }));
|
|
2413
|
-
else await error("No .agents/ directory found. Run `quiver-cli init` first.");
|
|
2414
|
-
process.exitCode = 1;
|
|
2415
|
-
return;
|
|
2416
|
-
}
|
|
2417
|
-
const { catalog } = loadRepoCatalog(options.targetRoot, lock.catalog.source);
|
|
2418
|
-
const haveSkills = new Set(catalog.skills.map((s) => s.name));
|
|
2419
|
-
const haveCommands = new Set(catalog.commands.map((c) => c.name));
|
|
2420
|
-
const haveMcp = new Set(catalog.mcp.map((m) => m.name));
|
|
2421
|
-
const missing = [];
|
|
2422
|
-
const changed = [];
|
|
2423
|
-
const skillByName = new Map(catalog.skills.map((s) => [s.name, s]));
|
|
2424
|
-
const commandByName = new Map(catalog.commands.map((c) => [c.name, c]));
|
|
2425
|
-
const mcpByName = new Map(catalog.mcp.map((m) => [m.name, m]));
|
|
2426
|
-
for (const [id, entry] of Object.entries(lock.entries)) {
|
|
2427
|
-
const p = parseEntryId(id);
|
|
2428
|
-
if (!p) continue;
|
|
2429
|
-
if (p.type === "skill") {
|
|
2430
|
-
if (!haveSkills.has(p.name)) missing.push(id);
|
|
2431
|
-
else if (entry.type === "skill" && skillByName.get(p.name).digest !== entry.digest)
|
|
2432
|
-
changed.push(id);
|
|
2433
|
-
} else if (p.type === "command") {
|
|
2434
|
-
if (!haveCommands.has(p.name)) missing.push(id);
|
|
2435
|
-
else if (entry.type === "command" && commandByName.get(p.name).digest !== entry.digest)
|
|
2436
|
-
changed.push(id);
|
|
2437
|
-
} else if (p.type === "mcp") {
|
|
2438
|
-
if (!haveMcp.has(p.name)) missing.push(id);
|
|
2439
|
-
else if (entry.type === "mcp" && mcpByName.get(p.name).configDigest !== entry.configDigest)
|
|
2440
|
-
changed.push(id);
|
|
2441
|
-
}
|
|
2442
|
-
}
|
|
2443
|
-
const shimProblems = checkProviders(options.targetRoot, catalog, lock);
|
|
2444
|
-
const ok = missing.length === 0 && changed.length === 0 && shimProblems.length === 0;
|
|
2445
|
-
if (options.json) {
|
|
2446
|
-
console.log(JSON.stringify({ ok, missing, changed, shims: shimProblems }, null, 2));
|
|
2447
|
-
if (!ok) process.exitCode = 1;
|
|
2448
|
-
return;
|
|
2449
|
-
}
|
|
2450
|
-
if (ok) {
|
|
2451
|
-
await success("In sync: lockfile, .agents/ and provider shims agree.");
|
|
2452
|
-
return;
|
|
2453
|
-
}
|
|
2454
|
-
if (missing.length)
|
|
2455
|
-
await warn(`Missing in .agents/: ${missing.join(", ")}`);
|
|
2456
|
-
if (changed.length)
|
|
2457
|
-
await warn(`Content changed since lockfile: ${changed.join(", ")}`);
|
|
2458
|
-
if (shimProblems.length)
|
|
2459
|
-
await warn(`Provider shims out of date:
|
|
2460
|
-
- ${shimProblems.join("\n - ")}`);
|
|
2461
|
-
await info("Run `quiver-cli sync` to regenerate provider shims and refresh digests.");
|
|
2462
|
-
process.exitCode = 1;
|
|
2463
|
-
};
|
|
2464
|
-
}
|
|
2465
|
-
});
|
|
2466
|
-
|
|
2467
2531
|
// src/mcp/diff.ts
|
|
2468
2532
|
var diffSnapshots, isEmptyDiff;
|
|
2469
2533
|
var init_diff = __esm({
|
|
@@ -2598,9 +2662,12 @@ var init_snapshot = __esm({
|
|
|
2598
2662
|
var check_exports = {};
|
|
2599
2663
|
__export(check_exports, {
|
|
2600
2664
|
check: () => check,
|
|
2665
|
+
hasCommand: () => hasCommand,
|
|
2601
2666
|
summarize: () => summarize
|
|
2602
2667
|
});
|
|
2603
|
-
|
|
2668
|
+
import { accessSync as accessSync2, constants as constants2 } from "fs";
|
|
2669
|
+
import { delimiter, resolve as resolve17 } from "path";
|
|
2670
|
+
var check, report2, driftLines, list2, recommend, summarize, hasCommand, truncate2, fail;
|
|
2604
2671
|
var init_check = __esm({
|
|
2605
2672
|
"src/commands/check.ts"() {
|
|
2606
2673
|
"use strict";
|
|
@@ -2610,6 +2677,7 @@ var init_check = __esm({
|
|
|
2610
2677
|
init_diff();
|
|
2611
2678
|
init_introspect();
|
|
2612
2679
|
init_snapshot();
|
|
2680
|
+
init_write();
|
|
2613
2681
|
init_interpolate();
|
|
2614
2682
|
init_prompts();
|
|
2615
2683
|
check = async (options) => {
|
|
@@ -2624,8 +2692,10 @@ var init_check = __esm({
|
|
|
2624
2692
|
const { catalog } = loadRepoCatalog(options.targetRoot, lock.catalog.source);
|
|
2625
2693
|
const skillByName = new Map(catalog.skills.map((s) => [s.name, s]));
|
|
2626
2694
|
const commandByName = new Map(catalog.commands.map((c) => [c.name, c]));
|
|
2695
|
+
const pluginByName = new Map(catalog.plugins.map((p) => [p.name, p]));
|
|
2627
2696
|
const skillDrift = [];
|
|
2628
|
-
const
|
|
2697
|
+
const pluginRequirements = [];
|
|
2698
|
+
const checked = { skills: 0, commands: 0, mcp: 0, plugins: 0 };
|
|
2629
2699
|
for (const [id, entry] of Object.entries(lock.entries)) {
|
|
2630
2700
|
const p = parseEntryId(id);
|
|
2631
2701
|
if (!p) continue;
|
|
@@ -2639,11 +2709,24 @@ var init_check = __esm({
|
|
|
2639
2709
|
if (!cat) continue;
|
|
2640
2710
|
checked.commands += 1;
|
|
2641
2711
|
if (cat.digest !== entry.digest) skillDrift.push({ id, kind: "content" });
|
|
2712
|
+
} else if (entry.type === "plugin") {
|
|
2713
|
+
const cat = pluginByName.get(p.name);
|
|
2714
|
+
if (!cat) continue;
|
|
2715
|
+
checked.plugins += 1;
|
|
2716
|
+
if (cat.digest !== entry.digest) skillDrift.push({ id, kind: "content" });
|
|
2717
|
+
if (lock.providers?.length && !lock.providers.includes(entry.provider)) {
|
|
2718
|
+
continue;
|
|
2719
|
+
}
|
|
2720
|
+
for (const command of entry.requires) {
|
|
2721
|
+
if (!hasCommand(command)) pluginRequirements.push({ id, command });
|
|
2722
|
+
}
|
|
2642
2723
|
}
|
|
2643
2724
|
}
|
|
2725
|
+
const shimProblems = checkProviders(options.targetRoot, catalog, lock);
|
|
2644
2726
|
const mcpReports = [];
|
|
2645
2727
|
let lockChanged = false;
|
|
2646
2728
|
for (const [id, entry] of Object.entries(lock.entries)) {
|
|
2729
|
+
if (options.offline) break;
|
|
2647
2730
|
if (entry.type !== "mcp") continue;
|
|
2648
2731
|
const p = parseEntryId(id);
|
|
2649
2732
|
const catMcp = catalog.mcp.find((m) => m.name === p.name);
|
|
@@ -2677,11 +2760,18 @@ var init_check = __esm({
|
|
|
2677
2760
|
}
|
|
2678
2761
|
}
|
|
2679
2762
|
if (lockChanged) writeLockfile(options.targetRoot, lock);
|
|
2680
|
-
const hasDrift = skillDrift.length > 0 || mcpReports.some((r) => r.status === "drift");
|
|
2763
|
+
const hasDrift = skillDrift.length > 0 || pluginRequirements.length > 0 || shimProblems.length > 0 || mcpReports.some((r) => r.status === "drift");
|
|
2681
2764
|
if (options.json) {
|
|
2682
2765
|
console.log(
|
|
2683
2766
|
JSON.stringify(
|
|
2684
|
-
{
|
|
2767
|
+
{
|
|
2768
|
+
ok: !hasDrift,
|
|
2769
|
+
checked,
|
|
2770
|
+
skillDrift,
|
|
2771
|
+
pluginRequirements,
|
|
2772
|
+
shims: shimProblems,
|
|
2773
|
+
mcp: mcpReports
|
|
2774
|
+
},
|
|
2685
2775
|
null,
|
|
2686
2776
|
2
|
|
2687
2777
|
)
|
|
@@ -2689,16 +2779,35 @@ var init_check = __esm({
|
|
|
2689
2779
|
if (hasDrift) process.exitCode = 1;
|
|
2690
2780
|
return;
|
|
2691
2781
|
}
|
|
2692
|
-
await report2(
|
|
2782
|
+
await report2(
|
|
2783
|
+
skillDrift,
|
|
2784
|
+
pluginRequirements,
|
|
2785
|
+
shimProblems,
|
|
2786
|
+
mcpReports,
|
|
2787
|
+
checked,
|
|
2788
|
+
options
|
|
2789
|
+
);
|
|
2693
2790
|
if (hasDrift) process.exitCode = 1;
|
|
2694
2791
|
};
|
|
2695
|
-
report2 = async (skillDrift, mcpReports, checked, options) => {
|
|
2792
|
+
report2 = async (skillDrift, pluginRequirements, shimProblems, mcpReports, checked, options) => {
|
|
2696
2793
|
if (skillDrift.length) {
|
|
2697
2794
|
await warn(
|
|
2698
|
-
`
|
|
2795
|
+
`Managed content changed since lockfile:
|
|
2699
2796
|
- ${skillDrift.map((s) => s.id).join("\n - ")}`
|
|
2700
2797
|
);
|
|
2701
2798
|
}
|
|
2799
|
+
if (pluginRequirements.length) {
|
|
2800
|
+
await warn(
|
|
2801
|
+
`Plugin requirements missing:
|
|
2802
|
+
- ${pluginRequirements.map((issue) => `${issue.id}: ${issue.command}`).join("\n - ")}`
|
|
2803
|
+
);
|
|
2804
|
+
}
|
|
2805
|
+
if (shimProblems.length) {
|
|
2806
|
+
await warn(
|
|
2807
|
+
`Provider shims out of date:
|
|
2808
|
+
- ${shimProblems.join("\n - ")}`
|
|
2809
|
+
);
|
|
2810
|
+
}
|
|
2702
2811
|
const skipped = mcpReports.filter((r) => r.status === "skipped");
|
|
2703
2812
|
if (skipped.length) {
|
|
2704
2813
|
const names = skipped.map((r) => parseEntryId(r.id)?.name ?? r.id);
|
|
@@ -2723,12 +2832,13 @@ var init_check = __esm({
|
|
|
2723
2832
|
- ${driftLines(r.diff, options.verbose).join("\n - ")}`);
|
|
2724
2833
|
}
|
|
2725
2834
|
const summary = summarize(checked);
|
|
2726
|
-
const hasDrift = skillDrift.length > 0 || drifted.length > 0;
|
|
2727
|
-
|
|
2835
|
+
const hasDrift = skillDrift.length > 0 || shimProblems.length > 0 || drifted.length > 0;
|
|
2836
|
+
const hasProblems = hasDrift || pluginRequirements.length > 0;
|
|
2837
|
+
if (!hasProblems) {
|
|
2728
2838
|
await success(`check passed: ${summary}, no drift detected.`);
|
|
2729
2839
|
} else {
|
|
2730
2840
|
await info(`checked ${summary}, drift detected.`);
|
|
2731
|
-
await recommend(skillDrift, drifted);
|
|
2841
|
+
await recommend(skillDrift, shimProblems, drifted);
|
|
2732
2842
|
}
|
|
2733
2843
|
};
|
|
2734
2844
|
driftLines = (diff, verbose) => {
|
|
@@ -2762,9 +2872,12 @@ var init_check = __esm({
|
|
|
2762
2872
|
const rest = names.length - sample;
|
|
2763
2873
|
return `${names.length} tools (${names.slice(0, sample).join(", ")}, \u2026 +${rest} more)`;
|
|
2764
2874
|
};
|
|
2765
|
-
recommend = async (skillDrift, drifted) => {
|
|
2875
|
+
recommend = async (skillDrift, shimProblems, drifted) => {
|
|
2766
2876
|
const c = palette();
|
|
2767
2877
|
const lines = ["to update the lockfile baseline:"];
|
|
2878
|
+
if (shimProblems.length) {
|
|
2879
|
+
lines.push(` ${c.cyan("quiver-cli sync")} regenerate provider shims`);
|
|
2880
|
+
}
|
|
2768
2881
|
if (drifted.length) {
|
|
2769
2882
|
lines.push(` ${c.cyan("quiver-cli check --accept")} accept the new MCP tool snapshots`);
|
|
2770
2883
|
}
|
|
@@ -2786,8 +2899,23 @@ var init_check = __esm({
|
|
|
2786
2899
|
if (c.skills) parts.push(plural(c.skills, "skill"));
|
|
2787
2900
|
if (c.commands) parts.push(plural(c.commands, "command"));
|
|
2788
2901
|
if (c.mcp) parts.push(plural(c.mcp, "MCP server"));
|
|
2902
|
+
if (c.plugins) parts.push(plural(c.plugins, "plugin"));
|
|
2789
2903
|
return parts.length ? parts.join(", ") : "nothing";
|
|
2790
2904
|
};
|
|
2905
|
+
hasCommand = (command) => {
|
|
2906
|
+
if (!/^[A-Za-z0-9._-]+$/.test(command)) return false;
|
|
2907
|
+
const extensions = process.platform === "win32" ? (process.env["PATHEXT"] ?? ".EXE;.CMD;.BAT;.COM").split(";") : [""];
|
|
2908
|
+
for (const dir of (process.env["PATH"] ?? "").split(delimiter)) {
|
|
2909
|
+
for (const extension of extensions) {
|
|
2910
|
+
try {
|
|
2911
|
+
accessSync2(resolve17(dir, command + extension), constants2.X_OK);
|
|
2912
|
+
return true;
|
|
2913
|
+
} catch {
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
return false;
|
|
2918
|
+
};
|
|
2791
2919
|
truncate2 = (s, max = 120) => s.length > max ? s.slice(0, max) + "\u2026" : s;
|
|
2792
2920
|
fail = async (options, code, message) => {
|
|
2793
2921
|
if (options.json) console.log(JSON.stringify({ ok: false, error: code }));
|
|
@@ -2802,9 +2930,9 @@ import { execFileSync as execFileSync3 } from "child_process";
|
|
|
2802
2930
|
import {
|
|
2803
2931
|
existsSync as existsSync13,
|
|
2804
2932
|
mkdtempSync as mkdtempSync2,
|
|
2805
|
-
readFileSync as
|
|
2806
|
-
rmSync as
|
|
2807
|
-
writeFileSync as
|
|
2933
|
+
readFileSync as readFileSync9,
|
|
2934
|
+
rmSync as rmSync6,
|
|
2935
|
+
writeFileSync as writeFileSync7
|
|
2808
2936
|
} from "fs";
|
|
2809
2937
|
import { tmpdir } from "os";
|
|
2810
2938
|
import { join, resolve as resolve18 } from "path";
|
|
@@ -2819,10 +2947,10 @@ var init_upstreams = __esm({
|
|
|
2819
2947
|
loadUpstreams = (catalog) => {
|
|
2820
2948
|
const path = upstreamsPath(catalog);
|
|
2821
2949
|
if (!existsSync13(path)) return {};
|
|
2822
|
-
return JSON.parse(
|
|
2950
|
+
return JSON.parse(readFileSync9(path, "utf8"));
|
|
2823
2951
|
};
|
|
2824
2952
|
writeUpstreams = (catalog, map) => {
|
|
2825
|
-
|
|
2953
|
+
writeFileSync7(upstreamsPath(catalog), JSON.stringify(map, null, 2) + "\n");
|
|
2826
2954
|
};
|
|
2827
2955
|
fetchLatestCommit = async (origin) => {
|
|
2828
2956
|
const url = `https://api.github.com/repos/${origin.repo}/commits?path=${encodeURIComponent(origin.path)}&sha=${encodeURIComponent(origin.ref)}&per_page=1`;
|
|
@@ -2861,7 +2989,7 @@ var init_upstreams = __esm({
|
|
|
2861
2989
|
};
|
|
2862
2990
|
fetchUpstreamDir = (origin) => {
|
|
2863
2991
|
const tmp = mkdtempSync2(join(tmpdir(), "quiver-pull-"));
|
|
2864
|
-
const cleanup = () =>
|
|
2992
|
+
const cleanup = () => rmSync6(tmp, { recursive: true, force: true });
|
|
2865
2993
|
const git = (args, cwd) => {
|
|
2866
2994
|
execFileSync3("git", args, {
|
|
2867
2995
|
cwd,
|
|
@@ -2936,7 +3064,7 @@ var upstream_exports = {};
|
|
|
2936
3064
|
__export(upstream_exports, {
|
|
2937
3065
|
upstream: () => upstream
|
|
2938
3066
|
});
|
|
2939
|
-
import { cpSync as cpSync3, rmSync as
|
|
3067
|
+
import { cpSync as cpSync3, rmSync as rmSync7 } from "fs";
|
|
2940
3068
|
var upstream, guardWritableCatalog, STATUS_ORDER, pull, report3, countByStatus;
|
|
2941
3069
|
var init_upstream = __esm({
|
|
2942
3070
|
"src/commands/upstream.ts"() {
|
|
@@ -2988,7 +3116,7 @@ var init_upstream = __esm({
|
|
|
2988
3116
|
guardWritableCatalog = async (catalog) => {
|
|
2989
3117
|
if (isCatalogWritable(catalog)) return false;
|
|
2990
3118
|
await error(
|
|
2991
|
-
"upstream is a catalog-maintenance command and the catalog here is not writable (the installed package or a remote cache).\nRun it inside the quiver-cli repo, or point at a writable local catalog with --catalog
|
|
3119
|
+
"upstream is a catalog-maintenance command and the catalog here is not writable (the installed package or a remote cache).\nRun it inside the quiver-cli repo, or point at a writable local catalog with --catalog=local:/absolute/path/.agents.\nTo update a consuming repo's installed entries, use quiver-cli check / quiver-cli update instead."
|
|
2992
3120
|
);
|
|
2993
3121
|
process.exitCode = 1;
|
|
2994
3122
|
return true;
|
|
@@ -3046,7 +3174,7 @@ var init_upstream = __esm({
|
|
|
3046
3174
|
continue;
|
|
3047
3175
|
}
|
|
3048
3176
|
try {
|
|
3049
|
-
|
|
3177
|
+
rmSync7(skill.absDir, { recursive: true, force: true });
|
|
3050
3178
|
cpSync3(fetched.dir, skill.absDir, { recursive: true, dereference: true });
|
|
3051
3179
|
} finally {
|
|
3052
3180
|
fetched.cleanup();
|
|
@@ -3120,14 +3248,23 @@ var init_upstream = __esm({
|
|
|
3120
3248
|
lines.push(` ${mark[r.status]} ${c.bold(name)} ${detail} ${where}`);
|
|
3121
3249
|
}
|
|
3122
3250
|
block(lines);
|
|
3123
|
-
const drifted = sorted.filter(
|
|
3124
|
-
|
|
3125
|
-
)
|
|
3126
|
-
if (drifted.length) {
|
|
3251
|
+
const drifted = sorted.filter((r) => r.status === "drift");
|
|
3252
|
+
const curated = sorted.filter((r) => r.status === "drift-curated");
|
|
3253
|
+
if (drifted.length || curated.length) {
|
|
3127
3254
|
const hint = [""];
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3255
|
+
if (drifted.length) {
|
|
3256
|
+
hint.push(
|
|
3257
|
+
` ${c.cyan("quiver-cli upstream pull")} ${c.dim(
|
|
3258
|
+
`update the catalog copy (${drifted.map((r) => r.name).join(", ")})`
|
|
3259
|
+
)}`
|
|
3260
|
+
);
|
|
3261
|
+
}
|
|
3262
|
+
for (const r of curated) {
|
|
3263
|
+
hint.push(
|
|
3264
|
+
` ${c.yellow(r.name)}: ${c.dim(
|
|
3265
|
+
"curated \u2014 reconcile by hand, or upstream pull --force"
|
|
3266
|
+
)}`
|
|
3267
|
+
);
|
|
3131
3268
|
}
|
|
3132
3269
|
block(hint);
|
|
3133
3270
|
}
|
|
@@ -3163,83 +3300,6 @@ var init_upstream = __esm({
|
|
|
3163
3300
|
}
|
|
3164
3301
|
});
|
|
3165
3302
|
|
|
3166
|
-
// src/commands/login.ts
|
|
3167
|
-
var login_exports = {};
|
|
3168
|
-
__export(login_exports, {
|
|
3169
|
-
login: () => login
|
|
3170
|
-
});
|
|
3171
|
-
import process3 from "process";
|
|
3172
|
-
var login, promptToken, readStdin;
|
|
3173
|
-
var init_login = __esm({
|
|
3174
|
-
"src/commands/login.ts"() {
|
|
3175
|
-
"use strict";
|
|
3176
|
-
init_auth();
|
|
3177
|
-
init_prompts();
|
|
3178
|
-
login = async (options) => {
|
|
3179
|
-
const token = process3.stdin.isTTY ? await promptToken() : await readStdin();
|
|
3180
|
-
if (!token) {
|
|
3181
|
-
await error(
|
|
3182
|
-
"No token provided. Create a GitHub personal access token with `repo` read access and try again."
|
|
3183
|
-
);
|
|
3184
|
-
process3.exitCode = 1;
|
|
3185
|
-
return;
|
|
3186
|
-
}
|
|
3187
|
-
const validation = await validateToken(token);
|
|
3188
|
-
if (!validation.ok) {
|
|
3189
|
-
await error(`Token validation failed: ${validation.reason}`);
|
|
3190
|
-
process3.exitCode = 1;
|
|
3191
|
-
return;
|
|
3192
|
-
}
|
|
3193
|
-
writeStoredToken(token, validation.login);
|
|
3194
|
-
if (options.json) {
|
|
3195
|
-
console.log(JSON.stringify({ ok: true, login: validation.login }, null, 2));
|
|
3196
|
-
return;
|
|
3197
|
-
}
|
|
3198
|
-
await success(
|
|
3199
|
-
`Logged in as ${validation.login}. Token stored in ${authFilePath()}.`
|
|
3200
|
-
);
|
|
3201
|
-
};
|
|
3202
|
-
promptToken = async () => {
|
|
3203
|
-
await info(
|
|
3204
|
-
"Paste a GitHub personal access token (used for github: catalogs; needs read access to the catalog repo)."
|
|
3205
|
-
);
|
|
3206
|
-
const value = await password("GitHub token");
|
|
3207
|
-
return value?.trim() || null;
|
|
3208
|
-
};
|
|
3209
|
-
readStdin = async () => {
|
|
3210
|
-
let data = "";
|
|
3211
|
-
for await (const chunk of process3.stdin) data += chunk;
|
|
3212
|
-
return data.trim() || null;
|
|
3213
|
-
};
|
|
3214
|
-
}
|
|
3215
|
-
});
|
|
3216
|
-
|
|
3217
|
-
// src/commands/logout.ts
|
|
3218
|
-
var logout_exports = {};
|
|
3219
|
-
__export(logout_exports, {
|
|
3220
|
-
logout: () => logout
|
|
3221
|
-
});
|
|
3222
|
-
var logout;
|
|
3223
|
-
var init_logout = __esm({
|
|
3224
|
-
"src/commands/logout.ts"() {
|
|
3225
|
-
"use strict";
|
|
3226
|
-
init_auth();
|
|
3227
|
-
init_prompts();
|
|
3228
|
-
logout = async (options) => {
|
|
3229
|
-
const removed = deleteStoredToken();
|
|
3230
|
-
if (options.json) {
|
|
3231
|
-
console.log(JSON.stringify({ ok: true, removed }, null, 2));
|
|
3232
|
-
return;
|
|
3233
|
-
}
|
|
3234
|
-
if (removed) {
|
|
3235
|
-
await success(`Logged out. Removed ${authFilePath()}.`);
|
|
3236
|
-
} else {
|
|
3237
|
-
await info("Not logged in - nothing to remove.");
|
|
3238
|
-
}
|
|
3239
|
-
};
|
|
3240
|
-
}
|
|
3241
|
-
});
|
|
3242
|
-
|
|
3243
3303
|
// src/version/notifier.ts
|
|
3244
3304
|
var notifier_exports = {};
|
|
3245
3305
|
__export(notifier_exports, {
|
|
@@ -3249,8 +3309,8 @@ __export(notifier_exports, {
|
|
|
3249
3309
|
installHint: () => installHint,
|
|
3250
3310
|
notifierSuppressed: () => notifierSuppressed
|
|
3251
3311
|
});
|
|
3252
|
-
import { existsSync as existsSync14, mkdirSync as
|
|
3253
|
-
import { homedir as
|
|
3312
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
|
|
3313
|
+
import { homedir as homedir2 } from "os";
|
|
3254
3314
|
import { dirname as dirname6, resolve as resolve19 } from "path";
|
|
3255
3315
|
var REGISTRY_URL, CHECK_TTL_MS, FETCH_TIMEOUT_MS, INSTALL_HINT, cacheFilePath, installHint, getCurrentVersion, compareSemver, readCache, writeCache, fetchLatestVersion, checkForUpdate, notifierSuppressed;
|
|
3256
3316
|
var init_notifier = __esm({
|
|
@@ -3262,14 +3322,14 @@ var init_notifier = __esm({
|
|
|
3262
3322
|
FETCH_TIMEOUT_MS = 2e3;
|
|
3263
3323
|
INSTALL_HINT = "pnpm add -g quiver-cli";
|
|
3264
3324
|
cacheFilePath = () => {
|
|
3265
|
-
const base = process.env["XDG_CACHE_HOME"] || resolve19(
|
|
3325
|
+
const base = process.env["XDG_CACHE_HOME"] || resolve19(homedir2(), ".cache");
|
|
3266
3326
|
return resolve19(base, "quiver", "update-check.json");
|
|
3267
3327
|
};
|
|
3268
3328
|
installHint = () => INSTALL_HINT;
|
|
3269
3329
|
getCurrentVersion = () => {
|
|
3270
3330
|
try {
|
|
3271
3331
|
const pkg = JSON.parse(
|
|
3272
|
-
|
|
3332
|
+
readFileSync10(resolve19(packageRoot, "package.json"), "utf8")
|
|
3273
3333
|
);
|
|
3274
3334
|
return pkg.version;
|
|
3275
3335
|
} catch {
|
|
@@ -3297,7 +3357,7 @@ var init_notifier = __esm({
|
|
|
3297
3357
|
const path = cacheFilePath();
|
|
3298
3358
|
if (!existsSync14(path)) return null;
|
|
3299
3359
|
try {
|
|
3300
|
-
return JSON.parse(
|
|
3360
|
+
return JSON.parse(readFileSync10(path, "utf8"));
|
|
3301
3361
|
} catch {
|
|
3302
3362
|
return null;
|
|
3303
3363
|
}
|
|
@@ -3305,8 +3365,8 @@ var init_notifier = __esm({
|
|
|
3305
3365
|
writeCache = (cache) => {
|
|
3306
3366
|
try {
|
|
3307
3367
|
const path = cacheFilePath();
|
|
3308
|
-
|
|
3309
|
-
|
|
3368
|
+
mkdirSync6(dirname6(path), { recursive: true });
|
|
3369
|
+
writeFileSync8(path, JSON.stringify(cache, null, 2) + "\n");
|
|
3310
3370
|
} catch {
|
|
3311
3371
|
}
|
|
3312
3372
|
};
|
|
@@ -3350,58 +3410,85 @@ var init_notifier = __esm({
|
|
|
3350
3410
|
|
|
3351
3411
|
// src/cli.ts
|
|
3352
3412
|
init_prompts();
|
|
3353
|
-
import
|
|
3354
|
-
var HELP = `quiver-cli - compose agent skills, commands & MCP servers into any repo
|
|
3413
|
+
import process3 from "process";
|
|
3414
|
+
var HELP = `quiver-cli - compose agent skills, commands, plugins & MCP servers into any repo
|
|
3355
3415
|
|
|
3356
3416
|
Usage:
|
|
3357
3417
|
quiver-cli <command> [options]
|
|
3358
3418
|
|
|
3359
3419
|
Commands:
|
|
3360
3420
|
init Interactive picker over the catalog; write native configs + quiver.lock
|
|
3361
|
-
add <id> Add
|
|
3421
|
+
add <id> Add an entry (skill:<name>, command:<name>, mcp:<name>, plugin:<name>)
|
|
3362
3422
|
remove <id> Remove a single entry; keep lockfile + configs consistent
|
|
3363
3423
|
sync Regenerate provider configs from .agents/ (warns on drift)
|
|
3364
3424
|
providers [a,b] Change which tools get configs (claude, opencode, codex)
|
|
3365
3425
|
update [id] Pull newer catalog content into .agents/ (all or one entry)
|
|
3366
|
-
list Show installed entries (skills, commands, MCP tool counts)
|
|
3367
|
-
|
|
3368
|
-
|
|
3426
|
+
list Show installed entries (skills, commands, plugins, MCP tool counts)
|
|
3427
|
+
check Detect drift: skill digests, provider shims, MCP tool
|
|
3428
|
+
snapshots (--offline skips MCP re-introspection)
|
|
3369
3429
|
upstream Catalog maintenance: check source repos for skill updates
|
|
3370
3430
|
(run in the quiver-cli repo or with a writable --catalog)
|
|
3371
3431
|
upstream pull Pull latest upstream content into the catalog [skill]
|
|
3372
|
-
login Store a GitHub token for remote (github:) catalogs
|
|
3373
|
-
logout Remove the stored GitHub token
|
|
3374
3432
|
help Show this help
|
|
3375
3433
|
version Show the quiver-cli version
|
|
3376
3434
|
|
|
3377
3435
|
Options:
|
|
3378
3436
|
-f, --force Overwrite existing files
|
|
3379
3437
|
--all, -y Keep everything without prompting (non-interactive)
|
|
3380
|
-
--json Machine-readable output (
|
|
3438
|
+
--json Machine-readable output (check/upstream/list)
|
|
3381
3439
|
-V, --verbose Show full tool lists and description diffs (check)
|
|
3382
3440
|
--accept Record the current MCP tool snapshots as the new baseline (check)
|
|
3441
|
+
--offline Skip MCP re-introspection; check digests + shims only (check)
|
|
3442
|
+
--dry-run Report what would change without writing (update)
|
|
3383
3443
|
--providers=a,b Generate configs only for these tools (init, sync, providers)
|
|
3384
3444
|
--catalog=<source> Catalog source for init (e.g. github:owner/repo[/path][#ref])
|
|
3385
3445
|
--introspect-stdio Allow introspecting stdio MCP servers (runs foreign code)
|
|
3386
3446
|
-v, --version Show the quiver-cli version
|
|
3387
3447
|
`;
|
|
3448
|
+
var KNOWN_FLAGS = /* @__PURE__ */ new Set([
|
|
3449
|
+
"--force",
|
|
3450
|
+
"-f",
|
|
3451
|
+
"--all",
|
|
3452
|
+
"--yes",
|
|
3453
|
+
"-y",
|
|
3454
|
+
"--json",
|
|
3455
|
+
"--verbose",
|
|
3456
|
+
"-V",
|
|
3457
|
+
"--accept",
|
|
3458
|
+
"--offline",
|
|
3459
|
+
"--dry-run",
|
|
3460
|
+
"--introspect-stdio",
|
|
3461
|
+
"--help",
|
|
3462
|
+
"-h",
|
|
3463
|
+
"--version",
|
|
3464
|
+
"-v"
|
|
3465
|
+
]);
|
|
3466
|
+
var KNOWN_VALUE_FLAGS = ["--providers", "--catalog"];
|
|
3467
|
+
var isKnownFlag = (arg) => {
|
|
3468
|
+
if (KNOWN_FLAGS.has(arg)) return true;
|
|
3469
|
+
return KNOWN_VALUE_FLAGS.some((f) => arg.startsWith(`${f}=`));
|
|
3470
|
+
};
|
|
3388
3471
|
var parse2 = (argv) => {
|
|
3389
3472
|
const [command = "init", ...rest] = argv;
|
|
3390
3473
|
const flags = new Set(rest.filter((a) => a.startsWith("-")));
|
|
3391
3474
|
const positionals = rest.filter((a) => !a.startsWith("-"));
|
|
3475
|
+
const unknownFlags = [...flags].filter((a) => !isKnownFlag(a));
|
|
3392
3476
|
const providersFlag = rest.find((a) => a.startsWith("--providers="));
|
|
3393
3477
|
const providers2 = providersFlag ? providersFlag.slice("--providers=".length).split(",").map((p) => p.trim()).filter(Boolean) : null;
|
|
3394
3478
|
const catalogFlag = rest.find((a) => a.startsWith("--catalog="));
|
|
3395
3479
|
const catalog = catalogFlag ? catalogFlag.slice("--catalog=".length).trim() || null : null;
|
|
3396
3480
|
return {
|
|
3397
3481
|
command,
|
|
3482
|
+
unknownFlags,
|
|
3398
3483
|
options: {
|
|
3399
|
-
targetRoot:
|
|
3484
|
+
targetRoot: process3.cwd(),
|
|
3400
3485
|
force: flags.has("--force") || flags.has("-f"),
|
|
3401
3486
|
all: flags.has("--all") || flags.has("--yes") || flags.has("-y"),
|
|
3402
3487
|
json: flags.has("--json"),
|
|
3403
3488
|
verbose: flags.has("--verbose") || flags.has("-V"),
|
|
3404
3489
|
accept: flags.has("--accept"),
|
|
3490
|
+
offline: flags.has("--offline"),
|
|
3491
|
+
dryRun: flags.has("--dry-run"),
|
|
3405
3492
|
introspectStdio: flags.has("--introspect-stdio"),
|
|
3406
3493
|
providers: providers2,
|
|
3407
3494
|
catalog,
|
|
@@ -3410,13 +3497,22 @@ var parse2 = (argv) => {
|
|
|
3410
3497
|
};
|
|
3411
3498
|
};
|
|
3412
3499
|
var run = async () => {
|
|
3413
|
-
const { command, options } = parse2(
|
|
3414
|
-
if (
|
|
3500
|
+
const { command, options, unknownFlags } = parse2(process3.argv.slice(2));
|
|
3501
|
+
if (unknownFlags.length) {
|
|
3502
|
+
await error(
|
|
3503
|
+
`Unknown option${unknownFlags.length === 1 ? "" : "s"}: ${unknownFlags.join(", ")}
|
|
3504
|
+
`
|
|
3505
|
+
);
|
|
3506
|
+
console.log(HELP);
|
|
3507
|
+
process3.exitCode = 1;
|
|
3508
|
+
return;
|
|
3509
|
+
}
|
|
3510
|
+
if (process3.stdin.isTTY && !options.all) {
|
|
3415
3511
|
const { checkNodeForCommand: checkNodeForCommand2 } = await Promise.resolve().then(() => (init_node_guard(), node_guard_exports));
|
|
3416
3512
|
const guard = checkNodeForCommand2(command);
|
|
3417
3513
|
if (!guard.ok) {
|
|
3418
3514
|
await error(guard.message);
|
|
3419
|
-
|
|
3515
|
+
process3.exitCode = 1;
|
|
3420
3516
|
return;
|
|
3421
3517
|
}
|
|
3422
3518
|
}
|
|
@@ -3458,11 +3554,6 @@ var run = async () => {
|
|
|
3458
3554
|
await list3(options);
|
|
3459
3555
|
break;
|
|
3460
3556
|
}
|
|
3461
|
-
case "status": {
|
|
3462
|
-
const { status: status2 } = await Promise.resolve().then(() => (init_status(), status_exports));
|
|
3463
|
-
await status2(options);
|
|
3464
|
-
break;
|
|
3465
|
-
}
|
|
3466
3557
|
case "check": {
|
|
3467
3558
|
const { check: check2 } = await Promise.resolve().then(() => (init_check(), check_exports));
|
|
3468
3559
|
await check2(options);
|
|
@@ -3473,16 +3564,6 @@ var run = async () => {
|
|
|
3473
3564
|
await upstream2(options);
|
|
3474
3565
|
break;
|
|
3475
3566
|
}
|
|
3476
|
-
case "login": {
|
|
3477
|
-
const { login: login2 } = await Promise.resolve().then(() => (init_login(), login_exports));
|
|
3478
|
-
await login2(options);
|
|
3479
|
-
break;
|
|
3480
|
-
}
|
|
3481
|
-
case "logout": {
|
|
3482
|
-
const { logout: logout2 } = await Promise.resolve().then(() => (init_logout(), logout_exports));
|
|
3483
|
-
await logout2(options);
|
|
3484
|
-
break;
|
|
3485
|
-
}
|
|
3486
3567
|
case "help":
|
|
3487
3568
|
case "--help":
|
|
3488
3569
|
case "-h":
|
|
@@ -3505,7 +3586,7 @@ var run = async () => {
|
|
|
3505
3586
|
console.error(`Unknown command: ${command}
|
|
3506
3587
|
`);
|
|
3507
3588
|
console.log(HELP);
|
|
3508
|
-
|
|
3589
|
+
process3.exitCode = 1;
|
|
3509
3590
|
}
|
|
3510
3591
|
if (!["version", "--version", "-v", "help", "--help", "-h"].includes(command)) {
|
|
3511
3592
|
await maybeNotifyUpdate(options.json);
|
|
@@ -3527,7 +3608,14 @@ ${c.yellow("\u25B2")} ${c.bold("update available")} ${c.dim(
|
|
|
3527
3608
|
} catch {
|
|
3528
3609
|
}
|
|
3529
3610
|
};
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3611
|
+
var main = () => {
|
|
3612
|
+
run().catch(async (err) => {
|
|
3613
|
+
await error(err instanceof Error ? err.message : String(err));
|
|
3614
|
+
process3.exitCode = 1;
|
|
3615
|
+
});
|
|
3616
|
+
};
|
|
3617
|
+
export {
|
|
3618
|
+
main,
|
|
3619
|
+
parse2 as parse,
|
|
3620
|
+
run
|
|
3621
|
+
};
|