@caik.dev/cli 0.1.7 → 0.1.8

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 +315 -294
  2. package/package.json +1 -2
package/dist/index.js CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
- import { readFileSync as readFileSync9 } from "fs";
5
+ import { readFileSync as readFileSync10 } from "fs";
6
6
  import { fileURLToPath } from "url";
7
- import { dirname as dirname5, join as join9 } from "path";
7
+ import { dirname as dirname5, join as join10 } from "path";
8
8
  import chalk3 from "chalk";
9
9
 
10
10
  // src/errors.ts
@@ -132,12 +132,6 @@ 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
- ];
141
135
  var DEFAULT_CONFIG = {
142
136
  apiUrl: "https://www.caik.dev",
143
137
  defaultLimit: 10,
@@ -309,7 +303,7 @@ import { existsSync as existsSync7, unlinkSync as unlinkSync2 } from "fs";
309
303
  import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync6 } from "fs";
310
304
  import { dirname as dirname3, resolve } from "path";
311
305
  import { execSync as execSync3 } from "child_process";
312
- import { select, confirm } from "@inquirer/prompts";
306
+ import { createInterface } from "readline/promises";
313
307
 
314
308
  // src/platform/detect.ts
315
309
  import { existsSync as existsSync2 } from "fs";
@@ -1410,13 +1404,20 @@ Examples:
1410
1404
  } else {
1411
1405
  const detected = detectPlatforms();
1412
1406
  if (detected.length > 1 && !opts.yes && !globalOpts.json) {
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
- });
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;
1420
1421
  } else if (detected.length > 0) {
1421
1422
  detectedPlatform = detected[0].name;
1422
1423
  }
@@ -1462,11 +1463,10 @@ Components (${ordered.length}):`));
1462
1463
  }
1463
1464
  console.log();
1464
1465
  if (!opts.yes && !globalOpts.json) {
1465
- const proceed = await confirm({
1466
- message: `Install all ${ordered.length} components?`,
1467
- default: true
1468
- });
1469
- if (!proceed) {
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") {
1470
1470
  console.log(info("Stack installation cancelled."));
1471
1471
  return;
1472
1472
  }
@@ -1593,8 +1593,10 @@ Components (${ordered.length}):`));
1593
1593
  console.log(info(`Post-install hook: ${installInfo.postInstallHook}`));
1594
1594
  }
1595
1595
  console.log();
1596
- const proceed = await confirm({ message: "Continue?", default: true });
1597
- if (!proceed) {
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") {
1598
1600
  console.log(info("Installation cancelled."));
1599
1601
  return;
1600
1602
  }
@@ -1708,8 +1710,7 @@ Components (${ordered.length}):`));
1708
1710
  }
1709
1711
 
1710
1712
  // src/commands/init.ts
1711
- import { select as select2 } from "@inquirer/prompts";
1712
- import { createInterface } from "readline/promises";
1713
+ import { createInterface as createInterface2 } from "readline/promises";
1713
1714
  import { stdin, stdout } from "process";
1714
1715
 
1715
1716
  // src/auth.ts
@@ -1731,135 +1732,12 @@ function openBrowser(url) {
1731
1732
  }
1732
1733
  });
1733
1734
  }
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
- }
1850
1735
  async function authenticate(apiUrl, port = 0) {
1851
1736
  const state = randomBytes(16).toString("hex");
1852
- let resolved = false;
1853
- let listenPort = 0;
1854
1737
  return new Promise((resolve3, reject) => {
1855
1738
  const server = createServer((req, res) => {
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}`);
1739
+ const actualPort = server.address().port;
1740
+ const reqUrl = new URL2(req.url ?? "/", `http://localhost:${actualPort}`);
1863
1741
  if (reqUrl.pathname !== "/callback") {
1864
1742
  res.writeHead(404, { "Content-Type": "text/plain" });
1865
1743
  res.end("Not found");
@@ -1868,57 +1746,43 @@ async function authenticate(apiUrl, port = 0) {
1868
1746
  const returnedState = reqUrl.searchParams.get("state");
1869
1747
  if (returnedState !== state) {
1870
1748
  res.writeHead(403, { "Content-Type": "text/html" });
1871
- res.end(authPage(
1872
- "Authentication Failed",
1873
- "Invalid state parameter (possible CSRF attack).",
1874
- false
1875
- ));
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
+ );
1876
1752
  return;
1877
1753
  }
1878
1754
  const apiKeyParam = reqUrl.searchParams.get("api_key");
1879
1755
  if (!apiKeyParam) {
1880
1756
  res.writeHead(400, { "Content-Type": "text/html" });
1881
- res.end(authPage(
1882
- "Authentication Failed",
1883
- "No API key was received from the server.",
1884
- false
1885
- ));
1757
+ res.end(
1758
+ "<html><body><h2>Authentication failed</h2><p>No API key received. You can close this tab.</p></body></html>"
1759
+ );
1886
1760
  cleanup();
1887
1761
  reject(new Error("No API key received in callback"));
1888
1762
  return;
1889
1763
  }
1890
1764
  setApiKey(apiKeyParam);
1891
1765
  res.writeHead(200, { "Content-Type": "text/html" });
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;
1766
+ res.end(
1767
+ "<html><body><h2>Authenticated!</h2><p>You can close this tab and return to your terminal.</p></body></html>"
1768
+ );
1898
1769
  cleanup();
1899
1770
  resolve3(apiKeyParam);
1900
1771
  });
