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.
Files changed (310) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +82 -33
  3. package/bin/cue-review-progress +107 -0
  4. package/bin/cue-review-watch +98 -0
  5. package/dist/cue.js +7352 -3744
  6. package/package.json +16 -5
  7. package/profiles/_types.ts +9 -0
  8. package/profiles/backend/profile.yaml +2 -0
  9. package/profiles/blog-writer/profile.yaml +10 -0
  10. package/profiles/browser/profile.yaml +9 -2
  11. package/profiles/builder/profile.yaml +3 -6
  12. package/profiles/career/profile.yaml +13 -2
  13. package/profiles/claude-api/profile.yaml +1 -1
  14. package/profiles/commerce/profile.yaml +27 -3
  15. package/profiles/core/logo.png +0 -0
  16. package/profiles/core/profile.yaml +62 -2
  17. package/profiles/dash-merge-test/profile.yaml +109 -0
  18. package/profiles/designer/profile.yaml +2 -0
  19. package/profiles/designer-medusa-next/profile.yaml +4 -1
  20. package/profiles/designer-medusa-vite/profile.yaml +4 -1
  21. package/profiles/docs-writer/profile.yaml +3 -1
  22. package/profiles/eu-tender-research/README.md +48 -0
  23. package/profiles/eu-tender-research/logo.png +0 -0
  24. package/profiles/eu-tender-research/profile.yaml +108 -0
  25. package/profiles/finance/logo.png +0 -0
  26. package/profiles/finance/profile.yaml +46 -0
  27. package/profiles/frontend/profile.yaml +5 -9
  28. package/profiles/growth/profile.yaml +2 -3
  29. package/profiles/gstack/profile.yaml +15 -0
  30. package/profiles/higgsfield/profile.yaml +3 -0
  31. package/profiles/hyperframes/logo.png +0 -0
  32. package/profiles/hyperframes/profile.yaml +59 -0
  33. package/profiles/improver/profile.yaml +88 -0
  34. package/profiles/marketing/profile.yaml +0 -3
  35. package/profiles/medusa-dev/profile.yaml +2 -0
  36. package/profiles/medusa-next/profile.yaml +2 -3
  37. package/profiles/medusa-vite/profile.yaml +2 -3
  38. package/profiles/n8n/logo.png +0 -0
  39. package/profiles/n8n/profile.yaml +50 -0
  40. package/profiles/nextjs/profile.yaml +2 -3
  41. package/profiles/ops/profile.yaml +2 -0
  42. package/profiles/postizz/profile.yaml +13 -3
  43. package/profiles/python/profile.yaml +3 -0
  44. package/profiles/research/profile.yaml +3 -1
  45. package/profiles/schema.json +10 -0
  46. package/profiles/secops/profile.yaml +2 -0
  47. package/profiles/seo/profile.yaml +56 -0
  48. package/profiles/skill-writer/profile.yaml +8 -0
  49. package/profiles/ssh/profile.yaml +32 -0
  50. package/profiles/strapi/logo.png +0 -0
  51. package/profiles/strapi/profile.yaml +45 -0
  52. package/profiles/stripe/logo.png +0 -0
  53. package/profiles/stripe/profile.yaml +1 -0
  54. package/profiles/supabase/logo.png +0 -0
  55. package/profiles/supabase/profile.yaml +85 -0
  56. package/profiles/vercel/logo.png +0 -0
  57. package/profiles/vercel/profile.yaml +25 -1
  58. package/profiles/vite/profile.yaml +4 -3
  59. package/profiles/web-frontend-base/profile.yaml +5 -4
  60. package/profiles/webshop/profile.yaml +23 -5
  61. package/profiles/x-growth-bot/profile.yaml +44 -0
  62. package/resources/icons/generate-icons.py +128 -2
  63. package/resources/mcps/configs/claude.sanitized.json +42 -0
  64. package/resources/mcps/configs/codex.sanitized.json +7 -0
  65. package/resources/skills/skills/career/resume-version-manager/SKILL.md +351 -0
  66. package/resources/skills/skills/career/salary-negotiation-prep/SKILL.md +378 -0
  67. package/resources/skills/skills/content/pdf/SKILL.md +2 -0
  68. package/resources/skills/skills/content/postiz-cards/SKILL.md +48 -0
  69. package/resources/skills/skills/content/postiz-cards/scripts/analytics.sh +38 -0
  70. package/resources/skills/skills/content/postiz-cards/scripts/card.sh +42 -0
  71. package/resources/skills/skills/content/postiz-cards/scripts/lint.py +38 -0
  72. package/resources/skills/skills/design/headless-gif-demo/SKILL.md +1 -1
  73. package/resources/skills/skills/design/readme-svg-design/SKILL.md +1 -1
  74. package/resources/skills/skills/eu-funding/grant-outreach/SKILL.md +70 -0
  75. package/resources/skills/skills/eu-funding/hu-grant-finder/SKILL.md +114 -0
  76. package/resources/skills/skills/eu-funding/hu-grant-finder/evals.md +26 -0
  77. package/resources/skills/skills/eu-funding/ted-tender-search/SKILL.md +80 -0
  78. package/resources/skills/skills/eu-funding/ted-tender-search/evals.md +26 -0
  79. package/resources/skills/skills/eu-funding/ted-tender-search/scripts/ted-search.sh +46 -0
  80. package/resources/skills/skills/event-design/wedding-invitations/SKILL.md +1 -1
  81. package/resources/skills/skills/github/gx-agents/SKILL.md +96 -0
  82. package/resources/skills/skills/gstack/design-shotgun/SKILL.md +1 -1
  83. package/resources/skills/skills/marketing/ab-test-analyzer/SKILL.md +1 -1
  84. package/resources/skills/skills/marketing/ab-test-setup-and-analysis/SKILL.md +1 -1
  85. package/resources/skills/skills/marketing/account-structure-review/SKILL.md +1 -1
  86. package/resources/skills/skills/marketing/ad-copy-variant-generator/SKILL.md +1 -1
  87. package/resources/skills/skills/marketing/ad-extension-audit/SKILL.md +1 -1
  88. package/resources/skills/skills/marketing/ad-spend-allocator/SKILL.md +1 -1
  89. package/resources/skills/skills/marketing/anomaly-detection/SKILL.md +1 -1
  90. package/resources/skills/skills/marketing/attribution-model-comparison/SKILL.md +1 -1
  91. package/resources/skills/skills/marketing/audience-overlap-analysis/SKILL.md +7 -1
  92. package/resources/skills/skills/marketing/bid-strategy-recommendations/SKILL.md +7 -1
  93. package/resources/skills/skills/marketing/budget-scenario-planner/SKILL.md +6 -1
  94. package/resources/skills/skills/marketing/campaign-naming-convention-builder/SKILL.md +7 -1
  95. package/resources/skills/skills/marketing/channel-mix-optimizer/SKILL.md +7 -1
  96. package/resources/skills/skills/marketing/client-report-narratives/SKILL.md +6 -1
  97. package/resources/skills/skills/marketing/competitor-creative-analysis/SKILL.md +1 -1
  98. package/resources/skills/skills/marketing/competitor-teardown/SKILL.md +1 -1
  99. package/resources/skills/skills/marketing/content-repurposer/SKILL.md +1 -1
  100. package/resources/skills/skills/marketing/conversion-path-analysis/SKILL.md +1 -1
  101. package/resources/skills/skills/marketing/cpa-diagnostics/SKILL.md +1 -1
  102. package/resources/skills/skills/marketing/creative-fatigue-detection/SKILL.md +1 -1
  103. package/resources/skills/skills/marketing/day-hour-performance-breakdown/SKILL.md +1 -1
  104. package/resources/skills/skills/marketing/device-performance-split/SKILL.md +1 -1
  105. package/resources/skills/skills/marketing/e2e-seo-assistant/SKILL.md +1 -1
  106. package/resources/skills/skills/marketing/email-sequence-writer/SKILL.md +1 -1
  107. package/resources/skills/skills/marketing/frequency-cap-recommendations/SKILL.md +1 -1
  108. package/resources/skills/skills/marketing/geo-performance-analysis/SKILL.md +1 -1
  109. package/resources/skills/skills/marketing/google-ads-audit/SKILL.md +1 -1
  110. package/resources/skills/skills/marketing/icp-research-assistant/SKILL.md +1 -1
  111. package/resources/skills/skills/marketing/keyword-cannibalization-check/SKILL.md +1 -1
  112. package/resources/skills/skills/marketing/landing-page-audit/SKILL.md +1 -1
  113. package/resources/skills/skills/marketing/landing-page-audit-quick/SKILL.md +1 -1
  114. package/resources/skills/skills/marketing/linkedin-ads-audit/SKILL.md +1 -1
  115. package/resources/skills/skills/marketing/meta-ads-audit/SKILL.md +1 -1
  116. package/resources/skills/skills/marketing/pacing-monitor/SKILL.md +1 -1
  117. package/resources/skills/skills/marketing/performance-benchmarking/SKILL.md +1 -1
  118. package/resources/skills/skills/marketing/programmatic-seo-builder/SKILL.md +1 -1
  119. package/resources/skills/skills/marketing/quality-score-breakdown/SKILL.md +1 -1
  120. package/resources/skills/skills/marketing/reddit-ads-audit/SKILL.md +1 -1
  121. package/resources/skills/skills/marketing/retargeting-window-analysis/SKILL.md +1 -1
  122. package/resources/skills/skills/marketing/roas-forecasting/SKILL.md +1 -1
  123. package/resources/skills/skills/marketing/search-term-mining/SKILL.md +1 -1
  124. package/resources/skills/skills/marketing/utm-tracking-generator/SKILL.md +1 -1
  125. package/resources/skills/skills/marketing/wasted-spend-finder/SKILL.md +1 -1
  126. package/resources/skills/skills/marketing/weekly-account-summary/SKILL.md +1 -1
  127. package/resources/skills/skills/meta/awesome-list-submit/SKILL.md +4 -4
  128. package/resources/skills/skills/meta/cue-dashboard/SKILL.md +109 -0
  129. package/resources/skills/skills/meta/cue-developer/SKILL.md +161 -0
  130. package/resources/skills/skills/meta/cue-developer/evals/evals.json +57 -0
  131. package/resources/skills/skills/meta/cue-developer/references/architecture.md +65 -0
  132. package/resources/skills/skills/meta/cue-developer/references/build_and_test.md +72 -0
  133. package/resources/skills/skills/meta/cue-developer/references/contributing.md +75 -0
  134. package/resources/skills/skills/meta/cue-developer/references/conventions.md +57 -0
  135. package/resources/skills/skills/meta/cue-developer/references/first_time_setup.md +51 -0
  136. package/resources/skills/skills/meta/cue-developer/references/skill_and_mcp_authoring.md +84 -0
  137. package/resources/skills/skills/meta/cue-developer/references/troubleshooting.md +42 -0
  138. package/resources/skills/skills/meta/delegation-check/SKILL.md +148 -0
  139. package/resources/skills/skills/meta/delegation-check/specs/scan-algorithm.md +125 -0
  140. package/resources/skills/skills/meta/delegation-check/specs/separation-rules.md +190 -0
  141. package/resources/skills/skills/meta/focus/SKILL.md +62 -0
  142. package/resources/skills/skills/meta/help/SKILL.md +1 -1
  143. package/resources/skills/skills/meta/integrity-tags/SKILL.md +2 -0
  144. package/resources/skills/skills/meta/next-steps/SKILL.md +124 -0
  145. package/resources/skills/skills/meta/next-steps/evals/eval-set.json +92 -0
  146. package/resources/skills/skills/meta/profile-from-docs/SKILL.md +141 -0
  147. package/resources/skills/skills/meta/ralph-loop/SKILL.md +83 -0
  148. package/resources/skills/skills/meta/ralph-loop/scripts/loop.sh +73 -0
  149. package/resources/skills/skills/meta/skill-simplify/SKILL.md +136 -0
  150. package/resources/skills/skills/meta/skill-simplify/phases/01-analysis.md +173 -0
  151. package/resources/skills/skills/meta/skill-simplify/phases/02-optimize.md +104 -0
  152. package/resources/skills/skills/meta/skill-simplify/phases/03-check.md +145 -0
  153. package/resources/skills/skills/meta/smart-loader/scripts/smart-lookup.sh +13 -4
  154. package/resources/skills/skills/meta/verify-council/SKILL.md +182 -0
  155. package/resources/skills/skills/meta/verify-council/references/lane-prompts.md +103 -0
  156. package/resources/skills/skills/meta/verify-council/references/workflow.js +217 -0
  157. package/resources/skills/skills/nvidia/aiq-research/SKILL.md +1 -1
  158. package/resources/skills/skills/nvidia/cuopt-developer/SKILL.md +16 -1
  159. package/resources/skills/skills/nvidia/cuopt-developer/resources/contributing.md +2 -2
  160. package/resources/skills/skills/nvidia/cuopt-developer/resources/numerical_debugging.md +128 -0
  161. package/resources/skills/skills/nvidia/cuopt-developer/resources/python_bindings.md +2 -9
  162. package/resources/skills/skills/nvidia/cuopt-developer/resources/vrp_skills.md +166 -0
  163. package/resources/skills/skills/nvidia/cuopt-install/SKILL.md +2 -10
  164. package/resources/skills/skills/nvidia/cuopt-numerical-optimization-api-c/SKILL.md +3 -23
  165. package/resources/skills/skills/nvidia/cuopt-numerical-optimization-api-c/resources/examples.md +40 -20
  166. package/resources/skills/skills/nvidia/cuopt-numerical-optimization-api-python/SKILL.md +5 -1
  167. package/resources/skills/skills/nvidia/skill-evolution/SKILL.md +4 -5
  168. package/resources/skills/skills/research/trendradar/SKILL.md +1 -1
  169. package/resources/skills/skills/ssh/ssh-config/SKILL.md +94 -0
  170. package/resources/skills/skills/ssh/ssh-copy/SKILL.md +92 -0
  171. package/resources/skills/skills/ssh/ssh-harden/SKILL.md +108 -0
  172. package/resources/skills/skills/ssh/ssh-keys/SKILL.md +82 -0
  173. package/resources/skills/skills/ssh/ssh-paste-image/LICENSE +28 -0
  174. package/resources/skills/skills/ssh/ssh-paste-image/SKILL.md +149 -0
  175. package/resources/skills/skills/ssh/ssh-paste-image/scripts/build.sh +29 -0
  176. package/resources/skills/skills/ssh/ssh-paste-image/scripts/client/go.mod +3 -0
  177. package/resources/skills/skills/ssh/ssh-paste-image/scripts/client/main.go +79 -0
  178. package/resources/skills/skills/ssh/ssh-paste-image/scripts/daemon/ccimgd.service +12 -0
  179. package/resources/skills/skills/ssh/ssh-paste-image/scripts/daemon/com.ccimgd.plist +20 -0
  180. package/resources/skills/skills/ssh/ssh-paste-image/scripts/daemon/go.mod +3 -0
  181. package/resources/skills/skills/ssh/ssh-paste-image/scripts/daemon/main.go +98 -0
  182. package/resources/skills/skills/ssh/ssh-tunnel/SKILL.md +96 -0
  183. package/resources/skills/skills/strapi/building-with-strapi/SKILL.md +112 -0
  184. package/resources/skills/skills/strapi/strapi-cli/SKILL.md +93 -0
  185. package/resources/skills/skills/strapi/strapi-content-api/SKILL.md +115 -0
  186. package/resources/skills/skills/strapi/strapi-deploy/SKILL.md +89 -0
  187. package/resources/skills/skills/strapi/strapi-mcp-setup/SKILL.md +101 -0
  188. package/resources/skills/skills/strapi/strapi-plugins/SKILL.md +97 -0
  189. package/resources/skills/skills/tools/context7/SKILL.md +101 -0
  190. package/resources/skills/skills/tools/opensrc/SKILL.md +1 -1
  191. package/resources/skills/skills/tools/portless/SKILL.md +186 -0
  192. package/resources/skills/skills/xbot/operate/SKILL.md +229 -0
  193. package/src/commands/_index.ts +8 -0
  194. package/src/commands/ai-score.e2e.test.ts +11 -4
  195. package/src/commands/ai.ts +3 -4
  196. package/src/commands/auto-detect.ts +1 -1
  197. package/src/commands/cli.test.ts +1 -2
  198. package/src/commands/cli.ts +1 -1
  199. package/src/commands/cloud.ts +1 -1
  200. package/src/commands/current.ts +1 -4
  201. package/src/commands/dash.test.ts +110 -0
  202. package/src/commands/dash.ts +194 -0
  203. package/src/commands/dashboard.ts +26 -0
  204. package/src/commands/diff.ts +1 -1
  205. package/src/commands/discover.test.ts +1 -1
  206. package/src/commands/discover.ts +90 -40
  207. package/src/commands/doctor.test.ts +58 -0
  208. package/src/commands/doctor.ts +79 -3
  209. package/src/commands/eval-behavior.ts +1 -1
  210. package/src/commands/eval.ts +2 -2
  211. package/src/commands/evolve.ts +4 -3
  212. package/src/commands/failures.test.ts +1 -1
  213. package/src/commands/features-batch1.test.ts +6 -1
  214. package/src/commands/icon.ts +1 -5
  215. package/src/commands/import-profile.ts +1 -1
  216. package/src/commands/init.ts +50 -7
  217. package/src/commands/install-sh.e2e.test.ts +65 -0
  218. package/src/commands/launch-handoff.e2e.test.ts +88 -0
  219. package/src/commands/launch.e2e.test.ts +8 -1
  220. package/src/commands/launch.test.ts +29 -0
  221. package/src/commands/launch.ts +185 -131
  222. package/src/commands/lock.ts +0 -1
  223. package/src/commands/marketplace.ts +0 -4
  224. package/src/commands/materialize.ts +1 -1
  225. package/src/commands/mem.ts +341 -0
  226. package/src/commands/optimizer.ts +0 -3
  227. package/src/commands/playground.ts +1 -2
  228. package/src/commands/profile-draft-skill.ts +1 -1
  229. package/src/commands/replay-whatif.ts +1 -6
  230. package/src/commands/score.ts +2 -2
  231. package/src/commands/security.test.ts +88 -0
  232. package/src/commands/security.ts +74 -28
  233. package/src/commands/shell.test.ts +65 -4
  234. package/src/commands/shell.ts +67 -7
  235. package/src/commands/skills-test.ts +0 -1
  236. package/src/commands/skills.ts +28 -2
  237. package/src/commands/sources.ts +1 -2
  238. package/src/commands/status.ts +2 -6
  239. package/src/commands/submit-profile.ts +1 -1
  240. package/src/commands/suggest.ts +35 -10
  241. package/src/commands/trigger-gaps.test.ts +50 -0
  242. package/src/commands/trigger-gaps.ts +63 -29
  243. package/src/commands/update.ts +1 -1
  244. package/src/commands/validate.ts +16 -4
  245. package/src/commands/watch-live.ts +1 -1
  246. package/src/commands/workspace.ts +1 -1
  247. package/src/index.ts +26 -10
  248. package/src/lib/active-sessions.ts +1 -1
  249. package/src/lib/agent-adapters.test.ts +100 -0
  250. package/src/lib/agent-adapters.ts +2 -2
  251. package/src/lib/analytics.test.ts +88 -0
  252. package/src/lib/analytics.ts +82 -1
  253. package/src/lib/auto-detect.test.ts +10 -4
  254. package/src/lib/auto-detect.ts +19 -23
  255. package/src/lib/brand-icons.ts +0 -1
  256. package/src/lib/cache.ts +2 -3
  257. package/src/lib/claude-mem-env.test.ts +148 -0
  258. package/src/lib/claude-mem-env.ts +172 -0
  259. package/src/lib/combo-history.test.ts +53 -0
  260. package/src/lib/combo-history.ts +83 -0
  261. package/src/lib/companion-detect.test.ts +108 -0
  262. package/src/lib/companion-detect.ts +140 -0
  263. package/src/lib/companion-fetch.ts +4 -6
  264. package/src/lib/conditional-skills.test.ts +1 -1
  265. package/src/lib/config-paths.test.ts +53 -0
  266. package/src/lib/config-paths.ts +33 -0
  267. package/src/lib/dashboard-server.test.ts +351 -0
  268. package/src/lib/dashboard-server.ts +1476 -27
  269. package/src/lib/debug-log.test.ts +66 -0
  270. package/src/lib/debug-log.ts +45 -0
  271. package/src/lib/mcp-catalog.test.ts +102 -0
  272. package/src/lib/mcp-catalog.ts +193 -0
  273. package/src/lib/pair-suggestions.test.ts +111 -0
  274. package/src/lib/pair-suggestions.ts +98 -5
  275. package/src/lib/permissions.test.ts +76 -0
  276. package/src/lib/permissions.ts +125 -0
  277. package/src/lib/picker.test.ts +1106 -1
  278. package/src/lib/picker.ts +1230 -142
  279. package/src/lib/plugin-discovery.ts +126 -0
  280. package/src/lib/pr-poster.ts +1 -1
  281. package/src/lib/pr-throttle.ts +2 -6
  282. package/src/lib/profile-linter.test.ts +67 -1
  283. package/src/lib/profile-linter.ts +59 -14
  284. package/src/lib/profile-loader.test.ts +21 -0
  285. package/src/lib/profile-loader.ts +22 -3
  286. package/src/lib/profile-metrics.ts +2 -6
  287. package/src/lib/profile-names.test.ts +58 -0
  288. package/src/lib/repos.test.ts +57 -0
  289. package/src/lib/repos.ts +167 -0
  290. package/src/lib/resolver-npx.ts +10 -1
  291. package/src/lib/runtime-materializer.test.ts +200 -3
  292. package/src/lib/runtime-materializer.ts +129 -20
  293. package/src/lib/shared-profiles.ts +2 -3
  294. package/src/lib/skill-clis.test.ts +113 -0
  295. package/src/lib/skill-clis.ts +232 -0
  296. package/src/lib/skill-dependencies.ts +9 -1
  297. package/src/lib/skill-deps.ts +1 -1
  298. package/src/lib/skill-linter.ts +1 -1
  299. package/src/lib/skill-quality.ts +0 -1
  300. package/src/lib/skill-sandbox.test.ts +1 -1
  301. package/src/lib/skills-lock.test.ts +1 -1
  302. package/src/lib/telemetry-consent.ts +3 -5
  303. package/src/lib/telemetry-report.test.ts +2 -2
  304. package/src/lib/token-budget.ts +111 -0
  305. package/src/lib/trigger-gaps.test.ts +70 -0
  306. package/src/lib/trigger-gaps.ts +48 -6
  307. package/src/lib/tui/data.ts +1 -5
  308. package/src/lib/workflow-store.ts +150 -0
  309. package/src/lib/workspace-secrets.ts +0 -4
  310. 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);
