pai-zero 0.9.2 → 0.10.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/cli/index.js CHANGED
@@ -500,6 +500,37 @@ async function interactiveInterview(analysis, _cwd, projectName) {
500
500
  if (database === "supabase") extraTools.push("supabase");
501
501
  extraTools.push("gstack", "harness");
502
502
  }
503
+ let mcp;
504
+ console.log("");
505
+ const { mcpDev } = await inquirer.prompt([{
506
+ type: "confirm",
507
+ name: "mcpDev",
508
+ message: "MCP(Model Context Protocol) \uC11C\uBC84\uB97C \uAC1C\uBC1C\uD558\uC2DC\uB098\uC694?",
509
+ default: false
510
+ }]);
511
+ if (mcpDev) {
512
+ console.log("");
513
+ const { mcpType } = await inquirer.prompt([{
514
+ type: "list",
515
+ name: "mcpType",
516
+ message: "MCP \uC11C\uBC84 \uC720\uD615:",
517
+ choices: [
518
+ { name: `Tools \uC11C\uBC84 ${colors.dim("\u2500 AI\uAC00 \uD638\uCD9C\uD560 \uAE30\uB2A5 \uC81C\uACF5")}`, value: "tools" },
519
+ { name: `Resources \uC11C\uBC84 ${colors.dim("\u2500 AI\uAC00 \uC77D\uC744 \uCEE8\uD14D\uC2A4\uD2B8 \uC81C\uACF5")}`, value: "resources" },
520
+ { name: `Prompts \uC11C\uBC84 ${colors.dim("\u2500 \uC7AC\uC0AC\uC6A9 \uD504\uB86C\uD504\uD2B8 \uD15C\uD50C\uB9BF")}`, value: "prompts" },
521
+ { name: `All-in-one ${colors.dim("\u2500 \uBAA8\uB450 \uD3EC\uD568")}`, value: "all" }
522
+ ]
523
+ }]);
524
+ const { mcpName } = await inquirer.prompt([{
525
+ type: "input",
526
+ name: "mcpName",
527
+ message: "MCP \uC11C\uBC84 \uC774\uB984:",
528
+ default: `${projectName}-mcp`,
529
+ validate: (v) => /^[a-z0-9][a-z0-9-]*$/.test(v.trim()) ? true : "\uC601\uBB38 \uC18C\uBB38\uC790, \uC22B\uC790, \uD558\uC774\uD508(-)\uB9CC \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4."
530
+ }]);
531
+ mcp = { enabled: true, type: mcpType, name: mcpName.trim() };
532
+ extraTools.push("mcp");
533
+ }
503
534
  step(3, 3, "\uC124\uCE58 \uD655\uC778");
504
535
  console.log("");
505
536
  console.log(colors.dim(" \uC124\uCE58\uB420 \uD56D\uBAA9"));
@@ -515,6 +546,9 @@ async function interactiveInterview(analysis, _cwd, projectName) {
515
546
  if (extraTools.includes("supabase")) console.log(` ${colors.accent("+")} Supabase \uB370\uC774\uD130\uBCA0\uC774\uC2A4`);
516
547
  if (extraTools.includes("gstack")) console.log(` ${colors.accent("+")} gstack \uD14C\uC2A4\uD2B8 \uC790\uB3D9\uD654`);
517
548
  if (extraTools.includes("harness")) console.log(` ${colors.accent("+")} Harness \uD488\uC9C8 \uAC80\uC99D`);
549
+ if (extraTools.includes("mcp") && mcp) {
550
+ console.log(` ${colors.accent("+")} MCP \uC11C\uBC84 (${mcp.name}, ${mcp.type})`);
551
+ }
518
552
  }
