@aiorg/cli 1.1.6 → 1.2.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.
package/dist/index.js CHANGED
@@ -30,6 +30,13 @@ var LatestVersionSchema = z.object({
30
30
  packageName: z.string(),
31
31
  packageDisplayName: z.string(),
32
32
  changelog: z.record(z.string(), z.any()).optional(),
33
+ allVersions: z.array(
34
+ z.object({
35
+ version: z.string(),
36
+ changelog: z.record(z.string(), z.any()).optional(),
37
+ releasedAt: z.string().optional()
38
+ })
39
+ ).optional(),
33
40
  tier: z.enum(["free", "paid", "private"]).optional(),
34
41
  type: z.enum(["template", "companion", "inject"]).optional()
35
42
  });
@@ -384,10 +391,10 @@ async function logout() {
384
391
  }
385
392
 
386
393
  // src/commands/init.ts
387
- import * as p3 from "@clack/prompts";
388
- import pc4 from "picocolors";
389
- import path3 from "path";
390
- import os3 from "os";
394
+ import * as p4 from "@clack/prompts";
395
+ import pc5 from "picocolors";
396
+ import path4 from "path";
397
+ import os4 from "os";
391
398
 
392
399
  // src/lib/extract.ts
393
400
  import extractZip from "extract-zip";
@@ -428,21 +435,301 @@ async function getFileSizeKB(filePath) {
428
435
  return Math.round(stats.size / 1024);
429
436
  }
430
437
 
