@waniwani/cli 0.0.26 → 0.0.29

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,624 @@ 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 { mkdir as mkdir3, readdir, readFile as readFile3, stat, writeFile as writeFile3 } 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 pullFilesFromSandbox(mcpId, targetDir) {
478
+ const result = await api.get(
479
+ `/api/mcp/sandboxes/${mcpId}/files/pull`
480
+ );
481
+ const writtenFiles = [];
482
+ for (const file of result.files) {
483
+ const localPath = join3(targetDir, file.path);
484
+ const dir = dirname(localPath);
485
+ await mkdir3(dir, { recursive: true });
486
+ if (file.encoding === "base64") {
487
+ await writeFile3(localPath, Buffer.from(file.content, "base64"));
488
+ } else {
489
+ await writeFile3(localPath, file.content, "utf8");
490
+ }
491
+ writtenFiles.push(file.path);
492
+ }
493
+ return { count: writtenFiles.length, files: writtenFiles };
494
+ }
495
+ async function collectSingleFile(projectRoot, filePath) {
496
+ const fullPath = join3(projectRoot, filePath);
497
+ const relativePath = relative(projectRoot, fullPath);
498
+ if (!existsSync2(fullPath)) {
499
+ return null;
500
+ }
501
+ try {
502
+ const fileStat = await stat(fullPath);
503
+ if (!fileStat.isFile()) {
504
+ return null;
505
+ }
506
+ const content = await readFile3(fullPath);
507
+ const isBinary = isBinaryPath(fullPath) || detectBinary(content);
508
+ return {
509
+ path: relativePath,
510
+ content: isBinary ? content.toString("base64") : content.toString("utf8"),
511
+ encoding: isBinary ? "base64" : "utf8"
512
+ };
513
+ } catch {
514
+ return null;
515
+ }
516
+ }
517
+
518
+ // src/commands/dev.ts
519
+ var BATCH_SIZE = 50;
520
+ var DEFAULT_DEBOUNCE_MS = 300;
521
+ var devCommand = new Command("dev").description("Watch and sync files to MCP sandbox").option("--no-initial-sync", "Skip initial sync of all files").option(
522
+ "--debounce <ms>",
523
+ "Debounce delay in milliseconds",
524
+ String(DEFAULT_DEBOUNCE_MS)
525
+ ).action(async (options, command) => {
526
+ const globalOptions = command.optsWithGlobals();
527
+ const json = globalOptions.json ?? false;
528
+ try {
529
+ const cwd = process.cwd();
530
+ const projectRoot = await findProjectRoot(cwd);
531
+ if (!projectRoot) {
532
+ throw new CLIError(
533
+ "Not in a WaniWani project. Run 'waniwani init <name>' first.",
534
+ "NOT_IN_PROJECT"
535
+ );
536
+ }
537
+ const mcpId = await loadProjectMcpId(projectRoot);
538
+ if (!mcpId) {
539
+ throw new CLIError(
540
+ "No MCP ID found in project config. Run 'waniwani init <name>' first.",
541
+ "NO_MCP_ID"
542
+ );
543
+ }
544
+ if (options.initialSync !== false) {
545
+ const spinner = ora("Initial sync...").start();
546
+ const files = await collectFiles(projectRoot);
547
+ if (files.length > 0) {
548
+ const totalBatches = Math.ceil(files.length / BATCH_SIZE);
549
+ let synced = 0;
550
+ for (let i = 0; i < totalBatches; i++) {
551
+ const batch = files.slice(i * BATCH_SIZE, (i + 1) * BATCH_SIZE);
552
+ spinner.text = `Syncing (${i + 1}/${totalBatches})...`;
553
+ const result = await api.post(
554
+ `/api/mcp/sandboxes/${mcpId}/files`,
555
+ {
556
+ files: batch.map((f) => ({
557
+ path: f.path,
558
+ content: f.content,
559
+ encoding: f.encoding
560
+ }))
561
+ }
562
+ );
563
+ synced += result.written.length;
564
+ }
565
+ spinner.succeed(`Initial sync complete (${synced} files)`);
566
+ } else {
567
+ spinner.info("No files to sync");
568
+ }
569
+ }
570
+ const ig = await loadIgnorePatterns(projectRoot);
571
+ const debounceMs = Number.parseInt(options.debounce, 10) || DEFAULT_DEBOUNCE_MS;
572
+ const syncFile = debounce(async (filePath) => {
573
+ const relativePath = relative2(projectRoot, filePath);
574
+ if (ig.ignores(relativePath)) {
575
+ return;
576
+ }
577
+ const file = await collectSingleFile(projectRoot, relativePath);
578
+ if (!file) {
579
+ console.log(chalk2.yellow("Skipped:"), relativePath);
580
+ return;
581
+ }
582
+ try {
583
+ await api.post(
584
+ `/api/mcp/sandboxes/${mcpId}/files`,
585
+ {
586
+ files: [
587
+ {
588
+ path: file.path,
589
+ content: file.content,
590
+ encoding: file.encoding
591
+ }
592
+ ]
593
+ }
594
+ );
595
+ console.log(chalk2.green("Synced:"), relativePath);
596
+ } catch (error) {
597
+ console.log(chalk2.red("Failed:"), relativePath);
598
+ if (globalOptions.verbose) {
599
+ console.error(error);
600
+ }
601
+ }
602
+ }, debounceMs);
603
+ console.log();
604
+ console.log(chalk2.bold("Watching for changes..."));
605
+ console.log(chalk2.dim("Press Ctrl+C to stop"));
606
+ console.log();
607
+ const watcher = chokidar.watch(projectRoot, {
608
+ ignored: (path) => {
609
+ const relativePath = relative2(projectRoot, path);
610
+ return ig.ignores(relativePath);
611
+ },
612
+ persistent: true,
613
+ ignoreInitial: true,
614
+ awaitWriteFinish: {
615
+ stabilityThreshold: 100,
616
+ pollInterval: 100
617
+ }
618
+ });
619
+ watcher.on("add", (path) => syncFile(path)).on("change", (path) => syncFile(path)).on("unlink", (path) => {
620
+ const relativePath = relative2(projectRoot, path);
621
+ console.log(chalk2.yellow("Deleted (local only):"), relativePath);
622
+ });
623
+ const cleanup = () => {
624
+ console.log();
625
+ console.log(chalk2.dim("Stopping watcher..."));
626
+ watcher.close().then(() => {
627
+ process.exit(0);
628
+ });
629
+ };
630
+ process.on("SIGINT", cleanup);
631
+ process.on("SIGTERM", cleanup);
632
+ } catch (error) {
633
+ handleError(error, json);
634
+ process.exit(1);
635
+ }
636
+ });
637
+
638
+ // src/commands/init.ts
639
+ import { existsSync as existsSync3 } from "fs";
640
+ import { mkdir as mkdir4, writeFile as writeFile4 } from "fs/promises";
641
+ import { join as join4 } from "path";
642
+ import { Command as Command2 } from "commander";
643
+ import ora2 from "ora";
644
+
645
+ // src/lib/output.ts
646
+ import chalk3 from "chalk";
647
+ function formatOutput(data, json) {
648
+ if (json) {
649
+ console.log(JSON.stringify({ success: true, data }, null, 2));
650
+ } else {
651
+ prettyPrint(data);
652
+ }
653
+ }
654
+ function formatSuccess(message, json) {
655
+ if (json) {
656
+ console.log(JSON.stringify({ success: true, message }));
657
+ } else {
658
+ console.log(chalk3.green("\u2713"), message);
659
+ }
660
+ }
661
+ function formatTable(headers, rows, json) {
662
+ if (json) {
663
+ const data = rows.map(
664
+ (row) => Object.fromEntries(headers.map((header, i) => [header, row[i]]))
665
+ );
666
+ console.log(JSON.stringify({ success: true, data }, null, 2));
667
+ } else {
668
+ const colWidths = headers.map(
669
+ (h, i) => Math.max(h.length, ...rows.map((r) => (r[i] || "").length))
670
+ );
671
+ const separator = colWidths.map((w) => "-".repeat(w + 2)).join("+");
672
+ const formatRow = (row) => row.map((cell, i) => ` ${(cell || "").padEnd(colWidths[i])} `).join("|");
673
+ console.log(chalk3.cyan(formatRow(headers)));
674
+ console.log(separator);
675
+ for (const row of rows) {
676
+ console.log(formatRow(row));
677
+ }
678
+ }
679
+ }
680
+ function formatList(items, json) {
681
+ if (json) {
682
+ const data = Object.fromEntries(
683
+ items.map((item) => [item.label, item.value])
684
+ );
685
+ console.log(JSON.stringify({ success: true, data }, null, 2));
686
+ } else {
687
+ const maxLabelLength = Math.max(...items.map((i) => i.label.length));
688
+ items.forEach((item) => {
689
+ console.log(
690
+ `${chalk3.gray(item.label.padEnd(maxLabelLength))} ${chalk3.white(item.value)}`
691
+ );
692
+ });
693
+ }
694
+ }
695
+ function prettyPrint(data, indent = 0) {
696
+ const prefix = " ".repeat(indent);
697
+ if (Array.isArray(data)) {
698
+ data.forEach((item, index) => {
699
+ console.log(`${prefix}${chalk3.gray(`[${index}]`)}`);
700
+ prettyPrint(item, indent + 1);
701
+ });
702
+ } else if (typeof data === "object" && data !== null) {
703
+ for (const [key, value] of Object.entries(data)) {
704
+ if (typeof value === "object" && value !== null) {
705
+ console.log(`${prefix}${chalk3.gray(key)}:`);
706
+ prettyPrint(value, indent + 1);
707
+ } else {
708
+ console.log(
709
+ `${prefix}${chalk3.gray(key)}: ${chalk3.white(String(value))}`
710
+ );
711
+ }
712
+ }
713
+ } else {
714
+ console.log(`${prefix}${chalk3.white(String(data))}`);
715
+ }
716
+ }
717
+
718
+ // src/commands/init.ts
719
+ var PROJECT_CONFIG_DIR = ".waniwani";
720
+ var PROJECT_CONFIG_FILE = "settings.json";
721
+ var initCommand = new Command2("init").description("Create a new MCP project from template").argument("<name>", "Name for the MCP project").action(async (name, _, command) => {
722
+ const globalOptions = command.optsWithGlobals();
723
+ const json = globalOptions.json ?? false;
724
+ try {
725
+ const cwd = process.cwd();
726
+ const projectDir = join4(cwd, name);
727
+ if (existsSync3(projectDir)) {
728
+ if (json) {
729
+ formatOutput(
730
+ {
731
+ success: false,
732
+ error: `Directory "${name}" already exists`
733
+ },
734
+ true
735
+ );
736
+ } else {
737
+ console.error(`Error: Directory "${name}" already exists`);
738
+ }
739
+ process.exit(1);
740
+ }
741
+ const spinner = ora2("Creating MCP sandbox...").start();
742
+ const result = await api.post("/api/mcp/sandboxes", {
743
+ name
744
+ });
745
+ spinner.text = "Downloading template files...";
746
+ await mkdir4(projectDir, { recursive: true });
747
+ await pullFilesFromSandbox(result.id, projectDir);
748
+ spinner.text = "Setting up project config...";
749
+ const configDir = join4(projectDir, PROJECT_CONFIG_DIR);
750
+ const configPath = join4(configDir, PROJECT_CONFIG_FILE);
751
+ await mkdir4(configDir, { recursive: true });
752
+ const projectConfig = {
753
+ mcpId: result.id,
754
+ defaults: {
755
+ model: "claude-sonnet-4-20250514",
756
+ maxSteps: 10
757
+ }
758
+ };
759
+ await writeFile4(
760
+ configPath,
761
+ JSON.stringify(projectConfig, null, " "),
762
+ "utf-8"
763
+ );
764
+ spinner.succeed("MCP project created");
765
+ if (json) {
766
+ formatOutput(
767
+ {
768
+ success: true,
769
+ projectDir,
770
+ mcpId: result.id,
771
+ sandboxId: result.sandboxId,
772
+ previewUrl: result.previewUrl
773
+ },
774
+ true
367
775
  );
368
- return true;
369
- } catch {
370
- await this.clear();
371
- return false;
776
+ } else {
777
+ console.log();
778
+ formatSuccess(`MCP project "${name}" created!`, false);
779
+ console.log();
780
+ console.log(` Project: ${projectDir}`);
781
+ console.log(` MCP ID: ${result.id}`);
782
+ console.log(` Preview URL: ${result.previewUrl}`);
783
+ console.log();
784
+ console.log("Next steps:");
785
+ console.log(` cd ${name}`);
786
+ console.log(" waniwani push # Sync files to sandbox");
787
+ console.log(" waniwani dev # Watch mode with auto-sync");
788
+ console.log(' waniwani task "..." # Send tasks to Claude');
372
789
  }
790
+ } catch (error) {
791
+ handleError(error, json);
792
+ process.exit(1);
373
793
  }
374
- };
375
- var auth = new AuthManager();
794
+ });
376
795
 