@@ -3,7 +3,7 @@
3
3
  * `cue diff --live <target>` — show impact of switching from current to target.
4
4
  */
5
5
 
6
- import { existsSync, readFileSync } from "node:fs";
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, homedir } from "node:os";
8
+ import { tmpdir, } from "node:os";
9
9
  import { join } from "node:path";
10
10
 
11
11
  import { run as discoverRun } from "./discover";
@@ -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 { loadProfile, listProfiles } from "../lib/profile-loader";
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-api": ["python", "fastapi", "django", "flask", "sqlalchemy", "pytest", "pip", "uvicorn", "pydantic", "celery"],
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-api", "rust", "go-api", "threejs",
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-api" && lang === "python") hits += 2;
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-api": ["python fastapi skill", "django api", "flask sqlalchemy", "pytest automation"],
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/claude-code-skills) for the **${profile}** profile.\n`;
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/claude-code-skills) — 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
+ 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/claude-code-skills) across **${byProfile.size} profiles**.
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/claude-code-skills](https://github.com/opencue/claude-code-skills)\n`;
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/cue/discovered/">
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/claude-code-skills">cue</a> across <strong>${byProfile.size} profiles</strong>.</p>
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/claude-code-skills">github.com/opencue/claude-code-skills</a></p>
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/cue/discovered/${profile}.html">
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/claude-code-skills">cue</a>. Last updated ${updated.split("T")[0]}.</p>
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/claude-code-skills) — 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
+ 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, "&quot;")}">
1260
1261
  <meta property="og:title" content="${gem.full_name} — Claude Code skill">
