shiva-code 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.
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,1144 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command as Command9 } from "commander";
5
+
6
+ // src/commands/login.ts
7
+ import { Command } from "commander";
8
+ import inquirer2 from "inquirer";
9
+
10
+ // src/utils/config.ts
11
+ import Conf from "conf";
12
+ var DEFAULT_API_ENDPOINT = "https://shiva-ai-api.slither-mutiplayer.workers.dev/api";
13
+ var config = new Conf({
14
+ projectName: "shiva-code",
15
+ defaults: {
16
+ apiEndpoint: DEFAULT_API_ENDPOINT,
17
+ token: null,
18
+ tokenExpiry: null,
19
+ userId: null,
20
+ email: null,
21
+ tier: null
22
+ }
23
+ });
24
+ function getConfig() {
25
+ return {
26
+ apiEndpoint: config.get("apiEndpoint"),
27
+ token: config.get("token"),
28
+ tokenExpiry: config.get("tokenExpiry"),
29
+ userId: config.get("userId"),
30
+ email: config.get("email"),
31
+ tier: config.get("tier")
32
+ };
33
+ }
34
+ function setConfig(key, value) {
35
+ config.set(key, value);
36
+ }
37
+ function getToken() {
38
+ const token = config.get("token");
39
+ const expiry = config.get("tokenExpiry");
40
+ if (token && expiry && Date.now() > expiry) {
41
+ clearAuth();
42
+ return null;
43
+ }
44
+ return token;
45
+ }
46
+ function setAuth(token, user) {
47
+ let expiry;
48
+ try {
49
+ const payload = JSON.parse(atob(token.includes(".") ? token.split(".")[1] : token));
50
+ expiry = payload.exp > 1e12 ? payload.exp : payload.exp * 1e3;
51
+ } catch {
52
+ expiry = Date.now() + 7 * 24 * 60 * 60 * 1e3;
53
+ }
54
+ config.set("token", token);
55
+ config.set("tokenExpiry", expiry);
56
+ config.set("userId", user.id);
57
+ config.set("email", user.email);
58
+ config.set("tier", user.tier);
59
+ }
60
+ function clearAuth() {
61
+ config.set("token", null);
62
+ config.set("tokenExpiry", null);
63
+ config.set("userId", null);
64
+ config.set("email", null);
65
+ config.set("tier", null);
66
+ }
67
+ function isAuthenticated() {
68
+ return getToken() !== null;
69
+ }
70
+ function getApiEndpoint() {
71
+ return config.get("apiEndpoint");
72
+ }
73
+ var CONFIG_PATH = config.path;
74
+ function clearConfig() {
75
+ config.clear();
76
+ config.set("apiEndpoint", DEFAULT_API_ENDPOINT);
77
+ }
78
+
79
+ // src/services/api.ts
80
+ var ApiClient = class {
81
+ getHeaders() {
82
+ const headers = {
83
+ "Content-Type": "application/json"
84
+ };
85
+ const token = getToken();
86
+ if (token) {
87
+ headers["Authorization"] = `Bearer ${token}`;
88
+ }
89
+ return headers;
90
+ }
91
+ async request(endpoint, options = {}) {
92
+ const baseUrl = getApiEndpoint();
93
+ const url = `${baseUrl}${endpoint}`;
94
+ const response = await fetch(url, {
95
+ ...options,
96
+ headers: {
97
+ ...this.getHeaders(),
98
+ ...options.headers
99
+ }
100
+ });
101
+ if (!response.ok) {
102
+ const error = await response.json().catch(() => ({ error: "Unknown error" }));
103
+ throw new Error(error.error || error.message || `HTTP ${response.status}`);
104
+ }
105
+ return response.json();
106
+ }
107
+ // Auth endpoints
108
+ async requestOtp(email) {
109
+ return this.request("/auth/otp/request", {
110
+ method: "POST",
111
+ body: JSON.stringify({ email })
112
+ });
113
+ }
114
+ async verifyOtp(token, otp) {
115
+ return this.request("/auth/otp/verify", {
116
+ method: "POST",
117
+ body: JSON.stringify({ token, otp })
118
+ });
119
+ }
120
+ async getCurrentUser() {
121
+ return this.request("/auth/me");
122
+ }
123
+ // Project endpoints
124
+ async getProjects() {
125
+ return this.request("/projects");
126
+ }
127
+ async getProject(id) {
128
+ return this.request(`/projects/${id}`);
129
+ }
130
+ async createOrUpdateProject(data) {
131
+ return this.request("/projects", {
132
+ method: "POST",
133
+ body: JSON.stringify(data)
134
+ });
135
+ }
136
+ async syncProject(id, data) {
137
+ return this.request(`/projects/${id}/sync`, {
138
+ method: "POST",
139
+ body: JSON.stringify(data)
140
+ });
141
+ }
142
+ async deleteProject(id) {
143
+ return this.request(`/projects/${id}`, {
144
+ method: "DELETE"
145
+ });
146
+ }
147
+ async addMemory(projectId, memory) {
148
+ return this.request(`/projects/${projectId}/memories`, {
149
+ method: "POST",
150
+ body: JSON.stringify(memory)
151
+ });
152
+ }
153
+ async connectProjects(projectA, projectB, connectionType) {
154
+ return this.request("/projects/connect", {
155
+ method: "POST",
156
+ body: JSON.stringify({
157
+ projectA,
158
+ projectB,
159
+ connectionType: connectionType || "related"
160
+ })
161
+ });
162
+ }
163
+ async disconnectProjects(projectA, projectB) {
164
+ return this.request("/projects/disconnect", {
165
+ method: "DELETE",
166
+ body: JSON.stringify({ projectA, projectB })
167
+ });
168
+ }
169
+ async getStats() {
170
+ return this.request("/projects/stats");
171
+ }
172
+ // Find project by path
173
+ async findProjectByPath(path) {
174
+ const projects = await this.getProjects();
175
+ return projects.find((p) => p.path === path) || null;
176
+ }
177
+ };
178
+ var api = new ApiClient();
179
+
180
+ // src/utils/logger.ts
181
+ import chalk from "chalk";
182
+ var log = {
183
+ // Success messages
184
+ success: (message) => console.log(chalk.green("\u2713"), message),
185
+ // Error messages
186
+ error: (message) => console.log(chalk.red("\u2717"), message),
187
+ // Warning messages
188
+ warn: (message) => console.log(chalk.yellow("\u26A0"), message),
189
+ // Info messages
190
+ info: (message) => console.log(chalk.blue("\u2139"), message),
191
+ // Plain message
192
+ plain: (message) => console.log(message),
193
+ // Dimmed text
194
+ dim: (message) => console.log(chalk.dim(message)),
195
+ // Bold text
196
+ bold: (message) => console.log(chalk.bold(message)),
197
+ // Newline
198
+ newline: () => console.log(),
199
+ // Header
200
+ header: (title) => {
201
+ console.log();
202
+ console.log(chalk.bold(title));
203
+ console.log(chalk.dim("\u2500".repeat(title.length)));
204
+ },
205
+ // Key-value pair
206
+ keyValue: (key, value, indent = 0) => {
207
+ const spaces = " ".repeat(indent);
208
+ console.log(`${spaces}${chalk.dim(key + ":")} ${value}`);
209
+ },
210
+ // List item
211
+ listItem: (text, status) => {
212
+ let icon = "\u2022";
213
+ if (status === "synced") icon = chalk.green("\u2713");
214
+ if (status === "pending") icon = chalk.yellow("\u25CB");
215
+ if (status === "error") icon = chalk.red("\u2717");
216
+ console.log(` ${icon} ${text}`);
217
+ },
218
+ // Tree structure
219
+ tree: {
220
+ item: (text, isLast = false) => {
221
+ const prefix = isLast ? "\u2514\u2500" : "\u251C\u2500";
222
+ console.log(chalk.dim(prefix), text);
223
+ }
224
+ },
225
+ // Branded header
226
+ brand: () => {
227
+ console.log();
228
+ console.log(chalk.hex("#FF6B2C").bold("SHIVA Code"));
229
+ console.log(chalk.dim("Makes Claude Code Persistent"));
230
+ console.log();
231
+ }
232
+ };
233
+ var colors = {
234
+ orange: chalk.hex("#FF6B2C"),
235
+ cyan: chalk.hex("#00D4FF"),
236
+ green: chalk.green,
237
+ red: chalk.red,
238
+ yellow: chalk.yellow,
239
+ dim: chalk.dim,
240
+ bold: chalk.bold
241
+ };
242
+
243
+ // src/services/auth.ts
244
+ import inquirer from "inquirer";
245
+ import open from "open";
246
+ async function loginWithOtp(email) {
247
+ try {
248
+ log.info(`Sende Login-Code an ${email}...`);
249
+ const otpResponse = await api.requestOtp(email);
250
+ if (!otpResponse.success) {
251
+ log.error(otpResponse.message || "Fehler beim Senden des Codes");
252
+ return false;
253
+ }
254
+ log.success("Code wurde gesendet!");
255
+ log.dim(`Verbleibende Versuche: ${otpResponse.remainingAttempts}`);
256
+ log.newline();
257
+ const { otp } = await inquirer.prompt([
258
+ {
259
+ type: "input",
260
+ name: "otp",
261
+ message: "Code eingeben:",
262
+ validate: (input) => {
263
+ if (!/^\d{6}$/.test(input)) {
264
+ return "Bitte gib den 6-stelligen Code ein";
265
+ }
266
+ return true;
267
+ }
268
+ }
269
+ ]);
270
+ const authResponse = await api.verifyOtp(otpResponse.token, otp);
271
+ if (!authResponse.success || !authResponse.token || !authResponse.user) {
272
+ log.error(authResponse.message || "Ung\xFCltiger Code");
273
+ return false;
274
+ }
275
+ setAuth(authResponse.token, authResponse.user);
276
+ log.newline();
277
+ log.success(`Angemeldet als ${authResponse.user.email} (${authResponse.user.tier.toUpperCase()})`);
278
+ return true;
279
+ } catch (error) {
280
+ const message = error instanceof Error ? error.message : "Unbekannter Fehler";
281
+ log.error(`Login fehlgeschlagen: ${message}`);
282
+ return false;
283
+ }
284
+ }
285
+ async function loginWithBrowser() {
286
+ const config2 = getConfig();
287
+ const loginUrl = `${config2.apiEndpoint.replace("/api", "")}/auth/cli-login`;
288
+ log.info("\xD6ffne Browser zur Anmeldung...");
289
+ log.dim(`URL: ${loginUrl}`);
290
+ log.newline();
291
+ try {
292
+ await open(loginUrl);
293
+ log.plain("Nachdem du dich im Browser angemeldet hast,");
294
+ log.plain("kopiere den Token und f\xFCge ihn hier ein:");
295
+ log.newline();
296
+ const { token } = await inquirer.prompt([
297
+ {
298
+ type: "password",
299
+ name: "token",
300
+ message: "Token:",
301
+ mask: "*"
302
+ }
303
+ ]);
304
+ if (!token) {
305
+ log.error("Kein Token eingegeben");
306
+ return false;
307
+ }
308
+ const originalToken = config2.token;
309
+ setAuth(token, { id: 0, email: "", tier: "free" });
310
+ try {
311
+ const response = await api.getCurrentUser();
312
+ setAuth(token, response.user);
313
+ log.newline();
314
+ log.success(`Angemeldet als ${response.user.email} (${response.user.tier.toUpperCase()})`);
315
+ return true;
316
+ } catch {
317
+ if (originalToken) {
318
+ setAuth(originalToken, { id: config2.userId, email: config2.email, tier: config2.tier });
319
+ } else {
320
+ clearAuth();
321
+ }
322
+ log.error("Ung\xFCltiger Token");
323
+ return false;
324
+ }
325
+ } catch (error) {
326
+ log.error("Konnte Browser nicht \xF6ffnen");
327
+ return false;
328
+ }
329
+ }
330
+ function logout() {
331
+ clearAuth();
332
+ log.success("Abgemeldet");
333
+ }
334
+
335
+ // src/commands/login.ts
336
+ var loginCommand = new Command("login").description("Mit shiva.li anmelden").option("-e, --email <email>", "Email-Adresse f\xFCr OTP-Login").option("-b, --browser", "Im Browser anmelden").action(async (options) => {
337
+ if (isAuthenticated()) {
338
+ const config2 = getConfig();
339
+ log.info(`Bereits angemeldet als ${config2.email}`);
340
+ const { relogin } = await inquirer2.prompt([
341
+ {
342
+ type: "confirm",
343
+ name: "relogin",
344
+ message: "M\xF6chtest du dich neu anmelden?",
345
+ default: false
346
+ }
347
+ ]);
348
+ if (!relogin) {
349
+ return;
350
+ }
351
+ }
352
+ log.brand();
353
+ if (options.browser) {
354
+ await loginWithBrowser();
355
+ return;
356
+ }
357
+ if (options.email) {
358
+ await loginWithOtp(options.email);
359
+ return;
360
+ }
361
+ const { email } = await inquirer2.prompt([
362
+ {
363
+ type: "input",
364
+ name: "email",
365
+ message: "Email-Adresse:",
366
+ validate: (input) => {
367
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input)) {
368
+ return "Bitte gib eine g\xFCltige Email-Adresse ein";
369
+ }
370
+ return true;
371
+ }
372
+ }
373
+ ]);
374
+ await loginWithOtp(email);
375
+ });
376
+
377
+ // src/commands/logout.ts
378
+ import { Command as Command2 } from "commander";
379
+ var logoutCommand = new Command2("logout").description("Abmelden").action(() => {
380
+ if (!isAuthenticated()) {
381
+ log.info("Du bist nicht angemeldet");
382
+ return;
383
+ }
384
+ logout();
385
+ });
386
+
387
+ // src/commands/init.ts
388
+ import { Command as Command3 } from "commander";
389
+ import { writeFileSync, existsSync as existsSync2 } from "fs";
390
+ import { resolve as resolve2 } from "path";
391
+ import ora from "ora";
392
+ import inquirer3 from "inquirer";
393
+
394
+ // src/services/scanner.ts
395
+ import { readFileSync, existsSync, readdirSync } from "fs";
396
+ import { resolve, basename } from "path";
397
+ async function scanProject(dir) {
398
+ const absolutePath = resolve(dir);
399
+ const projectName = basename(absolutePath);
400
+ const result = {
401
+ name: projectName,
402
+ path: absolutePath,
403
+ hasClaudeMd: false
404
+ };
405
+ const claudeMdPath = resolve(absolutePath, "CLAUDE.md");
406
+ if (existsSync(claudeMdPath)) {
407
+ result.hasClaudeMd = true;
408
+ result.claudeMdContent = readFileSync(claudeMdPath, "utf-8");
409
+ }
410
+ const packageJsonPath = resolve(absolutePath, "package.json");
411
+ if (existsSync(packageJsonPath)) {
412
+ try {
413
+ const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
414
+ if (pkg.name) result.name = pkg.name;
415
+ if (pkg.description) result.description = pkg.description;
416
+ result.language = detectLanguageFromPackage(pkg, absolutePath);
417
+ result.framework = detectFramework(pkg);
418
+ result.packageManager = detectPackageManager(absolutePath);
419
+ } catch {
420
+ }
421
+ }
422
+ if (!result.language) {
423
+ result.language = detectLanguageFromFiles(absolutePath);
424
+ }
425
+ if (!result.language) {
426
+ const pyprojectPath = resolve(absolutePath, "pyproject.toml");
427
+ const requirementsPath = resolve(absolutePath, "requirements.txt");
428
+ if (existsSync(pyprojectPath) || existsSync(requirementsPath)) {
429
+ result.language = "Python";
430
+ result.packageManager = existsSync(pyprojectPath) ? "poetry" : "pip";
431
+ }
432
+ }
433
+ if (!result.language) {
434
+ const goModPath = resolve(absolutePath, "go.mod");
435
+ if (existsSync(goModPath)) {
436
+ result.language = "Go";
437
+ result.packageManager = "go mod";
438
+ }
439
+ }
440
+ if (!result.language) {
441
+ const cargoPath = resolve(absolutePath, "Cargo.toml");
442
+ if (existsSync(cargoPath)) {
443
+ result.language = "Rust";
444
+ result.packageManager = "cargo";
445
+ }
446
+ }
447
+ return result;
448
+ }
449
+ function detectLanguageFromPackage(pkg, dir) {
450
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
451
+ if (allDeps["typescript"] || existsSync(resolve(dir, "tsconfig.json"))) {
452
+ return "TypeScript";
453
+ }
454
+ return "JavaScript";
455
+ }
456
+ function detectFramework(pkg) {
457
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
458
+ if (allDeps["next"]) return "Next.js";
459
+ if (allDeps["nuxt"]) return "Nuxt";
460
+ if (allDeps["@sveltejs/kit"]) return "SvelteKit";
461
+ if (allDeps["astro"]) return "Astro";
462
+ if (allDeps["react"]) return "React";
463
+ if (allDeps["vue"]) return "Vue";
464
+ if (allDeps["svelte"]) return "Svelte";
465
+ if (allDeps["angular"]) return "Angular";
466
+ if (allDeps["express"]) return "Express";
467
+ if (allDeps["fastify"]) return "Fastify";
468
+ if (allDeps["hono"]) return "Hono";
469
+ if (allDeps["koa"]) return "Koa";
470
+ if (allDeps["nestjs"] || allDeps["@nestjs/core"]) return "NestJS";
471
+ if (allDeps["electron"]) return "Electron";
472
+ if (allDeps["react-native"]) return "React Native";
473
+ return void 0;
474
+ }
475
+ function detectPackageManager(dir) {
476
+ if (existsSync(resolve(dir, "bun.lockb"))) return "bun";
477
+ if (existsSync(resolve(dir, "pnpm-lock.yaml"))) return "pnpm";
478
+ if (existsSync(resolve(dir, "yarn.lock"))) return "yarn";
479
+ if (existsSync(resolve(dir, "package-lock.json"))) return "npm";
480
+ return "npm";
481
+ }
482
+ function detectLanguageFromFiles(dir) {
483
+ try {
484
+ const files = readdirSync(dir);
485
+ const hasTs = files.some((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
486
+ const hasJs = files.some((f) => f.endsWith(".js") || f.endsWith(".jsx"));
487
+ const hasPy = files.some((f) => f.endsWith(".py"));
488
+ const hasGo = files.some((f) => f.endsWith(".go"));
489
+ const hasRs = files.some((f) => f.endsWith(".rs"));
490
+ if (hasTs) return "TypeScript";
491
+ if (hasJs) return "JavaScript";
492
+ if (hasPy) return "Python";
493
+ if (hasGo) return "Go";
494
+ if (hasRs) return "Rust";
495
+ } catch {
496
+ }
497
+ return void 0;
498
+ }
499
+
500
+ // src/services/claude-md.ts
501
+ function generateClaudeMd(scanned, project) {
502
+ const lines = [];
503
+ lines.push(`# ${scanned.name}`);
504
+ lines.push("");
505
+ lines.push("## Project Overview");
506
+ if (scanned.description) {
507
+ lines.push(scanned.description);
508
+ } else {
509
+ lines.push("<!-- Add project description here -->");
510
+ }
511
+ lines.push("");
512
+ lines.push("## Tech Stack");
513
+ if (scanned.language) {
514
+ lines.push(`- **Language**: ${scanned.language}`);
515
+ }
516
+ if (scanned.framework) {
517
+ lines.push(`- **Framework**: ${scanned.framework}`);
518
+ }
519
+ if (scanned.packageManager) {
520
+ lines.push(`- **Package Manager**: ${scanned.packageManager}`);
521
+ }
522
+ lines.push("");
523
+ lines.push("## SHIVA Code");
524
+ lines.push("");
525
+ lines.push("This project is managed by [SHIVA Code](https://shiva.li).");
526
+ lines.push("");
527
+ if (project) {
528
+ lines.push(`- **Status**: ${project.synced ? "Synced" : "Pending"}`);
529
+ if (project.last_synced_at) {
530
+ lines.push(`- **Last Sync**: ${new Date(project.last_synced_at).toLocaleString()}`);
531
+ }
532
+ lines.push(`- **Project ID**: ${project.id}`);
533
+ } else {
534
+ lines.push("- **Status**: Not synced");
535
+ lines.push("- **Last Sync**: Never");
536
+ }
537
+ lines.push("");
538
+ lines.push("## Memories");
539
+ lines.push("");
540
+ if (project?.memories && project.memories.length > 0) {
541
+ lines.push("The following context is remembered across sessions:");
542
+ lines.push("");
543
+ for (const memory of project.memories) {
544
+ lines.push(`- **${memory.key}** (${memory.category}): ${memory.value}`);
545
+ }
546
+ } else {
547
+ lines.push("No memories stored yet. Use `shiva sync` to sync your project.");
548
+ }
549
+ lines.push("");
550
+ if (project?.connections && project.connections.length > 0) {
551
+ lines.push("## Connected Projects");
552
+ lines.push("");
553
+ lines.push("This project is connected to:");
554
+ lines.push("");
555
+ for (const conn of project.connections) {
556
+ lines.push(`- **${conn.connected_project_name}** (${conn.connection_type})`);
557
+ }
558
+ lines.push("");
559
+ }
560
+ lines.push("---");
561
+ lines.push("");
562
+ lines.push("*Generated by SHIVA Code CLI*");
563
+ return lines.join("\n");
564
+ }
565
+ function parseClaudeMd(content) {
566
+ const memories = [];
567
+ const memoryRegex = /^- \*\*(.+?)\*\* \((.+?)\): (.+)$/gm;
568
+ let match;
569
+ while ((match = memoryRegex.exec(content)) !== null) {
570
+ memories.push({
571
+ key: match[1],
572
+ category: match[2],
573
+ value: match[3]
574
+ });
575
+ }
576
+ return { memories };
577
+ }
578
+
579
+ // src/commands/init.ts
580
+ var initCommand = new Command3("init").description("Projekt f\xFCr SHIVA Code initialisieren").option("-d, --dir <directory>", "Projektverzeichnis", ".").option("-f, --force", "CLAUDE.md \xFCberschreiben wenn vorhanden").option("--no-sync", "Nicht mit Cloud synchronisieren").action(async (options) => {
581
+ log.brand();
582
+ const projectDir = resolve2(options.dir);
583
+ if (!existsSync2(projectDir)) {
584
+ log.error(`Verzeichnis nicht gefunden: ${projectDir}`);
585
+ return;
586
+ }
587
+ const spinner = ora("Projekt wird gescannt...").start();
588
+ let scanned;
589
+ try {
590
+ scanned = await scanProject(projectDir);
591
+ spinner.succeed("Projekt gescannt");
592
+ } catch (error) {
593
+ spinner.fail("Fehler beim Scannen");
594
+ log.error(error instanceof Error ? error.message : "Unbekannter Fehler");
595
+ return;
596
+ }
597
+ log.newline();
598
+ log.header("Erkannt");
599
+ log.keyValue("Name", scanned.name, 2);
600
+ if (scanned.language) log.keyValue("Sprache", scanned.language, 2);
601
+ if (scanned.framework) log.keyValue("Framework", scanned.framework, 2);
602
+ if (scanned.packageManager) log.keyValue("Package Manager", scanned.packageManager, 2);
603
+ log.newline();
604
+ const claudeMdPath = resolve2(projectDir, "CLAUDE.md");
605
+ if (existsSync2(claudeMdPath) && !options.force) {
606
+ const { overwrite } = await inquirer3.prompt([
607
+ {
608
+ type: "confirm",
609
+ name: "overwrite",
610
+ message: "CLAUDE.md existiert bereits. \xDCberschreiben?",
611
+ default: false
612
+ }
613
+ ]);
614
+ if (!overwrite) {
615
+ log.info("CLAUDE.md wurde nicht ver\xE4ndert");
616
+ return;
617
+ }
618
+ }
619
+ let project = null;
620
+ if (options.sync && isAuthenticated()) {
621
+ const syncSpinner = ora("Synchronisiere mit Cloud...").start();
622
+ try {
623
+ const result = await api.createOrUpdateProject({
624
+ name: scanned.name,
625
+ path: scanned.path,
626
+ description: scanned.description,
627
+ metadata: {
628
+ language: scanned.language,
629
+ framework: scanned.framework,
630
+ packageManager: scanned.packageManager
631
+ }
632
+ });
633
+ project = await api.getProject(result.projectId);
634
+ syncSpinner.succeed(`Projekt ${result.action === "created" ? "erstellt" : "aktualisiert"}`);
635
+ } catch (error) {
636
+ syncSpinner.warn("Cloud-Sync fehlgeschlagen (offline?)");
637
+ }
638
+ } else if (!isAuthenticated()) {
639
+ log.dim('Tipp: Mit "shiva login" anmelden f\xFCr Cloud-Sync');
640
+ }
641
+ const claudeMdContent = generateClaudeMd(scanned, project || void 0);
642
+ writeFileSync(claudeMdPath, claudeMdContent);
643
+ log.success("CLAUDE.md erstellt");
644
+ log.newline();
645
+ log.success("Projekt initialisiert!");
646
+ if (!isAuthenticated()) {
647
+ log.newline();
648
+ log.info("N\xE4chste Schritte:");
649
+ log.plain(" 1. shiva login - Anmelden");
650
+ log.plain(" 2. shiva sync - Mit Cloud synchronisieren");
651
+ }
652
+ });
653
+
654
+ // src/commands/sync.ts
655
+ import { Command as Command4 } from "commander";
656
+ import { writeFileSync as writeFileSync2, existsSync as existsSync3 } from "fs";
657
+ import { resolve as resolve3 } from "path";
658
+ import ora2 from "ora";
659
+ var syncCommand = new Command4("sync").description("Projekt mit Cloud synchronisieren").option("-d, --dir <directory>", "Projektverzeichnis", ".").option("--pull", "Nur von Cloud herunterladen").option("--push", "Nur zu Cloud hochladen").action(async (options) => {
660
+ if (!isAuthenticated()) {
661
+ log.error('Nicht angemeldet. Bitte zuerst "shiva login" ausf\xFChren.');
662
+ return;
663
+ }
664
+ const projectDir = resolve3(options.dir);
665
+ if (!existsSync3(projectDir)) {
666
+ log.error(`Verzeichnis nicht gefunden: ${projectDir}`);
667
+ return;
668
+ }
669
+ const spinner = ora2("Synchronisiere...").start();
670
+ let scanned;
671
+ try {
672
+ scanned = await scanProject(projectDir);
673
+ } catch (error) {
674
+ spinner.fail("Fehler beim Scannen");
675
+ return;
676
+ }
677
+ let project = await api.findProjectByPath(scanned.path);
678
+ if (!project) {
679
+ spinner.text = "Erstelle Projekt...";
680
+ try {
681
+ const result = await api.createOrUpdateProject({
682
+ name: scanned.name,
683
+ path: scanned.path,
684
+ description: scanned.description,
685
+ metadata: {
686
+ language: scanned.language,
687
+ framework: scanned.framework,
688
+ packageManager: scanned.packageManager
689
+ },
690
+ claude_md_content: scanned.claudeMdContent
691
+ });
692
+ project = await api.getProject(result.projectId);
693
+ } catch (error) {
694
+ spinner.fail("Fehler beim Erstellen");
695
+ log.error(error instanceof Error ? error.message : "Unbekannter Fehler");
696
+ return;
697
+ }
698
+ }
699
+ let memories = [];
700
+ if (scanned.claudeMdContent) {
701
+ const parsed = parseClaudeMd(scanned.claudeMdContent);
702
+ memories = parsed.memories;
703
+ }
704
+ if (!options.pull) {
705
+ spinner.text = "Hochladen...";
706
+ try {
707
+ await api.syncProject(project.id, {
708
+ claude_md_content: scanned.claudeMdContent,
709
+ metadata: {
710
+ language: scanned.language,
711
+ framework: scanned.framework,
712
+ packageManager: scanned.packageManager
713
+ },
714
+ memories
715
+ });
716
+ } catch (error) {
717
+ spinner.fail("Fehler beim Hochladen");
718
+ log.error(error instanceof Error ? error.message : "Unbekannter Fehler");
719
+ return;
720
+ }
721
+ }
722
+ if (!options.push) {
723
+ spinner.text = "Aktualisiere CLAUDE.md...";
724
+ try {
725
+ project = await api.getProject(project.id);
726
+ const newClaudeMd = generateClaudeMd(scanned, project);
727
+ const claudeMdPath = resolve3(projectDir, "CLAUDE.md");
728
+ writeFileSync2(claudeMdPath, newClaudeMd);
729
+ } catch (error) {
730
+ spinner.fail("Fehler beim Aktualisieren");
731
+ log.error(error instanceof Error ? error.message : "Unbekannter Fehler");
732
+ return;
733
+ }
734
+ }
735
+ spinner.succeed("Synchronisiert");
736
+ log.newline();
737
+ log.tree.item("CLAUDE.md aktualisiert");
738
+ log.tree.item(`${project.memories?.length || 0} Memories synchronisiert`);
739
+ log.tree.item("Metadata aktualisiert", true);
740
+ log.newline();
741
+ log.success(`Sync erfolgreich (${(/* @__PURE__ */ new Date()).toLocaleTimeString()})`);
742
+ });
743
+
744
+ // src/commands/status.ts
745
+ import { Command as Command5 } from "commander";
746
+ import { existsSync as existsSync4 } from "fs";
747
+ import { resolve as resolve4 } from "path";
748
+ var statusCommand = new Command5("status").description("Status des aktuellen Projekts anzeigen").option("-d, --dir <directory>", "Projektverzeichnis", ".").action(async (options) => {
749
+ log.brand();
750
+ const projectDir = resolve4(options.dir);
751
+ if (!existsSync4(projectDir)) {
752
+ log.error(`Verzeichnis nicht gefunden: ${projectDir}`);
753
+ return;
754
+ }
755
+ let scanned;
756
+ try {
757
+ scanned = await scanProject(projectDir);
758
+ } catch (error) {
759
+ log.error("Fehler beim Scannen des Projekts");
760
+ return;
761
+ }
762
+ log.header("SHIVA Code Status");
763
+ log.keyValue("Projekt", scanned.name);
764
+ log.keyValue("Pfad", scanned.path);
765
+ if (scanned.language) log.keyValue("Sprache", scanned.language);
766
+ if (scanned.framework) log.keyValue("Framework", scanned.framework);
767
+ log.newline();
768
+ if (scanned.hasClaudeMd) {
769
+ log.listItem("CLAUDE.md vorhanden", "synced");
770
+ } else {
771
+ log.listItem("CLAUDE.md fehlt", "pending");
772
+ log.dim(' \u2192 F\xFChre "shiva init" aus');
773
+ }
774
+ if (!isAuthenticated()) {
775
+ log.newline();
776
+ log.listItem("Nicht angemeldet", "pending");
777
+ log.dim(' \u2192 F\xFChre "shiva login" aus');
778
+ return;
779
+ }
780
+ log.newline();
781
+ log.keyValue("Cloud Status", "...");
782
+ try {
783
+ const project = await api.findProjectByPath(scanned.path);
784
+ process.stdout.write("\x1B[1A\x1B[2K");
785
+ if (project) {
786
+ if (project.synced) {
787
+ log.keyValue("Cloud Sync", colors.green("\u2713 Synchronisiert"));
788
+ } else {
789
+ log.keyValue("Cloud Sync", colors.yellow("\u25CB Ausstehend"));
790
+ }
791
+ if (project.last_synced_at) {
792
+ const lastSync = new Date(project.last_synced_at);
793
+ const now = /* @__PURE__ */ new Date();
794
+ const diff = now.getTime() - lastSync.getTime();
795
+ const minutes = Math.floor(diff / 6e4);
796
+ const hours = Math.floor(minutes / 60);
797
+ const days = Math.floor(hours / 24);
798
+ let timeAgo;
799
+ if (days > 0) timeAgo = `vor ${days} Tag${days > 1 ? "en" : ""}`;
800
+ else if (hours > 0) timeAgo = `vor ${hours} Stunde${hours > 1 ? "n" : ""}`;
801
+ else if (minutes > 0) timeAgo = `vor ${minutes} Minute${minutes > 1 ? "n" : ""}`;
802
+ else timeAgo = "gerade eben";
803
+ log.keyValue("Letzter Sync", timeAgo);
804
+ }
805
+ log.newline();
806
+ log.keyValue("Memories", String(project.memories?.length || 0));
807
+ log.keyValue("Connections", String(project.connections?.length || 0));
808
+ } else {
809
+ log.keyValue("Cloud Sync", colors.yellow("\u25CB Nicht synchronisiert"));
810
+ log.dim(' \u2192 F\xFChre "shiva sync" aus');
811
+ }
812
+ log.newline();
813
+ const stats = await api.getStats();
814
+ const config2 = getConfig();
815
+ log.keyValue(
816
+ "Tier",
817
+ `${config2.tier?.toUpperCase() || "FREE"} (${stats.projects.total}/${stats.projects.limit} Projekte)`
818
+ );
819
+ } catch (error) {
820
+ process.stdout.write("\x1B[1A\x1B[2K");
821
+ log.keyValue("Cloud Sync", colors.red("\u2717 Offline"));
822
+ log.dim(" \u2192 Keine Verbindung zur Cloud");
823
+ }
824
+ });
825
+
826
+ // src/commands/projects.ts
827
+ import { Command as Command6 } from "commander";
828
+ import ora3 from "ora";
829
+ var projectsCommand = new Command6("projects").description("Alle Projekte auflisten").option("-v, --verbose", "Mehr Details anzeigen").action(async (options) => {
830
+ if (!isAuthenticated()) {
831
+ log.error('Nicht angemeldet. Bitte zuerst "shiva login" ausf\xFChren.');
832
+ return;
833
+ }
834
+ const spinner = ora3("Lade Projekte...").start();
835
+ try {
836
+ const [projects, stats] = await Promise.all([
837
+ api.getProjects(),
838
+ api.getStats()
839
+ ]);
840
+ spinner.stop();
841
+ log.newline();
842
+ log.header(`Deine Projekte (${stats.projects.total}/${stats.projects.limit})`);
843
+ log.newline();
844
+ if (projects.length === 0) {
845
+ log.dim("Keine Projekte gefunden.");
846
+ log.newline();
847
+ log.info('Starte mit "shiva init" in einem Projektverzeichnis.');
848
+ return;
849
+ }
850
+ projects.sort((a, b) => {
851
+ const aTime = new Date(a.updated_at).getTime();
852
+ const bTime = new Date(b.updated_at).getTime();
853
+ return bTime - aTime;
854
+ });
855
+ for (let i = 0; i < projects.length; i++) {
856
+ const project = projects[i];
857
+ const status = project.synced ? "synced" : "pending";
858
+ const statusIcon = project.synced ? colors.green("\u2713") : colors.yellow("\u25CB");
859
+ const statusText = project.synced ? "synced" : "pending";
860
+ let timeAgo = "nie";
861
+ if (project.last_synced_at) {
862
+ const lastSync = new Date(project.last_synced_at);
863
+ const now = /* @__PURE__ */ new Date();
864
+ const diff = now.getTime() - lastSync.getTime();
865
+ const minutes = Math.floor(diff / 6e4);
866
+ const hours = Math.floor(minutes / 60);
867
+ const days = Math.floor(hours / 24);
868
+ if (days > 0) timeAgo = `vor ${days}d`;
869
+ else if (hours > 0) timeAgo = `vor ${hours}h`;
870
+ else if (minutes > 0) timeAgo = `vor ${minutes}m`;
871
+ else timeAgo = "gerade";
872
+ }
873
+ const num = String(i + 1).padStart(2, " ");
874
+ const name = project.name.padEnd(20);
875
+ const syncStatus = statusText.padEnd(8);
876
+ console.log(
877
+ `${colors.dim(num + ".")} ${name} ${statusIcon} ${colors.dim(syncStatus)} ${colors.dim(timeAgo)}`
878
+ );
879
+ if (options.verbose) {
880
+ log.dim(` Pfad: ${project.path}`);
881
+ if (project.description) {
882
+ log.dim(` ${project.description}`);
883
+ }
884
+ }
885
+ }
886
+ log.newline();
887
+ const config2 = getConfig();
888
+ const usagePercent = stats.projects.total / stats.projects.limit * 100;
889
+ if (usagePercent >= 80) {
890
+ log.warn(`Du nutzt ${Math.round(usagePercent)}% deines Projekt-Limits.`);
891
+ if (config2.tier === "free") {
892
+ log.info("Upgrade auf Pro f\xFCr mehr Projekte: https://shiva.li/pricing");
893
+ }
894
+ }
895
+ } catch (error) {
896
+ spinner.fail("Fehler beim Laden");
897
+ log.error(error instanceof Error ? error.message : "Unbekannter Fehler");
898
+ }
899
+ });
900
+
901
+ // src/commands/connect.ts
902
+ import { Command as Command7 } from "commander";
903
+ import { existsSync as existsSync5 } from "fs";
904
+ import { resolve as resolve5 } from "path";
905
+ import ora4 from "ora";
906
+ var connectCommand = new Command7("connect").description("Zwei Projekte verbinden").argument("<project>", "Pfad zum anderen Projekt oder Projekt-ID").option("-t, --type <type>", "Verbindungstyp (related, depends_on, child_of)", "related").option("-d, --dir <directory>", "Aktuelles Projektverzeichnis", ".").action(async (targetProject, options) => {
907
+ if (!isAuthenticated()) {
908
+ log.error('Nicht angemeldet. Bitte zuerst "shiva login" ausf\xFChren.');
909
+ return;
910
+ }
911
+ const currentDir = resolve5(options.dir);
912
+ if (!existsSync5(currentDir)) {
913
+ log.error(`Verzeichnis nicht gefunden: ${currentDir}`);
914
+ return;
915
+ }
916
+ const spinner = ora4("Projekte werden verbunden...").start();
917
+ try {
918
+ const currentScanned = await scanProject(currentDir);
919
+ let currentProject = await api.findProjectByPath(currentScanned.path);
920
+ if (!currentProject) {
921
+ spinner.fail("Aktuelles Projekt nicht in Cloud gefunden");
922
+ log.info('F\xFChre zuerst "shiva sync" aus.');
923
+ return;
924
+ }
925
+ let targetProjectObj;
926
+ const targetId = parseInt(targetProject, 10);
927
+ if (!isNaN(targetId)) {
928
+ targetProjectObj = await api.getProject(targetId);
929
+ } else {
930
+ const targetPath = resolve5(targetProject);
931
+ if (!existsSync5(targetPath)) {
932
+ spinner.fail(`Ziel-Verzeichnis nicht gefunden: ${targetPath}`);
933
+ return;
934
+ }
935
+ const targetScanned = await scanProject(targetPath);
936
+ targetProjectObj = await api.findProjectByPath(targetScanned.path);
937
+ if (!targetProjectObj) {
938
+ spinner.fail("Ziel-Projekt nicht in Cloud gefunden");
939
+ log.info(`F\xFChre zuerst "shiva sync" in ${targetPath} aus.`);
940
+ return;
941
+ }
942
+ }
943
+ if (currentProject.id === targetProjectObj.id) {
944
+ spinner.fail("Ein Projekt kann nicht mit sich selbst verbunden werden.");
945
+ return;
946
+ }
947
+ await api.connectProjects(currentProject.id, targetProjectObj.id, options.type);
948
+ spinner.succeed("Projekte verbunden");
949
+ log.newline();
950
+ log.success(`${currentProject.name} \u2194 ${targetProjectObj.name}`);
951
+ log.dim(` Typ: ${options.type}`);
952
+ } catch (error) {
953
+ spinner.fail("Fehler beim Verbinden");
954
+ log.error(error instanceof Error ? error.message : "Unbekannter Fehler");
955
+ }
956
+ });
957
+ var disconnectCommand = new Command7("disconnect").description("Projekt-Verbindung trennen").argument("<project>", "Pfad zum anderen Projekt oder Projekt-ID").option("-d, --dir <directory>", "Aktuelles Projektverzeichnis", ".").action(async (targetProject, options) => {
958
+ if (!isAuthenticated()) {
959
+ log.error('Nicht angemeldet. Bitte zuerst "shiva login" ausf\xFChren.');
960
+ return;
961
+ }
962
+ const currentDir = resolve5(options.dir);
963
+ if (!existsSync5(currentDir)) {
964
+ log.error(`Verzeichnis nicht gefunden: ${currentDir}`);
965
+ return;
966
+ }
967
+ const spinner = ora4("Verbindung wird getrennt...").start();
968
+ try {
969
+ const currentScanned = await scanProject(currentDir);
970
+ const currentProject = await api.findProjectByPath(currentScanned.path);
971
+ if (!currentProject) {
972
+ spinner.fail("Aktuelles Projekt nicht in Cloud gefunden");
973
+ return;
974
+ }
975
+ let targetProjectObj;
976
+ const targetId = parseInt(targetProject, 10);
977
+ if (!isNaN(targetId)) {
978
+ targetProjectObj = await api.getProject(targetId);
979
+ } else {
980
+ const targetPath = resolve5(targetProject);
981
+ if (existsSync5(targetPath)) {
982
+ const targetScanned = await scanProject(targetPath);
983
+ targetProjectObj = await api.findProjectByPath(targetScanned.path);
984
+ }
985
+ }
986
+ if (!targetProjectObj) {
987
+ spinner.fail("Ziel-Projekt nicht gefunden");
988
+ return;
989
+ }
990
+ await api.disconnectProjects(currentProject.id, targetProjectObj.id);
991
+ spinner.succeed("Verbindung getrennt");
992
+ } catch (error) {
993
+ spinner.fail("Fehler beim Trennen");
994
+ log.error(error instanceof Error ? error.message : "Unbekannter Fehler");
995
+ }
996
+ });
997
+
998
+ // src/commands/config.ts
999
+ import { Command as Command8 } from "commander";
1000
+ var configCommand = new Command8("config").description("Konfiguration anzeigen oder \xE4ndern").argument("[key]", "Konfigurationsschl\xFCssel").argument("[value]", "Neuer Wert").option("-l, --list", "Alle Konfigurationswerte anzeigen").option("--reset", "Konfiguration zur\xFCcksetzen").action(async (key, value, options) => {
1001
+ if (options.reset) {
1002
+ clearConfig();
1003
+ log.success("Konfiguration zur\xFCckgesetzt");
1004
+ return;
1005
+ }
1006
+ const config2 = getConfig();
1007
+ if (options.list || !key && !value) {
1008
+ log.brand();
1009
+ log.header("Konfiguration");
1010
+ log.newline();
1011
+ log.keyValue("Config Pfad", CONFIG_PATH);
1012
+ log.newline();
1013
+ log.keyValue("API Endpoint", config2.apiEndpoint || "https://shiva-ai-api.slither-mutiplayer.workers.dev");
1014
+ log.keyValue("User", config2.email || colors.dim("nicht angemeldet"));
1015
+ log.keyValue("User ID", config2.userId ? String(config2.userId) : colors.dim("-"));
1016
+ log.keyValue("Tier", config2.tier?.toUpperCase() || "FREE");
1017
+ const token = getToken();
1018
+ if (token) {
1019
+ const masked = token.slice(0, 8) + "..." + token.slice(-4);
1020
+ log.keyValue("Token", masked);
1021
+ } else {
1022
+ log.keyValue("Token", colors.dim("nicht vorhanden"));
1023
+ }
1024
+ log.newline();
1025
+ log.dim("Verwendung: shiva config <key> <value>");
1026
+ log.dim("Beispiel: shiva config apiEndpoint https://custom.api.com");
1027
+ return;
1028
+ }
1029
+ if (key && !value) {
1030
+ const validKeys = ["apiEndpoint", "email", "userId", "tier", "token"];
1031
+ if (!validKeys.includes(key)) {
1032
+ log.error(`Unbekannter Schl\xFCssel: ${key}`);
1033
+ log.info(`G\xFCltige Schl\xFCssel: ${validKeys.join(", ")}`);
1034
+ return;
1035
+ }
1036
+ const configValue = key === "token" ? getToken() : config2[key];
1037
+ if (configValue) {
1038
+ if (key === "token") {
1039
+ const masked = String(configValue).slice(0, 8) + "..." + String(configValue).slice(-4);
1040
+ console.log(masked);
1041
+ } else {
1042
+ console.log(configValue);
1043
+ }
1044
+ } else {
1045
+ log.dim("(nicht gesetzt)");
1046
+ }
1047
+ return;
1048
+ }
1049
+ if (key && value) {
1050
+ const settableKeys = ["apiEndpoint"];
1051
+ if (!settableKeys.includes(key)) {
1052
+ log.error(`Schl\xFCssel "${key}" kann nicht manuell gesetzt werden`);
1053
+ log.info("Setzbarer Schl\xFCssel: apiEndpoint");
1054
+ log.dim('Andere Werte werden durch "shiva login" gesetzt.');
1055
+ return;
1056
+ }
1057
+ if (key === "apiEndpoint") {
1058
+ try {
1059
+ new URL(value);
1060
+ } catch {
1061
+ log.error("Ung\xFCltige URL");
1062
+ return;
1063
+ }
1064
+ }
1065
+ setConfig(key, value);
1066
+ log.success(`${key} aktualisiert`);
1067
+ log.keyValue(key, value);
1068
+ }
1069
+ });
1070
+ configCommand.command("get <key>").description("Konfigurationswert anzeigen").action((key) => {
1071
+ const config2 = getConfig();
1072
+ const validKeys = ["apiEndpoint", "email", "userId", "tier"];
1073
+ if (!validKeys.includes(key)) {
1074
+ log.error(`Unbekannter Schl\xFCssel: ${key}`);
1075
+ return;
1076
+ }
1077
+ const value = config2[key];
1078
+ if (value) {
1079
+ console.log(value);
1080
+ } else {
1081
+ log.dim("(nicht gesetzt)");
1082
+ }
1083
+ });
1084
+ configCommand.command("set <key> <value>").description("Konfigurationswert setzen").action((key, value) => {
1085
+ const settableKeys = ["apiEndpoint"];
1086
+ if (!settableKeys.includes(key)) {
1087
+ log.error(`Schl\xFCssel "${key}" kann nicht manuell gesetzt werden`);
1088
+ return;
1089
+ }
1090
+ if (key === "apiEndpoint") {
1091
+ try {
1092
+ new URL(value);
1093
+ } catch {
1094
+ log.error("Ung\xFCltige URL");
1095
+ return;
1096
+ }
1097
+ }
1098
+ setConfig(key, value);
1099
+ log.success(`${key} aktualisiert`);
1100
+ });
1101
+ configCommand.command("reset").description("Konfiguration zur\xFCcksetzen").action(() => {
1102
+ clearConfig();
1103
+ log.success("Konfiguration zur\xFCckgesetzt");
1104
+ });
1105
+
1106
+ // src/index.ts
1107
+ var program = new Command9();
1108
+ program.name("shiva").description("SHIVA Code - Makes Claude Code Persistent").version("0.1.0");
1109
+ program.addCommand(loginCommand);
1110
+ program.addCommand(logoutCommand);
1111
+ program.addCommand(initCommand);
1112
+ program.addCommand(syncCommand);
1113
+ program.addCommand(statusCommand);
1114
+ program.addCommand(projectsCommand);
1115
+ program.addCommand(connectCommand);
1116
+ program.addCommand(disconnectCommand);
1117
+ program.addCommand(configCommand);
1118
+ program.action(() => {
1119
+ log.brand();
1120
+ log.newline();
1121
+ log.plain("Verf\xFCgbare Befehle:");
1122
+ log.newline();
1123
+ log.plain(" shiva login Anmelden");
1124
+ log.plain(" shiva logout Abmelden");
1125
+ log.plain(" shiva init Projekt initialisieren");
1126
+ log.plain(" shiva sync Mit Cloud synchronisieren");
1127
+ log.plain(" shiva status Status anzeigen");
1128
+ log.plain(" shiva projects Alle Projekte auflisten");
1129
+ log.plain(" shiva connect Projekte verbinden");
1130
+ log.plain(" shiva disconnect Verbindung trennen");
1131
+ log.plain(" shiva config Konfiguration verwalten");
1132
+ log.newline();
1133
+ log.dim("Hilfe: shiva <befehl> --help");
1134
+ });
1135
+ program.exitOverride((err) => {
1136
+ if (err.code === "commander.help") {
1137
+ process.exit(0);
1138
+ }
1139
+ if (err.code === "commander.version") {
1140
+ process.exit(0);
1141
+ }
1142
+ process.exit(1);
1143
+ });
1144
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "shiva-code",
3
+ "version": "0.1.0",
4
+ "description": "Makes Claude Code Persistent - Cross-Project Memory CLI",
5
+ "author": "SHIVA AI",
6
+ "license": "MIT",
7
+ "bin": {
8
+ "shiva": "./dist/index.js"
9
+ },
10
+ "type": "module",
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsup src/index.ts --format esm --dts --clean",
16
+ "dev": "tsup src/index.ts --format esm --watch",
17
+ "start": "node dist/index.js",
18
+ "typecheck": "tsc --noEmit",
19
+ "prepublishOnly": "npm run build"
20
+ },
21
+ "dependencies": {
22
+ "chalk": "^5.3.0",
23
+ "commander": "^12.1.0",
24
+ "conf": "^13.0.1",
25
+ "inquirer": "^12.3.0",
26
+ "open": "^10.1.0",
27
+ "ora": "^8.1.1"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^22.10.0",
31
+ "tsup": "^8.3.5",
32
+ "typescript": "^5.7.2"
33
+ },
34
+ "engines": {
35
+ "node": ">=18"
36
+ },
37
+ "keywords": [
38
+ "claude",
39
+ "claude-code",
40
+ "ai",
41
+ "memory",
42
+ "cli",
43
+ "shiva"
44
+ ],
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "https://github.com/Aimtaim/shiva-code"
48
+ }
49
+ }