377
796
  // src/commands/login.ts
797
+ import { spawn } from "child_process";
798
+ import { createServer } from "http";
799
+ import chalk4 from "chalk";
800
+ import { Command as Command3 } from "commander";
801
+ import ora3 from "ora";
378
802
  var CALLBACK_PORT = 54321;
379
803
  var CALLBACK_URL = `http://localhost:${CALLBACK_PORT}/callback`;
380
804
  var CLIENT_NAME = "waniwani-cli";
@@ -649,7 +1073,7 @@ async function exchangeCodeForToken(code, codeVerifier, clientId, resource) {
649
1073
  }
650
1074
  return response.json();
651
1075
  }
652
- var loginCommand = new Command2("login").description("Log in to WaniWani").option("--no-browser", "Don't open the browser automatically").action(async (options, command) => {
1076
+ var loginCommand = new Command3("login").description("Log in to WaniWani").option("--no-browser", "Don't open the browser automatically").action(async (options, command) => {
653
1077
  const globalOptions = command.optsWithGlobals();
654
1078
  const json = globalOptions.json ?? false;
655
1079
  try {
@@ -661,14 +1085,14 @@ var loginCommand = new Command2("login").description("Log in to WaniWani").optio
661
1085
  formatOutput({ alreadyLoggedIn: true, refreshed: true }, true);
662
1086
  } else {
663
1087
  console.log(
664
- chalk3.green("Session refreshed. You're still logged in.")
1088
+ chalk4.green("Session refreshed. You're still logged in.")
665
1089
  );
666
1090
  }
667
1091
  return;
668
1092
  }
669
1093
  if (!json) {
670
1094
  console.log(
671
- chalk3.yellow("Session expired. Starting new login flow...")
1095
+ chalk4.yellow("Session expired. Starting new login flow...")
672
1096
  );
673
1097
  }
674
1098
  await auth.clear();
@@ -677,7 +1101,7 @@ var loginCommand = new Command2("login").description("Log in to WaniWani").optio
677
1101
  formatOutput({ alreadyLoggedIn: true }, true);
678
1102
  } else {
679
1103
  console.log(
680
- chalk3.yellow(
1104
+ chalk4.yellow(
681
1105
  "Already logged in. Use 'waniwani logout' to log out first."
682
1106
  )
683
1107
  );
@@ -686,9 +1110,9 @@ var loginCommand = new Command2("login").description("Log in to WaniWani").optio
686
1110
  }
687
1111
  }
688
1112
  if (!json) {
689
- console.log(chalk3.bold("\nWaniWani CLI Login\n"));
1113
+ console.log(chalk4.bold("\nWaniWani CLI Login\n"));
690
1114
  }
691
- const spinner = ora("Registering client...").start();
1115
+ const spinner = ora3("Registering client...").start();
692
1116
  const { client_id: clientId } = await registerClient();
693
1117
  spinner.text = "Preparing authentication...";
694
1118
  const codeVerifier = generateCodeVerifier();
@@ -708,7 +1132,7 @@ var loginCommand = new Command2("login").description("Log in to WaniWani").optio
708
1132
  console.log("Opening browser for authentication...\n");
709
1133
  console.log(`If the browser doesn't open, visit:
710
1134
  `);
711
- console.log(chalk3.cyan(` ${authUrl.toString()}`));
1135
+ console.log(chalk4.cyan(` ${authUrl.toString()}`));
712
1136
  console.log();
713
1137
  }
714
1138
  const callbackPromise = waitForCallback(state);
@@ -754,8 +1178,8 @@ var loginCommand = new Command2("login").description("Log in to WaniWani").optio
754
1178
  });