519
553
  if (authMethods.length > 0) {
520
554
  console.log("");
@@ -540,6 +574,7 @@ async function interactiveInterview(analysis, _cwd, projectName) {
540
574
  authMethods,
541
575
  customAuth,
542
576
  extraTools,
577
+ mcp,
543
578
  setupDomains: {
544
579
  claudeEnv: true,
545
580
  processDocs: true,
@@ -914,18 +949,348 @@ var init_platform = __esm({
914
949
  }
915
950
  });
916
951
 
952
+ // src/stages/environment/provisioners/mcp.ts
953
+ var mcp_exports = {};
954
+ __export(mcp_exports, {
955
+ provisionMcp: () => provisionMcp
956
+ });
957
+ import { join as join3 } from "path";
958
+ import fs4 from "fs-extra";
959
+ async function provisionMcp(ctx) {
960
+ const mcp = ctx.mcp;
961
+ if (!mcp?.enabled) return;
962
+ const mcpDir = join3(ctx.cwd, "mcp-server");
963
+ await fs4.ensureDir(mcpDir);
964
+ await fs4.ensureDir(join3(mcpDir, "src"));
965
+ await fs4.ensureDir(join3(mcpDir, "tests"));
966
+ const pkgJson = {
967
+ name: mcp.name,
968
+ version: "0.1.0",
969
+ description: `MCP server for ${ctx.projectName}`,
970
+ type: "module",
971
+ main: "dist/index.js",
972
+ bin: { [mcp.name]: "./dist/index.js" },
973
+ scripts: {
974
+ build: "tsc",
975
+ dev: "tsc --watch",
976
+ test: "vitest run",
977
+ start: "node dist/index.js"
978
+ },
979
+ dependencies: {
980
+ "@modelcontextprotocol/sdk": "^1.0.0"
981
+ },
982
+ devDependencies: {
983
+ typescript: "^5.7.0",
984
+ "@types/node": "^20.17.0",
985
+ vitest: "^3.0.0"
986
+ }
987
+ };
988
+ const pkgPath = join3(mcpDir, "package.json");
989
+ if (!await fs4.pathExists(pkgPath)) {
990
+ await fs4.writeJson(pkgPath, pkgJson, { spaces: 2 });
991
+ }
992
+ const tsconfig = {
993
+ compilerOptions: {
994
+ target: "ES2022",
995
+ module: "ESNext",
996
+ moduleResolution: "bundler",
997
+ outDir: "./dist",
998
+ rootDir: "./src",
999
+ strict: true,
1000
+ esModuleInterop: true,
1001
+ skipLibCheck: true,
1002
+ resolveJsonModule: true,
1003
+ declaration: true
1004
+ },
1005
+ include: ["src/**/*"],
1006
+ exclude: ["node_modules", "dist", "tests"]
1007
+ };
1008
+ const tsPath = join3(mcpDir, "tsconfig.json");
1009
+ if (!await fs4.pathExists(tsPath)) {
1010
+ await fs4.writeJson(tsPath, tsconfig, { spaces: 2 });
1011
+ }
1012
+ const indexPath = join3(mcpDir, "src", "index.ts");
1013
+ if (!await fs4.pathExists(indexPath)) {
1014
+ await fs4.writeFile(indexPath, buildIndexTs(mcp), "utf8");
1015
+ }
1016
+ if (mcp.type === "tools" || mcp.type === "all") {
1017
+ const toolsPath = join3(mcpDir, "src", "tools.ts");
1018
+ if (!await fs4.pathExists(toolsPath)) {
1019
+ await fs4.writeFile(toolsPath, TOOLS_TEMPLATE, "utf8");
1020
+ }
1021
+ }
1022
+ if (mcp.type === "resources" || mcp.type === "all") {
1023
+ const resPath = join3(mcpDir, "src", "resources.ts");
1024
+ if (!await fs4.pathExists(resPath)) {
1025
+ await fs4.writeFile(resPath, RESOURCES_TEMPLATE, "utf8");
1026
+ }
1027
+ }
1028
+ if (mcp.type === "prompts" || mcp.type === "all") {
1029
+ const promptsPath = join3(mcpDir, "src", "prompts.ts");
1030
+ if (!await fs4.pathExists(promptsPath)) {
1031
+ await fs4.writeFile(promptsPath, PROMPTS_TEMPLATE, "utf8");
1032
+ }
1033
+ }
1034
+ const testPath = join3(mcpDir, "tests", "server.test.ts");
1035
+ if (!await fs4.pathExists(testPath)) {
1036
+ await fs4.writeFile(testPath, TEST_TEMPLATE, "utf8");
1037
+ }
1038
+ const readmePath = join3(mcpDir, "README.md");
1039
+ if (!await fs4.pathExists(readmePath)) {
1040
+ await fs4.writeFile(readmePath, buildReadme(mcp), "utf8");
1041
+ }
1042
+ const mcpJsonPath = join3(ctx.cwd, ".mcp.json");
1043
+ const mcpJson = await fs4.pathExists(mcpJsonPath) ? await fs4.readJson(mcpJsonPath) : { mcpServers: {} };
1044
+ mcpJson.mcpServers[mcp.name] = {
1045
+ command: "node",
1046
+ args: ["./mcp-server/dist/index.js"]
1047
+ };
1048
+ await fs4.writeJson(mcpJsonPath, mcpJson, { spaces: 2 });
1049
+ const gitignorePath = join3(mcpDir, ".gitignore");
1050
+ if (!await fs4.pathExists(gitignorePath)) {
1051
+ await fs4.writeFile(gitignorePath, "node_modules/\ndist/\n*.log\n", "utf8");
1052
+ }
1053
+ }
1054
+ function buildIndexTs(mcp) {
1055
+ const imports = [];
1056
+ const registrations = [];
1057
+ if (mcp.type === "tools" || mcp.type === "all") {
1058
+ imports.push("import { registerTools } from './tools.js';");
1059
+ registrations.push(" registerTools(server);");
1060
+ }
1061
+ if (mcp.type === "resources" || mcp.type === "all") {
1062
+ imports.push("import { registerResources } from './resources.js';");
1063
+ registrations.push(" registerResources(server);");
1064
+ }
1065
+ if (mcp.type === "prompts" || mcp.type === "all") {
1066
+ imports.push("import { registerPrompts } from './prompts.js';");
1067
+ registrations.push(" registerPrompts(server);");
1068
+ }
1069
+ return `#!/usr/bin/env node
1070
+ /**
1071
+ * ${mcp.name} \u2014 MCP Server
1072
+ * Generated by PAI
1073
+ */
1074
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
1075
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
1076
+ ${imports.join("\n")}
1077
+
1078
+ async function main() {
1079
+ const server = new Server(
1080
+ {
1081
+ name: '${mcp.name}',
1082
+ version: '0.1.0',
1083
+ },
1084
+ {
1085
+ capabilities: {
1086
+ ${mcp.type === "tools" || mcp.type === "all" ? " tools: {}," : ""}
1087
+ ${mcp.type === "resources" || mcp.type === "all" ? " resources: {}," : ""}
1088
+ ${mcp.type === "prompts" || mcp.type === "all" ? " prompts: {}," : ""}
1089
+ },
1090
+ },
1091
+ );
1092
+
1093
+ ${registrations.join("\n")}
1094
+
1095
+ const transport = new StdioServerTransport();
1096
+ await server.connect(transport);
1097
+ console.error('${mcp.name} MCP server running on stdio');
1098
+ }
1099
+
1100
+ main().catch((err) => {
1101
+ console.error('Fatal error:', err);
1102
+ process.exit(1);
1103
+ });
1104
+ `;
1105
+ }
1106
+ function buildReadme(mcp) {
1107
+ return `# ${mcp.name} \u2014 MCP Server
1108
+
1109
+ PAI\uAC00 \uC0DD\uC131\uD55C MCP(Model Context Protocol) \uC11C\uBC84\uC785\uB2C8\uB2E4.
1110
+
1111
+ ## \uD0C0\uC785: ${mcp.type}
1112
+
1113
+ ${mcp.type === "tools" || mcp.type === "all" ? "- **Tools**: AI\uAC00 \uD638\uCD9C\uD560 \uAE30\uB2A5 \uC81C\uACF5 (src/tools.ts)" : ""}
1114
+ ${mcp.type === "resources" || mcp.type === "all" ? "- **Resources**: AI\uAC00 \uC77D\uC744 \uCEE8\uD14D\uC2A4\uD2B8 \uC81C\uACF5 (src/resources.ts)" : ""}
1115
+ ${mcp.type === "prompts" || mcp.type === "all" ? "- **Prompts**: \uC7AC\uC0AC\uC6A9 \uD504\uB86C\uD504\uD2B8 \uD15C\uD50C\uB9BF (src/prompts.ts)" : ""}
1116
+
1117
+ ## \uAC1C\uBC1C
1118
+
1119
+ \`\`\`bash
1120
+ cd mcp-server
1121
+ npm install
1122
+ npm run build
1123
+ npm test
1124
+ \`\`\`
1125
+
1126
+ ## Claude Code \uC5F0\uACB0
1127
+
1128
+ \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8\uC758 \`.mcp.json\` \uD30C\uC77C\uC5D0 \uC774\uBBF8 \uB4F1\uB85D\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.
1129
+ Claude Code\uB97C \uC7AC\uC2DC\uC791\uD558\uBA74 \uC790\uB3D9\uC73C\uB85C MCP \uC11C\uBC84\uAC00 \uB85C\uB4DC\uB429\uB2C8\uB2E4.
1130
+
1131
+ \uC218\uB3D9 \uB4F1\uB85D:
1132
+ \`\`\`bash
1133
+ claude mcp add ${mcp.name} -- node ./mcp-server/dist/index.js
1134
+ \`\`\`
1135
+
1136
+ ## \uB2E4\uC74C \uB2E8\uACC4
1137
+
1138
+ 1. \`npm install\` \u2014 \uC758\uC874\uC131 \uC124\uCE58
1139
+ 2. \`npm run build\` \u2014 TypeScript \uCEF4\uD30C\uC77C
1140
+ 3. \`src/\` \uB514\uB809\uD1A0\uB9AC\uC758 \uD15C\uD50C\uB9BF\uC744 \uC2E4\uC81C \uB85C\uC9C1\uC73C\uB85C \uC218\uC815
1141
+ 4. Claude Code \uC7AC\uC2DC\uC791 \u2192 MCP \uC11C\uBC84 \uC0AC\uC6A9
1142
+
1143
+ ## \uCC38\uACE0 \uBB38\uC11C
1144
+
1145
+ - [MCP \uACF5\uC2DD \uBB38\uC11C](https://modelcontextprotocol.io/)
1146
+ - [SDK \uBB38\uC11C](https://github.com/modelcontextprotocol/sdk)
1147
+ `;
1148
+ }
1149
+ var TOOLS_TEMPLATE, RESOURCES_TEMPLATE, PROMPTS_TEMPLATE, TEST_TEMPLATE;
1150
+ var init_mcp = __esm({
1151
+ "src/stages/environment/provisioners/mcp.ts"() {
1152
+ "use strict";
1153
+ TOOLS_TEMPLATE = `import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
1154
+ import {
1155
+ CallToolRequestSchema,
1156
+ ListToolsRequestSchema,
1157
+ } from '@modelcontextprotocol/sdk/types.js';
1158
+
1159
+ export function registerTools(server: Server): void {
1160
+ // Tool \uBAA9\uB85D \uC815\uC758
1161
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
1162
+ tools: [
1163
+ {
1164
+ name: 'hello',
1165
+ description: '\uD14C\uC2A4\uD2B8\uC6A9 \uC778\uC0AC \uB3C4\uAD6C \u2014 \uC774 \uD15C\uD50C\uB9BF\uC744 \uC218\uC815\uD574\uC11C \uC2E4\uC81C \uB3C4\uAD6C\uB97C \uAD6C\uD604\uD558\uC138\uC694',
1166
+ inputSchema: {
1167
+ type: 'object',
1168
+ properties: {
1169
+ name: { type: 'string', description: '\uC778\uC0AC\uD560 \uB300\uC0C1' },
1170
+ },
1171
+ required: ['name'],
1172
+ },
1173
+ },
1174
+ ],
1175
+ }));
1176
+
1177
+ // Tool \uD638\uCD9C \uCC98\uB9AC
1178
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1179
+ const { name, arguments: args } = request.params;
1180
+
1181
+ if (name === 'hello') {
1182
+ const target = (args as { name?: string })?.name ?? 'world';
1183
+ return {
1184
+ content: [{ type: 'text', text: \`Hello, \${target}!\` }],
1185
+ };
1186
+ }
1187
+
1188
+ throw new Error(\`Unknown tool: \${name}\`);
1189
+ });
1190
+ }
1191
+ `;
1192
+ RESOURCES_TEMPLATE = `import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
1193
+ import {
1194
+ ListResourcesRequestSchema,
1195
+ ReadResourceRequestSchema,
1196
+ } from '@modelcontextprotocol/sdk/types.js';
1197
+
1198
+ export function registerResources(server: Server): void {
1199
+ // Resource \uBAA9\uB85D \uC815\uC758
1200
+ server.setRequestHandler(ListResourcesRequestSchema, async () => ({
1201
+ resources: [
1202
+ {
1203
+ uri: 'example://sample',
1204
+ name: 'Sample Resource',
1205
+ description: '\uC608\uC81C \uB9AC\uC18C\uC2A4 \u2014 AI\uAC00 \uC77D\uC744 \uCEE8\uD14D\uC2A4\uD2B8',
1206
+ mimeType: 'text/plain',
1207
+ },
1208
+ ],
1209
+ }));
1210
+
1211
+ // Resource \uC77D\uAE30
1212
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
1213
+ const { uri } = request.params;
1214
+
1215
+ if (uri === 'example://sample') {
1216
+ return {
1217
+ contents: [{
1218
+ uri,
1219
+ mimeType: 'text/plain',
1220
+ text: 'This is a sample resource content.',
1221
+ }],
1222
+ };
1223
+ }
1224
+
1225
+ throw new Error(\`Unknown resource: \${uri}\`);
1226
+ });
1227
+ }
1228
+ `;
1229
+ PROMPTS_TEMPLATE = `import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
1230
+ import {
1231
+ GetPromptRequestSchema,
1232
+ ListPromptsRequestSchema,
1233
+ } from '@modelcontextprotocol/sdk/types.js';
1234
+
1235
+ export function registerPrompts(server: Server): void {
1236
+ // Prompt \uBAA9\uB85D \uC815\uC758
1237
+ server.setRequestHandler(ListPromptsRequestSchema, async () => ({
1238
+ prompts: [
1239
+ {
1240
+ name: 'review-code',
1241
+ description: '\uCF54\uB4DC \uB9AC\uBDF0 \uC694\uCCAD \uD504\uB86C\uD504\uD2B8',
1242
+ arguments: [
1243
+ { name: 'code', description: '\uB9AC\uBDF0\uD560 \uCF54\uB4DC', required: true },
1244
+ ],
1245
+ },
1246
+ ],
1247
+ }));
1248
+
1249
+ // Prompt \uAC00\uC838\uC624\uAE30
1250
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
1251
+ const { name, arguments: args } = request.params;
1252
+
1253
+ if (name === 'review-code') {
1254
+ const code = (args as { code?: string })?.code ?? '';
1255
+ return {
1256
+ messages: [{
1257
+ role: 'user',
1258
+ content: {
1259
+ type: 'text',
1260
+ text: \`\uB2E4\uC74C \uCF54\uB4DC\uB97C \uB9AC\uBDF0\uD574\uC8FC\uC138\uC694:\\n\\n\${code}\`,
1261
+ },
1262
+ }],
1263
+ };
1264
+ }
1265
+
1266
+ throw new Error(\`Unknown prompt: \${name}\`);
1267
+ });
1268
+ }
1269
+ `;
1270
+ TEST_TEMPLATE = `import { describe, it, expect } from 'vitest';
1271
+
1272
+ describe('MCP Server', () => {
1273
+ it('should be tested', () => {
1274
+ // TODO: MCP \uC11C\uBC84 \uD14C\uC2A4\uD2B8 \uC791\uC131
1275
+ expect(true).toBe(true);
1276
+ });
1277
+ });
1278
+ `;
1279
+ }
1280
+ });
1281
+
917
1282
  // src/stages/environment/provisioners/registry.ts
918
1283
  var registry_exports = {};
919
1284
  __export(registry_exports, {
920
1285
  PROVISIONERS: () => PROVISIONERS,
921
1286
  runProvisioners: () => runProvisioners
922
1287
  });
923
- import { join as join3 } from "path";
924
- import fs4 from "fs-extra";
1288
+ import { join as join4 } from "path";
1289
+ import fs5 from "fs-extra";
925
1290
  async function provisionGitHub(ctx) {
926
- const gitDir = join3(ctx.cwd, ".git");
927
- if (!await fs4.pathExists(gitDir)) {
928
- await fs4.ensureDir(join3(ctx.cwd, ".github", "workflows"));
1291
+ const gitDir = join4(ctx.cwd, ".git");
1292
+ if (!await fs5.pathExists(gitDir)) {
1293
+ await fs5.ensureDir(join4(ctx.cwd, ".github", "workflows"));
929
1294
  try {
930
1295
  const { execa } = await import("execa");
931
1296
  await execa("git", ["init"], { cwd: ctx.cwd });
@@ -934,11 +1299,11 @@ async function provisionGitHub(ctx) {
934
1299
  }
935
1300
  }
936
1301
  const dirs = ["src", "docs", "tests", "public", ".pai"];
937
- await Promise.all(dirs.map((d) => fs4.ensureDir(join3(ctx.cwd, d))));
938
- const handoffPath = join3(ctx.cwd, "handoff.md");
939
- if (!await fs4.pathExists(handoffPath)) {
1302
+ await Promise.all(dirs.map((d) => fs5.ensureDir(join4(ctx.cwd, d))));
1303
+ const handoffPath = join4(ctx.cwd, "handoff.md");
1304
+ if (!await fs5.pathExists(handoffPath)) {
940
1305
  const now = (/* @__PURE__ */ new Date()).toLocaleString("ko-KR");
941
- await fs4.writeFile(handoffPath, [
1306
+ await fs5.writeFile(handoffPath, [
942
1307
  `# Handoff \u2014 ${ctx.projectName}`,
943
1308
  "",
944
1309
  `> \uB9C8\uC9C0\uB9C9 \uC5C5\uB370\uC774\uD2B8: ${now}`,
@@ -967,9 +1332,9 @@ async function provisionGitHub(ctx) {
967
1332
  "- \uCEE8\uD14D\uC2A4\uD2B8\uAC00 \uBD80\uC871\uD558\uBA74 AI\uAC00 \uC790\uB3D9\uC73C\uB85C \uC2EC\uCE35 \uC778\uD130\uBDF0\uB97C \uC9C4\uD589\uD569\uB2C8\uB2E4"
968
1333
  ].join("\n") + "\n");
969
1334
  }
970
- const contribPath = join3(ctx.cwd, "CONTRIBUTING.md");
971
- if (!await fs4.pathExists(contribPath)) {
972
- await fs4.writeFile(contribPath, [
1335
+ const contribPath = join4(ctx.cwd, "CONTRIBUTING.md");
1336
+ if (!await fs5.pathExists(contribPath)) {
1337
+ await fs5.writeFile(contribPath, [
973
1338
  `# Contributing to ${ctx.projectName}`,
974
1339
  "",
975
1340
  "\uC774 \uD504\uB85C\uC81D\uD2B8\uC5D0 \uAE30\uC5EC\uD574 \uC8FC\uC154\uC11C \uAC10\uC0AC\uD569\uB2C8\uB2E4.",
@@ -1013,11 +1378,11 @@ async function provisionGitHub(ctx) {
1013
1378
  ""
1014
1379
  ].join("\n") + "\n");
1015
1380
  }
1016
- const contribSkillDir = join3(ctx.cwd, ".claude", "skills");
1017
- await fs4.ensureDir(contribSkillDir);
1018
- const contribSkillPath = join3(contribSkillDir, "contributing.md");
1019
- if (!await fs4.pathExists(contribSkillPath)) {
1020
- await fs4.writeFile(contribSkillPath, [
1381
+ const contribSkillDir = join4(ctx.cwd, ".claude", "skills");
1382
+ await fs5.ensureDir(contribSkillDir);
1383
+ const contribSkillPath = join4(contribSkillDir, "contributing.md");
1384
+ if (!await fs5.pathExists(contribSkillPath)) {
1385
+ await fs5.writeFile(contribSkillPath, [
1021
1386
  "---",
1022
1387
  "name: contributing",
1023
1388
  'description: "\uAE30\uC5EC\uC790 \uAC00\uC774\uB4DC \u2014 \uBE0C\uB79C\uCE58 \uADDC\uCE59, \uCEE4\uBC0B \uBA54\uC2DC\uC9C0, PR \uD504\uB85C\uC138\uC2A4 \uC790\uB3D9 \uC801\uC6A9"',
@@ -1042,9 +1407,9 @@ async function provisionGitHub(ctx) {
1042
1407
  ""
1043
1408
  ].join("\n") + "\n");
1044
1409
  }
1045
- const gitignorePath = join3(ctx.cwd, ".gitignore");
1046
- if (!await fs4.pathExists(gitignorePath)) {
1047
- await fs4.writeFile(gitignorePath, [
1410
+ const gitignorePath = join4(ctx.cwd, ".gitignore");
1411
+ if (!await fs5.pathExists(gitignorePath)) {
1412
+ await fs5.writeFile(gitignorePath, [
1048
1413
  "# Dependencies",
1049
1414
  "node_modules/",
1050
1415
  "",
@@ -1089,9 +1454,9 @@ async function provisionGitHub(ctx) {
1089
1454
  }
1090
1455
  }
1091
1456
  async function provisionVercel(ctx) {
1092
- const vercelJson = join3(ctx.cwd, "vercel.json");
1093
- if (!await fs4.pathExists(vercelJson)) {
1094
- await fs4.writeJson(vercelJson, {
1457
+ const vercelJson = join4(ctx.cwd, "vercel.json");
1458
+ if (!await fs5.pathExists(vercelJson)) {
1459
+ await fs5.writeJson(vercelJson, {
1095
1460
  version: 2,
1096
1461
  name: ctx.projectName,
1097
1462
  builds: [{ src: "src/**", use: "@vercel/static" }]
@@ -1099,20 +1464,20 @@ async function provisionVercel(ctx) {
1099
1464
  }
1100
1465
  }
1101
1466
  async function provisionSupabase(ctx) {
1102
- const supabaseDir = join3(ctx.cwd, "supabase");
1103
- await fs4.ensureDir(supabaseDir);
1104
- const configToml = join3(supabaseDir, "config.toml");
1105
- if (!await fs4.pathExists(configToml)) {
1106
- await fs4.writeFile(configToml, '[api]\nschemas = ["public"]\n[auth]\nsite_url = "http://localhost:3000"\n');
1467
+ const supabaseDir = join4(ctx.cwd, "supabase");
1468
+ await fs5.ensureDir(supabaseDir);
1469
+ const configToml = join4(supabaseDir, "config.toml");
1470
+ if (!await fs5.pathExists(configToml)) {
1471
+ await fs5.writeFile(configToml, '[api]\nschemas = ["public"]\n[auth]\nsite_url = "http://localhost:3000"\n');
1107
1472
  }
1108
1473
  ctx.envEntries["NEXT_PUBLIC_SUPABASE_URL"] = "YOUR_SUPABASE_URL";
1109
1474
  ctx.envEntries["NEXT_PUBLIC_SUPABASE_ANON_KEY"] = "YOUR_SUPABASE_ANON_KEY";
1110
1475
  }
1111
1476
  async function provisionOpenSpec(ctx) {
1112
- await fs4.ensureDir(join3(ctx.cwd, "docs"));
1113
- const openspecPath = join3(ctx.cwd, "docs", "openspec.md");
1114
- if (!await fs4.pathExists(openspecPath)) {
1115
- await fs4.writeFile(openspecPath, [
1477
+ await fs5.ensureDir(join4(ctx.cwd, "docs"));
1478
+ const openspecPath = join4(ctx.cwd, "docs", "openspec.md");
1479
+ if (!await fs5.pathExists(openspecPath)) {
1480
+ await fs5.writeFile(openspecPath, [
1116
1481
  `# OpenSpec \u2014 ${ctx.projectName}`,
1117
1482
  "",
1118
1483
  "## 1. \uBAA9\uC801 (Purpose)",
@@ -1139,10 +1504,10 @@ async function provisionOpenSpec(ctx) {
1139
1504
  }
1140
1505
  }
1141
1506
  async function provisionOMC(ctx) {
1142
- await fs4.ensureDir(join3(ctx.cwd, ".pai"));
1143
- const omcPath = join3(ctx.cwd, ".pai", "omc.md");
1144
- if (!await fs4.pathExists(omcPath)) {
1145
- await fs4.writeFile(omcPath, [
1507
+ await fs5.ensureDir(join4(ctx.cwd, ".pai"));
1508
+ const omcPath = join4(ctx.cwd, ".pai", "omc.md");
1509
+ if (!await fs5.pathExists(omcPath)) {
1510
+ await fs5.writeFile(omcPath, [
1146
1511
  `# OMC \u2014 Object Model Context (${ctx.projectName})`,
1147
1512
  "",
1148
1513
  "> AI\uAC00 \uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uB3C4\uBA54\uC778\uC744 \uC774\uD574\uD558\uAE30 \uC704\uD55C \uD575\uC2EC \uAC1D\uCCB4 \uBAA8\uB378",
@@ -1162,8 +1527,8 @@ async function provisionOMC(ctx) {
1162
1527
  }
1163
1528
  }
1164
1529
  async function provisionGstack(ctx) {
1165
- await fs4.ensureDir(join3(ctx.cwd, ".pai"));
1166
- await fs4.writeJson(join3(ctx.cwd, ".pai", "gstack.json"), {
1530
+ await fs5.ensureDir(join4(ctx.cwd, ".pai"));
1531
+ await fs5.writeJson(join4(ctx.cwd, ".pai", "gstack.json"), {
1167
1532
  version: "1.0",
1168
1533
  testRunner: "jest",
1169
1534
  coverageThreshold: { global: { lines: 80 } },
@@ -1171,10 +1536,10 @@ async function provisionGstack(ctx) {
1171
1536
  }, { spaces: 2 });
1172
1537
  }
1173
1538
  async function provisionRoboco(ctx) {
1174
- await fs4.ensureDir(join3(ctx.cwd, ".pai"));
1175
- const robocoPath = join3(ctx.cwd, ".pai", "roboco.json");
1176
- if (await fs4.pathExists(robocoPath)) return;
1177
- await fs4.writeJson(robocoPath, {
1539
+ await fs5.ensureDir(join4(ctx.cwd, ".pai"));
1540
+ const robocoPath = join4(ctx.cwd, ".pai", "roboco.json");
1541
+ if (await fs5.pathExists(robocoPath)) return;
1542
+ await fs5.writeJson(robocoPath, {
1178
1543
  version: "1.0",
1179
1544
  checks: ["github", "vercel", "supabase", "openspec", "omc"],
1180
1545
  reportOutput: "AI_READINESS_REPORT.md",
@@ -1182,14 +1547,18 @@ async function provisionRoboco(ctx) {
1182
1547
  }, { spaces: 2 });
1183
1548
  }
1184
1549
  async function provisionHarness(ctx) {
1185
- await fs4.ensureDir(join3(ctx.cwd, ".pai"));
1186
- await fs4.writeJson(join3(ctx.cwd, ".pai", "harness.json"), {
1550
+ await fs5.ensureDir(join4(ctx.cwd, ".pai"));
1551
+ await fs5.writeJson(join4(ctx.cwd, ".pai", "harness.json"), {
1187
1552
  version: "1.0",
1188
1553
  specFile: "docs/openspec.md",
1189
1554
  checkOn: ["pre-commit", "ci"],
1190
1555
  rules: ["spec-implementation-match", "api-contract-test"]
1191
1556
  }, { spaces: 2 });
1192
1557
  }
1558
+ async function provisionMcpWrapper(ctx) {
1559
+ const { provisionMcp: provisionMcp2 } = await Promise.resolve().then(() => (init_mcp(), mcp_exports));
1560
+ await provisionMcp2(ctx);
1561
+ }
1193
1562
  async function runProvisioners(keys, ctx) {
1194
1563
  const results = [];
1195
1564
  for (const key of keys) {
@@ -1207,7 +1576,7 @@ async function runProvisioners(keys, ctx) {
1207
1576
  }
1208
1577
  }
1209
1578
  if (Object.keys(ctx.envEntries).length > 0) {
1210
- writeEnvFile(join3(ctx.cwd, ".env.local"), ctx.envEntries);
1579
+ writeEnvFile(join4(ctx.cwd, ".env.local"), ctx.envEntries);
1211
1580
  }
1212
1581
  return results;
1213
1582
  }
@@ -1224,7 +1593,8 @@ var init_registry = __esm({
1224
1593
  omc: provisionOMC,
1225
1594
  gstack: provisionGstack,
1226
1595
  roboco: provisionRoboco,
1227
- harness: provisionHarness
1596
+ harness: provisionHarness,
1597
+ mcp: provisionMcpWrapper
1228
1598
  };
1229
1599
  }
1230
1600
  });
@@ -1235,21 +1605,21 @@ __export(claude_commands_exports, {
1235
1605
  provisionClaudeCommands: () => provisionClaudeCommands,
1236
1606
  upgradeClaudeCommands: () => upgradeClaudeCommands
1237
1607
  });
1238
- import { join as join4 } from "path";
1239
- import fs5 from "fs-extra";
1608
+ import { join as join5 } from "path";
1609
+ import fs6 from "fs-extra";
1240
1610
  async function writeCommandFiles(cmdDir, entries, options) {
1241
- await fs5.ensureDir(cmdDir);
1611
+ await fs6.ensureDir(cmdDir);
1242
1612
  const written = [];
1243
1613
  const skipped = [];
1244
1614
  const errors = [];
1245
1615
  for (const [filename, content] of Object.entries(entries)) {
1246
- const filePath = join4(cmdDir, filename);
1616
+ const filePath = join5(cmdDir, filename);
1247
1617
  try {
1248
- if (options.skipIfExists && await fs5.pathExists(filePath)) {
1618
+ if (options.skipIfExists && await fs6.pathExists(filePath)) {
1249
1619
  skipped.push(filename);
1250
1620
  continue;
1251
1621
  }
1252
- await fs5.writeFile(filePath, content);
1622
+ await fs6.writeFile(filePath, content);
1253
1623
  written.push(filename);
1254
1624
  } catch (err) {
1255
1625
  const msg = err instanceof Error ? err.message : String(err);
@@ -1259,13 +1629,13 @@ async function writeCommandFiles(cmdDir, entries, options) {
1259
1629
  return { written, skipped, errors };
1260
1630
  }
1261
1631
  async function provisionClaudeCommands(cwd) {
1262
- const skillDir = join4(cwd, ".claude", "skills", "pai");
1263
- await fs5.ensureDir(skillDir);
1264
- const skillPath = join4(skillDir, "SKILL.md");
1265
- if (!await fs5.pathExists(skillPath)) {
1266
- await fs5.writeFile(skillPath, SKILL_CONTENT);
1632
+ const skillDir = join5(cwd, ".claude", "skills", "pai");
1633
+ await fs6.ensureDir(skillDir);
1634
+ const skillPath = join5(skillDir, "SKILL.md");
1635
+ if (!await fs6.pathExists(skillPath)) {
1636
+ await fs6.writeFile(skillPath, SKILL_CONTENT);
1267
1637
  }
1268
- const cmdDir = join4(cwd, ".claude", "commands", "pai");
1638
+ const cmdDir = join5(cwd, ".claude", "commands", "pai");
1269
1639
  await writeCommandFiles(cmdDir, COMMANDS, { skipIfExists: true });
1270
1640
  }
1271
1641
  async function upgradeClaudeCommands(cwd) {
@@ -1274,16 +1644,16 @@ async function upgradeClaudeCommands(cwd) {
1274
1644
  commandsWritten: [],
1275
1645
  commandErrors: []
1276
1646
  };
1277
- const skillDir = join4(cwd, ".claude", "skills", "pai");
1278
- await fs5.ensureDir(skillDir);
1647
+ const skillDir = join5(cwd, ".claude", "skills", "pai");
1648
+ await fs6.ensureDir(skillDir);
1279
1649
  try {
1280
- await fs5.writeFile(join4(skillDir, "SKILL.md"), SKILL_CONTENT);
1650
+ await fs6.writeFile(join5(skillDir, "SKILL.md"), SKILL_CONTENT);
1281
1651
  result.skillUpdated = true;
1282
1652
  } catch (err) {
1283
1653
  const msg = err instanceof Error ? err.message : String(err);
1284
1654
  result.commandErrors.push({ file: "SKILL.md", error: msg });
1285
1655
  }
1286
- const cmdDir = join4(cwd, ".claude", "commands", "pai");
1656
+ const cmdDir = join5(cwd, ".claude", "commands", "pai");
1287
1657
  const { written, errors } = await writeCommandFiles(cmdDir, COMMANDS, { skipIfExists: false });
1288
1658
  result.commandsWritten = written;
1289
1659
  result.commandErrors.push(...errors);
@@ -1905,18 +2275,18 @@ npx pai-zero wakeup # \uC9C0\uAE08 \uB79C\uB364 \uBA54\uC2DC\uC9
1905
2275
  // src/core/config.ts
1906
2276
  import path from "path";
1907
2277
  import { createRequire } from "module";
1908
- import fs6 from "fs-extra";
2278
+ import fs7 from "fs-extra";
1909
2279
  async function loadConfig(cwd) {
1910
2280
  const configPath = path.join(cwd, CONFIG_DIR, CONFIG_FILE);
1911
- if (await fs6.pathExists(configPath)) {
1912
- return fs6.readJson(configPath);
2281
+ if (await fs7.pathExists(configPath)) {
2282
+ return fs7.readJson(configPath);
1913
2283
  }
1914
2284
  return null;
1915
2285
  }
1916
2286
  async function saveConfig(cwd, config) {
1917
2287
  const configDir = path.join(cwd, CONFIG_DIR);
1918
- await fs6.ensureDir(configDir);
1919
- await fs6.writeJson(path.join(configDir, CONFIG_FILE), config, { spaces: 2 });
2288
+ await fs7.ensureDir(configDir);
2289
+ await fs7.writeJson(path.join(configDir, CONFIG_FILE), config, { spaces: 2 });
1920
2290
  }
1921
2291
  function createDefaultConfig(projectName, mode) {
1922
2292
  return {
@@ -1943,9 +2313,9 @@ var doctor_exports = {};
1943
2313
  __export(doctor_exports, {
1944
2314
  runDoctor: () => runDoctor
1945
2315
  });
1946
- import { join as join5 } from "path";
2316
+ import { join as join6 } from "path";
1947
2317
  import { homedir } from "os";
1948
- import fs7 from "fs-extra";
2318
+ import fs8 from "fs-extra";
1949
2319
  async function runDoctor() {
1950
2320
  section("PAI Doctor \u2014 \uD658\uACBD \uC9C4\uB2E8");
1951
2321
  const checks = [];
@@ -1964,8 +2334,8 @@ async function runDoctor() {
1964
2334
  detail: claudeCheck.detail,
1965
2335
  fix: claudeCheck.ok ? void 0 : "npm install -g @anthropic-ai/claude-code"
1966
2336
  });
1967
- const globalConfigPath = join5(homedir(), ".pai", "config.json");
1968
- const hasGlobalConfig = await fs7.pathExists(globalConfigPath);
2337
+ const globalConfigPath = join6(homedir(), ".pai", "config.json");
2338
+ const hasGlobalConfig = await fs8.pathExists(globalConfigPath);
1969
2339
  checks.push({
1970
2340
  label: "\uAE00\uB85C\uBC8C \uC124\uC815",
1971
2341
  ok: true,
@@ -2087,7 +2457,8 @@ var init_environment = __esm({
2087
2457
  envEntries: {
2088
2458
  PAI_PROJECT_NAME: interview.projectName,
2089
2459
  PAI_MODE: interview.mode
2090
- }
2460
+ },
2461
+ mcp: interview.mcp
2091
2462
  };
2092
2463
  if (interview.authMethods.includes("custom")) {
2093
2464
  provCtx.envEntries["OAUTH_CLIENT_ID"] = interview.customAuth?.clientId || "YOUR_CLIENT_ID_HERE";
@@ -2107,6 +2478,7 @@ var init_environment = __esm({
2107
2478
  ...interview.extraTools.includes("supabase") ? ["supabase/config.toml"] : [],
2108
2479
  ...interview.extraTools.includes("gstack") ? [".pai/gstack.json"] : [],
2109
2480
  ...interview.extraTools.includes("harness") ? [".pai/harness.json"] : [],
2481
+ ...interview.extraTools.includes("mcp") ? ["mcp-server/", ".mcp.json"] : [],
2110
2482
  ".env.local"
2111
2483
  ];
2112
2484
  console.log("");
@@ -2174,7 +2546,7 @@ __export(detector_exports, {
2174
2546
  scanProjectState: () => scanProjectState
2175
2547
  });
2176
2548
  import path2 from "path";
2177
- import fs8 from "fs-extra";
2549
+ import fs9 from "fs-extra";
2178
2550
  async function scanProjectState(cwd) {
2179
2551
  const result = {
2180
2552
  isNewProject: true,
@@ -2185,10 +2557,10 @@ async function scanProjectState(cwd) {
2185
2557
  details: {}
2186
2558
  };
2187
2559
  const paiConfigPath = path2.join(cwd, ".pai", "config.json");
2188
- if (await fs8.pathExists(paiConfigPath)) {
2560
+ if (await fs9.pathExists(paiConfigPath)) {
2189
2561
  result.hasPaiConfig = true;
2190
2562
  try {
2191
- const config = await fs8.readJson(paiConfigPath);
2563
+ const config = await fs9.readJson(paiConfigPath);
2192
2564
  result.projectMode = config.mode ?? null;
2193
2565
  } catch {
2194
2566
  }
@@ -2196,7 +2568,7 @@ async function scanProjectState(cwd) {
2196
2568
  for (const [key, signatures] of Object.entries(PLUGIN_SIGNATURES)) {
2197
2569
  const installed = await Promise.any(
2198
2570
  signatures.map(async (sig) => {
2199
- if (await fs8.pathExists(path2.join(cwd, sig))) return true;
2571
+ if (await fs9.pathExists(path2.join(cwd, sig))) return true;
2200
2572
  throw new Error("not found");
2201
2573
  })
2202
2574
  ).catch(() => false);
@@ -2207,7 +2579,7 @@ async function scanProjectState(cwd) {
2207
2579
  result.missingPlugins.push(key);
2208
2580
  }
2209
2581
  }
2210
- const hasAnyContent = result.installedPlugins.length > 0 || await fs8.pathExists(path2.join(cwd, "package.json")) || await fs8.pathExists(path2.join(cwd, "src")) || await fs8.pathExists(path2.join(cwd, "README.md"));
2582
+ const hasAnyContent = result.installedPlugins.length > 0 || await fs9.pathExists(path2.join(cwd, "package.json")) || await fs9.pathExists(path2.join(cwd, "src")) || await fs9.pathExists(path2.join(cwd, "README.md"));
2211
2583
  result.isNewProject = !hasAnyContent;
2212
2584
  return result;
2213
2585
  }
@@ -2337,8 +2709,8 @@ var analyzer_exports2 = {};
2337
2709
  __export(analyzer_exports2, {
2338
2710
  analyzeRepository: () => analyzeRepository
2339
2711
  });
2340
- import { join as join6 } from "path";
2341
- import fs9 from "fs-extra";
2712
+ import { join as join7 } from "path";
2713
+ import fs10 from "fs-extra";
2342
2714
  async function analyzeRepository(repoPath) {
2343
2715
  try {
2344
2716
  return await aiAnalysis(repoPath);
@@ -2399,14 +2771,14 @@ async function checkTestCoverage(repoPath) {
2399
2771
  ".nycrc"
2400
2772
  ];
2401
2773
  for (const f of testConfigs) {
2402
- const found = await fs9.pathExists(join6(repoPath, f));
2774
+ const found = await fs10.pathExists(join7(repoPath, f));
2403
2775
  findings.push({ item: f, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
2404
2776
  if (found) score += 20;
2405
2777
  }
2406
2778
  const testDirs = ["tests", "test", "__tests__", "spec"];
2407
2779
  let hasTestDir = false;
2408
2780
  for (const d of testDirs) {
2409
- if (await fs9.pathExists(join6(repoPath, d))) {
2781
+ if (await fs10.pathExists(join7(repoPath, d))) {
2410
2782
  findings.push({ item: d, found: true, details: "\uD14C\uC2A4\uD2B8 \uB514\uB809\uD1A0\uB9AC \uC874\uC7AC" });
2411
2783
  hasTestDir = true;
2412
2784
  score += 30;
@@ -2430,7 +2802,7 @@ async function checkCiCd(repoPath) {
2430
2802
  { path: ".circleci", label: "CircleCI" }
2431
2803
  ];
2432
2804
  for (const { path: path3, label } of ciConfigs) {
2433
- const found = await fs9.pathExists(join6(repoPath, path3));
2805
+ const found = await fs10.pathExists(join7(repoPath, path3));
2434
2806
  findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
2435
2807
  if (found) score += 40;
2436
2808
  }
@@ -2449,7 +2821,7 @@ async function checkHooks(repoPath) {
2449
2821
  { path: ".claude/settings.json", label: "Claude Code settings" }
2450
2822
  ];
2451
2823
  for (const { path: path3, label } of hookConfigs) {
2452
- const found = await fs9.pathExists(join6(repoPath, path3));
2824
+ const found = await fs10.pathExists(join7(repoPath, path3));
2453
2825
  findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
2454
2826
  if (found) score += 20;
2455
2827
  }
@@ -2467,7 +2839,7 @@ async function checkRepoStructure(repoPath) {
2467
2839
  { path: ".gitignore", label: ".gitignore" }
2468
2840
  ];
2469
2841
  for (const { path: path3, label } of structureChecks) {
2470
- const found = await fs9.pathExists(join6(repoPath, path3));
2842
+ const found = await fs10.pathExists(join7(repoPath, path3));
2471
2843
  findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
2472
2844
  if (found) score += 25;
2473
2845
  }
@@ -2484,7 +2856,7 @@ async function checkDocumentation(repoPath) {
2484
2856
  { path: "docs/openspec.md", label: "OpenSpec PRD", points: 25 }
2485
2857
  ];
2486
2858
  for (const { path: path3, label, points } of docChecks) {
2487
- const found = await fs9.pathExists(join6(repoPath, path3));
2859
+ const found = await fs10.pathExists(join7(repoPath, path3));
2488
2860
  findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
2489
2861
  if (found) score += points;
2490
2862
  }
@@ -2503,7 +2875,7 @@ async function checkHarnessEngineering(repoPath) {
2503
2875
  { path: ".pai/config.json", label: "PAI config", points: 10 }
2504
2876
  ];
2505
2877
  for (const { path: path3, label, points } of harnessChecks) {
2506
- const found = await fs9.pathExists(join6(repoPath, path3));
2878
+ const found = await fs10.pathExists(join7(repoPath, path3));
2507
2879
  findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
2508
2880
  if (found) score += points;
2509
2881
  }
@@ -2886,56 +3258,56 @@ __export(shell_cd_exports, {
2886
3258
  installShellHelper: () => installShellHelper,
2887
3259
  requestCdAfter: () => requestCdAfter
2888
3260
  });
2889
- import { join as join7 } from "path";
3261
+ import { join as join8 } from "path";
2890
3262
  import { homedir as homedir2 } from "os";
2891
- import fs10 from "fs-extra";
3263
+ import fs11 from "fs-extra";
2892
3264
  async function requestCdAfter(targetDir) {
2893
- await fs10.ensureDir(PAI_DIR);
2894
- await fs10.writeFile(CD_FILE, targetDir);
3265
+ await fs11.ensureDir(PAI_DIR);
3266
+ await fs11.writeFile(CD_FILE, targetDir);
2895
3267
  }
2896
3268
  async function installShellHelper() {
2897
- await fs10.ensureDir(PAI_DIR);
3269
+ await fs11.ensureDir(PAI_DIR);
2898
3270
  if (isWindows) {
2899
3271
  return installPowerShellHelper();
2900
3272
  }
2901
3273
  return installBashHelper();
2902
3274
  }
2903
3275
  async function installBashHelper() {
2904
- await fs10.writeFile(HELPER_FILE_SH, BASH_HELPER);
3276
+ await fs11.writeFile(HELPER_FILE_SH, BASH_HELPER);
2905
3277
  const rcFile = getShellRcPath();
2906
3278
  const sourceLine = 'source "$HOME/.pai/shell-helper.sh"';
2907
- if (await fs10.pathExists(rcFile)) {
2908
- const content = await fs10.readFile(rcFile, "utf8");
3279
+ if (await fs11.pathExists(rcFile)) {
3280
+ const content = await fs11.readFile(rcFile, "utf8");
2909
3281
  if (content.includes("shell-helper.sh")) {
2910
3282
  return true;
2911
3283
  }
2912
- await fs10.appendFile(rcFile, `
3284
+ await fs11.appendFile(rcFile, `
2913
3285
  # PAI \u2014 \uC790\uB3D9 \uB514\uB809\uD1A0\uB9AC \uC774\uB3D9
2914
3286
  ${sourceLine}
2915
3287
  `);
2916
3288
  return false;
2917
3289
  }
2918
- await fs10.writeFile(rcFile, `${sourceLine}
3290
+ await fs11.writeFile(rcFile, `${sourceLine}
2919
3291
  `);
2920
3292
  return false;
2921
3293
  }
2922
3294
  async function installPowerShellHelper() {
2923
- await fs10.writeFile(HELPER_FILE_PS1, POWERSHELL_HELPER);
3295
+ await fs11.writeFile(HELPER_FILE_PS1, POWERSHELL_HELPER);
2924
3296
  const rcFile = getShellRcPath();
2925
3297
  const sourceLine = '. "$env:USERPROFILE\\.pai\\shell-helper.ps1"';
2926
- await fs10.ensureDir(join7(rcFile, ".."));
2927
- if (await fs10.pathExists(rcFile)) {
2928
- const content = await fs10.readFile(rcFile, "utf8");
3298
+ await fs11.ensureDir(join8(rcFile, ".."));
3299
+ if (await fs11.pathExists(rcFile)) {
3300
+ const content = await fs11.readFile(rcFile, "utf8");
2929
3301
  if (content.includes("shell-helper.ps1")) {
2930
3302
  return true;
2931
3303
  }
2932
- await fs10.appendFile(rcFile, `
3304
+ await fs11.appendFile(rcFile, `
2933
3305
  # PAI \u2014 \uC790\uB3D9 \uB514\uB809\uD1A0\uB9AC \uC774\uB3D9
2934
3306
  ${sourceLine}
2935
3307
  `);
2936
3308
  return false;
2937
3309
  }
2938
- await fs10.writeFile(rcFile, `${sourceLine}
3310
+ await fs11.writeFile(rcFile, `${sourceLine}
2939
3311
  `);
2940
3312
  return false;
2941
3313
  }
@@ -2944,10 +3316,10 @@ var init_shell_cd = __esm({
2944
3316
  "src/utils/shell-cd.ts"() {
2945
3317
  "use strict";
2946
3318
  init_platform();
2947
- PAI_DIR = join7(homedir2(), ".pai");
2948
- CD_FILE = join7(PAI_DIR, ".cd-after");
2949
- HELPER_FILE_SH = join7(PAI_DIR, "shell-helper.sh");
2950
- HELPER_FILE_PS1 = join7(PAI_DIR, "shell-helper.ps1");
3319
+ PAI_DIR = join8(homedir2(), ".pai");
3320
+ CD_FILE = join8(PAI_DIR, ".cd-after");
3321
+ HELPER_FILE_SH = join8(PAI_DIR, "shell-helper.sh");
3322
+ HELPER_FILE_PS1 = join8(PAI_DIR, "shell-helper.ps1");
2951
3323
  BASH_HELPER = `# PAI shell helper \u2014 \uC790\uB3D9 \uB514\uB809\uD1A0\uB9AC \uC774\uB3D9 \uC9C0\uC6D0
2952
3324
  pai() {
2953
3325
  local cd_target="$HOME/.pai/.cd-after"
@@ -2989,12 +3361,12 @@ function pai {
2989
3361
 
2990
3362
  // src/stages/evaluation/cache.ts
2991
3363
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
2992
- import { join as join8 } from "path";
3364
+ import { join as join9 } from "path";
2993
3365
  import { createHash } from "crypto";
2994
3366
  function computeRepoHash(repoPath) {
2995
3367
  const hash = createHash("sha256");
2996
3368
  for (const file of FILES_TO_HASH) {
2997
- const fullPath = join8(repoPath, file);
3369
+ const fullPath = join9(repoPath, file);
2998
3370
  try {
2999
3371
  const content = readFileSync(fullPath);
3000
3372
  hash.update(`${file}:${content.length}`);
@@ -3005,7 +3377,7 @@ function computeRepoHash(repoPath) {
3005
3377
  return hash.digest("hex").slice(0, 16);
3006
3378
  }
3007
3379
  function getCachePath(repoPath) {
3008
- return join8(repoPath, CACHE_DIR, CACHE_FILE);
3380
+ return join9(repoPath, CACHE_DIR, CACHE_FILE);
3009
3381
  }
3010
3382
  function loadCache(repoPath) {
3011
3383
  try {
@@ -3016,7 +3388,7 @@ function loadCache(repoPath) {
3016
3388
  }
3017
3389
  }
3018
3390
  function saveCache(repoPath, store) {
3019
- const cacheDir = join8(repoPath, CACHE_DIR, "cache");
3391
+ const cacheDir = join9(repoPath, CACHE_DIR, "cache");
3020
3392
  if (!existsSync(cacheDir)) {
3021
3393
  mkdirSync(cacheDir, { recursive: true });
3022
3394
  }
@@ -3075,8 +3447,8 @@ var evaluate_cmd_exports = {};
3075
3447
  __export(evaluate_cmd_exports, {
3076
3448
  evaluateCommand: () => evaluateCommand
3077
3449
  });
3078
- import { join as join9, basename } from "path";
3079
- import fs11 from "fs-extra";
3450
+ import { join as join10, basename } from "path";
3451
+ import fs12 from "fs-extra";
3080
3452
  async function evaluateCommand(cwd, options) {
3081
3453
  const useCache = options.cache !== false;
3082
3454
  let llmOutput = useCache ? getCachedResult(cwd) : null;
@@ -3096,17 +3468,17 @@ async function evaluateCommand(cwd, options) {
3096
3468
  const config = await loadConfig(cwd);
3097
3469
  const projectName = config?.projectName ?? basename(cwd);
3098
3470
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3099
- const reportDir = join9(cwd, "docs", "p-reports");
3100
- const reportPath = join9(reportDir, `${today}.md`);
3101
- await fs11.ensureDir(reportDir);
3471
+ const reportDir = join10(cwd, "docs", "p-reports");
3472
+ const reportPath = join10(reportDir, `${today}.md`);
3473
+ await fs12.ensureDir(reportDir);
3102
3474
  const detailedReport = buildDetailedReport(result, projectName);
3103
- await fs11.writeFile(reportPath, detailedReport, "utf8");
3475
+ await fs12.writeFile(reportPath, detailedReport, "utf8");
3104
3476
  console.log("");
3105
3477
  success(`\uB9AC\uD3EC\uD2B8 \uC800\uC7A5: docs/p-reports/${today}.md`);
3106
3478
  console.log("");
3107
3479
  console.log(detailedReport);
3108
3480
  if (options.output) {
3109
- await fs11.writeFile(options.output, detailedReport, "utf8");
3481
+ await fs12.writeFile(options.output, detailedReport, "utf8");
3110
3482
  success(`\uCD94\uAC00 \uC800\uC7A5: ${options.output}`);
3111
3483
  }
3112
3484
  if (options.failUnder && result.totalScore < options.failUnder) {
@@ -3234,8 +3606,8 @@ var init_cmd_exports = {};
3234
3606
  __export(init_cmd_exports, {
3235
3607
  initCommand: () => initCommand
3236
3608
  });
3237
- import { join as join10, basename as basename2 } from "path";
3238
- import fs12 from "fs-extra";
3609
+ import { join as join11, basename as basename2 } from "path";
3610
+ import fs13 from "fs-extra";
3239
3611
  async function initCommand(cwd, nameArg) {
3240
3612
  printBanner();
3241
3613
  const { isWindows: isWindows2, diagnoseWindowsEnv: diagnoseWindowsEnv2 } = await Promise.resolve().then(() => (init_platform(), platform_exports));
@@ -3293,11 +3665,11 @@ async function initCommand(cwd, nameArg) {
3293
3665
  const evalResult = computeResult2(llmOutput);
3294
3666
  printReport2(evalResult);
3295
3667
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3296
- const reportDir = join10(cwd, "docs", "p-reports");
3297
- await fs12.ensureDir(reportDir);
3668
+ const reportDir = join11(cwd, "docs", "p-reports");
3669
+ await fs13.ensureDir(reportDir);
3298
3670
  const legacyName = basename2(cwd);
3299
3671
  const detailedReport = buildDetailedReport3(evalResult, legacyName);
3300
- await fs12.writeFile(join10(reportDir, `${today}.md`), detailedReport, "utf8");
3672
+ await fs13.writeFile(join11(reportDir, `${today}.md`), detailedReport, "utf8");
3301
3673
  console.log("");
3302
3674
  success(`\uB9AC\uD3EC\uD2B8 \uC800\uC7A5: docs/p-reports/${today}.md`);
3303
3675
  } catch {
@@ -3342,8 +3714,8 @@ async function initCommand(cwd, nameArg) {
3342
3714
  }]);
3343
3715
  projectName = answer.name.trim();
3344
3716
  }
3345
- const projectDir = join10(cwd, projectName);
3346
- if (await fs12.pathExists(projectDir)) {
3717
+ const projectDir = join11(cwd, projectName);
3718
+ if (await fs13.pathExists(projectDir)) {
3347
3719
  const existingConfig = await loadConfig(projectDir);
3348
3720
  if (existingConfig) {
3349
3721
  console.log("");
@@ -3365,7 +3737,7 @@ async function initCommand(cwd, nameArg) {
3365
3737
  return;
3366
3738
  }
3367
3739
  } else {
3368
- await fs12.ensureDir(projectDir);
3740
+ await fs13.ensureDir(projectDir);
3369
3741
  success(`${projectName}/ \uD3F4\uB354 \uC0DD\uC131`);
3370
3742
  }
3371
3743
  await setupInDirectory(projectDir, projectName);
@@ -3432,6 +3804,9 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
3432
3804
  if (extraTools.includes("supabase")) {
3433
3805
  console.log(` ${chalk8.cyan("Supabase")} DB + \uC778\uC99D + \uC2A4\uD1A0\uB9AC\uC9C0 (PostgreSQL \uAE30\uBC18)`);
3434
3806
  }
3807
+ if (extraTools.includes("mcp")) {
3808
+ console.log(` ${chalk8.cyan("MCP \uC11C\uBC84")} AI \uB3C4\uAD6C \u2014 Claude\uAC00 \uD638\uCD9C\uD560 \uCEE4\uC2A4\uD140 \uAE30\uB2A5/\uB370\uC774\uD130`);
3809
+ }
3435
3810
  console.log("");
3436
3811
  console.log(colors.accent(" \uC124\uCE58 \uD655\uC778"));
3437
3812
  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"));
@@ -3443,7 +3818,7 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
3443
3818
  { label: "\uC2AC\uB798\uC2DC \uCEE4\uB9E8\uB4DC", path: ".claude/skills/pai/SKILL.md" }
3444
3819
  ];
3445
3820
  for (const check of checks) {
3446
- const exists = await fs12.pathExists(join10(projectDir, check.path));
3821
+ const exists = await fs13.pathExists(join11(projectDir, check.path));
3447
3822
  console.log(` ${exists ? colors.success("\u2713") : colors.err("\u2717")} ${check.label.padEnd(16)} ${colors.dim(check.path)}`);
3448
3823
  }
3449
3824
  console.log("");
@@ -3468,10 +3843,10 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
3468
3843
  printReport2(evalResult);
3469
3844
  await sleep2(500);
3470
3845
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3471
- const reportDir = join10(projectDir, "docs", "p-reports");
3472
- await fs12.ensureDir(reportDir);
3846
+ const reportDir = join11(projectDir, "docs", "p-reports");
3847
+ await fs13.ensureDir(reportDir);
3473
3848
  const detailedReport = buildDetailedReport3(evalResult, projectName);
3474
- await fs12.writeFile(join10(reportDir, `${today}.md`), detailedReport, "utf8");
3849
+ await fs13.writeFile(join11(reportDir, `${today}.md`), detailedReport, "utf8");
3475
3850
  await sleep2(500);
3476
3851
  console.log("");
3477
3852
  success(`\uB9AC\uD3EC\uD2B8 \uC800\uC7A5: docs/p-reports/${today}.md`);
@@ -3499,7 +3874,7 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
3499
3874
  const shellRc = getShellRcPath2();
3500
3875
  let hasYoloAliasSet = false;
3501
3876
  try {
3502
- const rcContent = await fs12.readFile(shellRc, "utf8");
3877
+ const rcContent = await fs13.readFile(shellRc, "utf8");
3503
3878
  hasYoloAliasSet = checkYolo(rcContent);
3504
3879
  } catch {
3505
3880
  }
@@ -3520,10 +3895,10 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
3520
3895
  const { getYoloAliasLine: getYoloAliasLine2 } = await Promise.resolve().then(() => (init_platform(), platform_exports));
3521
3896
  const aliasLine = getYoloAliasLine2();
3522
3897
  try {
3523
- const rcContent = await fs12.readFile(shellRc, "utf8").catch(() => "");
3898
+ const rcContent = await fs13.readFile(shellRc, "utf8").catch(() => "");
3524
3899
  if (!rcContent.includes("claude-yolo")) {
3525
- await fs12.ensureDir(join10(shellRc, ".."));
3526
- await fs12.appendFile(shellRc, `
3900
+ await fs13.ensureDir(join11(shellRc, ".."));
3901
+ await fs13.appendFile(shellRc, `
3527
3902
  # PAI \u2014 claude-YOLO mode
3528
3903
  ${aliasLine}
3529
3904
  `);
@@ -3754,9 +4129,9 @@ async function installOrchestratorOnly(projectDir, projectName) {
3754
4129
  const evalResult = computeResult2(llmOutput);
3755
4130
  printReport2(evalResult);
3756
4131
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3757
- const reportDir = join10(projectDir, "docs", "p-reports");
3758
- await fs12.ensureDir(reportDir);
3759
- await fs12.writeFile(join10(reportDir, `${today}.md`), buildDetailedReport3(evalResult, projectName), "utf8");
4132
+ const reportDir = join11(projectDir, "docs", "p-reports");
4133
+ await fs13.ensureDir(reportDir);
4134
+ await fs13.writeFile(join11(reportDir, `${today}.md`), buildDetailedReport3(evalResult, projectName), "utf8");
3760
4135
  console.log("");
3761
4136
  hint(`\uC0C1\uC138 \uB9AC\uD3EC\uD2B8: docs/p-reports/${today}.md`);
3762
4137
  } catch {
@@ -3806,7 +4181,7 @@ async function detectLegacyProject(cwd) {
3806
4181
  ".gitignore"
3807
4182
  ];
3808
4183
  for (const signal of signals) {
3809
- if (await fs12.pathExists(join10(cwd, signal))) return true;
4184
+ if (await fs13.pathExists(join11(cwd, signal))) return true;
3810
4185
  }
3811
4186
  return false;
3812
4187
  }
@@ -3823,17 +4198,17 @@ var init_init_cmd = __esm({
3823
4198
  });
3824
4199
 
3825
4200
  // src/stages/design/openspec.ts
3826
- import { join as join11 } from "path";
3827
- import fs13 from "fs-extra";
4201
+ import { join as join12 } from "path";
4202
+ import fs14 from "fs-extra";
3828
4203
  async function initOpenSpec(cwd, projectName) {
3829
- const docsDir = join11(cwd, "docs");
3830
- await fs13.ensureDir(docsDir);
3831
- const openspecPath = join11(docsDir, "openspec.md");
3832
- if (await fs13.pathExists(openspecPath)) {
4204
+ const docsDir = join12(cwd, "docs");
4205
+ await fs14.ensureDir(docsDir);
4206
+ const openspecPath = join12(docsDir, "openspec.md");
4207
+ if (await fs14.pathExists(openspecPath)) {
3833
4208
  info("docs/openspec.md \uC774\uBBF8 \uC874\uC7AC \u2014 \uAC74\uB108\uB700");
3834
4209
  return;
3835
4210
  }
3836
- await fs13.writeFile(openspecPath, [
4211
+ await fs14.writeFile(openspecPath, [
3837
4212
  `# OpenSpec \u2014 ${projectName}`,
3838
4213
  "",
3839
4214
  "## 1. \uBAA9\uC801 (Purpose)",
@@ -3861,13 +4236,13 @@ async function initOpenSpec(cwd, projectName) {
3861
4236
  }
3862
4237
  async function validateOpenSpec(cwd) {
3863
4238
  const candidates = [
3864
- join11(cwd, "docs", "openspec.md"),
3865
- join11(cwd, "openspec.md"),
3866
- join11(cwd, ".pai", "openspec.md")
4239
+ join12(cwd, "docs", "openspec.md"),
4240
+ join12(cwd, "openspec.md"),
4241
+ join12(cwd, ".pai", "openspec.md")
3867
4242
  ];
3868
4243
  let specPath = null;
3869
4244
  for (const p of candidates) {
3870
- if (await fs13.pathExists(p)) {
4245
+ if (await fs14.pathExists(p)) {
3871
4246
  specPath = p;
3872
4247
  break;
3873
4248
  }
@@ -3881,7 +4256,7 @@ async function validateOpenSpec(cwd) {
3881
4256
  warnings: ["openspec.md \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `pai design init` \uC744 \uC2E4\uD589\uD558\uC138\uC694."]
3882
4257
  };
3883
4258
  }
3884
- const content = await fs13.readFile(specPath, "utf8");
4259
+ const content = await fs14.readFile(specPath, "utf8");
3885
4260
  const missing = [];
3886
4261
  let filled = 0;
3887
4262
  for (const section2 of REQUIRED_SECTIONS) {
@@ -3929,17 +4304,17 @@ var init_openspec = __esm({
3929
4304
  });
3930
4305
 
3931
4306
  // src/stages/design/omc.ts
3932
- import { join as join12 } from "path";
3933
- import fs14 from "fs-extra";
4307
+ import { join as join13 } from "path";
4308
+ import fs15 from "fs-extra";
3934
4309
  async function initOMC(cwd, projectName) {
3935
- const paiDir = join12(cwd, ".pai");
3936
- await fs14.ensureDir(paiDir);
3937
- const omcPath = join12(paiDir, "omc.md");
3938
- if (await fs14.pathExists(omcPath)) {
4310
+ const paiDir = join13(cwd, ".pai");
4311
+ await fs15.ensureDir(paiDir);
4312
+ const omcPath = join13(paiDir, "omc.md");
4313
+ if (await fs15.pathExists(omcPath)) {
3939
4314
  info(".pai/omc.md \uC774\uBBF8 \uC874\uC7AC \u2014 \uAC74\uB108\uB700");
3940
4315
  return;
3941
4316
  }
3942
- await fs14.writeFile(omcPath, [
4317
+ await fs15.writeFile(omcPath, [
3943
4318
  `# OMC \u2014 Object Model Context (${projectName})`,
3944
4319
  "",
3945
4320
  "> AI\uAC00 \uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uB3C4\uBA54\uC778\uC744 \uC774\uD574\uD558\uAE30 \uC704\uD55C \uD575\uC2EC \uAC1D\uCCB4 \uBAA8\uB378",
@@ -4021,25 +4396,25 @@ var init_design_cmd = __esm({
4021
4396
  });
4022
4397
 
4023
4398
  // src/stages/validation/runner.ts
4024
- import { join as join13 } from "path";
4025
- import fs15 from "fs-extra";
4399
+ import { join as join14 } from "path";
4400
+ import fs16 from "fs-extra";
4026
4401
  async function runTests(cwd) {
4027
4402
  const start = Date.now();
4028
- const gstackPath = join13(cwd, ".pai", "gstack.json");
4403
+ const gstackPath = join14(cwd, ".pai", "gstack.json");
4029
4404
  let runner = "npm test";
4030
- if (await fs15.pathExists(gstackPath)) {
4405
+ if (await fs16.pathExists(gstackPath)) {
4031
4406
  try {
4032
- const config = await fs15.readJson(gstackPath);
4407
+ const config = await fs16.readJson(gstackPath);
4033
4408
  if (config.testRunner === "vitest") runner = "npx vitest run";
4034
4409
  else if (config.testRunner === "jest") runner = "npx jest";
4035
4410
  else if (config.testRunner === "mocha") runner = "npx mocha";
4036
4411
  } catch {
4037
4412
  }
4038
4413
  }
4039
- const pkgPath = join13(cwd, "package.json");
4040
- if (await fs15.pathExists(pkgPath)) {
4414
+ const pkgPath = join14(cwd, "package.json");
4415
+ if (await fs16.pathExists(pkgPath)) {
4041
4416
  try {
4042
- const pkg4 = await fs15.readJson(pkgPath);
4417
+ const pkg4 = await fs16.readJson(pkgPath);
4043
4418
  if (!pkg4.scripts?.test || pkg4.scripts.test.includes("no test specified")) {
4044
4419
  return {
4045
4420
  runner,
@@ -4081,16 +4456,16 @@ var init_runner = __esm({
4081
4456
  });
4082
4457
 
4083
4458
  // src/stages/validation/harness.ts
4084
- import { join as join14 } from "path";
4085
- import fs16 from "fs-extra";
4459
+ import { join as join15 } from "path";
4460
+ import fs17 from "fs-extra";
4086
4461
  async function runHarnessCheck(cwd) {
4087
- const harnessPath = join14(cwd, ".pai", "harness.json");
4088
- if (!await fs16.pathExists(harnessPath)) {
4462
+ const harnessPath = join15(cwd, ".pai", "harness.json");
4463
+ if (!await fs17.pathExists(harnessPath)) {
4089
4464
  return { enabled: false, specFile: null, rules: [], checks: [] };
4090
4465
  }
4091
4466
  let config;
4092
4467
  try {
4093
- config = await fs16.readJson(harnessPath);
4468
+ config = await fs17.readJson(harnessPath);
4094
4469
  } catch {
4095
4470
  return { enabled: false, specFile: null, rules: [], checks: [] };
4096
4471
  }
@@ -4098,8 +4473,8 @@ async function runHarnessCheck(cwd) {
4098
4473
  const rules = config.rules ?? [];
4099
4474
  const checks = [];
4100
4475
  if (rules.includes("spec-implementation-match")) {
4101
- const specExists = await fs16.pathExists(join14(cwd, specFile));
4102
- const srcExists = await fs16.pathExists(join14(cwd, "src"));
4476
+ const specExists = await fs17.pathExists(join15(cwd, specFile));
4477
+ const srcExists = await fs17.pathExists(join15(cwd, "src"));
4103
4478
  checks.push({
4104
4479
  rule: "spec-implementation-match",
4105
4480
  passed: specExists && srcExists,
@@ -4107,8 +4482,8 @@ async function runHarnessCheck(cwd) {
4107
4482
  });
4108
4483
  }
4109
4484
  if (rules.includes("api-contract-test")) {
4110
- const testDir = await fs16.pathExists(join14(cwd, "tests"));
4111
- const testDir2 = await fs16.pathExists(join14(cwd, "test"));
4485
+ const testDir = await fs17.pathExists(join15(cwd, "tests"));
4486
+ const testDir2 = await fs17.pathExists(join15(cwd, "test"));
4112
4487
  checks.push({
4113
4488
  rule: "api-contract-test",
4114
4489
  passed: testDir || testDir2,
@@ -4198,14 +4573,14 @@ var init_context = __esm({
4198
4573
  });
4199
4574
 
4200
4575
  // src/stages/design/index.ts
4201
- import { join as join15 } from "path";
4202
- import fs17 from "fs-extra";
4576
+ import { join as join16 } from "path";
4577
+ import fs18 from "fs-extra";
4203
4578
  async function autoInstallHarness(cwd) {
4204
- const harnessPath = join15(cwd, ".pai", "harness.json");
4205
- if (await fs17.pathExists(harnessPath)) return;
4579
+ const harnessPath = join16(cwd, ".pai", "harness.json");
4580
+ if (await fs18.pathExists(harnessPath)) return;
4206
4581
  await withSpinner("Harness Engineering \uC790\uB3D9 \uC124\uC815 \uC911...", async () => {
4207
- await fs17.ensureDir(join15(cwd, ".pai"));
4208
- await fs17.writeJson(harnessPath, {
4582
+ await fs18.ensureDir(join16(cwd, ".pai"));
4583
+ await fs18.writeJson(harnessPath, {
4209
4584
  version: "1.0",
4210
4585
  specFile: "docs/openspec.md",
4211
4586
  checkOn: ["pre-commit", "ci"],
@@ -4550,7 +4925,7 @@ __export(remove_cmd_exports, {
4550
4925
  removeCommand: () => removeCommand
4551
4926
  });
4552
4927
  import { basename as basename4, dirname } from "path";
4553
- import fs18 from "fs-extra";
4928
+ import fs19 from "fs-extra";
4554
4929
  async function removeCommand(cwd, options) {
4555
4930
  section("\uD504\uB85C\uC81D\uD2B8 \uC0AD\uC81C");
4556
4931
  const config = await loadConfig(cwd);
@@ -4568,7 +4943,7 @@ async function removeCommand(cwd, options) {
4568
4943
  console.log(colors.err(` ${folderName}/ \uD3F4\uB354 \uC804\uCCB4\uAC00 \uC0AD\uC81C\uB429\uB2C8\uB2E4.`));
4569
4944
  hint("\uC774 \uC791\uC5C5\uC740 \uB418\uB3CC\uB9B4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
4570
4945
  console.log("");
4571
- const items = await fs18.readdir(cwd);
4946
+ const items = await fs19.readdir(cwd);
4572
4947
  const fileCount = items.filter((i) => !i.startsWith(".")).length;
4573
4948
  const hiddenCount = items.filter((i) => i.startsWith(".")).length;
4574
4949
  info(`\uD30C\uC77C/\uD3F4\uB354 ${fileCount}\uAC1C, \uC228\uAE40 \uD56D\uBAA9 ${hiddenCount}\uAC1C`);
@@ -4587,7 +4962,7 @@ async function removeCommand(cwd, options) {
4587
4962
  }
4588
4963
  process.chdir(parentDir);
4589
4964
  try {
4590
- await fs18.remove(cwd);
4965
+ await fs19.remove(cwd);
4591
4966
  console.log("");
4592
4967
  success(`${folderName}/ \uD504\uB85C\uC81D\uD2B8\uAC00 \uC0AD\uC81C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.`);
4593
4968
  try {
@@ -4740,8 +5115,8 @@ var savetoken_cmd_exports = {};
4740
5115
  __export(savetoken_cmd_exports, {
4741
5116
  savetokenCommand: () => savetokenCommand
4742
5117
  });
4743
- import { join as join16, relative } from "path";
4744
- import fs19 from "fs-extra";
5118
+ import { join as join17, relative } from "path";
5119
+ import fs20 from "fs-extra";
4745
5120
  import chalk6 from "chalk";
4746
5121
  async function savetokenCommand(cwd) {
4747
5122
  const { createSpinner: createSpinner2 } = await Promise.resolve().then(() => (init_progress(), progress_exports));
@@ -4810,11 +5185,11 @@ async function savetokenCommand(cwd) {
4810
5185
  console.log(` \u2192 ${colors.dim("\uB8F0 \uAE30\uBC18 \uB85C\uC9C1, \uD0A4\uC6CC\uB4DC \uB9E4\uCE6D\uC73C\uB85C \uAC80\uD1A0 \uD6C4 \uB300\uCCB4")}`);
4811
5186
  console.log(` ${chalk6.red("\u25CF")} \uB192\uC74C \uCF54\uB4DC \uC0DD\uC131, \uBCF5\uC7A1\uD55C \uCD94\uB860, \uCC3D\uC758\uC801 \uC0DD\uC131`);
4812
5187
  console.log(` \u2192 ${colors.dim("AI \uD544\uC218 \u2014 \uD504\uB86C\uD504\uD2B8 \uCD5C\uC801\uD654\uB85C \uD1A0\uD070 \uC808\uAC10")}`);
4813
- const reportDir = join16(cwd, ".pai");
4814
- await fs19.ensureDir(reportDir);
5188
+ const reportDir = join17(cwd, ".pai");
5189
+ await fs20.ensureDir(reportDir);
4815
5190
  const report = buildReport(callSites, cwd);
4816
- const reportPath = join16(reportDir, "savetoken-report.md");
4817
- await fs19.writeFile(reportPath, report, "utf8");
5191
+ const reportPath = join17(reportDir, "savetoken-report.md");
5192
+ await fs20.writeFile(reportPath, report, "utf8");
4818
5193
  console.log("");
4819
5194
  success("\uC2A4\uCE94 \uB9AC\uD3EC\uD2B8 \uC800\uC7A5: .pai/savetoken-report.md");
4820
5195
  console.log("");
@@ -4970,9 +5345,9 @@ var wakeup_cmd_exports = {};
4970
5345
  __export(wakeup_cmd_exports, {
4971
5346
  wakeupCommand: () => wakeupCommand
4972
5347
  });
4973
- import { join as join17 } from "path";
5348
+ import { join as join18 } from "path";
4974
5349
  import { homedir as homedir3, platform as osPlatform } from "os";
4975
- import fs20 from "fs-extra";
5350
+ import fs21 from "fs-extra";
4976
5351
  import chalk7 from "chalk";
4977
5352
  async function wakeupCommand(timeOrAction, schedule = "\uD3C9\uC77C") {
4978
5353
  if (timeOrAction === "off") {
@@ -5019,9 +5394,9 @@ async function wakeupCommand(timeOrAction, schedule = "\uD3C9\uC77C") {
5019
5394
  projectDir,
5020
5395
  launchMode
5021
5396
  };
5022
- await fs20.ensureDir(PAI_DIR2);
5023
- await fs20.writeJson(CONFIG_FILE2, config, { spaces: 2 });
5024
- await fs20.writeJson(MESSAGES_FILE, MESSAGES);
5397
+ await fs21.ensureDir(PAI_DIR2);
5398
+ await fs21.writeJson(CONFIG_FILE2, config, { spaces: 2 });
5399
+ await fs21.writeJson(MESSAGES_FILE, MESSAGES);
5025
5400
  await createWakeupScript(config);
5026
5401
  if (osPlatform() === "darwin") {
5027
5402
  await setupMacOS(config);
@@ -5051,8 +5426,8 @@ async function wakeupCommand(timeOrAction, schedule = "\uD3C9\uC77C") {
5051
5426
  async function setupMacOS(config) {
5052
5427
  const { execa } = await import("execa");
5053
5428
  const [hour, minute] = config.time.split(":").map(Number);
5054
- const plistDir = join17(homedir3(), "Library", "LaunchAgents");
5055
- await fs20.ensureDir(plistDir);
5429
+ const plistDir = join18(homedir3(), "Library", "LaunchAgents");
5430
+ await fs21.ensureDir(plistDir);
5056
5431
  const weekdays = scheduleToWeekdays(config.schedule);
5057
5432
  let calendarEntries;
5058
5433
  if (weekdays.length === 7) {
@@ -5088,7 +5463,7 @@ ${calendarEntries}
5088
5463
  <string>${PAI_DIR2}/wakeup.log</string>
5089
5464
  </dict>
5090
5465
  </plist>`;
5091
- await fs20.writeFile(PLIST_PATH, plist);
5466
+ await fs21.writeFile(PLIST_PATH, plist);
5092
5467
  await execa("launchctl", ["unload", PLIST_PATH]).catch(() => {
5093
5468
  });
5094
5469
  await execa("launchctl", ["load", PLIST_PATH]);
@@ -5110,9 +5485,9 @@ ${calendarEntries}
5110
5485
  async function setupWindows(config) {
5111
5486
  const { execa } = await import("execa");
5112
5487
  const [hour, minute] = config.time.split(":").map(Number);
5113
- const psScriptDir = join17(homedir3(), ".pai");
5114
- await fs20.ensureDir(psScriptDir);
5115
- const psScriptPath = join17(psScriptDir, "wakeup.ps1");
5488
+ const psScriptDir = join18(homedir3(), ".pai");
5489
+ await fs21.ensureDir(psScriptDir);
5490
+ const psScriptPath = join18(psScriptDir, "wakeup.ps1");
5116
5491
  const claudeCmd = config.launchMode === "yolo" ? "claude --dangerously-skip-permissions" : "claude";
5117
5492
  const psScript = `# PAI Wakeup \u2014 Claude Code \uC138\uC158 \uC790\uB3D9 \uC2DC\uC791
5118
5493
  $paiDir = "$env:USERPROFILE\\.pai"
@@ -5141,7 +5516,7 @@ $notifier.Show([Windows.UI.Notifications.ToastNotification]::new($xml))
5141
5516
  # Open PowerShell with Claude Code
5142
5517
  Start-Process powershell -ArgumentList "-NoExit", "-Command", "Get-Content '$todayFile'; Write-Host ''; Set-Location '${config.projectDir}'; ${claudeCmd}"
5143
5518
  `;
5144
- await fs20.writeFile(psScriptPath, psScript, "utf8");
5519
+ await fs21.writeFile(psScriptPath, psScript, "utf8");
5145
5520
  const daysMap = {
5146
5521
  "\uD3C9\uC77C": "MON,TUE,WED,THU,FRI",
5147
5522
  "\uB9E4\uC77C": "MON,TUE,WED,THU,FRI,SAT,SUN",
@@ -5189,7 +5564,7 @@ async function disableWakeup() {
5189
5564
  if (osPlatform() === "darwin") {
5190
5565
  await execa("launchctl", ["unload", PLIST_PATH]).catch(() => {
5191
5566
  });
5192
- await fs20.remove(PLIST_PATH).catch(() => {
5567
+ await fs21.remove(PLIST_PATH).catch(() => {
5193
5568
  });
5194
5569
  success("launchd \uC2A4\uCF00\uC904 \uC81C\uAC70");
5195
5570
  console.log("");
@@ -5210,14 +5585,14 @@ async function disableWakeup() {
5210
5585
  } else {
5211
5586
  await removeCronEntry();
5212
5587
  }
5213
- await fs20.remove(CONFIG_FILE2).catch(() => {
5588
+ await fs21.remove(CONFIG_FILE2).catch(() => {
5214
5589
  });
5215
5590
  console.log("");
5216
5591
  success("\u2600\uFE0F \uC6E8\uC774\uD06C\uC5C5 \uD574\uC81C \uC644\uB8CC");
5217
5592
  }
5218
5593
  async function showStatus() {
5219
- if (await fs20.pathExists(CONFIG_FILE2)) {
5220
- const config = await fs20.readJson(CONFIG_FILE2);
5594
+ if (await fs21.pathExists(CONFIG_FILE2)) {
5595
+ const config = await fs21.readJson(CONFIG_FILE2);
5221
5596
  console.log("");
5222
5597
  success("\u2600\uFE0F \uC6E8\uC774\uD06C\uC5C5 \uD65C\uC131\uD654");
5223
5598
  console.log(` \uC2DC\uAC04 ${chalk7.white(config.time)}`);
@@ -5225,7 +5600,7 @@ async function showStatus() {
5225
5600
  console.log(` \uD504\uB85C\uC81D\uD2B8 ${chalk7.white(config.projectDir)}`);
5226
5601
  console.log(` \uBAA8\uB4DC ${chalk7.white(config.launchMode === "yolo" ? "claude-YOLO mode" : "\uC77C\uBC18 \uBAA8\uB4DC")}`);
5227
5602
  if (osPlatform() === "darwin") {
5228
- const plistExists = await fs20.pathExists(PLIST_PATH);
5603
+ const plistExists = await fs21.pathExists(PLIST_PATH);
5229
5604
  console.log(` launchd ${plistExists ? chalk7.green("\uD65C\uC131") : chalk7.red("\uBE44\uD65C\uC131")}`);
5230
5605
  }
5231
5606
  console.log("");
@@ -5378,7 +5753,7 @@ fi
5378
5753
 
5379
5754
  echo "[$(date)] PAI Wakeup completed" >> "$LOG_FILE"
5380
5755
  `;
5381
- await fs20.writeFile(SCRIPT_FILE, script, { mode: 493 });
5756
+ await fs21.writeFile(SCRIPT_FILE, script, { mode: 493 });
5382
5757
  }
5383
5758
  var PAI_DIR2, CONFIG_FILE2, MESSAGES_FILE, SCRIPT_FILE, PLIST_NAME, PLIST_PATH, CRON_MARKER, MESSAGES;
5384
5759
  var init_wakeup_cmd = __esm({
@@ -5386,12 +5761,12 @@ var init_wakeup_cmd = __esm({
5386
5761
  "use strict";
5387
5762
  init_ui();
5388
5763
  init_logger();
5389
- PAI_DIR2 = join17(homedir3(), ".pai");
5390
- CONFIG_FILE2 = join17(PAI_DIR2, "wakeup-config.json");
5391
- MESSAGES_FILE = join17(PAI_DIR2, "wakeup-messages.json");
5392
- SCRIPT_FILE = join17(PAI_DIR2, "wakeup.sh");
5764
+ PAI_DIR2 = join18(homedir3(), ".pai");
5765
+ CONFIG_FILE2 = join18(PAI_DIR2, "wakeup-config.json");
5766
+ MESSAGES_FILE = join18(PAI_DIR2, "wakeup-messages.json");
5767
+ SCRIPT_FILE = join18(PAI_DIR2, "wakeup.sh");
5393
5768
  PLIST_NAME = "com.pai.wakeup";
5394
- PLIST_PATH = join17(homedir3(), "Library", "LaunchAgents", `${PLIST_NAME}.plist`);
5769
+ PLIST_PATH = join18(homedir3(), "Library", "LaunchAgents", `${PLIST_NAME}.plist`);
5395
5770
  CRON_MARKER = "# PAI-WAKEUP";
5396
5771
  MESSAGES = [
5397
5772
  `Here's to the crazy ones. The misfits. The rebels. The troublemakers.