@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 +503 -109
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
395
|
-
import
|
|
396
|
-
import
|
|
397
|
-
import
|
|
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
|
-
|
|
441
|
-
const resolvedPath = targetPath ?
|
|
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: ${
|
|
444
|
-
log(
|
|
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 =
|
|
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 ${
|
|
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 =
|
|
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 ${
|
|
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
|
|
515
|
-
|
|
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
|
|
520
|
-
import
|
|
521
|
-
import
|
|
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
|
|
526
|
-
import
|
|
527
|
-
import { z as
|
|
528
|
-
var VersionJsonSchema =
|
|
529
|
-
version:
|
|
530
|
-
packageName:
|
|
531
|
-
packageDisplayName:
|
|
532
|
-
releasedAt:
|
|
533
|
-
minUpgradeFrom:
|
|
534
|
-
fileCategories:
|
|
535
|
-
alwaysReplace:
|
|
536
|
-
neverTouch:
|
|
537
|
-
mergeIfChanged:
|
|
538
|
-
addOnly:
|
|
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:
|
|
821
|
+
changelog: z4.record(z4.string(), z4.any()).optional()
|
|
541
822
|
});
|
|
542
|
-
var KitJsonSchema =
|
|
543
|
-
name:
|
|
544
|
-
displayName:
|
|
545
|
-
type:
|
|
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 =
|
|
549
|
-
const kitJsonPath =
|
|
550
|
-
if (!await
|
|
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
|
|
835
|
+
const versionRaw = await fs4.readJson(versionJsonPath);
|
|
555
836
|
const versionJson = VersionJsonSchema.parse(versionRaw);
|
|
556
837
|
let kitJson;
|
|
557
|
-
if (await
|
|
838
|
+
if (await fs4.pathExists(kitJsonPath)) {
|
|
558
839
|
try {
|
|
559
|
-
const kitRaw = await
|
|
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
|
|
582
|
-
import
|
|
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 =
|
|
621
|
-
const destPath =
|
|
901
|
+
const srcPath = path6.join(sourceDir, file);
|
|
902
|
+
const destPath = path6.join(destDir, file);
|
|
622
903
|
try {
|
|
623
|
-
await
|
|
624
|
-
await
|
|
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 =
|
|
653
|
-
const destPath =
|
|
933
|
+
const srcPath = path6.join(sourceDir, file);
|
|
934
|
+
const destPath = path6.join(destDir, file);
|
|
654
935
|
try {
|
|
655
|
-
const destExists = await
|
|
936
|
+
const destExists = await fs5.pathExists(destPath);
|
|
656
937
|
if (destExists && file.endsWith(".json")) {
|
|
657
|
-
const incoming = await
|
|
658
|
-
const existing = await
|
|
938
|
+
const incoming = await fs5.readJson(srcPath);
|
|
939
|
+
const existing = await fs5.readJson(destPath);
|
|
659
940
|
const merged = merge({}, incoming, existing);
|
|
660
|
-
await
|
|
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
|
|
666
|
-
await
|
|
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 =
|
|
696
|
-
const destPath =
|
|
976
|
+
const srcPath = path6.join(sourceDir, file);
|
|
977
|
+
const destPath = path6.join(destDir, file);
|
|
697
978
|
try {
|
|
698
|
-
const destExists = await
|
|
979
|
+
const destExists = await fs5.pathExists(destPath);
|
|
699
980
|
if (!destExists) {
|
|
700
|
-
await
|
|
701
|
-
await
|
|
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 =
|
|
723
|
-
return await
|
|
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
|
-
|
|
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(
|
|
1036
|
+
log(pc6.dim("Run this command from a folder with .claude/version.json"));
|
|
756
1037
|
process.exit(1);
|
|
757
1038
|
}
|
|
758
|
-
keyValue("Kit",
|
|
759
|
-
keyValue("Current version",
|
|
760
|
-
|
|
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 (${
|
|
773
|
-
|
|
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: ${
|
|
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(`${
|
|
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(
|
|
1096
|
+
log(pc6.dim(` + ${item}`));
|
|
804
1097
|
}
|
|
805
1098
|
if (entry.added.length > 2) {
|
|
806
|
-
log(
|
|
1099
|
+
log(pc6.dim(` + ... and ${entry.added.length - 2} more`));
|
|
807
1100
|
}
|
|
808
1101
|
}
|
|
809
1102
|
if (entry.upgradeNotes && version2 === latest.version) {
|
|
810
|
-
log(
|
|
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(
|
|
1112
|
+
listItem(pc6.dim(pattern));
|
|
820
1113
|
}
|
|
821
1114
|
if (neverTouch.length > 5) {
|
|
822
|
-
log(
|
|
1115
|
+
log(pc6.dim(` ... and ${neverTouch.length - 5} more patterns`));
|
|
823
1116
|
}
|
|
824
1117
|
if (!options.yes) {
|
|
825
1118
|
blank();
|
|
826
|
-
const shouldUpgrade = await
|
|
1119
|
+
const shouldUpgrade = await p5.confirm({
|
|
827
1120
|
message: "Proceed with upgrade?",
|
|
828
1121
|
initialValue: true
|
|
829
1122
|
});
|
|
830
|
-
if (
|
|
831
|
-
|
|
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
|
|
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 =
|
|
892
|
-
const extractPath =
|
|
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}: ${
|
|
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(
|
|
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(
|
|
940
|
-
log(
|
|
941
|
-
|
|
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
|
|
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",
|
|
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(
|
|
1248
|
+
log(pc7.dim("Run this command from a folder containing a kit"));
|
|
956
1249
|
return;
|
|
957
1250
|
}
|
|
958
|
-
keyValue(kit.displayName,
|
|
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: ${
|
|
1257
|
+
`Update available: ${pc7.cyan(`v${kit.version}`)} \u2192 ${pc7.green(`v${latest.version}`)}`
|
|
965
1258
|
);
|
|
966
|
-
log(
|
|
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(
|
|
1266
|
+
log(pc7.dim("Could not check for updates"));
|
|
974
1267
|
}
|
|
975
1268
|
}
|
|
976
1269
|
|
|
977
1270
|
// src/commands/list.ts
|
|
978
|
-
import
|
|
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(
|
|
1285
|
+
console.log(pc8.bold("Available Kits"));
|
|
993
1286
|
blank();
|
|
994
1287
|
if (freeKits.length > 0) {
|
|
995
|
-
console.log(
|
|
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(
|
|
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(
|
|
1301
|
+
console.log(pc8.dim("\u2500".repeat(50)));
|
|
1009
1302
|
blank();
|
|
1010
|
-
console.log(
|
|
1011
|
-
console.log(
|
|
1012
|
-
console.log(
|
|
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 ?
|
|
1310
|
+
const priceStr = price ? pc8.yellow(price) : "";
|
|
1018
1311
|
console.log(
|
|
1019
|
-
` ${
|
|
1312
|
+
` ${pc8.bold(kit.name.padEnd(24))} ${pc8.cyan(`v${kit.version}`)} ${priceStr}`
|
|
1020
1313
|
);
|
|
1021
1314
|
if (kit.description) {
|
|
1022
|
-
console.log(` ${
|
|
1315
|
+
console.log(` ${pc8.dim(kit.description)}`);
|
|
1023
1316
|
}
|
|
1024
1317
|
console.log(
|
|
1025
|
-
` ${
|
|
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,
|
|
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,
|
|
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();
|