755
1179
 
756
1180
  // src/commands/logout.ts
757
- import { Command as Command3 } from "commander";
758
- var logoutCommand = new Command3("logout").description("Log out from WaniWani").action(async (_, command) => {
1181
+ import { Command as Command4 } from "commander";
1182
+ var logoutCommand = new Command4("logout").description("Log out from WaniWani").action(async (_, command) => {
759
1183
  const globalOptions = command.optsWithGlobals();
760
1184
  const json = globalOptions.json ?? false;
761
1185
  try {
@@ -782,134 +1206,9 @@ var logoutCommand = new Command3("logout").description("Log out from WaniWani").
782
1206
  // src/commands/mcp/index.ts
783
1207
  import { Command as Command19 } from "commander";
784
1208
 
785
- // src/commands/mcp/create.ts
786
- import { Command as Command4 } from "commander";
787
- import ora2 from "ora";
788
-
789
- // src/lib/api.ts
790
- var ApiError = class extends CLIError {
791
- constructor(message, code, statusCode, details) {
792
- super(message, code, details);
793
- this.statusCode = statusCode;
794
- this.name = "ApiError";
795
- }
796
- };
797
- async function request(method, path, options) {
798
- const {
799
- body,
800
- requireAuth = true,
801
- headers: extraHeaders = {}
802
- } = options || {};
803
- const headers = {
804
- "Content-Type": "application/json",
805
- ...extraHeaders
806
- };
807
- if (requireAuth) {
808
- const token = await auth.getAccessToken();
809
- if (!token) {
810
- throw new AuthError(
811
- "Not logged in. Run 'waniwani login' to authenticate."
812
- );
813
- }
814
- headers.Authorization = `Bearer ${token}`;
815
- }
816
- const baseUrl = await config.getApiUrl();
817
- const url = `${baseUrl}${path}`;
818
- const response = await fetch(url, {
819
- method,
820
- headers,
821
- body: body ? JSON.stringify(body) : void 0
822
- });
823
- if (response.status === 204) {
824
- return void 0;
825
- }
826
- let data;
827
- let rawBody;
828
- try {
829
- rawBody = await response.text();
830
- data = JSON.parse(rawBody);
831
- } catch {
832
- throw new ApiError(
833
- rawBody || `Request failed with status ${response.status}`,
834
- "API_ERROR",
835
- response.status,
836
- { statusText: response.statusText }
837
- );
838
- }
839
- if (!response.ok || data.error) {
840
- const errorMessage = data.error?.message || data.message || data.error || rawBody || `Request failed with status ${response.status}`;
841
- const errorCode = data.error?.code || data.code || "API_ERROR";
842
- const errorDetails = {
843
- ...data.error?.details,
844
- statusText: response.statusText,
845
- ...data.error ? {} : { rawResponse: data }
846
- };
847
- const error = {
848
- code: errorCode,
849
- message: errorMessage,
850
- details: errorDetails
851
- };
852
- if (response.status === 401) {
853
- const refreshed = await auth.tryRefreshToken();
854
- if (refreshed) {
855
- return request(method, path, options);
856
- }
857
- throw new AuthError(
858
- "Session expired. Run 'waniwani login' to re-authenticate."
859
- );
860
- }
861
- throw new ApiError(
862
- error.message,
863
- error.code,
864
- response.status,
865
- error.details
866
- );
867
- }
868
- return data.data;
869
- }
870
- var api = {
871
- get: (path, options) => request("GET", path, options),
872
- post: (path, body, options) => request("POST", path, { body, ...options }),
873
- delete: (path, options) => request("DELETE", path, options),
874
- getBaseUrl: () => config.getApiUrl()
875
- };
876
-
877
- // src/commands/mcp/create.ts
878
- 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) => {
879
- const globalOptions = command.optsWithGlobals();
880
- const json = globalOptions.json ?? false;
881
- try {
882
- const spinner = ora2("Creating MCP sandbox...").start();
883
- const result = await api.post("/api/mcp/sandboxes", {
884
- name
885
- });
886
- spinner.succeed("MCP sandbox created");
887
- const cfg = options.global ? globalConfig : config;
888
- await cfg.setMcpId(result.id);
889
- if (json) {
890
- formatOutput({ ...result, scope: cfg.scope }, true);
891
- } else {
892
- console.log();
893
- formatSuccess(`MCP sandbox "${name}" created! (${cfg.scope})`, false);
894
- console.log();
895
- console.log(` MCP ID: ${result.id}`);
896
- console.log(` Sandbox ID: ${result.sandboxId}`);
897
- console.log(` Preview URL: ${result.previewUrl}`);
898
- console.log();
899
- console.log(`Next steps:`);
900
- console.log(` waniwani task "Add a tool that does X"`);
901
- console.log(` waniwani mcp test`);
902
- console.log(` waniwani mcp deploy`);
903
- }
904
- } catch (error) {
905
- handleError(error, json);
906
- process.exit(1);
907
- }
908
- });
909
-
910
1209
  // src/commands/mcp/delete.ts
911
1210
  import { Command as Command5 } from "commander";
912
- import ora3 from "ora";
1211
+ import ora4 from "ora";
913
1212
  var deleteCommand = new Command5("delete").description("Delete the MCP sandbox").option("--mcp-id <id>", "Specific MCP ID").action(async (options, command) => {
914
1213
  const globalOptions = command.optsWithGlobals();
915
1214
  const json = globalOptions.json ?? false;
@@ -921,7 +1220,7 @@ var deleteCommand = new Command5("delete").description("Delete the MCP sandbox")
921
1220
  throw new McpError("No active MCP. Use --mcp-id to specify one.");
922
1221
  }
923
1222
  }
924
- const spinner = ora3("Deleting MCP sandbox...").start();
1223
+ const spinner = ora4("Deleting MCP sandbox...").start();
925
1224
  await api.delete(`/api/mcp/sandboxes/${mcpId}`);
926
1225
  spinner.succeed("MCP sandbox deleted");
927
1226
  if (await config.getMcpId() === mcpId) {
@@ -940,7 +1239,7 @@ var deleteCommand = new Command5("delete").description("Delete the MCP sandbox")
940
1239
 
941
1240
  // src/commands/mcp/deploy.ts
942
1241
  import { Command as Command6 } from "commander";
943
- import ora4 from "ora";
1242
+ import ora5 from "ora";
944
1243
  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) => {
945
1244
  const globalOptions = command.optsWithGlobals();
946
1245
  const json = globalOptions.json ?? false;
@@ -954,7 +1253,7 @@ var deployCommand = new Command6("deploy").description("Deploy MCP server to Git
954
1253
  );
955
1254
  }
956
1255
  }
957
- const spinner = ora4("Deploying to GitHub...").start();
1256
+ const spinner = ora5("Deploying to GitHub...").start();
958
1257
  const result = await api.post(
959
1258
  `/api/admin/mcps/${mcpId}/deploy`,
960
1259
  {
@@ -989,9 +1288,9 @@ var deployCommand = new Command6("deploy").description("Deploy MCP server to Git
989
1288
  import { Command as Command10 } from "commander";
990
1289
 
991
1290
  // src/commands/mcp/file/list.ts
992
- import chalk4 from "chalk";
1291
+ import chalk5 from "chalk";
993
1292
  import { Command as Command7 } from "commander";
994
- import ora5 from "ora";
1293
+ import ora6 from "ora";
995
1294
  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) => {
996
1295
  const globalOptions = command.optsWithGlobals();
997
1296
  const json = globalOptions.json ?? false;
@@ -1005,7 +1304,7 @@ var listCommand = new Command7("list").description("List files in the MCP sandbo
1005
1304
  );
1006
1305
  }
1007
1306
  }
1008
- const spinner = ora5(`Listing ${path}...`).start();
1307
+ const spinner = ora6(`Listing ${path}...`).start();
1009
1308
  const result = await api.get(
1010
1309
  `/api/mcp/sandboxes/${mcpId}/files/list?path=${encodeURIComponent(path)}`
1011
1310
  );
@@ -1013,15 +1312,15 @@ var listCommand = new Command7("list").description("List files in the MCP sandbo
1013
1312
  if (json) {
1014
1313
  formatOutput(result, true);
1015
1314
  } else {
1016
- console.log(chalk4.bold(`
1315
+ console.log(chalk5.bold(`
1017
1316
  Directory: ${result.path}
1018
1317
  `));
1019
1318
  if (result.entries.length === 0) {
1020
1319
  console.log(" (empty)");
1021
1320
  } else {
1022
1321
  const rows = result.entries.map((entry) => {
1023
- const name = entry.type === "directory" ? chalk4.blue(`${entry.name}/`) : entry.name;
1024
- const size = entry.type === "directory" ? chalk4.gray("<dir>") : formatSize(entry.size);
1322
+ const name = entry.type === "directory" ? chalk5.blue(`${entry.name}/`) : entry.name;
1323
+ const size = entry.type === "directory" ? chalk5.gray("<dir>") : formatSize(entry.size);
1025
1324
  return [name, size];
1026
1325
  });
1027
1326
  formatTable(["Name", "Size"], rows, false);
@@ -1041,9 +1340,9 @@ function formatSize(bytes) {
1041
1340
  }
1042
1341
 
1043
1342
  // src/commands/mcp/file/read.ts
1044
- import { writeFile as writeFile4 } from "fs/promises";
1343
+ import { writeFile as writeFile5 } from "fs/promises";
1045
1344
  import { Command as Command8 } from "commander";
1046
- import ora6 from "ora";
1345
+ import ora7 from "ora";
1047
1346
  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) => {
1048
1347
  const globalOptions = command.optsWithGlobals();
1049
1348
  const json = globalOptions.json ?? false;
@@ -1058,7 +1357,7 @@ var readCommand = new Command8("read").description("Read a file from the MCP san
1058
1357
  }
1059
1358
  }
1060
1359
  const encoding = options.base64 ? "base64" : "utf8";
1061
- const spinner = ora6(`Reading ${path}...`).start();
1360
+ const spinner = ora7(`Reading ${path}...`).start();
1062
1361
  const result = await api.get(
1063
1362
  `/api/mcp/sandboxes/${mcpId}/files?path=${encodeURIComponent(path)}&encoding=${encoding}`
1064
1363
  );
@@ -1068,7 +1367,7 @@ var readCommand = new Command8("read").description("Read a file from the MCP san
1068
1367
  }
1069
1368
  if (options.output) {
1070
1369
  const buffer = result.encoding === "base64" ? Buffer.from(result.content, "base64") : Buffer.from(result.content, "utf8");
1071
- await writeFile4(options.output, buffer);
1370
+ await writeFile5(options.output, buffer);
1072
1371
  if (json) {
1073
1372
  formatOutput({ path, savedTo: options.output }, true);
1074
1373
  } else {
@@ -1089,9 +1388,9 @@ var readCommand = new Command8("read").description("Read a file from the MCP san
1089
1388
  });
1090
1389
 
1091
1390
  // src/commands/mcp/file/write.ts
1092
- import { readFile as readFile3 } from "fs/promises";
1391
+ import { readFile as readFile4 } from "fs/promises";
1093
1392
  import { Command as Command9 } from "commander";
1094
- import ora7 from "ora";
1393
+ import ora8 from "ora";
1095
1394
  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) => {
1096
1395
  const globalOptions = command.optsWithGlobals();
1097
1396
  const json = globalOptions.json ?? false;
@@ -1113,7 +1412,7 @@ var writeCommand = new Command9("write").description("Write a file to the MCP sa
1113
1412
  encoding = "base64";
1114
1413
  }
1115
1414
  } else if (options.file) {
1116
- const fileBuffer = await readFile3(options.file);
1415
+ const fileBuffer = await readFile4(options.file);
1117
1416
  if (options.base64) {
1118
1417
  content = fileBuffer.toString("base64");
1119
1418
  encoding = "base64";
@@ -1126,7 +1425,7 @@ var writeCommand = new Command9("write").description("Write a file to the MCP sa
1126
1425
  "MISSING_CONTENT"
1127
1426
  );
1128
1427
  }
1129
- const spinner = ora7(`Writing ${path}...`).start();
1428
+ const spinner = ora8(`Writing ${path}...`).start();
1130
1429
  const result = await api.post(
1131
1430
  `/api/mcp/sandboxes/${mcpId}/files`,
1132
1431
  {
@@ -1149,14 +1448,14 @@ var writeCommand = new Command9("write").description("Write a file to the MCP sa
1149
1448
  var fileCommand = new Command10("file").description("File operations in MCP sandbox").addCommand(readCommand).addCommand(writeCommand).addCommand(listCommand);
1150
1449
 
1151
1450
  // src/commands/mcp/list.ts
1152
- import chalk5 from "chalk";
1451
+ import chalk6 from "chalk";
1153
1452
  import { Command as Command11 } from "commander";
1154
- import ora8 from "ora";
1453
+ import ora9 from "ora";
1155
1454
  var listCommand2 = new Command11("list").description("List all MCPs in your organization").option("--all", "Include stopped/expired MCPs").action(async (options, command) => {
1156
1455
  const globalOptions = command.optsWithGlobals();
1157
1456
  const json = globalOptions.json ?? false;
1158
1457
  try {
1159
- const spinner = ora8("Fetching MCPs...").start();
1458
+ const spinner = ora9("Fetching MCPs...").start();
1160
1459
  const mcps = await api.get(
1161
1460
  `/api/mcp/sandboxes${options.all ? "?all=true" : ""}`
1162
1461
  );
@@ -1179,12 +1478,12 @@ var listCommand2 = new Command11("list").description("List all MCPs in your orga
1179
1478
  console.log("\nCreate a new MCP sandbox: waniwani mcp create <name>");
1180
1479
  return;
1181
1480
  }
1182
- console.log(chalk5.bold("\nMCPs:\n"));
1481
+ console.log(chalk6.bold("\nMCPs:\n"));
1183
1482
  const rows = mcps.map((m) => {
1184
1483
  const isActive = m.id === activeMcpId;
1185
- const statusColor = m.status === "active" ? chalk5.green : m.status === "stopped" ? chalk5.red : chalk5.yellow;
1484
+ const statusColor = m.status === "active" ? chalk6.green : m.status === "stopped" ? chalk6.red : chalk6.yellow;
1186
1485
  return [
1187
- isActive ? chalk5.cyan(`* ${m.id.slice(0, 8)}`) : ` ${m.id.slice(0, 8)}`,
1486
+ isActive ? chalk6.cyan(`* ${m.id.slice(0, 8)}`) : ` ${m.id.slice(0, 8)}`,
1188
1487
  m.name,
1189
1488
  statusColor(m.status),
1190
1489
  m.previewUrl,
@@ -1198,7 +1497,7 @@ var listCommand2 = new Command11("list").description("List all MCPs in your orga
1198
1497
  );
1199
1498
  console.log();
1200
1499
  if (activeMcpId) {
1201
- console.log(`Active MCP: ${chalk5.cyan(activeMcpId.slice(0, 8))}`);
1500
+ console.log(`Active MCP: ${chalk6.cyan(activeMcpId.slice(0, 8))}`);
1202
1501
  }
1203
1502
  console.log("\nSelect an MCP: waniwani mcp use <name>");
1204
1503
  }
@@ -1209,9 +1508,9 @@ var listCommand2 = new Command11("list").description("List all MCPs in your orga
1209
1508
  });
1210
1509
 
1211
1510
  // src/commands/mcp/logs.ts
1212
- import chalk6 from "chalk";
1511
+ import chalk7 from "chalk";
1213
1512
  import { Command as Command12 } from "commander";
1214
- import ora9 from "ora";
1513
+ import ora10 from "ora";
1215
1514
  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) => {
1216
1515
  const globalOptions = command.optsWithGlobals();
1217
1516
  const json = globalOptions.json ?? false;
@@ -1243,7 +1542,7 @@ var logsCommand = new Command12("logs").description("Stream logs from the MCP se
1243
1542
  }
1244
1543
  let cmdId = cmdIdArg;
1245
1544
  if (!cmdId) {
1246
- const spinner = ora9("Getting server status...").start();
1545
+ const spinner = ora10("Getting server status...").start();
1247
1546
  const status = await api.post(
1248
1547
  `/api/mcp/sandboxes/${mcpId}/server`,
1249
1548
  { action: "status" }
@@ -1260,8 +1559,8 @@ var logsCommand = new Command12("logs").description("Stream logs from the MCP se
1260
1559
  const streamParam = options.follow ? "?stream=true" : "";
1261
1560
  const url = `${baseUrl}/api/mcp/sandboxes/${mcpId}/commands/${cmdId}${streamParam}`;
1262
1561
  if (!json) {
1263
- console.log(chalk6.gray(`Streaming logs for command ${cmdId}...`));
1264
- console.log(chalk6.gray("Press Ctrl+C to stop\n"));
1562
+ console.log(chalk7.gray(`Streaming logs for command ${cmdId}...`));
1563
+ console.log(chalk7.gray("Press Ctrl+C to stop\n"));
1265
1564
  }
1266
1565
  const response = await fetch(url, {
1267
1566
  method: "GET",
@@ -1285,10 +1584,10 @@ var logsCommand = new Command12("logs").description("Stream logs from the MCP se
1285
1584
  process.stdout.write(data.stdout);
1286
1585
  }
1287
1586
  if (data.stderr) {
1288
- process.stderr.write(chalk6.red(data.stderr));
1587
+ process.stderr.write(chalk7.red(data.stderr));
1289
1588
  }
1290
1589
  if (data.exitCode !== void 0) {
1291
- console.log(chalk6.gray(`
1590
+ console.log(chalk7.gray(`
1292
1591
  Exit code: ${data.exitCode}`));
1293
1592
  }
1294
1593
  }
@@ -1327,18 +1626,18 @@ Exit code: ${data.exitCode}`));
1327
1626
  if (event.stream === "stdout") {
1328
1627
  process.stdout.write(event.data);
1329
1628
  } else if (event.stream === "stderr") {
1330
- process.stderr.write(chalk6.red(event.data));
1629
+ process.stderr.write(chalk7.red(event.data));
1331
1630
  }
1332
1631
  }
1333
1632
  if (event.exitCode !== void 0) {
1334
- const exitColor = event.exitCode === 0 ? chalk6.green : chalk6.red;
1633
+ const exitColor = event.exitCode === 0 ? chalk7.green : chalk7.red;
1335
1634
  console.log(
1336
1635
  exitColor(`
1337
1636
  Process exited with code ${event.exitCode}`)
1338
1637
  );
1339
1638
  }
1340
1639
  if (event.error) {
1341
- console.error(chalk6.red(`
1640
+ console.error(chalk7.red(`
1342
1641
  Error: ${event.error}`));
1343
1642
  }
1344
1643
  } catch {
@@ -1359,9 +1658,9 @@ Error: ${event.error}`));
1359
1658
  });
1360
1659
 
1361
1660
  // src/commands/mcp/run-command.ts
1362
- import chalk7 from "chalk";
1661
+ import chalk8 from "chalk";
1363
1662
  import { Command as Command13 } from "commander";
1364
- import ora10 from "ora";
1663
+ import ora11 from "ora";
1365
1664
  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(
1366
1665
  "--timeout <ms>",
1367
1666
  "Command timeout in milliseconds (default: 30000, max: 300000)"
@@ -1379,7 +1678,7 @@ var runCommandCommand = new Command13("run-command").description("Run a command
1379
1678
  }
1380
1679
  }
1381
1680
  const timeout = options.timeout ? Number.parseInt(options.timeout, 10) : void 0;
1382
- const spinner = ora10(`Running: ${cmd} ${args.join(" ")}`.trim()).start();
1681
+ const spinner = ora11(`Running: ${cmd} ${args.join(" ")}`.trim()).start();
1383
1682
  const result = await api.post(
1384
1683
  `/api/mcp/sandboxes/${mcpId}/commands`,
1385
1684
  {
@@ -1394,7 +1693,7 @@ var runCommandCommand = new Command13("run-command").description("Run a command
1394
1693
  formatOutput(result, true);
1395
1694
  } else {
1396
1695
  const cmdLine = [cmd, ...args].join(" ");
1397
- console.log(chalk7.gray(`$ ${cmdLine}`));
1696
+ console.log(chalk8.gray(`$ ${cmdLine}`));
1398
1697
  console.log();
1399
1698
  if (result.stdout) {
1400
1699
  process.stdout.write(result.stdout);
@@ -1403,16 +1702,16 @@ var runCommandCommand = new Command13("run-command").description("Run a command
1403
1702
  }
1404
1703
  }
1405
1704
  if (result.stderr) {
1406
- process.stderr.write(chalk7.red(result.stderr));
1705
+ process.stderr.write(chalk8.red(result.stderr));
1407
1706
  if (!result.stderr.endsWith("\n")) {
1408
1707
  process.stderr.write("\n");
1409
1708
  }
1410
1709
  }
1411
1710
  console.log();
1412
- const exitColor = result.exitCode === 0 ? chalk7.green : chalk7.red;
1711
+ const exitColor = result.exitCode === 0 ? chalk8.green : chalk8.red;
1413
1712
  console.log(
1414
1713
  exitColor(`Exit code: ${result.exitCode}`),
1415
- chalk7.gray(`(${(result.duration / 1e3).toFixed(2)}s)`)
1714
+ chalk8.gray(`(${(result.duration / 1e3).toFixed(2)}s)`)
1416
1715
  );
1417
1716
  }
1418
1717
  if (result.exitCode !== 0) {
@@ -1425,9 +1724,9 @@ var runCommandCommand = new Command13("run-command").description("Run a command
1425
1724
  });
1426
1725
 
1427
1726
  // src/commands/mcp/start.ts
1428
- import chalk8 from "chalk";
1727
+ import chalk9 from "chalk";
1429
1728
  import { Command as Command14 } from "commander";
1430
- import ora11 from "ora";
1729
+ import ora12 from "ora";
1431
1730
  var startCommand = new Command14("start").description("Start the MCP server (npm run dev)").option("--mcp-id <id>", "Specific MCP ID").action(async (options, command) => {
1432
1731
  const globalOptions = command.optsWithGlobals();
1433
1732
  const json = globalOptions.json ?? false;
@@ -1441,7 +1740,7 @@ var startCommand = new Command14("start").description("Start the MCP server (npm
1441
1740
  );
1442
1741
  }
1443
1742
  }
1444
- const spinner = ora11("Starting MCP server...").start();
1743
+ const spinner = ora12("Starting MCP server...").start();
1445
1744
  const result = await api.post(
1446
1745
  `/api/mcp/sandboxes/${mcpId}/server`,
1447
1746
  { action: "start" }
@@ -1454,13 +1753,13 @@ var startCommand = new Command14("start").description("Start the MCP server (npm
1454
1753
  formatList(
1455
1754
  [
1456
1755
  { label: "Command ID", value: result.cmdId },
1457
- { label: "Preview URL", value: chalk8.cyan(result.previewUrl) }
1756
+ { label: "Preview URL", value: chalk9.cyan(result.previewUrl) }
1458
1757
  ],
1459
1758
  false
1460
1759
  );
1461
1760
  console.log();
1462
1761
  console.log(
1463
- chalk8.gray("Run 'waniwani mcp logs' to stream server output")
1762
+ chalk9.gray("Run 'waniwani mcp logs' to stream server output")
1464
1763
  );
1465
1764
  }
1466
1765
  } catch (error) {
@@ -1470,9 +1769,9 @@ var startCommand = new Command14("start").description("Start the MCP server (npm
1470
1769
  });
1471
1770
 
1472
1771
  // src/commands/mcp/status.ts
1473
- import chalk9 from "chalk";
1772
+ import chalk10 from "chalk";
1474
1773
  import { Command as Command15 } from "commander";
1475
- import ora12 from "ora";
1774
+ import ora13 from "ora";
1476
1775
  var statusCommand = new Command15("status").description("Show current MCP sandbox status").option("--mcp-id <id>", "Specific MCP ID").action(async (options, command) => {
1477
1776
  const globalOptions = command.optsWithGlobals();
1478
1777
  const json = globalOptions.json ?? false;
@@ -1486,7 +1785,7 @@ var statusCommand = new Command15("status").description("Show current MCP sandbo
1486
1785
  );
1487
1786
  }
1488
1787
  }
1489
- const spinner = ora12("Fetching MCP status...").start();
1788
+ const spinner = ora13("Fetching MCP status...").start();
1490
1789
  const [result, serverStatus] = await Promise.all([
1491
1790
  api.get(`/api/mcp/sandboxes/${mcpId}`),
1492
1791
  api.post(`/api/mcp/sandboxes/${mcpId}/server`, {
@@ -1501,9 +1800,9 @@ var statusCommand = new Command15("status").description("Show current MCP sandbo
1501
1800
  if (json) {
1502
1801
  formatOutput({ ...result, server: serverStatus }, true);
1503
1802
  } else {
1504
- const statusColor = result.status === "active" ? chalk9.green : chalk9.red;
1803
+ const statusColor = result.status === "active" ? chalk10.green : chalk10.red;
1505
1804
  const serverRunning = serverStatus.running;
1506
- const serverStatusColor = serverRunning ? chalk9.green : chalk9.yellow;
1805
+ const serverStatusColor = serverRunning ? chalk10.green : chalk10.yellow;
1507
1806
  formatList(
1508
1807
  [
1509
1808
  { label: "MCP ID", value: result.id },
@@ -1530,7 +1829,7 @@ var statusCommand = new Command15("status").description("Show current MCP sandbo
1530
1829
 
1531
1830
  // src/commands/mcp/stop.ts
1532
1831
  import { Command as Command16 } from "commander";
1533
- import ora13 from "ora";
1832
+ import ora14 from "ora";
1534
1833
  var stopCommand = new Command16("stop").description("Stop the MCP server process").option("--mcp-id <id>", "Specific MCP ID").action(async (options, command) => {
1535
1834
  const globalOptions = command.optsWithGlobals();
1536
1835
  const json = globalOptions.json ?? false;
@@ -1544,7 +1843,7 @@ var stopCommand = new Command16("stop").description("Stop the MCP server process
1544
1843
  );
1545
1844
  }
1546
1845
  }
1547
- const spinner = ora13("Stopping MCP server...").start();
1846
+ const spinner = ora14("Stopping MCP server...").start();
1548
1847
  const result = await api.post(
1549
1848
  `/api/mcp/sandboxes/${mcpId}/server`,
1550
1849
  { action: "stop" }
@@ -1566,9 +1865,9 @@ var stopCommand = new Command16("stop").description("Stop the MCP server process
1566
1865
  });
1567
1866
 
1568
1867
  // src/commands/mcp/test.ts
1569
- import chalk10 from "chalk";
1868
+ import chalk11 from "chalk";
1570
1869
  import { Command as Command17 } from "commander";
1571
- import ora14 from "ora";
1870
+ import ora15 from "ora";
1572
1871
  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(
1573
1872
  async (tool, args, options, command) => {
1574
1873
  const globalOptions = command.optsWithGlobals();
@@ -1584,7 +1883,7 @@ var testCommand = new Command17("test").description("Test MCP tools via the sand
1584
1883
  }
1585
1884
  }
1586
1885
  if (!tool) {
1587
- const spinner = ora14("Fetching available tools...").start();
1886
+ const spinner = ora15("Fetching available tools...").start();
1588
1887
  const result = await api.post(
1589
1888
  `/api/mcp/sandboxes/${mcpId}/test`,
1590
1889
  { action: "list" }
@@ -1597,7 +1896,7 @@ var testCommand = new Command17("test").description("Test MCP tools via the sand
1597
1896
  if (tools.length === 0) {
1598
1897
  console.log("No tools available.");
1599
1898
  } else {
1600
- console.log(chalk10.bold("\nAvailable Tools:\n"));
1899
+ console.log(chalk11.bold("\nAvailable Tools:\n"));
1601
1900
  formatTable(
1602
1901
  ["Name", "Description"],
1603
1902
  tools.map((t) => [t.name, t.description || "No description"]),
@@ -1620,7 +1919,7 @@ Test a tool: waniwani mcp test <tool-name> '{"arg": "value"}'`
1620
1919
  );
1621
1920
  }
1622
1921
  }
1623
- const spinner = ora14(`Calling tool "${tool}"...`).start();
1922
+ const spinner = ora15(`Calling tool "${tool}"...`).start();
1624
1923
  const startTime = Date.now();
1625
1924
  const result = await api.post(
1626
1925
  `/api/mcp/sandboxes/${mcpId}/test`,
@@ -1641,11 +1940,11 @@ Test a tool: waniwani mcp test <tool-name> '{"arg": "value"}'`
1641
1940
  if (json) {
1642
1941
  formatOutput(output, true);
1643
1942
  } else {
1644
- console.log(chalk10.bold("\nTool Result:\n"));
1645
- console.log(chalk10.gray("Tool:"), tool);
1646
- console.log(chalk10.gray("Input:"), JSON.stringify(toolArgs));
1647
- console.log(chalk10.gray("Duration:"), `${duration}ms`);
1648
- console.log(chalk10.gray("Result:"));
1943
+ console.log(chalk11.bold("\nTool Result:\n"));
1944
+ console.log(chalk11.gray("Tool:"), tool);
1945
+ console.log(chalk11.gray("Input:"), JSON.stringify(toolArgs));
1946
+ console.log(chalk11.gray("Duration:"), `${duration}ms`);
1947
+ console.log(chalk11.gray("Result:"));
1649
1948
  console.log(JSON.stringify(result.result, null, 2));
1650
1949
  }
1651
1950
  }
@@ -1658,12 +1957,12 @@ Test a tool: waniwani mcp test <tool-name> '{"arg": "value"}'`
1658
1957
 
1659
1958
  // src/commands/mcp/use.ts
1660
1959
  import { Command as Command18 } from "commander";
1661
- import ora15 from "ora";
1960
+ import ora16 from "ora";
1662
1961
  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) => {
1663
1962
  const globalOptions = command.optsWithGlobals();
1664
1963
  const json = globalOptions.json ?? false;
1665
1964
  try {
1666
- const spinner = ora15("Fetching MCPs...").start();
1965
+ const spinner = ora16("Fetching MCPs...").start();
1667
1966
  const mcps = await api.get("/api/admin/mcps");
1668
1967
  spinner.stop();
1669
1968
  const mcp = mcps.find((m) => m.name === name);
@@ -1699,20 +1998,20 @@ var useCommand = new Command18("use").description("Select an MCP to use for subs
1699
1998
  });
1700
1999
 
1701
2000
  // src/commands/mcp/index.ts
1702
- 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);
2001
+ 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);
1703
2002
 