438
+ // src/lib/project.ts
439
+ import fs3 from "fs-extra";
440
+ import path3 from "path";
441
+ import os3 from "os";
442
+ import * as p3 from "@clack/prompts";
443
+ import pc4 from "picocolors";
444
+ import { z as z3 } from "zod";
445
+ var AiorgFileSchema = z3.object({
446
+ project: z3.string(),
447
+ version: z3.string().optional()
448
+ });
449
+ var ContextJsonSchema = z3.object({
450
+ version: z3.string(),
451
+ business: z3.object({
452
+ name: z3.string(),
453
+ description: z3.string().optional(),
454
+ stage: z3.enum(["idea", "building", "launched", "pmf", "scaling"]).optional(),
455
+ launchDate: z3.string().optional()
456
+ }),
457
+ validation: z3.object({
458
+ ideaValidated: z3.boolean().optional(),
459
+ ideaScore: z3.number().optional(),
460
+ targetCustomer: z3.string().optional(),
461
+ valueProp: z3.string().optional(),
462
+ validatedAt: z3.string().optional()
463
+ }).optional(),
464
+ pmf: z3.object({
465
+ status: z3.enum(["not-started", "searching", "approaching", "achieved"]).optional(),
466
+ score: z3.number().nullable().optional(),
467
+ seanEllisScore: z3.number().nullable().optional(),
468
+ activationRate: z3.number().nullable().optional(),
469
+ weeklyRetention: z3.number().nullable().optional(),
470
+ measuredAt: z3.string().nullable().optional()
471
+ }).optional(),
472
+ installedKits: z3.array(z3.string()).optional(),
473
+ lastUpdated: z3.string(),
474
+ updatedBy: z3.string()
475
+ });
476
+ function getAiorgDir() {
477
+ return path3.join(os3.homedir(), ".aiorg");
478
+ }
479
+ function getProjectsDir() {
480
+ return path3.join(getAiorgDir(), "projects");
481
+ }
482
+ function getProjectDir(projectName) {
483
+ return path3.join(getProjectsDir(), projectName);
484
+ }
485
+ async function initializeAiorg() {
486
+ const aiorgDir = getAiorgDir();
487
+ const projectsDir = getProjectsDir();
488
+ await fs3.ensureDir(projectsDir);
489
+ const configPath = path3.join(aiorgDir, "config.json");
490
+ if (!await fs3.pathExists(configPath)) {
491
+ await fs3.writeJson(configPath, {
492
+ version: "1.0.0",
493
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
494
+ }, { spaces: 2 });
495
+ }
496
+ }
497
+ async function listProjects() {
498
+ const projectsDir = getProjectsDir();
499
+ if (!await fs3.pathExists(projectsDir)) {
500
+ return [];
501
+ }
502
+ const entries = await fs3.readdir(projectsDir, { withFileTypes: true });
503
+ return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
504
+ }
505
+ async function createProject(projectName, businessName, kitName) {
506
+ const projectDir = getProjectDir(projectName);
507
+ await initializeAiorg();
508
+ await fs3.ensureDir(projectDir);
509
+ const contextJson = {
510
+ version: "1.0.0",
511
+ business: {
512
+ name: businessName,
513
+ stage: "building"
514
+ },
515
+ validation: {},
516
+ pmf: {
517
+ status: "not-started",
518
+ score: null,
519
+ seanEllisScore: null,
520
+ activationRate: null,
521
+ weeklyRetention: null,
522
+ measuredAt: null
523
+ },
524
+ installedKits: [kitName],
525
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
526
+ updatedBy: kitName
527
+ };
528
+ await fs3.writeJson(
529
+ path3.join(projectDir, "context.json"),
530
+ contextJson,
531
+ { spaces: 2 }
532
+ );
533
+ await fs3.writeJson(
534
+ path3.join(projectDir, "learnings.json"),
535
+ {
536
+ version: "1.0.0",
537
+ whatWorks: [],
538
+ whatDoesntWork: []
539
+ },
540
+ { spaces: 2 }
541
+ );
542
+ }
543
+ async function readAiorgFile(dirPath) {
544
+ const aiorgPath = path3.join(dirPath, ".aiorg");
545
+ if (!await fs3.pathExists(aiorgPath)) {
546
+ return null;
547
+ }
548
+ try {
549
+ const content = await fs3.readJson(aiorgPath);
550
+ return AiorgFileSchema.parse(content);
551
+ } catch {
552
+ return null;
553
+ }
554
+ }
555
+ async function writeAiorgFile(dirPath, projectName) {
556
+ const aiorgPath = path3.join(dirPath, ".aiorg");
557
+ const aiorgFile = {
558
+ project: projectName,
559
+ version: "1.0.0"
560
+ };
561
+ await fs3.writeJson(aiorgPath, aiorgFile, { spaces: 2 });
562
+ }
563
+ async function addKitToProject(projectName, kitName) {
564
+ const contextPath = path3.join(getProjectDir(projectName), "context.json");
565
+ if (!await fs3.pathExists(contextPath)) {
566
+ return;
567
+ }
568
+ try {
569
+ const context = await fs3.readJson(contextPath);
570
+ const installedKits = context.installedKits || [];
571
+ if (!installedKits.includes(kitName)) {
572
+ installedKits.push(kitName);
573
+ context.installedKits = installedKits;
574
+ context.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
575
+ await fs3.writeJson(contextPath, context, { spaces: 2 });
576
+ }
577
+ } catch {
578
+ }
579
+ }
580
+ async function readProjectContext(projectName) {
581
+ const contextPath = path3.join(getProjectDir(projectName), "context.json");
582
+ if (!await fs3.pathExists(contextPath)) {
583
+ return null;
584
+ }
585
+ try {
586
+ const content = await fs3.readJson(contextPath);
587
+ return ContextJsonSchema.parse(content);
588
+ } catch {
589
+ return null;
590
+ }
591
+ }
592
+ function suggestProjectName(dirPath) {
593
+ const dirName = path3.basename(dirPath);
594
+ return dirName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
595
+ }
596
+ async function setupProject(targetPath, kitName, options = {}) {
597
+ const existingAiorg = await readAiorgFile(targetPath);
598
+ if (existingAiorg) {
599
+ await addKitToProject(existingAiorg.project, kitName);
600
+ if (!options.silent) {
601
+ const context = await readProjectContext(existingAiorg.project);
602
+ if (context) {
603
+ blank();
604
+ info(`Linked to project: ${pc4.cyan(existingAiorg.project)}`);
605
+ if (context.business?.name) {
606
+ log(pc4.dim(` Business: ${context.business.name}`));
607
+ }
608
+ if (context.installedKits && context.installedKits.length > 0) {
609
+ log(pc4.dim(` Installed kits: ${context.installedKits.join(", ")}`));
610
+ }
611
+ }
612
+ }
613
+ return existingAiorg.project;
614
+ }
615
+ await initializeAiorg();
616
+ const existingProjects = await listProjects();
617
+ let projectName;
618
+ if (existingProjects.length > 0) {
619
+ blank();
620
+ header("Project Setup");
621
+ log(pc4.dim("AI Org kits can share context across a project."));
622
+ blank();
623
+ const projectChoice = await p3.select({
624
+ message: "Link this kit to:",
625
+ options: [
626
+ ...existingProjects.map((name) => ({
627
+ value: name,
628
+ label: name,
629
+ hint: "existing project"
630
+ })),
631
+ {
632
+ value: "__new__",
633
+ label: "Create new project",
634
+ hint: "start fresh"
635
+ }
636
+ ]
637
+ });
638
+ if (p3.isCancel(projectChoice)) {
639
+ return null;
640
+ }
641
+ if (projectChoice === "__new__") {
642
+ const newProjectName = await askForNewProject(targetPath, kitName);
643
+ if (!newProjectName) return null;
644
+ projectName = newProjectName;
645
+ } else {
646
+ projectName = projectChoice;
647
+ await addKitToProject(projectName, kitName);
648
+ }
649
+ } else {
650
+ blank();
651
+ header("Project Setup");
652
+ log(pc4.dim("AI Org kits share context through projects."));
653
+ log(pc4.dim("This helps kits work together and remember your business."));
654
+ blank();
655
+ const newProjectName = await askForNewProject(targetPath, kitName);
656
+ if (!newProjectName) return null;
657
+ projectName = newProjectName;
658
+ }
659
+ await writeAiorgFile(targetPath, projectName);
660
+ if (!options.silent) {
661
+ blank();
662
+ success(`Project ${pc4.cyan(projectName)} linked`);
663
+ log(pc4.dim(` Context: ~/.aiorg/projects/${projectName}/`));
664
+ }
665
+ return projectName;
666
+ }
667
+ async function askForNewProject(targetPath, kitName) {
668
+ const suggestedName = suggestProjectName(targetPath);
669
+ const projectName = await p3.text({
670
+ message: "Project name:",
671
+ placeholder: suggestedName,
672
+ defaultValue: suggestedName,
673
+ validate: (value) => {
674
+ if (!value) return "Project name is required";
675
+ if (!/^[a-z0-9-]+$/.test(value)) {
676
+ return "Use lowercase letters, numbers, and hyphens only";
677
+ }
678
+ return void 0;
679
+ }
680
+ });
681
+ if (p3.isCancel(projectName)) {
682
+ return null;
683
+ }
684
+ const businessName = await p3.text({
685
+ message: "Business/product name:",
686
+ placeholder: "My Startup",
687
+ validate: (value) => {
688
+ if (!value) return "Business name is required";
689
+ return void 0;
690
+ }
691
+ });
692
+ if (p3.isCancel(businessName)) {
693
+ return null;
694
+ }
695
+ await createProject(
696
+ projectName,
697
+ businessName,
698
+ kitName
699
+ );
700
+ return projectName;
701
+ }
702
+ async function needsProjectMigration(dirPath) {
703
+ const aiorgFile = await readAiorgFile(dirPath);
704
+ if (aiorgFile) {
705
+ return false;
706
+ }
707
+ const hasClaudeDir = await fs3.pathExists(path3.join(dirPath, ".claude"));
708
+ const hasVersionJson = await fs3.pathExists(path3.join(dirPath, ".claude", "version.json"));
709
+ return hasClaudeDir && hasVersionJson;
710
+ }
711
+ async function migrateToProjectSystem(dirPath, kitName) {
712
+ blank();
713
+ info("This kit installation needs to be linked to a project.");
714
+ log(pc4.dim("Projects allow kits to share context and work together."));
715
+ return setupProject(dirPath, kitName);
716
+ }
717
+
431
718
  // src/commands/init.ts
