@vibekiln/cutline-mcp-cli 0.4.0 → 0.4.1

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.
@@ -10,7 +10,6 @@ export async function deleteRefreshToken() {
10
10
  const config = loadConfig();
11
11
  if (!config.refreshToken)
12
12
  return false;
13
- delete config.refreshToken;
14
- saveConfig(config);
13
+ saveConfig({ ...config, refreshToken: undefined });
15
14
  return true;
16
15
  }
@@ -7093,7 +7093,7 @@ function deltaStr(current, previous) {
7093
7093
  return " (no change)";
7094
7094
  return diff > 0 ? ` (**+${diff}** since last scan)` : ` (**${diff}** since last scan)`;
7095
7095
  }
7096
- function formatAuditOutput(result, reportId, publicSiteUrl = "https://thecutline.ai", hiddenAuditDimensions = []) {
7096
+ function formatAuditOutput(result, reportId, publicSiteUrl = "https://thecutline.ai", hiddenAuditDimensions = [], fullReport = false) {
7097
7097
  const m = result.metrics;
7098
7098
  const p = result.previousMetrics;
7099
7099
  const isRescan = !!p;
@@ -7181,24 +7181,32 @@ function formatAuditOutput(result, reportId, publicSiteUrl = "https://thecutline
7181
7181
  const totalFindings = visibleFindings.length;
7182
7182
  const criticalCount = visibleFindings.filter((g) => g.severity === "critical" || g.severity === "high").length;
7183
7183
  if (totalFindings > 0) {
7184
- const topFinding = visibleFindings[0];
7185
- lines.push(``, `## #1 Finding \u2014 Fix This Now`, ``, `**[${topFinding.severity.toUpperCase()}] ${topFinding.title}**`, `*Category: ${topFinding.category}*`, ``, topFinding.description || "Address this finding to improve your readiness scores.", ``, `> Fix this issue, then re-run \`code_audit\` to see your scores improve.`);
7186
- const remaining = visibleFindings.slice(1);
7187
- if (remaining.length > 0) {
7188
- lines.push(``, `## ${remaining.length} More Finding${remaining.length > 1 ? "s" : ""} Detected`);
7189
- const teaserLimit = Math.min(remaining.length, 5);
7190
- for (let i = 0; i < teaserLimit; i++) {
7191
- lines.push(`- [${remaining[i].severity.toUpperCase()}] ${remaining[i].title}`);
7184
+ if (fullReport) {
7185
+ lines.push(``, `## Findings`, ``);
7186
+ for (const finding of visibleFindings) {
7187
+ lines.push(`**[${finding.severity.toUpperCase()}] ${finding.title}**`, `*Category: ${finding.category}*`, finding.description || "Address this finding to improve your readiness scores.", ``);
7192
7188
  }
7193
- if (remaining.length > teaserLimit) {
7194
- lines.push(`- ... and **${remaining.length - teaserLimit} more**`);
7189
+ lines.push(`> Re-run \`code_audit\` after fixes to measure score improvements.`);
7190
+ } else {
7191
+ const topFinding = visibleFindings[0];
7192
+ lines.push(``, `## #1 Finding \u2014 Fix This Now`, ``, `**[${topFinding.severity.toUpperCase()}] ${topFinding.title}**`, `*Category: ${topFinding.category}*`, ``, topFinding.description || "Address this finding to improve your readiness scores.", ``, `> Fix this issue, then re-run \`code_audit\` to see your scores improve.`);
7193
+ const remaining = visibleFindings.slice(1);
7194
+ if (remaining.length > 0) {
7195
+ lines.push(``, `## ${remaining.length} More Finding${remaining.length > 1 ? "s" : ""} Detected`);
7196
+ const teaserLimit = Math.min(remaining.length, 5);
7197
+ for (let i = 0; i < teaserLimit; i++) {
7198
+ lines.push(`- [${remaining[i].severity.toUpperCase()}] ${remaining[i].title}`);
7199
+ }
7200
+ if (remaining.length > teaserLimit) {
7201
+ lines.push(`- ... and **${remaining.length - teaserLimit} more**`);
7202
+ }
7195
7203
  }
7196
7204
  }
7197
7205
  }
7198
7206
  lines.push(``, `---`, ``);
7199
- if (totalFindings > 1) {
7207
+ if (!fullReport && totalFindings > 1) {
7200
7208
  lines.push(`### Unlock Full Analysis`, ``, `You fixed one \u2014 **${totalFindings - 1} findings** remain (${criticalCount} critical/high).`, `Upgrade to Cutline Pro for:`, `- Full details and remediation for every finding`, `- Prioritized RGR remediation plans your coding agent can execute`, `- Product-specific deep dive with feature-level constraint mapping`, `- Continuous score tracking with this audit as a baseline prior`, ``, `\u2192 Run \`cutline-mcp upgrade\` or visit **https://thecutline.ai/upgrade**`);
7201
- } else if (totalFindings === 1) {
7209
+ } else if (totalFindings >= 1) {
7202
7210
  lines.push(`### Next Steps`, ``, `Fix the finding above, then re-scan to see your scores improve.`, `When ready, run a **deep dive** for product-specific analysis \u2014`, `these generic scores will serve as a prior for your product graph.`);
7203
7211
  } else {
7204
7212
  lines.push(`### Next Steps`, ``, `No critical findings detected. Run a **deep dive** for product-specific`, `analysis with feature coverage and the generic scores as a prior.`);
@@ -8423,15 +8431,17 @@ Why AI: ${idea.whyAI}`
8423
8431
  return "engineering";
8424
8432
  return "security";
8425
8433
  };
8434
+ let hasPremiumSubscription = false;
8426
8435
  {
8427
8436
  const rateInfo = await getScanRateLimit();
8437
+ hasPremiumSubscription = rateInfo.subscription === "active" || rateInfo.subscription === "trialing";
8428
8438
  const now = /* @__PURE__ */ new Date();
8429
8439
  const resetAt = rateInfo.periodStart ? new Date(rateInfo.periodStart) : /* @__PURE__ */ new Date(0);
8430
8440
  const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
8431
8441
  if (resetAt < monthStart) {
8432
8442
  await updateScanRateLimit({ free_scan_counter: { count: 1, reset_at: now.toISOString() } });
8433
8443
  } else if (rateInfo.scanCount >= 3) {
8434
- if (rateInfo.subscription !== "active" && rateInfo.subscription !== "trialing") {
8444
+ if (!hasPremiumSubscription) {
8435
8445
  throw new McpError(ErrorCode.InvalidRequest, "Free scan limit reached (3/month). Run `cutline-mcp upgrade` in your terminal, or visit https://thecutline.ai/upgrade");
8436
8446
  }
8437
8447
  } else {
@@ -8500,7 +8510,7 @@ Why AI: ${idea.whyAI}`
8500
8510
  console.error("[code_audit] Report persistence failed (non-fatal):", e);
8501
8511
  }
8502
8512
  return {
8503
- content: [{ type: "text", text: formatAuditOutput(result, reportId, reportSiteUrl, hiddenAuditDimensions) }]
8513
+ content: [{ type: "text", text: formatAuditOutput(result, reportId, reportSiteUrl, hiddenAuditDimensions, hasPremiumSubscription) }]
8504
8514
  };
8505
8515
  }
8506
8516
  const authCtx = await resolveAuthContext(args.auth_token);
@@ -12,6 +12,13 @@ export function saveConfig(config) {
12
12
  ensureConfigDir();
13
13
  const current = loadConfig();
14
14
  const newConfig = { ...current, ...config };
15
+ // Treat explicit undefined values as key deletion so callers can clear
16
+ // persisted fields (for example during logout).
17
+ for (const [key, value] of Object.entries(config)) {
18
+ if (value === undefined) {
19
+ delete newConfig[key];
20
+ }
21
+ }
15
22
  // Remove legacy fields that are no longer stored (e.g. firebaseApiKey)
16
23
  // to prevent stale values from causing cross-project auth mismatches
17
24
  delete newConfig.firebaseApiKey;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibekiln/cutline-mcp-cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "CLI and MCP servers for Cutline — authenticate, then run constraint-aware MCP servers in Cursor or any MCP client.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",