@vibekiln/cutline-mcp-cli 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,6 +14,16 @@
14
14
  npm install -g @vibekiln/cutline-mcp-cli@latest
15
15
  ```
16
16
 
17
+ `@vibekiln/cutline-mcp-cli` includes the standard Cutline MCP servers and **SlopBurn** (the product quality engineering roguelike RPG mode) in the same install.
18
+
19
+ If you want a dedicated SlopBurn-first CLI with separate onboarding/config UX, use:
20
+
21
+ ```bash
22
+ npm install -g @vibekiln/slopburn-cli
23
+ slopburn setup
24
+ slopburn serve
25
+ ```
26
+
17
27
  ### Docker
18
28
 
19
29
  ```bash
@@ -62,6 +72,11 @@ Cutline interprets intent and tier:
62
72
  | **AI Personas** — stakeholder feedback on features | — | Full access |
63
73
  | **Idea Validation** — fast-track from free web validation | — | Included |
64
74
 
75
+ ### SlopBurn
76
+
77
+ SlopBurn is a **product quality engineering roguelike RPG** built into the Cutline MCP ecosystem.
78
+ It turns deep-dive + verification loops into game progression, where test checks act like encounters, XP tracks quality gains, and MCP servers unlock as spells.
79
+
65
80
  ## 54 MCP Tools
66
81
 
67
82
  ### Free Tier
@@ -2,5 +2,5 @@ export interface CallbackResult {
2
2
  token: string;
3
3
  email?: string;
4
4
  }
5
- export type CallbackSource = 'login' | 'setup' | 'upgrade';
5
+ export type CallbackSource = 'login' | 'setup' | 'upgrade' | 'slopburn';
6
6
  export declare function startCallbackServer(source?: CallbackSource): Promise<CallbackResult>;