432
719
  async function init(kitName, targetPath, options) {
433
- p3.intro(pc4.cyan(`aiorg init ${kitName}`));
434
- const resolvedPath = targetPath ? path3.resolve(targetPath.replace(/^~/, os3.homedir())) : path3.resolve(process.cwd(), kitName);
720
+ p4.intro(pc5.cyan(`aiorg init ${kitName}`));
721
+ const resolvedPath = targetPath ? path4.resolve(targetPath.replace(/^~/, os4.homedir())) : path4.resolve(process.cwd(), kitName);
435
722
  if (!options.force && await dirExistsAndNotEmpty(resolvedPath)) {
436
- error(`Folder already exists: ${pc4.yellow(resolvedPath)}`);
437
- log(pc4.dim("Use --force to overwrite"));
723
+ error(`Folder already exists: ${pc5.yellow(resolvedPath)}`);
724
+ log(pc5.dim("Use --force to overwrite"));
438
725
  process.exit(1);
439
726
  }
440
- const spinner4 = p3.spinner();
727
+ const spinner4 = p4.spinner();
441
728
  spinner4.start("Fetching version info...");
442
729
  let versionInfo;
443
730
  try {
444
731
  versionInfo = await fetchLatestVersion(kitName);
445
- spinner4.stop(`Found ${pc4.cyan(versionInfo.packageDisplayName)} v${versionInfo.version}`);
732
+ spinner4.stop(`Found ${pc5.cyan(versionInfo.packageDisplayName)} v${versionInfo.version}`);
446
733
  } catch (error2) {
447
734
  spinner4.stop("Failed to fetch version info");
448
735
  throw error2;
@@ -479,14 +766,14 @@ async function init(kitName, targetPath, options) {
479
766
  let tempDir = null;
480
767
  try {
481
768
  tempDir = await createTempDir("aiorg-init-");
482
- const zipPath = path3.join(tempDir, "kit.zip");
769
+ const zipPath = path4.join(tempDir, "kit.zip");
483
770
  const zipData = await downloadFile(downloadInfo.downloadUrl);
484
771
  await saveToFile(zipData, zipPath);
485
772
  const sizeKB = await getFileSizeKB(zipPath);
486
773
  spinner4.stop(`Downloaded ${kitName} v${versionInfo.version} (${sizeKB} KB)`);
487
774
  spinner4.start(`Extracting to ${resolvedPath}...`);
488
775
  await extractZipToDir(zipPath, resolvedPath);
489
- spinner4.stop(`Extracted to ${pc4.yellow(resolvedPath)}`);
776
+ spinner4.stop(`Extracted to ${pc5.yellow(resolvedPath)}`);
490
777
  await cleanupTempDir(tempDir);
491
778
  } catch (error2) {
492
779
  if (tempDir) {
@@ -496,6 +783,7 @@ async function init(kitName, targetPath, options) {
496
783
  }
497
784
  blank();
498
785
  success(`${versionInfo.packageDisplayName} v${versionInfo.version} installed!`);
786
+ await setupProject(resolvedPath, kitName);
499
787
  blank();
500
788
  log("Next steps:");
501
789
  listItem(`cd ${resolvedPath}`);
@@ -504,52 +792,52 @@ async function init(kitName, targetPath, options) {
504
792
  const outroMessages = {
505
793
  "investor-os": "Happy investing!"
506
794
  };
507
- const outro5 = outroMessages[kitName] || "Happy building!";
508
- p3.outro(pc4.green(outro5));
795
+ const outro6 = outroMessages[kitName] || "Happy building!";
796
+ p4.outro(pc5.green(outro6));
509
797
  }
510
798
 
511
799
  // src/commands/upgrade.ts
512
- import * as p4 from "@clack/prompts";
513
- import pc5 from "picocolors";
514
- import path6 from "path";
800
+ import * as p5 from "@clack/prompts";
801
+ import pc6 from "picocolors";
802
+ import path7 from "path";
515
803
  import semver from "semver";
516
804
 
517
805
  // src/lib/detect.ts
518
- import fs3 from "fs-extra";
519
- import path4 from "path";
520
- import { z as z3 } from "zod";
521
- var VersionJsonSchema = z3.object({
522
- version: z3.string(),
523
- packageName: z3.string(),
524
- packageDisplayName: z3.string().optional(),
525
- releasedAt: z3.string().optional(),
526
- minUpgradeFrom: z3.string().optional(),
527
- fileCategories: z3.object({
528
- alwaysReplace: z3.array(z3.string()).optional(),
529
- neverTouch: z3.array(z3.string()).optional(),
530
- mergeIfChanged: z3.array(z3.string()).optional(),
531
- addOnly: z3.array(z3.string()).optional()
806
+ import fs4 from "fs-extra";
807
+ import path5 from "path";
808
+ import { z as z4 } from "zod";
809
+ var VersionJsonSchema = z4.object({
810
+ version: z4.string(),
811
+ packageName: z4.string(),
812
+ packageDisplayName: z4.string().optional(),
813
+ releasedAt: z4.string().optional(),
814
+ minUpgradeFrom: z4.string().optional(),
815
+ fileCategories: z4.object({
816
+ alwaysReplace: z4.array(z4.string()).optional(),
817
+ neverTouch: z4.array(z4.string()).optional(),
818
+ mergeIfChanged: z4.array(z4.string()).optional(),
819
+ addOnly: z4.array(z4.string()).optional()
532
820
  }).optional(),
533
- changelog: z3.record(z3.string(), z3.any()).optional()
821
+ changelog: z4.record(z4.string(), z4.any()).optional()
534
822
  });
535
- var KitJsonSchema = z3.object({
536
- name: z3.string(),
537
- displayName: z3.string().optional(),
538
- type: z3.enum(["bootstrap", "inject"]).optional()
823
+ var KitJsonSchema = z4.object({
824
+ name: z4.string(),
825
+ displayName: z4.string().optional(),
826
+ type: z4.enum(["bootstrap", "inject"]).optional()
539
827
  });
540
828
  async function detectKit(dirPath) {
541
- const versionJsonPath = path4.join(dirPath, ".claude", "version.json");
542
- const kitJsonPath = path4.join(dirPath, ".claude", "kit.json");
543
- if (!await fs3.pathExists(versionJsonPath)) {
829
+ const versionJsonPath = path5.join(dirPath, ".claude", "version.json");
830
+ const kitJsonPath = path5.join(dirPath, ".claude", "kit.json");
831
+ if (!await fs4.pathExists(versionJsonPath)) {
544
832
  return null;
545
833
  }
546
834
  try {
547
- const versionRaw = await fs3.readJson(versionJsonPath);
835
+ const versionRaw = await fs4.readJson(versionJsonPath);
548
836
  const versionJson = VersionJsonSchema.parse(versionRaw);
549
837
  let kitJson;
550
- if (await fs3.pathExists(kitJsonPath)) {
838
+ if (await fs4.pathExists(kitJsonPath)) {
551
839
  try {
552
- const kitRaw = await fs3.readJson(kitJsonPath);
840
+ const kitRaw = await fs4.readJson(kitJsonPath);
553
841
  kitJson = KitJsonSchema.parse(kitRaw);
554
842
  } catch {
555
843
  }
@@ -571,8 +859,8 @@ async function detectKitInCwd() {
571
859
  }
572
860
 
573
861
  // src/lib/apply.ts
574
- import fs4 from "fs-extra";
575
- import path5 from "path";
862
+ import fs5 from "fs-extra";
863
+ import path6 from "path";
576
864
  import { glob } from "glob";
577
865
  import { minimatch } from "minimatch";
578
866
  import { merge } from "lodash-es";
@@ -610,11 +898,11 @@ async function applyFileCategories(sourceDir, destDir, versionJson) {
610
898
  processedFiles.add(file);
611
899
  continue;
612
900
  }
613
- const srcPath = path5.join(sourceDir, file);
614
- const destPath = path5.join(destDir, file);
901
+ const srcPath = path6.join(sourceDir, file);
902
+ const destPath = path6.join(destDir, file);
615
903
  try {
616
- await fs4.ensureDir(path5.dirname(destPath));
617
- await fs4.copy(srcPath, destPath, { overwrite: true });
904
+ await fs5.ensureDir(path6.dirname(destPath));
905
+ await fs5.copy(srcPath, destPath, { overwrite: true });
618
906
  result.replaced.push(file);
619
907
  processedFiles.add(file);
620
908
  } catch (err) {
@@ -642,21 +930,21 @@ async function applyFileCategories(sourceDir, destDir, versionJson) {
642
930
  processedFiles.add(file);
643
931
  continue;
644
932
  }
645
- const srcPath = path5.join(sourceDir, file);
646
- const destPath = path5.join(destDir, file);
933
+ const srcPath = path6.join(sourceDir, file);
934
+ const destPath = path6.join(destDir, file);
647
935
  try {
648
- const destExists = await fs4.pathExists(destPath);
936
+ const destExists = await fs5.pathExists(destPath);
649
937
  if (destExists && file.endsWith(".json")) {
650
- const incoming = await fs4.readJson(srcPath);
651
- const existing = await fs4.readJson(destPath);
938
+ const incoming = await fs5.readJson(srcPath);
939
+ const existing = await fs5.readJson(destPath);
652
940
  const merged = merge({}, incoming, existing);
653
- await fs4.writeJson(destPath, merged, { spaces: 2 });
941
+ await fs5.writeJson(destPath, merged, { spaces: 2 });
654
942
  result.merged.push(file);
655
943
  } else if (destExists) {
656
944
  result.skipped.push(file);
657
945
  } else {
658
- await fs4.ensureDir(path5.dirname(destPath));
659
- await fs4.copy(srcPath, destPath);
946
+ await fs5.ensureDir(path6.dirname(destPath));
947
+ await fs5.copy(srcPath, destPath);
660
948
  result.replaced.push(file);
661
949
  }
662
950
  processedFiles.add(file);
@@ -685,13 +973,13 @@ async function applyFileCategories(sourceDir, destDir, versionJson) {
685
973
  processedFiles.add(file);
686
974
  continue;
687
975
  }
688
- const srcPath = path5.join(sourceDir, file);
689
- const destPath = path5.join(destDir, file);
976
+ const srcPath = path6.join(sourceDir, file);
977
+ const destPath = path6.join(destDir, file);
690
978
  try {
691
- const destExists = await fs4.pathExists(destPath);
979
+ const destExists = await fs5.pathExists(destPath);
692
980
  if (!destExists) {
693
- await fs4.ensureDir(path5.dirname(destPath));
694
- await fs4.copy(srcPath, destPath);
981
+ await fs5.ensureDir(path6.dirname(destPath));
982
+ await fs5.copy(srcPath, destPath);
695
983
  result.added.push(file);
696
984
  } else {
697
985
  result.skipped.push(file);
@@ -712,8 +1000,8 @@ function matchesPattern(filePath, pattern) {
712
1000
  }
713
1001
  async function isGitRepo(dirPath) {
714
1002
  try {
715
- const gitDir = path5.join(dirPath, ".git");
716
- return await fs4.pathExists(gitDir);
1003
+ const gitDir = path6.join(dirPath, ".git");
1004
+ return await fs5.pathExists(gitDir);
717
1005
  } catch {
718
1006
  return false;
719
1007
  }
@@ -741,16 +1029,28 @@ async function createGitBackup(dirPath, message) {
741
1029
 
742
1030
  // src/commands/upgrade.ts
743
1031
  async function upgrade(options) {
744
- p4.intro(pc5.cyan("aiorg upgrade"));
1032
+ p5.intro(pc6.cyan("aiorg upgrade"));
745
1033
  const kit = await detectKitInCwd();
746
1034
  if (!kit) {
747
1035
  error("Not in a kit directory");
748
- log(pc5.dim("Run this command from a folder with .claude/version.json"));
1036
+ log(pc6.dim("Run this command from a folder with .claude/version.json"));
749
1037
  process.exit(1);
750
1038
  }
751
- keyValue("Kit", pc5.magenta(kit.displayName));
752
- keyValue("Current version", pc5.cyan(`v${kit.version}`));
753
- const spinner4 = p4.spinner();
1039
+ keyValue("Kit", pc6.magenta(kit.displayName));
1040
+ keyValue("Current version", pc6.cyan(`v${kit.version}`));
1041
+ if (await needsProjectMigration(kit.rootPath)) {
1042
+ const projectName = await migrateToProjectSystem(kit.rootPath, kit.name);
1043
+ if (!projectName) {
1044
+ blank();
1045
+ log(pc6.dim("Skipping project setup. You can run it later."));
1046
+ }
1047
+ } else {
1048
+ const aiorgFile = await readAiorgFile(kit.rootPath);
1049
+ if (aiorgFile) {
1050
+ await addKitToProject(aiorgFile.project, kit.name);
1051
+ }
1052
+ }
1053
+ const spinner4 = p5.spinner();
754
1054
  spinner4.start("Checking for updates...");
755
1055
  let latest;
756
1056
  try {
@@ -762,13 +1062,13 @@ async function upgrade(options) {
762
1062
  }
763
1063
  if (!semver.gt(latest.version, kit.version)) {
764
1064
  blank();
765
- success(`Already on latest version (${pc5.cyan(`v${kit.version}`)})`);
766
- p4.outro("");
1065
+ success(`Already on latest version (${pc6.cyan(`v${kit.version}`)})`);
1066
+ p5.outro("");
767
1067
  return;
768
1068
  }
769
1069
  blank();
770
1070
  log(
771
- `Update available: ${pc5.cyan(`v${kit.version}`)} \u2192 ${pc5.green(`v${latest.version}`)}`
1071
+ `Update available: ${pc6.cyan(`v${kit.version}`)} \u2192 ${pc6.green(`v${latest.version}`)}`
772
1072
  );
773
1073
  if (latest.allVersions && Array.isArray(latest.allVersions)) {
774
1074
  const versionsToShow = latest.allVersions.filter((v) => {
@@ -790,17 +1090,17 @@ async function upgrade(options) {
790
1090
  if (!entry) continue;
791
1091
  const highlights = entry.highlights?.join(", ") || "";
792
1092
  blank();
793
- log(`${pc5.green(`v${version2}`)}${highlights ? ` - ${pc5.white(highlights)}` : ""}`);
1093
+ log(`${pc6.green(`v${version2}`)}${highlights ? ` - ${pc6.white(highlights)}` : ""}`);
794
1094
  if (entry.added && entry.added.length > 0) {
795
1095
  for (const item of entry.added.slice(0, 2)) {
796
- log(pc5.dim(` + ${item}`));
1096
+ log(pc6.dim(` + ${item}`));
797
1097
  }
798
1098
  if (entry.added.length > 2) {
799
- log(pc5.dim(` + ... and ${entry.added.length - 2} more`));
1099
+ log(pc6.dim(` + ... and ${entry.added.length - 2} more`));
800
1100
  }
801
1101
  }
802
1102
  if (entry.upgradeNotes && version2 === latest.version) {
803
- log(pc5.yellow(` Note: ${entry.upgradeNotes}`));
1103
+ log(pc6.yellow(` Note: ${entry.upgradeNotes}`));
804
1104
  }
805
1105
  }
806
1106
  }
@@ -809,19 +1109,19 @@ async function upgrade(options) {
809
1109
  log("Your data will be preserved:");
810
1110
  const neverTouch = kit.versionJson.fileCategories?.neverTouch ?? [];
811
1111
  for (const pattern of neverTouch.slice(0, 5)) {
812
- listItem(pc5.dim(pattern));
1112
+ listItem(pc6.dim(pattern));
813
1113
  }
814
1114
  if (neverTouch.length > 5) {
815
- log(pc5.dim(` ... and ${neverTouch.length - 5} more patterns`));
1115
+ log(pc6.dim(` ... and ${neverTouch.length - 5} more patterns`));
816
1116
  }
817
1117
  if (!options.yes) {
818
1118
  blank();
819
- const shouldUpgrade = await p4.confirm({
1119
+ const shouldUpgrade = await p5.confirm({
820
1120
  message: "Proceed with upgrade?",
821
1121
  initialValue: true
822
1122
  });
823
- if (p4.isCancel(shouldUpgrade) || !shouldUpgrade) {
824
- p4.cancel("Upgrade cancelled");
1123
+ if (p5.isCancel(shouldUpgrade) || !shouldUpgrade) {
1124
+ p5.cancel("Upgrade cancelled");
825
1125
  return;
826
1126
  }
827
1127
  }
@@ -850,7 +1150,7 @@ async function upgrade(options) {
850
1150
  );
851
1151
  spinner4.stop(created ? "Git backup created" : "No changes to backup");
852
1152
  } else if (!options.yes) {
853
- const shouldBackup = await p4.confirm({
1153
+ const shouldBackup = await p5.confirm({
854
1154
  message: "Create git backup commit first?",
855
1155
  initialValue: true
856
1156
  });
@@ -881,8 +1181,8 @@ async function upgrade(options) {
881
1181
  let tempDir = null;
882
1182
  try {
883
1183
  tempDir = await createTempDir("aiorg-upgrade-");
884
- const zipPath = path6.join(tempDir, "kit.zip");
885
- const extractPath = path6.join(tempDir, "extracted");
1184
+ const zipPath = path7.join(tempDir, "kit.zip");
1185
+ const extractPath = path7.join(tempDir, "extracted");
886
1186
  const zipData = await downloadFile(downloadInfo.downloadUrl);
887
1187
  await saveToFile(zipData, zipPath);
888
1188
  const sizeKB = await getFileSizeKB(zipPath);
@@ -903,7 +1203,7 @@ async function upgrade(options) {
903
1203
  spinner4.stop("Updates applied");
904
1204
  blank();
905
1205
  success(
906
- `Upgraded ${kit.displayName}: ${pc5.cyan(`v${kit.version}`)} \u2192 ${pc5.green(`v${latest.version}`)}`
1206
+ `Upgraded ${kit.displayName}: ${pc6.cyan(`v${kit.version}`)} \u2192 ${pc6.green(`v${latest.version}`)}`
907
1207
  );
908
1208
  blank();
909
1209
  keyValue("Files updated", String(result.replaced.length));
@@ -918,7 +1218,7 @@ async function upgrade(options) {
918
1218
  blank();
919
1219
  warn(`${result.errors.length} errors occurred:`);
920
1220
  for (const err of result.errors) {
921
- listItem(pc5.dim(err));
1221
+ listItem(pc6.dim(err));
922
1222
  }
923
1223
  }
924
1224
  await cleanupTempDir(tempDir);
@@ -929,46 +1229,46 @@ async function upgrade(options) {
929
1229
  throw error2;
930
1230
  }
931
1231
  blank();
932
- log(pc5.yellow("\u26A0\uFE0F Restart Claude Code to use new commands"));
933
- log(pc5.dim(' Type "exit" then start a new session'));
934
- p4.outro(pc5.green("Upgrade complete!"));
1232
+ log(pc6.yellow("\u26A0\uFE0F Restart Claude Code to use new commands"));
1233
+ log(pc6.dim(' Type "exit" then start a new session'));
1234
+ p5.outro(pc6.green("Upgrade complete!"));
935
1235
  }
936
1236
 
937
1237
  // src/commands/version.ts
938
- import pc6 from "picocolors";
1238
+ import pc7 from "picocolors";
939
1239
  import semver2 from "semver";
940
1240
  var CLI_VERSION = "1.0.0";
941
1241
  async function version() {
942
1242
  header("aiorg version");
943
- keyValue("CLI", pc6.cyan(`v${CLI_VERSION}`));
1243
+ keyValue("CLI", pc7.cyan(`v${CLI_VERSION}`));
944
1244
  const kit = await detectKitInCwd();
945
1245
  if (!kit) {
946
1246
  blank();
947
1247
  info("No kit detected in current directory");
948
- log(pc6.dim("Run this command from a folder containing a kit"));
1248
+ log(pc7.dim("Run this command from a folder containing a kit"));
949
1249
  return;
950
1250
  }
951
- keyValue(kit.displayName, pc6.cyan(`v${kit.version}`));
1251
+ keyValue(kit.displayName, pc7.cyan(`v${kit.version}`));
952
1252
  try {
953
1253
  const latest = await fetchLatestVersion(kit.name);
954
1254
  if (semver2.gt(latest.version, kit.version)) {
955
1255
  blank();
956
1256
  warn(
957
- `Update available: ${pc6.cyan(`v${kit.version}`)} \u2192 ${pc6.green(`v${latest.version}`)}`
1257
+ `Update available: ${pc7.cyan(`v${kit.version}`)} \u2192 ${pc7.green(`v${latest.version}`)}`
958
1258
  );
959
- log(pc6.dim("Run 'aiorg upgrade' to update"));
1259
+ log(pc7.dim("Run 'aiorg upgrade' to update"));
960
1260
  } else {
961
1261
  blank();
962
1262
  success("You are on the latest version");
963
1263
  }
964
1264
  } catch {
965
1265
  blank();
966
- log(pc6.dim("Could not check for updates"));
1266
+ log(pc7.dim("Could not check for updates"));
967
1267
  }
968
1268
  }
969
1269
 
970
1270
  // src/commands/list.ts
971
- import pc7 from "picocolors";
1271
+ import pc8 from "picocolors";
972
1272
  function formatPrice(cents) {
973
1273
  if (cents === 0) return "";
974
1274
  return `$${(cents / 100).toFixed(0)}`;
@@ -982,44 +1282,137 @@ async function list() {
982
1282
  const freeKits = kits.filter((k) => k.tier === "free");
983
1283
  const paidKits = kits.filter((k) => k.tier === "paid");
984
1284
  blank();
985
- console.log(pc7.bold("Available Kits"));
1285
+ console.log(pc8.bold("Available Kits"));
986
1286
  blank();
987
1287
  if (freeKits.length > 0) {
988
- console.log(pc7.green(pc7.bold("FREE")));
1288
+ console.log(pc8.green(pc8.bold("FREE")));
989
1289
  blank();
990
1290
  for (const kit of freeKits) {
991
1291
  printKit(kit);
992
1292
  }
993
1293
  }
994
1294
  if (paidKits.length > 0) {
995
- console.log(pc7.yellow(pc7.bold("PAID")));
1295
+ console.log(pc8.yellow(pc8.bold("PAID")));
996
1296
  blank();
997
1297
  for (const kit of paidKits) {
998
1298
  printKit(kit);
999
1299
  }
1000
1300
  }
1001
- console.log(pc7.dim("\u2500".repeat(50)));
1301
+ console.log(pc8.dim("\u2500".repeat(50)));
1002
1302
  blank();
1003
- console.log(pc7.dim("Free kits work without login."));
1004
- console.log(pc7.dim(`Run '${pc7.cyan("aiorg login")}' first for paid kits.`));
1005
- console.log(pc7.dim(`Visit ${pc7.cyan("https://aiorg.dev")} for details.`));
1303
+ console.log(pc8.dim("Free kits work without login."));
1304
+ console.log(pc8.dim(`Run '${pc8.cyan("aiorg login")}' first for paid kits.`));
1305
+ console.log(pc8.dim(`Visit ${pc8.cyan("https://aiorg.dev")} for details.`));
1006
1306
  blank();
1007
1307
  }
1008
1308
  function printKit(kit) {
1009
1309
  const price = formatPrice(kit.priceCents);
1010
- const priceStr = price ? pc7.yellow(price) : "";
1310
+ const priceStr = price ? pc8.yellow(price) : "";
1011
1311
  console.log(
1012
- ` ${pc7.bold(kit.name.padEnd(24))} ${pc7.cyan(`v${kit.version}`)} ${priceStr}`
1312
+ ` ${pc8.bold(kit.name.padEnd(24))} ${pc8.cyan(`v${kit.version}`)} ${priceStr}`
1013
1313
  );
1014
1314
  if (kit.description) {
1015
- console.log(` ${pc7.dim(kit.description)}`);
1315
+ console.log(` ${pc8.dim(kit.description)}`);
1016
1316
  }
1017
1317
  console.log(
1018
- ` ${pc7.dim("\u2192")} ${pc7.dim("npx @aiorg/cli init")} ${pc7.magenta(kit.name)} ${pc7.dim("~/my-project")}`
1318
+ ` ${pc8.dim("\u2192")} ${pc8.dim("npx @aiorg/cli init")} ${pc8.magenta(kit.name)} ${pc8.dim("~/my-project")}`
1019
1319
  );
1020
1320
  blank();
1021
1321
  }
1022
1322
 
1323
+ // src/commands/status.ts
1324
+ import * as p6 from "@clack/prompts";
1325
+ import pc9 from "picocolors";
1326
+ async function status() {
1327
+ p6.intro(pc9.cyan("aiorg status"));
1328
+ const kit = await detectKitInCwd();
1329
+ const aiorgFile = await readAiorgFile(process.cwd());
1330
+ if (!kit && !aiorgFile) {
1331
+ const projects = await listProjects();
1332
+ if (projects.length === 0) {
1333
+ info("No AI Org projects found");
1334
+ blank();
1335
+ log(pc9.dim("Get started with:"));
1336
+ listItem("npx @aiorg/cli init <kit-name> <path>");
1337
+ p6.outro("");
1338
+ return;
1339
+ }
1340
+ header("Your Projects");
1341
+ for (const projectName of projects) {
1342
+ const context = await readProjectContext(projectName);
1343
+ if (context) {
1344
+ blank();
1345
+ log(pc9.cyan(projectName));
1346
+ if (context.business?.name) {
1347
+ log(pc9.dim(` Business: ${context.business.name}`));
1348
+ }
1349
+ if (context.business?.stage) {
1350
+ log(pc9.dim(` Stage: ${context.business.stage}`));
1351
+ }
1352
+ if (context.installedKits && context.installedKits.length > 0) {
1353
+ log(pc9.dim(` Kits: ${context.installedKits.join(", ")}`));
1354
+ }
1355
+ if (context.pmf?.status && context.pmf.status !== "not-started") {
1356
+ log(pc9.dim(` PMF: ${context.pmf.status}`));
1357
+ }
1358
+ } else {
1359
+ blank();
1360
+ log(pc9.cyan(projectName));
1361
+ log(pc9.dim(" (no context)"));
1362
+ }
1363
+ }
1364
+ blank();
1365
+ log(pc9.dim(`Projects stored in: ~/.aiorg/projects/`));
1366
+ p6.outro("");
1367
+ return;
1368
+ }
1369
+ if (kit) {
1370
+ keyValue("Kit", pc9.magenta(kit.displayName));
1371
+ keyValue("Version", pc9.cyan(`v${kit.version}`));
1372
+ }
1373
+ if (aiorgFile) {
1374
+ keyValue("Project", pc9.cyan(aiorgFile.project));
1375
+ const context = await readProjectContext(aiorgFile.project);
1376
+ if (context) {
1377
+ blank();
1378
+ header("Project Context");
1379
+ if (context.business?.name) {
1380
+ keyValue("Business", context.business.name);
1381
+ }
1382
+ if (context.business?.stage) {
1383
+ keyValue("Stage", context.business.stage);
1384
+ }
1385
+ if (context.validation?.ideaValidated) {
1386
+ keyValue("Idea Validated", pc9.green("Yes"));
1387
+ if (context.validation.ideaScore) {
1388
+ keyValue("Idea Score", `${context.validation.ideaScore}/100`);
1389
+ }
1390
+ }
1391
+ if (context.pmf?.status && context.pmf.status !== "not-started") {
1392
+ const pmfColor = context.pmf.status === "achieved" ? pc9.green : pc9.yellow;
1393
+ keyValue("PMF Status", pmfColor(context.pmf.status));
1394
+ if (context.pmf.score !== null && context.pmf.score !== void 0) {
1395
+ keyValue("PMF Score", `${context.pmf.score}/100`);
1396
+ }
1397
+ }
1398
+ if (context.installedKits && context.installedKits.length > 0) {
1399
+ blank();
1400
+ log(pc9.dim("Installed kits:"));
1401
+ for (const kitName of context.installedKits) {
1402
+ listItem(kitName);
1403
+ }
1404
+ }
1405
+ blank();
1406
+ log(pc9.dim(`Context: ~/.aiorg/projects/${aiorgFile.project}/`));
1407
+ }
1408
+ } else if (kit) {
1409
+ blank();
1410
+ warn("Not linked to a project");
1411
+ log(pc9.dim("Run upgrade to set up project linking"));
1412
+ }
1413
+ p6.outro("");
1414
+ }
1415
+
1023
1416
  // src/index.ts
1024
1417
  var cli = cac("aiorg");
1025
1418
  cli.command("login", "Save your license key").action(async () => {
@@ -1038,9 +1431,9 @@ cli.command("logout", "Remove saved license key").action(async () => {
1038
1431
  process.exit(1);
1039
1432
  }
1040
1433
  });
1041
- cli.command("init <kit> [path]", "Download and extract a kit").option("--force", "Overwrite existing folder").action(async (kit, path7, options) => {
1434
+ cli.command("init <kit> [path]", "Download and extract a kit").option("--force", "Overwrite existing folder").action(async (kit, path8, options) => {
1042
1435
  try {
1043
- await init(kit, path7, options);
1436
+ await init(kit, path8, options);
1044
1437
  } catch (error2) {
1045
1438
  error(error2 instanceof Error ? error2.message : "Init failed");
1046
1439
  process.exit(1);
@@ -1070,6 +1463,14 @@ cli.command("version", "Show CLI and kit versions").action(async () => {
1070
1463
  process.exit(1);
1071
1464
  }
1072
1465
  });
1466
+ cli.command("status", "Show project and kit status").action(async () => {
1467
+ try {
1468
+ await status();
1469
+ } catch (error2) {
1470
+ error(error2 instanceof Error ? error2.message : "Status check failed");
1471
+ process.exit(1);
1472
+ }
1473
+ });
1073
1474
  cli.help();
1074
1475
  cli.version("1.0.0");
1075
1476
  cli.parse();