pai-zero 0.13.2 → 0.14.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/bin/pai.js +1059 -189
- package/dist/bin/pai.js.map +1 -1
- package/dist/cli/index.js +1059 -189
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/pai.js
CHANGED
|
@@ -594,8 +594,8 @@ function parseAiResult(result, analysis, projectName) {
|
|
|
594
594
|
async function interactiveInterview(analysis, _cwd, projectName) {
|
|
595
595
|
const { default: inquirer } = await import("inquirer");
|
|
596
596
|
const chalk9 = (await import("chalk")).default;
|
|
597
|
-
|
|
598
|
-
|
|
597
|
+
const Separator = inquirer.Separator;
|
|
598
|
+
step(1, TOTAL_STEPS, "\uD504\uB85C\uC81D\uD2B8 \uC124\uC815");
|
|
599
599
|
console.log("");
|
|
600
600
|
const { mode } = await inquirer.prompt([{
|
|
601
601
|
type: "list",
|
|
@@ -603,48 +603,54 @@ async function interactiveInterview(analysis, _cwd, projectName) {
|
|
|
603
603
|
message: "\uC5B4\uB5A4 \uC218\uC900\uC758 \uD504\uB85C\uC81D\uD2B8\uC778\uAC00\uC694?",
|
|
604
604
|
choices: [
|
|
605
605
|
{
|
|
606
|
-
name: `\u{1F9EA} prototype ${colors.dim("\u2500 \uBE60\uB978 \uAC80\uC99D (\uC544\uC774\uB514\uC5B4
|
|
606
|
+
name: `\u{1F9EA} prototype ${colors.dim("\u2500 \uBE60\uB978 \uAC80\uC99D\uC6A9 (\uC544\uC774\uB514\uC5B4 \uD655\uC778 \xB7 \uD654\uBA74 \uC2DC\uC548 \xB7 \uAC04\uB2E8 \uD750\uB984)")}`,
|
|
607
607
|
value: "prototype"
|
|
608
608
|
},
|
|
609
609
|
{
|
|
610
|
-
name: `\u{1F52C} poc ${colors.dim("\u2500
|
|
610
|
+
name: `\u{1F52C} poc ${colors.dim("\u2500 \uB3D9\uC791 \uB370\uBAA8\uC6A9 (\uD575\uC2EC \uAE30\uB2A5 \uAD6C\uD604 \xB7 \uB0B4\uBD80 \uC2DC\uC5F0)")}`,
|
|
611
611
|
value: "poc"
|
|
612
612
|
},
|
|
613
613
|
{
|
|
614
|
-
name: `\u{1F3ED} production ${colors.dim("\u2500 \uC6B4\uC601 \uC11C\uBE44\uC2A4 (\uD14C\uC2A4\uD2B8\
|
|
614
|
+
name: `\u{1F3ED} production ${colors.dim("\u2500 \uC6B4\uC601 \uC11C\uBE44\uC2A4\uC6A9 (\uD14C\uC2A4\uD2B8 \xB7 QA \xB7 \uBC30\uD3EC \xB7 \uBAA8\uB2C8\uD130\uB9C1)")}`,
|
|
615
615
|
value: "production"
|
|
616
616
|
}
|
|
617
617
|
]
|
|
618
618
|
}]);
|
|
619
619
|
console.log("");
|
|
620
|
-
console.log(colors.dim(" \uC774 \uC218\uC900\uC5D0 \
|
|
620
|
+
console.log(colors.dim(" \uC774 \uC218\uC900\uC5D0 \uAE30\uBCF8 \uD3EC\uD568\uB418\uB294 \uD56D\uBAA9"));
|
|
621
621
|
console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
};
|
|
627
|
-
|
|
628
|
-
console.log(` ${colors.
|
|
622
|
+
console.log(` ${colors.success("\u2713")} GitHub \uD504\uB85C\uC81D\uD2B8 \uAD6C\uC870`);
|
|
623
|
+
console.log(` ${colors.success("\u2713")} PRD \uC124\uACC4 \uD15C\uD50C\uB9BF (OpenSpec)`);
|
|
624
|
+
console.log(` ${colors.success("\u2713")} AI \uCEE8\uD14D\uC2A4\uD2B8 (CLAUDE.md)`);
|
|
625
|
+
console.log(` ${colors.success("\u2713")} /pai \uC2AC\uB798\uC2DC \uCEE4\uB9E8\uB4DC`);
|
|
626
|
+
console.log(` ${colors.success("\u2713")} \uD488\uC9C8 \uC9C4\uB2E8 (Vibe-Ready)`);
|
|
627
|
+
if (mode === "poc" || mode === "production") {
|
|
628
|
+
console.log(` ${colors.accent("+")} OMC \uC5D0\uC774\uC804\uD2B8 \uC624\uCF00\uC2A4\uD2B8\uB808\uC774\uC158`);
|
|
629
|
+
}
|
|
630
|
+
if (mode === "production") {
|
|
631
|
+
console.log(` ${colors.accent("+")} gstack \uD14C\uC2A4\uD2B8 \xB7 Harness \uD488\uC9C8 \uAC80\uC99D`);
|
|
629
632
|
}
|
|
630
|
-
|
|
631
|
-
|
|
633
|
+
console.log(colors.dim(" + \uCD94\uAC00 \uAE30\uB2A5\uC740 \uB2E4\uC74C \uB2E8\uACC4\uC5D0\uC11C \uC120\uD0DD"));
|
|
634
|
+
console.log("");
|
|
635
|
+
step(2, TOTAL_STEPS, "\uCD94\uAC00 \uAE30\uB2A5 \uC120\uD0DD");
|
|
636
|
+
console.log("");
|
|
637
|
+
console.log(colors.dim(" \uC774 \uD504\uB85C\uC81D\uD2B8\uC5D0 \uD544\uC694\uD55C \uAE30\uB2A5\uB9CC \uC120\uD0DD\uD569\uB2C8\uB2E4."));
|
|
638
|
+
console.log(colors.dim(" \uC9C0\uAE08 \uC815\uD558\uC9C0 \uC54A\uC544\uB3C4 \uB098\uC911\uC5D0 \uB2E4\uC2DC \uC124\uC815\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4."));
|
|
632
639
|
console.log("");
|
|
640
|
+
let authMethods = [];
|
|
641
|
+
let customAuth;
|
|
633
642
|
const { needAuth } = await inquirer.prompt([{
|
|
634
643
|
type: "list",
|
|
635
644
|
name: "needAuth",
|
|
636
|
-
message:
|
|
645
|
+
message: `\uB85C\uADF8\uC778 \uAE30\uB2A5\uC774 \uD544\uC694\uD55C\uAC00\uC694? ${colors.dim("\u2500 \uC0AC\uC6A9\uC790\uB97C \uAD6C\uBD84\uD574\uC57C \uD560 \uB54C \uD544\uC694")}`,
|
|
637
646
|
choices: [
|
|
638
|
-
{ name: "\
|
|
647
|
+
{ name: "\uB098\uC911\uC5D0 \uC124\uC815", value: false },
|
|
639
648
|
{ name: "\uB124, \uC124\uC815\uD560\uAC8C\uC694", value: true }
|
|
640
649
|
]
|
|
641
650
|
}]);
|
|
642
|
-
let authMethods = [];
|
|
643
|
-
let customAuth;
|
|
644
651
|
if (needAuth) {
|
|
645
652
|
console.log("");
|
|
646
653
|
hint("Space\uB85C \uC120\uD0DD \xB7 \uC120\uD0DD \uD6C4 Enter\uB85C \uC644\uB8CC");
|
|
647
|
-
const Separator = inquirer.Separator;
|
|
648
654
|
const { auths } = await inquirer.prompt([{
|
|
649
655
|
type: "checkbox",
|
|
650
656
|
name: "auths",
|
|
@@ -684,55 +690,48 @@ async function interactiveInterview(analysis, _cwd, projectName) {
|
|
|
684
690
|
}
|
|
685
691
|
}
|
|
686
692
|
}
|
|
687
|
-
let extraTools = [];
|
|
688
|
-
if (mode === "poc" || mode === "production") {
|
|
689
|
-
extraTools.push("omc");
|
|
690
|
-
}
|
|
691
|
-
if (mode === "poc" || mode === "production") {
|
|
692
|
-
console.log("");
|
|
693
|
-
const { hosting } = await inquirer.prompt([{
|
|
694
|
-
type: "list",
|
|
695
|
-
name: "hosting",
|
|
696
|
-
message: "\uBC30\uD3EC \uD658\uACBD:",
|
|
697
|
-
choices: [
|
|
698
|
-
{ name: `Vercel ${colors.dim("\u2500 \uC790\uB3D9 \uBC30\uD3EC (Preview + Production)")}`, value: "vercel" },
|
|
699
|
-
{ name: `\uB2E4\uB978 \uC11C\uBE44\uC2A4 \uC0AC\uC6A9 ${colors.dim("\u2500 AWS, GCP, Netlify \uB4F1)")}`, value: "other" },
|
|
700
|
-
{ name: `\uB098\uC911\uC5D0 \uC815\uD558\uAE30 ${colors.dim("\u2500 \uBC30\uD3EC \uC124\uC815 \uC5C6\uC774 \uC2DC\uC791")}`, value: "skip" }
|
|
701
|
-
]
|
|
702
|
-
}]);
|
|
703
|
-
if (hosting === "vercel") extraTools.push("vercel");
|
|
704
|
-
}
|
|
705
|
-
if (mode === "production") {
|
|
706
|
-
console.log("");
|
|
707
|
-
const { database } = await inquirer.prompt([{
|
|
708
|
-
type: "list",
|
|
709
|
-
name: "database",
|
|
710
|
-
message: "\uB370\uC774\uD130\uBCA0\uC774\uC2A4:",
|
|
711
|
-
choices: [
|
|
712
|
-
{ name: `Supabase ${colors.dim("\u2500 PostgreSQL + Auth + Storage")}`, value: "supabase" },
|
|
713
|
-
{ name: `\uB2E4\uB978 \uC11C\uBE44\uC2A4 \uC0AC\uC6A9 ${colors.dim("\u2500 PlanetScale, Neon, Firebase \uB4F1")}`, value: "other" },
|
|
714
|
-
{ name: `\uB098\uC911\uC5D0 \uC815\uD558\uAE30 ${colors.dim("\u2500 DB \uC124\uC815 \uC5C6\uC774 \uC2DC\uC791")}`, value: "skip" }
|
|
715
|
-
]
|
|
716
|
-
}]);
|
|
717
|
-
if (database === "supabase") extraTools.push("supabase");
|
|
718
|
-
extraTools.push("gstack", "harness");
|
|
719
|
-
}
|
|
720
|
-
let mcp;
|
|
721
693
|
console.log("");
|
|
694
|
+
let hostingChoice = "skip";
|
|
695
|
+
const { hosting } = await inquirer.prompt([{
|
|
696
|
+
type: "list",
|
|
697
|
+
name: "hosting",
|
|
698
|
+
message: `\uBC30\uD3EC \uD658\uACBD\uC744 \uC124\uC815\uD560\uAE4C\uC694? ${colors.dim("\u2500 \uB9CC\uB4E0 \uC11C\uBE44\uC2A4\uB97C \uC778\uD130\uB137\uC5D0 \uACF5\uAC1C\uD560 \uB54C \uD544\uC694")}`,
|
|
699
|
+
choices: [
|
|
700
|
+
{ name: "\uB098\uC911\uC5D0 \uC124\uC815", value: "skip" },
|
|
701
|
+
{ name: `Vercel ${colors.dim("\u2500 \uC790\uB3D9 \uBC30\uD3EC (Preview + Production)")}`, value: "vercel" },
|
|
702
|
+
{ name: `\uB2E4\uB978 \uC11C\uBE44\uC2A4 \uC0AC\uC6A9 ${colors.dim("\u2500 AWS, GCP, Netlify \uB4F1")}`, value: "other" }
|
|
703
|
+
]
|
|
704
|
+
}]);
|
|
705
|
+
hostingChoice = hosting;
|
|
706
|
+
console.log("");
|
|
707
|
+
let dbChoice = "skip";
|
|
708
|
+
const { database } = await inquirer.prompt([{
|
|
709
|
+
type: "list",
|
|
710
|
+
name: "database",
|
|
711
|
+
message: `\uB370\uC774\uD130\uBCA0\uC774\uC2A4\uAC00 \uD544\uC694\uD55C\uAC00\uC694? ${colors.dim("\u2500 \uC0AC\uC6A9\uC790 \uB370\uC774\uD130\uB97C \uC800\uC7A5\uD560 \uB54C \uD544\uC694")}`,
|
|
712
|
+
choices: [
|
|
713
|
+
{ name: "\uB098\uC911\uC5D0 \uC124\uC815", value: "skip" },
|
|
714
|
+
{ name: `Supabase ${colors.dim("\u2500 PostgreSQL + Auth + Storage")}`, value: "supabase" },
|
|
715
|
+
{ name: `\uB2E4\uB978 \uC11C\uBE44\uC2A4 \uC0AC\uC6A9 ${colors.dim("\u2500 PlanetScale, Neon, Firebase \uB4F1")}`, value: "other" }
|
|
716
|
+
]
|
|
717
|
+
}]);
|
|
718
|
+
dbChoice = database;
|
|
719
|
+
console.log("");
|
|
720
|
+
let mcp;
|
|
721
|
+
let mcpEnabled = false;
|
|
722
722
|
const { mcpNeed } = await inquirer.prompt([{
|
|
723
723
|
type: "list",
|
|
724
724
|
name: "mcpNeed",
|
|
725
|
-
message:
|
|
725
|
+
message: `MCP \uC11C\uBC84\uAC00 \uD544\uC694\uD55C\uAC00\uC694? ${colors.dim("\u2500 Claude\uAC00 \uD638\uCD9C\uD560 \uCEE4\uC2A4\uD140 \uB3C4\uAD6C\uB97C \uB9CC\uB4E4 \uB54C \uD544\uC694")}`,
|
|
726
726
|
choices: [
|
|
727
|
-
{ name:
|
|
728
|
-
{ name:
|
|
729
|
-
]
|
|
730
|
-
default: "yes"
|
|
727
|
+
{ name: "\uB098\uC911\uC5D0 \uC124\uC815", value: "no" },
|
|
728
|
+
{ name: `\uB124, \uC124\uC815\uD560\uAC8C\uC694 ${colors.dim("\u2500 \uB3C4\uAD6C/\uB9AC\uC18C\uC2A4/\uD504\uB86C\uD504\uD2B8 \uC11C\uBC84 \uC0DD\uC131")}`, value: "yes" }
|
|
729
|
+
]
|
|
731
730
|
}]);
|
|
732
|
-
|
|
733
|
-
|
|
731
|
+
if (mcpNeed === "yes") {
|
|
732
|
+
mcpEnabled = true;
|
|
734
733
|
console.log("");
|
|
735
|
-
const hasSupabase =
|
|
734
|
+
const hasSupabase = dbChoice === "supabase";
|
|
736
735
|
const typeChoices = [
|
|
737
736
|
{ name: `Tools \uC11C\uBC84 (\uAC1C\uC778\uC6A9) ${colors.dim("\u2500 AI\uAC00 \uD638\uCD9C\uD560 \uAE30\uB2A5 \uC81C\uACF5")}`, value: "tools" }
|
|
738
737
|
];
|
|
@@ -765,48 +764,67 @@ async function interactiveInterview(analysis, _cwd, projectName) {
|
|
|
765
764
|
type: mcpType,
|
|
766
765
|
name: mcpName.trim()
|
|
767
766
|
};
|
|
768
|
-
extraTools.push("mcp");
|
|
769
767
|
}
|
|
770
768
|
const { RECIPES: RECIPES2 } = await Promise.resolve().then(() => (init_recipes(), recipes_exports));
|
|
771
769
|
const recipeKeys = Object.keys(RECIPES2);
|
|
772
770
|
let selectedRecipes = [];
|
|
773
771
|
if (recipeKeys.length > 0) {
|
|
774
772
|
console.log("");
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
name: "recipes",
|
|
780
|
-
message: "\uC0AC\uB0B4 \uC2DC\uC2A4\uD15C \uC5F0\uB3D9 \uB808\uC2DC\uD53C\uB97C \uC124\uCE58\uD560\uAE4C\uC694?",
|
|
773
|
+
const { needInternal } = await inquirer.prompt([{
|
|
774
|
+
type: "list",
|
|
775
|
+
name: "needInternal",
|
|
776
|
+
message: `\uC0AC\uB0B4 \uC2DC\uC2A4\uD15C\uACFC \uC5F0\uACB0\uC774 \uD544\uC694\uD55C\uAC00\uC694? ${colors.dim("\u2500 \uC0AC\uB0B4 \uC778\uC99D, \uBA54\uC2E0\uC800 \uB4F1 \uB0B4\uBD80 \uC11C\uBE44\uC2A4 \uC5F0\uB3D9")}`,
|
|
781
777
|
choices: [
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
value: k
|
|
785
|
-
})),
|
|
786
|
-
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
787
|
-
{ name: chalk9.green("\u21B5 \uC120\uD0DD \uC644\uB8CC (\uAC74\uB108\uB6F0\uAE30)"), value: "__done__" }
|
|
778
|
+
{ name: "\uC544\uB2C8\uC694", value: false },
|
|
779
|
+
{ name: "\uB124, \uC120\uD0DD\uD560\uAC8C\uC694", value: true }
|
|
788
780
|
]
|
|
789
781
|
}]);
|
|
790
|
-
|
|
782
|
+
if (needInternal) {
|
|
783
|
+
console.log("");
|
|
784
|
+
hint("Space\uB85C \uC120\uD0DD \xB7 \uC120\uD0DD \uD6C4 Enter\uB85C \uC644\uB8CC");
|
|
785
|
+
const recipeAnswer = await inquirer.prompt([{
|
|
786
|
+
type: "checkbox",
|
|
787
|
+
name: "recipes",
|
|
788
|
+
message: "\uC0AC\uB0B4 \uC5F0\uB3D9 \uB808\uC2DC\uD53C\uB97C \uC120\uD0DD\uD558\uC138\uC694:",
|
|
789
|
+
choices: [
|
|
790
|
+
...recipeKeys.map((k) => ({
|
|
791
|
+
name: `${RECIPES2[k].label} ${colors.dim("\u2500 " + RECIPES2[k].description)}`,
|
|
792
|
+
value: k
|
|
793
|
+
})),
|
|
794
|
+
new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
795
|
+
{ name: chalk9.green("\u21B5 \uC120\uD0DD \uC644\uB8CC"), value: "__done__" }
|
|
796
|
+
]
|
|
797
|
+
}]);
|
|
798
|
+
selectedRecipes = recipeAnswer.recipes.filter((x) => x !== "__done__");
|
|
799
|
+
}
|
|
791
800
|
}
|
|
792
|
-
|
|
801
|
+
const extraTools = [];
|
|
802
|
+
if (mode === "poc" || mode === "production") extraTools.push("omc");
|
|
803
|
+
if (mode === "production") extraTools.push("gstack", "harness");
|
|
804
|
+
if (hostingChoice === "vercel") extraTools.push("vercel");
|
|
805
|
+
if (dbChoice === "supabase") extraTools.push("supabase");
|
|
806
|
+
if (mcpEnabled) extraTools.push("mcp");
|
|
807
|
+
console.log("");
|
|
808
|
+
step(3, TOTAL_STEPS, "\uC124\uCE58 \uD655\uC778");
|
|
809
|
+
console.log("");
|
|
810
|
+
console.log(colors.dim(" \uC544\uB798 \uC124\uC815\uC73C\uB85C \uD504\uB85C\uC81D\uD2B8\uB97C \uC900\uBE44\uD569\uB2C8\uB2E4."));
|
|
793
811
|
console.log("");
|
|
794
|
-
console.log(colors.dim(" \uC124\uCE58\uB420 \
|
|
812
|
+
console.log(colors.dim(" \uC124\uCE58\uB420 \uAE30\uB2A5"));
|
|
795
813
|
console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
796
|
-
console.log(` ${colors.success("\u2713")} GitHub \uD504\uB85C\uC81D\uD2B8 \uAD6C\uC870`);
|
|
797
|
-
console.log(` ${colors.success("\u2713")} PRD \uC124\uACC4 \uD15C\uD50C\uB9BF`);
|
|
798
|
-
console.log(` ${colors.success("\u2713")} AI \
|
|
799
|
-
console.log(` ${colors.success("\u2713")}
|
|
800
|
-
console.log(` ${colors.success("\u2713")}
|
|
814
|
+
console.log(` ${colors.success("\u2713")} GitHub \uD504\uB85C\uC81D\uD2B8 \uAD6C\uC870 ${colors.dim("\u2500 \uD3F4\uB354 \xB7 \uBE0C\uB79C\uCE58 \xB7 .gitignore \uAE30\uBCF8 \uBF08\uB300")}`);
|
|
815
|
+
console.log(` ${colors.success("\u2713")} PRD \uC124\uACC4 \uD15C\uD50C\uB9BF ${colors.dim("\u2500 \uC694\uAD6C\uC0AC\uD56D\uC744 AI\uAC00 \uC774\uD574\uD558\uB294 \uD615\uC2DD\uC73C\uB85C \uC815\uB9AC")}`);
|
|
816
|
+
console.log(` ${colors.success("\u2713")} AI \uCEE8\uD14D\uC2A4\uD2B8 (CLAUDE.md) ${colors.dim("\u2500 Claude \uC791\uC5C5 \uAE30\uC900 \uBB38\uC11C")}`);
|
|
817
|
+
console.log(` ${colors.success("\u2713")} /pai \uC2AC\uB798\uC2DC \uCEE4\uB9E8\uB4DC ${colors.dim("\u2500 \uC124\uACC4\xB7\uAC80\uC99D\xB7\uD3C9\uAC00\uB97C Claude \uC548\uC5D0\uC11C \uC2E4\uD589")}`);
|
|
818
|
+
console.log(` ${colors.success("\u2713")} Harness \uD488\uC9C8 \uC815\uCC45 ${colors.dim("\u2500 hook \xB7 CI \xB7 lint \uADDC\uCE59 \uC790\uB3D9 \uAD6C\uC131")}`);
|
|
801
819
|
if (extraTools.length > 0) {
|
|
802
820
|
console.log("");
|
|
803
|
-
if (extraTools.includes("omc")) console.log(` ${colors.accent("+")} OMC
|
|
804
|
-
if (extraTools.includes("vercel")) console.log(` ${colors.accent("+")} Vercel
|
|
805
|
-
if (extraTools.includes("supabase")) console.log(` ${colors.accent("+")} Supabase
|
|
806
|
-
if (extraTools.includes("gstack")) console.log(` ${colors.accent("+")} gstack
|
|
807
|
-
if (extraTools.includes("harness")) console.log(` ${colors.accent("+")} Harness
|
|
821
|
+
if (extraTools.includes("omc")) console.log(` ${colors.accent("+")} OMC ${colors.dim("\u2500 AI \uC5D0\uC774\uC804\uD2B8 \uC791\uC5C5 \uBD84\uC5C5")}`);
|
|
822
|
+
if (extraTools.includes("vercel")) console.log(` ${colors.accent("+")} Vercel ${colors.dim("\u2500 \uC790\uB3D9 \uBC30\uD3EC (Preview + Production)")}`);
|
|
823
|
+
if (extraTools.includes("supabase")) console.log(` ${colors.accent("+")} Supabase ${colors.dim("\u2500 PostgreSQL + Auth + Storage")}`);
|
|
824
|
+
if (extraTools.includes("gstack")) console.log(` ${colors.accent("+")} gstack ${colors.dim("\u2500 \uD14C\uC2A4\uD2B8 \uC790\uB3D9\uD654")}`);
|
|
825
|
+
if (extraTools.includes("harness")) console.log(` ${colors.accent("+")} Harness ${colors.dim("\u2500 \uD488\uC9C8 \uAC80\uC99D")}`);
|
|
808
826
|
if (extraTools.includes("mcp") && mcp) {
|
|
809
|
-
console.log(` ${colors.accent("+")} MCP \uC11C\uBC84 (${mcp.name}
|
|
827
|
+
console.log(` ${colors.accent("+")} MCP \uC11C\uBC84 (${mcp.name}) ${colors.dim("\u2500 " + mcp.type)}`);
|
|
810
828
|
}
|
|
811
829
|
}
|
|
812
830
|
if (authMethods.length > 0) {
|
|
@@ -815,7 +833,29 @@ async function interactiveInterview(analysis, _cwd, projectName) {
|
|
|
815
833
|
}
|
|
816
834
|
if (selectedRecipes.length > 0) {
|
|
817
835
|
console.log("");
|
|
818
|
-
console.log(` ${colors.accent("+")} \uC0AC\uB0B4 \uC5F0\uB3D9
|
|
836
|
+
console.log(` ${colors.accent("+")} \uC0AC\uB0B4 \uC5F0\uB3D9: ${selectedRecipes.join(", ")}`);
|
|
837
|
+
}
|
|
838
|
+
console.log("");
|
|
839
|
+
console.log(colors.dim(" \uC0DD\uC131\uB420 \uD575\uC2EC \uD30C\uC77C"));
|
|
840
|
+
console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
841
|
+
const expectedFiles = [
|
|
842
|
+
"CLAUDE.md",
|
|
843
|
+
".claude/settings.json",
|
|
844
|
+
".harness.json",
|
|
845
|
+
"docs/openspec.md",
|
|
846
|
+
".pai/config.json"
|
|
847
|
+
];
|
|
848
|
+
if (authMethods.length > 0 || extraTools.includes("supabase") || extraTools.includes("vercel")) {
|
|
849
|
+
expectedFiles.push(".env.local");
|
|
850
|
+
}
|
|
851
|
+
if (extraTools.includes("omc")) expectedFiles.push(".pai/omc.md");
|
|
852
|
+
if (extraTools.includes("vercel")) expectedFiles.push("vercel.json");
|
|
853
|
+
if (extraTools.includes("mcp") && mcp) expectedFiles.push(`mcp-server/${mcp.name}/`);
|
|
854
|
+
if (selectedRecipes.length > 0) {
|
|
855
|
+
for (const r of selectedRecipes) expectedFiles.push(`docs/recipes/${r}/`);
|
|
856
|
+
}
|
|
857
|
+
for (const f of expectedFiles) {
|
|
858
|
+
console.log(` ${colors.dim("\xB7")} ${f}`);
|
|
819
859
|
}
|
|
820
860
|
console.log("");
|
|
821
861
|
const { proceed } = await inquirer.prompt([{
|
|
@@ -853,12 +893,14 @@ async function interactiveInterview(analysis, _cwd, projectName) {
|
|
|
853
893
|
autoGenerated: false
|
|
854
894
|
};
|
|
855
895
|
}
|
|
896
|
+
var TOTAL_STEPS;
|
|
856
897
|
var init_interviewer = __esm({
|
|
857
898
|
"src/stages/environment/interviewer.ts"() {
|
|
858
899
|
"use strict";
|
|
859
900
|
init_ui();
|
|
860
901
|
init_logger();
|
|
861
902
|
init_config();
|
|
903
|
+
TOTAL_STEPS = 4;
|
|
862
904
|
}
|
|
863
905
|
});
|
|
864
906
|
|
|
@@ -3605,6 +3647,543 @@ var init_detector = __esm({
|
|
|
3605
3647
|
}
|
|
3606
3648
|
});
|
|
3607
3649
|
|
|
3650
|
+
// src/core/harness-defaults.ts
|
|
3651
|
+
function resolveConfig(partial) {
|
|
3652
|
+
const base = HARNESS_PRESETS[partial.scenario];
|
|
3653
|
+
if (!partial.overrides) return { ...base };
|
|
3654
|
+
const overrides = partial.overrides;
|
|
3655
|
+
return {
|
|
3656
|
+
...base,
|
|
3657
|
+
gates: {
|
|
3658
|
+
preCommit: { ...base.gates.preCommit, ...overrides.gates?.preCommit },
|
|
3659
|
+
prePush: { ...base.gates.prePush, ...overrides.gates?.prePush },
|
|
3660
|
+
ci: { ...base.gates.ci, ...overrides.gates?.ci }
|
|
3661
|
+
},
|
|
3662
|
+
scripts: overrides.scripts ?? base.scripts,
|
|
3663
|
+
permissions: overrides.permissions ? {
|
|
3664
|
+
allow: [...base.permissions.allow, ...overrides.permissions.allow ?? []],
|
|
3665
|
+
deny: [...base.permissions.deny, ...overrides.permissions.deny ?? []]
|
|
3666
|
+
} : base.permissions,
|
|
3667
|
+
rules: { ...base.rules, ...overrides.rules }
|
|
3668
|
+
};
|
|
3669
|
+
}
|
|
3670
|
+
var SCRIPTS_CORE, SCRIPTS_TEST_BASIC, SCRIPTS_TEST_FULL, SCRIPTS_VERIFY, PERMISSIONS_BASE, PERMISSIONS_PRODUCTION, RULES_PROTOTYPE, RULES_PRODUCTION, RULES_TEAM, PRESET_PROTOTYPE, PRESET_PRODUCTION, PRESET_TEAM, HARNESS_PRESETS;
|
|
3671
|
+
var init_harness_defaults = __esm({
|
|
3672
|
+
"src/core/harness-defaults.ts"() {
|
|
3673
|
+
"use strict";
|
|
3674
|
+
SCRIPTS_CORE = [
|
|
3675
|
+
{ name: "format", command: "prettier --write .", required: true },
|
|
3676
|
+
{ name: "lint", command: "eslint .", required: true },
|
|
3677
|
+
{ name: "lint:fix", command: "eslint . --fix", required: false },
|
|
3678
|
+
{ name: "typecheck", command: "tsc --noEmit", required: true },
|
|
3679
|
+
{ name: "build", command: "tsup", required: true }
|
|
3680
|
+
];
|
|
3681
|
+
SCRIPTS_TEST_BASIC = [
|
|
3682
|
+
{ name: "test", command: "vitest run", required: true }
|
|
3683
|
+
];
|
|
3684
|
+
SCRIPTS_TEST_FULL = [
|
|
3685
|
+
{ name: "test", command: "vitest run", required: true },
|
|
3686
|
+
{ name: "test:unit", command: "vitest run --project unit", required: true },
|
|
3687
|
+
{ name: "test:integration", command: "vitest run --project integration", required: false },
|
|
3688
|
+
{ name: "test:smoke", command: "npm run build && node scripts/smoke.js", required: false }
|
|
3689
|
+
];
|
|
3690
|
+
SCRIPTS_VERIFY = [
|
|
3691
|
+
{ name: "verify", command: "npm run lint && npm run typecheck && npm run test", required: true },
|
|
3692
|
+
{ name: "verify:full", command: "npm run lint && npm run typecheck && npm run test && npm run build", required: false }
|
|
3693
|
+
];
|
|
3694
|
+
PERMISSIONS_BASE = {
|
|
3695
|
+
allow: [
|
|
3696
|
+
"Bash(npm run lint)",
|
|
3697
|
+
"Bash(npm run lint:fix)",
|
|
3698
|
+
"Bash(npm run format)",
|
|
3699
|
+
"Bash(npm run typecheck)",
|
|
3700
|
+
"Bash(npm run test)",
|
|
3701
|
+
"Bash(npm run build)",
|
|
3702
|
+
"Read(**)",
|
|
3703
|
+
"Edit(**)"
|
|
3704
|
+
],
|
|
3705
|
+
deny: [
|
|
3706
|
+
"Bash(rm -rf /)",
|
|
3707
|
+
"Bash(git push --force)",
|
|
3708
|
+
"Bash(git reset --hard)",
|
|
3709
|
+
"Bash(curl * | sh)",
|
|
3710
|
+
"Bash(npx -y *)"
|
|
3711
|
+
]
|
|
3712
|
+
};
|
|
3713
|
+
PERMISSIONS_PRODUCTION = {
|
|
3714
|
+
allow: [
|
|
3715
|
+
...PERMISSIONS_BASE.allow,
|
|
3716
|
+
"Bash(npm run test:unit)",
|
|
3717
|
+
"Bash(npm run test:integration)",
|
|
3718
|
+
"Bash(npm run verify)",
|
|
3719
|
+
"Bash(npm run verify:full)"
|
|
3720
|
+
],
|
|
3721
|
+
deny: [
|
|
3722
|
+
...PERMISSIONS_BASE.deny,
|
|
3723
|
+
"Bash(npm publish)",
|
|
3724
|
+
"Bash(git push origin main)"
|
|
3725
|
+
]
|
|
3726
|
+
};
|
|
3727
|
+
RULES_PROTOTYPE = {
|
|
3728
|
+
specBeforeCode: false,
|
|
3729
|
+
prMergeRequiresReview: false,
|
|
3730
|
+
branchProtection: false,
|
|
3731
|
+
requireWorkflow: false
|
|
3732
|
+
};
|
|
3733
|
+
RULES_PRODUCTION = {
|
|
3734
|
+
specBeforeCode: true,
|
|
3735
|
+
prMergeRequiresReview: true,
|
|
3736
|
+
branchProtection: true,
|
|
3737
|
+
requireWorkflow: true
|
|
3738
|
+
};
|
|
3739
|
+
RULES_TEAM = {
|
|
3740
|
+
specBeforeCode: true,
|
|
3741
|
+
prMergeRequiresReview: true,
|
|
3742
|
+
branchProtection: true,
|
|
3743
|
+
requireWorkflow: true
|
|
3744
|
+
};
|
|
3745
|
+
PRESET_PROTOTYPE = {
|
|
3746
|
+
version: "1.0",
|
|
3747
|
+
scenario: "prototype",
|
|
3748
|
+
preset: "prototype",
|
|
3749
|
+
gates: {
|
|
3750
|
+
preCommit: {
|
|
3751
|
+
enabled: true,
|
|
3752
|
+
scripts: ["format", "lint"]
|
|
3753
|
+
},
|
|
3754
|
+
prePush: {
|
|
3755
|
+
enabled: false,
|
|
3756
|
+
scripts: []
|
|
3757
|
+
},
|
|
3758
|
+
ci: {
|
|
3759
|
+
enabled: true,
|
|
3760
|
+
scripts: ["lint", "build"],
|
|
3761
|
+
triggers: {
|
|
3762
|
+
pullRequest: true,
|
|
3763
|
+
push: { branches: ["main"] }
|
|
3764
|
+
},
|
|
3765
|
+
nodeVersion: "20"
|
|
3766
|
+
}
|
|
3767
|
+
},
|
|
3768
|
+
scripts: [...SCRIPTS_CORE, ...SCRIPTS_TEST_BASIC],
|
|
3769
|
+
permissions: PERMISSIONS_BASE,
|
|
3770
|
+
rules: RULES_PROTOTYPE
|
|
3771
|
+
};
|
|
3772
|
+
PRESET_PRODUCTION = {
|
|
3773
|
+
version: "1.0",
|
|
3774
|
+
scenario: "production",
|
|
3775
|
+
preset: "production",
|
|
3776
|
+
gates: {
|
|
3777
|
+
preCommit: {
|
|
3778
|
+
enabled: true,
|
|
3779
|
+
scripts: ["format", "lint"]
|
|
3780
|
+
},
|
|
3781
|
+
prePush: {
|
|
3782
|
+
enabled: true,
|
|
3783
|
+
scripts: ["typecheck", "test:unit"]
|
|
3784
|
+
},
|
|
3785
|
+
ci: {
|
|
3786
|
+
enabled: true,
|
|
3787
|
+
scripts: ["lint", "typecheck", "test", "build"],
|
|
3788
|
+
triggers: {
|
|
3789
|
+
pullRequest: true,
|
|
3790
|
+
push: { branches: ["main", "develop"] }
|
|
3791
|
+
},
|
|
3792
|
+
nodeVersion: "20"
|
|
3793
|
+
}
|
|
3794
|
+
},
|
|
3795
|
+
scripts: [...SCRIPTS_CORE, ...SCRIPTS_TEST_FULL, ...SCRIPTS_VERIFY],
|
|
3796
|
+
permissions: PERMISSIONS_PRODUCTION,
|
|
3797
|
+
rules: RULES_PRODUCTION
|
|
3798
|
+
};
|
|
3799
|
+
PRESET_TEAM = {
|
|
3800
|
+
version: "1.0",
|
|
3801
|
+
scenario: "team",
|
|
3802
|
+
preset: "team",
|
|
3803
|
+
gates: {
|
|
3804
|
+
preCommit: {
|
|
3805
|
+
enabled: true,
|
|
3806
|
+
scripts: ["format", "lint"]
|
|
3807
|
+
},
|
|
3808
|
+
prePush: {
|
|
3809
|
+
enabled: true,
|
|
3810
|
+
scripts: ["typecheck", "test:unit"]
|
|
3811
|
+
},
|
|
3812
|
+
ci: {
|
|
3813
|
+
enabled: true,
|
|
3814
|
+
scripts: ["lint", "typecheck", "test", "build"],
|
|
3815
|
+
triggers: {
|
|
3816
|
+
pullRequest: true,
|
|
3817
|
+
push: { branches: ["main", "develop"] }
|
|
3818
|
+
},
|
|
3819
|
+
nodeVersion: "20"
|
|
3820
|
+
}
|
|
3821
|
+
},
|
|
3822
|
+
scripts: [...SCRIPTS_CORE, ...SCRIPTS_TEST_FULL, ...SCRIPTS_VERIFY],
|
|
3823
|
+
permissions: PERMISSIONS_PRODUCTION,
|
|
3824
|
+
rules: RULES_TEAM
|
|
3825
|
+
};
|
|
3826
|
+
HARNESS_PRESETS = {
|
|
3827
|
+
prototype: PRESET_PROTOTYPE,
|
|
3828
|
+
production: PRESET_PRODUCTION,
|
|
3829
|
+
team: PRESET_TEAM
|
|
3830
|
+
};
|
|
3831
|
+
}
|
|
3832
|
+
});
|
|
3833
|
+
|
|
3834
|
+
// src/roboco/generators/harness-json.ts
|
|
3835
|
+
import { writeFile } from "fs/promises";
|
|
3836
|
+
import { existsSync } from "fs";
|
|
3837
|
+
import { join as join6 } from "path";
|
|
3838
|
+
async function generateHarnessJson(opts) {
|
|
3839
|
+
const target = join6(opts.cwd, FILENAME);
|
|
3840
|
+
if (existsSync(target) && !opts.force) {
|
|
3841
|
+
return [{ file: FILENAME, action: "skipped", reason: "already exists" }];
|
|
3842
|
+
}
|
|
3843
|
+
const content = JSON.stringify(opts.config, null, 2) + "\n";
|
|
3844
|
+
await writeFile(target, content, "utf8");
|
|
3845
|
+
return [{ file: FILENAME, action: existsSync(target) ? "updated" : "created" }];
|
|
3846
|
+
}
|
|
3847
|
+
var FILENAME;
|
|
3848
|
+
var init_harness_json = __esm({
|
|
3849
|
+
"src/roboco/generators/harness-json.ts"() {
|
|
3850
|
+
"use strict";
|
|
3851
|
+
FILENAME = ".harness.json";
|
|
3852
|
+
}
|
|
3853
|
+
});
|
|
3854
|
+
|
|
3855
|
+
// src/roboco/generators/scripts.ts
|
|
3856
|
+
import { readFile, writeFile as writeFile2 } from "fs/promises";
|
|
3857
|
+
import { existsSync as existsSync2 } from "fs";
|
|
3858
|
+
import { join as join7 } from "path";
|
|
3859
|
+
async function generateScripts(opts) {
|
|
3860
|
+
const pkgPath = join7(opts.cwd, "package.json");
|
|
3861
|
+
if (!existsSync2(pkgPath)) {
|
|
3862
|
+
return [{ file: "package.json", action: "skipped", reason: "not found" }];
|
|
3863
|
+
}
|
|
3864
|
+
const raw = await readFile(pkgPath, "utf8");
|
|
3865
|
+
const pkg5 = JSON.parse(raw);
|
|
3866
|
+
const existing = pkg5.scripts ?? {};
|
|
3867
|
+
const added = [];
|
|
3868
|
+
for (const req of opts.config.scripts) {
|
|
3869
|
+
if (existing[req.name]) continue;
|
|
3870
|
+
if (!req.required && !opts.force) continue;
|
|
3871
|
+
existing[req.name] = req.command;
|
|
3872
|
+
added.push(req.name);
|
|
3873
|
+
}
|
|
3874
|
+
if (added.length === 0) {
|
|
3875
|
+
return [{ file: "package.json", action: "skipped", reason: "all scripts present" }];
|
|
3876
|
+
}
|
|
3877
|
+
pkg5.scripts = existing;
|
|
3878
|
+
const content = JSON.stringify(pkg5, null, 2) + "\n";
|
|
3879
|
+
await writeFile2(pkgPath, content, "utf8");
|
|
3880
|
+
return [{ file: "package.json", action: "updated", reason: `added: ${added.join(", ")}` }];
|
|
3881
|
+
}
|
|
3882
|
+
var init_scripts = __esm({
|
|
3883
|
+
"src/roboco/generators/scripts.ts"() {
|
|
3884
|
+
"use strict";
|
|
3885
|
+
}
|
|
3886
|
+
});
|
|
3887
|
+
|
|
3888
|
+
// src/roboco/generators/lint-staged.ts
|
|
3889
|
+
import { writeFile as writeFile3 } from "fs/promises";
|
|
3890
|
+
import { existsSync as existsSync3 } from "fs";
|
|
3891
|
+
import { join as join8 } from "path";
|
|
3892
|
+
async function generateLintStaged(opts) {
|
|
3893
|
+
const target = join8(opts.cwd, FILENAME2);
|
|
3894
|
+
if (existsSync3(target) && !opts.force) {
|
|
3895
|
+
return [{ file: FILENAME2, action: "skipped", reason: "already exists" }];
|
|
3896
|
+
}
|
|
3897
|
+
const scripts = opts.config.gates.preCommit.scripts;
|
|
3898
|
+
const hasFormat = scripts.includes("format");
|
|
3899
|
+
const hasLint = scripts.includes("lint");
|
|
3900
|
+
const config = {};
|
|
3901
|
+
if (hasFormat) {
|
|
3902
|
+
config["*.{js,ts,tsx,json,md}"] = ["prettier --write"];
|
|
3903
|
+
}
|
|
3904
|
+
if (hasLint) {
|
|
3905
|
+
config["*.{js,ts,tsx}"] = [
|
|
3906
|
+
...config["*.{js,ts,tsx}"] ?? [],
|
|
3907
|
+
"eslint --fix"
|
|
3908
|
+
];
|
|
3909
|
+
}
|
|
3910
|
+
if (Object.keys(config).length === 0) {
|
|
3911
|
+
return [{ file: FILENAME2, action: "skipped", reason: "no preCommit scripts mapped" }];
|
|
3912
|
+
}
|
|
3913
|
+
const content = JSON.stringify(config, null, 2) + "\n";
|
|
3914
|
+
await writeFile3(target, content, "utf8");
|
|
3915
|
+
return [{ file: FILENAME2, action: "created" }];
|
|
3916
|
+
}
|
|
3917
|
+
var FILENAME2;
|
|
3918
|
+
var init_lint_staged = __esm({
|
|
3919
|
+
"src/roboco/generators/lint-staged.ts"() {
|
|
3920
|
+
"use strict";
|
|
3921
|
+
FILENAME2 = ".lintstagedrc.json";
|
|
3922
|
+
}
|
|
3923
|
+
});
|
|
3924
|
+
|
|
3925
|
+
// src/roboco/generators/husky.ts
|
|
3926
|
+
import { writeFile as writeFile4, mkdir } from "fs/promises";
|
|
3927
|
+
import { existsSync as existsSync4 } from "fs";
|
|
3928
|
+
import { join as join9 } from "path";
|
|
3929
|
+
function buildHookScript(scripts, useLintStaged) {
|
|
3930
|
+
const lines = ["#!/usr/bin/env sh", '. "$(dirname -- "$0")/_/husky.sh"', ""];
|
|
3931
|
+
if (useLintStaged) {
|
|
3932
|
+
lines.push("npx lint-staged");
|
|
3933
|
+
} else {
|
|
3934
|
+
for (const s of scripts) {
|
|
3935
|
+
lines.push(`npm run ${s}`);
|
|
3936
|
+
}
|
|
3937
|
+
}
|
|
3938
|
+
return lines.join("\n") + "\n";
|
|
3939
|
+
}
|
|
3940
|
+
async function generateHusky(opts) {
|
|
3941
|
+
const results = [];
|
|
3942
|
+
const huskyDir = join9(opts.cwd, HUSKY_DIR);
|
|
3943
|
+
await mkdir(huskyDir, { recursive: true });
|
|
3944
|
+
const preCommit = opts.config.gates.preCommit;
|
|
3945
|
+
if (preCommit.enabled) {
|
|
3946
|
+
const file = join9(huskyDir, "pre-commit");
|
|
3947
|
+
const relPath = `${HUSKY_DIR}/pre-commit`;
|
|
3948
|
+
if (existsSync4(file) && !opts.force) {
|
|
3949
|
+
results.push({ file: relPath, action: "skipped", reason: "already exists" });
|
|
3950
|
+
} else {
|
|
3951
|
+
const content = buildHookScript(preCommit.scripts, true);
|
|
3952
|
+
await writeFile4(file, content, { mode: 493 });
|
|
3953
|
+
results.push({ file: relPath, action: "created" });
|
|
3954
|
+
}
|
|
3955
|
+
}
|
|
3956
|
+
const prePush = opts.config.gates.prePush;
|
|
3957
|
+
if (prePush.enabled) {
|
|
3958
|
+
const file = join9(huskyDir, "pre-push");
|
|
3959
|
+
const relPath = `${HUSKY_DIR}/pre-push`;
|
|
3960
|
+
if (existsSync4(file) && !opts.force) {
|
|
3961
|
+
results.push({ file: relPath, action: "skipped", reason: "already exists" });
|
|
3962
|
+
} else {
|
|
3963
|
+
const content = buildHookScript(prePush.scripts, false);
|
|
3964
|
+
await writeFile4(file, content, { mode: 493 });
|
|
3965
|
+
results.push({ file: relPath, action: "created" });
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
return results;
|
|
3969
|
+
}
|
|
3970
|
+
var HUSKY_DIR;
|
|
3971
|
+
var init_husky = __esm({
|
|
3972
|
+
"src/roboco/generators/husky.ts"() {
|
|
3973
|
+
"use strict";
|
|
3974
|
+
HUSKY_DIR = ".husky";
|
|
3975
|
+
}
|
|
3976
|
+
});
|
|
3977
|
+
|
|
3978
|
+
// src/roboco/generators/ci-workflow.ts
|
|
3979
|
+
import { writeFile as writeFile5, mkdir as mkdir2 } from "fs/promises";
|
|
3980
|
+
import { existsSync as existsSync5 } from "fs";
|
|
3981
|
+
import { join as join10 } from "path";
|
|
3982
|
+
function buildWorkflow(opts) {
|
|
3983
|
+
const ci = opts.config.gates.ci;
|
|
3984
|
+
const triggers = [];
|
|
3985
|
+
if (ci.triggers.pullRequest) {
|
|
3986
|
+
triggers.push(" pull_request:");
|
|
3987
|
+
}
|
|
3988
|
+
if (ci.triggers.push.branches.length > 0) {
|
|
3989
|
+
triggers.push(" push:");
|
|
3990
|
+
triggers.push(` branches: [${ci.triggers.push.branches.join(", ")}]`);
|
|
3991
|
+
}
|
|
3992
|
+
const steps = [
|
|
3993
|
+
" - uses: actions/checkout@v4",
|
|
3994
|
+
" - uses: actions/setup-node@v4",
|
|
3995
|
+
" with:",
|
|
3996
|
+
` node-version: ${ci.nodeVersion}`,
|
|
3997
|
+
" - run: npm ci"
|
|
3998
|
+
];
|
|
3999
|
+
for (const script of ci.scripts) {
|
|
4000
|
+
steps.push(` - run: npm run ${script}`);
|
|
4001
|
+
}
|
|
4002
|
+
return [
|
|
4003
|
+
"name: CI",
|
|
4004
|
+
"",
|
|
4005
|
+
"on:",
|
|
4006
|
+
...triggers,
|
|
4007
|
+
"",
|
|
4008
|
+
"jobs:",
|
|
4009
|
+
" verify:",
|
|
4010
|
+
" runs-on: ubuntu-latest",
|
|
4011
|
+
" steps:",
|
|
4012
|
+
...steps,
|
|
4013
|
+
""
|
|
4014
|
+
].join("\n");
|
|
4015
|
+
}
|
|
4016
|
+
async function generateCIWorkflow(opts) {
|
|
4017
|
+
if (!opts.config.gates.ci.enabled) {
|
|
4018
|
+
return [{ file: REL_PATH, action: "skipped", reason: "CI gate disabled" }];
|
|
4019
|
+
}
|
|
4020
|
+
const target = join10(opts.cwd, REL_PATH);
|
|
4021
|
+
if (existsSync5(target) && !opts.force) {
|
|
4022
|
+
return [{ file: REL_PATH, action: "skipped", reason: "already exists" }];
|
|
4023
|
+
}
|
|
4024
|
+
await mkdir2(join10(opts.cwd, ".github", "workflows"), { recursive: true });
|
|
4025
|
+
const content = buildWorkflow(opts);
|
|
4026
|
+
await writeFile5(target, content, "utf8");
|
|
4027
|
+
return [{ file: REL_PATH, action: "created" }];
|
|
4028
|
+
}
|
|
4029
|
+
var REL_PATH;
|
|
4030
|
+
var init_ci_workflow = __esm({
|
|
4031
|
+
"src/roboco/generators/ci-workflow.ts"() {
|
|
4032
|
+
"use strict";
|
|
4033
|
+
REL_PATH = ".github/workflows/ci.yml";
|
|
4034
|
+
}
|
|
4035
|
+
});
|
|
4036
|
+
|
|
4037
|
+
// src/roboco/generators/claude-settings.ts
|
|
4038
|
+
import { readFile as readFile2, writeFile as writeFile6, mkdir as mkdir3 } from "fs/promises";
|
|
4039
|
+
import { existsSync as existsSync6 } from "fs";
|
|
4040
|
+
import { join as join11 } from "path";
|
|
4041
|
+
function dedup(arr) {
|
|
4042
|
+
return [...new Set(arr)];
|
|
4043
|
+
}
|
|
4044
|
+
async function generateClaudeSettings2(opts) {
|
|
4045
|
+
const target = join11(opts.cwd, REL_PATH2);
|
|
4046
|
+
await mkdir3(join11(opts.cwd, ".claude"), { recursive: true });
|
|
4047
|
+
let settings = {};
|
|
4048
|
+
if (existsSync6(target)) {
|
|
4049
|
+
try {
|
|
4050
|
+
const raw = await readFile2(target, "utf8");
|
|
4051
|
+
settings = JSON.parse(raw.replace(/^\uFEFF/, ""));
|
|
4052
|
+
} catch {
|
|
4053
|
+
}
|
|
4054
|
+
}
|
|
4055
|
+
const existing = settings.permissions ?? {};
|
|
4056
|
+
const merged = {
|
|
4057
|
+
allow: dedup([...existing.allow ?? [], ...opts.config.permissions.allow]),
|
|
4058
|
+
deny: dedup([...existing.deny ?? [], ...opts.config.permissions.deny])
|
|
4059
|
+
};
|
|
4060
|
+
settings.permissions = merged;
|
|
4061
|
+
const content = JSON.stringify(settings, null, 2) + "\n";
|
|
4062
|
+
await writeFile6(target, content, "utf8");
|
|
4063
|
+
return [{ file: REL_PATH2, action: existsSync6(target) ? "updated" : "created" }];
|
|
4064
|
+
}
|
|
4065
|
+
var REL_PATH2;
|
|
4066
|
+
var init_claude_settings = __esm({
|
|
4067
|
+
"src/roboco/generators/claude-settings.ts"() {
|
|
4068
|
+
"use strict";
|
|
4069
|
+
REL_PATH2 = ".claude/settings.json";
|
|
4070
|
+
}
|
|
4071
|
+
});
|
|
4072
|
+
|
|
4073
|
+
// src/roboco/generators/claude-md.ts
|
|
4074
|
+
import { readFile as readFile3, writeFile as writeFile7 } from "fs/promises";
|
|
4075
|
+
import { existsSync as existsSync7 } from "fs";
|
|
4076
|
+
import { join as join12 } from "path";
|
|
4077
|
+
function buildRulesBlock(opts) {
|
|
4078
|
+
const { rules, scenario } = opts.config;
|
|
4079
|
+
const lines = [
|
|
4080
|
+
MARKER_START,
|
|
4081
|
+
`## Harness Rules (${scenario})`,
|
|
4082
|
+
""
|
|
4083
|
+
];
|
|
4084
|
+
if (rules.specBeforeCode) {
|
|
4085
|
+
lines.push("- spec \uC5C6\uB294 \uAE30\uB2A5 \uAD6C\uD604 \uAE08\uC9C0 \u2014 \uBC18\uB4DC\uC2DC OpenSpec \uBA3C\uC800 \uC791\uC131");
|
|
4086
|
+
}
|
|
4087
|
+
if (rules.requireWorkflow) {
|
|
4088
|
+
lines.push("- \uD070 \uBCC0\uACBD\uC740 plan \u2192 implement \u2192 review \u2192 verify \uC21C\uC11C\uB85C \uC9C4\uD589");
|
|
4089
|
+
}
|
|
4090
|
+
if (rules.prMergeRequiresReview) {
|
|
4091
|
+
lines.push("- PR \uBA38\uC9C0 \uC804 \uB9AC\uBDF0 \uD544\uC218");
|
|
4092
|
+
}
|
|
4093
|
+
if (rules.branchProtection) {
|
|
4094
|
+
lines.push("- main/develop \uBE0C\uB79C\uCE58 \uC9C1\uC811 push \uAE08\uC9C0");
|
|
4095
|
+
}
|
|
4096
|
+
lines.push("- pre-commit/pre-push/CI\uB97C \uD1B5\uACFC\uD558\uC9C0 \uBABB\uD558\uBA74 \uC644\uB8CC\uB85C \uBCF4\uC9C0 \uC54A\uC74C");
|
|
4097
|
+
lines.push("- \uAE30\uC874 script \uC678 \uC784\uC758 \uBA85\uB839 \uCD5C\uC18C\uD654 \u2014 package.json scripts\uB97C \uC0AC\uC6A9");
|
|
4098
|
+
lines.push("");
|
|
4099
|
+
lines.push(MARKER_END);
|
|
4100
|
+
return lines.join("\n");
|
|
4101
|
+
}
|
|
4102
|
+
async function generateClaudeMd2(opts) {
|
|
4103
|
+
const target = join12(opts.cwd, FILENAME3);
|
|
4104
|
+
const block = buildRulesBlock(opts);
|
|
4105
|
+
if (!existsSync7(target)) {
|
|
4106
|
+
const content2 = `# Project Rules
|
|
4107
|
+
|
|
4108
|
+
${block}
|
|
4109
|
+
`;
|
|
4110
|
+
await writeFile7(target, content2, "utf8");
|
|
4111
|
+
return [{ file: FILENAME3, action: "created" }];
|
|
4112
|
+
}
|
|
4113
|
+
let content = await readFile3(target, "utf8");
|
|
4114
|
+
const startIdx = content.indexOf(MARKER_START);
|
|
4115
|
+
const endIdx = content.indexOf(MARKER_END);
|
|
4116
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
4117
|
+
content = content.slice(0, startIdx) + block + content.slice(endIdx + MARKER_END.length);
|
|
4118
|
+
} else {
|
|
4119
|
+
content = content.trimEnd() + "\n\n" + block + "\n";
|
|
4120
|
+
}
|
|
4121
|
+
await writeFile7(target, content, "utf8");
|
|
4122
|
+
return [{ file: FILENAME3, action: "updated" }];
|
|
4123
|
+
}
|
|
4124
|
+
var FILENAME3, MARKER_START, MARKER_END;
|
|
4125
|
+
var init_claude_md = __esm({
|
|
4126
|
+
"src/roboco/generators/claude-md.ts"() {
|
|
4127
|
+
"use strict";
|
|
4128
|
+
FILENAME3 = "CLAUDE.md";
|
|
4129
|
+
MARKER_START = "<!-- harness:start -->";
|
|
4130
|
+
MARKER_END = "<!-- harness:end -->";
|
|
4131
|
+
}
|
|
4132
|
+
});
|
|
4133
|
+
|
|
4134
|
+
// src/roboco/generators/index.ts
|
|
4135
|
+
var init_generators = __esm({
|
|
4136
|
+
"src/roboco/generators/index.ts"() {
|
|
4137
|
+
"use strict";
|
|
4138
|
+
init_harness_json();
|
|
4139
|
+
init_scripts();
|
|
4140
|
+
init_lint_staged();
|
|
4141
|
+
init_husky();
|
|
4142
|
+
init_ci_workflow();
|
|
4143
|
+
init_claude_settings();
|
|
4144
|
+
init_claude_md();
|
|
4145
|
+
}
|
|
4146
|
+
});
|
|
4147
|
+
|
|
4148
|
+
// src/roboco/scaffold.ts
|
|
4149
|
+
async function scaffold(opts) {
|
|
4150
|
+
const scenario = opts.scenario ?? "prototype";
|
|
4151
|
+
const config = resolveConfig({ scenario, overrides: opts.overrides });
|
|
4152
|
+
const generatorOpts = {
|
|
4153
|
+
cwd: opts.cwd,
|
|
4154
|
+
config,
|
|
4155
|
+
force: opts.force
|
|
4156
|
+
};
|
|
4157
|
+
const files = [];
|
|
4158
|
+
for (const gen of GENERATORS) {
|
|
4159
|
+
const results = await gen(generatorOpts);
|
|
4160
|
+
files.push(...results);
|
|
4161
|
+
}
|
|
4162
|
+
const summary = {
|
|
4163
|
+
created: files.filter((f) => f.action === "created").length,
|
|
4164
|
+
updated: files.filter((f) => f.action === "updated").length,
|
|
4165
|
+
skipped: files.filter((f) => f.action === "skipped").length
|
|
4166
|
+
};
|
|
4167
|
+
return { scenario, config, files, summary };
|
|
4168
|
+
}
|
|
4169
|
+
var GENERATORS;
|
|
4170
|
+
var init_scaffold = __esm({
|
|
4171
|
+
"src/roboco/scaffold.ts"() {
|
|
4172
|
+
"use strict";
|
|
4173
|
+
init_harness_defaults();
|
|
4174
|
+
init_generators();
|
|
4175
|
+
GENERATORS = [
|
|
4176
|
+
generateHarnessJson,
|
|
4177
|
+
generateScripts,
|
|
4178
|
+
generateLintStaged,
|
|
4179
|
+
generateHusky,
|
|
4180
|
+
generateCIWorkflow,
|
|
4181
|
+
generateClaudeSettings2,
|
|
4182
|
+
generateClaudeMd2
|
|
4183
|
+
];
|
|
4184
|
+
}
|
|
4185
|
+
});
|
|
4186
|
+
|
|
3608
4187
|
// src/core/roboco.ts
|
|
3609
4188
|
import os2 from "os";
|
|
3610
4189
|
import path5 from "path";
|
|
@@ -4144,7 +4723,7 @@ var doctor_exports = {};
|
|
|
4144
4723
|
__export(doctor_exports, {
|
|
4145
4724
|
runDoctor: () => runDoctor
|
|
4146
4725
|
});
|
|
4147
|
-
import { join as
|
|
4726
|
+
import { join as join13 } from "path";
|
|
4148
4727
|
import { homedir } from "os";
|
|
4149
4728
|
import fs12 from "fs-extra";
|
|
4150
4729
|
async function runDoctor() {
|
|
@@ -4165,7 +4744,7 @@ async function runDoctor() {
|
|
|
4165
4744
|
detail: claudeCheck.detail,
|
|
4166
4745
|
fix: claudeCheck.ok ? void 0 : "npm install -g @anthropic-ai/claude-code"
|
|
4167
4746
|
});
|
|
4168
|
-
const globalConfigPath =
|
|
4747
|
+
const globalConfigPath = join13(homedir(), ".pai", "config.json");
|
|
4169
4748
|
const hasGlobalConfig = await fs12.pathExists(globalConfigPath);
|
|
4170
4749
|
checks.push({
|
|
4171
4750
|
label: "\uAE00\uB85C\uBC8C \uC124\uC815",
|
|
@@ -4543,6 +5122,16 @@ var init_fetch_cmd = __esm({
|
|
|
4543
5122
|
});
|
|
4544
5123
|
|
|
4545
5124
|
// src/stages/environment/index.ts
|
|
5125
|
+
function modeToScenario(mode) {
|
|
5126
|
+
switch (mode) {
|
|
5127
|
+
case "prototype":
|
|
5128
|
+
return "prototype";
|
|
5129
|
+
case "poc":
|
|
5130
|
+
return "production";
|
|
5131
|
+
case "production":
|
|
5132
|
+
return "production";
|
|
5133
|
+
}
|
|
5134
|
+
}
|
|
4546
5135
|
var environmentStage;
|
|
4547
5136
|
var init_environment = __esm({
|
|
4548
5137
|
"src/stages/environment/index.ts"() {
|
|
@@ -4555,6 +5144,7 @@ var init_environment = __esm({
|
|
|
4555
5144
|
init_claude_commands();
|
|
4556
5145
|
init_detector();
|
|
4557
5146
|
init_config();
|
|
5147
|
+
init_scaffold();
|
|
4558
5148
|
init_progress();
|
|
4559
5149
|
init_ui();
|
|
4560
5150
|
init_analyzer();
|
|
@@ -4673,6 +5263,21 @@ var init_environment = __esm({
|
|
|
4673
5263
|
}
|
|
4674
5264
|
}
|
|
4675
5265
|
console.log("");
|
|
5266
|
+
const scaffoldResult = await withSpinner("Harness \uC815\uCC45 \uC124\uC815 \uC911...", async () => {
|
|
5267
|
+
const result = await scaffold({
|
|
5268
|
+
cwd: input.cwd,
|
|
5269
|
+
scenario: modeToScenario(interview.mode)
|
|
5270
|
+
});
|
|
5271
|
+
await sleep(300);
|
|
5272
|
+
return result;
|
|
5273
|
+
});
|
|
5274
|
+
if (scaffoldResult.summary.created > 0 || scaffoldResult.summary.updated > 0) {
|
|
5275
|
+
const parts = [];
|
|
5276
|
+
if (scaffoldResult.summary.created > 0) parts.push(`${scaffoldResult.summary.created}\uAC1C \uC0DD\uC131`);
|
|
5277
|
+
if (scaffoldResult.summary.updated > 0) parts.push(`${scaffoldResult.summary.updated}\uAC1C \uC218\uC815`);
|
|
5278
|
+
success(`Harness: ${parts.join(", ")}`);
|
|
5279
|
+
}
|
|
5280
|
+
console.log("");
|
|
4676
5281
|
await withSpinner("\uC124\uC815 \uC800\uC7A5 \uC911...", async () => {
|
|
4677
5282
|
const config = createDefaultConfig(interview.projectName, interview.mode);
|
|
4678
5283
|
config.plugins = pluginKeys;
|
|
@@ -4707,11 +5312,11 @@ var init_environment = __esm({
|
|
|
4707
5312
|
});
|
|
4708
5313
|
|
|
4709
5314
|
// src/stages/validation/runner.ts
|
|
4710
|
-
import { join as
|
|
5315
|
+
import { join as join14 } from "path";
|
|
4711
5316
|
import fs15 from "fs-extra";
|
|
4712
5317
|
async function runTests(cwd) {
|
|
4713
5318
|
const start = Date.now();
|
|
4714
|
-
const gstackPath =
|
|
5319
|
+
const gstackPath = join14(cwd, ".pai", "gstack.json");
|
|
4715
5320
|
let runner = "npm test";
|
|
4716
5321
|
if (await fs15.pathExists(gstackPath)) {
|
|
4717
5322
|
try {
|
|
@@ -4722,7 +5327,7 @@ async function runTests(cwd) {
|
|
|
4722
5327
|
} catch {
|
|
4723
5328
|
}
|
|
4724
5329
|
}
|
|
4725
|
-
const pkgPath =
|
|
5330
|
+
const pkgPath = join14(cwd, "package.json");
|
|
4726
5331
|
if (await fs15.pathExists(pkgPath)) {
|
|
4727
5332
|
try {
|
|
4728
5333
|
const pkg5 = await fs15.readJson(pkgPath);
|
|
@@ -4767,10 +5372,10 @@ var init_runner = __esm({
|
|
|
4767
5372
|
});
|
|
4768
5373
|
|
|
4769
5374
|
// src/stages/validation/harness.ts
|
|
4770
|
-
import { join as
|
|
5375
|
+
import { join as join15 } from "path";
|
|
4771
5376
|
import fs16 from "fs-extra";
|
|
4772
5377
|
async function runHarnessCheck(cwd) {
|
|
4773
|
-
const harnessPath =
|
|
5378
|
+
const harnessPath = join15(cwd, ".pai", "harness.json");
|
|
4774
5379
|
if (!await fs16.pathExists(harnessPath)) {
|
|
4775
5380
|
return { enabled: false, specFile: null, rules: [], checks: [] };
|
|
4776
5381
|
}
|
|
@@ -4784,8 +5389,8 @@ async function runHarnessCheck(cwd) {
|
|
|
4784
5389
|
const rules = config.rules ?? [];
|
|
4785
5390
|
const checks = [];
|
|
4786
5391
|
if (rules.includes("spec-implementation-match")) {
|
|
4787
|
-
const specExists = await fs16.pathExists(
|
|
4788
|
-
const srcExists = await fs16.pathExists(
|
|
5392
|
+
const specExists = await fs16.pathExists(join15(cwd, specFile));
|
|
5393
|
+
const srcExists = await fs16.pathExists(join15(cwd, "src"));
|
|
4789
5394
|
checks.push({
|
|
4790
5395
|
rule: "spec-implementation-match",
|
|
4791
5396
|
passed: specExists && srcExists,
|
|
@@ -4793,8 +5398,8 @@ async function runHarnessCheck(cwd) {
|
|
|
4793
5398
|
});
|
|
4794
5399
|
}
|
|
4795
5400
|
if (rules.includes("api-contract-test")) {
|
|
4796
|
-
const testDir = await fs16.pathExists(
|
|
4797
|
-
const testDir2 = await fs16.pathExists(
|
|
5401
|
+
const testDir = await fs16.pathExists(join15(cwd, "tests"));
|
|
5402
|
+
const testDir2 = await fs16.pathExists(join15(cwd, "test"));
|
|
4798
5403
|
checks.push({
|
|
4799
5404
|
rule: "api-contract-test",
|
|
4800
5405
|
passed: testDir || testDir2,
|
|
@@ -4951,12 +5556,189 @@ var init_analyze = __esm({
|
|
|
4951
5556
|
}
|
|
4952
5557
|
});
|
|
4953
5558
|
|
|
5559
|
+
// src/vibe-ready/evaluator.ts
|
|
5560
|
+
var evaluator_exports = {};
|
|
5561
|
+
__export(evaluator_exports, {
|
|
5562
|
+
evaluateHarness: () => evaluateHarness
|
|
5563
|
+
});
|
|
5564
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
5565
|
+
import { existsSync as existsSync8 } from "fs";
|
|
5566
|
+
import { join as join16 } from "path";
|
|
5567
|
+
async function evaluateHarness(cwd) {
|
|
5568
|
+
const configPath = join16(cwd, ".harness.json");
|
|
5569
|
+
if (!existsSync8(configPath)) {
|
|
5570
|
+
return {
|
|
5571
|
+
configFound: false,
|
|
5572
|
+
scenario: null,
|
|
5573
|
+
gates: [],
|
|
5574
|
+
missingScripts: [],
|
|
5575
|
+
missingFiles: [],
|
|
5576
|
+
score: 0
|
|
5577
|
+
};
|
|
5578
|
+
}
|
|
5579
|
+
let config;
|
|
5580
|
+
try {
|
|
5581
|
+
const raw = await readFile4(configPath, "utf8");
|
|
5582
|
+
config = JSON.parse(raw);
|
|
5583
|
+
} catch {
|
|
5584
|
+
return {
|
|
5585
|
+
configFound: false,
|
|
5586
|
+
scenario: null,
|
|
5587
|
+
gates: [],
|
|
5588
|
+
missingScripts: [],
|
|
5589
|
+
missingFiles: [],
|
|
5590
|
+
score: 0
|
|
5591
|
+
};
|
|
5592
|
+
}
|
|
5593
|
+
const gates = [];
|
|
5594
|
+
const missingScripts = [];
|
|
5595
|
+
const missingFiles = [];
|
|
5596
|
+
let score = 10;
|
|
5597
|
+
gates.push(await evaluateGate(cwd, config, "preCommit", [".husky/pre-commit", ".lintstagedrc.json"]));
|
|
5598
|
+
if (gates[gates.length - 1].status === "pass") score += 15;
|
|
5599
|
+
else missingFiles.push(...gates[gates.length - 1].findings.filter((f) => f.startsWith("missing:")).map((f) => f.slice(8)));
|
|
5600
|
+
gates.push(await evaluateGate(cwd, config, "prePush", [".husky/pre-push"]));
|
|
5601
|
+
if (gates[gates.length - 1].status === "pass") score += 15;
|
|
5602
|
+
else if (gates[gates.length - 1].status === "skip") score += 15;
|
|
5603
|
+
gates.push(await evaluateGate(cwd, config, "ci", [".github/workflows/ci.yml"]));
|
|
5604
|
+
if (gates[gates.length - 1].status === "pass") score += 20;
|
|
5605
|
+
const scriptScore = await evaluateScripts(cwd, config, missingScripts);
|
|
5606
|
+
score += scriptScore;
|
|
5607
|
+
const permScore = await evaluatePermissions(cwd, config);
|
|
5608
|
+
score += permScore;
|
|
5609
|
+
const rulesScore = await evaluateRules(cwd, config);
|
|
5610
|
+
score += rulesScore;
|
|
5611
|
+
return {
|
|
5612
|
+
configFound: true,
|
|
5613
|
+
scenario: config.scenario,
|
|
5614
|
+
gates,
|
|
5615
|
+
missingScripts,
|
|
5616
|
+
missingFiles,
|
|
5617
|
+
score: Math.min(100, score)
|
|
5618
|
+
};
|
|
5619
|
+
}
|
|
5620
|
+
async function evaluateGate(cwd, config, gateName, expectedFiles) {
|
|
5621
|
+
const gate = config.gates[gateName];
|
|
5622
|
+
if (!gate.enabled) {
|
|
5623
|
+
return { gate: gateName, status: "skip", findings: ["gate disabled"] };
|
|
5624
|
+
}
|
|
5625
|
+
const findings = [];
|
|
5626
|
+
let allPresent = true;
|
|
5627
|
+
for (const file of expectedFiles) {
|
|
5628
|
+
if (!existsSync8(join16(cwd, file))) {
|
|
5629
|
+
findings.push(`missing:${file}`);
|
|
5630
|
+
allPresent = false;
|
|
5631
|
+
}
|
|
5632
|
+
}
|
|
5633
|
+
const pkg5 = await readPackageJson(cwd);
|
|
5634
|
+
if (pkg5) {
|
|
5635
|
+
for (const script of gate.scripts) {
|
|
5636
|
+
if (!pkg5.scripts?.[script]) {
|
|
5637
|
+
findings.push(`missing script: ${script}`);
|
|
5638
|
+
allPresent = false;
|
|
5639
|
+
}
|
|
5640
|
+
}
|
|
5641
|
+
}
|
|
5642
|
+
return {
|
|
5643
|
+
gate: gateName,
|
|
5644
|
+
status: allPresent ? "pass" : "fail",
|
|
5645
|
+
findings
|
|
5646
|
+
};
|
|
5647
|
+
}
|
|
5648
|
+
async function evaluateScripts(cwd, config, missingScripts) {
|
|
5649
|
+
const pkg5 = await readPackageJson(cwd);
|
|
5650
|
+
if (!pkg5) return 0;
|
|
5651
|
+
const required = config.scripts.filter((s) => s.required);
|
|
5652
|
+
if (required.length === 0) return 20;
|
|
5653
|
+
let found = 0;
|
|
5654
|
+
for (const req of required) {
|
|
5655
|
+
if (pkg5.scripts?.[req.name]) {
|
|
5656
|
+
found++;
|
|
5657
|
+
} else {
|
|
5658
|
+
missingScripts.push(req.name);
|
|
5659
|
+
}
|
|
5660
|
+
}
|
|
5661
|
+
return Math.round(found / required.length * 20);
|
|
5662
|
+
}
|
|
5663
|
+
async function evaluatePermissions(cwd, config) {
|
|
5664
|
+
const settingsPath = join16(cwd, ".claude", "settings.json");
|
|
5665
|
+
if (!existsSync8(settingsPath)) return 0;
|
|
5666
|
+
try {
|
|
5667
|
+
const raw = await readFile4(settingsPath, "utf8");
|
|
5668
|
+
const settings = JSON.parse(raw.replace(/^\uFEFF/, ""));
|
|
5669
|
+
const perms = settings.permissions;
|
|
5670
|
+
if (!perms) return 0;
|
|
5671
|
+
let matchCount = 0;
|
|
5672
|
+
let totalChecks = 0;
|
|
5673
|
+
for (const deny of config.permissions.deny) {
|
|
5674
|
+
totalChecks++;
|
|
5675
|
+
if (perms.deny?.includes(deny)) matchCount++;
|
|
5676
|
+
}
|
|
5677
|
+
for (const allow of config.permissions.allow) {
|
|
5678
|
+
totalChecks++;
|
|
5679
|
+
if (perms.allow?.includes(allow)) matchCount++;
|
|
5680
|
+
}
|
|
5681
|
+
if (totalChecks === 0) return 10;
|
|
5682
|
+
return Math.round(matchCount / totalChecks * 10);
|
|
5683
|
+
} catch {
|
|
5684
|
+
return 0;
|
|
5685
|
+
}
|
|
5686
|
+
}
|
|
5687
|
+
async function evaluateRules(cwd, config) {
|
|
5688
|
+
const claudeMdPath = join16(cwd, "CLAUDE.md");
|
|
5689
|
+
if (!existsSync8(claudeMdPath)) return 0;
|
|
5690
|
+
try {
|
|
5691
|
+
const content = await readFile4(claudeMdPath, "utf8");
|
|
5692
|
+
let matchCount = 0;
|
|
5693
|
+
let totalRules = 0;
|
|
5694
|
+
if (config.rules.specBeforeCode) {
|
|
5695
|
+
totalRules++;
|
|
5696
|
+
if (content.includes("spec") && content.includes("\uAE08\uC9C0")) matchCount++;
|
|
5697
|
+
}
|
|
5698
|
+
if (config.rules.requireWorkflow) {
|
|
5699
|
+
totalRules++;
|
|
5700
|
+
if (content.includes("plan") && content.includes("review")) matchCount++;
|
|
5701
|
+
}
|
|
5702
|
+
if (config.rules.prMergeRequiresReview) {
|
|
5703
|
+
totalRules++;
|
|
5704
|
+
if (content.includes("\uB9AC\uBDF0") || content.includes("review")) matchCount++;
|
|
5705
|
+
}
|
|
5706
|
+
if (config.rules.branchProtection) {
|
|
5707
|
+
totalRules++;
|
|
5708
|
+
if (content.includes("main") || content.includes("\uBE0C\uB79C\uCE58")) matchCount++;
|
|
5709
|
+
}
|
|
5710
|
+
if (content.includes("<!-- harness:start -->")) {
|
|
5711
|
+
matchCount++;
|
|
5712
|
+
totalRules++;
|
|
5713
|
+
}
|
|
5714
|
+
if (totalRules === 0) return 10;
|
|
5715
|
+
return Math.round(matchCount / totalRules * 10);
|
|
5716
|
+
} catch {
|
|
5717
|
+
return 0;
|
|
5718
|
+
}
|
|
5719
|
+
}
|
|
5720
|
+
async function readPackageJson(cwd) {
|
|
5721
|
+
const pkgPath = join16(cwd, "package.json");
|
|
5722
|
+
if (!existsSync8(pkgPath)) return null;
|
|
5723
|
+
try {
|
|
5724
|
+
const raw = await readFile4(pkgPath, "utf8");
|
|
5725
|
+
return JSON.parse(raw);
|
|
5726
|
+
} catch {
|
|
5727
|
+
return null;
|
|
5728
|
+
}
|
|
5729
|
+
}
|
|
5730
|
+
var init_evaluator = __esm({
|
|
5731
|
+
"src/vibe-ready/evaluator.ts"() {
|
|
5732
|
+
"use strict";
|
|
5733
|
+
}
|
|
5734
|
+
});
|
|
5735
|
+
|
|
4954
5736
|
// src/stages/evaluation/analyzer.ts
|
|
4955
5737
|
var analyzer_exports2 = {};
|
|
4956
5738
|
__export(analyzer_exports2, {
|
|
4957
5739
|
analyzeRepository: () => analyzeRepository
|
|
4958
5740
|
});
|
|
4959
|
-
import { join as
|
|
5741
|
+
import { join as join17 } from "path";
|
|
4960
5742
|
import fs17 from "fs-extra";
|
|
4961
5743
|
async function analyzeRepository(repoPath) {
|
|
4962
5744
|
try {
|
|
@@ -5018,14 +5800,14 @@ async function checkTestCoverage(repoPath) {
|
|
|
5018
5800
|
".nycrc"
|
|
5019
5801
|
];
|
|
5020
5802
|
for (const f of testConfigs) {
|
|
5021
|
-
const found = await fs17.pathExists(
|
|
5803
|
+
const found = await fs17.pathExists(join17(repoPath, f));
|
|
5022
5804
|
findings.push({ item: f, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
5023
5805
|
if (found) score += 20;
|
|
5024
5806
|
}
|
|
5025
5807
|
const testDirs = ["tests", "test", "__tests__", "spec"];
|
|
5026
5808
|
let hasTestDir = false;
|
|
5027
5809
|
for (const d of testDirs) {
|
|
5028
|
-
if (await fs17.pathExists(
|
|
5810
|
+
if (await fs17.pathExists(join17(repoPath, d))) {
|
|
5029
5811
|
findings.push({ item: d, found: true, details: "\uD14C\uC2A4\uD2B8 \uB514\uB809\uD1A0\uB9AC \uC874\uC7AC" });
|
|
5030
5812
|
hasTestDir = true;
|
|
5031
5813
|
score += 30;
|
|
@@ -5049,7 +5831,7 @@ async function checkCiCd(repoPath) {
|
|
|
5049
5831
|
{ path: ".circleci", label: "CircleCI" }
|
|
5050
5832
|
];
|
|
5051
5833
|
for (const { path: path10, label } of ciConfigs) {
|
|
5052
|
-
const found = await fs17.pathExists(
|
|
5834
|
+
const found = await fs17.pathExists(join17(repoPath, path10));
|
|
5053
5835
|
findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
5054
5836
|
if (found) score += 40;
|
|
5055
5837
|
}
|
|
@@ -5068,7 +5850,7 @@ async function checkHooks(repoPath) {
|
|
|
5068
5850
|
{ path: ".claude/settings.json", label: "Claude Code settings" }
|
|
5069
5851
|
];
|
|
5070
5852
|
for (const { path: path10, label } of hookConfigs) {
|
|
5071
|
-
const found = await fs17.pathExists(
|
|
5853
|
+
const found = await fs17.pathExists(join17(repoPath, path10));
|
|
5072
5854
|
findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
5073
5855
|
if (found) score += 20;
|
|
5074
5856
|
}
|
|
@@ -5086,7 +5868,7 @@ async function checkRepoStructure(repoPath) {
|
|
|
5086
5868
|
{ path: ".gitignore", label: ".gitignore" }
|
|
5087
5869
|
];
|
|
5088
5870
|
for (const { path: path10, label } of structureChecks) {
|
|
5089
|
-
const found = await fs17.pathExists(
|
|
5871
|
+
const found = await fs17.pathExists(join17(repoPath, path10));
|
|
5090
5872
|
findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
5091
5873
|
if (found) score += 25;
|
|
5092
5874
|
}
|
|
@@ -5103,7 +5885,7 @@ async function checkDocumentation(repoPath) {
|
|
|
5103
5885
|
{ path: "docs/openspec.md", label: "OpenSpec PRD", points: 25 }
|
|
5104
5886
|
];
|
|
5105
5887
|
for (const { path: path10, label, points } of docChecks) {
|
|
5106
|
-
const found = await fs17.pathExists(
|
|
5888
|
+
const found = await fs17.pathExists(join17(repoPath, path10));
|
|
5107
5889
|
findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
5108
5890
|
if (found) score += points;
|
|
5109
5891
|
}
|
|
@@ -5111,6 +5893,54 @@ async function checkDocumentation(repoPath) {
|
|
|
5111
5893
|
return { name: "\uBB38\uC11C\uD654 \uC218\uC900", tier: "nice", score, recommendations: [], rawFindings: findings };
|
|
5112
5894
|
}
|
|
5113
5895
|
async function checkHarnessEngineering(repoPath) {
|
|
5896
|
+
if (await fs17.pathExists(join17(repoPath, ".harness.json"))) {
|
|
5897
|
+
return checkHarnessWithPolicy(repoPath);
|
|
5898
|
+
}
|
|
5899
|
+
return checkHarnessLegacy(repoPath);
|
|
5900
|
+
}
|
|
5901
|
+
async function checkHarnessWithPolicy(repoPath) {
|
|
5902
|
+
const { evaluateHarness: evaluateHarness2 } = await Promise.resolve().then(() => (init_evaluator(), evaluator_exports));
|
|
5903
|
+
const evaluation = await evaluateHarness2(repoPath);
|
|
5904
|
+
const findings = [];
|
|
5905
|
+
findings.push({
|
|
5906
|
+
item: ".harness.json",
|
|
5907
|
+
found: evaluation.configFound,
|
|
5908
|
+
details: evaluation.configFound ? `\uC2DC\uB098\uB9AC\uC624: ${evaluation.scenario}` : "\uC5C6\uC74C"
|
|
5909
|
+
});
|
|
5910
|
+
for (const gate of evaluation.gates) {
|
|
5911
|
+
findings.push({
|
|
5912
|
+
item: `gate:${gate.gate}`,
|
|
5913
|
+
found: gate.status === "pass" || gate.status === "skip",
|
|
5914
|
+
details: gate.status === "pass" ? "\uD1B5\uACFC" : gate.status === "skip" ? "\uBE44\uD65C\uC131 (OK)" : gate.findings.join(", ")
|
|
5915
|
+
});
|
|
5916
|
+
}
|
|
5917
|
+
for (const script of evaluation.missingScripts) {
|
|
5918
|
+
findings.push({ item: `script:${script}`, found: false, details: "\uB204\uB77D" });
|
|
5919
|
+
}
|
|
5920
|
+
const recommendations = [];
|
|
5921
|
+
if (evaluation.missingScripts.length > 0) {
|
|
5922
|
+
recommendations.push({
|
|
5923
|
+
severity: "warning",
|
|
5924
|
+
message: `\uD544\uC218 script ${evaluation.missingScripts.length}\uAC1C \uB204\uB77D`,
|
|
5925
|
+
action: `package.json\uC5D0 \uCD94\uAC00: ${evaluation.missingScripts.join(", ")}`
|
|
5926
|
+
});
|
|
5927
|
+
}
|
|
5928
|
+
if (evaluation.missingFiles.length > 0) {
|
|
5929
|
+
recommendations.push({
|
|
5930
|
+
severity: "warning",
|
|
5931
|
+
message: `Harness \uD30C\uC77C ${evaluation.missingFiles.length}\uAC1C \uB204\uB77D`,
|
|
5932
|
+
action: `roboco scaffold \uC2E4\uD589 \uB610\uB294 \uC218\uB3D9 \uC0DD\uC131: ${evaluation.missingFiles.join(", ")}`
|
|
5933
|
+
});
|
|
5934
|
+
}
|
|
5935
|
+
return {
|
|
5936
|
+
name: "\uD558\uB124\uC2A4 \uC5D4\uC9C0\uB2C8\uC5B4\uB9C1",
|
|
5937
|
+
tier: "nice",
|
|
5938
|
+
score: evaluation.score,
|
|
5939
|
+
recommendations,
|
|
5940
|
+
rawFindings: findings
|
|
5941
|
+
};
|
|
5942
|
+
}
|
|
5943
|
+
async function checkHarnessLegacy(repoPath) {
|
|
5114
5944
|
const findings = [];
|
|
5115
5945
|
let score = 0;
|
|
5116
5946
|
const harnessChecks = [
|
|
@@ -5122,12 +5952,20 @@ async function checkHarnessEngineering(repoPath) {
|
|
|
5122
5952
|
{ path: ".pai/config.json", label: "PAI config", points: 10 }
|
|
5123
5953
|
];
|
|
5124
5954
|
for (const { path: path10, label, points } of harnessChecks) {
|
|
5125
|
-
const found = await fs17.pathExists(
|
|
5955
|
+
const found = await fs17.pathExists(join17(repoPath, path10));
|
|
5126
5956
|
findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
5127
5957
|
if (found) score += points;
|
|
5128
5958
|
}
|
|
5129
5959
|
score = Math.min(100, score);
|
|
5130
|
-
|
|
5960
|
+
const recommendations = [];
|
|
5961
|
+
if (!await fs17.pathExists(join17(repoPath, ".harness.json"))) {
|
|
5962
|
+
recommendations.push({
|
|
5963
|
+
severity: "info",
|
|
5964
|
+
message: ".harness.json \uBBF8\uAC10\uC9C0",
|
|
5965
|
+
action: "roboco scaffold\uB97C \uC2E4\uD589\uD558\uBA74 \uC120\uC5B8\uC801 \uC815\uCC45 \uAE30\uBC18 \uD3C9\uAC00\uB85C \uC804\uD658\uB429\uB2C8\uB2E4"
|
|
5966
|
+
});
|
|
5967
|
+
}
|
|
5968
|
+
return { name: "\uD558\uB124\uC2A4 \uC5D4\uC9C0\uB2C8\uC5B4\uB9C1", tier: "nice", score, recommendations, rawFindings: findings };
|
|
5131
5969
|
}
|
|
5132
5970
|
var init_analyzer2 = __esm({
|
|
5133
5971
|
"src/stages/evaluation/analyzer.ts"() {
|
|
@@ -5505,7 +6343,7 @@ __export(shell_cd_exports, {
|
|
|
5505
6343
|
installShellHelper: () => installShellHelper,
|
|
5506
6344
|
requestCdAfter: () => requestCdAfter
|
|
5507
6345
|
});
|
|
5508
|
-
import { join as
|
|
6346
|
+
import { join as join18 } from "path";
|
|
5509
6347
|
import { homedir as homedir2 } from "os";
|
|
5510
6348
|
import fs18 from "fs-extra";
|
|
5511
6349
|
async function requestCdAfter(targetDir) {
|
|
@@ -5542,7 +6380,7 @@ async function installPowerShellHelper() {
|
|
|
5542
6380
|
await fs18.writeFile(HELPER_FILE_PS1, POWERSHELL_HELPER);
|
|
5543
6381
|
const rcFile = getShellRcPath();
|
|
5544
6382
|
const sourceLine = '. "$env:USERPROFILE\\.pai\\shell-helper.ps1"';
|
|
5545
|
-
await fs18.ensureDir(
|
|
6383
|
+
await fs18.ensureDir(join18(rcFile, ".."));
|
|
5546
6384
|
if (await fs18.pathExists(rcFile)) {
|
|
5547
6385
|
const content = await fs18.readFile(rcFile, "utf8");
|
|
5548
6386
|
if (content.includes("shell-helper.ps1")) {
|
|
@@ -5563,10 +6401,10 @@ var init_shell_cd = __esm({
|
|
|
5563
6401
|
"src/utils/shell-cd.ts"() {
|
|
5564
6402
|
"use strict";
|
|
5565
6403
|
init_platform();
|
|
5566
|
-
PAI_DIR =
|
|
5567
|
-
CD_FILE =
|
|
5568
|
-
HELPER_FILE_SH =
|
|
5569
|
-
HELPER_FILE_PS1 =
|
|
6404
|
+
PAI_DIR = join18(homedir2(), ".pai");
|
|
6405
|
+
CD_FILE = join18(PAI_DIR, ".cd-after");
|
|
6406
|
+
HELPER_FILE_SH = join18(PAI_DIR, "shell-helper.sh");
|
|
6407
|
+
HELPER_FILE_PS1 = join18(PAI_DIR, "shell-helper.ps1");
|
|
5570
6408
|
BASH_HELPER = `# PAI shell helper \u2014 \uC790\uB3D9 \uB514\uB809\uD1A0\uB9AC \uC774\uB3D9 \uC9C0\uC6D0
|
|
5571
6409
|
pai() {
|
|
5572
6410
|
local cd_target="$HOME/.pai/.cd-after"
|
|
@@ -5701,7 +6539,7 @@ function mergeOmcIntoSettings(settings, marketplaceId, marketplaceUrl, pluginId)
|
|
|
5701
6539
|
return next;
|
|
5702
6540
|
}
|
|
5703
6541
|
var DEFAULT_MARKETPLACE_ID, DEFAULT_MARKETPLACE_URL, DEFAULT_PLUGIN_ID, ClaudeSettingsError;
|
|
5704
|
-
var
|
|
6542
|
+
var init_claude_settings2 = __esm({
|
|
5705
6543
|
"src/utils/claude-settings.ts"() {
|
|
5706
6544
|
"use strict";
|
|
5707
6545
|
init_platform();
|
|
@@ -5720,13 +6558,13 @@ var init_claude_settings = __esm({
|
|
|
5720
6558
|
});
|
|
5721
6559
|
|
|
5722
6560
|
// src/stages/evaluation/cache.ts
|
|
5723
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
5724
|
-
import { join as
|
|
6561
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync as existsSync9 } from "fs";
|
|
6562
|
+
import { join as join19 } from "path";
|
|
5725
6563
|
import { createHash } from "crypto";
|
|
5726
6564
|
function computeRepoHash(repoPath) {
|
|
5727
6565
|
const hash = createHash("sha256");
|
|
5728
6566
|
for (const file of FILES_TO_HASH) {
|
|
5729
|
-
const fullPath =
|
|
6567
|
+
const fullPath = join19(repoPath, file);
|
|
5730
6568
|
try {
|
|
5731
6569
|
const content = readFileSync(fullPath);
|
|
5732
6570
|
hash.update(`${file}:${content.length}`);
|
|
@@ -5737,7 +6575,7 @@ function computeRepoHash(repoPath) {
|
|
|
5737
6575
|
return hash.digest("hex").slice(0, 16);
|
|
5738
6576
|
}
|
|
5739
6577
|
function getCachePath(repoPath) {
|
|
5740
|
-
return
|
|
6578
|
+
return join19(repoPath, CACHE_DIR, CACHE_FILE);
|
|
5741
6579
|
}
|
|
5742
6580
|
function loadCache(repoPath) {
|
|
5743
6581
|
try {
|
|
@@ -5748,8 +6586,8 @@ function loadCache(repoPath) {
|
|
|
5748
6586
|
}
|
|
5749
6587
|
}
|
|
5750
6588
|
function saveCache(repoPath, store) {
|
|
5751
|
-
const cacheDir =
|
|
5752
|
-
if (!
|
|
6589
|
+
const cacheDir = join19(repoPath, CACHE_DIR, "cache");
|
|
6590
|
+
if (!existsSync9(cacheDir)) {
|
|
5753
6591
|
mkdirSync(cacheDir, { recursive: true });
|
|
5754
6592
|
}
|
|
5755
6593
|
writeFileSync(getCachePath(repoPath), JSON.stringify(store, null, 2));
|
|
@@ -5807,7 +6645,7 @@ var evaluate_cmd_exports = {};
|
|
|
5807
6645
|
__export(evaluate_cmd_exports, {
|
|
5808
6646
|
evaluateCommand: () => evaluateCommand
|
|
5809
6647
|
});
|
|
5810
|
-
import { join as
|
|
6648
|
+
import { join as join20, basename } from "path";
|
|
5811
6649
|
import fs20 from "fs-extra";
|
|
5812
6650
|
async function evaluateCommand(cwd, options) {
|
|
5813
6651
|
const useCache = options.cache !== false;
|
|
@@ -5831,8 +6669,8 @@ async function evaluateCommand(cwd, options) {
|
|
|
5831
6669
|
const config = await loadConfig(cwd);
|
|
5832
6670
|
const projectName = config?.projectName ?? basename(cwd);
|
|
5833
6671
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
5834
|
-
const reportDir =
|
|
5835
|
-
const reportPath =
|
|
6672
|
+
const reportDir = join20(cwd, "docs", "p-reports");
|
|
6673
|
+
const reportPath = join20(reportDir, `${today}.md`);
|
|
5836
6674
|
await fs20.ensureDir(reportDir);
|
|
5837
6675
|
const detailedReport = buildDetailedReport(result, projectName);
|
|
5838
6676
|
await fs20.writeFile(reportPath, detailedReport, "utf8");
|
|
@@ -6092,7 +6930,7 @@ var init_cmd_exports = {};
|
|
|
6092
6930
|
__export(init_cmd_exports, {
|
|
6093
6931
|
initCommand: () => initCommand
|
|
6094
6932
|
});
|
|
6095
|
-
import { join as
|
|
6933
|
+
import { join as join21, basename as basename2 } from "path";
|
|
6096
6934
|
import fs21 from "fs-extra";
|
|
6097
6935
|
async function initCommand(cwd, nameArg) {
|
|
6098
6936
|
printWelcomeBanner();
|
|
@@ -6151,11 +6989,11 @@ async function initCommand(cwd, nameArg) {
|
|
|
6151
6989
|
const evalResult = computeResult2(llmOutput);
|
|
6152
6990
|
printReport2(evalResult);
|
|
6153
6991
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6154
|
-
const reportDir =
|
|
6992
|
+
const reportDir = join21(cwd, "docs", "p-reports");
|
|
6155
6993
|
await fs21.ensureDir(reportDir);
|
|
6156
6994
|
const legacyName = basename2(cwd);
|
|
6157
6995
|
const detailedReport = buildDetailedReport3(evalResult, legacyName);
|
|
6158
|
-
await fs21.writeFile(
|
|
6996
|
+
await fs21.writeFile(join21(reportDir, `${today}.md`), detailedReport, "utf8");
|
|
6159
6997
|
console.log("");
|
|
6160
6998
|
success(`\uB9AC\uD3EC\uD2B8 \uC800\uC7A5: docs/p-reports/${today}.md`);
|
|
6161
6999
|
} catch {
|
|
@@ -6175,7 +7013,7 @@ async function initCommand(cwd, nameArg) {
|
|
|
6175
7013
|
return;
|
|
6176
7014
|
}
|
|
6177
7015
|
}
|
|
6178
|
-
step(1,
|
|
7016
|
+
step(1, TOTAL_STEPS, "\uD504\uB85C\uC81D\uD2B8 \uC124\uC815");
|
|
6179
7017
|
hint("\uC774 \uC774\uB984\uC73C\uB85C \uD504\uB85C\uC81D\uD2B8 \uD3F4\uB354\uAC00 \uC0DD\uC131\uB429\uB2C8\uB2E4.");
|
|
6180
7018
|
hint("\uC601\uBB38 \uC18C\uBB38\uC790, \uC22B\uC790, \uD558\uC774\uD508(-)\uC744 \uC0AC\uC6A9\uD574\uC8FC\uC138\uC694.");
|
|
6181
7019
|
console.log("");
|
|
@@ -6200,7 +7038,7 @@ async function initCommand(cwd, nameArg) {
|
|
|
6200
7038
|
}]);
|
|
6201
7039
|
projectName = answer.name.trim();
|
|
6202
7040
|
}
|
|
6203
|
-
const projectDir =
|
|
7041
|
+
const projectDir = join21(cwd, projectName);
|
|
6204
7042
|
if (await fs21.pathExists(projectDir)) {
|
|
6205
7043
|
const existingConfig = await loadConfig(projectDir);
|
|
6206
7044
|
if (existingConfig) {
|
|
@@ -6227,7 +7065,6 @@ async function setupInDirectory(projectDir, projectName) {
|
|
|
6227
7065
|
hint("\uC0C8 \uD130\uBBF8\uB110\uC5D0\uC11C pai \uBA85\uB839\uC73C\uB85C \uC790\uB3D9 \uB514\uB809\uD1A0\uB9AC \uC774\uB3D9\uC774 \uD65C\uC131\uD654\uB429\uB2C8\uB2E4.");
|
|
6228
7066
|
console.log("");
|
|
6229
7067
|
}
|
|
6230
|
-
step(2, 3, "\uD504\uB85C\uC81D\uD2B8 \uC124\uC815");
|
|
6231
7068
|
const config = createDefaultConfig(projectName, "prototype");
|
|
6232
7069
|
const input = {
|
|
6233
7070
|
cwd: projectDir,
|
|
@@ -6260,55 +7097,55 @@ async function setupInDirectory(projectDir, projectName) {
|
|
|
6260
7097
|
}
|
|
6261
7098
|
const interview = result.data.interview;
|
|
6262
7099
|
const isCurrentDir = projectDir === process.cwd();
|
|
6263
|
-
await showCompletion(projectName, projectDir, interview
|
|
7100
|
+
await showCompletion(projectName, projectDir, interview, isCurrentDir);
|
|
6264
7101
|
} else {
|
|
6265
7102
|
for (const err of result.errors) {
|
|
6266
7103
|
error(err.message);
|
|
6267
7104
|
}
|
|
6268
7105
|
}
|
|
6269
7106
|
}
|
|
6270
|
-
async function showCompletion(projectName, projectDir,
|
|
7107
|
+
async function showCompletion(projectName, projectDir, interview, isCurrentDir) {
|
|
6271
7108
|
const { default: inquirer } = await import("inquirer");
|
|
6272
7109
|
const chalk9 = (await import("chalk")).default;
|
|
6273
7110
|
const { sleep: sleep2 } = await Promise.resolve().then(() => (init_progress(), progress_exports));
|
|
6274
|
-
|
|
6275
|
-
|
|
7111
|
+
const extraTools = interview.extraTools;
|
|
7112
|
+
step(4, TOTAL_STEPS, "\uC124\uCE58 \uD6C4 \uC548\uB0B4");
|
|
7113
|
+
console.log(colors.success(" \uD504\uB85C\uC81D\uD2B8 \uC900\uBE44\uAC00 \uB05D\uB0AC\uC2B5\uB2C8\uB2E4!"));
|
|
7114
|
+
console.log(colors.dim(" \uC774\uC81C \uBC14\uB85C \uAD6C\uD604\uC744 \uC2DC\uC791\uD558\uAC70\uB098, \uD544\uC694\uD55C \uD658\uACBD \uBCC0\uC218\uB9CC \uCC44\uC6B0\uBA74 \uB429\uB2C8\uB2E4."));
|
|
6276
7115
|
console.log("");
|
|
6277
|
-
console.log(colors.
|
|
7116
|
+
console.log(colors.accent(" \uC124\uCE58\uB41C \uAE30\uB2A5"));
|
|
6278
7117
|
console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
6279
|
-
console.log(` ${
|
|
6280
|
-
console.log(` ${
|
|
6281
|
-
console.log(` ${
|
|
6282
|
-
console.log(` ${
|
|
6283
|
-
console.log("");
|
|
6284
|
-
console.log(colors.accent(" \uC124\uCE58\uB41C \uD50C\uB7EC\uADF8\uC778 & \uAE30\uB2A5"));
|
|
6285
|
-
console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
6286
|
-
console.log(` ${chalk9.cyan("OpenSpec+OMC")} \uC124\uACC4\uB3C4 & \uC9C0\uD615\uB3C4 \u2014 \uBB34\uC5C7\uC744, \uC5B4\uB514\uC5D0 \uB9CC\uB4E4\uC9C0 \uC815\uC758`);
|
|
6287
|
-
console.log(` ${chalk9.cyan("RoboCo CLI")} \uD604\uC7A5 \uC18C\uC7A5 \u2014 \uC804\uCCB4 \uACF5\uC815 \uAD00\uB9AC, AI\uC5D0\uAC8C \uC791\uC5C5 \uC9C0\uC2DC`);
|
|
6288
|
-
console.log(` ${chalk9.cyan("Vibe-Ready")} \uD488\uC9C8 \uAC80\uC218 \u2014 AI \uAC1C\uBC1C \uC900\uBE44\uB3C4 6\uCE74\uD14C\uACE0\uB9AC \uD3C9\uAC00`);
|
|
7118
|
+
console.log(` ${chalk9.cyan("OpenSpec")} PRD \uC124\uACC4 \uD15C\uD50C\uB9BF`);
|
|
7119
|
+
console.log(` ${chalk9.cyan("RoboCo")} \uD30C\uC774\uD504\uB77C\uC778 \uC624\uCF00\uC2A4\uD2B8\uB808\uC774\uC158`);
|
|
7120
|
+
console.log(` ${chalk9.cyan("Harness")} \uD488\uC9C8 \uC815\uCC45 (hook \xB7 CI \xB7 lint)`);
|
|
7121
|
+
console.log(` ${chalk9.cyan("Vibe-Ready")} AI \uAC1C\uBC1C \uC900\uBE44\uB3C4 \uC9C4\uB2E8`);
|
|
6289
7122
|
if (extraTools.includes("omc")) {
|
|
6290
|
-
console.log(` ${chalk9.cyan("OMC")}
|
|
7123
|
+
console.log(` ${chalk9.cyan("OMC")} AI \uC5D0\uC774\uC804\uD2B8 \uC791\uC5C5 \uBD84\uC5C5`);
|
|
6291
7124
|
}
|
|
6292
7125
|
if (extraTools.includes("gstack")) {
|
|
6293
|
-
console.log(` ${chalk9.cyan("gstack")}
|
|
6294
|
-
}
|
|
6295
|
-
if (extraTools.includes("harness")) {
|
|
6296
|
-
console.log(` ${chalk9.cyan("Harness")} \uC548\uC804\uC7A5\uCE58 & \uAC80\uC0AC\uB300 \u2014 AI \uCF54\uB4DC \uC791\uB3D9 \uD14C\uC2A4\uD2B8`);
|
|
7126
|
+
console.log(` ${chalk9.cyan("gstack")} \uD14C\uC2A4\uD2B8 \uC790\uB3D9\uD654`);
|
|
6297
7127
|
}
|
|
6298
7128
|
if (extraTools.includes("vercel")) {
|
|
6299
|
-
console.log(` ${chalk9.cyan("Vercel")}
|
|
7129
|
+
console.log(` ${chalk9.cyan("Vercel")} \uC790\uB3D9 \uBC30\uD3EC (Preview + Production)`);
|
|
6300
7130
|
}
|
|
6301
7131
|
if (extraTools.includes("supabase")) {
|
|
6302
|
-
console.log(` ${chalk9.cyan("Supabase")}
|
|
7132
|
+
console.log(` ${chalk9.cyan("Supabase")} DB + \uC778\uC99D + \uC2A4\uD1A0\uB9AC\uC9C0`);
|
|
7133
|
+
}
|
|
7134
|
+
if (extraTools.includes("mcp") && interview.mcp) {
|
|
7135
|
+
console.log(` ${chalk9.cyan("MCP \uC11C\uBC84")} ${interview.mcp.name} (${interview.mcp.type})`);
|
|
7136
|
+
}
|
|
7137
|
+
if (interview.authMethods.length > 0) {
|
|
7138
|
+
console.log(` ${chalk9.cyan("\uB85C\uADF8\uC778")} ${interview.authMethods.join(", ")}`);
|
|
6303
7139
|
}
|
|
6304
|
-
if (
|
|
6305
|
-
console.log(` ${chalk9.cyan("
|
|
7140
|
+
if (interview.recipes && interview.recipes.length > 0) {
|
|
7141
|
+
console.log(` ${chalk9.cyan("\uC0AC\uB0B4 \uC5F0\uB3D9")} ${interview.recipes.join(", ")}`);
|
|
6306
7142
|
}
|
|
6307
7143
|
console.log("");
|
|
6308
|
-
console.log(colors.accent(" \
|
|
7144
|
+
console.log(colors.accent(" \uD30C\uC77C \uD655\uC778"));
|
|
6309
7145
|
console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
6310
7146
|
const checks = [
|
|
6311
7147
|
{ label: "AI \uCEE8\uD14D\uC2A4\uD2B8", path: "CLAUDE.md" },
|
|
7148
|
+
{ label: "Harness \uC815\uCC45", path: ".harness.json" },
|
|
6312
7149
|
{ label: "PRD \uD15C\uD50C\uB9BF", path: "docs/openspec.md" },
|
|
6313
7150
|
{ label: "\uD504\uB85C\uC81D\uD2B8 \uC124\uC815", path: ".pai/config.json" },
|
|
6314
7151
|
{ label: "Claude \uC124\uC815", path: ".claude/settings.json" },
|
|
@@ -6316,10 +7153,42 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
|
|
|
6316
7153
|
...extraTools.includes("omc") ? [{ label: "OMC \uB7F0\uD0C0\uC784", path: ".omc" }] : []
|
|
6317
7154
|
];
|
|
6318
7155
|
for (const check of checks) {
|
|
6319
|
-
const exists = await fs21.pathExists(
|
|
7156
|
+
const exists = await fs21.pathExists(join21(projectDir, check.path));
|
|
6320
7157
|
console.log(` ${exists ? colors.success("\u2713") : colors.err("\u2717")} ${check.label.padEnd(16)} ${colors.dim(check.path)}`);
|
|
6321
7158
|
}
|
|
6322
7159
|
console.log("");
|
|
7160
|
+
console.log(colors.accent(" \uB2E4\uC74C \uD560 \uC77C"));
|
|
7161
|
+
console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
7162
|
+
if (interview.authMethods.length > 0) {
|
|
7163
|
+
if (interview.authMethods.includes("custom") && !interview.customAuth?.clientId) {
|
|
7164
|
+
console.log(` ${chalk9.yellow("\u2192")} .env.local \uC5D0 OAuth \uD0A4\uB97C \uCC44\uC6CC\uC8FC\uC138\uC694`);
|
|
7165
|
+
}
|
|
7166
|
+
if (interview.authMethods.includes("google")) {
|
|
7167
|
+
console.log(` ${chalk9.yellow("\u2192")} Google Cloud Console\uC5D0\uC11C OAuth \uD074\uB77C\uC774\uC5B8\uD2B8 \uC0DD\uC131`);
|
|
7168
|
+
}
|
|
7169
|
+
if (interview.authMethods.includes("kakao")) {
|
|
7170
|
+
console.log(` ${chalk9.yellow("\u2192")} Kakao Developers\uC5D0\uC11C \uC571 \uB4F1\uB85D`);
|
|
7171
|
+
}
|
|
7172
|
+
if (interview.authMethods.includes("naver")) {
|
|
7173
|
+
console.log(` ${chalk9.yellow("\u2192")} Naver Developers\uC5D0\uC11C \uC571 \uB4F1\uB85D`);
|
|
7174
|
+
}
|
|
7175
|
+
}
|
|
7176
|
+
if (extraTools.includes("vercel")) {
|
|
7177
|
+
console.log(` ${chalk9.yellow("\u2192")} vercel login \uC73C\uB85C Vercel \uACC4\uC815 \uC5F0\uACB0`);
|
|
7178
|
+
}
|
|
7179
|
+
if (extraTools.includes("supabase")) {
|
|
7180
|
+
console.log(` ${chalk9.yellow("\u2192")} .env.local \uC5D0 SUPABASE_URL, SUPABASE_ANON_KEY \uC785\uB825`);
|
|
7181
|
+
}
|
|
7182
|
+
if (extraTools.includes("mcp") && interview.mcp) {
|
|
7183
|
+
console.log(` ${chalk9.yellow("\u2192")} mcp-server/ \uC5D0\uC11C MCP \uC11C\uBC84 \uAC1C\uBC1C \uC2DC\uC791`);
|
|
7184
|
+
}
|
|
7185
|
+
if (interview.recipes && interview.recipes.length > 0) {
|
|
7186
|
+
for (const recipe of interview.recipes) {
|
|
7187
|
+
console.log(` ${chalk9.yellow("\u2192")} docs/recipes/${recipe}/ \uAC00\uC774\uB4DC \uD655\uC778`);
|
|
7188
|
+
}
|
|
7189
|
+
}
|
|
7190
|
+
console.log(` ${chalk9.yellow("\u2192")} docs/openspec.md \uC5D0 PRD \uC791\uC131 \uC2DC\uC791`);
|
|
7191
|
+
console.log("");
|
|
6323
7192
|
const { runEval } = await inquirer.prompt([{
|
|
6324
7193
|
type: "confirm",
|
|
6325
7194
|
name: "runEval",
|
|
@@ -6341,10 +7210,10 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
|
|
|
6341
7210
|
printReport2(evalResult);
|
|
6342
7211
|
await sleep2(500);
|
|
6343
7212
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6344
|
-
const reportDir =
|
|
7213
|
+
const reportDir = join21(projectDir, "docs", "p-reports");
|
|
6345
7214
|
await fs21.ensureDir(reportDir);
|
|
6346
7215
|
const detailedReport = buildDetailedReport3(evalResult, projectName);
|
|
6347
|
-
await fs21.writeFile(
|
|
7216
|
+
await fs21.writeFile(join21(reportDir, `${today}.md`), detailedReport, "utf8");
|
|
6348
7217
|
await sleep2(500);
|
|
6349
7218
|
console.log("");
|
|
6350
7219
|
success(`\uB9AC\uD3EC\uD2B8 \uC800\uC7A5: docs/p-reports/${today}.md`);
|
|
@@ -6372,7 +7241,7 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
|
|
|
6372
7241
|
const { createSpinner: createSpinner2 } = await Promise.resolve().then(() => (init_progress(), progress_exports));
|
|
6373
7242
|
const omcSpinner = createSpinner2("OMC \uD50C\uB7EC\uADF8\uC778 \uB4F1\uB85D \uC911...");
|
|
6374
7243
|
try {
|
|
6375
|
-
const { enableOmcPlugin: enableOmcPlugin2 } = await Promise.resolve().then(() => (
|
|
7244
|
+
const { enableOmcPlugin: enableOmcPlugin2 } = await Promise.resolve().then(() => (init_claude_settings2(), claude_settings_exports));
|
|
6376
7245
|
const result = await enableOmcPlugin2();
|
|
6377
7246
|
if (result.action === "already-enabled") {
|
|
6378
7247
|
omcSpinner.succeed("OMC \uD50C\uB7EC\uADF8\uC778 \uC774\uBBF8 \uB4F1\uB85D\uB428");
|
|
@@ -6417,7 +7286,7 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
|
|
|
6417
7286
|
const aliasLine = getYoloAliasLine2();
|
|
6418
7287
|
const rcContent = await fs21.readFile(shellRc, "utf8").catch(() => "");
|
|
6419
7288
|
if (!rcContent.includes("claude-yolo")) {
|
|
6420
|
-
await fs21.ensureDir(
|
|
7289
|
+
await fs21.ensureDir(join21(shellRc, ".."));
|
|
6421
7290
|
await fs21.appendFile(shellRc, `
|
|
6422
7291
|
# PAI \u2014 claude-YOLO mode
|
|
6423
7292
|
${aliasLine}
|
|
@@ -6611,9 +7480,9 @@ async function installOrchestratorOnly(projectDir, projectName) {
|
|
|
6611
7480
|
const evalResult = computeResult2(llmOutput);
|
|
6612
7481
|
printReport2(evalResult);
|
|
6613
7482
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6614
|
-
const reportDir =
|
|
7483
|
+
const reportDir = join21(projectDir, "docs", "p-reports");
|
|
6615
7484
|
await fs21.ensureDir(reportDir);
|
|
6616
|
-
await fs21.writeFile(
|
|
7485
|
+
await fs21.writeFile(join21(reportDir, `${today}.md`), buildDetailedReport3(evalResult, projectName), "utf8");
|
|
6617
7486
|
console.log("");
|
|
6618
7487
|
hint(`\uC0C1\uC138 \uB9AC\uD3EC\uD2B8: docs/p-reports/${today}.md`);
|
|
6619
7488
|
} catch {
|
|
@@ -6663,7 +7532,7 @@ async function detectLegacyProject(cwd) {
|
|
|
6663
7532
|
".gitignore"
|
|
6664
7533
|
];
|
|
6665
7534
|
for (const signal of signals) {
|
|
6666
|
-
if (await fs21.pathExists(
|
|
7535
|
+
if (await fs21.pathExists(join21(cwd, signal))) return true;
|
|
6667
7536
|
}
|
|
6668
7537
|
return false;
|
|
6669
7538
|
}
|
|
@@ -6676,6 +7545,7 @@ var init_init_cmd = __esm({
|
|
|
6676
7545
|
init_detector();
|
|
6677
7546
|
init_roboco();
|
|
6678
7547
|
init_validate_cmd();
|
|
7548
|
+
init_interviewer();
|
|
6679
7549
|
init_ui();
|
|
6680
7550
|
init_logger();
|
|
6681
7551
|
}
|
|
@@ -6756,12 +7626,12 @@ var init_help_cmd = __esm({
|
|
|
6756
7626
|
});
|
|
6757
7627
|
|
|
6758
7628
|
// src/stages/design/openspec.ts
|
|
6759
|
-
import { join as
|
|
7629
|
+
import { join as join22 } from "path";
|
|
6760
7630
|
import fs22 from "fs-extra";
|
|
6761
7631
|
async function initOpenSpec(cwd, projectName) {
|
|
6762
|
-
const docsDir =
|
|
7632
|
+
const docsDir = join22(cwd, "docs");
|
|
6763
7633
|
await fs22.ensureDir(docsDir);
|
|
6764
|
-
const openspecPath =
|
|
7634
|
+
const openspecPath = join22(docsDir, "openspec.md");
|
|
6765
7635
|
if (await fs22.pathExists(openspecPath)) {
|
|
6766
7636
|
info("docs/openspec.md \uC774\uBBF8 \uC874\uC7AC \u2014 \uAC74\uB108\uB700");
|
|
6767
7637
|
return;
|
|
@@ -6794,9 +7664,9 @@ async function initOpenSpec(cwd, projectName) {
|
|
|
6794
7664
|
}
|
|
6795
7665
|
async function validateOpenSpec(cwd) {
|
|
6796
7666
|
const candidates = [
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
7667
|
+
join22(cwd, "docs", "openspec.md"),
|
|
7668
|
+
join22(cwd, "openspec.md"),
|
|
7669
|
+
join22(cwd, ".pai", "openspec.md")
|
|
6800
7670
|
];
|
|
6801
7671
|
let specPath = null;
|
|
6802
7672
|
for (const p of candidates) {
|
|
@@ -6862,12 +7732,12 @@ var init_openspec = __esm({
|
|
|
6862
7732
|
});
|
|
6863
7733
|
|
|
6864
7734
|
// src/stages/design/omc.ts
|
|
6865
|
-
import { join as
|
|
7735
|
+
import { join as join23 } from "path";
|
|
6866
7736
|
import fs23 from "fs-extra";
|
|
6867
7737
|
async function initOMC(cwd, projectName) {
|
|
6868
|
-
const paiDir =
|
|
7738
|
+
const paiDir = join23(cwd, ".pai");
|
|
6869
7739
|
await fs23.ensureDir(paiDir);
|
|
6870
|
-
const omcPath =
|
|
7740
|
+
const omcPath = join23(paiDir, "omc.md");
|
|
6871
7741
|
if (await fs23.pathExists(omcPath)) {
|
|
6872
7742
|
info(".pai/omc.md \uC774\uBBF8 \uC874\uC7AC \u2014 \uAC74\uB108\uB700");
|
|
6873
7743
|
return;
|
|
@@ -7031,13 +7901,13 @@ var init_context = __esm({
|
|
|
7031
7901
|
});
|
|
7032
7902
|
|
|
7033
7903
|
// src/stages/design/index.ts
|
|
7034
|
-
import { join as
|
|
7904
|
+
import { join as join24 } from "path";
|
|
7035
7905
|
import fs24 from "fs-extra";
|
|
7036
7906
|
async function autoInstallHarness(cwd) {
|
|
7037
|
-
const harnessPath =
|
|
7907
|
+
const harnessPath = join24(cwd, ".pai", "harness.json");
|
|
7038
7908
|
if (await fs24.pathExists(harnessPath)) return;
|
|
7039
7909
|
await withSpinner("Harness Engineering \uC790\uB3D9 \uC124\uC815 \uC911...", async () => {
|
|
7040
|
-
await fs24.ensureDir(
|
|
7910
|
+
await fs24.ensureDir(join24(cwd, ".pai"));
|
|
7041
7911
|
await fs24.writeJson(harnessPath, {
|
|
7042
7912
|
version: "1.0",
|
|
7043
7913
|
specFile: "docs/openspec.md",
|
|
@@ -7754,7 +8624,7 @@ var savetoken_cmd_exports = {};
|
|
|
7754
8624
|
__export(savetoken_cmd_exports, {
|
|
7755
8625
|
savetokenCommand: () => savetokenCommand
|
|
7756
8626
|
});
|
|
7757
|
-
import { join as
|
|
8627
|
+
import { join as join25, relative } from "path";
|
|
7758
8628
|
import fs26 from "fs-extra";
|
|
7759
8629
|
import chalk7 from "chalk";
|
|
7760
8630
|
async function savetokenCommand(cwd) {
|
|
@@ -7824,10 +8694,10 @@ async function savetokenCommand(cwd) {
|
|
|
7824
8694
|
console.log(` \u2192 ${colors.dim("\uB8F0 \uAE30\uBC18 \uB85C\uC9C1, \uD0A4\uC6CC\uB4DC \uB9E4\uCE6D\uC73C\uB85C \uAC80\uD1A0 \uD6C4 \uB300\uCCB4")}`);
|
|
7825
8695
|
console.log(` ${chalk7.red("\u25CF")} \uB192\uC74C \uCF54\uB4DC \uC0DD\uC131, \uBCF5\uC7A1\uD55C \uCD94\uB860, \uCC3D\uC758\uC801 \uC0DD\uC131`);
|
|
7826
8696
|
console.log(` \u2192 ${colors.dim("AI \uD544\uC218 \u2014 \uD504\uB86C\uD504\uD2B8 \uCD5C\uC801\uD654\uB85C \uD1A0\uD070 \uC808\uAC10")}`);
|
|
7827
|
-
const reportDir =
|
|
8697
|
+
const reportDir = join25(cwd, ".pai");
|
|
7828
8698
|
await fs26.ensureDir(reportDir);
|
|
7829
8699
|
const report = buildReport(callSites, cwd);
|
|
7830
|
-
const reportPath =
|
|
8700
|
+
const reportPath = join25(reportDir, "savetoken-report.md");
|
|
7831
8701
|
await fs26.writeFile(reportPath, report, "utf8");
|
|
7832
8702
|
console.log("");
|
|
7833
8703
|
success("\uC2A4\uCE94 \uB9AC\uD3EC\uD2B8 \uC800\uC7A5: .pai/savetoken-report.md");
|
|
@@ -7984,7 +8854,7 @@ var wakeup_cmd_exports = {};
|
|
|
7984
8854
|
__export(wakeup_cmd_exports, {
|
|
7985
8855
|
wakeupCommand: () => wakeupCommand
|
|
7986
8856
|
});
|
|
7987
|
-
import { join as
|
|
8857
|
+
import { join as join26 } from "path";
|
|
7988
8858
|
import { homedir as homedir3, platform as osPlatform } from "os";
|
|
7989
8859
|
import fs27 from "fs-extra";
|
|
7990
8860
|
import chalk8 from "chalk";
|
|
@@ -8065,7 +8935,7 @@ async function wakeupCommand(timeOrAction, schedule = "\uD3C9\uC77C") {
|
|
|
8065
8935
|
async function setupMacOS(config) {
|
|
8066
8936
|
const { execa } = await import("execa");
|
|
8067
8937
|
const [hour, minute] = config.time.split(":").map(Number);
|
|
8068
|
-
const plistDir =
|
|
8938
|
+
const plistDir = join26(homedir3(), "Library", "LaunchAgents");
|
|
8069
8939
|
await fs27.ensureDir(plistDir);
|
|
8070
8940
|
const weekdays = scheduleToWeekdays(config.schedule);
|
|
8071
8941
|
let calendarEntries;
|
|
@@ -8124,9 +8994,9 @@ ${calendarEntries}
|
|
|
8124
8994
|
async function setupWindows(config) {
|
|
8125
8995
|
const { execa } = await import("execa");
|
|
8126
8996
|
const [hour, minute] = config.time.split(":").map(Number);
|
|
8127
|
-
const psScriptDir =
|
|
8997
|
+
const psScriptDir = join26(homedir3(), ".pai");
|
|
8128
8998
|
await fs27.ensureDir(psScriptDir);
|
|
8129
|
-
const psScriptPath =
|
|
8999
|
+
const psScriptPath = join26(psScriptDir, "wakeup.ps1");
|
|
8130
9000
|
const claudeCmd = config.launchMode === "yolo" ? "claude --dangerously-skip-permissions" : "claude";
|
|
8131
9001
|
const psScript = `# PAI Wakeup \u2014 Claude Code \uC138\uC158 \uC790\uB3D9 \uC2DC\uC791
|
|
8132
9002
|
$paiDir = "$env:USERPROFILE\\.pai"
|
|
@@ -8400,12 +9270,12 @@ var init_wakeup_cmd = __esm({
|
|
|
8400
9270
|
"use strict";
|
|
8401
9271
|
init_ui();
|
|
8402
9272
|
init_logger();
|
|
8403
|
-
PAI_DIR2 =
|
|
8404
|
-
CONFIG_FILE2 =
|
|
8405
|
-
MESSAGES_FILE =
|
|
8406
|
-
SCRIPT_FILE =
|
|
9273
|
+
PAI_DIR2 = join26(homedir3(), ".pai");
|
|
9274
|
+
CONFIG_FILE2 = join26(PAI_DIR2, "wakeup-config.json");
|
|
9275
|
+
MESSAGES_FILE = join26(PAI_DIR2, "wakeup-messages.json");
|
|
9276
|
+
SCRIPT_FILE = join26(PAI_DIR2, "wakeup.sh");
|
|
8407
9277
|
PLIST_NAME = "com.pai.wakeup";
|
|
8408
|
-
PLIST_PATH =
|
|
9278
|
+
PLIST_PATH = join26(homedir3(), "Library", "LaunchAgents", `${PLIST_NAME}.plist`);
|
|
8409
9279
|
CRON_MARKER = "# PAI-WAKEUP";
|
|
8410
9280
|
MESSAGES = [
|
|
8411
9281
|
`Here's to the crazy ones. The misfits. The rebels. The troublemakers.
|