@raven.js/cli 0.0.1 → 1.0.0-alpha.14

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,23 @@
1
+ # @raven.js/cli
2
+
3
+ CLI tool for RavenJS framework.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g @raven.js/cli
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```bash
14
+ raven --help
15
+ ```
16
+
17
+ ## Updating
18
+
19
+ ```bash
20
+ npm update -g @raven.js/cli
21
+ ```
22
+
23
+ For more information, see https://github.com/myWsq/RavenJS
package/bin/raven ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+ const { spawn } = require('child_process');
3
+ const path = require('path');
4
+ const fs = require('fs');
5
+
6
+ const targets = [
7
+ 'linux-x64',
8
+ 'linux-arm64',
9
+ 'darwin-x64',
10
+ 'darwin-arm64',
11
+ 'windows-x64'
12
+ ];
13
+
14
+ let binaryPath = null;
15
+ for (const target of targets) {
16
+ try {
17
+ const pkgPath = require.resolve(`@raven.js/cli-${target}`);
18
+ const pkgDir = path.dirname(pkgPath);
19
+ const candidate = path.join(pkgDir, process.platform === 'win32' ? 'raven.exe' : 'raven');
20
+ if (fs.existsSync(candidate)) {
21
+ binaryPath = candidate;
22
+ break;
23
+ }
24
+ } catch (e) {
25
+ continue;
26
+ }
27
+ }
28
+
29
+ if (!binaryPath) {
30
+ console.error('No compatible binary found for your platform');
31
+ process.exit(1);
32
+ }
33
+
34
+ const child = spawn(binaryPath, process.argv.slice(2), { stdio: 'inherit' });
35
+ child.on('exit', (code) => process.exit(code || 0));
package/package.json CHANGED
@@ -1,34 +1,16 @@
1
1
  {
2
2
  "name": "@raven.js/cli",
3
- "version": "0.0.1",
3
+ "version": "1.0.0-alpha.14",
4
4
  "description": "CLI tool for RavenJS framework",
5
- "type": "module",
6
- "main": "./index.ts",
7
5
  "bin": {
8
- "raven": "./index.ts"
6
+ "raven": "./bin/raven"
9
7
  },
10
- "exports": {
11
- ".": "./index.ts"
12
- },
13
- "files": [
14
- "index.ts",
15
- "registry.json",
16
- "scripts/"
17
- ],
18
- "scripts": {
19
- "prepublishOnly": "bun run scripts/generate-registry.ts 0.0.1",
20
- "prebuild": "bun run scripts/generate-registry.ts",
21
- "build": "bun build ./index.ts --compile --minify --outfile raven"
22
- },
23
- "dependencies": {
24
- "@clack/prompts": "^1.0.1",
25
- "cac": "^6.7.14",
26
- "picocolors": "^1.1.1",
27
- "semver": "^7.7.4",
28
- "yaml": "^2.6.0"
29
- },
30
- "engines": {
31
- "bun": ">=1.0.0"
8
+ "optionalDependencies": {
9
+ "@raven.js/cli-linux-x64": "1.0.0-alpha.14",
10
+ "@raven.js/cli-linux-arm64": "1.0.0-alpha.14",
11
+ "@raven.js/cli-darwin-x64": "1.0.0-alpha.14",
12
+ "@raven.js/cli-darwin-arm64": "1.0.0-alpha.14",
13
+ "@raven.js/cli-windows-x64": "1.0.0-alpha.14"
32
14
  },
33
15
  "keywords": [
34
16
  "ravenjs",
@@ -40,5 +22,8 @@
40
22
  "type": "git",
41
23
  "url": "https://github.com/myWsq/RavenJS.git"
42
24
  },
43
- "license": "MIT"
44
- }
25
+ "files": [
26
+ "bin",
27
+ "README.md"
28
+ ]
29
+ }
package/index.ts DELETED
@@ -1,795 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- import { cac } from "cac";
4
- import { mkdir, rm, readdir, stat, chmod, rename } from "node:fs/promises";
5
- import { join, dirname, resolve, isAbsolute } from "path";
6
- import { cwd } from "process";
7
- import pc from "picocolors";
8
- import { spinner as makeSpinner, log } from "@clack/prompts";
9
- import { parse, stringify } from "yaml";
10
-
11
- // @ts-ignore -- registry.json should generated dynamically
12
- import registryPath from "./registry.json" with { type: "file" };
13
- const GITHUB_REPO = "myWsq/RavenJS";
14
- const GITHUB_RAW_URL = `https://raw.githubusercontent.com/${GITHUB_REPO}`;
15
- const DEFAULT_ROOT = "raven";
16
-
17
- interface CLIOptions {
18
- verbose?: boolean;
19
- root?: string;
20
- source?: string;
21
- prerelease?: boolean;
22
- }
23
-
24
- interface RegistryModule {
25
- files: string[];
26
- fileMapping?: Record<string, string>;
27
- dependencies?: Record<string, string>;
28
- }
29
-
30
- interface RegistryAi {
31
- claude: Record<string, string>;
32
- }
33
-
34
- interface Registry {
35
- version: string;
36
- modules: Record<string, RegistryModule>;
37
- ai: RegistryAi;
38
- }
39
-
40
- const registry = (await Bun.file(registryPath as unknown as string).json()) as Registry;
41
-
42
- function getRoot(options: CLIOptions): string {
43
- return options.root || process.env.RAVEN_ROOT || DEFAULT_ROOT;
44
- }
45
-
46
- function getSource(options: CLIOptions): string | undefined {
47
- return options.source || process.env.RAVEN_SOURCE;
48
- }
49
-
50
- function resolveSourcePath(source?: string): string | undefined {
51
- if (!source || source === "github") return undefined;
52
- return isAbsolute(source) ? source : resolve(cwd(), source);
53
- }
54
-
55
- async function verboseLog(message: string, options?: CLIOptions) {
56
- if (options?.verbose) {
57
- console.log(message);
58
- }
59
- }
60
-
61
- function error(message: string): never {
62
- // Use stderr for programmatic consumption (e.g. tests, piping). @clack/prompts
63
- // log.error writes to stdout which breaks stderr-based assertions.
64
- console.error(message);
65
- process.exit(1);
66
- }
67
-
68
- function info(message: string) {
69
- log.info(message);
70
- }
71
-
72
- function success(message: string) {
73
- log.success(message);
74
- }
75
-
76
- function printSectionHeader(title: string) {
77
- log.step(title);
78
- }
79
-
80
- function printListItem(item: string) {
81
- log.message(item, { symbol: pc.dim("-") });
82
- }
83
-
84
- async function ensureDir(path: string) {
85
- try {
86
- await mkdir(path, { recursive: true });
87
- } catch (e: any) {
88
- if (e.code !== "EEXIST") throw e;
89
- }
90
- }
91
-
92
- async function isDirEmpty(path: string): Promise<boolean> {
93
- try {
94
- const entries = await readdir(path);
95
- return entries.length === 0;
96
- } catch (e: any) {
97
- if (e.code === "ENOENT") return true;
98
- throw e;
99
- }
100
- }
101
-
102
- async function pathExists(path: string): Promise<boolean> {
103
- try {
104
- await stat(path);
105
- return true;
106
- } catch (e: any) {
107
- if (e.code === "ENOENT") return false;
108
- throw e;
109
- }
110
- }
111
-
112
- function getModuleNames(): string[] {
113
- return Object.keys(registry.modules);
114
- }
115
-
116
- // === SECTION: Self-Update ===
117
-
118
- import { gt as semverGt } from "semver";
119
-
120
- const GITHUB_API_RELEASES = `https://api.github.com/repos/${GITHUB_REPO}/releases`;
121
- const INSTALL_DIR = `${process.env.HOME || process.env.USERPROFILE || ""}/.local/bin`;
122
- const INSTALL_PATH = `${INSTALL_DIR}/raven`;
123
-
124
- function detectOs(): "linux" | "darwin" {
125
- switch (process.platform) {
126
- case "linux":
127
- return "linux";
128
- case "darwin":
129
- return "darwin";
130
- default:
131
- error(`unsupported OS: ${process.platform}`);
132
- }
133
- }
134
-
135
- function detectArch(): "x64" | "arm64" {
136
- switch (process.arch) {
137
- case "x64":
138
- return "x64";
139
- case "arm64":
140
- return "arm64";
141
- default:
142
- error(`unsupported architecture: ${process.arch}`);
143
- }
144
- }
145
-
146
- async function getLatestVersion(includePrerelease = false): Promise<string> {
147
- const response = await fetch(GITHUB_API_RELEASES);
148
- if (!response.ok) {
149
- error("failed to get latest version");
150
- }
151
- const releases = (await response.json()) as { tag_name: string }[];
152
- const tag = includePrerelease
153
- ? releases[0]?.tag_name
154
- : releases.map((r) => r.tag_name).find((t) => !t.includes("-"));
155
- if (!tag) {
156
- error("failed to get latest version");
157
- }
158
- return tag.replace(/^v/, "");
159
- }
160
-
161
- async function downloadBinary(
162
- version: string,
163
- os: string,
164
- arch: string,
165
- ): Promise<ArrayBuffer> {
166
- const url = `https://github.com/${GITHUB_REPO}/releases/download/v${version}/raven-${version}-${os}-${arch}`;
167
- const response = await fetch(url);
168
- if (!response.ok) {
169
- throw new Error(
170
- `download failed (${response.status}). Please check if version v${version} exists and supports ${os}-${arch}`,
171
- );
172
- }
173
- return response.arrayBuffer();
174
- }
175
-
176
- function isLocalBinInPath(): boolean {
177
- const pathEnv = process.env.PATH || "";
178
- return pathEnv.split(":").includes(INSTALL_DIR);
179
- }
180
-
181
- async function downloadFile(url: string, destPath: string): Promise<void> {
182
- const response = await fetch(url);
183
- if (!response.ok) {
184
- throw new Error(`Failed to download ${url}: ${response.status}`);
185
- }
186
- const content = await response.text();
187
- await ensureDir(dirname(destPath));
188
- await Bun.write(destPath, content);
189
- }
190
-
191
- async function copyLocalFile(srcPath: string, destPath: string): Promise<void> {
192
- const exists = await Bun.file(srcPath).exists();
193
- if (!exists) {
194
- throw new Error(`Missing local file: ${srcPath}`);
195
- }
196
- await ensureDir(dirname(destPath));
197
- await Bun.write(destPath, Bun.file(srcPath));
198
- }
199
-
200
- async function downloadModule(
201
- moduleName: string,
202
- version: string,
203
- destDir: string,
204
- options?: CLIOptions,
205
- targetSubdir?: string,
206
- ): Promise<string[]> {
207
- const module = registry.modules[moduleName];
208
- if (!module) {
209
- throw new Error(`Module ${moduleName} not found in registry`);
210
- }
211
-
212
- const sourcePath = resolveSourcePath(getSource(options || {}));
213
- if (sourcePath) {
214
- verboseLog(`Using local source: ${sourcePath}`, options);
215
- }
216
- verboseLog(`Downloading ${moduleName} files...`, options);
217
-
218
- const modifiedFiles: string[] = [];
219
- const downloads = module.files.map(async (file: string) => {
220
- let destPath: string;
221
-
222
- if (module.fileMapping && module.fileMapping[file]) {
223
- destPath = join(destDir, module.fileMapping[file]);
224
- } else if (targetSubdir) {
225
- destPath = join(destDir, targetSubdir, file);
226
- } else {
227
- destPath = join(destDir, moduleName, file);
228
- }
229
-
230
- verboseLog(` Downloading ${file}...`, options);
231
-
232
- if (sourcePath) {
233
- const primaryPath = join(sourcePath, "modules", moduleName, file);
234
- const fallbackPath = join(sourcePath, moduleName, file);
235
- let sourceFile: string;
236
- if (await Bun.file(primaryPath).exists()) {
237
- sourceFile = primaryPath;
238
- } else if (await Bun.file(fallbackPath).exists()) {
239
- sourceFile = fallbackPath;
240
- } else {
241
- throw new Error(`Missing local file: ${primaryPath}`);
242
- }
243
- await copyLocalFile(sourceFile, destPath);
244
- } else {
245
- const url = `${GITHUB_RAW_URL}/v${version}/modules/${moduleName}/${file}`;
246
- await downloadFile(url, destPath);
247
- }
248
- modifiedFiles.push(destPath);
249
- });
250
-
251
- await Promise.all(downloads);
252
- return modifiedFiles;
253
- }
254
-
255
- async function downloadAiResources(
256
- version: string,
257
- destDir: string,
258
- options?: CLIOptions,
259
- ): Promise<string[]> {
260
- const ai = registry.ai;
261
- if (!ai?.claude) {
262
- throw new Error("AI resources not found in registry");
263
- }
264
-
265
- const mapping = ai.claude;
266
- const entries = Object.entries(mapping);
267
-
268
- const sourcePath = resolveSourcePath(getSource(options || {}));
269
- if (sourcePath) {
270
- verboseLog(`Using local source: ${sourcePath}`, options);
271
- }
272
- verboseLog("Downloading AI resources...", options);
273
-
274
- const modifiedFiles: string[] = [];
275
- const downloads = entries.map(async ([file, destRel]) => {
276
- const destPath = join(destDir, destRel);
277
- verboseLog(` Downloading ${file}...`, options);
278
-
279
- if (sourcePath) {
280
- const sourceFile = join(sourcePath, "packages", "ai", file);
281
- await copyLocalFile(sourceFile, destPath);
282
- } else {
283
- const url = `${GITHUB_RAW_URL}/v${version}/packages/ai/${file}`;
284
- await downloadFile(url, destPath);
285
- }
286
- modifiedFiles.push(destPath);
287
- });
288
-
289
- await Promise.all(downloads);
290
- return modifiedFiles;
291
- }
292
-
293
- async function cmdInit(options: CLIOptions) {
294
- const targetDir = cwd();
295
-
296
- verboseLog(`Initializing RavenJS AI resources in ${targetDir}`, options);
297
-
298
- const dotClaudeDir = join(targetDir, ".claude");
299
-
300
- // Check if .claude already exists with content
301
- if (await pathExists(dotClaudeDir)) {
302
- const empty = await isDirEmpty(dotClaudeDir);
303
- if (!empty) {
304
- error(
305
- `AI resources already initialized at .claude/. Use 'raven update' to update.`,
306
- );
307
- }
308
- }
309
-
310
- const version = registry.version;
311
- const modifiedFiles: string[] = [];
312
-
313
- if (options?.verbose) {
314
- verboseLog(`Initializing RavenJS AI resources in ${targetDir}`, options);
315
- await ensureDir(dotClaudeDir);
316
- const aiFiles = await downloadAiResources(version, targetDir, options);
317
- modifiedFiles.push(...aiFiles);
318
- } else {
319
- const s = makeSpinner();
320
- s.start("Initializing RavenJS AI resources...");
321
- try {
322
- await ensureDir(dotClaudeDir);
323
- const aiFiles = await downloadAiResources(version, targetDir, options);
324
- modifiedFiles.push(...aiFiles);
325
- s.stop("Initializing RavenJS AI resources...");
326
- } catch (e: any) {
327
- s.stop("Initialization failed");
328
- error(e.message);
329
- }
330
- }
331
-
332
- success("RavenJS AI resources initialized successfully!");
333
-
334
- printSectionHeader("Modified Files");
335
- for (const file of modifiedFiles) {
336
- printListItem(file);
337
- }
338
- }
339
-
340
- interface RavenYamlConfig {
341
- version: string;
342
- }
343
-
344
- async function loadRavenYaml(ravenDir: string): Promise<string> {
345
- const yamlPath = join(ravenDir, "raven.yaml");
346
- try {
347
- const content = await Bun.file(yamlPath).text();
348
- const config = parse(content) as RavenYamlConfig;
349
- if (!config?.version) {
350
- throw new Error("Invalid raven.yaml: version field is missing");
351
- }
352
- return config.version;
353
- } catch (e: any) {
354
- throw new Error(`Failed to load raven.yaml: ${e.message}`);
355
- }
356
- }
357
-
358
- async function createRavenYaml(destDir: string, version: string) {
359
- const content = stringify({ version });
360
- await Bun.write(join(destDir, "raven.yaml"), content);
361
- }
362
-
363
- async function cmdInstall(options: CLIOptions) {
364
- const targetDir = cwd();
365
- const root = getRoot(options);
366
-
367
- verboseLog(`Installing RavenJS in ${targetDir}`, options);
368
-
369
- const ravenDir = join(targetDir, root);
370
-
371
- if (await pathExists(ravenDir)) {
372
- const empty = await isDirEmpty(ravenDir);
373
- if (!empty) {
374
- error(
375
- `RavenJS is already installed at ${root}/. Use 'raven update' to update.`,
376
- );
377
- }
378
- }
379
-
380
- const version = registry.version;
381
- const modifiedFiles: string[] = [];
382
-
383
- if (options?.verbose) {
384
- verboseLog(`Installing RavenJS in ${targetDir}`, options);
385
- await ensureDir(join(targetDir, root));
386
- const coreFiles = await downloadModule(
387
- "core",
388
- version,
389
- join(targetDir, root),
390
- options,
391
- );
392
- modifiedFiles.push(...coreFiles);
393
- } else {
394
- const s = makeSpinner();
395
- s.start("Installing RavenJS...");
396
- try {
397
- await ensureDir(join(targetDir, root));
398
- const coreFiles = await downloadModule(
399
- "core",
400
- version,
401
- join(targetDir, root),
402
- options,
403
- );
404
- modifiedFiles.push(...coreFiles);
405
- s.stop("Installing RavenJS...");
406
- } catch (e: any) {
407
- s.stop("Installation failed");
408
- error(e.message);
409
- }
410
- }
411
-
412
- await createRavenYaml(join(targetDir, root), version);
413
- modifiedFiles.push(join(targetDir, root, "raven.yaml"));
414
-
415
- success("RavenJS installed successfully!");
416
-
417
- printSectionHeader("Modified Files");
418
- for (const file of modifiedFiles) {
419
- printListItem(file);
420
- }
421
-
422
- const coreModule = registry.modules["core"];
423
- if (
424
- coreModule?.dependencies &&
425
- Object.keys(coreModule.dependencies).length > 0
426
- ) {
427
- printSectionHeader("Required Dependencies");
428
- for (const [pkg, ver] of Object.entries(coreModule.dependencies)) {
429
- printListItem(`${pkg}@${ver}`);
430
- }
431
- }
432
- }
433
-
434
- async function cmdAdd(moduleName: string, options: CLIOptions) {
435
- if (!moduleName) {
436
- error(
437
- `Please specify a module to add. Available: ${getModuleNames().join(", ")}`,
438
- );
439
- }
440
-
441
- const available = getModuleNames();
442
-
443
- if (!available.includes(moduleName)) {
444
- info(`Available modules: ${available.join(", ")}`);
445
- error(`Unknown module: ${moduleName}`);
446
- return;
447
- }
448
-
449
- const targetDir = cwd();
450
- const root = getRoot(options);
451
- const ravenDir = join(targetDir, root);
452
-
453
- if (!(await pathExists(ravenDir))) {
454
- error(`RavenJS not installed at ${root}/. Run 'raven install' first.`);
455
- }
456
-
457
- let version: string;
458
- try {
459
- version = await loadRavenYaml(ravenDir);
460
- } catch (e: any) {
461
- error(e.message);
462
- return;
463
- }
464
-
465
- let modifiedFiles: string[];
466
- if (options?.verbose) {
467
- verboseLog(`Adding ${moduleName}...`, options);
468
- modifiedFiles = await downloadModule(moduleName, version, ravenDir, options);
469
- } else {
470
- const s = makeSpinner();
471
- s.start(`Adding ${moduleName}...`);
472
- try {
473
- modifiedFiles = await downloadModule(
474
- moduleName,
475
- version,
476
- ravenDir,
477
- options,
478
- );
479
- s.stop(`Adding ${moduleName}...`);
480
- } catch (e: any) {
481
- s.stop("Add failed");
482
- error(e.message);
483
- }
484
- }
485
-
486
- success(`${moduleName} added successfully!`);
487
-
488
- printSectionHeader("Modified Files");
489
- for (const file of modifiedFiles) {
490
- printListItem(file);
491
- }
492
-
493
- const module = registry.modules[moduleName];
494
- if (module?.dependencies && Object.keys(module.dependencies).length > 0) {
495
- printSectionHeader("Required Dependencies");
496
- for (const [pkg, ver] of Object.entries(module.dependencies)) {
497
- printListItem(`${pkg}@${ver}`);
498
- }
499
- }
500
-
501
- log.message(
502
- `The module has been added to ${root}/${moduleName}\nSee ${root}/${moduleName}/README.md for usage.`,
503
- { symbol: pc.dim("~") },
504
- );
505
- }
506
-
507
- async function cmdUpdate(options: CLIOptions) {
508
- const targetDir = cwd();
509
- const root = getRoot(options);
510
- const ravenDir = join(targetDir, root);
511
- const dotClaudeDir = join(targetDir, ".claude");
512
-
513
- // Check if at least one of raven/ or .claude/ exists
514
- const ravenExists = await pathExists(ravenDir);
515
- const dotClaudeExists = await pathExists(dotClaudeDir);
516
-
517
- if (!ravenExists && !dotClaudeExists) {
518
- error(`RavenJS not installed. Run 'raven install' or 'raven init' first.`);
519
- }
520
-
521
- let version: string = registry.version;
522
- if (ravenExists) {
523
- try {
524
- version = await loadRavenYaml(ravenDir);
525
- } catch (e: any) {
526
- error(e.message);
527
- return;
528
- }
529
- }
530
-
531
- const modifiedFiles: string[] = [];
532
- const allDependencies: Record<string, string> = {};
533
-
534
- if (options?.verbose) {
535
- try {
536
- info(`Updating RavenJS in ${targetDir}...`);
537
-
538
- // Update framework modules if raven/ exists
539
- if (ravenExists) {
540
- const availableModules = getModuleNames();
541
- for (const moduleName of availableModules) {
542
- const moduleDir = join(ravenDir, moduleName);
543
- if (await Bun.file(moduleDir).exists()) {
544
- await rm(moduleDir, { recursive: true, force: true });
545
- }
546
- const files = await downloadModule(
547
- moduleName,
548
- version,
549
- ravenDir,
550
- options,
551
- );
552
- modifiedFiles.push(...files);
553
-
554
- const module = registry.modules[moduleName];
555
- if (module?.dependencies) {
556
- for (const [pkg, ver] of Object.entries(module.dependencies)) {
557
- allDependencies[pkg] = ver;
558
- }
559
- }
560
- }
561
- }
562
-
563
- // Update AI resources if .claude/ exists
564
- if (dotClaudeExists) {
565
- verboseLog("Updating AI resources...", options);
566
- const aiFiles = await downloadAiResources(version, targetDir, options);
567
- modifiedFiles.push(...aiFiles);
568
- }
569
- } catch (e: unknown) {
570
- error(e instanceof Error ? e.message : String(e));
571
- }
572
- } else {
573
- const s = makeSpinner();
574
- s.start("Updating RavenJS...");
575
- try {
576
- // Update framework modules if raven/ exists
577
- if (ravenExists) {
578
- const availableModules = getModuleNames();
579
- for (const moduleName of availableModules) {
580
- const moduleDir = join(ravenDir, moduleName);
581
- if (await Bun.file(moduleDir).exists()) {
582
- await rm(moduleDir, { recursive: true, force: true });
583
- }
584
- const files = await downloadModule(moduleName, version, ravenDir, options);
585
- modifiedFiles.push(...files);
586
-
587
- const module = registry.modules[moduleName];
588
- if (module?.dependencies) {
589
- for (const [pkg, ver] of Object.entries(module.dependencies)) {
590
- allDependencies[pkg] = ver;
591
- }
592
- }
593
- }
594
- }
595
-
596
- // Update AI resources if .claude/ exists
597
- if (dotClaudeExists) {
598
- const aiFiles = await downloadAiResources(version, targetDir, options);
599
- modifiedFiles.push(...aiFiles);
600
- }
601
-
602
- s.stop("Updating RavenJS...");
603
- } catch (e: any) {
604
- s.stop("Update failed");
605
- error(e.message);
606
- }
607
- }
608
-
609
- if (ravenExists) {
610
- await createRavenYaml(ravenDir, version);
611
- modifiedFiles.push(join(ravenDir, "raven.yaml"));
612
- }
613
-
614
- success("RavenJS updated successfully!");
615
-
616
- printSectionHeader("Modified Files");
617
- for (const file of modifiedFiles) {
618
- printListItem(file);
619
- }
620
-
621
- if (Object.keys(allDependencies).length > 0) {
622
- printSectionHeader("Required Dependencies");
623
- for (const [pkg, ver] of Object.entries(allDependencies)) {
624
- printListItem(`${pkg}@${ver}`);
625
- }
626
- }
627
- }
628
-
629
- // === SECTION: Status ===
630
-
631
- interface StatusResult {
632
- core: { installed: boolean };
633
- modules: string[];
634
- }
635
-
636
- async function getStatus(options: CLIOptions): Promise<StatusResult> {
637
- const targetDir = cwd();
638
- const root = getRoot(options);
639
- const ravenDir = join(targetDir, root);
640
-
641
- let coreInstalled = false;
642
- const installedModules: string[] = [];
643
- if (await pathExists(ravenDir)) {
644
- const coreDir = join(ravenDir, "core");
645
- coreInstalled =
646
- (await pathExists(coreDir)) && !(await isDirEmpty(coreDir));
647
-
648
- const knownModules = getModuleNames();
649
- const entries = await readdir(ravenDir, { withFileTypes: true });
650
- for (const entry of entries) {
651
- if (
652
- entry.isDirectory() &&
653
- entry.name !== "core" &&
654
- knownModules.includes(entry.name)
655
- ) {
656
- const modDir = join(ravenDir, entry.name);
657
- if (!(await isDirEmpty(modDir))) {
658
- installedModules.push(entry.name);
659
- }
660
- }
661
- }
662
- installedModules.sort();
663
- }
664
-
665
- return {
666
- core: { installed: coreInstalled },
667
- modules: installedModules,
668
- };
669
- }
670
-
671
- interface StatusCLIOptions extends CLIOptions {
672
- json?: boolean;
673
- }
674
-
675
- async function cmdStatus(options: StatusCLIOptions) {
676
- const status = await getStatus(options);
677
- if (options.json) {
678
- console.log(JSON.stringify(status));
679
- return;
680
- }
681
- printSectionHeader("RavenJS Status");
682
- printListItem(`core: ${status.core.installed ? "installed" : "not installed"}`);
683
- printListItem(
684
- `modules: ${status.modules.length > 0 ? status.modules.join(", ") : "none"}`,
685
- );
686
- }
687
-
688
- async function cmdSelfUpdate(options: CLIOptions) {
689
- info("Checking for updates...");
690
-
691
- const os = detectOs();
692
- const arch = detectArch();
693
-
694
- let latestVersion: string;
695
- try {
696
- latestVersion = (await getLatestVersion(options.prerelease)).replace(/^v/, "");
697
- } catch (e: unknown) {
698
- error(e instanceof Error ? e.message : "failed to get latest version");
699
- }
700
-
701
- const currentVersion = (registry.version || "").replace(/^v/, "");
702
-
703
- let shouldUpdate: boolean;
704
- try {
705
- shouldUpdate = semverGt(latestVersion, currentVersion);
706
- } catch {
707
- shouldUpdate = false;
708
- }
709
-
710
- if (!shouldUpdate) {
711
- success("Already up to date");
712
- return;
713
- }
714
-
715
- info(`detected system: ${os}-${arch}`);
716
- info(`installing version: v${latestVersion}`);
717
-
718
- let buffer: ArrayBuffer;
719
- try {
720
- buffer = await downloadBinary(latestVersion, os, arch);
721
- } catch (e: unknown) {
722
- error(
723
- e instanceof Error
724
- ? e.message
725
- : `download failed. Please check if version v${latestVersion} exists and supports ${os}-${arch}`,
726
- );
727
- }
728
-
729
- if (!buffer || buffer.byteLength === 0) {
730
- error(
731
- `download failed - file is empty. Please check if version v${latestVersion} exists and supports ${os}-${arch}`,
732
- );
733
- }
734
-
735
- await ensureDir(INSTALL_DIR);
736
- const tempPath = `${INSTALL_PATH}.${process.pid}.tmp`;
737
- try {
738
- await Bun.write(tempPath, buffer);
739
- await chmod(tempPath, 0o755);
740
- await rename(tempPath, INSTALL_PATH);
741
- } finally {
742
- try {
743
- await rm(tempPath, { force: true });
744
- } catch {
745
- /* ignore cleanup (file may already be renamed away) */
746
- }
747
- }
748
-
749
- success(`installed successfully to: ${INSTALL_PATH}`);
750
-
751
- if (!isLocalBinInPath()) {
752
- log.warn(`${INSTALL_DIR} is not in your PATH`);
753
- log.warn("add it to your shell config (e.g., ~/.bashrc, ~/.zshrc):");
754
- log.message(`export PATH="$HOME/.local/bin:$PATH"`, { symbol: pc.dim("~") });
755
- }
756
-
757
- log.message("done! run 'raven --help' to get started", { symbol: pc.dim("~") });
758
- }
759
-
760
- const cli = cac("raven");
761
-
762
- cli.version(registry.version).help();
763
-
764
- cli
765
- .option("--root <dir>", "RavenJS root directory (default: raven)")
766
- .option("--source <path>", "Local module source path (default: github)")
767
- .option("--verbose, -v", "Verbose output");
768
-
769
- cli
770
- .command("init", "Initialize RavenJS AI resources")
771
- .action((options) => cmdInit(options as CLIOptions));
772
-
773
- cli
774
- .command("install", "Install RavenJS into the current project")
775
- .action((options) => cmdInstall(options as CLIOptions));
776
-
777
- cli
778
- .command("add <module>", "Add a module (e.g., jtd-validator)")
779
- .action((module, options) => cmdAdd(module, options as CLIOptions));
780
-
781
- cli
782
- .command("update", "Update RavenJS to latest version")
783
- .action((options) => cmdUpdate(options as CLIOptions));
784
-
785
- cli
786
- .command("status", "Show RavenJS installation status (core, modules)")
787
- .option("--json", "Output as JSON for programmatic use")
788
- .action((options) => cmdStatus(options as StatusCLIOptions));
789
-
790
- cli
791
- .command("self-update", "Update RavenJS CLI to latest version")
792
- .option("--prerelease", "Include prerelease versions when checking for updates")
793
- .action((options) => cmdSelfUpdate(options as CLIOptions));
794
-
795
- cli.parse();
package/registry.json DELETED
@@ -1,29 +0,0 @@
1
- {
2
- "version": "0.0.1",
3
- "modules": {
4
- "core": {
5
- "files": [
6
- "index.ts",
7
- "main.ts",
8
- "README.md"
9
- ],
10
- "dependencies": {}
11
- },
12
- "jtd-validator": {
13
- "files": [
14
- "index.ts",
15
- "main.ts",
16
- "README.md"
17
- ],
18
- "dependencies": {
19
- "ajv": "^8.18.0"
20
- }
21
- }
22
- },
23
- "ai": {
24
- "claude": {
25
- "add/skill.md": ".claude/skills/raven-add/SKILL.md",
26
- "install/skill.md": ".claude/skills/raven-install/SKILL.md"
27
- }
28
- }
29
- }
@@ -1,96 +0,0 @@
1
- import { readdir, readFile, writeFile } from "fs/promises";
2
- import { join } from "path";
3
-
4
- const ROOT_DIR = join(import.meta.dir, "..", "..", "..");
5
- const MODULES_DIR = join(ROOT_DIR, "modules");
6
- const AI_PACKAGE_DIR = join(ROOT_DIR, "packages", "ai");
7
- const OUTPUT_DIR = join(ROOT_DIR, "packages", "cli");
8
- const OUTPUT_FILE = join(OUTPUT_DIR, "registry.json");
9
-
10
- interface ModuleInfo {
11
- files: string[];
12
- dependencies: Record<string, string>;
13
- }
14
-
15
- interface Registry {
16
- version: string;
17
- modules: Record<string, ModuleInfo>;
18
- ai: { claude: Record<string, string> };
19
- }
20
-
21
- async function scanModules(): Promise<Record<string, ModuleInfo>> {
22
- const modules: Record<string, ModuleInfo> = {};
23
-
24
- const entries = await readdir(MODULES_DIR, { withFileTypes: true });
25
-
26
- for (const entry of entries) {
27
- if (!entry.isDirectory()) continue;
28
-
29
- const moduleDir = join(MODULES_DIR, entry.name);
30
- const packageJsonPath = join(moduleDir, "package.json");
31
-
32
- try {
33
- const content = await readFile(packageJsonPath, "utf-8");
34
- const pkg = JSON.parse(content);
35
-
36
- if (!pkg.files) {
37
- console.warn(`Warning: ${entry.name} has no files field, skipping`);
38
- continue;
39
- }
40
-
41
- modules[entry.name] = {
42
- files: pkg.files,
43
- dependencies: pkg.dependencies || {},
44
- };
45
- } catch (e) {
46
- console.warn(`Warning: Could not read package.json for ${entry.name}`);
47
- }
48
- }
49
-
50
- return modules;
51
- }
52
-
53
- async function scanAi(): Promise<{ claude: Record<string, string> }> {
54
- const packageJsonPath = join(AI_PACKAGE_DIR, "package.json");
55
- const content = await readFile(packageJsonPath, "utf-8");
56
- const pkg = JSON.parse(content);
57
-
58
- const claude = pkg.claude;
59
- if (!claude || typeof claude !== "object") {
60
- throw new Error("packages/ai/package.json must have a 'claude' mapping");
61
- }
62
-
63
- return { claude };
64
- }
65
-
66
- async function generateRegistry(): Promise<void> {
67
- const args = process.argv.slice(2);
68
- const argVersion = args[0];
69
- const envVersion =
70
- process.env.RAVEN_VERSION ||
71
- process.env.RELEASE_VERSION ||
72
- process.env.CLI_VERSION;
73
- const version = argVersion || envVersion;
74
-
75
- if (!version) {
76
- console.error("Error: Version argument required");
77
- console.error("Usage: bun run scripts/generate-registry.ts <version>");
78
- process.exit(1);
79
- }
80
-
81
- const [modules, ai] = await Promise.all([scanModules(), scanAi()]);
82
-
83
- const registry: Registry = {
84
- version,
85
- modules,
86
- ai,
87
- };
88
-
89
- await writeFile(OUTPUT_FILE, JSON.stringify(registry, null, 2));
90
- console.log(`Registry generated at ${OUTPUT_FILE}`);
91
- console.log(`Version: ${version}`);
92
- console.log(`Modules: ${Object.keys(modules).join(", ")}`);
93
- console.log(`AI: ${Object.keys(ai.claude).length} files (claude)`);
94
- }
95
-
96
- generateRegistry().catch(console.error);