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