clivly 0.4.0 → 0.4.1

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.
package/README.md CHANGED
@@ -40,9 +40,10 @@ import { createWorkOSAuthAdapter } from "clivly/auth/workos";
40
40
  import { createBetterAuthAdapter } from "clivly/auth/better-auth";
41
41
  ```
42
42
 
43
- Provider and framework integrations use optional peer dependencies. Install only
44
- what your app actually uses, such as `drizzle-orm`, `vite`, `@clerk/backend`, or
45
- `@workos-inc/node`.
43
+ Provider and framework integrations use optional peer dependencies.
44
+ **Supported auth providers: Clerk, WorkOS, and BetterAuth.** Install only
45
+ what your app actually uses, such as `drizzle-orm`, `vite`, `@clerk/backend`,
46
+ `@workos-inc/node`, or `better-auth`.
46
47
 
47
48
  `clivly init` detects your framework from `package.json`, locates your Drizzle
48
49
  schema, and generates a `clivly.config.ts` that maps likely contact and company
package/index.cjs CHANGED
@@ -1389,9 +1389,10 @@ function parentDir(dir) {
1389
1389
  const parent = (0, node_path.dirname)(dir);
1390
1390
  return parent === dir ? null : parent;
1391
1391
  }
1392
- function findNearestPackageDir(start) {
1392
+ function findNearestPackageDir(start, boundary) {
1393
1393
  let current = (0, node_path.resolve)(start);
1394
1394
  while (true) {
1395
+ if (current === boundary) return null;
1395
1396
  if ((0, node_fs.existsSync)((0, node_path.join)(current, "package.json"))) return current;
1396
1397
  const parent = parentDir(current);
1397
1398
  if (!parent) return null;
@@ -1423,9 +1424,10 @@ function expandWorkspaceGlob(root, pattern) {
1423
1424
  const exact = (0, node_path.join)(root, pattern);
1424
1425
  return (0, node_fs.existsSync)((0, node_path.join)(exact, "package.json")) ? [exact] : [];
1425
1426
  }
1426
- function findWorkspaceRoot(start) {
1427
+ function findWorkspaceRoot(start, boundary) {
1427
1428
  let current = (0, node_path.resolve)(start);
1428
1429
  while (true) {
1430
+ if (current === boundary) return null;
1429
1431
  if (readWorkspaceGlobs(current).length > 0) return current;
1430
1432
  const parent = parentDir(current);
1431
1433
  if (!parent) return null;
@@ -1444,13 +1446,14 @@ function rankCandidates(paths, start) {
1444
1446
  return distance(a) - distance(b) || a.localeCompare(b);
1445
1447
  });
1446
1448
  }
1447
- function resolveTargetApp(start) {
1448
- const nearestPackageDir = findNearestPackageDir(start);
1449
+ function resolveTargetApp(start, boundary = (0, node_os.homedir)()) {
1450
+ const stop = (0, node_path.resolve)(boundary);
1451
+ const nearestPackageDir = findNearestPackageDir(start, stop);
1449
1452
  if (nearestPackageDir && isDrizzleApp(nearestPackageDir)) return {
1450
1453
  cwd: nearestPackageDir,
1451
1454
  reason: "direct"
1452
1455
  };
1453
- const workspaceRoot = findWorkspaceRoot(start);
1456
+ const workspaceRoot = findWorkspaceRoot(start, stop);
1454
1457
  if (!workspaceRoot) return {
1455
1458
  cwd: nearestPackageDir ?? (0, node_path.resolve)(start),
1456
1459
  reason: "direct"
@@ -1710,6 +1713,40 @@ function flagValue(argv, flag) {
1710
1713
  const index = argv.indexOf(flag);
1711
1714
  return index >= 0 ? argv[index + 1] : void 0;
1712
1715
  }
1716
+ const KNOWN_COMMANDS = [
1717
+ "init",
1718
+ "status",
1719
+ "push-schema",
1720
+ "dev",
1721
+ "login",
1722
+ "help"
1723
+ ];
1724
+ function editDistance(a, b) {
1725
+ const m = a.length;
1726
+ const n = b.length;
1727
+ const prev = Array.from({ length: n + 1 }, (_, i) => i);
1728
+ const curr = Array.from({ length: n + 1 }, () => 0);
1729
+ for (let i = 1; i <= m; i++) {
1730
+ curr[0] = i;
1731
+ for (let j = 1; j <= n; j++) {
1732
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
1733
+ curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
1734
+ }
1735
+ for (let j = 0; j <= n; j++) prev[j] = curr[j];
1736
+ }
1737
+ return curr[n];
1738
+ }
1739
+ function nearestCommand(input) {
1740
+ let best;
1741
+ for (const command of KNOWN_COMMANDS) {
1742
+ const distance = editDistance(input, command);
1743
+ if (distance <= 2 && (!best || distance < best.distance)) best = {
1744
+ command,
1745
+ distance
1746
+ };
1747
+ }
1748
+ return best?.command;
1749
+ }
1713
1750
  function readDeps(cwd) {
1714
1751
  const pkgPath = (0, node_path.join)(cwd, "package.json");
1715
1752
  if (!(0, node_fs.existsSync)(pkgPath)) return {};
@@ -1780,7 +1817,7 @@ async function acquireSchema(options, deps) {
1780
1817
  const { out } = deps;
1781
1818
  const appDeps = deps.readDepsFn(options.cwd);
1782
1819
  if (Object.keys(appDeps).length === 0) {
1783
- out("No package.json found here. Run `clivly init` from your app root.\n");
1820
+ out(`No package.json found in ${options.cwd}. Run \`clivly init\` from inside an existing app — cd into your project first.\n`);
1784
1821
  return { code: 1 };
