pai-zero 0.11.5 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/pai.js +578 -177
- package/dist/bin/pai.js.map +1 -1
- package/dist/cli/index.js +578 -177
- 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,
|
|
@@ -3263,6 +3345,308 @@ var init_doctor = __esm({
|
|
|
3263
3345
|
}
|
|
3264
3346
|
});
|
|
3265
3347
|
|
|
3348
|
+
// src/utils/github-fetch.ts
|
|
3349
|
+
import path4 from "path";
|
|
3350
|
+
import fs10 from "fs-extra";
|
|
3351
|
+
async function httpGet(url, timeoutMs, accept) {
|
|
3352
|
+
const controller = new AbortController();
|
|
3353
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
3354
|
+
try {
|
|
3355
|
+
return await fetch(url, {
|
|
3356
|
+
signal: controller.signal,
|
|
3357
|
+
headers: { "Accept": accept, "User-Agent": "pai-zero" }
|
|
3358
|
+
});
|
|
3359
|
+
} finally {
|
|
3360
|
+
clearTimeout(timer);
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
async function listDir(repo, ref, dirPath, timeoutMs) {
|
|
3364
|
+
const url = `https://api.github.com/repos/${repo}/contents/${encodeURI(dirPath)}?ref=${encodeURIComponent(ref)}`;
|
|
3365
|
+
const res = await httpGet(url, timeoutMs, "application/vnd.github+json");
|
|
3366
|
+
if (!res.ok) {
|
|
3367
|
+
throw new Error(`GitHub API ${res.status} ${res.statusText} (${url})`);
|
|
3368
|
+
}
|
|
3369
|
+
const data = await res.json();
|
|
3370
|
+
if (!Array.isArray(data)) {
|
|
3371
|
+
throw new Error(`Expected directory, got single entry at ${dirPath}`);
|
|
3372
|
+
}
|
|
3373
|
+
return data;
|
|
3374
|
+
}
|
|
3375
|
+
async function downloadFile(downloadUrl, destPath, timeoutMs) {
|
|
3376
|
+
const res = await httpGet(downloadUrl, timeoutMs, "*/*");
|
|
3377
|
+
if (!res.ok) {
|
|
3378
|
+
throw new Error(`Download failed ${res.status} ${res.statusText} (${downloadUrl})`);
|
|
3379
|
+
}
|
|
3380
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
3381
|
+
await fs10.ensureDir(path4.dirname(destPath));
|
|
3382
|
+
await fs10.writeFile(destPath, buf);
|
|
3383
|
+
}
|
|
3384
|
+
async function fetchGithubDir(opts) {
|
|
3385
|
+
const {
|
|
3386
|
+
repo,
|
|
3387
|
+
ref = "main",
|
|
3388
|
+
srcPath,
|
|
3389
|
+
destDir,
|
|
3390
|
+
overwrite = false,
|
|
3391
|
+
timeoutMs = 1e4
|
|
3392
|
+
} = opts;
|
|
3393
|
+
const result = { written: [], skipped: [], errors: [] };
|
|
3394
|
+
async function walk(currentSrc, currentDest) {
|
|
3395
|
+
let entries;
|
|
3396
|
+
try {
|
|
3397
|
+
entries = await listDir(repo, ref, currentSrc, timeoutMs);
|
|
3398
|
+
} catch (err) {
|
|
3399
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3400
|
+
result.errors.push({ path: currentSrc, error: msg });
|
|
3401
|
+
return;
|
|
3402
|
+
}
|
|
3403
|
+
for (const entry of entries) {
|
|
3404
|
+
const relName = entry.name;
|
|
3405
|
+
if (entry.type === "dir") {
|
|
3406
|
+
await walk(`${currentSrc}/${relName}`, path4.join(currentDest, relName));
|
|
3407
|
+
} else if (entry.type === "file") {
|
|
3408
|
+
const destFile = path4.join(currentDest, relName);
|
|
3409
|
+
if (!overwrite && await fs10.pathExists(destFile)) {
|
|
3410
|
+
result.skipped.push(destFile);
|
|
3411
|
+
continue;
|
|
3412
|
+
}
|
|
3413
|
+
if (!entry.download_url) {
|
|
3414
|
+
result.errors.push({ path: entry.path, error: "no download_url" });
|
|
3415
|
+
continue;
|
|
3416
|
+
}
|
|
3417
|
+
try {
|
|
3418
|
+
await downloadFile(entry.download_url, destFile, timeoutMs);
|
|
3419
|
+
result.written.push(destFile);
|
|
3420
|
+
} catch (err) {
|
|
3421
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3422
|
+
result.errors.push({ path: entry.path, error: msg });
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
await walk(srcPath, destDir);
|
|
3428
|
+
return result;
|
|
3429
|
+
}
|
|
3430
|
+
var init_github_fetch = __esm({
|
|
3431
|
+
"src/utils/github-fetch.ts"() {
|
|
3432
|
+
"use strict";
|
|
3433
|
+
}
|
|
3434
|
+
});
|
|
3435
|
+
|
|
3436
|
+
// src/cli/commands/fetch.cmd.ts
|
|
3437
|
+
var fetch_cmd_exports = {};
|
|
3438
|
+
__export(fetch_cmd_exports, {
|
|
3439
|
+
fetchCommand: () => fetchCommand
|
|
3440
|
+
});
|
|
3441
|
+
import path5 from "path";
|
|
3442
|
+
import fs11 from "fs-extra";
|
|
3443
|
+
import chalk4 from "chalk";
|
|
3444
|
+
async function fetchCommand(cwd, recipeKey, options) {
|
|
3445
|
+
if (options.list) {
|
|
3446
|
+
section("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uB808\uC2DC\uD53C");
|
|
3447
|
+
for (const key of listRecipeKeys()) {
|
|
3448
|
+
const r = RECIPES[key];
|
|
3449
|
+
console.log(` ${colors.accent(key.padEnd(14))} ${r.label}`);
|
|
3450
|
+
console.log(` ${colors.dim(" " + r.description)}`);
|
|
3451
|
+
console.log(` ${colors.dim(" \uC18C\uC2A4: github.com/" + r.source.repo + "/tree/" + r.source.ref + "/" + r.source.path)}`);
|
|
3452
|
+
console.log("");
|
|
3453
|
+
}
|
|
3454
|
+
hint("\uC0AC\uC6A9: pai fetch <key> \uB610\uB294 pai fetch --all");
|
|
3455
|
+
return;
|
|
3456
|
+
}
|
|
3457
|
+
let keys;
|
|
3458
|
+
if (options.all) {
|
|
3459
|
+
keys = listRecipeKeys();
|
|
3460
|
+
} else {
|
|
3461
|
+
if (!recipeKey) {
|
|
3462
|
+
error("\uB808\uC2DC\uD53C \uD0A4\uB97C \uC9C0\uC815\uD558\uC138\uC694.");
|
|
3463
|
+
hint(`\uC0AC\uC6A9 \uAC00\uB2A5: ${listRecipeKeys().join(", ")}`);
|
|
3464
|
+
hint("\uBAA9\uB85D: pai fetch --list");
|
|
3465
|
+
process.exitCode = 1;
|
|
3466
|
+
return;
|
|
3467
|
+
}
|
|
3468
|
+
if (!getRecipe(recipeKey)) {
|
|
3469
|
+
error(`\uC54C \uC218 \uC5C6\uB294 \uB808\uC2DC\uD53C: ${recipeKey}`);
|
|
3470
|
+
hint(`\uC0AC\uC6A9 \uAC00\uB2A5: ${listRecipeKeys().join(", ")}`);
|
|
3471
|
+
process.exitCode = 1;
|
|
3472
|
+
return;
|
|
3473
|
+
}
|
|
3474
|
+
keys = [recipeKey.toLowerCase()];
|
|
3475
|
+
}
|
|
3476
|
+
section("\uB808\uC2DC\uD53C \uB2E4\uC6B4\uB85C\uB4DC");
|
|
3477
|
+
const installed = [];
|
|
3478
|
+
for (const key of keys) {
|
|
3479
|
+
const recipe = RECIPES[key];
|
|
3480
|
+
const targetDir = path5.join(cwd, recipe.target);
|
|
3481
|
+
console.log("");
|
|
3482
|
+
console.log(` ${colors.accent(key)} \u2014 ${recipe.label}`);
|
|
3483
|
+
const result = await withSpinner(`\uB2E4\uC6B4\uB85C\uB4DC \uC911...`, async () => {
|
|
3484
|
+
return fetchGithubDir({
|
|
3485
|
+
repo: recipe.source.repo,
|
|
3486
|
+
ref: recipe.source.ref,
|
|
3487
|
+
srcPath: recipe.source.path,
|
|
3488
|
+
destDir: targetDir,
|
|
3489
|
+
overwrite: options.overwrite ?? false
|
|
3490
|
+
});
|
|
3491
|
+
});
|
|
3492
|
+
if (result.errors.length > 0) {
|
|
3493
|
+
error(`\uB2E4\uC6B4\uB85C\uB4DC \uC2E4\uD328: ${result.errors[0].error}`);
|
|
3494
|
+
for (const e of result.errors.slice(1)) {
|
|
3495
|
+
console.log(chalk4.gray(` ${e.path}: ${e.error}`));
|
|
3496
|
+
}
|
|
3497
|
+
continue;
|
|
3498
|
+
}
|
|
3499
|
+
if (result.written.length > 0) {
|
|
3500
|
+
success(`${result.written.length}\uAC1C \uD30C\uC77C \uC800\uC7A5`);
|
|
3501
|
+
for (const f of result.written) {
|
|
3502
|
+
console.log(chalk4.gray(` ${path5.relative(cwd, f)}`));
|
|
3503
|
+
}
|
|
3504
|
+
}
|
|
3505
|
+
if (result.skipped.length > 0) {
|
|
3506
|
+
info(`${result.skipped.length}\uAC1C \uD30C\uC77C \uAC74\uB108\uB700 (\uC774\uBBF8 \uC874\uC7AC \u2014 \uB36E\uC5B4\uC4F0\uAE30: --overwrite)`);
|
|
3507
|
+
}
|
|
3508
|
+
await appendEnvKeys(cwd, recipe);
|
|
3509
|
+
installed.push(key);
|
|
3510
|
+
}
|
|
3511
|
+
if (installed.length === 0) {
|
|
3512
|
+
return;
|
|
3513
|
+
}
|
|
3514
|
+
await upsertRecipesSkill(cwd, installed);
|
|
3515
|
+
await upsertClaudeMdBlock(cwd, installed);
|
|
3516
|
+
console.log("");
|
|
3517
|
+
success(`\uB808\uC2DC\uD53C ${installed.length}\uAC1C \uC124\uCE58 \uC644\uB8CC: ${installed.join(", ")}`);
|
|
3518
|
+
console.log("");
|
|
3519
|
+
console.log(colors.accent(" \uB2E4\uC74C \uB2E8\uACC4"));
|
|
3520
|
+
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"));
|
|
3521
|
+
console.log(` ${colors.success("1.")} ${colors.dim(".env.local \uD30C\uC77C\uC744 \uC5F4\uC5B4 \uD658\uACBD\uBCC0\uC218 \uAC12\uC744 \uCC44\uC6B0\uC138\uC694")}`);
|
|
3522
|
+
for (const key of installed) {
|
|
3523
|
+
const recipe = RECIPES[key];
|
|
3524
|
+
for (const ek of recipe.envKeys) {
|
|
3525
|
+
console.log(` ${chalk4.cyan(ek.key.padEnd(22))} ${colors.dim(ek.hint ?? "")}`);
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3528
|
+
console.log("");
|
|
3529
|
+
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")}`);
|
|
3530
|
+
for (const key of installed) {
|
|
3531
|
+
const recipe = RECIPES[key];
|
|
3532
|
+
console.log(` ${chalk4.cyan(recipe.target + "/")}`);
|
|
3533
|
+
}
|
|
3534
|
+
console.log("");
|
|
3535
|
+
}
|
|
3536
|
+
async function appendEnvKeys(cwd, recipe) {
|
|
3537
|
+
if (recipe.envKeys.length === 0) return;
|
|
3538
|
+
const envPath = path5.join(cwd, ".env.local");
|
|
3539
|
+
let content = "";
|
|
3540
|
+
if (await fs11.pathExists(envPath)) {
|
|
3541
|
+
content = await fs11.readFile(envPath, "utf8");
|
|
3542
|
+
}
|
|
3543
|
+
const missingKeys = recipe.envKeys.filter((ek) => !content.includes(`${ek.key}=`));
|
|
3544
|
+
if (missingKeys.length === 0) return;
|
|
3545
|
+
const lines = [];
|
|
3546
|
+
if (content.length > 0 && !content.endsWith("\n")) lines.push("");
|
|
3547
|
+
lines.push("");
|
|
3548
|
+
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`);
|
|
3549
|
+
lines.push(`# \uAC00\uC774\uB4DC: ${recipe.target}/guideline.md`);
|
|
3550
|
+
for (const ek of missingKeys) {
|
|
3551
|
+
if (ek.hint) lines.push(`# ${ek.hint}`);
|
|
3552
|
+
lines.push(`${ek.key}=${ek.default ?? ""}`);
|
|
3553
|
+
}
|
|
3554
|
+
await fs11.ensureFile(envPath);
|
|
3555
|
+
await fs11.appendFile(envPath, lines.join("\n") + "\n");
|
|
3556
|
+
}
|
|
3557
|
+
async function upsertRecipesSkill(cwd, installedKeys) {
|
|
3558
|
+
const skillDir = path5.join(cwd, ".claude", "skills", "recipes");
|
|
3559
|
+
await fs11.ensureDir(skillDir);
|
|
3560
|
+
const skillPath = path5.join(skillDir, "SKILL.md");
|
|
3561
|
+
const recipes = installedKeys.map((k) => RECIPES[k]);
|
|
3562
|
+
const triggers = recipes.map((r) => r.skillDescription ?? `${r.label} \uAD00\uB828 \uAE30\uB2A5 \uAD6C\uD604 \uC2DC ${r.target}/ \uCC38\uC870`).join("\n- ");
|
|
3563
|
+
const body = [
|
|
3564
|
+
"---",
|
|
3565
|
+
"name: recipes",
|
|
3566
|
+
`description: "\uC0AC\uB0B4 \uC2DC\uC2A4\uD15C \uC5F0\uB3D9 \uB808\uC2DC\uD53C \u2014 ${recipes.map((r) => r.label).join(", ")}"`,
|
|
3567
|
+
"---",
|
|
3568
|
+
"",
|
|
3569
|
+
"# \uC0AC\uB0B4 \uC2DC\uC2A4\uD15C \uC5F0\uB3D9 \uB808\uC2DC\uD53C",
|
|
3570
|
+
"",
|
|
3571
|
+
"\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.",
|
|
3572
|
+
"\uAD00\uB828 \uC694\uCCAD\uC744 \uBC1B\uC73C\uBA74 \uD574\uB2F9 \uACBD\uB85C\uC758 `guideline.md`\uB97C **\uBC18\uB4DC\uC2DC \uBA3C\uC800 \uC77D\uACE0**",
|
|
3573
|
+
"\uC9C0\uCE68\uC5D0 \uB9DE\uAC8C \uAD6C\uD604\uD558\uC138\uC694.",
|
|
3574
|
+
"",
|
|
3575
|
+
"## \uD2B8\uB9AC\uAC70",
|
|
3576
|
+
"",
|
|
3577
|
+
"- " + triggers,
|
|
3578
|
+
"",
|
|
3579
|
+
"## \uC124\uCE58\uB41C \uB808\uC2DC\uD53C",
|
|
3580
|
+
"",
|
|
3581
|
+
...recipes.map((r) => [
|
|
3582
|
+
`### ${r.label}`,
|
|
3583
|
+
"",
|
|
3584
|
+
`- \uACBD\uB85C: \`${r.target}/\``,
|
|
3585
|
+
`- \uC8FC\uC694 \uBB38\uC11C: \`${r.target}/guideline.md\``,
|
|
3586
|
+
`- \uD658\uACBD\uBCC0\uC218: ${r.envKeys.map((e) => "`" + e.key + "`").join(", ")}`,
|
|
3587
|
+
""
|
|
3588
|
+
].join("\n")),
|
|
3589
|
+
"",
|
|
3590
|
+
"## \uC791\uC5C5 \uC21C\uC11C",
|
|
3591
|
+
"",
|
|
3592
|
+
"1. \uC0AC\uC6A9\uC790\uC758 \uC694\uCCAD\uC774 \uC704 \uD2B8\uB9AC\uAC70 \uC911 \uD558\uB098\uC5D0 \uD574\uB2F9\uD558\uB294\uC9C0 \uD310\uB2E8",
|
|
3593
|
+
"2. \uD574\uB2F9 \uB808\uC2DC\uD53C\uC758 `guideline.md`\uB97C Read \uB3C4\uAD6C\uB85C \uC77D\uAE30",
|
|
3594
|
+
"3. \uC0D8\uD50C \uCF54\uB4DC(`*.html`, `*.ts` \uB4F1)\uAC00 \uC788\uC73C\uBA74 \uD568\uAED8 \uD655\uC778",
|
|
3595
|
+
"4. `.env.local`\uC5D0 \uAD00\uB828 \uD658\uACBD\uBCC0\uC218\uAC00 \uCC44\uC6CC\uC838 \uC788\uB294\uC9C0 \uD655\uC778",
|
|
3596
|
+
"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",
|
|
3597
|
+
""
|
|
3598
|
+
].join("\n");
|
|
3599
|
+
await fs11.writeFile(skillPath, body);
|
|
3600
|
+
}
|
|
3601
|
+
async function upsertClaudeMdBlock(cwd, installedKeys) {
|
|
3602
|
+
const claudeMdPath = path5.join(cwd, "CLAUDE.md");
|
|
3603
|
+
const BLOCK_START = "<!-- pai:recipes:start -->";
|
|
3604
|
+
const BLOCK_END = "<!-- pai:recipes:end -->";
|
|
3605
|
+
const lines = [];
|
|
3606
|
+
lines.push(BLOCK_START);
|
|
3607
|
+
lines.push("## \uC0AC\uB0B4 \uC2DC\uC2A4\uD15C \uC5F0\uB3D9");
|
|
3608
|
+
lines.push("");
|
|
3609
|
+
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");
|
|
3610
|
+
lines.push("`guideline.md`\uB97C \uBA3C\uC800 \uC77D\uACE0 \uC9C0\uCE68\uC744 \uB530\uB974\uC138\uC694:");
|
|
3611
|
+
lines.push("");
|
|
3612
|
+
for (const key of installedKeys) {
|
|
3613
|
+
const r = RECIPES[key];
|
|
3614
|
+
lines.push(`- **${r.label}** \u2014 \`${r.target}/\``);
|
|
3615
|
+
lines.push(` \uD658\uACBD\uBCC0\uC218: ${r.envKeys.map((e) => "`" + e.key + "`").join(", ")}`);
|
|
3616
|
+
}
|
|
3617
|
+
lines.push("");
|
|
3618
|
+
lines.push("\uD658\uACBD\uBCC0\uC218 \uAC12\uC740 `.env.local`\uC5D0 \uCC44\uC6CC\uC838 \uC788\uC5B4\uC57C \uD569\uB2C8\uB2E4.");
|
|
3619
|
+
lines.push(BLOCK_END);
|
|
3620
|
+
const block = lines.join("\n");
|
|
3621
|
+
let content = "";
|
|
3622
|
+
if (await fs11.pathExists(claudeMdPath)) {
|
|
3623
|
+
content = await fs11.readFile(claudeMdPath, "utf8");
|
|
3624
|
+
}
|
|
3625
|
+
const startIdx = content.indexOf(BLOCK_START);
|
|
3626
|
+
const endIdx = content.indexOf(BLOCK_END);
|
|
3627
|
+
if (startIdx >= 0 && endIdx > startIdx) {
|
|
3628
|
+
const before = content.slice(0, startIdx);
|
|
3629
|
+
const after = content.slice(endIdx + BLOCK_END.length);
|
|
3630
|
+
content = before + block + after;
|
|
3631
|
+
} else {
|
|
3632
|
+
if (content.length > 0 && !content.endsWith("\n")) content += "\n";
|
|
3633
|
+
if (content.length > 0) content += "\n";
|
|
3634
|
+
content += block + "\n";
|
|
3635
|
+
}
|
|
3636
|
+
await fs11.ensureFile(claudeMdPath);
|
|
3637
|
+
await fs11.writeFile(claudeMdPath, content);
|
|
3638
|
+
}
|
|
3639
|
+
var init_fetch_cmd = __esm({
|
|
3640
|
+
"src/cli/commands/fetch.cmd.ts"() {
|
|
3641
|
+
"use strict";
|
|
3642
|
+
init_ui();
|
|
3643
|
+
init_logger();
|
|
3644
|
+
init_recipes();
|
|
3645
|
+
init_github_fetch();
|
|
3646
|
+
init_progress();
|
|
3647
|
+
}
|
|
3648
|
+
});
|
|
3649
|
+
|
|
3266
3650
|
// src/stages/environment/index.ts
|
|
3267
3651
|
var environmentStage;
|
|
3268
3652
|
var init_environment = __esm({
|
|
@@ -3378,6 +3762,19 @@ var init_environment = __esm({
|
|
|
3378
3762
|
console.log("");
|
|
3379
3763
|
const installResults = await installTools(interview.tools, input.cwd);
|
|
3380
3764
|
printInstallReport(installResults, interview.tools);
|
|
3765
|
+
if (interview.recipes && interview.recipes.length > 0) {
|
|
3766
|
+
console.log("");
|
|
3767
|
+
const { fetchCommand: fetchCommand2 } = await Promise.resolve().then(() => (init_fetch_cmd(), fetch_cmd_exports));
|
|
3768
|
+
for (const recipeKey of interview.recipes) {
|
|
3769
|
+
try {
|
|
3770
|
+
await fetchCommand2(input.cwd, recipeKey, {});
|
|
3771
|
+
} catch (err) {
|
|
3772
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3773
|
+
warn(`\uB808\uC2DC\uD53C '${recipeKey}' \uB2E4\uC6B4\uB85C\uB4DC \uC2E4\uD328 \u2014 ${msg}`);
|
|
3774
|
+
hint(`\uB098\uC911\uC5D0 \uC218\uB3D9: pai fetch ${recipeKey}`);
|
|
3775
|
+
}
|
|
3776
|
+
}
|
|
3777
|
+
}
|
|
3381
3778
|
console.log("");
|
|
3382
3779
|
await withSpinner("\uC124\uC815 \uC800\uC7A5 \uC911...", async () => {
|
|
3383
3780
|
const config = createDefaultConfig(interview.projectName, interview.mode);
|
|
@@ -3422,8 +3819,8 @@ __export(detector_exports, {
|
|
|
3422
3819
|
pluginsAddedByPromotion: () => pluginsAddedByPromotion,
|
|
3423
3820
|
scanProjectState: () => scanProjectState
|
|
3424
3821
|
});
|
|
3425
|
-
import
|
|
3426
|
-
import
|
|
3822
|
+
import path6 from "path";
|
|
3823
|
+
import fs12 from "fs-extra";
|
|
3427
3824
|
async function scanProjectState(cwd) {
|
|
3428
3825
|
const result = {
|
|
3429
3826
|
isNewProject: true,
|
|
@@ -3433,11 +3830,11 @@ async function scanProjectState(cwd) {
|
|
|
3433
3830
|
missingPlugins: [],
|
|
3434
3831
|
details: {}
|
|
3435
3832
|
};
|
|
3436
|
-
const paiConfigPath =
|
|
3437
|
-
if (await
|
|
3833
|
+
const paiConfigPath = path6.join(cwd, ".pai", "config.json");
|
|
3834
|
+
if (await fs12.pathExists(paiConfigPath)) {
|
|
3438
3835
|
result.hasPaiConfig = true;
|
|
3439
3836
|
try {
|
|
3440
|
-
const config = await
|
|
3837
|
+
const config = await fs12.readJson(paiConfigPath);
|
|
3441
3838
|
result.projectMode = config.mode != null ? normalizeMode(config.mode) : null;
|
|
3442
3839
|
} catch {
|
|
3443
3840
|
}
|
|
@@ -3445,7 +3842,7 @@ async function scanProjectState(cwd) {
|
|
|
3445
3842
|
for (const [key, signatures] of Object.entries(PLUGIN_SIGNATURES)) {
|
|
3446
3843
|
const installed = await Promise.any(
|
|
3447
3844
|
signatures.map(async (sig) => {
|
|
3448
|
-
if (await
|
|
3845
|
+
if (await fs12.pathExists(path6.join(cwd, sig))) return true;
|
|
3449
3846
|
throw new Error("not found");
|
|
3450
3847
|
})
|
|
3451
3848
|
).catch(() => false);
|
|
@@ -3456,7 +3853,7 @@ async function scanProjectState(cwd) {
|
|
|
3456
3853
|
result.missingPlugins.push(key);
|
|
3457
3854
|
}
|
|
3458
3855
|
}
|
|
3459
|
-
const hasAnyContent = result.installedPlugins.length > 0 || await
|
|
3856
|
+
const hasAnyContent = result.installedPlugins.length > 0 || await fs12.pathExists(path6.join(cwd, "package.json")) || await fs12.pathExists(path6.join(cwd, "src")) || await fs12.pathExists(path6.join(cwd, "README.md"));
|
|
3460
3857
|
result.isNewProject = !hasAnyContent;
|
|
3461
3858
|
return result;
|
|
3462
3859
|
}
|
|
@@ -3603,7 +4000,7 @@ __export(analyzer_exports2, {
|
|
|
3603
4000
|
analyzeRepository: () => analyzeRepository
|
|
3604
4001
|
});
|
|
3605
4002
|
import { join as join7 } from "path";
|
|
3606
|
-
import
|
|
4003
|
+
import fs13 from "fs-extra";
|
|
3607
4004
|
async function analyzeRepository(repoPath) {
|
|
3608
4005
|
try {
|
|
3609
4006
|
return await aiAnalysis(repoPath);
|
|
@@ -3664,14 +4061,14 @@ async function checkTestCoverage(repoPath) {
|
|
|
3664
4061
|
".nycrc"
|
|
3665
4062
|
];
|
|
3666
4063
|
for (const f of testConfigs) {
|
|
3667
|
-
const found = await
|
|
4064
|
+
const found = await fs13.pathExists(join7(repoPath, f));
|
|
3668
4065
|
findings.push({ item: f, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
3669
4066
|
if (found) score += 20;
|
|
3670
4067
|
}
|
|
3671
4068
|
const testDirs = ["tests", "test", "__tests__", "spec"];
|
|
3672
4069
|
let hasTestDir = false;
|
|
3673
4070
|
for (const d of testDirs) {
|
|
3674
|
-
if (await
|
|
4071
|
+
if (await fs13.pathExists(join7(repoPath, d))) {
|
|
3675
4072
|
findings.push({ item: d, found: true, details: "\uD14C\uC2A4\uD2B8 \uB514\uB809\uD1A0\uB9AC \uC874\uC7AC" });
|
|
3676
4073
|
hasTestDir = true;
|
|
3677
4074
|
score += 30;
|
|
@@ -3694,8 +4091,8 @@ async function checkCiCd(repoPath) {
|
|
|
3694
4091
|
{ path: "Jenkinsfile", label: "Jenkins" },
|
|
3695
4092
|
{ path: ".circleci", label: "CircleCI" }
|
|
3696
4093
|
];
|
|
3697
|
-
for (const { path:
|
|
3698
|
-
const found = await
|
|
4094
|
+
for (const { path: path8, label } of ciConfigs) {
|
|
4095
|
+
const found = await fs13.pathExists(join7(repoPath, path8));
|
|
3699
4096
|
findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
3700
4097
|
if (found) score += 40;
|
|
3701
4098
|
}
|
|
@@ -3713,8 +4110,8 @@ async function checkHooks(repoPath) {
|
|
|
3713
4110
|
{ path: "commitlint.config.js", label: "commitlint" },
|
|
3714
4111
|
{ path: ".claude/settings.json", label: "Claude Code settings" }
|
|
3715
4112
|
];
|
|
3716
|
-
for (const { path:
|
|
3717
|
-
const found = await
|
|
4113
|
+
for (const { path: path8, label } of hookConfigs) {
|
|
4114
|
+
const found = await fs13.pathExists(join7(repoPath, path8));
|
|
3718
4115
|
findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
3719
4116
|
if (found) score += 20;
|
|
3720
4117
|
}
|
|
@@ -3731,8 +4128,8 @@ async function checkRepoStructure(repoPath) {
|
|
|
3731
4128
|
{ path: ".env.example", label: "\uD658\uACBD\uBCC0\uC218 \uC608\uC2DC" },
|
|
3732
4129
|
{ path: ".gitignore", label: ".gitignore" }
|
|
3733
4130
|
];
|
|
3734
|
-
for (const { path:
|
|
3735
|
-
const found = await
|
|
4131
|
+
for (const { path: path8, label } of structureChecks) {
|
|
4132
|
+
const found = await fs13.pathExists(join7(repoPath, path8));
|
|
3736
4133
|
findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
3737
4134
|
if (found) score += 25;
|
|
3738
4135
|
}
|
|
@@ -3748,8 +4145,8 @@ async function checkDocumentation(repoPath) {
|
|
|
3748
4145
|
{ path: "docs", label: "docs/ \uB514\uB809\uD1A0\uB9AC", points: 25 },
|
|
3749
4146
|
{ path: "docs/openspec.md", label: "OpenSpec PRD", points: 25 }
|
|
3750
4147
|
];
|
|
3751
|
-
for (const { path:
|
|
3752
|
-
const found = await
|
|
4148
|
+
for (const { path: path8, label, points } of docChecks) {
|
|
4149
|
+
const found = await fs13.pathExists(join7(repoPath, path8));
|
|
3753
4150
|
findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
3754
4151
|
if (found) score += points;
|
|
3755
4152
|
}
|
|
@@ -3767,8 +4164,8 @@ async function checkHarnessEngineering(repoPath) {
|
|
|
3767
4164
|
{ path: ".claude/commands", label: ".claude/commands/", points: 10 },
|
|
3768
4165
|
{ path: ".pai/config.json", label: "PAI config", points: 10 }
|
|
3769
4166
|
];
|
|
3770
|
-
for (const { path:
|
|
3771
|
-
const found = await
|
|
4167
|
+
for (const { path: path8, label, points } of harnessChecks) {
|
|
4168
|
+
const found = await fs13.pathExists(join7(repoPath, path8));
|
|
3772
4169
|
findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
|
|
3773
4170
|
if (found) score += points;
|
|
3774
4171
|
}
|
|
@@ -3902,41 +4299,41 @@ __export(reporter_exports, {
|
|
|
3902
4299
|
printReport: () => printReport,
|
|
3903
4300
|
printVerboseFindings: () => printVerboseFindings
|
|
3904
4301
|
});
|
|
3905
|
-
import
|
|
4302
|
+
import chalk5 from "chalk";
|
|
3906
4303
|
function printReport(result) {
|
|
3907
4304
|
console.log("");
|
|
3908
|
-
console.log(
|
|
3909
|
-
console.log(
|
|
4305
|
+
console.log(chalk5.hex("#7B93DB")(" PAI Evaluation Report"));
|
|
4306
|
+
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
4307
|
console.log("");
|
|
3911
4308
|
const gradeColor = GRADE_COLORS[result.totalGrade];
|
|
3912
4309
|
console.log(` \uC885\uD569 \uC810\uC218: ${gradeColor(String(result.totalScore))} / 100 \uB4F1\uAE09: ${gradeColor(result.totalGrade)}`);
|
|
3913
4310
|
if (result.penaltyApplied) {
|
|
3914
|
-
console.log(
|
|
4311
|
+
console.log(chalk5.red(` \u26A0 ${result.penaltyReason}`));
|
|
3915
4312
|
}
|
|
3916
4313
|
console.log("");
|
|
3917
|
-
console.log(
|
|
4314
|
+
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
4315
|
for (const cat of result.categories) {
|
|
3919
4316
|
const color = GRADE_COLORS[cat.grade];
|
|
3920
|
-
const tierLabel = cat.tier === "must" ?
|
|
4317
|
+
const tierLabel = cat.tier === "must" ? chalk5.hex("#E06C75")("[\uD544\uC218]") : chalk5.gray("[\uC120\uD0DD]");
|
|
3921
4318
|
const bar = renderBar(cat.score);
|
|
3922
4319
|
console.log(` ${tierLabel} ${cat.name.padEnd(16)} ${bar} ${color(`${cat.score}`).padStart(12)} ${color(cat.grade)}`);
|
|
3923
4320
|
}
|
|
3924
4321
|
console.log("");
|
|
3925
|
-
console.log(
|
|
4322
|
+
console.log(chalk5.gray(` ${result.summary}`));
|
|
3926
4323
|
console.log("");
|
|
3927
4324
|
}
|
|
3928
4325
|
function printVerboseFindings(result) {
|
|
3929
4326
|
for (const cat of result.categories) {
|
|
3930
4327
|
console.log("");
|
|
3931
|
-
console.log(
|
|
4328
|
+
console.log(chalk5.bold(` ${cat.name} (${cat.grade}, ${cat.score}\uC810)`));
|
|
3932
4329
|
for (const f of cat.rawFindings) {
|
|
3933
|
-
const icon = f.found ?
|
|
4330
|
+
const icon = f.found ? chalk5.green("\u2713") : chalk5.red("\u2717");
|
|
3934
4331
|
console.log(` ${icon} ${f.item} \u2014 ${f.details}`);
|
|
3935
4332
|
}
|
|
3936
4333
|
for (const r of cat.recommendations) {
|
|
3937
|
-
const severity = r.severity === "critical" ?
|
|
4334
|
+
const severity = r.severity === "critical" ? chalk5.red("!") : r.severity === "warning" ? chalk5.yellow("!") : chalk5.gray("i");
|
|
3938
4335
|
console.log(` ${severity} ${r.message}`);
|
|
3939
|
-
console.log(
|
|
4336
|
+
console.log(chalk5.gray(` \u2192 ${r.action}`));
|
|
3940
4337
|
}
|
|
3941
4338
|
}
|
|
3942
4339
|
}
|
|
@@ -3944,8 +4341,8 @@ function renderBar(score) {
|
|
|
3944
4341
|
const width = 20;
|
|
3945
4342
|
const filled = Math.round(score / 100 * width);
|
|
3946
4343
|
const empty = width - filled;
|
|
3947
|
-
const color = score >= 80 ?
|
|
3948
|
-
return color("\u2588".repeat(filled)) +
|
|
4344
|
+
const color = score >= 80 ? chalk5.hex("#6BCB77") : score >= 60 ? chalk5.hex("#E2B340") : chalk5.hex("#E06C75");
|
|
4345
|
+
return color("\u2588".repeat(filled)) + chalk5.gray("\u2591".repeat(empty));
|
|
3949
4346
|
}
|
|
3950
4347
|
function buildMarkdownReport(result) {
|
|
3951
4348
|
const lines = [
|
|
@@ -4136,11 +4533,11 @@ var init_reporter = __esm({
|
|
|
4136
4533
|
"src/stages/evaluation/reporter.ts"() {
|
|
4137
4534
|
"use strict";
|
|
4138
4535
|
GRADE_COLORS = {
|
|
4139
|
-
A:
|
|
4140
|
-
B:
|
|
4141
|
-
C:
|
|
4142
|
-
D:
|
|
4143
|
-
F:
|
|
4536
|
+
A: chalk5.hex("#6BCB77"),
|
|
4537
|
+
B: chalk5.hex("#7B93DB"),
|
|
4538
|
+
C: chalk5.hex("#E2B340"),
|
|
4539
|
+
D: chalk5.hex("#E06C75"),
|
|
4540
|
+
F: chalk5.hex("#CC4444")
|
|
4144
4541
|
};
|
|
4145
4542
|
}
|
|
4146
4543
|
});
|
|
@@ -4153,54 +4550,54 @@ __export(shell_cd_exports, {
|
|
|
4153
4550
|
});
|
|
4154
4551
|
import { join as join8 } from "path";
|
|
4155
4552
|
import { homedir as homedir2 } from "os";
|
|
4156
|
-
import
|
|
4553
|
+
import fs14 from "fs-extra";
|
|
4157
4554
|
async function requestCdAfter(targetDir) {
|
|
4158
|
-
await
|
|
4159
|
-
await
|
|
4555
|
+
await fs14.ensureDir(PAI_DIR);
|
|
4556
|
+
await fs14.writeFile(CD_FILE, targetDir);
|
|
4160
4557
|
}
|
|
4161
4558
|
async function installShellHelper() {
|
|
4162
|
-
await
|
|
4559
|
+
await fs14.ensureDir(PAI_DIR);
|
|
4163
4560
|
if (isWindows) {
|
|
4164
4561
|
return installPowerShellHelper();
|
|
4165
4562
|
}
|
|
4166
4563
|
return installBashHelper();
|
|
4167
4564
|
}
|
|
4168
4565
|
async function installBashHelper() {
|
|
4169
|
-
await
|
|
4566
|
+
await fs14.writeFile(HELPER_FILE_SH, BASH_HELPER);
|
|
4170
4567
|
const rcFile = getShellRcPath();
|
|
4171
4568
|
const sourceLine = 'source "$HOME/.pai/shell-helper.sh"';
|
|
4172
|
-
if (await
|
|
4173
|
-
const content = await
|
|
4569
|
+
if (await fs14.pathExists(rcFile)) {
|
|
4570
|
+
const content = await fs14.readFile(rcFile, "utf8");
|
|
4174
4571
|
if (content.includes("shell-helper.sh")) {
|
|
4175
4572
|
return true;
|
|
4176
4573
|
}
|
|
4177
|
-
await
|
|
4574
|
+
await fs14.appendFile(rcFile, `
|
|
4178
4575
|
# PAI \u2014 \uC790\uB3D9 \uB514\uB809\uD1A0\uB9AC \uC774\uB3D9
|
|
4179
4576
|
${sourceLine}
|
|
4180
4577
|
`);
|
|
4181
4578
|
return false;
|
|
4182
4579
|
}
|
|
4183
|
-
await
|
|
4580
|
+
await fs14.writeFile(rcFile, `${sourceLine}
|
|
4184
4581
|
`);
|
|
4185
4582
|
return false;
|
|
4186
4583
|
}
|
|
4187
4584
|
async function installPowerShellHelper() {
|
|
4188
|
-
await
|
|
4585
|
+
await fs14.writeFile(HELPER_FILE_PS1, POWERSHELL_HELPER);
|
|
4189
4586
|
const rcFile = getShellRcPath();
|
|
4190
4587
|
const sourceLine = '. "$env:USERPROFILE\\.pai\\shell-helper.ps1"';
|
|
4191
|
-
await
|
|
4192
|
-
if (await
|
|
4193
|
-
const content = await
|
|
4588
|
+
await fs14.ensureDir(join8(rcFile, ".."));
|
|
4589
|
+
if (await fs14.pathExists(rcFile)) {
|
|
4590
|
+
const content = await fs14.readFile(rcFile, "utf8");
|
|
4194
4591
|
if (content.includes("shell-helper.ps1")) {
|
|
4195
4592
|
return true;
|
|
4196
4593
|
}
|
|
4197
|
-
await
|
|
4594
|
+
await fs14.appendFile(rcFile, `
|
|
4198
4595
|
# PAI \u2014 \uC790\uB3D9 \uB514\uB809\uD1A0\uB9AC \uC774\uB3D9
|
|
4199
4596
|
${sourceLine}
|
|
4200
4597
|
`);
|
|
4201
4598
|
return false;
|
|
4202
4599
|
}
|
|
4203
|
-
await
|
|
4600
|
+
await fs14.writeFile(rcFile, `${sourceLine}
|
|
4204
4601
|
`);
|
|
4205
4602
|
return false;
|
|
4206
4603
|
}
|
|
@@ -4263,10 +4660,10 @@ __export(claude_settings_exports, {
|
|
|
4263
4660
|
mergeOmcIntoSettings: () => mergeOmcIntoSettings
|
|
4264
4661
|
});
|
|
4265
4662
|
import os2 from "os";
|
|
4266
|
-
import
|
|
4267
|
-
import
|
|
4663
|
+
import path7 from "path";
|
|
4664
|
+
import fs15 from "fs-extra";
|
|
4268
4665
|
function getClaudeSettingsPath(homeDir = os2.homedir()) {
|
|
4269
|
-
return
|
|
4666
|
+
return path7.join(homeDir, ".claude", "settings.json");
|
|
4270
4667
|
}
|
|
4271
4668
|
function parseJsonWithBom(raw) {
|
|
4272
4669
|
const stripped = raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw;
|
|
@@ -4281,13 +4678,13 @@ async function enableOmcPlugin(options = {}) {
|
|
|
4281
4678
|
const pluginId = options.pluginId ?? DEFAULT_PLUGIN_ID;
|
|
4282
4679
|
const wantBackup = options.backup ?? true;
|
|
4283
4680
|
const settingsPath = getClaudeSettingsPath();
|
|
4284
|
-
await
|
|
4285
|
-
if (!await
|
|
4681
|
+
await fs15.ensureDir(path7.dirname(settingsPath));
|
|
4682
|
+
if (!await fs15.pathExists(settingsPath)) {
|
|
4286
4683
|
const skeleton = buildSkeleton(marketplaceId, marketplaceUrl, pluginId);
|
|
4287
|
-
await
|
|
4684
|
+
await fs15.writeFile(settingsPath, JSON.stringify(skeleton, null, 2) + "\n", "utf8");
|
|
4288
4685
|
return { action: "created", settingsPath };
|
|
4289
4686
|
}
|
|
4290
|
-
const raw = await
|
|
4687
|
+
const raw = await fs15.readFile(settingsPath, "utf8");
|
|
4291
4688
|
let parsed;
|
|
4292
4689
|
try {
|
|
4293
4690
|
const value = parseJsonWithBom(raw);
|
|
@@ -4297,7 +4694,7 @@ async function enableOmcPlugin(options = {}) {
|
|
|
4297
4694
|
parsed = value;
|
|
4298
4695
|
} catch (err) {
|
|
4299
4696
|
const backupPath2 = `${settingsPath}.backup-${timestampSuffix()}`;
|
|
4300
|
-
await
|
|
4697
|
+
await fs15.copy(settingsPath, backupPath2);
|
|
4301
4698
|
throw new ClaudeSettingsError(
|
|
4302
4699
|
`settings.json \uD30C\uC2F1 \uC2E4\uD328: ${err.message}. \uBC31\uC5C5: ${backupPath2}`,
|
|
4303
4700
|
backupPath2
|
|
@@ -4309,10 +4706,10 @@ async function enableOmcPlugin(options = {}) {
|
|
|
4309
4706
|
let backupPath;
|
|
4310
4707
|
if (wantBackup) {
|
|
4311
4708
|
backupPath = `${settingsPath}.backup-${timestampSuffix()}`;
|
|
4312
|
-
await
|
|
4709
|
+
await fs15.copy(settingsPath, backupPath);
|
|
4313
4710
|
}
|
|
4314
4711
|
const merged = mergeOmcIntoSettings(parsed, marketplaceId, marketplaceUrl, pluginId);
|
|
4315
|
-
await
|
|
4712
|
+
await fs15.writeFile(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
4316
4713
|
return { action: "added", settingsPath, backupPath };
|
|
4317
4714
|
}
|
|
4318
4715
|
function buildSkeleton(marketplaceId, marketplaceUrl, pluginId) {
|
|
@@ -4454,7 +4851,7 @@ __export(evaluate_cmd_exports, {
|
|
|
4454
4851
|
evaluateCommand: () => evaluateCommand
|
|
4455
4852
|
});
|
|
4456
4853
|
import { join as join10, basename } from "path";
|
|
4457
|
-
import
|
|
4854
|
+
import fs16 from "fs-extra";
|
|
4458
4855
|
async function evaluateCommand(cwd, options) {
|
|
4459
4856
|
const useCache = options.cache !== false;
|
|
4460
4857
|
let llmOutput = useCache ? getCachedResult(cwd) : null;
|
|
@@ -4476,15 +4873,15 @@ async function evaluateCommand(cwd, options) {
|
|
|
4476
4873
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4477
4874
|
const reportDir = join10(cwd, "docs", "p-reports");
|
|
4478
4875
|
const reportPath = join10(reportDir, `${today}.md`);
|
|
4479
|
-
await
|
|
4876
|
+
await fs16.ensureDir(reportDir);
|
|
4480
4877
|
const detailedReport = buildDetailedReport(result, projectName);
|
|
4481
|
-
await
|
|
4878
|
+
await fs16.writeFile(reportPath, detailedReport, "utf8");
|
|
4482
4879
|
console.log("");
|
|
4483
4880
|
success(`\uB9AC\uD3EC\uD2B8 \uC800\uC7A5: docs/p-reports/${today}.md`);
|
|
4484
4881
|
console.log("");
|
|
4485
4882
|
console.log(detailedReport);
|
|
4486
4883
|
if (options.output) {
|
|
4487
|
-
await
|
|
4884
|
+
await fs16.writeFile(options.output, detailedReport, "utf8");
|
|
4488
4885
|
success(`\uCD94\uAC00 \uC800\uC7A5: ${options.output}`);
|
|
4489
4886
|
}
|
|
4490
4887
|
if (options.failUnder && result.totalScore < options.failUnder) {
|
|
@@ -4657,7 +5054,7 @@ __export(init_cmd_exports, {
|
|
|
4657
5054
|
initCommand: () => initCommand
|
|
4658
5055
|
});
|
|
4659
5056
|
import { join as join11, basename as basename2 } from "path";
|
|
4660
|
-
import
|
|
5057
|
+
import fs17 from "fs-extra";
|
|
4661
5058
|
async function initCommand(cwd, nameArg) {
|
|
4662
5059
|
printWelcomeBanner();
|
|
4663
5060
|
const { isWindows: isWindows2, diagnoseWindowsEnv: diagnoseWindowsEnv2 } = await Promise.resolve().then(() => (init_platform(), platform_exports));
|
|
@@ -4695,7 +5092,7 @@ async function initCommand(cwd, nameArg) {
|
|
|
4695
5092
|
const isLegacy = await detectLegacyProject(cwd);
|
|
4696
5093
|
if (isLegacy) {
|
|
4697
5094
|
const { default: inquirer2 } = await import("inquirer");
|
|
4698
|
-
const
|
|
5095
|
+
const chalk9 = (await import("chalk")).default;
|
|
4699
5096
|
console.log("");
|
|
4700
5097
|
info("\uAE30\uC874 \uD504\uB85C\uC81D\uD2B8\uAC00 \uAC10\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
4701
5098
|
console.log("");
|
|
@@ -4716,10 +5113,10 @@ async function initCommand(cwd, nameArg) {
|
|
|
4716
5113
|
printReport2(evalResult);
|
|
4717
5114
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4718
5115
|
const reportDir = join11(cwd, "docs", "p-reports");
|
|
4719
|
-
await
|
|
5116
|
+
await fs17.ensureDir(reportDir);
|
|
4720
5117
|
const legacyName = basename2(cwd);
|
|
4721
5118
|
const detailedReport = buildDetailedReport3(evalResult, legacyName);
|
|
4722
|
-
await
|
|
5119
|
+
await fs17.writeFile(join11(reportDir, `${today}.md`), detailedReport, "utf8");
|
|
4723
5120
|
console.log("");
|
|
4724
5121
|
success(`\uB9AC\uD3EC\uD2B8 \uC800\uC7A5: docs/p-reports/${today}.md`);
|
|
4725
5122
|
} catch {
|
|
@@ -4765,7 +5162,7 @@ async function initCommand(cwd, nameArg) {
|
|
|
4765
5162
|
projectName = answer.name.trim();
|
|
4766
5163
|
}
|
|
4767
5164
|
const projectDir = join11(cwd, projectName);
|
|
4768
|
-
if (await
|
|
5165
|
+
if (await fs17.pathExists(projectDir)) {
|
|
4769
5166
|
const existingConfig = await loadConfig(projectDir);
|
|
4770
5167
|
if (existingConfig) {
|
|
4771
5168
|
console.log("");
|
|
@@ -4778,7 +5175,7 @@ async function initCommand(cwd, nameArg) {
|
|
|
4778
5175
|
warn(`${projectName}/ \uD3F4\uB354\uAC00 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4.`);
|
|
4779
5176
|
hint("\uC774 \uD3F4\uB354\uC5D0 PAI\uB97C \uC124\uCE58\uD569\uB2C8\uB2E4.");
|
|
4780
5177
|
} else {
|
|
4781
|
-
await
|
|
5178
|
+
await fs17.ensureDir(projectDir);
|
|
4782
5179
|
success(`${projectName}/ \uD3F4\uB354 \uC0DD\uC131`);
|
|
4783
5180
|
}
|
|
4784
5181
|
await setupInDirectory(projectDir, projectName);
|
|
@@ -4816,7 +5213,7 @@ async function setupInDirectory(projectDir, projectName) {
|
|
|
4816
5213
|
}
|
|
4817
5214
|
async function showCompletion(projectName, projectDir, extraTools, isCurrentDir) {
|
|
4818
5215
|
const { default: inquirer } = await import("inquirer");
|
|
4819
|
-
const
|
|
5216
|
+
const chalk9 = (await import("chalk")).default;
|
|
4820
5217
|
const { sleep: sleep2 } = await Promise.resolve().then(() => (init_progress(), progress_exports));
|
|
4821
5218
|
step(3, 3, "\uC644\uB8CC");
|
|
4822
5219
|
console.log(colors.success(" \uD504\uB85C\uC81D\uD2B8\uAC00 \uC900\uBE44\uB418\uC5C8\uC2B5\uB2C8\uB2E4!"));
|
|
@@ -4830,26 +5227,26 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
|
|
|
4830
5227
|
console.log("");
|
|
4831
5228
|
console.log(colors.accent(" \uC124\uCE58\uB41C \uD50C\uB7EC\uADF8\uC778 & \uAE30\uB2A5"));
|
|
4832
5229
|
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(` ${
|
|
5230
|
+
console.log(` ${chalk9.cyan("OpenSpec+OMC")} \uC124\uACC4\uB3C4 & \uC9C0\uD615\uB3C4 \u2014 \uBB34\uC5C7\uC744, \uC5B4\uB514\uC5D0 \uB9CC\uB4E4\uC9C0 \uC815\uC758`);
|
|
5231
|
+
console.log(` ${chalk9.cyan("RoboCo CLI")} \uD604\uC7A5 \uC18C\uC7A5 \u2014 \uC804\uCCB4 \uACF5\uC815 \uAD00\uB9AC, AI\uC5D0\uAC8C \uC791\uC5C5 \uC9C0\uC2DC`);
|
|
5232
|
+
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
5233
|
if (extraTools.includes("omc")) {
|
|
4837
|
-
console.log(` ${
|
|
5234
|
+
console.log(` ${chalk9.cyan("OMC")} AI \uC5D0\uC774\uC804\uD2B8 \uC624\uCF00\uC2A4\uD2B8\uB808\uC774\uC158 (oh-my-claudecode)`);
|
|
4838
5235
|
}
|
|
4839
5236
|
if (extraTools.includes("gstack")) {
|
|
4840
|
-
console.log(` ${
|
|
5237
|
+
console.log(` ${chalk9.cyan("gstack")} \uD45C\uC900 \uC790\uC7AC & \uACF5\uBC95 \u2014 \uAE30\uC220 \uC2A4\uD0DD \uD45C\uC900 \uAD6C\uC870 \uC81C\uACF5`);
|
|
4841
5238
|
}
|
|
4842
5239
|
if (extraTools.includes("harness")) {
|
|
4843
|
-
console.log(` ${
|
|
5240
|
+
console.log(` ${chalk9.cyan("Harness")} \uC548\uC804\uC7A5\uCE58 & \uAC80\uC0AC\uB300 \u2014 AI \uCF54\uB4DC \uC791\uB3D9 \uD14C\uC2A4\uD2B8`);
|
|
4844
5241
|
}
|
|
4845
5242
|
if (extraTools.includes("vercel")) {
|
|
4846
|
-
console.log(` ${
|
|
5243
|
+
console.log(` ${chalk9.cyan("Vercel")} \uC790\uB3D9 \uBC30\uD3EC \u2014 Preview + Production`);
|
|
4847
5244
|
}
|
|
4848
5245
|
if (extraTools.includes("supabase")) {
|
|
4849
|
-
console.log(` ${
|
|
5246
|
+
console.log(` ${chalk9.cyan("Supabase")} DB + \uC778\uC99D + \uC2A4\uD1A0\uB9AC\uC9C0 (PostgreSQL \uAE30\uBC18)`);
|
|
4850
5247
|
}
|
|
4851
5248
|
if (extraTools.includes("mcp")) {
|
|
4852
|
-
console.log(` ${
|
|
5249
|
+
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
5250
|
}
|
|
4854
5251
|
console.log("");
|
|
4855
5252
|
console.log(colors.accent(" \uC124\uCE58 \uD655\uC778"));
|
|
@@ -4863,7 +5260,7 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
|
|
|
4863
5260
|
...extraTools.includes("omc") ? [{ label: "OMC \uB7F0\uD0C0\uC784", path: ".omc" }] : []
|
|
4864
5261
|
];
|
|
4865
5262
|
for (const check of checks) {
|
|
4866
|
-
const exists = await
|
|
5263
|
+
const exists = await fs17.pathExists(join11(projectDir, check.path));
|
|
4867
5264
|
console.log(` ${exists ? colors.success("\u2713") : colors.err("\u2717")} ${check.label.padEnd(16)} ${colors.dim(check.path)}`);
|
|
4868
5265
|
}
|
|
4869
5266
|
console.log("");
|
|
@@ -4889,9 +5286,9 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
|
|
|
4889
5286
|
await sleep2(500);
|
|
4890
5287
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4891
5288
|
const reportDir = join11(projectDir, "docs", "p-reports");
|
|
4892
|
-
await
|
|
5289
|
+
await fs17.ensureDir(reportDir);
|
|
4893
5290
|
const detailedReport = buildDetailedReport3(evalResult, projectName);
|
|
4894
|
-
await
|
|
5291
|
+
await fs17.writeFile(join11(reportDir, `${today}.md`), detailedReport, "utf8");
|
|
4895
5292
|
await sleep2(500);
|
|
4896
5293
|
console.log("");
|
|
4897
5294
|
success(`\uB9AC\uD3EC\uD2B8 \uC800\uC7A5: docs/p-reports/${today}.md`);
|
|
@@ -4909,7 +5306,7 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
|
|
|
4909
5306
|
}
|
|
4910
5307
|
console.log("");
|
|
4911
5308
|
if (!isCurrentDir) {
|
|
4912
|
-
console.log(` ${
|
|
5309
|
+
console.log(` ${chalk9.green("\u2192")} cd ${projectName} ${colors.dim("\uC774\uB3D9 \uC644\uB8CC")}`);
|
|
4913
5310
|
}
|
|
4914
5311
|
console.log("");
|
|
4915
5312
|
success("\uC774\uC81C Claude Code\uC640 \uD568\uAED8 PRD \uBB38\uC11C\uB97C \uC791\uC131\uD558\uC138\uC694.");
|
|
@@ -4942,7 +5339,7 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
|
|
|
4942
5339
|
const shellRc = getShellRcPath2();
|
|
4943
5340
|
let yoloAlreadyAliased = false;
|
|
4944
5341
|
try {
|
|
4945
|
-
const rcContent = await
|
|
5342
|
+
const rcContent = await fs17.readFile(shellRc, "utf8");
|
|
4946
5343
|
yoloAlreadyAliased = checkYolo(rcContent);
|
|
4947
5344
|
} catch {
|
|
4948
5345
|
}
|
|
@@ -4962,10 +5359,10 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
|
|
|
4962
5359
|
try {
|
|
4963
5360
|
const { getYoloAliasLine: getYoloAliasLine2 } = await Promise.resolve().then(() => (init_platform(), platform_exports));
|
|
4964
5361
|
const aliasLine = getYoloAliasLine2();
|
|
4965
|
-
const rcContent = await
|
|
5362
|
+
const rcContent = await fs17.readFile(shellRc, "utf8").catch(() => "");
|
|
4966
5363
|
if (!rcContent.includes("claude-yolo")) {
|
|
4967
|
-
await
|
|
4968
|
-
await
|
|
5364
|
+
await fs17.ensureDir(join11(shellRc, ".."));
|
|
5365
|
+
await fs17.appendFile(shellRc, `
|
|
4969
5366
|
# PAI \u2014 claude-YOLO mode
|
|
4970
5367
|
${aliasLine}
|
|
4971
5368
|
`);
|
|
@@ -5004,7 +5401,7 @@ ${aliasLine}
|
|
|
5004
5401
|
async function handleExistingProject(cwd, state) {
|
|
5005
5402
|
const { PLUGIN_META: PLUGIN_META2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
|
|
5006
5403
|
const { default: inquirer } = await import("inquirer");
|
|
5007
|
-
const
|
|
5404
|
+
const chalk9 = (await import("chalk")).default;
|
|
5008
5405
|
section("\uAE30\uC874 \uD504\uB85C\uC81D\uD2B8");
|
|
5009
5406
|
const allPlugins = Object.entries(PLUGIN_META2).map(([key, meta]) => ({
|
|
5010
5407
|
key,
|
|
@@ -5015,7 +5412,7 @@ async function handleExistingProject(cwd, state) {
|
|
|
5015
5412
|
if (p.installed) {
|
|
5016
5413
|
success(p.label);
|
|
5017
5414
|
} else {
|
|
5018
|
-
console.log(colors.dim(` \xB7 ${p.label} ${
|
|
5415
|
+
console.log(colors.dim(` \xB7 ${p.label} ${chalk9.gray("\uBBF8\uC124\uCE58")}`));
|
|
5019
5416
|
}
|
|
5020
5417
|
}
|
|
5021
5418
|
const currentConfig = await loadConfig(cwd);
|
|
@@ -5041,7 +5438,7 @@ async function handleExistingProject(cwd, state) {
|
|
|
5041
5438
|
name: `\u{1FA7A} \uD658\uACBD \uC810\uAC80 ${colors.dim("(pai check \u2014 Node/Git/Claude/OMC)")}`,
|
|
5042
5439
|
value: "doctor"
|
|
5043
5440
|
},
|
|
5044
|
-
{ name:
|
|
5441
|
+
{ name: chalk9.gray("\u{1F6AA} \uC885\uB8CC"), value: "exit" }
|
|
5045
5442
|
]
|
|
5046
5443
|
}]);
|
|
5047
5444
|
switch (action) {
|
|
@@ -5066,7 +5463,7 @@ async function handleExistingProject(cwd, state) {
|
|
|
5066
5463
|
}
|
|
5067
5464
|
async function installOrchestratorOnly(projectDir, projectName) {
|
|
5068
5465
|
const { default: inquirer } = await import("inquirer");
|
|
5069
|
-
const
|
|
5466
|
+
const chalk9 = (await import("chalk")).default;
|
|
5070
5467
|
const { generateFiles: generateFiles2 } = await Promise.resolve().then(() => (init_generator(), generator_exports));
|
|
5071
5468
|
const { analyzeProject: analyzeProject2 } = await Promise.resolve().then(() => (init_analyzer(), analyzer_exports));
|
|
5072
5469
|
const { provisionClaudeCommands: provisionClaudeCommands2 } = await Promise.resolve().then(() => (init_claude_commands(), claude_commands_exports));
|
|
@@ -5088,7 +5485,7 @@ async function installOrchestratorOnly(projectDir, projectName) {
|
|
|
5088
5485
|
{ name: `gstack ${colors.dim("\u2500 \uD45C\uC900 \uC790\uC7AC & \uACF5\uBC95 (\uAE30\uC220 \uC2A4\uD0DD)")}`, value: "gstack" },
|
|
5089
5486
|
{ name: `Harness ${colors.dim("\u2500 \uC548\uC804\uC7A5\uCE58 & \uAC80\uC0AC\uB300 (\uD14C\uC2A4\uD2B8)")}`, value: "harness" },
|
|
5090
5487
|
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:
|
|
5488
|
+
{ name: chalk9.green("\u21B5 \uC120\uD0DD \uC644\uB8CC"), value: "__done__" }
|
|
5092
5489
|
]
|
|
5093
5490
|
}]);
|
|
5094
5491
|
const selectedPlugins = plugins.filter((p) => p !== "__done__");
|
|
@@ -5159,8 +5556,8 @@ async function installOrchestratorOnly(projectDir, projectName) {
|
|
|
5159
5556
|
printReport2(evalResult);
|
|
5160
5557
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
5161
5558
|
const reportDir = join11(projectDir, "docs", "p-reports");
|
|
5162
|
-
await
|
|
5163
|
-
await
|
|
5559
|
+
await fs17.ensureDir(reportDir);
|
|
5560
|
+
await fs17.writeFile(join11(reportDir, `${today}.md`), buildDetailedReport3(evalResult, projectName), "utf8");
|
|
5164
5561
|
console.log("");
|
|
5165
5562
|
hint(`\uC0C1\uC138 \uB9AC\uD3EC\uD2B8: docs/p-reports/${today}.md`);
|
|
5166
5563
|
} catch {
|
|
@@ -5175,7 +5572,7 @@ async function installOrchestratorOnly(projectDir, projectName) {
|
|
|
5175
5572
|
message: "Claude Code\uB97C \uC2DC\uC791\uD560\uAE4C\uC694?",
|
|
5176
5573
|
choices: [
|
|
5177
5574
|
{ name: `Claude \uBC14\uB85C \uC2DC\uC791 ${colors.dim("\u2500 claude")}`, value: "claude" },
|
|
5178
|
-
{ name:
|
|
5575
|
+
{ name: chalk9.gray("\uB098\uC911\uC5D0 \uC9C1\uC811 \uC2E4\uD589"), value: "none" }
|
|
5179
5576
|
]
|
|
5180
5577
|
}]);
|
|
5181
5578
|
if (launch === "none") {
|
|
@@ -5210,7 +5607,7 @@ async function detectLegacyProject(cwd) {
|
|
|
5210
5607
|
".gitignore"
|
|
5211
5608
|
];
|
|
5212
5609
|
for (const signal of signals) {
|
|
5213
|
-
if (await
|
|
5610
|
+
if (await fs17.pathExists(join11(cwd, signal))) return true;
|
|
5214
5611
|
}
|
|
5215
5612
|
return false;
|
|
5216
5613
|
}
|
|
@@ -5302,16 +5699,16 @@ var init_help_cmd = __esm({
|
|
|
5302
5699
|
|
|
5303
5700
|
// src/stages/design/openspec.ts
|
|
5304
5701
|
import { join as join12 } from "path";
|
|
5305
|
-
import
|
|
5702
|
+
import fs18 from "fs-extra";
|
|
5306
5703
|
async function initOpenSpec(cwd, projectName) {
|
|
5307
5704
|
const docsDir = join12(cwd, "docs");
|
|
5308
|
-
await
|
|
5705
|
+
await fs18.ensureDir(docsDir);
|
|
5309
5706
|
const openspecPath = join12(docsDir, "openspec.md");
|
|
5310
|
-
if (await
|
|
5707
|
+
if (await fs18.pathExists(openspecPath)) {
|
|
5311
5708
|
info("docs/openspec.md \uC774\uBBF8 \uC874\uC7AC \u2014 \uAC74\uB108\uB700");
|
|
5312
5709
|
return;
|
|
5313
5710
|
}
|
|
5314
|
-
await
|
|
5711
|
+
await fs18.writeFile(openspecPath, [
|
|
5315
5712
|
`# OpenSpec \u2014 ${projectName}`,
|
|
5316
5713
|
"",
|
|
5317
5714
|
"## 1. \uBAA9\uC801 (Purpose)",
|
|
@@ -5345,7 +5742,7 @@ async function validateOpenSpec(cwd) {
|
|
|
5345
5742
|
];
|
|
5346
5743
|
let specPath = null;
|
|
5347
5744
|
for (const p of candidates) {
|
|
5348
|
-
if (await
|
|
5745
|
+
if (await fs18.pathExists(p)) {
|
|
5349
5746
|
specPath = p;
|
|
5350
5747
|
break;
|
|
5351
5748
|
}
|
|
@@ -5359,7 +5756,7 @@ async function validateOpenSpec(cwd) {
|
|
|
5359
5756
|
warnings: ["openspec.md \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `pai design init` \uC744 \uC2E4\uD589\uD558\uC138\uC694."]
|
|
5360
5757
|
};
|
|
5361
5758
|
}
|
|
5362
|
-
const content = await
|
|
5759
|
+
const content = await fs18.readFile(specPath, "utf8");
|
|
5363
5760
|
const missing = [];
|
|
5364
5761
|
let filled = 0;
|
|
5365
5762
|
for (const section2 of REQUIRED_SECTIONS) {
|
|
@@ -5408,16 +5805,16 @@ var init_openspec = __esm({
|
|
|
5408
5805
|
|
|
5409
5806
|
// src/stages/design/omc.ts
|
|
5410
5807
|
import { join as join13 } from "path";
|
|
5411
|
-
import
|
|
5808
|
+
import fs19 from "fs-extra";
|
|
5412
5809
|
async function initOMC(cwd, projectName) {
|
|
5413
5810
|
const paiDir = join13(cwd, ".pai");
|
|
5414
|
-
await
|
|
5811
|
+
await fs19.ensureDir(paiDir);
|
|
5415
5812
|
const omcPath = join13(paiDir, "omc.md");
|
|
5416
|
-
if (await
|
|
5813
|
+
if (await fs19.pathExists(omcPath)) {
|
|
5417
5814
|
info(".pai/omc.md \uC774\uBBF8 \uC874\uC7AC \u2014 \uAC74\uB108\uB700");
|
|
5418
5815
|
return;
|
|
5419
5816
|
}
|
|
5420
|
-
await
|
|
5817
|
+
await fs19.writeFile(omcPath, [
|
|
5421
5818
|
`# OMC \u2014 Object Model Context (${projectName})`,
|
|
5422
5819
|
"",
|
|
5423
5820
|
"> 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 +5897,14 @@ var init_design_cmd = __esm({
|
|
|
5500
5897
|
|
|
5501
5898
|
// src/stages/validation/runner.ts
|
|
5502
5899
|
import { join as join14 } from "path";
|
|
5503
|
-
import
|
|
5900
|
+
import fs20 from "fs-extra";
|
|
5504
5901
|
async function runTests(cwd) {
|
|
5505
5902
|
const start = Date.now();
|
|
5506
5903
|
const gstackPath = join14(cwd, ".pai", "gstack.json");
|
|
5507
5904
|
let runner = "npm test";
|
|
5508
|
-
if (await
|
|
5905
|
+
if (await fs20.pathExists(gstackPath)) {
|
|
5509
5906
|
try {
|
|
5510
|
-
const config = await
|
|
5907
|
+
const config = await fs20.readJson(gstackPath);
|
|
5511
5908
|
if (config.testRunner === "vitest") runner = "npx vitest run";
|
|
5512
5909
|
else if (config.testRunner === "jest") runner = "npx jest";
|
|
5513
5910
|
else if (config.testRunner === "mocha") runner = "npx mocha";
|
|
@@ -5515,9 +5912,9 @@ async function runTests(cwd) {
|
|
|
5515
5912
|
}
|
|
5516
5913
|
}
|
|
5517
5914
|
const pkgPath = join14(cwd, "package.json");
|
|
5518
|
-
if (await
|
|
5915
|
+
if (await fs20.pathExists(pkgPath)) {
|
|
5519
5916
|
try {
|
|
5520
|
-
const pkg5 = await
|
|
5917
|
+
const pkg5 = await fs20.readJson(pkgPath);
|
|
5521
5918
|
if (!pkg5.scripts?.test || pkg5.scripts.test.includes("no test specified")) {
|
|
5522
5919
|
return {
|
|
5523
5920
|
runner,
|
|
@@ -5560,15 +5957,15 @@ var init_runner = __esm({
|
|
|
5560
5957
|
|
|
5561
5958
|
// src/stages/validation/harness.ts
|
|
5562
5959
|
import { join as join15 } from "path";
|
|
5563
|
-
import
|
|
5960
|
+
import fs21 from "fs-extra";
|
|
5564
5961
|
async function runHarnessCheck(cwd) {
|
|
5565
5962
|
const harnessPath = join15(cwd, ".pai", "harness.json");
|
|
5566
|
-
if (!await
|
|
5963
|
+
if (!await fs21.pathExists(harnessPath)) {
|
|
5567
5964
|
return { enabled: false, specFile: null, rules: [], checks: [] };
|
|
5568
5965
|
}
|
|
5569
5966
|
let config;
|
|
5570
5967
|
try {
|
|
5571
|
-
config = await
|
|
5968
|
+
config = await fs21.readJson(harnessPath);
|
|
5572
5969
|
} catch {
|
|
5573
5970
|
return { enabled: false, specFile: null, rules: [], checks: [] };
|
|
5574
5971
|
}
|
|
@@ -5576,8 +5973,8 @@ async function runHarnessCheck(cwd) {
|
|
|
5576
5973
|
const rules = config.rules ?? [];
|
|
5577
5974
|
const checks = [];
|
|
5578
5975
|
if (rules.includes("spec-implementation-match")) {
|
|
5579
|
-
const specExists = await
|
|
5580
|
-
const srcExists = await
|
|
5976
|
+
const specExists = await fs21.pathExists(join15(cwd, specFile));
|
|
5977
|
+
const srcExists = await fs21.pathExists(join15(cwd, "src"));
|
|
5581
5978
|
checks.push({
|
|
5582
5979
|
rule: "spec-implementation-match",
|
|
5583
5980
|
passed: specExists && srcExists,
|
|
@@ -5585,8 +5982,8 @@ async function runHarnessCheck(cwd) {
|
|
|
5585
5982
|
});
|
|
5586
5983
|
}
|
|
5587
5984
|
if (rules.includes("api-contract-test")) {
|
|
5588
|
-
const testDir = await
|
|
5589
|
-
const testDir2 = await
|
|
5985
|
+
const testDir = await fs21.pathExists(join15(cwd, "tests"));
|
|
5986
|
+
const testDir2 = await fs21.pathExists(join15(cwd, "test"));
|
|
5590
5987
|
checks.push({
|
|
5591
5988
|
rule: "api-contract-test",
|
|
5592
5989
|
passed: testDir || testDir2,
|
|
@@ -5701,13 +6098,13 @@ var init_context = __esm({
|
|
|
5701
6098
|
|
|
5702
6099
|
// src/stages/design/index.ts
|
|
5703
6100
|
import { join as join16 } from "path";
|
|
5704
|
-
import
|
|
6101
|
+
import fs22 from "fs-extra";
|
|
5705
6102
|
async function autoInstallHarness(cwd) {
|
|
5706
6103
|
const harnessPath = join16(cwd, ".pai", "harness.json");
|
|
5707
|
-
if (await
|
|
6104
|
+
if (await fs22.pathExists(harnessPath)) return;
|
|
5708
6105
|
await withSpinner("Harness Engineering \uC790\uB3D9 \uC124\uC815 \uC911...", async () => {
|
|
5709
|
-
await
|
|
5710
|
-
await
|
|
6106
|
+
await fs22.ensureDir(join16(cwd, ".pai"));
|
|
6107
|
+
await fs22.writeJson(harnessPath, {
|
|
5711
6108
|
version: "1.0",
|
|
5712
6109
|
specFile: "docs/openspec.md",
|
|
5713
6110
|
checkOn: ["pre-commit", "ci"],
|
|
@@ -6064,7 +6461,7 @@ __export(remove_cmd_exports, {
|
|
|
6064
6461
|
removeCommand: () => removeCommand
|
|
6065
6462
|
});
|
|
6066
6463
|
import { basename as basename4, dirname } from "path";
|
|
6067
|
-
import
|
|
6464
|
+
import fs23 from "fs-extra";
|
|
6068
6465
|
async function removeCommand(cwd, options) {
|
|
6069
6466
|
section("\uD504\uB85C\uC81D\uD2B8 \uC0AD\uC81C");
|
|
6070
6467
|
const config = await loadConfig(cwd);
|
|
@@ -6082,7 +6479,7 @@ async function removeCommand(cwd, options) {
|
|
|
6082
6479
|
console.log(colors.err(` ${folderName}/ \uD3F4\uB354 \uC804\uCCB4\uAC00 \uC0AD\uC81C\uB429\uB2C8\uB2E4.`));
|
|
6083
6480
|
hint("\uC774 \uC791\uC5C5\uC740 \uB418\uB3CC\uB9B4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
6084
6481
|
console.log("");
|
|
6085
|
-
const items = await
|
|
6482
|
+
const items = await fs23.readdir(cwd);
|
|
6086
6483
|
const fileCount = items.filter((i) => !i.startsWith(".")).length;
|
|
6087
6484
|
const hiddenCount = items.filter((i) => i.startsWith(".")).length;
|
|
6088
6485
|
info(`\uD30C\uC77C/\uD3F4\uB354 ${fileCount}\uAC1C, \uC228\uAE40 \uD56D\uBAA9 ${hiddenCount}\uAC1C`);
|
|
@@ -6101,7 +6498,7 @@ async function removeCommand(cwd, options) {
|
|
|
6101
6498
|
}
|
|
6102
6499
|
process.chdir(parentDir);
|
|
6103
6500
|
try {
|
|
6104
|
-
await
|
|
6501
|
+
await fs23.remove(cwd);
|
|
6105
6502
|
console.log("");
|
|
6106
6503
|
success(`${folderName}/ \uD504\uB85C\uC81D\uD2B8\uAC00 \uC0AD\uC81C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.`);
|
|
6107
6504
|
try {
|
|
@@ -6222,7 +6619,7 @@ __export(upgrade_cmd_exports, {
|
|
|
6222
6619
|
});
|
|
6223
6620
|
import { createRequire as createRequire3 } from "module";
|
|
6224
6621
|
import { fileURLToPath } from "url";
|
|
6225
|
-
import
|
|
6622
|
+
import chalk6 from "chalk";
|
|
6226
6623
|
async function upgradeCommand(cwd, options) {
|
|
6227
6624
|
section("PAI \uC5C5\uADF8\uB808\uC774\uB4DC");
|
|
6228
6625
|
const currentVersion = pkg3.version;
|
|
@@ -6245,8 +6642,8 @@ async function upgradeCommand(cwd, options) {
|
|
|
6245
6642
|
}
|
|
6246
6643
|
const configVersion = config.version;
|
|
6247
6644
|
console.log("");
|
|
6248
|
-
console.log(` \uD504\uB85C\uC81D\uD2B8 config: ${
|
|
6249
|
-
console.log(` \uD604\uC7AC CLI: ${
|
|
6645
|
+
console.log(` \uD504\uB85C\uC81D\uD2B8 config: ${chalk6.gray(`v${configVersion}`)}`);
|
|
6646
|
+
console.log(` \uD604\uC7AC CLI: ${chalk6.cyan(`v${currentVersion}`)}`);
|
|
6250
6647
|
if (!options.force && compareSemver(configVersion, currentVersion) >= 0) {
|
|
6251
6648
|
console.log("");
|
|
6252
6649
|
success("\uD504\uB85C\uC81D\uD2B8 \uD30C\uC77C\uC774 \uC774\uBBF8 \uCD5C\uC2E0 \uBC84\uC804\uC785\uB2C8\uB2E4.");
|
|
@@ -6264,7 +6661,7 @@ async function upgradeCommand(cwd, options) {
|
|
|
6264
6661
|
if (result.commandsWritten.length > 0) {
|
|
6265
6662
|
success(`\uC2AC\uB798\uC2DC \uCEE4\uB9E8\uB4DC ${result.commandsWritten.length}\uAC1C \uC5C5\uB370\uC774\uD2B8`);
|
|
6266
6663
|
for (const f of result.commandsWritten) {
|
|
6267
|
-
console.log(
|
|
6664
|
+
console.log(chalk6.gray(` ${f}`));
|
|
6268
6665
|
}
|
|
6269
6666
|
}
|
|
6270
6667
|
if (result.commandErrors.length > 0) {
|
|
@@ -6284,7 +6681,7 @@ async function upgradeCli(currentVersion, pkgName, force) {
|
|
|
6284
6681
|
const scriptPath = fileURLToPath(import.meta.url);
|
|
6285
6682
|
const mode = detectInstallMode(scriptPath);
|
|
6286
6683
|
console.log("");
|
|
6287
|
-
console.log(` \uD604\uC7AC CLI: ${
|
|
6684
|
+
console.log(` \uD604\uC7AC CLI: ${chalk6.cyan(`v${currentVersion}`)} ${chalk6.gray(`(${mode})`)}`);
|
|
6288
6685
|
if (mode === "npx") {
|
|
6289
6686
|
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
6687
|
hint("\uB2E4\uC74C npx \uC2E4\uD589 \uC2DC \uC790\uB3D9\uC73C\uB85C \uCD5C\uC2E0 \uBC84\uC804\uC744 \uC0AC\uC6A9\uD569\uB2C8\uB2E4.");
|
|
@@ -6299,7 +6696,7 @@ async function upgradeCli(currentVersion, pkgName, force) {
|
|
|
6299
6696
|
hint(`\uC218\uB3D9 \uC5C5\uADF8\uB808\uC774\uB4DC: npm install -g ${pkgName}@latest`);
|
|
6300
6697
|
return;
|
|
6301
6698
|
}
|
|
6302
|
-
console.log(` \uCD5C\uC2E0: ${
|
|
6699
|
+
console.log(` \uCD5C\uC2E0: ${chalk6.cyan(`v${latestVersion}`)}`);
|
|
6303
6700
|
const cmp = compareSemver(currentVersion, latestVersion);
|
|
6304
6701
|
if (!force && cmp >= 0) {
|
|
6305
6702
|
console.log("");
|
|
@@ -6380,8 +6777,8 @@ __export(savetoken_cmd_exports, {
|
|
|
6380
6777
|
savetokenCommand: () => savetokenCommand
|
|
6381
6778
|
});
|
|
6382
6779
|
import { join as join17, relative } from "path";
|
|
6383
|
-
import
|
|
6384
|
-
import
|
|
6780
|
+
import fs24 from "fs-extra";
|
|
6781
|
+
import chalk7 from "chalk";
|
|
6385
6782
|
async function savetokenCommand(cwd) {
|
|
6386
6783
|
const { createSpinner: createSpinner2 } = await Promise.resolve().then(() => (init_progress(), progress_exports));
|
|
6387
6784
|
console.log("");
|
|
@@ -6405,14 +6802,14 @@ async function savetokenCommand(cwd) {
|
|
|
6405
6802
|
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
6803
|
for (const sdk of sdks) {
|
|
6407
6804
|
const count = callSites.filter((s) => s.sdk === sdk).length;
|
|
6408
|
-
console.log(` ${
|
|
6805
|
+
console.log(` ${chalk7.cyan(sdk.padEnd(20))} ${chalk7.white(String(count))}\uAC74`);
|
|
6409
6806
|
}
|
|
6410
6807
|
if (imports.length > 0) {
|
|
6411
6808
|
console.log("");
|
|
6412
6809
|
console.log(colors.dim(" SDK \uC784\uD3EC\uD2B8"));
|
|
6413
6810
|
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
6811
|
for (const site of imports) {
|
|
6415
|
-
console.log(` ${colors.dim(site.file)}:${
|
|
6812
|
+
console.log(` ${colors.dim(site.file)}:${chalk7.yellow(String(site.line))}`);
|
|
6416
6813
|
console.log(` ${site.content.trim().substring(0, 80)}`);
|
|
6417
6814
|
}
|
|
6418
6815
|
}
|
|
@@ -6421,7 +6818,7 @@ async function savetokenCommand(cwd) {
|
|
|
6421
6818
|
console.log(colors.dim(" AI API \uD638\uCD9C \uC9C0\uC810"));
|
|
6422
6819
|
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
6820
|
for (const site of apiCalls) {
|
|
6424
|
-
console.log(` ${colors.dim(site.file)}:${
|
|
6821
|
+
console.log(` ${colors.dim(site.file)}:${chalk7.yellow(String(site.line))} ${chalk7.cyan(site.sdk)}`);
|
|
6425
6822
|
console.log(` ${site.content.trim().substring(0, 80)}`);
|
|
6426
6823
|
}
|
|
6427
6824
|
}
|
|
@@ -6430,7 +6827,7 @@ async function savetokenCommand(cwd) {
|
|
|
6430
6827
|
console.log(colors.dim(" Fetch \uAE30\uBC18 AI \uD638\uCD9C"));
|
|
6431
6828
|
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
6829
|
for (const site of fetchCalls) {
|
|
6433
|
-
console.log(` ${colors.dim(site.file)}:${
|
|
6830
|
+
console.log(` ${colors.dim(site.file)}:${chalk7.yellow(String(site.line))} ${chalk7.cyan(site.sdk)}`);
|
|
6434
6831
|
console.log(` ${site.content.trim().substring(0, 80)}`);
|
|
6435
6832
|
}
|
|
6436
6833
|
}
|
|
@@ -6438,22 +6835,22 @@ async function savetokenCommand(cwd) {
|
|
|
6438
6835
|
console.log("");
|
|
6439
6836
|
console.log(colors.accent(" \uC808\uAC10 \uAC00\uB2A5\uC131 \uCD94\uC815"));
|
|
6440
6837
|
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 ${
|
|
6838
|
+
console.log(` \uCD1D API \uD638\uCD9C \uC9C0\uC810 ${chalk7.white(String(totalApiPoints))}\uAC74`);
|
|
6839
|
+
console.log(` \uBD84\uC11D \uD544\uC694 ${chalk7.yellow("Claude Code\uC5D0\uC11C \uC2EC\uCE35 \uBD84\uC11D \uD544\uC694")}`);
|
|
6443
6840
|
console.log("");
|
|
6444
6841
|
console.log(colors.dim(" \uC601\uD5A5\uB3C4\uBCC4 \uB300\uCCB4 \uC804\uB7B5"));
|
|
6445
6842
|
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(` ${
|
|
6843
|
+
console.log(` ${chalk7.green("\u25CF")} \uB0AE\uC74C \uD14D\uC2A4\uD2B8 \uD3EC\uB9F7\uD305, \uC720\uD6A8\uC131 \uAC80\uC99D, \uD15C\uD50C\uB9BF \uC0DD\uC131`);
|
|
6447
6844
|
console.log(` \u2192 ${colors.dim("regex, JSON schema, \uD15C\uD50C\uB9BF \uC5D4\uC9C4\uC73C\uB85C \uC989\uC2DC \uB300\uCCB4")}`);
|
|
6448
|
-
console.log(` ${
|
|
6845
|
+
console.log(` ${chalk7.yellow("\u25CF")} \uC911\uAC04 \uB370\uC774\uD130 \uBD84\uB958, \uC694\uC57D, \uAC04\uB2E8\uD55C \uCD94\uCD9C`);
|
|
6449
6846
|
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(` ${
|
|
6847
|
+
console.log(` ${chalk7.red("\u25CF")} \uB192\uC74C \uCF54\uB4DC \uC0DD\uC131, \uBCF5\uC7A1\uD55C \uCD94\uB860, \uCC3D\uC758\uC801 \uC0DD\uC131`);
|
|
6451
6848
|
console.log(` \u2192 ${colors.dim("AI \uD544\uC218 \u2014 \uD504\uB86C\uD504\uD2B8 \uCD5C\uC801\uD654\uB85C \uD1A0\uD070 \uC808\uAC10")}`);
|
|
6452
6849
|
const reportDir = join17(cwd, ".pai");
|
|
6453
|
-
await
|
|
6850
|
+
await fs24.ensureDir(reportDir);
|
|
6454
6851
|
const report = buildReport(callSites, cwd);
|
|
6455
6852
|
const reportPath = join17(reportDir, "savetoken-report.md");
|
|
6456
|
-
await
|
|
6853
|
+
await fs24.writeFile(reportPath, report, "utf8");
|
|
6457
6854
|
console.log("");
|
|
6458
6855
|
success("\uC2A4\uCE94 \uB9AC\uD3EC\uD2B8 \uC800\uC7A5: .pai/savetoken-report.md");
|
|
6459
6856
|
console.log("");
|
|
@@ -6611,8 +7008,8 @@ __export(wakeup_cmd_exports, {
|
|
|
6611
7008
|
});
|
|
6612
7009
|
import { join as join18 } from "path";
|
|
6613
7010
|
import { homedir as homedir3, platform as osPlatform } from "os";
|
|
6614
|
-
import
|
|
6615
|
-
import
|
|
7011
|
+
import fs25 from "fs-extra";
|
|
7012
|
+
import chalk8 from "chalk";
|
|
6616
7013
|
async function wakeupCommand(timeOrAction, schedule = "\uD3C9\uC77C") {
|
|
6617
7014
|
if (timeOrAction === "off") {
|
|
6618
7015
|
await disableWakeup();
|
|
@@ -6648,8 +7045,8 @@ async function wakeupCommand(timeOrAction, schedule = "\uD3C9\uC77C") {
|
|
|
6648
7045
|
name: "launchMode",
|
|
6649
7046
|
message: "\uC2E4\uD589 \uBAA8\uB4DC:",
|
|
6650
7047
|
choices: [
|
|
6651
|
-
{ name: `claude ${
|
|
6652
|
-
{ name: `claude --dangerously-skip-permissions ${
|
|
7048
|
+
{ name: `claude ${chalk8.gray("\uC77C\uBC18 \uBAA8\uB4DC")}`, value: "normal" },
|
|
7049
|
+
{ name: `claude --dangerously-skip-permissions ${chalk8.gray("claude-YOLO mode")}`, value: "yolo" }
|
|
6653
7050
|
]
|
|
6654
7051
|
}]);
|
|
6655
7052
|
const config = {
|
|
@@ -6658,9 +7055,9 @@ async function wakeupCommand(timeOrAction, schedule = "\uD3C9\uC77C") {
|
|
|
6658
7055
|
projectDir,
|
|
6659
7056
|
launchMode
|
|
6660
7057
|
};
|
|
6661
|
-
await
|
|
6662
|
-
await
|
|
6663
|
-
await
|
|
7058
|
+
await fs25.ensureDir(PAI_DIR2);
|
|
7059
|
+
await fs25.writeJson(CONFIG_FILE2, config, { spaces: 2 });
|
|
7060
|
+
await fs25.writeJson(MESSAGES_FILE, MESSAGES);
|
|
6664
7061
|
await createWakeupScript(config);
|
|
6665
7062
|
if (osPlatform() === "darwin") {
|
|
6666
7063
|
await setupMacOS(config);
|
|
@@ -6673,12 +7070,12 @@ async function wakeupCommand(timeOrAction, schedule = "\uD3C9\uC77C") {
|
|
|
6673
7070
|
success("\u2600\uFE0F \uC6E8\uC774\uD06C\uC5C5 \uC124\uC815 \uC644\uB8CC");
|
|
6674
7071
|
console.log("");
|
|
6675
7072
|
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 ${
|
|
7073
|
+
console.log(` \uC2DC\uAC04 ${chalk8.white(config.time)}`);
|
|
7074
|
+
console.log(` \uC2A4\uCF00\uC904 ${chalk8.white(scheduleToLabel(config.schedule))}`);
|
|
7075
|
+
console.log(` \uD504\uB85C\uC81D\uD2B8 ${chalk8.white(config.projectDir)}`);
|
|
7076
|
+
console.log(` \uBAA8\uB4DC ${chalk8.white(config.launchMode === "yolo" ? "claude-YOLO mode" : "\uC77C\uBC18 \uBAA8\uB4DC")}`);
|
|
6680
7077
|
if (osPlatform() === "darwin") {
|
|
6681
|
-
console.log(` \uC2A4\uCF00\uC904\uB7EC ${
|
|
7078
|
+
console.log(` \uC2A4\uCF00\uC904\uB7EC ${chalk8.white("launchd + pmset (wake-from-sleep)")}`);
|
|
6682
7079
|
}
|
|
6683
7080
|
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
7081
|
console.log("");
|
|
@@ -6691,7 +7088,7 @@ async function setupMacOS(config) {
|
|
|
6691
7088
|
const { execa } = await import("execa");
|
|
6692
7089
|
const [hour, minute] = config.time.split(":").map(Number);
|
|
6693
7090
|
const plistDir = join18(homedir3(), "Library", "LaunchAgents");
|
|
6694
|
-
await
|
|
7091
|
+
await fs25.ensureDir(plistDir);
|
|
6695
7092
|
const weekdays = scheduleToWeekdays(config.schedule);
|
|
6696
7093
|
let calendarEntries;
|
|
6697
7094
|
if (weekdays.length === 7) {
|
|
@@ -6727,7 +7124,7 @@ ${calendarEntries}
|
|
|
6727
7124
|
<string>${PAI_DIR2}/wakeup.log</string>
|
|
6728
7125
|
</dict>
|
|
6729
7126
|
</plist>`;
|
|
6730
|
-
await
|
|
7127
|
+
await fs25.writeFile(PLIST_PATH, plist);
|
|
6731
7128
|
await execa("launchctl", ["unload", PLIST_PATH]).catch(() => {
|
|
6732
7129
|
});
|
|
6733
7130
|
await execa("launchctl", ["load", PLIST_PATH]);
|
|
@@ -6750,7 +7147,7 @@ async function setupWindows(config) {
|
|
|
6750
7147
|
const { execa } = await import("execa");
|
|
6751
7148
|
const [hour, minute] = config.time.split(":").map(Number);
|
|
6752
7149
|
const psScriptDir = join18(homedir3(), ".pai");
|
|
6753
|
-
await
|
|
7150
|
+
await fs25.ensureDir(psScriptDir);
|
|
6754
7151
|
const psScriptPath = join18(psScriptDir, "wakeup.ps1");
|
|
6755
7152
|
const claudeCmd = config.launchMode === "yolo" ? "claude --dangerously-skip-permissions" : "claude";
|
|
6756
7153
|
const psScript = `# PAI Wakeup \u2014 Claude Code \uC138\uC158 \uC790\uB3D9 \uC2DC\uC791
|
|
@@ -6780,7 +7177,7 @@ $notifier.Show([Windows.UI.Notifications.ToastNotification]::new($xml))
|
|
|
6780
7177
|
# Open PowerShell with Claude Code
|
|
6781
7178
|
Start-Process powershell -ArgumentList "-NoExit", "-Command", "Get-Content '$todayFile'; Write-Host ''; Set-Location '${config.projectDir}'; ${claudeCmd}"
|
|
6782
7179
|
`;
|
|
6783
|
-
await
|
|
7180
|
+
await fs25.writeFile(psScriptPath, psScript, "utf8");
|
|
6784
7181
|
const daysMap = {
|
|
6785
7182
|
"\uD3C9\uC77C": "MON,TUE,WED,THU,FRI",
|
|
6786
7183
|
"\uB9E4\uC77C": "MON,TUE,WED,THU,FRI,SAT,SUN",
|
|
@@ -6828,7 +7225,7 @@ async function disableWakeup() {
|
|
|
6828
7225
|
if (osPlatform() === "darwin") {
|
|
6829
7226
|
await execa("launchctl", ["unload", PLIST_PATH]).catch(() => {
|
|
6830
7227
|
});
|
|
6831
|
-
await
|
|
7228
|
+
await fs25.remove(PLIST_PATH).catch(() => {
|
|
6832
7229
|
});
|
|
6833
7230
|
success("launchd \uC2A4\uCF00\uC904 \uC81C\uAC70");
|
|
6834
7231
|
console.log("");
|
|
@@ -6849,23 +7246,23 @@ async function disableWakeup() {
|
|
|
6849
7246
|
} else {
|
|
6850
7247
|
await removeCronEntry();
|
|
6851
7248
|
}
|
|
6852
|
-
await
|
|
7249
|
+
await fs25.remove(CONFIG_FILE2).catch(() => {
|
|
6853
7250
|
});
|
|
6854
7251
|
console.log("");
|
|
6855
7252
|
success("\u2600\uFE0F \uC6E8\uC774\uD06C\uC5C5 \uD574\uC81C \uC644\uB8CC");
|
|
6856
7253
|
}
|
|
6857
7254
|
async function showStatus() {
|
|
6858
|
-
if (await
|
|
6859
|
-
const config = await
|
|
7255
|
+
if (await fs25.pathExists(CONFIG_FILE2)) {
|
|
7256
|
+
const config = await fs25.readJson(CONFIG_FILE2);
|
|
6860
7257
|
console.log("");
|
|
6861
7258
|
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 ${
|
|
7259
|
+
console.log(` \uC2DC\uAC04 ${chalk8.white(config.time)}`);
|
|
7260
|
+
console.log(` \uC2A4\uCF00\uC904 ${chalk8.white(scheduleToLabel(config.schedule))}`);
|
|
7261
|
+
console.log(` \uD504\uB85C\uC81D\uD2B8 ${chalk8.white(config.projectDir)}`);
|
|
7262
|
+
console.log(` \uBAA8\uB4DC ${chalk8.white(config.launchMode === "yolo" ? "claude-YOLO mode" : "\uC77C\uBC18 \uBAA8\uB4DC")}`);
|
|
6866
7263
|
if (osPlatform() === "darwin") {
|
|
6867
|
-
const plistExists = await
|
|
6868
|
-
console.log(` launchd ${plistExists ?
|
|
7264
|
+
const plistExists = await fs25.pathExists(PLIST_PATH);
|
|
7265
|
+
console.log(` launchd ${plistExists ? chalk8.green("\uD65C\uC131") : chalk8.red("\uBE44\uD65C\uC131")}`);
|
|
6869
7266
|
}
|
|
6870
7267
|
console.log("");
|
|
6871
7268
|
} else {
|
|
@@ -7017,7 +7414,7 @@ fi
|
|
|
7017
7414
|
|
|
7018
7415
|
echo "[$(date)] PAI Wakeup completed" >> "$LOG_FILE"
|
|
7019
7416
|
`;
|
|
7020
|
-
await
|
|
7417
|
+
await fs25.writeFile(SCRIPT_FILE, script, { mode: 493 });
|
|
7021
7418
|
}
|
|
7022
7419
|
var PAI_DIR2, CONFIG_FILE2, MESSAGES_FILE, SCRIPT_FILE, PLIST_NAME, PLIST_PATH, CRON_MARKER, MESSAGES;
|
|
7023
7420
|
var init_wakeup_cmd = __esm({
|
|
@@ -7158,6 +7555,10 @@ function createProgram() {
|
|
|
7158
7555
|
const { removeCommand: removeCommand2 } = await Promise.resolve().then(() => (init_remove_cmd(), remove_cmd_exports));
|
|
7159
7556
|
await removeCommand2(process.cwd(), options);
|
|
7160
7557
|
});
|
|
7558
|
+
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) => {
|
|
7559
|
+
const { fetchCommand: fetchCommand2 } = await Promise.resolve().then(() => (init_fetch_cmd(), fetch_cmd_exports));
|
|
7560
|
+
await fetchCommand2(process.cwd(), recipe, options);
|
|
7561
|
+
});
|
|
7161
7562
|
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
7563
|
const { upgradeCommand: upgradeCommand2 } = await Promise.resolve().then(() => (init_upgrade_cmd(), upgrade_cmd_exports));
|
|
7163
7564
|
await upgradeCommand2(process.cwd(), options);
|