@kylewadegrove/cutline-mcp-cli 0.7.0 → 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,13 @@ 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
+ }
899
+ async function cfGenerateEmbeddings(texts, taskType = "RETRIEVAL_DOCUMENT") {
900
+ return proxy("embeddings.generate", { texts, taskType });
901
+ }
907
902
  async function cfExplorationAgent(message, context) {
908
903
  return callCF("explorationAgent", { message, context }, 12e4);
909
904
  }
@@ -1059,6 +1054,8 @@ export {
1059
1054
  cfPremortemChatAgent,
1060
1055
  cfRegenAssumptions,
1061
1056
  cfRegenExperiments,
1057
+ cfGenerateStructuredContent,
1058
+ cfGenerateEmbeddings,
1062
1059
  cfExplorationAgent,
1063
1060
  cfConsultingDiscoveryAgent,
1064
1061
  cfCreateLinearIssues,
@@ -23,7 +23,9 @@ import {
23
23
  cfCreateLinearIssues,
24
24
  cfGenerateAnswer,
25
25
  cfGenerateChatSuggestion,
26
+ cfGenerateEmbeddings,
26
27
  cfGenerateExplorationResponse,
28
+ cfGenerateStructuredContent,
27
29
  cfGenerateTemplateResponse,
28
30
  cfGenerateTrialRun,
29
31
  cfGetWikiMarkdown,
@@ -71,7 +73,7 @@ import {
71
73
  upsertEntities,
72
74
  upsertNodes,
73
75
  validateRequestSize
74
- } from "./chunk-XKS3J74N.js";
76
+ } from "./chunk-ZVWDXO6M.js";
75
77
  import {
76
78
  GraphTraverser,
77
79
  computeGenericGraphMetrics,
@@ -85,10 +87,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
85
87
  import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError } from "@modelcontextprotocol/sdk/types.js";
86
88
 
87
89
  // ../mcp/dist/mcp/src/context-graph/embeddings.js
88
- import { GoogleAuth } from "google-auth-library";
89
- var EMBEDDING_MODEL = "text-embedding-005";
90
90
  var EMBEDDING_DIMENSIONS = 768;
91
- var DEFAULT_LOCATION = process.env.VERTEX_LOCATION || "us-central1";
92
91
  var QUERY_EMBED_CACHE_SIZE = 20;
93
92
  var QUERY_EMBED_CACHE_TTL = 30 * 60 * 1e3;
94
93
  var queryEmbedCache = /* @__PURE__ */ new Map();
@@ -113,17 +112,6 @@ function setQueryEmbedCached(query, result) {
113
112
  }
114
113
  queryEmbedCache.set(query, { result, fetchedAt: Date.now() });
115
114
  }