1261
1262
  <meta property="og:description" content="${desc.replace(/"/g, "&quot;")}">
1262
- <link rel="canonical" href="https://opencue.github.io/cue/discovered/skills/${gem.full_name.replace("/", "-").toLowerCase()}.html">
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/claude-code-skills">cue</a>. Repo authors can opt out by adding <code>&lt;!-- cue: ignore --&gt;</code> to their README.</small></p>
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>&lt;!-- cue: ignore --&gt;</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/cue/discovered";
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/claude-code-skills">
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/claude-code-skills, keyed by YYYY-MM-DD.
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/claude-code-skills) — lower-pressure analog to --notify
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/claude-code-skills";
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/claude-code-skills) — repos newly indexed on **${date}**, grouped by profile.
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/claude-code-skills</text>
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/claude-code-skills) 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
+ [**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/claude-code-skills/blob/main/src/commands/discover.ts)
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/claude-code-skills/blob/main/docs/discovered.md) | ✅ Indexed |
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/claude-code-skills"><img src="${readmeBadge}" alt="cue hidden gem"></a>
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
- [![cue hidden gem](${readmeBadge})](https://github.com/opencue/claude-code-skills)
1911
+ [![cue hidden gem](${readmeBadge})](https://github.com/opencue/cuecards)
1911
1912
  \`\`\`
1912
1913
 
1913
1914
  ---
1914
1915
 
1915
- <sub>Opened by <a href="https://github.com/opencue/claude-code-skills"><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/claude-code-skills/issues/new">file an issue against cue</a>.</sub>`;
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
- export function autoInstallClis(skillName: string): void {
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
- // Auto-install CLI dependencies from the skill's SKILL.md Prerequisites
2121
- autoInstallClis(gem.name);
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/claude-code-skills — covers everything we just touched,
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") {