@@ -59,12 +59,20 @@ export async function startCallbackServer(source = 'login') {
59
59
  <div class="checkmark">&#10003;</div>
60
60
  <h1>You're in!</h1>
61
61
  ${email ? `<p class="email">${email}</p>` : ''}
62
- ${source === 'login' ? `
62
+ ${(source === 'login' || source === 'slopburn') ? `
63
63
  <div class="steps">
64
64
  <h3>Next in your terminal</h3>
65
+ ${source === 'slopburn'
66
+ ? `
67
+ <div class="step"><span class="num">1</span><span>Run <code>slopburn setup --staging</code> to install SlopBurn MCP config</span></div>
68
+ <div class="step"><span class="num">2</span><span>Run <code>slopburn serve --staging</code> to start the SlopBurn server</span></div>
69
+ <div class="step"><span class="num">3</span><span>Tell your agent: <em>"start slopburn and create my first run"</em></span></div>
70
+ `
71
+ : `
65
72
  <div class="step"><span class="num">1</span><span>Run <code>cutline-mcp init</code> to generate IDE rules</span></div>
66
73
  <div class="step"><span class="num">2</span><span>Run <code>cutline-mcp setup</code> to connect MCP servers</span></div>
67
74
  <div class="step"><span class="num">3</span><span>Ask your agent: <em>"Run an engineering audit"</em></span></div>
75
+ `}
68
76
  </div>` : `
69
77
  <div class="steps">
70
78
  <h3>Go back to your terminal</h3>
@@ -91,7 +91,10 @@ export async function loginCommand(options) {
91
91
  }
92
92
  // Start callback server
93
93
  spinner.text = 'Waiting for authentication...';
94
- const serverPromise = startCallbackServer(options.source ?? 'login');
94
+ const envSource = process.env.CUTLINE_MCP_AUTH_SOURCE;
95
+ const validSource = (value) => value === 'login' || value === 'setup' || value === 'upgrade' || value === 'slopburn';
96
+ const callbackSource = options.source ?? (validSource(envSource) ? envSource : 'login');
97
+ const serverPromise = startCallbackServer(callbackSource);
95
98
  // Open browser — default is the quick email-only flow
96
99
  let authUrl = `${config.AUTH_URL}?callback=${encodeURIComponent(config.CALLBACK_URL)}`;
97
100
  if (options.signup) {
@@ -1,8 +1,12 @@
1
+ export declare const SERVER_MAP: Record<string, string>;
1
2
  interface ServeOptions {
2
3
  http?: boolean;
3
4
  host?: string;
4
5
  port?: string;
5
6
  path?: string;
7
+ staging?: boolean;
6
8
  }
9
+ export declare function resolveServerFileName(serverName: string): string | null;
10
+ export declare function resolveServerBundlePath(serverName: string, baseDir?: string): string | null;
7
11
  export declare function serveCommand(serverName: string, options?: ServeOptions): void;
8
12
  export {};
@@ -4,14 +4,22 @@ import { resolve, dirname } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { existsSync } from 'node:fs';
6
6
  const __dirname = dirname(fileURLToPath(import.meta.url));
7
- const SERVER_MAP = {
7
+ export const SERVER_MAP = {
8
8
  constraints: 'cutline-server.js',
9
9
  premortem: 'premortem-server.js',
10
10
  exploration: 'exploration-server.js',
11
11
  tools: 'tools-server.js',
12
12
  output: 'output-server.js',
13
13
  integrations: 'integrations-server.js',
14
+ slopburn: 'slopburn-server.js',
14
15
  };
16
+ export function resolveServerFileName(serverName) {
17
+ return SERVER_MAP[serverName] ?? null;
18
+ }
19
+ export function resolveServerBundlePath(serverName, baseDir = __dirname) {
20
+ const fileName = resolveServerFileName(serverName);
21
+ return fileName ? resolve(baseDir, '../servers', fileName) : null;
22
+ }
15
23
  function handleChildMessage(message, pending) {
16
24
  const parsed = message;
17
25
  const id = normalizeId(parsed?.id);
@@ -72,9 +80,12 @@ function serveHttpBridge(serverName, serverPath, opts) {
72
80
  console.error(`Invalid --port value: ${opts.port}`);
73
81
  process.exit(1);
74
82
  }
83
+ const childEnv = opts.staging
84
+ ? { ...process.env, CUTLINE_ENV: 'staging' }
85
+ : process.env;
75
86
  const child = spawn(process.execPath, [serverPath], {
76
87
  stdio: ['pipe', 'pipe', 'inherit'],
77
- env: process.env,
88
+ env: childEnv,
78
89
  });
79
90
  const pending = new Map();
80
91
  let stdoutBuffer = Buffer.alloc(0);
@@ -216,18 +227,20 @@ function serveHttpBridge(serverName, serverPath, opts) {
216
227
  });
217
228
  }
218
229
  export function serveCommand(serverName, options = {}) {
219
- const fileName = SERVER_MAP[serverName];
220
- if (!fileName) {
230
+ const serverPath = resolveServerBundlePath(serverName);
231
+ if (!serverPath) {
221
232
  const valid = Object.keys(SERVER_MAP).join(', ');
222
233
  console.error(`Unknown server: "${serverName}". Valid names: ${valid}`);
223
234
  process.exit(1);
224
235
  }
225
- const serverPath = resolve(__dirname, '../servers', fileName);
226
236
  if (!existsSync(serverPath)) {
227
237
  console.error(`Server bundle not found at ${serverPath}`);
228
238
  console.error('The package may not have been built correctly.');
229
239
  process.exit(1);
230
240
  }
241
+ const childEnv = options.staging
242
+ ? { ...process.env, CUTLINE_ENV: 'staging' }
243
+ : process.env;
231
244
  if (options.http) {
232
245
  serveHttpBridge(serverName, serverPath, options);
233
246
  return;
@@ -236,7 +249,7 @@ export function serveCommand(serverName, options = {}) {
236
249
  try {
237
250
  execFileSync(process.execPath, [serverPath], {
238
251
  stdio: 'inherit',
239
- env: process.env,
252
+ env: childEnv,
240
253
  });
241
254
  }
242
255
  catch (err) {
@@ -27,6 +27,7 @@ const SERVER_NAMES = [
27
27
  'tools',
28
28
  'output',
29
29
  'integrations',
30
+ 'slopburn',
30
31
  ];
31
32
  const AUDIT_DIMENSIONS = ['engineering', 'security', 'reliability', 'scalability', 'compliance'];
32
33
  async function detectTier(options) {
@@ -146,7 +147,7 @@ function writeAuditVisibilityConfig(hiddenDimensions) {
146
147
  mkdirSync(configDir, { recursive: true });
147
148
  writeFileSync(configPath, JSON.stringify(next, null, 2) + '\n');
148
149
  }
149
- function resolveServeRuntime() {
150
+ function resolveServeRuntime(options) {
150
151
  // Optional explicit override for advanced environments.
151
152
  if (process.env.CUTLINE_MCP_BIN?.trim()) {
152
153
  return {
@@ -167,18 +168,22 @@ function resolveServeRuntime() {
167
168
  // Fallback for machines without a global install.
168
169
  const voltaNpx = join(homedir(), '.volta', 'bin', 'npx');
169
170
  const npxCommand = existsSync(voltaNpx) ? voltaNpx : 'npx';
171
+ const packageName = options?.staging
172
+ ? '@kylewadegrove/cutline-mcp-cli-staging@latest'
173
+ : '@vibekiln/cutline-mcp-cli@latest';
170
174
  return {
171
175
  command: npxCommand,
172
- argsPrefix: ['-y', '@vibekiln/cutline-mcp-cli@latest'],
176
+ argsPrefix: ['-y', packageName],
173
177
  source: 'npx',
174
178
  };
175
179
  }
176
- function buildServerConfig(runtime) {
180
+ function buildServerConfig(runtime, options) {
177
181
  const config = {};
182
+ const envArgs = options?.staging ? ['--staging'] : [];
178
183
  for (const name of SERVER_NAMES) {
179
184
  config[`cutline-${name}`] = {
180
185
  command: runtime.command,
181
- args: [...runtime.argsPrefix, 'serve', name],
186
+ args: [...runtime.argsPrefix, 'serve', name, ...envArgs],
182
187
  };
183
188
  }
184
189
  return config;
@@ -338,8 +343,8 @@ export async function setupCommand(options) {
338
343
  console.log(chalk.dim(' Hidden audit dimensions: none\n'));
339
344
  }
340
345
  // ── 3. Write MCP server config to IDEs ───────────────────────────────────
341
- const runtime = resolveServeRuntime();
342
- const serverConfig = buildServerConfig(runtime);
346
+ const runtime = resolveServeRuntime({ staging: options.staging });
347
+ const serverConfig = buildServerConfig(runtime, { staging: options.staging });
343
348
  const home = homedir();
344
349
  const ideConfigs = [
345
350
  { name: 'Cursor', path: join(home, '.cursor', 'mcp.json') },
@@ -443,8 +448,9 @@ export async function setupCommand(options) {
443
448
  console.log(chalk.bold(' Claude Code one-liner alternative:\n'));
444
449
  console.log(chalk.dim(' If you prefer `claude mcp add` instead of ~/.claude.json:\n'));
445
450
  const coreServers = ['constraints', 'premortem', 'tools', 'exploration'];
451
+ const stagingArg = options.staging ? ' --staging' : '';
446
452
  for (const name of coreServers) {
447
- const invocation = [runtime.command, ...runtime.argsPrefix, 'serve', name].join(' ');
453
+ const invocation = [runtime.command, ...runtime.argsPrefix, 'serve', name].join(' ') + stagingArg;
448
454
  console.log(chalk.cyan(` claude mcp add cutline-${name} -- ${invocation}`));
449
455
  }
450
456
  console.log();
@@ -492,7 +498,10 @@ export async function setupCommand(options) {
492
498
  }
493
499
  console.log();
494
500
  console.log(chalk.dim(` cutline-mcp v${version} · docs: https://thecutline.ai/docs/setup`));
495
- console.log(chalk.dim(' Testing bootstrap:'), chalk.cyan('npx -y @vibekiln/cutline-mcp-cli@latest setup'));
501
+ const bootstrapPackage = options.staging
502
+ ? '@kylewadegrove/cutline-mcp-cli-staging@latest'
503
+ : '@vibekiln/cutline-mcp-cli@latest';
504
+ console.log(chalk.dim(' Testing bootstrap:'), chalk.cyan(`npx -y ${bootstrapPackage} setup${options.staging ? ' --staging' : ''}`));
496
505
  console.log(chalk.dim(' Optional repo policy contract:'), chalk.cyan('cutline-mcp policy-init'));
497
506
  console.log();
498
507
  }
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-
17
17
  const program = new Command();
18
18
  program
19
19
  .name('cutline-mcp')
20
- .description('CLI and MCP servers for Cutline')
20
+ .description('CLI and MCP servers for Cutline, including SlopBurn (a product quality engineering roguelike RPG)')
21
21
  .version(pkg.version);
22
22
  program
23
23
  .command('login')
@@ -47,7 +47,8 @@ program
47
47
  .action(upgradeCommand);
48
48
  program
49
49
  .command('serve <server>')
50
- .description('Start an MCP server (constraints, premortem, exploration, tools, output, integrations)')
50
+ .description('Start an MCP server (constraints, premortem, exploration, tools, output, integrations, slopburn)')
51
+ .option('--staging', 'Use staging environment')
51
52
  .option('--http', 'Expose the selected stdio server over an HTTP bridge')
52
53
  .option('--host <host>', 'HTTP bind host for bridge mode (default: 0.0.0.0)')
53
54
  .option('--port <port>', 'HTTP port for bridge mode (default: 8080)')
@@ -57,6 +58,7 @@ program
57
58
  host: opts.host,
58
59
  port: opts.port,
59
60
  path: opts.path,
61
+ staging: opts.staging,
60
62
  }));
61
63
  program
62
64
  .command('setup')