@keepgoingdev/cli 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +363 -33
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -52,17 +52,46 @@ function formatRelativeTime(timestamp) {
52
52
 
53
53
  // ../../packages/shared/src/gitUtils.ts
54
54
  import { execFileSync, execFile } from "child_process";
55
+ import path from "path";
55
56
  import { promisify } from "util";
56
57
  var execFileAsync = promisify(execFile);
57
58
  function findGitRoot(startPath) {
58
59
  try {
59
- const result = execFileSync("git", ["rev-parse", "--show-toplevel"], {
60
+ const gitCommonDir = execFileSync("git", ["rev-parse", "--git-common-dir"], {
60
61
  cwd: startPath,
61
62
  encoding: "utf-8",
62
63
  timeout: 5e3
63
- });
64
- return result.trim() || startPath;
64
+ }).trim();
65
+ if (!gitCommonDir) return startPath;
66
+ const absoluteGitDir = path.isAbsolute(gitCommonDir) ? gitCommonDir : path.resolve(startPath, gitCommonDir);
67
+ return path.dirname(absoluteGitDir);
68
+ } catch {
69
+ return startPath;
70
+ }
71
+ }
72
+ var storageRootCache = /* @__PURE__ */ new Map();
73
+ function resolveStorageRoot(startPath) {
74
+ const cached = storageRootCache.get(startPath);
75
+ if (cached !== void 0) {
76
+ return cached;
77
+ }
78
+ try {
79
+ const toplevel = execFileSync("git", ["rev-parse", "--show-toplevel"], {
80
+ cwd: startPath,
81
+ encoding: "utf-8",
82
+ timeout: 5e3
83
+ }).trim();
84
+ const commonDir = execFileSync("git", ["rev-parse", "--git-common-dir"], {
85
+ cwd: startPath,
86
+ encoding: "utf-8",
87
+ timeout: 5e3
88
+ }).trim();
89
+ const absoluteCommonDir = path.resolve(toplevel, commonDir);
90
+ const mainRoot = path.dirname(absoluteCommonDir);
91
+ storageRootCache.set(startPath, mainRoot);
92
+ return mainRoot;
65
93
  } catch {
94
+ storageRootCache.set(startPath, startPath);
66
95
  return startPath;
67
96
  }
68
97
  }
@@ -102,7 +131,7 @@ function getRecentSessions(allSessions, count = RECENT_SESSION_COUNT) {
102
131
 
103
132
  // ../../packages/shared/src/storage.ts
104
133
  import fs from "fs";
105
- import path from "path";
134
+ import path2 from "path";
106
135
  import { randomUUID as randomUUID2 } from "crypto";
107
136
  var STORAGE_DIR = ".keepgoing";
108
137
  var META_FILE = "meta.json";
@@ -114,10 +143,11 @@ var KeepGoingWriter = class {
114
143
  stateFilePath;
115
144
  metaFilePath;
116
145
  constructor(workspacePath) {
117
- this.storagePath = path.join(workspacePath, STORAGE_DIR);
118
- this.sessionsFilePath = path.join(this.storagePath, SESSIONS_FILE);
119
- this.stateFilePath = path.join(this.storagePath, STATE_FILE);
120
- this.metaFilePath = path.join(this.storagePath, META_FILE);
146
+ const mainRoot = resolveStorageRoot(workspacePath);
147
+ this.storagePath = path2.join(mainRoot, STORAGE_DIR);
148
+ this.sessionsFilePath = path2.join(this.storagePath, SESSIONS_FILE);
149
+ this.stateFilePath = path2.join(this.storagePath, STATE_FILE);
150
+ this.metaFilePath = path2.join(this.storagePath, META_FILE);
121
151
  }
122
152
  ensureDir() {
123
153
  if (!fs.existsSync(this.storagePath)) {
@@ -182,7 +212,7 @@ var KeepGoingWriter = class {
182
212
 
183
213
  // ../../packages/shared/src/decisionStorage.ts
184
214
  import fs2 from "fs";
185
- import path2 from "path";
215
+ import path3 from "path";
186
216
 
187
217
  // ../../packages/shared/src/featureGate.ts
188
218
  var DefaultFeatureGate = class {
@@ -192,9 +222,152 @@ var DefaultFeatureGate = class {
192
222
  };
193
223
  var currentGate = new DefaultFeatureGate();
194
224
 
195
- // src/storage.ts
225
+ // ../../packages/shared/src/license.ts
226
+ import crypto from "crypto";
196
227
  import fs3 from "fs";
197
- import path3 from "path";
228
+ import os from "os";
229
+ import path4 from "path";
230
+ var LICENSE_FILE = "license.json";
231
+ var DEVICE_ID_FILE = "device-id";
232
+ function getGlobalLicenseDir() {
233
+ return path4.join(os.homedir(), ".keepgoing");
234
+ }
235
+ function getGlobalLicensePath() {
236
+ return path4.join(getGlobalLicenseDir(), LICENSE_FILE);
237
+ }
238
+ function getDeviceId() {
239
+ const dir = getGlobalLicenseDir();
240
+ const filePath = path4.join(dir, DEVICE_ID_FILE);
241
+ try {
242
+ const existing = fs3.readFileSync(filePath, "utf-8").trim();
243
+ if (existing) return existing;
244
+ } catch {
245
+ }
246
+ const id = crypto.randomUUID();
247
+ if (!fs3.existsSync(dir)) {
248
+ fs3.mkdirSync(dir, { recursive: true });
249
+ }
250
+ fs3.writeFileSync(filePath, id, "utf-8");
251
+ return id;
252
+ }
253
+ function readLicenseCache() {
254
+ const licensePath = getGlobalLicensePath();
255
+ try {
256
+ if (!fs3.existsSync(licensePath)) {
257
+ return void 0;
258
+ }
259
+ const raw = fs3.readFileSync(licensePath, "utf-8");
260
+ return JSON.parse(raw);
261
+ } catch {
262
+ return void 0;
263
+ }
264
+ }
265
+ function writeLicenseCache(cache) {
266
+ const dirPath = getGlobalLicenseDir();
267
+ if (!fs3.existsSync(dirPath)) {
268
+ fs3.mkdirSync(dirPath, { recursive: true });
269
+ }
270
+ const licensePath = path4.join(dirPath, LICENSE_FILE);
271
+ fs3.writeFileSync(licensePath, JSON.stringify(cache, null, 2), "utf-8");
272
+ }
273
+ function deleteLicenseCache() {
274
+ const licensePath = getGlobalLicensePath();
275
+ try {
276
+ if (fs3.existsSync(licensePath)) {
277
+ fs3.unlinkSync(licensePath);
278
+ }
279
+ } catch {
280
+ }
281
+ }
282
+ function isCachedLicenseValid(cache) {
283
+ return cache?.status === "active";
284
+ }
285
+ var REVALIDATION_THRESHOLD_MS = 24 * 60 * 60 * 1e3;
286
+
287
+ // ../../packages/shared/src/licenseClient.ts
288
+ var BASE_URL = "https://api.lemonsqueezy.com/v1/licenses";
289
+ var REQUEST_TIMEOUT_MS = 15e3;
290
+ var EXPECTED_STORE_ID = 301555;
291
+ var EXPECTED_PRODUCT_ID = 864311;
292
+ function fetchWithTimeout(url, init) {
293
+ const controller = new AbortController();
294
+ const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
295
+ return fetch(url, { ...init, signal: controller.signal }).finally(() => clearTimeout(timer));
296
+ }
297
+ function validateProductIdentity(meta) {
298
+ if (!meta) return "License response missing product metadata.";
299
+ if (meta.store_id !== EXPECTED_STORE_ID || meta.product_id !== EXPECTED_PRODUCT_ID) {
300
+ return "This license key does not belong to KeepGoing.";
301
+ }
302
+ return void 0;
303
+ }
304
+ async function safeJson(res) {
305
+ try {
306
+ const text = await res.text();
307
+ return JSON.parse(text);
308
+ } catch {
309
+ return null;
310
+ }
311
+ }
312
+ async function activateLicense(licenseKey, instanceName, options) {
313
+ try {
314
+ const res = await fetchWithTimeout(`${BASE_URL}/activate`, {
315
+ method: "POST",
316
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
317
+ body: new URLSearchParams({ license_key: licenseKey, instance_name: instanceName })
318
+ });
319
+ const data = await safeJson(res);
320
+ if (!res.ok || !data?.activated) {
321
+ return { valid: false, error: data?.error || `Activation failed (${res.status})` };
322
+ }
323
+ if (!options?.allowTestMode && data.license_key?.test_mode) {
324
+ if (data.license_key?.key && data.instance?.id) {
325
+ await deactivateLicense(data.license_key.key, data.instance.id);
326
+ }
327
+ return { valid: false, error: "This is a test license key. Please use a production license key from your purchase confirmation." };
328
+ }
329
+ if (!options?.allowTestMode) {
330
+ const productError = validateProductIdentity(data.meta);
331
+ if (productError) {
332
+ if (data.license_key?.key && data.instance?.id) {
333
+ await deactivateLicense(data.license_key.key, data.instance.id);
334
+ }
335
+ return { valid: false, error: productError };
336
+ }
337
+ }
338
+ return {
339
+ valid: true,
340
+ licenseKey: data.license_key?.key,
341
+ instanceId: data.instance?.id,
342
+ customerName: data.meta?.customer_name,
343
+ productName: data.meta?.product_name
344
+ };
345
+ } catch (err) {
346
+ const message = err instanceof Error && err.name === "AbortError" ? "Request timed out. Please check your network connection and try again." : err instanceof Error ? err.message : "Network error";
347
+ return { valid: false, error: message };
348
+ }
349
+ }
350
+ async function deactivateLicense(licenseKey, instanceId) {
351
+ try {
352
+ const res = await fetchWithTimeout(`${BASE_URL}/deactivate`, {
353
+ method: "POST",
354
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
355
+ body: new URLSearchParams({ license_key: licenseKey, instance_id: instanceId })
356
+ });
357
+ const data = await safeJson(res);
358
+ if (!res.ok || !data?.deactivated) {
359
+ return { deactivated: false, error: data?.error || `Deactivation failed (${res.status})` };
360
+ }
361
+ return { deactivated: true };
362
+ } catch (err) {
363
+ const message = err instanceof Error && err.name === "AbortError" ? "Request timed out. Please check your network connection and try again." : err instanceof Error ? err.message : "Network error";
364
+ return { deactivated: false, error: message };
365
+ }
366
+ }
367
+
368
+ // src/storage.ts
369
+ import fs4 from "fs";
370
+ import path5 from "path";
198
371
  var STORAGE_DIR2 = ".keepgoing";
199
372
  var META_FILE2 = "meta.json";
200
373
  var SESSIONS_FILE2 = "sessions.json";
@@ -205,13 +378,13 @@ var KeepGoingReader = class {
205
378
  sessionsFilePath;
206
379
  stateFilePath;
207
380
  constructor(workspacePath) {
208
- this.storagePath = path3.join(workspacePath, STORAGE_DIR2);
209
- this.metaFilePath = path3.join(this.storagePath, META_FILE2);
210
- this.sessionsFilePath = path3.join(this.storagePath, SESSIONS_FILE2);
211
- this.stateFilePath = path3.join(this.storagePath, STATE_FILE2);
381
+ this.storagePath = path5.join(workspacePath, STORAGE_DIR2);
382
+ this.metaFilePath = path5.join(this.storagePath, META_FILE2);
383
+ this.sessionsFilePath = path5.join(this.storagePath, SESSIONS_FILE2);
384
+ this.stateFilePath = path5.join(this.storagePath, STATE_FILE2);
212
385
  }
213
386
  exists() {
214
- return fs3.existsSync(this.storagePath);
387
+ return fs4.existsSync(this.storagePath);
215
388
  }
216
389
  getState() {
217
390
  return this.readJsonFile(this.stateFilePath);
@@ -249,8 +422,8 @@ var KeepGoingReader = class {
249
422
  }
250
423
  readJsonFile(filePath) {
251
424
  try {
252
- if (!fs3.existsSync(filePath)) return void 0;
253
- const raw = fs3.readFileSync(filePath, "utf-8");
425
+ if (!fs4.existsSync(filePath)) return void 0;
426
+ const raw = fs4.readFileSync(filePath, "utf-8");
254
427
  return JSON.parse(raw);
255
428
  } catch {
256
429
  return void 0;
@@ -305,7 +478,96 @@ function renderNoData() {
305
478
  );
306
479
  }
307
480
 
481
+ // src/updateCheck.ts
482
+ import { spawn } from "child_process";
483
+ import { readFileSync, existsSync } from "fs";
484
+ import path6 from "path";
485
+ import os2 from "os";
486
+ var CLI_VERSION = "0.2.1";
487
+ var NPM_REGISTRY_URL = "https://registry.npmjs.org/@keepgoingdev/cli/latest";
488
+ var FETCH_TIMEOUT_MS = 5e3;
489
+ var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
490
+ var CACHE_DIR = path6.join(os2.homedir(), ".keepgoing");
491
+ var CACHE_PATH = path6.join(CACHE_DIR, "update-check.json");
492
+ function isNewerVersion(current, latest) {
493
+ const cur = current.split(".").map(Number);
494
+ const lat = latest.split(".").map(Number);
495
+ for (let i = 0; i < 3; i++) {
496
+ if ((lat[i] ?? 0) > (cur[i] ?? 0)) return true;
497
+ if ((lat[i] ?? 0) < (cur[i] ?? 0)) return false;
498
+ }
499
+ return false;
500
+ }
501
+ function getCachedUpdateInfo() {
502
+ try {
503
+ if (!existsSync(CACHE_PATH)) return null;
504
+ const raw = readFileSync(CACHE_PATH, "utf-8");
505
+ const cache = JSON.parse(raw);
506
+ if (!cache.latest || !cache.checkedAt) return null;
507
+ const age = Date.now() - new Date(cache.checkedAt).getTime();
508
+ if (age > CHECK_INTERVAL_MS) return null;
509
+ return {
510
+ current: CLI_VERSION,
511
+ latest: cache.latest,
512
+ updateAvailable: isNewerVersion(CLI_VERSION, cache.latest)
513
+ };
514
+ } catch {
515
+ return null;
516
+ }
517
+ }
518
+ function spawnBackgroundCheck() {
519
+ try {
520
+ if (existsSync(CACHE_PATH)) {
521
+ const raw = readFileSync(CACHE_PATH, "utf-8");
522
+ const cache = JSON.parse(raw);
523
+ const age = Date.now() - new Date(cache.checkedAt).getTime();
524
+ if (age < CHECK_INTERVAL_MS) return;
525
+ }
526
+ } catch {
527
+ }
528
+ const script = `
529
+ const https = require('https');
530
+ const fs = require('fs');
531
+ const path = require('path');
532
+ const os = require('os');
533
+
534
+ const url = ${JSON.stringify(NPM_REGISTRY_URL)};
535
+ const cacheDir = path.join(os.homedir(), '.keepgoing');
536
+ const cachePath = path.join(cacheDir, 'update-check.json');
537
+ const currentVersion = ${JSON.stringify(CLI_VERSION)};
538
+
539
+ const req = https.get(url, { timeout: ${FETCH_TIMEOUT_MS} }, (res) => {
540
+ let data = '';
541
+ res.on('data', (chunk) => { data += chunk; });
542
+ res.on('end', () => {
543
+ try {
544
+ const latest = JSON.parse(data).version;
545
+ if (!latest) process.exit(0);
546
+ if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true });
547
+ fs.writeFileSync(cachePath, JSON.stringify({
548
+ latest,
549
+ current: currentVersion,
550
+ checkedAt: new Date().toISOString(),
551
+ }));
552
+ } catch {}
553
+ process.exit(0);
554
+ });
555
+ });
556
+ req.on('error', () => process.exit(0));
557
+ req.on('timeout', () => { req.destroy(); process.exit(0); });
558
+ `;
559
+ const child = spawn(process.execPath, ["-e", script], {
560
+ detached: true,
561
+ stdio: "ignore",
562
+ env: { ...process.env }
563
+ });
564
+ child.unref();
565
+ }
566
+
308
567
  // src/commands/status.ts
568
+ var RESET2 = "\x1B[0m";
569
+ var BOLD2 = "\x1B[1m";
570
+ var DIM2 = "\x1B[2m";
309
571
  async function statusCommand(opts) {
310
572
  const reader = new KeepGoingReader(opts.cwd);
311
573
  if (!reader.exists()) {
@@ -333,11 +595,16 @@ async function statusCommand(opts) {
333
595
  (Date.now() - new Date(lastSession.timestamp).getTime()) / (1e3 * 60 * 60 * 24)
334
596
  );
335
597
  renderCheckpoint(lastSession, daysSince);
598
+ const cached = getCachedUpdateInfo();
599
+ if (cached?.updateAvailable) {
600
+ console.log(`${DIM2}Update available: ${cached.current} \u2192 ${cached.latest}. Run: ${RESET2}${BOLD2}npm install -g @keepgoingdev/cli@latest${RESET2}`);
601
+ }
602
+ spawnBackgroundCheck();
336
603
  }
337
604
 
338
605
  // src/commands/save.ts
339
606
  import readline from "readline";
340
- import path4 from "path";
607
+ import path7 from "path";
341
608
  function prompt(rl, question) {
342
609
  return new Promise((resolve) => {
343
610
  rl.question(question, (answer) => {
@@ -381,16 +648,16 @@ async function saveCommand(opts) {
381
648
  workspaceRoot: opts.cwd,
382
649
  source: "manual"
383
650
  });
384
- const projectName = path4.basename(opts.cwd);
651
+ const projectName = path7.basename(opts.cwd);
385
652
  const writer = new KeepGoingWriter(opts.cwd);
386
653
  writer.saveCheckpoint(checkpoint, projectName);
387
654
  console.log("Checkpoint saved.");
388
655
  }
389
656
 
390
657
  // src/commands/hook.ts
391
- import fs4 from "fs";
392
- import path5 from "path";
393
- import os from "os";
658
+ import fs5 from "fs";
659
+ import path8 from "path";
660
+ import os3 from "os";
394
661
  var HOOK_MARKER_START = "# keepgoing-hook-start";
395
662
  var HOOK_MARKER_END = "# keepgoing-hook-end";
396
663
  var ZSH_HOOK = `${HOOK_MARKER_START}
@@ -416,12 +683,12 @@ fi
416
683
  ${HOOK_MARKER_END}`;
417
684
  function detectShellRcFile() {
418
685
  const shellEnv = process.env["SHELL"] ?? "";
419
- const home = os.homedir();
686
+ const home = os3.homedir();
420
687
  if (shellEnv.endsWith("zsh")) {
421
- return { shell: "zsh", rcFile: path5.join(home, ".zshrc") };
688
+ return { shell: "zsh", rcFile: path8.join(home, ".zshrc") };
422
689
  }
423
690
  if (shellEnv.endsWith("bash")) {
424
- return { shell: "bash", rcFile: path5.join(home, ".bashrc") };
691
+ return { shell: "bash", rcFile: path8.join(home, ".bashrc") };
425
692
  }
426
693
  return void 0;
427
694
  }
@@ -437,14 +704,14 @@ function hookInstallCommand() {
437
704
  const hookBlock = shell === "zsh" ? ZSH_HOOK : BASH_HOOK;
438
705
  let existing = "";
439
706
  try {
440
- existing = fs4.readFileSync(rcFile, "utf-8");
707
+ existing = fs5.readFileSync(rcFile, "utf-8");
441
708
  } catch {
442
709
  }
443
710
  if (existing.includes(HOOK_MARKER_START)) {
444
711
  console.log(`KeepGoing hook is already installed in ${rcFile}.`);
445
712
  return;
446
713
  }
447
- fs4.appendFileSync(rcFile, `
714
+ fs5.appendFileSync(rcFile, `
448
715
  ${hookBlock}
449
716
  `, "utf-8");
450
717
  console.log(`KeepGoing hook installed in ${rcFile}.`);
@@ -463,7 +730,7 @@ function hookUninstallCommand() {
463
730
  const { rcFile } = detected;
464
731
  let existing = "";
465
732
  try {
466
- existing = fs4.readFileSync(rcFile, "utf-8");
733
+ existing = fs5.readFileSync(rcFile, "utf-8");
467
734
  } catch {
468
735
  console.log(`${rcFile} not found \u2014 nothing to remove.`);
469
736
  return;
@@ -479,26 +746,78 @@ function hookUninstallCommand() {
479
746
  "g"
480
747
  );
481
748
  const updated = existing.replace(pattern, "").replace(/\n{3,}/g, "\n\n");
482
- fs4.writeFileSync(rcFile, updated, "utf-8");
749
+ fs5.writeFileSync(rcFile, updated, "utf-8");
483
750
  console.log(`KeepGoing hook removed from ${rcFile}.`);
484
751
  console.log(`Reload your shell config to deactivate it:
485
752
  `);
486
753
  console.log(` source ${rcFile}`);
487
754
  }
488
755
 
756
+ // src/commands/activate.ts
757
+ async function activateCommand({ licenseKey }) {
758
+ if (!licenseKey) {
759
+ console.error("Usage: keepgoing activate <license-key>");
760
+ process.exit(1);
761
+ }
762
+ const existing = readLicenseCache();
763
+ if (isCachedLicenseValid(existing)) {
764
+ const who2 = existing.customerName ? ` (${existing.customerName})` : "";
765
+ console.log(`Pro license is already active${who2}.`);
766
+ return;
767
+ }
768
+ console.log("Activating license...");
769
+ const result = await activateLicense(licenseKey, getDeviceId());
770
+ if (!result.valid) {
771
+ console.error(`Activation failed: ${result.error ?? "unknown error"}`);
772
+ process.exit(1);
773
+ }
774
+ const now = (/* @__PURE__ */ new Date()).toISOString();
775
+ writeLicenseCache({
776
+ licenseKey: result.licenseKey || licenseKey,
777
+ instanceId: result.instanceId || getDeviceId(),
778
+ status: "active",
779
+ lastValidatedAt: now,
780
+ activatedAt: now,
781
+ customerName: result.customerName,
782
+ productName: result.productName
783
+ });
784
+ const who = result.customerName ? ` Welcome, ${result.customerName}!` : "";
785
+ console.log(`Pro license activated successfully.${who}`);
786
+ }
787
+
788
+ // src/commands/deactivate.ts
789
+ async function deactivateCommand() {
790
+ const cache = readLicenseCache();
791
+ if (!cache) {
792
+ console.log("No active license found on this device.");
793
+ return;
794
+ }
795
+ console.log("Deactivating license...");
796
+ const result = await deactivateLicense(cache.licenseKey, cache.instanceId);
797
+ deleteLicenseCache();
798
+ if (!result.deactivated) {
799
+ console.error(`License cleared locally, but remote deactivation failed: ${result.error ?? "unknown error"}`);
800
+ process.exit(1);
801
+ }
802
+ console.log("Pro license deactivated successfully. The activation slot has been freed.");
803
+ }
804
+
489
805
  // src/index.ts
490
806
  var HELP_TEXT = `
491
807
  keepgoing: resume side projects without the mental friction
492
808
 
493
809
  Usage:
494
- keepgoing status Show the last checkpoint for this project
495
- keepgoing save Save a new checkpoint interactively
496
- keepgoing hook Manage the shell hook
810
+ keepgoing status Show the last checkpoint for this project
811
+ keepgoing save Save a new checkpoint interactively
812
+ keepgoing hook Manage the shell hook
813
+ keepgoing activate <key> Activate a Pro license on this device
814
+ keepgoing deactivate Deactivate the Pro license from this device
497
815
 
498
816
  Options:
499
817
  --cwd <path> Override the working directory (default: current directory)
500
818
  --json Output raw JSON (status only)
501
819
  --quiet Output a single summary line (status only)
820
+ -v, --version Show the CLI version
502
821
  -h, --help Show this help text
503
822
 
504
823
  Hook subcommands:
@@ -520,6 +839,8 @@ function parseArgs(argv) {
520
839
  json = true;
521
840
  } else if (arg === "--quiet") {
522
841
  quiet = true;
842
+ } else if (arg === "-v" || arg === "--version") {
843
+ command = "version";
523
844
  } else if (arg === "-h" || arg === "--help") {
524
845
  command = "help";
525
846
  } else if (!command) {
@@ -552,6 +873,15 @@ async function main() {
552
873
  process.exit(1);
553
874
  }
554
875
  break;
876
+ case "version":
877
+ console.log(`keepgoing v${"0.2.1"}`);
878
+ break;
879
+ case "activate":
880
+ await activateCommand({ licenseKey: subcommand });
881
+ break;
882
+ case "deactivate":
883
+ await deactivateCommand();
884
+ break;
555
885
  case "help":
556
886
  case "":
557
887
  console.log(HELP_TEXT);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keepgoingdev/cli",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "Terminal CLI for KeepGoing. Resume side projects without the mental friction.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",