1785
1822
  }
1786
1823
  const framework = detectFramework(appDeps);
@@ -2166,19 +2203,29 @@ async function main(argv) {
2166
2203
  openBrowser: !rest.includes("--no-browser"),
2167
2204
  deps: defaultLoginDeps()
2168
2205
  });
2206
+ case "--version":
2207
+ case "-v":
2208
+ process.stdout.write(`${readOwnVersion() ?? "unknown"}\n`);
2209
+ return 0;
2169
2210
  case void 0:
2170
2211
  case "help":
2171
2212
  case "--help":
2172
- process.stdout.write("clivly — embed a CRM in your app\n\nUsage:\n clivly init [--force] [--dry-run] [--schema <path>] [--no-login] [--no-browser] Scaffold Clivly (config, routes, .env) for your app\n clivly status [--verbose] [--config <path>] Check your setup, step by step\n clivly push-schema [--config <path>] Report your schema to Clivly without deploying\n clivly dev [--port <n>] [--api-url <url>] [--route-path <path>] [-- <cmd>] Tunnel localhost for remote sync\n clivly login [--force] [--rotate] [--api-url <url>] [--no-browser] Pair this machine and write CLIVLY_API_KEY / CLIVLY_SYNC_TRIGGER_SECRET to .env\n");
2213
+ process.stdout.write("clivly — embed a CRM in your app\n\nUsage:\n clivly init [--force] [--dry-run] [--schema <path>] [--no-login] [--no-browser] Scaffold Clivly (config, routes, .env) for your app\n clivly status [--verbose] [--config <path>] Check your setup, step by step\n clivly push-schema [--config <path>] Report your schema to Clivly without deploying\n clivly dev [--port <n>] [--api-url <url>] [--route-path <path>] [-- <cmd>] Tunnel localhost for remote sync\n clivly login [--force] [--rotate] [--api-url <url>] [--no-browser] Pair this machine and write CLIVLY_API_KEY / CLIVLY_SYNC_TRIGGER_SECRET to .env\n clivly --version Print the installed clivly version\n");
2173
2214
  return 0;
