@aiorg/cli 1.1.7 → 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
@@ -391,10 +391,10 @@ async function logout() {
391
391
  }
392
392
 
393
393
  // src/commands/init.ts
394
- import * as p3 from "@clack/prompts";
395
- import pc4 from "picocolors";
396
- import path3 from "path";
397
- 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";
398
398
 
399
399
  // src/lib/extract.ts
400
400
  import extractZip from "extract-zip";
@@ -435,21 +435,301 @@ async function getFileSizeKB(filePath) {
435
435
  return Math.round(stats.size / 1024);
436
436
  }
437
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
+
438
718
  // src/commands/init.ts
439
719
  async function init(kitName, targetPath, options) {
440
- p3.intro(pc4.cyan(`aiorg init ${kitName}`));
441
- 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);
442
722
  if (!options.force && await dirExistsAndNotEmpty(resolvedPath)) {
443
- error(`Folder already exists: ${pc4.yellow(resolvedPath)}`);
444
- log(pc4.dim("Use --force to overwrite"));
723
+ error(`Folder already exists: ${pc5.yellow(resolvedPath)}`);
724
+ log(pc5.dim("Use --force to overwrite"));
445
725
  process.exit(1);
446
726
  }
447
- const spinner4 = p3.spinner();
727
+ const spinner4 = p4.spinner();
448
728
  spinner4.start("Fetching version info...");
449
729
  let versionInfo;
