@waniwani/cli 0.0.25 → 0.0.28

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
@@ -1,209 +1,31 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { Command as Command24 } from "commander";
5
-
6
- // src/commands/init.ts
7
- import { existsSync } from "fs";
8
- import { mkdir, writeFile } from "fs/promises";
9
- import { join } from "path";
10
- import { Command } from "commander";
4
+ import { Command as Command25 } from "commander";
11
5
 
12
- // src/lib/errors.ts
13
- import chalk from "chalk";
14
- import { ZodError } from "zod";
15
- var CLIError = class extends Error {
16
- constructor(message, code, details) {
17
- super(message);
18
- this.code = code;
19
- this.details = details;
20
- this.name = "CLIError";
21
- }
22
- };
23
- var AuthError = class extends CLIError {
24
- constructor(message, details) {
25
- super(message, "AUTH_ERROR", details);
26
- }
27
- };
28
- var SandboxError = class extends CLIError {
29
- constructor(message, details) {
30
- super(message, "SANDBOX_ERROR", details);
31
- }
32
- };
33
- var McpError = class extends CLIError {
34
- constructor(message, details) {
35
- super(message, "MCP_ERROR", details);
36
- }
37
- };
38
- function handleError(error, json) {
39
- if (error instanceof ZodError) {
40
- const message = error.issues.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
41
- outputError("VALIDATION_ERROR", `Invalid input: ${message}`, json);
42
- } else if (error instanceof CLIError) {
43
- outputError(error.code, error.message, json, error.details);
44
- } else if (error instanceof Error) {
45
- outputError("UNKNOWN_ERROR", error.message, json);
46
- } else {
47
- outputError("UNKNOWN_ERROR", String(error), json);
48
- }
49
- }
50
- function outputError(code, message, json, details) {
51
- if (json) {
52
- console.error(
53
- JSON.stringify({ success: false, error: { code, message, details } })
54
- );
55
- } else {
56
- console.error(chalk.red(`Error [${code}]:`), message);
57
- if (details) {
58
- console.error(chalk.gray("Details:"), JSON.stringify(details, null, 2));
59
- }
60
- }
61
- }
62
-
63
- // src/lib/output.ts
6
+ // src/commands/dev.ts
7
+ import { relative as relative2 } from "path";
64
8
  import chalk2 from "chalk";
65
- function formatOutput(data, json) {
66
- if (json) {
67
- console.log(JSON.stringify({ success: true, data }, null, 2));
68
- } else {
69
- prettyPrint(data);
70
- }
71
- }
72
- function formatSuccess(message, json) {
73
- if (json) {
74
- console.log(JSON.stringify({ success: true, message }));
75
- } else {
76
- console.log(chalk2.green("\u2713"), message);
77
- }
78
- }
79
- function formatTable(headers, rows, json) {
80
- if (json) {
81
- const data = rows.map(
82
- (row) => Object.fromEntries(headers.map((header, i) => [header, row[i]]))
83
- );
84
- console.log(JSON.stringify({ success: true, data }, null, 2));
85
- } else {
86
- const colWidths = headers.map(
87
- (h, i) => Math.max(h.length, ...rows.map((r) => (r[i] || "").length))
88
- );
89
- const separator = colWidths.map((w) => "-".repeat(w + 2)).join("+");
90
- const formatRow = (row) => row.map((cell, i) => ` ${(cell || "").padEnd(colWidths[i])} `).join("|");
91
- console.log(chalk2.cyan(formatRow(headers)));
92
- console.log(separator);
93
- for (const row of rows) {
94
- console.log(formatRow(row));
95
- }
96
- }
97
- }
98
- function formatList(items, json) {
99
- if (json) {
100
- const data = Object.fromEntries(
101
- items.map((item) => [item.label, item.value])
102
- );
103
- console.log(JSON.stringify({ success: true, data }, null, 2));
104
- } else {
105
- const maxLabelLength = Math.max(...items.map((i) => i.label.length));
106
- items.forEach((item) => {
107
- console.log(
108
- `${chalk2.gray(item.label.padEnd(maxLabelLength))} ${chalk2.white(item.value)}`
109
- );
110
- });
111
- }
112
- }
113
- function prettyPrint(data, indent = 0) {
114
- const prefix = " ".repeat(indent);
115
- if (Array.isArray(data)) {
116
- data.forEach((item, index) => {
117
- console.log(`${prefix}${chalk2.gray(`[${index}]`)}`);
118
- prettyPrint(item, indent + 1);
119
- });
120
- } else if (typeof data === "object" && data !== null) {
121
- for (const [key, value] of Object.entries(data)) {
122
- if (typeof value === "object" && value !== null) {
123
- console.log(`${prefix}${chalk2.gray(key)}:`);
124
- prettyPrint(value, indent + 1);
125
- } else {
126
- console.log(
127
- `${prefix}${chalk2.gray(key)}: ${chalk2.white(String(value))}`
128
- );
129
- }
130
- }
131
- } else {
132
- console.log(`${prefix}${chalk2.white(String(data))}`);
133
- }
134
- }
135
-
136
- // src/commands/init.ts
137
- var PROJECT_CONFIG_DIR = ".waniwani";
138
- var PROJECT_CONFIG_FILE = "settings.local.json";
139
- var initCommand = new Command("init").description("Initialize WaniWani project config in current directory").action(async (_, command) => {
140
- const globalOptions = command.optsWithGlobals();
141
- const json = globalOptions.json ?? false;
142
- try {
143
- const cwd = process.cwd();
144
- const configDir = join(cwd, PROJECT_CONFIG_DIR);
145
- const configPath = join(configDir, PROJECT_CONFIG_FILE);
146
- if (existsSync(configDir)) {
147
- if (json) {
148
- formatOutput(
149
- { initialized: false, message: "Already initialized" },
150
- true
151
- );
152
- } else {
153
- console.log("Project already initialized (.waniwani/ exists)");
154
- }
155
- return;
156
- }
157
- await mkdir(configDir, { recursive: true });
158
- const defaultConfig = {
159
- mcpId: null,
160
- defaults: {}
161
- };
162
- await writeFile(
163
- configPath,
164
- JSON.stringify(defaultConfig, null, " "),
165
- "utf-8"
166
- );
167
- if (json) {
168
- formatOutput({ initialized: true, path: configDir }, true);
169
- } else {
170
- formatSuccess("Initialized WaniWani project config", false);
171
- console.log();
172
- console.log(` Created: ${PROJECT_CONFIG_DIR}/${PROJECT_CONFIG_FILE}`);
173
- console.log();
174
- console.log("Now run:");
175
- console.log(' waniwani mcp create "my-mcp"');
176
- console.log(" waniwani mcp use <name>");
177
- }
178
- } catch (error) {
179
- handleError(error, json);
180
- process.exit(1);
181
- }
182
- });
183
-
184
- // src/commands/login.ts
185
- import { spawn } from "child_process";
186
- import { createServer } from "http";
187
- import chalk3 from "chalk";
188
- import { Command as Command2 } from "commander";
9
+ import chokidar from "chokidar";
10
+ import { Command } from "commander";
189
11
  import ora from "ora";
190
12
 
191
13
  // src/lib/auth.ts
