@kylewadegrove/cutline-mcp-cli 0.7.1 → 0.7.2

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.
@@ -1,12 +1,16 @@
1
- import keytar from 'keytar';
2
- const SERVICE_NAME = 'cutline-mcp';
3
- const ACCOUNT_NAME = 'refresh-token';
1
+ import { saveConfig, loadConfig } from '../utils/config-store.js';
4
2
  export async function storeRefreshToken(token) {
5
- await keytar.setPassword(SERVICE_NAME, ACCOUNT_NAME, token);
3
+ saveConfig({ refreshToken: token });
6
4
  }
7
5
  export async function getRefreshToken() {
8
- return await keytar.getPassword(SERVICE_NAME, ACCOUNT_NAME);
6
+ const config = loadConfig();
7
+ return config.refreshToken || null;
9
8
  }
10
9
  export async function deleteRefreshToken() {
11
- return await keytar.deletePassword(SERVICE_NAME, ACCOUNT_NAME);
10
+ const config = loadConfig();
11
+ if (!config.refreshToken)
12
+ return false;
13
+ delete config.refreshToken;
14
+ saveConfig(config);
15
+ return true;
12
16
  }
@@ -200,21 +200,17 @@ export async function setupCommand(options) {
200
200
  const home = homedir();
201
201
  const ideConfigs = [
202
202
  { name: 'Cursor', path: join(home, '.cursor', 'mcp.json') },
203
- { name: 'Claude Code', path: join(home, '.claude', 'settings.json') },
203
+ { name: 'Claude Code', path: join(home, '.claude.json') },
204
204
  ];
205
205
  let wroteAny = false;
206
206
  for (const ide of ideConfigs) {
207
- // Write to Cursor always (primary target); write to Claude if dir exists
208
- const dirExists = existsSync(join(ide.path, '..'));
209
- if (ide.name === 'Cursor' || dirExists) {
210
- try {
211
- mergeIdeConfig(ide.path, serverConfig);
212
- console.log(chalk.green(` ✓ ${ide.name}`), chalk.dim(ide.path));
213
- wroteAny = true;
214
- }
215
- catch (err) {
216
- console.log(chalk.red(` ✗ ${ide.name}`), chalk.dim(err.message));
217
- }
207
+ try {
208
+ mergeIdeConfig(ide.path, serverConfig);
209
+ console.log(chalk.green(` ✓ ${ide.name}`), chalk.dim(ide.path));
210
+ wroteAny = true;
211
+ }
212
+ catch (err) {
213
+ console.log(chalk.red(` ✗ ${ide.name}`), chalk.dim(err.message));
218
214
  }
219
215
  }
220
216
  if (wroteAny) {
@@ -230,7 +226,7 @@ export async function setupCommand(options) {
230
226
  await initCommand({ projectRoot: options.projectRoot, staging: options.staging });
231
227
  // ── 5. Claude Code one-liners ────────────────────────────────────────────
232
228
  console.log(chalk.bold(' Claude Code one-liner alternative:\n'));
233
- console.log(chalk.dim(' If you prefer `claude mcp add` instead of settings.json:\n'));
229
+ console.log(chalk.dim(' If you prefer `claude mcp add` instead of ~/.claude.json:\n'));
234
230
  const coreServers = ['constraints', 'premortem', 'tools', 'exploration'];
235
231
  for (const name of coreServers) {
236
232
  console.log(chalk.cyan(` claude mcp add cutline-${name} -- npx -y @kylewadegrove/cutline-mcp-cli serve ${name}`));
@@ -330,18 +330,6 @@ async function getStoredToken() {
330
330
  } catch (e) {
331
331
  console.error("[MCP Auth] Failed to read config file:", e);
332
332
  }
333
- try {
334
- console.error("[MCP Auth Debug] Attempting to import keytar...");
335
- const keytar = await import("keytar");
336
- console.error("[MCP Auth Debug] Keytar imported successfully, getting password...");
337
- const token = await keytar.getPassword("cutline-mcp", "refresh-token");
338
- console.error("[MCP Auth Debug] Token retrieved:", token ? "YES (length: " + token.length + ")" : "NO TOKEN FOUND");
339
- if (token) {
340
- return { refreshToken: token };
341
- }
342
- } catch (error) {
343
- console.error("[MCP Auth Error] Failed to access keychain:", error);
344
- }
345
333
  return null;
346
334
  }
347
335
  async function requirePremiumWithAutoAuth(authToken) {
@@ -904,6 +892,10 @@ async function cfRegenAssumptions(input, doc) {
904
892
  async function cfRegenExperiments(input, doc) {
905
893
  return callCF("regenExperiments", { input, doc }, 9e4);
906
894
  }
895
+ async function cfGenerateStructuredContent(options) {
896
+ const res = await proxy("llm.generate", options);
897
+ return res.text;
898
+ }
907
899
  async function cfGenerateEmbeddings(texts, taskType = "RETRIEVAL_DOCUMENT") {
908
900
  return proxy("embeddings.generate", { texts, taskType });
909
901
  }
@@ -1062,6 +1054,7 @@ export {
1062
1054
  cfPremortemChatAgent,
1063
1055
  cfRegenAssumptions,
1064
1056
  cfRegenExperiments,
1057
+ cfGenerateStructuredContent,
1065
1058
  cfGenerateEmbeddings,
1066
1059
  cfExplorationAgent,
1067
1060
  cfConsultingDiscoveryAgent,
@@ -25,6 +25,7 @@ import {
25
25
  cfGenerateChatSuggestion,
26
26
  cfGenerateEmbeddings,
27
27
  cfGenerateExplorationResponse,
28
+ cfGenerateStructuredContent,
28
29
  cfGenerateTemplateResponse,
29
30
  cfGenerateTrialRun,
30
31
  cfGetWikiMarkdown,
@@ -72,7 +73,7 @@ import {
72
73
  upsertEntities,
73
74
  upsertNodes,
74
75
  validateRequestSize
75
- } from "./chunk-TJTE5Q2C.js";
76
+ } from "./chunk-ZVWDXO6M.js";
76
77
  import {
77
78
  GraphTraverser,
78
79
  computeGenericGraphMetrics,
@@ -2752,512 +2753,6 @@ async function propagateConstraints(productId, sourceEntityId, targetEntityId, s
2752
2753
  return propagated;
2753
2754
  }
2754
2755
 
2755
- // ../mcp/dist/src/orchestrator/agents/shared/vertex.js
2756
- import { VertexAI } from "@google-cloud/vertexai";
2757
- import { GoogleAuth } from "google-auth-library";
2758
- import { Buffer } from "node:buffer";
2759
-
2760
- // ../mcp/dist/src/shared/circuit-breaker.js
2761
- var DEFAULT_OPTIONS = {
2762
- failureThreshold: 5,
2763
- resetTimeoutMs: 6e4,
2764
- halfOpenMaxAttempts: 1,
2765
- name: "default"
2766
- };
2767
- var CircuitBreakerOpenError = class extends Error {
2768
- circuitName;
2769
- retriesAt;
2770
- constructor(circuitName, retriesAt) {
2771
- super(`Circuit "${circuitName}" is open \u2014 fast-failing. Retries at ${new Date(retriesAt).toISOString()}`);
2772
- this.circuitName = circuitName;
2773
- this.retriesAt = retriesAt;
2774
- this.name = "CircuitBreakerOpenError";
2775
- }
2776
- };
2777
- var CircuitBreaker = class {
2778
- state = "closed";
2779
- failureCount = 0;
2780
- lastFailureTime = 0;
2781
- halfOpenAttempts = 0;
2782
- opts;
2783
- constructor(options) {
2784
- this.opts = { ...DEFAULT_OPTIONS, ...options };
2785
- }
2786
- getState() {
2787
- this.maybeTransition();
2788
- return this.state;
2789
- }
2790
- getStats() {
2791
- return { state: this.getState(), failureCount: this.failureCount, lastFailureTime: this.lastFailureTime };
2792
- }
2793
- /**
2794
- * Execute a function through the circuit breaker.
2795
- * Throws CircuitBreakerOpenError if the circuit is open.
2796
- */
2797
- async execute(fn) {
2798
- this.maybeTransition();
2799
- if (this.state === "open") {
2800
- throw new CircuitBreakerOpenError(this.opts.name, this.lastFailureTime + this.opts.resetTimeoutMs);
2801
- }
2802
- if (this.state === "half_open" && this.halfOpenAttempts >= this.opts.halfOpenMaxAttempts) {
2803
- throw new CircuitBreakerOpenError(this.opts.name, this.lastFailureTime + this.opts.resetTimeoutMs);
2804
- }
2805
- if (this.state === "half_open") {
2806
- this.halfOpenAttempts++;
2807
- }
2808
- try {
2809
- const result = await fn();
2810
- this.onSuccess();
2811
- return result;
2812
- } catch (err) {
2813
- this.onFailure();
2814
- throw err;
2815
- }
2816
- }
2817
- /** Force the circuit into a specific state (useful for testing) */
2818
- reset() {
2819
- this.state = "closed";
2820
- this.failureCount = 0;
2821
- this.halfOpenAttempts = 0;
2822
- this.lastFailureTime = 0;
2823
- }
2824
- onSuccess() {
2825
- this.failureCount = 0;
2826
- this.halfOpenAttempts = 0;
2827
- if (this.state === "half_open") {
2828
- this.state = "closed";
2829
- console.log(JSON.stringify({
2830
- severity: "INFO",
2831
- circuit_breaker: this.opts.name,
2832
- event: "circuit_closed",
2833
- message: `Circuit "${this.opts.name}" recovered \u2014 closing`
2834
- }));
2835
- }
2836
- }
2837
- onFailure() {
2838
- this.failureCount++;
2839
- this.lastFailureTime = Date.now();
2840
- if (this.state === "half_open") {
2841
- this.state = "open";
2842
- console.log(JSON.stringify({
2843
- severity: "WARNING",
2844
- circuit_breaker: this.opts.name,
2845
- event: "circuit_reopened",
2846
- message: `Circuit "${this.opts.name}" probe failed \u2014 re-opening`
2847
- }));
2848
- return;
2849
- }
2850
- if (this.failureCount >= this.opts.failureThreshold) {
2851
- this.state = "open";
2852
- console.log(JSON.stringify({
2853
- severity: "WARNING",
2854
- circuit_breaker: this.opts.name,
2855
- event: "circuit_opened",
2856
- failure_count: this.failureCount,
2857
- message: `Circuit "${this.opts.name}" opened after ${this.failureCount} consecutive failures`
2858
- }));
2859
- }
2860
- }
2861
- maybeTransition() {
2862
- if (this.state === "open") {
2863
- const elapsed = Date.now() - this.lastFailureTime;
2864
- if (elapsed >= this.opts.resetTimeoutMs) {
2865
- this.state = "half_open";
2866
- this.halfOpenAttempts = 0;
2867
- console.log(JSON.stringify({
2868
- severity: "INFO",
2869
- circuit_breaker: this.opts.name,
2870
- event: "circuit_half_open",
2871
- message: `Circuit "${this.opts.name}" entering half-open after ${Math.round(elapsed / 1e3)}s`
2872
- }));
2873
- }
2874
- }
2875
- }
2876
- };
2877
-
2878
- // ../mcp/dist/src/shared/metrics.js
2879
- function emitMetric(name2, value, labels) {
2880
- const record = {
2881
- metric: true,
2882
- severity: "INFO",
2883
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2884
- name: name2,
2885
- value,
2886
- ...labels && { labels: stripUndefined(labels) }
2887
- };
2888
- console.log(JSON.stringify(record));
2889
- }
2890
- function stripUndefined(obj) {
2891
- const out = {};
2892
- for (const [k, v] of Object.entries(obj)) {
2893
- if (v !== void 0)
2894
- out[k] = v;
2895
- }
2896
- return out;
2897
- }
2898
-
2899
- // ../mcp/dist/src/orchestrator/agents/shared/vertex.js
2900
- var MODEL_PRICING = {
2901
- "gemini-2.5-pro": { inputPerM: 1.25, outputPerM: 10 },
2902
- "gemini-2.5-flash": { inputPerM: 0.15, outputPerM: 0.6 },
2903
- "gemini-2.0-flash": { inputPerM: 0.1, outputPerM: 0.4 },
2904
- "gemini-1.5-pro": { inputPerM: 1.25, outputPerM: 5 },
2905
- "gemini-1.5-flash": { inputPerM: 0.075, outputPerM: 0.3 }
2906
- };
2907
- function estimateCostUsd(model, tokensIn, tokensOut) {
2908
- const key = Object.keys(MODEL_PRICING).find((k) => model.startsWith(k)) ?? "";
2909
- const price = MODEL_PRICING[key];
2910
- if (!price)
2911
- return 0;
2912
- return Math.round((tokensIn * price.inputPerM + tokensOut * price.outputPerM) / 1e6 * 1e6) / 1e6;
2913
- }
2914
- var DEFAULT_MODEL_ID = process.env.MODEL_ID || "gemini-2.5-pro";
2915
- var DEFAULT_LOCATION = process.env.VERTEX_LOCATION || "us-central1";
2916
- var VERBOSE = (process.env.AGENT_LOG_VERBOSE ?? process.env.PREMORTEM_LOG_VERBOSE) === "true";
2917
- var PREVIEW = process.env.AGENT_LOG_PREVIEW === "true";
2918
- var PREVIEW_LIMIT = Math.max(0, parseInt(process.env.AGENT_LOG_PREVIEW_LIMIT || "400", 10) || 400);
2919
- function vlog(...args) {
2920
- if (VERBOSE) {
2921
- try {
2922
- console.log(...args);
2923
- } catch {
2924
- }
2925
- }
2926
- }
2927
- function safeStringify(x) {
2928
- try {
2929
- return JSON.stringify(x);
2930
- } catch {
2931
- return String(x);
2932
- }
2933
- }
2934
- function previewOf(x) {
2935
- const s = typeof x === "string" ? x : safeStringify(x);
2936
- return s.length > PREVIEW_LIMIT ? s.slice(0, PREVIEW_LIMIT) + "\u2026" : s;
2937
- }
2938
- var vertexCircuit = new CircuitBreaker({
2939
- name: "vertex-ai",
2940
- failureThreshold: 5,
2941
- resetTimeoutMs: 6e4,
2942
- halfOpenMaxAttempts: 1
2943
- });
2944
- var LAST_DIAG = null;
2945
- function asVertexResponseSchema(schema) {
2946
- const convert = (s) => {
2947
- const out = {};
2948
- if (s.type)
2949
- out.type = String(s.type).toLowerCase();
2950
- if (s.description != null)
2951
- out.description = s.description;
2952
- if (Array.isArray(s.enum))
2953
- out.enum = s.enum;
2954
- if (s.items)
2955
- out.items = convert(s.items);
2956
- if (s.properties) {
2957
- out.properties = {};
2958
- for (const [k, v] of Object.entries(s.properties)) {
2959
- out.properties[k] = convert(v);
2960
- }
2961
- }
2962
- if (Array.isArray(s.required))
2963
- out.required = s.required;
2964
- if (s.minItems != null)
2965
- out.minItems = s.minItems;
2966
- if (s.maxItems != null)
2967
- out.maxItems = s.maxItems;
2968
- if (s.minimum != null)
2969
- out.minimum = s.minimum;
2970
- if (s.maximum != null)
2971
- out.maximum = s.maximum;
2972
- if (s.minLength != null)
2973
- out.minLength = s.minLength;
2974
- if (s.maxLength != null)
2975
- out.maxLength = s.maxLength;
2976
- if (s.pattern != null)
2977
- out.pattern = s.pattern;
2978
- if (s.nullable != null)
2979
- out.nullable = s.nullable;
2980
- return out;
2981
- };
2982
- return convert(schema);
2983
- }
2984
- async function retryWithBackoff(fn, opts) {
2985
- let lastErr;
2986
- for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
2987
- try {
2988
- return await fn();
2989
- } catch (err) {
2990
- lastErr = err;
2991
- if (attempt >= opts.maxRetries || !opts.shouldRetry(err))
2992
- throw err;
2993
- const delayMs = opts.baseDelayMs * Math.pow(2, attempt) + Math.random() * 500;
2994
- vlog("vertex_retry", { attempt: attempt + 1, delayMs: Math.round(delayMs), error: String(err?.message || err).slice(0, 200) });
2995
- await new Promise((resolve) => setTimeout(resolve, delayMs));
2996
- }
2997
- }
2998
- throw lastErr;
2999
- }
3000
- async function generateStructuredContent(options) {
3001
- const project = await resolveProjectId();
3002
- const modelId = options.modelId || DEFAULT_MODEL_ID;
3003
- const location = options.location || DEFAULT_LOCATION;
3004
- const vertex = new VertexAI({ project, location });
3005
- const fullSystem = `${GLOBAL_SYSTEM_PROMPT}
3006
-
3007
- ${options.system || ""}`.trim();
3008
- const model = vertex.getGenerativeModel({
3009
- model: modelId,
3010
- systemInstruction: { role: "system", parts: [{ text: fullSystem }] }
3011
- });
3012
- const t0 = Date.now();
3013
- if (PREVIEW) {
3014
- try {
3015
- vlog("vertex_prompt", { model: modelId, location, system_preview: previewOf(fullSystem), user_preview: previewOf(options.user) });
3016
- } catch {
3017
- }
3018
- }
3019
- const tools = options.useGoogleSearch ? [{
3020
- googleSearch: {}
3021
- }] : void 0;
3022
- const useStructuredOutput = options.responseSchema && !options.useGoogleSearch;
3023
- const requestPayload = {
3024
- contents: [{ role: "user", parts: [{ text: options.user }] }],
3025
- generationConfig: {
3026
- temperature: options.temperature ?? 0.2,
3027
- maxOutputTokens: options.maxOutputTokens ?? 400,
3028
- responseMimeType: useStructuredOutput ? "application/json" : void 0,
3029
- responseSchema: useStructuredOutput ? asVertexResponseSchema(options.responseSchema) : void 0,
3030
- topP: options.topP ?? 0.9,
3031
- topK: options.topK ?? 40
3032
- },
3033
- tools
3034
- };
3035
- const response = await vertexCircuit.execute(() => retryWithBackoff(() => model.generateContent(requestPayload), {
3036
- maxRetries: 3,
3037
- baseDelayMs: 2e3,
3038
- shouldRetry: (err) => {
3039
- const msg = String(err?.message || err || "");
3040
- const name2 = String(err?.name || "");
3041
- if (msg.includes("429") || msg.includes("RESOURCE_EXHAUSTED"))
3042
- return true;
3043
- if (msg.includes("503") || msg.includes("500") || msg.includes("UNAVAILABLE"))
3044
- return true;
3045
- if (msg.includes("exception posting request") || msg.includes("ECONNRESET") || msg.includes("ETIMEDOUT") || msg.includes("socket hang up") || msg.includes("network") || msg.includes("fetch failed"))
3046
- return true;
3047
- if (name2 === "GoogleGenerativeAIError" || name2 === "GoogleGenerativeAIFetchError")
3048
- return true;
3049
- return false;
3050
- }
3051
- }));
3052
- const candidate = response.response.candidates?.[0];
3053
- const parts = candidate?.content?.parts ?? [];
3054
- const finishReason = candidate?.finishReason ?? candidate?.finish_reason;
3055
- const usage = response?.response?.usageMetadata ?? response?.usageMetadata;
3056
- const safety = candidate?.safetyRatings ?? candidate?.safety_ratings;
3057
- const partTypes = parts.map((p) => p?.functionCall ? "functionCall" : p?.structValue ? "structValue" : p?.inlineData ? "inlineData" : typeof p?.text === "string" ? "text" : "other");
3058
- const groundingMeta = candidate?.groundingMetadata;
3059
- const groundingMetadata = groundingMeta ? {
3060
- searchQueries: groundingMeta.searchEntryPoint?.renderedContent ? [groundingMeta.searchEntryPoint.renderedContent] : void 0,
3061
- webSearchQueries: groundingMeta.webSearchQueries,
3062
- groundingChunks: groundingMeta.groundingChunks?.length
3063
- } : void 0;
3064
- if (groundingMetadata?.groundingChunks) {
3065
- vlog("vertex_grounding", {
3066
- searchQueries: groundingMetadata.webSearchQueries,
3067
- chunks: groundingMetadata.groundingChunks
3068
- });
3069
- }
3070
- const pickNum = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
3071
- const tokens_in = pickNum(usage?.promptTokenCount ?? usage?.inputTokenCount ?? usage?.promptTokens ?? usage?.input_tokens);
3072
- const tokens_out = pickNum(usage?.candidatesTokenCount ?? usage?.outputTokenCount ?? usage?.completionTokens ?? usage?.output_tokens);
3073
- const total_tokens = pickNum(usage?.totalTokenCount ?? usage?.totalTokens) ?? (tokens_in ?? 0) + (tokens_out ?? 0);
3074
- const cost_usd = estimateCostUsd(modelId, tokens_in ?? 0, tokens_out ?? 0);
3075
- emitMetric("vertex.call.tokens", total_tokens ?? 0, {
3076
- model: modelId,
3077
- tokens_in: tokens_in ?? 0,
3078
- tokens_out: tokens_out ?? 0,
3079
- cost_usd,
3080
- latency_ms: Date.now() - t0
3081
- });
3082
- const logVertex = (selected, lengths) => {
3083
- if (!VERBOSE)
3084
- return;
3085
- vlog("vertex_done", { model: modelId, location, tokens_in, tokens_out, total_tokens, finishReason, selected, parts_count: partTypes.length, latency_ms: Date.now() - t0, lengths });
3086
- };
3087
- if (!parts.length) {
3088
- try {
3089
- const out = JSON.stringify(response.response);
3090
- LAST_DIAG = { finishReason, usage, safety, parts: { count: 0, types: [] }, selected: "text", lengths: { funcArgs: 0, inline: 0, text: out.length }, groundingMetadata };
3091
- logVertex("text", { funcArgs: 0, inline: 0, text: out.length });
3092
- return out;
3093
- } catch {
3094
- }
3095
- LAST_DIAG = { finishReason, usage, safety, parts: { count: 0, types: [] }, selected: "text", lengths: { funcArgs: 0, inline: 0, text: 0 }, groundingMetadata };
3096
- logVertex("text", { funcArgs: 0, inline: 0, text: 0 });
3097
- return "";
3098
- }
3099
- const textSegments = [];
3100
- let funcArgs = "";
3101
- let sawFuncArgs = false;
3102
- let structCandidate = void 0;
3103
- const inlineSegments = [];
3104
- const preferText = !!options.preferText && !options.responseSchema;
3105
- for (const p of parts) {
3106
- if (p?.functionCall?.arguments !== void 0) {
3107
- const args = p.functionCall.arguments;
3108
- sawFuncArgs = true;
3109
- if (typeof args === "string") {
3110
- funcArgs += args;
3111
- } else {
3112
- try {
3113
- structCandidate = args;
3114
- } catch {
3115
- }
3116
- }
3117
- continue;
3118
- }
3119
- if (p?.structValue) {
3120
- structCandidate = p.structValue;
3121
- continue;
3122
- }
3123
- if (p?.inlineData?.data) {
3124
- try {
3125
- const decoded = Buffer.from(p.inlineData.data, "base64").toString("utf8");
3126
- inlineSegments.push(decoded);
3127
- } catch {
3128
- }
3129
- continue;
3130
- }
3131
- if (typeof p?.text === "string") {
3132
- textSegments.push(p.text);
3133
- }
3134
- }
3135
- if (!preferText && sawFuncArgs && funcArgs.trim().length) {
3136
- let out = funcArgs.trim();
3137
- if (out.startsWith("```")) {
3138
- out = out.replace(/^```[a-zA-Z]*\n?/, "");
3139
- if (out.endsWith("```"))
3140
- out = out.replace(/```\s*$/, "");
3141
- }
3142
- LAST_DIAG = { finishReason, usage, safety, parts: { count: partTypes.length, types: partTypes }, selected: "args", lengths: { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: textSegments.join("").length }, groundingMetadata };
3143
- logVertex("args", { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: textSegments.join("").length });
3144
- return out;
3145
- }
3146
- if (!preferText && structCandidate !== void 0) {
3147
- try {
3148
- const out = JSON.stringify(structCandidate);
3149
- LAST_DIAG = { finishReason, usage, safety, parts: { count: partTypes.length, types: partTypes }, selected: "struct", lengths: { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: textSegments.join("").length }, groundingMetadata };
3150
- logVertex("struct", { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: textSegments.join("").length });
3151
- return out;
3152
- } catch {
3153
- }
3154
- }
3155
- if (inlineSegments.length) {
3156
- let inlineText = inlineSegments.join("").trim();
3157
- if (inlineText.startsWith("```")) {
3158
- inlineText = inlineText.replace(/^```[a-zA-Z]*\n?/, "");
3159
- if (inlineText.endsWith("```"))
3160
- inlineText = inlineText.replace(/```\s*$/, "");
3161
- }
3162
- if (options.responseSchema) {
3163
- try {
3164
- JSON.parse(inlineText);
3165
- LAST_DIAG = { finishReason, usage, safety, parts: { count: partTypes.length, types: partTypes }, selected: "inline", lengths: { funcArgs: funcArgs.length, inline: inlineText.length, text: textSegments.join("").length }, groundingMetadata };
3166
- logVertex("inline", { funcArgs: funcArgs.length, inline: inlineText.length, text: textSegments.join("").length });
3167
- return inlineText;
3168
- } catch {
3169
- }
3170
- const start = inlineText.indexOf("{");
3171
- const end = inlineText.lastIndexOf("}");
3172
- if (start !== -1 && end > start) {
3173
- const inner = inlineText.slice(start, end + 1).trim();
3174
- try {
3175
- JSON.parse(inner);
3176
- LAST_DIAG = { finishReason, usage, safety, parts: { count: partTypes.length, types: partTypes }, selected: "inline", lengths: { funcArgs: funcArgs.length, inline: inner.length, text: textSegments.join("").length }, groundingMetadata };
3177
- logVertex("inline", { funcArgs: funcArgs.length, inline: inner.length, text: textSegments.join("").length });
3178
- return inner;
3179
- } catch {
3180
- }
3181
- }
3182
- }
3183
- if (inlineText) {
3184
- textSegments.push(inlineText);
3185
- }
3186
- }
3187
- let text = textSegments.join("").trim();
3188
- if (!text) {
3189
- LAST_DIAG = { finishReason, usage, safety, parts: { count: partTypes.length, types: partTypes }, selected: "text", lengths: { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: 0 }, groundingMetadata };
3190
- logVertex("text", { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: 0 });
3191
- return "";
3192
- }
3193
- if (text.startsWith("```")) {
3194
- text = text.replace(/^```[a-zA-Z]*\n?/, "");
3195
- if (text.endsWith("```"))
3196
- text = text.replace(/```\s*$/, "");
3197
- }
3198
- if (options.responseSchema) {
3199
- try {
3200
- JSON.parse(text);
3201
- LAST_DIAG = { finishReason, usage, safety, parts: { count: partTypes.length, types: partTypes }, selected: "text", lengths: { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: text.length }, groundingMetadata };
3202
- logVertex("text", { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: text.length });
3203
- return text;
3204
- } catch {
3205
- }
3206
- const start = text.indexOf("{");
3207
- const end = text.lastIndexOf("}");
3208
- if (start !== -1 && end > start) {
3209
- const inner = text.slice(start, end + 1).trim();
3210
- try {
3211
- JSON.parse(inner);
3212
- LAST_DIAG = { finishReason, usage, safety, parts: { count: partTypes.length, types: partTypes }, selected: "text", lengths: { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: inner.length }, groundingMetadata };
3213
- logVertex("text", { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: inner.length });
3214
- return inner;
3215
- } catch {
3216
- }
3217
- }
3218
- }
3219
- LAST_DIAG = { finishReason, usage, safety, parts: { count: partTypes.length, types: partTypes }, selected: "text", lengths: { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: text.length }, groundingMetadata };
3220
- logVertex("text", { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: text.length });
3221
- return text;
3222
- }
3223
- var GLOBAL_SYSTEM_PROMPT = [
3224
- "You are a research-first, skepticism-driven product analyst. Your job is to reduce optimism bias and avoid guesswork.",
3225
- "",
3226
- "Behavioral guardrails:",
3227
- "- Evidence > opinions. Prefer verifiable facts, base rates, and conservative assumptions.",
3228
- "- Do not fabricate specifics (data points, sources, user counts, dates). If unknown, say so.",
3229
- "- Calibrate: reflect uncertainty explicitly and note assumptions driving your outputs.",
3230
- "- Prefer breadth from reference classes over narrow anecdotes. De-duplicate aggressively.",
3231
- "",
3232
- "When context is insufficient:",
3233
- "- Identify Information Gaps (what is missing to answer confidently).",
3234
- "- Propose How to Research with 3\u20136 concrete queries and credible source types to check.",
3235
- "- Use analogous reference classes to ground estimates, but flag them as assumptions.",
3236
- "",
3237
- "Estimation policy:",
3238
- "- For any numeric estimate required by the schema, return a realistic P50 value.",
3239
- "- In rationale, include a short range and the main driver(s). Default conservative when uncertainty is high.",
3240
- "",
3241
- "MoSCoW policy:",
3242
- "- MUST only if critical to core value or risk mitigation; SHOULD for strong value; COULD if deferrable; WONT if low value now or blocked (say why).",
3243
- "",
3244
- "Output hygiene:",
3245
- "- Follow the requested output schema exactly. If a field is unknown, omit it or use null if allowed.",
3246
- "- Keep rationales crisp (8\u201330 words), focusing on evidence, base rates, and key assumptions.",
3247
- "- No marketing language, no boilerplate, no code fences, no markdown\u2014only the required JSON/plain text."
3248
- ].join("\n");
3249
- async function resolveProjectId() {
3250
- if (process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT) {
3251
- return process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT;
3252
- }
3253
- try {
3254
- const auth = new GoogleAuth();
3255
- return await auth.getProjectId();
3256
- } catch (err) {
3257
- throw new Error(`Unable to resolve Google Cloud project id: ${err?.message ?? err}`);
3258
- }
3259
- }
3260
-
3261
2756
  // ../mcp/dist/mcp/src/constraints/constraint-matcher.js
3262
2757
  var MATCH_RULES = [
3263
2758
  {
@@ -7697,7 +7192,7 @@ function mcpAudit(entry) {
7697
7192
  }));
7698
7193
  }
7699
7194
  var DEFAULT_MODEL = process.env.MODEL_ID || "gemini-2.5-pro";
7700
- var generateStructuredContent2 = (options) => withLlmMonitor(options.modelId || DEFAULT_MODEL, () => generateStructuredContent(options));
7195
+ var generateStructuredContent = (options) => withLlmMonitor(options.modelId || DEFAULT_MODEL, () => cfGenerateStructuredContent(options));
7701
7196
  var explorationSessions = /* @__PURE__ */ new Map();
7702
7197
  function generateId(prefix = "id") {
7703
7198
  return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
@@ -8747,7 +8242,7 @@ Why AI: ${idea.whyAI}`
8747
8242
  getAllEdges: (pid) => getAllEdges(pid),
8748
8243
  getAllNodes: (pid) => getAllNodes(pid),
8749
8244
  getAllBindings: (pid) => getAllBindings(pid),
8750
- generateStructuredContent: generateStructuredContent2,
8245
+ generateStructuredContent,
8751
8246
  upsertNodes: (pid, sourceType, sourceId, nodes) => upsertNodes(pid, sourceType, sourceId, nodes),
8752
8247
  addEntity: (pid, entity) => addEntity(pid, entity),
8753
8248
  addEdges: (pid, edges) => addEdges(pid, edges),
@@ -9165,7 +8660,7 @@ Meta: ${JSON.stringify(output.meta)}` }
9165
8660
  throw new McpError(ErrorCode.InvalidParams, "Document text too short (min 50 chars)");
9166
8661
  }
9167
8662
  const finalDocId = doc_id || `doc_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
9168
- const nodes = await extractConstraintsFromDoc(finalDocId, text, generateStructuredContent2, { documentTitle: title, documentUrl: url });
8663
+ const nodes = await extractConstraintsFromDoc(finalDocId, text, generateStructuredContent, { documentTitle: title, documentUrl: url });
9169
8664
  if (nodes.length === 0) {
9170
8665
  return {
9171
8666
  content: [{
@@ -9902,7 +9397,7 @@ Meta: ${JSON.stringify(output.meta)}` }
9902
9397
  }
9903
9398
  const ingestionId = source_id || `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
9904
9399
  const existingEntities = await getAllEntities(product_id);
9905
- const graph = await ingestRequirements(ingestionId, text, generateStructuredContent2, existingEntities, { documentTitle: title });
9400
+ const graph = await ingestRequirements(ingestionId, text, generateStructuredContent, existingEntities, { documentTitle: title });
9906
9401
  if (graph.entities.length > 0) {
9907
9402
  await upsertEntities(product_id, "doc", ingestionId, graph.entities);
9908
9403
  }
@@ -10058,7 +9553,7 @@ Meta: ${JSON.stringify(meta)}`
10058
9553
  }]
10059
9554
  };
10060
9555
  }
10061
- const result = await inferBindings(entities, file_paths, use_llm ? generateStructuredContent2 : void 0);
9556
+ const result = await inferBindings(entities, file_paths, use_llm ? generateStructuredContent : void 0);
10062
9557
  if (result.bindings.length > 0) {
10063
9558
  await upsertBindings(product_id, result.bindings);
10064
9559
  }
@@ -10732,7 +10227,7 @@ Meta: ${JSON.stringify({
10732
10227
  getAllEdges: (pid) => getAllEdges(pid),
10733
10228
  getAllNodes: (pid) => getAllNodes(pid),
10734
10229
  getAllBindings: (pid) => getAllBindings(pid),
10735
- generateStructuredContent: generateStructuredContent2,
10230
+ generateStructuredContent,
10736
10231
  upsertNodes: (pid, sourceType, sourceId, nodes) => upsertNodes(pid, sourceType, sourceId, nodes),
10737
10232
  addEntity: (pid, entity) => addEntity(pid, entity),
10738
10233
  addEdges: (pid, edges) => addEdges(pid, edges),
@@ -13,6 +13,7 @@ import {
13
13
  cfGenerateChatSuggestion,
14
14
  cfGenerateEmbeddings,
15
15
  cfGenerateExplorationResponse,
16
+ cfGenerateStructuredContent,
16
17
  cfGenerateTemplateResponse,
17
18
  cfGenerateTrialRun,
18
19
  cfGetWikiMarkdown,
@@ -76,7 +77,7 @@ import {
76
77
  upsertEdges,
77
78
  upsertEntities,
78
79
  upsertNodes
79
- } from "./chunk-TJTE5Q2C.js";
80
+ } from "./chunk-ZVWDXO6M.js";
80
81
  export {
81
82
  addEdges,
82
83
  addEntity,
@@ -92,6 +93,7 @@ export {
92
93
  cfGenerateChatSuggestion,
93
94
  cfGenerateEmbeddings,
94
95
  cfGenerateExplorationResponse,
96
+ cfGenerateStructuredContent,
95
97
  cfGenerateTemplateResponse,
96
98
  cfGenerateTrialRun,
97
99
  cfGetWikiMarkdown,
@@ -14,7 +14,7 @@ import {
14
14
  requirePremiumWithAutoAuth,
15
15
  updateExplorationSession,
16
16
  validateRequestSize
17
- } from "./chunk-TJTE5Q2C.js";
17
+ } from "./chunk-ZVWDXO6M.js";
18
18
 
19
19
  // ../mcp/dist/mcp/src/exploration-server.js
20
20
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -13,7 +13,7 @@ import {
13
13
  requirePremiumWithAutoAuth,
14
14
  validateAuth,
15
15
  validateRequestSize
16
- } from "./chunk-TJTE5Q2C.js";
16
+ } from "./chunk-ZVWDXO6M.js";
17
17
 
18
18
  // ../mcp/dist/mcp/src/integrations-server.js
19
19
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -13,7 +13,7 @@ import {
13
13
  mapErrorToMcp,
14
14
  requirePremiumWithAutoAuth,
15
15
  validateRequestSize
16
- } from "./chunk-TJTE5Q2C.js";
16
+ } from "./chunk-ZVWDXO6M.js";
17
17
 
18
18
  // ../mcp/dist/mcp/src/output-server.js
19
19
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -27,7 +27,7 @@ import {
27
27
  updatePremortem,
28
28
  validateAuth,
29
29
  validateRequestSize
30
- } from "./chunk-TJTE5Q2C.js";
30
+ } from "./chunk-ZVWDXO6M.js";
31
31
 
32
32
  // ../mcp/dist/mcp/src/premortem-server.js
33
33
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -21,7 +21,7 @@ import {
21
21
  requirePremiumWithAutoAuth,
22
22
  validateAuth,
23
23
  validateRequestSize
24
- } from "./chunk-TJTE5Q2C.js";
24
+ } from "./chunk-ZVWDXO6M.js";
25
25
 
26
26
  // ../mcp/dist/mcp/src/tools-server.js
27
27
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kylewadegrove/cutline-mcp-cli",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
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",
@@ -53,13 +53,10 @@
53
53
  "author": "Cutline",
54
54
  "license": "MIT",
55
55
  "dependencies": {
56
- "@google-cloud/vertexai": "^1.9.2",
57
56
  "@modelcontextprotocol/sdk": "^1.0.0",
58
57
  "chalk": "^4.1.2",
59
58
  "commander": "^11.1.0",
60
59
  "dotenv": "^16.4.5",
61
- "google-auth-library": "^9.14.2",
62
- "keytar": "^7.9.0",
63
60
  "open": "^9.1.0",
64
61
  "ora": "^5.4.1",
65
62
  "zod": "^3.23.8"