@vibekiln/cutline-mcp-cli 0.12.0 → 0.14.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) {
@@ -173,12 +174,13 @@ function resolveServeRuntime() {
173
174
  source: 'npx',
174
175
  };
175
176
  }
176
- function buildServerConfig(runtime) {
177
+ function buildServerConfig(runtime, options) {
177
178
  const config = {};
179
+ const envArgs = options?.staging ? ['--staging'] : [];
178
180
  for (const name of SERVER_NAMES) {
179
181
  config[`cutline-${name}`] = {
180
182
  command: runtime.command,
181
- args: [...runtime.argsPrefix, 'serve', name],
183
+ args: [...runtime.argsPrefix, 'serve', name, ...envArgs],
182
184
  };
183
185
  }
184
186
  return config;
@@ -339,7 +341,7 @@ export async function setupCommand(options) {
339
341
  }
340
342
  // ── 3. Write MCP server config to IDEs ───────────────────────────────────
341
343
  const runtime = resolveServeRuntime();
342
- const serverConfig = buildServerConfig(runtime);
344
+ const serverConfig = buildServerConfig(runtime, { staging: options.staging });
343
345
  const home = homedir();
344
346
  const ideConfigs = [
345
347
  { name: 'Cursor', path: join(home, '.cursor', 'mcp.json') },
@@ -443,8 +445,9 @@ export async function setupCommand(options) {
443
445
  console.log(chalk.bold(' Claude Code one-liner alternative:\n'));
444
446
  console.log(chalk.dim(' If you prefer `claude mcp add` instead of ~/.claude.json:\n'));
445
447
  const coreServers = ['constraints', 'premortem', 'tools', 'exploration'];
448
+ const stagingArg = options.staging ? ' --staging' : '';
446
449
  for (const name of coreServers) {
447
- const invocation = [runtime.command, ...runtime.argsPrefix, 'serve', name].join(' ');
450
+ const invocation = [runtime.command, ...runtime.argsPrefix, 'serve', name].join(' ') + stagingArg;
448
451
  console.log(chalk.cyan(` claude mcp add cutline-${name} -- ${invocation}`));
449
452
  }
450
453
  console.log();
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')