@kylewadegrove/cutline-mcp-cli 0.6.0 → 0.6.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.
- package/dist/auth/callback.d.ts +2 -1
- package/dist/auth/callback.js +7 -2
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +1 -1
- package/dist/commands/setup.js +2 -2
- package/dist/commands/upgrade.js +1 -1
- package/dist/servers/chunk-7N4HJ3KR.js +1024 -0
- package/dist/servers/chunk-DE7R7WKY.js +331 -0
- package/dist/servers/chunk-OP4EO6FV.js +454 -0
- package/dist/servers/cutline-server.js +200 -693
- package/dist/servers/data-client-PF2AI2MX.js +148 -0
- package/dist/servers/exploration-server.js +63 -142
- package/dist/servers/integrations-server.js +8 -22
- package/dist/servers/output-server.js +12 -25
- package/dist/servers/premortem-server.js +170 -127
- package/dist/servers/tools-server.js +26 -30
- package/package.json +1 -10
- package/dist/servers/chunk-7FHM2GD3.js +0 -5836
- package/dist/servers/chunk-IVWF7VYZ.js +0 -10086
- package/dist/servers/chunk-JBJYSV4P.js +0 -139
- package/dist/servers/chunk-PD2HN2R5.js +0 -908
- package/dist/servers/chunk-PU7TL6S3.js +0 -91
- package/dist/servers/chunk-TGSEURMN.js +0 -46
- package/dist/servers/pipeline-O5GJPNR4.js +0 -20
- package/dist/servers/premortem-handoff-XT4K3YDJ.js +0 -10
- package/dist/servers/score-history-HO5KRVGC.js +0 -6
|
@@ -1,35 +1,147 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
} from "./chunk-
|
|
3
|
+
RunInputSchema
|
|
4
|
+
} from "./chunk-DE7R7WKY.js";
|
|
5
5
|
import {
|
|
6
6
|
isWriteTool
|
|
7
7
|
} from "./chunk-KMUSQOTJ.js";
|
|
8
|
-
import "./chunk-IVWF7VYZ.js";
|
|
9
8
|
import {
|
|
10
9
|
guardBoundary,
|
|
11
10
|
guardOutput,
|
|
11
|
+
withPerfTracking
|
|
12
|
+
} from "./chunk-OP4EO6FV.js";
|
|
13
|
+
import {
|
|
14
|
+
cfRegenAssumptions,
|
|
15
|
+
cfRegenExperiments,
|
|
16
|
+
createPremortem,
|
|
17
|
+
getChat,
|
|
18
|
+
getPremortem,
|
|
19
|
+
listPremortems,
|
|
12
20
|
mapErrorToMcp,
|
|
13
21
|
requirePremiumWithAutoAuth,
|
|
14
22
|
resolveAuthContext,
|
|
23
|
+
saveChat,
|
|
24
|
+
updateChat,
|
|
25
|
+
updatePremortem,
|
|
15
26
|
validateAuth,
|
|
16
|
-
validateRequestSize
|
|
17
|
-
|
|
18
|
-
} from "./chunk-PD2HN2R5.js";
|
|
19
|
-
import {
|
|
20
|
-
RunInputSchema,
|
|
21
|
-
regenerateAssumptions,
|
|
22
|
-
regenerateExperiments
|
|
23
|
-
} from "./chunk-7FHM2GD3.js";
|
|
24
|
-
import "./chunk-JBJYSV4P.js";
|
|
27
|
+
validateRequestSize
|
|
28
|
+
} from "./chunk-7N4HJ3KR.js";
|
|
25
29
|
|
|
26
30
|
// ../mcp/dist/mcp/src/premortem-server.js
|
|
27
31
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
28
32
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
29
33
|
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
|
|
35
|
+
// ../mcp/dist/mcp/src/premortem-handoff.js
|
|
36
|
+
function buildBrief(ctx) {
|
|
37
|
+
if (!ctx)
|
|
38
|
+
return "";
|
|
39
|
+
const parts = [];
|
|
40
|
+
if (ctx.brief)
|
|
41
|
+
parts.push(ctx.brief);
|
|
42
|
+
if (ctx.problemSolved && !ctx.brief?.toLowerCase().includes(ctx.problemSolved.toLowerCase().slice(0, 20)))
|
|
43
|
+
parts.push(`Problem: ${ctx.problemSolved}`);
|
|
44
|
+
if (ctx.uniqueValue && !ctx.brief?.toLowerCase().includes(ctx.uniqueValue.toLowerCase().slice(0, 20)))
|
|
45
|
+
parts.push(`Unique value: ${ctx.uniqueValue}`);
|
|
46
|
+
if (ctx.targetUser && !ctx.brief?.toLowerCase().includes(ctx.targetUser.toLowerCase().slice(0, 15)))
|
|
47
|
+
parts.push(`Target users: ${ctx.targetUser}`);
|
|
48
|
+
if (ctx.businessModel && !ctx.brief?.toLowerCase().includes(ctx.businessModel.toLowerCase().slice(0, 15)))
|
|
49
|
+
parts.push(`Business model: ${ctx.businessModel}`);
|
|
50
|
+
return parts.join(". ").trim() || "No description provided.";
|
|
51
|
+
}
|
|
52
|
+
function buildReferenceClasses(competitors) {
|
|
53
|
+
if (competitors && competitors.length > 0) {
|
|
54
|
+
return competitors.filter((c) => c.name && c.name.length > 0).map((c) => c.name).slice(0, 5);
|
|
55
|
+
}
|
|
56
|
+
return ["General market alternatives"];
|
|
57
|
+
}
|
|
58
|
+
function buildSeedPersonas(ctx) {
|
|
59
|
+
if (!ctx.productContext?.targetUser)
|
|
60
|
+
return void 0;
|
|
61
|
+
return {
|
|
62
|
+
user_personas: [{ name: "Primary User", description: ctx.productContext.targetUser }]
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function buildRunInput(request) {
|
|
66
|
+
const { projectName, conversationalContext: ctx, mode = "product", clientContext } = request;
|
|
67
|
+
let brief = buildBrief(ctx.productContext);
|
|
68
|
+
if (ctx.risks.length > 0) {
|
|
69
|
+
const topRisks = ctx.risks.filter((r) => r.severity === "critical" || r.severity === "high").slice(0, 2).map((r) => r.title);
|
|
70
|
+
if (topRisks.length > 0)
|
|
71
|
+
brief += ` Key concerns to investigate: ${topRisks.join(", ")}.`;
|
|
72
|
+
}
|
|
73
|
+
if (ctx.verdict?.summary)
|
|
74
|
+
brief += ` Initial assessment: ${ctx.verdict.summary}`;
|
|
75
|
+
const runInput = {
|
|
76
|
+
mode,
|
|
77
|
+
project: { name: projectName, brief, reference_classes: buildReferenceClasses(ctx.competitors) }
|
|
78
|
+
};
|
|
79
|
+
if (mode === "consulting" && clientContext) {
|
|
80
|
+
runInput.client = {
|
|
81
|
+
name: clientContext.name,
|
|
82
|
+
industry: clientContext.industry,
|
|
83
|
+
size: clientContext.size,
|
|
84
|
+
stakeholders: clientContext.stakeholders
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const seedPersonas = buildSeedPersonas(ctx);
|
|
88
|
+
if (seedPersonas)
|
|
89
|
+
runInput.seed_personas = seedPersonas;
|
|
90
|
+
if (ctx.risks.length > 0 || ctx.assumptions.length > 0 || ctx.competitors.length > 0) {
|
|
91
|
+
runInput.conversational_artifacts = {
|
|
92
|
+
risks: ctx.risks.length > 0 ? ctx.risks.map((r) => ({
|
|
93
|
+
title: r.title,
|
|
94
|
+
description: r.description,
|
|
95
|
+
category: r.category,
|
|
96
|
+
severity: r.severity,
|
|
97
|
+
likelihood: r.likelihood,
|
|
98
|
+
impact: r.impact
|
|
99
|
+
})) : void 0,
|
|
100
|
+
assumptions: ctx.assumptions.length > 0 ? ctx.assumptions.map((a) => ({
|
|
101
|
+
statement: a.statement,
|
|
102
|
+
category: a.category,
|
|
103
|
+
confidence: a.confidence,
|
|
104
|
+
importance: a.importance
|
|
105
|
+
})) : void 0,
|
|
106
|
+
competitors: ctx.competitors.length > 0 ? ctx.competitors.map((c) => ({
|
|
107
|
+
name: c.name,
|
|
108
|
+
description: c.description,
|
|
109
|
+
threat_level: c.threatLevel
|
|
110
|
+
})) : void 0,
|
|
111
|
+
conversation_summary: ctx.verdict?.summary
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return runInput;
|
|
115
|
+
}
|
|
116
|
+
function validateForGraduation(ctx) {
|
|
117
|
+
const errors = [];
|
|
118
|
+
const warnings = [];
|
|
119
|
+
if (!ctx.productContext?.brief && !ctx.productContext?.problemSolved)
|
|
120
|
+
errors.push("No product description found. Please describe what you're building first.");
|
|
121
|
+
if (ctx.assumptions.length === 0)
|
|
122
|
+
warnings.push("No assumptions were surfaced. The full analysis will discover them.");
|
|
123
|
+
if (ctx.risks.length === 0)
|
|
124
|
+
warnings.push("No risks were identified. The full analysis will analyze risks in depth.");
|
|
125
|
+
if (ctx.competitors.length === 0)
|
|
126
|
+
warnings.push("No competitors were discussed. Provide reference companies for better analysis.");
|
|
127
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
128
|
+
}
|
|
129
|
+
function buildGraduationMetadata(sessionId, ctx, currentAct) {
|
|
130
|
+
return {
|
|
131
|
+
sourceType: "conversational_premortem",
|
|
132
|
+
sourceSessionId: sessionId,
|
|
133
|
+
graduatedAt: Date.now(),
|
|
134
|
+
conversationSummary: {
|
|
135
|
+
actsCompleted: currentAct,
|
|
136
|
+
assumptionsCount: ctx.assumptions.length,
|
|
137
|
+
risksCount: ctx.risks.length,
|
|
138
|
+
competitorsCount: ctx.competitors.length,
|
|
139
|
+
hasVerdict: !!ctx.verdict
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ../mcp/dist/mcp/src/premortem-server.js
|
|
33
145
|
import { GoogleAuth } from "google-auth-library";
|
|
34
146
|
var PREMORTEM_CHAT_FUNCTION_URL = process.env.PREMORTEM_CHAT_AGENT_FUNCTION_URL || "https://us-central1-cutline-prod.cloudfunctions.net/premortemChatAgent";
|
|
35
147
|
var PREMORTEM_KICK_FUNCTION_URL = process.env.PREMORTEM_KICK_FUNCTION_URL || "https://us-central1-cutline-prod.cloudfunctions.net/premortemKick";
|
|
@@ -54,22 +166,12 @@ var chatSessionCache = /* @__PURE__ */ new Map();
|
|
|
54
166
|
function generateChatSessionId() {
|
|
55
167
|
return `pmc_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
56
168
|
}
|
|
57
|
-
function getDb() {
|
|
58
|
-
const app = getApps().length > 0 ? getApp() : void 0;
|
|
59
|
-
if (!app)
|
|
60
|
-
return null;
|
|
61
|
-
return getFirestore(app);
|
|
62
|
-
}
|
|
63
169
|
async function saveChatSession(session) {
|
|
64
170
|
session.updatedAt = Date.now();
|
|
65
171
|
chatSessionCache.set(session.id, session);
|
|
66
|
-
|
|
67
|
-
if (db && session.uid) {
|
|
172
|
+
if (session.uid) {
|
|
68
173
|
try {
|
|
69
|
-
await
|
|
70
|
-
...session,
|
|
71
|
-
updatedAt: admin.firestore.FieldValue.serverTimestamp()
|
|
72
|
-
}, { merge: true }), { operationName: "premortem_chat_persist" });
|
|
174
|
+
await saveChat(session.id, { ...session });
|
|
73
175
|
} catch (e) {
|
|
74
176
|
console.error("[PremortemChat] Failed to persist session:", e);
|
|
75
177
|
}
|
|
@@ -79,18 +181,14 @@ async function getChatSession(sessionId) {
|
|
|
79
181
|
if (chatSessionCache.has(sessionId)) {
|
|
80
182
|
return chatSessionCache.get(sessionId);
|
|
81
183
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const data = doc.data();
|
|
88
|
-
chatSessionCache.set(sessionId, data);
|
|
89
|
-
return data;
|
|
90
|
-
}
|
|
91
|
-
} catch (e) {
|
|
92
|
-
console.error("[PremortemChat] Failed to load session:", e);
|
|
184
|
+
try {
|
|
185
|
+
const data = await getChat(sessionId);
|
|
186
|
+
if (data) {
|
|
187
|
+
chatSessionCache.set(sessionId, data);
|
|
188
|
+
return data;
|
|
93
189
|
}
|
|
190
|
+
} catch (e) {
|
|
191
|
+
console.error("[PremortemChat] Failed to load session:", e);
|
|
94
192
|
}
|
|
95
193
|
return null;
|
|
96
194
|
}
|
|
@@ -398,23 +496,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
398
496
|
console.error(`[Premortem DEBUG] Step 2: Parsing input...`);
|
|
399
497
|
const parsedInput = RunInputSchema.parse(input);
|
|
400
498
|
console.error(`[Premortem DEBUG] Step 2 done: project=${parsedInput.project.name}`);
|
|
401
|
-
console.error(`[Premortem DEBUG] Step 3: Getting Firebase app...`);
|
|
402
|
-
const app = getApps().length > 0 ? getApp() : void 0;
|
|
403
|
-
if (!app)
|
|
404
|
-
throw new McpError(ErrorCode.InternalError, "Firebase Admin not initialized");
|
|
405
|
-
const db = getFirestore(app);
|
|
406
|
-
console.error(`[Premortem DEBUG] Step 3 done: Firebase ready`);
|
|
407
499
|
console.error(`[Premortem] Queuing analysis for: ${parsedInput.project.name}`);
|
|
408
500
|
const startTime = Date.now();
|
|
409
501
|
console.error(`[Premortem DEBUG] Step 4: Creating job document...`);
|
|
410
|
-
const
|
|
502
|
+
const { id: jobId } = await createPremortem({
|
|
411
503
|
status: "queued",
|
|
412
504
|
payload: parsedInput,
|
|
413
505
|
uid: decoded?.uid || null,
|
|
414
|
-
source: "mcp_sync"
|
|
415
|
-
|
|
416
|
-
}), { operationName: "premortem_job_create" });
|
|
417
|
-
const jobId = docRef.id;
|
|
506
|
+
source: "mcp_sync"
|
|
507
|
+
});
|
|
418
508
|
console.error(`[Premortem DEBUG] Step 4 done: jobId=${jobId}`);
|
|
419
509
|
console.error(`[Premortem] Job queued: ${jobId}, polling for completion...`);
|
|
420
510
|
const maxWaitMs = 10 * 60 * 1e3;
|
|
@@ -423,8 +513,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
423
513
|
let kickedFallback = false;
|
|
424
514
|
while (Date.now() - startTime < maxWaitMs) {
|
|
425
515
|
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
426
|
-
const
|
|
427
|
-
const data = snap.data() || {};
|
|
516
|
+
const data = await getPremortem(jobId) || {};
|
|
428
517
|
if (!kickedFallback && data.status === "queued" && Date.now() - startTime > 1e4) {
|
|
429
518
|
console.error(`[Premortem] Job still queued after 10s, kicking via Cloud Function...`);
|
|
430
519
|
kickedFallback = true;
|
|
@@ -475,31 +564,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
475
564
|
const { input, auth_token } = args;
|
|
476
565
|
const decoded = await requirePremiumWithAutoAuth(auth_token);
|
|
477
566
|
const parsedInput = RunInputSchema.parse(input);
|
|
478
|
-
const
|
|
479
|
-
if (!app)
|
|
480
|
-
throw new McpError(ErrorCode.InternalError, "Firebase Admin not initialized");
|
|
481
|
-
const db = getFirestore(app);
|
|
482
|
-
const docRef = await firestoreRetry(() => db.collection("premortem_jobs").add({
|
|
567
|
+
const { id: queuedJobId } = await createPremortem({
|
|
483
568
|
status: "queued",
|
|
484
569
|
payload: parsedInput,
|
|
485
|
-
createdAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
486
570
|
uid: decoded?.uid || null
|
|
487
|
-
})
|
|
571
|
+
});
|
|
488
572
|
return {
|
|
489
|
-
content: [{ type: "text", text: JSON.stringify({ jobId:
|
|
573
|
+
content: [{ type: "text", text: JSON.stringify({ jobId: queuedJobId }) }]
|
|
490
574
|
};
|
|
491
575
|
}
|
|
492
576
|
case "premortem_status": {
|
|
493
577
|
const { jobId, auth_token } = args;
|
|
494
578
|
const { effectiveUid } = await resolveAuthContext(auth_token);
|
|
495
|
-
const
|
|
496
|
-
if (!
|
|
497
|
-
throw new McpError(ErrorCode.InternalError, "Firebase Admin not initialized");
|
|
498
|
-
const firestore = getFirestore(app);
|
|
499
|
-
const snap = await firestore.collection("premortem_jobs").doc(jobId).get();
|
|
500
|
-
if (!snap.exists)
|
|
579
|
+
const data = await getPremortem(jobId);
|
|
580
|
+
if (!data)
|
|
501
581
|
throw new McpError(ErrorCode.InvalidRequest, "Job not found");
|
|
502
|
-
const data = snap.data() || {};
|
|
503
582
|
const allowPublic = process.env.ALLOW_PUBLIC_PREMORTEM === "true";
|
|
504
583
|
if (!allowPublic && effectiveUid && data.uid && data.uid !== effectiveUid) {
|
|
505
584
|
throw new McpError(ErrorCode.InvalidRequest, "You do not have permission to access this job");
|
|
@@ -550,7 +629,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
550
629
|
case "premortem_regen_assumptions": {
|
|
551
630
|
const { input, doc, auth_token } = args;
|
|
552
631
|
await requirePremiumWithAutoAuth(auth_token);
|
|
553
|
-
const out = await
|
|
632
|
+
const out = await cfRegenAssumptions(input, doc);
|
|
554
633
|
return {
|
|
555
634
|
content: [{ type: "text", text: JSON.stringify(out) }]
|
|
556
635
|
};
|
|
@@ -558,7 +637,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
558
637
|
case "premortem_regen_experiments": {
|
|
559
638
|
const { input, doc, auth_token } = args;
|
|
560
639
|
await requirePremiumWithAutoAuth(auth_token);
|
|
561
|
-
const out = await
|
|
640
|
+
const out = await cfRegenExperiments(input, doc);
|
|
562
641
|
return {
|
|
563
642
|
content: [{ type: "text", text: JSON.stringify(out) }]
|
|
564
643
|
};
|
|
@@ -566,27 +645,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
566
645
|
case "premortem_list": {
|
|
567
646
|
const { limit = 10, auth_token } = args;
|
|
568
647
|
const { effectiveUid } = await resolveAuthContext(auth_token);
|
|
569
|
-
const
|
|
570
|
-
if (!app)
|
|
571
|
-
throw new McpError(ErrorCode.InternalError, "Firebase Admin not initialized");
|
|
572
|
-
const firestore = getFirestore(app);
|
|
573
|
-
const snapshot = await firestore.collection("premortem_jobs").where("uid", "==", effectiveUid).orderBy("createdAt", "desc").limit(limit).get();
|
|
574
|
-
const premortems = snapshot.docs.map((doc) => {
|
|
575
|
-
const data = doc.data();
|
|
576
|
-
return {
|
|
577
|
-
id: doc.id,
|
|
578
|
-
uid: data.uid,
|
|
579
|
-
status: data.status,
|
|
580
|
-
stage: data.stage,
|
|
581
|
-
stage_label: data.stage_label,
|
|
582
|
-
progress: data.progress,
|
|
583
|
-
createdAt: data.createdAt?.toDate()?.toISOString(),
|
|
584
|
-
startedAt: data.startedAt?.toDate()?.toISOString(),
|
|
585
|
-
updatedAt: data.updatedAt?.toDate()?.toISOString(),
|
|
586
|
-
productName: data.payload?.project?.name || data.result?.project?.name || "Untitled",
|
|
587
|
-
productDescription: data.payload?.project?.brief || data.result?.project?.brief || ""
|
|
588
|
-
};
|
|
589
|
-
});
|
|
648
|
+
const premortems = await listPremortems({ limit });
|
|
590
649
|
return {
|
|
591
650
|
content: [{ type: "text", text: JSON.stringify(premortems, null, 2) }]
|
|
592
651
|
};
|
|
@@ -603,11 +662,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
603
662
|
throw new McpError(ErrorCode.InvalidParams, "conversational_context is required");
|
|
604
663
|
}
|
|
605
664
|
const decoded = await requirePremiumWithAutoAuth(auth_token);
|
|
606
|
-
const app = getApps().length > 0 ? getApp() : void 0;
|
|
607
|
-
if (!app)
|
|
608
|
-
throw new McpError(ErrorCode.InternalError, "Firebase Admin not initialized");
|
|
609
|
-
const db = getFirestore(app);
|
|
610
|
-
const { buildRunInput, validateForGraduation, buildGraduationMetadata } = await import("./premortem-handoff-XT4K3YDJ.js");
|
|
611
665
|
const validation = validateForGraduation(conversational_context);
|
|
612
666
|
if (!validation.valid) {
|
|
613
667
|
throw new McpError(ErrorCode.InvalidParams, `Cannot graduate: ${validation.errors.join(", ")}`);
|
|
@@ -624,24 +678,22 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
624
678
|
conversational_context.verdict ? 5 : 4
|
|
625
679
|
// Estimate current act
|
|
626
680
|
);
|
|
627
|
-
const
|
|
681
|
+
const { id: gradJobId } = await createPremortem({
|
|
628
682
|
status: "queued",
|
|
629
683
|
payload: parsedInput,
|
|
630
|
-
createdAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
631
684
|
uid: decoded?.uid || null,
|
|
632
685
|
source: "mcp_graduate",
|
|
633
686
|
graduation: graduationMetadata
|
|
634
|
-
})
|
|
635
|
-
console.error(`[Premortem Graduate] Created job ${
|
|
687
|
+
});
|
|
688
|
+
console.error(`[Premortem Graduate] Created job ${gradJobId} from session ${session_id}`);
|
|
636
689
|
try {
|
|
637
|
-
const
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
console.error(`[Premortem Graduate] Linked session ${session_id} to job ${docRef.id}`);
|
|
690
|
+
const sessionData = await getPremortem(session_id);
|
|
691
|
+
if (sessionData) {
|
|
692
|
+
await updatePremortem(session_id, {
|
|
693
|
+
linkedJobId: gradJobId,
|
|
694
|
+
graduatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
695
|
+
});
|
|
696
|
+
console.error(`[Premortem Graduate] Linked session ${session_id} to job ${gradJobId}`);
|
|
645
697
|
}
|
|
646
698
|
} catch (linkError) {
|
|
647
699
|
console.error(`[Premortem Graduate] Failed to link session:`, linkError);
|
|
@@ -649,11 +701,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
649
701
|
return {
|
|
650
702
|
content: [{ type: "text", text: JSON.stringify({
|
|
651
703
|
success: true,
|
|
652
|
-
jobId:
|
|
704
|
+
jobId: gradJobId,
|
|
653
705
|
sourceSessionId: session_id,
|
|
654
706
|
projectName: project_name,
|
|
655
707
|
warnings: validation.warnings,
|
|
656
|
-
message: `\u2615 Full analysis queued! This will take a minute\u2014go grab a cup of coffee. Use premortem_status with jobId "${
|
|
708
|
+
message: `\u2615 Full analysis queued! This will take a minute\u2014go grab a cup of coffee. Use premortem_status with jobId "${gradJobId}" to check progress.`
|
|
657
709
|
}, null, 2) }]
|
|
658
710
|
};
|
|
659
711
|
}
|
|
@@ -856,10 +908,6 @@ Let's start with the basics. Tell me more about what you're building:
|
|
|
856
908
|
if (!graduationStatus.canGraduate) {
|
|
857
909
|
throw new McpError(ErrorCode.InvalidRequest, `Cannot graduate: ${graduationStatus.reason}`);
|
|
858
910
|
}
|
|
859
|
-
const app = getApps().length > 0 ? getApp() : void 0;
|
|
860
|
-
if (!app)
|
|
861
|
-
throw new McpError(ErrorCode.InternalError, "Firebase Admin not initialized");
|
|
862
|
-
const db = getFirestore(app);
|
|
863
911
|
let productContext = session.productContext;
|
|
864
912
|
if (!productContext?.brief) {
|
|
865
913
|
console.error("[PremortemGraduate] Building fallback product context from initialInput");
|
|
@@ -878,7 +926,6 @@ Let's start with the basics. Tell me more about what you're building:
|
|
|
878
926
|
competitors: session.competitors,
|
|
879
927
|
verdict: session.verdict
|
|
880
928
|
};
|
|
881
|
-
const { buildRunInput, validateForGraduation, buildGraduationMetadata } = await import("./premortem-handoff-XT4K3YDJ.js");
|
|
882
929
|
const validation = validateForGraduation(conversational_context);
|
|
883
930
|
if (!validation.valid) {
|
|
884
931
|
throw new McpError(ErrorCode.InvalidParams, `Cannot graduate: ${validation.errors.join(", ")}`);
|
|
@@ -890,39 +937,35 @@ Let's start with the basics. Tell me more about what you're building:
|
|
|
890
937
|
conversationalContext: conversational_context
|
|
891
938
|
});
|
|
892
939
|
const parsedInput = RunInputSchema.parse(runInput);
|
|
893
|
-
const
|
|
940
|
+
const { id: chatGradJobId } = await createPremortem({
|
|
894
941
|
status: "queued",
|
|
895
942
|
payload: parsedInput,
|
|
896
943
|
uid: decoded?.uid || null,
|
|
897
944
|
source: "mcp_chat_graduate",
|
|
898
|
-
metadata: buildGraduationMetadata(
|
|
899
|
-
|
|
900
|
-
conversationalContext: conversational_context
|
|
901
|
-
}),
|
|
902
|
-
createdAt: admin.firestore.FieldValue.serverTimestamp()
|
|
903
|
-
}), { operationName: "premortem_chat_graduate" });
|
|
945
|
+
metadata: buildGraduationMetadata(session_id, conversational_context, conversational_context.verdict ? 5 : 4)
|
|
946
|
+
});
|
|
904
947
|
session.conversationHistory.push({
|
|
905
948
|
role: "assistant",
|
|
906
949
|
content: `\u{1F393} **Graduated to Full Analysis!**
|
|
907
950
|
|
|
908
951
|
\u2615 This will take a minute\u2014go grab a cup of coffee while our 9 AI agents stress-test your idea.
|
|
909
952
|
|
|
910
|
-
Job ID: \`${
|
|
953
|
+
Job ID: \`${chatGradJobId}\`
|
|
911
954
|
|
|
912
955
|
The full pipeline includes deeper competitor research, market sizing, financial modeling, and more.`
|
|
913
956
|
});
|
|
914
957
|
await saveChatSession(session);
|
|
915
958
|
try {
|
|
916
|
-
await
|
|
917
|
-
linkedJobId:
|
|
918
|
-
graduatedAt:
|
|
919
|
-
})
|
|
959
|
+
await updateChat(session_id, {
|
|
960
|
+
linkedJobId: chatGradJobId,
|
|
961
|
+
graduatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
962
|
+
});
|
|
920
963
|
} catch {
|
|
921
964
|
}
|
|
922
965
|
return {
|
|
923
966
|
content: [{ type: "text", text: JSON.stringify({
|
|
924
967
|
success: true,
|
|
925
|
-
jobId:
|
|
968
|
+
jobId: chatGradJobId,
|
|
926
969
|
sourceSessionId: session_id,
|
|
927
970
|
projectName: project_name,
|
|
928
971
|
warnings: validation.warnings,
|
|
@@ -932,7 +975,7 @@ The full pipeline includes deeper competitor research, market sizing, financial
|
|
|
932
975
|
competitors: session.competitors.length,
|
|
933
976
|
has_verdict: !!session.verdict
|
|
934
977
|
},
|
|
935
|
-
message: `\u2615 Full analysis queued! This will take a minute\u2014go grab a cup of coffee. Use premortem_status with jobId "${
|
|
978
|
+
message: `\u2615 Full analysis queued! This will take a minute\u2014go grab a cup of coffee. Use premortem_status with jobId "${chatGradJobId}" to check progress.`
|
|
936
979
|
}, null, 2) }]
|
|
937
980
|
};
|
|
938
981
|
}
|
|
@@ -2,29 +2,26 @@
|
|
|
2
2
|
import {
|
|
3
3
|
isWriteTool
|
|
4
4
|
} from "./chunk-KMUSQOTJ.js";
|
|
5
|
-
import {
|
|
6
|
-
applyEditsLogic,
|
|
7
|
-
chatWithPersona,
|
|
8
|
-
generateChatSuggestion,
|
|
9
|
-
generateTrialRun,
|
|
10
|
-
getPersona,
|
|
11
|
-
getWikiMarkdown,
|
|
12
|
-
listPersonas,
|
|
13
|
-
saveWikiMarkdown
|
|
14
|
-
} from "./chunk-IVWF7VYZ.js";
|
|
15
5
|
import {
|
|
16
6
|
guardBoundary,
|
|
17
7
|
guardOutput,
|
|
18
|
-
|
|
8
|
+
withPerfTracking
|
|
9
|
+
} from "./chunk-OP4EO6FV.js";
|
|
10
|
+
import {
|
|
11
|
+
cfApplyEdits,
|
|
12
|
+
cfChatWithPersona,
|
|
13
|
+
cfGenerateChatSuggestion,
|
|
14
|
+
cfGenerateTrialRun,
|
|
15
|
+
cfGetWikiMarkdown,
|
|
16
|
+
cfSaveWikiMarkdown,
|
|
17
|
+
getPersona,
|
|
18
|
+
getPodcastIntroductions,
|
|
19
|
+
listPersonas,
|
|
19
20
|
mapErrorToMcp,
|
|
20
21
|
requirePremiumWithAutoAuth,
|
|
21
|
-
resolveAuthContext,
|
|
22
22
|
validateAuth,
|
|
23
|
-
validateRequestSize
|
|
24
|
-
|
|
25
|
-
} from "./chunk-PD2HN2R5.js";
|
|
26
|
-
import "./chunk-7FHM2GD3.js";
|
|
27
|
-
import "./chunk-JBJYSV4P.js";
|
|
23
|
+
validateRequestSize
|
|
24
|
+
} from "./chunk-7N4HJ3KR.js";
|
|
28
25
|
|
|
29
26
|
// ../mcp/dist/mcp/src/tools-server.js
|
|
30
27
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -184,7 +181,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
184
181
|
throw new McpError(ErrorCode.InvalidParams, "Missing arguments");
|
|
185
182
|
validateRequestSize(rawArgs);
|
|
186
183
|
const { args } = guardBoundary(name, rawArgs);
|
|
187
|
-
initFirebase();
|
|
188
184
|
const rawResponse = await withPerfTracking(name, async () => {
|
|
189
185
|
if (isWriteTool(name)) {
|
|
190
186
|
const peekToken = args.auth_token;
|
|
@@ -198,7 +194,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
198
194
|
switch (name) {
|
|
199
195
|
case "trial_generate": {
|
|
200
196
|
const { prompt } = args;
|
|
201
|
-
const text = await
|
|
197
|
+
const text = await cfGenerateTrialRun(prompt);
|
|
202
198
|
return {
|
|
203
199
|
content: [{ type: "text", text: JSON.stringify({ ok: true, text }) }]
|
|
204
200
|
};
|
|
@@ -206,7 +202,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
206
202
|
case "agent_chat": {
|
|
207
203
|
const { prompt, wikiMarkdown, auth_token } = args;
|
|
208
204
|
await requirePremiumWithAutoAuth(auth_token);
|
|
209
|
-
const text = await
|
|
205
|
+
const text = await cfGenerateChatSuggestion(prompt, wikiMarkdown);
|
|
210
206
|
return {
|
|
211
207
|
content: [{ type: "text", text: JSON.stringify({ text }) }]
|
|
212
208
|
};
|
|
@@ -214,7 +210,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
214
210
|
case "wiki_load": {
|
|
215
211
|
const { projectId, auth_token } = args;
|
|
216
212
|
await requirePremiumWithAutoAuth(auth_token);
|
|
217
|
-
const markdown = await
|
|
213
|
+
const markdown = await cfGetWikiMarkdown(projectId);
|
|
218
214
|
return {
|
|
219
215
|
content: [{ type: "text", text: JSON.stringify({ markdown }) }]
|
|
220
216
|
};
|
|
@@ -222,7 +218,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
222
218
|
case "wiki_save": {
|
|
223
219
|
const { projectId, markdown, auth_token } = args;
|
|
224
220
|
const decoded = await requirePremiumWithAutoAuth(auth_token);
|
|
225
|
-
await
|
|
221
|
+
await cfSaveWikiMarkdown(projectId, markdown);
|
|
226
222
|
return {
|
|
227
223
|
content: [{ type: "text", text: JSON.stringify({ ok: true }) }]
|
|
228
224
|
};
|
|
@@ -230,7 +226,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
230
226
|
case "wiki_apply_edits": {
|
|
231
227
|
const { edits, auth_token } = args;
|
|
232
228
|
await requirePremiumWithAutoAuth(auth_token);
|
|
233
|
-
const result = await
|
|
229
|
+
const result = await cfApplyEdits(edits);
|
|
234
230
|
return {
|
|
235
231
|
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
236
232
|
};
|
|
@@ -238,8 +234,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
238
234
|
case "personas_list": {
|
|
239
235
|
const { productId, auth_token } = args;
|
|
240
236
|
const normalizedAuthToken = auth_token && auth_token !== "auto" && auth_token.trim() !== "" ? auth_token : void 0;
|
|
241
|
-
|
|
242
|
-
const personas = await listPersonas(
|
|
237
|
+
await requirePremiumWithAutoAuth(normalizedAuthToken);
|
|
238
|
+
const personas = await listPersonas(productId);
|
|
243
239
|
return {
|
|
244
240
|
content: [{ type: "text", text: JSON.stringify({ personas }) }]
|
|
245
241
|
};
|
|
@@ -247,8 +243,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
247
243
|
case "personas_get": {
|
|
248
244
|
const { personaId, auth_token } = args;
|
|
249
245
|
const normalizedAuthToken = auth_token && auth_token !== "auto" && auth_token.trim() !== "" ? auth_token : void 0;
|
|
250
|
-
|
|
251
|
-
const persona = await getPersona(
|
|
246
|
+
await requirePremiumWithAutoAuth(normalizedAuthToken);
|
|
247
|
+
const persona = await getPersona(personaId);
|
|
252
248
|
return {
|
|
253
249
|
content: [{ type: "text", text: JSON.stringify({ persona }) }]
|
|
254
250
|
};
|
|
@@ -257,7 +253,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
257
253
|
const { persona, userMessage, product, conversationHistory, auth_token } = args;
|
|
258
254
|
const normalizedAuthToken = auth_token && auth_token !== "auto" && auth_token.trim() !== "" ? auth_token : void 0;
|
|
259
255
|
await requirePremiumWithAutoAuth(normalizedAuthToken);
|
|
260
|
-
const result = await
|
|
256
|
+
const result = await cfChatWithPersona(persona, userMessage, product, conversationHistory);
|
|
261
257
|
return {
|
|
262
258
|
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
263
259
|
};
|
|
@@ -265,8 +261,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
265
261
|
case "podcast_get_participants": {
|
|
266
262
|
const { productId, format, auth_token } = args;
|
|
267
263
|
const normalizedAuthToken = auth_token && auth_token !== "auto" && auth_token.trim() !== "" ? auth_token : void 0;
|
|
268
|
-
|
|
269
|
-
const result = await (
|
|
264
|
+
await requirePremiumWithAutoAuth(normalizedAuthToken);
|
|
265
|
+
const result = await getPodcastIntroductions(productId, format || "json");
|
|
270
266
|
return {
|
|
271
267
|
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
272
268
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kylewadegrove/cutline-mcp-cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.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,24 +53,15 @@
|
|
|
53
53
|
"author": "Cutline",
|
|
54
54
|
"license": "MIT",
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@google-cloud/storage": "^7.7.0",
|
|
57
56
|
"@google-cloud/vertexai": "^1.9.2",
|
|
58
57
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
59
|
-
"@pdf-lib/fontkit": "^1.1.1",
|
|
60
58
|
"chalk": "^4.1.2",
|
|
61
59
|
"commander": "^11.1.0",
|
|
62
|
-
"customerio-node": "^4.0.0",
|
|
63
60
|
"dotenv": "^16.4.5",
|
|
64
|
-
"express": "^4.18.2",
|
|
65
|
-
"firebase-admin": "^12.0.0",
|
|
66
|
-
"firebase-functions": "^6.3.0",
|
|
67
61
|
"google-auth-library": "^9.14.2",
|
|
68
62
|
"keytar": "^7.9.0",
|
|
69
|
-
"node-fetch": "^3.3.2",
|
|
70
63
|
"open": "^9.1.0",
|
|
71
64
|
"ora": "^5.4.1",
|
|
72
|
-
"pdf-lib": "^1.17.1",
|
|
73
|
-
"stripe": "^14.20.0",
|
|
74
65
|
"zod": "^3.23.8"
|
|
75
66
|
},
|
|
76
67
|
"devDependencies": {
|