@apart-tech/apart-intelligence 1.0.5 → 1.0.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.
@@ -5,7 +5,7 @@ import { randomBytes } from "crypto";
5
5
  import chalk from "chalk";
6
6
  import prompts from "prompts";
7
7
  import { PrismaClient } from "@prisma/client";
8
- import { withAccelerate } from "@prisma/extension-accelerate";
8
+ import { saveCredentials } from "../api-client/index.js";
9
9
  const LOGO = `
10
10
  ╔══════════════════════════════════════╗
11
11
  ║ ${chalk.bold("Apart Intelligence")} ║
@@ -33,304 +33,398 @@ export function initCommand(program) {
33
33
  process.exit(0);
34
34
  }
35
35
  }
36
- // ── Step 1: Database ────────────────────────────────────────────
37
- console.log(chalk.bold("\n1. Database\n"));
38
- const { dbChoice } = await prompts({
36
+ // ── Step 1: Choose setup mode ─────────────────────────────────
37
+ console.log(chalk.bold("\n1. Setup\n"));
38
+ const { setupMode } = await prompts({
39
39
  type: "select",
40
- name: "dbChoice",
41
- message: "How would you like to connect to a database?",
40
+ name: "setupMode",
41
+ message: "How would you like to get started?",
42
42
  choices: [
43
43
  {
44
- title: "Bring your own PostgreSQL",
45
- description: "Connect to an existing PostgreSQL instance with pgvector",
46
- value: "byod",
44
+ title: "Cloud",
45
+ description: "Connect to Apart Intelligence cloud",
46
+ value: "cloud",
47
47
  },
48
48
  {
49
- title: "Prisma Postgres (cloud)",
50
- description: "Use a Prisma Postgres connection string",
51
- value: "prisma",
49
+ title: "Self-hosted",
50
+ description: "Connect your own PostgreSQL database",
51
+ value: "self-hosted",
52
52
  },
53
53
  ],
54
54
  });
55
- if (dbChoice === undefined)
55
+ if (setupMode === undefined)
56
56
  abort();
57
- let databaseUrl;
58
- if (dbChoice === "prisma") {
59
- const { url } = await prompts({
60
- type: "text",
61
- name: "url",
62
- message: "Prisma Postgres connection string:",
63
- validate: (v) => v.startsWith("prisma+postgres://") || v.startsWith("prisma://")
64
- ? true
65
- : "Must start with prisma+postgres:// or prisma://",
66
- });
67
- if (!url)
68
- abort();
69
- databaseUrl = url;
57
+ if (setupMode === "cloud") {
58
+ // ── Cloud flow ────────────────────────────────────────────────
59
+ await initCloud(configPath);
70
60
  }
71
61
  else {
72
- const { url } = await prompts({
73
- type: "text",
74
- name: "url",
75
- message: "PostgreSQL connection string:",
76
- initial: "postgresql://localhost:5432/apart",
77
- validate: (v) => v.startsWith("postgresql://") || v.startsWith("postgres://")
78
- ? true
79
- : "Must start with postgresql:// or postgres://",
80
- });
81
- if (!url)
82
- abort();
83
- databaseUrl = url;
62
+ // ── Self-hosted flow ──────────────────────────────────────────
63
+ await initSelfHosted(configPath);
84
64
  }
85
- // ── Step 2: Test connection & set up schema ─────────────────────
86
- console.log(chalk.dim("\nTesting database connection..."));
87
- let db;
88
- try {
89
- if (databaseUrl.startsWith("prisma+postgres://") || databaseUrl.startsWith("prisma://")) {
90
- process.env.DATABASE_URL = databaseUrl;
91
- db = new PrismaClient().$extends(withAccelerate());
92
- }
93
- else {
94
- db = new PrismaClient({ datasourceUrl: databaseUrl });
95
- }
96
- await db.$connect();
97
- console.log(chalk.green(" ✓ Connected"));
65
+ process.exit(0);
66
+ });
67
+ }
68
+ function abort() {
69
+ console.log(chalk.dim("\nAborted."));
70
+ process.exit(0);
71
+ }
72
+ function findPrismaSchema() {
73
+ const candidates = [
74
+ join(process.cwd(), "packages", "core"),
75
+ join(process.cwd(), "node_modules", "@apart-tech", "intelligence-core"),
76
+ resolve(new URL(".", import.meta.url).pathname, "..", "..", ".."),
77
+ ];
78
+ for (const dir of candidates) {
79
+ if (existsSync(join(dir, "prisma", "schema.prisma"))) {
80
+ return dir;
98
81
  }
99
- catch (err) {
100
- console.error(chalk.red(` ✗ Connection failed: ${err.message}`));
101
- console.log(chalk.dim(" Check your connection string and try again."));
82
+ }
83
+ const pkgDir = resolve(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "core");
84
+ if (existsSync(join(pkgDir, "prisma", "schema.prisma"))) {
85
+ return pkgDir;
86
+ }
87
+ throw new Error("Could not find Prisma schema. Make sure @apart-tech/intelligence-core is installed.");
88
+ }
89
+ // ── Cloud flow ────────────────────────────────────────────────────────
90
+ const DEFAULT_SERVER = "https://intelligence-api-prod-959271008232.europe-west3.run.app";
91
+ async function initCloud(configPath) {
92
+ // ── Server ────────────────────────────────────────────────────────
93
+ console.log(chalk.bold("\n2. Connect to Apart Intelligence\n"));
94
+ const { server } = await prompts({
95
+ type: "text",
96
+ name: "server",
97
+ message: "Server URL:",
98
+ initial: DEFAULT_SERVER,
99
+ });
100
+ if (!server)
101
+ abort();
102
+ const serverUrl = server.replace(/\/$/, "");
103
+ // Validate server
104
+ console.log(chalk.dim("\nConnecting to server..."));
105
+ try {
106
+ const healthRes = await fetch(`${serverUrl}/api/health`);
107
+ if (!healthRes.ok) {
108
+ console.error(chalk.red(` ✗ Server not reachable: ${serverUrl}`));
102
109
  process.exit(1);
103
110
  }
104
- // Push schema
105
- console.log(chalk.dim("Setting up database schema..."));
106
- try {
107
- const prismaSchemaDir = findPrismaSchema();
108
- execSync(`npx prisma db push --skip-generate --accept-data-loss`, {
109
- cwd: prismaSchemaDir,
110
- stdio: "pipe",
111
- env: { ...process.env, DATABASE_URL: databaseUrl },
112
- });
113
- console.log(chalk.green(" ✓ Schema synced"));
114
- }
115
- catch (err) {
116
- if (err.stderr?.toString().includes('type "vector" does not exist')) {
117
- console.log(chalk.dim(" Enabling pgvector extension..."));
118
- try {
119
- const prismaSchemaDir = findPrismaSchema();
120
- execSync(`echo "CREATE EXTENSION IF NOT EXISTS vector;" | npx prisma db execute --schema prisma/schema.prisma --stdin`, {
121
- cwd: prismaSchemaDir,
122
- stdio: "pipe",
123
- env: { ...process.env, DATABASE_URL: databaseUrl },
124
- });
125
- execSync(`npx prisma db push --skip-generate --accept-data-loss`, {
126
- cwd: prismaSchemaDir,
127
- stdio: "pipe",
128
- env: { ...process.env, DATABASE_URL: databaseUrl },
129
- });
130
- console.log(chalk.green(" ✓ pgvector enabled & schema synced"));
131
- }
132
- catch (innerErr) {
133
- console.error(chalk.red(` ✗ Schema push failed: ${innerErr.message}`));
134
- console.log(chalk.dim(" You may need to manually enable pgvector: CREATE EXTENSION IF NOT EXISTS vector;"));
135
- process.exit(1);
136
- }
137
- }
138
- else {
139
- console.error(chalk.red(` ✗ Schema push failed`));
140
- console.log(chalk.dim(err.stderr?.toString().slice(0, 300) ?? err.message));
141
- process.exit(1);
142
- }
143
- }
144
- // ── Step 3: Organization ────────────────────────────────────────
145
- console.log(chalk.bold("\n2. Organization\n"));
146
- const { orgName } = await prompts({
147
- type: "text",
148
- name: "orgName",
149
- message: "Organization name:",
150
- initial: "My Company",
111
+ console.log(chalk.green(" ✓ Server reachable"));
112
+ }
113
+ catch (err) {
114
+ console.error(chalk.red(` ✗ Cannot connect to server: ${err.message}`));
115
+ process.exit(1);
116
+ }
117
+ // ── Organization ──────────────────────────────────────────────────
118
+ console.log(chalk.bold("\n3. Organization\n"));
119
+ const { orgName } = await prompts({
120
+ type: "text",
121
+ name: "orgName",
122
+ message: "Organization name:",
123
+ initial: "My Company",
124
+ });
125
+ if (!orgName)
126
+ abort();
127
+ console.log(chalk.dim("Creating organization..."));
128
+ let apiKey;
129
+ try {
130
+ const res = await fetch(`${serverUrl}/api/register`, {
131
+ method: "POST",
132
+ headers: { "Content-Type": "application/json" },
133
+ body: JSON.stringify({ name: orgName }),
151
134
  });
152
- if (!orgName)
153
- abort();
154
- const orgSlug = orgName
155
- .toLowerCase()
156
- .replace(/[^a-z0-9]+/g, "-")
157
- .replace(/^-|-$/g, "");
158
- // Reconnect with fresh client after schema push
159
- if (databaseUrl.startsWith("prisma+postgres://") || databaseUrl.startsWith("prisma://")) {
160
- process.env.DATABASE_URL = databaseUrl;
161
- db = new PrismaClient().$extends(withAccelerate());
135
+ if (res.status === 409) {
136
+ console.error(chalk.red(` ✗ Organization "${orgName}" already exists`));
137
+ process.exit(1);
162
138
  }
163
- else {
164
- db = new PrismaClient({ datasourceUrl: databaseUrl });
139
+ if (!res.ok) {
140
+ const err = await res.json().catch(() => ({}));
141
+ console.error(chalk.red(` ✗ Registration failed: ${err.error || `HTTP ${res.status}`}`));
142
+ process.exit(1);
165
143
  }
166
- let org;
144
+ const result = await res.json();
145
+ apiKey = result.apiKey;
146
+ console.log(chalk.green(` ✓ Organization created: ${result.organization.name}`));
147
+ console.log(chalk.bold(`\n API Key: ${apiKey}\n`));
148
+ console.log(chalk.yellow(" Save this key — it won't be shown again."));
149
+ }
150
+ catch (err) {
151
+ console.error(chalk.red(` ✗ Registration failed: ${err.message}`));
152
+ process.exit(1);
153
+ }
154
+ // Save credentials
155
+ saveCredentials({ server: serverUrl, apiKey });
156
+ console.log(chalk.green("\n ✓ Credentials saved"));
157
+ // ── Seed domains ──────────────────────────────────────────────────
158
+ console.log(chalk.bold("\n4. Knowledge Domains\n"));
159
+ const { seedDomains } = await prompts({
160
+ type: "confirm",
161
+ name: "seedDomains",
162
+ message: "Seed default domain taxonomy (strategy, people, products, etc.)?",
163
+ initial: true,
164
+ });
165
+ if (seedDomains) {
167
166
  try {
168
- org = await db.organization.findFirst({ where: { slug: orgSlug } });
169
- if (org) {
170
- console.log(chalk.yellow(` Organization "${orgName}" already exists (${org.id})`));
167
+ const seedRes = await fetch(`${serverUrl}/api/domains/seed`, {
168
+ method: "POST",
169
+ headers: { Authorization: `Bearer ${apiKey}` },
170
+ });
171
+ if (seedRes.ok) {
172
+ const result = await seedRes.json();
173
+ console.log(chalk.green(` ✓ ${result.created} domains seeded`));
171
174
  }
172
175
  else {
173
- org = await db.organization.create({
174
- data: { name: orgName, slug: orgSlug },
175
- });
176
- console.log(chalk.green(` ✓ Organization created: ${org.id}`));
176
+ console.error(chalk.red(" ✗ Domain seeding failed"));
177
177
  }
178
178
  }
179
179
  catch (err) {
180
- console.error(chalk.red(` ✗ Failed to create organization: ${err.message}`));
181
- process.exit(1);
180
+ console.error(chalk.red(` ✗ Domain seeding failed: ${err.message}`));
182
181
  }
183
- // ── Step 4: API Key ─────────────────────────────────────────────
184
- console.log(chalk.bold("\n3. API Key\n"));
185
- const { createApiKey } = await prompts({
186
- type: "confirm",
187
- name: "createApiKey",
188
- message: "Generate an API key for this organization?",
189
- initial: true,
182
+ }
183
+ // ── Write config ──────────────────────────────────────────────────
184
+ console.log(chalk.bold("\n5. Writing config\n"));
185
+ writeConfigFile(configPath);
186
+ // ── Done ──────────────────────────────────────────────────────────
187
+ console.log(chalk.bold("\n✓ Setup complete!\n"));
188
+ console.log(" Get started:");
189
+ console.log(chalk.cyan(" ai add --type note --title 'My first note' 'Hello from Apart Intelligence!'"));
190
+ console.log(chalk.cyan(" ai search 'hello'"));
191
+ console.log(chalk.cyan(" ai map . # map a codebase"));
192
+ console.log(chalk.cyan(" ai graph # visualize the graph"));
193
+ console.log();
194
+ }
195
+ // ── Self-hosted flow ──────────────────────────────────────────────────
196
+ async function initSelfHosted(configPath) {
197
+ // ── Database ──────────────────────────────────────────────────────
198
+ console.log(chalk.bold("\n2. Database\n"));
199
+ const { url } = await prompts({
200
+ type: "text",
201
+ name: "url",
202
+ message: "PostgreSQL connection string:",
203
+ initial: "postgresql://localhost:5432/apart",
204
+ validate: (v) => v.startsWith("postgresql://") || v.startsWith("postgres://")
205
+ ? true
206
+ : "Must start with postgresql:// or postgres://",
207
+ });
208
+ if (!url)
209
+ abort();
210
+ const databaseUrl = url;
211
+ // Test connection
212
+ console.log(chalk.dim("\nTesting database connection..."));
213
+ let db;
214
+ try {
215
+ db = new PrismaClient({ datasourceUrl: databaseUrl });
216
+ await db.$connect();
217
+ console.log(chalk.green(" ✓ Connected"));
218
+ }
219
+ catch (err) {
220
+ console.error(chalk.red(` ✗ Connection failed: ${err.message}`));
221
+ console.log(chalk.dim(" Check your connection string and try again."));
222
+ process.exit(1);
223
+ }
224
+ // Push schema
225
+ console.log(chalk.dim("Setting up database schema..."));
226
+ try {
227
+ const prismaSchemaDir = findPrismaSchema();
228
+ execSync(`npx prisma db push --skip-generate --accept-data-loss`, {
229
+ cwd: prismaSchemaDir,
230
+ stdio: "pipe",
231
+ env: { ...process.env, DATABASE_URL: databaseUrl },
190
232
  });
191
- let apiKeyValue = null;
192
- if (createApiKey) {
193
- apiKeyValue = `apart_${randomBytes(24).toString("hex")}`;
194
- const { createHash } = await import("crypto");
195
- const keyHash = createHash("sha256").update(apiKeyValue).digest("hex");
233
+ console.log(chalk.green(" ✓ Schema synced"));
234
+ }
235
+ catch (err) {
236
+ if (err.stderr?.toString().includes('type "vector" does not exist')) {
237
+ console.log(chalk.dim(" Enabling pgvector extension..."));
196
238
  try {
197
- await db.apiKey.create({
198
- data: {
199
- name: `${orgName} — CLI`,
200
- keyHash,
201
- createdBy: "ai-init",
202
- organizationId: org.id,
203
- },
239
+ const prismaSchemaDir = findPrismaSchema();
240
+ execSync(`echo "CREATE EXTENSION IF NOT EXISTS vector;" | npx prisma db execute --schema prisma/schema.prisma --stdin`, {
241
+ cwd: prismaSchemaDir,
242
+ stdio: "pipe",
243
+ env: { ...process.env, DATABASE_URL: databaseUrl },
244
+ });
245
+ execSync(`npx prisma db push --skip-generate --accept-data-loss`, {
246
+ cwd: prismaSchemaDir,
247
+ stdio: "pipe",
248
+ env: { ...process.env, DATABASE_URL: databaseUrl },
204
249
  });
205
- console.log(chalk.green(" ✓ API key created"));
206
- console.log(chalk.bold(`\n ${apiKeyValue}\n`));
207
- console.log(chalk.yellow(" Save this key — it won't be shown again."));
250
+ console.log(chalk.green(" ✓ pgvector enabled & schema synced"));
208
251
  }
209
- catch (err) {
210
- console.error(chalk.red(` ✗ Failed to create API key: ${err.message}`));
252
+ catch (innerErr) {
253
+ console.error(chalk.red(` ✗ Schema push failed: ${innerErr.message}`));
254
+ console.log(chalk.dim(" You may need to manually enable pgvector: CREATE EXTENSION IF NOT EXISTS vector;"));
255
+ process.exit(1);
211
256
  }
212
257
  }
213
- // ── Step 5: Embeddings ──────────────────────────────────────────
214
- console.log(chalk.bold("\n4. Embeddings\n"));
215
- const { openaiKey } = await prompts({
216
- type: "password",
217
- name: "openaiKey",
218
- message: "OpenAI API key (for embeddings):",
219
- validate: (v) => v.startsWith("sk-") ? true : "Must start with sk-",
220
- });
221
- if (!openaiKey)
222
- abort();
223
- // ── Step 6: Seed domains ────────────────────────────────────────
224
- console.log(chalk.bold("\n5. Knowledge Domains\n"));
225
- const { seedDomains } = await prompts({
226
- type: "confirm",
227
- name: "seedDomains",
228
- message: "Seed default domain taxonomy (strategy, people, products, etc.)?",
229
- initial: true,
230
- });
231
- if (seedDomains) {
232
- try {
233
- const { DomainService, getTenantDb } = await import("@apart-tech/intelligence-core");
234
- const tenantDb = getTenantDb(databaseUrl, { organizationId: org.id });
235
- const domainService = new DomainService(tenantDb, { organizationId: org.id });
236
- const result = await domainService.seed();
237
- console.log(chalk.green(` ✓ ${result.created} domains seeded`));
238
- }
239
- catch (err) {
240
- console.error(chalk.red(` ✗ Domain seeding failed: ${err.message}`));
241
- }
258
+ else {
259
+ console.error(chalk.red(` ✗ Schema push failed`));
260
+ console.log(chalk.dim(err.stderr?.toString().slice(0, 300) ?? err.message));
261
+ process.exit(1);
242
262
  }
243
- // ── Step 7: Write config files ──────────────────────────────────
244
- console.log(chalk.bold("\n6. Writing config files\n"));
245
- // .apart.yaml
246
- const yamlContent = [
247
- "# Apart Intelligence configuration",
248
- "# Docs: https://github.com/aleksander-apart/the-collective",
249
- "",
250
- "embeddings:",
251
- " provider: openai",
252
- " model: text-embedding-3-small",
253
- " dimensions: 1536",
254
- "",
255
- "search:",
256
- " semanticWeight: 0.6",
257
- " defaultLimit: 10",
258
- " includeDrafts: false",
259
- "",
260
- "context:",
261
- " maxDepth: 1",
262
- " maxNodes: 20",
263
- " minRelevance: 0.001",
264
- "",
265
- "mcp:",
266
- " port: 3100",
267
- " auth: none",
268
- "",
269
- ].join("\n");
270
- writeFileSync(configPath, yamlContent, "utf-8");
271
- console.log(chalk.green(" ✓ .apart.yaml"));
272
- // .env
273
- const envPath = join(process.cwd(), ".env");
274
- const envContent = [
275
- `# Apart Intelligence — generated by ai init`,
276
- `APART_DATABASE_URL=${databaseUrl}`,
277
- `OPENAI_API_KEY=${openaiKey}`,
278
- apiKeyValue ? `APART_API_KEY=${apiKeyValue}` : `# APART_API_KEY=`,
279
- `APART_ORG_ID=${org.id}`,
280
- "",
281
- ].join("\n");
282
- if (existsSync(envPath)) {
283
- const { overwriteEnv } = await prompts({
284
- type: "confirm",
285
- name: "overwriteEnv",
286
- message: ".env already exists. Overwrite?",
287
- initial: false,
288
- });
289
- if (overwriteEnv) {
290
- writeFileSync(envPath, envContent, "utf-8");
291
- console.log(chalk.green(" ✓ .env (overwritten)"));
292
- }
293
- else {
294
- const altPath = join(process.cwd(), ".env.apart");
295
- writeFileSync(altPath, envContent, "utf-8");
296
- console.log(chalk.green(` ✓ .env.apart (merge manually into .env)`));
297
- }
263
+ }
264
+ // ── Organization ──────────────────────────────────────────────────
265
+ console.log(chalk.bold("\n3. Organization\n"));
266
+ const { orgName } = await prompts({
267
+ type: "text",
268
+ name: "orgName",
269
+ message: "Organization name:",
270
+ initial: "My Company",
271
+ });
272
+ if (!orgName)
273
+ abort();
274
+ const orgSlug = orgName
275
+ .toLowerCase()
276
+ .replace(/[^a-z0-9]+/g, "-")
277
+ .replace(/^-|-$/g, "");
278
+ // Reconnect with fresh client after schema push
279
+ db = new PrismaClient({ datasourceUrl: databaseUrl });
280
+ let org;
281
+ try {
282
+ org = await db.organization.findFirst({ where: { slug: orgSlug } });
283
+ if (org) {
284
+ console.log(chalk.yellow(` Organization "${orgName}" already exists (${org.id})`));
298
285
  }
299
286
  else {
300
- writeFileSync(envPath, envContent, "utf-8");
301
- console.log(chalk.green(" ✓ .env"));
287
+ org = await db.organization.create({
288
+ data: { name: orgName, slug: orgSlug },
289
+ });
290
+ console.log(chalk.green(` ✓ Organization created: ${org.id}`));
302
291
  }
303
- // ── Done ────────────────────────────────────────────────────────
304
- console.log(chalk.bold("\n✓ Setup complete!\n"));
305
- console.log(" Get started:");
306
- console.log(chalk.cyan(" ai add --type note --title 'My first note' 'Hello from Apart Intelligence!'"));
307
- console.log(chalk.cyan(" ai search 'hello'"));
308
- console.log(chalk.cyan(" ai map . # map a codebase"));
309
- console.log(chalk.cyan(" ai graph # visualize the graph"));
310
- console.log();
311
- await db.$disconnect();
312
- process.exit(0);
292
+ }
293
+ catch (err) {
294
+ console.error(chalk.red(` Failed to create organization: ${err.message}`));
295
+ process.exit(1);
296
+ }
297
+ // ── API Key ───────────────────────────────────────────────────────
298
+ console.log(chalk.bold("\n4. API Key\n"));
299
+ const { createApiKey } = await prompts({
300
+ type: "confirm",
301
+ name: "createApiKey",
302
+ message: "Generate an API key for this organization?",
303
+ initial: true,
313
304
  });
