cue-ai 0.9.0 → 0.9.2
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/CHANGELOG.md +40 -0
- package/README.md +82 -33
- package/bin/cue-review-progress +107 -0
- package/bin/cue-review-watch +98 -0
- package/dist/cue.js +7352 -3744
- package/package.json +16 -5
- package/profiles/_types.ts +9 -0
- package/profiles/backend/profile.yaml +2 -0
- package/profiles/blog-writer/profile.yaml +10 -0
- package/profiles/browser/profile.yaml +9 -2
- package/profiles/builder/profile.yaml +3 -6
- package/profiles/career/profile.yaml +13 -2
- package/profiles/claude-api/profile.yaml +1 -1
- package/profiles/commerce/profile.yaml +27 -3
- package/profiles/core/logo.png +0 -0
- package/profiles/core/profile.yaml +62 -2
- package/profiles/dash-merge-test/profile.yaml +109 -0
- package/profiles/designer/profile.yaml +2 -0
- package/profiles/designer-medusa-next/profile.yaml +4 -1
- package/profiles/designer-medusa-vite/profile.yaml +4 -1
- package/profiles/docs-writer/profile.yaml +3 -1
- package/profiles/eu-tender-research/README.md +48 -0
- package/profiles/eu-tender-research/logo.png +0 -0
- package/profiles/eu-tender-research/profile.yaml +108 -0
- package/profiles/finance/logo.png +0 -0
- package/profiles/finance/profile.yaml +46 -0
- package/profiles/frontend/profile.yaml +5 -9
- package/profiles/growth/profile.yaml +2 -3
- package/profiles/gstack/profile.yaml +15 -0
- package/profiles/higgsfield/profile.yaml +3 -0
- package/profiles/hyperframes/logo.png +0 -0
- package/profiles/hyperframes/profile.yaml +59 -0
- package/profiles/improver/profile.yaml +88 -0
- package/profiles/marketing/profile.yaml +0 -3
- package/profiles/medusa-dev/profile.yaml +2 -0
- package/profiles/medusa-next/profile.yaml +2 -3
- package/profiles/medusa-vite/profile.yaml +2 -3
- package/profiles/n8n/logo.png +0 -0
- package/profiles/n8n/profile.yaml +50 -0
- package/profiles/nextjs/profile.yaml +2 -3
- package/profiles/ops/profile.yaml +2 -0
- package/profiles/postizz/profile.yaml +13 -3
- package/profiles/python/profile.yaml +3 -0
- package/profiles/research/profile.yaml +3 -1
- package/profiles/schema.json +10 -0
- package/profiles/secops/profile.yaml +2 -0
- package/profiles/seo/profile.yaml +56 -0
- package/profiles/skill-writer/profile.yaml +8 -0
- package/profiles/ssh/profile.yaml +32 -0
- package/profiles/strapi/logo.png +0 -0
- package/profiles/strapi/profile.yaml +45 -0
- package/profiles/stripe/logo.png +0 -0
- package/profiles/stripe/profile.yaml +1 -0
- package/profiles/supabase/logo.png +0 -0
- package/profiles/supabase/profile.yaml +85 -0
- package/profiles/vercel/logo.png +0 -0
- package/profiles/vercel/profile.yaml +25 -1
- package/profiles/vite/profile.yaml +4 -3
- package/profiles/web-frontend-base/profile.yaml +5 -4
- package/profiles/webshop/profile.yaml +23 -5
- package/profiles/x-growth-bot/profile.yaml +44 -0
- package/resources/icons/generate-icons.py +128 -2
- package/resources/mcps/configs/claude.sanitized.json +42 -0
- package/resources/mcps/configs/codex.sanitized.json +7 -0
- package/resources/skills/skills/career/resume-version-manager/SKILL.md +351 -0
- package/resources/skills/skills/career/salary-negotiation-prep/SKILL.md +378 -0
- package/resources/skills/skills/content/pdf/SKILL.md +2 -0
- package/resources/skills/skills/content/postiz-cards/SKILL.md +48 -0
- package/resources/skills/skills/content/postiz-cards/scripts/analytics.sh +38 -0
- package/resources/skills/skills/content/postiz-cards/scripts/card.sh +42 -0
- package/resources/skills/skills/content/postiz-cards/scripts/lint.py +38 -0
- package/resources/skills/skills/design/headless-gif-demo/SKILL.md +1 -1
- package/resources/skills/skills/design/readme-svg-design/SKILL.md +1 -1
- package/resources/skills/skills/eu-funding/grant-outreach/SKILL.md +70 -0
- package/resources/skills/skills/eu-funding/hu-grant-finder/SKILL.md +114 -0
- package/resources/skills/skills/eu-funding/hu-grant-finder/evals.md +26 -0
- package/resources/skills/skills/eu-funding/ted-tender-search/SKILL.md +80 -0
- package/resources/skills/skills/eu-funding/ted-tender-search/evals.md +26 -0
- package/resources/skills/skills/eu-funding/ted-tender-search/scripts/ted-search.sh +46 -0
- package/resources/skills/skills/event-design/wedding-invitations/SKILL.md +1 -1
- package/resources/skills/skills/github/gx-agents/SKILL.md +96 -0
- package/resources/skills/skills/gstack/design-shotgun/SKILL.md +1 -1
- package/resources/skills/skills/marketing/ab-test-analyzer/SKILL.md +1 -1
- package/resources/skills/skills/marketing/ab-test-setup-and-analysis/SKILL.md +1 -1
- package/resources/skills/skills/marketing/account-structure-review/SKILL.md +1 -1
- package/resources/skills/skills/marketing/ad-copy-variant-generator/SKILL.md +1 -1
- package/resources/skills/skills/marketing/ad-extension-audit/SKILL.md +1 -1
- package/resources/skills/skills/marketing/ad-spend-allocator/SKILL.md +1 -1
- package/resources/skills/skills/marketing/anomaly-detection/SKILL.md +1 -1
- package/resources/skills/skills/marketing/attribution-model-comparison/SKILL.md +1 -1
- package/resources/skills/skills/marketing/audience-overlap-analysis/SKILL.md +7 -1
- package/resources/skills/skills/marketing/bid-strategy-recommendations/SKILL.md +7 -1
- package/resources/skills/skills/marketing/budget-scenario-planner/SKILL.md +6 -1
- package/resources/skills/skills/marketing/campaign-naming-convention-builder/SKILL.md +7 -1
- package/resources/skills/skills/marketing/channel-mix-optimizer/SKILL.md +7 -1
- package/resources/skills/skills/marketing/client-report-narratives/SKILL.md +6 -1
- package/resources/skills/skills/marketing/competitor-creative-analysis/SKILL.md +1 -1
- package/resources/skills/skills/marketing/competitor-teardown/SKILL.md +1 -1
- package/resources/skills/skills/marketing/content-repurposer/SKILL.md +1 -1
- package/resources/skills/skills/marketing/conversion-path-analysis/SKILL.md +1 -1
- package/resources/skills/skills/marketing/cpa-diagnostics/SKILL.md +1 -1
- package/resources/skills/skills/marketing/creative-fatigue-detection/SKILL.md +1 -1
- package/resources/skills/skills/marketing/day-hour-performance-breakdown/SKILL.md +1 -1
- package/resources/skills/skills/marketing/device-performance-split/SKILL.md +1 -1
- package/resources/skills/skills/marketing/e2e-seo-assistant/SKILL.md +1 -1
- package/resources/skills/skills/marketing/email-sequence-writer/SKILL.md +1 -1
- package/resources/skills/skills/marketing/frequency-cap-recommendations/SKILL.md +1 -1
- package/resources/skills/skills/marketing/geo-performance-analysis/SKILL.md +1 -1
- package/resources/skills/skills/marketing/google-ads-audit/SKILL.md +1 -1
- package/resources/skills/skills/marketing/icp-research-assistant/SKILL.md +1 -1
- package/resources/skills/skills/marketing/keyword-cannibalization-check/SKILL.md +1 -1
- package/resources/skills/skills/marketing/landing-page-audit/SKILL.md +1 -1
- package/resources/skills/skills/marketing/landing-page-audit-quick/SKILL.md +1 -1
- package/resources/skills/skills/marketing/linkedin-ads-audit/SKILL.md +1 -1
- package/resources/skills/skills/marketing/meta-ads-audit/SKILL.md +1 -1
- package/resources/skills/skills/marketing/pacing-monitor/SKILL.md +1 -1
- package/resources/skills/skills/marketing/performance-benchmarking/SKILL.md +1 -1
- package/resources/skills/skills/marketing/programmatic-seo-builder/SKILL.md +1 -1
- package/resources/skills/skills/marketing/quality-score-breakdown/SKILL.md +1 -1
- package/resources/skills/skills/marketing/reddit-ads-audit/SKILL.md +1 -1
- package/resources/skills/skills/marketing/retargeting-window-analysis/SKILL.md +1 -1
- package/resources/skills/skills/marketing/roas-forecasting/SKILL.md +1 -1
- package/resources/skills/skills/marketing/search-term-mining/SKILL.md +1 -1
- package/resources/skills/skills/marketing/utm-tracking-generator/SKILL.md +1 -1
- package/resources/skills/skills/marketing/wasted-spend-finder/SKILL.md +1 -1
- package/resources/skills/skills/marketing/weekly-account-summary/SKILL.md +1 -1
- package/resources/skills/skills/meta/awesome-list-submit/SKILL.md +4 -4
- package/resources/skills/skills/meta/cue-dashboard/SKILL.md +109 -0
- package/resources/skills/skills/meta/cue-developer/SKILL.md +161 -0
- package/resources/skills/skills/meta/cue-developer/evals/evals.json +57 -0
- package/resources/skills/skills/meta/cue-developer/references/architecture.md +65 -0
- package/resources/skills/skills/meta/cue-developer/references/build_and_test.md +72 -0
- package/resources/skills/skills/meta/cue-developer/references/contributing.md +75 -0
- package/resources/skills/skills/meta/cue-developer/references/conventions.md +57 -0
- package/resources/skills/skills/meta/cue-developer/references/first_time_setup.md +51 -0
- package/resources/skills/skills/meta/cue-developer/references/skill_and_mcp_authoring.md +84 -0
- package/resources/skills/skills/meta/cue-developer/references/troubleshooting.md +42 -0
- package/resources/skills/skills/meta/delegation-check/SKILL.md +148 -0
- package/resources/skills/skills/meta/delegation-check/specs/scan-algorithm.md +125 -0
- package/resources/skills/skills/meta/delegation-check/specs/separation-rules.md +190 -0
- package/resources/skills/skills/meta/focus/SKILL.md +62 -0
- package/resources/skills/skills/meta/help/SKILL.md +1 -1
- package/resources/skills/skills/meta/integrity-tags/SKILL.md +2 -0
- package/resources/skills/skills/meta/next-steps/SKILL.md +124 -0
- package/resources/skills/skills/meta/next-steps/evals/eval-set.json +92 -0
- package/resources/skills/skills/meta/profile-from-docs/SKILL.md +141 -0
- package/resources/skills/skills/meta/ralph-loop/SKILL.md +83 -0
- package/resources/skills/skills/meta/ralph-loop/scripts/loop.sh +73 -0
- package/resources/skills/skills/meta/skill-simplify/SKILL.md +136 -0
- package/resources/skills/skills/meta/skill-simplify/phases/01-analysis.md +173 -0
- package/resources/skills/skills/meta/skill-simplify/phases/02-optimize.md +104 -0
- package/resources/skills/skills/meta/skill-simplify/phases/03-check.md +145 -0
- package/resources/skills/skills/meta/smart-loader/scripts/smart-lookup.sh +13 -4
- package/resources/skills/skills/meta/verify-council/SKILL.md +182 -0
- package/resources/skills/skills/meta/verify-council/references/lane-prompts.md +103 -0
- package/resources/skills/skills/meta/verify-council/references/workflow.js +217 -0
- package/resources/skills/skills/nvidia/aiq-research/SKILL.md +1 -1
- package/resources/skills/skills/nvidia/cuopt-developer/SKILL.md +16 -1
- package/resources/skills/skills/nvidia/cuopt-developer/resources/contributing.md +2 -2
- package/resources/skills/skills/nvidia/cuopt-developer/resources/numerical_debugging.md +128 -0
- package/resources/skills/skills/nvidia/cuopt-developer/resources/python_bindings.md +2 -9
- package/resources/skills/skills/nvidia/cuopt-developer/resources/vrp_skills.md +166 -0
- package/resources/skills/skills/nvidia/cuopt-install/SKILL.md +2 -10
- package/resources/skills/skills/nvidia/cuopt-numerical-optimization-api-c/SKILL.md +3 -23
- package/resources/skills/skills/nvidia/cuopt-numerical-optimization-api-c/resources/examples.md +40 -20
- package/resources/skills/skills/nvidia/cuopt-numerical-optimization-api-python/SKILL.md +5 -1
- package/resources/skills/skills/nvidia/skill-evolution/SKILL.md +4 -5
- package/resources/skills/skills/research/trendradar/SKILL.md +1 -1
- package/resources/skills/skills/ssh/ssh-config/SKILL.md +94 -0
- package/resources/skills/skills/ssh/ssh-copy/SKILL.md +92 -0
- package/resources/skills/skills/ssh/ssh-harden/SKILL.md +108 -0
- package/resources/skills/skills/ssh/ssh-keys/SKILL.md +82 -0
- package/resources/skills/skills/ssh/ssh-paste-image/LICENSE +28 -0
- package/resources/skills/skills/ssh/ssh-paste-image/SKILL.md +149 -0
- package/resources/skills/skills/ssh/ssh-paste-image/scripts/build.sh +29 -0
- package/resources/skills/skills/ssh/ssh-paste-image/scripts/client/go.mod +3 -0
- package/resources/skills/skills/ssh/ssh-paste-image/scripts/client/main.go +79 -0
- package/resources/skills/skills/ssh/ssh-paste-image/scripts/daemon/ccimgd.service +12 -0
- package/resources/skills/skills/ssh/ssh-paste-image/scripts/daemon/com.ccimgd.plist +20 -0
- package/resources/skills/skills/ssh/ssh-paste-image/scripts/daemon/go.mod +3 -0
- package/resources/skills/skills/ssh/ssh-paste-image/scripts/daemon/main.go +98 -0
- package/resources/skills/skills/ssh/ssh-tunnel/SKILL.md +96 -0
- package/resources/skills/skills/strapi/building-with-strapi/SKILL.md +112 -0
- package/resources/skills/skills/strapi/strapi-cli/SKILL.md +93 -0
- package/resources/skills/skills/strapi/strapi-content-api/SKILL.md +115 -0
- package/resources/skills/skills/strapi/strapi-deploy/SKILL.md +89 -0
- package/resources/skills/skills/strapi/strapi-mcp-setup/SKILL.md +101 -0
- package/resources/skills/skills/strapi/strapi-plugins/SKILL.md +97 -0
- package/resources/skills/skills/tools/context7/SKILL.md +101 -0
- package/resources/skills/skills/tools/opensrc/SKILL.md +1 -1
- package/resources/skills/skills/tools/portless/SKILL.md +186 -0
- package/resources/skills/skills/xbot/operate/SKILL.md +229 -0
- package/src/commands/_index.ts +8 -0
- package/src/commands/ai-score.e2e.test.ts +11 -4
- package/src/commands/ai.ts +3 -4
- package/src/commands/auto-detect.ts +1 -1
- package/src/commands/cli.test.ts +1 -2
- package/src/commands/cli.ts +1 -1
- package/src/commands/cloud.ts +1 -1
- package/src/commands/current.ts +1 -4
- package/src/commands/dash.test.ts +110 -0
- package/src/commands/dash.ts +194 -0
- package/src/commands/dashboard.ts +26 -0
- package/src/commands/diff.ts +1 -1
- package/src/commands/discover.test.ts +1 -1
- package/src/commands/discover.ts +90 -40
- package/src/commands/doctor.test.ts +58 -0
- package/src/commands/doctor.ts +79 -3
- package/src/commands/eval-behavior.ts +1 -1
- package/src/commands/eval.ts +2 -2
- package/src/commands/evolve.ts +4 -3
- package/src/commands/failures.test.ts +1 -1
- package/src/commands/features-batch1.test.ts +6 -1
- package/src/commands/icon.ts +1 -5
- package/src/commands/import-profile.ts +1 -1
- package/src/commands/init.ts +50 -7
- package/src/commands/install-sh.e2e.test.ts +65 -0
- package/src/commands/launch-handoff.e2e.test.ts +88 -0
- package/src/commands/launch.e2e.test.ts +8 -1
- package/src/commands/launch.test.ts +29 -0
- package/src/commands/launch.ts +185 -131
- package/src/commands/lock.ts +0 -1
- package/src/commands/marketplace.ts +0 -4
- package/src/commands/materialize.ts +1 -1
- package/src/commands/mem.ts +341 -0
- package/src/commands/optimizer.ts +0 -3
- package/src/commands/playground.ts +1 -2
- package/src/commands/profile-draft-skill.ts +1 -1
- package/src/commands/replay-whatif.ts +1 -6
- package/src/commands/score.ts +2 -2
- package/src/commands/security.test.ts +88 -0
- package/src/commands/security.ts +74 -28
- package/src/commands/shell.test.ts +65 -4
- package/src/commands/shell.ts +67 -7
- package/src/commands/skills-test.ts +0 -1
- package/src/commands/skills.ts +28 -2
- package/src/commands/sources.ts +1 -2
- package/src/commands/status.ts +2 -6
- package/src/commands/submit-profile.ts +1 -1
- package/src/commands/suggest.ts +35 -10
- package/src/commands/trigger-gaps.test.ts +50 -0
- package/src/commands/trigger-gaps.ts +63 -29
- package/src/commands/update.ts +1 -1
- package/src/commands/validate.ts +16 -4
- package/src/commands/watch-live.ts +1 -1
- package/src/commands/workspace.ts +1 -1
- package/src/index.ts +26 -10
- package/src/lib/active-sessions.ts +1 -1
- package/src/lib/agent-adapters.test.ts +100 -0
- package/src/lib/agent-adapters.ts +2 -2
- package/src/lib/analytics.test.ts +88 -0
- package/src/lib/analytics.ts +82 -1
- package/src/lib/auto-detect.test.ts +10 -4
- package/src/lib/auto-detect.ts +19 -23
- package/src/lib/brand-icons.ts +0 -1
- package/src/lib/cache.ts +2 -3
- package/src/lib/claude-mem-env.test.ts +148 -0
- package/src/lib/claude-mem-env.ts +172 -0
- package/src/lib/combo-history.test.ts +53 -0
- package/src/lib/combo-history.ts +83 -0
- package/src/lib/companion-detect.test.ts +108 -0
- package/src/lib/companion-detect.ts +140 -0
- package/src/lib/companion-fetch.ts +4 -6
- package/src/lib/conditional-skills.test.ts +1 -1
- package/src/lib/config-paths.test.ts +53 -0
- package/src/lib/config-paths.ts +33 -0
- package/src/lib/dashboard-server.test.ts +351 -0
- package/src/lib/dashboard-server.ts +1476 -27
- package/src/lib/debug-log.test.ts +66 -0
- package/src/lib/debug-log.ts +45 -0
- package/src/lib/mcp-catalog.test.ts +102 -0
- package/src/lib/mcp-catalog.ts +193 -0
- package/src/lib/pair-suggestions.test.ts +111 -0
- package/src/lib/pair-suggestions.ts +98 -5
- package/src/lib/permissions.test.ts +76 -0
- package/src/lib/permissions.ts +125 -0
- package/src/lib/picker.test.ts +1106 -1
- package/src/lib/picker.ts +1230 -142
- package/src/lib/plugin-discovery.ts +126 -0
- package/src/lib/pr-poster.ts +1 -1
- package/src/lib/pr-throttle.ts +2 -6
- package/src/lib/profile-linter.test.ts +67 -1
- package/src/lib/profile-linter.ts +59 -14
- package/src/lib/profile-loader.test.ts +21 -0
- package/src/lib/profile-loader.ts +22 -3
- package/src/lib/profile-metrics.ts +2 -6
- package/src/lib/profile-names.test.ts +58 -0
- package/src/lib/repos.test.ts +57 -0
- package/src/lib/repos.ts +167 -0
- package/src/lib/resolver-npx.ts +10 -1
- package/src/lib/runtime-materializer.test.ts +200 -3
- package/src/lib/runtime-materializer.ts +129 -20
- package/src/lib/shared-profiles.ts +2 -3
- package/src/lib/skill-clis.test.ts +113 -0
- package/src/lib/skill-clis.ts +232 -0
- package/src/lib/skill-dependencies.ts +9 -1
- package/src/lib/skill-deps.ts +1 -1
- package/src/lib/skill-linter.ts +1 -1
- package/src/lib/skill-quality.ts +0 -1
- package/src/lib/skill-sandbox.test.ts +1 -1
- package/src/lib/skills-lock.test.ts +1 -1
- package/src/lib/telemetry-consent.ts +3 -5
- package/src/lib/telemetry-report.test.ts +2 -2
- package/src/lib/token-budget.ts +111 -0
- package/src/lib/trigger-gaps.test.ts +70 -0
- package/src/lib/trigger-gaps.ts +48 -6
- package/src/lib/tui/data.ts +1 -5
- package/src/lib/workflow-store.ts +150 -0
- package/src/lib/workspace-secrets.ts +0 -4
- package/src/lib/workspaces.ts +1 -1
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cue dash` — a thin CLI over the running cue studio dashboard's REST API
|
|
3
|
+
* (`cue dashboard`, default http://127.0.0.1:7891/api/v1/*). Read subcommands
|
|
4
|
+
* query live profile/skill/MCP/gap data; mutating ones add an MCP to a profile,
|
|
5
|
+
* kill a session, or run a merge. The dashboard must be running.
|
|
6
|
+
*
|
|
7
|
+
* The MCP server (resources/mcps/cue-dashboard) wraps the same endpoints as
|
|
8
|
+
* tools; both are independent HTTP clients — dashboard-server.ts is the only
|
|
9
|
+
* source of truth.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const DEFAULT_PORT = 7891;
|
|
13
|
+
const DEFAULT_HOST = "127.0.0.1";
|
|
14
|
+
|
|
15
|
+
export interface DashArgs {
|
|
16
|
+
sub: string;
|
|
17
|
+
rest: string[];
|
|
18
|
+
port: number;
|
|
19
|
+
host: string;
|
|
20
|
+
json: boolean;
|
|
21
|
+
sigkill: boolean;
|
|
22
|
+
as?: string;
|
|
23
|
+
help: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Parse `cue dash` argv: `<sub> [positional...] [--port N] [--host H] [--json] [--sigkill] [--as name]`. */
|
|
27
|
+
export function parseDashArgs(argv: string[]): DashArgs {
|
|
28
|
+
const out: DashArgs = {
|
|
29
|
+
sub: "",
|
|
30
|
+
rest: [],
|
|
31
|
+
port: Number(process.env.CUE_DASH_PORT) || DEFAULT_PORT,
|
|
32
|
+
host: process.env.CUE_DASH_HOST || DEFAULT_HOST,
|
|
33
|
+
json: false,
|
|
34
|
+
sigkill: false,
|
|
35
|
+
help: false,
|
|
36
|
+
};
|
|
37
|
+
for (let i = 0; i < argv.length; i++) {
|
|
38
|
+
const a = argv[i]!;
|
|
39
|
+
if (a === "--help" || a === "-h") out.help = true;
|
|
40
|
+
else if (a === "--json") out.json = true;
|
|
41
|
+
else if (a === "--sigkill") out.sigkill = true;
|
|
42
|
+
else if (a === "--port") { const v = Number(argv[++i]); if (Number.isFinite(v) && v > 0 && v < 65536) out.port = v; }
|
|
43
|
+
else if (a === "--host") { const v = argv[++i]; if (v) out.host = v; }
|
|
44
|
+
else if (a === "--as") { const v = argv[++i]; if (v) out.as = v; }
|
|
45
|
+
else if (!out.sub) out.sub = a;
|
|
46
|
+
else out.rest.push(a);
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Build the absolute API URL for a path + optional query. Exported for tests. */
|
|
52
|
+
export function dashUrl(host: string, port: number, path: string, query?: Record<string, string>): string {
|
|
53
|
+
const qs = query && Object.keys(query).length > 0 ? "?" + new URLSearchParams(query).toString() : "";
|
|
54
|
+
return `http://${host}:${port}/api/v1${path}${qs}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
type Envelope<T> = { ok: true; data: T } | { ok: false; error: string };
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Fetch one API path and unwrap the `{ok, data|error}` envelope. Throws on a
|
|
61
|
+
* `{ok:false}` body (message = the server's `error`) and on a refused
|
|
62
|
+
* connection (the dashboard isn't running) with an actionable hint.
|
|
63
|
+
*/
|
|
64
|
+
export async function dashFetch<T = unknown>(
|
|
65
|
+
args: Pick<DashArgs, "host" | "port">,
|
|
66
|
+
path: string,
|
|
67
|
+
opts: { query?: Record<string, string>; method?: "GET" | "POST"; body?: unknown } = {},
|
|
68
|
+
): Promise<T> {
|
|
69
|
+
const url = dashUrl(args.host, args.port, path, opts.query);
|
|
70
|
+
let res: Response;
|
|
71
|
+
try {
|
|
72
|
+
res = await fetch(url, {
|
|
73
|
+
method: opts.method ?? "GET",
|
|
74
|
+
headers: opts.body ? { "content-type": "application/json" } : undefined,
|
|
75
|
+
body: opts.body ? JSON.stringify(opts.body) : undefined,
|
|
76
|
+
});
|
|
77
|
+
} catch (err) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
`cannot reach the dashboard at ${args.host}:${args.port} (${(err as Error).message}). ` +
|
|
80
|
+
`Start it with \`cue dashboard\`.`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
let env: Envelope<T>;
|
|
84
|
+
try { env = (await res.json()) as Envelope<T>; }
|
|
85
|
+
catch { throw new Error(`dashboard returned non-JSON (HTTP ${res.status}) for ${path}`); }
|
|
86
|
+
if (!env.ok) throw new Error(env.error);
|
|
87
|
+
return env.data;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Subcommand → how to call the API. Pure data so the help text and the MCP can mirror it. */
|
|
91
|
+
interface Route {
|
|
92
|
+
summary: string;
|
|
93
|
+
build: (a: DashArgs) => { path: string; query?: Record<string, string>; method?: "POST"; body?: unknown };
|
|
94
|
+
mutating?: boolean;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const ROUTES: Record<string, Route> = {
|
|
98
|
+
status: { summary: "dashboard + runtime status", build: () => ({ path: "/status" }) },
|
|
99
|
+
profiles: { summary: "all profiles with counts", build: () => ({ path: "/profiles" }) },
|
|
100
|
+
profile: {
|
|
101
|
+
summary: "one profile's resolved skills/mcps/plugins/commands (arg: <name>)",
|
|
102
|
+
build: (a) => ({ path: "/profile-detail", query: { profile: a.rest[0] ?? "" } }),
|
|
103
|
+
},
|
|
104
|
+
"trigger-gaps": {
|
|
105
|
+
summary: "skills that matched a prompt but never fired (arg: [profile])",
|
|
106
|
+
build: (a) => ({ path: "/trigger-gaps", query: a.rest[0] ? { profile: a.rest[0] } : undefined }),
|
|
107
|
+
},
|
|
108
|
+
"skill-report": { summary: "per-skill usage report", build: () => ({ path: "/skill-report" }) },
|
|
109
|
+
pairs: { summary: "profile pair-affinity suggestions", build: () => ({ path: "/pairs" }) },
|
|
110
|
+
sessions: { summary: "active cue sessions", build: () => ({ path: "/active-sessions" }) },
|
|
111
|
+
mcps: { summary: "the MCP catalog cue knows about", build: () => ({ path: "/mcps/catalog" }) },
|
|
112
|
+
plugins: { summary: "plugins discovered on the machine", build: () => ({ path: "/plugins/discovered" }) },
|
|
113
|
+
timeline: { summary: "telemetry timeline", build: () => ({ path: "/telemetry/timeline" }) },
|
|
114
|
+
"add-mcp": {
|
|
115
|
+
summary: "add an MCP to a profile (args: <profile> <mcp>)",
|
|
116
|
+
mutating: true,
|
|
117
|
+
build: (a) => ({ path: "/mcps/add", method: "POST", body: { profile: a.rest[0], id: a.rest[1] } }),
|
|
118
|
+
},
|
|
119
|
+
kill: {
|
|
120
|
+
summary: "kill a cue session by pid (arg: <pid>, --sigkill for SIGKILL)",
|
|
121
|
+
mutating: true,
|
|
122
|
+
build: (a) => ({ path: "/sessions/kill", method: "POST", body: { pid: Number(a.rest[0]), signal: a.sigkill ? "SIGKILL" : "SIGTERM" } }),
|
|
123
|
+
},
|
|
124
|
+
"merge-preview": {
|
|
125
|
+
summary: "preview merging 2+ profiles (args: <name> <name> ...)",
|
|
126
|
+
mutating: false,
|
|
127
|
+
build: (a) => ({ path: "/merge/preview", method: "POST", body: { names: a.rest } }),
|
|
128
|
+
},
|
|
129
|
+
"merge-save": {
|
|
130
|
+
summary: "save a merge of 2+ profiles (args: <name> <name> ... [--as <name>])",
|
|
131
|
+
mutating: true,
|
|
132
|
+
build: (a) => ({ path: "/merge/save", method: "POST", body: { names: a.rest, name: a.as } }),
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
function helpText(): string {
|
|
137
|
+
const rows = Object.entries(ROUTES)
|
|
138
|
+
.map(([k, r]) => ` ${k.padEnd(15)} ${r.summary}${r.mutating ? " [mutates]" : ""}`)
|
|
139
|
+
.join("\n");
|
|
140
|
+
return [
|
|
141
|
+
"cue dash — query and drive the running cue studio dashboard",
|
|
142
|
+
"",
|
|
143
|
+
"Usage: cue dash <subcommand> [args] [--port N] [--host H] [--json]",
|
|
144
|
+
"",
|
|
145
|
+
"Subcommands:",
|
|
146
|
+
rows,
|
|
147
|
+
"",
|
|
148
|
+
"Defaults: --host 127.0.0.1 --port 7891 (override with CUE_DASH_HOST/CUE_DASH_PORT).",
|
|
149
|
+
"Requires `cue dashboard` to be running.",
|
|
150
|
+
"",
|
|
151
|
+
].join("\n");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Write to stdout and wait until it has flushed to the OS. `cue` exits via
|
|
156
|
+
* `process.exit()` (src/index.ts), which does NOT drain a pending stdout
|
|
157
|
+
* buffer — so a large payload piped to another process (`cue dash profile X |
|
|
158
|
+
* jq`) would be truncated. Awaiting the write callback blocks until the reader
|
|
159
|
+
* has drained the pipe, so the full output always lands.
|
|
160
|
+
*/
|
|
161
|
+
function writeOut(s: string): Promise<void> {
|
|
162
|
+
return new Promise<void>((resolve) => {
|
|
163
|
+
process.stdout.write(s, () => resolve());
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export async function run(argv: string[]): Promise<number> {
|
|
168
|
+
const a = parseDashArgs(argv);
|
|
169
|
+
if (a.help || !a.sub) {
|
|
170
|
+
process.stdout.write(helpText());
|
|
171
|
+
return a.sub ? 0 : a.help ? 0 : 1;
|
|
172
|
+
}
|
|
173
|
+
const route = ROUTES[a.sub];
|
|
174
|
+
if (!route) {
|
|
175
|
+
process.stderr.write(`unknown subcommand: ${a.sub}\n\n${helpText()}`);
|
|
176
|
+
return 1;
|
|
177
|
+
}
|
|
178
|
+
// Guard required positionals so we fail fast with a clear message, not a
|
|
179
|
+
// server-side "missing-profile" / NaN-pid.
|
|
180
|
+
if (a.sub === "profile" && !a.rest[0]) { process.stderr.write("profile: needs a profile name\n"); return 1; }
|
|
181
|
+
if (a.sub === "add-mcp" && (!a.rest[0] || !a.rest[1])) { process.stderr.write("add-mcp: needs <profile> <mcp>\n"); return 1; }
|
|
182
|
+
if (a.sub === "kill" && !Number.isFinite(Number(a.rest[0]))) { process.stderr.write("kill: needs a numeric <pid>\n"); return 1; }
|
|
183
|
+
|
|
184
|
+
const spec = route.build(a);
|
|
185
|
+
try {
|
|
186
|
+
const data = await dashFetch(a, spec.path, { query: spec.query, method: spec.method, body: spec.body });
|
|
187
|
+
if (route.mutating && !a.json) await writeOut(`✓ ${a.sub}: done\n`);
|
|
188
|
+
await writeOut(JSON.stringify(data, null, a.json ? 0 : 2) + "\n");
|
|
189
|
+
return 0;
|
|
190
|
+
} catch (err) {
|
|
191
|
+
process.stderr.write(`✗ ${a.sub}: ${(err as Error).message}\n`);
|
|
192
|
+
return 1;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -48,6 +48,32 @@ async function toWebRequest(req: IncomingMessage, origin: string): Promise<Reque
|
|
|
48
48
|
async function writeWebResponse(res: Response, out: ServerResponse): Promise<void> {
|
|
49
49
|
const headers: Record<string, string> = {};
|
|
50
50
|
res.headers.forEach((v, k) => { headers[k] = v; });
|
|
51
|
+
|
|
52
|
+
// Streaming bodies (Server-Sent Events) must be piped chunk-by-chunk, not
|
|
53
|
+
// buffered — `await res.arrayBuffer()` on a never-ending stream would hang
|
|
54
|
+
// forever and the client would never see an event.
|
|
55
|
+
const isStream = (headers["content-type"] ?? "").includes("text/event-stream");
|
|
56
|
+
if (isStream && res.body) {
|
|
57
|
+
out.writeHead(res.status, headers);
|
|
58
|
+
out.flushHeaders();
|
|
59
|
+
const reader = res.body.getReader();
|
|
60
|
+
// Client disconnect → cancel the reader so the stream's interval timers stop.
|
|
61
|
+
const onClose = () => { void reader.cancel().catch(() => { /* already gone */ }); };
|
|
62
|
+
out.on("close", onClose);
|
|
63
|
+
try {
|
|
64
|
+
for (;;) {
|
|
65
|
+
const { done, value } = await reader.read();
|
|
66
|
+
if (done) break;
|
|
67
|
+
out.write(Buffer.from(value));
|
|
68
|
+
}
|
|
69
|
+
} catch { /* client went away mid-write */ }
|
|
70
|
+
finally {
|
|
71
|
+
out.off("close", onClose);
|
|
72
|
+
out.end();
|
|
73
|
+
}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
51
77
|
out.writeHead(res.status, headers);
|
|
52
78
|
const buf = Buffer.from(await res.arrayBuffer());
|
|
53
79
|
out.end(buf);
|
package/src/commands/diff.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* `cue diff --live <target>` — show impact of switching from current to target.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
7
7
|
import { join, resolve, dirname } from "node:path";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { describe, expect, test, beforeEach, afterEach } from "bun:test";
|
|
7
7
|
import { mkdirSync, writeFileSync, rmSync, existsSync, readFileSync, readdirSync } from "node:fs";
|
|
8
|
-
import { tmpdir,
|
|
8
|
+
import { tmpdir, } from "node:os";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
|
|
11
11
|
import { run as discoverRun } from "./discover";
|
package/src/commands/discover.ts
CHANGED
|
@@ -16,10 +16,11 @@ import { resolve, dirname, join } from "node:path";
|
|
|
16
16
|
import { fileURLToPath } from "node:url";
|
|
17
17
|
import { homedir } from "node:os";
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import { listProfiles } from "../lib/profile-loader";
|
|
20
20
|
import { clusterByKeywords, clusterByEmbeddings, unclustered, type Cluster, type ClusterItem } from "../lib/cluster-skills";
|
|
21
21
|
import { findRealClaudeBin } from "../lib/claude-binary";
|
|
22
22
|
import { fetchCompanionFiles, detectSkillPath } from "../lib/companion-fetch";
|
|
23
|
+
import { gateFreshSkill } from "./security";
|
|
23
24
|
|
|
24
25
|
const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
25
26
|
// Cache path resolved lazily so tests can redirect via XDG_CONFIG_HOME without
|
|
@@ -521,11 +522,11 @@ export function applyFilters(gems: GemRepo[], f: GemFilter): GemRepo[] {
|
|
|
521
522
|
// Profile suggestion (keyword matching)
|
|
522
523
|
// ---------------------------------------------------------------------------
|
|
523
524
|
|
|
524
|
-
const PROFILE_KEYWORDS: Record<string, string[]> = {
|
|
525
|
+
export const PROFILE_KEYWORDS: Record<string, string[]> = {
|
|
525
526
|
backend: ["api", "server", "express", "fastapi", "django", "flask", "webhook", "database", "sql", "graphql", "deploy", "docker", "kubernetes", "microservice", "redis", "postgres", "mongo", "supabase", "prisma"],
|
|
526
527
|
frontend: ["react", "vue", "svelte", "nextjs", "css", "tailwind", "component", "browser", "dom", "ui", "ux", "responsive", "animation", "spa"],
|
|
527
528
|
nextjs: ["nextjs", "next.js", "vercel", "app-router", "server-component", "next-auth"],
|
|
528
|
-
"python
|
|
529
|
+
"python": ["python", "fastapi", "django", "flask", "sqlalchemy", "pytest", "pip", "uvicorn", "pydantic", "celery"],
|
|
529
530
|
rust: ["rust", "cargo", "tokio", "crate", "wasm", "async-std"],
|
|
530
531
|
"go-api": ["golang", "gin", "echo", "chi", "gorm", "goroutine"],
|
|
531
532
|
cybersecurity: ["security", "pentest", "vulnerability", "exploit", "forensic", "dfir", "red-team", "blue-team", "malware", "audit", "cve", "owasp", "threat", "osint", "recon", "dork", "credential", "phishing"],
|
|
@@ -535,7 +536,7 @@ const PROFILE_KEYWORDS: Record<string, string[]> = {
|
|
|
535
536
|
threejs: ["three.js", "threejs", "webgl", "shader", "3d", "scene", "geometry"],
|
|
536
537
|
video: ["video", "ffmpeg", "transcription", "frame", "subtitle", "stream", "recording", "youtube"],
|
|
537
538
|
marketing: ["seo", "marketing", "copywriting", "growth", "conversion", "analytics", "campaign", "funnel", "landing-page"],
|
|
538
|
-
medusa: ["medusa", "ecommerce", "storefront", "shop", "cart", "checkout", "product-catalog", "amazon", "seller"],
|
|
539
|
+
"medusa-dev": ["medusa", "ecommerce", "storefront", "shop", "cart", "checkout", "product-catalog", "amazon", "seller"],
|
|
539
540
|
"fleet-control": ["multi-agent", "orchestrat", "coordinator", "dispatch", "parallel", "swarm", "colony"],
|
|
540
541
|
};
|
|
541
542
|
|
|
@@ -554,8 +555,8 @@ const NICHE_SUBJECT_HINTS: RegExp[] = [
|
|
|
554
555
|
/\b(bilibili|spotify|reddit|bbc|wechat|notion|jira|servicenow|obsidian|home\s?assistant|farming\s?simulator|gospel|grant)\b/i,
|
|
555
556
|
];
|
|
556
557
|
|
|
557
|
-
const STACK_PROFILES: ReadonlySet<string> = new Set([
|
|
558
|
-
"frontend", "backend", "nextjs", "python
|
|
558
|
+
export const STACK_PROFILES: ReadonlySet<string> = new Set([
|
|
559
|
+
"frontend", "backend", "nextjs", "python", "rust", "go-api", "threejs",
|
|
559
560
|
]);
|
|
560
561
|
|
|
561
562
|
export function suggestProfiles(repo: GemRepo): string[] {
|
|
@@ -588,7 +589,7 @@ export function suggestProfiles(repo: GemRepo): string[] {
|
|
|
588
589
|
}
|
|
589
590
|
|
|
590
591
|
// Language-based boost
|
|
591
|
-
if (profile === "python
|
|
592
|
+
if (profile === "python" && lang === "python") hits += 2;
|
|
592
593
|
if (profile === "rust" && lang === "rust") hits += 2;
|
|
593
594
|
if (profile === "go-api" && lang === "go") hits += 2;
|
|
594
595
|
if (profile === "frontend" && (lang === "typescript" || lang === "javascript")) hits += 1;
|
|
@@ -656,7 +657,7 @@ export function buildProfileQueries(profile: string): { q: string; label: string
|
|
|
656
657
|
backend: ["api server deploy", "webhook microservice", "database migration", "docker kubernetes skill", "ci cd pipeline"],
|
|
657
658
|
frontend: ["react component skill", "ui design system", "tailwind css", "browser testing", "responsive web"],
|
|
658
659
|
nextjs: ["nextjs skill", "next.js vercel", "app router server component", "next-auth"],
|
|
659
|
-
"python
|
|
660
|
+
"python": ["python fastapi skill", "django api", "flask sqlalchemy", "pytest automation"],
|
|
660
661
|
rust: ["rust cargo skill", "rust cli tool", "tokio async", "rust wasm"],
|
|
661
662
|
"go-api": ["golang api skill", "go gin echo", "golang microservice"],
|
|
662
663
|
cybersecurity: ["security audit skill", "pentest tool", "vulnerability scanner", "red team blue team", "threat detection"],
|
|
@@ -666,7 +667,7 @@ export function buildProfileQueries(profile: string): { q: string; label: string
|
|
|
666
667
|
threejs: ["three.js skill", "webgl shader", "3d scene interactive"],
|
|
667
668
|
video: ["video processing skill", "ffmpeg automation", "transcription subtitle", "youtube tool"],
|
|
668
669
|
marketing: ["seo optimization skill", "marketing automation", "copywriting ai", "conversion funnel", "growth hacking"],
|
|
669
|
-
medusa: ["medusa ecommerce", "storefront skill", "shopping cart", "product catalog", "amazon seller"],
|
|
670
|
+
"medusa-dev": ["medusa ecommerce", "storefront skill", "shopping cart", "product catalog", "amazon seller"],
|
|
670
671
|
"fleet-control": ["multi-agent orchestration", "agent coordinator", "parallel agent", "task dispatch"],
|
|
671
672
|
coolify: ["coolify deploy", "self-hosted paas", "server management"],
|
|
672
673
|
hostinger: ["hosting dns", "vps management", "domain config"],
|
|
@@ -1010,7 +1011,7 @@ tags: [claude-code, ${profile}, skills, mcp, ai-agents]
|
|
|
1010
1011
|
|
|
1011
1012
|
`;
|
|
1012
1013
|
md += `# Claude Code Skills for \`${profile}\`\n\n`;
|
|
1013
|
-
md += `> ${gems.length} community-built skills curated by [cue](https://github.com/opencue/
|
|
1014
|
+
md += `> ${gems.length} community-built skills curated by [cue](https://github.com/opencue/cuecards) for the **${profile}** profile.\n`;
|
|
1014
1015
|
md += `> Each one was discovered via GitHub Code Search, scored on signal quality, and mapped to this profile by keyword overlap.\n\n`;
|
|
1015
1016
|
md += `**[← back to all discovered skills](./index.md)**\n\n---\n\n`;
|
|
1016
1017
|
|
|
@@ -1032,7 +1033,7 @@ tags: [claude-code, ${profile}, skills, mcp, ai-agents]
|
|
|
1032
1033
|
}
|
|
1033
1034
|
md += `\`\`\`bash\ncue skills add ${gem.full_name} --profile ${profile}\n\`\`\`\n\n---\n\n`;
|
|
1034
1035
|
}
|
|
1035
|
-
md += `## About this list\n\nGenerated by [cue](https://github.com/opencue/
|
|
1036
|
+
md += `## About this list\n\nGenerated by [cue](https://github.com/opencue/cuecards) — an open-source agent profile manager. cue runs nightly GitHub Code Search for \`filename:SKILL.md\` and scores each repo by recency, skill format, MCP integration, and engagement signals.\n\n**Authors:** if you'd rather not be listed, add \`<!-- cue: ignore -->\` to your README — we respect it permanently. Want to opt in explicitly? Add \`<!-- cue: ok -->\`.\n\n`;
|
|
1036
1037
|
return md;
|
|
1037
1038
|
}
|
|
1038
1039
|
|
|
@@ -1048,7 +1049,7 @@ tags: [claude-code, skills, mcp, ai-agents, marketplace]
|
|
|
1048
1049
|
|
|
1049
1050
|
# 🎯 Discovered Claude Code Skills
|
|
1050
1051
|
|
|
1051
|
-
> **${totalGems} hidden-gem skills** discovered by [cue](https://github.com/opencue/
|
|
1052
|
+
> **${totalGems} hidden-gem skills** discovered by [cue](https://github.com/opencue/cuecards) across **${byProfile.size} profiles**.
|
|
1052
1053
|
> Last updated: ${updated.split("T")[0]} · refreshed nightly via GitHub Code Search.
|
|
1053
1054
|
|
|
1054
1055
|
## Browse by profile
|
|
@@ -1062,7 +1063,7 @@ tags: [claude-code, skills, mcp, ai-agents, marketplace]
|
|
|
1062
1063
|
md += `| [**${profile}**](./${profile}.md) | ${gems.length} | ${samples} |\n`;
|
|
1063
1064
|
}
|
|
1064
1065
|
md += `\n## How scoring works\n\n| Tier | Score | Meaning |\n|---|---|---|\n| 💎 exceptional | 8+ | Active repo, proper skill format, low star count (true hidden gem) |\n| ✨ strong | 5–7 | Good signal mix — proven format or active maintainer |\n| 🔹 potential | 3–4 | Some signal — worth a look |\n\n`;
|
|
1065
|
-
md += `## Use cue to install any of these\n\n\`\`\`bash\nnpx cue@latest\ncue skills add owner/repo --profile <profile>\n\`\`\`\n\n## About cue\n\ncue is an agent profile manager for Claude Code and Codex CLI. [github.com/opencue/
|
|
1066
|
+
md += `## Use cue to install any of these\n\n\`\`\`bash\nnpx cue@latest\ncue skills add owner/repo --profile <profile>\n\`\`\`\n\n## About cue\n\ncue is an agent profile manager for Claude Code and Codex CLI. [github.com/opencue/cuecards](https://github.com/opencue/cuecards)\n`;
|
|
1066
1067
|
return md;
|
|
1067
1068
|
}
|
|
1068
1069
|
|
|
@@ -1113,21 +1114,21 @@ function buildIndexHtml(byProfile: Map<string, GemRepo[]>, totalGems: number, up
|
|
|
1113
1114
|
<meta property="og:title" content="Discovered Claude Code Skills · cue">
|
|
1114
1115
|
<meta property="og:description" content="${totalGems} community Claude Code skills curated by cue.">
|
|
1115
1116
|
<meta property="og:type" content="website">
|
|
1116
|
-
<link rel="canonical" href="https://opencue.github.io/
|
|
1117
|
+
<link rel="canonical" href="https://opencue.github.io/cuecards/discovered/">
|
|
1117
1118
|
<style>body{font:16px/1.6 -apple-system,sans-serif;max-width:760px;margin:2em auto;padding:0 1em;color:#222}table{border-collapse:collapse;width:100%}th,td{padding:.5em .75em;border-bottom:1px solid #eee;text-align:left}code{background:#f4f4f4;padding:1px 5px;border-radius:3px;font-size:.9em}a{color:#0a58ca;text-decoration:none}a:hover{text-decoration:underline}</style>
|
|
1118
1119
|
<script type="application/ld+json">
|
|
1119
1120
|
${jsonLd}
|
|
1120
1121
|
</script>
|
|
1121
1122
|
</head><body>
|
|
1122
1123
|
<h1>🎯 Discovered Claude Code Skills</h1>
|
|
1123
|
-
<p><strong>${totalGems} hidden-gem skills</strong> discovered by <a href="https://github.com/opencue/
|
|
1124
|
+
<p><strong>${totalGems} hidden-gem skills</strong> discovered by <a href="https://github.com/opencue/cuecards">cue</a> across <strong>${byProfile.size} profiles</strong>.</p>
|
|
1124
1125
|
<p><small>Last updated: ${updated.split("T")[0]} · refreshed nightly via GitHub Code Search.</small></p>
|
|
1125
1126
|
<h2>Browse by profile</h2>
|
|
1126
1127
|
<table><thead><tr><th>Profile</th><th>Skills</th><th>Sample</th></tr></thead>
|
|
1127
1128
|
<tbody>
|
|
1128
1129
|
${rows}
|
|
1129
1130
|
</tbody></table>
|
|
1130
|
-
<p><a href="https://github.com/opencue/
|
|
1131
|
+
<p><a href="https://github.com/opencue/cuecards">github.com/opencue/cuecards</a></p>
|
|
1131
1132
|
</body></html>
|
|
1132
1133
|
`;
|
|
1133
1134
|
}
|
|
@@ -1156,7 +1157,7 @@ function buildProfileHtml(profile: string, gems: GemRepo[], updated: string): st
|
|
|
1156
1157
|
<meta name="description" content="${gems.length} community Claude Code skills for ${profile}, curated by cue. Discovered via GitHub Code Search, scored on signal quality.">
|
|
1157
1158
|
<meta property="og:title" content="Claude Code Skills for ${profile} · cue">
|
|
1158
1159
|
<meta property="og:description" content="${gems.length} skills for ${profile}, curated by cue.">
|
|
1159
|
-
<link rel="canonical" href="https://opencue.github.io/
|
|
1160
|
+
<link rel="canonical" href="https://opencue.github.io/cuecards/discovered/${profile}.html">
|
|
1160
1161
|
<style>body{font:16px/1.6 -apple-system,sans-serif;max-width:760px;margin:2em auto;padding:0 1em;color:#222}article{border-bottom:1px solid #eee;padding:1em 0}pre{background:#f4f4f4;padding:.6em;border-radius:4px;overflow-x:auto}a{color:#0a58ca;text-decoration:none}a:hover{text-decoration:underline}</style>
|
|
1161
1162
|
<script type="application/ld+json">
|
|
1162
1163
|
${jsonLd}
|
|
@@ -1164,7 +1165,7 @@ ${jsonLd}
|
|
|
1164
1165
|
</head><body>
|
|
1165
1166
|
<p><a href="./index.html">← back to all profiles</a></p>
|
|
1166
1167
|
<h1>Claude Code Skills for <code>${profile}</code></h1>
|
|
1167
|
-
<p>${gems.length} skills discovered by <a href="https://github.com/opencue/
|
|
1168
|
+
<p>${gems.length} skills discovered by <a href="https://github.com/opencue/cuecards">cue</a>. Last updated ${updated.split("T")[0]}.</p>
|
|
1168
1169
|
${cards}
|
|
1169
1170
|
</body></html>
|
|
1170
1171
|
`;
|
|
@@ -1220,7 +1221,7 @@ cue skills add ${gem.full_name}${gem.suggested_profiles[0] ? ` --profile ${gem.s
|
|
|
1220
1221
|
|
|
1221
1222
|
## About
|
|
1222
1223
|
|
|
1223
|
-
This page was auto-generated by [cue](https://github.com/opencue/
|
|
1224
|
+
This page was auto-generated by [cue](https://github.com/opencue/cuecards) — an open-source agent profile manager for Claude Code, Codex, Cursor, Cline, Gemini, Copilot, and 4 other AI coding agents. cue scopes skills + MCPs + plugins per-directory so each project only loads what it needs.
|
|
1224
1225
|
|
|
1225
1226
|
**Repo author:** if you'd rather we don't list this skill, add \`<!-- cue: ignore -->\` to your README and we'll skip it permanently.
|
|
1226
1227
|
|
|
@@ -1259,7 +1260,7 @@ function buildRepoHtml(gem: GemRepo, updated: string): string {
|
|
|
1259
1260
|
<meta name="description" content="${desc.replace(/"/g, """)}">
|
|
1260
1261
|
<meta property="og:title" content="${gem.full_name} — Claude Code skill">
|
|
1261
1262
|
<meta property="og:description" content="${desc.replace(/"/g, """)}">
|
|
1262
|
-
<link rel="canonical" href="https://opencue.github.io/
|
|
1263
|
+
<link rel="canonical" href="https://opencue.github.io/cuecards/discovered/skills/${gem.full_name.replace("/", "-").toLowerCase()}.html">
|
|
1263
1264
|
<style>body{font:16px/1.6 -apple-system,sans-serif;max-width:760px;margin:2em auto;padding:0 1em;color:#222}code{background:#f4f4f4;padding:1px 5px;border-radius:3px;font-size:.9em}pre{background:#f4f4f4;padding:.6em;border-radius:4px;overflow-x:auto}a{color:#0a58ca;text-decoration:none}a:hover{text-decoration:underline}</style>
|
|
1264
1265
|
<script type="application/ld+json">
|
|
1265
1266
|
${JSON.stringify(jsonLd, null, 2)}
|
|
@@ -1276,7 +1277,7 @@ ${JSON.stringify(jsonLd, null, 2)}
|
|
|
1276
1277
|
cue skills add ${gem.full_name}${gem.suggested_profiles[0] ? ` --profile ${gem.suggested_profiles[0]}` : ""}</code></pre>
|
|
1277
1278
|
<p><a href="${gem.url}">View repo on GitHub →</a></p>
|
|
1278
1279
|
<hr>
|
|
1279
|
-
<p><small>This page was auto-generated by <a href="https://github.com/opencue/
|
|
1280
|
+
<p><small>This page was auto-generated by <a href="https://github.com/opencue/cuecards">cue</a>. Repo authors can opt out by adding <code><!-- cue: ignore --></code> to their README.</small></p>
|
|
1280
1281
|
</body></html>
|
|
1281
1282
|
`;
|
|
1282
1283
|
}
|
|
@@ -1286,7 +1287,7 @@ cue skills add ${gem.full_name}${gem.suggested_profiles[0] ? ` --profile ${gem.s
|
|
|
1286
1287
|
// ---------------------------------------------------------------------------
|
|
1287
1288
|
|
|
1288
1289
|
function buildSitemap(byProfile: Map<string, GemRepo[]>, gems: GemRepo[], updated: string): string {
|
|
1289
|
-
const base = "https://opencue.github.io/
|
|
1290
|
+
const base = "https://opencue.github.io/cuecards/discovered";
|
|
1290
1291
|
const date = updated.split("T")[0];
|
|
1291
1292
|
const urls: string[] = [];
|
|
1292
1293
|
urls.push(`<url><loc>${base}/index.html</loc><lastmod>${date}</lastmod><changefreq>daily</changefreq><priority>1.0</priority></url>`);
|
|
@@ -1415,7 +1416,7 @@ Want out? Add \`<!-- cue: ignore -->\` to your README.
|
|
|
1415
1416
|
---
|
|
1416
1417
|
|
|
1417
1418
|
<p align="center">
|
|
1418
|
-
<a href="https://github.com/opencue/
|
|
1419
|
+
<a href="https://github.com/opencue/cuecards">
|
|
1419
1420
|
<img src="https://img.shields.io/badge/powered_by-cue-6366f1?style=flat-square&labelColor=1e1b4b" alt="powered by cue">
|
|
1420
1421
|
</a>
|
|
1421
1422
|
</p>
|
|
@@ -1568,7 +1569,7 @@ const NOTIFY_LOG = join(
|
|
|
1568
1569
|
|
|
1569
1570
|
interface NotifyLog {
|
|
1570
1571
|
notified: Record<string, { date: string; issueUrl: string }>;
|
|
1571
|
-
// Daily digest discussion posts in opencue/
|
|
1572
|
+
// Daily digest discussion posts in opencue/cuecards, keyed by YYYY-MM-DD.
|
|
1572
1573
|
digests?: Record<string, { discussionUrl: string; gems: string[] }>;
|
|
1573
1574
|
}
|
|
1574
1575
|
|
|
@@ -1587,10 +1588,10 @@ function saveNotifyLog(log: NotifyLog): void {
|
|
|
1587
1588
|
}
|
|
1588
1589
|
|
|
1589
1590
|
// ---------------------------------------------------------------------------
|
|
1590
|
-
// Daily digest discussion (in opencue/
|
|
1591
|
+
// Daily digest discussion (in opencue/cuecards) — lower-pressure analog to --notify
|
|
1591
1592
|
// ---------------------------------------------------------------------------
|
|
1592
1593
|
|
|
1593
|
-
const DIGEST_REPO = process.env.CUE_DIGEST_REPO ?? "opencue/
|
|
1594
|
+
const DIGEST_REPO = process.env.CUE_DIGEST_REPO ?? "opencue/cuecards";
|
|
1594
1595
|
const DIGEST_CATEGORY_PREF = ["Discoveries", "Show and tell", "Announcements", "General"];
|
|
1595
1596
|
|
|
1596
1597
|
interface DiscussionTarget { repoId: string; categoryId: string; categoryName: string }
|
|
@@ -1644,7 +1645,7 @@ function buildDigestBody(gems: GemRepo[], date: string): string {
|
|
|
1644
1645
|
sections.push(`### \`${profile}\` (${list.length})\n\n${lines}`);
|
|
1645
1646
|
}
|
|
1646
1647
|
|
|
1647
|
-
return `> Daily digest from [\`cue discover\`](https://github.com/opencue/
|
|
1648
|
+
return `> Daily digest from [\`cue discover\`](https://github.com/opencue/cuecards) — repos newly indexed on **${date}**, grouped by profile.
|
|
1648
1649
|
|
|
1649
1650
|
cue scans GitHub for high-quality skill repos (\`SKILL.md\`, \`.claude/\`, MCP servers) and routes them into per-profile bundles for users of Claude Code, Codex, and other agents. Today we indexed **${gems.length}** gem(s) across **${byProfile.size}** profile(s).
|
|
1650
1651
|
|
|
@@ -1814,7 +1815,7 @@ ${cards.map((c, i) => {
|
|
|
1814
1815
|
</g>`;
|
|
1815
1816
|
}).join("\n")}
|
|
1816
1817
|
<text class="footer" x="${padX}" y="${H - 14}">cue discovery engine · scored ${updated}</text>
|
|
1817
|
-
<text class="footer" x="${W - padX}" y="${H - 14}" text-anchor="end">github.com/opencue/
|
|
1818
|
+
<text class="footer" x="${W - padX}" y="${H - 14}" text-anchor="end">github.com/opencue/cuecards</text>
|
|
1818
1819
|
</svg>`;
|
|
1819
1820
|
}
|
|
1820
1821
|
|
|
@@ -1867,7 +1868,7 @@ ${heroBadges}
|
|
|
1867
1868
|
|
|
1868
1869
|
<h2 align="center">💎 Your repo was added to cue's <code>${profile}</code> profile</h2>
|
|
1869
1870
|
|
|
1870
|
-
[**cue**](https://github.com/opencue/
|
|
1871
|
+
[**cue**](https://github.com/opencue/cuecards) scans GitHub for high-quality skill repos and routes them to per-profile bundles for Claude Code & Codex users. \`${gem.full_name}\` cleared the discovery threshold (score **${gem.gem_score}**, tier **${t.tier}**) and is now auto-loaded for everyone on the **\`${profile}\`** profile.
|
|
1871
1872
|
|
|
1872
1873
|
## Install (one line)
|
|
1873
1874
|
|
|
@@ -1887,7 +1888,7 @@ ${evidenceList}
|
|
|
1887
1888
|
${breakdownRows}
|
|
1888
1889
|
| **${gem.gem_score}** | **Total** |
|
|
1889
1890
|
|
|
1890
|
-
[Full scoring rubric →](https://github.com/opencue/
|
|
1891
|
+
[Full scoring rubric →](https://github.com/opencue/cuecards/blob/main/src/commands/discover.ts)
|
|
1891
1892
|
|
|
1892
1893
|
</details>
|
|
1893
1894
|
|
|
@@ -1897,22 +1898,22 @@ ${breakdownRows}
|
|
|
1897
1898
|
|---|---|
|
|
1898
1899
|
| \`cue discover\` results | ✅ Listed |
|
|
1899
1900
|
| \`cue optimizer\` dashboard | ✅ Shown to all users on \`${profile}\` |
|
|
1900
|
-
| [\`docs/discovered.md\`](https://github.com/opencue/
|
|
1901
|
+
| [\`docs/discovered.md\`](https://github.com/opencue/cuecards/blob/main/docs/discovered.md) | ✅ Indexed |
|
|
1901
1902
|
| GitHub backlink traffic | ✅ Active |
|
|
1902
1903
|
|
|
1903
1904
|
## Optional: README badge
|
|
1904
1905
|
|
|
1905
1906
|
<p align="center">
|
|
1906
|
-
<a href="https://github.com/opencue/
|
|
1907
|
+
<a href="https://github.com/opencue/cuecards"><img src="${readmeBadge}" alt="cue hidden gem"></a>
|
|
1907
1908
|
</p>
|
|
1908
1909
|
|
|
1909
1910
|
\`\`\`markdown
|
|
1910
|
-
[](https://github.com/opencue/
|
|
1911
|
+
[](https://github.com/opencue/cuecards)
|
|
1911
1912
|
\`\`\`
|
|
1912
1913
|
|
|
1913
1914
|
---
|
|
1914
1915
|
|
|
1915
|
-
<sub>Opened by <a href="https://github.com/opencue/
|
|
1916
|
+
<sub>Opened by <a href="https://github.com/opencue/cuecards"><code>cue discover install --notify</code></a>. One issue per repo, ever. To opt out, close this issue or <a href="https://github.com/opencue/cuecards/issues/new">file an issue against cue</a>.</sub>`;
|
|
1916
1917
|
|
|
1917
1918
|
if (opts.dryRun) {
|
|
1918
1919
|
process.stdout.write(`\n${wrap(ANSI.bold, "─── DRY RUN ───────────────────────────────────────────────────────────")}\n`);
|
|
@@ -1973,7 +1974,19 @@ async function cmdNotify(repo: string | undefined, opts: { profile?: string; dry
|
|
|
1973
1974
|
// Auto-install CLI dependencies from skill's SKILL.md
|
|
1974
1975
|
// ---------------------------------------------------------------------------
|
|
1975
1976
|
|
|
1976
|
-
|
|
1977
|
+
/**
|
|
1978
|
+
* Surface (and optionally install) the CLI prerequisites a skill declares in
|
|
1979
|
+
* its `## Prerequisites` block.
|
|
1980
|
+
*
|
|
1981
|
+
* SECURITY: the prerequisite lines come from a SKILL.md that was just fetched
|
|
1982
|
+
* from an arbitrary GitHub repo, so the commands are untrusted input. We never
|
|
1983
|
+
* run a package installer parsed out of them unless the caller explicitly opts
|
|
1984
|
+
* in with `{ yes: true }` (wired to the `--yes` flag). Without it we only print
|
|
1985
|
+
* the prerequisites as warnings and let the user install them deliberately —
|
|
1986
|
+
* otherwise a typosquatted skill repo with one crafted `## Prerequisites` line
|
|
1987
|
+
* could trigger arbitrary global package installs (npm/brew/cargo/pipx).
|
|
1988
|
+
*/
|
|
1989
|
+
export function autoInstallClis(skillName: string, opts: { yes?: boolean } = {}): void {
|
|
1977
1990
|
const skillsDir = join(homedir(), ".claude", "skills");
|
|
1978
1991
|
const skillMdPath = join(skillsDir, skillName, "SKILL.md");
|
|
1979
1992
|
if (!existsSync(skillMdPath)) return;
|
|
@@ -2017,6 +2030,19 @@ export function autoInstallClis(skillName: string): void {
|
|
|
2017
2030
|
|
|
2018
2031
|
if (installCmds.length === 0) return;
|
|
2019
2032
|
|
|
2033
|
+
// Consent gate: never auto-run installers parsed from an untrusted SKILL.md.
|
|
2034
|
+
// Default is warn-only; the caller must pass `{ yes: true }` (via `--yes`) to
|
|
2035
|
+
// actually install. Keeps `cue discover install` and the interactive wizard
|
|
2036
|
+
// from silently mutating the global toolchain.
|
|
2037
|
+
if (!opts.yes) {
|
|
2038
|
+
process.stdout.write(` ⚠️ ${skillName} declares CLI prerequisites (not auto-installed):\n`);
|
|
2039
|
+
for (const { label } of installCmds) {
|
|
2040
|
+
process.stdout.write(` ${label}\n`);
|
|
2041
|
+
}
|
|
2042
|
+
process.stdout.write(` Review them, then install manually or re-run with --yes to auto-install.\n`);
|
|
2043
|
+
return;
|
|
2044
|
+
}
|
|
2045
|
+
|
|
2020
2046
|
for (const { cmd, args, label } of installCmds) {
|
|
2021
2047
|
// Check if the package manager exists
|
|
2022
2048
|
if (spawnSync("which", [cmd], { encoding: "utf8" }).status !== 0) {
|
|
@@ -2037,7 +2063,7 @@ export function autoInstallClis(skillName: string): void {
|
|
|
2037
2063
|
// Install gems into profiles
|
|
2038
2064
|
// ---------------------------------------------------------------------------
|
|
2039
2065
|
|
|
2040
|
-
async function cmdInstall(opts: { profile?: string; minScore: number; minQuality: number; dryRun: boolean; all: boolean; notify: boolean; digest: boolean }): Promise<number> {
|
|
2066
|
+
async function cmdInstall(opts: { profile?: string; minScore: number; minQuality: number; dryRun: boolean; all: boolean; notify: boolean; digest: boolean; yes: boolean; allowUnsafe: boolean }): Promise<number> {
|
|
2041
2067
|
if (!existsSync(cacheFile())) {
|
|
2042
2068
|
process.stderr.write("No cached gems. Run `cue discover search` first.\n");
|
|
2043
2069
|
return 1;
|
|
@@ -2117,8 +2143,27 @@ async function cmdInstall(opts: { profile?: string; minScore: number; minQuality
|
|
|
2117
2143
|
}
|
|
2118
2144
|
}
|
|
2119
2145
|
|
|
2120
|
-
//
|
|
2121
|
-
|
|
2146
|
+
// Security gate: the skill files are now on disk but not yet registered to
|
|
2147
|
+
// a profile. Scan the just-fetched (untrusted) skill and block on critical
|
|
2148
|
+
// findings (secret/data exfiltration, prompt injection) unless --allow-unsafe.
|
|
2149
|
+
const gate = gateFreshSkill(gem.name, { allowUnsafe: opts.allowUnsafe });
|
|
2150
|
+
if (!gate.ok) {
|
|
2151
|
+
process.stdout.write(` 🔴 BLOCKED: ${gem.full_name} has ${gate.critical.length} critical security finding(s):\n`);
|
|
2152
|
+
for (const c of gate.critical) {
|
|
2153
|
+
process.stdout.write(` [${c.code}] ${c.message}${c.line ? ` (line ${c.line})` : ""}\n`);
|
|
2154
|
+
}
|
|
2155
|
+
process.stdout.write(` Left on disk at ~/.claude/skills/${gem.name} but NOT registered to a profile.\n`);
|
|
2156
|
+
process.stdout.write(` Review it, then re-run with --allow-unsafe to register anyway.\n`);
|
|
2157
|
+
skipped++;
|
|
2158
|
+
continue;
|
|
2159
|
+
}
|
|
2160
|
+
if (!gate.scanned) {
|
|
2161
|
+
process.stdout.write(` ⚠️ ${gem.full_name}: no SKILL.md found at ~/.claude/skills/${gem.name} to scan — review manually.\n`);
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
// Surface CLI dependencies from the skill's SKILL.md Prerequisites.
|
|
2165
|
+
// Only auto-installs when the user passed --yes (see autoInstallClis).
|
|
2166
|
+
autoInstallClis(gem.name, { yes: opts.yes });
|
|
2122
2167
|
|
|
2123
2168
|
// Add to profile.yaml
|
|
2124
2169
|
const profileYaml = join(REPO_ROOT, "profiles", targetProfile, "profile.yaml");
|
|
@@ -2181,7 +2226,7 @@ async function cmdInstall(opts: { profile?: string; minScore: number; minQuality
|
|
|
2181
2226
|
process.stdout.write(`\n Done: ${installed} installed, ${skipped} skipped\n`);
|
|
2182
2227
|
if (opts.dryRun) process.stdout.write(` (dry-run — no changes made. Remove --dry-run to apply)\n`);
|
|
2183
2228
|
|
|
2184
|
-
// Daily digest discussion in opencue/
|
|
2229
|
+
// Daily digest discussion in opencue/cuecards — covers everything we just touched,
|
|
2185
2230
|
// so owners who don't get a --notify issue can still find the post.
|
|
2186
2231
|
if (opts.digest) {
|
|
2187
2232
|
const log = loadNotifyLog();
|
|
@@ -2704,6 +2749,9 @@ Filters (any combination, no extra GitHub calls — read from cache):
|
|
|
2704
2749
|
|
|
2705
2750
|
Install options:
|
|
2706
2751
|
--dry-run Preview installs without making changes
|
|
2752
|
+
--yes, -y Auto-install CLI prerequisites from a skill's SKILL.md
|
|
2753
|
+
--allow-unsafe Register a skill even if the security scan finds a
|
|
2754
|
+
critical issue (default: block + leave it unregistered)
|
|
2707
2755
|
--notify Open a one-time GitHub issue on each indexed repo
|
|
2708
2756
|
--digest Post a daily GitHub Discussion summarizing new gems
|
|
2709
2757
|
|
|
@@ -2776,8 +2824,10 @@ Examples:
|
|
|
2776
2824
|
const all = args.includes("--all");
|
|
2777
2825
|
const notify = args.includes("--notify");
|
|
2778
2826
|
const digest = args.includes("--digest");
|
|
2827
|
+
const yes = args.includes("--yes") || args.includes("-y");
|
|
2828
|
+
const allowUnsafe = args.includes("--allow-unsafe");
|
|
2779
2829
|
const minQuality = intFlag(args, "--min-quality", 7)!;
|
|
2780
|
-
return cmdInstall({ profile, minScore, minQuality, dryRun, all, notify, digest });
|
|
2830
|
+
return cmdInstall({ profile, minScore, minQuality, dryRun, all, notify, digest, yes, allowUnsafe });
|
|
2781
2831
|
}
|
|
2782
2832
|
|
|
2783
2833
|
if (rest[0] === "notify") {
|