cue-ai 0.9.2 → 0.9.4

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 (278) hide show
  1. package/CHANGELOG.md +4 -3
  2. package/README.md +154 -394
  3. package/bin/cue-learnings +30 -4
  4. package/bin/cue-review-progress +0 -0
  5. package/bin/cue-review-watch +0 -0
  6. package/dist/cue.js +4328 -3108
  7. package/package.json +1 -1
  8. package/plugins/cue/commands/cue-switch.md +1 -1
  9. package/plugins/cue/commands/cue.md +1 -1
  10. package/profiles/backend/profile.yaml +4 -0
  11. package/profiles/browser/profile.yaml +4 -0
  12. package/profiles/career/profile.yaml +2 -13
  13. package/profiles/commerce/profile.yaml +0 -2
  14. package/profiles/coolify/profile.yaml +0 -1
  15. package/profiles/core/profile.yaml +78 -11
  16. package/profiles/dash-merge-test/profile.yaml +6 -1
  17. package/profiles/designer/profile.yaml +9 -1
  18. package/profiles/dropshipping/profile.yaml +69 -0
  19. package/profiles/frontend/profile.yaml +4 -0
  20. package/profiles/google-ads/profile.yaml +34 -0
  21. package/profiles/google-analytics/profile.yaml +34 -0
  22. package/profiles/google-drive/profile.yaml +34 -0
  23. package/profiles/gstack/profile.yaml +117 -29
  24. package/profiles/marketing/profile.yaml +0 -1
  25. package/profiles/media/README.md +70 -0
  26. package/profiles/media/profile.yaml +104 -0
  27. package/profiles/nano-banana/profile.yaml +52 -0
  28. package/profiles/ops/profile.yaml +1 -2
  29. package/profiles/secops/profile.yaml +3 -0
  30. package/profiles/skill-writer/profile.yaml +15 -0
  31. package/profiles/video/profile.yaml +3 -0
  32. package/profiles/web-frontend-base/profile.yaml +6 -0
  33. package/profiles/webshop/profile.yaml +0 -1
  34. package/profiles/webshop-google/profile.yaml +1 -0
  35. package/profiles/x-growth-bot/profile.yaml +2 -0
  36. package/resources/icons/generate-icons.py +2 -128
  37. package/resources/mcps/configs/claude.sanitized.json +88 -20
  38. package/resources/mcps/configs/claude_runtime.sanitized.json +40 -1
  39. package/resources/mcps/configs/codex.sanitized.json +29 -0
  40. package/resources/skills/skills/career/job-hunter/LICENSE +21 -0
  41. package/resources/skills/skills/career/job-hunter/README.md +323 -0
  42. package/resources/skills/skills/career/job-hunter/SKILL.md +91 -0
  43. package/resources/skills/skills/career/job-hunter/agents/README.md +96 -0
  44. package/resources/skills/skills/career/job-hunter/agents/apply-assessment-prep.md +195 -0
  45. package/resources/skills/skills/career/job-hunter/agents/apply-ats-scan.md +155 -0
  46. package/resources/skills/skills/career/job-hunter/agents/apply-bias-audit.md +224 -0
  47. package/resources/skills/skills/career/job-hunter/agents/apply-cover-letter.md +69 -0
  48. package/resources/skills/skills/career/job-hunter/agents/apply-decode-jd.md +117 -0
  49. package/resources/skills/skills/career/job-hunter/agents/apply-fit-score.md +183 -0
  50. package/resources/skills/skills/career/job-hunter/agents/apply-linkedin-audit.md +74 -0
  51. package/resources/skills/skills/career/job-hunter/agents/apply-linkedin-scrape.md +255 -0
  52. package/resources/skills/skills/career/job-hunter/agents/apply-portfolio-brief.md +123 -0
  53. package/resources/skills/skills/career/job-hunter/agents/apply-reality-check.md +164 -0
  54. package/resources/skills/skills/career/job-hunter/agents/apply-reference-prep.md +150 -0
  55. package/resources/skills/skills/career/job-hunter/agents/apply-rejection-analysis.md +172 -0
  56. package/resources/skills/skills/career/job-hunter/agents/apply-resume.md +70 -0
  57. package/resources/skills/skills/career/job-hunter/agents/apply-skills-gap-filler.md +109 -0
  58. package/resources/skills/skills/career/job-hunter/agents/career-internal.md +94 -0
  59. package/resources/skills/skills/career/job-hunter/agents/career-linkedin-content.md +173 -0
  60. package/resources/skills/skills/career/job-hunter/agents/career-linkedin-scanner.md +262 -0
  61. package/resources/skills/skills/career/job-hunter/agents/career-network-message.md +108 -0
  62. package/resources/skills/skills/career/job-hunter/agents/career-promote.md +102 -0
  63. package/resources/skills/skills/career/job-hunter/agents/career-review.md +71 -0
  64. package/resources/skills/skills/career/job-hunter/agents/interview-debrief.md +117 -0
  65. package/resources/skills/skills/career/job-hunter/agents/interview-mock.md +171 -0
  66. package/resources/skills/skills/career/job-hunter/agents/interview-panel-decoder.md +152 -0
  67. package/resources/skills/skills/career/job-hunter/agents/interview-prep.md +184 -0
  68. package/resources/skills/skills/career/job-hunter/agents/interview-question-bank.md +133 -0
  69. package/resources/skills/skills/career/job-hunter/agents/interview-research.md +148 -0
  70. package/resources/skills/skills/career/job-hunter/agents/offer-compare.md +117 -0
  71. package/resources/skills/skills/career/job-hunter/agents/offer-counteroffer.md +144 -0
  72. package/resources/skills/skills/career/job-hunter/agents/offer-deadline-manager.md +148 -0
  73. package/resources/skills/skills/career/job-hunter/agents/offer-negotiate.md +126 -0
  74. package/resources/skills/skills/career/job-hunter/agents/offer-schedule.md +99 -0
  75. package/resources/skills/skills/career/job-hunter/agents/offer-thankyou.md +80 -0
  76. package/resources/skills/skills/career/job-hunter/agents/search-company-research.md +146 -0
  77. package/resources/skills/skills/career/job-hunter/agents/search-follow-up.md +129 -0
  78. package/resources/skills/skills/career/job-hunter/agents/search-ghost-job-detector.md +152 -0
  79. package/resources/skills/skills/career/job-hunter/agents/search-inbox-scan.md +193 -0
  80. package/resources/skills/skills/career/job-hunter/agents/search-interview-scorecard.md +164 -0
  81. package/resources/skills/skills/career/job-hunter/agents/search-jobs.md +149 -0
  82. package/resources/skills/skills/career/job-hunter/agents/search-momentum-check.md +194 -0
  83. package/resources/skills/skills/career/job-hunter/agents/search-outreach.md +85 -0
  84. package/resources/skills/skills/career/job-hunter/agents/search-referral-finder.md +124 -0
  85. package/resources/skills/skills/career/job-hunter/agents/search-salary.md +96 -0
  86. package/resources/skills/skills/career/job-hunter/agents/search-send-email.md +109 -0
  87. package/resources/skills/skills/career/job-hunter/agents/search-tracker-update.md +127 -0
  88. package/resources/skills/skills/career/job-hunter/inputs/README.md +26 -0
  89. package/resources/skills/skills/career/job-hunter/inputs/apply-linkedin-url.txt +8 -0
  90. package/resources/skills/skills/career/job-hunter/inputs/interview-context.md +24 -0
  91. package/resources/skills/skills/career/job-hunter/inputs/job-description.md +20 -0
  92. package/resources/skills/skills/career/job-hunter/inputs/job-search-criteria.md +36 -0
  93. package/resources/skills/skills/career/job-hunter/inputs/my-linkedin.md +24 -0
  94. package/resources/skills/skills/career/job-hunter/inputs/my-resume.md +28 -0
  95. package/resources/skills/skills/career/job-hunter/inputs/search-outreach-target.md +24 -0
  96. package/resources/skills/skills/career/job-hunter/rules/README.md +37 -0
  97. package/resources/skills/skills/career/job-hunter/rules/writing-rules.md +81 -0
  98. package/resources/skills/skills/design/banana/SKILL.md +375 -0
  99. package/resources/skills/skills/design/banana/references/cost-tracking.md +47 -0
  100. package/resources/skills/skills/design/banana/references/gemini-models.md +236 -0
  101. package/resources/skills/skills/design/banana/references/mcp-tools.md +145 -0
  102. package/resources/skills/skills/design/banana/references/post-processing.md +192 -0
  103. package/resources/skills/skills/design/banana/references/presets.md +69 -0
  104. package/resources/skills/skills/design/banana/references/prompt-engineering.md +481 -0
  105. package/resources/skills/skills/design/banana/scripts/batch.py +97 -0
  106. package/resources/skills/skills/design/banana/scripts/cost_tracker.py +191 -0
  107. package/resources/skills/skills/design/banana/scripts/edit.py +159 -0
  108. package/resources/skills/skills/design/banana/scripts/generate.py +168 -0
  109. package/resources/skills/skills/design/banana/scripts/presets.py +154 -0
  110. package/resources/skills/skills/design/banana/scripts/setup_mcp.py +151 -0
  111. package/resources/skills/skills/design/banana/scripts/validate_setup.py +133 -0
  112. package/resources/skills/skills/gstack/ship/SKILL.md +13 -0
  113. package/resources/skills/skills/media/3d-logo-animation/SKILL.md +59 -0
  114. package/resources/skills/skills/media/action-figure-generator/SKILL.md +48 -0
  115. package/resources/skills/skills/media/ad-creative/SKILL.md +79 -0
  116. package/resources/skills/skills/media/ai-clipping/SKILL.md +194 -0
  117. package/resources/skills/skills/media/ai-clipping/scripts/run-ai-clipping.sh +200 -0
  118. package/resources/skills/skills/media/ai-fight-scene/SKILL.md +132 -0
  119. package/resources/skills/skills/media/amazon-product-listing/SKILL.md +68 -0
  120. package/resources/skills/skills/media/animal-video-generator/SKILL.md +59 -0
  121. package/resources/skills/skills/media/award-ceremony-video/SKILL.md +87 -0
  122. package/resources/skills/skills/media/blog-header/SKILL.md +61 -0
  123. package/resources/skills/skills/media/brand-kit/SKILL.md +72 -0
  124. package/resources/skills/skills/media/brochures/SKILL.md +65 -0
  125. package/resources/skills/skills/media/cartoon-dance-animation/SKILL.md +62 -0
  126. package/resources/skills/skills/media/character-story-video/SKILL.md +84 -0
  127. package/resources/skills/skills/media/chibi-collage-effect/SKILL.md +63 -0
  128. package/resources/skills/skills/media/cinema-director/SKILL.md +93 -0
  129. package/resources/skills/skills/media/cinema-director/scripts/generate-film.sh +78 -0
  130. package/resources/skills/skills/media/color-analysis-board/SKILL.md +71 -0
  131. package/resources/skills/skills/media/core-edit/SKILL.md +48 -0
  132. package/resources/skills/skills/media/core-edit/edit-image.sh +54 -0
  133. package/resources/skills/skills/media/core-edit/enhance-image.sh +191 -0
  134. package/resources/skills/skills/media/core-edit/lipsync.sh +144 -0
  135. package/resources/skills/skills/media/core-edit/video-effects.sh +193 -0
  136. package/resources/skills/skills/media/core-media/SKILL.md +49 -0
  137. package/resources/skills/skills/media/core-media/create-music.sh +169 -0
  138. package/resources/skills/skills/media/core-media/generate-image.sh +161 -0
  139. package/resources/skills/skills/media/core-media/generate-video.sh +137 -0
  140. package/resources/skills/skills/media/core-media/image-to-video.sh +228 -0
  141. package/resources/skills/skills/media/core-media/schema_data.json +18708 -0
  142. package/resources/skills/skills/media/core-media/upload.sh +41 -0
  143. package/resources/skills/skills/media/core-platform/SKILL.md +41 -0
  144. package/resources/skills/skills/media/core-platform/check-result.sh +37 -0
  145. package/resources/skills/skills/media/core-platform/setup.sh +31 -0
  146. package/resources/skills/skills/media/couple-grid-creator/SKILL.md +47 -0
  147. package/resources/skills/skills/media/design-guide/SKILL.md +73 -0
  148. package/resources/skills/skills/media/drone-style-video/SKILL.md +61 -0
  149. package/resources/skills/skills/media/fashion-try-on/SKILL.md +61 -0
  150. package/resources/skills/skills/media/floor-plan-rendering/SKILL.md +56 -0
  151. package/resources/skills/skills/media/freeze-effect-video/SKILL.md +100 -0
  152. package/resources/skills/skills/media/giant-product-showcase/SKILL.md +61 -0
  153. package/resources/skills/skills/media/instagram-post/SKILL.md +58 -0
  154. package/resources/skills/skills/media/interior-design/SKILL.md +61 -0
  155. package/resources/skills/skills/media/interior-design-visualizer/SKILL.md +57 -0
  156. package/resources/skills/skills/media/jewelry-product-video/SKILL.md +61 -0
  157. package/resources/skills/skills/media/kdenlive/SKILL.md +106 -0
  158. package/resources/skills/skills/media/kdenlive/scripts/assemble.sh +57 -0
  159. package/resources/skills/skills/media/kdenlive/scripts/common.sh +30 -0
  160. package/resources/skills/skills/media/kdenlive/scripts/inspect.sh +19 -0
  161. package/resources/skills/skills/media/kdenlive/scripts/reframe.sh +22 -0
  162. package/resources/skills/skills/media/kdenlive/scripts/render.sh +16 -0
  163. package/resources/skills/skills/media/kdenlive/scripts/title-card.sh +25 -0
  164. package/resources/skills/skills/media/keyboard-art-maker/SKILL.md +44 -0
  165. package/resources/skills/skills/media/logo-branding/SKILL.md +70 -0
  166. package/resources/skills/skills/media/logo-creator/SKILL.md +80 -0
  167. package/resources/skills/skills/media/logo-creator/scripts/create-logo.sh +38 -0
  168. package/resources/skills/skills/media/logo-generator/SKILL.md +56 -0
  169. package/resources/skills/skills/media/multi-angle-reshoot/SKILL.md +70 -0
  170. package/resources/skills/skills/media/multi-angle-shots/SKILL.md +73 -0
  171. package/resources/skills/skills/media/music-video/SKILL.md +61 -0
  172. package/resources/skills/skills/media/nano-banana/SKILL.md +80 -0
  173. package/resources/skills/skills/media/nano-banana/scripts/generate-nano-art.sh +54 -0
  174. package/resources/skills/skills/media/one-shot-video/SKILL.md +56 -0
  175. package/resources/skills/skills/media/photo-pack-generator/SKILL.md +205 -0
  176. package/resources/skills/skills/media/photo-pack-generator/scripts/generate-pack.sh +241 -0
  177. package/resources/skills/skills/media/product-ad-cinematic/SKILL.md +78 -0
  178. package/resources/skills/skills/media/product-campaign/SKILL.md +76 -0
  179. package/resources/skills/skills/media/product-showcase-video/SKILL.md +60 -0
  180. package/resources/skills/skills/media/product-video-ad-maker/SKILL.md +59 -0
  181. package/resources/skills/skills/media/rednote-cover/SKILL.md +57 -0
  182. package/resources/skills/skills/media/seedance-2/SKILL.md +632 -0
  183. package/resources/skills/skills/media/seedance-2/scripts/generate-seedance.sh +701 -0
  184. package/resources/skills/skills/media/selfie-with-celebrities/SKILL.md +64 -0
  185. package/resources/skills/skills/media/social-media-video/SKILL.md +277 -0
  186. package/resources/skills/skills/media/social-media-video/scripts/run-social-video.sh +316 -0
  187. package/resources/skills/skills/media/social-pack/SKILL.md +58 -0
  188. package/resources/skills/skills/media/storyboard/SKILL.md +57 -0
  189. package/resources/skills/skills/media/storyboard-to-cooking-video/SKILL.md +143 -0
  190. package/resources/skills/skills/media/talking-baby-video/SKILL.md +57 -0
  191. package/resources/skills/skills/media/ugc-ads-workflow/SKILL.md +70 -0
  192. package/resources/skills/skills/media/ugc-lifestyle-try-on/SKILL.md +65 -0
  193. package/resources/skills/skills/media/ugc-video-factory/SKILL.md +134 -0
  194. package/resources/skills/skills/media/ui-design/SKILL.md +81 -0
  195. package/resources/skills/skills/media/ui-design/scripts/generate-mockup.sh +49 -0
  196. package/resources/skills/skills/media/url-to-design/SKILL.md +61 -0
  197. package/resources/skills/skills/media/workflow/SKILL.md +197 -0
  198. package/resources/skills/skills/media/workflow/scripts/discover-workflow.sh +18 -0
  199. package/resources/skills/skills/media/workflow/scripts/generate-workflow.sh +33 -0
  200. package/resources/skills/skills/media/workflow/scripts/interactive-run.sh +16 -0
  201. package/resources/skills/skills/media/workflow/scripts/list-workflows.sh +20 -0
  202. package/resources/skills/skills/media/workflow/scripts/run-workflow.sh +34 -0
  203. package/resources/skills/skills/media/youtube-shorts/SKILL.md +173 -0
  204. package/resources/skills/skills/media/youtube-shorts/scripts/run-youtube-shorts.sh +141 -0
  205. package/resources/skills/skills/media/youtube-thumbnail/SKILL.md +66 -0
  206. package/resources/skills/skills/meta/cue-developer/references/architecture.md +2 -2
  207. package/resources/skills/skills/meta/cue-usage/SKILL.md +1 -1
  208. package/resources/skills/skills/meta/profile-fit-monitor/SKILL.md +2 -2
  209. package/resources/skills/skills/meta/profile-optimizer/SKILL.md +1 -1
  210. package/resources/skills/skills/meta/profile-suggest/SKILL.md +7 -7
  211. package/resources/skills/skills/meta/profile-summon/SKILL.md +159 -0
  212. package/resources/skills/skills/meta/profile-summon/evals/evals.json +53 -0
  213. package/resources/skills/skills/meta/save-profile/SKILL.md +1 -1
  214. package/resources/skills/skills/meta/skill-reviewer/SKILL.md +3 -0
  215. package/resources/skills/skills/meta/skill-reviewer/references/tdd-for-skills.md +55 -0
  216. package/resources/skills/skills/research/find-skills/SKILL.md +1 -1
  217. package/resources/skills/skills/review/code-review-deep/SKILL.md +20 -0
  218. package/resources/skills/skills/security/trivy-scan/SKILL.md +139 -0
  219. package/resources/skills/skills/security/trivy-scan/scripts/ensure-trivy.sh +21 -0
  220. package/resources/skills/skills/tools/ccusage/SKILL.md +142 -0
  221. package/src/commands/_index.ts +8 -0
  222. package/src/commands/ai.ts +2 -2
  223. package/src/commands/auto-detect.test.ts +74 -0
  224. package/src/commands/auto-detect.ts +9 -7
  225. package/src/commands/cli.test.ts +20 -4
  226. package/src/commands/cli.ts +36 -20
  227. package/src/commands/create-profile.ts +2 -2
  228. package/src/commands/debug.ts +2 -2
  229. package/src/commands/discover.ts +14 -4
  230. package/src/commands/export-docker.ts +1 -1
  231. package/src/commands/features-batch1.test.ts +1 -1
  232. package/src/commands/gates.ts +1 -1
  233. package/src/commands/import-profile.ts +1 -1
  234. package/src/commands/init.ts +15 -11
  235. package/src/commands/install.test.ts +192 -0
  236. package/src/commands/install.ts +610 -0
  237. package/src/commands/launch-handoff.e2e.test.ts +33 -1
  238. package/src/commands/launch.e2e.test.ts +15 -10
  239. package/src/commands/launch.ts +73 -116
  240. package/src/commands/materialize.ts +2 -2
  241. package/src/commands/prune.ts +1 -1
  242. package/src/commands/security-audit.ts +1 -1
  243. package/src/commands/shell.ts +7 -7
  244. package/src/commands/skill-report.ts +1 -1
  245. package/src/commands/skills.ts +3 -3
  246. package/src/commands/snapshot.ts +2 -2
  247. package/src/commands/summon.test.ts +116 -0
  248. package/src/commands/summon.ts +338 -0
  249. package/src/commands/trigger-gaps.ts +1 -1
  250. package/src/commands/use.ts +47 -3
  251. package/src/commands/watch-live.ts +5 -5
  252. package/src/commands/watch.ts +8 -8
  253. package/src/index.ts +2 -0
  254. package/src/lib/active-sessions.test.ts +3 -3
  255. package/src/lib/active-sessions.ts +4 -4
  256. package/src/lib/auto-detect.test.ts +172 -8
  257. package/src/lib/auto-detect.ts +191 -136
  258. package/src/lib/codex-persona-parity.test.ts +58 -0
  259. package/src/lib/companion-detect.test.ts +43 -1
  260. package/src/lib/companion-detect.ts +35 -0
  261. package/src/lib/credentials-sync.test.ts +121 -1
  262. package/src/lib/credentials-sync.ts +95 -1
  263. package/src/lib/cwd-resolver.test.ts +8 -8
  264. package/src/lib/cwd-resolver.ts +2 -2
  265. package/src/lib/dashboard-merge.test.ts +9 -4
  266. package/src/lib/dashboard-server.ts +1 -1
  267. package/src/lib/picker.test.ts +1 -1
  268. package/src/lib/picker.ts +5 -5
  269. package/src/lib/profile-merge.test.ts +8 -0
  270. package/src/lib/profile-names.test.ts +3 -3
  271. package/src/lib/runtime-install.ts +166 -0
  272. package/src/lib/runtime-materializer.test.ts +137 -0
  273. package/src/lib/runtime-materializer.ts +105 -2
  274. package/src/lib/skill-router.test.ts +38 -0
  275. package/src/lib/skill-router.ts +65 -4
  276. package/profiles/eu-tender-research/README.md +0 -48
  277. package/profiles/eu-tender-research/logo.png +0 -0
  278. package/profiles/eu-tender-research/profile.yaml +0 -108
