cue-ai 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/README.md +757 -110
  2. package/package.json +5 -5
  3. package/profiles/README.md +12 -12
  4. package/profiles/SCHEMA.md +31 -3
  5. package/profiles/_cache/README.md +1 -1
  6. package/profiles/_types.ts +26 -1
  7. package/profiles/backend/profile.yaml +1 -0
  8. package/profiles/career/profile.yaml +13 -0
  9. package/profiles/core/profile.yaml +76 -9
  10. package/profiles/creative-media/README.md +1 -1
  11. package/profiles/cybersecurity/profile.yaml +779 -756
  12. package/profiles/ecc/profile.yaml +39 -0
  13. package/profiles/event-design/profile.yaml +10 -0
  14. package/profiles/fleet-control/README.md +1 -1
  15. package/profiles/frontend/profile.yaml +14 -0
  16. package/profiles/full/README.md +1 -1
  17. package/profiles/go-api/profile.yaml +1 -0
  18. package/profiles/marketing/profile.yaml +12 -1
  19. package/profiles/predict-everything/profile.yaml +9 -0
  20. package/profiles/rust/profile.yaml +22 -3
  21. package/profiles/rust-cli/profile.yaml +14 -0
  22. package/profiles/rust-core/profile.yaml +35 -0
  23. package/profiles/rust-embedded/profile.yaml +11 -0
  24. package/profiles/rust-ffi/profile.yaml +13 -0
  25. package/profiles/rust-game/profile.yaml +11 -0
  26. package/profiles/rust-wasm/profile.yaml +11 -0
  27. package/profiles/rust-web/profile.yaml +17 -0
  28. package/profiles/schema.json +44 -4
  29. package/profiles/trendradar/profile.yaml +11 -0
  30. package/resources/mcps/README.md +39 -164
  31. package/resources/mcps/configs/claude.sanitized.json +55 -0
  32. package/resources/mcps/configs/claude_runtime.sanitized.json +47 -0
  33. package/resources/skills/README.md +70 -113
  34. package/resources/skills/skills/event-design/wedding-invitations/SKILL.md +43 -0
  35. package/resources/skills/skills/meta/acpx/SKILL.md +78 -0
  36. package/resources/skills/skills/meta/cue-usage/SKILL.md +24 -0
  37. package/resources/skills/skills/meta/profile-fit-monitor/SKILL.md +24 -0
  38. package/resources/skills/skills/predict-everything/mirofish/SKILL.md +75 -0
  39. package/resources/skills/skills/research/trendradar/SKILL.md +88 -0
  40. package/resources/skills/skills/rust/async-tokio/SKILL.md +27 -0
  41. package/resources/skills/skills/rust/axum-api/SKILL.md +38 -0
  42. package/resources/skills/skills/rust/bacon-watch/SKILL.md +24 -0
  43. package/resources/skills/skills/rust/bevy/SKILL.md +43 -0
  44. package/resources/skills/skills/rust/bindgen/SKILL.md +39 -0
  45. package/resources/skills/skills/rust/cargo-audit/SKILL.md +26 -0
  46. package/resources/skills/skills/rust/cargo-basics/SKILL.md +28 -0
  47. package/resources/skills/skills/rust/cargo-chef/SKILL.md +43 -0
  48. package/resources/skills/skills/rust/cargo-edit/SKILL.md +26 -0
  49. package/resources/skills/skills/rust/cargo-expand/SKILL.md +24 -0
  50. package/resources/skills/skills/rust/cargo-flamegraph/SKILL.md +26 -0
  51. package/resources/skills/skills/rust/cargo-fuzz/SKILL.md +34 -0
  52. package/resources/skills/skills/rust/cargo-hack/SKILL.md +26 -0
  53. package/resources/skills/skills/rust/cargo-msrv/SKILL.md +30 -0
  54. package/resources/skills/skills/rust/cargo-mutants/SKILL.md +26 -0
  55. package/resources/skills/skills/rust/cargo-nextest/SKILL.md +24 -0
  56. package/resources/skills/skills/rust/cargo-readme/SKILL.md +36 -0
  57. package/resources/skills/skills/rust/cbindgen/SKILL.md +41 -0
  58. package/resources/skills/skills/rust/chisel-tool/SKILL.md +32 -0
  59. package/resources/skills/skills/rust/clap-cli/SKILL.md +44 -0
  60. package/resources/skills/skills/rust/clippy-and-fmt/SKILL.md +25 -0
  61. package/resources/skills/skills/rust/cross-compile/SKILL.md +26 -0
  62. package/resources/skills/skills/rust/embedded/SKILL.md +33 -0
  63. package/resources/skills/skills/rust/error-handling/SKILL.md +32 -0
  64. package/resources/skills/skills/rust/just-runner/SKILL.md +26 -0
  65. package/resources/skills/skills/rust/mdbook/SKILL.md +25 -0
  66. package/resources/skills/skills/rust/napi-rs/SKILL.md +32 -0
  67. package/resources/skills/skills/rust/no-std/SKILL.md +42 -0
  68. package/resources/skills/skills/rust/property-testing/SKILL.md +35 -0
  69. package/resources/skills/skills/rust/pyo3/SKILL.md +40 -0
  70. package/resources/skills/skills/rust/ratatui-tui/SKILL.md +36 -0
  71. package/resources/skills/skills/rust/release-plz/SKILL.md +27 -0
  72. package/resources/skills/skills/rust/reqwest/SKILL.md +37 -0
  73. package/resources/skills/skills/rust/sccache/SKILL.md +28 -0
  74. package/resources/skills/skills/rust/serde/SKILL.md +30 -0
  75. package/resources/skills/skills/rust/snapshot-testing/SKILL.md +30 -0
  76. package/resources/skills/skills/rust/sqlx-cli/SKILL.md +33 -0
  77. package/resources/skills/skills/rust/tracing/SKILL.md +36 -0
  78. package/resources/skills/skills/rust/typos-spellcheck/SKILL.md +31 -0
  79. package/resources/skills/skills/rust/uniffi/SKILL.md +38 -0
  80. package/resources/skills/skills/rust/wasm-rust/SKILL.md +27 -0
  81. package/resources/skills/skills/security/agentshield/SKILL.md +119 -0
  82. package/src/commands/_index.ts +47 -3
  83. package/src/commands/cli.test.ts +192 -0
  84. package/src/commands/cli.ts +303 -0
  85. package/src/commands/current.ts +1 -1
  86. package/src/commands/debug.test.ts +62 -0
  87. package/src/commands/debug.ts +212 -0
  88. package/src/commands/discover.scoring.test.ts +216 -0
  89. package/src/commands/discover.test.ts +145 -0
  90. package/src/commands/discover.ts +2618 -0
  91. package/src/commands/eval-behavior.test.ts +56 -0
  92. package/src/commands/eval-behavior.ts +189 -0
  93. package/src/commands/eval.test.ts +102 -0
  94. package/src/commands/eval.ts +348 -0
  95. package/src/commands/evolve.ts +291 -0
  96. package/src/commands/failures.test.ts +78 -0
  97. package/src/commands/failures.ts +393 -0
  98. package/src/commands/feedback.ts +219 -0
  99. package/src/commands/init.ts +26 -0
  100. package/src/commands/launch.e2e.test.ts +9 -1
  101. package/src/commands/launch.ts +174 -11
  102. package/src/commands/lint-skill.ts +157 -0
  103. package/src/commands/marketplace.ts +763 -2
  104. package/src/commands/new.ts +1 -1
  105. package/src/commands/optimizer.ts +92 -28
  106. package/src/commands/profile-draft-skill.test.ts +96 -0
  107. package/src/commands/profile-draft-skill.ts +287 -0
  108. package/src/commands/profile-evolve.test.ts +126 -0
  109. package/src/commands/profile-evolve.ts +0 -0
  110. package/src/commands/profile-suggest.ts +223 -0
  111. package/src/commands/profile.ts +41 -0
  112. package/src/commands/quick.ts +2 -17
  113. package/src/commands/scan.ts +2 -2
  114. package/src/commands/score.ts +1 -1
  115. package/src/commands/share.ts +1 -1
  116. package/src/commands/sources.ts +2 -2
  117. package/src/commands/submit-profile.ts +262 -0
  118. package/src/commands/upgrade.ts +1 -1
  119. package/src/commands/use.ts +35 -5
  120. package/src/commands/validate.ts +1 -1
  121. package/src/index.ts +66 -0
  122. package/src/lib/analytics.ts +48 -2
  123. package/src/lib/claude-binary.ts +39 -0
  124. package/src/lib/cli-extractor.ts +77 -0
  125. package/src/lib/cluster-skills.test.ts +268 -0
  126. package/src/lib/cluster-skills.ts +290 -0
  127. package/src/lib/credentials-sync.test.ts +208 -0
  128. package/src/lib/credentials-sync.ts +205 -0
  129. package/src/lib/mcp-materializer.test.ts +1 -1
  130. package/src/lib/persona-playbooks.test.ts +111 -0
  131. package/src/lib/pr-poster.test.ts +243 -0
  132. package/src/lib/pr-poster.ts +285 -0
  133. package/src/lib/pr-throttle.test.ts +148 -0
  134. package/src/lib/pr-throttle.ts +209 -0
  135. package/src/lib/profile-generator.test.ts +1 -1
  136. package/src/lib/profile-generator.ts +2 -2
  137. package/src/lib/profile-linter.test.ts +6 -3
  138. package/src/lib/profile-linter.ts +71 -8
  139. package/src/lib/profile-loader.test.ts +1 -1
  140. package/src/lib/profile-loader.ts +16 -0
  141. package/src/lib/resolver-local.test.ts +1 -1
  142. package/src/lib/resolver-npx.test.ts +76 -1
  143. package/src/lib/resolver-npx.ts +35 -3
  144. package/src/lib/resolver-plugins.test.ts +1 -1
  145. package/src/lib/runtime-materializer.test.ts +191 -7
  146. package/src/lib/runtime-materializer.ts +310 -42
  147. package/src/lib/scan-plugins.test.ts +1 -1
  148. package/src/lib/skill-linter.test.ts +174 -0
  149. package/src/lib/skill-linter.ts +507 -0
  150. package/src/lib/skill-subset.test.ts +95 -0
  151. package/src/lib/skill-subset.ts +166 -0
  152. package/src/lib/star-prompt.ts +1 -1
  153. package/src/lib/uvx-installer.test.ts +229 -0
  154. package/src/lib/uvx-installer.ts +278 -0
@@ -187,7 +187,7 @@ describe("materializeRuntime", () => {
187
187
  expect(st.isFile()).toBe(true);
188
188
  });
189
189
 
190
- test("credentialsSource: merges existing settings.json (preserves permissions)", async () => {
190
+ test("credentialsSource: preserves account-level settings but isolates MCPs + plugins per profile", async () => {
191
191
  const credSrc = join(root, "creds");
192
192
  const { mkdir, writeFile } = await import("node:fs/promises");
193
193
  await mkdir(credSrc, { recursive: true });
@@ -197,8 +197,14 @@ describe("materializeRuntime", () => {
197
197
  permissions: { allow: ["Bash(*)"], defaultMode: "auto" },
198
198
  trustedDirectories: ["/home/user/work"],
199
199
  skipAutoPermissionPrompt: true,
200
- enabledPlugins: { "existing@marketplace": true },
201
- mcpServers: { existingMcp: { command: "x" } },
200
+ // These two MUST NOT leak into the profile runtime — the profile is
201
+ // the sole source of truth for MCPs + plugins. Otherwise every MCP
202
+ // the user has registered globally appears in EVERY profile, defeating
203
+ // isolation. Pinned by this test (regression: profiles like
204
+ // `cybersecurity` with `mcps: []` were inheriting random user-scoped
205
+ // MCPs like `teherguminet-admin` because of the merge).
206
+ enabledPlugins: { "user-globally-installed@marketplace": true },
207
+ mcpServers: { userGloballyInstalledMcp: { command: "x" } },
202
208
  }),
203
209
  );
204
210
 
@@ -213,17 +219,16 @@ describe("materializeRuntime", () => {
213
219
  });
214
220
 
215
221
  const settings = JSON.parse(await readFile(join(out.runtimeDir, "settings.json"), "utf8"));
216
- // Account-level settings preserved
222
+ // Account-level settings preserved (these are user-scoped, not profile-scoped)
217
223
  expect(settings.permissions).toEqual({ allow: ["Bash(*)"], defaultMode: "auto" });
218
224
  expect(settings.trustedDirectories).toEqual(["/home/user/work"]);
219
225
  expect(settings.skipAutoPermissionPrompt).toBe(true);
220
- // Profile plugins/mcps merged on top
226
+ // Profile plugins/mcps are EXCLUSIVE — only what the profile declared.
227
+ // The account-level entries from the source settings.json must NOT leak.
221
228
  expect(settings.enabledPlugins).toEqual({
222
- "existing@marketplace": true,
223
229
  "frontend-design@claude-plugins-official": true,
224
230
  });
225
231
  expect(settings.mcpServers).toEqual({
226
- existingMcp: { command: "x" },
227
232
  "claude-mem": { command: "claude-mem" },
228
233
  });
229
234
  });
@@ -286,4 +291,183 @@ describe("materializeRuntime", () => {
286
291
  expect(claudemd).not.toContain("$(date)");
287
292
  expect(claudemd).toMatch(/generated \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/);
288
293
  });
294
+
295
+ // ---------------------------------------------------------------------------
296
+ // Rules / commands / hooks — ECC-derived resource paths
297
+ // ---------------------------------------------------------------------------
298
+
299
+ test("commands: symlinks each ref into commands/ and lists them in CLAUDE.md", async () => {
300
+ // Materializer resolves command refs against <repo>/resources/commands/<ref>.md
301
+ // — we already vendor a few of these, so use a known-good one.
302
+ const profile: ResolvedProfile = {
303
+ ...sampleProfile,
304
+ name: "test-cmds",
305
+ inheritanceChain: ["test-cmds"],
306
+ rules: [], hooks: [],
307
+ commands: ["code-review", "checkpoint"],
308
+ };
309
+ const out = await materializeRuntime({
310
+ profile, agent: "claude-code",
311
+ runtimeRoot: join(root, "runtime"),
312
+ skillSourceLookup: async (id) => `/fake/source/${id}`,
313
+ mcpRegistry: {},
314
+ userClaudeMd: "",
315
+ });
316
+ const cmdLink = await readlink(join(out.runtimeDir, "commands", "code-review.md"));
317
+ expect(cmdLink).toContain("resources/commands/code-review.md");
318
+ const claudemd = await readFile(join(out.runtimeDir, "CLAUDE.md"), "utf8");
319
+ expect(claudemd).toContain("## Available Commands");
320
+ expect(claudemd).toContain("/code-review");
321
+ expect(claudemd).toContain("/checkpoint");
322
+ });
323
+
324
+ test("rules: symlinks into rules/ + writes index (NOT inlined bodies)", async () => {
325
+ const profile: ResolvedProfile = {
326
+ ...sampleProfile,
327
+ name: "test-rules",
328
+ inheritanceChain: ["test-rules"],
329
+ commands: [], hooks: [],
330
+ rules: ["common/security", "common/testing"],
331
+ };
332
+ const out = await materializeRuntime({
333
+ profile, agent: "claude-code",
334
+ runtimeRoot: join(root, "runtime"),
335
+ skillSourceLookup: async (id) => `/fake/source/${id}`,
336
+ mcpRegistry: {},
337
+ userClaudeMd: "",
338
+ });
339
+ const link = await readlink(join(out.runtimeDir, "rules", "security.md"));
340
+ expect(link).toContain("resources/rules/common/security.md");
341
+ const claudemd = await readFile(join(out.runtimeDir, "CLAUDE.md"), "utf8");
342
+ // Index reference present, but the rule body must NOT be inlined — the
343
+ // whole point of the symlink-only approach is to skip the token bleed.
344
+ expect(claudemd).toContain("## Rules (2)");
345
+ expect(claudemd).toContain("`rules/security.md`");
346
+ expect(claudemd).not.toMatch(/^## Security Review Triggers/m);
347
+ });
348
+
349
+ test("hooks: merges hook JSON into settings.json under matching event keys", async () => {
350
+ const profile: ResolvedProfile = {
351
+ ...sampleProfile,
352
+ name: "test-hooks",
353
+ inheritanceChain: ["test-hooks"],
354
+ rules: [], commands: [],
355
+ hooks: ["bash-quality-preflight.json", "session-summary.json"],
356
+ };
357
+ const out = await materializeRuntime({
358
+ profile, agent: "claude-code",
359
+ runtimeRoot: join(root, "runtime"),
360
+ skillSourceLookup: async (id) => `/fake/source/${id}`,
361
+ mcpRegistry: {},
362
+ userClaudeMd: "",
363
+ });
364
+ const settings = JSON.parse(await readFile(join(out.runtimeDir, "settings.json"), "utf8"));
365
+ expect(settings.hooks.PreToolUse).toBeArray();
366
+ expect(settings.hooks.PreToolUse[0].matcher).toBe("Bash");
367
+ expect(settings.hooks.Stop).toBeArray();
368
+ expect(settings.hooks.Stop[0].hooks[0].id).toBe("cue:stop:session-summary");
369
+ // Symlinks also created under hooks/
370
+ const link = await readlink(join(out.runtimeDir, "hooks", "bash-quality-preflight.json"));
371
+ expect(link).toContain("resources/hooks/bash-quality-preflight.json");
372
+ });
373
+
374
+ // Claude Code reads MCP servers from .claude.json (top-level `mcpServers`),
375
+ // NOT from settings.json. The materializer must therefore merge profile MCPs
376
+ // into .claude.json — and copy (not symlink) it so mutations don't leak back
377
+ // into the shared account file.
378
+ test("merges profile MCPs into .claude.json + copies (not symlinks) it", async () => {
379
+ const credSrc = join(root, "creds");
380
+ const { mkdir, writeFile, lstat } = await import("node:fs/promises");
381
+ await mkdir(credSrc, { recursive: true });
382
+ await writeFile(
383
+ join(credSrc, ".claude.json"),
384
+ JSON.stringify({
385
+ numStartups: 42,
386
+ oauthAccount: { emailAddress: "u@example.com" },
387
+ mcpServers: { "preexisting": { command: "/bin/pre" } },
388
+ }),
389
+ );
390
+
391
+ const out = await materializeRuntime({
392
+ profile: sampleProfile,
393
+ agent: "claude-code",
394
+ runtimeRoot: join(root, "runtime"),
395
+ skillSourceLookup: async (id) => `/fake/source/${id}`,
396
+ mcpRegistry: { "claude-mem": { command: "claude-mem", args: [] } },
397
+ userClaudeMd: "",
398
+ credentialsSource: credSrc,
399
+ });
400
+
401
+ // Must be a real file, not a symlink — otherwise mutations leak back to
402
+ // the source account file and pollute other profiles sharing the account.
403
+ const st = await lstat(join(out.runtimeDir, ".claude.json"));
404
+ expect(st.isSymbolicLink()).toBe(false);
405
+ expect(st.isFile()).toBe(true);
406
+
407
+ // Profile MCPs merged in under top-level `mcpServers`, preserving the
408
+ // source's preexisting entries and other top-level fields.
409
+ const cj = JSON.parse(await readFile(join(out.runtimeDir, ".claude.json"), "utf8"));
410
+ expect(cj.mcpServers["claude-mem"]).toEqual({ command: "claude-mem", args: [] });
411
+ expect(cj.mcpServers["preexisting"]).toEqual({ command: "/bin/pre" });
412
+ expect(cj.numStartups).toBe(42);
413
+ expect(cj.oauthAccount).toEqual({ emailAddress: "u@example.com" });
414
+
415
+ // Source .claude.json untouched — proof the copy isolates per-profile writes.
416
+ const src = JSON.parse(await readFile(join(credSrc, ".claude.json"), "utf8"));
417
+ expect(src.mcpServers).toEqual({ "preexisting": { command: "/bin/pre" } });
418
+ });
419
+
420
+ // Cache-hit path must also re-sync MCPs into .claude.json, so adding/removing
421
+ // an MCP to a profile takes effect even when the profile hash hasn't changed
422
+ // for unrelated reasons. (In practice, adding an MCP changes the hash — but
423
+ // an account swap with a different source .claude.json triggers a cache hit.)
424
+ test("cache hit: refreshes .claude.json mcpServers from current registry", async () => {
425
+ const credSrc = join(root, "creds");
426
+ const { mkdir, writeFile } = await import("node:fs/promises");
427
+ await mkdir(credSrc, { recursive: true });
428
+ await writeFile(join(credSrc, ".claude.json"), JSON.stringify({ numStartups: 1 }));
429
+
430
+ const args = {
431
+ profile: sampleProfile,
432
+ agent: "claude-code" as const,
433
+ runtimeRoot: join(root, "runtime"),
434
+ skillSourceLookup: async (id: string) => `/fake/source/${id}`,
435
+ mcpRegistry: { "claude-mem": { command: "claude-mem-v1" } },
436
+ userClaudeMd: "",
437
+ credentialsSource: credSrc,
438
+ };
439
+ await materializeRuntime(args);
440
+
441
+ // Second build: same profile (hash hit) but registry changed.
442
+ const second = await materializeRuntime({
443
+ ...args,
444
+ mcpRegistry: { "claude-mem": { command: "claude-mem-v2" } },
445
+ });
446
+ expect(second.rebuilt).toBe(false);
447
+
448
+ const cj = JSON.parse(await readFile(join(second.runtimeDir, ".claude.json"), "utf8"));
449
+ expect(cj.mcpServers["claude-mem"]).toEqual({ command: "claude-mem-v2" });
450
+ });
451
+
452
+ test("missing rule/command/hook ref is non-fatal", async () => {
453
+ const profile: ResolvedProfile = {
454
+ ...sampleProfile,
455
+ name: "test-missing",
456
+ inheritanceChain: ["test-missing"],
457
+ rules: ["does/not/exist"],
458
+ commands: ["ghost-command"],
459
+ hooks: ["nope.json"],
460
+ };
461
+ const out = await materializeRuntime({
462
+ profile, agent: "claude-code",
463
+ runtimeRoot: join(root, "runtime"),
464
+ skillSourceLookup: async (id) => `/fake/source/${id}`,
465
+ mcpRegistry: {},
466
+ userClaudeMd: "",
467
+ });
468
+ expect(out.rebuilt).toBe(true);
469
+ // No symlinks created for missing refs — directories may exist but be empty.
470
+ const settings = JSON.parse(await readFile(join(out.runtimeDir, "settings.json"), "utf8"));
471
+ expect(settings.hooks).toBeUndefined();
472
+ });
289
473
  });