unrag 0.1.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 ADDED
@@ -0,0 +1,47 @@
1
+ # Unrag
2
+
3
+ Unrag is a **RAG installer** for TypeScript projects that installs **composable, drop-in primitives** as source files you own.
4
+
5
+ It installs small, auditable source files into your repo:
6
+ - `unrag.config.ts` (project root)
7
+ - `lib/unrag/**` (or your chosen directory)
8
+ - `lib/unrag/unrag.md` (setup notes + schema)
9
+
10
+ ## Usage
11
+
12
+ ```bash
13
+ bunx unrag init
14
+ ```
15
+
16
+ ### Common flags
17
+
18
+ ```bash
19
+ bunx unrag init --yes --store drizzle --dir lib/unrag --alias @unrag
20
+ ```
21
+
22
+ - `--store`: `drizzle` | `prisma` | `raw-sql`
23
+ - `--dir`: where to install the module code (default `lib/unrag`)
24
+ - `--alias`: import alias base (default `@unrag`) used to patch `tsconfig.json` in Next.js projects
25
+
26
+ ## After install
27
+
28
+ Import the engine from your project root config:
29
+
30
+ ```ts
31
+ import { createUnragEngine } from "@unrag/config";
32
+ ```
33
+
34
+ Then use the primitives:
35
+
36
+ ```ts
37
+ const engine = createUnragEngine();
38
+ await engine.ingest({ sourceId: "doc-1", content: "..." });
39
+ const result = await engine.retrieve({ query: "search", topK: 5 });
40
+ ```
41
+
42
+ ## Database
43
+
44
+ Unrag assumes **Postgres + pgvector**. You manage migrations yourself.
45
+ See the installed `lib/unrag/unrag.md` for the expected schema and indexes.
46
+
47
+
@@ -0,0 +1,535 @@
1
+ #!/usr/bin/env node
2
+
3
+ // cli/run.ts
4
+ import { intro, outro as outro2 } from "@clack/prompts";
5
+
6
+ // cli/commands/init.ts
7
+ import { cancel as cancel2, isCancel as isCancel2, outro, select, text } from "@clack/prompts";
8
+ import path5 from "node:path";
9
+ import { fileURLToPath } from "node:url";
10
+
11
+ // cli/lib/registry.ts
12
+ import path2 from "node:path";
13
+ import { readFile, writeFile } from "node:fs/promises";
14
+ import { confirm, isCancel, cancel } from "@clack/prompts";
15
+
16
+ // cli/lib/fs.ts
17
+ import { access, mkdir, readdir, stat } from "node:fs/promises";
18
+ import path from "node:path";
19
+ async function exists(filePath) {
20
+ try {
21
+ await access(filePath);
22
+ return true;
23
+ } catch {
24
+ return false;
25
+ }
26
+ }
27
+ async function ensureDir(dirPath) {
28
+ await mkdir(dirPath, { recursive: true });
29
+ }
30
+ function normalizePosixPath(p) {
31
+ return p.replace(/\\/g, "/").replace(/\/+$/, "");
32
+ }
33
+ async function tryFindProjectRoot(startDir) {
34
+ return findUp(startDir, "package.json");
35
+ }
36
+ async function findUp(startDir, filename) {
37
+ let current = path.resolve(startDir);
38
+ while (true) {
39
+ const candidate = path.join(current, filename);
40
+ if (await exists(candidate)) {
41
+ return current;
42
+ }
43
+ const parent = path.dirname(current);
44
+ if (parent === current) {
45
+ return null;
46
+ }
47
+ current = parent;
48
+ }
49
+ }
50
+
51
+ // cli/lib/registry.ts
52
+ var readText = (filePath) => readFile(filePath, "utf8");
53
+ var writeText = async (filePath, content) => {
54
+ await ensureDir(path2.dirname(filePath));
55
+ await writeFile(filePath, content, "utf8");
56
+ };
57
+ var renderUnragConfig = (content, selection) => {
58
+ const installImportBase = `./${selection.installDir.replace(/\\/g, "/")}`;
59
+ const baseImports = [
60
+ `import { createContextEngine, defineConfig } from "${installImportBase}/core";`,
61
+ `import { createAiEmbeddingProvider } from "${installImportBase}/embedding/ai";`
62
+ ];
63
+ const storeImports = [];
64
+ const storeCreateLines = [];
65
+ if (selection.storeAdapter === "drizzle") {
66
+ storeImports.push(`import { createDrizzleVectorStore } from "${installImportBase}/store/drizzle";`, `import { drizzle } from "drizzle-orm/node-postgres";`, `import { Pool } from "pg";`);
67
+ storeCreateLines.push(` const databaseUrl = process.env.DATABASE_URL;`, ` if (!databaseUrl) throw new Error("DATABASE_URL is required");`, ``, ` const pool = (globalThis as any).__unragPool ?? new Pool({ connectionString: databaseUrl });`, ` (globalThis as any).__unragPool = pool;`, ``, ` const db = (globalThis as any).__unragDrizzleDb ?? drizzle(pool);`, ` (globalThis as any).__unragDrizzleDb = db;`, ``, ` const store = createDrizzleVectorStore(db);`);
68
+ } else if (selection.storeAdapter === "raw-sql") {
69
+ storeImports.push(`import { createRawSqlVectorStore } from "${installImportBase}/store/raw-sql";`, `import { Pool } from "pg";`);
70
+ storeCreateLines.push(` const databaseUrl = process.env.DATABASE_URL;`, ` if (!databaseUrl) throw new Error("DATABASE_URL is required");`, ``, ` const pool = (globalThis as any).__unragPool ?? new Pool({ connectionString: databaseUrl });`, ` (globalThis as any).__unragPool = pool;`, ``, ` const store = createRawSqlVectorStore(pool);`);
71
+ } else {
72
+ storeImports.push(`import { createPrismaVectorStore } from "${installImportBase}/store/prisma";`, `import { PrismaClient } from "@prisma/client";`);
73
+ storeCreateLines.push(` const prisma = (globalThis as any).__unragPrisma ?? new PrismaClient();`, ` (globalThis as any).__unragPrisma = prisma;`, ` const store = createPrismaVectorStore(prisma);`);
74
+ }
75
+ const importsBlock = [...baseImports, ...storeImports].join(`
76
+ `);
77
+ const createEngineBlock = [
78
+ `export function createUnragEngine() {`,
79
+ ` const embedding = createAiEmbeddingProvider({`,
80
+ ` model: unragConfig.embedding.model,`,
81
+ ` timeoutMs: unragConfig.embedding.timeoutMs,`,
82
+ ` });`,
83
+ ...storeCreateLines,
84
+ ``,
85
+ ` return createContextEngine(`,
86
+ ` defineConfig({`,
87
+ ` embedding,`,
88
+ ` store,`,
89
+ ` defaults: unragConfig.chunking,`,
90
+ ` })`,
91
+ ` );`,
92
+ `}`,
93
+ ``,
94
+ `export async function retrieve(query: string) {`,
95
+ ` const engine = createUnragEngine();`,
96
+ ` return engine.retrieve({ query, topK: unragConfig.retrieval.topK });`,
97
+ `}`
98
+ ].join(`
99
+ `);
100
+ return content.replace("// __UNRAG_IMPORTS__", importsBlock).replace("// __UNRAG_CREATE_ENGINE__", createEngineBlock);
101
+ };
102
+ var renderDocs = (content, selection) => {
103
+ const notes = [];
104
+ if (selection.storeAdapter === "drizzle") {
105
+ notes.push("## Store adapter: Drizzle", "", "You can import the generated Drizzle schema module into your app’s main Drizzle schema to avoid duplicating table definitions.", "", "Example pattern:", "```ts", `import * as rag from "./${selection.installDir}/store/drizzle/schema";`, "", "export const schema = {", " ...rag.schema,", " // ...your app tables", "};", "```", "", "Then run Drizzle migrations from your app as usual.");
106
+ } else if (selection.storeAdapter === "prisma") {
107
+ notes.push("## Store adapter: Prisma", "", "This adapter uses `prisma.$executeRaw` / `prisma.$queryRaw` so you can keep your Prisma models minimal or skip them entirely.", "", 'If you want Prisma models, pgvector is typically represented as `Unsupported("vector")`.', "You can still run migrations however you prefer (SQL migrations are the simplest for pgvector).");
108
+ } else {
109
+ notes.push("## Store adapter: Raw SQL", "", "This adapter uses a `pg` Pool and parameterized SQL queries against the tables described above.", "It’s the most portable option when you don’t want ORM coupling.");
110
+ }
111
+ const withNotes = content.replace("<!-- __UNRAG_ADAPTER_NOTES__ -->", notes.join(`
112
+ `));
113
+ return withNotes.replaceAll("@unrag/config", `${selection.aliasBase}/config`).replaceAll("`@unrag/*`", `\`${selection.aliasBase}/*\``);
114
+ };
115
+ async function copyRegistryFiles(selection) {
116
+ const toAbs = (projectRelative) => path2.join(selection.projectRoot, projectRelative);
117
+ const installBaseAbs = toAbs(selection.installDir);
118
+ const fileMappings = [
119
+ {
120
+ src: path2.join(selection.registryRoot, "config/unrag.config.ts"),
121
+ dest: toAbs("unrag.config.ts"),
122
+ transform: (c) => renderUnragConfig(c, selection)
123
+ },
124
+ {
125
+ src: path2.join(selection.registryRoot, "docs/unrag.md"),
126
+ dest: path2.join(installBaseAbs, "unrag.md"),
127
+ transform: (c) => renderDocs(c, selection)
128
+ },
129
+ {
130
+ src: path2.join(selection.registryRoot, "core/index.ts"),
131
+ dest: path2.join(installBaseAbs, "core/index.ts")
132
+ },
133
+ {
134
+ src: path2.join(selection.registryRoot, "core/types.ts"),
135
+ dest: path2.join(installBaseAbs, "core/types.ts")
136
+ },
137
+ {
138
+ src: path2.join(selection.registryRoot, "core/chunking.ts"),
139
+ dest: path2.join(installBaseAbs, "core/chunking.ts")
140
+ },
141
+ {
142
+ src: path2.join(selection.registryRoot, "core/config.ts"),
143
+ dest: path2.join(installBaseAbs, "core/config.ts")
144
+ },
145
+ {
146
+ src: path2.join(selection.registryRoot, "core/context-engine.ts"),
147
+ dest: path2.join(installBaseAbs, "core/context-engine.ts")
148
+ },
149
+ {
150
+ src: path2.join(selection.registryRoot, "core/ingest.ts"),
151
+ dest: path2.join(installBaseAbs, "core/ingest.ts")
152
+ },
153
+ {
154
+ src: path2.join(selection.registryRoot, "core/retrieve.ts"),
155
+ dest: path2.join(installBaseAbs, "core/retrieve.ts")
156
+ },
157
+ {
158
+ src: path2.join(selection.registryRoot, "embedding/ai.ts"),
159
+ dest: path2.join(installBaseAbs, "embedding/ai.ts")
160
+ }
161
+ ];
162
+ if (selection.storeAdapter === "drizzle") {
163
+ fileMappings.push({
164
+ src: path2.join(selection.registryRoot, "store/drizzle-postgres-pgvector/index.ts"),
165
+ dest: path2.join(installBaseAbs, "store/drizzle/index.ts")
166
+ }, {
167
+ src: path2.join(selection.registryRoot, "store/drizzle-postgres-pgvector/schema.ts"),
168
+ dest: path2.join(installBaseAbs, "store/drizzle/schema.ts")
169
+ }, {
170
+ src: path2.join(selection.registryRoot, "store/drizzle-postgres-pgvector/store.ts"),
171
+ dest: path2.join(installBaseAbs, "store/drizzle/store.ts")
172
+ });
173
+ } else if (selection.storeAdapter === "raw-sql") {
174
+ fileMappings.push({
175
+ src: path2.join(selection.registryRoot, "store/raw-sql-postgres-pgvector/index.ts"),
176
+ dest: path2.join(installBaseAbs, "store/raw-sql/index.ts")
177
+ }, {
178
+ src: path2.join(selection.registryRoot, "store/raw-sql-postgres-pgvector/store.ts"),
179
+ dest: path2.join(installBaseAbs, "store/raw-sql/store.ts")
180
+ });
181
+ } else {
182
+ fileMappings.push({
183
+ src: path2.join(selection.registryRoot, "store/prisma-postgres-pgvector/index.ts"),
184
+ dest: path2.join(installBaseAbs, "store/prisma/index.ts")
185
+ }, {
186
+ src: path2.join(selection.registryRoot, "store/prisma-postgres-pgvector/store.ts"),
187
+ dest: path2.join(installBaseAbs, "store/prisma/store.ts")
188
+ });
189
+ }
190
+ for (const mapping of fileMappings) {
191
+ if (!await exists(mapping.src)) {
192
+ throw new Error(`Registry file missing: ${mapping.src}`);
193
+ }
194
+ if (await exists(mapping.dest)) {
195
+ const answer = await confirm({
196
+ message: `Overwrite ${path2.relative(selection.projectRoot, mapping.dest)}?`,
197
+ initialValue: false
198
+ });
199
+ if (isCancel(answer)) {
200
+ cancel("Cancelled.");
201
+ return;
202
+ }
203
+ if (!answer) {
204
+ continue;
205
+ }
206
+ }
207
+ const raw = await readText(mapping.src);
208
+ const content = mapping.transform ? mapping.transform(raw) : raw;
209
+ await writeText(mapping.dest, content);
210
+ }
211
+ }
212
+
213
+ // cli/lib/json.ts
214
+ import { readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
215
+ async function readJsonFile(filePath) {
216
+ try {
217
+ const raw = await readFile2(filePath, "utf8");
218
+ return JSON.parse(raw);
219
+ } catch {
220
+ return null;
221
+ }
222
+ }
223
+ async function writeJsonFile(filePath, data) {
224
+ await writeFile2(filePath, JSON.stringify(data, null, 2) + `
225
+ `, "utf8");
226
+ }
227
+
228
+ // cli/lib/packageJson.ts
229
+ import path3 from "node:path";
230
+ import { readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
231
+ async function detectPackageManager(projectRoot) {
232
+ if (await exists(path3.join(projectRoot, "bun.lock")))
233
+ return "bun";
234
+ if (await exists(path3.join(projectRoot, "pnpm-lock.yaml")))
235
+ return "pnpm";
236
+ if (await exists(path3.join(projectRoot, "yarn.lock")))
237
+ return "yarn";
238
+ if (await exists(path3.join(projectRoot, "package-lock.json")))
239
+ return "npm";
240
+ return "npm";
241
+ }
242
+ async function readPackageJson(projectRoot) {
243
+ const raw = await readFile3(path3.join(projectRoot, "package.json"), "utf8");
244
+ return JSON.parse(raw);
245
+ }
246
+ async function writePackageJson(projectRoot, pkg) {
247
+ await writeFile3(path3.join(projectRoot, "package.json"), JSON.stringify(pkg, null, 2) + `
248
+ `, "utf8");
249
+ }
250
+ function mergeDeps(pkg, deps, devDeps) {
251
+ const next = { ...pkg };
252
+ next.dependencies = { ...pkg.dependencies ?? {} };
253
+ next.devDependencies = { ...pkg.devDependencies ?? {} };
254
+ const changes = [];
255
+ for (const [name, version] of Object.entries(deps)) {
256
+ if (!next.dependencies[name] && !next.devDependencies[name]) {
257
+ next.dependencies[name] = version;
258
+ changes.push({ name, version, kind: "dep" });
259
+ }
260
+ }
261
+ for (const [name, version] of Object.entries(devDeps)) {
262
+ if (!next.dependencies[name] && !next.devDependencies[name]) {
263
+ next.devDependencies[name] = version;
264
+ changes.push({ name, version, kind: "devDep" });
265
+ }
266
+ }
267
+ return { pkg: next, changes };
268
+ }
269
+ function depsForAdapter(adapter) {
270
+ const deps = {
271
+ ai: "^5.0.113"
272
+ };
273
+ const devDeps = {};
274
+ if (adapter === "drizzle") {
275
+ deps["drizzle-orm"] = "^0.45.1";
276
+ deps["pg"] = "^8.16.3";
277
+ devDeps["@types/pg"] = "^8.16.0";
278
+ }
279
+ if (adapter === "raw-sql") {
280
+ deps["pg"] = "^8.16.3";
281
+ devDeps["@types/pg"] = "^8.16.0";
282
+ }
283
+ if (adapter === "prisma") {
284
+ deps["@prisma/client"] = "^6.0.0";
285
+ devDeps["prisma"] = "^6.0.0";
286
+ }
287
+ return { deps, devDeps };
288
+ }
289
+ function installCmd(pm) {
290
+ if (pm === "bun")
291
+ return "bun install";
292
+ if (pm === "pnpm")
293
+ return "pnpm install";
294
+ if (pm === "yarn")
295
+ return "yarn";
296
+ return "npm install";
297
+ }
298
+
299
+ // cli/lib/tsconfig.ts
300
+ import path4 from "node:path";
301
+ import { readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
302
+ import { parse } from "jsonc-parser";
303
+ var parseJsoncLoose = (raw) => {
304
+ const errors = [];
305
+ const result = parse(raw, errors, { allowTrailingComma: true });
306
+ if (errors.length > 0 || !result || typeof result !== "object") {
307
+ throw new Error("Failed to parse tsconfig JSONC");
308
+ }
309
+ return result;
310
+ };
311
+ async function patchTsconfigPaths(params) {
312
+ const configFile = await exists(path4.join(params.projectRoot, "tsconfig.json")) ? "tsconfig.json" : await exists(path4.join(params.projectRoot, "jsconfig.json")) ? "jsconfig.json" : null;
313
+ const aliasBase = params.aliasBase;
314
+ const aliasKey = `${aliasBase}/*`;
315
+ const target = [`./${params.installDir.replace(/\\/g, "/")}/*`];
316
+ const configAliasKey = `${aliasBase}/config`;
317
+ const configTarget = ["./unrag.config.ts"];
318
+ if (!configFile) {
319
+ const abs2 = path4.join(params.projectRoot, "tsconfig.json");
320
+ const next2 = {
321
+ compilerOptions: {
322
+ baseUrl: ".",
323
+ paths: {
324
+ [aliasKey]: target,
325
+ [configAliasKey]: configTarget
326
+ }
327
+ }
328
+ };
329
+ await writeFile4(abs2, JSON.stringify(next2, null, 2) + `
330
+ `, "utf8");
331
+ return { changed: true, file: "tsconfig.json" };
332
+ }
333
+ const abs = path4.join(params.projectRoot, configFile);
334
+ const raw = await readFile4(abs, "utf8");
335
+ let parsed;
336
+ try {
337
+ parsed = parseJsoncLoose(raw);
338
+ } catch {
339
+ return { changed: false, file: configFile };
340
+ }
341
+ const next = { ...parsed };
342
+ next.compilerOptions = parsed.compilerOptions && typeof parsed.compilerOptions === "object" ? { ...parsed.compilerOptions } : {};
343
+ const parsedPaths = next.compilerOptions.paths && typeof next.compilerOptions.paths === "object" ? next.compilerOptions.paths : {};
344
+ next.compilerOptions.paths = { ...parsedPaths };
345
+ const hasBaseUrl = typeof next.compilerOptions.baseUrl === "string" && next.compilerOptions.baseUrl.length > 0;
346
+ const hasRagAlias = Array.isArray(next.compilerOptions.paths[aliasKey]);
347
+ const hasRagConfigAlias = Array.isArray(next.compilerOptions.paths[configAliasKey]);
348
+ const needsWrite = !hasBaseUrl || !hasRagAlias || !hasRagConfigAlias;
349
+ if (!needsWrite)
350
+ return { changed: false, file: configFile };
351
+ if (!hasBaseUrl) {
352
+ next.compilerOptions.baseUrl = ".";
353
+ }
354
+ if (!hasRagAlias) {
355
+ next.compilerOptions.paths[aliasKey] = target;
356
+ }
357
+ if (!hasRagConfigAlias) {
358
+ next.compilerOptions.paths[configAliasKey] = configTarget;
359
+ }
360
+ await writeFile4(abs, JSON.stringify(next, null, 2) + `
361
+ `, "utf8");
362
+ return { changed: true, file: configFile };
363
+ }
364
+
365
+ // cli/commands/init.ts
366
+ var CONFIG_FILE = "unrag.json";
367
+ var CONFIG_VERSION = 1;
368
+ var __filename2 = fileURLToPath(import.meta.url);
369
+ var __dirname2 = path5.dirname(__filename2);
370
+ var parseInitArgs = (args) => {
371
+ const out = {};
372
+ for (let i = 0;i < args.length; i++) {
373
+ const a = args[i];
374
+ if (a === "--yes" || a === "-y") {
375
+ out.yes = true;
376
+ continue;
377
+ }
378
+ if (a === "--dir" || a === "--install-dir") {
379
+ const v = args[i + 1];
380
+ if (v) {
381
+ out.installDir = v;
382
+ i++;
383
+ }
384
+ continue;
385
+ }
386
+ if (a === "--store") {
387
+ const v = args[i + 1];
388
+ if (v === "drizzle" || v === "prisma" || v === "raw-sql") {
389
+ out.storeAdapter = v;
390
+ i++;
391
+ }
392
+ continue;
393
+ }
394
+ if (a === "--alias") {
395
+ const v = args[i + 1];
396
+ if (v) {
397
+ out.aliasBase = v;
398
+ i++;
399
+ }
400
+ continue;
401
+ }
402
+ }
403
+ return out;
404
+ };
405
+ async function initCommand(args) {
406
+ const root = await tryFindProjectRoot(process.cwd());
407
+ if (!root) {
408
+ throw new Error("Could not find a project root (no package.json found).");
409
+ }
410
+ const cliPackageRoot = await findUp(__dirname2, "package.json");
411
+ if (!cliPackageRoot) {
412
+ throw new Error("Could not locate CLI package root (package.json not found).");
413
+ }
414
+ const registryRoot = path5.join(cliPackageRoot, "registry");
415
+ const existing = await readJsonFile(path5.join(root, CONFIG_FILE));
416
+ const parsed = parseInitArgs(args);
417
+ const defaults = {
418
+ installDir: existing?.installDir ?? "lib/unrag",
419
+ storeAdapter: existing?.storeAdapter ?? "drizzle",
420
+ aliasBase: existing?.aliasBase ?? "@unrag"
421
+ };
422
+ const nonInteractive = parsed.yes || !process.stdin.isTTY;
423
+ const installDirAnswer = parsed.installDir ? parsed.installDir : nonInteractive ? defaults.installDir : await text({
424
+ message: "Install directory",
425
+ initialValue: defaults.installDir,
426
+ validate: (v) => {
427
+ if (!v.trim())
428
+ return "Install directory is required";
429
+ if (v.startsWith("/"))
430
+ return "Use a project-relative path";
431
+ return;
432
+ }
433
+ });
434
+ if (isCancel2(installDirAnswer)) {
435
+ cancel2("Cancelled.");
436
+ return;
437
+ }
438
+ const installDir = normalizePosixPath(String(installDirAnswer));
439
+ const storeAdapterAnswer = parsed.storeAdapter ? parsed.storeAdapter : nonInteractive ? defaults.storeAdapter : await select({
440
+ message: "Store adapter",
441
+ initialValue: defaults.storeAdapter,
442
+ options: [
443
+ { value: "drizzle", label: "Drizzle (Postgres + pgvector)" },
444
+ { value: "prisma", label: "Prisma (Postgres + pgvector)" },
445
+ { value: "raw-sql", label: "Raw SQL (Postgres + pgvector)" }
446
+ ]
447
+ });
448
+ if (isCancel2(storeAdapterAnswer)) {
449
+ cancel2("Cancelled.");
450
+ return;
451
+ }
452
+ const aliasAnswer = parsed.aliasBase ? parsed.aliasBase : nonInteractive ? defaults.aliasBase : await text({
453
+ message: "Import alias base",
454
+ initialValue: defaults.aliasBase,
455
+ validate: (v) => {
456
+ const s = v.trim();
457
+ if (!s)
458
+ return "Alias is required";
459
+ if (s.includes(" "))
460
+ return "Alias must not contain spaces";
461
+ if (!s.startsWith("@"))
462
+ return 'Alias should start with "@" (e.g. "@unrag")';
463
+ if (s.endsWith("/"))
464
+ return "Alias must not end with /";
465
+ return;
466
+ }
467
+ });
468
+ if (isCancel2(aliasAnswer)) {
469
+ cancel2("Cancelled.");
470
+ return;
471
+ }
472
+ const aliasBase = String(aliasAnswer).trim();
473
+ const selection = {
474
+ installDir,
475
+ storeAdapter: storeAdapterAnswer,
476
+ projectRoot: root,
477
+ registryRoot,
478
+ aliasBase
479
+ };
480
+ await copyRegistryFiles(selection);
481
+ const pkg = await readPackageJson(root);
482
+ const { deps, devDeps } = depsForAdapter(storeAdapterAnswer);
483
+ const merged = mergeDeps(pkg, deps, devDeps);
484
+ if (merged.changes.length > 0) {
485
+ await writePackageJson(root, merged.pkg);
486
+ }
487
+ const config = {
488
+ installDir,
489
+ storeAdapter: storeAdapterAnswer,
490
+ aliasBase,
491
+ version: CONFIG_VERSION
492
+ };
493
+ await writeJsonFile(path5.join(root, CONFIG_FILE), config);
494
+ const pm = await detectPackageManager(root);
495
+ const installLine = merged.changes.length > 0 ? `Next: run \`${installCmd(pm)}\`` : "Dependencies already satisfied.";
496
+ const isNext = Boolean((merged.pkg.dependencies ?? {})["next"]) || Boolean((merged.pkg.devDependencies ?? {})["next"]);
497
+ const tsconfigResult = isNext ? await patchTsconfigPaths({ projectRoot: root, installDir, aliasBase }) : { changed: false };
498
+ outro([
499
+ "Installed Unrag.",
500
+ "",
501
+ `- Code: ${path5.join(installDir)}`,
502
+ `- Docs: ${path5.join(installDir, "unrag.md")}`,
503
+ `- Config: unrag.config.ts`,
504
+ `- Imports: ${aliasBase}/* and ${aliasBase}/config`,
505
+ isNext ? tsconfigResult.changed ? `- Next.js: updated ${tsconfigResult.file} (added aliases)` : `- Next.js: no tsconfig changes needed` : `- Next.js: not detected`,
506
+ "",
507
+ merged.changes.length > 0 ? `Added deps: ${merged.changes.map((c) => c.name).join(", ")}` : "Added deps: none",
508
+ installLine,
509
+ "",
510
+ `Saved ${CONFIG_FILE}.`
511
+ ].join(`
512
+ `));
513
+ }
514
+
515
+ // cli/run.ts
516
+ async function run(argv) {
517
+ const [, , command, ...rest] = argv;
518
+ intro("unrag");
519
+ if (!command || command === "help" || command === "--help" || command === "-h") {
520
+ outro2("Usage: unrag init");
521
+ return;
522
+ }
523
+ if (command === "init") {
524
+ await initCommand(rest);
525
+ return;
526
+ }
527
+ outro2(`Unknown command: ${command}`);
528
+ process.exitCode = 1;
529
+ }
530
+
531
+ // cli/index.ts
532
+ run(process.argv).catch((err) => {
533
+ console.error(err);
534
+ process.exit(1);
535
+ });
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "unrag",
3
+ "type": "module",
4
+ "bin": {
5
+ "unrag": "./dist/cli/index.js"
6
+ },
7
+ "version": "0.1.1",
8
+ "private": false,
9
+ "license": "Apache-2.0",
10
+ "devDependencies": {
11
+ "@types/bun": "latest",
12
+ "@types/pg": "^8.16.0",
13
+ "@prisma/client": "^6.0.0",
14
+ "prisma": "^6.0.0",
15
+ "drizzle-orm": "^0.45.1",
16
+ "drizzle-kit": "^0.31.8",
17
+ "ai": "^5.0.113",
18
+ "pg": "^8.16.3"
19
+ },
20
+ "dependencies": {
21
+ "@clack/prompts": "^0.11.0",
22
+ "jsonc-parser": "^3.3.1"
23
+ },
24
+ "peerDependencies": {
25
+ "typescript": "^5"
26
+ },
27
+ "scripts": {
28
+ "build": "bun build --target=node --format=esm --packages=external --outfile dist/cli/index.js cli/index.ts",
29
+ "test": "bun test"
30
+ },
31
+ "files": [
32
+ "dist/**",
33
+ "registry/**",
34
+ "README.md"
35
+ ],
36
+ "publishConfig": {
37
+ "access": "public"
38
+ }
39
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Root Unrag config (generated).
3
+ *
4
+ * This file is meant to be the single place you tweak:
5
+ * - Embedding provider/model/timeouts
6
+ * - Chunking defaults
7
+ * - Retrieval defaults
8
+ * - How you construct your DB client (Pool/Prisma/etc)
9
+ *
10
+ * The files under your install dir (e.g. `lib/unrag/**`) are intended to be
11
+ * treated like vendored source code.
12
+ */
13
+
14
+ // __UNRAG_IMPORTS__
15
+
16
+ export const unragConfig = {
17
+ chunking: {
18
+ chunkSize: 200,
19
+ chunkOverlap: 40,
20
+ },
21
+ retrieval: {
22
+ topK: 8,
23
+ },
24
+ embedding: {
25
+ model: "openai/text-embedding-3-small",
26
+ timeoutMs: 15_000,
27
+ },
28
+ } as const;
29
+
30
+ // __UNRAG_CREATE_ENGINE__
31
+
32
+