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
@@ -13,19 +13,30 @@
13
13
 
14
14
  import { spawn } from "node:child_process";
15
15
  import { readFile } from "node:fs/promises";
16
- import { existsSync, readFileSync } from "node:fs";
16
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
17
17
  import { basename, join, resolve } from "node:path";
18
18
  import { homedir } from "node:os";
19
+ import { configDir } from "../lib/config-paths";
20
+ import { debug } from "../lib/debug-log";
21
+ import {
22
+ computeTokenBreakdown,
23
+ splitSkillBytes,
24
+ tokenLevelEmoji,
25
+ type SkillTokens,
26
+ type TokenBreakdown,
27
+ } from "../lib/token-budget";
19
28
 
20
29
  import { loadProfile, listProfiles, listFeaturedProfiles, parseProfileSelector } from "../lib/profile-loader";
21
30
  import { resolveProfileForCwd } from "../lib/cwd-resolver";
22
- import { DIVIDER_PREFIX, runPicker, type PickerOption } from "../lib/picker";
31
+ import { DIVIDER_PREFIX, runPicker, type PickerOption, type ProfileTally } from "../lib/picker";
23
32
  import { materializeRuntime, type McpServerConfig } from "../lib/runtime-materializer";
24
33
  import { resolveLocalSkill, listAllSkillIds } from "../lib/resolver-local";
25
34
  import { detectKittyTerminal, kittyPlaceholderLabel, transmitKittyImage } from "../lib/kitty-image";
26
35
  import { computeStats } from "../lib/analytics";
27
36
  import { detectProfileV2, type DetectionResultV2 } from "../lib/auto-detect";
37
+ import { detectCompanions, type CompanionSignal } from "../lib/companion-detect";
28
38
  import type { ResolvedProfile } from "../../profiles/_types";
39
+ import type { ProfileAffinity, UniversalSuggestion } from "../lib/pair-suggestions";
29
40
  import { hasWorkspaces, getActiveWorkspace, computeOverrides, resolveWorkspaceForCwd } from "../lib/workspaces";
30
41
 
31
42
  // ---------------------------------------------------------------------------
@@ -120,15 +131,6 @@ async function applyWorkspaceOverrides(profile: ResolvedProfile): Promise<Resolv
120
131
  return result;
121
132
  }
122
133
 
123
- // ---------------------------------------------------------------------------
124
- // Config dir helper
125
- // ---------------------------------------------------------------------------
126
-
127
- function configDir(): string {
128
- return process.env.XDG_CONFIG_HOME
129
- ? join(process.env.XDG_CONFIG_HOME, "cue")
130
- : join(homedir(), ".config", "cue");
131
- }
132
134
 
133
135
  // ---------------------------------------------------------------------------
134
136
  // Exec helper — spawn with inherited stdio so interactive sessions work