1901
1772
  const timeout = setTimeout(() => {
1902
1773
  cleanup();
1903
- if (!resolved) {
1904
- reject(new Error("Authentication timed out after 60 seconds"));
1905
- }
1774
+ reject(new Error("Authentication timed out after 60 seconds"));
1906
1775
  }, TIMEOUT_MS);
1907
1776
  function cleanup() {
1908
1777
  clearTimeout(timeout);
1909
1778
  server.close();
1910
1779
  }
1911
1780
  server.listen(port, async () => {
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`;
1781
+ const actualPort = server.address().port;
1782
+ const callbackUrl = `http://localhost:${actualPort}/callback`;
1919
1783
  const cliCallbackUrl = `${apiUrl}/api/auth/cli-callback?redirect=${encodeURIComponent(callbackUrl)}&state=${encodeURIComponent(state)}`;
1920
1784
  const loginUrl = `${apiUrl}/cli-auth?continue=${encodeURIComponent(cliCallbackUrl)}`;
1921
- console.log(info(`Listening on http://localhost:${listenPort} for callback...`));
1785
+ console.log(info(`Listening on http://localhost:${actualPort} for callback...`));
1922
1786
  const opened = await openBrowser(loginUrl);
1923
1787
  if (opened) {
1924
1788
  console.log(info("Browser opened. Complete sign-in to continue."));
@@ -1942,33 +1806,6 @@ async function authenticate(apiUrl, port = 0) {
1942
1806
  }
1943
1807
 
1944
1808
  // 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
- }
1972
1809
  function registerInitCommand(program2) {
1973
1810
  program2.command("init").description("Configure the CAIK CLI").option("--no-auth", "Skip authentication, only configure API URL").addHelpText("after", `
1974
1811
  Examples:
@@ -1993,12 +1830,10 @@ Examples:
1993
1830
  console.log(info(`Config saved to ~/.caik/config.json`));
1994
1831
  } catch (err) {
1995
1832
  console.log(error(`Authentication failed: ${err instanceof Error ? err.message : String(err)}`));
1996
- return;
1997
1833
  }
1998
- await promptContributionLevel();
1999
1834
  return;
2000
1835
  }
2001
- const rl = createInterface({ input: stdin, output: stdout });
1836
+ const rl = createInterface2({ input: stdin, output: stdout });
2002
1837
  try {
2003
1838
  const apiUrl = await rl.question(`API URL (${config.apiUrl}): `);
2004
1839
  if (apiUrl.trim()) {
@@ -2047,7 +1882,6 @@ Examples:
2047
1882
  } finally {
2048
1883
  rl.close();
2049
1884
  }
2050
- await promptContributionLevel();
2051
1885
  });
2052
1886
  }
2053
1887
 
@@ -2390,7 +2224,7 @@ import { existsSync as existsSync10 } from "fs";
2390
2224
  import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "fs";
2391
2225
  import { dirname as dirname4, resolve as resolve2 } from "path";
2392
2226
  import { execSync as execSync4 } from "child_process";
2393
- import { confirm as confirm2 } from "@inquirer/prompts";
2227
+ import { createInterface as createInterface3 } from "readline/promises";
2394
2228
  function registerUpdateCommand(program2) {
2395
2229
  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", `
2396
2230
  Examples:
@@ -2495,8 +2329,10 @@ Examples:
2495
2329
  console.log(renderTable(["Artifact", "Platform", "Installed", "Latest"], rows));
2496
2330
  console.log(info(`${candidates.length} update(s) available.`));
2497
2331
  if (!skipConfirm) {
2498
- const proceed = await confirm2({ message: "Update all?", default: true });
2499
- if (!proceed) {
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") {
2500
2336
  console.log(info("Update cancelled."));
2501
2337
  return;
2502
2338
  }
@@ -2550,8 +2386,10 @@ async function performUpdate(client, entry, skipConfirm, globalOpts) {
2550
2386
  console.log(info(`Install command: ${installInfo.installCommand}`));
2551
2387
  }
2552
2388
  console.log();
2553
- const proceed = await confirm2({ message: "Continue?", default: true });
2554
- if (!proceed) {
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") {
2555
2393
  console.log(info("Update cancelled."));
2556
2394
  return;
2557
2395
  }
@@ -2605,7 +2443,7 @@ async function performUpdate(client, entry, skipConfirm, globalOpts) {
2605
2443
  }
2606
2444
 
2607
2445
  // src/commands/uninstall.ts
2608
- import { confirm as confirm3 } from "@inquirer/prompts";
2446
+ import { createInterface as createInterface4 } from "readline/promises";
2609
2447
  function registerUninstallCommand(program2) {
2610
2448
  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", `
2611
2449
  Examples:
@@ -2657,8 +2495,10 @@ Artifact: ${entry.slug} (${entry.artifactType})`));
2657
2495
  }
2658
2496
  }
2659
2497
  if (!skipConfirm) {
2660
- const proceed = await confirm3({ message: "Uninstall?", default: false });
2661
- if (!proceed) {
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") {
2662
2502
  console.log(info("Uninstall cancelled."));
2663
2503
  return;
2664
2504
  }
@@ -2745,12 +2585,57 @@ function registerKarmaCommand(program2) {
2745
2585
  }
2746
2586
 
2747
2587
  // src/commands/hook.ts
2748
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8, existsSync as existsSync11 } from "fs";
2588
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8, existsSync as existsSync11, appendFileSync } from "fs";
2749
2589
  import { join as join7 } from "path";
2750
2590
  import { homedir as homedir6 } from "os";
2751
- var PENDING_EVENTS_PATH = join7(homedir6(), ".caik", "pending-events.json");
2591
+ var CAIK_DIR = join7(homedir6(), ".caik");
2592
+ var PENDING_EVENTS_PATH = join7(CAIK_DIR, "pending-events.json");
2593
+ var AUDIT_LOG_PATH = join7(CAIK_DIR, "audit.log");
2752
2594
  var FLUSH_THRESHOLD = 50;
2753
2595
  var API_TIMEOUT_MS = 2e3;
2596
+ function readStdin() {
2597
+ return new Promise((resolve3) => {
2598
+ if (process.stdin.isTTY) {
2599
+ resolve3("");
2600
+ return;
2601
+ }
2602
+ let data = "";
2603
+ process.stdin.setEncoding("utf-8");
2604
+ process.stdin.on("data", (chunk) => {
2605
+ data += chunk;
2606
+ });
2607
+ process.stdin.on("end", () => resolve3(data));
2608
+ setTimeout(() => resolve3(data), 500);
2609
+ });
2610
+ }
2611
+ function parseStdin(raw) {
2612
+ if (!raw.trim()) return null;
2613
+ try {
2614
+ return JSON.parse(raw);
2615
+ } catch {
2616
+ return null;
2617
+ }
2618
+ }
2619
+ function shouldSendTelemetry() {
2620
+ const config = readConfig();
2621
+ const level = config.contributionLevel ?? "contributor";
2622
+ return level !== "none";
2623
+ }
2624
+ function shouldSendToolUse() {
2625
+ const config = readConfig();
2626
+ const level = config.contributionLevel ?? "contributor";
2627
+ return level === "contributor" || level === "collective";
2628
+ }
2629
+ function auditLog(event) {
2630
+ try {
2631
+ if (!existsSync11(CAIK_DIR)) {
2632
+ mkdirSync8(CAIK_DIR, { recursive: true, mode: 448 });
2633
+ }
2634
+ const line = JSON.stringify(event) + "\n";
2635
+ appendFileSync(AUDIT_LOG_PATH, line, "utf-8");
2636
+ } catch {
2637
+ }
2638
+ }
2754
2639
  function readPendingEvents() {
2755
2640
  try {
2756
2641
  if (!existsSync11(PENDING_EVENTS_PATH)) return [];
@@ -2762,9 +2647,8 @@ function readPendingEvents() {
2762
2647
  }
2763
2648
  }
2764
2649
  function writePendingEvents(events) {
2765
- const dir = join7(homedir6(), ".caik");
2766
- if (!existsSync11(dir)) {
2767
- mkdirSync8(dir, { recursive: true, mode: 448 });
2650
+ if (!existsSync11(CAIK_DIR)) {
2651
+ mkdirSync8(CAIK_DIR, { recursive: true, mode: 448 });
2768
2652
  }
2769
2653
  writeFileSync8(PENDING_EVENTS_PATH, JSON.stringify(events, null, 2) + "\n", "utf-8");
2770
2654
  }
@@ -2814,12 +2698,20 @@ function registerHookCommand(program2) {
2814
2698
  const hook = program2.command("hook").description("Platform hook callbacks (observation only, never gates agent actions)");
2815
2699
  hook.command("session-start").description("Log session start event").option("--platform <name>", "Platform name", "claude-code").action(async (opts) => {
2816
2700
  try {
2817
- const client = createClient(program2);
2701
+ if (!shouldSendTelemetry()) {
2702
+ process.exit(0);
2703
+ return;
2704
+ }
2705
+ const raw = await readStdin();
2706
+ const input = parseStdin(raw);
2818
2707
  const event = {
2819
2708
  type: "session_start",
2820
2709
  platform: opts.platform ?? "claude-code",
2710
+ sessionId: input?.session_id,
2821
2711
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
2822
2712
  };
2713
+ auditLog(event);
2714
+ const client = createClient(program2);
2823
2715
  await postSingleEventWithTimeout(client, event);
2824
2716
  } catch {
2825
2717
  }
@@ -2827,28 +2719,47 @@ function registerHookCommand(program2) {
2827
2719
  });
2828
2720
  hook.command("session-end").description("Flush pending events and log session end").option("--platform <name>", "Platform name", "claude-code").action(async (opts) => {
2829
2721
  try {
2830
- const client = createClient(program2);
2722
+ if (!shouldSendTelemetry()) {
2723
+ clearPendingEvents();
2724
+ process.exit(0);
2725
+ return;
2726
+ }
2727
+ const raw = await readStdin();
2728
+ const input = parseStdin(raw);
2831
2729
  const pending = readPendingEvents();
2832
2730
  const endEvent = {
2833
2731
  type: "session_end",
2834
2732
  platform: opts.platform ?? "claude-code",
2733
+ sessionId: input?.session_id,
2835
2734
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
2836
2735
  };
2837
2736
  pending.push(endEvent);
2737
+ auditLog(endEvent);
2738
+ const client = createClient(program2);
2838
2739
  await postEventsWithTimeout(client, pending);
2839
2740
  } catch {
2840
2741
  }
2841
2742
  process.exit(0);
2842
2743
  });
2843
- hook.command("post-tool-use").description("Buffer a tool-use event").option("--platform <name>", "Platform name", "claude-code").option("--tool <name>", "Tool name").option("--success <bool>", "Whether the tool call succeeded").action(async (opts) => {
2744
+ hook.command("post-tool-use").description("Buffer a tool-use event").option("--platform <name>", "Platform name", "claude-code").option("--tool <name>", "Tool name (fallback if stdin unavailable)").option("--success <bool>", "Success flag (fallback if stdin unavailable)").action(async (opts) => {
2844
2745
  try {
2746
+ if (!shouldSendToolUse()) {
2747
+ process.exit(0);
2748
+ return;
2749
+ }
2750
+ const raw = await readStdin();
2751
+ const input = parseStdin(raw);
2752
+ const toolName = input?.tool_name ?? opts.tool;
2753
+ const success2 = input?.tool_response?.success ?? opts.success === "true" ?? false;
2845
2754
  const event = {
2846
2755
  type: "tool_use",
2847
2756
  platform: opts.platform ?? "claude-code",
2848
- tool: opts.tool,
2849
- success: opts.success === "true" || opts.success === true,
2757
+ sessionId: input?.session_id,
2758
+ tool: toolName,
2759
+ success: Boolean(success2),
2850
2760
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
2851
2761
  };
2762
+ auditLog(event);
2852
2763
  const events = bufferEvent(event);
2853
2764
  if (events.length >= FLUSH_THRESHOLD) {
2854
2765
  const client = createClient(program2);
@@ -2860,12 +2771,17 @@ function registerHookCommand(program2) {
2860
2771
  });
2861
2772
  hook.command("cursor-session-start").description("Log Cursor session start event").action(async () => {
2862
2773
  try {
2863
- const client = createClient(program2);
2774
+ if (!shouldSendTelemetry()) {
2775
+ process.exit(0);
2776
+ return;
2777
+ }
2864
2778
  const event = {
2865
2779
  type: "session_start",
2866
2780
  platform: "cursor",
2867
2781
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
2868
2782
  };
2783
+ auditLog(event);
2784
+ const client = createClient(program2);
2869
2785
  await postSingleEventWithTimeout(client, event);
2870
2786
  } catch {
2871
2787
  }
@@ -2873,6 +2789,10 @@ function registerHookCommand(program2) {
2873
2789
  });
2874
2790
  hook.command("cursor-mcp-exec").description("Buffer a Cursor MCP tool execution event").option("--server <name>", "MCP server name").option("--tool <name>", "Tool name").action(async (opts) => {
2875
2791
  try {
2792
+ if (!shouldSendToolUse()) {
2793
+ process.exit(0);
2794
+ return;
2795
+ }
2876
2796
  const event = {
2877
2797
  type: "mcp_exec",
2878
2798
  platform: "cursor",
@@ -2880,6 +2800,7 @@ function registerHookCommand(program2) {
2880
2800
  tool: opts.tool,
2881
2801
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
2882
2802
  };
2803
+ auditLog(event);
2883
2804
  const events = bufferEvent(event);
2884
2805
  if (events.length >= FLUSH_THRESHOLD) {
2885
2806
  const client = createClient(program2);
@@ -2891,7 +2812,11 @@ function registerHookCommand(program2) {
2891
2812
  });
2892
2813
  hook.command("cursor-session-end").description("Flush pending events and log Cursor session end").action(async () => {
2893
2814
  try {
2894
- const client = createClient(program2);
2815
+ if (!shouldSendTelemetry()) {
2816
+ clearPendingEvents();
2817
+ process.exit(0);
2818
+ return;
2819
+ }
2895
2820
  const pending = readPendingEvents();
2896
2821
  const endEvent = {
2897
2822
  type: "session_end",
@@ -2899,6 +2824,8 @@ function registerHookCommand(program2) {
2899
2824
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
2900
2825
  };
2901
2826
  pending.push(endEvent);
2827
+ auditLog(endEvent);
2828
+ const client = createClient(program2);
2902
2829
  await postEventsWithTimeout(client, pending);
2903
2830
  } catch {
2904
2831
  }
@@ -2907,7 +2834,7 @@ function registerHookCommand(program2) {
2907
2834
  }
2908
2835
 
2909
2836
  // src/commands/setup.ts
2910
- import { checkbox } from "@inquirer/prompts";
2837
+ import { createInterface as createInterface5 } from "readline/promises";
2911
2838
  import { existsSync as existsSync12 } from "fs";
2912
2839
 
2913
2840
  // src/platform/templates/claude-code-skill.ts
@@ -3287,17 +3214,23 @@ Examples:
3287
3214
  if (autoYes || detected.length === 1 || platformFlag) {
3288
3215
  selected = detected;
3289
3216
  } else {
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;
3217
+ const rl = createInterface5({ input: process.stdin, output: process.stdout });
3218
+ console.log(info("Which platforms would you like to configure?"));
3219
+ for (let i = 0; i < detected.length; i++) {
3220
+ console.log(` ${i + 1}. ${detected[i].name}`);
3221
+ }
3222
+ console.log(` a. All`);
3223
+ const answer = await rl.question("\nSelect (numbers separated by commas, or 'a' for all): ");
3224
+ rl.close();
3225
+ if (answer.toLowerCase() === "a" || answer.trim() === "") {
3226
+ selected = detected;
3227
+ } else {
3228
+ const indices = answer.split(",").map((s) => parseInt(s.trim(), 10) - 1);
3229
+ selected = indices.filter((i) => i >= 0 && i < detected.length).map((i) => detected[i]);
3230
+ if (selected.length === 0) {
3231
+ console.log(warn("No valid platforms selected. Exiting."));
3232
+ return;
3233
+ }
3301
3234
  }
3302
3235
  }
3303
3236
  console.log();
@@ -3505,65 +3438,154 @@ function registerUpgradeCommand(program2) {
3505
3438
  });
3506
3439
  }
3507
3440
 
3508
- // src/commands/logout.ts
3509
- function registerLogoutCommand(program2) {
3510
- program2.command("logout").description("Sign out and remove stored API key").action(() => {
3441
+ // src/commands/audit.ts
3442
+ import { readFileSync as readFileSync9, existsSync as existsSync14, writeFileSync as writeFileSync10 } from "fs";
3443
+ import { join as join9 } from "path";
3444
+ import { homedir as homedir8 } from "os";
3445
+ var CAIK_DIR2 = join9(homedir8(), ".caik");
3446
+ var AUDIT_LOG_PATH2 = join9(CAIK_DIR2, "audit.log");
3447
+ var PENDING_EVENTS_PATH2 = join9(CAIK_DIR2, "pending-events.json");
3448
+ function registerAuditCommand(program2) {
3449
+ const cmd = program2.command("audit").description("See exactly what telemetry CAIK collects \u2014 full transparency");
3450
+ cmd.command("log").description("Show recent telemetry events (from local audit log)").option("-n, --lines <count>", "Number of recent events to show", "20").option("--all", "Show all events").action((opts) => {
3451
+ console.log(heading("\nCAIK Telemetry Audit Log"));
3452
+ console.log("\u2550".repeat(50));
3511
3453
  const config = readConfig();
3512
- if (!config.apiKey) {
3513
- console.log(info("No API key stored \u2014 already signed out."));
3454
+ const level = config.contributionLevel ?? "contributor";
3455
+ console.log(info(`Contribution level: ${level}`));
3456
+ console.log(dim(`Log path: ${AUDIT_LOG_PATH2}`));
3457
+ console.log();
3458
+ if (!existsSync14(AUDIT_LOG_PATH2)) {
3459
+ console.log(dim("No events logged yet. Events appear here after your next Claude Code session."));
3514
3460
  return;
3515
3461
  }
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")}`);
3462
+ const raw = readFileSync9(AUDIT_LOG_PATH2, "utf-8").trim();
3463
+ if (!raw) {
3464
+ console.log(dim("Audit log is empty."));
3465
+ return;
3466
+ }
3467
+ const lines = raw.split("\n");
3468
+ const limit = opts.all ? lines.length : Math.min(parseInt(opts.lines, 10) || 20, lines.length);
3469
+ const recent = lines.slice(-limit);
3470
+ if (!opts.all && lines.length > limit) {
3471
+ console.log(dim(`Showing last ${limit} of ${lines.length} events. Use --all to see everything.
3472
+ `));
3473
+ }
3474
+ for (const line of recent) {
3475
+ try {
3476
+ const event = JSON.parse(line);
3477
+ const time = new Date(event.timestamp).toLocaleTimeString();
3478
+ const type = event.type.padEnd(14);
3479
+ const platform2 = (event.platform ?? "").padEnd(12);
3480
+ const tool = event.tool ? ` tool=${event.tool}` : "";
3481
+ const ok = event.success !== void 0 ? ` success=${event.success}` : "";
3482
+ const sid = event.sessionId ? ` sid=${event.sessionId.slice(0, 8)}\u2026` : "";
3483
+ console.log(` ${dim(time)} ${type} ${dim(platform2)}${tool}${ok}${sid}`);
3484
+ } catch {
3485
+ console.log(` ${dim(line)}`);
3486
+ }
3487
+ }
3535
3488
  console.log();
3489
+ console.log(dim("This is everything CAIK sends. No prompts, files, or personal data \u2014 ever."));
3490
+ console.log(dim("Change your level: caik config contribution"));
3536
3491
  });
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(", ")}`);
3492
+ cmd.command("pending").description("Show events buffered locally (not yet sent to API)").action(() => {
3493
+ console.log(heading("\nPending Events (buffered locally)"));
3494
+ console.log("\u2500".repeat(50));
3495
+ console.log(dim(`Path: ${PENDING_EVENTS_PATH2}
3496
+ `));
3497
+ if (!existsSync14(PENDING_EVENTS_PATH2)) {
3498
+ console.log(dim("No pending events."));
3499
+ return;
3500
+ }
3501
+ try {
3502
+ const raw = readFileSync9(PENDING_EVENTS_PATH2, "utf-8");
3503
+ const events = JSON.parse(raw);
3504
+ if (events.length === 0) {
3505
+ console.log(dim("No pending events."));
3544
3506
  return;
3545
3507
  }
3546
- config.contributionLevel = level;
3547
- writeConfig(config);
3548
- console.log(success(`Contribution level set to ${level}`));
3549
- return;
3508
+ console.log(info(`${events.length} event(s) buffered, waiting for session end to flush:
3509
+ `));
3510
+ const counts = /* @__PURE__ */ new Map();
3511
+ const tools = /* @__PURE__ */ new Map();
3512
+ let successCount = 0;
3513
+ let failCount = 0;
3514
+ for (const e of events) {
3515
+ counts.set(e.type, (counts.get(e.type) ?? 0) + 1);
3516
+ if (e.tool) tools.set(e.tool, (tools.get(e.tool) ?? 0) + 1);
3517
+ if (e.success === true) successCount++;
3518
+ if (e.success === false) failCount++;
3519
+ }
3520
+ for (const [type, count] of counts) {
3521
+ console.log(` ${type}: ${count}`);
3522
+ }
3523
+ if (tools.size > 0) {
3524
+ console.log();
3525
+ console.log(info("Tools used:"));
3526
+ const sorted = [...tools.entries()].sort((a, b) => b[1] - a[1]);
3527
+ for (const [tool, count] of sorted.slice(0, 10)) {
3528
+ console.log(` ${tool}: ${count}`);
3529
+ }
3530
+ if (sorted.length > 10) {
3531
+ console.log(dim(` \u2026 and ${sorted.length - 10} more`));
3532
+ }
3533
+ }
3534
+ if (successCount + failCount > 0) {
3535
+ console.log();
3536
+ console.log(` ${success(`${successCount} succeeded`)} / ${warn(`${failCount} failed`)}`);
3537
+ }
3538
+ } catch {
3539
+ console.log(warn("Could not parse pending events file."));
3550
3540
  }
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."));
3541
+ });
3542
+ cmd.command("clear").description("Clear the local audit log and pending events").action(() => {
3543
+ if (existsSync14(AUDIT_LOG_PATH2)) {
3544
+ writeFileSync10(AUDIT_LOG_PATH2, "", "utf-8");
3545
+ }
3546
+ if (existsSync14(PENDING_EVENTS_PATH2)) {
3547
+ writeFileSync10(PENDING_EVENTS_PATH2, "[]", "utf-8");
3548
+ }
3549
+ console.log(success("Audit log and pending events cleared."));
3550
+ });
3551
+ cmd.action(() => {
3552
+ const config = readConfig();
3553
+ const level = config.contributionLevel ?? "contributor";
3554
+ console.log(heading("\nCAIK Telemetry Transparency"));
3555
+ console.log("\u2550".repeat(50));
3556
+ console.log();
3557
+ console.log(` Contribution Level: ${level}`);
3558
+ let logCount = 0;
3559
+ if (existsSync14(AUDIT_LOG_PATH2)) {
3560
+ const raw = readFileSync9(AUDIT_LOG_PATH2, "utf-8").trim();
3561
+ if (raw) logCount = raw.split("\n").length;
3562
+ }
3563
+ console.log(` Events Logged: ${logCount}`);
3564
+ let pendingCount = 0;
3565
+ if (existsSync14(PENDING_EVENTS_PATH2)) {
3566
+ try {
3567
+ const parsed = JSON.parse(readFileSync9(PENDING_EVENTS_PATH2, "utf-8"));
3568
+ if (Array.isArray(parsed)) pendingCount = parsed.length;
3569
+ } catch {
3570
+ }
3566
3571
  }
3572
+ console.log(` Pending (unsent): ${pendingCount}`);
3573
+ console.log(` Audit Log: ${dim(AUDIT_LOG_PATH2)}`);
3574
+ console.log();
3575
+ console.log(heading("What each level sends:"));
3576
+ console.log(` ${dim("none")} Nothing. Directory access only.`);
3577
+ console.log(` ${dim("minimal")} Install/uninstall events (artifact ID + timestamp).`);
3578
+ console.log(` ${dim("contributor")} + Tool-use signals (tool name, success/fail). No content.`);
3579
+ console.log(` ${dim("collective")} + Workflow patterns, stack signals. Anonymized.`);
3580
+ console.log();
3581
+ console.log(dim("NEVER sent: prompts, file contents, conversation text, file paths, personal data."));
3582
+ console.log();
3583
+ console.log(info("Commands:"));
3584
+ console.log(` ${dim("caik audit log")} Show recent events`);
3585
+ console.log(` ${dim("caik audit pending")} Show buffered events`);
3586
+ console.log(` ${dim("caik audit clear")} Clear all local telemetry data`);
3587
+ console.log(` ${dim("caik config contribution")} Change your contribution level`);
3588
+ console.log();
3567
3589
  });
3568
3590
  }
3569
3591
 
@@ -3572,8 +3594,8 @@ var __filename = fileURLToPath(import.meta.url);
3572
3594
  var __dirname = dirname5(__filename);
3573
3595
  var version = "0.0.1";
3574
3596
  try {
3575
- const pkgPath = join9(__dirname, "..", "package.json");
3576
- const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
3597
+ const pkgPath = join10(__dirname, "..", "package.json");
3598
+ const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
3577
3599
  version = pkg.version;
3578
3600
  } catch {
3579
3601
  }
@@ -3581,8 +3603,6 @@ var program = new Command().name("caik").description("CAIK \u2014 Collective AI
3581
3603
  registerSearchCommand(program);
3582
3604
  registerInstallCommand(program);
3583
3605
  registerInitCommand(program);
3584
- registerLogoutCommand(program);
3585
- registerConfigCommand(program);
3586
3606
  registerStatusCommand(program);
3587
3607
  registerStatsCommand(program);
3588
3608
  registerPublishCommand(program);
@@ -3596,6 +3616,7 @@ registerKarmaCommand(program);
3596
3616
  registerHookCommand(program);
3597
3617
  registerSetupCommand(program);
3598
3618
  registerUpgradeCommand(program);
3619
+ registerAuditCommand(program);
3599
3620
  program.exitOverride();
3600
3621
  async function main() {
3601
3622
  const updateHint = printUpdateHint();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caik.dev/cli",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "description": "CAIK CLI — Search, install, and publish AI artifacts from your terminal",
6
6
  "keywords": [
@@ -34,7 +34,6 @@
34
34
  "typecheck": "tsc --noEmit"
35
35
  },
36
36
  "dependencies": {
37
- "@inquirer/prompts": "^8.3.2",
38
37
  "chalk": "^5.6.2",
39
38
  "cli-table3": "^0.6.5",
40
39
  "commander": "^14.0.3",