pai-zero 0.13.2 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/pai.js +1131 -192
- package/dist/bin/pai.js.map +1 -1
- package/dist/cli/index.js +1131 -192
- 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
629
|
}
|
|
630
|
-
|
|
631
|
-
|
|
630
|
+
if (mode === "production") {
|
|
631
|
+
console.log(` ${colors.accent("+")} gstack \uD14C\uC2A4\uD2B8 \xB7 Harness \uD488\uC9C8 \uAC80\uC99D`);
|
|
632
|
+
}
|
|
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");
|
|
632
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."));
|
|
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
|
|
|
@@ -2845,17 +2887,65 @@ description: "Ideation \u2192 PRD \u2192 \uD558\uB124\uC2A4 \uAD6C\uC131\uAE4C\u
|
|
|
2845
2887
|
|
|
2846
2888
|
# PAI \uC124\uACC4 \u2014 \uC2EC\uCE35 \uC778\uD130\uBDF0 \uAE30\uBC18 \uD30C\uC774\uD504\uB77C\uC778
|
|
2847
2889
|
|
|
2890
|
+
## \uC138\uC158 \uAD00\uB9AC \uC6D0\uCE59 (\uBC18\uB4DC\uC2DC \uC900\uC218)
|
|
2891
|
+
|
|
2892
|
+
### Phase \uC804\uD658 \uCCB4\uD06C\uD3EC\uC778\uD2B8
|
|
2893
|
+
\uAC01 Phase\uAC00 \uC644\uB8CC\uB420 \uB54C\uB9C8\uB2E4 \uB2E4\uC74C \uC21C\uC11C\uB85C **\uBC18\uB4DC\uC2DC** \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uD655\uC778\uD558\uC138\uC694:
|
|
2894
|
+
|
|
2895
|
+
\`\`\`
|
|
2896
|
+
\u2705 Phase N \uC644\uB8CC.
|
|
2897
|
+
|
|
2898
|
+
[1] git commit \uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?
|
|
2899
|
+
\u2192 \uCEE4\uBC0B \uBA54\uC2DC\uC9C0 \uC81C\uC548: "docs: ..."
|
|
2900
|
+
[2] \uB2E4\uC74C Phase(N+1)\uB85C \uB118\uC5B4\uAC00\uAE30 \uC804\uC5D0 /clear \uB85C \uCEE8\uD14D\uC2A4\uD2B8\uB97C \uCD08\uAE30\uD654\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?
|
|
2901
|
+
\u2192 \uCD08\uAE30\uD654\uD558\uBA74 \uD604\uC7AC \uB300\uD654\uB294 \uC0AC\uB77C\uC9C0\uC9C0\uB9CC \uD30C\uC77C\uC740 \uC720\uC9C0\uB429\uB2C8\uB2E4.
|
|
2902
|
+
\u2192 \uCD08\uAE30\uD654 \uD6C4 /pai design prd \uB610\uB294 /pai design validate \uB85C \uC7AC\uC2DC\uC791\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.
|
|
2903
|
+
\`\`\`
|
|
2904
|
+
|
|
2905
|
+
\uC0AC\uC6A9\uC790\uAC00 **\uB458 \uB2E4 \uC6D0\uD558\uC9C0 \uC54A\uB294\uB2E4\uBA74** \uBC14\uB85C \uB2E4\uC74C Phase\uB97C \uC9C4\uD589\uD558\uC138\uC694.
|
|
2906
|
+
|
|
2907
|
+
### \uCEE8\uD14D\uC2A4\uD2B8 \uD06C\uAE30 \uACBD\uACE0
|
|
2908
|
+
Phase \uC9C4\uD589 \uC911 \uB300\uD654\uAC00 \uAE38\uC5B4\uC84C\uB2E4\uACE0 \uD310\uB2E8\uB420 \uB54C (\uBA54\uC2DC\uC9C0\uAC00 10\uAC1C \uC774\uC0C1\uC774\uAC70\uB098 \uC778\uD130\uBDF0\uAC00 \uBC18\uBCF5\uB420 \uB54C):
|
|
2909
|
+
|
|
2910
|
+
\`\`\`
|
|
2911
|
+
\u26A0\uFE0F \uB300\uD654\uAC00 \uAE38\uC5B4\uC84C\uC2B5\uB2C8\uB2E4. /compact \uB85C \uCEE8\uD14D\uC2A4\uD2B8\uB97C \uC555\uCD95\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?
|
|
2912
|
+
\u2192 \uC555\uCD95\uD574\uB3C4 \uC791\uC5C5 \uD750\uB984\uC740 \uC720\uC9C0\uB429\uB2C8\uB2E4.
|
|
2913
|
+
\`\`\`
|
|
2914
|
+
|
|
2915
|
+
> \uC774 \uB450 \uAC00\uC9C0 \uD655\uC778\uC740 \uC0AC\uC6A9\uC790\uAC00 \uB9E5\uB77D \uAD00\uB9AC\uB97C \uB193\uCE58\uC9C0 \uC54A\uB3C4\uB85D \uB3D5\uB294 \uD575\uC2EC \uC9C0\uCE68\uC785\uB2C8\uB2E4. \uC808\uB300 \uC0DD\uB7B5\uD558\uC9C0 \uB9C8\uC138\uC694.
|
|
2916
|
+
|
|
2848
2917
|
## \uC778\uC790: $ARGUMENTS
|
|
2849
|
-
- \uC778\uC790 \uC5C6\uC74C \u2192 \uC804\uCCB4 \uD30C\uC774\uD504\uB77C\uC778 (Ideation \u2192 PRD \u2192 \uD558\uB124\uC2A4)
|
|
2918
|
+
- \uC778\uC790 \uC5C6\uC74C \u2192 \uC804\uCCB4 \uD30C\uC774\uD504\uB77C\uC778 (Git Init \u2192 Ideation \u2192 PRD \u2192 \uD558\uB124\uC2A4)
|
|
2850
2919
|
- \`ideation\` \u2192 Ideation\uB9CC \uC218\uD589
|
|
2851
2920
|
- \`prd\` \u2192 PRD\uB9CC \uC218\uD589
|
|
2852
2921
|
- \`validate\` \u2192 \uAC80\uC99D\uB9CC \uC218\uD589
|
|
2853
2922
|
|
|
2923
|
+
---
|
|
2924
|
+
|
|
2925
|
+
## Phase 0: Git \uC800\uC7A5\uC18C \uCD08\uAE30\uD654
|
|
2926
|
+
|
|
2927
|
+
**\uBAA9\uD45C**: \uC124\uACC4 \uC0B0\uCD9C\uBB3C\uC744 \uBC84\uC804 \uAD00\uB9AC \uC544\uB798\uC5D0 \uB450\uAE30
|
|
2928
|
+
|
|
2929
|
+
1. \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC\uC5D0 \`.git\`\uC774 \uC788\uB294\uC9C0 \uD655\uC778 (\`git status\`)
|
|
2930
|
+
2. \`.git\`\uC774 \uC5C6\uC73C\uBA74:
|
|
2931
|
+
\`\`\`bash
|
|
2932
|
+
git init
|
|
2933
|
+
git add .
|
|
2934
|
+
git commit -m "chore: initial commit"
|
|
2935
|
+
\`\`\`
|
|
2936
|
+
3. \`.git\`\uC774 \uC774\uBBF8 \uC788\uC73C\uBA74 \uAC74\uB108\uB6F0\uACE0 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC54C\uB9BC
|
|
2937
|
+
|
|
2938
|
+
> \uC774 Phase\uB294 \uC778\uC790\uC640 \uBB34\uAD00\uD558\uAC8C **\uD56D\uC0C1 \uBA3C\uC800 \uC2E4\uD589**\uD558\uC138\uC694.
|
|
2939
|
+
|
|
2940
|
+
---
|
|
2941
|
+
|
|
2854
2942
|
## Phase 1: Ideation (\uC544\uC774\uB514\uC5B4 \uAD6C\uCCB4\uD654)
|
|
2855
2943
|
|
|
2856
2944
|
**\uBAA9\uD45C**: \uC0AC\uC6A9\uC790\uC758 \uBA38\uB9BF\uC18D \uC544\uC774\uB514\uC5B4\uB97C \`docs/ideation.md\`\uB85C \uAD6C\uCCB4\uD654
|
|
2857
2945
|
|
|
2858
|
-
|
|
2946
|
+
\`docs/ideation.md\`\uAC00 \uC774\uBBF8 \uC874\uC7AC\uD558\uBA74 \uB0B4\uC6A9\uC744 \uC77D\uACE0 Phase 2\uB85C \uB118\uC5B4\uAC00\uC138\uC694.
|
|
2947
|
+
|
|
2948
|
+
\uC5C6\uC73C\uBA74 AskUserQuestion \uB3C4\uAD6C\uB85C \uC2EC\uCE35 \uC778\uD130\uBDF0\uB97C \uC9C4\uD589\uD558\uC138\uC694:
|
|
2859
2949
|
1. "\uC5B4\uB5A4 \uBB38\uC81C\uB97C \uD574\uACB0\uD558\uACE0 \uC2F6\uC73C\uC138\uC694?" \u2014 \uD575\uC2EC \uACFC\uC81C \uD30C\uC545
|
|
2860
2950
|
2. "\uC774 \uC11C\uBE44\uC2A4\uB97C \uC8FC\uB85C \uC0AC\uC6A9\uD560 \uC0AC\uB78C\uC740 \uB204\uAD6C\uC778\uAC00\uC694?" \u2014 \uB300\uC0C1 \uC0AC\uC6A9\uC790
|
|
2861
2951
|
3. "\uBE44\uC2B7\uD55C \uC11C\uBE44\uC2A4\uAC00 \uC788\uB2E4\uBA74 \uBB50\uAC00 \uB2E4\uB978\uAC00\uC694?" \u2014 \uCC28\uBCC4\uC810
|
|
@@ -2864,6 +2954,14 @@ AskUserQuestion \uB3C4\uAD6C\uB85C \uC2EC\uCE35 \uC778\uD130\uBDF0\uB97C \uC9C4\
|
|
|
2864
2954
|
|
|
2865
2955
|
\uB2F5\uBCC0\uC744 \uC885\uD569\uD558\uC5EC \`docs/ideation.md\`\uB97C \uC0DD\uC131\uD558\uC138\uC694.
|
|
2866
2956
|
|
|
2957
|
+
**Phase 1 \uC644\uB8CC \uD6C4 \uCEE4\uBC0B**:
|
|
2958
|
+
\`\`\`bash
|
|
2959
|
+
git add docs/ideation.md
|
|
2960
|
+
git commit -m "docs: add ideation \u2014 [\uD504\uB85C\uC81D\uD2B8 \uD55C \uC904 \uC124\uBA85]"
|
|
2961
|
+
\`\`\`
|
|
2962
|
+
|
|
2963
|
+
---
|
|
2964
|
+
|
|
2867
2965
|
## Phase 2: PRD \uC791\uC131 (\uC81C\uD488 \uC694\uAD6C\uC0AC\uD56D)
|
|
2868
2966
|
|
|
2869
2967
|
**\uBAA9\uD45C**: Ideation\uC744 \uAE30\uBC18\uC73C\uB85C \`docs/openspec.md\` PRD \uC791\uC131
|
|
@@ -2881,6 +2979,12 @@ AskUserQuestion \uB3C4\uAD6C\uB85C \uC2EC\uCE35 \uC778\uD130\uBDF0\uB97C \uC9C4\
|
|
|
2881
2979
|
- \uAE30\uC220 \uC2A4\uD0DD (Stack)
|
|
2882
2980
|
- API \uC5D4\uB4DC\uD3EC\uC778\uD2B8
|
|
2883
2981
|
|
|
2982
|
+
**Phase 2 \uC644\uB8CC \uD6C4 \uCEE4\uBC0B**:
|
|
2983
|
+
\`\`\`bash
|
|
2984
|
+
git add docs/openspec.md
|
|
2985
|
+
git commit -m "docs: add openspec PRD \u2014 [\uD504\uB85C\uC81D\uD2B8\uBA85]"
|
|
2986
|
+
\`\`\`
|
|
2987
|
+
|
|
2884
2988
|
## Phase 2.5: MCP \uC11C\uBC84 \uC124\uACC4 (MCP \uD504\uB85C\uC81D\uD2B8\uB9CC)
|
|
2885
2989
|
|
|
2886
2990
|
\\\`.pai/config.json\\\`\uC758 \\\`mcp.enabled\\\`\uAC00 true\uC774\uBA74 \uC774 Phase\uB97C \uC218\uD589\uD558\uC138\uC694:
|
|
@@ -2927,18 +3031,25 @@ AskUserQuestion\uC73C\uB85C \uAE30\uC220\uC801 \uC81C\uC57D \uD30C\uC545:
|
|
|
2927
3031
|
- \uD14C\uC2A4\uD2B8 \uD658\uACBD \u2014 \`tests/\` \uB514\uB809\uD1A0\uB9AC + \uD14C\uC2A4\uD2B8 \uD504\uB808\uC784\uC6CC\uD06C \uC874\uC7AC \uD655\uC778
|
|
2928
3032
|
- \uBD80\uC871\uD55C \uD56D\uBAA9\uC740 \uC790\uB3D9 \uC0DD\uC131 \uC81C\uC548
|
|
2929
3033
|
|
|
2930
|
-
## Phase 4: \uAC80\uC99D
|
|
3034
|
+
## Phase 4: \uAC80\uC99D + \uCD5C\uC885 \uCEE4\uBC0B
|
|
2931
3035
|
|
|
2932
3036
|
\uBAA8\uB4E0 Phase \uC644\uB8CC \uD6C4:
|
|
2933
3037
|
1. \`docs/ideation.md\` \uC874\uC7AC + \uB0B4\uC6A9 \uD655\uC778
|
|
2934
3038
|
2. \`docs/openspec.md\` 5\uAC1C \uC139\uC158 \uC644\uC131\uB3C4 \uAC80\uC99D
|
|
2935
3039
|
3. \`handoff.md\` \uC5C5\uB370\uC774\uD2B8 \u2014 \uC644\uB8CC\uB41C \uD56D\uBAA9 \uCCB4\uD06C, \uB2E4\uC74C \uB2E8\uACC4 \uAC31\uC2E0
|
|
2936
3040
|
|
|
3041
|
+
**\uCD5C\uC885 \uCEE4\uBC0B** (\uBCC0\uACBD\uC0AC\uD56D\uC774 \uC788\uC744 \uACBD\uC6B0):
|
|
3042
|
+
\`\`\`bash
|
|
3043
|
+
git add -A
|
|
3044
|
+
git commit -m "docs: complete design phase \u2014 ideation + PRD"
|
|
3045
|
+
\`\`\`
|
|
3046
|
+
|
|
2937
3047
|
## \uC911\uC694 \uC6D0\uCE59
|
|
2938
3048
|
|
|
2939
3049
|
- **\uCD94\uCE21 \uAE08\uC9C0**: \uCEE8\uD14D\uC2A4\uD2B8\uAC00 \uBD80\uC871\uD558\uBA74 \uBC18\uB4DC\uC2DC AskUserQuestion\uC73C\uB85C \uC9C8\uBB38
|
|
2940
3050
|
- **\uC774\uBBF8 \uC874\uC7AC\uD558\uB294 \uD30C\uC77C \uB36E\uC5B4\uC4F0\uC9C0 \uC54A\uC74C** \u2014 \uBCF4\uAC15\uB9CC \uC81C\uC548
|
|
2941
3051
|
- **\uAC01 Phase \uC644\uB8CC \uC2DC \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uACB0\uACFC \uC694\uC57D** \uBCF4\uACE0
|
|
3052
|
+
- **Git \uCEE4\uBC0B\uC740 \uAC01 Phase \uC644\uB8CC \uC9C1\uD6C4** \u2014 \uC124\uACC4 \uC774\uB825 \uCD94\uC801 \uAC00\uB2A5\uD558\uAC8C
|
|
2942
3053
|
`,
|
|
2943
3054
|
"validate.md": `---
|
|
2944
3055
|
name: "PAI: Validate"
|
|
@@ -3605,6 +3716,543 @@ var init_detector = __esm({
|
|
|
3605
3716
|
}
|
|
3606
3717
|
});
|
|
3607
3718
|
|
|
3719
|
+
// src/core/harness-defaults.ts
|
|
3720
|
+
function resolveConfig(partial) {
|
|
3721
|
+
const base = HARNESS_PRESETS[partial.scenario];
|
|
3722
|
+
if (!partial.overrides) return { ...base };
|
|
3723
|
+
const overrides = partial.overrides;
|
|
3724
|
+
return {
|
|
3725
|
+
...base,
|
|
3726
|
+
gates: {
|
|
3727
|
+
preCommit: { ...base.gates.preCommit, ...overrides.gates?.preCommit },
|
|
3728
|
+
prePush: { ...base.gates.prePush, ...overrides.gates?.prePush },
|
|
3729
|
+
ci: { ...base.gates.ci, ...overrides.gates?.ci }
|
|
3730
|
+
},
|
|
3731
|
+
scripts: overrides.scripts ?? base.scripts,
|
|
3732
|
+
permissions: overrides.permissions ? {
|
|
3733
|
+
allow: [...base.permissions.allow, ...overrides.permissions.allow ?? []],
|
|
3734
|
+
deny: [...base.permissions.deny, ...overrides.permissions.deny ?? []]
|
|
3735
|
+
} : base.permissions,
|
|
3736
|
+
rules: { ...base.rules, ...overrides.rules }
|
|
3737
|
+
};
|
|
3738
|
+
}
|
|
3739
|
+
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;
|
|
3740
|
+
var init_harness_defaults = __esm({
|
|
3741
|
+
"src/core/harness-defaults.ts"() {
|
|
3742
|
+
"use strict";
|
|
3743
|
+
SCRIPTS_CORE = [
|
|
3744
|
+
{ name: "format", command: "prettier --write .", required: true },
|
|
3745
|
+
{ name: "lint", command: "eslint .", required: true },
|
|
3746
|
+
{ name: "lint:fix", command: "eslint . --fix", required: false },
|
|
3747
|
+
{ name: "typecheck", command: "tsc --noEmit", required: true },
|
|
3748
|
+
{ name: "build", command: "tsup", required: true }
|
|
3749
|
+
];
|
|
3750
|
+
SCRIPTS_TEST_BASIC = [
|
|
3751
|
+
{ name: "test", command: "vitest run", required: true }
|
|
3752
|
+
];
|
|
3753
|
+
SCRIPTS_TEST_FULL = [
|
|
3754
|
+
{ name: "test", command: "vitest run", required: true },
|
|
3755
|
+
{ name: "test:unit", command: "vitest run --project unit", required: true },
|
|
3756
|
+
{ name: "test:integration", command: "vitest run --project integration", required: false },
|
|
3757
|
+
{ name: "test:smoke", command: "npm run build && node scripts/smoke.js", required: false }
|
|
3758
|
+
];
|
|
3759
|
+
SCRIPTS_VERIFY = [
|
|
3760
|
+
{ name: "verify", command: "npm run lint && npm run typecheck && npm run test", required: true },
|
|
3761
|
+
{ name: "verify:full", command: "npm run lint && npm run typecheck && npm run test && npm run build", required: false }
|
|
3762
|
+
];
|
|
3763
|
+
PERMISSIONS_BASE = {
|
|
3764
|
+
allow: [
|
|
3765
|
+
"Bash(npm run lint)",
|
|
3766
|
+
"Bash(npm run lint:fix)",
|
|
3767
|
+
"Bash(npm run format)",
|
|
3768
|
+
"Bash(npm run typecheck)",
|
|
3769
|
+
"Bash(npm run test)",
|
|
3770
|
+
"Bash(npm run build)",
|
|
3771
|
+
"Read(**)",
|
|
3772
|
+
"Edit(**)"
|
|
3773
|
+
],
|
|
3774
|
+
deny: [
|
|
3775
|
+
"Bash(rm -rf /)",
|
|
3776
|
+
"Bash(git push --force)",
|
|
3777
|
+
"Bash(git reset --hard)",
|
|
3778
|
+
"Bash(curl * | sh)",
|
|
3779
|
+
"Bash(npx -y *)"
|
|
3780
|
+
]
|
|
3781
|
+
};
|
|
3782
|
+
PERMISSIONS_PRODUCTION = {
|
|
3783
|
+
allow: [
|
|
3784
|
+
...PERMISSIONS_BASE.allow,
|
|
3785
|
+
"Bash(npm run test:unit)",
|
|
3786
|
+
"Bash(npm run test:integration)",
|
|
3787
|
+
"Bash(npm run verify)",
|
|
3788
|
+
"Bash(npm run verify:full)"
|
|
3789
|
+
],
|
|
3790
|
+
deny: [
|
|
3791
|
+
...PERMISSIONS_BASE.deny,
|
|
3792
|
+
"Bash(npm publish)",
|
|
3793
|
+
"Bash(git push origin main)"
|
|
3794
|
+
]
|
|
3795
|
+
};
|
|
3796
|
+
RULES_PROTOTYPE = {
|
|
3797
|
+
specBeforeCode: false,
|
|
3798
|
+
prMergeRequiresReview: false,
|
|
3799
|
+
branchProtection: false,
|
|
3800
|
+
requireWorkflow: false
|
|
3801
|
+
};
|
|
3802
|
+
RULES_PRODUCTION = {
|
|
3803
|
+
specBeforeCode: true,
|
|
3804
|
+
prMergeRequiresReview: true,
|
|
3805
|
+
branchProtection: true,
|
|
3806
|
+
requireWorkflow: true
|
|
3807
|
+
};
|
|
3808
|
+
RULES_TEAM = {
|
|
3809
|
+
specBeforeCode: true,
|
|
3810
|
+
prMergeRequiresReview: true,
|
|
3811
|
+
branchProtection: true,
|
|
3812
|
+
requireWorkflow: true
|
|
3813
|
+
};
|
|
3814
|
+
PRESET_PROTOTYPE = {
|
|
3815
|
+
version: "1.0",
|
|
3816
|
+
scenario: "prototype",
|
|
3817
|
+
preset: "prototype",
|
|
3818
|
+
gates: {
|
|
3819
|
+
preCommit: {
|
|
3820
|
+
enabled: true,
|
|
3821
|
+
scripts: ["format", "lint"]
|
|
3822
|
+
},
|
|
3823
|
+
prePush: {
|
|
3824
|
+
enabled: false,
|
|
3825
|
+
scripts: []
|
|
3826
|
+
},
|
|
3827
|
+
ci: {
|
|
3828
|
+
enabled: true,
|
|
3829
|
+
scripts: ["lint", "build"],
|
|
3830
|
+
triggers: {
|
|
3831
|
+
pullRequest: true,
|
|
3832
|
+
push: { branches: ["main"] }
|
|
3833
|
+
},
|
|
3834
|
+
nodeVersion: "20"
|
|
3835
|
+
}
|
|
3836
|
+
},
|
|
3837
|
+
scripts: [...SCRIPTS_CORE, ...SCRIPTS_TEST_BASIC],
|
|
3838
|
+
permissions: PERMISSIONS_BASE,
|
|
3839
|
+
rules: RULES_PROTOTYPE
|
|
3840
|
+
};
|
|
3841
|
+
PRESET_PRODUCTION = {
|
|
3842
|
+
version: "1.0",
|
|
3843
|
+
scenario: "production",
|
|
3844
|
+
preset: "production",
|
|
3845
|
+
gates: {
|
|
3846
|
+
preCommit: {
|
|
3847
|
+
enabled: true,
|
|
3848
|
+
scripts: ["format", "lint"]
|
|
3849
|
+
},
|
|
3850
|
+
prePush: {
|
|
3851
|
+
enabled: true,
|
|
3852
|
+
scripts: ["typecheck", "test:unit"]
|
|
3853
|
+
},
|
|
3854
|
+
ci: {
|
|
3855
|
+
enabled: true,
|
|
3856
|
+
scripts: ["lint", "typecheck", "test", "build"],
|
|
3857
|
+
triggers: {
|
|
3858
|
+
pullRequest: true,
|
|
3859
|
+
push: { branches: ["main", "develop"] }
|
|
3860
|
+
},
|
|
3861
|
+
nodeVersion: "20"
|
|
3862
|
+
}
|
|
3863
|
+
},
|
|
3864
|
+
scripts: [...SCRIPTS_CORE, ...SCRIPTS_TEST_FULL, ...SCRIPTS_VERIFY],
|
|
3865
|
+
permissions: PERMISSIONS_PRODUCTION,
|
|
3866
|
+
rules: RULES_PRODUCTION
|
|
3867
|
+
};
|
|
3868
|
+
PRESET_TEAM = {
|
|
3869
|
+
version: "1.0",
|
|
3870
|
+
scenario: "team",
|
|
3871
|
+
preset: "team",
|
|
3872
|
+
gates: {
|
|
3873
|
+
preCommit: {
|
|
3874
|
+
enabled: true,
|
|
3875
|
+
scripts: ["format", "lint"]
|
|
3876
|
+
},
|
|
3877
|
+
prePush: {
|
|
3878
|
+
enabled: true,
|
|
3879
|
+
scripts: ["typecheck", "test:unit"]
|
|
3880
|
+
},
|
|
3881
|
+
ci: {
|
|
3882
|
+
enabled: true,
|
|
3883
|
+
scripts: ["lint", "typecheck", "test", "build"],
|
|
3884
|
+
triggers: {
|
|
3885
|
+
pullRequest: true,
|
|
3886
|
+
push: { branches: ["main", "develop"] }
|
|
3887
|
+
},
|
|
3888
|
+
nodeVersion: "20"
|
|
3889
|
+
}
|
|
3890
|
+
},
|
|
3891
|
+
scripts: [...SCRIPTS_CORE, ...SCRIPTS_TEST_FULL, ...SCRIPTS_VERIFY],
|
|
3892
|
+
permissions: PERMISSIONS_PRODUCTION,
|
|
3893
|
+
rules: RULES_TEAM
|
|
3894
|
+
};
|
|
3895
|
+
HARNESS_PRESETS = {
|
|
3896
|
+
prototype: PRESET_PROTOTYPE,
|
|
3897
|
+
production: PRESET_PRODUCTION,
|
|
3898
|
+
team: PRESET_TEAM
|
|
3899
|
+
};
|
|
3900
|
+
}
|
|
3901
|
+
});
|
|
3902
|
+
|
|
3903
|
+
// src/roboco/generators/harness-json.ts
|
|
3904
|
+
import { writeFile } from "fs/promises";
|
|
3905
|
+
import { existsSync } from "fs";
|
|
3906
|
+
import { join as join6 } from "path";
|
|
3907
|
+
async function generateHarnessJson(opts) {
|
|
3908
|
+
const target = join6(opts.cwd, FILENAME);
|
|
3909
|
+
if (existsSync(target) && !opts.force) {
|
|
3910
|
+
return [{ file: FILENAME, action: "skipped", reason: "already exists" }];
|
|
3911
|
+
}
|
|
3912
|
+
const content = JSON.stringify(opts.config, null, 2) + "\n";
|
|
3913
|
+
await writeFile(target, content, "utf8");
|
|
3914
|
+
return [{ file: FILENAME, action: existsSync(target) ? "updated" : "created" }];
|
|
3915
|
+
}
|
|
3916
|
+
var FILENAME;
|
|
3917
|
+
var init_harness_json = __esm({
|
|
3918
|
+
"src/roboco/generators/harness-json.ts"() {
|
|
3919
|
+
"use strict";
|
|
3920
|
+
FILENAME = ".harness.json";
|
|
3921
|
+
}
|
|
3922
|
+
});
|
|
3923
|
+
|
|
3924
|
+
// src/roboco/generators/scripts.ts
|
|
3925
|
+
import { readFile, writeFile as writeFile2 } from "fs/promises";
|
|
3926
|
+
import { existsSync as existsSync2 } from "fs";
|
|
3927
|
+
import { join as join7 } from "path";
|
|
3928
|
+
async function generateScripts(opts) {
|
|
3929
|
+
const pkgPath = join7(opts.cwd, "package.json");
|
|
3930
|
+
if (!existsSync2(pkgPath)) {
|
|
3931
|
+
return [{ file: "package.json", action: "skipped", reason: "not found" }];
|
|
3932
|
+
}
|
|
3933
|
+
const raw = await readFile(pkgPath, "utf8");
|
|
3934
|
+
const pkg5 = JSON.parse(raw);
|
|
3935
|
+
const existing = pkg5.scripts ?? {};
|
|
3936
|
+
const added = [];
|
|
3937
|
+
for (const req of opts.config.scripts) {
|
|
3938
|
+
if (existing[req.name]) continue;
|
|
3939
|
+
if (!req.required && !opts.force) continue;
|
|
3940
|
+
existing[req.name] = req.command;
|
|
3941
|
+
added.push(req.name);
|
|
3942
|
+
}
|
|
3943
|
+
if (added.length === 0) {
|
|
3944
|
+
return [{ file: "package.json", action: "skipped", reason: "all scripts present" }];
|
|
3945
|
+
}
|
|
3946
|
+
pkg5.scripts = existing;
|
|
3947
|
+
const content = JSON.stringify(pkg5, null, 2) + "\n";
|
|
3948
|
+
await writeFile2(pkgPath, content, "utf8");
|
|
3949
|
+
return [{ file: "package.json", action: "updated", reason: `added: ${added.join(", ")}` }];
|
|
3950
|
+
}
|
|
3951
|
+
var init_scripts = __esm({
|
|
3952
|
+
"src/roboco/generators/scripts.ts"() {
|
|
3953
|
+
"use strict";
|
|
3954
|
+
}
|
|
3955
|
+
});
|
|
3956
|
+
|
|
3957
|
+
// src/roboco/generators/lint-staged.ts
|
|
3958
|
+
import { writeFile as writeFile3 } from "fs/promises";
|
|
3959
|
+
import { existsSync as existsSync3 } from "fs";
|
|
3960
|
+
import { join as join8 } from "path";
|
|
3961
|
+
async function generateLintStaged(opts) {
|
|
3962
|
+
const target = join8(opts.cwd, FILENAME2);
|
|
3963
|
+
if (existsSync3(target) && !opts.force) {
|
|
3964
|
+
return [{ file: FILENAME2, action: "skipped", reason: "already exists" }];
|
|
3965
|
+
}
|
|
3966
|
+
const scripts = opts.config.gates.preCommit.scripts;
|
|
3967
|
+
const hasFormat = scripts.includes("format");
|
|
3968
|
+
const hasLint = scripts.includes("lint");
|
|
3969
|
+
const config = {};
|
|
3970
|
+
if (hasFormat) {
|
|
3971
|
+
config["*.{js,ts,tsx,json,md}"] = ["prettier --write"];
|
|
3972
|
+
}
|
|
3973
|
+
if (hasLint) {
|
|
3974
|
+
config["*.{js,ts,tsx}"] = [
|
|
3975
|
+
...config["*.{js,ts,tsx}"] ?? [],
|
|
3976
|
+
"eslint --fix"
|
|
3977
|
+
];
|
|
3978
|
+
}
|
|
3979
|
+
if (Object.keys(config).length === 0) {
|
|
3980
|
+
return [{ file: FILENAME2, action: "skipped", reason: "no preCommit scripts mapped" }];
|
|
3981
|
+
}
|
|
3982
|
+
const content = JSON.stringify(config, null, 2) + "\n";
|
|
3983
|
+
await writeFile3(target, content, "utf8");
|
|
3984
|
+
return [{ file: FILENAME2, action: "created" }];
|
|
3985
|
+
}
|
|
3986
|
+
var FILENAME2;
|
|
3987
|
+
var init_lint_staged = __esm({
|
|
3988
|
+
"src/roboco/generators/lint-staged.ts"() {
|
|
3989
|
+
"use strict";
|
|
3990
|
+
FILENAME2 = ".lintstagedrc.json";
|
|
3991
|
+
}
|
|
3992
|
+
});
|
|
3993
|
+
|
|
3994
|
+
// src/roboco/generators/husky.ts
|
|
3995
|
+
import { writeFile as writeFile4, mkdir } from "fs/promises";
|
|
3996
|
+
import { existsSync as existsSync4 } from "fs";
|
|
3997
|
+
import { join as join9 } from "path";
|
|
3998
|
+
function buildHookScript(scripts, useLintStaged) {
|
|
3999
|
+
const lines = ["#!/usr/bin/env sh", '. "$(dirname -- "$0")/_/husky.sh"', ""];
|
|
4000
|
+
if (useLintStaged) {
|
|
4001
|
+
lines.push("npx lint-staged");
|
|
4002
|
+
} else {
|
|
4003
|
+
for (const s of scripts) {
|
|
4004
|
+
lines.push(`npm run ${s}`);
|
|
4005
|
+
}
|
|
4006
|
+
}
|
|
4007
|
+
return lines.join("\n") + "\n";
|
|
4008
|
+
}
|
|
4009
|
+
async function generateHusky(opts) {
|
|
4010
|
+
const results = [];
|
|
4011
|
+
const huskyDir = join9(opts.cwd, HUSKY_DIR);
|
|
4012
|
+
await mkdir(huskyDir, { recursive: true });
|
|
4013
|
+
const preCommit = opts.config.gates.preCommit;
|
|
4014
|
+
if (preCommit.enabled) {
|
|
4015
|
+
const file = join9(huskyDir, "pre-commit");
|
|
4016
|
+
const relPath = `${HUSKY_DIR}/pre-commit`;
|
|
4017
|
+
if (existsSync4(file) && !opts.force) {
|
|
4018
|
+
results.push({ file: relPath, action: "skipped", reason: "already exists" });
|
|
4019
|
+
} else {
|
|
4020
|
+
const content = buildHookScript(preCommit.scripts, true);
|
|
4021
|
+
await writeFile4(file, content, { mode: 493 });
|
|
4022
|
+
results.push({ file: relPath, action: "created" });
|
|
4023
|
+
}
|
|
4024
|
+
}
|
|
4025
|
+
const prePush = opts.config.gates.prePush;
|
|
4026
|
+
if (prePush.enabled) {
|
|
4027
|
+
const file = join9(huskyDir, "pre-push");
|
|
4028
|
+
const relPath = `${HUSKY_DIR}/pre-push`;
|
|
4029
|
+
if (existsSync4(file) && !opts.force) {
|
|
4030
|
+
results.push({ file: relPath, action: "skipped", reason: "already exists" });
|
|
4031
|
+
} else {
|
|
4032
|
+
const content = buildHookScript(prePush.scripts, false);
|
|
4033
|
+
await writeFile4(file, content, { mode: 493 });
|
|
4034
|
+
results.push({ file: relPath, action: "created" });
|
|
4035
|
+
}
|
|
4036
|
+
}
|
|
4037
|
+
return results;
|
|
4038
|
+
}
|
|
4039
|
+
var HUSKY_DIR;
|
|
4040
|
+
var init_husky = __esm({
|
|
4041
|
+
"src/roboco/generators/husky.ts"() {
|
|
4042
|
+
"use strict";
|
|
4043
|
+
HUSKY_DIR = ".husky";
|
|
4044
|
+
}
|
|
4045
|
+
});
|
|
4046
|
+
|
|
4047
|
+
// src/roboco/generators/ci-workflow.ts
|
|
4048
|
+
import { writeFile as writeFile5, mkdir as mkdir2 } from "fs/promises";
|
|
4049
|
+
import { existsSync as existsSync5 } from "fs";
|
|
4050
|
+
import { join as join10 } from "path";
|
|
4051
|
+
function buildWorkflow(opts) {
|
|
4052
|
+
const ci = opts.config.gates.ci;
|
|
4053
|
+
const triggers = [];
|
|
4054
|
+
if (ci.triggers.pullRequest) {
|
|
4055
|
+
triggers.push(" pull_request:");
|
|
4056
|
+
}
|
|
4057
|
+
if (ci.triggers.push.branches.length > 0) {
|
|
4058
|
+
triggers.push(" push:");
|
|
4059
|
+
triggers.push(` branches: [${ci.triggers.push.branches.join(", ")}]`);
|
|
4060
|
+
}
|
|
4061
|
+
const steps = [
|
|
4062
|
+
" - uses: actions/checkout@v4",
|
|
4063
|
+
" - uses: actions/setup-node@v4",
|
|
4064
|
+
" with:",
|
|
4065
|
+
` node-version: ${ci.nodeVersion}`,
|
|
4066
|
+
" - run: npm ci"
|
|
4067
|
+
];
|
|
4068
|
+
for (const script of ci.scripts) {
|
|
4069
|
+
steps.push(` - run: npm run ${script}`);
|
|
4070
|
+
}
|
|
4071
|
+
return [
|
|
4072
|
+
"name: CI",
|
|
4073
|
+
"",
|
|
4074
|
+
"on:",
|
|
4075
|
+
...triggers,
|
|
4076
|
+
"",
|
|
4077
|
+
"jobs:",
|
|
4078
|
+
" verify:",
|
|
4079
|
+
" runs-on: ubuntu-latest",
|
|
4080
|
+
" steps:",
|
|
4081
|
+
...steps,
|
|
4082
|
+
""
|
|
4083
|
+
].join("\n");
|
|
4084
|
+
}
|
|
4085
|
+
async function generateCIWorkflow(opts) {
|
|
4086
|
+
if (!opts.config.gates.ci.enabled) {
|
|
4087
|
+
return [{ file: REL_PATH, action: "skipped", reason: "CI gate disabled" }];
|
|
4088
|
+
}
|
|
4089
|
+
const target = join10(opts.cwd, REL_PATH);
|
|
4090
|
+
if (existsSync5(target) && !opts.force) {
|
|
4091
|
+
return [{ file: REL_PATH, action: "skipped", reason: "already exists" }];
|
|
4092
|
+
}
|
|
4093
|
+
await mkdir2(join10(opts.cwd, ".github", "workflows"), { recursive: true });
|
|
4094
|
+
const content = buildWorkflow(opts);
|
|
4095
|
+
await writeFile5(target, content, "utf8");
|
|
4096
|
+
return [{ file: REL_PATH, action: "created" }];
|
|
4097
|
+
}
|
|
4098
|
+
var REL_PATH;
|
|
4099
|
+
var init_ci_workflow = __esm({
|
|
4100
|
+
"src/roboco/generators/ci-workflow.ts"() {
|
|
4101
|
+
"use strict";
|
|
4102
|
+
REL_PATH = ".github/workflows/ci.yml";
|
|
4103
|
+
}
|
|
4104
|
+
});
|
|
4105
|
+
|
|
4106
|
+
// src/roboco/generators/claude-settings.ts
|
|
4107
|
+
import { readFile as readFile2, writeFile as writeFile6, mkdir as mkdir3 } from "fs/promises";
|
|
4108
|
+
import { existsSync as existsSync6 } from "fs";
|
|
4109
|
+
import { join as join11 } from "path";
|
|
4110
|
+
function dedup(arr) {
|
|
4111
|
+
return [...new Set(arr)];
|
|
4112
|
+
}
|
|
4113
|
+
async function generateClaudeSettings2(opts) {
|
|
4114
|
+
const target = join11(opts.cwd, REL_PATH2);
|
|
4115
|
+
await mkdir3(join11(opts.cwd, ".claude"), { recursive: true });
|
|
4116
|
+
let settings = {};
|
|
4117
|
+
if (existsSync6(target)) {
|
|
4118
|
+
try {
|
|
4119
|
+
const raw = await readFile2(target, "utf8");
|
|
4120
|
+
settings = JSON.parse(raw.replace(/^\uFEFF/, ""));
|
|
4121
|
+
} catch {
|
|
4122
|
+
}
|
|
4123
|
+
}
|
|
4124
|
+
const existing = settings.permissions ?? {};
|
|
4125
|
+
const merged = {
|
|
4126
|
+
allow: dedup([...existing.allow ?? [], ...opts.config.permissions.allow]),
|
|
4127
|
+
deny: dedup([...existing.deny ?? [], ...opts.config.permissions.deny])
|
|
4128
|
+
};
|
|
4129
|
+
settings.permissions = merged;
|
|
4130
|
+
const content = JSON.stringify(settings, null, 2) + "\n";
|
|
4131
|
+
await writeFile6(target, content, "utf8");
|
|
4132
|
+
return [{ file: REL_PATH2, action: existsSync6(target) ? "updated" : "created" }];
|
|
4133
|
+
}
|
|
4134
|
+
var REL_PATH2;
|
|
4135
|
+
var init_claude_settings = __esm({
|
|
4136
|
+
"src/roboco/generators/claude-settings.ts"() {
|
|
4137
|
+
"use strict";
|
|
4138
|
+
REL_PATH2 = ".claude/settings.json";
|
|
4139
|
+
}
|
|
4140
|
+
});
|
|
4141
|
+
|
|
4142
|
+
// src/roboco/generators/claude-md.ts
|
|
4143
|
+
import { readFile as readFile3, writeFile as writeFile7 } from "fs/promises";
|
|
4144
|
+
import { existsSync as existsSync7 } from "fs";
|
|
4145
|
+
import { join as join12 } from "path";
|
|
4146
|
+
function buildRulesBlock(opts) {
|
|
4147
|
+
const { rules, scenario } = opts.config;
|
|
4148
|
+
const lines = [
|
|
4149
|
+
MARKER_START,
|
|
4150
|
+
`## Harness Rules (${scenario})`,
|
|
4151
|
+
""
|
|
4152
|
+
];
|
|
4153
|
+
if (rules.specBeforeCode) {
|
|
4154
|
+
lines.push("- spec \uC5C6\uB294 \uAE30\uB2A5 \uAD6C\uD604 \uAE08\uC9C0 \u2014 \uBC18\uB4DC\uC2DC OpenSpec \uBA3C\uC800 \uC791\uC131");
|
|
4155
|
+
}
|
|
4156
|
+
if (rules.requireWorkflow) {
|
|
4157
|
+
lines.push("- \uD070 \uBCC0\uACBD\uC740 plan \u2192 implement \u2192 review \u2192 verify \uC21C\uC11C\uB85C \uC9C4\uD589");
|
|
4158
|
+
}
|
|
4159
|
+
if (rules.prMergeRequiresReview) {
|
|
4160
|
+
lines.push("- PR \uBA38\uC9C0 \uC804 \uB9AC\uBDF0 \uD544\uC218");
|
|
4161
|
+
}
|
|
4162
|
+
if (rules.branchProtection) {
|
|
4163
|
+
lines.push("- main/develop \uBE0C\uB79C\uCE58 \uC9C1\uC811 push \uAE08\uC9C0");
|
|
4164
|
+
}
|
|
4165
|
+
lines.push("- pre-commit/pre-push/CI\uB97C \uD1B5\uACFC\uD558\uC9C0 \uBABB\uD558\uBA74 \uC644\uB8CC\uB85C \uBCF4\uC9C0 \uC54A\uC74C");
|
|
4166
|
+
lines.push("- \uAE30\uC874 script \uC678 \uC784\uC758 \uBA85\uB839 \uCD5C\uC18C\uD654 \u2014 package.json scripts\uB97C \uC0AC\uC6A9");
|
|
4167
|
+
lines.push("");
|
|
4168
|
+
lines.push(MARKER_END);
|
|
4169
|
+
return lines.join("\n");
|
|
4170
|
+
}
|
|
4171
|
+
async function generateClaudeMd2(opts) {
|
|
4172
|
+
const target = join12(opts.cwd, FILENAME3);
|
|
4173
|
+
const block = buildRulesBlock(opts);
|
|
4174
|
+
if (!existsSync7(target)) {
|
|
4175
|
+
const content2 = `# Project Rules
|
|
4176
|
+
|
|
4177
|
+
${block}
|
|
4178
|
+
`;
|
|
4179
|
+
await writeFile7(target, content2, "utf8");
|
|
4180
|
+
return [{ file: FILENAME3, action: "created" }];
|
|
4181
|
+
}
|
|
4182
|
+
let content = await readFile3(target, "utf8");
|
|
4183
|
+
const startIdx = content.indexOf(MARKER_START);
|
|
4184
|
+
const endIdx = content.indexOf(MARKER_END);
|
|
4185
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
4186
|
+
content = content.slice(0, startIdx) + block + content.slice(endIdx + MARKER_END.length);
|
|
4187
|
+
} else {
|
|
4188
|
+
content = content.trimEnd() + "\n\n" + block + "\n";
|
|
4189
|
+
}
|
|
4190
|
+
await writeFile7(target, content, "utf8");
|
|
4191
|
+
return [{ file: FILENAME3, action: "updated" }];
|
|
4192
|
+
}
|
|
4193
|
+
var FILENAME3, MARKER_START, MARKER_END;
|
|
4194
|
+
var init_claude_md = __esm({
|
|
4195
|
+
"src/roboco/generators/claude-md.ts"() {
|
|
4196
|
+
"use strict";
|
|
4197
|
+
FILENAME3 = "CLAUDE.md";
|
|
4198
|
+
MARKER_START = "<!-- harness:start -->";
|
|
4199
|
+
MARKER_END = "<!-- harness:end -->";
|
|
4200
|
+
}
|
|
4201
|
+
});
|
|
4202
|
+
|
|
4203
|
+
// src/roboco/generators/index.ts
|
|
4204
|
+
var init_generators = __esm({
|
|
4205
|
+
"src/roboco/generators/index.ts"() {
|
|
4206
|
+
"use strict";
|
|
4207
|
+
init_harness_json();
|
|
4208
|
+
init_scripts();
|
|
4209
|
+
init_lint_staged();
|
|
4210
|
+
init_husky();
|
|
4211
|
+
init_ci_workflow();
|
|
4212
|
+
init_claude_settings();
|
|
4213
|
+
init_claude_md();
|
|
4214
|
+
}
|
|
4215
|
+
});
|
|
4216
|
+
|
|
4217
|
+
// src/roboco/scaffold.ts
|
|
4218
|
+
async function scaffold(opts) {
|
|
4219
|
+
const scenario = opts.scenario ?? "prototype";
|
|
4220
|
+
const config = resolveConfig({ scenario, overrides: opts.overrides });
|
|
4221
|
+
const generatorOpts = {
|
|
4222
|
+
cwd: opts.cwd,
|
|
4223
|
+
config,
|
|
4224
|
+
force: opts.force
|
|
4225
|
+
};
|
|
4226
|
+
const files = [];
|
|
4227
|
+
for (const gen of GENERATORS) {
|
|
4228
|
+
const results = await gen(generatorOpts);
|
|
4229
|
+
files.push(...results);
|
|
4230
|
+
}
|
|
4231
|
+
const summary = {
|
|
4232
|
+
created: files.filter((f) => f.action === "created").length,
|
|
4233
|
+
updated: files.filter((f) => f.action === "updated").length,
|
|
4234
|
+
skipped: files.filter((f) => f.action === "skipped").length
|
|
4235
|
+
};
|
|
4236
|
+
return { scenario, config, files, summary };
|
|
4237
|
+
}
|
|
4238
|
+
var GENERATORS;
|
|
4239
|
+
var init_scaffold = __esm({
|
|
4240
|
+
"src/roboco/scaffold.ts"() {
|
|
4241
|
+
"use strict";
|
|
4242
|
+
init_harness_defaults();
|
|
4243
|
+
init_generators();
|
|
4244
|
+
GENERATORS = [
|
|
4245
|
+
generateHarnessJson,
|
|
4246
|
+
generateScripts,
|
|
4247
|
+
generateLintStaged,
|
|
4248
|
+
generateHusky,
|
|
4249
|
+
generateCIWorkflow,
|
|
4250
|
+
generateClaudeSettings2,
|
|
4251
|
+
generateClaudeMd2
|
|
4252
|
+
];
|
|
4253
|
+
}
|
|
4254
|
+
});
|
|
4255
|
+
|
|
3608
4256
|
// src/core/roboco.ts
|
|
3609
4257
|
import os2 from "os";
|
|
3610
4258
|
import path5 from "path";
|
|
@@ -4144,7 +4792,7 @@ var doctor_exports = {};
|
|
|
4144
4792
|
__export(doctor_exports, {
|
|
4145
4793
|
runDoctor: () => runDoctor
|
|
4146
4794
|
});
|
|
4147
|
-
import { join as
|
|
4795
|
+
import { join as join13 } from "path";
|
|
4148
4796
|
import { homedir } from "os";
|
|
4149
4797
|
import fs12 from "fs-extra";
|
|
4150
4798
|
async function runDoctor() {
|
|
@@ -4165,7 +4813,7 @@ async function runDoctor() {
|
|
|
4165
4813
|
detail: claudeCheck.detail,
|
|
4166
4814
|
fix: claudeCheck.ok ? void 0 : "npm install -g @anthropic-ai/claude-code"
|
|
4167
4815
|
});
|
|
4168
|
-
const globalConfigPath =
|
|
4816
|
+
const globalConfigPath = join13(homedir(), ".pai", "config.json");
|
|
4169
4817
|
const hasGlobalConfig = await fs12.pathExists(globalConfigPath);
|
|
4170
4818
|
checks.push({
|
|
4171
4819
|
label: "\uAE00\uB85C\uBC8C \uC124\uC815",
|
|
@@ -4543,6 +5191,16 @@ var init_fetch_cmd = __esm({
|
|
|
4543
5191
|
});
|
|
4544
5192
|
|
|
4545
5193
|
// src/stages/environment/index.ts
|
|
5194
|
+
function modeToScenario(mode) {
|
|
5195
|
+
switch (mode) {
|
|
5196
|
+
case "prototype":
|
|
5197
|
+
return "prototype";
|
|
5198
|
+
case "poc":
|
|
5199
|
+
return "production";
|
|
5200
|
+
case "production":
|
|
5201
|
+
return "production";
|
|
5202
|
+
}
|
|
5203
|
+
}
|
|
4546
5204
|
var environmentStage;
|
|
4547
5205
|
var init_environment = __esm({
|
|
4548
5206
|
"src/stages/environment/index.ts"() {
|
|
@@ -4555,6 +5213,7 @@ var init_environment = __esm({
|
|
|
4555
5213
|
init_claude_commands();
|
|
4556
5214
|
init_detector();
|
|
4557
5215
|
init_config();
|
|
5216
|
+
init_scaffold();
|
|
4558
5217
|
init_progress();
|
|
4559
5218
|
init_ui();
|
|
4560
5219
|
init_analyzer();
|
|
@@ -4673,6 +5332,21 @@ var init_environment = __esm({
|
|
|
4673
5332
|
}
|
|
4674
5333
|
}
|
|
4675
5334
|
console.log("");
|
|
5335
|
+
const scaffoldResult = await withSpinner("Harness \uC815\uCC45 \uC124\uC815 \uC911...", async () => {
|
|
5336
|
+
const result = await scaffold({
|
|
5337
|
+
cwd: input.cwd,
|
|
5338
|
+
scenario: modeToScenario(interview.mode)
|
|
5339
|
+
});
|
|
5340
|
+
await sleep(300);
|
|
5341
|
+
return result;
|
|
5342
|
+
});
|
|
5343
|
+
if (scaffoldResult.summary.created > 0 || scaffoldResult.summary.updated > 0) {
|
|
5344
|
+
const parts = [];
|
|
5345
|
+
if (scaffoldResult.summary.created > 0) parts.push(`${scaffoldResult.summary.created}\uAC1C \uC0DD\uC131`);
|
|
5346
|
+
if (scaffoldResult.summary.updated > 0) parts.push(`${scaffoldResult.summary.updated}\uAC1C \uC218\uC815`);
|
|
5347
|
+
success(`Harness: ${parts.join(", ")}`);
|
|
5348
|
+
}
|
|
5349
|
+
console.log("");
|
|
4676
5350
|
await withSpinner("\uC124\uC815 \uC800\uC7A5 \uC911...", async () => {
|
|
4677
5351
|
const config = createDefaultConfig(interview.projectName, interview.mode);
|
|
4678
5352
|
config.plugins = pluginKeys;
|
|
@@ -4707,11 +5381,11 @@ var init_environment = __esm({
|
|
|
4707
5381
|
});
|
|
4708
5382
|
|
|
4709
5383
|
// src/stages/validation/runner.ts
|
|
4710
|
-
import { join as
|
|
5384
|
+
import { join as join14 } from "path";
|
|
4711
5385
|
import fs15 from "fs-extra";
|
|
4712
5386
|
async function runTests(cwd) {
|
|
4713
5387
|
const start = Date.now();
|
|
4714
|
-
const gstackPath =
|
|
5388
|
+
const gstackPath = join14(cwd, ".pai", "gstack.json");
|
|
4715
5389
|
let runner = "npm test";
|
|
4716
5390
|
if (await fs15.pathExists(gstackPath)) {
|
|
4717
5391
|
try {
|
|
@@ -4722,7 +5396,7 @@ async function runTests(cwd) {
|
|
|
4722
5396
|
} catch {
|
|
4723
5397
|
}
|
|
4724
5398
|
}
|
|
4725
|
-
const pkgPath =
|
|
5399
|
+
const pkgPath = join14(cwd, "package.json");
|
|
4726
5400
|
if (await fs15.pathExists(pkgPath)) {
|
|
4727
5401
|
try {
|
|
4728
5402
|
const pkg5 = await fs15.readJson(pkgPath);
|
|
@@ -4767,10 +5441,10 @@ var init_runner = __esm({
|
|
|
4767
5441
|
});
|
|
4768
5442
|
|
|
4769
5443
|
// src/stages/validation/harness.ts
|
|
4770
|
-
import { join as
|
|
5444
|
+
import { join as join15 } from "path";
|
|
4771
5445
|
import fs16 from "fs-extra";
|
|
4772
5446
|
async function runHarnessCheck(cwd) {
|
|
4773
|
-
const harnessPath =
|
|
5447
|
+
const harnessPath = join15(cwd, ".pai", "harness.json");
|
|
4774
5448
|
if (!await fs16.pathExists(harnessPath)) {
|
|
4775
5449
|
return { enabled: false, specFile: null, rules: [], checks: [] };
|
|
4776
5450
|
}
|
|
@@ -4784,8 +5458,8 @@ async function runHarnessCheck(cwd) {
|
|
|
4784
5458
|
const rules = config.rules ?? [];
|
|
4785
5459
|
const checks = [];
|
|
4786
5460
|
if (rules.includes("spec-implementation-match")) {
|
|
4787
|
-
const specExists = await fs16.pathExists(
|
|
4788
|
-
const srcExists = await fs16.pathExists(
|
|
5461
|
+
const specExists = await fs16.pathExists(join15(cwd, specFile));
|
|
5462
|
+
const srcExists = await fs16.pathExists(join15(cwd, "src"));
|
|
4789
5463
|
checks.push({
|
|
4790
5464
|
rule: "spec-implementation-match",
|
|
4791
5465
|
passed: specExists && srcExists,
|
|
@@ -4793,8 +5467,8 @@ async function runHarnessCheck(cwd) {
|
|
|
4793
5467
|
});
|
|
4794
5468
|
}
|
|
4795
5469
|
if (rules.includes("api-contract-test")) {
|
|
4796
|
-
const testDir = await fs16.pathExists(
|
|
4797
|
-
const testDir2 = await fs16.pathExists(
|
|
5470
|
+
const testDir = await fs16.pathExists(join15(cwd, "tests"));
|
|
5471
|
+
const testDir2 = await fs16.pathExists(join15(cwd, "test"));
|
|
4798
5472
|
checks.push({
|
|
4799
5473
|
rule: "api-contract-test",
|
|
4800
5474
|
passed: testDir || testDir2,
|
|
@@ -4951,12 +5625,189 @@ var init_analyze = __esm({
|
|
|
4951
5625
|
}
|
|
4952
5626
|
});
|
|
4953
5627
|
|
|
5628
|
+
// src/vibe-ready/evaluator.ts
|
|
5629
|
+
var evaluator_exports = {};
|
|
5630
|
+
__export(evaluator_exports, {
|
|
5631
|
+
evaluateHarness: () => evaluateHarness
|
|
5632
|
+
});
|
|
5633
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
5634
|
+
import { existsSync as existsSync8 } from "fs";
|
|
5635
|
+
import { join as join16 } from "path";
|
|
5636
|
+
async function evaluateHarness(cwd) {
|
|
5637
|
+
const configPath = join16(cwd, ".harness.json");
|
|
5638
|
+
if (!existsSync8(configPath)) {
|
|
5639
|
+
return {
|
|
5640
|
+
configFound: false,
|
|
5641
|
+
scenario: null,
|
|
5642
|
+
gates: [],
|
|
5643
|
+
missingScripts: [],
|
|
5644
|
+
missingFiles: [],
|
|
5645
|
+
score: 0
|
|
5646
|
+
};
|
|
5647
|
+
}
|
|
5648
|
+
let config;
|
|
5649
|
+
try {
|
|
5650
|
+
const raw = await readFile4(configPath, "utf8");
|
|
5651
|
+
config = JSON.parse(raw);
|
|
5652
|
+
} catch {
|
|
5653
|
+
return {
|
|
5654
|
+
configFound: false,
|
|
5655
|
+
scenario: null,
|
|
5656
|
+
gates: [],
|
|
5657
|
+
missingScripts: [],
|
|
5658
|
+
missingFiles: [],
|
|
5659
|
+
score: 0
|
|
5660
|
+
};
|
|
5661
|
+
}
|
|
5662
|
+
const gates = [];
|
|
5663
|
+
const missingScripts = [];
|
|
5664
|
+
const missingFiles = [];
|
|
5665
|
+
let score = 10;
|
|
5666
|
+
gates.push(await evaluateGate(cwd, config, "preCommit", [".husky/pre-commit", ".lintstagedrc.json"]));
|
|
5667
|
+
if (gates[gates.length - 1].status === "pass") score += 15;
|
|
5668
|
+
else missingFiles.push(...gates[gates.length - 1].findings.filter((f) => f.startsWith("missing:")).map((f) => f.slice(8)));
|
|
5669
|
+
gates.push(await evaluateGate(cwd, config, "prePush", [".husky/pre-push"]));
|
|
5670
|
+
if (gates[gates.length - 1].status === "pass") score += 15;
|
|
5671
|
+
else if (gates[gates.length - 1].status === "skip") score += 15;
|
|
5672
|
+
gates.push(await evaluateGate(cwd, config, "ci", [".github/workflows/ci.yml"]));
|
|
5673
|
+
if (gates[gates.length - 1].status === "pass") score += 20;
|
|
5674
|
+
const scriptScore = await evaluateScripts(cwd, config, missingScripts);
|
|
5675
|
+
score += scriptScore;
|
|
5676
|
+
const permScore = await evaluatePermissions(cwd, config);
|
|
5677
|
+
score += permScore;
|
|
5678
|
+
const rulesScore = await evaluateRules(cwd, config);
|
|
5679
|
+
score += rulesScore;
|
|
5680
|
+
return {
|
|
5681
|
+
configFound: true,
|
|
5682
|
+
scenario: config.scenario,
|
|
5683
|
+
gates,
|
|
5684
|
+
missingScripts,
|
|
5685
|
+
missingFiles,
|
|
5686
|
+
score: Math.min(100, score)
|
|
5687
|
+
};
|
|
5688
|
+
}
|
|
5689
|
+
async function evaluateGate(cwd, config, gateName, expectedFiles) {
|
|
5690
|
+
const gate = config.gates[gateName];
|
|
5691
|
+
if (!gate.enabled) {
|
|
5692
|
+
return { gate: gateName, status: "skip", findings: ["gate disabled"] };
|
|
5693
|
+
}
|
|
5694
|
+
const findings = [];
|
|
5695
|
+
let allPresent = true;
|
|
5696
|
+
for (const file of expectedFiles) {
|
|
5697
|
+
if (!existsSync8(join16(cwd, file))) {
|
|
5698
|
+
findings.push(`missing:${file}`);
|
|
5699
|
+
allPresent = false;
|
|
5700
|
+
}
|
|
5701
|
+
}
|
|
5702
|
+
const pkg5 = await readPackageJson(cwd);
|
|
5703
|
+
if (pkg5) {
|
|
5704
|
+
for (const script of gate.scripts) {
|
|
5705
|
+
if (!pkg5.scripts?.[script]) {
|
|
5706
|
+
findings.push(`missing script: ${script}`);
|
|
5707
|
+
allPresent = false;
|
|
5708
|
+
}
|
|
5709
|
+
}
|
|
5710
|
+
}
|
|
5711
|
+
return {
|
|
5712
|
+
gate: gateName,
|
|
5713
|
+
status: allPresent ? "pass" : "fail",
|
|
5714
|
+
findings
|
|
5715
|
+
};
|
|
5716
|
+
}
|
|
5717
|
+
async function evaluateScripts(cwd, config, missingScripts) {
|
|
5718
|
+
const pkg5 = await readPackageJson(cwd);
|
|
5719
|
+
if (!pkg5) return 0;
|
|
5720
|
+
const required = config.scripts.filter((s) => s.required);
|
|
5721
|
+
if (required.length === 0) return 20;
|
|
5722
|
+
let found = 0;
|
|
5723
|
+
for (const req of required) {
|
|
5724
|
+
if (pkg5.scripts?.[req.name]) {
|
|
5725
|
+
found++;
|
|
5726
|
+
} else {
|
|
5727
|
+
missingScripts.push(req.name);
|
|
5728
|
+
}
|
|
5729
|
+
}
|
|
5730
|
+
return Math.round(found / required.length * 20);
|
|
5731
|
+
}
|
|
5732
|
+
async function evaluatePermissions(cwd, config) {
|
|
5733
|
+
const settingsPath = join16(cwd, ".claude", "settings.json");
|
|
5734
|
+
if (!existsSync8(settingsPath)) return 0;
|
|
5735
|
+
try {
|
|
5736
|
+
const raw = await readFile4(settingsPath, "utf8");
|
|
5737
|
+
const settings = JSON.parse(raw.replace(/^\uFEFF/, ""));
|
|
5738
|
+
const perms = settings.permissions;
|
|
5739
|
+
if (!perms) return 0;
|
|
5740
|
+
let matchCount = 0;
|
|
5741
|
+
let totalChecks = 0;
|
|
5742
|
+
for (const deny of config.permissions.deny) {
|
|
5743
|
+
totalChecks++;
|
|
5744
|
+
if (perms.deny?.includes(deny)) matchCount++;
|
|
5745
|
+
}
|
|
5746
|
+
for (const allow of config.permissions.allow) {
|
|
5747
|
+
totalChecks++;
|
|
5748
|
+
if (perms.allow?.includes(allow)) matchCount++;
|
|
5749
|
+
}
|
|
5750
|
+
if (totalChecks === 0) return 10;
|
|
5751
|
+
return Math.round(matchCount / totalChecks * 10);
|
|
5752
|
+
} catch {
|
|
5753
|
+
return 0;
|
|
5754
|
+
}
|
|
5755
|
+
}
|
|
5756
|
+
async function evaluateRules(cwd, config) {
|
|
5757
|
+
const claudeMdPath = join16(cwd, "CLAUDE.md");
|
|
5758
|
+
if (!existsSync8(claudeMdPath)) return 0;
|
|
5759
|
+
try {
|
|
5760
|
+
const content = await readFile4(claudeMdPath, "utf8");
|
|
5761
|
+
let matchCount = 0;
|
|
5762
|
+
let totalRules = 0;
|
|
5763
|
+
if (config.rules.specBeforeCode) {
|
|
5764
|
+
totalRules++;
|
|
5765
|
+
if (content.includes("spec") && content.includes("\uAE08\uC9C0")) matchCount++;
|
|
5766
|
+
}
|
|
5767
|
+
if (config.rules.requireWorkflow) {
|
|
5768
|
+
totalRules++;
|
|
5769
|
+
if (content.includes("plan") && content.includes("review")) matchCount++;
|
|
5770
|
+
}
|
|
5771
|
+
if (config.rules.prMergeRequiresReview) {
|
|
5772
|
+
totalRules++;
|
|
5773
|
+
if (content.includes("\uB9AC\uBDF0") || content.includes("review")) matchCount++;
|
|
5774
|
+
}
|
|
5775
|
+
if (config.rules.branchProtection) {
|
|
5776
|
+
totalRules++;
|
|
5777
|
+
if (content.includes("main") || content.includes("\uBE0C\uB79C\uCE58")) matchCount++;
|
|
5778
|
+
}
|
|
5779
|
+
if (content.includes("<!-- harness:start -->")) {
|
|
5780
|
+
matchCount++;
|
|
5781
|
+
totalRules++;
|
|
5782
|
+
}
|
|
5783
|
+
if (totalRules === 0) return 10;
|
|
5784
|
+
return Math.round(matchCount / totalRules * 10);
|
|
5785
|
+
} catch {
|
|
5786
|
+
return 0;
|
|
5787
|
+
}
|
|
5788
|
+
}
|
|
5789
|
+
async function readPackageJson(cwd) {
|
|
5790
|
+
const pkgPath = join16(cwd, "package.json");
|
|
5791
|
+
if (!existsSync8(pkgPath)) return null;
|
|
5792
|
+
try {
|
|
5793
|
+
const raw = await readFile4(pkgPath, "utf8");
|
|
5794
|
+
return JSON.parse(raw);
|
|
5795
|
+
} catch {
|
|
5796
|
+
return null;
|
|
5797
|
+
}
|
|
5798
|
+
}
|
|
5799
|
+
var init_evaluator = __esm({
|
|
5800
|
+
"src/vibe-ready/evaluator.ts"() {
|
|
5801
|
+
"use strict";
|
|
5802
|
+
}
|
|
5803
|
+
});
|
|
5804
|
+
|
|
4954
5805
|
// src/stages/evaluation/analyzer.ts
|
|
4955
5806
|
var analyzer_exports2 = {};
|
|
4956
5807
|
__export(analyzer_exports2, {
|
|
4957
5808
|
analyzeRepository: () => analyzeRepository
|
|
4958
5809
|
});
|
|
4959
|
-
import { join as
|
|
5810
|
+
import { join as join17 } from "path";
|
|
4960
5811
|
import fs17 from "fs-extra";
|
|
4961
5812
|
async function analyzeRepository(repoPath) {
|
|
4962
5813
|
try {
|
|
@@ -5018,14 +5869,14 @@ async function checkTestCoverage(repoPath) {
|
|
|
5018
5869
|
".nycrc"
|
|
5019
5870
|
];
|
|
5020
5871
|
for (const f of testConfigs) {
|
|
5021
|
-
const found = await fs17.pathExists(
|
|
5872
|
+
const found = await fs17.pathExists(join17(repoPath, f));
|
|
5022
5873
|
findings.push({ item: f, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
5023
5874
|
if (found) score += 20;
|
|
5024
5875
|
}
|
|
5025
5876
|
const testDirs = ["tests", "test", "__tests__", "spec"];
|
|
5026
5877
|
let hasTestDir = false;
|
|
5027
5878
|
for (const d of testDirs) {
|
|
5028
|
-
if (await fs17.pathExists(
|
|
5879
|
+
if (await fs17.pathExists(join17(repoPath, d))) {
|
|
5029
5880
|
findings.push({ item: d, found: true, details: "\uD14C\uC2A4\uD2B8 \uB514\uB809\uD1A0\uB9AC \uC874\uC7AC" });
|
|
5030
5881
|
hasTestDir = true;
|
|
5031
5882
|
score += 30;
|
|
@@ -5049,7 +5900,7 @@ async function checkCiCd(repoPath) {
|
|
|
5049
5900
|
{ path: ".circleci", label: "CircleCI" }
|
|
5050
5901
|
];
|
|
5051
5902
|
for (const { path: path10, label } of ciConfigs) {
|
|
5052
|
-
const found = await fs17.pathExists(
|
|
5903
|
+
const found = await fs17.pathExists(join17(repoPath, path10));
|
|
5053
5904
|
findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
5054
5905
|
if (found) score += 40;
|
|
5055
5906
|
}
|
|
@@ -5068,7 +5919,7 @@ async function checkHooks(repoPath) {
|
|
|
5068
5919
|
{ path: ".claude/settings.json", label: "Claude Code settings" }
|
|
5069
5920
|
];
|
|
5070
5921
|
for (const { path: path10, label } of hookConfigs) {
|
|
5071
|
-
const found = await fs17.pathExists(
|
|
5922
|
+
const found = await fs17.pathExists(join17(repoPath, path10));
|
|
5072
5923
|
findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
5073
5924
|
if (found) score += 20;
|
|
5074
5925
|
}
|
|
@@ -5086,7 +5937,7 @@ async function checkRepoStructure(repoPath) {
|
|
|
5086
5937
|
{ path: ".gitignore", label: ".gitignore" }
|
|
5087
5938
|
];
|
|
5088
5939
|
for (const { path: path10, label } of structureChecks) {
|
|
5089
|
-
const found = await fs17.pathExists(
|
|
5940
|
+
const found = await fs17.pathExists(join17(repoPath, path10));
|
|
5090
5941
|
findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
5091
5942
|
if (found) score += 25;
|
|
5092
5943
|
}
|
|
@@ -5103,7 +5954,7 @@ async function checkDocumentation(repoPath) {
|
|
|
5103
5954
|
{ path: "docs/openspec.md", label: "OpenSpec PRD", points: 25 }
|
|
5104
5955
|
];
|
|
5105
5956
|
for (const { path: path10, label, points } of docChecks) {
|
|
5106
|
-
const found = await fs17.pathExists(
|
|
5957
|
+
const found = await fs17.pathExists(join17(repoPath, path10));
|
|
5107
5958
|
findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
5108
5959
|
if (found) score += points;
|
|
5109
5960
|
}
|
|
@@ -5111,6 +5962,54 @@ async function checkDocumentation(repoPath) {
|
|
|
5111
5962
|
return { name: "\uBB38\uC11C\uD654 \uC218\uC900", tier: "nice", score, recommendations: [], rawFindings: findings };
|
|
5112
5963
|
}
|
|
5113
5964
|
async function checkHarnessEngineering(repoPath) {
|
|
5965
|
+
if (await fs17.pathExists(join17(repoPath, ".harness.json"))) {
|
|
5966
|
+
return checkHarnessWithPolicy(repoPath);
|
|
5967
|
+
}
|
|
5968
|
+
return checkHarnessLegacy(repoPath);
|
|
5969
|
+
}
|
|
5970
|
+
async function checkHarnessWithPolicy(repoPath) {
|
|
5971
|
+
const { evaluateHarness: evaluateHarness2 } = await Promise.resolve().then(() => (init_evaluator(), evaluator_exports));
|
|
5972
|
+
const evaluation = await evaluateHarness2(repoPath);
|
|
5973
|
+
const findings = [];
|
|
5974
|
+
findings.push({
|
|
5975
|
+
item: ".harness.json",
|
|
5976
|
+
found: evaluation.configFound,
|
|
5977
|
+
details: evaluation.configFound ? `\uC2DC\uB098\uB9AC\uC624: ${evaluation.scenario}` : "\uC5C6\uC74C"
|
|
5978
|
+
});
|
|
5979
|
+
for (const gate of evaluation.gates) {
|
|
5980
|
+
findings.push({
|
|
5981
|
+
item: `gate:${gate.gate}`,
|
|
5982
|
+
found: gate.status === "pass" || gate.status === "skip",
|
|
5983
|
+
details: gate.status === "pass" ? "\uD1B5\uACFC" : gate.status === "skip" ? "\uBE44\uD65C\uC131 (OK)" : gate.findings.join(", ")
|
|
5984
|
+
});
|
|
5985
|
+
}
|
|
5986
|
+
for (const script of evaluation.missingScripts) {
|
|
5987
|
+
findings.push({ item: `script:${script}`, found: false, details: "\uB204\uB77D" });
|
|
5988
|
+
}
|
|
5989
|
+
const recommendations = [];
|
|
5990
|
+
if (evaluation.missingScripts.length > 0) {
|
|
5991
|
+
recommendations.push({
|
|
5992
|
+
severity: "warning",
|
|
5993
|
+
message: `\uD544\uC218 script ${evaluation.missingScripts.length}\uAC1C \uB204\uB77D`,
|
|
5994
|
+
action: `package.json\uC5D0 \uCD94\uAC00: ${evaluation.missingScripts.join(", ")}`
|
|
5995
|
+
});
|
|
5996
|
+
}
|
|
5997
|
+
if (evaluation.missingFiles.length > 0) {
|
|
5998
|
+
recommendations.push({
|
|
5999
|
+
severity: "warning",
|
|
6000
|
+
message: `Harness \uD30C\uC77C ${evaluation.missingFiles.length}\uAC1C \uB204\uB77D`,
|
|
6001
|
+
action: `roboco scaffold \uC2E4\uD589 \uB610\uB294 \uC218\uB3D9 \uC0DD\uC131: ${evaluation.missingFiles.join(", ")}`
|
|
6002
|
+
});
|
|
6003
|
+
}
|
|
6004
|
+
return {
|
|
6005
|
+
name: "\uD558\uB124\uC2A4 \uC5D4\uC9C0\uB2C8\uC5B4\uB9C1",
|
|
6006
|
+
tier: "nice",
|
|
6007
|
+
score: evaluation.score,
|
|
6008
|
+
recommendations,
|
|
6009
|
+
rawFindings: findings
|
|
6010
|
+
};
|
|
6011
|
+
}
|
|
6012
|
+
async function checkHarnessLegacy(repoPath) {
|
|
5114
6013
|
const findings = [];
|
|
5115
6014
|
let score = 0;
|
|
5116
6015
|
const harnessChecks = [
|
|
@@ -5122,12 +6021,20 @@ async function checkHarnessEngineering(repoPath) {
|
|
|
5122
6021
|
{ path: ".pai/config.json", label: "PAI config", points: 10 }
|
|
5123
6022
|
];
|
|
5124
6023
|
for (const { path: path10, label, points } of harnessChecks) {
|
|
5125
|
-
const found = await fs17.pathExists(
|
|
6024
|
+
const found = await fs17.pathExists(join17(repoPath, path10));
|
|
5126
6025
|
findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
5127
6026
|
if (found) score += points;
|
|
5128
6027
|
}
|
|
5129
6028
|
score = Math.min(100, score);
|
|
5130
|
-
|
|
6029
|
+
const recommendations = [];
|
|
6030
|
+
if (!await fs17.pathExists(join17(repoPath, ".harness.json"))) {
|
|
6031
|
+
recommendations.push({
|
|
6032
|
+
severity: "info",
|
|
6033
|
+
message: ".harness.json \uBBF8\uAC10\uC9C0",
|
|
6034
|
+
action: "roboco scaffold\uB97C \uC2E4\uD589\uD558\uBA74 \uC120\uC5B8\uC801 \uC815\uCC45 \uAE30\uBC18 \uD3C9\uAC00\uB85C \uC804\uD658\uB429\uB2C8\uB2E4"
|
|
6035
|
+
});
|
|
6036
|
+
}
|
|
6037
|
+
return { name: "\uD558\uB124\uC2A4 \uC5D4\uC9C0\uB2C8\uC5B4\uB9C1", tier: "nice", score, recommendations, rawFindings: findings };
|
|
5131
6038
|
}
|
|
5132
6039
|
var init_analyzer2 = __esm({
|
|
5133
6040
|
"src/stages/evaluation/analyzer.ts"() {
|
|
@@ -5505,7 +6412,7 @@ __export(shell_cd_exports, {
|
|
|
5505
6412
|
installShellHelper: () => installShellHelper,
|
|
5506
6413
|
requestCdAfter: () => requestCdAfter
|
|
5507
6414
|
});
|
|
5508
|
-
import { join as
|
|
6415
|
+
import { join as join18 } from "path";
|
|
5509
6416
|
import { homedir as homedir2 } from "os";
|
|
5510
6417
|
import fs18 from "fs-extra";
|
|
5511
6418
|
async function requestCdAfter(targetDir) {
|
|
@@ -5542,7 +6449,7 @@ async function installPowerShellHelper() {
|
|
|
5542
6449
|
await fs18.writeFile(HELPER_FILE_PS1, POWERSHELL_HELPER);
|
|
5543
6450
|
const rcFile = getShellRcPath();
|
|
5544
6451
|
const sourceLine = '. "$env:USERPROFILE\\.pai\\shell-helper.ps1"';
|
|
5545
|
-
await fs18.ensureDir(
|
|
6452
|
+
await fs18.ensureDir(join18(rcFile, ".."));
|
|
5546
6453
|
if (await fs18.pathExists(rcFile)) {
|
|
5547
6454
|
const content = await fs18.readFile(rcFile, "utf8");
|
|
5548
6455
|
if (content.includes("shell-helper.ps1")) {
|
|
@@ -5563,10 +6470,10 @@ var init_shell_cd = __esm({
|
|
|
5563
6470
|
"src/utils/shell-cd.ts"() {
|
|
5564
6471
|
"use strict";
|
|
5565
6472
|
init_platform();
|
|
5566
|
-
PAI_DIR =
|
|
5567
|
-
CD_FILE =
|
|
5568
|
-
HELPER_FILE_SH =
|
|
5569
|
-
HELPER_FILE_PS1 =
|
|
6473
|
+
PAI_DIR = join18(homedir2(), ".pai");
|
|
6474
|
+
CD_FILE = join18(PAI_DIR, ".cd-after");
|
|
6475
|
+
HELPER_FILE_SH = join18(PAI_DIR, "shell-helper.sh");
|
|
6476
|
+
HELPER_FILE_PS1 = join18(PAI_DIR, "shell-helper.ps1");
|
|
5570
6477
|
BASH_HELPER = `# PAI shell helper \u2014 \uC790\uB3D9 \uB514\uB809\uD1A0\uB9AC \uC774\uB3D9 \uC9C0\uC6D0
|
|
5571
6478
|
pai() {
|
|
5572
6479
|
local cd_target="$HOME/.pai/.cd-after"
|
|
@@ -5701,7 +6608,7 @@ function mergeOmcIntoSettings(settings, marketplaceId, marketplaceUrl, pluginId)
|
|
|
5701
6608
|
return next;
|
|
5702
6609
|
}
|
|
5703
6610
|
var DEFAULT_MARKETPLACE_ID, DEFAULT_MARKETPLACE_URL, DEFAULT_PLUGIN_ID, ClaudeSettingsError;
|
|
5704
|
-
var
|
|
6611
|
+
var init_claude_settings2 = __esm({
|
|
5705
6612
|
"src/utils/claude-settings.ts"() {
|
|
5706
6613
|
"use strict";
|
|
5707
6614
|
init_platform();
|
|
@@ -5720,13 +6627,13 @@ var init_claude_settings = __esm({
|
|
|
5720
6627
|
});
|
|
5721
6628
|
|
|
5722
6629
|
// src/stages/evaluation/cache.ts
|
|
5723
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
5724
|
-
import { join as
|
|
6630
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync as existsSync9 } from "fs";
|
|
6631
|
+
import { join as join19 } from "path";
|
|
5725
6632
|
import { createHash } from "crypto";
|
|
5726
6633
|
function computeRepoHash(repoPath) {
|
|
5727
6634
|
const hash = createHash("sha256");
|
|
5728
6635
|
for (const file of FILES_TO_HASH) {
|
|
5729
|
-
const fullPath =
|
|
6636
|
+
const fullPath = join19(repoPath, file);
|
|
5730
6637
|
try {
|
|
5731
6638
|
const content = readFileSync(fullPath);
|
|
5732
6639
|
hash.update(`${file}:${content.length}`);
|
|
@@ -5737,7 +6644,7 @@ function computeRepoHash(repoPath) {
|
|
|
5737
6644
|
return hash.digest("hex").slice(0, 16);
|
|
5738
6645
|
}
|
|
5739
6646
|
function getCachePath(repoPath) {
|
|
5740
|
-
return
|
|
6647
|
+
return join19(repoPath, CACHE_DIR, CACHE_FILE);
|
|
5741
6648
|
}
|
|
5742
6649
|
function loadCache(repoPath) {
|
|
5743
6650
|
try {
|
|
@@ -5748,8 +6655,8 @@ function loadCache(repoPath) {
|
|
|
5748
6655
|
}
|
|
5749
6656
|
}
|
|
5750
6657
|
function saveCache(repoPath, store) {
|
|
5751
|
-
const cacheDir =
|
|
5752
|
-
if (!
|
|
6658
|
+
const cacheDir = join19(repoPath, CACHE_DIR, "cache");
|
|
6659
|
+
if (!existsSync9(cacheDir)) {
|
|
5753
6660
|
mkdirSync(cacheDir, { recursive: true });
|
|
5754
6661
|
}
|
|
5755
6662
|
writeFileSync(getCachePath(repoPath), JSON.stringify(store, null, 2));
|
|
@@ -5807,7 +6714,7 @@ var evaluate_cmd_exports = {};
|
|
|
5807
6714
|
__export(evaluate_cmd_exports, {
|
|
5808
6715
|
evaluateCommand: () => evaluateCommand
|
|
5809
6716
|
});
|
|
5810
|
-
import { join as
|
|
6717
|
+
import { join as join20, basename } from "path";
|
|
5811
6718
|
import fs20 from "fs-extra";
|
|
5812
6719
|
async function evaluateCommand(cwd, options) {
|
|
5813
6720
|
const useCache = options.cache !== false;
|
|
@@ -5831,8 +6738,8 @@ async function evaluateCommand(cwd, options) {
|
|
|
5831
6738
|
const config = await loadConfig(cwd);
|
|
5832
6739
|
const projectName = config?.projectName ?? basename(cwd);
|
|
5833
6740
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
5834
|
-
const reportDir =
|
|
5835
|
-
const reportPath =
|
|
6741
|
+
const reportDir = join20(cwd, "docs", "p-reports");
|
|
6742
|
+
const reportPath = join20(reportDir, `${today}.md`);
|
|
5836
6743
|
await fs20.ensureDir(reportDir);
|
|
5837
6744
|
const detailedReport = buildDetailedReport(result, projectName);
|
|
5838
6745
|
await fs20.writeFile(reportPath, detailedReport, "utf8");
|
|
@@ -6092,7 +6999,7 @@ var init_cmd_exports = {};
|
|
|
6092
6999
|
__export(init_cmd_exports, {
|
|
6093
7000
|
initCommand: () => initCommand
|
|
6094
7001
|
});
|
|
6095
|
-
import { join as
|
|
7002
|
+
import { join as join21, basename as basename2 } from "path";
|
|
6096
7003
|
import fs21 from "fs-extra";
|
|
6097
7004
|
async function initCommand(cwd, nameArg) {
|
|
6098
7005
|
printWelcomeBanner();
|
|
@@ -6151,11 +7058,11 @@ async function initCommand(cwd, nameArg) {
|
|
|
6151
7058
|
const evalResult = computeResult2(llmOutput);
|
|
6152
7059
|
printReport2(evalResult);
|
|
6153
7060
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6154
|
-
const reportDir =
|
|
7061
|
+
const reportDir = join21(cwd, "docs", "p-reports");
|
|
6155
7062
|
await fs21.ensureDir(reportDir);
|
|
6156
7063
|
const legacyName = basename2(cwd);
|
|
6157
7064
|
const detailedReport = buildDetailedReport3(evalResult, legacyName);
|
|
6158
|
-
await fs21.writeFile(
|
|
7065
|
+
await fs21.writeFile(join21(reportDir, `${today}.md`), detailedReport, "utf8");
|
|
6159
7066
|
console.log("");
|
|
6160
7067
|
success(`\uB9AC\uD3EC\uD2B8 \uC800\uC7A5: docs/p-reports/${today}.md`);
|
|
6161
7068
|
} catch {
|
|
@@ -6175,7 +7082,7 @@ async function initCommand(cwd, nameArg) {
|
|
|
6175
7082
|
return;
|
|
6176
7083
|
}
|
|
6177
7084
|
}
|
|
6178
|
-
step(1,
|
|
7085
|
+
step(1, TOTAL_STEPS, "\uD504\uB85C\uC81D\uD2B8 \uC124\uC815");
|
|
6179
7086
|
hint("\uC774 \uC774\uB984\uC73C\uB85C \uD504\uB85C\uC81D\uD2B8 \uD3F4\uB354\uAC00 \uC0DD\uC131\uB429\uB2C8\uB2E4.");
|
|
6180
7087
|
hint("\uC601\uBB38 \uC18C\uBB38\uC790, \uC22B\uC790, \uD558\uC774\uD508(-)\uC744 \uC0AC\uC6A9\uD574\uC8FC\uC138\uC694.");
|
|
6181
7088
|
console.log("");
|
|
@@ -6200,7 +7107,7 @@ async function initCommand(cwd, nameArg) {
|
|
|
6200
7107
|
}]);
|
|
6201
7108
|
projectName = answer.name.trim();
|
|
6202
7109
|
}
|
|
6203
|
-
const projectDir =
|
|
7110
|
+
const projectDir = join21(cwd, projectName);
|
|
6204
7111
|
if (await fs21.pathExists(projectDir)) {
|
|
6205
7112
|
const existingConfig = await loadConfig(projectDir);
|
|
6206
7113
|
if (existingConfig) {
|
|
@@ -6227,7 +7134,6 @@ async function setupInDirectory(projectDir, projectName) {
|
|
|
6227
7134
|
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
7135
|
console.log("");
|
|
6229
7136
|
}
|
|
6230
|
-
step(2, 3, "\uD504\uB85C\uC81D\uD2B8 \uC124\uC815");
|
|
6231
7137
|
const config = createDefaultConfig(projectName, "prototype");
|
|
6232
7138
|
const input = {
|
|
6233
7139
|
cwd: projectDir,
|
|
@@ -6260,55 +7166,55 @@ async function setupInDirectory(projectDir, projectName) {
|
|
|
6260
7166
|
}
|
|
6261
7167
|
const interview = result.data.interview;
|
|
6262
7168
|
const isCurrentDir = projectDir === process.cwd();
|
|
6263
|
-
await showCompletion(projectName, projectDir, interview
|
|
7169
|
+
await showCompletion(projectName, projectDir, interview, isCurrentDir);
|
|
6264
7170
|
} else {
|
|
6265
7171
|
for (const err of result.errors) {
|
|
6266
7172
|
error(err.message);
|
|
6267
7173
|
}
|
|
6268
7174
|
}
|
|
6269
7175
|
}
|
|
6270
|
-
async function showCompletion(projectName, projectDir,
|
|
7176
|
+
async function showCompletion(projectName, projectDir, interview, isCurrentDir) {
|
|
6271
7177
|
const { default: inquirer } = await import("inquirer");
|
|
6272
7178
|
const chalk9 = (await import("chalk")).default;
|
|
6273
7179
|
const { sleep: sleep2 } = await Promise.resolve().then(() => (init_progress(), progress_exports));
|
|
6274
|
-
|
|
6275
|
-
|
|
7180
|
+
const extraTools = interview.extraTools;
|
|
7181
|
+
step(4, TOTAL_STEPS, "\uC124\uCE58 \uD6C4 \uC548\uB0B4");
|
|
7182
|
+
console.log(colors.success(" \uD504\uB85C\uC81D\uD2B8 \uC900\uBE44\uAC00 \uB05D\uB0AC\uC2B5\uB2C8\uB2E4!"));
|
|
7183
|
+
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
7184
|
console.log("");
|
|
6277
|
-
console.log(colors.
|
|
7185
|
+
console.log(colors.accent(" \uC124\uCE58\uB41C \uAE30\uB2A5"));
|
|
6278
7186
|
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`);
|
|
7187
|
+
console.log(` ${chalk9.cyan("OpenSpec")} PRD \uC124\uACC4 \uD15C\uD50C\uB9BF`);
|
|
7188
|
+
console.log(` ${chalk9.cyan("RoboCo")} \uD30C\uC774\uD504\uB77C\uC778 \uC624\uCF00\uC2A4\uD2B8\uB808\uC774\uC158`);
|
|
7189
|
+
console.log(` ${chalk9.cyan("Harness")} \uD488\uC9C8 \uC815\uCC45 (hook \xB7 CI \xB7 lint)`);
|
|
7190
|
+
console.log(` ${chalk9.cyan("Vibe-Ready")} AI \uAC1C\uBC1C \uC900\uBE44\uB3C4 \uC9C4\uB2E8`);
|
|
6289
7191
|
if (extraTools.includes("omc")) {
|
|
6290
|
-
console.log(` ${chalk9.cyan("OMC")}
|
|
7192
|
+
console.log(` ${chalk9.cyan("OMC")} AI \uC5D0\uC774\uC804\uD2B8 \uC791\uC5C5 \uBD84\uC5C5`);
|
|
6291
7193
|
}
|
|
6292
7194
|
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`);
|
|
7195
|
+
console.log(` ${chalk9.cyan("gstack")} \uD14C\uC2A4\uD2B8 \uC790\uB3D9\uD654`);
|
|
6297
7196
|
}
|
|
6298
7197
|
if (extraTools.includes("vercel")) {
|
|
6299
|
-
console.log(` ${chalk9.cyan("Vercel")}
|
|
7198
|
+
console.log(` ${chalk9.cyan("Vercel")} \uC790\uB3D9 \uBC30\uD3EC (Preview + Production)`);
|
|
6300
7199
|
}
|
|
6301
7200
|
if (extraTools.includes("supabase")) {
|
|
6302
|
-
console.log(` ${chalk9.cyan("Supabase")}
|
|
7201
|
+
console.log(` ${chalk9.cyan("Supabase")} DB + \uC778\uC99D + \uC2A4\uD1A0\uB9AC\uC9C0`);
|
|
6303
7202
|
}
|
|
6304
|
-
if (extraTools.includes("mcp")) {
|
|
6305
|
-
console.log(` ${chalk9.cyan("MCP \uC11C\uBC84")}
|
|
7203
|
+
if (extraTools.includes("mcp") && interview.mcp) {
|
|
7204
|
+
console.log(` ${chalk9.cyan("MCP \uC11C\uBC84")} ${interview.mcp.name} (${interview.mcp.type})`);
|
|
7205
|
+
}
|
|
7206
|
+
if (interview.authMethods.length > 0) {
|
|
7207
|
+
console.log(` ${chalk9.cyan("\uB85C\uADF8\uC778")} ${interview.authMethods.join(", ")}`);
|
|
7208
|
+
}
|
|
7209
|
+
if (interview.recipes && interview.recipes.length > 0) {
|
|
7210
|
+
console.log(` ${chalk9.cyan("\uC0AC\uB0B4 \uC5F0\uB3D9")} ${interview.recipes.join(", ")}`);
|
|
6306
7211
|
}
|
|
6307
7212
|
console.log("");
|
|
6308
|
-
console.log(colors.accent(" \
|
|
7213
|
+
console.log(colors.accent(" \uD30C\uC77C \uD655\uC778"));
|
|
6309
7214
|
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
7215
|
const checks = [
|
|
6311
7216
|
{ label: "AI \uCEE8\uD14D\uC2A4\uD2B8", path: "CLAUDE.md" },
|
|
7217
|
+
{ label: "Harness \uC815\uCC45", path: ".harness.json" },
|
|
6312
7218
|
{ label: "PRD \uD15C\uD50C\uB9BF", path: "docs/openspec.md" },
|
|
6313
7219
|
{ label: "\uD504\uB85C\uC81D\uD2B8 \uC124\uC815", path: ".pai/config.json" },
|
|
6314
7220
|
{ label: "Claude \uC124\uC815", path: ".claude/settings.json" },
|
|
@@ -6316,10 +7222,42 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
|
|
|
6316
7222
|
...extraTools.includes("omc") ? [{ label: "OMC \uB7F0\uD0C0\uC784", path: ".omc" }] : []
|
|
6317
7223
|
];
|
|
6318
7224
|
for (const check of checks) {
|
|
6319
|
-
const exists = await fs21.pathExists(
|
|
7225
|
+
const exists = await fs21.pathExists(join21(projectDir, check.path));
|
|
6320
7226
|
console.log(` ${exists ? colors.success("\u2713") : colors.err("\u2717")} ${check.label.padEnd(16)} ${colors.dim(check.path)}`);
|
|
6321
7227
|
}
|
|
6322
7228
|
console.log("");
|
|
7229
|
+
console.log(colors.accent(" \uB2E4\uC74C \uD560 \uC77C"));
|
|
7230
|
+
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"));
|
|
7231
|
+
if (interview.authMethods.length > 0) {
|
|
7232
|
+
if (interview.authMethods.includes("custom") && !interview.customAuth?.clientId) {
|
|
7233
|
+
console.log(` ${chalk9.yellow("\u2192")} .env.local \uC5D0 OAuth \uD0A4\uB97C \uCC44\uC6CC\uC8FC\uC138\uC694`);
|
|
7234
|
+
}
|
|
7235
|
+
if (interview.authMethods.includes("google")) {
|
|
7236
|
+
console.log(` ${chalk9.yellow("\u2192")} Google Cloud Console\uC5D0\uC11C OAuth \uD074\uB77C\uC774\uC5B8\uD2B8 \uC0DD\uC131`);
|
|
7237
|
+
}
|
|
7238
|
+
if (interview.authMethods.includes("kakao")) {
|
|
7239
|
+
console.log(` ${chalk9.yellow("\u2192")} Kakao Developers\uC5D0\uC11C \uC571 \uB4F1\uB85D`);
|
|
7240
|
+
}
|
|
7241
|
+
if (interview.authMethods.includes("naver")) {
|
|
7242
|
+
console.log(` ${chalk9.yellow("\u2192")} Naver Developers\uC5D0\uC11C \uC571 \uB4F1\uB85D`);
|
|
7243
|
+
}
|
|
7244
|
+
}
|
|
7245
|
+
if (extraTools.includes("vercel")) {
|
|
7246
|
+
console.log(` ${chalk9.yellow("\u2192")} vercel login \uC73C\uB85C Vercel \uACC4\uC815 \uC5F0\uACB0`);
|
|
7247
|
+
}
|
|
7248
|
+
if (extraTools.includes("supabase")) {
|
|
7249
|
+
console.log(` ${chalk9.yellow("\u2192")} .env.local \uC5D0 SUPABASE_URL, SUPABASE_ANON_KEY \uC785\uB825`);
|
|
7250
|
+
}
|
|
7251
|
+
if (extraTools.includes("mcp") && interview.mcp) {
|
|
7252
|
+
console.log(` ${chalk9.yellow("\u2192")} mcp-server/ \uC5D0\uC11C MCP \uC11C\uBC84 \uAC1C\uBC1C \uC2DC\uC791`);
|
|
7253
|
+
}
|
|
7254
|
+
if (interview.recipes && interview.recipes.length > 0) {
|
|
7255
|
+
for (const recipe of interview.recipes) {
|
|
7256
|
+
console.log(` ${chalk9.yellow("\u2192")} docs/recipes/${recipe}/ \uAC00\uC774\uB4DC \uD655\uC778`);
|
|
7257
|
+
}
|
|
7258
|
+
}
|
|
7259
|
+
console.log(` ${chalk9.yellow("\u2192")} docs/openspec.md \uC5D0 PRD \uC791\uC131 \uC2DC\uC791`);
|
|
7260
|
+
console.log("");
|
|
6323
7261
|
const { runEval } = await inquirer.prompt([{
|
|
6324
7262
|
type: "confirm",
|
|
6325
7263
|
name: "runEval",
|
|
@@ -6341,10 +7279,10 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
|
|
|
6341
7279
|
printReport2(evalResult);
|
|
6342
7280
|
await sleep2(500);
|
|
6343
7281
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6344
|
-
const reportDir =
|
|
7282
|
+
const reportDir = join21(projectDir, "docs", "p-reports");
|
|
6345
7283
|
await fs21.ensureDir(reportDir);
|
|
6346
7284
|
const detailedReport = buildDetailedReport3(evalResult, projectName);
|
|
6347
|
-
await fs21.writeFile(
|
|
7285
|
+
await fs21.writeFile(join21(reportDir, `${today}.md`), detailedReport, "utf8");
|
|
6348
7286
|
await sleep2(500);
|
|
6349
7287
|
console.log("");
|
|
6350
7288
|
success(`\uB9AC\uD3EC\uD2B8 \uC800\uC7A5: docs/p-reports/${today}.md`);
|
|
@@ -6372,7 +7310,7 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
|
|
|
6372
7310
|
const { createSpinner: createSpinner2 } = await Promise.resolve().then(() => (init_progress(), progress_exports));
|
|
6373
7311
|
const omcSpinner = createSpinner2("OMC \uD50C\uB7EC\uADF8\uC778 \uB4F1\uB85D \uC911...");
|
|
6374
7312
|
try {
|
|
6375
|
-
const { enableOmcPlugin: enableOmcPlugin2 } = await Promise.resolve().then(() => (
|
|
7313
|
+
const { enableOmcPlugin: enableOmcPlugin2 } = await Promise.resolve().then(() => (init_claude_settings2(), claude_settings_exports));
|
|
6376
7314
|
const result = await enableOmcPlugin2();
|
|
6377
7315
|
if (result.action === "already-enabled") {
|
|
6378
7316
|
omcSpinner.succeed("OMC \uD50C\uB7EC\uADF8\uC778 \uC774\uBBF8 \uB4F1\uB85D\uB428");
|
|
@@ -6417,7 +7355,7 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
|
|
|
6417
7355
|
const aliasLine = getYoloAliasLine2();
|
|
6418
7356
|
const rcContent = await fs21.readFile(shellRc, "utf8").catch(() => "");
|
|
6419
7357
|
if (!rcContent.includes("claude-yolo")) {
|
|
6420
|
-
await fs21.ensureDir(
|
|
7358
|
+
await fs21.ensureDir(join21(shellRc, ".."));
|
|
6421
7359
|
await fs21.appendFile(shellRc, `
|
|
6422
7360
|
# PAI \u2014 claude-YOLO mode
|
|
6423
7361
|
${aliasLine}
|
|
@@ -6611,9 +7549,9 @@ async function installOrchestratorOnly(projectDir, projectName) {
|
|
|
6611
7549
|
const evalResult = computeResult2(llmOutput);
|
|
6612
7550
|
printReport2(evalResult);
|
|
6613
7551
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6614
|
-
const reportDir =
|
|
7552
|
+
const reportDir = join21(projectDir, "docs", "p-reports");
|
|
6615
7553
|
await fs21.ensureDir(reportDir);
|
|
6616
|
-
await fs21.writeFile(
|
|
7554
|
+
await fs21.writeFile(join21(reportDir, `${today}.md`), buildDetailedReport3(evalResult, projectName), "utf8");
|
|
6617
7555
|
console.log("");
|
|
6618
7556
|
hint(`\uC0C1\uC138 \uB9AC\uD3EC\uD2B8: docs/p-reports/${today}.md`);
|
|
6619
7557
|
} catch {
|
|
@@ -6663,7 +7601,7 @@ async function detectLegacyProject(cwd) {
|
|
|
6663
7601
|
".gitignore"
|
|
6664
7602
|
];
|
|
6665
7603
|
for (const signal of signals) {
|
|
6666
|
-
if (await fs21.pathExists(
|
|
7604
|
+
if (await fs21.pathExists(join21(cwd, signal))) return true;
|
|
6667
7605
|
}
|
|
6668
7606
|
return false;
|
|
6669
7607
|
}
|
|
@@ -6676,6 +7614,7 @@ var init_init_cmd = __esm({
|
|
|
6676
7614
|
init_detector();
|
|
6677
7615
|
init_roboco();
|
|
6678
7616
|
init_validate_cmd();
|
|
7617
|
+
init_interviewer();
|
|
6679
7618
|
init_ui();
|
|
6680
7619
|
init_logger();
|
|
6681
7620
|
}
|
|
@@ -6756,12 +7695,12 @@ var init_help_cmd = __esm({
|
|
|
6756
7695
|
});
|
|
6757
7696
|
|
|
6758
7697
|
// src/stages/design/openspec.ts
|
|
6759
|
-
import { join as
|
|
7698
|
+
import { join as join22 } from "path";
|
|
6760
7699
|
import fs22 from "fs-extra";
|
|
6761
7700
|
async function initOpenSpec(cwd, projectName) {
|
|
6762
|
-
const docsDir =
|
|
7701
|
+
const docsDir = join22(cwd, "docs");
|
|
6763
7702
|
await fs22.ensureDir(docsDir);
|
|
6764
|
-
const openspecPath =
|
|
7703
|
+
const openspecPath = join22(docsDir, "openspec.md");
|
|
6765
7704
|
if (await fs22.pathExists(openspecPath)) {
|
|
6766
7705
|
info("docs/openspec.md \uC774\uBBF8 \uC874\uC7AC \u2014 \uAC74\uB108\uB700");
|
|
6767
7706
|
return;
|
|
@@ -6794,9 +7733,9 @@ async function initOpenSpec(cwd, projectName) {
|
|
|
6794
7733
|
}
|
|
6795
7734
|
async function validateOpenSpec(cwd) {
|
|
6796
7735
|
const candidates = [
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
7736
|
+
join22(cwd, "docs", "openspec.md"),
|
|
7737
|
+
join22(cwd, "openspec.md"),
|
|
7738
|
+
join22(cwd, ".pai", "openspec.md")
|
|
6800
7739
|
];
|
|
6801
7740
|
let specPath = null;
|
|
6802
7741
|
for (const p of candidates) {
|
|
@@ -6862,12 +7801,12 @@ var init_openspec = __esm({
|
|
|
6862
7801
|
});
|
|
6863
7802
|
|
|
6864
7803
|
// src/stages/design/omc.ts
|
|
6865
|
-
import { join as
|
|
7804
|
+
import { join as join23 } from "path";
|
|
6866
7805
|
import fs23 from "fs-extra";
|
|
6867
7806
|
async function initOMC(cwd, projectName) {
|
|
6868
|
-
const paiDir =
|
|
7807
|
+
const paiDir = join23(cwd, ".pai");
|
|
6869
7808
|
await fs23.ensureDir(paiDir);
|
|
6870
|
-
const omcPath =
|
|
7809
|
+
const omcPath = join23(paiDir, "omc.md");
|
|
6871
7810
|
if (await fs23.pathExists(omcPath)) {
|
|
6872
7811
|
info(".pai/omc.md \uC774\uBBF8 \uC874\uC7AC \u2014 \uAC74\uB108\uB700");
|
|
6873
7812
|
return;
|
|
@@ -7031,13 +7970,13 @@ var init_context = __esm({
|
|
|
7031
7970
|
});
|
|
7032
7971
|
|
|
7033
7972
|
// src/stages/design/index.ts
|
|
7034
|
-
import { join as
|
|
7973
|
+
import { join as join24 } from "path";
|
|
7035
7974
|
import fs24 from "fs-extra";
|
|
7036
7975
|
async function autoInstallHarness(cwd) {
|
|
7037
|
-
const harnessPath =
|
|
7976
|
+
const harnessPath = join24(cwd, ".pai", "harness.json");
|
|
7038
7977
|
if (await fs24.pathExists(harnessPath)) return;
|
|
7039
7978
|
await withSpinner("Harness Engineering \uC790\uB3D9 \uC124\uC815 \uC911...", async () => {
|
|
7040
|
-
await fs24.ensureDir(
|
|
7979
|
+
await fs24.ensureDir(join24(cwd, ".pai"));
|
|
7041
7980
|
await fs24.writeJson(harnessPath, {
|
|
7042
7981
|
version: "1.0",
|
|
7043
7982
|
specFile: "docs/openspec.md",
|
|
@@ -7754,7 +8693,7 @@ var savetoken_cmd_exports = {};
|
|
|
7754
8693
|
__export(savetoken_cmd_exports, {
|
|
7755
8694
|
savetokenCommand: () => savetokenCommand
|
|
7756
8695
|
});
|
|
7757
|
-
import { join as
|
|
8696
|
+
import { join as join25, relative } from "path";
|
|
7758
8697
|
import fs26 from "fs-extra";
|
|
7759
8698
|
import chalk7 from "chalk";
|
|
7760
8699
|
async function savetokenCommand(cwd) {
|
|
@@ -7824,10 +8763,10 @@ async function savetokenCommand(cwd) {
|
|
|
7824
8763
|
console.log(` \u2192 ${colors.dim("\uB8F0 \uAE30\uBC18 \uB85C\uC9C1, \uD0A4\uC6CC\uB4DC \uB9E4\uCE6D\uC73C\uB85C \uAC80\uD1A0 \uD6C4 \uB300\uCCB4")}`);
|
|
7825
8764
|
console.log(` ${chalk7.red("\u25CF")} \uB192\uC74C \uCF54\uB4DC \uC0DD\uC131, \uBCF5\uC7A1\uD55C \uCD94\uB860, \uCC3D\uC758\uC801 \uC0DD\uC131`);
|
|
7826
8765
|
console.log(` \u2192 ${colors.dim("AI \uD544\uC218 \u2014 \uD504\uB86C\uD504\uD2B8 \uCD5C\uC801\uD654\uB85C \uD1A0\uD070 \uC808\uAC10")}`);
|
|
7827
|
-
const reportDir =
|
|
8766
|
+
const reportDir = join25(cwd, ".pai");
|
|
7828
8767
|
await fs26.ensureDir(reportDir);
|
|
7829
8768
|
const report = buildReport(callSites, cwd);
|
|
7830
|
-
const reportPath =
|
|
8769
|
+
const reportPath = join25(reportDir, "savetoken-report.md");
|
|
7831
8770
|
await fs26.writeFile(reportPath, report, "utf8");
|
|
7832
8771
|
console.log("");
|
|
7833
8772
|
success("\uC2A4\uCE94 \uB9AC\uD3EC\uD2B8 \uC800\uC7A5: .pai/savetoken-report.md");
|
|
@@ -7984,7 +8923,7 @@ var wakeup_cmd_exports = {};
|
|
|
7984
8923
|
__export(wakeup_cmd_exports, {
|
|
7985
8924
|
wakeupCommand: () => wakeupCommand
|
|
7986
8925
|
});
|
|
7987
|
-
import { join as
|
|
8926
|
+
import { join as join26 } from "path";
|
|
7988
8927
|
import { homedir as homedir3, platform as osPlatform } from "os";
|
|
7989
8928
|
import fs27 from "fs-extra";
|
|
7990
8929
|
import chalk8 from "chalk";
|
|
@@ -8065,7 +9004,7 @@ async function wakeupCommand(timeOrAction, schedule = "\uD3C9\uC77C") {
|
|
|
8065
9004
|
async function setupMacOS(config) {
|
|
8066
9005
|
const { execa } = await import("execa");
|
|
8067
9006
|
const [hour, minute] = config.time.split(":").map(Number);
|
|
8068
|
-
const plistDir =
|
|
9007
|
+
const plistDir = join26(homedir3(), "Library", "LaunchAgents");
|
|
8069
9008
|
await fs27.ensureDir(plistDir);
|
|
8070
9009
|
const weekdays = scheduleToWeekdays(config.schedule);
|
|
8071
9010
|
let calendarEntries;
|
|
@@ -8124,9 +9063,9 @@ ${calendarEntries}
|
|
|
8124
9063
|
async function setupWindows(config) {
|
|
8125
9064
|
const { execa } = await import("execa");
|
|
8126
9065
|
const [hour, minute] = config.time.split(":").map(Number);
|
|
8127
|
-
const psScriptDir =
|
|
9066
|
+
const psScriptDir = join26(homedir3(), ".pai");
|
|
8128
9067
|
await fs27.ensureDir(psScriptDir);
|
|
8129
|
-
const psScriptPath =
|
|
9068
|
+
const psScriptPath = join26(psScriptDir, "wakeup.ps1");
|
|
8130
9069
|
const claudeCmd = config.launchMode === "yolo" ? "claude --dangerously-skip-permissions" : "claude";
|
|
8131
9070
|
const psScript = `# PAI Wakeup \u2014 Claude Code \uC138\uC158 \uC790\uB3D9 \uC2DC\uC791
|
|
8132
9071
|
$paiDir = "$env:USERPROFILE\\.pai"
|
|
@@ -8400,12 +9339,12 @@ var init_wakeup_cmd = __esm({
|
|
|
8400
9339
|
"use strict";
|
|
8401
9340
|
init_ui();
|
|
8402
9341
|
init_logger();
|
|
8403
|
-
PAI_DIR2 =
|
|
8404
|
-
CONFIG_FILE2 =
|
|
8405
|
-
MESSAGES_FILE =
|
|
8406
|
-
SCRIPT_FILE =
|
|
9342
|
+
PAI_DIR2 = join26(homedir3(), ".pai");
|
|
9343
|
+
CONFIG_FILE2 = join26(PAI_DIR2, "wakeup-config.json");
|
|
9344
|
+
MESSAGES_FILE = join26(PAI_DIR2, "wakeup-messages.json");
|
|
9345
|
+
SCRIPT_FILE = join26(PAI_DIR2, "wakeup.sh");
|
|
8407
9346
|
PLIST_NAME = "com.pai.wakeup";
|
|
8408
|
-
PLIST_PATH =
|
|
9347
|
+
PLIST_PATH = join26(homedir3(), "Library", "LaunchAgents", `${PLIST_NAME}.plist`);
|
|
8409
9348
|
CRON_MARKER = "# PAI-WAKEUP";
|
|
8410
9349
|
MESSAGES = [
|
|
8411
9350
|
`Here's to the crazy ones. The misfits. The rebels. The troublemakers.
|