450
730
  try {
451
731
  versionInfo = await fetchLatestVersion(kitName);
452
- spinner4.stop(`Found ${pc4.cyan(versionInfo.packageDisplayName)} v${versionInfo.version}`);
732
+ spinner4.stop(`Found ${pc5.cyan(versionInfo.packageDisplayName)} v${versionInfo.version}`);
453
733
  } catch (error2) {
454
734
  spinner4.stop("Failed to fetch version info");
455
735
  throw error2;
@@ -486,14 +766,14 @@ async function init(kitName, targetPath, options) {
486
766
  let tempDir = null;
487
767
  try {
488
768
  tempDir = await createTempDir("aiorg-init-");
489
- const zipPath = path3.join(tempDir, "kit.zip");
769
+ const zipPath = path4.join(tempDir, "kit.zip");
490
770
  const zipData = await downloadFile(downloadInfo.downloadUrl);
491
771
  await saveToFile(zipData, zipPath);
492
772
  const sizeKB = await getFileSizeKB(zipPath);
493
773
  spinner4.stop(`Downloaded ${kitName} v${versionInfo.version} (${sizeKB} KB)`);
494
774
  spinner4.start(`Extracting to ${resolvedPath}...`);
495
775
  await extractZipToDir(zipPath, resolvedPath);
496
- spinner4.stop(`Extracted to ${pc4.yellow(resolvedPath)}`);
776
+ spinner4.stop(`Extracted to ${pc5.yellow(resolvedPath)}`);
497
777
  await cleanupTempDir(tempDir);
498
778
  } catch (error2) {
499
779
  if (tempDir) {
@@ -503,6 +783,7 @@ async function init(kitName, targetPath, options) {
503
783
  }
504
784
  blank();
505
785
  success(`${versionInfo.packageDisplayName} v${versionInfo.version} installed!`);
786
+ await setupProject(resolvedPath, kitName);
506
787
  blank();
507
788
  log("Next steps:");
508
789
  listItem(`cd ${resolvedPath}`);
@@ -511,52 +792,52 @@ async function init(kitName, targetPath, options) {
511
792
  const outroMessages = {
512
793
  "investor-os": "Happy investing!"
513
794
  };
514
- const outro5 = outroMessages[kitName] || "Happy building!";
515
- p3.outro(pc4.green(outro5));
795
+ const outro6 = outroMessages[kitName] || "Happy building!";
796
+ p4.outro(pc5.green(outro6));
516
797
  }
517
798
 
518
799
  // src/commands/upgrade.ts
519
- import * as p4 from "@clack/prompts";
520
- import pc5 from "picocolors";
521
- import path6 from "path";
800
+ import * as p5 from "@clack/prompts";
801
+ import pc6 from "picocolors";
802
+ import path7 from "path";
522
803
  import semver from "semver";
523
804
 
524
805
  // src/lib/detect.ts
525
- import fs3 from "fs-extra";
526
- import path4 from "path";
527
- import { z as z3 } from "zod";
528
- var VersionJsonSchema = z3.object({
529
- version: z3.string(),
530
- packageName: z3.string(),
531
- packageDisplayName: z3.string().optional(),
532
- releasedAt: z3.string().optional(),
533
- minUpgradeFrom: z3.string().optional(),
534
- fileCategories: z3.object({
535
- alwaysReplace: z3.array(z3.string()).optional(),
536
- neverTouch: z3.array(z3.string()).optional(),
537
- mergeIfChanged: z3.array(z3.string()).optional(),
538
- 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()
539
820
  }).optional(),
540
- changelog: z3.record(z3.string(), z3.any()).optional()
821
+ changelog: z4.record(z4.string(), z4.any()).optional()
541
822
  });
542
- var KitJsonSchema = z3.object({
543
- name: z3.string(),
544
- displayName: z3.string().optional(),
545
- 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()
546
827
  });
547
828
  async function detectKit(dirPath) {
548
- const versionJsonPath = path4.join(dirPath, ".claude", "version.json");
549
- const kitJsonPath = path4.join(dirPath, ".claude", "kit.json");
550
- 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)) {
551
832
  return null;
552
833
  }
553
834
  try {
554
- const versionRaw = await fs3.readJson(versionJsonPath);
835
+ const versionRaw = await fs4.readJson(versionJsonPath);
555
836
  const versionJson = VersionJsonSchema.parse(versionRaw);
556
837
  let kitJson;
557
- if (await fs3.pathExists(kitJsonPath)) {
838
+ if (await fs4.pathExists(kitJsonPath)) {
558
839
  try {
559
- const kitRaw = await fs3.readJson(kitJsonPath);
840
+ const kitRaw = await fs4.readJson(kitJsonPath);
560
841
  kitJson = KitJsonSchema.parse(kitRaw);
561
842
  } catch {
562
843
  }
@@ -578,8 +859,8 @@ async function detectKitInCwd() {
578
859
  }
579
860
 
580
861
  // src/lib/apply.ts
581
- import fs4 from "fs-extra";
582
- import path5 from "path";
862
+ import fs5 from "fs-extra";
863
+ import path6 from "path";
583
864
  import { glob } from "glob";
584
865
  import { minimatch } from "minimatch";
585
866
  import { merge } from "lodash-es";
@@ -617,11 +898,11 @@ async function applyFileCategories(sourceDir, destDir, versionJson) {
617
898
  processedFiles.add(file);
618
899
  continue;
619
900
  }
620
- const srcPath = path5.join(sourceDir, file);
621
- const destPath = path5.join(destDir, file);
901
+ const srcPath = path6.join(sourceDir, file);
902
+ const destPath = path6.join(destDir, file);
622
903
  try {
623
- await fs4.ensureDir(path5.dirname(destPath));
624
- await fs4.copy(srcPath, destPath, { overwrite: true });
904
+ await fs5.ensureDir(path6.dirname(destPath));
905
+ await fs5.copy(srcPath, destPath, { overwrite: true });
625
906
  result.replaced.push(file);
626
907
  processedFiles.add(file);
627
908
  } catch (err) {
@@ -649,21 +930,21 @@ async function applyFileCategories(sourceDir, destDir, versionJson) {
649
930
  processedFiles.add(file);
650
931
  continue;
651
932
  }
652
- const srcPath = path5.join(sourceDir, file);
653
- const destPath = path5.join(destDir, file);
933
+ const srcPath = path6.join(sourceDir, file);
934
+ const destPath = path6.join(destDir, file);
654
935
  try {
655
- const destExists = await fs4.pathExists(destPath);
936
+ const destExists = await fs5.pathExists(destPath);
656
937
  if (destExists && file.endsWith(".json")) {
657
- const incoming = await fs4.readJson(srcPath);
658
- const existing = await fs4.readJson(destPath);
938
+ const incoming = await fs5.readJson(srcPath);
939
+ const existing = await fs5.readJson(destPath);
659
940
  const merged = merge({}, incoming, existing);
660
- await fs4.writeJson(destPath, merged, { spaces: 2 });
941
+ await fs5.writeJson(destPath, merged, { spaces: 2 });
661
942
  result.merged.push(file);
662
943
  } else if (destExists) {
663
944
  result.skipped.push(file);
664
945
  } else {
665
- await fs4.ensureDir(path5.dirname(destPath));
666
- await fs4.copy(srcPath, destPath);
946
+ await fs5.ensureDir(path6.dirname(destPath));
947
+ await fs5.copy(srcPath, destPath);
667
948
  result.replaced.push(file);
668
949
  }
669
950
  processedFiles.add(file);
@@ -692,13 +973,13 @@ async function applyFileCategories(sourceDir, destDir, versionJson) {
692
973
  processedFiles.add(file);
693
974
  continue;
694
975
  }
695
- const srcPath = path5.join(sourceDir, file);
696
- const destPath = path5.join(destDir, file);
976
+ const srcPath = path6.join(sourceDir, file);
977
+ const destPath = path6.join(destDir, file);
697
978
  try {
698
- const destExists = await fs4.pathExists(destPath);
979
+ const destExists = await fs5.pathExists(destPath);
699
980
  if (!destExists) {
700
- await fs4.ensureDir(path5.dirname(destPath));
701
- await fs4.copy(srcPath, destPath);
981
+ await fs5.ensureDir(path6.dirname(destPath));
982
+ await fs5.copy(srcPath, destPath);
702
983
  result.added.push(file);
703
984
  } else {
704
985
  result.skipped.push(file);
@@ -719,8 +1000,8 @@ function matchesPattern(filePath, pattern) {
719
1000
  }
720
1001
  async function isGitRepo(dirPath) {
721
1002
  try {
722
- const gitDir = path5.join(dirPath, ".git");
723
- return await fs4.pathExists(gitDir);
1003
+ const gitDir = path6.join(dirPath, ".git");
1004
+ return await fs5.pathExists(gitDir);
724
1005
  } catch {
725
1006
  return false;
726
1007
  }
@@ -748,16 +1029,28 @@ async function createGitBackup(dirPath, message) {
748
1029
 
749
1030
  // src/commands/upgrade.ts
750
1031
  async function upgrade(options) {
751
- p4.intro(pc5.cyan("aiorg upgrade"));
1032
+ p5.intro(pc6.cyan("aiorg upgrade"));
752
1033
  const kit = await detectKitInCwd();
753
1034
  if (!kit) {
754
1035
  error("Not in a kit directory");
755
- 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"));
756
1037
  process.exit(1);
757
1038
  }
758
- keyValue("Kit", pc5.magenta(kit.displayName));
759
- keyValue("Current version", pc5.cyan(`v${kit.version}`));
760
- 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();
761
1054
  spinner4.start("Checking for updates...");
762
1055
  let latest;
763
1056
  try {
@@ -769,13 +1062,13 @@ async function upgrade(options) {
769
1062
  }
770
1063
  if (!semver.gt(latest.version, kit.version)) {
771
1064
  blank();
772
- success(`Already on latest version (${pc5.cyan(`v${kit.version}`)})`);
773
- p4.outro("");
1065
+ success(`Already on latest version (${pc6.cyan(`v${kit.version}`)})`);
1066
+ p5.outro("");
774
1067
  return;
775
1068
  }
776
1069
  blank();
777
1070
  log(
778
- `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}`)}`
779
1072
  );
780
1073
  if (latest.allVersions && Array.isArray(latest.allVersions)) {
781
1074
  const versionsToShow = latest.allVersions.filter((v) => {
@@ -797,17 +1090,17 @@ async function upgrade(options) {
797
1090
  if (!entry) continue;
798
1091
  const highlights = entry.highlights?.join(", ") || "";
799
1092
  blank();
800
- log(`${pc5.green(`v${version2}`)}${highlights ? ` - ${pc5.white(highlights)}` : ""}`);
1093
+ log(`${pc6.green(`v${version2}`)}${highlights ? ` - ${pc6.white(highlights)}` : ""}`);
801
1094
  if (entry.added && entry.added.length > 0) {
802
1095
  for (const item of entry.added.slice(0, 2)) {
803
- log(pc5.dim(` + ${item}`));
1096
+ log(pc6.dim(` + ${item}`));
804
1097
  }
805
1098
  if (entry.added.length > 2) {
806
- log(pc5.dim(` + ... and ${entry.added.length - 2} more`));
1099
+ log(pc6.dim(` + ... and ${entry.added.length - 2} more`));
807
1100
  }
808
1101
  }
809
1102
  if (entry.upgradeNotes && version2 === latest.version) {
810
- log(pc5.yellow(` Note: ${entry.upgradeNotes}`));
1103
+ log(pc6.yellow(` Note: ${entry.upgradeNotes}`));
811
1104
  }
812
1105
  }
813
1106
  }
@@ -816,19 +1109,19 @@ async function upgrade(options) {
816
1109
  log("Your data will be preserved:");
817
1110
  const neverTouch = kit.versionJson.fileCategories?.neverTouch ?? [];
818
1111
  for (const pattern of neverTouch.slice(0, 5)) {
819
- listItem(pc5.dim(pattern));
1112
+ listItem(pc6.dim(pattern));
820
1113
  }
821
1114
  if (neverTouch.length > 5) {
822
- log(pc5.dim(` ... and ${neverTouch.length - 5} more patterns`));
1115
+ log(pc6.dim(` ... and ${neverTouch.length - 5} more patterns`));
823
1116
  }
824
1117
  if (!options.yes) {
825
1118
  blank();
826
- const shouldUpgrade = await p4.confirm({
1119
+ const shouldUpgrade = await p5.confirm({
827
1120
  message: "Proceed with upgrade?",
828
1121
  initialValue: true
829
1122
  });
830
- if (p4.isCancel(shouldUpgrade) || !shouldUpgrade) {
831
- p4.cancel("Upgrade cancelled");
1123
+ if (p5.isCancel(shouldUpgrade) || !shouldUpgrade) {
1124
+ p5.cancel("Upgrade cancelled");
832
1125
  return;
833
1126
  }
834
1127
  }
@@ -857,7 +1150,7 @@ async function upgrade(options) {
857
1150
  );
858
1151
  spinner4.stop(created ? "Git backup created" : "No changes to backup");
859
1152
  } else if (!options.yes) {
860
- const shouldBackup = await p4.confirm({
1153
+ const shouldBackup = await p5.confirm({
861
1154
  message: "Create git backup commit first?",
862
1155
  initialValue: true
863
1156
  });
@@ -888,8 +1181,8 @@ async function upgrade(options) {
888
1181
  let tempDir = null;
889
1182
  try {
890
1183
  tempDir = await createTempDir("aiorg-upgrade-");
891
- const zipPath = path6.join(tempDir, "kit.zip");
892
- const extractPath = path6.join(tempDir, "extracted");
1184
+ const zipPath = path7.join(tempDir, "kit.zip");
1185
+ const extractPath = path7.join(tempDir, "extracted");
893
1186
  const zipData = await downloadFile(downloadInfo.downloadUrl);
894
1187
  await saveToFile(zipData, zipPath);
895
1188
  const sizeKB = await getFileSizeKB(zipPath);
@@ -910,7 +1203,7 @@ async function upgrade(options) {
910
1203
  spinner4.stop("Updates applied");
911
1204
  blank();
912
1205
  success(
913
- `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}`)}`
914
1207
  );
915
1208
  blank();
916
1209
  keyValue("Files updated", String(result.replaced.length));
@@ -925,7 +1218,7 @@ async function upgrade(options) {
925
1218
  blank();
926
1219
  warn(`${result.errors.length} errors occurred:`);
927
1220
  for (const err of result.errors) {
928
- listItem(pc5.dim(err));
1221
+ listItem(pc6.dim(err));
929
1222
  }
930
1223
  }
931
1224
  await cleanupTempDir(tempDir);
@@ -936,46 +1229,46 @@ async function upgrade(options) {
936
1229
  throw error2;
937
1230
  }
938
1231
  blank();
939
- log(pc5.yellow("\u26A0\uFE0F Restart Claude Code to use new commands"));
940
- log(pc5.dim(' Type "exit" then start a new session'));
941
- 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!"));
942
1235
  }
943
1236
 
944
1237
  // src/commands/version.ts
945
- import pc6 from "picocolors";
1238
+ import pc7 from "picocolors";
946
1239
  import semver2 from "semver";
947
1240
  var CLI_VERSION = "1.0.0";
948
1241
  async function version() {
949
1242
  header("aiorg version");
950
- keyValue("CLI", pc6.cyan(`v${CLI_VERSION}`));
1243
+ keyValue("CLI", pc7.cyan(`v${CLI_VERSION}`));
951
1244
  const kit = await detectKitInCwd();
952
1245
  if (!kit) {
953
1246
  blank();
954
1247
  info("No kit detected in current directory");
955
- 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"));
956
1249
  return;
957
1250
  }
958
- keyValue(kit.displayName, pc6.cyan(`v${kit.version}`));
1251
+ keyValue(kit.displayName, pc7.cyan(`v${kit.version}`));
959
1252
  try {
960
1253
  const latest = await fetchLatestVersion(kit.name);
961
1254
  if (semver2.gt(latest.version, kit.version)) {
962
1255
  blank();
963
1256
  warn(
964
- `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}`)}`
965
1258
  );
966
- log(pc6.dim("Run 'aiorg upgrade' to update"));
1259
+ log(pc7.dim("Run 'aiorg upgrade' to update"));
967
1260
  } else {
968
1261
  blank();
969
1262
  success("You are on the latest version");
970
1263
  }
971
1264
  } catch {
972
1265
  blank();
973
- log(pc6.dim("Could not check for updates"));
1266
+ log(pc7.dim("Could not check for updates"));
974
1267
  }
975
1268
  }
976
1269
 
977
1270
  // src/commands/list.ts
978
- import pc7 from "picocolors";
1271
+ import pc8 from "picocolors";
979
1272
  function formatPrice(cents) {
980
1273
  if (cents === 0) return "";
981
1274
  return `$${(cents / 100).toFixed(0)}`;
@@ -989,44 +1282,137 @@ async function list() {
989
1282
  const freeKits = kits.filter((k) => k.tier === "free");
990
1283
  const paidKits = kits.filter((k) => k.tier === "paid");
991
1284
  blank();
992
- console.log(pc7.bold("Available Kits"));
1285
+ console.log(pc8.bold("Available Kits"));
993
1286
  blank();
994
1287
  if (freeKits.length > 0) {
995
- console.log(pc7.green(pc7.bold("FREE")));
1288
+ console.log(pc8.green(pc8.bold("FREE")));
996
1289
  blank();
997
1290
  for (const kit of freeKits) {
998
1291
  printKit(kit);
999
1292
  }
1000
1293
  }
1001
1294
  if (paidKits.length > 0) {
1002
- console.log(pc7.yellow(pc7.bold("PAID")));
1295
+ console.log(pc8.yellow(pc8.bold("PAID")));
1003
1296
  blank();
1004
1297
  for (const kit of paidKits) {
1005
1298
  printKit(kit);
1006
1299
  }
1007
1300
  }
1008
- console.log(pc7.dim("\u2500".repeat(50)));
1301
+ console.log(pc8.dim("\u2500".repeat(50)));
1009
1302
  blank();
1010
- console.log(pc7.dim("Free kits work without login."));
1011
- console.log(pc7.dim(`Run '${pc7.cyan("aiorg login")}' first for paid kits.`));
1012
- 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.`));
1013
1306
  blank();
1014
1307
  }
1015
1308
  function printKit(kit) {
1016
1309
  const price = formatPrice(kit.priceCents);
1017
- const priceStr = price ? pc7.yellow(price) : "";
1310
+ const priceStr = price ? pc8.yellow(price) : "";
1018
1311
  console.log(
1019
- ` ${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}`
1020
1313
  );
1021
1314
  if (kit.description) {
1022
- console.log(` ${pc7.dim(kit.description)}`);
1315
+ console.log(` ${pc8.dim(kit.description)}`);
1023
1316
  }
1024
1317
  console.log(
1025
- ` ${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")}`
1026
1319
  );
1027
1320
  blank();
1028
1321
  }
1029
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
+
1030
1416
  // src/index.ts
1031
1417
  var cli = cac("aiorg");
1032
1418
  cli.command("login", "Save your license key").action(async () => {
@@ -1045,9 +1431,9 @@ cli.command("logout", "Remove saved license key").action(async () => {
1045
1431
  process.exit(1);
1046
1432
  }
1047
1433
  });
1048
- 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) => {
1049
1435
  try {
1050
- await init(kit, path7, options);
1436
+ await init(kit, path8, options);
1051
1437
  } catch (error2) {
1052
1438
  error(error2 instanceof Error ? error2.message : "Init failed");
1053
1439
  process.exit(1);
@@ -1077,6 +1463,14 @@ cli.command("version", "Show CLI and kit versions").action(async () => {
1077
1463
  process.exit(1);
1078
1464
  }
1079
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
+ });
1080
1474
  cli.help();
1081
1475
  cli.version("1.0.0");
1082
1476
  cli.parse();