@@ -0,0 +1,338 @@
1
+ /**
2
+ * `cue summon [profile]` — bind a profile into the LIVE session, no cold restart.
3
+ *
4
+ * When you open a directory with no `.cue.profile`, the right profile's skills
5
+ * and MCPs normally need a pin + a full `claude` restart (CLAUDE_CONFIG_DIR, the
6
+ * Skill() list, MCP servers, and /slash commands are frozen at boot). `summon`
7
+ * is the two-tier bridge:
8
+ *
9
+ * Tier A (now, zero restart): resolve a profile, list its skills as
10
+ * readable SKILL.md paths + a persona, so the running agent can soft-load
11
+ * them inline (the `meta/profile-summon` skill drives this — same mechanism
12
+ * as `meta/smart-loader`, just whole-profile).
13
+ * Tier B (durable + full fidelity): write the `.cue.profile` pin so the next
14
+ * launch is correct, and hand back the warm re-exec (`claude --continue`)
15
+ * that resumes THIS conversation under the fully-materialized profile —
16
+ * the only honest way to get the MCP servers + /slash commands the soft
17
+ * load can't fake.
18
+ *
19
+ * This command never spawns an agent and never fakes MCP tools. Its only side
20
+ * effect is writing the pin (skippable with `--no-pin`).
21
+ *
22
+ * Flags:
23
+ * [profile] force this profile (else auto-detect from cwd)
24
+ * --json machine-readable output (consumed by meta/profile-summon)
25
+ * --no-pin don't write .cue.profile
26
+ * --pick list detected candidates and exit (no pin, no apply)
27
+ * --active <name> override active-session profile detection (for mcp_status)
28
+ * --dry-run compute everything, write nothing
29
+ */
30
+
31
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
32
+ import { basename, join } from "node:path";
33
+
34
+ import { loadProfile, listProfiles } from "../lib/profile-loader";
35
+ import { resolveLocalSkill } from "../lib/resolver-local";
36
+ import { detectProfileV2 } from "../lib/auto-detect";
37
+ import { getSkillDependencies } from "../lib/skill-dependencies";
38
+
39
+ /** Minimum auto-detect confidence to summon without an explicit arg. Mirrors
40
+ * SUGGESTED_MIN_CONFIDENCE in launch.ts so the picker and summon agree. */
41
+ export const SUMMON_MIN_CONFIDENCE = 0.5;
42
+
43
+ /** The warm re-exec that resumes this conversation under the full profile. */
44
+ export const REEXEC_CMD = "claude --continue";
45
+
46
+ export interface SummonOptions {
47
+ cwd: string;
48
+ /** Explicit target profile; when null, auto-detect from cwd. */
49
+ profile?: string | null;
50
+ /** Override active-session profile detection (for mcp_status). */
51
+ active?: string | null;
52
+ noPin?: boolean;
53
+ dryRun?: boolean;
54
+ }
55
+
56
+ export interface SummonSkill {
57
+ id: string;
58
+ /** Absolute SKILL.md path to Read, or "" when it can't be resolved on disk. */
59
+ path: string;
60
+ /** "ok" (soft-loadable now) or "missing:<mcp1,mcp2>" (needs the harness). */
61
+ mcp_status: string;
62
+ }
63
+
64
+ export interface SummonResult {
65
+ profile: string;
66
+ /** Profile persona prose to apply inline ("" when none declared). */
67
+ persona: string;
68
+ /** true when the profile was auto-detected (no explicit arg). */
69
+ detected: boolean;
70
+ confidence?: number;
71
+ reasons?: string[];
72
+ /** Active running-session profile selector, or null when undetectable. */
73
+ active_profile: string | null;
74
+ pin_written: boolean;
75
+ pin_path: string;
76
+ /** Existing `.cue.profile` content before this summon, or null when none.
77
+ * Lets the consumer flag a re-pin over a *different* profile instead of a
78
+ * silent clobber, and skip a redundant write when it already matches. */
79
+ pin_previous: string | null;
80
+ /** Local skills — soft-loadable inline (read each `path`). */
81
+ skills: SummonSkill[];
82
+ /** npx skills — loaded at launch only, can't be soft-loaded as prose. */
83
+ npx_skills: string[];
84
+ /** Profile MCPs and whether they're already loaded in the active session. */
85
+ mcps: { id: string; loaded: boolean }[];
86
+ /** /slash commands the profile adds (need the harness). */
87
+ commands: string[];
88
+ plugins: string[];
89
+ reexec_cmd: string;
90
+ }
91
+
92
+ /**
93
+ * Detect the profile of the *currently running* session (not the cwd's pin).
94
+ * Mirrors `resolve_active_profile` in smart-lookup.sh: env first, then the
95
+ * CLAUDE_CONFIG_DIR runtime path. Returns a selector like "core+skill-writer".
96
+ */
97
+ export function detectActiveProfile(env: NodeJS.ProcessEnv = process.env): string | null {
98
+ const fromEnv = env.CUE_ACTIVE_PROFILE || env.CUE_PROFILE;
99
+ if (fromEnv) return fromEnv;
100
+ const ccd = env.CLAUDE_CONFIG_DIR;
101
+ if (ccd) {
102
+ const m = ccd.match(/\/cue\/runtime\/(.+?)\/claude\/?$/);
103
+ if (m) return m[1]!;
104
+ }
105
+ return null;
106
+ }
107
+
108
+ /** Union of MCP ids (lowercased) loaded by an active profile selector. */
109
+ async function loadActiveMcpIds(selector: string | null): Promise<Set<string>> {
110
+ const ids = new Set<string>();
111
+ if (!selector) return ids;
112
+ for (const part of selector.split("+").map((s) => s.trim()).filter(Boolean)) {
113
+ try {
114
+ const p = await loadProfile(part);
115
+ for (const m of p.mcps) ids.add(m.id.toLowerCase());
116
+ } catch {
117
+ // Unknown/stale part — skip; conservative (its MCPs count as not loaded).
118
+ }
119
+ }
120
+ return ids;
121
+ }
122
+
123
+ /** "ok" when every MCP a skill needs is loaded in the active session, else the
124
+ * missing list. Skills with no MCP deps are always "ok" (pure-prose soft-load). */
125
+ function skillMcpStatus(skillId: string, activeMcps: Set<string>): string {
126
+ const deps = getSkillDependencies(skillId);
127
+ if (deps.length === 0) return "ok";
128
+ const missing = [
129
+ ...new Set(deps.map((d) => d.mcpId).filter((m) => !activeMcps.has(m.toLowerCase()))),
130
+ ];
131
+ return missing.length === 0 ? "ok" : `missing:${missing.join(",")}`;
132
+ }
133
+
134
+ /**
135
+ * Resolve which profile to summon: explicit arg (must exist) or the top
136
+ * auto-detection above the confidence floor. Throws a user-facing Error when
137
+ * nothing resolves.
138
+ */
139
+ async function resolveTarget(
140
+ explicit: string | null | undefined,
141
+ cwd: string,
142
+ ): Promise<{ profile: string; detected: boolean; confidence?: number; reasons?: string[] }> {
143
+ const known = new Set(await listProfiles());
144
+ if (explicit) {
145
+ if (!known.has(explicit)) {
146
+ throw new Error(`unknown profile "${explicit}" — run \`cue list\` to see profiles`);
147
+ }
148
+ return { profile: explicit, detected: false };
149
+ }
150
+ const dets = detectProfileV2(cwd).filter((d) => known.has(d.profile));
151
+ const top = dets[0];
152
+ if (!top || top.confidence < SUMMON_MIN_CONFIDENCE) {
153
+ throw new Error(
154
+ "no profile confidently matches this directory — pass one explicitly: `cue summon <profile>`",
155
+ );
156
+ }
157
+ return { profile: top.profile, detected: true, confidence: top.confidence, reasons: top.reasons };
158
+ }
159
+
160
+ /**
161
+ * Pure core: resolve, enumerate, (optionally) pin. No printing, no agent spawn.
162
+ * Exported for tests and for the `meta/profile-summon` skill via `--json`.
163
+ */
164
+ export async function summon(opts: SummonOptions): Promise<SummonResult> {
165
+ const target = await resolveTarget(opts.profile, opts.cwd);
166
+ const profile = await loadProfile(target.profile);
167
+
168
+ // `undefined` (omitted) → auto-detect the running session; explicit `null` →
169
+ // treat as no active session (every MCP-gated skill counts as not loaded).
170
+ const active = opts.active === undefined ? detectActiveProfile() : opts.active;
171
+ const activeMcps = await loadActiveMcpIds(active);
172
+
173
+ const skills: SummonSkill[] = [];
174
+ for (const s of profile.skills.local) {
175
+ let path = "";
176
+ try {
177
+ path = join(await resolveLocalSkill(s.id), "SKILL.md");
178
+ } catch {
179
+ // Skill id doesn't resolve on disk — still report it; the consumer can
180
+ // fall through to smart-loader's filesystem scan.
181
+ }
182
+ skills.push({ id: s.id, path, mcp_status: skillMcpStatus(s.id, activeMcps) });
183
+ }
184
+
185
+ const pinPath = join(opts.cwd, ".cue.profile");
186
+ const pinPrevious = existsSync(pinPath) ? readFileSync(pinPath, "utf8").trim() || null : null;
187
+ let pinWritten = false;
188
+ // Skip the write when it's already pinned to this profile (no-op, respects
189
+ // the skill's pinned-noop contract); otherwise write — a re-pin over a
190
+ // DIFFERENT profile is surfaced via `pin_previous`, never silently clobbered.
191
+ if (!opts.noPin && !opts.dryRun && pinPrevious !== profile.name) {
192
+ writeFileSync(pinPath, `${profile.name}\n`);
193
+ pinWritten = true;
194
+ }
195
+
196
+ return {
197
+ profile: profile.name,
198
+ persona: profile.persona ?? "",
199
+ detected: target.detected,
200
+ confidence: target.confidence,
201
+ reasons: target.reasons,
202
+ active_profile: active,
203
+ pin_written: pinWritten,
204
+ pin_path: pinPath,
205
+ pin_previous: pinPrevious,
206
+ skills,
207
+ npx_skills: profile.skills.npx.flatMap((n) => n.skills),
208
+ mcps: profile.mcps.map((m) => ({ id: m.id, loaded: activeMcps.has(m.id.toLowerCase()) })),
209
+ commands: profile.commands.map((c) => `/${basename(c, ".md")}`),
210
+ plugins: profile.plugins.map((p) => p.id),
211
+ reexec_cmd: REEXEC_CMD,
212
+ };
213
+ }
214
+
215
+ // ---------------------------------------------------------------------------
216
+ // CLI wrapper
217
+ // ---------------------------------------------------------------------------
218
+
219
+ const HELP = `cue summon — bind a profile into the LIVE session, no cold restart
220
+
221
+ Usage: cue summon [profile] [flags]
222
+
223
+ Resolves a profile (explicit arg, else auto-detected from this directory),
224
+ lists its skills as readable SKILL.md paths so the running agent can soft-load
225
+ them inline, pins .cue.profile, and prints the warm re-exec (\`${REEXEC_CMD}\`)
226
+ that resumes this conversation under the full profile (MCPs + /slash commands).
227
+
228
+ Flags:
229
+ --json machine-readable output (for meta/profile-summon)
230
+ --no-pin don't write .cue.profile
231
+ --pick list detected candidates and exit (no pin)
232
+ --active <name> override active-session profile (affects mcp_status)
233
+ --dry-run compute everything, write nothing
234
+ -h, --help show this help
235
+
236
+ Examples:
237
+ cue summon vercel # summon a known profile here
238
+ cue summon # auto-detect from cwd
239
+ cue summon --json | jq # drive from the meta/profile-summon skill
240
+ `;
241
+
242
+ function printHuman(r: SummonResult): void {
243
+ const out: string[] = [];
244
+ const det = r.detected ? ` (auto-detected, ${Math.round((r.confidence ?? 0) * 100)}% match)` : "";
245
+ out.push(`🔮 summon ${r.profile}${det}`);
246
+ if (r.detected && r.reasons?.length) out.push(` why: ${r.reasons.slice(0, 3).join(", ")}`);
247
+ out.push("");
248
+
249
+ const loadable = r.skills.filter((s) => s.mcp_status === "ok");
250
+ const gated = r.skills.filter((s) => s.mcp_status !== "ok");
251
+ out.push(`✅ soft-load now (no restart): persona${r.persona ? "" : " (none)"} + ${loadable.length} skill${loadable.length === 1 ? "" : "s"}`);
252
+ for (const s of loadable.slice(0, 8)) out.push(` • ${s.id}`);
253
+ if (loadable.length > 8) out.push(` …and ${loadable.length - 8} more`);
254
+
255
+ const harnessBits: string[] = [];
256
+ if (gated.length) harnessBits.push(`${gated.length} MCP-gated skill${gated.length === 1 ? "" : "s"}`);
257
+ if (r.npx_skills.length) harnessBits.push(`${r.npx_skills.length} npx skill${r.npx_skills.length === 1 ? "" : "s"}`);
258
+ const unloadedMcps = r.mcps.filter((m) => !m.loaded).map((m) => m.id);
259
+ if (unloadedMcps.length) harnessBits.push(`MCP: ${unloadedMcps.join(", ")}`);
260
+ if (r.plugins.length) harnessBits.push(`plugins: ${r.plugins.join(", ")}`);
261
+ if (r.commands.length) harnessBits.push(`commands: ${r.commands.slice(0, 6).join(" ")}`);
262
+ if (harnessBits.length) {
263
+ out.push("");
264
+ out.push(`🔒 needs the harness (won't soft-load): ${harnessBits.join(" · ")}`);
265
+ }
266
+
267
+ out.push("");
268
+ if (r.pin_written && r.pin_previous && r.pin_previous !== r.profile) {
269
+ out.push(`📌 repinned .cue.profile: ${r.pin_previous} → ${r.profile} (replaced a different pin)`);
270
+ } else if (r.pin_written) {
271
+ out.push(`📌 pinned .cue.profile → ${r.profile}`);
272
+ } else if (r.pin_previous === r.profile) {
273
+ out.push(`📌 already pinned → ${r.profile}`);
274
+ } else {
275
+ out.push(`📌 pin skipped`);
276
+ }
277
+ out.push(`↻ full fidelity (MCPs + /slash): run \`${r.reexec_cmd}\` resumes this conversation`);
278
+ process.stdout.write(out.join("\n") + "\n");
279
+ }
280
+
281
+ export async function run(args: string[]): Promise<number> {
282
+ if (args.includes("-h") || args.includes("--help")) {
283
+ process.stdout.write(HELP);
284
+ return 0;
285
+ }
286
+
287
+ let profile: string | null = null;
288
+ // undefined → auto-detect the running session; only set when --active passed.
289
+ let active: string | undefined;
290
+ let json = false;
291
+ let noPin = false;
292
+ let pick = false;
293
+ let dryRun = false;
294
+ for (let i = 0; i < args.length; i++) {
295
+ const a = args[i]!;
296
+ if (a === "--json") json = true;
297
+ else if (a === "--no-pin") noPin = true;
298
+ else if (a === "--pick") pick = true;
299
+ else if (a === "--dry-run") dryRun = true;
300
+ else if (a === "--active") active = args[++i] ?? undefined;
301
+ else if (!a.startsWith("-") && profile === null) profile = a;
302
+ }
303
+
304
+ const cwd = process.cwd();
305
+
306
+ // --pick: just surface the candidates, don't act.
307
+ if (pick) {
308
+ const known = new Set(await listProfiles());
309
+ const dets = detectProfileV2(cwd).filter((d) => known.has(d.profile));
310
+ if (json) {
311
+ process.stdout.write(JSON.stringify({ candidates: dets }, null, 2) + "\n");
312
+ } else if (dets.length === 0) {
313
+ process.stdout.write("No profile confidently matches this directory.\n");
314
+ } else {
315
+ process.stdout.write("Detected profiles for this directory:\n");
316
+ for (const d of dets.slice(0, 5)) {
317
+ process.stdout.write(` ${Math.round(d.confidence * 100)}% ${d.profile} — ${d.reasons.slice(0, 2).join(", ")}\n`);
318
+ }
319
+ process.stdout.write(`\nSummon one: cue summon <profile>\n`);
320
+ }
321
+ return 0;
322
+ }
323
+
324
+ let result: SummonResult;
325
+ try {
326
+ result = await summon({ cwd, profile, active, noPin, dryRun });
327
+ } catch (err) {
328
+ process.stderr.write(`cue summon: ${err instanceof Error ? err.message : String(err)}\n`);
329
+ return 1;
330
+ }
331
+
332
+ if (json) {
333
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
334
+ } else {
335
+ printHuman(result);
336
+ }
337
+ return 0;
338
+ }
@@ -73,7 +73,7 @@ function helpText(): string {
73
73
 
74
74
  function resolveActiveProfile(explicit: string | null): string | null {
75
75
  if (explicit) return explicit;
76
- const pin = join(process.cwd(), ".cue-profile");
76
+ const pin = join(process.cwd(), ".cue.profile");
77
77
  if (existsSync(pin)) {
78
78
  try {
79
79
  const txt = readFileSync(pin, "utf8").trim().split("\n")[0]?.trim();
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * `cue use <profile>` — pin a profile to the current directory.
3
3
  *
4
- * Writes `.cue-profile` in CWD (or $HOME with --global).
4
+ * Writes `.cue.profile` in CWD (or $HOME with --global).
5
5
  *
6
6
  * Composite selectors are accepted: `cue use postizz+trendradar` validates
7
7
  * each part separately and pins the full `a+b` string verbatim.
@@ -11,7 +11,7 @@
11
11
  * prompt with `--no-prompt` (or in non-TTY environments — auto-skipped).
12
12
  */
13
13
 
14
- import { writeFileSync } from "node:fs";
14
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
15
15
  import { join } from "node:path";
16
16
  import { homedir } from "node:os";
17
17
  import { createInterface } from "node:readline/promises";
@@ -19,9 +19,44 @@ import { stdin, stdout } from "node:process";
19
19
 
20
20
  import { isCompositeSelector, listProfiles, loadProfile, parseProfileSelector } from "../lib/profile-loader";
21
21
 
22
+ /** Ensure `entry` appears in the .gitignore at `dir` (idempotent). */
23
+ function ensureGitignoreEntry(dir: string, entry: string): void {
24
+ const path = join(dir, ".gitignore");
25
+ const existing = existsSync(path) ? readFileSync(path, "utf8") : "";
26
+ const lines = existing.split("\n").map(l => l.trim());
27
+ if (lines.includes(entry)) return;
28
+ const suffix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
29
+ writeFileSync(path, existing + suffix + entry + "\n");
30
+ }
31
+
32
+ /** Write profiles/<name>.json for each profile part into dir (best-effort). */
33
+ async function writeProfilesDir(parts: string[], dir: string): Promise<void> {
34
+ try {
35
+ mkdirSync(join(dir, "profiles"), { recursive: true });
36
+ await Promise.all(parts.map(async (name) => {
37
+ try {
38
+ const p = await loadProfile(name);
39
+ const snapshot = {
40
+ name: p.name,
41
+ description: p.description,
42
+ ...(p.icon ? { icon: p.icon } : {}),
43
+ ...(p.inheritanceChain.length > 1 ? { inherits: p.inheritanceChain.slice(0, -1) } : {}),
44
+ ...(p.recommends.length > 0 ? { recommends: p.recommends } : {}),
45
+ };
46
+ writeFileSync(join(dir, "profiles", `${name}.json`), JSON.stringify(snapshot, null, 2) + "\n");
47
+ } catch {
48
+ // best-effort per profile — missing profile metadata is non-fatal
49
+ }
50
+ }));
51
+ } catch {
52
+ // never fail the pin because of profiles/ write errors
53
+ }
54
+ }
55
+
22
56
  export async function run(args: string[]): Promise<number> {
23
57
  const global = args.includes("--global") || args.includes("-g");
24
58
  const noPrompt = args.includes("--no-prompt");
59
+ const noProfilesDir = args.includes("--no-profiles-dir");
25
60
  const selector = args.find(a => !a.startsWith("-"));
26
61
 
27
62
  if (!selector) {
@@ -49,7 +84,7 @@ export async function run(args: string[]): Promise<number> {
49
84
  }
50
85
 
51
86
  const writePin = (value: string) => {
52
- const target = global ? join(homedir(), ".cue-profile") : join(process.cwd(), ".cue-profile");
87
+ const target = global ? join(homedir(), ".cue.profile") : join(process.cwd(), ".cue.profile");
53
88
  writeFileSync(target, value + "\n");
54
89
  };
55
90
 
@@ -57,6 +92,15 @@ export async function run(args: string[]): Promise<number> {
57
92
  const scope = global ? "globally" : `in ${process.cwd()}`;
58
93
  process.stdout.write(`✅ Now using "${selector}" ${scope}\n`);
59
94
 
95
+ // Write profiles/<name>.json manifest + gitignore both cue artifacts (project-local only).
96
+ if (!global && !noProfilesDir) {
97
+ await writeProfilesDir(parts, process.cwd());
98
+ const created = parts.map(p => `profiles/${p}.json`).join(", ");
99
+ process.stdout.write(`📁 Profile manifest written: ${created}\n`);
100
+ ensureGitignoreEntry(process.cwd(), ".cue.profile");
101
+ ensureGitignoreEntry(process.cwd(), "profiles/");
102
+ }
103
+
60
104
  // Recommendation surfacing — only on plain (non-composite) selections.
61
105
  if (!isCompositeSelector(selector)) {
62
106
  try {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * `cue watch-live [--profile <name>]` — file watcher for auto-rematerialization.
3
3
  *
4
- * Monitors profile.yaml, referenced SKILL.md files, and .cue-profile in cwd.
4
+ * Monitors profile.yaml, referenced SKILL.md files, and .cue.profile in cwd.
5
5
  * On change: re-runs materialization with 500ms debounce.
6
6
  */
7
7
 
@@ -25,7 +25,7 @@ Usage: cue watch-live [--profile <name>]
25
25
  Monitors:
26
26
  • Active profile's profile.yaml
27
27
  • All SKILL.md files referenced by the profile
28
- • .cue-profile in cwd
28
+ • .cue.profile in cwd
29
29
 
30
30
  On any change, re-runs materialization (500ms debounce).
31
31
  Press Ctrl+C to stop.
@@ -43,7 +43,7 @@ Press Ctrl+C to stop.
43
43
  }
44
44
 
45
45
  if (!profileName) {
46
- process.stderr.write("No active profile. Use --profile <name> or set .cue-profile.\n");
46
+ process.stderr.write("No active profile. Use --profile <name> or set .cue.profile.\n");
47
47
  return 1;
48
48
  }
49
49
 
@@ -68,8 +68,8 @@ Press Ctrl+C to stop.
68
68
  if (existsSync(skillMd)) watchPaths.push(skillMd);
69
69
  }
70
70
 
71
- // 3. .cue-profile in cwd
72
- const cueProfile = join(process.cwd(), ".cue-profile");
71
+ // 3. .cue.profile in cwd
72
+ const cueProfile = join(process.cwd(), ".cue.profile");
73
73
  if (existsSync(cueProfile)) watchPaths.push(cueProfile);
74
74
 
75
75
  if (watchPaths.length === 0) {
@@ -5,7 +5,7 @@
5
5
  * eval "$(cue watch bash)" # add to ~/.bashrc
6
6
  * eval "$(cue watch zsh)" # add to ~/.zshrc
7
7
  *
8
- * The hook runs after every `cd` and checks if the .cue-profile changed.
8
+ * The hook runs after every `cd` and checks if the .cue.profile changed.
9
9
  * If it did, it shows a notification with the new profile.
10
10
  */
11
11
 
@@ -18,10 +18,10 @@ __cue_watch() {
18
18
  local profile_file=""
19
19
  local dir="$PWD"
20
20
 
21
- # Walk up to find .cue-profile
21
+ # Walk up to find .cue.profile
22
22
  while [[ "$dir" != "/" && "$dir" != "$HOME" ]]; do
23
- if [[ -f "$dir/.cue-profile" ]]; then
24
- profile_file="$dir/.cue-profile"
23
+ if [[ -f "$dir/.cue.profile" ]]; then
24
+ profile_file="$dir/.cue.profile"
25
25
  break
26
26
  fi
27
27
  dir="$(dirname "$dir")"
@@ -71,10 +71,10 @@ function zshHook(): string {
71
71
  ' local profile_file=""',
72
72
  ' local dir="$PWD"',
73
73
  "",
74
- " # Walk up to find .cue-profile",
74
+ " # Walk up to find .cue.profile",
75
75
  ' while [[ "$dir" != "/" && "$dir" != "$HOME" ]]; do',
76
- ' if [[ -f "$dir/.cue-profile" ]]; then',
77
- ' profile_file="$dir/.cue-profile"',
76
+ ' if [[ -f "$dir/.cue.profile" ]]; then',
77
+ ' profile_file="$dir/.cue.profile"',
78
78
  " break",
79
79
  " fi",
80
80
  ' dir="${dir:h}"',
@@ -125,7 +125,7 @@ export async function run(args: string[]): Promise<number> {
125
125
  "Usage:\n" +
126
126
  ' eval "$(cue watch bash)" # add to ~/.bashrc\n' +
127
127
  ' eval "$(cue watch zsh)" # add to ~/.zshrc\n\n' +
128
- "When you cd into a directory with a .cue-profile, it shows:\n" +
128
+ "When you cd into a directory with a .cue.profile, it shows:\n" +
129
129
  " ⚡ cue: profile switched frontend → backend\n"
130
130
  );
131
131
  } else {
package/src/index.ts CHANGED
@@ -69,7 +69,9 @@ function printHelp(): void {
69
69
  ["mem", "Inspect/manage per-profile claude-mem stores"],
70
70
  ],
71
71
  "Launch & Shell": [
72
+ ["install", "Prepare profile runtimes and optionally install required CLIs"],
72
73
  ["launch", "Resolve + materialize + exec claude/codex"],
74
+ ["summon", "Bind a profile into the live session (soft-load + pin), no restart"],
73
75
  ["shell", "Install/uninstall shims (~/.local/bin)"],
74
76
  ["update", "Self-update: git pull + bun install"],
75
77
  ["upgrade", "Pull new skills from the registry"],
@@ -94,10 +94,10 @@ describe("profileFromConfigDir", () => {
94
94
  });
95
95
 
96
96
  describe("profileFromCwdPin", () => {
97
- test("returns the first line of .cue-profile when present", () => {
97
+ test("returns the first line of .cue.profile when present", () => {
98
98
  const dir = mkdtempSync(join(tmpdir(), "cue-pin-test-"));
99
99
  try {
100
- writeFileSync(join(dir, ".cue-profile"), "skill-writer+ecc\n# comment line\n");
100
+ writeFileSync(join(dir, ".cue.profile"), "skill-writer+ecc\n# comment line\n");
101
101
  expect(profileFromCwdPin(dir)).toBe("skill-writer+ecc");
102
102
  } finally {
103
103
  rmSync(dir, { recursive: true, force: true });
@@ -108,7 +108,7 @@ describe("profileFromCwdPin", () => {
108
108
  const dir = mkdtempSync(join(tmpdir(), "cue-pin-test-"));
109
109
  try {
110
110
  expect(profileFromCwdPin(dir)).toBeNull();
111
- writeFileSync(join(dir, ".cue-profile"), "");
111
+ writeFileSync(join(dir, ".cue.profile"), "");
112
112
  expect(profileFromCwdPin(dir)).toBeNull();
113
113
  } finally {
114
114
  rmSync(dir, { recursive: true, force: true });
@@ -10,7 +10,7 @@
10
10
  * b. CLAUDE_CONFIG_DIR matches `<runtimeRoot>/<profile>/claude`
11
11
  * — works for plain `claude` wrappers like claude-account2 that
12
12
  * bypass `cue launch` but still point at a cue runtime
13
- * c. `.cue-profile` file in the process's cwd
13
+ * c. `.cue.profile` file in the process's cwd
14
14
  * d. "(unpinned)" — agent is running but isn't using a cue profile
15
15
  *
16
16
  * Linux-only. macOS exposes env via `ps eww` but with different escaping;
@@ -115,13 +115,13 @@ export function profileFromConfigDir(configDir: string | undefined): string | nu
115
115
  }
116
116
 
117
117
  /**
118
- * Read `.cue-profile` from a cwd (process-cwd fallback). Trimmed first line.
118
+ * Read `.cue.profile` from a cwd (process-cwd fallback). Trimmed first line.
119
119
  * Exported for tests.
120
120
  */
121
121
  export function profileFromCwdPin(cwd: string | null): string | null {
122
122
  if (!cwd) return null;
123
123
  try {
124
- const raw = readFileSync(join(cwd, ".cue-profile"), "utf8");
124
+ const raw = readFileSync(join(cwd, ".cue.profile"), "utf8");
125
125
  const first = raw.split("\n")[0]?.trim();
126
126
  return first && first.length > 0 ? first : null;
127
127
  } catch {
@@ -192,7 +192,7 @@ export function listActiveSessions(): ActiveSession[] {
192
192
 
193
193
  // Profile resolution. CUE_PROFILE wins; otherwise sniff CLAUDE_CONFIG_DIR
194
194
  // (covers wrappers like `claude-account2` that bypass `cue launch` but
195
- // still point at a cue runtime); otherwise read .cue-profile from cwd.
195
+ // still point at a cue runtime); otherwise read .cue.profile from cwd.
196
196
  const env = readEnviron(pid);
197
197
  let profile: string | null = null;
198
198
  let profileSource: ActiveSession["profileSource"] = "unpinned";