314
- }
315
- function abort() {
316
- console.log(chalk.dim("\nAborted."));
317
- process.exit(0);
318
- }
319
- function findPrismaSchema() {
320
- const candidates = [
321
- join(process.cwd(), "packages", "core"),
322
- join(process.cwd(), "node_modules", "@apart-tech", "intelligence-core"),
323
- resolve(new URL(".", import.meta.url).pathname, "..", "..", ".."),
324
- ];
325
- for (const dir of candidates) {
326
- if (existsSync(join(dir, "prisma", "schema.prisma"))) {
327
- return dir;
305
+ let apiKeyValue = null;
306
+ if (createApiKey) {
307
+ apiKeyValue = `apart_${randomBytes(24).toString("hex")}`;
308
+ const { createHash } = await import("crypto");
309
+ const keyHash = createHash("sha256").update(apiKeyValue).digest("hex");
310
+ try {
311
+ await db.apiKey.create({
312
+ data: {
313
+ name: `${orgName} CLI`,
314
+ keyHash,
315
+ createdBy: "ai-init",
316
+ organizationId: org.id,
317
+ },
318
+ });
319
+ console.log(chalk.green(" ✓ API key created"));
320
+ console.log(chalk.bold(`\n ${apiKeyValue}\n`));
321
+ console.log(chalk.yellow(" Save this key — it won't be shown again."));
322
+ }
323
+ catch (err) {
324
+ console.error(chalk.red(` ✗ Failed to create API key: ${err.message}`));
328
325
  }
329
326
  }
330
- const pkgDir = resolve(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "core");
331
- if (existsSync(join(pkgDir, "prisma", "schema.prisma"))) {
332
- return pkgDir;
327
+ // ── Embeddings ────────────────────────────────────────────────────
328
+ console.log(chalk.bold("\n5. Embeddings\n"));
329
+ const { openaiKey } = await prompts({
330
+ type: "password",
331
+ name: "openaiKey",
332
+ message: "OpenAI API key (for embeddings):",
333
+ validate: (v) => v.startsWith("sk-") ? true : "Must start with sk-",
334
+ });
335
+ if (!openaiKey)
336
+ abort();
337
+ // ── Seed domains ──────────────────────────────────────────────────
338
+ console.log(chalk.bold("\n6. Knowledge Domains\n"));
339
+ const { seedDomains } = await prompts({
340
+ type: "confirm",
341
+ name: "seedDomains",
342
+ message: "Seed default domain taxonomy (strategy, people, products, etc.)?",
343
+ initial: true,
344
+ });
345
+ if (seedDomains) {
346
+ try {
347
+ const { DomainService, getTenantDb } = await import("@apart-tech/intelligence-core");
348
+ const tenantDb = getTenantDb(databaseUrl, { organizationId: org.id });
349
+ const domainService = new DomainService(tenantDb, { organizationId: org.id });
350
+ const result = await domainService.seed();
351
+ console.log(chalk.green(` ✓ ${result.created} domains seeded`));
352
+ }
353
+ catch (err) {
354
+ console.error(chalk.red(` ✗ Domain seeding failed: ${err.message}`));
355
+ }
333
356
  }
334
- throw new Error("Could not find Prisma schema. Make sure @apart-tech/intelligence-core is installed.");
357
+ // ── Write config files ────────────────────────────────────────────
358
+ console.log(chalk.bold("\n7. Writing config files\n"));
359
+ writeConfigFile(configPath);
360
+ // .env
361
+ const envPath = join(process.cwd(), ".env");
362
+ const envContent = [
363
+ `# Apart Intelligence — generated by ai init`,
364
+ `APART_DATABASE_URL=${databaseUrl}`,
365
+ `OPENAI_API_KEY=${openaiKey}`,
366
+ apiKeyValue ? `APART_API_KEY=${apiKeyValue}` : `# APART_API_KEY=`,
367
+ `APART_ORG_ID=${org.id}`,
368
+ "",
369
+ ].join("\n");
370
+ if (existsSync(envPath)) {
371
+ const { overwriteEnv } = await prompts({
372
+ type: "confirm",
373
+ name: "overwriteEnv",
374
+ message: ".env already exists. Overwrite?",
375
+ initial: false,
376
+ });
377
+ if (overwriteEnv) {
378
+ writeFileSync(envPath, envContent, "utf-8");
379
+ console.log(chalk.green(" ✓ .env (overwritten)"));
380
+ }
381
+ else {
382
+ const altPath = join(process.cwd(), ".env.apart");
383
+ writeFileSync(altPath, envContent, "utf-8");
384
+ console.log(chalk.green(` ✓ .env.apart (merge manually into .env)`));
385
+ }
386
+ }
387
+ else {
388
+ writeFileSync(envPath, envContent, "utf-8");
389
+ console.log(chalk.green(" ✓ .env"));
390
+ }
391
+ // ── Done ──────────────────────────────────────────────────────────
392
+ console.log(chalk.bold("\n✓ Setup complete!\n"));
393
+ console.log(" Get started:");
394
+ console.log(chalk.cyan(" ai add --type note --title 'My first note' 'Hello from Apart Intelligence!'"));
395
+ console.log(chalk.cyan(" ai search 'hello'"));
396
+ console.log(chalk.cyan(" ai map . # map a codebase"));
397
+ console.log(chalk.cyan(" ai graph # visualize the graph"));
398
+ console.log();
399
+ await db.$disconnect();
400
+ }
401
+ // ── Shared helpers ────────────────────────────────────────────────────
402
+ function writeConfigFile(configPath) {
403
+ const yamlContent = [
404
+ "# Apart Intelligence configuration",
405
+ "# Docs: https://github.com/aleksander-apart/the-collective",
406
+ "",
407
+ "embeddings:",
408
+ " provider: openai",
409
+ " model: text-embedding-3-small",
410
+ " dimensions: 1536",
411
+ "",
412
+ "search:",
413
+ " semanticWeight: 0.6",
414
+ " defaultLimit: 10",
415
+ " includeDrafts: false",
416
+ "",
417
+ "context:",
418
+ " maxDepth: 1",
419
+ " maxNodes: 20",
420
+ " minRelevance: 0.001",
421
+ "",
422
+ "mcp:",
423
+ " port: 3100",
424
+ " auth: none",
425
+ "",
426
+ ].join("\n");
427
+ writeFileSync(configPath, yamlContent, "utf-8");
428
+ console.log(chalk.green(" ✓ .apart.yaml"));
335
429
  }
336
430
  //# sourceMappingURL=init.js.map