116
- async function resolveProjectId() {
117
- if (process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT) {
118
- return process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT;
119
- }
120
- try {
121
- const auth = new GoogleAuth();
122
- return await auth.getProjectId();
123
- } catch (err) {
124
- throw new Error(`Unable to resolve Google Cloud project id: ${err?.message ?? err}`);
125
- }
126
- }
127
115
  async function generateEmbedding(text) {
128
116
  const results = await generateEmbeddings([text]);
129
117
  return {
@@ -133,93 +121,40 @@ async function generateEmbedding(text) {
133
121
  }
134
122
  async function generateEmbeddings(texts) {
135
123
  if (texts.length === 0) {
136
- return { embeddings: [], model: EMBEDDING_MODEL };
124
+ return { embeddings: [], model: "text-embedding-005" };
137
125
  }
138
- const project = await resolveProjectId();
139
- const location = DEFAULT_LOCATION;
140
- const endpoint = `https://${location}-aiplatform.googleapis.com/v1/projects/${project}/locations/${location}/publishers/google/models/${EMBEDDING_MODEL}:predict`;
141
- const auth = new GoogleAuth({
142
- scopes: ["https://www.googleapis.com/auth/cloud-platform"]
143
- });
144
- const client = await auth.getClient();
145
- const batchSize = 5;
146
- const allEmbeddings = [];
147
- for (let i = 0; i < texts.length; i += batchSize) {
148
- const batch = texts.slice(i, i + batchSize);
149
- const requestBody = {
150
- instances: batch.map((text) => ({
151
- content: text,
152
- task_type: "RETRIEVAL_DOCUMENT"
153
- // Optimized for retrieval
154
- }))
126
+ try {
127
+ return await cfGenerateEmbeddings(texts, "RETRIEVAL_DOCUMENT");
128
+ } catch (error) {
129
+ console.error("Embedding proxy error:", error);
130
+ return {
131
+ embeddings: texts.map(() => new Array(EMBEDDING_DIMENSIONS).fill(0)),
132
+ model: "text-embedding-005"
155
133
  };
156
- try {
157
- const response = await client.request({
158
- url: endpoint,
159
- method: "POST",
160
- data: requestBody
161
- });
162
- const data = response.data;
163
- const predictions = data.predictions || [];
164
- for (const pred of predictions) {
165
- const embedding = pred.embeddings?.values || [];
166
- allEmbeddings.push(embedding);
167
- }
168
- } catch (error) {
169
- console.error("Embedding API error:", error);
170
- for (let j = 0; j < batch.length; j++) {
171
- allEmbeddings.push(new Array(EMBEDDING_DIMENSIONS).fill(0));
172
- }
173
- }
174
134
  }
175
- return {
176
- embeddings: allEmbeddings,
177
- model: EMBEDDING_MODEL
178
- };
179
135
  }
180
136
  async function generateQueryEmbedding(query) {
181
137
  const cached = getQueryEmbedCached(query);
182
138
  if (cached)
183
139
  return cached;
184
- const result = await generateQueryEmbeddingUncached(query);
185
- if (result.embedding.some((v) => v !== 0)) {
186
- setQueryEmbedCached(query, result);
187
- }
188
- return result;
189
- }
190
- async function generateQueryEmbeddingUncached(query) {
191
- const project = await resolveProjectId();
192
- const location = DEFAULT_LOCATION;
193
- const endpoint = `https://${location}-aiplatform.googleapis.com/v1/projects/${project}/locations/${location}/publishers/google/models/${EMBEDDING_MODEL}:predict`;
194
- const auth = new GoogleAuth({
195
- scopes: ["https://www.googleapis.com/auth/cloud-platform"]
196
- });
197
- const client = await auth.getClient();
198
- const requestBody = {
199
- instances: [{
200
- content: query,
201
- task_type: "RETRIEVAL_QUERY"
202
- }]
203
- };
140
+ let result;
204
141
  try {
205
- const response = await client.request({
206
- url: endpoint,
207
- method: "POST",
208
- data: requestBody
209
- });
210
- const data = response.data;
211
- const embedding = data.predictions?.[0]?.embeddings?.values || [];
212
- return {
213
- embedding,
142
+ const resp = await cfGenerateEmbeddings([query], "RETRIEVAL_QUERY");
143
+ result = {
144
+ embedding: resp.embeddings[0] || new Array(EMBEDDING_DIMENSIONS).fill(0),
214
145
  dimensions: EMBEDDING_DIMENSIONS
215
146
  };
216
147
  } catch (error) {
217
- console.error("Query embedding error:", error);
218
- return {
148
+ console.error("Query embedding proxy error:", error);
149
+ result = {
219
150
  embedding: new Array(EMBEDDING_DIMENSIONS).fill(0),
220
151
  dimensions: EMBEDDING_DIMENSIONS
221
152
  };
222
153
  }
154
+ if (result.embedding.some((v) => v !== 0)) {
155
+ setQueryEmbedCached(query, result);
156
+ }
157
+ return result;
223
158
  }
224
159
  function cosineSimilarity(a, b) {
225
160
  if (a.length !== b.length || a.length === 0)
@@ -2818,512 +2753,6 @@ async function propagateConstraints(productId, sourceEntityId, targetEntityId, s
2818
2753
  return propagated;
2819
2754
  }
2820
2755
 
2821
- // ../mcp/dist/src/orchestrator/agents/shared/vertex.js
2822
- import { VertexAI } from "@google-cloud/vertexai";
2823
- import { GoogleAuth as GoogleAuth2 } from "google-auth-library";
2824
- import { Buffer } from "node:buffer";
2825
-
2826
- // ../mcp/dist/src/shared/circuit-breaker.js
2827
- var DEFAULT_OPTIONS = {
2828
- failureThreshold: 5,
2829
- resetTimeoutMs: 6e4,
2830
- halfOpenMaxAttempts: 1,
2831
- name: "default"
2832
- };
2833
- var CircuitBreakerOpenError = class extends Error {
2834
- circuitName;
2835
- retriesAt;
2836
- constructor(circuitName, retriesAt) {
2837
- super(`Circuit "${circuitName}" is open \u2014 fast-failing. Retries at ${new Date(retriesAt).toISOString()}`);
2838
- this.circuitName = circuitName;
2839
- this.retriesAt = retriesAt;
2840
- this.name = "CircuitBreakerOpenError";
2841
- }
2842
- };
2843
- var CircuitBreaker = class {
2844
- state = "closed";
2845
- failureCount = 0;
2846
- lastFailureTime = 0;
2847
- halfOpenAttempts = 0;
2848
- opts;
2849
- constructor(options) {
2850
- this.opts = { ...DEFAULT_OPTIONS, ...options };
2851
- }
2852
- getState() {
2853
- this.maybeTransition();
2854
- return this.state;
2855
- }
2856
- getStats() {
2857
- return { state: this.getState(), failureCount: this.failureCount, lastFailureTime: this.lastFailureTime };
2858
- }
2859
- /**
2860
- * Execute a function through the circuit breaker.
2861
- * Throws CircuitBreakerOpenError if the circuit is open.
2862
- */
2863
- async execute(fn) {
2864
- this.maybeTransition();
2865
- if (this.state === "open") {
2866
- throw new CircuitBreakerOpenError(this.opts.name, this.lastFailureTime + this.opts.resetTimeoutMs);
2867
- }
2868
- if (this.state === "half_open" && this.halfOpenAttempts >= this.opts.halfOpenMaxAttempts) {
2869
- throw new CircuitBreakerOpenError(this.opts.name, this.lastFailureTime + this.opts.resetTimeoutMs);
2870
- }
2871
- if (this.state === "half_open") {
2872
- this.halfOpenAttempts++;
2873
- }
2874
- try {
2875
- const result = await fn();
2876
- this.onSuccess();
2877
- return result;
2878
- } catch (err) {
2879
- this.onFailure();
2880
- throw err;
2881
- }
2882
- }
2883
- /** Force the circuit into a specific state (useful for testing) */
2884
- reset() {
2885
- this.state = "closed";
2886
- this.failureCount = 0;
2887
- this.halfOpenAttempts = 0;
2888
- this.lastFailureTime = 0;
2889
- }
2890
- onSuccess() {
2891
- this.failureCount = 0;
2892
- this.halfOpenAttempts = 0;
2893
- if (this.state === "half_open") {
2894
- this.state = "closed";
2895
- console.log(JSON.stringify({
2896
- severity: "INFO",
2897
- circuit_breaker: this.opts.name,
2898
- event: "circuit_closed",
2899
- message: `Circuit "${this.opts.name}" recovered \u2014 closing`
2900
- }));
2901
- }
2902
- }
2903
- onFailure() {
2904
- this.failureCount++;
2905
- this.lastFailureTime = Date.now();
2906
- if (this.state === "half_open") {
2907
- this.state = "open";
2908
- console.log(JSON.stringify({
2909
- severity: "WARNING",
2910
- circuit_breaker: this.opts.name,
2911
- event: "circuit_reopened",
2912
- message: `Circuit "${this.opts.name}" probe failed \u2014 re-opening`
2913
- }));
2914
- return;
2915
- }
2916
- if (this.failureCount >= this.opts.failureThreshold) {
2917
- this.state = "open";
2918
- console.log(JSON.stringify({
2919
- severity: "WARNING",
2920
- circuit_breaker: this.opts.name,
2921
- event: "circuit_opened",
2922
- failure_count: this.failureCount,
2923
- message: `Circuit "${this.opts.name}" opened after ${this.failureCount} consecutive failures`
2924
- }));
2925
- }
2926
- }
2927
- maybeTransition() {
2928
- if (this.state === "open") {
2929
- const elapsed = Date.now() - this.lastFailureTime;
2930
- if (elapsed >= this.opts.resetTimeoutMs) {
2931
- this.state = "half_open";
2932
- this.halfOpenAttempts = 0;
2933
- console.log(JSON.stringify({
2934
- severity: "INFO",
2935
- circuit_breaker: this.opts.name,
2936
- event: "circuit_half_open",
2937
- message: `Circuit "${this.opts.name}" entering half-open after ${Math.round(elapsed / 1e3)}s`
2938
- }));
2939
- }
2940
- }
2941
- }
2942
- };
2943
-
2944
- // ../mcp/dist/src/shared/metrics.js
2945
- function emitMetric(name2, value, labels) {
2946
- const record = {
2947
- metric: true,
2948
- severity: "INFO",
2949
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2950
- name: name2,
2951
- value,
2952
- ...labels && { labels: stripUndefined(labels) }
2953
- };
2954
- console.log(JSON.stringify(record));
2955
- }
2956
- function stripUndefined(obj) {
2957
- const out = {};
2958
- for (const [k, v] of Object.entries(obj)) {
2959
- if (v !== void 0)
2960
- out[k] = v;
2961
- }
2962
- return out;
2963
- }
2964
-
2965
- // ../mcp/dist/src/orchestrator/agents/shared/vertex.js
2966
- var MODEL_PRICING = {
2967
- "gemini-2.5-pro": { inputPerM: 1.25, outputPerM: 10 },
2968
- "gemini-2.5-flash": { inputPerM: 0.15, outputPerM: 0.6 },
2969
- "gemini-2.0-flash": { inputPerM: 0.1, outputPerM: 0.4 },
2970
- "gemini-1.5-pro": { inputPerM: 1.25, outputPerM: 5 },
2971
- "gemini-1.5-flash": { inputPerM: 0.075, outputPerM: 0.3 }
2972
- };
2973
- function estimateCostUsd(model, tokensIn, tokensOut) {
2974
- const key = Object.keys(MODEL_PRICING).find((k) => model.startsWith(k)) ?? "";
2975
- const price = MODEL_PRICING[key];
2976
- if (!price)
2977
- return 0;
2978
- return Math.round((tokensIn * price.inputPerM + tokensOut * price.outputPerM) / 1e6 * 1e6) / 1e6;
2979
- }
2980
- var DEFAULT_MODEL_ID = process.env.MODEL_ID || "gemini-2.5-pro";
2981
- var DEFAULT_LOCATION2 = process.env.VERTEX_LOCATION || "us-central1";
2982
- var VERBOSE = (process.env.AGENT_LOG_VERBOSE ?? process.env.PREMORTEM_LOG_VERBOSE) === "true";
2983
- var PREVIEW = process.env.AGENT_LOG_PREVIEW === "true";
2984
- var PREVIEW_LIMIT = Math.max(0, parseInt(process.env.AGENT_LOG_PREVIEW_LIMIT || "400", 10) || 400);
2985
- function vlog(...args) {
2986
- if (VERBOSE) {
2987
- try {
2988
- console.log(...args);
2989
- } catch {
2990
- }
2991
- }
2992
- }
2993
- function safeStringify(x) {
2994
- try {
2995
- return JSON.stringify(x);
2996
- } catch {
2997
- return String(x);
2998
- }
2999
- }
3000
- function previewOf(x) {
3001
- const s = typeof x === "string" ? x : safeStringify(x);
3002
- return s.length > PREVIEW_LIMIT ? s.slice(0, PREVIEW_LIMIT) + "\u2026" : s;
3003
- }
3004
- var vertexCircuit = new CircuitBreaker({
3005
- name: "vertex-ai",
3006
- failureThreshold: 5,
3007
- resetTimeoutMs: 6e4,
3008
- halfOpenMaxAttempts: 1
3009
- });
3010
- var LAST_DIAG = null;
3011
- function asVertexResponseSchema(schema) {
3012
- const convert = (s) => {
3013
- const out = {};
3014
- if (s.type)
3015
- out.type = String(s.type).toLowerCase();
3016
- if (s.description != null)
3017
- out.description = s.description;
3018
- if (Array.isArray(s.enum))
3019
- out.enum = s.enum;
3020
- if (s.items)
3021
- out.items = convert(s.items);
3022
- if (s.properties) {
3023
- out.properties = {};
3024
- for (const [k, v] of Object.entries(s.properties)) {
3025
- out.properties[k] = convert(v);
3026
- }
3027
- }
3028
- if (Array.isArray(s.required))
3029
- out.required = s.required;
3030
- if (s.minItems != null)
3031
- out.minItems = s.minItems;
3032
- if (s.maxItems != null)
3033
- out.maxItems = s.maxItems;
3034
- if (s.minimum != null)
3035
- out.minimum = s.minimum;
3036
- if (s.maximum != null)
3037
- out.maximum = s.maximum;
3038
- if (s.minLength != null)
3039
- out.minLength = s.minLength;
3040
- if (s.maxLength != null)
3041
- out.maxLength = s.maxLength;
3042
- if (s.pattern != null)
3043
- out.pattern = s.pattern;
3044
- if (s.nullable != null)
3045
- out.nullable = s.nullable;
3046
- return out;
3047
- };
3048
- return convert(schema);
3049
- }
3050
- async function retryWithBackoff(fn, opts) {
3051
- let lastErr;
3052
- for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
3053
- try {
3054
- return await fn();
3055
- } catch (err) {
3056
- lastErr = err;
3057
- if (attempt >= opts.maxRetries || !opts.shouldRetry(err))
3058
- throw err;
3059
- const delayMs = opts.baseDelayMs * Math.pow(2, attempt) + Math.random() * 500;
3060
- vlog("vertex_retry", { attempt: attempt + 1, delayMs: Math.round(delayMs), error: String(err?.message || err).slice(0, 200) });
3061
- await new Promise((resolve) => setTimeout(resolve, delayMs));
3062
- }
3063
- }
3064
- throw lastErr;
3065
- }
3066
- async function generateStructuredContent(options) {
3067
- const project = await resolveProjectId2();
3068
- const modelId = options.modelId || DEFAULT_MODEL_ID;
3069
- const location = options.location || DEFAULT_LOCATION2;
3070
- const vertex = new VertexAI({ project, location });
3071
- const fullSystem = `${GLOBAL_SYSTEM_PROMPT}
3072
-
3073
- ${options.system || ""}`.trim();
3074
- const model = vertex.getGenerativeModel({
3075
- model: modelId,
3076
- systemInstruction: { role: "system", parts: [{ text: fullSystem }] }
3077
- });
3078
- const t0 = Date.now();
3079
- if (PREVIEW) {
3080
- try {
3081
- vlog("vertex_prompt", { model: modelId, location, system_preview: previewOf(fullSystem), user_preview: previewOf(options.user) });
3082
- } catch {
3083
- }
3084
- }
3085
- const tools = options.useGoogleSearch ? [{
3086
- googleSearch: {}
3087
- }] : void 0;
3088
- const useStructuredOutput = options.responseSchema && !options.useGoogleSearch;
3089
- const requestPayload = {
3090
- contents: [{ role: "user", parts: [{ text: options.user }] }],
3091
- generationConfig: {
3092
- temperature: options.temperature ?? 0.2,
3093
- maxOutputTokens: options.maxOutputTokens ?? 400,
3094
- responseMimeType: useStructuredOutput ? "application/json" : void 0,
3095
- responseSchema: useStructuredOutput ? asVertexResponseSchema(options.responseSchema) : void 0,
3096
- topP: options.topP ?? 0.9,
3097
- topK: options.topK ?? 40
3098
- },
3099
- tools
3100
- };
3101
- const response = await vertexCircuit.execute(() => retryWithBackoff(() => model.generateContent(requestPayload), {
3102
- maxRetries: 3,
3103
- baseDelayMs: 2e3,
3104
- shouldRetry: (err) => {
3105
- const msg = String(err?.message || err || "");
3106
- const name2 = String(err?.name || "");
3107
- if (msg.includes("429") || msg.includes("RESOURCE_EXHAUSTED"))
3108
- return true;
3109
- if (msg.includes("503") || msg.includes("500") || msg.includes("UNAVAILABLE"))
3110
- return true;
3111
- if (msg.includes("exception posting request") || msg.includes("ECONNRESET") || msg.includes("ETIMEDOUT") || msg.includes("socket hang up") || msg.includes("network") || msg.includes("fetch failed"))
3112
- return true;
3113
- if (name2 === "GoogleGenerativeAIError" || name2 === "GoogleGenerativeAIFetchError")
3114
- return true;
3115
- return false;
3116
- }
3117
- }));
3118
- const candidate = response.response.candidates?.[0];
3119
- const parts = candidate?.content?.parts ?? [];
3120
- const finishReason = candidate?.finishReason ?? candidate?.finish_reason;
3121
- const usage = response?.response?.usageMetadata ?? response?.usageMetadata;
3122
- const safety = candidate?.safetyRatings ?? candidate?.safety_ratings;
3123
- const partTypes = parts.map((p) => p?.functionCall ? "functionCall" : p?.structValue ? "structValue" : p?.inlineData ? "inlineData" : typeof p?.text === "string" ? "text" : "other");
3124
- const groundingMeta = candidate?.groundingMetadata;
3125
- const groundingMetadata = groundingMeta ? {
3126
- searchQueries: groundingMeta.searchEntryPoint?.renderedContent ? [groundingMeta.searchEntryPoint.renderedContent] : void 0,
3127
- webSearchQueries: groundingMeta.webSearchQueries,
3128
- groundingChunks: groundingMeta.groundingChunks?.length
3129
- } : void 0;
3130
- if (groundingMetadata?.groundingChunks) {
3131
- vlog("vertex_grounding", {
3132
- searchQueries: groundingMetadata.webSearchQueries,
3133
- chunks: groundingMetadata.groundingChunks
3134
- });
3135
- }
3136
- const pickNum = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
3137
- const tokens_in = pickNum(usage?.promptTokenCount ?? usage?.inputTokenCount ?? usage?.promptTokens ?? usage?.input_tokens);
3138
- const tokens_out = pickNum(usage?.candidatesTokenCount ?? usage?.outputTokenCount ?? usage?.completionTokens ?? usage?.output_tokens);
3139
- const total_tokens = pickNum(usage?.totalTokenCount ?? usage?.totalTokens) ?? (tokens_in ?? 0) + (tokens_out ?? 0);
3140
- const cost_usd = estimateCostUsd(modelId, tokens_in ?? 0, tokens_out ?? 0);
3141
- emitMetric("vertex.call.tokens", total_tokens ?? 0, {
3142
- model: modelId,
3143
- tokens_in: tokens_in ?? 0,
3144
- tokens_out: tokens_out ?? 0,
3145
- cost_usd,
3146
- latency_ms: Date.now() - t0
3147
- });
3148
- const logVertex = (selected, lengths) => {
3149
- if (!VERBOSE)
3150
- return;
3151
- vlog("vertex_done", { model: modelId, location, tokens_in, tokens_out, total_tokens, finishReason, selected, parts_count: partTypes.length, latency_ms: Date.now() - t0, lengths });
3152
- };
3153
- if (!parts.length) {
3154
- try {
3155
- const out = JSON.stringify(response.response);
3156
- LAST_DIAG = { finishReason, usage, safety, parts: { count: 0, types: [] }, selected: "text", lengths: { funcArgs: 0, inline: 0, text: out.length }, groundingMetadata };
3157
- logVertex("text", { funcArgs: 0, inline: 0, text: out.length });
3158
- return out;
3159
- } catch {
3160
- }
3161
- LAST_DIAG = { finishReason, usage, safety, parts: { count: 0, types: [] }, selected: "text", lengths: { funcArgs: 0, inline: 0, text: 0 }, groundingMetadata };
3162
- logVertex("text", { funcArgs: 0, inline: 0, text: 0 });
3163
- return "";
3164
- }
3165
- const textSegments = [];
3166
- let funcArgs = "";
3167
- let sawFuncArgs = false;
3168
- let structCandidate = void 0;
3169
- const inlineSegments = [];
3170
- const preferText = !!options.preferText && !options.responseSchema;
3171
- for (const p of parts) {
3172
- if (p?.functionCall?.arguments !== void 0) {
3173
- const args = p.functionCall.arguments;
3174
- sawFuncArgs = true;
3175
- if (typeof args === "string") {
3176
- funcArgs += args;
3177
- } else {
3178
- try {
3179
- structCandidate = args;
3180
- } catch {
3181
- }
3182
- }
3183
- continue;
3184
- }
3185
- if (p?.structValue) {
3186
- structCandidate = p.structValue;
3187
- continue;
3188
- }
3189
- if (p?.inlineData?.data) {
3190
- try {
3191
- const decoded = Buffer.from(p.inlineData.data, "base64").toString("utf8");
3192
- inlineSegments.push(decoded);
3193
- } catch {
3194
- }
3195
- continue;
3196
- }
3197
- if (typeof p?.text === "string") {
3198
- textSegments.push(p.text);
3199
- }
3200
- }
3201
- if (!preferText && sawFuncArgs && funcArgs.trim().length) {
3202
- let out = funcArgs.trim();
3203
- if (out.startsWith("```")) {
3204
- out = out.replace(/^```[a-zA-Z]*\n?/, "");
3205
- if (out.endsWith("```"))
3206
- out = out.replace(/```\s*$/, "");
3207
- }
3208
- 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 };
3209
- logVertex("args", { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: textSegments.join("").length });
3210
- return out;
3211
- }
3212
- if (!preferText && structCandidate !== void 0) {
3213
- try {
3214
- const out = JSON.stringify(structCandidate);
3215
- 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 };
3216
- logVertex("struct", { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: textSegments.join("").length });
3217
- return out;
3218
- } catch {
3219
- }
3220
- }
3221
- if (inlineSegments.length) {
3222
- let inlineText = inlineSegments.join("").trim();
3223
- if (inlineText.startsWith("```")) {
3224
- inlineText = inlineText.replace(/^```[a-zA-Z]*\n?/, "");
3225
- if (inlineText.endsWith("```"))
3226
- inlineText = inlineText.replace(/```\s*$/, "");
3227
- }
3228
- if (options.responseSchema) {
3229
- try {
3230
- JSON.parse(inlineText);
3231
- 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 };
3232
- logVertex("inline", { funcArgs: funcArgs.length, inline: inlineText.length, text: textSegments.join("").length });
3233
- return inlineText;
3234
- } catch {
3235
- }
3236
- const start = inlineText.indexOf("{");
3237
- const end = inlineText.lastIndexOf("}");
3238
- if (start !== -1 && end > start) {
3239
- const inner = inlineText.slice(start, end + 1).trim();
3240
- try {
3241
- JSON.parse(inner);
3242
- 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 };
3243
- logVertex("inline", { funcArgs: funcArgs.length, inline: inner.length, text: textSegments.join("").length });
3244
- return inner;
3245
- } catch {
3246
- }
3247
- }
3248
- }
3249
- if (inlineText) {
3250
- textSegments.push(inlineText);
3251
- }
3252
- }
3253
- let text = textSegments.join("").trim();
3254
- if (!text) {
3255
- LAST_DIAG = { finishReason, usage, safety, parts: { count: partTypes.length, types: partTypes }, selected: "text", lengths: { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: 0 }, groundingMetadata };
3256
- logVertex("text", { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: 0 });
3257
- return "";
3258
- }
3259
- if (text.startsWith("```")) {
3260
- text = text.replace(/^```[a-zA-Z]*\n?/, "");
3261
- if (text.endsWith("```"))
3262
- text = text.replace(/```\s*$/, "");
3263
- }
3264
- if (options.responseSchema) {
3265
- try {
3266
- JSON.parse(text);
3267
- 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 };
3268
- logVertex("text", { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: text.length });
3269
- return text;
3270
- } catch {
3271
- }
3272
- const start = text.indexOf("{");
3273
- const end = text.lastIndexOf("}");
3274
- if (start !== -1 && end > start) {
3275
- const inner = text.slice(start, end + 1).trim();
3276
- try {
3277
- JSON.parse(inner);
3278
- 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 };
3279
- logVertex("text", { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: inner.length });
3280
- return inner;
3281
- } catch {
3282
- }
3283
- }
3284
- }
3285
- 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 };
3286
- logVertex("text", { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: text.length });
3287
- return text;
3288
- }
3289
- var GLOBAL_SYSTEM_PROMPT = [
3290
- "You are a research-first, skepticism-driven product analyst. Your job is to reduce optimism bias and avoid guesswork.",
3291
- "",
3292
- "Behavioral guardrails:",
3293
- "- Evidence > opinions. Prefer verifiable facts, base rates, and conservative assumptions.",
3294
- "- Do not fabricate specifics (data points, sources, user counts, dates). If unknown, say so.",
3295
- "- Calibrate: reflect uncertainty explicitly and note assumptions driving your outputs.",
3296
- "- Prefer breadth from reference classes over narrow anecdotes. De-duplicate aggressively.",
3297
- "",
3298
- "When context is insufficient:",
3299
- "- Identify Information Gaps (what is missing to answer confidently).",
3300
- "- Propose How to Research with 3\u20136 concrete queries and credible source types to check.",
3301
- "- Use analogous reference classes to ground estimates, but flag them as assumptions.",
3302
- "",
3303
- "Estimation policy:",
3304
- "- For any numeric estimate required by the schema, return a realistic P50 value.",
3305
- "- In rationale, include a short range and the main driver(s). Default conservative when uncertainty is high.",
3306
- "",
3307
- "MoSCoW policy:",
3308
- "- 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).",
3309
- "",
3310
- "Output hygiene:",
3311
- "- Follow the requested output schema exactly. If a field is unknown, omit it or use null if allowed.",
3312
- "- Keep rationales crisp (8\u201330 words), focusing on evidence, base rates, and key assumptions.",
3313
- "- No marketing language, no boilerplate, no code fences, no markdown\u2014only the required JSON/plain text."
3314
- ].join("\n");
3315
- async function resolveProjectId2() {
3316
- if (process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT) {
3317
- return process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT;
3318
- }
3319
- try {
3320
- const auth = new GoogleAuth2();
3321
- return await auth.getProjectId();
3322
- } catch (err) {
3323
- throw new Error(`Unable to resolve Google Cloud project id: ${err?.message ?? err}`);
3324
- }
3325
- }
3326
-
3327
2756
  // ../mcp/dist/mcp/src/constraints/constraint-matcher.js
3328
2757
  var MATCH_RULES = [
3329
2758
  {
@@ -7763,7 +7192,7 @@ function mcpAudit(entry) {
7763
7192
  }));
7764
7193
  }
7765
7194
  var DEFAULT_MODEL = process.env.MODEL_ID || "gemini-2.5-pro";
7766
- var generateStructuredContent2 = (options) => withLlmMonitor(options.modelId || DEFAULT_MODEL, () => generateStructuredContent(options));
7195
+ var generateStructuredContent = (options) => withLlmMonitor(options.modelId || DEFAULT_MODEL, () => cfGenerateStructuredContent(options));
7767
7196
  var explorationSessions = /* @__PURE__ */ new Map();
7768
7197
  function generateId(prefix = "id") {
7769
7198
  return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
@@ -8813,7 +8242,7 @@ Why AI: ${idea.whyAI}`
8813
8242
  getAllEdges: (pid) => getAllEdges(pid),
8814
8243
  getAllNodes: (pid) => getAllNodes(pid),
8815
8244
  getAllBindings: (pid) => getAllBindings(pid),
8816
- generateStructuredContent: generateStructuredContent2,
8245
+ generateStructuredContent,
8817
8246
  upsertNodes: (pid, sourceType, sourceId, nodes) => upsertNodes(pid, sourceType, sourceId, nodes),
8818
8247
  addEntity: (pid, entity) => addEntity(pid, entity),
8819
8248
  addEdges: (pid, edges) => addEdges(pid, edges),
@@ -9231,7 +8660,7 @@ Meta: ${JSON.stringify(output.meta)}` }
9231
8660
  throw new McpError(ErrorCode.InvalidParams, "Document text too short (min 50 chars)");
9232
8661
  }
9233
8662
  const finalDocId = doc_id || `doc_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
9234
- const nodes = await extractConstraintsFromDoc(finalDocId, text, generateStructuredContent2, { documentTitle: title, documentUrl: url });
8663
+ const nodes = await extractConstraintsFromDoc(finalDocId, text, generateStructuredContent, { documentTitle: title, documentUrl: url });
9235
8664
  if (nodes.length === 0) {
9236
8665
  return {
9237
8666
  content: [{
@@ -9968,7 +9397,7 @@ Meta: ${JSON.stringify(output.meta)}` }
9968
9397
  }
9969
9398
  const ingestionId = source_id || `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
9970
9399
  const existingEntities = await getAllEntities(product_id);
9971
- const graph = await ingestRequirements(ingestionId, text, generateStructuredContent2, existingEntities, { documentTitle: title });
9400
+ const graph = await ingestRequirements(ingestionId, text, generateStructuredContent, existingEntities, { documentTitle: title });
9972
9401
  if (graph.entities.length > 0) {
9973
9402
  await upsertEntities(product_id, "doc", ingestionId, graph.entities);
9974
9403
  }
@@ -10124,7 +9553,7 @@ Meta: ${JSON.stringify(meta)}`
10124
9553
  }]
10125
9554
  };
10126
9555
  }
10127
- 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);
10128
9557
  if (result.bindings.length > 0) {
10129
9558
  await upsertBindings(product_id, result.bindings);
10130
9559
  }
@@ -10798,7 +10227,7 @@ Meta: ${JSON.stringify({
10798
10227
  getAllEdges: (pid) => getAllEdges(pid),
10799
10228
  getAllNodes: (pid) => getAllNodes(pid),
10800
10229
  getAllBindings: (pid) => getAllBindings(pid),
10801
- generateStructuredContent: generateStructuredContent2,
10230
+ generateStructuredContent,
10802
10231
  upsertNodes: (pid, sourceType, sourceId, nodes) => upsertNodes(pid, sourceType, sourceId, nodes),
10803
10232
  addEntity: (pid, entity) => addEntity(pid, entity),
10804
10233
  addEdges: (pid, edges) => addEdges(pid, edges),
@@ -11,7 +11,9 @@ import {
11
11
  cfExplorationAgent,
12
12
  cfGenerateAnswer,
13
13
  cfGenerateChatSuggestion,
14
+ cfGenerateEmbeddings,
14
15
  cfGenerateExplorationResponse,
16
+ cfGenerateStructuredContent,
15
17
  cfGenerateTemplateResponse,
16
18
  cfGenerateTrialRun,
17
19
  cfGetWikiMarkdown,
@@ -75,7 +77,7 @@ import {
75
77
  upsertEdges,
76
78
  upsertEntities,
77
79
  upsertNodes
78
- } from "./chunk-XKS3J74N.js";
80
+ } from "./chunk-ZVWDXO6M.js";
79
81
  export {
80
82
  addEdges,
81
83
  addEntity,
@@ -89,7 +91,9 @@ export {
89
91
  cfExplorationAgent,
90
92
  cfGenerateAnswer,
91
93
  cfGenerateChatSuggestion,
94
+ cfGenerateEmbeddings,
92
95
  cfGenerateExplorationResponse,
96
+ cfGenerateStructuredContent,
93
97
  cfGenerateTemplateResponse,
94
98
  cfGenerateTrialRun,
95
99
  cfGetWikiMarkdown,
@@ -14,7 +14,7 @@ import {
14
14
  requirePremiumWithAutoAuth,
15
15
  updateExplorationSession,
16
16
  validateRequestSize
17
- } from "./chunk-XKS3J74N.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-XKS3J74N.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-XKS3J74N.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-XKS3J74N.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-XKS3J74N.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.0",
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"