pai-zero 0.11.5 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/pai.js +988 -390
- package/dist/bin/pai.js.map +1 -1
- package/dist/cli/index.js +988 -390
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/pai.js
CHANGED
|
@@ -441,6 +441,61 @@ var init_analyzer = __esm({
|
|
|
441
441
|
}
|
|
442
442
|
});
|
|
443
443
|
|
|
444
|
+
// src/core/recipes.ts
|
|
445
|
+
var recipes_exports = {};
|
|
446
|
+
__export(recipes_exports, {
|
|
447
|
+
RECIPES: () => RECIPES,
|
|
448
|
+
getRecipe: () => getRecipe,
|
|
449
|
+
listRecipeKeys: () => listRecipeKeys
|
|
450
|
+
});
|
|
451
|
+
function listRecipeKeys() {
|
|
452
|
+
return Object.keys(RECIPES);
|
|
453
|
+
}
|
|
454
|
+
function getRecipe(key) {
|
|
455
|
+
return RECIPES[key.toLowerCase()];
|
|
456
|
+
}
|
|
457
|
+
var RECIPES;
|
|
458
|
+
var init_recipes = __esm({
|
|
459
|
+
"src/core/recipes.ts"() {
|
|
460
|
+
"use strict";
|
|
461
|
+
RECIPES = {
|
|
462
|
+
oauth: {
|
|
463
|
+
label: "\uC0AC\uB0B4 OAuth2 \uC778\uC99D",
|
|
464
|
+
description: "\uC0AC\uB0B4 SSO / \uC778\uC99D \uD3EC\uD138 \uC5F0\uACB0 \uAC00\uC774\uB4DC + \uC0D8\uD50C \uCF54\uB4DC",
|
|
465
|
+
source: {
|
|
466
|
+
repo: "SoInKyu/setup-repo",
|
|
467
|
+
ref: "main",
|
|
468
|
+
path: "repo/oauth"
|
|
469
|
+
},
|
|
470
|
+
target: "docs/recipes/oauth",
|
|
471
|
+
envKeys: [
|
|
472
|
+
{ key: "OAUTH_HOST", hint: "\uC608: auth.mycompany.com" },
|
|
473
|
+
{ key: "OAUTH_CLIENT_ID", hint: "\uC778\uC99D \uD3EC\uD138\uC5D0\uC11C \uBC1C\uAE09\uBC1B\uC740 Client ID" },
|
|
474
|
+
{ key: "OAUTH_CLIENT_SECRET", hint: "\uC778\uC99D \uD3EC\uD138\uC5D0\uC11C \uBC1C\uAE09\uBC1B\uC740 Client Secret" },
|
|
475
|
+
{ key: "OAUTH_REDIRECT_URI", hint: "\uC608: http://localhost:3000/auth/callback", default: "http://localhost:3000/auth/callback" }
|
|
476
|
+
],
|
|
477
|
+
skillDescription: "\uC0AC\uB0B4 OAuth2 \uC778\uC99D / SSO / \uB85C\uADF8\uC778 \uAD6C\uD604 \uC2DC \uC774 \uB808\uC2DC\uD53C \uBB38\uC11C\uB97C \uC6B0\uC120 \uCC38\uC870"
|
|
478
|
+
},
|
|
479
|
+
officechat: {
|
|
480
|
+
label: "\uBA54\uC2E0\uC800 \uCC57\uBD07 (Office Chat)",
|
|
481
|
+
description: "\uC0AC\uB0B4 \uBA54\uC2E0\uC800 \uCC57\uBD07 \uC5F0\uACB0 \uAC00\uC774\uB4DC + \uC0D8\uD50C \uCF54\uB4DC",
|
|
482
|
+
source: {
|
|
483
|
+
repo: "SoInKyu/setup-repo",
|
|
484
|
+
ref: "main",
|
|
485
|
+
path: "repo/officechat"
|
|
486
|
+
},
|
|
487
|
+
target: "docs/recipes/officechat",
|
|
488
|
+
envKeys: [
|
|
489
|
+
{ key: "OFFICECHAT_HOST", hint: "\uC0AC\uB0B4 \uBA54\uC2E0\uC800 \uC11C\uBC84 \uC8FC\uC18C" },
|
|
490
|
+
{ key: "OFFICECHAT_TOKEN", hint: "\uBD07 \uD1A0\uD070" },
|
|
491
|
+
{ key: "OFFICECHAT_CHANNEL", hint: "\uAE30\uBCF8 \uCC44\uB110 ID" }
|
|
492
|
+
],
|
|
493
|
+
skillDescription: "\uBA54\uC2E0\uC800 \uCC57\uBD07 / \uC54C\uB9BC \uBD07 / Office Chat \uC5F0\uB3D9 \uAD6C\uD604 \uC2DC \uC774 \uB808\uC2DC\uD53C \uBB38\uC11C\uB97C \uC6B0\uC120 \uCC38\uC870"
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
|
|
444
499
|
// src/stages/environment/interviewer.ts
|
|
445
500
|
async function runInterview(analysis, cwd, projectName) {
|
|
446
501
|
try {
|
|
@@ -531,7 +586,7 @@ function parseAiResult(result, analysis, projectName) {
|
|
|
531
586
|
}
|
|
532
587
|
async function interactiveInterview(analysis, _cwd, projectName) {
|
|
533
588
|
const { default: inquirer } = await import("inquirer");
|
|
534
|
-
const
|
|
589
|
+
const chalk9 = (await import("chalk")).default;
|
|
535
590
|
step(1, 3, "\uD504\uB85C\uC81D\uD2B8 \uC218\uC900\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694");
|
|
536
591
|
hint("\uC120\uD0DD\uC5D0 \uB530\uB77C \uC124\uCE58\uB418\uB294 \uD50C\uB7EC\uADF8\uC778 \uC138\uD2B8\uAC00 \uB2EC\uB77C\uC9D1\uB2C8\uB2E4.");
|
|
537
592
|
console.log("");
|
|
@@ -593,7 +648,7 @@ async function interactiveInterview(analysis, _cwd, projectName) {
|
|
|
593
648
|
{ name: "Naver \uB85C\uADF8\uC778", value: "naver" },
|
|
594
649
|
{ name: `\uC0AC\uB0B4 \uC778\uC99D ${colors.dim("(OAuth2)")}`, value: "custom" },
|
|
595
650
|
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"),
|
|
596
|
-
{ name:
|
|
651
|
+
{ name: chalk9.green("\u21B5 \uC120\uD0DD \uC644\uB8CC"), value: "__done__" }
|
|
597
652
|
],
|
|
598
653
|
validate: (answer) => {
|
|
599
654
|
const items = answer.filter((a) => a !== "__done__");
|
|
@@ -705,6 +760,28 @@ async function interactiveInterview(analysis, _cwd, projectName) {
|
|
|
705
760
|
};
|
|
706
761
|
extraTools.push("mcp");
|
|
707
762
|
}
|
|
763
|
+
const { RECIPES: RECIPES2 } = await Promise.resolve().then(() => (init_recipes(), recipes_exports));
|
|
764
|
+
const recipeKeys = Object.keys(RECIPES2);
|
|
765
|
+
let selectedRecipes = [];
|
|
766
|
+
if (recipeKeys.length > 0) {
|
|
767
|
+
console.log("");
|
|
768
|
+
hint("Space\uB85C \uC120\uD0DD \xB7 \uC120\uD0DD \uD6C4 Enter\uB85C \uC644\uB8CC (\uAC74\uB108\uB6F0\uB824\uBA74 \uBC14\uB85C Enter)");
|
|
769
|
+
const Separator2 = inquirer.Separator;
|
|
770
|
+
const recipeAnswer = await inquirer.prompt([{
|
|
771
|
+
type: "checkbox",
|
|
772
|
+
name: "recipes",
|
|
773
|
+
message: "\uC0AC\uB0B4 \uC2DC\uC2A4\uD15C \uC5F0\uB3D9 \uB808\uC2DC\uD53C\uB97C \uC124\uCE58\uD560\uAE4C\uC694?",
|
|
774
|
+
choices: [
|
|
775
|
+
...recipeKeys.map((k) => ({
|
|
776
|
+
name: `${RECIPES2[k].label} ${colors.dim("\u2500 " + RECIPES2[k].description)}`,
|
|
777
|
+
value: k
|
|
778
|
+
})),
|
|
779
|
+
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"),
|
|
780
|
+
{ name: chalk9.green("\u21B5 \uC120\uD0DD \uC644\uB8CC (\uAC74\uB108\uB6F0\uAE30)"), value: "__done__" }
|
|
781
|
+
]
|
|
782
|
+
}]);
|
|
783
|
+
selectedRecipes = recipeAnswer.recipes.filter((x) => x !== "__done__");
|
|
784
|
+
}
|
|
708
785
|
step(3, 3, "\uC124\uCE58 \uD655\uC778");
|
|
709
786
|
console.log("");
|
|
710
787
|
console.log(colors.dim(" \uC124\uCE58\uB420 \uD56D\uBAA9"));
|
|
@@ -729,6 +806,10 @@ async function interactiveInterview(analysis, _cwd, projectName) {
|
|
|
729
806
|
console.log("");
|
|
730
807
|
console.log(` ${colors.accent("+")} \uB85C\uADF8\uC778: ${authMethods.join(", ")}`);
|
|
731
808
|
}
|
|
809
|
+
if (selectedRecipes.length > 0) {
|
|
810
|
+
console.log("");
|
|
811
|
+
console.log(` ${colors.accent("+")} \uC0AC\uB0B4 \uC5F0\uB3D9 \uB808\uC2DC\uD53C: ${selectedRecipes.join(", ")}`);
|
|
812
|
+
}
|
|
732
813
|
console.log("");
|
|
733
814
|
const { proceed } = await inquirer.prompt([{
|
|
734
815
|
type: "confirm",
|
|
@@ -749,6 +830,7 @@ async function interactiveInterview(analysis, _cwd, projectName) {
|
|
|
749
830
|
authMethods,
|
|
750
831
|
customAuth,
|
|
751
832
|
extraTools,
|
|
833
|
+
recipes: selectedRecipes,
|
|
752
834
|
mcp,
|
|
753
835
|
setupDomains: {
|
|
754
836
|
claudeEnv: true,
|
|
@@ -808,20 +890,22 @@ function generateClaudeMd(cwd, analysis, interview) {
|
|
|
808
890
|
|
|
809
891
|
## Project Overview
|
|
810
892
|
|
|
811
|
-
|
|
893
|
+
<!-- TODO: \uC774 \uD504\uB85C\uC81D\uD2B8\uAC00 \uBB34\uC5C7\uC744 \uD574\uACB0\uD558\uB294\uC9C0 2-3\uBB38\uC7A5\uC73C\uB85C \uAE30\uC220. \uC0C1\uC138 \uB0B4\uC6A9\uC740 docs/openspec.md \uCC38\uC870. -->
|
|
894
|
+
|
|
895
|
+
\uC790\uC138\uD55C \uC81C\uD488 \uC694\uAD6C\uC0AC\uD56D: \`docs/openspec.md\` \xB7 \uB3C4\uBA54\uC778 \uBAA8\uB378: \`.pai/omc.md\`
|
|
812
896
|
|
|
813
897
|
<pai>
|
|
814
898
|
## PAI Configuration
|
|
815
899
|
|
|
816
900
|
This project uses PAI (Plugin-based AI) for structured AI development.
|
|
817
901
|
|
|
818
|
-
### Pipeline: \
|
|
902
|
+
### Pipeline: Environment \u2192 Design \u2192 Execution \u2192 Validation \u2192 Evaluation
|
|
819
903
|
|
|
820
|
-
1. **Environment** \u2014 \uD504\uB85C\uC81D\uD2B8 \uD658\uACBD \uAD6C\uC131 (\
|
|
821
|
-
2. **Design** \u2014 OpenSpec/OMC \uAE30\uBC18 \uC124\uACC4
|
|
822
|
-
3. **Execution** \u2014 AI \uCF54\uB4DC \uC0DD\uC131
|
|
823
|
-
4. **Validation** \u2014 gstack \uD14C\uC2A4\uD2B8
|
|
824
|
-
5. **Evaluation** \u2014 \uD488\uC9C8 \uD3C9\uAC00
|
|
904
|
+
1. **Environment** \u2014 \uD504\uB85C\uC81D\uD2B8 \uD658\uACBD \uAD6C\uC131 (\uC644\uB8CC)
|
|
905
|
+
2. **Design** \u2014 OpenSpec (\`docs/openspec.md\`) + OMC (\`.pai/omc.md\`) \uAE30\uBC18 \uC124\uACC4
|
|
906
|
+
3. **Execution** \u2014 AI \uCF54\uB4DC \uC0DD\uC131 (\uC774 \uB2E8\uACC4 Claude\uAC00 \uB2F4\uB2F9)
|
|
907
|
+
4. **Validation** \u2014 gstack \uD14C\uC2A4\uD2B8 + harness(\uC124\uACC4\u2194\uAD6C\uD604) \uAC80\uC99D
|
|
908
|
+
5. **Evaluation** \u2014 6\uCE74\uD14C\uACE0\uB9AC \uD488\uC9C8 \uD3C9\uAC00
|
|
825
909
|
|
|
826
910
|
### Stack
|
|
827
911
|
- **Languages**: ${stack}
|
|
@@ -829,15 +913,30 @@ This project uses PAI (Plugin-based AI) for structured AI development.
|
|
|
829
913
|
- **Mode**: ${interview.mode}
|
|
830
914
|
- **Config**: \`.pai/config.json\`
|
|
831
915
|
|
|
832
|
-
|
|
916
|
+
### \uD50C\uB7EC\uADF8\uC778 \uAC04 \uACC4\uC57D (\uD30C\uC774\uD504\uB77C\uC778 \uC5E3\uC9C0)
|
|
917
|
+
|
|
918
|
+
\`.pai/config.json\`\uC758 \`edges\` \uBC30\uC5F4\uC774 \uD50C\uB7EC\uADF8\uC778 \uAC04 \uBA85\uC2DC\uC801 \uACC4\uC57D\uC744 \uC815\uC758\uD569\uB2C8\uB2E4.
|
|
919
|
+
\uCF54\uB4DC \uC791\uC131 \uC2DC \uBC18\uB4DC\uC2DC \uACC4\uC57D\uC744 \uC9C0\uD0A4\uC138\uC694:
|
|
920
|
+
|
|
921
|
+
- **harness \u2192 openspec**: \`docs/openspec.md\`\uC758 \uC5D4\uB4DC\uD3EC\uC778\uD2B8 \uD14C\uC774\uBE14\uC774 \uAD6C\uD604\uACFC \uC77C\uCE58\uD574\uC57C \uD568 (\`pai test\` \uC2E4\uD328)
|
|
922
|
+
- **gstack \u2192 openspec**: \uAC01 \uC5D4\uB4DC\uD3EC\uC778\uD2B8\uB294 \uD14C\uC2A4\uD2B8\uAC00 \uC874\uC7AC\uD574\uC57C \uD568
|
|
923
|
+
- **omc \u2192 openspec**: \uB3C4\uBA54\uC778 \uAC1D\uCCB4\uB294 API \uC694\uCCAD/\uC751\uB2F5 \uBAA8\uB378\uACFC \uC815\uD569 (\uC0C8 \uC5D4\uB4DC\uD3EC\uC778\uD2B8 \uCD94\uAC00 \uC2DC omc.md\uB3C4 \uC5C5\uB370\uC774\uD2B8)
|
|
924
|
+
- **harness \u2192 omc**: \`.pai/omc.md\`\uC758 \uBE44\uC988\uB2C8\uC2A4 \uADDC\uCE59\uC774 \uCF54\uB4DC\uB85C \uAD6C\uD604\uB418\uC5B4\uC57C \uD568
|
|
833
925
|
|
|
834
926
|
### Global Rules
|
|
835
927
|
|
|
836
|
-
- **\uC2EC\uCE35 \uC778\uD130\uBDF0 \uC790\uB3D9 \uC218\uD589**: \
|
|
837
|
-
-
|
|
838
|
-
- **\
|
|
839
|
-
- **\
|
|
840
|
-
- **Git \uCEE4\uBC0B \uADDC\uCE59**: \uC791\uC5C5 \uB2E8\uC704\uBCC4\uB85C \uC758\uBBF8 \uC788\uB294 \uCEE4\uBC0B \uBA54\uC2DC\uC9C0
|
|
928
|
+
- **\uC2EC\uCE35 \uC778\uD130\uBDF0 \uC790\uB3D9 \uC218\uD589**: \uBAA8\uD638\uD55C \uACBD\uC6B0 AskUserQuestion\uC73C\uB85C \uC989\uC2DC \uC9C8\uBB38. \uCD94\uCE21 \uAE08\uC9C0.
|
|
929
|
+
- **\uC124\uACC4 \uBA3C\uC800**: \uCF54\uB4DC \uC804 \`docs/openspec.md\`\uB97C \uBA3C\uC800 \uC77D\uACE0, \uD544\uC694\uD55C \uC5D4\uB4DC\uD3EC\uC778\uD2B8/\uBAA8\uB378\uC744 \uC815\uC758.
|
|
930
|
+
- **\uACC4\uC57D \uC591\uBC29\uD5A5 \uC5C5\uB370\uC774\uD2B8**: API \uCD94\uAC00 \uC2DC openspec.md + omc.md \uB3D9\uC2DC \uC218\uC815. \uD558\uB098\uB9CC \uC5C5\uB370\uC774\uD2B8\uD558\uB294 PR \uAE08\uC9C0.
|
|
931
|
+
- **\uAC80\uC99D \uC6B0\uC120**: \`pai test\` \uC2E4\uD328 \uC2DC \uC218\uC815 \uBC94\uC704\uB97C \uC881\uD788\uACE0 \uC6D0\uC778\uC744 \uC815\uD655\uD788 \uC774\uD574\uD560 \uAC83. \`--skip\`/ disable \uAE08\uC9C0.
|
|
932
|
+
- **Git \uCEE4\uBC0B \uADDC\uCE59**: \uC791\uC5C5 \uB2E8\uC704\uBCC4\uB85C \uC758\uBBF8 \uC788\uB294 \uCEE4\uBC0B \uBA54\uC2DC\uC9C0.
|
|
933
|
+
|
|
934
|
+
### \uBA85\uB839\uC5B4
|
|
935
|
+
|
|
936
|
+
- \`pai status\` \u2014 \uD604\uC7AC \uD50C\uB7EC\uADF8\uC778/\uBAA8\uB4DC \uD655\uC778
|
|
937
|
+
- \`pai test\` \u2014 \uD14C\uC2A4\uD2B8 + \uD558\uB124\uC2A4 \uAC80\uC99D
|
|
938
|
+
- \`pai grade\` \u2014 6\uCE74\uD14C\uACE0\uB9AC \uD488\uC9C8 \uD3C9\uAC00
|
|
939
|
+
- \`pai add\` \u2014 \uD50C\uB7EC\uADF8\uC778 \uCD94\uAC00 / \uBAA8\uB4DC \uC2B9\uACA9
|
|
841
940
|
|
|
842
941
|
### Contribution Guide
|
|
843
942
|
|
|
@@ -2162,54 +2261,189 @@ async function provisionSupabase(ctx) {
|
|
|
2162
2261
|
async function provisionOpenSpec(ctx) {
|
|
2163
2262
|
await fs7.ensureDir(join4(ctx.cwd, "docs"));
|
|
2164
2263
|
const openspecPath = join4(ctx.cwd, "docs", "openspec.md");
|
|
2165
|
-
if (
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2264
|
+
if (await fs7.pathExists(openspecPath)) return;
|
|
2265
|
+
const langs = ctx.analysis?.stack.languages ?? [];
|
|
2266
|
+
const frameworks = ctx.analysis?.stack.frameworks ?? [];
|
|
2267
|
+
const pkgMgr = ctx.analysis?.stack.packageManager;
|
|
2268
|
+
const frontendFrameworks = frameworks.filter(
|
|
2269
|
+
(f) => ["React", "Vue", "Next.js", "Nuxt", "Svelte", "Angular"].includes(f)
|
|
2270
|
+
);
|
|
2271
|
+
const backendFrameworks = frameworks.filter(
|
|
2272
|
+
(f) => ["Express", "Fastify", "NestJS", "Koa", "Django", "Flask", "FastAPI", "Rails", "Spring"].includes(f)
|
|
2273
|
+
);
|
|
2274
|
+
const frontendLine = frontendFrameworks.length > 0 ? frontendFrameworks.join(" + ") : "<!-- TODO: \uD504\uB860\uD2B8\uC5D4\uB4DC \uD504\uB808\uC784\uC6CC\uD06C (React / Vue / Next.js \uB4F1) -->";
|
|
2275
|
+
const backendLine = backendFrameworks.length > 0 ? backendFrameworks.join(" + ") : langs.includes("TypeScript") || langs.includes("JavaScript") ? "<!-- TODO: Node.js \uBC31\uC5D4\uB4DC \uD504\uB808\uC784\uC6CC\uD06C (Express / Fastify / NestJS \uB4F1) -->" : "<!-- TODO: \uBC31\uC5D4\uB4DC \uD504\uB808\uC784\uC6CC\uD06C -->";
|
|
2276
|
+
const modeHint = ctx.mode === "prototype" ? "\uD504\uB85C\uD1A0\uD0C0\uC785 \u2014 \uBE60\uB978 \uAC80\uC99D \uBAA9\uC801. \uD575\uC2EC \uC2DC\uB098\uB9AC\uC624 1-2\uAC1C\uB9CC \uC815\uC758\uD574\uB3C4 \uCDA9\uBD84." : ctx.mode === "poc" ? "PoC \u2014 \uB3D9\uC791 \uB370\uBAA8. \uC8FC\uC694 \uAE30\uB2A5 3-5\uAC1C + \uD575\uC2EC API \uC5D4\uB4DC\uD3EC\uC778\uD2B8 \uC815\uC758 \uD544\uC694." : "\uC6B4\uC601 \u2014 \uD14C\uC2A4\uD2B8/QA \uB300\uC0C1. \uBAA8\uB4E0 \uAE30\uB2A5\uACFC API \uACC4\uC57D\uC744 \uC644\uC804\uD788 \uC815\uC758\uD574\uC57C \uD568.";
|
|
2277
|
+
await fs7.writeFile(openspecPath, [
|
|
2278
|
+
`# OpenSpec \u2014 ${ctx.projectName}`,
|
|
2279
|
+
"",
|
|
2280
|
+
`> **\uBAA8\uB4DC**: ${ctx.mode} \xB7 ${modeHint}`,
|
|
2281
|
+
`> **\uC704\uCE58**: \uC774 \uBB38\uC11C\uAC00 \uD30C\uC774\uD504\uB77C\uC778\uC758 \uC911\uC559 \uD5C8\uBE0C\uC785\uB2C8\uB2E4 \u2014 gstack, harness, omc\uAC00 \uBAA8\uB450 \uCC38\uC870.`,
|
|
2282
|
+
`> **\uC791\uC131 \uAC00\uC774\uB4DC**: \`/pai design\` \uC2E4\uD589 \uC2DC Claude\uAC00 \uC2EC\uCE35 \uC778\uD130\uBDF0\uB85C \uBE48 \uC139\uC158\uC744 \uCC44\uC6C1\uB2C8\uB2E4.`,
|
|
2283
|
+
"",
|
|
2284
|
+
"---",
|
|
2285
|
+
"",
|
|
2286
|
+
"## 1. \uBAA9\uC801 (Purpose)",
|
|
2287
|
+
"",
|
|
2288
|
+
"<!-- TODO: \uC774 \uC11C\uBE44\uC2A4\uAC00 \uBB34\uC5C7\uC744 \uD574\uACB0\uD558\uB294\uC9C0 2-3\uBB38\uC7A5\uC73C\uB85C \uAE30\uC220 -->",
|
|
2289
|
+
"<!-- \uC608\uC2DC:",
|
|
2290
|
+
" - \uB204\uAD6C\uC758 \uC5B4\uB5A4 \uBB38\uC81C\uB97C \uD574\uACB0\uD558\uB294\uAC00?",
|
|
2291
|
+
" - \uAE30\uC874 \uB300\uC548(\uC788\uB2E4\uBA74) \uB300\uBE44 \uCC28\uBCC4\uC810\uC740?",
|
|
2292
|
+
"-->",
|
|
2293
|
+
"",
|
|
2294
|
+
"## 2. \uC0AC\uC6A9\uC790 (Users)",
|
|
2295
|
+
"",
|
|
2296
|
+
"<!-- TODO: \uC2E4\uC81C \uC0AC\uC6A9\uC790 \uC5ED\uD560\uC744 \uC815\uC758 \u2014 role, \uAD8C\uD55C, \uC8FC\uC694 \uC0AC\uC6A9 \uC2DC\uB098\uB9AC\uC624 -->",
|
|
2297
|
+
"",
|
|
2298
|
+
"| \uC5ED\uD560 | \uAD8C\uD55C | \uC8FC\uC694 \uC0AC\uC6A9 \uC2DC\uB098\uB9AC\uC624 |",
|
|
2299
|
+
"|------|------|-------------------|",
|
|
2300
|
+
"| \uC77C\uBC18 \uC0AC\uC6A9\uC790 | \uC77D\uAE30/\uC4F0\uAE30 | (\uC608: \uAC8C\uC2DC\uAE00 \uC791\uC131, \uAC80\uC0C9) |",
|
|
2301
|
+
"| \uAD00\uB9AC\uC790 | \uC804\uCCB4 | (\uC608: \uC0AC\uC6A9\uC790 \uAD00\uB9AC, \uD1B5\uACC4 \uC870\uD68C) |",
|
|
2302
|
+
"",
|
|
2303
|
+
"## 3. \uD575\uC2EC \uAE30\uB2A5 (Features)",
|
|
2304
|
+
"",
|
|
2305
|
+
"<!-- TODO: MVP \uAE30\uB2A5 \uBAA9\uB85D. \uCCB4\uD06C\uBC15\uC2A4\uB85C \uC9C4\uD589\uC0C1\uD0DC \uD45C\uC2DC -->",
|
|
2306
|
+
"",
|
|
2307
|
+
"- [ ] \uC0AC\uC6A9\uC790 \uC778\uC99D (\uB85C\uADF8\uC778/\uB85C\uADF8\uC544\uC6C3)",
|
|
2308
|
+
"- [ ] \uD575\uC2EC \uAE30\uB2A5 1",
|
|
2309
|
+
"- [ ] \uD575\uC2EC \uAE30\uB2A5 2",
|
|
2310
|
+
"",
|
|
2311
|
+
"## 4. \uAE30\uC220 \uC2A4\uD0DD (Stack)",
|
|
2312
|
+
"",
|
|
2313
|
+
`- **Language**: ${langs.length > 0 ? langs.join(", ") : "<!-- TODO: \uC5B8\uC5B4 -->"}`,
|
|
2314
|
+
`- **Frontend**: ${frontendLine}`,
|
|
2315
|
+
`- **Backend**: ${backendLine}`,
|
|
2316
|
+
`- **Database**: <!-- TODO: PostgreSQL / MySQL / SQLite / MongoDB \uB4F1 -->`,
|
|
2317
|
+
`- **Package Manager**: ${pkgMgr ?? "<!-- TODO -->"}`,
|
|
2318
|
+
`- **Deployment**: <!-- TODO: Vercel / AWS / Docker \uB4F1 -->`,
|
|
2319
|
+
"",
|
|
2320
|
+
"## 5. API \uC5D4\uB4DC\uD3EC\uC778\uD2B8 (Endpoints)",
|
|
2321
|
+
"",
|
|
2322
|
+
"<!-- \uC911\uC694: \uC774 \uD45C\uAC00 gstack \uD14C\uC2A4\uD2B8 \uD0C0\uAC9F + harness \uAC80\uC99D \uD0C0\uAC9F\uC785\uB2C8\uB2E4.",
|
|
2323
|
+
" \uC2E0\uADDC \uC5D4\uB4DC\uD3EC\uC778\uD2B8 \uCD94\uAC00 \uC2DC \uBC18\uB4DC\uC2DC \uC5EC\uAE30 \uBA3C\uC800 \uC120\uC5B8. -->",
|
|
2324
|
+
"",
|
|
2325
|
+
"| Method | Path | \uC124\uBA85 | \uC694\uCCAD \uBAA8\uB378 | \uC751\uB2F5 \uBAA8\uB378 |",
|
|
2326
|
+
"|--------|------|------|-----------|-----------|",
|
|
2327
|
+
"| GET | /api/health | \uD5EC\uC2A4\uCCB4\uD06C | - | `{ ok: true }` |",
|
|
2328
|
+
"| POST | /api/auth/login | \uB85C\uADF8\uC778 | `{ email, password }` | `{ token }` |",
|
|
2329
|
+
"<!-- TODO: \uD504\uB85C\uC81D\uD2B8 \uACE0\uC720 \uC5D4\uB4DC\uD3EC\uC778\uD2B8 \uCD94\uAC00 -->",
|
|
2330
|
+
"",
|
|
2331
|
+
"## 6. \uBE44\uAE30\uB2A5 \uC694\uAD6C\uC0AC\uD56D (\uC120\uD0DD)",
|
|
2332
|
+
"",
|
|
2333
|
+
"<!-- TODO: \uC131\uB2A5/\uBCF4\uC548/\uD655\uC7A5\uC131 \uC694\uAD6C \u2014 production \uBAA8\uB4DC\uC5D0\uC11C \uAD8C\uC7A5 -->",
|
|
2334
|
+
"",
|
|
2335
|
+
"---",
|
|
2336
|
+
"",
|
|
2337
|
+
"## \uD30C\uC774\uD504\uB77C\uC778 \uACC4\uC57D (\uC774 \uBB38\uC11C\uAC00 \uCC38\uC870\uB418\uB294 \uACF3)",
|
|
2338
|
+
"",
|
|
2339
|
+
"- `harness.json` \u2192 `specFile`\uB85C \uC774 \uBB38\uC11C \uCC38\uC870 (\uC124\uACC4\u2194\uAD6C\uD604 \uC77C\uCE58 \uAC80\uC99D)",
|
|
2340
|
+
"- `gstack.json` \u2192 \uAC01 \uC5D4\uB4DC\uD3EC\uC778\uD2B8\uC5D0 \uB300\uD55C \uD14C\uC2A4\uD2B8 \uC874\uC7AC \uD655\uC778",
|
|
2341
|
+
"- `.pai/omc.md` \u2192 \uB3C4\uBA54\uC778 \uAC1D\uCCB4\uAC00 \uC694\uCCAD/\uC751\uB2F5 \uBAA8\uB378\uACFC \uC815\uD569\uD558\uB294\uC9C0 \uD655\uC778",
|
|
2342
|
+
""
|
|
2343
|
+
].join("\n") + "\n");
|
|
2344
|
+
}
|
|
2345
|
+
function buildOMCSkeleton(projectName, mode) {
|
|
2346
|
+
const lines = [
|
|
2347
|
+
`# OMC \u2014 Object Model Context (${projectName})`,
|
|
2348
|
+
"",
|
|
2349
|
+
`> **\uBAA8\uB4DC**: ${mode}`,
|
|
2350
|
+
"> **\uC5ED\uD560**: AI\uAC00 \uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uB3C4\uBA54\uC778\uC744 \uC774\uD574\uD558\uAE30 \uC704\uD55C \uD575\uC2EC \uAC1D\uCCB4 \uBAA8\uB378.",
|
|
2351
|
+
"> **\uD30C\uC774\uD504\uB77C\uC778 \uACC4\uC57D**:",
|
|
2352
|
+
"> - \uC5EC\uAE30 \uC815\uC758\uB41C \uB3C4\uBA54\uC778 \uAC1D\uCCB4\uB294 `docs/openspec.md`\uC758 API \uC694\uCCAD/\uC751\uB2F5 \uBAA8\uB378\uACFC \uC815\uD569\uD574\uC57C \uD568",
|
|
2353
|
+
"> - `.pai/harness.json`\uC758 `businessRules` \uAC80\uC99D\uC774 \uC774 \uBB38\uC11C\uB97C \uCC38\uC870",
|
|
2354
|
+
"> **\uC791\uC131 \uAC00\uC774\uB4DC**: `/pai design` \uC2E4\uD589 \uC2DC Claude\uAC00 openspec.md \uC5D4\uB4DC\uD3EC\uC778\uD2B8\uC5D0 \uB9DE\uCDB0 \uBE48 \uC139\uC158\uC744 \uCC44\uC6C1\uB2C8\uB2E4.",
|
|
2355
|
+
"",
|
|
2356
|
+
"---",
|
|
2357
|
+
"",
|
|
2358
|
+
"## \uB3C4\uBA54\uC778 \uAC1D\uCCB4",
|
|
2359
|
+
"",
|
|
2360
|
+
"### User",
|
|
2361
|
+
"",
|
|
2362
|
+
"```ts",
|
|
2363
|
+
"interface User {",
|
|
2364
|
+
" id: string;",
|
|
2365
|
+
" email: string;",
|
|
2366
|
+
" name: string;",
|
|
2367
|
+
" role: 'admin' | 'member';",
|
|
2368
|
+
" createdAt: Date;",
|
|
2369
|
+
"}",
|
|
2370
|
+
"```",
|
|
2371
|
+
"",
|
|
2372
|
+
"<!-- TODO: \uD504\uB85C\uC81D\uD2B8 \uACE0\uC720 \uB3C4\uBA54\uC778 \uAC1D\uCCB4 \uCD94\uAC00. openspec.md\uC758 \uC5D4\uB4DC\uD3EC\uC778\uD2B8\uAC00 \uC694\uAD6C\uD558\uB294 \uBAA8\uB378\uC744 \uC5EC\uAE30 \uBA3C\uC800 \uC815\uC758 -->",
|
|
2373
|
+
""
|
|
2374
|
+
];
|
|
2375
|
+
if (mode === "poc" || mode === "production") {
|
|
2376
|
+
lines.push(
|
|
2377
|
+
"### Session",
|
|
2179
2378
|
"",
|
|
2180
|
-
"
|
|
2181
|
-
"
|
|
2182
|
-
"
|
|
2183
|
-
"
|
|
2379
|
+
"```ts",
|
|
2380
|
+
"interface Session {",
|
|
2381
|
+
" token: string;",
|
|
2382
|
+
" userId: string;",
|
|
2383
|
+
" expiresAt: Date;",
|
|
2384
|
+
"}",
|
|
2385
|
+
"```",
|
|
2386
|
+
""
|
|
2387
|
+
);
|
|
2388
|
+
}
|
|
2389
|
+
if (mode === "production") {
|
|
2390
|
+
lines.push(
|
|
2391
|
+
"### AuditLog",
|
|
2184
2392
|
"",
|
|
2185
|
-
"
|
|
2186
|
-
"
|
|
2187
|
-
"
|
|
2188
|
-
"
|
|
2189
|
-
|
|
2393
|
+
"```ts",
|
|
2394
|
+
"interface AuditLog {",
|
|
2395
|
+
" id: string;",
|
|
2396
|
+
" userId: string;",
|
|
2397
|
+
" action: 'create' | 'update' | 'delete' | 'read';",
|
|
2398
|
+
" resource: string;",
|
|
2399
|
+
" at: Date;",
|
|
2400
|
+
" metadata?: Record<string, unknown>;",
|
|
2401
|
+
"}",
|
|
2402
|
+
"```",
|
|
2403
|
+
""
|
|
2404
|
+
);
|
|
2405
|
+
}
|
|
2406
|
+
lines.push(
|
|
2407
|
+
"## \uBE44\uC988\uB2C8\uC2A4 \uADDC\uCE59",
|
|
2408
|
+
"",
|
|
2409
|
+
'<!-- \uADDC\uCE59\uC740 harness\uAC00 \uAC80\uC99D \uB300\uC0C1\uC73C\uB85C \uCC38\uC870. \uAC01 \uADDC\uCE59\uC740 "\uBB34\uC5C7\uC774 \uCC38\uC774\uC5B4\uC57C \uD558\uB294\uAC00"\uB85C \uAE30\uC220. -->',
|
|
2410
|
+
"",
|
|
2411
|
+
"- **\uC778\uC99D \uD544\uC218**: `User.id`\uAC00 \uC5C6\uB294 \uC694\uCCAD\uC740 \uAD00\uB9AC\uC790 API \uC811\uADFC \uBD88\uAC00",
|
|
2412
|
+
"- **\uC774\uBA54\uC77C \uC720\uC77C\uC131**: `User.email`\uC740 \uC2DC\uC2A4\uD15C \uC804\uCCB4\uC5D0\uC11C \uC720\uC77C"
|
|
2413
|
+
);
|
|
2414
|
+
if (mode === "poc" || mode === "production") {
|
|
2415
|
+
lines.push(
|
|
2416
|
+
"- **\uC138\uC158 \uB9CC\uB8CC**: `Session.expiresAt < now()` \uC774\uBA74 \uBAA8\uB4E0 API 401"
|
|
2417
|
+
);
|
|
2418
|
+
}
|
|
2419
|
+
if (mode === "production") {
|
|
2420
|
+
lines.push(
|
|
2421
|
+
"- **\uAC10\uC0AC \uB85C\uADF8**: \uBAA8\uB4E0 \uC4F0\uAE30 \uC561\uC158(create/update/delete)\uC740 `AuditLog`\uC5D0 \uAE30\uB85D",
|
|
2422
|
+
'- **\uAD8C\uD55C \uBD84\uB9AC**: `role === "member"` \uC0AC\uC6A9\uC790\uB294 \uB2E4\uB978 \uC0AC\uC6A9\uC790\uC758 \uB9AC\uC18C\uC2A4 \uC218\uC815 \uBD88\uAC00'
|
|
2423
|
+
);
|
|
2190
2424
|
}
|
|
2425
|
+
lines.push(
|
|
2426
|
+
"",
|
|
2427
|
+
"<!-- TODO: \uD504\uB85C\uC81D\uD2B8 \uACE0\uC720 \uBE44\uC988\uB2C8\uC2A4 \uADDC\uCE59 \uCD94\uAC00. \uAC01 \uADDC\uCE59\uC740 \uAC00\uB2A5\uD558\uBA74 \uD14C\uC2A4\uD2B8 \uAC00\uB2A5\uD55C \uD615\uD0DC\uB85C. -->",
|
|
2428
|
+
"",
|
|
2429
|
+
"## \uB3C4\uBA54\uC778 - API \uB9E4\uD551 (\uC120\uD0DD)",
|
|
2430
|
+
"",
|
|
2431
|
+
"<!-- openspec.md \uC5D4\uB4DC\uD3EC\uC778\uD2B8\uC640 \uB3C4\uBA54\uC778 \uAC1D\uCCB4 \uC5F0\uACB0. harness \uAC80\uC99D \uC6A9. -->",
|
|
2432
|
+
"",
|
|
2433
|
+
"| Endpoint | \uC694\uCCAD \uBAA8\uB378 | \uC751\uB2F5 \uBAA8\uB378 | \uBE44\uACE0 |",
|
|
2434
|
+
"|----------|-----------|-----------|------|",
|
|
2435
|
+
"| POST /api/auth/login | `{ email, password }` | `Session` | \uC131\uACF5 \uC2DC token \uBC1C\uAE09 |",
|
|
2436
|
+
"<!-- TODO: \uCD94\uAC00 \uC5D4\uB4DC\uD3EC\uC778\uD2B8 \uB9E4\uD551 -->",
|
|
2437
|
+
""
|
|
2438
|
+
);
|
|
2439
|
+
return lines.join("\n") + "\n";
|
|
2191
2440
|
}
|
|
2192
2441
|
async function provisionOMC(ctx) {
|
|
2193
2442
|
await fs7.ensureDir(join4(ctx.cwd, ".pai"));
|
|
2194
2443
|
const omcPath = join4(ctx.cwd, ".pai", "omc.md");
|
|
2195
2444
|
if (!await fs7.pathExists(omcPath)) {
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
"",
|
|
2199
|
-
"> AI\uAC00 \uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uB3C4\uBA54\uC778\uC744 \uC774\uD574\uD558\uAE30 \uC704\uD55C \uD575\uC2EC \uAC1D\uCCB4 \uBAA8\uB378",
|
|
2200
|
-
"",
|
|
2201
|
-
"## \uB3C4\uBA54\uC778 \uAC1D\uCCB4",
|
|
2202
|
-
"",
|
|
2203
|
-
"### User",
|
|
2204
|
-
"```",
|
|
2205
|
-
"id: string",
|
|
2206
|
-
"email: string",
|
|
2207
|
-
'role: "admin" | "member"',
|
|
2208
|
-
"```",
|
|
2209
|
-
"",
|
|
2210
|
-
"## \uBE44\uC988\uB2C8\uC2A4 \uADDC\uCE59",
|
|
2211
|
-
"- (\uC5EC\uAE30\uC5D0 \uD575\uC2EC \uBE44\uC988\uB2C8\uC2A4 \uB85C\uC9C1\uC744 \uAE30\uC220)"
|
|
2212
|
-
].join("\n") + "\n");
|
|
2445
|
+
const skeleton = buildOMCSkeleton(ctx.projectName, ctx.mode);
|
|
2446
|
+
await fs7.writeFile(omcPath, skeleton);
|
|
2213
2447
|
}
|
|
2214
2448
|
const omcDir = join4(ctx.cwd, ".omc");
|
|
2215
2449
|
await fs7.ensureDir(omcDir);
|
|
@@ -3181,6 +3415,183 @@ description: "\uD50C\uB7EC\uADF8\uC778 \uCD94\uAC00 \uC124\uCE58 \u2014 Mockup\u
|
|
|
3181
3415
|
}
|
|
3182
3416
|
});
|
|
3183
3417
|
|
|
3418
|
+
// src/core/detector.ts
|
|
3419
|
+
var detector_exports = {};
|
|
3420
|
+
__export(detector_exports, {
|
|
3421
|
+
PLUGIN_META: () => PLUGIN_META,
|
|
3422
|
+
PLUGIN_SIGNATURES: () => PLUGIN_SIGNATURES,
|
|
3423
|
+
deriveEdges: () => deriveEdges,
|
|
3424
|
+
getBasePluginsForMode: () => getBasePluginsForMode,
|
|
3425
|
+
getPluginsForMode: () => getPluginsForMode,
|
|
3426
|
+
nextMode: () => nextMode,
|
|
3427
|
+
pluginsAddedByPromotion: () => pluginsAddedByPromotion,
|
|
3428
|
+
scanProjectState: () => scanProjectState
|
|
3429
|
+
});
|
|
3430
|
+
import path4 from "path";
|
|
3431
|
+
import fs9 from "fs-extra";
|
|
3432
|
+
async function scanProjectState(cwd) {
|
|
3433
|
+
const result = {
|
|
3434
|
+
isNewProject: true,
|
|
3435
|
+
hasPaiConfig: false,
|
|
3436
|
+
projectMode: null,
|
|
3437
|
+
installedPlugins: [],
|
|
3438
|
+
missingPlugins: [],
|
|
3439
|
+
details: {}
|
|
3440
|
+
};
|
|
3441
|
+
const paiConfigPath = path4.join(cwd, ".pai", "config.json");
|
|
3442
|
+
if (await fs9.pathExists(paiConfigPath)) {
|
|
3443
|
+
result.hasPaiConfig = true;
|
|
3444
|
+
try {
|
|
3445
|
+
const config = await fs9.readJson(paiConfigPath);
|
|
3446
|
+
result.projectMode = config.mode != null ? normalizeMode(config.mode) : null;
|
|
3447
|
+
} catch {
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
for (const [key, signatures] of Object.entries(PLUGIN_SIGNATURES)) {
|
|
3451
|
+
const installed = await Promise.any(
|
|
3452
|
+
signatures.map(async (sig) => {
|
|
3453
|
+
if (await fs9.pathExists(path4.join(cwd, sig))) return true;
|
|
3454
|
+
throw new Error("not found");
|
|
3455
|
+
})
|
|
3456
|
+
).catch(() => false);
|
|
3457
|
+
result.details[key] = { installed, signatures };
|
|
3458
|
+
if (installed) {
|
|
3459
|
+
result.installedPlugins.push(key);
|
|
3460
|
+
} else {
|
|
3461
|
+
result.missingPlugins.push(key);
|
|
3462
|
+
}
|
|
3463
|
+
}
|
|
3464
|
+
const hasAnyContent = result.installedPlugins.length > 0 || await fs9.pathExists(path4.join(cwd, "package.json")) || await fs9.pathExists(path4.join(cwd, "src")) || await fs9.pathExists(path4.join(cwd, "README.md"));
|
|
3465
|
+
result.isNewProject = !hasAnyContent;
|
|
3466
|
+
return result;
|
|
3467
|
+
}
|
|
3468
|
+
function getPluginsForMode(mode) {
|
|
3469
|
+
return Object.entries(PLUGIN_META).filter(([, meta]) => meta.modes.includes(mode)).map(([key, meta]) => ({ key, ...meta }));
|
|
3470
|
+
}
|
|
3471
|
+
function getBasePluginsForMode(mode) {
|
|
3472
|
+
return Object.entries(PLUGIN_META).filter(([, meta]) => meta.modes.includes(mode) && meta.required).map(([key]) => key);
|
|
3473
|
+
}
|
|
3474
|
+
function nextMode(current) {
|
|
3475
|
+
if (current === "prototype") return "poc";
|
|
3476
|
+
if (current === "poc") return "production";
|
|
3477
|
+
return null;
|
|
3478
|
+
}
|
|
3479
|
+
function pluginsAddedByPromotion(from, to) {
|
|
3480
|
+
const fromSet = new Set(getPluginsForMode(from).map((p) => p.key));
|
|
3481
|
+
return getPluginsForMode(to).map((p) => p.key).filter((k) => !fromSet.has(k));
|
|
3482
|
+
}
|
|
3483
|
+
function deriveEdges(installedPlugins) {
|
|
3484
|
+
const has = new Set(installedPlugins);
|
|
3485
|
+
const edges = [];
|
|
3486
|
+
if (has.has("harness") && has.has("openspec")) {
|
|
3487
|
+
edges.push({
|
|
3488
|
+
from: "harness",
|
|
3489
|
+
to: "openspec",
|
|
3490
|
+
via: "specFile",
|
|
3491
|
+
path: "docs/openspec.md",
|
|
3492
|
+
description: "harness\uAC00 openspec\uC758 API \uC5D4\uB4DC\uD3EC\uC778\uD2B8 \uBAA9\uB85D\uC744 \uC77D\uC5B4 \uAD6C\uD604 \uC77C\uCE58 \uAC80\uC99D"
|
|
3493
|
+
});
|
|
3494
|
+
}
|
|
3495
|
+
if (has.has("gstack") && has.has("openspec")) {
|
|
3496
|
+
edges.push({
|
|
3497
|
+
from: "gstack",
|
|
3498
|
+
to: "openspec",
|
|
3499
|
+
via: "endpoints",
|
|
3500
|
+
path: "docs/openspec.md",
|
|
3501
|
+
description: "gstack \uD14C\uC2A4\uD2B8\uAC00 openspec\uC758 \uAC01 \uC5D4\uB4DC\uD3EC\uC778\uD2B8\uC5D0 \uB300\uD55C \uD14C\uC2A4\uD2B8 \uC874\uC7AC\uB97C \uC694\uAD6C"
|
|
3502
|
+
});
|
|
3503
|
+
}
|
|
3504
|
+
if (has.has("omc") && has.has("openspec")) {
|
|
3505
|
+
edges.push({
|
|
3506
|
+
from: "omc",
|
|
3507
|
+
to: "openspec",
|
|
3508
|
+
via: "domain",
|
|
3509
|
+
path: ".pai/omc.md",
|
|
3510
|
+
description: "omc\uC758 \uB3C4\uBA54\uC778 \uAC1D\uCCB4\uAC00 openspec\uC758 API \uC694\uCCAD/\uC751\uB2F5 \uBAA8\uB378\uACFC \uC815\uD569"
|
|
3511
|
+
});
|
|
3512
|
+
}
|
|
3513
|
+
if (has.has("harness") && has.has("omc")) {
|
|
3514
|
+
edges.push({
|
|
3515
|
+
from: "harness",
|
|
3516
|
+
to: "omc",
|
|
3517
|
+
via: "businessRules",
|
|
3518
|
+
path: ".pai/omc.md",
|
|
3519
|
+
description: "harness\uAC00 omc\uC758 \uBE44\uC988\uB2C8\uC2A4 \uADDC\uCE59\uC744 \uAC80\uC99D \uADDC\uCE59\uC73C\uB85C \uCC38\uC870"
|
|
3520
|
+
});
|
|
3521
|
+
}
|
|
3522
|
+
return edges;
|
|
3523
|
+
}
|
|
3524
|
+
var PLUGIN_SIGNATURES, PLUGIN_META;
|
|
3525
|
+
var init_detector = __esm({
|
|
3526
|
+
"src/core/detector.ts"() {
|
|
3527
|
+
"use strict";
|
|
3528
|
+
init_config();
|
|
3529
|
+
PLUGIN_SIGNATURES = {
|
|
3530
|
+
github: [".git", ".github"],
|
|
3531
|
+
vercel: [".vercel", "vercel.json"],
|
|
3532
|
+
supabase: ["supabase", ".supabase"],
|
|
3533
|
+
openspec: ["openspec.md", "docs/openspec.md", ".pai/openspec.md"],
|
|
3534
|
+
omc: [".pai/omc.md", "omc.config.js", "omc.config.json"],
|
|
3535
|
+
gstack: [".pai/gstack.json", "gstack.config.js"],
|
|
3536
|
+
roboco: [".pai/roboco.json", "roboco.config.js", ".roboco"],
|
|
3537
|
+
harness: [".pai/harness.json", "harness.config.js", ".harness"]
|
|
3538
|
+
};
|
|
3539
|
+
PLUGIN_META = {
|
|
3540
|
+
github: {
|
|
3541
|
+
label: "GitHub \uB808\uD3EC & \uD3F4\uB354 \uAD6C\uC870",
|
|
3542
|
+
description: "\uB808\uD3EC \uCD08\uAE30\uD654, .gitignore, \uAE30\uBCF8 \uBE0C\uB79C\uCE58 \uC124\uC815",
|
|
3543
|
+
modes: ["prototype", "poc", "production"],
|
|
3544
|
+
required: true
|
|
3545
|
+
},
|
|
3546
|
+
openspec: {
|
|
3547
|
+
label: "OpenSpec (PRD \uC124\uACC4)",
|
|
3548
|
+
description: "AI \uAE30\uBC18 PRD \uC0DD\uC131 \uBC0F \uC2A4\uD399 \uBB38\uC11C\uD654",
|
|
3549
|
+
modes: ["prototype", "poc", "production"],
|
|
3550
|
+
required: true
|
|
3551
|
+
},
|
|
3552
|
+
roboco: {
|
|
3553
|
+
label: "roboco (AI \uC9C4\uB2E8)",
|
|
3554
|
+
description: "\uC124\uCE58 \uC0C1\uD0DC \uD3C9\uAC00 \uBC0F AI \uC900\uBE44\uB3C4 \uB9AC\uD3EC\uD2B8 \uC0DD\uC131",
|
|
3555
|
+
modes: ["prototype", "poc", "production"],
|
|
3556
|
+
required: false,
|
|
3557
|
+
url: "https://github.com/SoInKyu/roboco-cli"
|
|
3558
|
+
},
|
|
3559
|
+
omc: {
|
|
3560
|
+
label: "OMC (oh-my-claudecode)",
|
|
3561
|
+
description: "\uAC1D\uCCB4 \uBAA8\uB378 \uCEE8\uD14D\uC2A4\uD2B8 + Claude Code \uBA40\uD2F0 \uC5D0\uC774\uC804\uD2B8 \uC624\uCF00\uC2A4\uD2B8\uB808\uC774\uC158",
|
|
3562
|
+
modes: ["poc", "production"],
|
|
3563
|
+
required: false,
|
|
3564
|
+
url: "https://github.com/SoInKyu/oh-my-claudecode"
|
|
3565
|
+
},
|
|
3566
|
+
vercel: {
|
|
3567
|
+
label: "Vercel \uBC30\uD3EC \uC5F0\uB3D9",
|
|
3568
|
+
description: "\uC790\uB3D9 \uBC30\uD3EC \uD30C\uC774\uD504\uB77C\uC778 \uBC0F Preview URL \uC124\uC815",
|
|
3569
|
+
modes: ["poc", "production"],
|
|
3570
|
+
required: false
|
|
3571
|
+
},
|
|
3572
|
+
gstack: {
|
|
3573
|
+
label: "gstack (QA / \uD488\uC9C8\uAD00\uB9AC)",
|
|
3574
|
+
description: "\uD14C\uC2A4\uD2B8 \uC790\uB3D9\uD654 \uBC0F \uD488\uC9C8 \uAE30\uC900 \uC124\uC815",
|
|
3575
|
+
modes: ["production"],
|
|
3576
|
+
required: false,
|
|
3577
|
+
url: "https://github.com/SoInKyu/gstack"
|
|
3578
|
+
},
|
|
3579
|
+
harness: {
|
|
3580
|
+
label: "Harness Engineering (\uAC80\uC99D \uC790\uB3D9\uD654)",
|
|
3581
|
+
description: "\uC124\uACC4(OpenSpec)\uC640 \uAD6C\uD604 \uC77C\uCE58 \uC5EC\uBD80 \uC790\uB3D9 \uCCB4\uD06C",
|
|
3582
|
+
modes: ["production"],
|
|
3583
|
+
required: false
|
|
3584
|
+
},
|
|
3585
|
+
supabase: {
|
|
3586
|
+
label: "Supabase (DB & Auth)",
|
|
3587
|
+
description: "\uB370\uC774\uD130\uBCA0\uC774\uC2A4, \uC778\uC99D, API \uD0A4 \uC790\uB3D9 \uC5F0\uB3D9",
|
|
3588
|
+
modes: ["production"],
|
|
3589
|
+
required: false
|
|
3590
|
+
}
|
|
3591
|
+
};
|
|
3592
|
+
}
|
|
3593
|
+
});
|
|
3594
|
+
|
|
3184
3595
|
// src/stages/environment/doctor.ts
|
|
3185
3596
|
var doctor_exports = {};
|
|
3186
3597
|
__export(doctor_exports, {
|
|
@@ -3188,7 +3599,7 @@ __export(doctor_exports, {
|
|
|
3188
3599
|
});
|
|
3189
3600
|
import { join as join6 } from "path";
|
|
3190
3601
|
import { homedir } from "os";
|
|
3191
|
-
import
|
|
3602
|
+
import fs10 from "fs-extra";
|
|
3192
3603
|
async function runDoctor() {
|
|
3193
3604
|
section("PAI Doctor \u2014 \uD658\uACBD \uC9C4\uB2E8");
|
|
3194
3605
|
const checks = [];
|
|
@@ -3208,7 +3619,7 @@ async function runDoctor() {
|
|
|
3208
3619
|
fix: claudeCheck.ok ? void 0 : "npm install -g @anthropic-ai/claude-code"
|
|
3209
3620
|
});
|
|
3210
3621
|
const globalConfigPath = join6(homedir(), ".pai", "config.json");
|
|
3211
|
-
const hasGlobalConfig = await
|
|
3622
|
+
const hasGlobalConfig = await fs10.pathExists(globalConfigPath);
|
|
3212
3623
|
checks.push({
|
|
3213
3624
|
label: "\uAE00\uB85C\uBC8C \uC124\uC815",
|
|
3214
3625
|
ok: true,
|
|
@@ -3220,46 +3631,348 @@ async function runDoctor() {
|
|
|
3220
3631
|
hasSdk = true;
|
|
3221
3632
|
} catch {
|
|
3222
3633
|
}
|
|
3223
|
-
checks.push({
|
|
3224
|
-
label: "Agent SDK",
|
|
3225
|
-
ok: true,
|
|
3226
|
-
// optional이므로 항상 OK
|
|
3227
|
-
detail: hasSdk ? "\uC124\uCE58\uB428 (AI \uAE30\uB2A5 \uD65C\uC131\uD654)" : "\uBBF8\uC124\uCE58 (\uC815\uC801 \uBD84\uC11D \uBAA8\uB4DC)"
|
|
3228
|
-
});
|
|
3229
|
-
console.log("");
|
|
3230
|
-
let passed = 0;
|
|
3231
|
-
for (const check of checks) {
|
|
3232
|
-
const icon = check.ok ? "\u2713" : "\u2717";
|
|
3233
|
-
const pad = " ".repeat(Math.max(1, 20 - check.label.length));
|
|
3234
|
-
if (check.ok) {
|
|
3235
|
-
success(`${icon} ${check.label}${pad}${check.detail}`);
|
|
3236
|
-
passed++;
|
|
3237
|
-
} else {
|
|
3238
|
-
error(`${icon} ${check.label}${pad}${check.detail}`);
|
|
3239
|
-
if (check.fix) {
|
|
3240
|
-
info(` \u2192 ${check.fix}`);
|
|
3634
|
+
checks.push({
|
|
3635
|
+
label: "Agent SDK",
|
|
3636
|
+
ok: true,
|
|
3637
|
+
// optional이므로 항상 OK
|
|
3638
|
+
detail: hasSdk ? "\uC124\uCE58\uB428 (AI \uAE30\uB2A5 \uD65C\uC131\uD654)" : "\uBBF8\uC124\uCE58 (\uC815\uC801 \uBD84\uC11D \uBAA8\uB4DC)"
|
|
3639
|
+
});
|
|
3640
|
+
console.log("");
|
|
3641
|
+
let passed = 0;
|
|
3642
|
+
for (const check of checks) {
|
|
3643
|
+
const icon = check.ok ? "\u2713" : "\u2717";
|
|
3644
|
+
const pad = " ".repeat(Math.max(1, 20 - check.label.length));
|
|
3645
|
+
if (check.ok) {
|
|
3646
|
+
success(`${icon} ${check.label}${pad}${check.detail}`);
|
|
3647
|
+
passed++;
|
|
3648
|
+
} else {
|
|
3649
|
+
error(`${icon} ${check.label}${pad}${check.detail}`);
|
|
3650
|
+
if (check.fix) {
|
|
3651
|
+
info(` \u2192 ${check.fix}`);
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
console.log("");
|
|
3656
|
+
info(`${passed}/${checks.length} \uD56D\uBAA9 \uD1B5\uACFC`);
|
|
3657
|
+
if (passed < checks.length) {
|
|
3658
|
+
process.exitCode = 1;
|
|
3659
|
+
}
|
|
3660
|
+
}
|
|
3661
|
+
async function checkCommand(cmd, args) {
|
|
3662
|
+
try {
|
|
3663
|
+
const { execa } = await import("execa");
|
|
3664
|
+
const { stdout } = await execa(cmd, args, { timeout: 1e4 });
|
|
3665
|
+
return { ok: true, detail: stdout.trim().split("\n")[0] ?? "ok" };
|
|
3666
|
+
} catch {
|
|
3667
|
+
return { ok: false, detail: "not found" };
|
|
3668
|
+
}
|
|
3669
|
+
}
|
|
3670
|
+
var init_doctor = __esm({
|
|
3671
|
+
"src/stages/environment/doctor.ts"() {
|
|
3672
|
+
"use strict";
|
|
3673
|
+
init_ui();
|
|
3674
|
+
}
|
|
3675
|
+
});
|
|
3676
|
+
|
|
3677
|
+
// src/utils/github-fetch.ts
|
|
3678
|
+
import path5 from "path";
|
|
3679
|
+
import fs11 from "fs-extra";
|
|
3680
|
+
async function httpGet(url, timeoutMs, accept) {
|
|
3681
|
+
const controller = new AbortController();
|
|
3682
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
3683
|
+
try {
|
|
3684
|
+
return await fetch(url, {
|
|
3685
|
+
signal: controller.signal,
|
|
3686
|
+
headers: { "Accept": accept, "User-Agent": "pai-zero" }
|
|
3687
|
+
});
|
|
3688
|
+
} finally {
|
|
3689
|
+
clearTimeout(timer);
|
|
3690
|
+
}
|
|
3691
|
+
}
|
|
3692
|
+
async function listDir(repo, ref, dirPath, timeoutMs) {
|
|
3693
|
+
const url = `https://api.github.com/repos/${repo}/contents/${encodeURI(dirPath)}?ref=${encodeURIComponent(ref)}`;
|
|
3694
|
+
const res = await httpGet(url, timeoutMs, "application/vnd.github+json");
|
|
3695
|
+
if (!res.ok) {
|
|
3696
|
+
throw new Error(`GitHub API ${res.status} ${res.statusText} (${url})`);
|
|
3697
|
+
}
|
|
3698
|
+
const data = await res.json();
|
|
3699
|
+
if (!Array.isArray(data)) {
|
|
3700
|
+
throw new Error(`Expected directory, got single entry at ${dirPath}`);
|
|
3701
|
+
}
|
|
3702
|
+
return data;
|
|
3703
|
+
}
|
|
3704
|
+
async function downloadFile(downloadUrl, destPath, timeoutMs) {
|
|
3705
|
+
const res = await httpGet(downloadUrl, timeoutMs, "*/*");
|
|
3706
|
+
if (!res.ok) {
|
|
3707
|
+
throw new Error(`Download failed ${res.status} ${res.statusText} (${downloadUrl})`);
|
|
3708
|
+
}
|
|
3709
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
3710
|
+
await fs11.ensureDir(path5.dirname(destPath));
|
|
3711
|
+
await fs11.writeFile(destPath, buf);
|
|
3712
|
+
}
|
|
3713
|
+
async function fetchGithubDir(opts) {
|
|
3714
|
+
const {
|
|
3715
|
+
repo,
|
|
3716
|
+
ref = "main",
|
|
3717
|
+
srcPath,
|
|
3718
|
+
destDir,
|
|
3719
|
+
overwrite = false,
|
|
3720
|
+
timeoutMs = 1e4
|
|
3721
|
+
} = opts;
|
|
3722
|
+
const result = { written: [], skipped: [], errors: [] };
|
|
3723
|
+
async function walk(currentSrc, currentDest) {
|
|
3724
|
+
let entries;
|
|
3725
|
+
try {
|
|
3726
|
+
entries = await listDir(repo, ref, currentSrc, timeoutMs);
|
|
3727
|
+
} catch (err) {
|
|
3728
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3729
|
+
result.errors.push({ path: currentSrc, error: msg });
|
|
3730
|
+
return;
|
|
3731
|
+
}
|
|
3732
|
+
for (const entry of entries) {
|
|
3733
|
+
const relName = entry.name;
|
|
3734
|
+
if (entry.type === "dir") {
|
|
3735
|
+
await walk(`${currentSrc}/${relName}`, path5.join(currentDest, relName));
|
|
3736
|
+
} else if (entry.type === "file") {
|
|
3737
|
+
const destFile = path5.join(currentDest, relName);
|
|
3738
|
+
if (!overwrite && await fs11.pathExists(destFile)) {
|
|
3739
|
+
result.skipped.push(destFile);
|
|
3740
|
+
continue;
|
|
3741
|
+
}
|
|
3742
|
+
if (!entry.download_url) {
|
|
3743
|
+
result.errors.push({ path: entry.path, error: "no download_url" });
|
|
3744
|
+
continue;
|
|
3745
|
+
}
|
|
3746
|
+
try {
|
|
3747
|
+
await downloadFile(entry.download_url, destFile, timeoutMs);
|
|
3748
|
+
result.written.push(destFile);
|
|
3749
|
+
} catch (err) {
|
|
3750
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3751
|
+
result.errors.push({ path: entry.path, error: msg });
|
|
3752
|
+
}
|
|
3753
|
+
}
|
|
3754
|
+
}
|
|
3755
|
+
}
|
|
3756
|
+
await walk(srcPath, destDir);
|
|
3757
|
+
return result;
|
|
3758
|
+
}
|
|
3759
|
+
var init_github_fetch = __esm({
|
|
3760
|
+
"src/utils/github-fetch.ts"() {
|
|
3761
|
+
"use strict";
|
|
3762
|
+
}
|
|
3763
|
+
});
|
|
3764
|
+
|
|
3765
|
+
// src/cli/commands/fetch.cmd.ts
|
|
3766
|
+
var fetch_cmd_exports = {};
|
|
3767
|
+
__export(fetch_cmd_exports, {
|
|
3768
|
+
fetchCommand: () => fetchCommand
|
|
3769
|
+
});
|
|
3770
|
+
import path6 from "path";
|
|
3771
|
+
import fs12 from "fs-extra";
|
|
3772
|
+
import chalk4 from "chalk";
|
|
3773
|
+
async function fetchCommand(cwd, recipeKey, options) {
|
|
3774
|
+
if (options.list) {
|
|
3775
|
+
section("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uB808\uC2DC\uD53C");
|
|
3776
|
+
for (const key of listRecipeKeys()) {
|
|
3777
|
+
const r = RECIPES[key];
|
|
3778
|
+
console.log(` ${colors.accent(key.padEnd(14))} ${r.label}`);
|
|
3779
|
+
console.log(` ${colors.dim(" " + r.description)}`);
|
|
3780
|
+
console.log(` ${colors.dim(" \uC18C\uC2A4: github.com/" + r.source.repo + "/tree/" + r.source.ref + "/" + r.source.path)}`);
|
|
3781
|
+
console.log("");
|
|
3782
|
+
}
|
|
3783
|
+
hint("\uC0AC\uC6A9: pai fetch <key> \uB610\uB294 pai fetch --all");
|
|
3784
|
+
return;
|
|
3785
|
+
}
|
|
3786
|
+
let keys;
|
|
3787
|
+
if (options.all) {
|
|
3788
|
+
keys = listRecipeKeys();
|
|
3789
|
+
} else {
|
|
3790
|
+
if (!recipeKey) {
|
|
3791
|
+
error("\uB808\uC2DC\uD53C \uD0A4\uB97C \uC9C0\uC815\uD558\uC138\uC694.");
|
|
3792
|
+
hint(`\uC0AC\uC6A9 \uAC00\uB2A5: ${listRecipeKeys().join(", ")}`);
|
|
3793
|
+
hint("\uBAA9\uB85D: pai fetch --list");
|
|
3794
|
+
process.exitCode = 1;
|
|
3795
|
+
return;
|
|
3796
|
+
}
|
|
3797
|
+
if (!getRecipe(recipeKey)) {
|
|
3798
|
+
error(`\uC54C \uC218 \uC5C6\uB294 \uB808\uC2DC\uD53C: ${recipeKey}`);
|
|
3799
|
+
hint(`\uC0AC\uC6A9 \uAC00\uB2A5: ${listRecipeKeys().join(", ")}`);
|
|
3800
|
+
process.exitCode = 1;
|
|
3801
|
+
return;
|
|
3802
|
+
}
|
|
3803
|
+
keys = [recipeKey.toLowerCase()];
|
|
3804
|
+
}
|
|
3805
|
+
section("\uB808\uC2DC\uD53C \uB2E4\uC6B4\uB85C\uB4DC");
|
|
3806
|
+
const installed = [];
|
|
3807
|
+
for (const key of keys) {
|
|
3808
|
+
const recipe = RECIPES[key];
|
|
3809
|
+
const targetDir = path6.join(cwd, recipe.target);
|
|
3810
|
+
console.log("");
|
|
3811
|
+
console.log(` ${colors.accent(key)} \u2014 ${recipe.label}`);
|
|
3812
|
+
const result = await withSpinner(`\uB2E4\uC6B4\uB85C\uB4DC \uC911...`, async () => {
|
|
3813
|
+
return fetchGithubDir({
|
|
3814
|
+
repo: recipe.source.repo,
|
|
3815
|
+
ref: recipe.source.ref,
|
|
3816
|
+
srcPath: recipe.source.path,
|
|
3817
|
+
destDir: targetDir,
|
|
3818
|
+
overwrite: options.overwrite ?? false
|
|
3819
|
+
});
|
|
3820
|
+
});
|
|
3821
|
+
if (result.errors.length > 0) {
|
|
3822
|
+
error(`\uB2E4\uC6B4\uB85C\uB4DC \uC2E4\uD328: ${result.errors[0].error}`);
|
|
3823
|
+
for (const e of result.errors.slice(1)) {
|
|
3824
|
+
console.log(chalk4.gray(` ${e.path}: ${e.error}`));
|
|
3825
|
+
}
|
|
3826
|
+
continue;
|
|
3827
|
+
}
|
|
3828
|
+
if (result.written.length > 0) {
|
|
3829
|
+
success(`${result.written.length}\uAC1C \uD30C\uC77C \uC800\uC7A5`);
|
|
3830
|
+
for (const f of result.written) {
|
|
3831
|
+
console.log(chalk4.gray(` ${path6.relative(cwd, f)}`));
|
|
3241
3832
|
}
|
|
3242
3833
|
}
|
|
3834
|
+
if (result.skipped.length > 0) {
|
|
3835
|
+
info(`${result.skipped.length}\uAC1C \uD30C\uC77C \uAC74\uB108\uB700 (\uC774\uBBF8 \uC874\uC7AC \u2014 \uB36E\uC5B4\uC4F0\uAE30: --overwrite)`);
|
|
3836
|
+
}
|
|
3837
|
+
await appendEnvKeys(cwd, recipe);
|
|
3838
|
+
installed.push(key);
|
|
3839
|
+
}
|
|
3840
|
+
if (installed.length === 0) {
|
|
3841
|
+
return;
|
|
3842
|
+
}
|
|
3843
|
+
await upsertRecipesSkill(cwd, installed);
|
|
3844
|
+
await upsertClaudeMdBlock(cwd, installed);
|
|
3845
|
+
console.log("");
|
|
3846
|
+
success(`\uB808\uC2DC\uD53C ${installed.length}\uAC1C \uC124\uCE58 \uC644\uB8CC: ${installed.join(", ")}`);
|
|
3847
|
+
console.log("");
|
|
3848
|
+
console.log(colors.accent(" \uB2E4\uC74C \uB2E8\uACC4"));
|
|
3849
|
+
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"));
|
|
3850
|
+
console.log(` ${colors.success("1.")} ${colors.dim(".env.local \uD30C\uC77C\uC744 \uC5F4\uC5B4 \uD658\uACBD\uBCC0\uC218 \uAC12\uC744 \uCC44\uC6B0\uC138\uC694")}`);
|
|
3851
|
+
for (const key of installed) {
|
|
3852
|
+
const recipe = RECIPES[key];
|
|
3853
|
+
for (const ek of recipe.envKeys) {
|
|
3854
|
+
console.log(` ${chalk4.cyan(ek.key.padEnd(22))} ${colors.dim(ek.hint ?? "")}`);
|
|
3855
|
+
}
|
|
3243
3856
|
}
|
|
3244
3857
|
console.log("");
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3858
|
+
console.log(` ${colors.success("2.")} ${colors.dim("Claude Code\uC5D0\uC11C \uAD00\uB828 \uAE30\uB2A5 \uAD6C\uD604 \uC2DC \uC790\uB3D9\uC73C\uB85C \uB808\uC2DC\uD53C \uBB38\uC11C\uB97C \uCC38\uC870\uD569\uB2C8\uB2E4")}`);
|
|
3859
|
+
for (const key of installed) {
|
|
3860
|
+
const recipe = RECIPES[key];
|
|
3861
|
+
console.log(` ${chalk4.cyan(recipe.target + "/")}`);
|
|
3248
3862
|
}
|
|
3863
|
+
console.log("");
|
|
3249
3864
|
}
|
|
3250
|
-
async function
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3865
|
+
async function appendEnvKeys(cwd, recipe) {
|
|
3866
|
+
if (recipe.envKeys.length === 0) return;
|
|
3867
|
+
const envPath = path6.join(cwd, ".env.local");
|
|
3868
|
+
let content = "";
|
|
3869
|
+
if (await fs12.pathExists(envPath)) {
|
|
3870
|
+
content = await fs12.readFile(envPath, "utf8");
|
|
3871
|
+
}
|
|
3872
|
+
const missingKeys = recipe.envKeys.filter((ek) => !content.includes(`${ek.key}=`));
|
|
3873
|
+
if (missingKeys.length === 0) return;
|
|
3874
|
+
const lines = [];
|
|
3875
|
+
if (content.length > 0 && !content.endsWith("\n")) lines.push("");
|
|
3876
|
+
lines.push("");
|
|
3877
|
+
lines.push(`# \u2500\u2500 ${recipe.label} \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`);
|
|
3878
|
+
lines.push(`# \uAC00\uC774\uB4DC: ${recipe.target}/guideline.md`);
|
|
3879
|
+
for (const ek of missingKeys) {
|
|
3880
|
+
if (ek.hint) lines.push(`# ${ek.hint}`);
|
|
3881
|
+
lines.push(`${ek.key}=${ek.default ?? ""}`);
|
|
3882
|
+
}
|
|
3883
|
+
await fs12.ensureFile(envPath);
|
|
3884
|
+
await fs12.appendFile(envPath, lines.join("\n") + "\n");
|
|
3885
|
+
}
|
|
3886
|
+
async function upsertRecipesSkill(cwd, installedKeys) {
|
|
3887
|
+
const skillDir = path6.join(cwd, ".claude", "skills", "recipes");
|
|
3888
|
+
await fs12.ensureDir(skillDir);
|
|
3889
|
+
const skillPath = path6.join(skillDir, "SKILL.md");
|
|
3890
|
+
const recipes = installedKeys.map((k) => RECIPES[k]);
|
|
3891
|
+
const triggers = recipes.map((r) => r.skillDescription ?? `${r.label} \uAD00\uB828 \uAE30\uB2A5 \uAD6C\uD604 \uC2DC ${r.target}/ \uCC38\uC870`).join("\n- ");
|
|
3892
|
+
const body = [
|
|
3893
|
+
"---",
|
|
3894
|
+
"name: recipes",
|
|
3895
|
+
`description: "\uC0AC\uB0B4 \uC2DC\uC2A4\uD15C \uC5F0\uB3D9 \uB808\uC2DC\uD53C \u2014 ${recipes.map((r) => r.label).join(", ")}"`,
|
|
3896
|
+
"---",
|
|
3897
|
+
"",
|
|
3898
|
+
"# \uC0AC\uB0B4 \uC2DC\uC2A4\uD15C \uC5F0\uB3D9 \uB808\uC2DC\uD53C",
|
|
3899
|
+
"",
|
|
3900
|
+
"\uC774 \uD504\uB85C\uC81D\uD2B8\uC5D0\uB294 \uB2E4\uC74C \uC0AC\uB0B4 \uC5F0\uB3D9 \uB808\uC2DC\uD53C\uAC00 \uC124\uCE58\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
3901
|
+
"\uAD00\uB828 \uC694\uCCAD\uC744 \uBC1B\uC73C\uBA74 \uD574\uB2F9 \uACBD\uB85C\uC758 `guideline.md`\uB97C **\uBC18\uB4DC\uC2DC \uBA3C\uC800 \uC77D\uACE0**",
|
|
3902
|
+
"\uC9C0\uCE68\uC5D0 \uB9DE\uAC8C \uAD6C\uD604\uD558\uC138\uC694.",
|
|
3903
|
+
"",
|
|
3904
|
+
"## \uD2B8\uB9AC\uAC70",
|
|
3905
|
+
"",
|
|
3906
|
+
"- " + triggers,
|
|
3907
|
+
"",
|
|
3908
|
+
"## \uC124\uCE58\uB41C \uB808\uC2DC\uD53C",
|
|
3909
|
+
"",
|
|
3910
|
+
...recipes.map((r) => [
|
|
3911
|
+
`### ${r.label}`,
|
|
3912
|
+
"",
|
|
3913
|
+
`- \uACBD\uB85C: \`${r.target}/\``,
|
|
3914
|
+
`- \uC8FC\uC694 \uBB38\uC11C: \`${r.target}/guideline.md\``,
|
|
3915
|
+
`- \uD658\uACBD\uBCC0\uC218: ${r.envKeys.map((e) => "`" + e.key + "`").join(", ")}`,
|
|
3916
|
+
""
|
|
3917
|
+
].join("\n")),
|
|
3918
|
+
"",
|
|
3919
|
+
"## \uC791\uC5C5 \uC21C\uC11C",
|
|
3920
|
+
"",
|
|
3921
|
+
"1. \uC0AC\uC6A9\uC790\uC758 \uC694\uCCAD\uC774 \uC704 \uD2B8\uB9AC\uAC70 \uC911 \uD558\uB098\uC5D0 \uD574\uB2F9\uD558\uB294\uC9C0 \uD310\uB2E8",
|
|
3922
|
+
"2. \uD574\uB2F9 \uB808\uC2DC\uD53C\uC758 `guideline.md`\uB97C Read \uB3C4\uAD6C\uB85C \uC77D\uAE30",
|
|
3923
|
+
"3. \uC0D8\uD50C \uCF54\uB4DC(`*.html`, `*.ts` \uB4F1)\uAC00 \uC788\uC73C\uBA74 \uD568\uAED8 \uD655\uC778",
|
|
3924
|
+
"4. `.env.local`\uC5D0 \uAD00\uB828 \uD658\uACBD\uBCC0\uC218\uAC00 \uCC44\uC6CC\uC838 \uC788\uB294\uC9C0 \uD655\uC778",
|
|
3925
|
+
"5. \uAC00\uC774\uB4DC \uC21C\uC11C\uC5D0 \uB530\uB77C \uAD6C\uD604 \u2014 \uB808\uC2DC\uD53C \uADDC\uCE59\uC744 \uC808\uB300 \uC6B0\uD68C\uD558\uC9C0 \uB9D0 \uAC83",
|
|
3926
|
+
""
|
|
3927
|
+
].join("\n");
|
|
3928
|
+
await fs12.writeFile(skillPath, body);
|
|
3929
|
+
}
|
|
3930
|
+
async function upsertClaudeMdBlock(cwd, installedKeys) {
|
|
3931
|
+
const claudeMdPath = path6.join(cwd, "CLAUDE.md");
|
|
3932
|
+
const BLOCK_START = "<!-- pai:recipes:start -->";
|
|
3933
|
+
const BLOCK_END = "<!-- pai:recipes:end -->";
|
|
3934
|
+
const lines = [];
|
|
3935
|
+
lines.push(BLOCK_START);
|
|
3936
|
+
lines.push("## \uC0AC\uB0B4 \uC2DC\uC2A4\uD15C \uC5F0\uB3D9");
|
|
3937
|
+
lines.push("");
|
|
3938
|
+
lines.push("\uB2E4\uC74C \uB808\uC2DC\uD53C\uAC00 \uC124\uCE58\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4. \uAD00\uB828 \uAE30\uB2A5 \uAD6C\uD604 \uC2DC **\uBC18\uB4DC\uC2DC** \uD574\uB2F9 \uACBD\uB85C\uC758");
|
|
3939
|
+
lines.push("`guideline.md`\uB97C \uBA3C\uC800 \uC77D\uACE0 \uC9C0\uCE68\uC744 \uB530\uB974\uC138\uC694:");
|
|
3940
|
+
lines.push("");
|
|
3941
|
+
for (const key of installedKeys) {
|
|
3942
|
+
const r = RECIPES[key];
|
|
3943
|
+
lines.push(`- **${r.label}** \u2014 \`${r.target}/\``);
|
|
3944
|
+
lines.push(` \uD658\uACBD\uBCC0\uC218: ${r.envKeys.map((e) => "`" + e.key + "`").join(", ")}`);
|
|
3945
|
+
}
|
|
3946
|
+
lines.push("");
|
|
3947
|
+
lines.push("\uD658\uACBD\uBCC0\uC218 \uAC12\uC740 `.env.local`\uC5D0 \uCC44\uC6CC\uC838 \uC788\uC5B4\uC57C \uD569\uB2C8\uB2E4.");
|
|
3948
|
+
lines.push(BLOCK_END);
|
|
3949
|
+
const block = lines.join("\n");
|
|
3950
|
+
let content = "";
|
|
3951
|
+
if (await fs12.pathExists(claudeMdPath)) {
|
|
3952
|
+
content = await fs12.readFile(claudeMdPath, "utf8");
|
|
3953
|
+
}
|
|
3954
|
+
const startIdx = content.indexOf(BLOCK_START);
|
|
3955
|
+
const endIdx = content.indexOf(BLOCK_END);
|
|
3956
|
+
if (startIdx >= 0 && endIdx > startIdx) {
|
|
3957
|
+
const before = content.slice(0, startIdx);
|
|
3958
|
+
const after = content.slice(endIdx + BLOCK_END.length);
|
|
3959
|
+
content = before + block + after;
|
|
3960
|
+
} else {
|
|
3961
|
+
if (content.length > 0 && !content.endsWith("\n")) content += "\n";
|
|
3962
|
+
if (content.length > 0) content += "\n";
|
|
3963
|
+
content += block + "\n";
|
|
3257
3964
|
}
|
|
3965
|
+
await fs12.ensureFile(claudeMdPath);
|
|
3966
|
+
await fs12.writeFile(claudeMdPath, content);
|
|
3258
3967
|
}
|
|
3259
|
-
var
|
|
3260
|
-
"src/
|
|
3968
|
+
var init_fetch_cmd = __esm({
|
|
3969
|
+
"src/cli/commands/fetch.cmd.ts"() {
|
|
3261
3970
|
"use strict";
|
|
3262
3971
|
init_ui();
|
|
3972
|
+
init_logger();
|
|
3973
|
+
init_recipes();
|
|
3974
|
+
init_github_fetch();
|
|
3975
|
+
init_progress();
|
|
3263
3976
|
}
|
|
3264
3977
|
});
|
|
3265
3978
|
|
|
@@ -3274,6 +3987,7 @@ var init_environment = __esm({
|
|
|
3274
3987
|
init_installer();
|
|
3275
3988
|
init_registry();
|
|
3276
3989
|
init_claude_commands();
|
|
3990
|
+
init_detector();
|
|
3277
3991
|
init_config();
|
|
3278
3992
|
init_progress();
|
|
3279
3993
|
init_ui();
|
|
@@ -3331,6 +4045,7 @@ var init_environment = __esm({
|
|
|
3331
4045
|
PAI_PROJECT_NAME: interview.projectName,
|
|
3332
4046
|
PAI_MODE: interview.mode
|
|
3333
4047
|
},
|
|
4048
|
+
analysis,
|
|
3334
4049
|
mcp: interview.mcp
|
|
3335
4050
|
};
|
|
3336
4051
|
if (interview.authMethods.includes("custom")) {
|
|
@@ -3378,10 +4093,24 @@ var init_environment = __esm({
|
|
|
3378
4093
|
console.log("");
|
|
3379
4094
|
const installResults = await installTools(interview.tools, input.cwd);
|
|
3380
4095
|
printInstallReport(installResults, interview.tools);
|
|
4096
|
+
if (interview.recipes && interview.recipes.length > 0) {
|
|
4097
|
+
console.log("");
|
|
4098
|
+
const { fetchCommand: fetchCommand2 } = await Promise.resolve().then(() => (init_fetch_cmd(), fetch_cmd_exports));
|
|
4099
|
+
for (const recipeKey of interview.recipes) {
|
|
4100
|
+
try {
|
|
4101
|
+
await fetchCommand2(input.cwd, recipeKey, {});
|
|
4102
|
+
} catch (err) {
|
|
4103
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4104
|
+
warn(`\uB808\uC2DC\uD53C '${recipeKey}' \uB2E4\uC6B4\uB85C\uB4DC \uC2E4\uD328 \u2014 ${msg}`);
|
|
4105
|
+
hint(`\uB098\uC911\uC5D0 \uC218\uB3D9: pai fetch ${recipeKey}`);
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4108
|
+
}
|
|
3381
4109
|
console.log("");
|
|
3382
4110
|
await withSpinner("\uC124\uC815 \uC800\uC7A5 \uC911...", async () => {
|
|
3383
4111
|
const config = createDefaultConfig(interview.projectName, interview.mode);
|
|
3384
4112
|
config.plugins = pluginKeys;
|
|
4113
|
+
config.edges = deriveEdges(pluginKeys);
|
|
3385
4114
|
if (interview.mcp) config.mcp = interview.mcp;
|
|
3386
4115
|
await saveConfig(input.cwd, config);
|
|
3387
4116
|
await sleep(300);
|
|
@@ -3411,141 +4140,6 @@ var init_environment = __esm({
|
|
|
3411
4140
|
}
|
|
3412
4141
|
});
|
|
3413
4142
|
|
|
3414
|
-
// src/core/detector.ts
|
|
3415
|
-
var detector_exports = {};
|
|
3416
|
-
__export(detector_exports, {
|
|
3417
|
-
PLUGIN_META: () => PLUGIN_META,
|
|
3418
|
-
PLUGIN_SIGNATURES: () => PLUGIN_SIGNATURES,
|
|
3419
|
-
getBasePluginsForMode: () => getBasePluginsForMode,
|
|
3420
|
-
getPluginsForMode: () => getPluginsForMode,
|
|
3421
|
-
nextMode: () => nextMode,
|
|
3422
|
-
pluginsAddedByPromotion: () => pluginsAddedByPromotion,
|
|
3423
|
-
scanProjectState: () => scanProjectState
|
|
3424
|
-
});
|
|
3425
|
-
import path4 from "path";
|
|
3426
|
-
import fs10 from "fs-extra";
|
|
3427
|
-
async function scanProjectState(cwd) {
|
|
3428
|
-
const result = {
|
|
3429
|
-
isNewProject: true,
|
|
3430
|
-
hasPaiConfig: false,
|
|
3431
|
-
projectMode: null,
|
|
3432
|
-
installedPlugins: [],
|
|
3433
|
-
missingPlugins: [],
|
|
3434
|
-
details: {}
|
|
3435
|
-
};
|
|
3436
|
-
const paiConfigPath = path4.join(cwd, ".pai", "config.json");
|
|
3437
|
-
if (await fs10.pathExists(paiConfigPath)) {
|
|
3438
|
-
result.hasPaiConfig = true;
|
|
3439
|
-
try {
|
|
3440
|
-
const config = await fs10.readJson(paiConfigPath);
|
|
3441
|
-
result.projectMode = config.mode != null ? normalizeMode(config.mode) : null;
|
|
3442
|
-
} catch {
|
|
3443
|
-
}
|
|
3444
|
-
}
|
|
3445
|
-
for (const [key, signatures] of Object.entries(PLUGIN_SIGNATURES)) {
|
|
3446
|
-
const installed = await Promise.any(
|
|
3447
|
-
signatures.map(async (sig) => {
|
|
3448
|
-
if (await fs10.pathExists(path4.join(cwd, sig))) return true;
|
|
3449
|
-
throw new Error("not found");
|
|
3450
|
-
})
|
|
3451
|
-
).catch(() => false);
|
|
3452
|
-
result.details[key] = { installed, signatures };
|
|
3453
|
-
if (installed) {
|
|
3454
|
-
result.installedPlugins.push(key);
|
|
3455
|
-
} else {
|
|
3456
|
-
result.missingPlugins.push(key);
|
|
3457
|
-
}
|
|
3458
|
-
}
|
|
3459
|
-
const hasAnyContent = result.installedPlugins.length > 0 || await fs10.pathExists(path4.join(cwd, "package.json")) || await fs10.pathExists(path4.join(cwd, "src")) || await fs10.pathExists(path4.join(cwd, "README.md"));
|
|
3460
|
-
result.isNewProject = !hasAnyContent;
|
|
3461
|
-
return result;
|
|
3462
|
-
}
|
|
3463
|
-
function getPluginsForMode(mode) {
|
|
3464
|
-
return Object.entries(PLUGIN_META).filter(([, meta]) => meta.modes.includes(mode)).map(([key, meta]) => ({ key, ...meta }));
|
|
3465
|
-
}
|
|
3466
|
-
function getBasePluginsForMode(mode) {
|
|
3467
|
-
return Object.entries(PLUGIN_META).filter(([, meta]) => meta.modes.includes(mode) && meta.required).map(([key]) => key);
|
|
3468
|
-
}
|
|
3469
|
-
function nextMode(current) {
|
|
3470
|
-
if (current === "prototype") return "poc";
|
|
3471
|
-
if (current === "poc") return "production";
|
|
3472
|
-
return null;
|
|
3473
|
-
}
|
|
3474
|
-
function pluginsAddedByPromotion(from, to) {
|
|
3475
|
-
const fromSet = new Set(getPluginsForMode(from).map((p) => p.key));
|
|
3476
|
-
return getPluginsForMode(to).map((p) => p.key).filter((k) => !fromSet.has(k));
|
|
3477
|
-
}
|
|
3478
|
-
var PLUGIN_SIGNATURES, PLUGIN_META;
|
|
3479
|
-
var init_detector = __esm({
|
|
3480
|
-
"src/core/detector.ts"() {
|
|
3481
|
-
"use strict";
|
|
3482
|
-
init_config();
|
|
3483
|
-
PLUGIN_SIGNATURES = {
|
|
3484
|
-
github: [".git", ".github"],
|
|
3485
|
-
vercel: [".vercel", "vercel.json"],
|
|
3486
|
-
supabase: ["supabase", ".supabase"],
|
|
3487
|
-
openspec: ["openspec.md", "docs/openspec.md", ".pai/openspec.md"],
|
|
3488
|
-
omc: [".pai/omc.md", "omc.config.js", "omc.config.json"],
|
|
3489
|
-
gstack: [".pai/gstack.json", "gstack.config.js"],
|
|
3490
|
-
roboco: [".pai/roboco.json", "roboco.config.js", ".roboco"],
|
|
3491
|
-
harness: [".pai/harness.json", "harness.config.js", ".harness"]
|
|
3492
|
-
};
|
|
3493
|
-
PLUGIN_META = {
|
|
3494
|
-
github: {
|
|
3495
|
-
label: "GitHub \uB808\uD3EC & \uD3F4\uB354 \uAD6C\uC870",
|
|
3496
|
-
description: "\uB808\uD3EC \uCD08\uAE30\uD654, .gitignore, \uAE30\uBCF8 \uBE0C\uB79C\uCE58 \uC124\uC815",
|
|
3497
|
-
modes: ["prototype", "poc", "production"],
|
|
3498
|
-
required: true
|
|
3499
|
-
},
|
|
3500
|
-
openspec: {
|
|
3501
|
-
label: "OpenSpec (PRD \uC124\uACC4)",
|
|
3502
|
-
description: "AI \uAE30\uBC18 PRD \uC0DD\uC131 \uBC0F \uC2A4\uD399 \uBB38\uC11C\uD654",
|
|
3503
|
-
modes: ["prototype", "poc", "production"],
|
|
3504
|
-
required: true
|
|
3505
|
-
},
|
|
3506
|
-
roboco: {
|
|
3507
|
-
label: "roboco (AI \uC9C4\uB2E8)",
|
|
3508
|
-
description: "\uC124\uCE58 \uC0C1\uD0DC \uD3C9\uAC00 \uBC0F AI \uC900\uBE44\uB3C4 \uB9AC\uD3EC\uD2B8 \uC0DD\uC131",
|
|
3509
|
-
modes: ["prototype", "poc", "production"],
|
|
3510
|
-
required: false,
|
|
3511
|
-
url: "https://github.com/SoInKyu/roboco-cli"
|
|
3512
|
-
},
|
|
3513
|
-
omc: {
|
|
3514
|
-
label: "OMC (oh-my-claudecode)",
|
|
3515
|
-
description: "\uAC1D\uCCB4 \uBAA8\uB378 \uCEE8\uD14D\uC2A4\uD2B8 + Claude Code \uBA40\uD2F0 \uC5D0\uC774\uC804\uD2B8 \uC624\uCF00\uC2A4\uD2B8\uB808\uC774\uC158",
|
|
3516
|
-
modes: ["poc", "production"],
|
|
3517
|
-
required: false,
|
|
3518
|
-
url: "https://github.com/SoInKyu/oh-my-claudecode"
|
|
3519
|
-
},
|
|
3520
|
-
vercel: {
|
|
3521
|
-
label: "Vercel \uBC30\uD3EC \uC5F0\uB3D9",
|
|
3522
|
-
description: "\uC790\uB3D9 \uBC30\uD3EC \uD30C\uC774\uD504\uB77C\uC778 \uBC0F Preview URL \uC124\uC815",
|
|
3523
|
-
modes: ["poc", "production"],
|
|
3524
|
-
required: false
|
|
3525
|
-
},
|
|
3526
|
-
gstack: {
|
|
3527
|
-
label: "gstack (QA / \uD488\uC9C8\uAD00\uB9AC)",
|
|
3528
|
-
description: "\uD14C\uC2A4\uD2B8 \uC790\uB3D9\uD654 \uBC0F \uD488\uC9C8 \uAE30\uC900 \uC124\uC815",
|
|
3529
|
-
modes: ["production"],
|
|
3530
|
-
required: false,
|
|
3531
|
-
url: "https://github.com/SoInKyu/gstack"
|
|
3532
|
-
},
|
|
3533
|
-
harness: {
|
|
3534
|
-
label: "Harness Engineering (\uAC80\uC99D \uC790\uB3D9\uD654)",
|
|
3535
|
-
description: "\uC124\uACC4(OpenSpec)\uC640 \uAD6C\uD604 \uC77C\uCE58 \uC5EC\uBD80 \uC790\uB3D9 \uCCB4\uD06C",
|
|
3536
|
-
modes: ["production"],
|
|
3537
|
-
required: false
|
|
3538
|
-
},
|
|
3539
|
-
supabase: {
|
|
3540
|
-
label: "Supabase (DB & Auth)",
|
|
3541
|
-
description: "\uB370\uC774\uD130\uBCA0\uC774\uC2A4, \uC778\uC99D, API \uD0A4 \uC790\uB3D9 \uC5F0\uB3D9",
|
|
3542
|
-
modes: ["production"],
|
|
3543
|
-
required: false
|
|
3544
|
-
}
|
|
3545
|
-
};
|
|
3546
|
-
}
|
|
3547
|
-
});
|
|
3548
|
-
|
|
3549
4143
|
// src/stages/evaluation/prompts/analyze.ts
|
|
3550
4144
|
var analyze_exports = {};
|
|
3551
4145
|
__export(analyze_exports, {
|
|
@@ -3603,7 +4197,7 @@ __export(analyzer_exports2, {
|
|
|
3603
4197
|
analyzeRepository: () => analyzeRepository
|
|
3604
4198
|
});
|
|
3605
4199
|
import { join as join7 } from "path";
|
|
3606
|
-
import
|
|
4200
|
+
import fs13 from "fs-extra";
|
|
3607
4201
|
async function analyzeRepository(repoPath) {
|
|
3608
4202
|
try {
|
|
3609
4203
|
return await aiAnalysis(repoPath);
|
|
@@ -3664,14 +4258,14 @@ async function checkTestCoverage(repoPath) {
|
|
|
3664
4258
|
".nycrc"
|
|
3665
4259
|
];
|
|
3666
4260
|
for (const f of testConfigs) {
|
|
3667
|
-
const found = await
|
|
4261
|
+
const found = await fs13.pathExists(join7(repoPath, f));
|
|
3668
4262
|
findings.push({ item: f, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
3669
4263
|
if (found) score += 20;
|
|
3670
4264
|
}
|
|
3671
4265
|
const testDirs = ["tests", "test", "__tests__", "spec"];
|
|
3672
4266
|
let hasTestDir = false;
|
|
3673
4267
|
for (const d of testDirs) {
|
|
3674
|
-
if (await
|
|
4268
|
+
if (await fs13.pathExists(join7(repoPath, d))) {
|
|
3675
4269
|
findings.push({ item: d, found: true, details: "\uD14C\uC2A4\uD2B8 \uB514\uB809\uD1A0\uB9AC \uC874\uC7AC" });
|
|
3676
4270
|
hasTestDir = true;
|
|
3677
4271
|
score += 30;
|
|
@@ -3694,8 +4288,8 @@ async function checkCiCd(repoPath) {
|
|
|
3694
4288
|
{ path: "Jenkinsfile", label: "Jenkins" },
|
|
3695
4289
|
{ path: ".circleci", label: "CircleCI" }
|
|
3696
4290
|
];
|
|
3697
|
-
for (const { path:
|
|
3698
|
-
const found = await
|
|
4291
|
+
for (const { path: path8, label } of ciConfigs) {
|
|
4292
|
+
const found = await fs13.pathExists(join7(repoPath, path8));
|
|
3699
4293
|
findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
3700
4294
|
if (found) score += 40;
|
|
3701
4295
|
}
|
|
@@ -3713,8 +4307,8 @@ async function checkHooks(repoPath) {
|
|
|
3713
4307
|
{ path: "commitlint.config.js", label: "commitlint" },
|
|
3714
4308
|
{ path: ".claude/settings.json", label: "Claude Code settings" }
|
|
3715
4309
|
];
|
|
3716
|
-
for (const { path:
|
|
3717
|
-
const found = await
|
|
4310
|
+
for (const { path: path8, label } of hookConfigs) {
|
|
4311
|
+
const found = await fs13.pathExists(join7(repoPath, path8));
|
|
3718
4312
|
findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
3719
4313
|
if (found) score += 20;
|
|
3720
4314
|
}
|
|
@@ -3731,8 +4325,8 @@ async function checkRepoStructure(repoPath) {
|
|
|
3731
4325
|
{ path: ".env.example", label: "\uD658\uACBD\uBCC0\uC218 \uC608\uC2DC" },
|
|
3732
4326
|
{ path: ".gitignore", label: ".gitignore" }
|
|
3733
4327
|
];
|
|
3734
|
-
for (const { path:
|
|
3735
|
-
const found = await
|
|
4328
|
+
for (const { path: path8, label } of structureChecks) {
|
|
4329
|
+
const found = await fs13.pathExists(join7(repoPath, path8));
|
|
3736
4330
|
findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
3737
4331
|
if (found) score += 25;
|
|
3738
4332
|
}
|
|
@@ -3748,8 +4342,8 @@ async function checkDocumentation(repoPath) {
|
|
|
3748
4342
|
{ path: "docs", label: "docs/ \uB514\uB809\uD1A0\uB9AC", points: 25 },
|
|
3749
4343
|
{ path: "docs/openspec.md", label: "OpenSpec PRD", points: 25 }
|
|
3750
4344
|
];
|
|
3751
|
-
for (const { path:
|
|
3752
|
-
const found = await
|
|
4345
|
+
for (const { path: path8, label, points } of docChecks) {
|
|
4346
|
+
const found = await fs13.pathExists(join7(repoPath, path8));
|
|
3753
4347
|
findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
3754
4348
|
if (found) score += points;
|
|
3755
4349
|
}
|
|
@@ -3767,8 +4361,8 @@ async function checkHarnessEngineering(repoPath) {
|
|
|
3767
4361
|
{ path: ".claude/commands", label: ".claude/commands/", points: 10 },
|
|
3768
4362
|
{ path: ".pai/config.json", label: "PAI config", points: 10 }
|
|
3769
4363
|
];
|
|
3770
|
-
for (const { path:
|
|
3771
|
-
const found = await
|
|
4364
|
+
for (const { path: path8, label, points } of harnessChecks) {
|
|
4365
|
+
const found = await fs13.pathExists(join7(repoPath, path8));
|
|
3772
4366
|
findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
3773
4367
|
if (found) score += points;
|
|
3774
4368
|
}
|
|
@@ -3902,41 +4496,41 @@ __export(reporter_exports, {
|
|
|
3902
4496
|
printReport: () => printReport,
|
|
3903
4497
|
printVerboseFindings: () => printVerboseFindings
|
|
3904
4498
|
});
|
|
3905
|
-
import
|
|
4499
|
+
import chalk5 from "chalk";
|
|
3906
4500
|
function printReport(result) {
|
|
3907
4501
|
console.log("");
|
|
3908
|
-
console.log(
|
|
3909
|
-
console.log(
|
|
4502
|
+
console.log(chalk5.hex("#7B93DB")(" PAI Evaluation Report"));
|
|
4503
|
+
console.log(chalk5.gray(" \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\u2500\u2500\u2500\u2500"));
|
|
3910
4504
|
console.log("");
|
|
3911
4505
|
const gradeColor = GRADE_COLORS[result.totalGrade];
|
|
3912
4506
|
console.log(` \uC885\uD569 \uC810\uC218: ${gradeColor(String(result.totalScore))} / 100 \uB4F1\uAE09: ${gradeColor(result.totalGrade)}`);
|
|
3913
4507
|
if (result.penaltyApplied) {
|
|
3914
|
-
console.log(
|
|
4508
|
+
console.log(chalk5.red(` \u26A0 ${result.penaltyReason}`));
|
|
3915
4509
|
}
|
|
3916
4510
|
console.log("");
|
|
3917
|
-
console.log(
|
|
4511
|
+
console.log(chalk5.gray(" \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
3918
4512
|
for (const cat of result.categories) {
|
|
3919
4513
|
const color = GRADE_COLORS[cat.grade];
|
|
3920
|
-
const tierLabel = cat.tier === "must" ?
|
|
4514
|
+
const tierLabel = cat.tier === "must" ? chalk5.hex("#E06C75")("[\uD544\uC218]") : chalk5.gray("[\uC120\uD0DD]");
|
|
3921
4515
|
const bar = renderBar(cat.score);
|
|
3922
4516
|
console.log(` ${tierLabel} ${cat.name.padEnd(16)} ${bar} ${color(`${cat.score}`).padStart(12)} ${color(cat.grade)}`);
|
|
3923
4517
|
}
|
|
3924
4518
|
console.log("");
|
|
3925
|
-
console.log(
|
|
4519
|
+
console.log(chalk5.gray(` ${result.summary}`));
|
|
3926
4520
|
console.log("");
|
|
3927
4521
|
}
|
|
3928
4522
|
function printVerboseFindings(result) {
|
|
3929
4523
|
for (const cat of result.categories) {
|
|
3930
4524
|
console.log("");
|
|
3931
|
-
console.log(
|
|
4525
|
+
console.log(chalk5.bold(` ${cat.name} (${cat.grade}, ${cat.score}\uC810)`));
|
|
3932
4526
|
for (const f of cat.rawFindings) {
|
|
3933
|
-
const icon = f.found ?
|
|
4527
|
+
const icon = f.found ? chalk5.green("\u2713") : chalk5.red("\u2717");
|
|
3934
4528
|
console.log(` ${icon} ${f.item} \u2014 ${f.details}`);
|
|
3935
4529
|
}
|
|
3936
4530
|
for (const r of cat.recommendations) {
|
|
3937
|
-
const severity = r.severity === "critical" ?
|
|
4531
|
+
const severity = r.severity === "critical" ? chalk5.red("!") : r.severity === "warning" ? chalk5.yellow("!") : chalk5.gray("i");
|
|
3938
4532
|
console.log(` ${severity} ${r.message}`);
|
|
3939
|
-
console.log(
|
|
4533
|
+
console.log(chalk5.gray(` \u2192 ${r.action}`));
|
|
3940
4534
|
}
|
|
3941
4535
|
}
|
|
3942
4536
|
}
|
|
@@ -3944,8 +4538,8 @@ function renderBar(score) {
|
|
|
3944
4538
|
const width = 20;
|
|
3945
4539
|
const filled = Math.round(score / 100 * width);
|
|
3946
4540
|
const empty = width - filled;
|
|
3947
|
-
const color = score >= 80 ?
|
|
3948
|
-
return color("\u2588".repeat(filled)) +
|
|
4541
|
+
const color = score >= 80 ? chalk5.hex("#6BCB77") : score >= 60 ? chalk5.hex("#E2B340") : chalk5.hex("#E06C75");
|
|
4542
|
+
return color("\u2588".repeat(filled)) + chalk5.gray("\u2591".repeat(empty));
|
|
3949
4543
|
}
|
|
3950
4544
|
function buildMarkdownReport(result) {
|
|
3951
4545
|
const lines = [
|
|
@@ -4136,11 +4730,11 @@ var init_reporter = __esm({
|
|
|
4136
4730
|
"src/stages/evaluation/reporter.ts"() {
|
|
4137
4731
|
"use strict";
|
|
4138
4732
|
GRADE_COLORS = {
|
|
4139
|
-
A:
|
|
4140
|
-
B:
|
|
4141
|
-
C:
|
|
4142
|
-
D:
|
|
4143
|
-
F:
|
|
4733
|
+
A: chalk5.hex("#6BCB77"),
|
|
4734
|
+
B: chalk5.hex("#7B93DB"),
|
|
4735
|
+
C: chalk5.hex("#E2B340"),
|
|
4736
|
+
D: chalk5.hex("#E06C75"),
|
|
4737
|
+
F: chalk5.hex("#CC4444")
|
|
4144
4738
|
};
|
|
4145
4739
|
}
|
|
4146
4740
|
});
|
|
@@ -4153,54 +4747,54 @@ __export(shell_cd_exports, {
|
|
|
4153
4747
|
});
|
|
4154
4748
|
import { join as join8 } from "path";
|
|
4155
4749
|
import { homedir as homedir2 } from "os";
|
|
4156
|
-
import
|
|
4750
|
+
import fs14 from "fs-extra";
|
|
4157
4751
|
async function requestCdAfter(targetDir) {
|
|
4158
|
-
await
|
|
4159
|
-
await
|
|
4752
|
+
await fs14.ensureDir(PAI_DIR);
|
|
4753
|
+
await fs14.writeFile(CD_FILE, targetDir);
|
|
4160
4754
|
}
|
|
4161
4755
|
async function installShellHelper() {
|
|
4162
|
-
await
|
|
4756
|
+
await fs14.ensureDir(PAI_DIR);
|
|
4163
4757
|
if (isWindows) {
|
|
4164
4758
|
return installPowerShellHelper();
|
|
4165
4759
|
}
|
|
4166
4760
|
return installBashHelper();
|
|
4167
4761
|
}
|
|
4168
4762
|
async function installBashHelper() {
|
|
4169
|
-
await
|
|
4763
|
+
await fs14.writeFile(HELPER_FILE_SH, BASH_HELPER);
|
|
4170
4764
|
const rcFile = getShellRcPath();
|
|
4171
4765
|
const sourceLine = 'source "$HOME/.pai/shell-helper.sh"';
|
|
4172
|
-
if (await
|
|
4173
|
-
const content = await
|
|
4766
|
+
if (await fs14.pathExists(rcFile)) {
|
|
4767
|
+
const content = await fs14.readFile(rcFile, "utf8");
|
|
4174
4768
|
if (content.includes("shell-helper.sh")) {
|
|
4175
4769
|
return true;
|
|
4176
4770
|
}
|
|
4177
|
-
await
|
|
4771
|
+
await fs14.appendFile(rcFile, `
|
|
4178
4772
|
# PAI \u2014 \uC790\uB3D9 \uB514\uB809\uD1A0\uB9AC \uC774\uB3D9
|
|
4179
4773
|
${sourceLine}
|
|
4180
4774
|
`);
|
|
4181
4775
|
return false;
|
|
4182
4776
|
}
|
|
4183
|
-
await
|
|
4777
|
+
await fs14.writeFile(rcFile, `${sourceLine}
|
|
4184
4778
|
`);
|
|
4185
4779
|
return false;
|
|
4186
4780
|
}
|
|
4187
4781
|
async function installPowerShellHelper() {
|
|
4188
|
-
await
|
|
4782
|
+
await fs14.writeFile(HELPER_FILE_PS1, POWERSHELL_HELPER);
|
|
4189
4783
|
const rcFile = getShellRcPath();
|
|
4190
4784
|
const sourceLine = '. "$env:USERPROFILE\\.pai\\shell-helper.ps1"';
|
|
4191
|
-
await
|
|
4192
|
-
if (await
|
|
4193
|
-
const content = await
|
|
4785
|
+
await fs14.ensureDir(join8(rcFile, ".."));
|
|
4786
|
+
if (await fs14.pathExists(rcFile)) {
|
|
4787
|
+
const content = await fs14.readFile(rcFile, "utf8");
|
|
4194
4788
|
if (content.includes("shell-helper.ps1")) {
|
|
4195
4789
|
return true;
|
|
4196
4790
|
}
|
|
4197
|
-
await
|
|
4791
|
+
await fs14.appendFile(rcFile, `
|
|
4198
4792
|
# PAI \u2014 \uC790\uB3D9 \uB514\uB809\uD1A0\uB9AC \uC774\uB3D9
|
|
4199
4793
|
${sourceLine}
|
|
4200
4794
|
`);
|
|
4201
4795
|
return false;
|
|
4202
4796
|
}
|
|
4203
|
-
await
|
|
4797
|
+
await fs14.writeFile(rcFile, `${sourceLine}
|
|
4204
4798
|
`);
|
|
4205
4799
|
return false;
|
|
4206
4800
|
}
|
|
@@ -4263,10 +4857,10 @@ __export(claude_settings_exports, {
|
|
|
4263
4857
|
mergeOmcIntoSettings: () => mergeOmcIntoSettings
|
|
4264
4858
|
});
|
|
4265
4859
|
import os2 from "os";
|
|
4266
|
-
import
|
|
4267
|
-
import
|
|
4860
|
+
import path7 from "path";
|
|
4861
|
+
import fs15 from "fs-extra";
|
|
4268
4862
|
function getClaudeSettingsPath(homeDir = os2.homedir()) {
|
|
4269
|
-
return
|
|
4863
|
+
return path7.join(homeDir, ".claude", "settings.json");
|
|
4270
4864
|
}
|
|
4271
4865
|
function parseJsonWithBom(raw) {
|
|
4272
4866
|
const stripped = raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw;
|
|
@@ -4281,13 +4875,13 @@ async function enableOmcPlugin(options = {}) {
|
|
|
4281
4875
|
const pluginId = options.pluginId ?? DEFAULT_PLUGIN_ID;
|
|
4282
4876
|
const wantBackup = options.backup ?? true;
|
|
4283
4877
|
const settingsPath = getClaudeSettingsPath();
|
|
4284
|
-
await
|
|
4285
|
-
if (!await
|
|
4878
|
+
await fs15.ensureDir(path7.dirname(settingsPath));
|
|
4879
|
+
if (!await fs15.pathExists(settingsPath)) {
|
|
4286
4880
|
const skeleton = buildSkeleton(marketplaceId, marketplaceUrl, pluginId);
|
|
4287
|
-
await
|
|
4881
|
+
await fs15.writeFile(settingsPath, JSON.stringify(skeleton, null, 2) + "\n", "utf8");
|
|
4288
4882
|
return { action: "created", settingsPath };
|
|
4289
4883
|
}
|
|
4290
|
-
const raw = await
|
|
4884
|
+
const raw = await fs15.readFile(settingsPath, "utf8");
|
|
4291
4885
|
let parsed;
|
|
4292
4886
|
try {
|
|
4293
4887
|
const value = parseJsonWithBom(raw);
|
|
@@ -4297,7 +4891,7 @@ async function enableOmcPlugin(options = {}) {
|
|
|
4297
4891
|
parsed = value;
|
|
4298
4892
|
} catch (err) {
|
|
4299
4893
|
const backupPath2 = `${settingsPath}.backup-${timestampSuffix()}`;
|
|
4300
|
-
await
|
|
4894
|
+
await fs15.copy(settingsPath, backupPath2);
|
|
4301
4895
|
throw new ClaudeSettingsError(
|
|
4302
4896
|
`settings.json \uD30C\uC2F1 \uC2E4\uD328: ${err.message}. \uBC31\uC5C5: ${backupPath2}`,
|
|
4303
4897
|
backupPath2
|
|
@@ -4309,10 +4903,10 @@ async function enableOmcPlugin(options = {}) {
|
|
|
4309
4903
|
let backupPath;
|
|
4310
4904
|
if (wantBackup) {
|
|
4311
4905
|
backupPath = `${settingsPath}.backup-${timestampSuffix()}`;
|
|
4312
|
-
await
|
|
4906
|
+
await fs15.copy(settingsPath, backupPath);
|
|
4313
4907
|
}
|
|
4314
4908
|
const merged = mergeOmcIntoSettings(parsed, marketplaceId, marketplaceUrl, pluginId);
|
|
4315
|
-
await
|
|
4909
|
+
await fs15.writeFile(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
4316
4910
|
return { action: "added", settingsPath, backupPath };
|
|
4317
4911
|
}
|
|
4318
4912
|
function buildSkeleton(marketplaceId, marketplaceUrl, pluginId) {
|
|
@@ -4454,7 +5048,7 @@ __export(evaluate_cmd_exports, {
|
|
|
4454
5048
|
evaluateCommand: () => evaluateCommand
|
|
4455
5049
|
});
|
|
4456
5050
|
import { join as join10, basename } from "path";
|
|
4457
|
-
import
|
|
5051
|
+
import fs16 from "fs-extra";
|
|
4458
5052
|
async function evaluateCommand(cwd, options) {
|
|
4459
5053
|
const useCache = options.cache !== false;
|
|
4460
5054
|
let llmOutput = useCache ? getCachedResult(cwd) : null;
|
|
@@ -4476,15 +5070,15 @@ async function evaluateCommand(cwd, options) {
|
|
|
4476
5070
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4477
5071
|
const reportDir = join10(cwd, "docs", "p-reports");
|
|
4478
5072
|
const reportPath = join10(reportDir, `${today}.md`);
|
|
4479
|
-
await
|
|
5073
|
+
await fs16.ensureDir(reportDir);
|
|
4480
5074
|
const detailedReport = buildDetailedReport(result, projectName);
|
|
4481
|
-
await
|
|
5075
|
+
await fs16.writeFile(reportPath, detailedReport, "utf8");
|
|
4482
5076
|
console.log("");
|
|
4483
5077
|
success(`\uB9AC\uD3EC\uD2B8 \uC800\uC7A5: docs/p-reports/${today}.md`);
|
|
4484
5078
|
console.log("");
|
|
4485
5079
|
console.log(detailedReport);
|
|
4486
5080
|
if (options.output) {
|
|
4487
|
-
await
|
|
5081
|
+
await fs16.writeFile(options.output, detailedReport, "utf8");
|
|
4488
5082
|
success(`\uCD94\uAC00 \uC800\uC7A5: ${options.output}`);
|
|
4489
5083
|
}
|
|
4490
5084
|
if (options.failUnder && result.totalScore < options.failUnder) {
|
|
@@ -4657,7 +5251,7 @@ __export(init_cmd_exports, {
|
|
|
4657
5251
|
initCommand: () => initCommand
|
|
4658
5252
|
});
|
|
4659
5253
|
import { join as join11, basename as basename2 } from "path";
|
|
4660
|
-
import
|
|
5254
|
+
import fs17 from "fs-extra";
|
|
4661
5255
|
async function initCommand(cwd, nameArg) {
|
|
4662
5256
|
printWelcomeBanner();
|
|
4663
5257
|
const { isWindows: isWindows2, diagnoseWindowsEnv: diagnoseWindowsEnv2 } = await Promise.resolve().then(() => (init_platform(), platform_exports));
|
|
@@ -4695,7 +5289,7 @@ async function initCommand(cwd, nameArg) {
|
|
|
4695
5289
|
const isLegacy = await detectLegacyProject(cwd);
|
|
4696
5290
|
if (isLegacy) {
|
|
4697
5291
|
const { default: inquirer2 } = await import("inquirer");
|
|
4698
|
-
const
|
|
5292
|
+
const chalk9 = (await import("chalk")).default;
|
|
4699
5293
|
console.log("");
|
|
4700
5294
|
info("\uAE30\uC874 \uD504\uB85C\uC81D\uD2B8\uAC00 \uAC10\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
4701
5295
|
console.log("");
|
|
@@ -4716,10 +5310,10 @@ async function initCommand(cwd, nameArg) {
|
|
|
4716
5310
|
printReport2(evalResult);
|
|
4717
5311
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4718
5312
|
const reportDir = join11(cwd, "docs", "p-reports");
|
|
4719
|
-
await
|
|
5313
|
+
await fs17.ensureDir(reportDir);
|
|
4720
5314
|
const legacyName = basename2(cwd);
|
|
4721
5315
|
const detailedReport = buildDetailedReport3(evalResult, legacyName);
|
|
4722
|
-
await
|
|
5316
|
+
await fs17.writeFile(join11(reportDir, `${today}.md`), detailedReport, "utf8");
|
|
4723
5317
|
console.log("");
|
|
4724
5318
|
success(`\uB9AC\uD3EC\uD2B8 \uC800\uC7A5: docs/p-reports/${today}.md`);
|
|
4725
5319
|
} catch {
|
|
@@ -4765,7 +5359,7 @@ async function initCommand(cwd, nameArg) {
|
|
|
4765
5359
|
projectName = answer.name.trim();
|
|
4766
5360
|
}
|
|
4767
5361
|
const projectDir = join11(cwd, projectName);
|
|
4768
|
-
if (await
|
|
5362
|
+
if (await fs17.pathExists(projectDir)) {
|
|
4769
5363
|
const existingConfig = await loadConfig(projectDir);
|
|
4770
5364
|
if (existingConfig) {
|
|
4771
5365
|
console.log("");
|
|
@@ -4778,7 +5372,7 @@ async function initCommand(cwd, nameArg) {
|
|
|
4778
5372
|
warn(`${projectName}/ \uD3F4\uB354\uAC00 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4.`);
|
|
4779
5373
|
hint("\uC774 \uD3F4\uB354\uC5D0 PAI\uB97C \uC124\uCE58\uD569\uB2C8\uB2E4.");
|
|
4780
5374
|
} else {
|
|
4781
|
-
await
|
|
5375
|
+
await fs17.ensureDir(projectDir);
|
|
4782
5376
|
success(`${projectName}/ \uD3F4\uB354 \uC0DD\uC131`);
|
|
4783
5377
|
}
|
|
4784
5378
|
await setupInDirectory(projectDir, projectName);
|
|
@@ -4816,7 +5410,7 @@ async function setupInDirectory(projectDir, projectName) {
|
|
|
4816
5410
|
}
|
|
4817
5411
|
async function showCompletion(projectName, projectDir, extraTools, isCurrentDir) {
|
|
4818
5412
|
const { default: inquirer } = await import("inquirer");
|
|
4819
|
-
const
|
|
5413
|
+
const chalk9 = (await import("chalk")).default;
|
|
4820
5414
|
const { sleep: sleep2 } = await Promise.resolve().then(() => (init_progress(), progress_exports));
|
|
4821
5415
|
step(3, 3, "\uC644\uB8CC");
|
|
4822
5416
|
console.log(colors.success(" \uD504\uB85C\uC81D\uD2B8\uAC00 \uC900\uBE44\uB418\uC5C8\uC2B5\uB2C8\uB2E4!"));
|
|
@@ -4830,26 +5424,26 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
|
|
|
4830
5424
|
console.log("");
|
|
4831
5425
|
console.log(colors.accent(" \uC124\uCE58\uB41C \uD50C\uB7EC\uADF8\uC778 & \uAE30\uB2A5"));
|
|
4832
5426
|
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"));
|
|
4833
|
-
console.log(` ${
|
|
4834
|
-
console.log(` ${
|
|
4835
|
-
console.log(` ${
|
|
5427
|
+
console.log(` ${chalk9.cyan("OpenSpec+OMC")} \uC124\uACC4\uB3C4 & \uC9C0\uD615\uB3C4 \u2014 \uBB34\uC5C7\uC744, \uC5B4\uB514\uC5D0 \uB9CC\uB4E4\uC9C0 \uC815\uC758`);
|
|
5428
|
+
console.log(` ${chalk9.cyan("RoboCo CLI")} \uD604\uC7A5 \uC18C\uC7A5 \u2014 \uC804\uCCB4 \uACF5\uC815 \uAD00\uB9AC, AI\uC5D0\uAC8C \uC791\uC5C5 \uC9C0\uC2DC`);
|
|
5429
|
+
console.log(` ${chalk9.cyan("Vibe-Ready")} \uD488\uC9C8 \uAC80\uC218 \u2014 AI \uAC1C\uBC1C \uC900\uBE44\uB3C4 6\uCE74\uD14C\uACE0\uB9AC \uD3C9\uAC00`);
|
|
4836
5430
|
if (extraTools.includes("omc")) {
|
|
4837
|
-
console.log(` ${
|
|
5431
|
+
console.log(` ${chalk9.cyan("OMC")} AI \uC5D0\uC774\uC804\uD2B8 \uC624\uCF00\uC2A4\uD2B8\uB808\uC774\uC158 (oh-my-claudecode)`);
|
|
4838
5432
|
}
|
|
4839
5433
|
if (extraTools.includes("gstack")) {
|
|
4840
|
-
console.log(` ${
|
|
5434
|
+
console.log(` ${chalk9.cyan("gstack")} \uD45C\uC900 \uC790\uC7AC & \uACF5\uBC95 \u2014 \uAE30\uC220 \uC2A4\uD0DD \uD45C\uC900 \uAD6C\uC870 \uC81C\uACF5`);
|
|
4841
5435
|
}
|
|
4842
5436
|
if (extraTools.includes("harness")) {
|
|
4843
|
-
console.log(` ${
|
|
5437
|
+
console.log(` ${chalk9.cyan("Harness")} \uC548\uC804\uC7A5\uCE58 & \uAC80\uC0AC\uB300 \u2014 AI \uCF54\uB4DC \uC791\uB3D9 \uD14C\uC2A4\uD2B8`);
|
|
4844
5438
|
}
|
|
4845
5439
|
if (extraTools.includes("vercel")) {
|
|
4846
|
-
console.log(` ${
|
|
5440
|
+
console.log(` ${chalk9.cyan("Vercel")} \uC790\uB3D9 \uBC30\uD3EC \u2014 Preview + Production`);
|
|
4847
5441
|
}
|
|
4848
5442
|
if (extraTools.includes("supabase")) {
|
|
4849
|
-
console.log(` ${
|
|
5443
|
+
console.log(` ${chalk9.cyan("Supabase")} DB + \uC778\uC99D + \uC2A4\uD1A0\uB9AC\uC9C0 (PostgreSQL \uAE30\uBC18)`);
|
|
4850
5444
|
}
|
|
4851
5445
|
if (extraTools.includes("mcp")) {
|
|
4852
|
-
console.log(` ${
|
|
5446
|
+
console.log(` ${chalk9.cyan("MCP \uC11C\uBC84")} AI \uB3C4\uAD6C \u2014 Claude\uAC00 \uD638\uCD9C\uD560 \uCEE4\uC2A4\uD140 \uAE30\uB2A5/\uB370\uC774\uD130`);
|
|
4853
5447
|
}
|
|
4854
5448
|
console.log("");
|
|
4855
5449
|
console.log(colors.accent(" \uC124\uCE58 \uD655\uC778"));
|
|
@@ -4863,7 +5457,7 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
|
|
|
4863
5457
|
...extraTools.includes("omc") ? [{ label: "OMC \uB7F0\uD0C0\uC784", path: ".omc" }] : []
|
|
4864
5458
|
];
|
|
4865
5459
|
for (const check of checks) {
|
|
4866
|
-
const exists = await
|
|
5460
|
+
const exists = await fs17.pathExists(join11(projectDir, check.path));
|
|
4867
5461
|
console.log(` ${exists ? colors.success("\u2713") : colors.err("\u2717")} ${check.label.padEnd(16)} ${colors.dim(check.path)}`);
|
|
4868
5462
|
}
|
|
4869
5463
|
console.log("");
|
|
@@ -4889,9 +5483,9 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
|
|
|
4889
5483
|
await sleep2(500);
|
|
4890
5484
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4891
5485
|
const reportDir = join11(projectDir, "docs", "p-reports");
|
|
4892
|
-
await
|
|
5486
|
+
await fs17.ensureDir(reportDir);
|
|
4893
5487
|
const detailedReport = buildDetailedReport3(evalResult, projectName);
|
|
4894
|
-
await
|
|
5488
|
+
await fs17.writeFile(join11(reportDir, `${today}.md`), detailedReport, "utf8");
|
|
4895
5489
|
await sleep2(500);
|
|
4896
5490
|
console.log("");
|
|
4897
5491
|
success(`\uB9AC\uD3EC\uD2B8 \uC800\uC7A5: docs/p-reports/${today}.md`);
|
|
@@ -4909,7 +5503,7 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
|
|
|
4909
5503
|
}
|
|
4910
5504
|
console.log("");
|
|
4911
5505
|
if (!isCurrentDir) {
|
|
4912
|
-
console.log(` ${
|
|
5506
|
+
console.log(` ${chalk9.green("\u2192")} cd ${projectName} ${colors.dim("\uC774\uB3D9 \uC644\uB8CC")}`);
|
|
4913
5507
|
}
|
|
4914
5508
|
console.log("");
|
|
4915
5509
|
success("\uC774\uC81C Claude Code\uC640 \uD568\uAED8 PRD \uBB38\uC11C\uB97C \uC791\uC131\uD558\uC138\uC694.");
|
|
@@ -4942,7 +5536,7 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
|
|
|
4942
5536
|
const shellRc = getShellRcPath2();
|
|
4943
5537
|
let yoloAlreadyAliased = false;
|
|
4944
5538
|
try {
|
|
4945
|
-
const rcContent = await
|
|
5539
|
+
const rcContent = await fs17.readFile(shellRc, "utf8");
|
|
4946
5540
|
yoloAlreadyAliased = checkYolo(rcContent);
|
|
4947
5541
|
} catch {
|
|
4948
5542
|
}
|
|
@@ -4962,10 +5556,10 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
|
|
|
4962
5556
|
try {
|
|
4963
5557
|
const { getYoloAliasLine: getYoloAliasLine2 } = await Promise.resolve().then(() => (init_platform(), platform_exports));
|
|
4964
5558
|
const aliasLine = getYoloAliasLine2();
|
|
4965
|
-
const rcContent = await
|
|
5559
|
+
const rcContent = await fs17.readFile(shellRc, "utf8").catch(() => "");
|
|
4966
5560
|
if (!rcContent.includes("claude-yolo")) {
|
|
4967
|
-
await
|
|
4968
|
-
await
|
|
5561
|
+
await fs17.ensureDir(join11(shellRc, ".."));
|
|
5562
|
+
await fs17.appendFile(shellRc, `
|
|
4969
5563
|
# PAI \u2014 claude-YOLO mode
|
|
4970
5564
|
${aliasLine}
|
|
4971
5565
|
`);
|
|
@@ -5004,7 +5598,7 @@ ${aliasLine}
|
|
|
5004
5598
|
async function handleExistingProject(cwd, state) {
|
|
5005
5599
|
const { PLUGIN_META: PLUGIN_META2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
|
|
5006
5600
|
const { default: inquirer } = await import("inquirer");
|
|
5007
|
-
const
|
|
5601
|
+
const chalk9 = (await import("chalk")).default;
|
|
5008
5602
|
section("\uAE30\uC874 \uD504\uB85C\uC81D\uD2B8");
|
|
5009
5603
|
const allPlugins = Object.entries(PLUGIN_META2).map(([key, meta]) => ({
|
|
5010
5604
|
key,
|
|
@@ -5015,7 +5609,7 @@ async function handleExistingProject(cwd, state) {
|
|
|
5015
5609
|
if (p.installed) {
|
|
5016
5610
|
success(p.label);
|
|
5017
5611
|
} else {
|
|
5018
|
-
console.log(colors.dim(` \xB7 ${p.label} ${
|
|
5612
|
+
console.log(colors.dim(` \xB7 ${p.label} ${chalk9.gray("\uBBF8\uC124\uCE58")}`));
|
|
5019
5613
|
}
|
|
5020
5614
|
}
|
|
5021
5615
|
const currentConfig = await loadConfig(cwd);
|
|
@@ -5041,7 +5635,7 @@ async function handleExistingProject(cwd, state) {
|
|
|
5041
5635
|
name: `\u{1FA7A} \uD658\uACBD \uC810\uAC80 ${colors.dim("(pai check \u2014 Node/Git/Claude/OMC)")}`,
|
|
5042
5636
|
value: "doctor"
|
|
5043
5637
|
},
|
|
5044
|
-
{ name:
|
|
5638
|
+
{ name: chalk9.gray("\u{1F6AA} \uC885\uB8CC"), value: "exit" }
|
|
5045
5639
|
]
|
|
5046
5640
|
}]);
|
|
5047
5641
|
switch (action) {
|
|
@@ -5066,7 +5660,7 @@ async function handleExistingProject(cwd, state) {
|
|
|
5066
5660
|
}
|
|
5067
5661
|
async function installOrchestratorOnly(projectDir, projectName) {
|
|
5068
5662
|
const { default: inquirer } = await import("inquirer");
|
|
5069
|
-
const
|
|
5663
|
+
const chalk9 = (await import("chalk")).default;
|
|
5070
5664
|
const { generateFiles: generateFiles2 } = await Promise.resolve().then(() => (init_generator(), generator_exports));
|
|
5071
5665
|
const { analyzeProject: analyzeProject2 } = await Promise.resolve().then(() => (init_analyzer(), analyzer_exports));
|
|
5072
5666
|
const { provisionClaudeCommands: provisionClaudeCommands2 } = await Promise.resolve().then(() => (init_claude_commands(), claude_commands_exports));
|
|
@@ -5088,7 +5682,7 @@ async function installOrchestratorOnly(projectDir, projectName) {
|
|
|
5088
5682
|
{ name: `gstack ${colors.dim("\u2500 \uD45C\uC900 \uC790\uC7AC & \uACF5\uBC95 (\uAE30\uC220 \uC2A4\uD0DD)")}`, value: "gstack" },
|
|
5089
5683
|
{ name: `Harness ${colors.dim("\u2500 \uC548\uC804\uC7A5\uCE58 & \uAC80\uC0AC\uB300 (\uD14C\uC2A4\uD2B8)")}`, value: "harness" },
|
|
5090
5684
|
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"),
|
|
5091
|
-
{ name:
|
|
5685
|
+
{ name: chalk9.green("\u21B5 \uC120\uD0DD \uC644\uB8CC"), value: "__done__" }
|
|
5092
5686
|
]
|
|
5093
5687
|
}]);
|
|
5094
5688
|
const selectedPlugins = plugins.filter((p) => p !== "__done__");
|
|
@@ -5159,8 +5753,8 @@ async function installOrchestratorOnly(projectDir, projectName) {
|
|
|
5159
5753
|
printReport2(evalResult);
|
|
5160
5754
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
5161
5755
|
const reportDir = join11(projectDir, "docs", "p-reports");
|
|
5162
|
-
await
|
|
5163
|
-
await
|
|
5756
|
+
await fs17.ensureDir(reportDir);
|
|
5757
|
+
await fs17.writeFile(join11(reportDir, `${today}.md`), buildDetailedReport3(evalResult, projectName), "utf8");
|
|
5164
5758
|
console.log("");
|
|
5165
5759
|
hint(`\uC0C1\uC138 \uB9AC\uD3EC\uD2B8: docs/p-reports/${today}.md`);
|
|
5166
5760
|
} catch {
|
|
@@ -5175,7 +5769,7 @@ async function installOrchestratorOnly(projectDir, projectName) {
|
|
|
5175
5769
|
message: "Claude Code\uB97C \uC2DC\uC791\uD560\uAE4C\uC694?",
|
|
5176
5770
|
choices: [
|
|
5177
5771
|
{ name: `Claude \uBC14\uB85C \uC2DC\uC791 ${colors.dim("\u2500 claude")}`, value: "claude" },
|
|
5178
|
-
{ name:
|
|
5772
|
+
{ name: chalk9.gray("\uB098\uC911\uC5D0 \uC9C1\uC811 \uC2E4\uD589"), value: "none" }
|
|
5179
5773
|
]
|
|
5180
5774
|
}]);
|
|
5181
5775
|
if (launch === "none") {
|
|
@@ -5210,7 +5804,7 @@ async function detectLegacyProject(cwd) {
|
|
|
5210
5804
|
".gitignore"
|
|
5211
5805
|
];
|
|
5212
5806
|
for (const signal of signals) {
|
|
5213
|
-
if (await
|
|
5807
|
+
if (await fs17.pathExists(join11(cwd, signal))) return true;
|
|
5214
5808
|
}
|
|
5215
5809
|
return false;
|
|
5216
5810
|
}
|
|
@@ -5302,16 +5896,16 @@ var init_help_cmd = __esm({
|
|
|
5302
5896
|
|
|
5303
5897
|
// src/stages/design/openspec.ts
|
|
5304
5898
|
import { join as join12 } from "path";
|
|
5305
|
-
import
|
|
5899
|
+
import fs18 from "fs-extra";
|
|
5306
5900
|
async function initOpenSpec(cwd, projectName) {
|
|
5307
5901
|
const docsDir = join12(cwd, "docs");
|
|
5308
|
-
await
|
|
5902
|
+
await fs18.ensureDir(docsDir);
|
|
5309
5903
|
const openspecPath = join12(docsDir, "openspec.md");
|
|
5310
|
-
if (await
|
|
5904
|
+
if (await fs18.pathExists(openspecPath)) {
|
|
5311
5905
|
info("docs/openspec.md \uC774\uBBF8 \uC874\uC7AC \u2014 \uAC74\uB108\uB700");
|
|
5312
5906
|
return;
|
|
5313
5907
|
}
|
|
5314
|
-
await
|
|
5908
|
+
await fs18.writeFile(openspecPath, [
|
|
5315
5909
|
`# OpenSpec \u2014 ${projectName}`,
|
|
5316
5910
|
"",
|
|
5317
5911
|
"## 1. \uBAA9\uC801 (Purpose)",
|
|
@@ -5345,7 +5939,7 @@ async function validateOpenSpec(cwd) {
|
|
|
5345
5939
|
];
|
|
5346
5940
|
let specPath = null;
|
|
5347
5941
|
for (const p of candidates) {
|
|
5348
|
-
if (await
|
|
5942
|
+
if (await fs18.pathExists(p)) {
|
|
5349
5943
|
specPath = p;
|
|
5350
5944
|
break;
|
|
5351
5945
|
}
|
|
@@ -5359,7 +5953,7 @@ async function validateOpenSpec(cwd) {
|
|
|
5359
5953
|
warnings: ["openspec.md \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `pai design init` \uC744 \uC2E4\uD589\uD558\uC138\uC694."]
|
|
5360
5954
|
};
|
|
5361
5955
|
}
|
|
5362
|
-
const content = await
|
|
5956
|
+
const content = await fs18.readFile(specPath, "utf8");
|
|
5363
5957
|
const missing = [];
|
|
5364
5958
|
let filled = 0;
|
|
5365
5959
|
for (const section2 of REQUIRED_SECTIONS) {
|
|
@@ -5408,16 +6002,16 @@ var init_openspec = __esm({
|
|
|
5408
6002
|
|
|
5409
6003
|
// src/stages/design/omc.ts
|
|
5410
6004
|
import { join as join13 } from "path";
|
|
5411
|
-
import
|
|
6005
|
+
import fs19 from "fs-extra";
|
|
5412
6006
|
async function initOMC(cwd, projectName) {
|
|
5413
6007
|
const paiDir = join13(cwd, ".pai");
|
|
5414
|
-
await
|
|
6008
|
+
await fs19.ensureDir(paiDir);
|
|
5415
6009
|
const omcPath = join13(paiDir, "omc.md");
|
|
5416
|
-
if (await
|
|
6010
|
+
if (await fs19.pathExists(omcPath)) {
|
|
5417
6011
|
info(".pai/omc.md \uC774\uBBF8 \uC874\uC7AC \u2014 \uAC74\uB108\uB700");
|
|
5418
6012
|
return;
|
|
5419
6013
|
}
|
|
5420
|
-
await
|
|
6014
|
+
await fs19.writeFile(omcPath, [
|
|
5421
6015
|
`# OMC \u2014 Object Model Context (${projectName})`,
|
|
5422
6016
|
"",
|
|
5423
6017
|
"> AI\uAC00 \uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uB3C4\uBA54\uC778\uC744 \uC774\uD574\uD558\uAE30 \uC704\uD55C \uD575\uC2EC \uAC1D\uCCB4 \uBAA8\uB378",
|
|
@@ -5500,14 +6094,14 @@ var init_design_cmd = __esm({
|
|
|
5500
6094
|
|
|
5501
6095
|
// src/stages/validation/runner.ts
|
|
5502
6096
|
import { join as join14 } from "path";
|
|
5503
|
-
import
|
|
6097
|
+
import fs20 from "fs-extra";
|
|
5504
6098
|
async function runTests(cwd) {
|
|
5505
6099
|
const start = Date.now();
|
|
5506
6100
|
const gstackPath = join14(cwd, ".pai", "gstack.json");
|
|
5507
6101
|
let runner = "npm test";
|
|
5508
|
-
if (await
|
|
6102
|
+
if (await fs20.pathExists(gstackPath)) {
|
|
5509
6103
|
try {
|
|
5510
|
-
const config = await
|
|
6104
|
+
const config = await fs20.readJson(gstackPath);
|
|
5511
6105
|
if (config.testRunner === "vitest") runner = "npx vitest run";
|
|
5512
6106
|
else if (config.testRunner === "jest") runner = "npx jest";
|
|
5513
6107
|
else if (config.testRunner === "mocha") runner = "npx mocha";
|
|
@@ -5515,9 +6109,9 @@ async function runTests(cwd) {
|
|
|
5515
6109
|
}
|
|
5516
6110
|
}
|
|
5517
6111
|
const pkgPath = join14(cwd, "package.json");
|
|
5518
|
-
if (await
|
|
6112
|
+
if (await fs20.pathExists(pkgPath)) {
|
|
5519
6113
|
try {
|
|
5520
|
-
const pkg5 = await
|
|
6114
|
+
const pkg5 = await fs20.readJson(pkgPath);
|
|
5521
6115
|
if (!pkg5.scripts?.test || pkg5.scripts.test.includes("no test specified")) {
|
|
5522
6116
|
return {
|
|
5523
6117
|
runner,
|
|
@@ -5560,15 +6154,15 @@ var init_runner = __esm({
|
|
|
5560
6154
|
|
|
5561
6155
|
// src/stages/validation/harness.ts
|
|
5562
6156
|
import { join as join15 } from "path";
|
|
5563
|
-
import
|
|
6157
|
+
import fs21 from "fs-extra";
|
|
5564
6158
|
async function runHarnessCheck(cwd) {
|
|
5565
6159
|
const harnessPath = join15(cwd, ".pai", "harness.json");
|
|
5566
|
-
if (!await
|
|
6160
|
+
if (!await fs21.pathExists(harnessPath)) {
|
|
5567
6161
|
return { enabled: false, specFile: null, rules: [], checks: [] };
|
|
5568
6162
|
}
|
|
5569
6163
|
let config;
|
|
5570
6164
|
try {
|
|
5571
|
-
config = await
|
|
6165
|
+
config = await fs21.readJson(harnessPath);
|
|
5572
6166
|
} catch {
|
|
5573
6167
|
return { enabled: false, specFile: null, rules: [], checks: [] };
|
|
5574
6168
|
}
|
|
@@ -5576,8 +6170,8 @@ async function runHarnessCheck(cwd) {
|
|
|
5576
6170
|
const rules = config.rules ?? [];
|
|
5577
6171
|
const checks = [];
|
|
5578
6172
|
if (rules.includes("spec-implementation-match")) {
|
|
5579
|
-
const specExists = await
|
|
5580
|
-
const srcExists = await
|
|
6173
|
+
const specExists = await fs21.pathExists(join15(cwd, specFile));
|
|
6174
|
+
const srcExists = await fs21.pathExists(join15(cwd, "src"));
|
|
5581
6175
|
checks.push({
|
|
5582
6176
|
rule: "spec-implementation-match",
|
|
5583
6177
|
passed: specExists && srcExists,
|
|
@@ -5585,8 +6179,8 @@ async function runHarnessCheck(cwd) {
|
|
|
5585
6179
|
});
|
|
5586
6180
|
}
|
|
5587
6181
|
if (rules.includes("api-contract-test")) {
|
|
5588
|
-
const testDir = await
|
|
5589
|
-
const testDir2 = await
|
|
6182
|
+
const testDir = await fs21.pathExists(join15(cwd, "tests"));
|
|
6183
|
+
const testDir2 = await fs21.pathExists(join15(cwd, "test"));
|
|
5590
6184
|
checks.push({
|
|
5591
6185
|
rule: "api-contract-test",
|
|
5592
6186
|
passed: testDir || testDir2,
|
|
@@ -5701,13 +6295,13 @@ var init_context = __esm({
|
|
|
5701
6295
|
|
|
5702
6296
|
// src/stages/design/index.ts
|
|
5703
6297
|
import { join as join16 } from "path";
|
|
5704
|
-
import
|
|
6298
|
+
import fs22 from "fs-extra";
|
|
5705
6299
|
async function autoInstallHarness(cwd) {
|
|
5706
6300
|
const harnessPath = join16(cwd, ".pai", "harness.json");
|
|
5707
|
-
if (await
|
|
6301
|
+
if (await fs22.pathExists(harnessPath)) return;
|
|
5708
6302
|
await withSpinner("Harness Engineering \uC790\uB3D9 \uC124\uC815 \uC911...", async () => {
|
|
5709
|
-
await
|
|
5710
|
-
await
|
|
6303
|
+
await fs22.ensureDir(join16(cwd, ".pai"));
|
|
6304
|
+
await fs22.writeJson(harnessPath, {
|
|
5711
6305
|
version: "1.0",
|
|
5712
6306
|
specFile: "docs/openspec.md",
|
|
5713
6307
|
checkOn: ["pre-commit", "ci"],
|
|
@@ -6064,7 +6658,7 @@ __export(remove_cmd_exports, {
|
|
|
6064
6658
|
removeCommand: () => removeCommand
|
|
6065
6659
|
});
|
|
6066
6660
|
import { basename as basename4, dirname } from "path";
|
|
6067
|
-
import
|
|
6661
|
+
import fs23 from "fs-extra";
|
|
6068
6662
|
async function removeCommand(cwd, options) {
|
|
6069
6663
|
section("\uD504\uB85C\uC81D\uD2B8 \uC0AD\uC81C");
|
|
6070
6664
|
const config = await loadConfig(cwd);
|
|
@@ -6082,7 +6676,7 @@ async function removeCommand(cwd, options) {
|
|
|
6082
6676
|
console.log(colors.err(` ${folderName}/ \uD3F4\uB354 \uC804\uCCB4\uAC00 \uC0AD\uC81C\uB429\uB2C8\uB2E4.`));
|
|
6083
6677
|
hint("\uC774 \uC791\uC5C5\uC740 \uB418\uB3CC\uB9B4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
6084
6678
|
console.log("");
|
|
6085
|
-
const items = await
|
|
6679
|
+
const items = await fs23.readdir(cwd);
|
|
6086
6680
|
const fileCount = items.filter((i) => !i.startsWith(".")).length;
|
|
6087
6681
|
const hiddenCount = items.filter((i) => i.startsWith(".")).length;
|
|
6088
6682
|
info(`\uD30C\uC77C/\uD3F4\uB354 ${fileCount}\uAC1C, \uC228\uAE40 \uD56D\uBAA9 ${hiddenCount}\uAC1C`);
|
|
@@ -6101,7 +6695,7 @@ async function removeCommand(cwd, options) {
|
|
|
6101
6695
|
}
|
|
6102
6696
|
process.chdir(parentDir);
|
|
6103
6697
|
try {
|
|
6104
|
-
await
|
|
6698
|
+
await fs23.remove(cwd);
|
|
6105
6699
|
console.log("");
|
|
6106
6700
|
success(`${folderName}/ \uD504\uB85C\uC81D\uD2B8\uAC00 \uC0AD\uC81C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.`);
|
|
6107
6701
|
try {
|
|
@@ -6222,7 +6816,7 @@ __export(upgrade_cmd_exports, {
|
|
|
6222
6816
|
});
|
|
6223
6817
|
import { createRequire as createRequire3 } from "module";
|
|
6224
6818
|
import { fileURLToPath } from "url";
|
|
6225
|
-
import
|
|
6819
|
+
import chalk6 from "chalk";
|
|
6226
6820
|
async function upgradeCommand(cwd, options) {
|
|
6227
6821
|
section("PAI \uC5C5\uADF8\uB808\uC774\uB4DC");
|
|
6228
6822
|
const currentVersion = pkg3.version;
|
|
@@ -6245,8 +6839,8 @@ async function upgradeCommand(cwd, options) {
|
|
|
6245
6839
|
}
|
|
6246
6840
|
const configVersion = config.version;
|
|
6247
6841
|
console.log("");
|
|
6248
|
-
console.log(` \uD504\uB85C\uC81D\uD2B8 config: ${
|
|
6249
|
-
console.log(` \uD604\uC7AC CLI: ${
|
|
6842
|
+
console.log(` \uD504\uB85C\uC81D\uD2B8 config: ${chalk6.gray(`v${configVersion}`)}`);
|
|
6843
|
+
console.log(` \uD604\uC7AC CLI: ${chalk6.cyan(`v${currentVersion}`)}`);
|
|
6250
6844
|
if (!options.force && compareSemver(configVersion, currentVersion) >= 0) {
|
|
6251
6845
|
console.log("");
|
|
6252
6846
|
success("\uD504\uB85C\uC81D\uD2B8 \uD30C\uC77C\uC774 \uC774\uBBF8 \uCD5C\uC2E0 \uBC84\uC804\uC785\uB2C8\uB2E4.");
|
|
@@ -6264,7 +6858,7 @@ async function upgradeCommand(cwd, options) {
|
|
|
6264
6858
|
if (result.commandsWritten.length > 0) {
|
|
6265
6859
|
success(`\uC2AC\uB798\uC2DC \uCEE4\uB9E8\uB4DC ${result.commandsWritten.length}\uAC1C \uC5C5\uB370\uC774\uD2B8`);
|
|
6266
6860
|
for (const f of result.commandsWritten) {
|
|
6267
|
-
console.log(
|
|
6861
|
+
console.log(chalk6.gray(` ${f}`));
|
|
6268
6862
|
}
|
|
6269
6863
|
}
|
|
6270
6864
|
if (result.commandErrors.length > 0) {
|
|
@@ -6284,7 +6878,7 @@ async function upgradeCli(currentVersion, pkgName, force) {
|
|
|
6284
6878
|
const scriptPath = fileURLToPath(import.meta.url);
|
|
6285
6879
|
const mode = detectInstallMode(scriptPath);
|
|
6286
6880
|
console.log("");
|
|
6287
|
-
console.log(` \uD604\uC7AC CLI: ${
|
|
6881
|
+
console.log(` \uD604\uC7AC CLI: ${chalk6.cyan(`v${currentVersion}`)} ${chalk6.gray(`(${mode})`)}`);
|
|
6288
6882
|
if (mode === "npx") {
|
|
6289
6883
|
info("npx\uB294 \uB9E4\uBC88 \uCD5C\uC2E0 \uBC84\uC804\uC744 \uAC00\uC838\uC624\uBBC0\uB85C \uBCC4\uB3C4 \uC5C5\uADF8\uB808\uC774\uB4DC\uAC00 \uBD88\uD544\uC694\uD569\uB2C8\uB2E4.");
|
|
6290
6884
|
hint("\uB2E4\uC74C npx \uC2E4\uD589 \uC2DC \uC790\uB3D9\uC73C\uB85C \uCD5C\uC2E0 \uBC84\uC804\uC744 \uC0AC\uC6A9\uD569\uB2C8\uB2E4.");
|
|
@@ -6299,7 +6893,7 @@ async function upgradeCli(currentVersion, pkgName, force) {
|
|
|
6299
6893
|
hint(`\uC218\uB3D9 \uC5C5\uADF8\uB808\uC774\uB4DC: npm install -g ${pkgName}@latest`);
|
|
6300
6894
|
return;
|
|
6301
6895
|
}
|
|
6302
|
-
console.log(` \uCD5C\uC2E0: ${
|
|
6896
|
+
console.log(` \uCD5C\uC2E0: ${chalk6.cyan(`v${latestVersion}`)}`);
|
|
6303
6897
|
const cmp = compareSemver(currentVersion, latestVersion);
|
|
6304
6898
|
if (!force && cmp >= 0) {
|
|
6305
6899
|
console.log("");
|
|
@@ -6380,8 +6974,8 @@ __export(savetoken_cmd_exports, {
|
|
|
6380
6974
|
savetokenCommand: () => savetokenCommand
|
|
6381
6975
|
});
|
|
6382
6976
|
import { join as join17, relative } from "path";
|
|
6383
|
-
import
|
|
6384
|
-
import
|
|
6977
|
+
import fs24 from "fs-extra";
|
|
6978
|
+
import chalk7 from "chalk";
|
|
6385
6979
|
async function savetokenCommand(cwd) {
|
|
6386
6980
|
const { createSpinner: createSpinner2 } = await Promise.resolve().then(() => (init_progress(), progress_exports));
|
|
6387
6981
|
console.log("");
|
|
@@ -6405,14 +6999,14 @@ async function savetokenCommand(cwd) {
|
|
|
6405
6999
|
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"));
|
|
6406
7000
|
for (const sdk of sdks) {
|
|
6407
7001
|
const count = callSites.filter((s) => s.sdk === sdk).length;
|
|
6408
|
-
console.log(` ${
|
|
7002
|
+
console.log(` ${chalk7.cyan(sdk.padEnd(20))} ${chalk7.white(String(count))}\uAC74`);
|
|
6409
7003
|
}
|
|
6410
7004
|
if (imports.length > 0) {
|
|
6411
7005
|
console.log("");
|
|
6412
7006
|
console.log(colors.dim(" SDK \uC784\uD3EC\uD2B8"));
|
|
6413
7007
|
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"));
|
|
6414
7008
|
for (const site of imports) {
|
|
6415
|
-
console.log(` ${colors.dim(site.file)}:${
|
|
7009
|
+
console.log(` ${colors.dim(site.file)}:${chalk7.yellow(String(site.line))}`);
|
|
6416
7010
|
console.log(` ${site.content.trim().substring(0, 80)}`);
|
|
6417
7011
|
}
|
|
6418
7012
|
}
|
|
@@ -6421,7 +7015,7 @@ async function savetokenCommand(cwd) {
|
|
|
6421
7015
|
console.log(colors.dim(" AI API \uD638\uCD9C \uC9C0\uC810"));
|
|
6422
7016
|
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"));
|
|
6423
7017
|
for (const site of apiCalls) {
|
|
6424
|
-
console.log(` ${colors.dim(site.file)}:${
|
|
7018
|
+
console.log(` ${colors.dim(site.file)}:${chalk7.yellow(String(site.line))} ${chalk7.cyan(site.sdk)}`);
|
|
6425
7019
|
console.log(` ${site.content.trim().substring(0, 80)}`);
|
|
6426
7020
|
}
|
|
6427
7021
|
}
|
|
@@ -6430,7 +7024,7 @@ async function savetokenCommand(cwd) {
|
|
|
6430
7024
|
console.log(colors.dim(" Fetch \uAE30\uBC18 AI \uD638\uCD9C"));
|
|
6431
7025
|
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"));
|
|
6432
7026
|
for (const site of fetchCalls) {
|
|
6433
|
-
console.log(` ${colors.dim(site.file)}:${
|
|
7027
|
+
console.log(` ${colors.dim(site.file)}:${chalk7.yellow(String(site.line))} ${chalk7.cyan(site.sdk)}`);
|
|
6434
7028
|
console.log(` ${site.content.trim().substring(0, 80)}`);
|
|
6435
7029
|
}
|
|
6436
7030
|
}
|
|
@@ -6438,22 +7032,22 @@ async function savetokenCommand(cwd) {
|
|
|
6438
7032
|
console.log("");
|
|
6439
7033
|
console.log(colors.accent(" \uC808\uAC10 \uAC00\uB2A5\uC131 \uCD94\uC815"));
|
|
6440
7034
|
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"));
|
|
6441
|
-
console.log(` \uCD1D API \uD638\uCD9C \uC9C0\uC810 ${
|
|
6442
|
-
console.log(` \uBD84\uC11D \uD544\uC694 ${
|
|
7035
|
+
console.log(` \uCD1D API \uD638\uCD9C \uC9C0\uC810 ${chalk7.white(String(totalApiPoints))}\uAC74`);
|
|
7036
|
+
console.log(` \uBD84\uC11D \uD544\uC694 ${chalk7.yellow("Claude Code\uC5D0\uC11C \uC2EC\uCE35 \uBD84\uC11D \uD544\uC694")}`);
|
|
6443
7037
|
console.log("");
|
|
6444
7038
|
console.log(colors.dim(" \uC601\uD5A5\uB3C4\uBCC4 \uB300\uCCB4 \uC804\uB7B5"));
|
|
6445
7039
|
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"));
|
|
6446
|
-
console.log(` ${
|
|
7040
|
+
console.log(` ${chalk7.green("\u25CF")} \uB0AE\uC74C \uD14D\uC2A4\uD2B8 \uD3EC\uB9F7\uD305, \uC720\uD6A8\uC131 \uAC80\uC99D, \uD15C\uD50C\uB9BF \uC0DD\uC131`);
|
|
6447
7041
|
console.log(` \u2192 ${colors.dim("regex, JSON schema, \uD15C\uD50C\uB9BF \uC5D4\uC9C4\uC73C\uB85C \uC989\uC2DC \uB300\uCCB4")}`);
|
|
6448
|
-
console.log(` ${
|
|
7042
|
+
console.log(` ${chalk7.yellow("\u25CF")} \uC911\uAC04 \uB370\uC774\uD130 \uBD84\uB958, \uC694\uC57D, \uAC04\uB2E8\uD55C \uCD94\uCD9C`);
|
|
6449
7043
|
console.log(` \u2192 ${colors.dim("\uB8F0 \uAE30\uBC18 \uB85C\uC9C1, \uD0A4\uC6CC\uB4DC \uB9E4\uCE6D\uC73C\uB85C \uAC80\uD1A0 \uD6C4 \uB300\uCCB4")}`);
|
|
6450
|
-
console.log(` ${
|
|
7044
|
+
console.log(` ${chalk7.red("\u25CF")} \uB192\uC74C \uCF54\uB4DC \uC0DD\uC131, \uBCF5\uC7A1\uD55C \uCD94\uB860, \uCC3D\uC758\uC801 \uC0DD\uC131`);
|
|
6451
7045
|
console.log(` \u2192 ${colors.dim("AI \uD544\uC218 \u2014 \uD504\uB86C\uD504\uD2B8 \uCD5C\uC801\uD654\uB85C \uD1A0\uD070 \uC808\uAC10")}`);
|
|
6452
7046
|
const reportDir = join17(cwd, ".pai");
|
|
6453
|
-
await
|
|
7047
|
+
await fs24.ensureDir(reportDir);
|
|
6454
7048
|
const report = buildReport(callSites, cwd);
|
|
6455
7049
|
const reportPath = join17(reportDir, "savetoken-report.md");
|
|
6456
|
-
await
|
|
7050
|
+
await fs24.writeFile(reportPath, report, "utf8");
|
|
6457
7051
|
console.log("");
|
|
6458
7052
|
success("\uC2A4\uCE94 \uB9AC\uD3EC\uD2B8 \uC800\uC7A5: .pai/savetoken-report.md");
|
|
6459
7053
|
console.log("");
|
|
@@ -6611,8 +7205,8 @@ __export(wakeup_cmd_exports, {
|
|
|
6611
7205
|
});
|
|
6612
7206
|
import { join as join18 } from "path";
|
|
6613
7207
|
import { homedir as homedir3, platform as osPlatform } from "os";
|
|
6614
|
-
import
|
|
6615
|
-
import
|
|
7208
|
+
import fs25 from "fs-extra";
|
|
7209
|
+
import chalk8 from "chalk";
|
|
6616
7210
|
async function wakeupCommand(timeOrAction, schedule = "\uD3C9\uC77C") {
|
|
6617
7211
|
if (timeOrAction === "off") {
|
|
6618
7212
|
await disableWakeup();
|
|
@@ -6648,8 +7242,8 @@ async function wakeupCommand(timeOrAction, schedule = "\uD3C9\uC77C") {
|
|
|
6648
7242
|
name: "launchMode",
|
|
6649
7243
|
message: "\uC2E4\uD589 \uBAA8\uB4DC:",
|
|
6650
7244
|
choices: [
|
|
6651
|
-
{ name: `claude ${
|
|
6652
|
-
{ name: `claude --dangerously-skip-permissions ${
|
|
7245
|
+
{ name: `claude ${chalk8.gray("\uC77C\uBC18 \uBAA8\uB4DC")}`, value: "normal" },
|
|
7246
|
+
{ name: `claude --dangerously-skip-permissions ${chalk8.gray("claude-YOLO mode")}`, value: "yolo" }
|
|
6653
7247
|
]
|
|
6654
7248
|
}]);
|
|
6655
7249
|
const config = {
|
|
@@ -6658,9 +7252,9 @@ async function wakeupCommand(timeOrAction, schedule = "\uD3C9\uC77C") {
|
|
|
6658
7252
|
projectDir,
|
|
6659
7253
|
launchMode
|
|
6660
7254
|
};
|
|
6661
|
-
await
|
|
6662
|
-
await
|
|
6663
|
-
await
|
|
7255
|
+
await fs25.ensureDir(PAI_DIR2);
|
|
7256
|
+
await fs25.writeJson(CONFIG_FILE2, config, { spaces: 2 });
|
|
7257
|
+
await fs25.writeJson(MESSAGES_FILE, MESSAGES);
|
|
6664
7258
|
await createWakeupScript(config);
|
|
6665
7259
|
if (osPlatform() === "darwin") {
|
|
6666
7260
|
await setupMacOS(config);
|
|
@@ -6673,12 +7267,12 @@ async function wakeupCommand(timeOrAction, schedule = "\uD3C9\uC77C") {
|
|
|
6673
7267
|
success("\u2600\uFE0F \uC6E8\uC774\uD06C\uC5C5 \uC124\uC815 \uC644\uB8CC");
|
|
6674
7268
|
console.log("");
|
|
6675
7269
|
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"));
|
|
6676
|
-
console.log(` \uC2DC\uAC04 ${
|
|
6677
|
-
console.log(` \uC2A4\uCF00\uC904 ${
|
|
6678
|
-
console.log(` \uD504\uB85C\uC81D\uD2B8 ${
|
|
6679
|
-
console.log(` \uBAA8\uB4DC ${
|
|
7270
|
+
console.log(` \uC2DC\uAC04 ${chalk8.white(config.time)}`);
|
|
7271
|
+
console.log(` \uC2A4\uCF00\uC904 ${chalk8.white(scheduleToLabel(config.schedule))}`);
|
|
7272
|
+
console.log(` \uD504\uB85C\uC81D\uD2B8 ${chalk8.white(config.projectDir)}`);
|
|
7273
|
+
console.log(` \uBAA8\uB4DC ${chalk8.white(config.launchMode === "yolo" ? "claude-YOLO mode" : "\uC77C\uBC18 \uBAA8\uB4DC")}`);
|
|
6680
7274
|
if (osPlatform() === "darwin") {
|
|
6681
|
-
console.log(` \uC2A4\uCF00\uC904\uB7EC ${
|
|
7275
|
+
console.log(` \uC2A4\uCF00\uC904\uB7EC ${chalk8.white("launchd + pmset (wake-from-sleep)")}`);
|
|
6682
7276
|
}
|
|
6683
7277
|
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"));
|
|
6684
7278
|
console.log("");
|
|
@@ -6691,7 +7285,7 @@ async function setupMacOS(config) {
|
|
|
6691
7285
|
const { execa } = await import("execa");
|
|
6692
7286
|
const [hour, minute] = config.time.split(":").map(Number);
|
|
6693
7287
|
const plistDir = join18(homedir3(), "Library", "LaunchAgents");
|
|
6694
|
-
await
|
|
7288
|
+
await fs25.ensureDir(plistDir);
|
|
6695
7289
|
const weekdays = scheduleToWeekdays(config.schedule);
|
|
6696
7290
|
let calendarEntries;
|
|
6697
7291
|
if (weekdays.length === 7) {
|
|
@@ -6727,7 +7321,7 @@ ${calendarEntries}
|
|
|
6727
7321
|
<string>${PAI_DIR2}/wakeup.log</string>
|
|
6728
7322
|
</dict>
|
|
6729
7323
|
</plist>`;
|
|
6730
|
-
await
|
|
7324
|
+
await fs25.writeFile(PLIST_PATH, plist);
|
|
6731
7325
|
await execa("launchctl", ["unload", PLIST_PATH]).catch(() => {
|
|
6732
7326
|
});
|
|
6733
7327
|
await execa("launchctl", ["load", PLIST_PATH]);
|
|
@@ -6750,7 +7344,7 @@ async function setupWindows(config) {
|
|
|
6750
7344
|
const { execa } = await import("execa");
|
|
6751
7345
|
const [hour, minute] = config.time.split(":").map(Number);
|
|
6752
7346
|
const psScriptDir = join18(homedir3(), ".pai");
|
|
6753
|
-
await
|
|
7347
|
+
await fs25.ensureDir(psScriptDir);
|
|
6754
7348
|
const psScriptPath = join18(psScriptDir, "wakeup.ps1");
|
|
6755
7349
|
const claudeCmd = config.launchMode === "yolo" ? "claude --dangerously-skip-permissions" : "claude";
|
|
6756
7350
|
const psScript = `# PAI Wakeup \u2014 Claude Code \uC138\uC158 \uC790\uB3D9 \uC2DC\uC791
|
|
@@ -6780,7 +7374,7 @@ $notifier.Show([Windows.UI.Notifications.ToastNotification]::new($xml))
|
|
|
6780
7374
|
# Open PowerShell with Claude Code
|
|
6781
7375
|
Start-Process powershell -ArgumentList "-NoExit", "-Command", "Get-Content '$todayFile'; Write-Host ''; Set-Location '${config.projectDir}'; ${claudeCmd}"
|
|
6782
7376
|
`;
|
|
6783
|
-
await
|
|
7377
|
+
await fs25.writeFile(psScriptPath, psScript, "utf8");
|
|
6784
7378
|
const daysMap = {
|
|
6785
7379
|
"\uD3C9\uC77C": "MON,TUE,WED,THU,FRI",
|
|
6786
7380
|
"\uB9E4\uC77C": "MON,TUE,WED,THU,FRI,SAT,SUN",
|
|
@@ -6828,7 +7422,7 @@ async function disableWakeup() {
|
|
|
6828
7422
|
if (osPlatform() === "darwin") {
|
|
6829
7423
|
await execa("launchctl", ["unload", PLIST_PATH]).catch(() => {
|
|
6830
7424
|
});
|
|
6831
|
-
await
|
|
7425
|
+
await fs25.remove(PLIST_PATH).catch(() => {
|
|
6832
7426
|
});
|
|
6833
7427
|
success("launchd \uC2A4\uCF00\uC904 \uC81C\uAC70");
|
|
6834
7428
|
console.log("");
|
|
@@ -6849,23 +7443,23 @@ async function disableWakeup() {
|
|
|
6849
7443
|
} else {
|
|
6850
7444
|
await removeCronEntry();
|
|
6851
7445
|
}
|
|
6852
|
-
await
|
|
7446
|
+
await fs25.remove(CONFIG_FILE2).catch(() => {
|
|
6853
7447
|
});
|
|
6854
7448
|
console.log("");
|
|
6855
7449
|
success("\u2600\uFE0F \uC6E8\uC774\uD06C\uC5C5 \uD574\uC81C \uC644\uB8CC");
|
|
6856
7450
|
}
|
|
6857
7451
|
async function showStatus() {
|
|
6858
|
-
if (await
|
|
6859
|
-
const config = await
|
|
7452
|
+
if (await fs25.pathExists(CONFIG_FILE2)) {
|
|
7453
|
+
const config = await fs25.readJson(CONFIG_FILE2);
|
|
6860
7454
|
console.log("");
|
|
6861
7455
|
success("\u2600\uFE0F \uC6E8\uC774\uD06C\uC5C5 \uD65C\uC131\uD654");
|
|
6862
|
-
console.log(` \uC2DC\uAC04 ${
|
|
6863
|
-
console.log(` \uC2A4\uCF00\uC904 ${
|
|
6864
|
-
console.log(` \uD504\uB85C\uC81D\uD2B8 ${
|
|
6865
|
-
console.log(` \uBAA8\uB4DC ${
|
|
7456
|
+
console.log(` \uC2DC\uAC04 ${chalk8.white(config.time)}`);
|
|
7457
|
+
console.log(` \uC2A4\uCF00\uC904 ${chalk8.white(scheduleToLabel(config.schedule))}`);
|
|
7458
|
+
console.log(` \uD504\uB85C\uC81D\uD2B8 ${chalk8.white(config.projectDir)}`);
|
|
7459
|
+
console.log(` \uBAA8\uB4DC ${chalk8.white(config.launchMode === "yolo" ? "claude-YOLO mode" : "\uC77C\uBC18 \uBAA8\uB4DC")}`);
|
|
6866
7460
|
if (osPlatform() === "darwin") {
|
|
6867
|
-
const plistExists = await
|
|
6868
|
-
console.log(` launchd ${plistExists ?
|
|
7461
|
+
const plistExists = await fs25.pathExists(PLIST_PATH);
|
|
7462
|
+
console.log(` launchd ${plistExists ? chalk8.green("\uD65C\uC131") : chalk8.red("\uBE44\uD65C\uC131")}`);
|
|
6869
7463
|
}
|
|
6870
7464
|
console.log("");
|
|
6871
7465
|
} else {
|
|
@@ -7017,7 +7611,7 @@ fi
|
|
|
7017
7611
|
|
|
7018
7612
|
echo "[$(date)] PAI Wakeup completed" >> "$LOG_FILE"
|
|
7019
7613
|
`;
|
|
7020
|
-
await
|
|
7614
|
+
await fs25.writeFile(SCRIPT_FILE, script, { mode: 493 });
|
|
7021
7615
|
}
|
|
7022
7616
|
var PAI_DIR2, CONFIG_FILE2, MESSAGES_FILE, SCRIPT_FILE, PLIST_NAME, PLIST_PATH, CRON_MARKER, MESSAGES;
|
|
7023
7617
|
var init_wakeup_cmd = __esm({
|
|
@@ -7158,6 +7752,10 @@ function createProgram() {
|
|
|
7158
7752
|
const { removeCommand: removeCommand2 } = await Promise.resolve().then(() => (init_remove_cmd(), remove_cmd_exports));
|
|
7159
7753
|
await removeCommand2(process.cwd(), options);
|
|
7160
7754
|
});
|
|
7755
|
+
program2.command("fetch [recipe]").description("\uC678\uBD80 GitHub \uC800\uC7A5\uC18C\uC5D0\uC11C \uB808\uC2DC\uD53C(OAuth, \uCC57\uBD07 \uB4F1) \uB2E4\uC6B4\uB85C\uB4DC").option("--list", "\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uB808\uC2DC\uD53C \uBAA9\uB85D").option("--all", "\uB4F1\uB85D\uB41C \uBAA8\uB4E0 \uB808\uC2DC\uD53C \uC124\uCE58").option("--overwrite", "\uC774\uBBF8 \uC874\uC7AC\uD558\uB294 \uD30C\uC77C \uB36E\uC5B4\uC4F0\uAE30").action(async (recipe, options) => {
|
|
7756
|
+
const { fetchCommand: fetchCommand2 } = await Promise.resolve().then(() => (init_fetch_cmd(), fetch_cmd_exports));
|
|
7757
|
+
await fetchCommand2(process.cwd(), recipe, options);
|
|
7758
|
+
});
|
|
7161
7759
|
program2.command("upgrade").description("CLI + \uD504\uB85C\uC81D\uD2B8 \uC2AC\uB798\uC2DC \uCEE4\uB9E8\uB4DC\uB97C \uCD5C\uC2E0 \uBC84\uC804\uC73C\uB85C \uC5C5\uADF8\uB808\uC774\uB4DC").option("--force", "\uBC84\uC804 \uCCB4\uD06C \uBB34\uC2DC\uD558\uACE0 \uAC15\uC81C \uC5C5\uADF8\uB808\uC774\uB4DC").option("--skip-cli", "CLI \uC5C5\uADF8\uB808\uC774\uB4DC \uC0DD\uB7B5 (\uD504\uB85C\uC81D\uD2B8 \uD30C\uC77C\uB9CC)").option("--skip-project", "\uD504\uB85C\uC81D\uD2B8 \uD30C\uC77C \uC5C5\uADF8\uB808\uC774\uB4DC \uC0DD\uB7B5 (CLI\uB9CC)").action(async (options) => {
|
|
7162
7760
|
const { upgradeCommand: upgradeCommand2 } = await Promise.resolve().then(() => (init_upgrade_cmd(), upgrade_cmd_exports));
|
|
7163
7761
|
await upgradeCommand2(process.cwd(), options);
|