gistajs 0.0.6 → 0.0.8

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/dist/bin.cjs CHANGED
@@ -64,7 +64,7 @@ var import_node_process2 = __toESM(require("process"), 1);
64
64
  var tar = __toESM(require("tar"), 1);
65
65
 
66
66
  // src/git.ts
67
- var import_node_child_process = require("child_process");
67
+ var import_node_child_process2 = require("child_process");
68
68
 
69
69
  // src/prompt.ts
70
70
  var import_node_process = __toESM(require("process"), 1);
@@ -116,6 +116,24 @@ async function confirm(rl, message) {
116
116
  return answer === "y" || answer === "yes";
117
117
  }
118
118
 
119
+ // src/subprocess.ts
120
+ var import_node_child_process = require("child_process");
121
+ async function run(command, args, cwd) {
122
+ await new Promise((resolve2, reject) => {
123
+ let child = (0, import_node_child_process.spawn)(command, args, {
124
+ cwd,
125
+ stdio: "inherit"
126
+ });
127
+ child.once("error", reject);
128
+ child.once("exit", (code) => {
129
+ if (code === 0) resolve2();
130
+ else {
131
+ reject(new Error(`${command} ${args.join(" ")} exited with code ${code}`));
132
+ }
133
+ });
134
+ });
135
+ }
136
+
119
137
  // src/git.ts
