clivly 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Clivly
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,16 @@
1
+ # clivly
2
+
3
+ Scaffold [Clivly](https://clivly.com) — the CRM that lives inside your app.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ npx clivly init # detect your stack and print the scaffold plan
9
+ npx clivly init --write # write clivly.config.ts
10
+ ```
11
+
12
+ `clivly init` detects your framework (Next.js, TanStack Start, SvelteKit, Remix, Nuxt) and ORM (Drizzle, Prisma, Kysely) from `package.json`, then reports the routes and schema fragment it will scaffold.
13
+
14
+ ## License
15
+
16
+ MIT
package/dist/index.mjs ADDED
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ //#region src/detect.ts
5
+ const FRAMEWORK_MARKERS = [
6
+ {
7
+ framework: "nextjs",
8
+ deps: ["next"]
9
+ },
10
+ {
11
+ framework: "tanstack-start",
12
+ deps: ["@tanstack/react-start"]
13
+ },
14
+ {
15
+ framework: "sveltekit",
16
+ deps: ["@sveltejs/kit"]
17
+ },
18
+ {
19
+ framework: "remix",
20
+ deps: ["@remix-run/react", "@remix-run/node"]
21
+ },
22
+ {
23
+ framework: "nuxt",
24
+ deps: ["nuxt"]
25
+ }
26
+ ];
27
+ const ORM_MARKERS = [
28
+ {
29
+ orm: "drizzle",
30
+ deps: ["drizzle-orm"]
31
+ },
32
+ {
33
+ orm: "prisma",
34
+ deps: ["@prisma/client", "prisma"]
35
+ },
36
+ {
37
+ orm: "kysely",
38
+ deps: ["kysely"]
39
+ }
40
+ ];
41
+ const FRAMEWORK_LABELS = {
42
+ nextjs: "Next.js",
43
+ "tanstack-start": "TanStack Start",
44
+ sveltekit: "SvelteKit",
45
+ remix: "Remix",
46
+ nuxt: "Nuxt",
47
+ unknown: "Unknown"
48
+ };
49
+ function detectFramework(deps) {
50
+ for (const { framework, deps: markers } of FRAMEWORK_MARKERS) if (markers.some((dep) => dep in deps)) return framework;
51
+ return "unknown";
52
+ }
53
+ function detectOrm(deps) {
54
+ for (const { orm, deps: markers } of ORM_MARKERS) if (markers.some((dep) => dep in deps)) return orm;
55
+ return "unknown";
56
+ }
57
+ //#endregion
58
+ //#region src/scaffold.ts
59
+ const FRAMEWORK_ROUTES = {
60
+ nextjs: [{
61
+ path: "app/crm/[[...slug]]/page.tsx",
62
+ description: "Mounts the Clivly SPA at /crm",
63
+ owned: true
64
+ }, {
65
+ path: "app/api/clivly/[...path]/route.ts",
66
+ description: "Clivly API handler",
67
+ owned: true
68
+ }],
69
+ "tanstack-start": [{
70
+ path: "src/routes/crm/$.tsx",
71
+ description: "Mounts the Clivly SPA at /crm",
72
+ owned: true
73
+ }, {
74
+ path: "src/routes/api/clivly/$.ts",
75
+ description: "Clivly API handler",
76
+ owned: true
77
+ }],
78
+ sveltekit: [{
79
+ path: "src/routes/crm/[...slug]/+page.svelte",
80
+ description: "Mounts the Clivly SPA at /crm",
81
+ owned: true
82
+ }, {
83
+ path: "src/routes/api/clivly/[...path]/+server.ts",
84
+ description: "Clivly API handler",
85
+ owned: true
86
+ }],
87
+ remix: [{
88
+ path: "app/routes/crm.$.tsx",
89
+ description: "Mounts the Clivly SPA at /crm",
90
+ owned: true
91
+ }, {
92
+ path: "app/routes/api.clivly.$.ts",
93
+ description: "Clivly API handler",
94
+ owned: true
95
+ }],
96
+ nuxt: [{
97
+ path: "pages/crm/[...slug].vue",
98
+ description: "Mounts the Clivly SPA at /crm",
99
+ owned: true
100
+ }, {
101
+ path: "server/api/clivly/[...path].ts",
102
+ description: "Clivly API handler",
103
+ owned: true
104
+ }],
105
+ unknown: []
106
+ };
107
+ const COMMON_FILES = [{
108
+ path: "drizzle/schema/clivly.ts",
109
+ description: "crm_* schema fragment — merge into your schema",
110
+ owned: false
111
+ }, {
112
+ path: "clivly.config.ts",
113
+ description: "Your Clivly configuration",
114
+ owned: false
115
+ }];
116
+ function planScaffold(framework) {
117
+ return [...FRAMEWORK_ROUTES[framework] ?? [], ...COMMON_FILES];
118
+ }
119
+ function renderConfigTemplate(contactEntity) {
120
+ return `import { defineClivlyConfig } from "clivly";
121
+
122
+ export default defineClivlyConfig({
123
+ // The host table Clivly treats as the contact entity.
124
+ contactEntity: "${contactEntity}",
125
+ contactFields: ["id", "email", "name", "created_at"],
126
+ });
127
+ `;
128
+ }
129
+ //#endregion
130
+ //#region src/index.ts
131
+ function readDeps(cwd) {
132
+ const pkgPath = join(cwd, "package.json");
133
+ if (!existsSync(pkgPath)) return {};
134
+ try {
135
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
136
+ return {
137
+ ...pkg.dependencies,
138
+ ...pkg.devDependencies
139
+ };
140
+ } catch {
141
+ return {};
142
+ }
143
+ }
144
+ function runInit(cwd, write) {
145
+ const deps = readDeps(cwd);
146
+ if (Object.keys(deps).length === 0) {
147
+ process.stdout.write("No package.json found here. Run `clivly init` from your app root.\n");
148
+ return 1;
149
+ }
150
+ const framework = detectFramework(deps);
151
+ const orm = detectOrm(deps);
152
+ process.stdout.write("\nClivly init\n");
153
+ process.stdout.write(` Framework: ${FRAMEWORK_LABELS[framework]}\n`);
154
+ process.stdout.write(` ORM: ${orm}\n\n`);
155
+ if (framework === "unknown") {
156
+ process.stdout.write("Could not detect a supported framework. Supported: Next.js, TanStack Start, SvelteKit, Remix, Nuxt.\n");
157
+ return 1;
158
+ }
159
+ const files = planScaffold(framework);
160
+ process.stdout.write("Will scaffold:\n");
161
+ for (const file of files) {
162
+ const tag = file.owned ? "generated" : "yours";
163
+ process.stdout.write(` ${file.path} (${tag}) — ${file.description}\n`);
164
+ }
165
+ process.stdout.write("\n");
166
+ if (write) {
167
+ const configPath = join(cwd, "clivly.config.ts");
168
+ if (existsSync(configPath)) process.stdout.write("clivly.config.ts already exists — skipped.\n");
169
+ else {
170
+ writeFileSync(configPath, renderConfigTemplate("users"), "utf8");
171
+ process.stdout.write("Created clivly.config.ts\n");
172
+ }
173
+ } else process.stdout.write("Dry run. Re-run with --write to scaffold.\n");
174
+ return 0;
175
+ }
176
+ function main(argv) {
177
+ const [command, ...rest] = argv;
178
+ const write = rest.includes("--write");
179
+ switch (command) {
180
+ case "init": return runInit(process.cwd(), write);
181
+ case void 0:
182
+ case "help":
183
+ case "--help":
184
+ process.stdout.write("clivly — embed a CRM in your app\n\nUsage:\n clivly init [--write] Detect your stack and scaffold Clivly\n");
185
+ return 0;
186
+ default:
187
+ process.stdout.write(`Unknown command: ${command}\n`);
188
+ return 1;
189
+ }
190
+ }
191
+ process.exitCode = main(process.argv.slice(2));
192
+ //#endregion
193
+ export {};
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "clivly",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "description": "Clivly CLI — scaffold a CRM into your app (clivly init).",
7
+ "license": "MIT",
8
+ "bin": {
9
+ "clivly": "./dist/index.mjs"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/index.mjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^25.9.1",
24
+ "tsdown": "^0.21.9",
25
+ "typescript": "^6",
26
+ "@clivly.com/config": "0.0.0"
27
+ },
28
+ "scripts": {
29
+ "build": "tsdown",
30
+ "check-types": "tsc --noEmit"
31
+ },
32
+ "main": "./dist/index.mjs"
33
+ }