@waniwani/cli 0.0.37 → 0.0.38

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/index.js CHANGED
@@ -868,7 +868,7 @@ async function requireSessionId() {
868
868
  const sessionId = await config.getSessionId();
869
869
  if (!sessionId) {
870
870
  throw new McpError(
871
- "No active session. Run 'waniwani mcp dev' to start development."
871
+ "No active session. Run 'waniwani mcp preview' to start development."
872
872
  );
873
873
  }
874
874
  return sessionId;
@@ -954,172 +954,138 @@ var deleteCommand = new Command5("delete").description("Delete the MCP (includes
954
954
  }
955
955
  });
956
956
 
957
- // src/commands/mcp/deploy.ts
958
- import { input } from "@inquirer/prompts";
957
+ // src/commands/mcp/file/index.ts
958
+ import { Command as Command9 } from "commander";
959
+
960
+ // src/commands/mcp/file/list.ts
961
+ import chalk5 from "chalk";
959
962
  import { Command as Command6 } from "commander";
960
963
  import ora3 from "ora";
961
-
962
- // src/lib/sync.ts
963
- import { existsSync as existsSync3 } from "fs";
964
- import { mkdir as mkdir2, readdir, readFile as readFile2, stat, writeFile as writeFile2 } from "fs/promises";
965
- import { dirname, join as join3, relative } from "path";
966
- import ignore from "ignore";
967
- var PROJECT_DIR = ".waniwani";
968
- async function findProjectRoot(startDir) {
969
- let current = startDir;
970
- const root = dirname(current);
971
- while (current !== root) {
972
- if (existsSync3(join3(current, PROJECT_DIR))) {
973
- return current;
964
+ var listCommand = new Command6("list").description("List files in the MCP sandbox").argument("[path]", "Directory path (defaults to /app)", "/app").option("--mcp-id <id>", "Specific MCP ID").action(async (path, options, command) => {
965
+ const globalOptions = command.optsWithGlobals();
966
+ const json = globalOptions.json ?? false;
967
+ try {
968
+ await requireMcpId(options.mcpId);
969
+ const sessionId = await requireSessionId();
970
+ const spinner = ora3(`Listing ${path}...`).start();
971
+ const result = await api.get(
972
+ `/api/mcp/sessions/${sessionId}/files/list?path=${encodeURIComponent(path)}`
973
+ );
974
+ spinner.stop();
975
+ if (json) {
976
+ formatOutput(result, true);
977
+ } else {
978
+ console.log(chalk5.bold(`
979
+ Directory: ${result.path}
980
+ `));
981
+ if (result.entries.length === 0) {
982
+ console.log(" (empty)");
983
+ } else {
984
+ const rows = result.entries.map((entry) => {
985
+ const name = entry.type === "directory" ? chalk5.blue(`${entry.name}/`) : entry.name;
986
+ const size = entry.type === "directory" ? chalk5.gray("<dir>") : formatSize(entry.size);
987
+ return [name, size];
988
+ });
989
+ formatTable(["Name", "Size"], rows, false);
990
+ }
991
+ console.log();
974
992
  }
975
- const parent = dirname(current);
976
- if (parent === current) break;
977
- current = parent;
978
- }
979
- if (existsSync3(join3(current, PROJECT_DIR))) {
980
- return current;
993
+ } catch (error) {
994
+ handleError(error, json);
995
+ process.exit(1);
981
996
  }
982
- return null;
997
+ });
998
+ function formatSize(bytes) {
999
+ if (bytes === void 0) return "";
1000
+ if (bytes < 1024) return `${bytes} B`;
1001
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1002
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
983
1003
  }
984
- var DEFAULT_IGNORE_PATTERNS = [
985
- ".waniwani",
986
- ".git",
987
- "node_modules",
988
- ".env",
989
- ".env.*",
990
- ".DS_Store",
991
- "*.log",
992
- ".cache",
993
- "dist",
994
- "coverage",
995
- ".turbo",
996
- ".next",
997
- ".nuxt",
998
- ".vercel"
999
- ];
1000
- async function loadIgnorePatterns(projectRoot) {
1001
- const ig = ignore();
1002
- ig.add(DEFAULT_IGNORE_PATTERNS);
1003
- const gitignorePath = join3(projectRoot, ".gitignore");
1004
- if (existsSync3(gitignorePath)) {
1005
- try {
1006
- const content = await readFile2(gitignorePath, "utf-8");
1007
- ig.add(content);
1008
- } catch {
1004
+
1005
+ // src/commands/mcp/file/read.ts
1006
+ import { writeFile as writeFile2 } from "fs/promises";
1007
+ import { Command as Command7 } from "commander";
1008
+ import ora4 from "ora";
1009
+ var readCommand = new Command7("read").description("Read a file from the MCP sandbox").argument("<path>", "Path in sandbox (e.g., /app/src/index.ts)").option("--mcp-id <id>", "Specific MCP ID").option("--output <file>", "Write to local file instead of stdout").option("--base64", "Output as base64 (for binary files)").action(async (path, options, command) => {
1010
+ const globalOptions = command.optsWithGlobals();
1011
+ const json = globalOptions.json ?? false;
1012
+ try {
1013
+ await requireMcpId(options.mcpId);
1014
+ const sessionId = await requireSessionId();
1015
+ const encoding = options.base64 ? "base64" : "utf8";
1016
+ const spinner = ora4(`Reading ${path}...`).start();
1017
+ const result = await api.get(
1018
+ `/api/mcp/sessions/${sessionId}/files?path=${encodeURIComponent(path)}&encoding=${encoding}`
1019
+ );
1020
+ spinner.stop();
1021
+ if (!result.exists) {
1022
+ throw new McpError(`File not found: ${path}`);
1009
1023
  }
1010
- }
1011
- return ig;
1012
- }
1013
- async function collectFiles(projectRoot) {
1014
- const ig = await loadIgnorePatterns(projectRoot);
1015
- const files = [];
1016
- async function walk(dir) {
1017
- const entries = await readdir(dir, { withFileTypes: true });
1018
- for (const entry of entries) {
1019
- const fullPath = join3(dir, entry.name);
1020
- const relativePath = relative(projectRoot, fullPath);
1021
- if (ig.ignores(relativePath)) {
1022
- continue;
1023
- }
1024
- if (entry.isDirectory()) {
1025
- await walk(fullPath);
1026
- } else if (entry.isFile()) {
1027
- try {
1028
- const content = await readFile2(fullPath);
1029
- const isBinary = isBinaryPath(fullPath) || detectBinary(content);
1030
- files.push({
1031
- path: relativePath,
1032
- content: isBinary ? content.toString("base64") : content.toString("utf8"),
1033
- encoding: isBinary ? "base64" : "utf8"
1034
- });
1035
- } catch {
1036
- }
1024
+ if (options.output) {
1025
+ const buffer = result.encoding === "base64" ? Buffer.from(result.content, "base64") : Buffer.from(result.content, "utf8");
1026
+ await writeFile2(options.output, buffer);
1027
+ if (json) {
1028
+ formatOutput({ path, savedTo: options.output }, true);
1029
+ } else {
1030
+ formatSuccess(`Saved to ${options.output}`, false);
1037
1031
  }
1038
- }
1039
- }
1040
- await walk(projectRoot);
1041
- return files;
1042
- }
1043
- async function pullFilesFromGithub(mcpId, targetDir) {
1044
- const result = await api.get(
1045
- `/api/mcp/repositories/${mcpId}/files`
1046
- );
1047
- const writtenFiles = [];
1048
- for (const file of result.files) {
1049
- const localPath = join3(targetDir, file.path);
1050
- const dir = dirname(localPath);
1051
- await mkdir2(dir, { recursive: true });
1052
- if (file.encoding === "base64") {
1053
- await writeFile2(localPath, Buffer.from(file.content, "base64"));
1032
+ } else if (json) {
1033
+ formatOutput(result, true);
1054
1034
  } else {
1055
- await writeFile2(localPath, file.content, "utf8");
1056
- }
1057
- writtenFiles.push(file.path);
1058
- }
1059
- return { count: writtenFiles.length, files: writtenFiles };
1060
- }
1061
- async function collectSingleFile(projectRoot, filePath) {
1062
- const fullPath = join3(projectRoot, filePath);
1063
- const relativePath = relative(projectRoot, fullPath);
1064
- if (!existsSync3(fullPath)) {
1065
- return null;
1066
- }
1067
- try {
1068
- const fileStat = await stat(fullPath);
1069
- if (!fileStat.isFile()) {
1070
- return null;
1035
+ process.stdout.write(result.content);
1036
+ if (!result.content.endsWith("\n")) {
1037
+ process.stdout.write("\n");
1038
+ }
1071
1039
  }
1072
- const content = await readFile2(fullPath);
1073
- const isBinary = isBinaryPath(fullPath) || detectBinary(content);
1074
- return {
1075
- path: relativePath,
1076
- content: isBinary ? content.toString("base64") : content.toString("utf8"),
1077
- encoding: isBinary ? "base64" : "utf8"
1078
- };
1079
- } catch {
1080
- return null;
1040
+ } catch (error) {
1041
+ handleError(error, json);
1042
+ process.exit(1);
1081
1043
  }
1082
- }
1044
+ });
1083
1045
 
1084
- // src/commands/mcp/deploy.ts
1085
- var deployCommand = new Command6("deploy").description("Push local files to GitHub and trigger deployment").option("-m, --message <msg>", "Commit message").option("--mcp-id <id>", "Specific MCP ID").action(async (options, command) => {
1046
+ // src/commands/mcp/file/write.ts
1047
+ import { readFile as readFile2 } from "fs/promises";
1048
+ import { Command as Command8 } from "commander";
1049
+ import ora5 from "ora";
1050
+ var writeCommand = new Command8("write").description("Write a file to the MCP sandbox").argument("<path>", "Path in sandbox (e.g., /app/src/index.ts)").option("--mcp-id <id>", "Specific MCP ID").option("--content <content>", "Content to write").option("--file <localFile>", "Local file to upload").option("--base64", "Treat content as base64 encoded").action(async (path, options, command) => {
1086
1051
  const globalOptions = command.optsWithGlobals();
1087
1052
  const json = globalOptions.json ?? false;
1088
1053
  try {
1089
- const mcpId = await requireMcpId(options.mcpId);
1090
- const projectRoot = await findProjectRoot(process.cwd());
1091
- if (!projectRoot) {
1054
+ await requireMcpId(options.mcpId);
1055
+ const sessionId = await requireSessionId();
1056
+ let content;
1057
+ let encoding = "utf8";
1058
+ if (options.content) {
1059
+ content = options.content;
1060
+ if (options.base64) {
1061
+ encoding = "base64";
1062
+ }
1063
+ } else if (options.file) {
1064
+ const fileBuffer = await readFile2(options.file);
1065
+ if (options.base64) {
1066
+ content = fileBuffer.toString("base64");
1067
+ encoding = "base64";
1068
+ } else {
1069
+ content = fileBuffer.toString("utf8");
1070
+ }
1071
+ } else {
1092
1072
  throw new CLIError(
1093
- "Not in a WaniWani project. Run 'waniwani mcp init <name>' first.",
1094
- "NOT_IN_PROJECT"
1073
+ "Either --content or --file is required",
1074
+ "MISSING_CONTENT"
1095
1075
  );
1096
1076
  }
1097
- let message = options.message;
1098
- if (!message) {
1099
- message = await input({
1100
- message: "Commit message:",
1101
- validate: (value) => value.trim() ? true : "Commit message is required"
1102
- });
1103
- }
1104
- const spinner = ora3("Collecting files...").start();
1105
- const files = await collectFiles(projectRoot);
1106
- if (files.length === 0) {
1107
- spinner.fail("No files to deploy");
1108
- return;
1109
- }
1110
- spinner.text = `Pushing ${files.length} files to GitHub...`;
1077
+ const spinner = ora5(`Writing ${path}...`).start();
1111
1078
  const result = await api.post(
1112
- `/api/mcp/repositories/${mcpId}/deploy`,
1113
- { files, message }
1079
+ `/api/mcp/sessions/${sessionId}/files`,
1080
+ {
1081
+ files: [{ path, content, encoding }]
1082
+ }
1114
1083
  );
1115
- spinner.succeed(`Pushed to GitHub (${result.commitSha.slice(0, 7)})`);
1084
+ spinner.succeed(`Wrote ${path}`);
1116
1085
  if (json) {
1117
1086
  formatOutput(result, true);
1118
1087
  } else {
1119
- console.log();
1120
- formatSuccess("Files pushed to GitHub!", false);
1121
- console.log();
1122
- console.log("Deployment will start automatically via webhook.");
1088
+ formatSuccess(`File written: ${path}`, false);
1123
1089
  }
1124
1090
  } catch (error) {
1125
1091
  handleError(error, json);
@@ -1127,296 +1093,36 @@ var deployCommand = new Command6("deploy").description("Push local files to GitH
1127
1093
  }
1128
1094
  });
1129
1095
 
1130
- // src/commands/mcp/dev.ts
1131
- import { watch } from "chokidar";
1132
- import { Command as Command7 } from "commander";
1133
- import ora4 from "ora";
1134
- var devCommand = new Command7("dev").description("Start live development with sandbox and file watching").option("--mcp-id <id>", "Specific MCP ID").option("--no-watch", "Skip file watching").option("--no-logs", "Don't stream logs to terminal").action(async (options, command) => {
1135
- const globalOptions = command.optsWithGlobals();
1136
- const json = globalOptions.json ?? false;
1137
- try {
1138
- const projectRoot = await findProjectRoot(process.cwd());
1139
- if (!projectRoot) {
1140
- throw new CLIError(
1141
- "Not in a WaniWani project. Run 'waniwani mcp init <name>' first.",
1142
- "NOT_IN_PROJECT"
1143
- );
1144
- }
1145
- let mcpId = options.mcpId;
1146
- if (!mcpId) {
1147
- mcpId = await config.getMcpId();
1148
- }
1149
- if (!mcpId) {
1150
- throw new CLIError(
1151
- "No MCP found. Run 'waniwani mcp init <name>' or use --mcp-id.",
1152
- "NO_MCP"
1153
- );
1154
- }
1155
- const spinner = ora4("Starting development environment...").start();
1156
- spinner.text = "Starting session...";
1157
- let sessionId;
1158
- try {
1159
- const sessionResponse = await api.post(
1160
- `/api/mcp/repositories/${mcpId}/session`,
1161
- {}
1162
- );
1163
- sessionId = sessionResponse.sandbox.id;
1164
- } catch {
1165
- const existing = await api.get(
1166
- `/api/mcp/repositories/${mcpId}/session`
1167
- );
1168
- if (!existing) {
1169
- throw new CLIError("Failed to start session", "SESSION_ERROR");
1170
- }
1171
- sessionId = existing.id;
1172
- }
1173
- await config.setSessionId(sessionId);
1174
- spinner.text = "Syncing files to sandbox...";
1175
- const files = await collectFiles(projectRoot);
1176
- if (files.length > 0) {
1177
- await api.post(
1178
- `/api/mcp/sessions/${sessionId}/files`,
1179
- { files }
1180
- );
1181
- }
1182
- spinner.text = "Starting MCP server...";
1183
- const serverResult = await api.post(
1184
- `/api/mcp/sessions/${sessionId}/server`,
1185
- { action: "start" }
1186
- );
1187
- spinner.succeed("Development environment ready");
1188
- console.log();
1189
- formatSuccess("Live preview started!", false);
1190
- console.log();
1191
- console.log(` Preview URL: ${serverResult.previewUrl}`);
1192
- console.log();
1193
- console.log(` MCP Inspector:`);
1194
- console.log(
1195
- ` npx @anthropic-ai/mcp-inspector@latest "${serverResult.previewUrl}/mcp"`
1196
- );
1197
- console.log();
1198
- if (options.watch !== false) {
1199
- console.log("Watching for file changes... (Ctrl+C to stop)");
1200
- console.log();
1201
- const ig = await loadIgnorePatterns(projectRoot);
1202
- const watcher = watch(projectRoot, {
1203
- ignored: (path) => {
1204
- const relative2 = path.replace(`${projectRoot}/`, "");
1205
- if (relative2 === path) return false;
1206
- return ig.ignores(relative2);
1207
- },
1208
- persistent: true,
1209
- ignoreInitial: true,
1210
- awaitWriteFinish: {
1211
- stabilityThreshold: 100,
1212
- pollInterval: 50
1213
- }
1214
- });
1215
- const syncFile = async (filePath) => {
1216
- const relativePath = filePath.replace(`${projectRoot}/`, "");
1217
- const file = await collectSingleFile(projectRoot, relativePath);
1218
- if (file) {
1219
- try {
1220
- await api.post(
1221
- `/api/mcp/sessions/${sessionId}/files`,
1222
- { files: [file] }
1223
- );
1224
- console.log(` Synced: ${relativePath}`);
1225
- } catch {
1226
- console.error(` Failed to sync: ${relativePath}`);
1227
- }
1228
- }
1229
- };
1230
- watcher.on("add", syncFile);
1231
- watcher.on("change", syncFile);
1232
- watcher.on("unlink", (filePath) => {
1233
- const relativePath = filePath.replace(`${projectRoot}/`, "");
1234
- console.log(` Deleted: ${relativePath}`);
1235
- });
1236
- process.on("SIGINT", async () => {
1237
- console.log();
1238
- console.log("Stopping development environment...");
1239
- await watcher.close();
1240
- process.exit(0);
1241
- });
1242
- await new Promise(() => {
1243
- });
1244
- }
1245
- } catch (error) {
1246
- handleError(error, json);
1247
- process.exit(1);
1248
- }
1249
- });
1250
-
1251
- // src/commands/mcp/file/index.ts
1252
- import { Command as Command11 } from "commander";
1253
-
1254
- // src/commands/mcp/file/list.ts
1255
- import chalk5 from "chalk";
1256
- import { Command as Command8 } from "commander";
1257
- import ora5 from "ora";
1258
- var listCommand = new Command8("list").description("List files in the MCP sandbox").argument("[path]", "Directory path (defaults to /app)", "/app").option("--mcp-id <id>", "Specific MCP ID").action(async (path, options, command) => {
1259
- const globalOptions = command.optsWithGlobals();
1260
- const json = globalOptions.json ?? false;
1261
- try {
1262
- await requireMcpId(options.mcpId);
1263
- const sessionId = await requireSessionId();
1264
- const spinner = ora5(`Listing ${path}...`).start();
1265
- const result = await api.get(
1266
- `/api/mcp/sessions/${sessionId}/files/list?path=${encodeURIComponent(path)}`
1267
- );
1268
- spinner.stop();
1269
- if (json) {
1270
- formatOutput(result, true);
1271
- } else {
1272
- console.log(chalk5.bold(`
1273
- Directory: ${result.path}
1274
- `));
1275
- if (result.entries.length === 0) {
1276
- console.log(" (empty)");
1277
- } else {
1278
- const rows = result.entries.map((entry) => {
1279
- const name = entry.type === "directory" ? chalk5.blue(`${entry.name}/`) : entry.name;
1280
- const size = entry.type === "directory" ? chalk5.gray("<dir>") : formatSize(entry.size);
1281
- return [name, size];
1282
- });
1283
- formatTable(["Name", "Size"], rows, false);
1284
- }
1285
- console.log();
1286
- }
1287
- } catch (error) {
1288
- handleError(error, json);
1289
- process.exit(1);
1290
- }
1291
- });
1292
- function formatSize(bytes) {
1293
- if (bytes === void 0) return "";
1294
- if (bytes < 1024) return `${bytes} B`;
1295
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1296
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1297
- }
1298
-
1299
- // src/commands/mcp/file/read.ts
1300
- import { writeFile as writeFile3 } from "fs/promises";
1301
- import { Command as Command9 } from "commander";
1302
- import ora6 from "ora";
1303
- var readCommand = new Command9("read").description("Read a file from the MCP sandbox").argument("<path>", "Path in sandbox (e.g., /app/src/index.ts)").option("--mcp-id <id>", "Specific MCP ID").option("--output <file>", "Write to local file instead of stdout").option("--base64", "Output as base64 (for binary files)").action(async (path, options, command) => {
1304
- const globalOptions = command.optsWithGlobals();
1305
- const json = globalOptions.json ?? false;
1306
- try {
1307
- await requireMcpId(options.mcpId);
1308
- const sessionId = await requireSessionId();
1309
- const encoding = options.base64 ? "base64" : "utf8";
1310
- const spinner = ora6(`Reading ${path}...`).start();
1311
- const result = await api.get(
1312
- `/api/mcp/sessions/${sessionId}/files?path=${encodeURIComponent(path)}&encoding=${encoding}`
1313
- );
1314
- spinner.stop();
1315
- if (!result.exists) {
1316
- throw new McpError(`File not found: ${path}`);
1317
- }
1318
- if (options.output) {
1319
- const buffer = result.encoding === "base64" ? Buffer.from(result.content, "base64") : Buffer.from(result.content, "utf8");
1320
- await writeFile3(options.output, buffer);
1321
- if (json) {
1322
- formatOutput({ path, savedTo: options.output }, true);
1323
- } else {
1324
- formatSuccess(`Saved to ${options.output}`, false);
1325
- }
1326
- } else if (json) {
1327
- formatOutput(result, true);
1328
- } else {
1329
- process.stdout.write(result.content);
1330
- if (!result.content.endsWith("\n")) {
1331
- process.stdout.write("\n");
1332
- }
1333
- }
1334
- } catch (error) {
1335
- handleError(error, json);
1336
- process.exit(1);
1337
- }
1338
- });
1339
-
1340
- // src/commands/mcp/file/write.ts
1341
- import { readFile as readFile3 } from "fs/promises";
1342
- import { Command as Command10 } from "commander";
1343
- import ora7 from "ora";
1344
- var writeCommand = new Command10("write").description("Write a file to the MCP sandbox").argument("<path>", "Path in sandbox (e.g., /app/src/index.ts)").option("--mcp-id <id>", "Specific MCP ID").option("--content <content>", "Content to write").option("--file <localFile>", "Local file to upload").option("--base64", "Treat content as base64 encoded").action(async (path, options, command) => {
1345
- const globalOptions = command.optsWithGlobals();
1346
- const json = globalOptions.json ?? false;
1347
- try {
1348
- await requireMcpId(options.mcpId);
1349
- const sessionId = await requireSessionId();
1350
- let content;
1351
- let encoding = "utf8";
1352
- if (options.content) {
1353
- content = options.content;
1354
- if (options.base64) {
1355
- encoding = "base64";
1356
- }
1357
- } else if (options.file) {
1358
- const fileBuffer = await readFile3(options.file);
1359
- if (options.base64) {
1360
- content = fileBuffer.toString("base64");
1361
- encoding = "base64";
1362
- } else {
1363
- content = fileBuffer.toString("utf8");
1364
- }
1365
- } else {
1366
- throw new CLIError(
1367
- "Either --content or --file is required",
1368
- "MISSING_CONTENT"
1369
- );
1370
- }
1371
- const spinner = ora7(`Writing ${path}...`).start();
1372
- const result = await api.post(
1373
- `/api/mcp/sessions/${sessionId}/files`,
1374
- {
1375
- files: [{ path, content, encoding }]
1376
- }
1377
- );
1378
- spinner.succeed(`Wrote ${path}`);
1379
- if (json) {
1380
- formatOutput(result, true);
1381
- } else {
1382
- formatSuccess(`File written: ${path}`, false);
1383
- }
1384
- } catch (error) {
1385
- handleError(error, json);
1386
- process.exit(1);
1387
- }
1388
- });
1389
-
1390
- // src/commands/mcp/file/index.ts
1391
- var fileCommand = new Command11("file").description("File operations in MCP sandbox").addCommand(readCommand).addCommand(writeCommand).addCommand(listCommand);
1392
-
1393
- // src/commands/mcp/init.ts
1394
- import { existsSync as existsSync4, mkdirSync } from "fs";
1395
- import { readFile as readFile4 } from "fs/promises";
1396
- import { join as join4 } from "path";
1397
- import { Command as Command12 } from "commander";
1398
- import ora8 from "ora";
1399
- async function loadParentConfig(cwd) {
1400
- const parentConfigPath = join4(cwd, LOCAL_CONFIG_DIR, CONFIG_FILE_NAME);
1401
- if (!existsSync4(parentConfigPath)) {
1402
- return null;
1403
- }
1404
- try {
1405
- const content = await readFile4(parentConfigPath, "utf-8");
1406
- const config2 = JSON.parse(content);
1407
- const { mcpId: _, sessionId: __, ...rest } = config2;
1408
- return rest;
1409
- } catch {
1410
- return null;
1411
- }
1412
- }
1413
- var initCommand = new Command12("init").description("Create a new MCP project").argument("<name>", "Name for the MCP project").action(async (name, _options, command) => {
1096
+ // src/commands/mcp/file/index.ts
1097
+ var fileCommand = new Command9("file").description("File operations in MCP sandbox").addCommand(readCommand).addCommand(writeCommand).addCommand(listCommand);
1098
+
1099
+ // src/commands/mcp/init.ts
1100
+ import { existsSync as existsSync3, mkdirSync } from "fs";
1101
+ import { readFile as readFile3 } from "fs/promises";
1102
+ import { join as join3 } from "path";
1103
+ import { Command as Command10 } from "commander";
1104
+ import ora6 from "ora";
1105
+ async function loadParentConfig(cwd) {
1106
+ const parentConfigPath = join3(cwd, LOCAL_CONFIG_DIR, CONFIG_FILE_NAME);
1107
+ if (!existsSync3(parentConfigPath)) {
1108
+ return null;
1109
+ }
1110
+ try {
1111
+ const content = await readFile3(parentConfigPath, "utf-8");
1112
+ const config2 = JSON.parse(content);
1113
+ const { mcpId: _, sessionId: __, ...rest } = config2;
1114
+ return rest;
1115
+ } catch {
1116
+ return null;
1117
+ }
1118
+ }
1119
+ var initCommand = new Command10("init").description("Create a new MCP project").argument("<name>", "Name for the MCP project").action(async (name, _options, command) => {
1414
1120
  const globalOptions = command.optsWithGlobals();
1415
1121
  const json = globalOptions.json ?? false;
1416
1122
  try {
1417
1123
  const cwd = process.cwd();
1418
- const projectDir = join4(cwd, name);
1419
- if (existsSync4(projectDir)) {
1124
+ const projectDir = join3(cwd, name);
1125
+ if (existsSync3(projectDir)) {
1420
1126
  if (json) {
1421
1127
  formatOutput(
1422
1128
  {
@@ -1430,7 +1136,7 @@ var initCommand = new Command12("init").description("Create a new MCP project").
1430
1136
  }
1431
1137
  process.exit(1);
1432
1138
  }
1433
- const spinner = ora8("Creating MCP...").start();
1139
+ const spinner = ora6("Creating MCP...").start();
1434
1140
  const result = await api.post("/api/mcp/repositories", {
1435
1141
  name
1436
1142
  });
@@ -1457,7 +1163,7 @@ var initCommand = new Command12("init").description("Create a new MCP project").
1457
1163
  console.log("Next steps:");
1458
1164
  console.log(` cd ${name}`);
1459
1165
  console.log(" waniwani mcp sync # Pull template files");
1460
- console.log(" waniwani mcp dev # Start developing");
1166
+ console.log(" waniwani mcp preview # Start developing");
1461
1167
  }
1462
1168
  } catch (error) {
1463
1169
  handleError(error, json);
@@ -1467,13 +1173,13 @@ var initCommand = new Command12("init").description("Create a new MCP project").
1467
1173
 
1468
1174
  // src/commands/mcp/list.ts
1469
1175
  import chalk6 from "chalk";
1470
- import { Command as Command13 } from "commander";
1471
- import ora9 from "ora";
1472
- var listCommand2 = new Command13("list").description("List all MCPs in your organization").action(async (_, command) => {
1176
+ import { Command as Command11 } from "commander";
1177
+ import ora7 from "ora";
1178
+ var listCommand2 = new Command11("list").description("List all MCPs in your organization").action(async (_, command) => {
1473
1179
  const globalOptions = command.optsWithGlobals();
1474
1180
  const json = globalOptions.json ?? false;
1475
1181
  try {
1476
- const spinner = ora9("Fetching MCPs...").start();
1182
+ const spinner = ora7("Fetching MCPs...").start();
1477
1183
  const mcps = await api.get(
1478
1184
  "/api/mcp/repositories"
1479
1185
  );
@@ -1527,10 +1233,10 @@ var listCommand2 = new Command13("list").description("List all MCPs in your orga
1527
1233
 
1528
1234
  // src/commands/mcp/logs.ts
1529
1235
  import chalk7 from "chalk";
1530
- import { Command as Command14 } from "commander";
1531
- import ora10 from "ora";
1532
- var logsCommand = new Command14("logs").description("Stream logs from the MCP server").argument("[cmdId]", "Command ID (defaults to running server)").option("--mcp-id <id>", "Specific MCP ID").option("-f, --follow", "Keep streaming logs (default)", true).option("--no-follow", "Fetch logs and exit").action(async (cmdIdArg, options, command) => {
1533
- const globalOptions = command.optsWithGlobals();
1236
+ import { Command as Command12 } from "commander";
1237
+ import ora8 from "ora";
1238
+ var logsCommand = new Command12("logs").description("Stream logs from the MCP server").argument("[cmdId]", "Command ID (defaults to running server)").option("--mcp-id <id>", "Specific MCP ID").option("-f, --follow", "Keep streaming logs (default)", true).option("--no-follow", "Fetch logs and exit").action(async (cmdIdArg, options, command) => {
1239
+ const globalOptions = command.optsWithGlobals();
1534
1240
  const json = globalOptions.json ?? false;
1535
1241
  let reader;
1536
1242
  const cleanup = () => {
@@ -1553,7 +1259,7 @@ var logsCommand = new Command14("logs").description("Stream logs from the MCP se
1553
1259
  }
1554
1260
  let cmdId = cmdIdArg;
1555
1261
  if (!cmdId) {
1556
- const spinner = ora10("Getting server status...").start();
1262
+ const spinner = ora8("Getting server status...").start();
1557
1263
  const status = await api.post(
1558
1264
  `/api/mcp/sessions/${sessionId}/server`,
1559
1265
  { action: "status" }
@@ -1561,7 +1267,7 @@ var logsCommand = new Command14("logs").description("Stream logs from the MCP se
1561
1267
  spinner.stop();
1562
1268
  if (!status.running || !status.cmdId) {
1563
1269
  throw new McpError(
1564
- "No server is running. Run 'waniwani mcp dev' first."
1270
+ "No server is running. Run 'waniwani mcp preview' first."
1565
1271
  );
1566
1272
  }
1567
1273
  cmdId = status.cmdId;
@@ -1668,6 +1374,300 @@ Error: ${event.error}`));
1668
1374
  }
1669
1375
  });
1670
1376
 
1377
+ // src/commands/mcp/preview.ts
1378
+ import { watch } from "chokidar";
1379
+ import { Command as Command13 } from "commander";
1380
+ import ora9 from "ora";
1381
+
1382
+ // src/lib/sync.ts
1383
+ import { existsSync as existsSync4 } from "fs";
1384
+ import { mkdir as mkdir2, readdir, readFile as readFile4, stat, writeFile as writeFile3 } from "fs/promises";
1385
+ import { dirname, join as join4, relative } from "path";
1386
+ import ignore from "ignore";
1387
+ var PROJECT_DIR = ".waniwani";
1388
+ async function findProjectRoot(startDir) {
1389
+ let current = startDir;
1390
+ const root = dirname(current);
1391
+ while (current !== root) {
1392
+ if (existsSync4(join4(current, PROJECT_DIR))) {
1393
+ return current;
1394
+ }
1395
+ const parent = dirname(current);
1396
+ if (parent === current) break;
1397
+ current = parent;
1398
+ }
1399
+ if (existsSync4(join4(current, PROJECT_DIR))) {
1400
+ return current;
1401
+ }
1402
+ return null;
1403
+ }
1404
+ var DEFAULT_IGNORE_PATTERNS = [
1405
+ ".waniwani",
1406
+ ".git",
1407
+ "node_modules",
1408
+ ".env",
1409
+ ".env.*",
1410
+ ".DS_Store",
1411
+ "*.log",
1412
+ ".cache",
1413
+ "dist",
1414
+ "coverage",
1415
+ ".turbo",
1416
+ ".next",
1417
+ ".nuxt",
1418
+ ".vercel"
1419
+ ];
1420
+ async function loadIgnorePatterns(projectRoot) {
1421
+ const ig = ignore();
1422
+ ig.add(DEFAULT_IGNORE_PATTERNS);
1423
+ const gitignorePath = join4(projectRoot, ".gitignore");
1424
+ if (existsSync4(gitignorePath)) {
1425
+ try {
1426
+ const content = await readFile4(gitignorePath, "utf-8");
1427
+ ig.add(content);
1428
+ } catch {
1429
+ }
1430
+ }
1431
+ return ig;
1432
+ }
1433
+ async function collectFiles(projectRoot) {
1434
+ const ig = await loadIgnorePatterns(projectRoot);
1435
+ const files = [];
1436
+ async function walk(dir) {
1437
+ const entries = await readdir(dir, { withFileTypes: true });
1438
+ for (const entry of entries) {
1439
+ const fullPath = join4(dir, entry.name);
1440
+ const relativePath = relative(projectRoot, fullPath);
1441
+ if (ig.ignores(relativePath)) {
1442
+ continue;
1443
+ }
1444
+ if (entry.isDirectory()) {
1445
+ await walk(fullPath);
1446
+ } else if (entry.isFile()) {
1447
+ try {
1448
+ const content = await readFile4(fullPath);
1449
+ const isBinary = isBinaryPath(fullPath) || detectBinary(content);
1450
+ files.push({
1451
+ path: relativePath,
1452
+ content: isBinary ? content.toString("base64") : content.toString("utf8"),
1453
+ encoding: isBinary ? "base64" : "utf8"
1454
+ });
1455
+ } catch {
1456
+ }
1457
+ }
1458
+ }
1459
+ }
1460
+ await walk(projectRoot);
1461
+ return files;
1462
+ }
1463
+ async function pullFilesFromGithub(mcpId, targetDir) {
1464
+ const result = await api.get(
1465
+ `/api/mcp/repositories/${mcpId}/files`
1466
+ );
1467
+ const writtenFiles = [];
1468
+ for (const file of result.files) {
1469
+ const localPath = join4(targetDir, file.path);
1470
+ const dir = dirname(localPath);
1471
+ await mkdir2(dir, { recursive: true });
1472
+ if (file.encoding === "base64") {
1473
+ await writeFile3(localPath, Buffer.from(file.content, "base64"));
1474
+ } else {
1475
+ await writeFile3(localPath, file.content, "utf8");
1476
+ }
1477
+ writtenFiles.push(file.path);
1478
+ }
1479
+ return { count: writtenFiles.length, files: writtenFiles };
1480
+ }
1481
+ async function collectSingleFile(projectRoot, filePath) {
1482
+ const fullPath = join4(projectRoot, filePath);
1483
+ const relativePath = relative(projectRoot, fullPath);
1484
+ if (!existsSync4(fullPath)) {
1485
+ return null;
1486
+ }
1487
+ try {
1488
+ const fileStat = await stat(fullPath);
1489
+ if (!fileStat.isFile()) {
1490
+ return null;
1491
+ }
1492
+ const content = await readFile4(fullPath);
1493
+ const isBinary = isBinaryPath(fullPath) || detectBinary(content);
1494
+ return {
1495
+ path: relativePath,
1496
+ content: isBinary ? content.toString("base64") : content.toString("utf8"),
1497
+ encoding: isBinary ? "base64" : "utf8"
1498
+ };
1499
+ } catch {
1500
+ return null;
1501
+ }
1502
+ }
1503
+
1504
+ // src/commands/mcp/preview.ts
1505
+ var previewCommand = new Command13("preview").description("Start live development with sandbox and file watching").option("--mcp-id <id>", "Specific MCP ID").option("--no-watch", "Skip file watching").option("--no-logs", "Don't stream logs to terminal").action(async (options, command) => {
1506
+ const globalOptions = command.optsWithGlobals();
1507
+ const json = globalOptions.json ?? false;
1508
+ try {
1509
+ const projectRoot = await findProjectRoot(process.cwd());
1510
+ if (!projectRoot) {
1511
+ throw new CLIError(
1512
+ "Not in a WaniWani project. Run 'waniwani mcp init <name>' first.",
1513
+ "NOT_IN_PROJECT"
1514
+ );
1515
+ }
1516
+ let mcpId = options.mcpId;
1517
+ if (!mcpId) {
1518
+ mcpId = await config.getMcpId();
1519
+ }
1520
+ if (!mcpId) {
1521
+ throw new CLIError(
1522
+ "No MCP found. Run 'waniwani mcp init <name>' or use --mcp-id.",
1523
+ "NO_MCP"
1524
+ );
1525
+ }
1526
+ const spinner = ora9("Starting development environment...").start();
1527
+ spinner.text = "Starting session...";
1528
+ let sessionId;
1529
+ try {
1530
+ const sessionResponse = await api.post(
1531
+ `/api/mcp/repositories/${mcpId}/session`,
1532
+ {}
1533
+ );
1534
+ sessionId = sessionResponse.sandbox.id;
1535
+ } catch {
1536
+ const existing = await api.get(
1537
+ `/api/mcp/repositories/${mcpId}/session`
1538
+ );
1539
+ if (!existing) {
1540
+ throw new CLIError("Failed to start session", "SESSION_ERROR");
1541
+ }
1542
+ sessionId = existing.id;
1543
+ }
1544
+ await config.setSessionId(sessionId);
1545
+ spinner.text = "Syncing files to sandbox...";
1546
+ const files = await collectFiles(projectRoot);
1547
+ if (files.length > 0) {
1548
+ await api.post(
1549
+ `/api/mcp/sessions/${sessionId}/files`,
1550
+ { files }
1551
+ );
1552
+ }
1553
+ spinner.text = "Starting MCP server...";
1554
+ const serverResult = await api.post(
1555
+ `/api/mcp/sessions/${sessionId}/server`,
1556
+ { action: "start" }
1557
+ );
1558
+ spinner.succeed("Development environment ready");
1559
+ console.log();
1560
+ formatSuccess("Live preview started!", false);
1561
+ console.log();
1562
+ console.log(` Preview URL: ${serverResult.previewUrl}`);
1563
+ console.log();
1564
+ console.log(` MCP Inspector:`);
1565
+ console.log(
1566
+ ` npx @anthropic-ai/mcp-inspector@latest "${serverResult.previewUrl}/mcp"`
1567
+ );
1568
+ console.log();
1569
+ if (options.watch !== false) {
1570
+ console.log("Watching for file changes... (Ctrl+C to stop)");
1571
+ console.log();
1572
+ const ig = await loadIgnorePatterns(projectRoot);
1573
+ const watcher = watch(projectRoot, {
1574
+ ignored: (path) => {
1575
+ const relative2 = path.replace(`${projectRoot}/`, "");
1576
+ if (relative2 === path) return false;
1577
+ return ig.ignores(relative2);
1578
+ },
1579
+ persistent: true,
1580
+ ignoreInitial: true,
1581
+ awaitWriteFinish: {
1582
+ stabilityThreshold: 100,
1583
+ pollInterval: 50
1584
+ }
1585
+ });
1586
+ const syncFile = async (filePath) => {
1587
+ const relativePath = filePath.replace(`${projectRoot}/`, "");
1588
+ const file = await collectSingleFile(projectRoot, relativePath);
1589
+ if (file) {
1590
+ try {
1591
+ await api.post(
1592
+ `/api/mcp/sessions/${sessionId}/files`,
1593
+ { files: [file] }
1594
+ );
1595
+ console.log(` Synced: ${relativePath}`);
1596
+ } catch {
1597
+ console.error(` Failed to sync: ${relativePath}`);
1598
+ }
1599
+ }
1600
+ };
1601
+ watcher.on("add", syncFile);
1602
+ watcher.on("change", syncFile);
1603
+ watcher.on("unlink", (filePath) => {
1604
+ const relativePath = filePath.replace(`${projectRoot}/`, "");
1605
+ console.log(` Deleted: ${relativePath}`);
1606
+ });
1607
+ process.on("SIGINT", async () => {
1608
+ console.log();
1609
+ console.log("Stopping development environment...");
1610
+ await watcher.close();
1611
+ process.exit(0);
1612
+ });
1613
+ await new Promise(() => {
1614
+ });
1615
+ }
1616
+ } catch (error) {
1617
+ handleError(error, json);
1618
+ process.exit(1);
1619
+ }
1620
+ });
1621
+
1622
+ // src/commands/mcp/publish.ts
1623
+ import { input } from "@inquirer/prompts";
1624
+ import { Command as Command14 } from "commander";
1625
+ import ora10 from "ora";
1626
+ var publishCommand = new Command14("publish").description("Push local files to GitHub and trigger deployment").option("-m, --message <msg>", "Commit message").option("--mcp-id <id>", "Specific MCP ID").action(async (options, command) => {
1627
+ const globalOptions = command.optsWithGlobals();
1628
+ const json = globalOptions.json ?? false;
1629
+ try {
1630
+ const mcpId = await requireMcpId(options.mcpId);
1631
+ const projectRoot = await findProjectRoot(process.cwd());
1632
+ if (!projectRoot) {
1633
+ throw new CLIError(
1634
+ "Not in a WaniWani project. Run 'waniwani mcp init <name>' first.",
1635
+ "NOT_IN_PROJECT"
1636
+ );
1637
+ }
1638
+ let message = options.message;
1639
+ if (!message) {
1640
+ message = await input({
1641
+ message: "Commit message:",
1642
+ validate: (value) => value.trim() ? true : "Commit message is required"
1643
+ });
1644
+ }
1645
+ const spinner = ora10("Collecting files...").start();
1646
+ const files = await collectFiles(projectRoot);
1647
+ if (files.length === 0) {
1648
+ spinner.fail("No files to deploy");
1649
+ return;
1650
+ }
1651
+ spinner.text = `Pushing ${files.length} files to GitHub...`;
1652
+ const result = await api.post(
1653
+ `/api/mcp/repositories/${mcpId}/deploy`,
1654
+ { files, message }
1655
+ );
1656
+ spinner.succeed(`Pushed to GitHub (${result.commitSha.slice(0, 7)})`);
1657
+ if (json) {
1658
+ formatOutput(result, true);
1659
+ } else {
1660
+ console.log();
1661
+ formatSuccess("Files pushed to GitHub!", false);
1662
+ console.log();
1663
+ console.log("Deployment will start automatically via webhook.");
1664
+ }
1665
+ } catch (error) {
1666
+ handleError(error, json);
1667
+ process.exit(1);
1668
+ }
1669
+ });
1670
+
1671
1671
  // src/commands/mcp/run-command.ts
1672
1672
  import chalk8 from "chalk";
1673
1673
  import { Command as Command15 } from "commander";
@@ -1793,7 +1793,7 @@ var statusCommand = new Command16("status").description("Show current MCP status
1793
1793
  formatList(items, false);
1794
1794
  console.log();
1795
1795
  if (!result.activeSandbox) {
1796
- console.log("Start development: waniwani mcp dev");
1796
+ console.log("Start development: waniwani mcp preview");
1797
1797
  } else if (!serverStatus?.running) {
1798
1798
  console.log("View logs: waniwani mcp logs");
1799
1799
  }
@@ -1828,7 +1828,7 @@ var stopCommand = new Command17("stop").description("Stop the development enviro
1828
1828
  } else {
1829
1829
  formatSuccess("Sandbox stopped.", false);
1830
1830
  console.log();
1831
- console.log("Start again: waniwani mcp dev");
1831
+ console.log("Start again: waniwani mcp preview");
1832
1832
  }
1833
1833
  } catch (error) {
1834
1834
  handleError(error, json);
@@ -1900,7 +1900,7 @@ var useCommand = new Command19("use").description("Select an MCP to use for subs
1900
1900
  console.log(` MCP ID: ${mcp.id}`);
1901
1901
  console.log();
1902
1902
  console.log("Next steps:");
1903
- console.log(" waniwani mcp dev # Start live preview");
1903
+ console.log(" waniwani mcp preview # Start live preview");
1904
1904
  console.log(" waniwani mcp status # Check status");
1905
1905
  }
1906
1906
  } catch (error) {
@@ -1910,7 +1910,7 @@ var useCommand = new Command19("use").description("Select an MCP to use for subs
1910
1910
  });
1911
1911
 
1912
1912
  // src/commands/mcp/index.ts
1913
- var mcpCommand = new Command20("mcp").description("MCP management commands").addCommand(initCommand).addCommand(listCommand2).addCommand(useCommand).addCommand(statusCommand).addCommand(devCommand).addCommand(stopCommand).addCommand(logsCommand).addCommand(syncCommand).addCommand(deployCommand).addCommand(deleteCommand).addCommand(fileCommand).addCommand(runCommandCommand);
1913
+ var mcpCommand = new Command20("mcp").description("MCP management commands").addCommand(initCommand).addCommand(listCommand2).addCommand(useCommand).addCommand(statusCommand).addCommand(previewCommand).addCommand(stopCommand).addCommand(logsCommand).addCommand(syncCommand).addCommand(publishCommand).addCommand(deleteCommand).addCommand(fileCommand).addCommand(runCommandCommand);
1914
1914
 
1915
1915
  // src/commands/org/index.ts
1916
1916
  import { Command as Command23 } from "commander";