192
- import { access, mkdir as mkdir3, readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
14
+ import { access, mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
193
15
  import { homedir as homedir2 } from "os";
194
- import { join as join3 } from "path";
16
+ import { join as join2 } from "path";
195
17
  import { z as z2 } from "zod";
196
18
 
197
19
  // src/lib/config.ts
198
- import { existsSync as existsSync2 } from "fs";
199
- import { mkdir as mkdir2, readFile, writeFile as writeFile2 } from "fs/promises";
20
+ import { existsSync } from "fs";
21
+ import { mkdir, readFile, writeFile } from "fs/promises";
200
22
  import { homedir } from "os";
201
- import { join as join2 } from "path";
23
+ import { join } from "path";
202
24
  import { z } from "zod";
203
- var LOCAL_DIR = join2(process.cwd(), ".waniwani");
204
- var LOCAL_FILE = join2(LOCAL_DIR, "settings.json");
205
- var GLOBAL_DIR = join2(homedir(), ".waniwani");
206
- var GLOBAL_FILE = join2(GLOBAL_DIR, "settings.json");
25
+ var LOCAL_DIR = join(process.cwd(), ".waniwani");
26
+ var LOCAL_FILE = join(LOCAL_DIR, "settings.json");
27
+ var GLOBAL_DIR = join(homedir(), ".waniwani");
28
+ var GLOBAL_FILE = join(GLOBAL_DIR, "settings.json");
207
29
  var DEFAULT_API_URL = "https://app.waniwani.ai";
208
30
  var ConfigSchema = z.object({
209
31
  defaults: z.object({ model: z.string(), maxSteps: z.number() }).default({ model: "claude-sonnet-4-20250514", maxSteps: 10 }),
@@ -216,7 +38,7 @@ var Config = class {
216
38
  cache = null;
217
39
  scope;
218
40
  constructor(forceGlobal = false) {
219
- const useLocal = !forceGlobal && existsSync2(LOCAL_DIR);
41
+ const useLocal = !forceGlobal && existsSync(LOCAL_DIR);
220
42
  this.dir = useLocal ? LOCAL_DIR : GLOBAL_DIR;
221
43
  this.file = useLocal ? LOCAL_FILE : GLOBAL_FILE;
222
44
  this.scope = useLocal ? "local" : "global";
@@ -235,8 +57,8 @@ var Config = class {
235
57
  }
236
58
  async save(data) {
237
59
  this.cache = data;
238
- await mkdir2(this.dir, { recursive: true });
239
- await writeFile2(this.file, JSON.stringify(data, null, " "));
60
+ await mkdir(this.dir, { recursive: true });
61
+ await writeFile(this.file, JSON.stringify(data, null, " "));
240
62
  }
241
63
  async getDefaults() {
242
64
  return (await this.load()).defaults;
@@ -271,8 +93,8 @@ var config = new Config();
271
93
  var globalConfig = new Config(true);
272
94
 
273
95
  // src/lib/auth.ts
274
- var CONFIG_DIR = join3(homedir2(), ".waniwani");
275
- var AUTH_FILE = join3(CONFIG_DIR, "auth.json");
96
+ var CONFIG_DIR = join2(homedir2(), ".waniwani");
97
+ var AUTH_FILE = join2(CONFIG_DIR, "auth.json");
276
98
  var AuthStoreSchema = z2.object({
277
99
  accessToken: z2.string().nullable().default(null),
278
100
  refreshToken: z2.string().nullable().default(null),
@@ -280,7 +102,7 @@ var AuthStoreSchema = z2.object({
280
102
  clientId: z2.string().nullable().default(null)
281
103
  });
282
104
  async function ensureConfigDir() {
283
- await mkdir3(CONFIG_DIR, { recursive: true });
105
+ await mkdir2(CONFIG_DIR, { recursive: true });
284
106
  }
285
107
  async function readAuthStore() {
286
108
  await ensureConfigDir();
@@ -294,7 +116,7 @@ async function readAuthStore() {
294
116
  }
295
117
  async function writeAuthStore(store) {
296
118
  await ensureConfigDir();
297
- await writeFile3(AUTH_FILE, JSON.stringify(store, null, 2), "utf-8");
119
+ await writeFile2(AUTH_FILE, JSON.stringify(store, null, 2), "utf-8");
298
120
  }
299
121
  var AuthManager = class {
300
122
  storeCache = null;
@@ -359,22 +181,612 @@ var AuthManager = class {
359
181
  await this.clear();
360
182
  return false;
361
183
  }
362
- const data = await response.json();
363
- await this.setTokens(
364
- data.access_token,
365
- data.refresh_token,
366
- data.expires_in
184
+ const data = await response.json();
185
+ await this.setTokens(
186
+ data.access_token,
187
+ data.refresh_token,
188
+ data.expires_in
189
+ );
190
+ return true;
191
+ } catch {
192
+ await this.clear();
193
+ return false;
194
+ }
195
+ }
196
+ };
197
+ var auth = new AuthManager();
198
+
199
+ // src/lib/errors.ts
200
+ import chalk from "chalk";
201
+ import { ZodError } from "zod";
202
+ var CLIError = class extends Error {
203
+ constructor(message, code, details) {
204
+ super(message);
205
+ this.code = code;
206
+ this.details = details;
207
+ this.name = "CLIError";
208
+ }
209
+ };
210
+ var AuthError = class extends CLIError {
211
+ constructor(message, details) {
212
+ super(message, "AUTH_ERROR", details);
213
+ }
214
+ };
215
+ var SandboxError = class extends CLIError {
216
+ constructor(message, details) {
217
+ super(message, "SANDBOX_ERROR", details);
218
+ }
219
+ };
220
+ var McpError = class extends CLIError {
221
+ constructor(message, details) {
222
+ super(message, "MCP_ERROR", details);
223
+ }
224
+ };
225
+ function handleError(error, json) {
226
+ if (error instanceof ZodError) {
227
+ const message = error.issues.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
228
+ outputError("VALIDATION_ERROR", `Invalid input: ${message}`, json);
229
+ } else if (error instanceof CLIError) {
230
+ outputError(error.code, error.message, json, error.details);
231
+ } else if (error instanceof Error) {
232
+ outputError("UNKNOWN_ERROR", error.message, json);
233
+ } else {
234
+ outputError("UNKNOWN_ERROR", String(error), json);
235
+ }
236
+ }
237
+ function outputError(code, message, json, details) {
238
+ if (json) {
239
+ console.error(
240
+ JSON.stringify({ success: false, error: { code, message, details } })
241
+ );
242
+ } else {
243
+ console.error(chalk.red(`Error [${code}]:`), message);
244
+ if (details) {
245
+ console.error(chalk.gray("Details:"), JSON.stringify(details, null, 2));
246
+ }
247
+ }
248
+ }
249
+
250
+ // src/lib/api.ts
251
+ var ApiError = class extends CLIError {
252
+ constructor(message, code, statusCode, details) {
253
+ super(message, code, details);
254
+ this.statusCode = statusCode;
255
+ this.name = "ApiError";
256
+ }
257
+ };
258
+ async function request(method, path, options) {
259
+ const {
260
+ body,
261
+ requireAuth = true,
262
+ headers: extraHeaders = {}
263
+ } = options || {};
264
+ const headers = {
265
+ "Content-Type": "application/json",
266
+ ...extraHeaders
267
+ };
268
+ if (requireAuth) {
269
+ const token = await auth.getAccessToken();
270
+ if (!token) {
271
+ throw new AuthError(
272
+ "Not logged in. Run 'waniwani login' to authenticate."
273
+ );
274
+ }
275
+ headers.Authorization = `Bearer ${token}`;
276
+ }
277
+ const baseUrl = await config.getApiUrl();
278
+ const url = `${baseUrl}${path}`;
279
+ const response = await fetch(url, {
280
+ method,
281
+ headers,
282
+ body: body ? JSON.stringify(body) : void 0
283
+ });
284
+ if (response.status === 204) {
285
+ return void 0;
286
+ }
287
+ let data;
288
+ let rawBody;
289
+ try {
290
+ rawBody = await response.text();
291
+ data = JSON.parse(rawBody);
292
+ } catch {
293
+ throw new ApiError(
294
+ rawBody || `Request failed with status ${response.status}`,
295
+ "API_ERROR",
296
+ response.status,
297
+ { statusText: response.statusText }
298
+ );
299
+ }
300
+ if (!response.ok || data.error) {
301
+ const errorMessage = data.error?.message || data.message || data.error || rawBody || `Request failed with status ${response.status}`;
302
+ const errorCode = data.error?.code || data.code || "API_ERROR";
303
+ const errorDetails = {
304
+ ...data.error?.details,
305
+ statusText: response.statusText,
306
+ ...data.error ? {} : { rawResponse: data }
307
+ };
308
+ const error = {
309
+ code: errorCode,
310
+ message: errorMessage,
311
+ details: errorDetails
312
+ };
313
+ if (response.status === 401) {
314
+ const refreshed = await auth.tryRefreshToken();
315
+ if (refreshed) {
316
+ return request(method, path, options);
317
+ }
318
+ throw new AuthError(
319
+ "Session expired. Run 'waniwani login' to re-authenticate."
320
+ );
321
+ }
322
+ throw new ApiError(
323
+ error.message,
324
+ error.code,
325
+ response.status,
326
+ error.details
327
+ );
328
+ }
329
+ return data.data;
330
+ }
331
+ var api = {
332
+ get: (path, options) => request("GET", path, options),
333
+ post: (path, body, options) => request("POST", path, { body, ...options }),
334
+ delete: (path, options) => request("DELETE", path, options),
335
+ getBaseUrl: () => config.getApiUrl()
336
+ };
337
+
338
+ // src/lib/sync.ts
339
+ import { existsSync as existsSync2 } from "fs";
340
+ import { readdir, readFile as readFile3, stat } from "fs/promises";
341
+ import { dirname, join as join3, relative } from "path";
342
+ import ignore from "ignore";
343
+
344
+ // src/lib/utils.ts
345
+ function debounce(fn, delay) {
346
+ let timeoutId;
347
+ return (...args) => {
348
+ clearTimeout(timeoutId);
349
+ timeoutId = setTimeout(() => fn(...args), delay);
350
+ };
351
+ }
352
+ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
353
+ ".png",
354
+ ".jpg",
355
+ ".jpeg",
356
+ ".gif",
357
+ ".ico",
358
+ ".webp",
359
+ ".svg",
360
+ ".woff",
361
+ ".woff2",
362
+ ".ttf",
363
+ ".eot",
364
+ ".otf",
365
+ ".zip",
366
+ ".tar",
367
+ ".gz",
368
+ ".pdf",
369
+ ".exe",
370
+ ".dll",
371
+ ".so",
372
+ ".dylib",
373
+ ".bin",
374
+ ".mp3",
375
+ ".mp4",
376
+ ".wav",
377
+ ".ogg",
378
+ ".webm"
379
+ ]);
380
+ function isBinaryPath(filePath) {
381
+ const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
382
+ return BINARY_EXTENSIONS.has(ext);
383
+ }
384
+ function detectBinary(buffer) {
385
+ const sample = buffer.subarray(0, 8192);
386
+ return sample.includes(0);
387
+ }
388
+
389
+ // src/lib/sync.ts
390
+ var PROJECT_DIR = ".waniwani";
391
+ var SETTINGS_FILE = "settings.json";
392
+ async function findProjectRoot(startDir) {
393
+ let current = startDir;
394
+ const root = dirname(current);
395
+ while (current !== root) {
396
+ if (existsSync2(join3(current, PROJECT_DIR))) {
397
+ return current;
398
+ }
399
+ const parent = dirname(current);
400
+ if (parent === current) break;
401
+ current = parent;
402
+ }
403
+ if (existsSync2(join3(current, PROJECT_DIR))) {
404
+ return current;
405
+ }
406
+ return null;
407
+ }
408
+ async function loadProjectMcpId(projectRoot) {
409
+ const settingsPath = join3(projectRoot, PROJECT_DIR, SETTINGS_FILE);
410
+ try {
411
+ const content = await readFile3(settingsPath, "utf-8");
412
+ const settings = JSON.parse(content);
413
+ return settings.mcpId ?? null;
414
+ } catch {
415
+ return null;
416
+ }
417
+ }
418
+ var DEFAULT_IGNORE_PATTERNS = [
419
+ ".waniwani",
420
+ ".git",
421
+ "node_modules",
422
+ ".env",
423
+ ".env.*",
424
+ ".DS_Store",
425
+ "*.log",
426
+ ".cache",
427
+ "dist",
428
+ "coverage",
429
+ ".turbo",
430
+ ".next",
431
+ ".nuxt",
432
+ ".vercel"
433
+ ];
434
+ async function loadIgnorePatterns(projectRoot) {
435
+ const ig = ignore();
436
+ ig.add(DEFAULT_IGNORE_PATTERNS);
437
+ const gitignorePath = join3(projectRoot, ".gitignore");
438
+ if (existsSync2(gitignorePath)) {
439
+ try {
440
+ const content = await readFile3(gitignorePath, "utf-8");
441
+ ig.add(content);
442
+ } catch {
443
+ }
444
+ }
445
+ return ig;
446
+ }
447
+ async function collectFiles(projectRoot) {
448
+ const ig = await loadIgnorePatterns(projectRoot);
449
+ const files = [];
450
+ async function walk(dir) {
451
+ const entries = await readdir(dir, { withFileTypes: true });
452
+ for (const entry of entries) {
453
+ const fullPath = join3(dir, entry.name);
454
+ const relativePath = relative(projectRoot, fullPath);
455
+ if (ig.ignores(relativePath)) {
456
+ continue;
457
+ }
458
+ if (entry.isDirectory()) {
459
+ await walk(fullPath);
460
+ } else if (entry.isFile()) {
461
+ try {
462
+ const content = await readFile3(fullPath);
463
+ const isBinary = isBinaryPath(fullPath) || detectBinary(content);
464
+ files.push({
465
+ path: relativePath,
466
+ content: isBinary ? content.toString("base64") : content.toString("utf8"),
467
+ encoding: isBinary ? "base64" : "utf8"
468
+ });
469
+ } catch {
470
+ }
471
+ }
472
+ }
473
+ }
474
+ await walk(projectRoot);
475
+ return files;
476
+ }
477
+ async function collectSingleFile(projectRoot, filePath) {
478
+ const fullPath = join3(projectRoot, filePath);
479
+ const relativePath = relative(projectRoot, fullPath);
480
+ if (!existsSync2(fullPath)) {
481
+ return null;
482
+ }
483
+ try {
484
+ const fileStat = await stat(fullPath);
485
+ if (!fileStat.isFile()) {
486
+ return null;
487
+ }
488
+ const content = await readFile3(fullPath);
489
+ const isBinary = isBinaryPath(fullPath) || detectBinary(content);
490
+ return {
491
+ path: relativePath,
492
+ content: isBinary ? content.toString("base64") : content.toString("utf8"),
493
+ encoding: isBinary ? "base64" : "utf8"
494
+ };
495
+ } catch {
496
+ return null;
497
+ }
498
+ }
499
+
500
+ // src/commands/dev.ts
501
+ var BATCH_SIZE = 50;
502
+ var DEFAULT_DEBOUNCE_MS = 300;
503
+ var devCommand = new Command("dev").description("Watch and sync files to MCP sandbox").option("--no-initial-sync", "Skip initial sync of all files").option(
504
+ "--debounce <ms>",
505
+ "Debounce delay in milliseconds",
506
+ String(DEFAULT_DEBOUNCE_MS)
507
+ ).action(async (options, command) => {
508
+ const globalOptions = command.optsWithGlobals();
509
+ const json = globalOptions.json ?? false;
510
+ try {
511
+ const cwd = process.cwd();
512
+ const projectRoot = await findProjectRoot(cwd);
513
+ if (!projectRoot) {
514
+ throw new CLIError(
515
+ "Not in a WaniWani project. Run 'waniwani init <name>' first.",
516
+ "NOT_IN_PROJECT"
517
+ );
518
+ }
519
+ const mcpId = await loadProjectMcpId(projectRoot);
520
+ if (!mcpId) {
521
+ throw new CLIError(
522
+ "No MCP ID found in project config. Run 'waniwani init <name>' first.",
523
+ "NO_MCP_ID"
524
+ );
525
+ }
526
+ if (options.initialSync !== false) {
527
+ const spinner = ora("Initial sync...").start();
528
+ const files = await collectFiles(projectRoot);
529
+ if (files.length > 0) {
530
+ const totalBatches = Math.ceil(files.length / BATCH_SIZE);
531
+ let synced = 0;
532
+ for (let i = 0; i < totalBatches; i++) {
533
+ const batch = files.slice(i * BATCH_SIZE, (i + 1) * BATCH_SIZE);
534
+ spinner.text = `Syncing (${i + 1}/${totalBatches})...`;
535
+ const result = await api.post(
536
+ `/api/mcp/sandboxes/${mcpId}/files`,
537
+ {
538
+ files: batch.map((f) => ({
539
+ path: f.path,
540
+ content: f.content,
541
+ encoding: f.encoding
542
+ }))
543
+ }
544
+ );
545
+ synced += result.written.length;
546
+ }
547
+ spinner.succeed(`Initial sync complete (${synced} files)`);
548
+ } else {
549
+ spinner.info("No files to sync");
550
+ }
551
+ }
552
+ const ig = await loadIgnorePatterns(projectRoot);
553
+ const debounceMs = Number.parseInt(options.debounce, 10) || DEFAULT_DEBOUNCE_MS;
554
+ const syncFile = debounce(async (filePath) => {
555
+ const relativePath = relative2(projectRoot, filePath);
556
+ if (ig.ignores(relativePath)) {
557
+ return;
558
+ }
559
+ const file = await collectSingleFile(projectRoot, relativePath);
560
+ if (!file) {
561
+ console.log(chalk2.yellow("Skipped:"), relativePath);
562
+ return;
563
+ }
564
+ try {
565
+ await api.post(
566
+ `/api/mcp/sandboxes/${mcpId}/files`,
567
+ {
568
+ files: [
569
+ {
570
+ path: file.path,
571
+ content: file.content,
572
+ encoding: file.encoding
573
+ }
574
+ ]
575
+ }
576
+ );
577
+ console.log(chalk2.green("Synced:"), relativePath);
578
+ } catch (error) {
579
+ console.log(chalk2.red("Failed:"), relativePath);
580
+ if (globalOptions.verbose) {
581
+ console.error(error);
582
+ }
583
+ }
584
+ }, debounceMs);
585
+ console.log();
586
+ console.log(chalk2.bold("Watching for changes..."));
587
+ console.log(chalk2.dim("Press Ctrl+C to stop"));
588
+ console.log();
589
+ const watcher = chokidar.watch(projectRoot, {
590
+ ignored: (path) => {
591
+ const relativePath = relative2(projectRoot, path);
592
+ return ig.ignores(relativePath);
593
+ },
594
+ persistent: true,
595
+ ignoreInitial: true,
596
+ awaitWriteFinish: {
597
+ stabilityThreshold: 100,
598
+ pollInterval: 100
599
+ }
600
+ });
601
+ watcher.on("add", (path) => syncFile(path)).on("change", (path) => syncFile(path)).on("unlink", (path) => {
602
+ const relativePath = relative2(projectRoot, path);
603
+ console.log(chalk2.yellow("Deleted (local only):"), relativePath);
604
+ });
605
+ const cleanup = () => {
606
+ console.log();
607
+ console.log(chalk2.dim("Stopping watcher..."));
608
+ watcher.close().then(() => {
609
+ process.exit(0);
610
+ });
611
+ };
612
+ process.on("SIGINT", cleanup);
613
+ process.on("SIGTERM", cleanup);
614
+ } catch (error) {
615
+ handleError(error, json);
616
+ process.exit(1);
617
+ }
618
+ });
619
+
620
+ // src/commands/init.ts
621
+ import { existsSync as existsSync3 } from "fs";
622
+ import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
623
+ import { join as join4 } from "path";
624
+ import { Command as Command2 } from "commander";
625
+ import degit from "degit";
626
+ import ora2 from "ora";
627
+
628
+ // src/lib/output.ts
629
+ import chalk3 from "chalk";
630
+ function formatOutput(data, json) {
631
+ if (json) {
632
+ console.log(JSON.stringify({ success: true, data }, null, 2));
633
+ } else {
634
+ prettyPrint(data);
635
+ }
636
+ }
637
+ function formatSuccess(message, json) {
638
+ if (json) {
639
+ console.log(JSON.stringify({ success: true, message }));
640
+ } else {
641
+ console.log(chalk3.green("\u2713"), message);
642
+ }
643
+ }
644
+ function formatTable(headers, rows, json) {
645
+ if (json) {
646
+ const data = rows.map(
647
+ (row) => Object.fromEntries(headers.map((header, i) => [header, row[i]]))
648
+ );
649
+ console.log(JSON.stringify({ success: true, data }, null, 2));
650
+ } else {
651
+ const colWidths = headers.map(
652
+ (h, i) => Math.max(h.length, ...rows.map((r) => (r[i] || "").length))
653
+ );
654
+ const separator = colWidths.map((w) => "-".repeat(w + 2)).join("+");
655
+ const formatRow = (row) => row.map((cell, i) => ` ${(cell || "").padEnd(colWidths[i])} `).join("|");
656
+ console.log(chalk3.cyan(formatRow(headers)));
657
+ console.log(separator);
658
+ for (const row of rows) {
659
+ console.log(formatRow(row));
660
+ }
661
+ }
662
+ }
663
+ function formatList(items, json) {
664
+ if (json) {
665
+ const data = Object.fromEntries(
666
+ items.map((item) => [item.label, item.value])
667
+ );
668
+ console.log(JSON.stringify({ success: true, data }, null, 2));
669
+ } else {
670
+ const maxLabelLength = Math.max(...items.map((i) => i.label.length));
671
+ items.forEach((item) => {
672
+ console.log(
673
+ `${chalk3.gray(item.label.padEnd(maxLabelLength))} ${chalk3.white(item.value)}`
674
+ );
675
+ });
676
+ }
677
+ }
678
+ function prettyPrint(data, indent = 0) {
679
+ const prefix = " ".repeat(indent);
680
+ if (Array.isArray(data)) {
681
+ data.forEach((item, index) => {
682
+ console.log(`${prefix}${chalk3.gray(`[${index}]`)}`);
683
+ prettyPrint(item, indent + 1);
684
+ });
685
+ } else if (typeof data === "object" && data !== null) {
686
+ for (const [key, value] of Object.entries(data)) {
687
+ if (typeof value === "object" && value !== null) {
688
+ console.log(`${prefix}${chalk3.gray(key)}:`);
689
+ prettyPrint(value, indent + 1);
690
+ } else {
691
+ console.log(
692
+ `${prefix}${chalk3.gray(key)}: ${chalk3.white(String(value))}`
693
+ );
694
+ }
695
+ }
696
+ } else {
697
+ console.log(`${prefix}${chalk3.white(String(data))}`);
698
+ }
699
+ }
700
+
701
+ // src/commands/init.ts
702
+ var PROJECT_CONFIG_DIR = ".waniwani";
703
+ var PROJECT_CONFIG_FILE = "settings.json";
704
+ var initCommand = new Command2("init").description("Create a new MCP project from template").argument("<name>", "Name for the MCP project").action(async (name, _, command) => {
705
+ const globalOptions = command.optsWithGlobals();
706
+ const json = globalOptions.json ?? false;
707
+ try {
708
+ const cwd = process.cwd();
709
+ const projectDir = join4(cwd, name);
710
+ if (existsSync3(projectDir)) {
711
+ if (json) {
712
+ formatOutput(
713
+ {
714
+ success: false,
715
+ error: `Directory "${name}" already exists`
716
+ },
717
+ true
718
+ );
719
+ } else {
720
+ console.error(`Error: Directory "${name}" already exists`);
721
+ }
722
+ process.exit(1);
723
+ }
724
+ const spinner = ora2("Creating MCP sandbox...").start();
725
+ const result = await api.post("/api/mcp/sandboxes", {
726
+ name
727
+ });
728
+ spinner.text = "Cloning template...";
729
+ const templateRef = result.templateBranch ? `${result.templateGitUrl}#${result.templateBranch}` : result.templateGitUrl;
730
+ const emitter = degit(templateRef, {
731
+ cache: false,
732
+ force: true,
733
+ verbose: false
734
+ });
735
+ await emitter.clone(projectDir);
736
+ spinner.text = "Setting up project config...";
737
+ const configDir = join4(projectDir, PROJECT_CONFIG_DIR);
738
+ const configPath = join4(configDir, PROJECT_CONFIG_FILE);
739
+ await mkdir3(configDir, { recursive: true });
740
+ const projectConfig = {
741
+ mcpId: result.id,
742
+ defaults: {
743
+ model: "claude-sonnet-4-20250514",
744
+ maxSteps: 10
745
+ }
746
+ };
747
+ await writeFile3(
748
+ configPath,
749
+ JSON.stringify(projectConfig, null, " "),
750
+ "utf-8"
751
+ );
752
+ spinner.succeed("MCP project created");
753
+ if (json) {
754
+ formatOutput(
755
+ {
756
+ success: true,
757
+ projectDir,
758
+ mcpId: result.id,
759
+ sandboxId: result.sandboxId,
760
+ previewUrl: result.previewUrl
761
+ },
762
+ true
367
763
  );
368
- return true;
369
- } catch {
370
- await this.clear();
371
- return false;
764
+ } else {
765
+ console.log();
766
+ formatSuccess(`MCP project "${name}" created!`, false);
767
+ console.log();
768
+ console.log(` Project: ${projectDir}`);
769
+ console.log(` MCP ID: ${result.id}`);
770
+ console.log(` Preview URL: ${result.previewUrl}`);
771
+ console.log();
772
+ console.log("Next steps:");
773
+ console.log(` cd ${name}`);
774
+ console.log(" waniwani push # Sync files to sandbox");
775
+ console.log(" waniwani dev # Watch mode with auto-sync");
776
+ console.log(' waniwani task "..." # Send tasks to Claude');
372
777
  }
778
+ } catch (error) {
779
+ handleError(error, json);
780
+ process.exit(1);
373
781
  }
374
- };
375
- var auth = new AuthManager();
782
+ });
376
783
 
377
784
  // src/commands/login.ts
785
+ import { spawn } from "child_process";
786
+ import { createServer } from "http";
787
+ import chalk4 from "chalk";
788
+ import { Command as Command3 } from "commander";
789
+ import ora3 from "ora";
378
790
  var CALLBACK_PORT = 54321;
379
791
  var CALLBACK_URL = `http://localhost:${CALLBACK_PORT}/callback`;
380
792
  var CLIENT_NAME = "waniwani-cli";
@@ -438,13 +850,106 @@ async function waitForCallback(expectedState, timeoutMs = 3e5) {
438
850
  sockets.clear();
439
851
  server?.close();
440
852
  };
441
- const htmlResponse = (title, message, color) => `<html>
442
- <body style="font-family: system-ui; padding: 40px; text-align: center;">
443
- <h1 style="color: ${color};">${title}</h1>
444
- <p>${message}</p>
445
- <p>You can close this window.</p>
446
- </body>
447
- </html>`;
853
+ const htmlResponse = (title, message, isSuccess) => `<!DOCTYPE html>
854
+ <html>
855
+ <head>
856
+ <meta charset="UTF-8">
857
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
858
+ <title>${title} - WaniWani</title>
859
+ <style>
860
+ * { margin: 0; padding: 0; box-sizing: border-box; }
861
+ body {
862
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
863
+ min-height: 100vh;
864
+ display: flex;
865
+ align-items: center;
866
+ justify-content: center;
867
+ background: #fafafa;
868
+ position: relative;
869
+ overflow: hidden;
870
+ }
871
+ .blob {
872
+ position: absolute;
873
+ border-radius: 50%;
874
+ filter: blur(60px);
875
+ pointer-events: none;
876
+ }
877
+ .blob-1 {
878
+ top: 0;
879
+ left: 25%;
880
+ width: 24rem;
881
+ height: 24rem;
882
+ background: linear-gradient(to bottom right, rgba(253, 224, 71, 0.3), rgba(251, 146, 60, 0.3));
883
+ }
884
+ .blob-2 {
885
+ bottom: 0;
886
+ right: 25%;
887
+ width: 24rem;
888
+ height: 24rem;
889
+ background: linear-gradient(to bottom right, rgba(134, 239, 172, 0.3), rgba(52, 211, 153, 0.3));
890
+ }
891
+ .blob-3 {
892
+ top: 50%;
893
+ left: 50%;
894
+ transform: translate(-50%, -50%);
895
+ width: 40rem;
896
+ height: 40rem;
897
+ background: linear-gradient(to bottom right, rgba(255, 237, 213, 0.2), rgba(254, 249, 195, 0.2));
898
+ }
899
+ .container {
900
+ display: flex;
901
+ flex-direction: column;
902
+ align-items: center;
903
+ gap: 2rem;
904
+ padding: 2rem;
905
+ z-index: 10;
906
+ text-align: center;
907
+ }
908
+ .logo {
909
+ height: 40px;
910
+ }
911
+ .icon-circle {
912
+ width: 80px;
913
+ height: 80px;
914
+ border-radius: 50%;
915
+ display: flex;
916
+ align-items: center;
917
+ justify-content: center;
918
+ background: ${isSuccess ? "rgba(52, 211, 153, 0.15)" : "rgba(239, 68, 68, 0.15)"};
919
+ }
920
+ .icon {
921
+ width: 40px;
922
+ height: 40px;
923
+ color: ${isSuccess ? "#10b981" : "#ef4444"};
924
+ }
925
+ h1 {
926
+ font-size: 2rem;
927
+ font-weight: 700;
928
+ color: #1e293b;
929
+ }
930
+ p {
931
+ font-size: 1.125rem;
932
+ color: #64748b;
933
+ max-width: 400px;
934
+ }
935
+ </style>
936
+ </head>
937
+ <body>
938
+ <div class="blob blob-1"></div>
939
+ <div class="blob blob-2"></div>
940
+ <div class="blob blob-3"></div>
941
+ <div class="container">
942
+ <svg class="logo" viewBox="0 0 248 40" fill="none" xmlns="http://www.w3.org/2000/svg">
943
+ <text x="0" y="32" font-family="system-ui" font-size="28" font-weight="bold" fill="#1e293b">WaniWani</text>
944
+ </svg>
945
+ <div class="icon-circle">
946
+ ${isSuccess ? '<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"></path></svg>' : '<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"></path><path d="m6 6 12 12"></path></svg>'}
947
+ </div>
948
+ <h1>${title}</h1>
949
+ <p>${message}</p>
950
+ </div>
951
+ </body>
952
+ </html>`;
448
953
  try {
449
954
  server = createServer((req, res) => {
450
955
  const url = new URL(
@@ -458,7 +963,7 @@ async function waitForCallback(expectedState, timeoutMs = 3e5) {
458
963
  res.setHeader("Content-Type", "text/html");
459
964
  if (error) {
460
965
  res.statusCode = 400;
461
- res.end(htmlResponse("Login Failed", `Error: ${error}`, "#ef4444"));
966
+ res.end(htmlResponse("Login Failed", `Error: ${error}`, false));
462
967
  cleanup();
463
968
  reject(new CLIError(`OAuth error: ${error}`, "OAUTH_ERROR"));
464
969
  return;
@@ -469,7 +974,7 @@ async function waitForCallback(expectedState, timeoutMs = 3e5) {
469
974
  htmlResponse(
470
975
  "Login Failed",
471
976
  "Invalid state parameter. Please try again.",
472
- "#ef4444"
977
+ false
473
978
  )
474
979
  );
475
980
  cleanup();
@@ -482,7 +987,7 @@ async function waitForCallback(expectedState, timeoutMs = 3e5) {
482
987
  htmlResponse(
483
988
  "Login Failed",
484
989
  "No authorization code received.",
485
- "#ef4444"
990
+ false
486
991
  )
487
992
  );
488
993
  cleanup();
@@ -494,7 +999,7 @@ async function waitForCallback(expectedState, timeoutMs = 3e5) {
494
999
  htmlResponse(
495
1000
  "Login Successful!",
496
1001
  "You can close this window and return to the terminal.",
497
- "#22c55e"
1002
+ true
498
1003
  )
499
1004
  );
500
1005
  setTimeout(() => {
@@ -556,7 +1061,7 @@ async function exchangeCodeForToken(code, codeVerifier, clientId, resource) {
556
1061
  }
557
1062
  return response.json();
558
1063
  }
559
- var loginCommand = new Command2("login").description("Log in to WaniWani").option("--no-browser", "Don't open the browser automatically").action(async (options, command) => {
1064
+ var loginCommand = new Command3("login").description("Log in to WaniWani").option("--no-browser", "Don't open the browser automatically").action(async (options, command) => {
560
1065
  const globalOptions = command.optsWithGlobals();
561
1066
  const json = globalOptions.json ?? false;
562
1067
  try {
@@ -568,14 +1073,14 @@ var loginCommand = new Command2("login").description("Log in to WaniWani").optio
568
1073
  formatOutput({ alreadyLoggedIn: true, refreshed: true }, true);
569
1074
  } else {
570
1075
  console.log(
571
- chalk3.green("Session refreshed. You're still logged in.")
1076
+ chalk4.green("Session refreshed. You're still logged in.")
572
1077
  );
573
1078
  }
574
1079
  return;
575
1080
  }
576
1081
  if (!json) {
577
1082
  console.log(
578
- chalk3.yellow("Session expired. Starting new login flow...")
1083
+ chalk4.yellow("Session expired. Starting new login flow...")
579
1084
  );
580
1085
  }
581
1086
  await auth.clear();
@@ -584,7 +1089,7 @@ var loginCommand = new Command2("login").description("Log in to WaniWani").optio
584
1089
  formatOutput({ alreadyLoggedIn: true }, true);
585
1090
  } else {
586
1091
  console.log(
587
- chalk3.yellow(
1092
+ chalk4.yellow(
588
1093
  "Already logged in. Use 'waniwani logout' to log out first."
589
1094
  )
590
1095
  );
@@ -593,9 +1098,9 @@ var loginCommand = new Command2("login").description("Log in to WaniWani").optio
593
1098
  }
594
1099
  }
595
1100
  if (!json) {
596
- console.log(chalk3.bold("\nWaniWani CLI Login\n"));
1101
+ console.log(chalk4.bold("\nWaniWani CLI Login\n"));
597
1102
  }
598
- const spinner = ora("Registering client...").start();
1103
+ const spinner = ora3("Registering client...").start();
599
1104
  const { client_id: clientId } = await registerClient();
600
1105
  spinner.text = "Preparing authentication...";
601
1106
  const codeVerifier = generateCodeVerifier();
@@ -615,7 +1120,7 @@ var loginCommand = new Command2("login").description("Log in to WaniWani").optio
615
1120
  console.log("Opening browser for authentication...\n");
616
1121
  console.log(`If the browser doesn't open, visit:
617
1122
  `);
618
- console.log(chalk3.cyan(` ${authUrl.toString()}`));
1123
+ console.log(chalk4.cyan(` ${authUrl.toString()}`));
619
1124
  console.log();
620
1125
  }
621
1126
  const callbackPromise = waitForCallback(state);
@@ -661,8 +1166,8 @@ var loginCommand = new Command2("login").description("Log in to WaniWani").optio
661
1166
  });
662
1167
 
663
1168
  // src/commands/logout.ts
664
- import { Command as Command3 } from "commander";
665
- var logoutCommand = new Command3("logout").description("Log out from WaniWani").action(async (_, command) => {
1169
+ import { Command as Command4 } from "commander";
1170
+ var logoutCommand = new Command4("logout").description("Log out from WaniWani").action(async (_, command) => {
666
1171
  const globalOptions = command.optsWithGlobals();
667
1172
  const json = globalOptions.json ?? false;
668
1173
  try {
@@ -689,134 +1194,9 @@ var logoutCommand = new Command3("logout").description("Log out from WaniWani").
689
1194
  // src/commands/mcp/index.ts
690
1195
  import { Command as Command19 } from "commander";
691
1196
 
692
- // src/commands/mcp/create.ts
693
- import { Command as Command4 } from "commander";
694
- import ora2 from "ora";
695
-
696
- // src/lib/api.ts
697
- var ApiError = class extends CLIError {
698
- constructor(message, code, statusCode, details) {
699
- super(message, code, details);
700
- this.statusCode = statusCode;
701
- this.name = "ApiError";
702
- }
703
- };
704
- async function request(method, path, options) {
705
- const {
706
- body,
707
- requireAuth = true,
708
- headers: extraHeaders = {}
709
- } = options || {};
710
- const headers = {
711
- "Content-Type": "application/json",
712
- ...extraHeaders
713
- };
714
- if (requireAuth) {
715
- const token = await auth.getAccessToken();
716
- if (!token) {
717
- throw new AuthError(
718
- "Not logged in. Run 'waniwani login' to authenticate."
719
- );
720
- }
721
- headers.Authorization = `Bearer ${token}`;
722
- }
723
- const baseUrl = await config.getApiUrl();
724
- const url = `${baseUrl}${path}`;
725
- const response = await fetch(url, {
726
- method,
727
- headers,
728
- body: body ? JSON.stringify(body) : void 0
729
- });
730
- if (response.status === 204) {
731
- return void 0;
732
- }
733
- let data;
734
- let rawBody;
735
- try {
736
- rawBody = await response.text();
737
- data = JSON.parse(rawBody);
738
- } catch {
739
- throw new ApiError(
740
- rawBody || `Request failed with status ${response.status}`,
741
- "API_ERROR",
742
- response.status,
743
- { statusText: response.statusText }
744
- );
745
- }
746
- if (!response.ok || data.error) {
747
- const errorMessage = data.error?.message || data.message || data.error || rawBody || `Request failed with status ${response.status}`;
748
- const errorCode = data.error?.code || data.code || "API_ERROR";
749
- const errorDetails = {
750
- ...data.error?.details,
751
- statusText: response.statusText,
752
- ...data.error ? {} : { rawResponse: data }
753
- };
754
- const error = {
755
- code: errorCode,
756
- message: errorMessage,
757
- details: errorDetails
758
- };
759
- if (response.status === 401) {
760
- const refreshed = await auth.tryRefreshToken();
761
- if (refreshed) {
762
- return request(method, path, options);
763
- }
764
- throw new AuthError(
765
- "Session expired. Run 'waniwani login' to re-authenticate."
766
- );
767
- }
768
- throw new ApiError(
769
- error.message,
770
- error.code,
771
- response.status,
772
- error.details
773
- );
774
- }
775
- return data.data;
776
- }
777
- var api = {
778
- get: (path, options) => request("GET", path, options),
779
- post: (path, body, options) => request("POST", path, { body, ...options }),
780
- delete: (path, options) => request("DELETE", path, options),
781
- getBaseUrl: () => config.getApiUrl()
782
- };
783
-
784
- // src/commands/mcp/create.ts
785
- var createCommand = new Command4("create").description("Create a new MCP sandbox from template").argument("<name>", "Name for the MCP project").option("--global", "Save to global config instead of project config").action(async (name, options, command) => {
786
- const globalOptions = command.optsWithGlobals();
787
- const json = globalOptions.json ?? false;
788
- try {
789
- const spinner = ora2("Creating MCP sandbox...").start();
790
- const result = await api.post("/api/mcp/sandboxes", {
791
- name
792
- });
793
- spinner.succeed("MCP sandbox created");
794
- const cfg = options.global ? globalConfig : config;
795
- await cfg.setMcpId(result.id);
796
- if (json) {
797
- formatOutput({ ...result, scope: cfg.scope }, true);
798
- } else {
799
- console.log();
800
- formatSuccess(`MCP sandbox "${name}" created! (${cfg.scope})`, false);
801
- console.log();
802
- console.log(` MCP ID: ${result.id}`);
803
- console.log(` Sandbox ID: ${result.sandboxId}`);
804
- console.log(` Preview URL: ${result.previewUrl}`);
805
- console.log();
806
- console.log(`Next steps:`);
807
- console.log(` waniwani task "Add a tool that does X"`);
808
- console.log(` waniwani mcp test`);
809
- console.log(` waniwani mcp deploy`);
810
- }
811
- } catch (error) {
812
- handleError(error, json);
813
- process.exit(1);
814
- }
815
- });
816
-
817
1197
  // src/commands/mcp/delete.ts
818
1198
  import { Command as Command5 } from "commander";
819
- import ora3 from "ora";
1199
+ import ora4 from "ora";
820
1200
  var deleteCommand = new Command5("delete").description("Delete the MCP sandbox").option("--mcp-id <id>", "Specific MCP ID").action(async (options, command) => {
821
1201
  const globalOptions = command.optsWithGlobals();
822
1202
  const json = globalOptions.json ?? false;
@@ -828,7 +1208,7 @@ var deleteCommand = new Command5("delete").description("Delete the MCP sandbox")
828
1208
  throw new McpError("No active MCP. Use --mcp-id to specify one.");
829
1209
  }
830
1210
  }
831
- const spinner = ora3("Deleting MCP sandbox...").start();
1211
+ const spinner = ora4("Deleting MCP sandbox...").start();
832
1212
  await api.delete(`/api/mcp/sandboxes/${mcpId}`);
833
1213
  spinner.succeed("MCP sandbox deleted");
834
1214
  if (await config.getMcpId() === mcpId) {
@@ -847,7 +1227,7 @@ var deleteCommand = new Command5("delete").description("Delete the MCP sandbox")
847
1227
 
848
1228
  // src/commands/mcp/deploy.ts
849
1229
  import { Command as Command6 } from "commander";
850
- import ora4 from "ora";
1230
+ import ora5 from "ora";
851
1231
  var deployCommand = new Command6("deploy").description("Deploy MCP server to GitHub + Vercel from sandbox").option("--repo <name>", "GitHub repository name").option("--org <name>", "GitHub organization").option("--private", "Create private repository").option("--mcp-id <id>", "Specific MCP ID").action(async (options, command) => {
852
1232
  const globalOptions = command.optsWithGlobals();
853
1233
  const json = globalOptions.json ?? false;
@@ -861,7 +1241,7 @@ var deployCommand = new Command6("deploy").description("Deploy MCP server to Git
861
1241
  );
862
1242
  }
863
1243
  }
864
- const spinner = ora4("Deploying to GitHub...").start();
1244
+ const spinner = ora5("Deploying to GitHub...").start();
865
1245
  const result = await api.post(
866
1246
  `/api/admin/mcps/${mcpId}/deploy`,
867
1247
  {
@@ -896,9 +1276,9 @@ var deployCommand = new Command6("deploy").description("Deploy MCP server to Git
896
1276
  import { Command as Command10 } from "commander";
897
1277
 
898
1278
  // src/commands/mcp/file/list.ts
899
- import chalk4 from "chalk";
1279
+ import chalk5 from "chalk";
900
1280
  import { Command as Command7 } from "commander";
901
- import ora5 from "ora";
1281
+ import ora6 from "ora";
902
1282
  var listCommand = new Command7("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) => {
903
1283
  const globalOptions = command.optsWithGlobals();
904
1284
  const json = globalOptions.json ?? false;
@@ -912,7 +1292,7 @@ var listCommand = new Command7("list").description("List files in the MCP sandbo
912
1292
  );
913
1293
  }
914
1294
  }
915
- const spinner = ora5(`Listing ${path}...`).start();
1295
+ const spinner = ora6(`Listing ${path}...`).start();
916
1296
  const result = await api.get(
917
1297
  `/api/mcp/sandboxes/${mcpId}/files/list?path=${encodeURIComponent(path)}`
918
1298
  );
@@ -920,15 +1300,15 @@ var listCommand = new Command7("list").description("List files in the MCP sandbo
920
1300
  if (json) {
921
1301
  formatOutput(result, true);
922
1302
  } else {
923
- console.log(chalk4.bold(`
1303
+ console.log(chalk5.bold(`
924
1304
  Directory: ${result.path}
925
1305
  `));
926
1306
  if (result.entries.length === 0) {
927
1307
  console.log(" (empty)");
928
1308
  } else {
929
1309
  const rows = result.entries.map((entry) => {
930
- const name = entry.type === "directory" ? chalk4.blue(`${entry.name}/`) : entry.name;
931
- const size = entry.type === "directory" ? chalk4.gray("<dir>") : formatSize(entry.size);
1310
+ const name = entry.type === "directory" ? chalk5.blue(`${entry.name}/`) : entry.name;
1311
+ const size = entry.type === "directory" ? chalk5.gray("<dir>") : formatSize(entry.size);
932
1312
  return [name, size];
933
1313
  });
934
1314
  formatTable(["Name", "Size"], rows, false);
@@ -950,7 +1330,7 @@ function formatSize(bytes) {
950
1330
  // src/commands/mcp/file/read.ts
951
1331
  import { writeFile as writeFile4 } from "fs/promises";
952
1332
  import { Command as Command8 } from "commander";
953
- import ora6 from "ora";
1333
+ import ora7 from "ora";
954
1334
  var readCommand = new Command8("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) => {
955
1335
  const globalOptions = command.optsWithGlobals();
956
1336
  const json = globalOptions.json ?? false;
@@ -965,7 +1345,7 @@ var readCommand = new Command8("read").description("Read a file from the MCP san
965
1345
  }
966
1346
  }
967
1347
  const encoding = options.base64 ? "base64" : "utf8";
968
- const spinner = ora6(`Reading ${path}...`).start();
1348
+ const spinner = ora7(`Reading ${path}...`).start();
969
1349
  const result = await api.get(
970
1350
  `/api/mcp/sandboxes/${mcpId}/files?path=${encodeURIComponent(path)}&encoding=${encoding}`
971
1351
  );
@@ -996,9 +1376,9 @@ var readCommand = new Command8("read").description("Read a file from the MCP san
996
1376
  });
997
1377
 
998
1378
  // src/commands/mcp/file/write.ts
999
- import { readFile as readFile3 } from "fs/promises";
1379
+ import { readFile as readFile4 } from "fs/promises";
1000
1380
  import { Command as Command9 } from "commander";
1001
- import ora7 from "ora";
1381
+ import ora8 from "ora";
1002
1382
  var writeCommand = new Command9("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) => {
1003
1383
  const globalOptions = command.optsWithGlobals();
1004
1384
  const json = globalOptions.json ?? false;
@@ -1020,7 +1400,7 @@ var writeCommand = new Command9("write").description("Write a file to the MCP sa
1020
1400
  encoding = "base64";
1021
1401
  }
1022
1402
  } else if (options.file) {
1023
- const fileBuffer = await readFile3(options.file);
1403
+ const fileBuffer = await readFile4(options.file);
1024
1404
  if (options.base64) {
1025
1405
  content = fileBuffer.toString("base64");
1026
1406
  encoding = "base64";
@@ -1033,7 +1413,7 @@ var writeCommand = new Command9("write").description("Write a file to the MCP sa
1033
1413
  "MISSING_CONTENT"
1034
1414
  );
1035
1415
  }
1036
- const spinner = ora7(`Writing ${path}...`).start();
1416
+ const spinner = ora8(`Writing ${path}...`).start();
1037
1417
  const result = await api.post(
1038
1418
  `/api/mcp/sandboxes/${mcpId}/files`,
1039
1419
  {
@@ -1056,14 +1436,14 @@ var writeCommand = new Command9("write").description("Write a file to the MCP sa
1056
1436
  var fileCommand = new Command10("file").description("File operations in MCP sandbox").addCommand(readCommand).addCommand(writeCommand).addCommand(listCommand);
1057
1437
 
1058
1438
  // src/commands/mcp/list.ts
1059
- import chalk5 from "chalk";
1439
+ import chalk6 from "chalk";
1060
1440
  import { Command as Command11 } from "commander";
1061
- import ora8 from "ora";
1441
+ import ora9 from "ora";
1062
1442
  var listCommand2 = new Command11("list").description("List all MCPs in your organization").option("--all", "Include stopped/expired MCPs").action(async (options, command) => {
1063
1443
  const globalOptions = command.optsWithGlobals();
1064
1444
  const json = globalOptions.json ?? false;
1065
1445
  try {
1066
- const spinner = ora8("Fetching MCPs...").start();
1446
+ const spinner = ora9("Fetching MCPs...").start();
1067
1447
  const mcps = await api.get(
1068
1448
  `/api/mcp/sandboxes${options.all ? "?all=true" : ""}`
1069
1449
  );
@@ -1086,12 +1466,12 @@ var listCommand2 = new Command11("list").description("List all MCPs in your orga
1086
1466
  console.log("\nCreate a new MCP sandbox: waniwani mcp create <name>");
1087
1467
  return;
1088
1468
  }
1089
- console.log(chalk5.bold("\nMCPs:\n"));
1469
+ console.log(chalk6.bold("\nMCPs:\n"));
1090
1470
  const rows = mcps.map((m) => {
1091
1471
  const isActive = m.id === activeMcpId;
1092
- const statusColor = m.status === "active" ? chalk5.green : m.status === "stopped" ? chalk5.red : chalk5.yellow;
1472
+ const statusColor = m.status === "active" ? chalk6.green : m.status === "stopped" ? chalk6.red : chalk6.yellow;
1093
1473
  return [
1094
- isActive ? chalk5.cyan(`* ${m.id.slice(0, 8)}`) : ` ${m.id.slice(0, 8)}`,
1474
+ isActive ? chalk6.cyan(`* ${m.id.slice(0, 8)}`) : ` ${m.id.slice(0, 8)}`,
1095
1475
  m.name,
1096
1476
  statusColor(m.status),
1097
1477
  m.previewUrl,
@@ -1105,7 +1485,7 @@ var listCommand2 = new Command11("list").description("List all MCPs in your orga
1105
1485
  );
1106
1486
  console.log();
1107
1487
  if (activeMcpId) {
1108
- console.log(`Active MCP: ${chalk5.cyan(activeMcpId.slice(0, 8))}`);
1488
+ console.log(`Active MCP: ${chalk6.cyan(activeMcpId.slice(0, 8))}`);
1109
1489
  }
1110
1490
  console.log("\nSelect an MCP: waniwani mcp use <name>");
1111
1491
  }
@@ -1116,9 +1496,9 @@ var listCommand2 = new Command11("list").description("List all MCPs in your orga
1116
1496
  });
1117
1497
 
1118
1498
  // src/commands/mcp/logs.ts
1119
- import chalk6 from "chalk";
1499
+ import chalk7 from "chalk";
1120
1500
  import { Command as Command12 } from "commander";
1121
- import ora9 from "ora";
1501
+ import ora10 from "ora";
1122
1502
  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) => {
1123
1503
  const globalOptions = command.optsWithGlobals();
1124
1504
  const json = globalOptions.json ?? false;
@@ -1150,7 +1530,7 @@ var logsCommand = new Command12("logs").description("Stream logs from the MCP se
1150
1530
  }
1151
1531
  let cmdId = cmdIdArg;
1152
1532
  if (!cmdId) {
1153
- const spinner = ora9("Getting server status...").start();
1533
+ const spinner = ora10("Getting server status...").start();
1154
1534
  const status = await api.post(
1155
1535
  `/api/mcp/sandboxes/${mcpId}/server`,
1156
1536
  { action: "status" }
@@ -1167,8 +1547,8 @@ var logsCommand = new Command12("logs").description("Stream logs from the MCP se
1167
1547
  const streamParam = options.follow ? "?stream=true" : "";
1168
1548
  const url = `${baseUrl}/api/mcp/sandboxes/${mcpId}/commands/${cmdId}${streamParam}`;
1169
1549
  if (!json) {
1170
- console.log(chalk6.gray(`Streaming logs for command ${cmdId}...`));
1171
- console.log(chalk6.gray("Press Ctrl+C to stop\n"));
1550
+ console.log(chalk7.gray(`Streaming logs for command ${cmdId}...`));
1551
+ console.log(chalk7.gray("Press Ctrl+C to stop\n"));
1172
1552
  }
1173
1553
  const response = await fetch(url, {
1174
1554
  method: "GET",
@@ -1192,10 +1572,10 @@ var logsCommand = new Command12("logs").description("Stream logs from the MCP se
1192
1572
  process.stdout.write(data.stdout);
1193
1573
  }
1194
1574
  if (data.stderr) {
1195
- process.stderr.write(chalk6.red(data.stderr));
1575
+ process.stderr.write(chalk7.red(data.stderr));
1196
1576
  }
1197
1577
  if (data.exitCode !== void 0) {
1198
- console.log(chalk6.gray(`
1578
+ console.log(chalk7.gray(`
1199
1579
  Exit code: ${data.exitCode}`));
1200
1580
  }
1201
1581
  }
@@ -1234,18 +1614,18 @@ Exit code: ${data.exitCode}`));
1234
1614
  if (event.stream === "stdout") {
1235
1615
  process.stdout.write(event.data);
1236
1616
  } else if (event.stream === "stderr") {
1237
- process.stderr.write(chalk6.red(event.data));
1617
+ process.stderr.write(chalk7.red(event.data));
1238
1618
  }
1239
1619
  }
1240
1620
  if (event.exitCode !== void 0) {
1241
- const exitColor = event.exitCode === 0 ? chalk6.green : chalk6.red;
1621
+ const exitColor = event.exitCode === 0 ? chalk7.green : chalk7.red;
1242
1622
  console.log(
1243
1623
  exitColor(`
1244
1624
  Process exited with code ${event.exitCode}`)
1245
1625
  );
1246
1626
  }
1247
1627
  if (event.error) {
1248
- console.error(chalk6.red(`
1628
+ console.error(chalk7.red(`
1249
1629
  Error: ${event.error}`));
1250
1630
  }
1251
1631
  } catch {
@@ -1266,9 +1646,9 @@ Error: ${event.error}`));
1266
1646
  });
1267
1647
 
1268
1648
  // src/commands/mcp/run-command.ts
1269
- import chalk7 from "chalk";
1649
+ import chalk8 from "chalk";
1270
1650
  import { Command as Command13 } from "commander";
1271
- import ora10 from "ora";
1651
+ import ora11 from "ora";
1272
1652
  var runCommandCommand = new Command13("run-command").description("Run a command in the MCP sandbox").argument("<command>", "Command to run").argument("[args...]", "Command arguments").option("--mcp-id <id>", "Specific MCP ID").option("--cwd <path>", "Working directory").option(
1273
1653
  "--timeout <ms>",
1274
1654
  "Command timeout in milliseconds (default: 30000, max: 300000)"
@@ -1286,7 +1666,7 @@ var runCommandCommand = new Command13("run-command").description("Run a command
1286
1666
  }
1287
1667
  }
1288
1668
  const timeout = options.timeout ? Number.parseInt(options.timeout, 10) : void 0;
1289
- const spinner = ora10(`Running: ${cmd} ${args.join(" ")}`.trim()).start();
1669
+ const spinner = ora11(`Running: ${cmd} ${args.join(" ")}`.trim()).start();
1290
1670
  const result = await api.post(
1291
1671
  `/api/mcp/sandboxes/${mcpId}/commands`,
1292
1672
  {
@@ -1301,7 +1681,7 @@ var runCommandCommand = new Command13("run-command").description("Run a command
1301
1681
  formatOutput(result, true);
1302
1682
  } else {
1303
1683
  const cmdLine = [cmd, ...args].join(" ");
1304
- console.log(chalk7.gray(`$ ${cmdLine}`));
1684
+ console.log(chalk8.gray(`$ ${cmdLine}`));
1305
1685
  console.log();
1306
1686
  if (result.stdout) {
1307
1687
  process.stdout.write(result.stdout);
@@ -1310,16 +1690,16 @@ var runCommandCommand = new Command13("run-command").description("Run a command
1310
1690
  }
1311
1691
  }
1312
1692
  if (result.stderr) {
1313
- process.stderr.write(chalk7.red(result.stderr));
1693
+ process.stderr.write(chalk8.red(result.stderr));
1314
1694
  if (!result.stderr.endsWith("\n")) {
1315
1695
  process.stderr.write("\n");
1316
1696
  }
1317
1697
  }
1318
1698
  console.log();
1319
- const exitColor = result.exitCode === 0 ? chalk7.green : chalk7.red;
1699
+ const exitColor = result.exitCode === 0 ? chalk8.green : chalk8.red;
1320
1700
  console.log(
1321
1701
  exitColor(`Exit code: ${result.exitCode}`),
1322
- chalk7.gray(`(${(result.duration / 1e3).toFixed(2)}s)`)
1702
+ chalk8.gray(`(${(result.duration / 1e3).toFixed(2)}s)`)
1323
1703
  );
1324
1704
  }
1325
1705
  if (result.exitCode !== 0) {
@@ -1332,9 +1712,9 @@ var runCommandCommand = new Command13("run-command").description("Run a command
1332
1712
  });
1333
1713
 
1334
1714
  // src/commands/mcp/start.ts
1335
- import chalk8 from "chalk";
1715
+ import chalk9 from "chalk";
1336
1716
  import { Command as Command14 } from "commander";
1337
- import ora11 from "ora";
1717
+ import ora12 from "ora";
1338
1718
  var startCommand = new Command14("start").description("Start the MCP server (npm run dev)").option("--mcp-id <id>", "Specific MCP ID").action(async (options, command) => {
1339
1719
  const globalOptions = command.optsWithGlobals();
1340
1720
  const json = globalOptions.json ?? false;
@@ -1348,7 +1728,7 @@ var startCommand = new Command14("start").description("Start the MCP server (npm
1348
1728
  );
1349
1729
  }
1350
1730
  }
1351
- const spinner = ora11("Starting MCP server...").start();
1731
+ const spinner = ora12("Starting MCP server...").start();
1352
1732
  const result = await api.post(
1353
1733
  `/api/mcp/sandboxes/${mcpId}/server`,
1354
1734
  { action: "start" }
@@ -1361,13 +1741,13 @@ var startCommand = new Command14("start").description("Start the MCP server (npm
1361
1741
  formatList(
1362
1742
  [
1363
1743
  { label: "Command ID", value: result.cmdId },
1364
- { label: "Preview URL", value: chalk8.cyan(result.previewUrl) }
1744
+ { label: "Preview URL", value: chalk9.cyan(result.previewUrl) }
1365
1745
  ],
1366
1746
  false
1367
1747
  );
1368
1748
  console.log();
1369
1749
  console.log(
1370
- chalk8.gray("Run 'waniwani mcp logs' to stream server output")
1750
+ chalk9.gray("Run 'waniwani mcp logs' to stream server output")
1371
1751
  );
1372
1752
  }
1373
1753
  } catch (error) {
@@ -1377,9 +1757,9 @@ var startCommand = new Command14("start").description("Start the MCP server (npm
1377
1757
  });
1378
1758
 
1379
1759
  // src/commands/mcp/status.ts
1380
- import chalk9 from "chalk";
1760
+ import chalk10 from "chalk";
1381
1761
  import { Command as Command15 } from "commander";
1382
- import ora12 from "ora";
1762
+ import ora13 from "ora";
1383
1763
  var statusCommand = new Command15("status").description("Show current MCP sandbox status").option("--mcp-id <id>", "Specific MCP ID").action(async (options, command) => {
1384
1764
  const globalOptions = command.optsWithGlobals();
1385
1765
  const json = globalOptions.json ?? false;
@@ -1393,7 +1773,7 @@ var statusCommand = new Command15("status").description("Show current MCP sandbo
1393
1773
  );
1394
1774
  }
1395
1775
  }
1396
- const spinner = ora12("Fetching MCP status...").start();
1776
+ const spinner = ora13("Fetching MCP status...").start();
1397
1777
  const [result, serverStatus] = await Promise.all([
1398
1778
  api.get(`/api/mcp/sandboxes/${mcpId}`),
1399
1779
  api.post(`/api/mcp/sandboxes/${mcpId}/server`, {
@@ -1408,9 +1788,9 @@ var statusCommand = new Command15("status").description("Show current MCP sandbo
1408
1788
  if (json) {
1409
1789
  formatOutput({ ...result, server: serverStatus }, true);
1410
1790
  } else {
1411
- const statusColor = result.status === "active" ? chalk9.green : chalk9.red;
1791
+ const statusColor = result.status === "active" ? chalk10.green : chalk10.red;
1412
1792
  const serverRunning = serverStatus.running;
1413
- const serverStatusColor = serverRunning ? chalk9.green : chalk9.yellow;
1793
+ const serverStatusColor = serverRunning ? chalk10.green : chalk10.yellow;
1414
1794
  formatList(
1415
1795
  [
1416
1796
  { label: "MCP ID", value: result.id },
@@ -1437,7 +1817,7 @@ var statusCommand = new Command15("status").description("Show current MCP sandbo
1437
1817
 
1438
1818
  // src/commands/mcp/stop.ts
1439
1819
  import { Command as Command16 } from "commander";
1440
- import ora13 from "ora";
1820
+ import ora14 from "ora";
1441
1821
  var stopCommand = new Command16("stop").description("Stop the MCP server process").option("--mcp-id <id>", "Specific MCP ID").action(async (options, command) => {
1442
1822
  const globalOptions = command.optsWithGlobals();
1443
1823
  const json = globalOptions.json ?? false;
@@ -1451,7 +1831,7 @@ var stopCommand = new Command16("stop").description("Stop the MCP server process
1451
1831
  );
1452
1832
  }
1453
1833
  }
1454
- const spinner = ora13("Stopping MCP server...").start();
1834
+ const spinner = ora14("Stopping MCP server...").start();
1455
1835
  const result = await api.post(
1456
1836
  `/api/mcp/sandboxes/${mcpId}/server`,
1457
1837
  { action: "stop" }
@@ -1473,9 +1853,9 @@ var stopCommand = new Command16("stop").description("Stop the MCP server process
1473
1853
  });
1474
1854
 
1475
1855
  // src/commands/mcp/test.ts
1476
- import chalk10 from "chalk";
1856
+ import chalk11 from "chalk";
1477
1857
  import { Command as Command17 } from "commander";
1478
- import ora14 from "ora";
1858
+ import ora15 from "ora";
1479
1859
  var testCommand = new Command17("test").description("Test MCP tools via the sandbox").argument("[tool]", "Tool name to test (lists tools if omitted)").argument("[args...]", "JSON arguments for the tool").option("--mcp-id <id>", "Specific MCP ID").action(
1480
1860
  async (tool, args, options, command) => {
1481
1861
  const globalOptions = command.optsWithGlobals();
@@ -1491,7 +1871,7 @@ var testCommand = new Command17("test").description("Test MCP tools via the sand
1491
1871
  }
1492
1872
  }
1493
1873
  if (!tool) {
1494
- const spinner = ora14("Fetching available tools...").start();
1874
+ const spinner = ora15("Fetching available tools...").start();
1495
1875
  const result = await api.post(
1496
1876
  `/api/mcp/sandboxes/${mcpId}/test`,
1497
1877
  { action: "list" }
@@ -1504,7 +1884,7 @@ var testCommand = new Command17("test").description("Test MCP tools via the sand
1504
1884
  if (tools.length === 0) {
1505
1885
  console.log("No tools available.");
1506
1886
  } else {
1507
- console.log(chalk10.bold("\nAvailable Tools:\n"));
1887
+ console.log(chalk11.bold("\nAvailable Tools:\n"));
1508
1888
  formatTable(
1509
1889
  ["Name", "Description"],
1510
1890
  tools.map((t) => [t.name, t.description || "No description"]),
@@ -1527,7 +1907,7 @@ Test a tool: waniwani mcp test <tool-name> '{"arg": "value"}'`
1527
1907
  );
1528
1908
  }
1529
1909
  }
1530
- const spinner = ora14(`Calling tool "${tool}"...`).start();
1910
+ const spinner = ora15(`Calling tool "${tool}"...`).start();
1531
1911
  const startTime = Date.now();
1532
1912
  const result = await api.post(
1533
1913
  `/api/mcp/sandboxes/${mcpId}/test`,
@@ -1548,11 +1928,11 @@ Test a tool: waniwani mcp test <tool-name> '{"arg": "value"}'`
1548
1928
  if (json) {
1549
1929
  formatOutput(output, true);
1550
1930
  } else {
1551
- console.log(chalk10.bold("\nTool Result:\n"));
1552
- console.log(chalk10.gray("Tool:"), tool);
1553
- console.log(chalk10.gray("Input:"), JSON.stringify(toolArgs));
1554
- console.log(chalk10.gray("Duration:"), `${duration}ms`);
1555
- console.log(chalk10.gray("Result:"));
1931
+ console.log(chalk11.bold("\nTool Result:\n"));
1932
+ console.log(chalk11.gray("Tool:"), tool);
1933
+ console.log(chalk11.gray("Input:"), JSON.stringify(toolArgs));
1934
+ console.log(chalk11.gray("Duration:"), `${duration}ms`);
1935
+ console.log(chalk11.gray("Result:"));
1556
1936
  console.log(JSON.stringify(result.result, null, 2));
1557
1937
  }
1558
1938
  }
@@ -1565,12 +1945,12 @@ Test a tool: waniwani mcp test <tool-name> '{"arg": "value"}'`
1565
1945
 
1566
1946
  // src/commands/mcp/use.ts
1567
1947
  import { Command as Command18 } from "commander";
1568
- import ora15 from "ora";
1948
+ import ora16 from "ora";
1569
1949
  var useCommand = new Command18("use").description("Select an MCP to use for subsequent commands").argument("<name>", "Name of the MCP to use").option("--global", "Save to global config instead of project config").action(async (name, options, command) => {
1570
1950
  const globalOptions = command.optsWithGlobals();
1571
1951
  const json = globalOptions.json ?? false;
1572
1952
  try {
1573
- const spinner = ora15("Fetching MCPs...").start();
1953
+ const spinner = ora16("Fetching MCPs...").start();
1574
1954
  const mcps = await api.get("/api/admin/mcps");
1575
1955
  spinner.stop();
1576
1956
  const mcp = mcps.find((m) => m.name === name);
@@ -1606,20 +1986,20 @@ var useCommand = new Command18("use").description("Select an MCP to use for subs
1606
1986
  });
1607
1987
 
1608
1988
  // src/commands/mcp/index.ts
1609
- var mcpCommand = new Command19("mcp").description("MCP sandbox management commands").addCommand(createCommand).addCommand(listCommand2).addCommand(useCommand).addCommand(statusCommand).addCommand(startCommand).addCommand(stopCommand).addCommand(logsCommand).addCommand(deleteCommand).addCommand(testCommand).addCommand(deployCommand).addCommand(fileCommand).addCommand(runCommandCommand);
1989
+ var mcpCommand = new Command19("mcp").description("MCP sandbox management commands").addCommand(listCommand2).addCommand(useCommand).addCommand(statusCommand).addCommand(startCommand).addCommand(stopCommand).addCommand(logsCommand).addCommand(deleteCommand).addCommand(testCommand).addCommand(deployCommand).addCommand(fileCommand).addCommand(runCommandCommand);
1610
1990
 
1611
1991
  // src/commands/org/index.ts
1612
1992
  import { Command as Command22 } from "commander";
1613
1993
 
1614
1994
  // src/commands/org/list.ts
1615
- import chalk11 from "chalk";
1995
+ import chalk12 from "chalk";
1616
1996
  import { Command as Command20 } from "commander";
1617
- import ora16 from "ora";
1997
+ import ora17 from "ora";
1618
1998
  var listCommand3 = new Command20("list").description("List your organizations").action(async (_, command) => {
1619
1999
  const globalOptions = command.optsWithGlobals();
1620
2000
  const json = globalOptions.json ?? false;
1621
2001
  try {
1622
- const spinner = ora16("Fetching organizations...").start();
2002
+ const spinner = ora17("Fetching organizations...").start();
1623
2003
  const result = await api.get("/api/oauth/orgs");
1624
2004
  spinner.stop();
1625
2005
  const { orgs, activeOrgId } = result;
@@ -1639,11 +2019,11 @@ var listCommand3 = new Command20("list").description("List your organizations").
1639
2019
  console.log("No organizations found.");
1640
2020
  return;
1641
2021
  }
1642
- console.log(chalk11.bold("\nOrganizations:\n"));
2022
+ console.log(chalk12.bold("\nOrganizations:\n"));
1643
2023
  const rows = orgs.map((o) => {
1644
2024
  const isActive = o.id === activeOrgId;
1645
2025
  return [
1646
- isActive ? chalk11.cyan(`* ${o.name}`) : ` ${o.name}`,
2026
+ isActive ? chalk12.cyan(`* ${o.name}`) : ` ${o.name}`,
1647
2027
  o.slug,
1648
2028
  o.role
1649
2029
  ];
@@ -1653,7 +2033,7 @@ var listCommand3 = new Command20("list").description("List your organizations").
1653
2033
  if (activeOrgId) {
1654
2034
  const activeOrg = orgs.find((o) => o.id === activeOrgId);
1655
2035
  if (activeOrg) {
1656
- console.log(`Active organization: ${chalk11.cyan(activeOrg.name)}`);
2036
+ console.log(`Active organization: ${chalk12.cyan(activeOrg.name)}`);
1657
2037
  }
1658
2038
  }
1659
2039
  console.log("\nSwitch organization: waniwani org switch <name>");
@@ -1666,12 +2046,12 @@ var listCommand3 = new Command20("list").description("List your organizations").
1666
2046
 
1667
2047
  // src/commands/org/switch.ts
1668
2048
  import { Command as Command21 } from "commander";
1669
- import ora17 from "ora";
2049
+ import ora18 from "ora";
1670
2050
  var switchCommand = new Command21("switch").description("Switch to a different organization").argument("<name>", "Name or slug of the organization to switch to").action(async (name, _, command) => {
1671
2051
  const globalOptions = command.optsWithGlobals();
1672
2052
  const json = globalOptions.json ?? false;
1673
2053
  try {
1674
- const spinner = ora17("Fetching organizations...").start();
2054
+ const spinner = ora18("Fetching organizations...").start();
1675
2055
  const { orgs } = await api.get("/api/oauth/orgs");
1676
2056
  const org = orgs.find((o) => o.name === name || o.slug === name);
1677
2057
  if (!org) {
@@ -1706,11 +2086,92 @@ var switchCommand = new Command21("switch").description("Switch to a different o
1706
2086
  // src/commands/org/index.ts
1707
2087
  var orgCommand = new Command22("org").description("Organization management commands").addCommand(listCommand3).addCommand(switchCommand);
1708
2088
 
1709
- // src/commands/task.ts
1710
- import chalk12 from "chalk";
2089
+ // src/commands/push.ts
2090
+ import chalk13 from "chalk";
1711
2091
  import { Command as Command23 } from "commander";
1712
- import ora18 from "ora";
1713
- var taskCommand = new Command23("task").description("Send a task to Claude running in the sandbox").argument("<prompt>", "Task description/prompt").option("--mcp-id <id>", "Specific MCP ID").option("--model <model>", "Claude model to use").option("--max-steps <n>", "Maximum tool use steps").action(async (prompt, options, command) => {
2092
+ import ora19 from "ora";
2093
+ var BATCH_SIZE2 = 50;
2094
+ var pushCommand = new Command23("push").description("Sync local files to MCP sandbox").option("--dry-run", "Show what would be synced without uploading").action(async (options, command) => {
2095
+ const globalOptions = command.optsWithGlobals();
2096
+ const json = globalOptions.json ?? false;
2097
+ try {
2098
+ const cwd = process.cwd();
2099
+ const projectRoot = await findProjectRoot(cwd);
2100
+ if (!projectRoot) {
2101
+ throw new CLIError(
2102
+ "Not in a WaniWani project. Run 'waniwani init <name>' first.",
2103
+ "NOT_IN_PROJECT"
2104
+ );
2105
+ }
2106
+ const mcpId = await loadProjectMcpId(projectRoot);
2107
+ if (!mcpId) {
2108
+ throw new CLIError(
2109
+ "No MCP ID found in project config. Run 'waniwani init <name>' first.",
2110
+ "NO_MCP_ID"
2111
+ );
2112
+ }
2113
+ const spinner = ora19("Collecting files...").start();
2114
+ const files = await collectFiles(projectRoot);
2115
+ if (files.length === 0) {
2116
+ spinner.info("No files to sync");
2117
+ if (json) {
2118
+ formatOutput({ synced: 0, files: [] }, true);
2119
+ }
2120
+ return;
2121
+ }
2122
+ spinner.text = `Found ${files.length} files`;
2123
+ if (options.dryRun) {
2124
+ spinner.stop();
2125
+ console.log();
2126
+ console.log(chalk13.bold("Files that would be synced:"));
2127
+ for (const file of files) {
2128
+ console.log(` ${file.path}`);
2129
+ }
2130
+ console.log();
2131
+ console.log(`Total: ${files.length} files`);
2132
+ return;
2133
+ }
2134
+ const allWritten = [];
2135
+ const totalBatches = Math.ceil(files.length / BATCH_SIZE2);
2136
+ for (let i = 0; i < totalBatches; i++) {
2137
+ const batch = files.slice(i * BATCH_SIZE2, (i + 1) * BATCH_SIZE2);
2138
+ spinner.text = `Syncing files (${i + 1}/${totalBatches})...`;
2139
+ const result = await api.post(
2140
+ `/api/mcp/sandboxes/${mcpId}/files`,
2141
+ {
2142
+ files: batch.map((f) => ({
2143
+ path: f.path,
2144
+ content: f.content,
2145
+ encoding: f.encoding
2146
+ }))
2147
+ }
2148
+ );
2149
+ allWritten.push(...result.written);
2150
+ }
2151
+ spinner.succeed(`Synced ${allWritten.length} files`);
2152
+ if (json) {
2153
+ formatOutput(
2154
+ {
2155
+ synced: allWritten.length,
2156
+ files: allWritten
2157
+ },
2158
+ true
2159
+ );
2160
+ } else {
2161
+ console.log();
2162
+ formatSuccess(`${allWritten.length} files synced to sandbox`, false);
2163
+ }
2164
+ } catch (error) {
2165
+ handleError(error, json);
2166
+ process.exit(1);
2167
+ }
2168
+ });
2169
+
2170
+ // src/commands/task.ts
2171
+ import chalk14 from "chalk";
2172
+ import { Command as Command24 } from "commander";
2173
+ import ora20 from "ora";
2174
+ var taskCommand = new Command24("task").description("Send a task to Claude running in the sandbox").argument("<prompt>", "Task description/prompt").option("--mcp-id <id>", "Specific MCP ID").option("--model <model>", "Claude model to use").option("--max-steps <n>", "Maximum tool use steps").action(async (prompt, options, command) => {
1714
2175
  const globalOptions = command.optsWithGlobals();
1715
2176
  const json = globalOptions.json ?? false;
1716
2177
  try {
@@ -1734,10 +2195,10 @@ var taskCommand = new Command23("task").description("Send a task to Claude runni
1734
2195
  const maxSteps = options.maxSteps ? Number.parseInt(options.maxSteps, 10) : defaults.maxSteps;
1735
2196
  if (!json) {
1736
2197
  console.log();
1737
- console.log(chalk12.bold("Task:"), prompt);
2198
+ console.log(chalk14.bold("Task:"), prompt);
1738
2199
  console.log();
1739
2200
  }
1740
- const spinner = ora18("Starting task...").start();
2201
+ const spinner = ora20("Starting task...").start();
1741
2202
  const baseUrl = await api.getBaseUrl();
1742
2203
  const response = await fetch(
1743
2204
  `${baseUrl}/api/mcp/sandboxes/${mcpId}/task`,
@@ -1791,7 +2252,7 @@ var taskCommand = new Command23("task").description("Send a task to Claude runni
1791
2252
  const event = parsed;
1792
2253
  steps.push({ type: "text", text: event.content });
1793
2254
  if (!json && event.content) {
1794
- console.log(chalk12.white(event.content));
2255
+ console.log(chalk14.white(event.content));
1795
2256
  }
1796
2257
  } else if (parsed.type === "tool_call") {
1797
2258
  const event = parsed;
@@ -1802,24 +2263,24 @@ var taskCommand = new Command23("task").description("Send a task to Claude runni
1802
2263
  output: event.output
1803
2264
  });
1804
2265
  if (!json) {
1805
- console.log(chalk12.cyan(`> Using tool: ${event.tool}`));
2266
+ console.log(chalk14.cyan(`> Using tool: ${event.tool}`));
1806
2267
  if (event.input?.command) {
1807
- console.log(chalk12.gray(` $ ${event.input.command}`));
2268
+ console.log(chalk14.gray(` $ ${event.input.command}`));
1808
2269
  }
1809
2270
  if (event.output) {
1810
2271
  const outputLines = event.output.split("\n");
1811
2272
  if (outputLines.length > 10) {
1812
2273
  console.log(
1813
- chalk12.gray(outputLines.slice(0, 5).join("\n"))
2274
+ chalk14.gray(outputLines.slice(0, 5).join("\n"))
1814
2275
  );
1815
2276
  console.log(
1816
- chalk12.gray(
2277
+ chalk14.gray(
1817
2278
  ` ... (${outputLines.length - 10} more lines)`
1818
2279
  )
1819
2280
  );
1820
- console.log(chalk12.gray(outputLines.slice(-5).join("\n")));
2281
+ console.log(chalk14.gray(outputLines.slice(-5).join("\n")));
1821
2282
  } else {
1822
- console.log(chalk12.gray(event.output));
2283
+ console.log(chalk14.gray(event.output));
1823
2284
  }
1824
2285
  }
1825
2286
  console.log();
@@ -1846,12 +2307,12 @@ var taskCommand = new Command23("task").description("Send a task to Claude runni
1846
2307
  } else {
1847
2308
  console.log();
1848
2309
  console.log(
1849
- chalk12.green("\u2713"),
2310
+ chalk14.green("\u2713"),
1850
2311
  `Task completed in ${finalStepCount} steps.`
1851
2312
  );
1852
2313
  if (maxStepsReached) {
1853
2314
  console.log(
1854
- chalk12.yellow("\u26A0"),
2315
+ chalk14.yellow("\u26A0"),
1855
2316
  "Maximum steps reached. Task may be incomplete."
1856
2317
  );
1857
2318
  }
@@ -1864,10 +2325,12 @@ var taskCommand = new Command23("task").description("Send a task to Claude runni
1864
2325
 
1865
2326
  // src/cli.ts
1866
2327
  var version = "0.1.0";
1867
- var program = new Command24().name("waniwani").description("WaniWani CLI for MCP development workflow").version(version).option("--json", "Output results as JSON").option("--verbose", "Enable verbose logging");
2328
+ var program = new Command25().name("waniwani").description("WaniWani CLI for MCP development workflow").version(version).option("--json", "Output results as JSON").option("--verbose", "Enable verbose logging");
1868
2329
  program.addCommand(loginCommand);
1869
2330
  program.addCommand(logoutCommand);
1870
2331
  program.addCommand(initCommand);
2332
+ program.addCommand(pushCommand);
2333
+ program.addCommand(devCommand);
1871
2334
  program.addCommand(mcpCommand);
1872
2335
  program.addCommand(taskCommand);
1873
2336
  program.addCommand(orgCommand);