@@ -301,6 +303,7 @@ async function expandWildcards(profile: ResolvedProfile): Promise<void> {
301
303
  * Colors are emitted only when stdout is a TTY and `NO_COLOR` is unset.
302
304
  */
303
305
  const LIST_TRUNCATE = 8;
306
+ const BREAKDOWN_MAX = 6; // per-profile skill breakdown cap before "+N more"
304
307
  const COMMANDS_PER_LINE = 4;
305
308
  const LABEL_WIDTH = 10; // "commands " — keep visually aligned
306
309
 
@@ -320,10 +323,16 @@ export function formatProfileSummary(
320
323
  const breakdown = npxCount > 0 ? ` (${localCount} local, ${npxCount} npx)` : "";
321
324
  let line = `${label("skills")}${c.yellow(String(totalSkills))}${c.dim(breakdown)}`;
322
325
  if (parts && parts.length > 1) {
323
- const split = parts
324
- .map((p) => `${p.icon ? `${p.icon} ` : ""}${p.name}:${p.skills.local.length + p.skills.npx.length}`)
325
- .join(" + ");
326
- line += ` ${c.dim("←")} ${c.dim(split)}`;
326
+ const segs = parts.map(
327
+ (p) => `${p.icon ? `${p.icon} ` : ""}${p.name}:${p.skills.local.length + p.skills.npx.length}`,
328
+ );
329
+ // Cap the per-profile breakdown so a fat composite doesn't wrap into a
330
+ // multi-line wall — the headline total already carries the full picture.
331
+ const shown =
332
+ segs.length > BREAKDOWN_MAX
333
+ ? `${segs.slice(0, BREAKDOWN_MAX).join(" + ")} +${segs.length - BREAKDOWN_MAX} more`
334
+ : segs.join(" + ");
335
+ line += ` ${c.dim("←")} ${c.dim(shown)}`;
327
336
  }
328
337
  lines.push(line);
329
338
  if (localCount >= 5) {
@@ -409,106 +418,16 @@ function colorFns() {
409
418
  // at the call site that supplies them with real file measurements.
410
419
  // ---------------------------------------------------------------------------
411
420
 
412
- export interface SkillTokens {
413
- /** Tokens for the YAML frontmatter (always-on, loaded into skill router). */
414
- frontmatter: number;
415
- /** Tokens for the rest of SKILL.md (load-on-activate). */
416
- body: number;
417
- }
418
-
419
- export interface TokenBreakdown {
420
- /** Sum of frontmatter tokens across every skill — the real always-on cost. */
421
- alwaysOn: number;
422
- /** Sum of body tokens — the ceiling if every skill activates this session. */
423
- maxIfAllActivate: number;
424
- /** Skill count for the header line. */
425
- totalSkills: number;
426
- /**
427
- * Per-profile attribution of `alwaysOn` for composite selectors (length > 1).
428
- * Each skill is credited to the first part that declares it, so per-part
429
- * numbers sum to `alwaysOn` (no double-counting from overlap). Empty for
430
- * single-part profiles. `icon` carries the part's emoji when declared.
431
- */
432
- byProfile: { name: string; icon?: string; tokens: number; skillCount: number }[];
433
- /** Skills sorted by body size, descending — for the "heaviest if activated" hint. */
434
- heaviestBodies: { id: string; tokens: number }[];
435
- }
436
-
437
- export function computeTokenBreakdown(
438
- profile: ResolvedProfile,
439
- parts: ResolvedProfile[] | undefined,
440
- tokensForSkill: (id: string) => SkillTokens,
441
- ): TokenBreakdown {
442
- let alwaysOn = 0;
443
- let maxIfAllActivate = 0;
444
- const heaviestBodies: { id: string; tokens: number }[] = [];
445
- for (const s of profile.skills.local) {
446
- const { frontmatter, body } = tokensForSkill(s.id);
447
- alwaysOn += frontmatter;
448
- maxIfAllActivate += body;
449
- if (body > 0) heaviestBodies.push({ id: s.id, tokens: body });
450
- }
451
- heaviestBodies.sort((a, b) => b.tokens - a.tokens);
452
-
453
- const byProfile: TokenBreakdown["byProfile"] = [];
454
- if (parts && parts.length > 1) {
455
- const credited = new Set<string>();
456
- for (const part of parts) {
457
- let pTokens = 0;
458
- let pCount = 0;
459
- for (const s of part.skills.local) {
460
- if (credited.has(s.id)) continue;
461
- credited.add(s.id);
462
- const { frontmatter } = tokensForSkill(s.id);
463
- if (frontmatter > 0) {
464
- pTokens += frontmatter;
465
- pCount += 1;
466
- }
467
- }
468
- byProfile.push({ name: part.name, icon: part.icon, tokens: pTokens, skillCount: pCount });
469
- }
470
- }
471
-
472
- return {
473
- alwaysOn,
474
- maxIfAllActivate,
475
- totalSkills: profile.skills.local.length,
476
- byProfile,
477
- heaviestBodies,
478
- };
479
- }
480
-
481
- /**
482
- * Extract frontmatter byte length from a SKILL.md string. Returns
483
- * `{ frontmatter, body }` byte counts. Falls back to a token count of zero
484
- * when the file lacks the leading `---` block (still legal but rare).
485
- */
486
- export function splitSkillBytes(source: string): { frontmatter: number; body: number } {
487
- if (!source.startsWith("---\n") && !source.startsWith("---\r\n")) {
488
- return { frontmatter: 0, body: source.length };
489
- }
490
- // Find the closing `---` on its own line. Search starts after the opener.
491
- const closer = source.indexOf("\n---", 4);
492
- if (closer === -1) {
493
- return { frontmatter: source.length, body: 0 };
494
- }
495
- // Include the closing `---\n` in the frontmatter block.
496
- const fmEnd = source.indexOf("\n", closer + 1);
497
- const cut = fmEnd === -1 ? source.length : fmEnd + 1;
498
- return { frontmatter: cut, body: source.length - cut };
499
- }
500
-
501
- /**
502
- * Map an always-on token count to the bands we color in the CLI banner and
503
- * the tmux pane-border badge. Single source of truth so the two displays
504
- * never drift apart on threshold values.
505
- */
506
- export function tokenLevelEmoji(alwaysOn: number): "🔴" | "🟠" | "🟡" | "🟢" {
507
- return alwaysOn > 15000 ? "🔴"
508
- : alwaysOn > 10000 ? "🟠"
509
- : alwaysOn > 5000 ? "🟡"
510
- : "🟢";
511
- }
421
+ // Token-budget math (SkillTokens, TokenBreakdown, computeTokenBreakdown,
422
+ // splitSkillBytes, tokenLevelEmoji) moved to lib/token-budget.ts. Re-exported
423
+ // here so existing importers of these from "./launch" keep resolving.
424
+ export {
425
+ computeTokenBreakdown,
426
+ splitSkillBytes,
427
+ tokenLevelEmoji,
428
+ type SkillTokens,
429
+ type TokenBreakdown,
430
+ } from "../lib/token-budget";
512
431
 
513
432
  /** Format the token-overhead block. Returns `[]` under the 2K always-on floor. */
514
433
  export function formatTokenWarning(b: TokenBreakdown): string[] {
@@ -691,6 +610,28 @@ export const SUGGESTED_MIN_CONFIDENCE = 0.5;
691
610
  */
692
611
  export const SUGGESTED_AUTO_PICK_CONFIDENCE = 0.7;
693
612
 
613
+ /**
614
+ * Registered postizz brand folder names (dir basenames under
615
+ * profiles/postizz/brands), used by the combine companion detector to suggest
616
+ * postizz when the cwd is a brand dir. Best-effort: missing dir → empty set.
617
+ * Resolves the profiles root exactly like listProfileOptions does.
618
+ */
619
+ function listPostizzBrands(): Set<string> {
620
+ try {
621
+ const profilesRoot =
622
+ process.env.CUE_PROFILES_DIR ??
623
+ process.env.SOUL_PROFILES_DIR ??
624
+ join(resolve(new URL(import.meta.url).pathname, "..", "..", ".."), "profiles");
625
+ return new Set(
626
+ readdirSync(join(profilesRoot, "postizz", "brands"), { withFileTypes: true })
627
+ .filter((d) => d.isDirectory())
628
+ .map((d) => d.name),
629
+ );
630
+ } catch {
631
+ return new Set();
632
+ }
633
+ }
634
+
694
635
  // Recent now answers "what were the last N profiles I picked," not "what do
695
636
  // I pick most often." It sorts strictly by lastUsed timestamp and applies no
696
637
  // session-count floor — a single deliberate pick yesterday belongs in Recent
@@ -801,9 +742,17 @@ function makeSelectorOption(selector: string, allProfileOpts: PickerOption[]): P
801
742
  // synthesize a self-describing row for; a bare unknown name is a stale or
802
743
  // deleted profile and is dropped (undefined) so it never shows in the picker.
803
744
  if (!selector.includes("+")) return undefined;
745
+ // Reuse each part's own option label so the combined row carries every part's
746
+ // icon — emoji or kitty image placeholder — e.g. "📈 improver + 🔒 secops + 🐻
747
+ // builder" instead of bare "improver + secops + builder". Parts with no
748
+ // resolved option (stale) fall back to the bare name.
749
+ const label = selector
750
+ .split("+")
751
+ .map((part) => allProfileOpts.find((o) => o.value === part)?.label ?? part)
752
+ .join(" + ");
804
753
  return {
805
754
  value: selector,
806
- label: selector.split("+").join(" + "),
755
+ label,
807
756
  hint: "stacked profile",
808
757
  };
809
758
  }
@@ -966,7 +915,7 @@ export function getDefaultSelector(
966
915
  .map((s) => s.trim())
967
916
  .map((s) => s.replace(/#.*$/, "").trim())
968
917
  .filter((s) => s.length > 0 && s !== "core");
969
- } catch { /* file missing -> core only */ }
918
+ } catch (err) { debug("launch:default-profile", err); /* missing core only */ }
970
919
  // Dedupe while preserving order.
971
920
  const seen = new Set<string>(["core"]);
972
921
  const parts = ["core"];
@@ -1027,8 +976,9 @@ async function listProfileOptions(pinnedProfile?: string): Promise<PickerOption[
1027
976
  const label = warning ? `${nameLabel}${warning.labelSuffix}` : nameLabel;
1028
977
  const hint = warning ? warning.hint : p.description;
1029
978
  const recommends = p.recommends.filter((r) => r !== name && knownNames.has(r));
979
+ const autoSelect = p.autoSelect.filter((r) => r !== name && knownNames.has(r));
1030
980
  const conflicts = p.conflicts.filter((c) => c !== name && knownNames.has(c));
1031
- opts.push({ value: name, label, hint, recommends, conflicts });
981
+ opts.push({ value: name, label, hint, recommends, autoSelect, conflicts });
1032
982
  } catch {
1033
983
  opts.push({ value: name, label: name, hint: "" });
1034
984
  }
@@ -1152,7 +1102,7 @@ async function loadMcpRegistry(agent: "claude-code" | "codex"): Promise<Record<s
1152
1102
  for (const [k, v] of Object.entries(raw.servers ?? {})) {
1153
1103
  merged[k] = v;
1154
1104
  }
1155
- } catch { /* master missing keep runtime fallbacks */ }
1105
+ } catch (err) { debug("launch:master-config", err); /* keep runtime fallbacks */ }
1156
1106
 
1157
1107
  return merged;
1158
1108
  }
@@ -1301,7 +1251,7 @@ async function resolveClaudeCredentialsSource(): Promise<string> {
1301
1251
  `▸ cue: refreshed source credentials from a sibling runtime (rotated refresh-token healed)\n`,
1302
1252
  );
1303
1253
  }
1304
- } catch { /* heal is best-effort — never block the launch */ }
1254
+ } catch (err) { debug("launch:runtime-heal", err); /* best-effort — never blocks launch */ }
1305
1255
  return picked;