1704
2003
  // src/commands/org/index.ts
1705
2004
  import { Command as Command22 } from "commander";
1706
2005
 
1707
2006
  // src/commands/org/list.ts
1708
- import chalk11 from "chalk";
2007
+ import chalk12 from "chalk";
1709
2008
  import { Command as Command20 } from "commander";
1710
- import ora16 from "ora";
2009
+ import ora17 from "ora";
1711
2010
  var listCommand3 = new Command20("list").description("List your organizations").action(async (_, command) => {
1712
2011
  const globalOptions = command.optsWithGlobals();
1713
2012
  const json = globalOptions.json ?? false;
1714
2013
  try {
1715
- const spinner = ora16("Fetching organizations...").start();
2014
+ const spinner = ora17("Fetching organizations...").start();
1716
2015
  const result = await api.get("/api/oauth/orgs");
1717
2016
  spinner.stop();
1718
2017
  const { orgs, activeOrgId } = result;
@@ -1732,11 +2031,11 @@ var listCommand3 = new Command20("list").description("List your organizations").
1732
2031
  console.log("No organizations found.");
1733
2032
  return;
1734
2033
  }
1735
- console.log(chalk11.bold("\nOrganizations:\n"));
2034
+ console.log(chalk12.bold("\nOrganizations:\n"));
1736
2035
  const rows = orgs.map((o) => {
1737
2036
  const isActive = o.id === activeOrgId;
1738
2037
  return [
1739
- isActive ? chalk11.cyan(`* ${o.name}`) : ` ${o.name}`,
2038
+ isActive ? chalk12.cyan(`* ${o.name}`) : ` ${o.name}`,
1740
2039
  o.slug,
1741
2040
  o.role
1742
2041
  ];
@@ -1746,7 +2045,7 @@ var listCommand3 = new Command20("list").description("List your organizations").
1746
2045
  if (activeOrgId) {
1747
2046
  const activeOrg = orgs.find((o) => o.id === activeOrgId);
1748
2047
  if (activeOrg) {
1749
- console.log(`Active organization: ${chalk11.cyan(activeOrg.name)}`);
2048
+ console.log(`Active organization: ${chalk12.cyan(activeOrg.name)}`);
1750
2049
  }
1751
2050
  }
1752
2051
  console.log("\nSwitch organization: waniwani org switch <name>");
@@ -1759,12 +2058,12 @@ var listCommand3 = new Command20("list").description("List your organizations").
1759
2058
 
1760
2059
  // src/commands/org/switch.ts
1761
2060
  import { Command as Command21 } from "commander";
1762
- import ora17 from "ora";
2061
+ import ora18 from "ora";
1763
2062
  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) => {
1764
2063
  const globalOptions = command.optsWithGlobals();
1765
2064
  const json = globalOptions.json ?? false;
1766
2065
  try {
1767
- const spinner = ora17("Fetching organizations...").start();
2066
+ const spinner = ora18("Fetching organizations...").start();
1768
2067
  const { orgs } = await api.get("/api/oauth/orgs");
1769
2068
  const org = orgs.find((o) => o.name === name || o.slug === name);
1770
2069
  if (!org) {
@@ -1799,11 +2098,92 @@ var switchCommand = new Command21("switch").description("Switch to a different o
1799
2098
  // src/commands/org/index.ts
1800
2099
  var orgCommand = new Command22("org").description("Organization management commands").addCommand(listCommand3).addCommand(switchCommand);
1801
2100
 
1802
- // src/commands/task.ts
1803
- import chalk12 from "chalk";
2101
+ // src/commands/push.ts
2102
+ import chalk13 from "chalk";
1804
2103
  import { Command as Command23 } from "commander";
1805
- import ora18 from "ora";
1806
- 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) => {
2104
+ import ora19 from "ora";
2105
+ var BATCH_SIZE2 = 50;
2106
+ 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) => {
2107
+ const globalOptions = command.optsWithGlobals();
2108
+ const json = globalOptions.json ?? false;
2109
+ try {
2110
+ const cwd = process.cwd();
2111
+ const projectRoot = await findProjectRoot(cwd);
2112
+ if (!projectRoot) {
2113
+ throw new CLIError(
2114
+ "Not in a WaniWani project. Run 'waniwani init <name>' first.",
2115
+ "NOT_IN_PROJECT"
2116
+ );
2117
+ }
2118
+ const mcpId = await loadProjectMcpId(projectRoot);
2119
+ if (!mcpId) {
2120
+ throw new CLIError(
2121
+ "No MCP ID found in project config. Run 'waniwani init <name>' first.",
2122
+ "NO_MCP_ID"
2123
+ );
2124
+ }
2125
+ const spinner = ora19("Collecting files...").start();
2126
+ const files = await collectFiles(projectRoot);
2127
+ if (files.length === 0) {
2128
+ spinner.info("No files to sync");
2129
+ if (json) {
2130
+ formatOutput({ synced: 0, files: [] }, true);
2131
+ }
2132
+ return;
2133
+ }
2134
+ spinner.text = `Found ${files.length} files`;
2135
+ if (options.dryRun) {
2136
+ spinner.stop();
2137
+ console.log();
2138
+ console.log(chalk13.bold("Files that would be synced:"));
2139
+ for (const file of files) {
2140
+ console.log(` ${file.path}`);
2141
+ }
2142
+ console.log();
2143
+ console.log(`Total: ${files.length} files`);
2144
+ return;
2145
+ }
2146
+ const allWritten = [];
2147
+ const totalBatches = Math.ceil(files.length / BATCH_SIZE2);
2148
+ for (let i = 0; i < totalBatches; i++) {
2149
+ const batch = files.slice(i * BATCH_SIZE2, (i + 1) * BATCH_SIZE2);
2150
+ spinner.text = `Syncing files (${i + 1}/${totalBatches})...`;
2151
+ const result = await api.post(
2152
+ `/api/mcp/sandboxes/${mcpId}/files`,
2153
+ {
2154
+ files: batch.map((f) => ({
2155
+ path: f.path,
2156
+ content: f.content,
2157
+ encoding: f.encoding
2158
+ }))
2159
+ }
2160
+ );
2161
+ allWritten.push(...result.written);
2162
+ }
2163
+ spinner.succeed(`Synced ${allWritten.length} files`);
2164
+ if (json) {
2165
+ formatOutput(
2166
+ {
2167
+ synced: allWritten.length,
2168
+ files: allWritten
2169
+ },
2170
+ true
2171
+ );
2172
+ } else {
2173
+ console.log();
2174
+ formatSuccess(`${allWritten.length} files synced to sandbox`, false);
2175
+ }
2176
+ } catch (error) {
2177
+ handleError(error, json);
2178
+ process.exit(1);
2179
+ }
2180
+ });
2181
+
2182
+ // src/commands/task.ts
2183
+ import chalk14 from "chalk";
2184
+ import { Command as Command24 } from "commander";
2185
+ import ora20 from "ora";
2186
+ 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) => {
1807
2187
  const globalOptions = command.optsWithGlobals();
1808
2188
  const json = globalOptions.json ?? false;
1809
2189
  try {
@@ -1827,10 +2207,10 @@ var taskCommand = new Command23("task").description("Send a task to Claude runni
1827
2207
  const maxSteps = options.maxSteps ? Number.parseInt(options.maxSteps, 10) : defaults.maxSteps;
1828
2208
  if (!json) {
1829
2209
  console.log();
1830
- console.log(chalk12.bold("Task:"), prompt);
2210
+ console.log(chalk14.bold("Task:"), prompt);
1831
2211
  console.log();
1832
2212
  }
1833
- const spinner = ora18("Starting task...").start();
2213
+ const spinner = ora20("Starting task...").start();
1834
2214
  const baseUrl = await api.getBaseUrl();
1835
2215
  const response = await fetch(
1836
2216
  `${baseUrl}/api/mcp/sandboxes/${mcpId}/task`,
@@ -1884,7 +2264,7 @@ var taskCommand = new Command23("task").description("Send a task to Claude runni
1884
2264
  const event = parsed;
1885
2265
  steps.push({ type: "text", text: event.content });
1886
2266
  if (!json && event.content) {
1887
- console.log(chalk12.white(event.content));
2267
+ console.log(chalk14.white(event.content));
1888
2268
  }
1889
2269
  } else if (parsed.type === "tool_call") {
1890
2270
  const event = parsed;
@@ -1895,24 +2275,24 @@ var taskCommand = new Command23("task").description("Send a task to Claude runni
1895
2275
  output: event.output
1896
2276
  });
1897
2277
  if (!json) {
1898
- console.log(chalk12.cyan(`> Using tool: ${event.tool}`));
2278
+ console.log(chalk14.cyan(`> Using tool: ${event.tool}`));
1899
2279
  if (event.input?.command) {
1900
- console.log(chalk12.gray(` $ ${event.input.command}`));
2280
+ console.log(chalk14.gray(` $ ${event.input.command}`));
1901
2281
  }
1902
2282
  if (event.output) {
1903
2283
  const outputLines = event.output.split("\n");
1904
2284
  if (outputLines.length > 10) {
1905
2285
  console.log(
1906
- chalk12.gray(outputLines.slice(0, 5).join("\n"))
2286
+ chalk14.gray(outputLines.slice(0, 5).join("\n"))
1907
2287
  );
1908
2288
  console.log(
1909
- chalk12.gray(
2289
+ chalk14.gray(
1910
2290
  ` ... (${outputLines.length - 10} more lines)`
1911
2291
  )
1912
2292
  );
1913
- console.log(chalk12.gray(outputLines.slice(-5).join("\n")));
2293
+ console.log(chalk14.gray(outputLines.slice(-5).join("\n")));
1914
2294
  } else {
1915
- console.log(chalk12.gray(event.output));
2295
+ console.log(chalk14.gray(event.output));
1916
2296
  }
1917
2297
  }
1918
2298
  console.log();
@@ -1939,12 +2319,12 @@ var taskCommand = new Command23("task").description("Send a task to Claude runni
1939
2319
  } else {
1940
2320
  console.log();
1941
2321
  console.log(
1942
- chalk12.green("\u2713"),
2322
+ chalk14.green("\u2713"),
1943
2323
  `Task completed in ${finalStepCount} steps.`
1944
2324
  );
1945
2325
  if (maxStepsReached) {
1946
2326
  console.log(
1947
- chalk12.yellow("\u26A0"),
2327
+ chalk14.yellow("\u26A0"),
1948
2328
  "Maximum steps reached. Task may be incomplete."
1949
2329
  );
1950
2330
  }
@@ -1957,10 +2337,12 @@ var taskCommand = new Command23("task").description("Send a task to Claude runni
1957
2337
 
1958
2338
  // src/cli.ts
1959
2339
  var version = "0.1.0";
1960
- 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");
2340
+ 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");
1961
2341
  program.addCommand(loginCommand);
1962
2342
  program.addCommand(logoutCommand);
1963
2343
  program.addCommand(initCommand);
2344
+ program.addCommand(pushCommand);
2345
+ program.addCommand(devCommand);
1964
2346
  program.addCommand(mcpCommand);
1965
2347
  program.addCommand(taskCommand);
1966
2348
  program.addCommand(orgCommand);