120
138
  var defaultDeps = {
121
139
  promptForGitIdentity,
@@ -164,7 +182,7 @@ async function resolveGitIdentity(cwd, deps) {
164
182
  }
165
183
  function readGitConfig(cwd, key) {
166
184
  try {
167
- return (0, import_node_child_process.execFileSync)("git", ["config", "--get", key], {
185
+ return (0, import_node_child_process2.execFileSync)("git", ["config", "--get", key], {
168
186
  cwd,
169
187
  encoding: "utf-8"
170
188
  }).trim();
@@ -172,22 +190,6 @@ function readGitConfig(cwd, key) {
172
190
  return "";
173
191
  }
174
192
  }
175
- async function run(command, args, cwd) {
176
- await new Promise((resolve2, reject) => {
177
- let child = (0, import_node_child_process.spawn)(command, args, {
178
- cwd,
179
- stdio: "inherit"
180
- });
181
- child.once("error", reject);
182
- child.once("exit", (code) => {
183
- if (code === 0) resolve2();
184
- else
185
- reject(
186
- new Error(`${command} ${args.join(" ")} exited with code ${code}`)
187
- );
188
- });
189
- });
190
- }
191
193
 
192
194
  // src/create.ts
193
195
  async function createProject(starter, options) {
@@ -314,28 +316,160 @@ async function assertSafeProjectRoot(root) {
314
316
  await (0, import_promises2.mkdir)(parent, { recursive: true });
315
317
  }
316
318
 
319
+ // src/diff.ts
320
+ var import_node_child_process3 = require("child_process");
321
+ var import_promises3 = require("fs/promises");
322
+ var import_node_os2 = require("os");
323
+ var import_node_path2 = require("path");
324
+ var defaultDeps2 = {
325
+ mkdtemp: import_promises3.mkdtemp,
326
+ rm: import_promises3.rm,
327
+ run,
328
+ runOutput
329
+ };
330
+ async function diffStarter(starter, options, deps = defaultDeps2) {
331
+ if (!options.fromReleaseKey || !options.toReleaseKey) {
332
+ throw new Error("Diff requires both from and to release keys");
333
+ }
334
+ let root = await deps.mkdtemp((0, import_node_path2.join)((0, import_node_os2.tmpdir)(), "gistajs-diff-"));
335
+ let repoUrl = getStarterRepoUrl(starter);
336
+ let fromTag = resolveStarterTagName(starter.slug, options.fromReleaseKey);
337
+ let toTag = resolveStarterTagName(starter.slug, options.toReleaseKey);
338
+ try {
339
+ await git2(root, ["init", "-q"], deps);
340
+ await git2(root, ["remote", "add", "origin", repoUrl], deps);
341
+ await fetchTag(root, fromTag, repoUrl, deps);
342
+ await fetchTag(root, toTag, repoUrl, deps);
343
+ return await gitOutput(
344
+ root,
345
+ ["diff", "--stat", `refs/tags/${fromTag}`, `refs/tags/${toTag}`],
346
+ repoUrl,
347
+ deps
348
+ );
349
+ } finally {
350
+ await deps.rm(root, { recursive: true, force: true });
351
+ }
352
+ }
353
+ function resolveStarterTagName(starter, releaseKey) {
354
+ return `${starter}/${releaseKey}`;
355
+ }
356
+ function getStarterRepoUrl(starter) {
357
+ return `https://github.com/${starter.repo}.git`;
358
+ }
359
+ async function fetchTag(cwd, tag, repoUrl, deps) {
360
+ try {
361
+ await git2(
362
+ cwd,
363
+ ["fetch", "--quiet", "--no-tags", "origin", `refs/tags/${tag}:refs/tags/${tag}`],
364
+ deps
365
+ );
366
+ } catch (error) {
367
+ throw new Error(
368
+ `Failed to fetch tag ${tag} from ${repoUrl}: ${getErrorMessage(error)}`
369
+ );
370
+ }
371
+ }
372
+ async function git2(cwd, args, deps) {
373
+ await deps.run("git", args, cwd);
374
+ }
375
+ async function gitOutput(cwd, args, repoUrl, deps) {
376
+ try {
377
+ return await deps.runOutput("git", args, cwd);
378
+ } catch (error) {
379
+ throw new Error(
380
+ `Failed to diff starter snapshots from ${repoUrl}: ${getErrorMessage(error)}`
381
+ );
382
+ }
383
+ }
384
+ function getErrorMessage(error) {
385
+ return error instanceof Error ? error.message : String(error);
386
+ }
387
+ async function runOutput(command, args, cwd) {
388
+ return await new Promise((resolve2, reject) => {
389
+ let stdout = "";
390
+ let stderr = "";
391
+ let child = (0, import_node_child_process3.spawn)(command, args, {
392
+ cwd,
393
+ stdio: ["ignore", "pipe", "pipe"]
394
+ });
395
+ child.stdout.on("data", (chunk) => {
396
+ stdout += String(chunk);
397
+ });
398
+ child.stderr.on("data", (chunk) => {
399
+ stderr += String(chunk);
400
+ });
401
+ child.once("error", reject);
402
+ child.once("exit", (code) => {
403
+ if (code === 0) {
404
+ resolve2(stdout);
405
+ return;
406
+ }
407
+ let detail = stderr.trim();
408
+ reject(
409
+ new Error(
410
+ detail || `${command} ${args.join(" ")} exited with code ${code}`
411
+ )
412
+ );
413
+ });
414
+ });
415
+ }
416
+
317
417
  // src/cli.ts
318
- async function runCli(argv = import_node_process3.default.argv.slice(2)) {
418
+ var defaultDeps3 = {
419
+ loadCatalog,
420
+ createProject,
421
+ diffStarter,
422
+ promptForStarter,
423
+ stdout: console
424
+ };
425
+ var UsageError = class extends Error {
426
+ constructor(message) {
427
+ super(message);
428
+ this.name = "UsageError";
429
+ }
430
+ };
431
+ async function runCli(argv = import_node_process3.default.argv.slice(2), deps = defaultDeps3) {
319
432
  let [command, ...rest] = argv;
320
433
  if (!command || command === "--help" || command === "-h") {
321
- printHelp();
434
+ deps.stdout.log(getHelpText());
322
435
  return;
323
436
  }
324
- if (command !== "create") {
325
- throw new Error(`Unknown command: ${command}`);
326
- }
327
- let options = parseCreateArgs(rest);
328
- let catalog = await loadCatalog(options.catalogUrl);
329
- let starterName = options.starter || await promptForStarter(catalog);
330
- let starter = catalog.find((entry) => entry.slug === starterName);
331
- if (!starter) {
332
- throw new Error(`Unknown starter: ${starterName}`);
437
+ if (command === "create") {
438
+ let options = parseCreateArgs(rest);
439
+ if (!options.projectName) {
440
+ throw new UsageError("Project name is required");
441
+ }
442
+ let catalog = await deps.loadCatalog(options.catalogUrl);
443
+ let starterName = options.starter || await deps.promptForStarter(catalog);
444
+ let starter = catalog.find((entry) => entry.slug === starterName);
445
+ if (!starter) {
446
+ throw new UsageError(`Unknown starter: ${starterName}`);
447
+ }
448
+ let root = await deps.createProject(starter, options);
449
+ deps.stdout.log(`Created ${starter.slug} project in ${root}`);
450
+ return;
333
451
  }
334
- if (!options.projectName) {
335
- throw new Error("Project name is required");
452
+ if (command === "diff") {
453
+ let options = parseDiffArgs(rest);
454
+ if (!options.starter) {
455
+ throw new UsageError("Starter is required");
456
+ }
457
+ if (!options.fromReleaseKey) {
458
+ throw new UsageError("From release key is required");
459
+ }
460
+ if (!options.toReleaseKey) {
461
+ throw new UsageError("To release key is required");
462
+ }
463
+ let catalog = await deps.loadCatalog(options.catalogUrl);
464
+ let starter = catalog.find((entry) => entry.slug === options.starter);
465
+ if (!starter) {
466
+ throw new UsageError(`Unknown starter: ${options.starter}`);
467
+ }
468
+ let output = await deps.diffStarter(starter, options);
469
+ deps.stdout.log(output.trimEnd());
470
+ return;
336
471
  }
337
- let root = await createProject(starter, options);
338
- console.log(`Created ${starter.slug} project in ${root}`);
472
+ throw new UsageError(`Unknown command: ${command}`);
339
473
  }
340
474
  async function main() {
341
475
  try {
@@ -343,6 +477,9 @@ async function main() {
343
477
  } catch (error) {
344
478
  let message = error instanceof Error ? error.message : String(error);
345
479
  console.error(message);
480
+ if (error instanceof UsageError) {
481
+ console.error(getHelpText());
482
+ }
346
483
  import_node_process3.default.exitCode = 1;
347
484
  }
348
485
  }
@@ -359,7 +496,7 @@ function parseCreateArgs(argv) {
359
496
  continue;
360
497
  }
361
498
  if (arg === "--starter") {
362
- if (!argv[index + 1]) throw new Error("--starter requires a value");
499
+ if (!argv[index + 1]) throw new UsageError("--starter requires a value");
363
500
  options.starter = argv[index + 1];
364
501
  index += 1;
365
502
  continue;
@@ -373,22 +510,59 @@ function parseCreateArgs(argv) {
373
510
  continue;
374
511
  }
375
512
  if (arg === "--catalog-url") {
376
- if (!argv[index + 1]) throw new Error("--catalog-url requires a value");
513
+ if (!argv[index + 1]) {
514
+ throw new UsageError("--catalog-url requires a value");
515
+ }
377
516
  options.catalogUrl = argv[index + 1];
378
517
  index += 1;
379
518
  continue;
380
519
  }
381
- throw new Error(`Unknown argument: ${arg}`);
520
+ throw new UsageError(`Unknown argument: ${arg}`);
382
521
  }
383
522
  return options;
384
523
  }
385
- function printHelp() {
386
- console.log(
387
- [
388
- "Usage:",
389
- " gistajs create <project-name> [--starter <slug>] [--no-install] [--no-git]"
390
- ].join("\n")
391
- );
524
+ function parseDiffArgs(argv) {
525
+ let options = {};
526
+ for (let index = 0; index < argv.length; index += 1) {
527
+ let arg = argv[index];
528
+ if (!arg) continue;
529
+ if (!arg.startsWith("--") && !options.starter) {
530
+ options.starter = arg;
531
+ continue;
532
+ }
533
+ if (!arg.startsWith("--") && !options.fromReleaseKey) {
534
+ options.fromReleaseKey = arg;
535
+ continue;
536
+ }
537
+ if (!arg.startsWith("--") && !options.toReleaseKey) {
538
+ options.toReleaseKey = arg;
539
+ continue;
540
+ }
541
+ if (arg === "--catalog-url") {
542
+ if (!argv[index + 1]) {
543
+ throw new UsageError("--catalog-url requires a value");
544
+ }
545
+ options.catalogUrl = argv[index + 1];
546
+ index += 1;
547
+ continue;
548
+ }
549
+ throw new UsageError(`Unknown argument: ${arg}`);
550
+ }
551
+ return options;
552
+ }
553
+ function getHelpText() {
554
+ return [
555
+ "Usage:",
556
+ " gistajs create <project-name> [--starter <slug>] [--no-install] [--no-git]",
557
+ " gistajs diff <starter> <from-release-key> <to-release-key>",
558
+ "",
559
+ "Examples:",
560
+ " gistajs create my-app",
561
+ " gistajs create my-app --starter website",
562
+ " gistajs diff auth 2026-03-28-001 2026-03-29-001",
563
+ "",
564
+ "Run `gistajs` with no arguments to show this help."
565
+ ].join("\n");
392
566
  }
393
567
 
394
568
  // src/bin.ts
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { mkdtemp, rm } from 'node:fs/promises';
2
+
1
3
  type StarterSpec = {
2
4
  slug: string;
3
5
  repo: string;
@@ -12,9 +14,37 @@ type CreateOptions = {
12
14
  git?: boolean;
13
15
  catalogUrl?: string;
14
16
  };
17
+ type DiffOptions = {
18
+ starter?: string;
19
+ fromReleaseKey?: string;
20
+ toReleaseKey?: string;
21
+ catalogUrl?: string;
22
+ };
15
23
 
16
- declare function runCli(argv?: string[]): Promise<void>;
24
+ declare function loadCatalog(url?: string): Promise<StarterSpec[]>;
17
25
 
18
26
  declare function createProject(starter: StarterSpec, options: CreateOptions): Promise<string>;
19
27
 
20
- export { type CreateOptions, type StarterSpec, createProject, runCli };
28
+ declare function run(command: string, args: string[], cwd: string): Promise<void>;
29
+
30
+ type DiffDeps = {
31
+ mkdtemp: typeof mkdtemp;
32
+ rm: typeof rm;
33
+ run: typeof run;
34
+ runOutput: typeof runOutput;
35
+ };
36
+ declare function diffStarter(starter: StarterSpec, options: DiffOptions, deps?: DiffDeps): Promise<string>;
37
+ declare function runOutput(command: string, args: string[], cwd: string): Promise<string>;
38
+
39
+ declare function promptForStarter(starters: StarterSpec[]): Promise<string>;
40
+
41
+ type CliDeps = {
42
+ loadCatalog: typeof loadCatalog;
43
+ createProject: typeof createProject;
44
+ diffStarter: typeof diffStarter;
45
+ promptForStarter: typeof promptForStarter;
46
+ stdout: Pick<typeof console, 'log'>;
47
+ };
48
+ declare function runCli(argv?: string[], deps?: CliDeps): Promise<void>;
49
+
50
+ export { type CreateOptions, type DiffOptions, type StarterSpec, createProject, diffStarter, runCli };
package/dist/index.js CHANGED
@@ -49,7 +49,7 @@ import process2 from "process";
49
49
  import * as tar from "tar";
50
50
 
51
51
  // src/git.ts
52
- import { execFileSync, spawn } from "child_process";
52
+ import { execFileSync } from "child_process";
53
53
 
54
54
  // src/prompt.ts
55
55
  import process from "process";
@@ -101,6 +101,24 @@ async function confirm(rl, message) {
101
101
  return answer === "y" || answer === "yes";
102
102
  }
103
103
 
104
+ // src/subprocess.ts
105
+ import { spawn } from "child_process";
106
+ async function run(command, args, cwd) {
107
+ await new Promise((resolve2, reject) => {
108
+ let child = spawn(command, args, {
109
+ cwd,
110
+ stdio: "inherit"
111
+ });
112
+ child.once("error", reject);
113
+ child.once("exit", (code) => {
114
+ if (code === 0) resolve2();
115
+ else {
116
+ reject(new Error(`${command} ${args.join(" ")} exited with code ${code}`));
117
+ }
118
+ });
119
+ });
120
+ }
121
+
104
122
  // src/git.ts
105
123
  var defaultDeps = {
106
124
  promptForGitIdentity,
@@ -157,22 +175,6 @@ function readGitConfig(cwd, key) {
157
175
  return "";
158
176
  }
159
177
  }
160
- async function run(command, args, cwd) {
161
- await new Promise((resolve2, reject) => {
162
- let child = spawn(command, args, {
163
- cwd,
164
- stdio: "inherit"
165
- });
166
- child.once("error", reject);
167
- child.once("exit", (code) => {
168
- if (code === 0) resolve2();
169
- else
170
- reject(
171
- new Error(`${command} ${args.join(" ")} exited with code ${code}`)
172
- );
173
- });
174
- });
175
- }
176
178
 
177
179
  // src/create.ts
178
180
  async function createProject(starter, options) {
@@ -299,28 +301,160 @@ async function assertSafeProjectRoot(root) {
299
301
  await mkdir(parent, { recursive: true });
300
302
  }
301
303
 
304
+ // src/diff.ts
305
+ import { spawn as spawn2 } from "child_process";
306
+ import { mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
307
+ import { tmpdir as tmpdir2 } from "os";
308
+ import { join as join2 } from "path";
309
+ var defaultDeps2 = {
310
+ mkdtemp: mkdtemp2,
311
+ rm: rm2,
312
+ run,
313
+ runOutput
314
+ };
315
+ async function diffStarter(starter, options, deps = defaultDeps2) {
316
+ if (!options.fromReleaseKey || !options.toReleaseKey) {
317
+ throw new Error("Diff requires both from and to release keys");
318
+ }
319
+ let root = await deps.mkdtemp(join2(tmpdir2(), "gistajs-diff-"));
320
+ let repoUrl = getStarterRepoUrl(starter);
321
+ let fromTag = resolveStarterTagName(starter.slug, options.fromReleaseKey);
322
+ let toTag = resolveStarterTagName(starter.slug, options.toReleaseKey);
323
+ try {
324
+ await git2(root, ["init", "-q"], deps);
325
+ await git2(root, ["remote", "add", "origin", repoUrl], deps);
326
+ await fetchTag(root, fromTag, repoUrl, deps);
327
+ await fetchTag(root, toTag, repoUrl, deps);
328
+ return await gitOutput(
329
+ root,
330
+ ["diff", "--stat", `refs/tags/${fromTag}`, `refs/tags/${toTag}`],
331
+ repoUrl,
332
+ deps
333
+ );
334
+ } finally {
335
+ await deps.rm(root, { recursive: true, force: true });
336
+ }
337
+ }
338
+ function resolveStarterTagName(starter, releaseKey) {
339
+ return `${starter}/${releaseKey}`;
340
+ }
341
+ function getStarterRepoUrl(starter) {
342
+ return `https://github.com/${starter.repo}.git`;
343
+ }
344
+ async function fetchTag(cwd, tag, repoUrl, deps) {
345
+ try {
346
+ await git2(
347
+ cwd,
348
+ ["fetch", "--quiet", "--no-tags", "origin", `refs/tags/${tag}:refs/tags/${tag}`],
349
+ deps
350
+ );
351
+ } catch (error) {
352
+ throw new Error(
353
+ `Failed to fetch tag ${tag} from ${repoUrl}: ${getErrorMessage(error)}`
354
+ );
355
+ }
356
+ }
357
+ async function git2(cwd, args, deps) {
358
+ await deps.run("git", args, cwd);
359
+ }
360
+ async function gitOutput(cwd, args, repoUrl, deps) {
361
+ try {
362
+ return await deps.runOutput("git", args, cwd);
363
+ } catch (error) {
364
+ throw new Error(
365
+ `Failed to diff starter snapshots from ${repoUrl}: ${getErrorMessage(error)}`
366
+ );
367
+ }
368
+ }
369
+ function getErrorMessage(error) {
370
+ return error instanceof Error ? error.message : String(error);
371
+ }
372
+ async function runOutput(command, args, cwd) {
373
+ return await new Promise((resolve2, reject) => {
374
+ let stdout = "";
375
+ let stderr = "";
376
+ let child = spawn2(command, args, {
377
+ cwd,
378
+ stdio: ["ignore", "pipe", "pipe"]
379
+ });
380
+ child.stdout.on("data", (chunk) => {
381
+ stdout += String(chunk);
382
+ });
383
+ child.stderr.on("data", (chunk) => {
384
+ stderr += String(chunk);
385
+ });
386
+ child.once("error", reject);
387
+ child.once("exit", (code) => {
388
+ if (code === 0) {
389
+ resolve2(stdout);
390
+ return;
391
+ }
392
+ let detail = stderr.trim();
393
+ reject(
394
+ new Error(
395
+ detail || `${command} ${args.join(" ")} exited with code ${code}`
396
+ )
397
+ );
398
+ });
399
+ });
400
+ }
401
+
302
402
  // src/cli.ts
303
- async function runCli(argv = process3.argv.slice(2)) {
403
+ var defaultDeps3 = {
404
+ loadCatalog,
405
+ createProject,
406
+ diffStarter,
407
+ promptForStarter,
408
+ stdout: console
409
+ };
410
+ var UsageError = class extends Error {
411
+ constructor(message) {
412
+ super(message);
413
+ this.name = "UsageError";
414
+ }
415
+ };
416
+ async function runCli(argv = process3.argv.slice(2), deps = defaultDeps3) {
304
417
  let [command, ...rest] = argv;
305
418
  if (!command || command === "--help" || command === "-h") {
306
- printHelp();
419
+ deps.stdout.log(getHelpText());
307
420
  return;
308
421
  }
309
- if (command !== "create") {
310
- throw new Error(`Unknown command: ${command}`);
311
- }
312
- let options = parseCreateArgs(rest);
313
- let catalog = await loadCatalog(options.catalogUrl);
314
- let starterName = options.starter || await promptForStarter(catalog);
315
- let starter = catalog.find((entry) => entry.slug === starterName);
316
- if (!starter) {
317
- throw new Error(`Unknown starter: ${starterName}`);
422
+ if (command === "create") {
423
+ let options = parseCreateArgs(rest);
424
+ if (!options.projectName) {
425
+ throw new UsageError("Project name is required");
426
+ }
427
+ let catalog = await deps.loadCatalog(options.catalogUrl);
428
+ let starterName = options.starter || await deps.promptForStarter(catalog);
429
+ let starter = catalog.find((entry) => entry.slug === starterName);
430
+ if (!starter) {
431
+ throw new UsageError(`Unknown starter: ${starterName}`);
432
+ }
433
+ let root = await deps.createProject(starter, options);
434
+ deps.stdout.log(`Created ${starter.slug} project in ${root}`);
435
+ return;
318
436
  }
319
- if (!options.projectName) {
320
- throw new Error("Project name is required");
437
+ if (command === "diff") {
438
+ let options = parseDiffArgs(rest);
439
+ if (!options.starter) {
440
+ throw new UsageError("Starter is required");
441
+ }
442
+ if (!options.fromReleaseKey) {
443
+ throw new UsageError("From release key is required");
444
+ }
445
+ if (!options.toReleaseKey) {
446
+ throw new UsageError("To release key is required");
447
+ }
448
+ let catalog = await deps.loadCatalog(options.catalogUrl);
449
+ let starter = catalog.find((entry) => entry.slug === options.starter);
450
+ if (!starter) {
451
+ throw new UsageError(`Unknown starter: ${options.starter}`);
452
+ }
453
+ let output = await deps.diffStarter(starter, options);
454
+ deps.stdout.log(output.trimEnd());
455
+ return;
321
456
  }
322
- let root = await createProject(starter, options);
323
- console.log(`Created ${starter.slug} project in ${root}`);
457
+ throw new UsageError(`Unknown command: ${command}`);
324
458
  }
325
459
  function parseCreateArgs(argv) {
326
460
  let options = {
@@ -335,7 +469,7 @@ function parseCreateArgs(argv) {
335
469
  continue;
336
470
  }
337
471
  if (arg === "--starter") {
338
- if (!argv[index + 1]) throw new Error("--starter requires a value");
472
+ if (!argv[index + 1]) throw new UsageError("--starter requires a value");
339
473
  options.starter = argv[index + 1];
340
474
  index += 1;
341
475
  continue;
@@ -349,24 +483,62 @@ function parseCreateArgs(argv) {
349
483
  continue;
350
484
  }
351
485
  if (arg === "--catalog-url") {
352
- if (!argv[index + 1]) throw new Error("--catalog-url requires a value");
486
+ if (!argv[index + 1]) {
487
+ throw new UsageError("--catalog-url requires a value");
488
+ }
353
489
  options.catalogUrl = argv[index + 1];
354
490
  index += 1;
355
491
  continue;
356
492
  }
357
- throw new Error(`Unknown argument: ${arg}`);
493
+ throw new UsageError(`Unknown argument: ${arg}`);
358
494
  }
359
495
  return options;
360
496
  }
361
- function printHelp() {
362
- console.log(
363
- [
364
- "Usage:",
365
- " gistajs create <project-name> [--starter <slug>] [--no-install] [--no-git]"
366
- ].join("\n")
367
- );
497
+ function parseDiffArgs(argv) {
498
+ let options = {};
499
+ for (let index = 0; index < argv.length; index += 1) {
500
+ let arg = argv[index];
501
+ if (!arg) continue;
502
+ if (!arg.startsWith("--") && !options.starter) {
503
+ options.starter = arg;
504
+ continue;
505
+ }
506
+ if (!arg.startsWith("--") && !options.fromReleaseKey) {
507
+ options.fromReleaseKey = arg;
508
+ continue;
509
+ }
510
+ if (!arg.startsWith("--") && !options.toReleaseKey) {
511
+ options.toReleaseKey = arg;
512
+ continue;
513
+ }
514
+ if (arg === "--catalog-url") {
515
+ if (!argv[index + 1]) {
516
+ throw new UsageError("--catalog-url requires a value");
517
+ }
518
+ options.catalogUrl = argv[index + 1];
519
+ index += 1;
520
+ continue;
521
+ }
522
+ throw new UsageError(`Unknown argument: ${arg}`);
523
+ }
524
+ return options;
525
+ }
526
+ function getHelpText() {
527
+ return [
528
+ "Usage:",
529
+ " gistajs create <project-name> [--starter <slug>] [--no-install] [--no-git]",
530
+ " gistajs diff <starter> <from-release-key> <to-release-key>",
531
+ "",
532
+ "Examples:",
533
+ " gistajs create my-app",
534
+ " gistajs create my-app --starter website",
535
+ " gistajs diff auth 2026-03-28-001 2026-03-29-001",
536
+ "",
537
+ "Run `gistajs` with no arguments to show this help."
538
+ ].join("\n");
368
539
  }
369
540
  export {
370
541
  createProject,
542
+ diffStarter,
371
543
  runCli
372
544
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gistajs",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Create Gista.js starter projects",
5
5
  "keywords": [
6
6
  "cli",
@@ -48,8 +48,10 @@
48
48
  },
49
49
  "scripts": {
50
50
  "build": "tsup",
51
- "test": "vitest",
51
+ "test": "vitest run",
52
+ "test:watch": "vitest",
52
53
  "typecheck": "tsc -b",
53
- "np": "np"
54
+ "release:prepare": "pnpm typecheck && pnpm build && pnpm test",
55
+ "np": "pnpm release:prepare && np"
54
56
  }
55
57
  }