1306
1256
  }
1307
1257
 
@@ -1414,7 +1364,6 @@ export async function run(args: string[]): Promise<number> {
1414
1364
  const ok = await runGlobalOnboarding();
1415
1365
  if (ok) {
1416
1366
  try {
1417
- const { configDir } = await import("../lib/telemetry-consent");
1418
1367
  mkdirSync(configDir(), { recursive: true });
1419
1368
  writeFileSync(marker, new Date().toISOString() + "\n");
1420
1369
  } catch { /* non-fatal */ }
@@ -1428,32 +1377,108 @@ export async function run(args: string[]): Promise<number> {
1428
1377
  // The picker pre-checks empirical partners in the combine multiselect.
1429
1378
  // Best-effort: any failure (missing log, malformed lines) yields empty.
1430
1379
  let pairSuggestions: Map<string, string[]> | undefined;
1380
+ // Affinity map (own-pick + co-occurrence counts) is mined once here and
1381
+ // reused by the cross-profile frequency suggestions below.
1382
+ let affinity: Map<string, ProfileAffinity> = new Map();
1431
1383
  try {
1432
1384
  const { computeAffinityMap, suggestionsByProfile } = await import("../lib/pair-suggestions");
1433
- const affinity = computeAffinityMap();
1434
- const sug = suggestionsByProfile(affinity);
1385
+ affinity = computeAffinityMap();
1386
+ // Surface a partner after a *single* prior combo: these now render
1387
+ // unchecked + hinted ("you paired these before"), so a low bar is a gentle
1388
+ // recommendation, not an auto-pin. (The stricter defaults still apply to
1389
+ // `cue suggest-pairs`, which reports rather than pre-fills.)
1390
+ const sug = suggestionsByProfile(affinity, { minCount: 1, minAffinity: 0, limit: 6 });
1435
1391
  pairSuggestions = new Map();
1436
1392
  for (const [name, partners] of sug) {
1437
1393
  pairSuggestions.set(name, partners.map((p) => p.name));
1438
1394
  }
1439
- } catch { /* non-fatal */ }
1395
+ } catch (err) { debug("launch:pair-suggestions", err); }
1396
+ // Installed profile names, computed ONCE and shared by the autodetect +
1397
+ // companion passes below (both filter their detections to known profiles).
1398
+ // Previously each pass re-walked listProfiles() independently.
1399
+ let knownProfileNames = new Set<string>();
1400
+ try {
1401
+ knownProfileNames = new Set(await listProfiles());
1402
+ } catch (err) { debug("launch:list-profiles", err); }
1440
1403
  // Cwd autodetect signals, forwarded to runPicker so it can offer a
1441
1404
  // "switch to <X>?" nudge when the user picks a profile that conflicts
1442
1405
  // with what the directory actually looks like (e.g. picking medusa-next
1443
1406
  // in a vite.config.ts project).
1444
1407
  let detected: ReadonlyArray<{ name: string; reasons: string[]; confidence: number }> = [];
1445
1408
  try {
1446
- const knownProfileNames = new Set(await listProfiles());
1447
1409
  detected = detectProfileV2(cwd)
1448
1410
  .filter((d) => knownProfileNames.has(d.profile))
1449
1411
  .map((d) => ({ name: d.profile, reasons: d.reasons, confidence: d.confidence }));
1450
- } catch { /* non-fatal */ }
1412
+ } catch (err) { debug("launch:autodetect", err); }
1413
+ // Content-aware combine companions: scan the cwd for asset/draft/brand
1414
+ // signals and feed matching profiles into the combine multiselect.
1415
+ let companions: CompanionSignal[] = [];
1416
+ try {
1417
+ companions = detectCompanions({ cwd, knownProfiles: knownProfileNames, brands: listPostizzBrands() });
1418
+ } catch (err) { debug("launch:companions", err); }
1419
+ // Cross-profile combine suggestions offered under every primary: the curated
1420
+ // `_featured.yaml` set (improver, secops, builder, …) plus the profiles the
1421
+ // user picks most often (from the affinity map above). Offered unchecked.
1422
+ let universalSuggestions: UniversalSuggestion[] = [];
1423
+ try {
1424
+ const { buildUniversalSuggestions } = await import("../lib/pair-suggestions");
1425
+ const featured = await listFeaturedProfiles();
1426
+ universalSuggestions = buildUniversalSuggestions({ featured, affinity, known: knownProfileNames });
1427
+ } catch (err) { debug("launch:universal-suggestions", err); }
1428
+ // Per-profile resource tally for the combine multiselect's live preview +
1429
+ // per-row hints. Memoized so each offered profile loads at most once.
1430
+ // A shared skill-token reader feeds the always-on estimate (frontmatter
1431
+ // bytes ÷4 ≈ tokens) — same approximation as the post-launch overhead
1432
+ // banner below, so the picker's heads-up and the banner agree.
1433
+ const { readFileSync: readSkillFile } = await import("node:fs");
1434
+ const skillsRootForTally = join(
1435
+ process.env.CUE_REPO_ROOT ?? resolve(new URL(import.meta.url).pathname, "..", "..", ".."),
1436
+ "resources", "skills", "skills",
1437
+ );
1438
+ const skillTokenCache = new Map<string, SkillTokens>();
1439
+ const tokensForSkill = (id: string): SkillTokens => {
1440
+ const c = skillTokenCache.get(id);
1441
+ if (c) return c;
1442
+ let result: SkillTokens = { frontmatter: 0, body: 0 };
1443
+ try {
1444
+ const { frontmatter, body } = splitSkillBytes(readSkillFile(join(skillsRootForTally, id, "SKILL.md"), "utf8"));
1445
+ result = { frontmatter: Math.ceil(frontmatter / 4), body: Math.ceil(body / 4) };
1446
+ } catch { /* skill missing on disk → counts as 0 */ }
1447
+ skillTokenCache.set(id, result);
1448
+ return result;
1449
+ };
1450
+ const tallyCache = new Map<string, ProfileTally>();
1451
+ const resourceTally = async (value: string): Promise<ProfileTally> => {
1452
+ const hit = tallyCache.get(value);
1453
+ if (hit) return hit;
1454
+ const prof = await loadProfile(value);
1455
+ await expandWildcards(prof);
1456
+ const tally: ProfileTally = {
1457
+ // Skills mirror the headline count: one entry per local skill + one per
1458
+ // npx repo (an npx ref bundles several skills under one repo).
1459
+ skills: [
1460
+ ...prof.skills.local.map((s) => `local:${s.id}`),
1461
+ ...prof.skills.npx.map((n) => `npx:${n.repo}`),
1462
+ ],
1463
+ mcps: prof.mcps.map((m) => m.id),
1464
+ plugins: prof.plugins.map((pl) => pl.id),
1465
+ commands: (prof.commands ?? []).slice(),
1466
+ // This profile's own always-on frontmatter cost (parts=undefined → just
1467
+ // its own skills). The picker sums these across the selection.
1468
+ alwaysOn: computeTokenBreakdown(prof, undefined, tokensForSkill).alwaysOn,
1469
+ };
1470
+ tallyCache.set(value, tally);
1471
+ return tally;
1472
+ };
1451
1473
  const picked = await runPicker({
1452
1474
  cwd,
1453
1475
  options,
1454
1476
  noPin: isAccountAlias,
1455
1477
  pairSuggestions,
1456
1478
  detected,
1479
+ companions,
1480
+ universalSuggestions,
1481
+ resourceTally,
1457
1482
  details: async (name) => {
1458
1483
  const loaded = await loadProfile(name);
1459
1484
  await expandWildcards(loaded);
@@ -1562,7 +1587,7 @@ export async function run(args: string[]): Promise<number> {
1562
1587
  try { await rmFile(hashPath, { force: true }); } catch { /* ok */ }
1563
1588
  process.stderr.write(`[cue] profile changed, rebuilding runtime...\n`);
1564
1589
  }
1565
- } catch { /* fail-open — staleness check is best-effort, never blocks launch */ }
1590
+ } catch (err) { debug("launch:staleness", err); /* fail-open — never blocks launch */ }
1566
1591
  }
1567
1592
 
1568
1593
  // --subset / CUE_SMART_SUBSET: ask claude --print which skills are relevant
@@ -1702,6 +1727,16 @@ export async function run(args: string[]): Promise<number> {
1702
1727
  CUE_LAUNCHING: "1",
1703
1728
  };
1704
1729
 
1730
+ // Per-profile claude-mem memory: point the (cue-managed) claude-mem plugin at
1731
+ // an isolated, SQLite-only store + its own worker/server ports so one profile's
1732
+ // memory never bleeds into another's. Best-effort — a failure here must never
1733
+ // block the launch. Opt out with CUE_CLAUDE_MEM_ISOLATE=0. See lib/claude-mem-env.ts.
1734
+ try {
1735
+ const { resolveClaudeMemEnv } = await import("../lib/claude-mem-env");
1736
+ const memEnv = resolveClaudeMemEnv(profileName, { existingEnv: process.env });
1737
+ if (memEnv) Object.assign(childEnv, memEnv);
1738
+ } catch { /* non-fatal — memory isolation is an enhancement, not a gate */ }
1739
+
1705
1740
  if (parsed.dryRun) {
1706
1741
  process.stdout.write(
1707
1742
  JSON.stringify(
@@ -1711,7 +1746,13 @@ export async function run(args: string[]): Promise<number> {
1711
1746
  runtimeDir: runtime.runtimeDir,
1712
1747
  rebuilt: runtime.rebuilt,
1713
1748
  hash: runtime.hash,
1714
- env: { [envKey]: childEnv[envKey] },
1749
+ env: {
1750
+ [envKey]: childEnv[envKey],
1751
+ CLAUDE_MEM_DATA_DIR: childEnv.CLAUDE_MEM_DATA_DIR,
1752
+ CLAUDE_MEM_CHROMA_ENABLED: childEnv.CLAUDE_MEM_CHROMA_ENABLED,
1753
+ CLAUDE_MEM_WORKER_PORT: childEnv.CLAUDE_MEM_WORKER_PORT,
1754
+ CLAUDE_MEM_SERVER_PORT: childEnv.CLAUDE_MEM_SERVER_PORT,
1755
+ },
1715
1756
  command: [parsed.agent, ...parsed.passthrough],
1716
1757
  },
1717
1758
  null,
@@ -1815,13 +1856,26 @@ export async function run(args: string[]): Promise<number> {
1815
1856
  const duration_s = Math.round((Date.now() - new Date(startTs).getTime()) / 1000);
1816
1857
  recordEvent({ ts: new Date().toISOString(), event: "end", profile: profileName, agent: agentKind, cwd: process.cwd(), duration_s });
1817
1858
  } catch { /* best-effort */ }
1818
- // Sync refreshed credentials back to source so next launch has valid tokens
1859
+ // Sync refreshed credentials back to source so next launch has valid tokens.
1860
+ // Freshness guard (mirrors the materializer's preserve step at
1861
+ // runtime-materializer.ts:704): write back ONLY when the runtime token is
1862
+ // strictly newer than source. Without it, a stale runtime — e.g. a sibling
1863
+ // profile rotated the shared source mid-session — would drag a dead, rotated
1864
+ // token over a live one and force a re-login. Anthropic rotates the refresh
1865
+ // token on every refresh, so the highest expiresAt holds the live token.
1866
+ // Must stay synchronous: process.on("exit") handlers cannot await.
1819
1867
  if (credentialsSource) {
1820
1868
  try {
1821
- const { copyFileSync, existsSync: ex } = require("node:fs");
1869
+ const { copyFileSync, readFileSync: rf, existsSync: ex } = require("node:fs");
1822
1870
  const runtimeCreds = join(runtime.runtimeDir, ".credentials.json");
1823
1871
  const sourceCreds = join(credentialsSource, ".credentials.json");
1824
- if (ex(runtimeCreds)) {
1872
+ const expiresAt = (p: string): number => {
1873
+ try {
1874
+ const v = JSON.parse(rf(p, "utf8"))?.claudeAiOauth?.expiresAt;
1875
+ return typeof v === "number" ? v : 0;
1876
+ } catch { return 0; }
1877
+ };
1878
+ if (ex(runtimeCreds) && expiresAt(runtimeCreds) > expiresAt(sourceCreds)) {
1825
1879
  copyFileSync(runtimeCreds, sourceCreds);
1826
1880
  }
1827
1881
  } catch { /* best-effort */ }
@@ -26,7 +26,6 @@ export function isProfileLocked(profileName: string): { locked: boolean; by?: st
26
26
  }
27
27
 
28
28
  export async function run(args: string[]): Promise<number> {
29
- const sub = args[0]; // "lock" or "unlock" (routed from _index.ts)
30
29
  // When called as `cue lock <profile>`, args = ["<profile>", ...]
31
30
  // The command name is already stripped by the router
32
31
  const profileName = args.find(a => !a.startsWith("-"));
@@ -819,10 +819,6 @@ async function cmdPrPreview(repo: string, json: boolean): Promise<number> {
819
819
  return { path, before, after: fixed, fixedRules, leftover: afterDiags };
820
820
  });
821
821
 
822
- // Build one combined PR body (per-file sections)
823
- const allFixed = reports.flatMap((r) => r.fixedRules.map((rule) => ({ path: r.path, rule })));
824
- const allLeftover = reports.flatMap((r) => r.leftover.map((d) => ({ path: r.path, ...d })));
825
-
826
822
  // PR body: passes every file so the diff blocks come out per-file.
827
823
  const primary = reports.find((r) => r.fixedRules.length > 0) ?? reports[0]!;
828
824
  const allFixedRulesPrev = [...new Set(reports.flatMap((r) => r.fixedRules))];
@@ -18,7 +18,7 @@ import { fileURLToPath } from "node:url";
18
18
 
19
19
  import { loadProfile } from "../lib/profile-loader";
20
20
  import { resolveActiveProfile } from "../lib/cwd-resolver";
21
- import { getAdapter, AGENT_IDS, ADAPTERS } from "../lib/agent-adapters";
21
+ import { getAdapter, AGENT_IDS, } from "../lib/agent-adapters";
22
22
 
23
23
  const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
24
24
  const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills");