@caik.dev/cli 0.1.6 → 0.1.7

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 (2) hide show
  1. package/dist/index.js +291 -73
  2. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -132,6 +132,12 @@ var CaikApiClient = class {
132
132
  import { readFileSync, writeFileSync, mkdirSync, chmodSync, existsSync } from "fs";
133
133
  import { join } from "path";
134
134
  import { homedir } from "os";
135
+ var CONTRIBUTION_LEVELS = [
136
+ { value: "none", name: "None", description: "Nothing sent. Directory access only." },
137
+ { value: "minimal", name: "Minimal", description: "Install/uninstall events only. Basic recommendations." },
138
+ { value: "contributor", name: "Contributor", description: "Error/success signals + co-installs. Full recommendations. (default)" },
139
+ { value: "collective", name: "Collective", description: "Workflow patterns + stack signals. Proactive recommendations." }
140
+ ];
135
141
  var DEFAULT_CONFIG = {
136
142
  apiUrl: "https://www.caik.dev",
137
143
  defaultLimit: 10,
@@ -303,7 +309,7 @@ import { existsSync as existsSync7, unlinkSync as unlinkSync2 } from "fs";
303
309
  import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync6 } from "fs";
304
310
  import { dirname as dirname3, resolve } from "path";
305
311
  import { execSync as execSync3 } from "child_process";
306
- import { createInterface } from "readline/promises";
312
+ import { select, confirm } from "@inquirer/prompts";
307
313
 
308
314
  // src/platform/detect.ts
309
315
  import { existsSync as existsSync2 } from "fs";
@@ -1404,20 +1410,13 @@ Examples:
1404
1410
  } else {
1405
1411
  const detected = detectPlatforms();
1406
1412
  if (detected.length > 1 && !opts.yes && !globalOpts.json) {
1407
- console.log(info("Multiple platforms detected:"));
1408
- for (let i = 0; i < detected.length; i++) {
1409
- const d = detected[i];
1410
- console.log(info(` ${i + 1}) ${d.name} (${d.tier})`));
1411
- }
1412
- const rl = createInterface({ input: process.stdin, output: process.stdout });
1413
- const answer = await rl.question(`Select platform [1-${detected.length}]: `);
1414
- rl.close();
1415
- const idx = parseInt(answer, 10);
1416
- if (isNaN(idx) || idx < 1 || idx > detected.length) {
1417
- console.log(error("Invalid selection. Aborting."));
1418
- return;
1419
- }
1420
- detectedPlatform = detected[idx - 1].name;
1413
+ detectedPlatform = await select({
1414
+ message: "Multiple platforms detected \u2014 install for which?",
1415
+ choices: detected.map((d) => ({
1416
+ name: `${d.name} (${d.tier})`,
1417
+ value: d.name
1418
+ }))
1419
+ });
1421
1420
  } else if (detected.length > 0) {
1422
1421
  detectedPlatform = detected[0].name;
1423
1422
  }
@@ -1463,10 +1462,11 @@ Components (${ordered.length}):`));
1463
1462
  }
1464
1463
  console.log();
1465
1464
  if (!opts.yes && !globalOpts.json) {
1466
- const rl = createInterface({ input: process.stdin, output: process.stdout });
1467
- const answer = await rl.question(`Install all ${ordered.length} components? (y/N) `);
1468
- rl.close();
1469
- if (answer.toLowerCase() !== "y") {
1465
+ const proceed = await confirm({
1466
+ message: `Install all ${ordered.length} components?`,
1467
+ default: true
1468
+ });
1469
+ if (!proceed) {
1470
1470
  console.log(info("Stack installation cancelled."));
1471
1471
  return;
1472
1472
  }
@@ -1593,10 +1593,8 @@ Components (${ordered.length}):`));
1593
1593
  console.log(info(`Post-install hook: ${installInfo.postInstallHook}`));
1594
1594
  }
1595
1595
  console.log();
1596
- const rl = createInterface({ input: process.stdin, output: process.stdout });
1597
- const answer = await rl.question("Continue? (y/N) ");
1598
- rl.close();
1599
- if (answer.toLowerCase() !== "y") {
1596
+ const proceed = await confirm({ message: "Continue?", default: true });
1597
+ if (!proceed) {
1600
1598
  console.log(info("Installation cancelled."));
1601
1599
  return;
1602
1600
  }
@@ -1710,7 +1708,8 @@ Components (${ordered.length}):`));
1710
1708
  }
1711
1709
 
1712
1710
  // src/commands/init.ts
1713
- import { createInterface as createInterface2 } from "readline/promises";
1711
+ import { select as select2 } from "@inquirer/prompts";
1712
+ import { createInterface } from "readline/promises";
1714
1713
  import { stdin, stdout } from "process";
1715
1714
 
1716
1715
  // src/auth.ts
@@ -1732,12 +1731,135 @@ function openBrowser(url) {
1732
1731
  }
1733
1732
  });
1734
1733
  }
1734
+ function authPage(title, message, isSuccess) {
1735
+ const iconColor = isSuccess ? "#059669" : "#DC2626";
1736
+ const iconBg = isSuccess ? "rgba(5,150,105,0.1)" : "rgba(220,38,38,0.1)";
1737
+ const icon = isSuccess ? `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 256 256" fill="${iconColor}"><path d="M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"/></svg>` : `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 256 256" fill="${iconColor}"><path d="M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z"/></svg>`;
1738
+ return `<!DOCTYPE html>
1739
+ <html lang="en">
1740
+ <head>
1741
+ <meta charset="utf-8" />
1742
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
1743
+ <title>${title} \u2014 CAIK</title>${isSuccess ? `
1744
+ <script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.3/dist/confetti.browser.min.js"></script>` : ""}
1745
+ <style>
1746
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
1747
+ body {
1748
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
1749
+ background: #0C0A09;
1750
+ color: #FAFAF9;
1751
+ display: flex;
1752
+ align-items: center;
1753
+ justify-content: center;
1754
+ min-height: 100vh;
1755
+ padding: 1rem;
1756
+ }
1757
+ .card {
1758
+ max-width: 420px;
1759
+ width: 100%;
1760
+ background: rgba(28, 25, 23, 0.8);
1761
+ border: 1px solid rgba(168,162,158,0.15);
1762
+ border-radius: 20px;
1763
+ padding: 2.5rem 2rem;
1764
+ text-align: center;
1765
+ backdrop-filter: blur(12px);
1766
+ box-shadow: 0 25px 50px rgba(0,0,0,0.4);
1767
+ animation: fadeUp 0.4s ease-out;
1768
+ }
1769
+ @keyframes fadeUp {
1770
+ from { opacity: 0; transform: translateY(12px); }
1771
+ to { opacity: 1; transform: translateY(0); }
1772
+ }
1773
+ .icon-wrap {
1774
+ display: inline-flex;
1775
+ align-items: center;
1776
+ justify-content: center;
1777
+ width: 64px;
1778
+ height: 64px;
1779
+ border-radius: 50%;
1780
+ background: ${iconBg};
1781
+ margin-bottom: 1.25rem;
1782
+ }
1783
+ h1 {
1784
+ font-size: 1.5rem;
1785
+ font-weight: 700;
1786
+ margin-bottom: 0.5rem;
1787
+ letter-spacing: -0.02em;
1788
+ }
1789
+ .subtitle {
1790
+ font-size: 0.938rem;
1791
+ color: #A8A29E;
1792
+ line-height: 1.5;
1793
+ margin-bottom: 1.5rem;
1794
+ }
1795
+ .badge {
1796
+ display: inline-flex;
1797
+ align-items: center;
1798
+ gap: 6px;
1799
+ font-size: 0.75rem;
1800
+ font-weight: 500;
1801
+ padding: 6px 14px;
1802
+ border-radius: 999px;
1803
+ background: rgba(234,88,12,0.12);
1804
+ color: #F97316;
1805
+ letter-spacing: 0.01em;
1806
+ }
1807
+ .badge svg { width: 14px; height: 14px; fill: currentColor; }
1808
+ .divider {
1809
+ height: 1px;
1810
+ background: rgba(168,162,158,0.12);
1811
+ margin: 1.5rem 0;
1812
+ }
1813
+ .hint {
1814
+ font-size: 0.813rem;
1815
+ color: #78716C;
1816
+ }
1817
+ .hint code {
1818
+ background: rgba(168,162,158,0.12);
1819
+ padding: 2px 8px;
1820
+ border-radius: 6px;
1821
+ font-family: "Geist Mono", ui-monospace, monospace;
1822
+ font-size: 0.75rem;
1823
+ color: #A8A29E;
1824
+ }
1825
+ </style>
1826
+ </head>
1827
+ <body>
1828
+ <div class="card">
1829
+ <div class="icon-wrap">${icon}</div>
1830
+ <h1>${title}</h1>
1831
+ <p class="subtitle">${message}</p>
1832
+ <p class="hint">You can close this tab and return to your terminal.</p>
1833
+ </div>${isSuccess ? `
1834
+ <script>
1835
+ (function(){
1836
+ var colors = ["#ff640d", "#0076db", "#ff915a", "#80def9"];
1837
+ var end = Date.now() + 3000;
1838
+ function frame() {
1839
+ if (Date.now() > end) return;
1840
+ confetti({ particleCount: 2, angle: 60, spread: 55, startVelocity: 60, origin: { x: 0, y: 0.5 }, colors: colors });
1841
+ confetti({ particleCount: 2, angle: 120, spread: 55, startVelocity: 60, origin: { x: 1, y: 0.5 }, colors: colors });
1842
+ requestAnimationFrame(frame);
1843
+ }
1844
+ frame();
1845
+ })();
1846
+ </script>` : ""}
1847
+ </body>
1848
+ </html>`;
1849
+ }
1735
1850
  async function authenticate(apiUrl, port = 0) {
1736
1851
  const state = randomBytes(16).toString("hex");
1852
+ let resolved = false;
1853
+ let listenPort = 0;
1737
1854
  return new Promise((resolve3, reject) => {
1738
1855
  const server = createServer((req, res) => {
1739
- const actualPort = server.address().port;
1740
- const reqUrl = new URL2(req.url ?? "/", `http://localhost:${actualPort}`);
1856
+ const addr = server.address();
1857
+ if (!addr || typeof addr === "string") {
1858
+ res.writeHead(404, { "Content-Type": "text/plain" });
1859
+ res.end("Not found");
1860
+ return;
1861
+ }
1862
+ const reqUrl = new URL2(req.url ?? "/", `http://localhost:${listenPort}`);
1741
1863
  if (reqUrl.pathname !== "/callback") {
1742
1864
  res.writeHead(404, { "Content-Type": "text/plain" });
1743
1865
  res.end("Not found");
@@ -1746,43 +1868,57 @@ async function authenticate(apiUrl, port = 0) {
1746
1868
  const returnedState = reqUrl.searchParams.get("state");
1747
1869
  if (returnedState !== state) {
1748
1870
  res.writeHead(403, { "Content-Type": "text/html" });
1749
- res.end(
1750
- "<html><body><h2>Authentication failed</h2><p>Invalid state parameter (possible CSRF). You can close this tab.</p></body></html>"
1751
- );
1871
+ res.end(authPage(
1872
+ "Authentication Failed",
1873
+ "Invalid state parameter (possible CSRF attack).",
1874
+ false
1875
+ ));
1752
1876
  return;
1753
1877
  }
1754
1878
  const apiKeyParam = reqUrl.searchParams.get("api_key");
1755
1879
  if (!apiKeyParam) {
1756
1880
  res.writeHead(400, { "Content-Type": "text/html" });
1757
- res.end(
1758
- "<html><body><h2>Authentication failed</h2><p>No API key received. You can close this tab.</p></body></html>"
1759
- );
1881
+ res.end(authPage(
1882
+ "Authentication Failed",
1883
+ "No API key was received from the server.",
1884
+ false
1885
+ ));
1760
1886
  cleanup();
1761
1887
  reject(new Error("No API key received in callback"));
1762
1888
  return;
1763
1889
  }
1764
1890
  setApiKey(apiKeyParam);
1765
1891
  res.writeHead(200, { "Content-Type": "text/html" });
1766
- res.end(
1767
- "<html><body><h2>Authenticated!</h2><p>You can close this tab and return to your terminal.</p></body></html>"
1768
- );
1892
+ res.end(authPage(
1893
+ "Authenticated!",
1894
+ "Your CLI is now connected to your CAIK account. You're ready to search, install, and publish artifacts.",
1895
+ true
1896
+ ));
1897
+ resolved = true;
1769
1898
  cleanup();
1770
1899
  resolve3(apiKeyParam);
1771
1900
  });
1772
1901
  const timeout = setTimeout(() => {
1773
1902
  cleanup();
1774
- reject(new Error("Authentication timed out after 60 seconds"));
1903
+ if (!resolved) {
1904
+ reject(new Error("Authentication timed out after 60 seconds"));
1905
+ }
1775
1906
  }, TIMEOUT_MS);
1776
1907
  function cleanup() {
1777
1908
  clearTimeout(timeout);
1778
1909
  server.close();
1779
1910
  }
1780
1911
  server.listen(port, async () => {
1781
- const actualPort = server.address().port;
1782
- const callbackUrl = `http://localhost:${actualPort}/callback`;
1912
+ const addr = server.address();
1913
+ if (!addr || typeof addr === "string") {
1914
+ reject(new Error("Failed to get server address after listen"));
1915
+ return;
1916
+ }
1917
+ listenPort = addr.port;
1918
+ const callbackUrl = `http://localhost:${listenPort}/callback`;
1783
1919
  const cliCallbackUrl = `${apiUrl}/api/auth/cli-callback?redirect=${encodeURIComponent(callbackUrl)}&state=${encodeURIComponent(state)}`;
1784
1920
  const loginUrl = `${apiUrl}/cli-auth?continue=${encodeURIComponent(cliCallbackUrl)}`;
1785
- console.log(info(`Listening on http://localhost:${actualPort} for callback...`));
1921
+ console.log(info(`Listening on http://localhost:${listenPort} for callback...`));
1786
1922
  const opened = await openBrowser(loginUrl);
1787
1923
  if (opened) {
1788
1924
  console.log(info("Browser opened. Complete sign-in to continue."));
@@ -1806,6 +1942,33 @@ async function authenticate(apiUrl, port = 0) {
1806
1942
  }
1807
1943
 
1808
1944
  // src/commands/init.ts
1945
+ async function promptContributionLevel() {
1946
+ const config = readConfig();
1947
+ if (config.contributionLevel) return;
1948
+ console.log("");
1949
+ console.log(heading("Contribution Level"));
1950
+ console.log("\u2500".repeat(40));
1951
+ console.log(dim("CAIK is powered by collective intelligence. Your agent can"));
1952
+ console.log(dim("contribute anonymous usage signals to improve recommendations"));
1953
+ console.log(dim("for everyone. No prompts, files, or personal data \u2014 ever."));
1954
+ console.log("");
1955
+ const selected = await select2({
1956
+ message: "What level of contribution works for you?",
1957
+ choices: CONTRIBUTION_LEVELS.map((l) => ({
1958
+ name: `${l.name} \u2014 ${l.description}`,
1959
+ value: l.value
1960
+ })),
1961
+ default: "contributor"
1962
+ });
1963
+ config.contributionLevel = selected;
1964
+ writeConfig(config);
1965
+ console.log(success(`Contribution level set to ${selected}`));
1966
+ if (selected === "none") {
1967
+ console.log(info("No problem \u2014 you can change this anytime with `caik config contribution`."));
1968
+ } else {
1969
+ console.log(info("Change anytime with `caik config contribution`."));
1970
+ }
1971
+ }
1809
1972
  function registerInitCommand(program2) {
1810
1973
  program2.command("init").description("Configure the CAIK CLI").option("--no-auth", "Skip authentication, only configure API URL").addHelpText("after", `
1811
1974
  Examples:
@@ -1830,10 +1993,12 @@ Examples:
1830
1993
  console.log(info(`Config saved to ~/.caik/config.json`));
1831
1994
  } catch (err) {
1832
1995
  console.log(error(`Authentication failed: ${err instanceof Error ? err.message : String(err)}`));
1996
+ return;
1833
1997
  }
1998
+ await promptContributionLevel();
1834
1999
  return;
1835
2000
  }
1836
- const rl = createInterface2({ input: stdin, output: stdout });
2001
+ const rl = createInterface({ input: stdin, output: stdout });
1837
2002
  try {
1838
2003
  const apiUrl = await rl.question(`API URL (${config.apiUrl}): `);
1839
2004
  if (apiUrl.trim()) {
@@ -1882,6 +2047,7 @@ Examples:
1882
2047
  } finally {
1883
2048
  rl.close();
1884
2049
  }
2050
+ await promptContributionLevel();
1885
2051
  });
1886
2052
  }
1887
2053
 
@@ -2224,7 +2390,7 @@ import { existsSync as existsSync10 } from "fs";
2224
2390
  import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "fs";
2225
2391
  import { dirname as dirname4, resolve as resolve2 } from "path";
2226
2392
  import { execSync as execSync4 } from "child_process";
2227
- import { createInterface as createInterface3 } from "readline/promises";
2393
+ import { confirm as confirm2 } from "@inquirer/prompts";
2228
2394
  function registerUpdateCommand(program2) {
2229
2395
  program2.command("update [slug]").description("Check for and apply artifact updates").option("--platform <platform>", "Filter by platform").option("-y, --yes", "Skip confirmation prompts").addHelpText("after", `
2230
2396
  Examples:
@@ -2329,10 +2495,8 @@ Examples:
2329
2495
  console.log(renderTable(["Artifact", "Platform", "Installed", "Latest"], rows));
2330
2496
  console.log(info(`${candidates.length} update(s) available.`));
2331
2497
  if (!skipConfirm) {
2332
- const rl = createInterface3({ input: process.stdin, output: process.stdout });
2333
- const answer = await rl.question("\nUpdate all? (y/N) ");
2334
- rl.close();
2335
- if (answer.toLowerCase() !== "y") {
2498
+ const proceed = await confirm2({ message: "Update all?", default: true });
2499
+ if (!proceed) {
2336
2500
  console.log(info("Update cancelled."));
2337
2501
  return;
2338
2502
  }
@@ -2386,10 +2550,8 @@ async function performUpdate(client, entry, skipConfirm, globalOpts) {
2386
2550
  console.log(info(`Install command: ${installInfo.installCommand}`));
2387
2551
  }
2388
2552
  console.log();
2389
- const rl = createInterface3({ input: process.stdin, output: process.stdout });
2390
- const answer = await rl.question("Continue? (y/N) ");
2391
- rl.close();
2392
- if (answer.toLowerCase() !== "y") {
2553
+ const proceed = await confirm2({ message: "Continue?", default: true });
2554
+ if (!proceed) {
2393
2555
  console.log(info("Update cancelled."));
2394
2556
  return;
2395
2557
  }
@@ -2443,7 +2605,7 @@ async function performUpdate(client, entry, skipConfirm, globalOpts) {
2443
2605
  }
2444
2606
 
2445
2607
  // src/commands/uninstall.ts
2446
- import { createInterface as createInterface4 } from "readline/promises";
2608
+ import { confirm as confirm3 } from "@inquirer/prompts";
2447
2609
  function registerUninstallCommand(program2) {
2448
2610
  program2.command("uninstall <slug>").description("Remove an installed artifact").option("--platform <platform>", "Target platform (disambiguates if installed on multiple)").option("-y, --yes", "Skip confirmation prompts").addHelpText("after", `
2449
2611
  Examples:
@@ -2495,10 +2657,8 @@ Artifact: ${entry.slug} (${entry.artifactType})`));
2495
2657
  }
2496
2658
  }
2497
2659
  if (!skipConfirm) {
2498
- const rl = createInterface4({ input: process.stdin, output: process.stdout });
2499
- const answer = await rl.question("\nUninstall? (y/N) ");
2500
- rl.close();
2501
- if (answer.toLowerCase() !== "y") {
2660
+ const proceed = await confirm3({ message: "Uninstall?", default: false });
2661
+ if (!proceed) {
2502
2662
  console.log(info("Uninstall cancelled."));
2503
2663
  return;
2504
2664
  }
@@ -2747,7 +2907,7 @@ function registerHookCommand(program2) {
2747
2907
  }
2748
2908
 
2749
2909
  // src/commands/setup.ts
2750
- import { createInterface as createInterface5 } from "readline/promises";
2910
+ import { checkbox } from "@inquirer/prompts";
2751
2911
  import { existsSync as existsSync12 } from "fs";
2752
2912
 
2753
2913
  // src/platform/templates/claude-code-skill.ts
@@ -3127,23 +3287,17 @@ Examples:
3127
3287
  if (autoYes || detected.length === 1 || platformFlag) {
3128
3288
  selected = detected;
3129
3289
  } else {
3130
- const rl = createInterface5({ input: process.stdin, output: process.stdout });
3131
- console.log(info("Which platforms would you like to configure?"));
3132
- for (let i = 0; i < detected.length; i++) {
3133
- console.log(` ${i + 1}. ${detected[i].name}`);
3134
- }
3135
- console.log(` a. All`);
3136
- const answer = await rl.question("\nSelect (numbers separated by commas, or 'a' for all): ");
3137
- rl.close();
3138
- if (answer.toLowerCase() === "a" || answer.trim() === "") {
3139
- selected = detected;
3140
- } else {
3141
- const indices = answer.split(",").map((s) => parseInt(s.trim(), 10) - 1);
3142
- selected = indices.filter((i) => i >= 0 && i < detected.length).map((i) => detected[i]);
3143
- if (selected.length === 0) {
3144
- console.log(warn("No valid platforms selected. Exiting."));
3145
- return;
3146
- }
3290
+ selected = await checkbox({
3291
+ message: "Which platforms would you like to configure?",
3292
+ choices: detected.map((p) => ({
3293
+ name: `${p.name} ${dim(`(${p.tier})`)}`,
3294
+ value: p,
3295
+ checked: true
3296
+ }))
3297
+ });
3298
+ if (selected.length === 0) {
3299
+ console.log(warn("No platforms selected. Exiting."));
3300
+ return;
3147
3301
  }
3148
3302
  }
3149
3303
  console.log();
@@ -3343,7 +3497,7 @@ function registerUpgradeCommand(program2) {
3343
3497
  });
3344
3498
  spinner.stop();
3345
3499
  console.log(success(`Upgraded to ${update.latest}`));
3346
- } catch (err) {
3500
+ } catch {
3347
3501
  spinner.stop();
3348
3502
  console.log(error("Upgrade failed. Try manually:"));
3349
3503
  console.log(info("npm install -g @caik.dev/cli@latest"));
@@ -3351,6 +3505,68 @@ function registerUpgradeCommand(program2) {
3351
3505
  });
3352
3506
  }
3353
3507
 
3508
+ // src/commands/logout.ts
3509
+ function registerLogoutCommand(program2) {
3510
+ program2.command("logout").description("Sign out and remove stored API key").action(() => {
3511
+ const config = readConfig();
3512
+ if (!config.apiKey) {
3513
+ console.log(info("No API key stored \u2014 already signed out."));
3514
+ return;
3515
+ }
3516
+ delete config.apiKey;
3517
+ writeConfig(config);
3518
+ console.log(success("Signed out. API key removed from ~/.caik/config.json"));
3519
+ });
3520
+ }
3521
+
3522
+ // src/commands/config.ts
3523
+ import { select as select3 } from "@inquirer/prompts";
3524
+ function registerConfigCommand(program2) {
3525
+ const cmd = program2.command("config").description("View or update CLI configuration");
3526
+ cmd.command("show").description("Show current configuration").action(() => {
3527
+ const config = readConfig();
3528
+ console.log(heading("\nCAIK Configuration"));
3529
+ console.log("\u2500".repeat(40));
3530
+ console.log(` API URL: ${config.apiUrl}`);
3531
+ console.log(` API Key: ${config.apiKey ? "\u2022\u2022\u2022\u2022" + config.apiKey.slice(-6) : dim("not set")}`);
3532
+ console.log(` Contribution Level: ${config.contributionLevel ?? "contributor"} ${dim("(default)")}`);
3533
+ console.log(` Default Platform: ${config.defaultPlatform ?? dim("auto-detect")}`);
3534
+ console.log(` Config Path: ${dim("~/.caik/config.json")}`);
3535
+ console.log();
3536
+ });
3537
+ cmd.command("contribution").description("Set your contribution level").argument("[level]", "Level to set: none, minimal, contributor, collective").action(async (level) => {
3538
+ const config = readConfig();
3539
+ const current = config.contributionLevel ?? "contributor";
3540
+ if (level) {
3541
+ const valid = CONTRIBUTION_LEVELS.map((l) => l.value);
3542
+ if (!valid.includes(level)) {
3543
+ console.log(`Invalid level "${level}". Choose: ${valid.join(", ")}`);
3544
+ return;
3545
+ }
3546
+ config.contributionLevel = level;
3547
+ writeConfig(config);
3548
+ console.log(success(`Contribution level set to ${level}`));
3549
+ return;
3550
+ }
3551
+ const selected = await select3({
3552
+ message: "Select your contribution level",
3553
+ choices: CONTRIBUTION_LEVELS.map((l) => ({
3554
+ name: `${l.name} \u2014 ${l.description}`,
3555
+ value: l.value
3556
+ })),
3557
+ default: current
3558
+ });
3559
+ config.contributionLevel = selected;
3560
+ writeConfig(config);
3561
+ console.log(success(`Contribution level set to ${selected}`));
3562
+ if (selected === "none") {
3563
+ console.log(info("No telemetry will be sent. You can still search and install artifacts."));
3564
+ } else if (selected === "collective") {
3565
+ console.log(info("Thank you! You'll get proactive recommendations and provider analytics."));
3566
+ }
3567
+ });
3568
+ }
3569
+
3354
3570
  // src/index.ts
3355
3571
  var __filename = fileURLToPath(import.meta.url);
3356
3572
  var __dirname = dirname5(__filename);
@@ -3365,6 +3581,8 @@ var program = new Command().name("caik").description("CAIK \u2014 Collective AI
3365
3581
  registerSearchCommand(program);
3366
3582
  registerInstallCommand(program);
3367
3583
  registerInitCommand(program);
3584
+ registerLogoutCommand(program);
3585
+ registerConfigCommand(program);
3368
3586
  registerStatusCommand(program);
3369
3587
  registerStatsCommand(program);
3370
3588
  registerPublishCommand(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caik.dev/cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "type": "module",
5
5
  "description": "CAIK CLI — Search, install, and publish AI artifacts from your terminal",
6
6
  "keywords": [
@@ -34,6 +34,7 @@
34
34
  "typecheck": "tsc --noEmit"
35
35
  },
36
36
  "dependencies": {
37
+ "@inquirer/prompts": "^8.3.2",
37
38
  "chalk": "^5.6.2",
38
39
  "cli-table3": "^0.6.5",
39
40
  "commander": "^14.0.3",