2174
- default:
2215
+ default: {
2175
2216
  process.stdout.write(`Unknown command: ${command}\n`);
2217
+ const suggestion = nearestCommand(command);
2218
+ if (suggestion) process.stdout.write(`Did you mean \`clivly ${suggestion}\`?\n`);
2219
+ process.stdout.write("Run `clivly --help` to see available commands.\n");
2176
2220
  return 1;
2221
+ }
2177
2222
  }
2178
2223
  }
2179
2224
  if (isEntrypoint(process.argv[1], require("url").pathToFileURL(__filename).href)) main(process.argv.slice(2)).then((code) => {
2180
2225
  process.exitCode = code;
2181
2226
  });
2182
2227
  //#endregion
2228
+ exports.main = main;
2229
+ exports.nearestCommand = nearestCommand;
2183
2230
  exports.runInit = runInit;
2184
2231
  exports.runInitFromArgv = runInitFromArgv;
package/index.d.cts CHANGED
@@ -36,6 +36,7 @@ interface ProbeHit {
36
36
  }
37
37
  //#endregion
38
38
  //#region src/index.d.ts
39
+ declare function nearestCommand(input: string): string | undefined;
39
40
  interface InstallResult {
40
41
  command: string;
41
42
  ok: boolean;
@@ -73,5 +74,6 @@ interface RunInitOptions {
73
74
  }
74
75
  declare function runInit(options: RunInitOptions): Promise<number>;
75
76
  declare function runInitFromArgv(argv: string[], cwd: string): Promise<number>;
77
+ declare function main(argv: string[]): Promise<number>;
76
78
  //#endregion
77
- export { RunInitOptions, runInit, runInitFromArgv };
79
+ export { RunInitOptions, main, nearestCommand, runInit, runInitFromArgv };
package/index.d.mts CHANGED
@@ -36,6 +36,7 @@ interface ProbeHit {
36
36
  }
37
37
  //#endregion
38
38
  //#region src/index.d.ts
39
+ declare function nearestCommand(input: string): string | undefined;
39
40
  interface InstallResult {
40
41
  command: string;
41
42
  ok: boolean;
@@ -73,5 +74,6 @@ interface RunInitOptions {
73
74
  }
74
75
  declare function runInit(options: RunInitOptions): Promise<number>;
75
76
  declare function runInitFromArgv(argv: string[], cwd: string): Promise<number>;
77
+ declare function main(argv: string[]): Promise<number>;
76
78
  //#endregion
77
- export { RunInitOptions, runInit, runInitFromArgv };
79
+ export { RunInitOptions, main, nearestCommand, runInit, runInitFromArgv };
package/index.mjs CHANGED
@@ -6,7 +6,7 @@ import { t as createClivlySDK } from "./sdk-DdzBxgMx.mjs";
6
6
  import { createRequire } from "node:module";
7
7
  import { spawn } from "node:child_process";
8
8
  import { accessSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync, writeFileSync } from "node:fs";
9
- import { hostname } from "node:os";
9
+ import { homedir, hostname } from "node:os";
10
10
  import { delimiter, dirname, isAbsolute, join, relative, resolve } from "node:path";
11
11
  import { createInterface } from "node:readline/promises";
12
12
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -1388,9 +1388,10 @@ function parentDir(dir) {
1388
1388
  const parent = dirname(dir);
1389
1389
  return parent === dir ? null : parent;
1390
1390
  }
1391
- function findNearestPackageDir(start) {
1391
+ function findNearestPackageDir(start, boundary) {
1392
1392
  let current = resolve(start);
1393
1393
  while (true) {
1394
+ if (current === boundary) return null;
1394
1395
  if (existsSync(join(current, "package.json"))) return current;
1395
1396
  const parent = parentDir(current);
1396
1397
  if (!parent) return null;
@@ -1422,9 +1423,10 @@ function expandWorkspaceGlob(root, pattern) {
1422
1423
  const exact = join(root, pattern);
1423
1424
  return existsSync(join(exact, "package.json")) ? [exact] : [];
1424
1425
  }
1425
- function findWorkspaceRoot(start) {
1426
+ function findWorkspaceRoot(start, boundary) {
1426
1427
  let current = resolve(start);
1427
1428
  while (true) {
1429
+ if (current === boundary) return null;
1428
1430
  if (readWorkspaceGlobs(current).length > 0) return current;
1429
1431
  const parent = parentDir(current);
1430
1432
  if (!parent) return null;
@@ -1443,13 +1445,14 @@ function rankCandidates(paths, start) {
1443
1445
  return distance(a) - distance(b) || a.localeCompare(b);
1444
1446
  });
1445
1447
  }
1446
- function resolveTargetApp(start) {
1447
- const nearestPackageDir = findNearestPackageDir(start);
1448
+ function resolveTargetApp(start, boundary = homedir()) {
1449
+ const stop = resolve(boundary);
1450
+ const nearestPackageDir = findNearestPackageDir(start, stop);
1448
1451
  if (nearestPackageDir && isDrizzleApp(nearestPackageDir)) return {
1449
1452
  cwd: nearestPackageDir,
1450
1453
  reason: "direct"
1451
1454
  };
1452
- const workspaceRoot = findWorkspaceRoot(start);
1455
+ const workspaceRoot = findWorkspaceRoot(start, stop);
1453
1456
  if (!workspaceRoot) return {
1454
1457
  cwd: nearestPackageDir ?? resolve(start),
1455
1458
  reason: "direct"
@@ -1709,6 +1712,40 @@ function flagValue(argv, flag) {
1709
1712
  const index = argv.indexOf(flag);
1710
1713
  return index >= 0 ? argv[index + 1] : void 0;
1711
1714
  }
1715
+ const KNOWN_COMMANDS = [
1716
+ "init",
1717
+ "status",
1718
+ "push-schema",
1719
+ "dev",
1720
+ "login",
1721
+ "help"
1722
+ ];
1723
+ function editDistance(a, b) {
1724
+ const m = a.length;
1725
+ const n = b.length;
1726
+ const prev = Array.from({ length: n + 1 }, (_, i) => i);
1727
+ const curr = Array.from({ length: n + 1 }, () => 0);
1728
+ for (let i = 1; i <= m; i++) {
1729
+ curr[0] = i;
1730
+ for (let j = 1; j <= n; j++) {
1731
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
1732
+ curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
1733
+ }
1734
+ for (let j = 0; j <= n; j++) prev[j] = curr[j];
1735
+ }
1736
+ return curr[n];
1737
+ }
1738
+ function nearestCommand(input) {
1739
+ let best;
1740
+ for (const command of KNOWN_COMMANDS) {
1741
+ const distance = editDistance(input, command);
1742
+ if (distance <= 2 && (!best || distance < best.distance)) best = {
1743
+ command,
1744
+ distance
1745
+ };
1746
+ }
1747
+ return best?.command;
1748
+ }
1712
1749
  function readDeps(cwd) {
1713
1750
  const pkgPath = join(cwd, "package.json");
1714
1751
  if (!existsSync(pkgPath)) return {};
@@ -1779,7 +1816,7 @@ async function acquireSchema(options, deps) {
1779
1816
  const { out } = deps;
1780
1817
  const appDeps = deps.readDepsFn(options.cwd);
1781
1818
  if (Object.keys(appDeps).length === 0) {
1782
- out("No package.json found here. Run `clivly init` from your app root.\n");
1819
+ out(`No package.json found in ${options.cwd}. Run \`clivly init\` from inside an existing app — cd into your project first.\n`);
1783
1820
  return { code: 1 };
1784
1821
  }
1785
1822
  const framework = detectFramework(appDeps);
@@ -2165,18 +2202,26 @@ async function main(argv) {
2165
2202
  openBrowser: !rest.includes("--no-browser"),
2166
2203
  deps: defaultLoginDeps()
2167
2204
  });
2205
+ case "--version":
2206
+ case "-v":
2207
+ process.stdout.write(`${readOwnVersion() ?? "unknown"}\n`);
2208
+ return 0;
2168
2209
  case void 0:
2169
2210
  case "help":
2170
2211
  case "--help":
2171
- process.stdout.write("clivly — embed a CRM in your app\n\nUsage:\n clivly init [--force] [--dry-run] [--schema <path>] [--no-login] [--no-browser] Scaffold Clivly (config, routes, .env) for your app\n clivly status [--verbose] [--config <path>] Check your setup, step by step\n clivly push-schema [--config <path>] Report your schema to Clivly without deploying\n clivly dev [--port <n>] [--api-url <url>] [--route-path <path>] [-- <cmd>] Tunnel localhost for remote sync\n clivly login [--force] [--rotate] [--api-url <url>] [--no-browser] Pair this machine and write CLIVLY_API_KEY / CLIVLY_SYNC_TRIGGER_SECRET to .env\n");
2212
+ process.stdout.write("clivly — embed a CRM in your app\n\nUsage:\n clivly init [--force] [--dry-run] [--schema <path>] [--no-login] [--no-browser] Scaffold Clivly (config, routes, .env) for your app\n clivly status [--verbose] [--config <path>] Check your setup, step by step\n clivly push-schema [--config <path>] Report your schema to Clivly without deploying\n clivly dev [--port <n>] [--api-url <url>] [--route-path <path>] [-- <cmd>] Tunnel localhost for remote sync\n clivly login [--force] [--rotate] [--api-url <url>] [--no-browser] Pair this machine and write CLIVLY_API_KEY / CLIVLY_SYNC_TRIGGER_SECRET to .env\n clivly --version Print the installed clivly version\n");
2172
2213
  return 0;
2173
- default:
2214
+ default: {
2174
2215
  process.stdout.write(`Unknown command: ${command}\n`);
2216
+ const suggestion = nearestCommand(command);
2217
+ if (suggestion) process.stdout.write(`Did you mean \`clivly ${suggestion}\`?\n`);
2218
+ process.stdout.write("Run `clivly --help` to see available commands.\n");
2175
2219
  return 1;
2220
+ }
2176
2221
  }
2177
2222
  }
2178
2223
  if (isEntrypoint(process.argv[1], import.meta.url)) main(process.argv.slice(2)).then((code) => {
2179
2224
  process.exitCode = code;
2180
2225
  });
2181
2226
  //#endregion
2182
- export { runInit, runInitFromArgv };
2227
+ export { main, nearestCommand, runInit, runInitFromArgv };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clivly",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Clivly CLI — scaffold a CRM into your app (clivly init).",
@@ -210,7 +210,8 @@
210
210
  "types": "./auth-better-auth.d.cts",
211
211
  "default": "./auth-better-auth.cjs"
212
212
  }
213
- }
213
+ },
214
+ "./package.json": "./package.json"
214
215
  },
215
216
  "dependencies": {
216
217
  "jiti": "^2.7.0"
@@ -218,6 +219,7 @@
218
219
  "peerDependencies": {
219
220
  "@clerk/backend": ">=1",
220
221
  "@workos-inc/node": ">=7",
222
+ "better-auth": ">=1",
221
223
  "drizzle-orm": ">=0.30",
222
224
  "vite": ">=5"
223
225
  },
@@ -228,6 +230,9 @@
228
230
  "@workos-inc/node": {
229
231
  "optional": true
230
232
  },
233
+ "better-auth": {
234
+ "optional": true
235
+ },
231
236
  "drizzle-orm": {
232
237
  "optional": true
233
238
  },