@kylewadegrove/cutline-mcp-cli 0.5.1 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,35 +1,40 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- firestoreRetry
4
- } from "./chunk-PU7TL6S3.js";
5
2
  import {
6
3
  isWriteTool
7
4
  } from "./chunk-KMUSQOTJ.js";
8
- import "./chunk-IVWF7VYZ.js";
5
+ import "./chunk-M37M2UA4.js";
6
+ import "./chunk-JBJYSV4P.js";
9
7
  import {
10
8
  guardBoundary,
11
9
  guardOutput,
10
+ withPerfTracking
11
+ } from "./chunk-OP4EO6FV.js";
12
+ import {
13
+ createPremortem,
14
+ getChat,
15
+ getPremortem,
16
+ listPremortems,
17
+ saveChat,
18
+ updateChat,
19
+ updatePremortem
20
+ } from "./chunk-PQUAX5YW.js";
21
+ import {
12
22
  mapErrorToMcp,
13
23
  requirePremiumWithAutoAuth,
14
24
  resolveAuthContext,
15
25
  validateAuth,
16
- validateRequestSize,
17
- withPerfTracking
18
- } from "./chunk-PD2HN2R5.js";
26
+ validateRequestSize
27
+ } from "./chunk-NUBIEJTU.js";
19
28
  import {
20
29
  RunInputSchema,
21
30
  regenerateAssumptions,
22
31
  regenerateExperiments
23
32
  } from "./chunk-7FHM2GD3.js";
24
- import "./chunk-JBJYSV4P.js";
25
33
 
26
34
  // ../mcp/dist/mcp/src/premortem-server.js
27
35
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
28
36
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
29
37
  import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError } from "@modelcontextprotocol/sdk/types.js";
30
- import admin from "firebase-admin";
31
- import { getApps, getApp } from "firebase-admin/app";
32
- import { getFirestore } from "firebase-admin/firestore";
33
38
  import { GoogleAuth } from "google-auth-library";
34
39
  var PREMORTEM_CHAT_FUNCTION_URL = process.env.PREMORTEM_CHAT_AGENT_FUNCTION_URL || "https://us-central1-cutline-prod.cloudfunctions.net/premortemChatAgent";
35
40
  var PREMORTEM_KICK_FUNCTION_URL = process.env.PREMORTEM_KICK_FUNCTION_URL || "https://us-central1-cutline-prod.cloudfunctions.net/premortemKick";
@@ -54,22 +59,12 @@ var chatSessionCache = /* @__PURE__ */ new Map();
54
59
  function generateChatSessionId() {
55
60
  return `pmc_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
56
61
  }
57
- function getDb() {
58
- const app = getApps().length > 0 ? getApp() : void 0;
59
- if (!app)
60
- return null;
61
- return getFirestore(app);
62
- }
63
62
  async function saveChatSession(session) {
64
63
  session.updatedAt = Date.now();
65
64
  chatSessionCache.set(session.id, session);
66
- const db = getDb();
67
- if (db && session.uid) {
65
+ if (session.uid) {
68
66
  try {
69
- await firestoreRetry(() => db.collection("premortem_chats").doc(session.id).set({
70
- ...session,
71
- updatedAt: admin.firestore.FieldValue.serverTimestamp()
72
- }, { merge: true }), { operationName: "premortem_chat_persist" });
67
+ await saveChat(session.id, { ...session });
73
68
  } catch (e) {
74
69
  console.error("[PremortemChat] Failed to persist session:", e);
75
70
  }
@@ -79,18 +74,14 @@ async function getChatSession(sessionId) {
79
74
  if (chatSessionCache.has(sessionId)) {
80
75
  return chatSessionCache.get(sessionId);
81
76
  }
82
- const db = getDb();
83
- if (db) {
84
- try {
85
- const doc = await db.collection("premortem_chats").doc(sessionId).get();
86
- if (doc.exists) {
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);
77
+ try {
78
+ const data = await getChat(sessionId);
79
+ if (data) {
80
+ chatSessionCache.set(sessionId, data);
81
+ return data;
93
82
  }
83
+ } catch (e) {
84
+ console.error("[PremortemChat] Failed to load session:", e);
94
85
  }
95
86
  return null;
96
87
  }
@@ -398,23 +389,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
398
389
  console.error(`[Premortem DEBUG] Step 2: Parsing input...`);
399
390
  const parsedInput = RunInputSchema.parse(input);
400
391
  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
392
  console.error(`[Premortem] Queuing analysis for: ${parsedInput.project.name}`);
408
393
  const startTime = Date.now();
409
394
  console.error(`[Premortem DEBUG] Step 4: Creating job document...`);
410
- const docRef = await firestoreRetry(() => db.collection("premortem_jobs").add({
395
+ const { id: jobId } = await createPremortem({
411
396
  status: "queued",
412
397
  payload: parsedInput,
413
398
  uid: decoded?.uid || null,
414
- source: "mcp_sync",
415
- createdAt: admin.firestore.FieldValue.serverTimestamp()
416
- }), { operationName: "premortem_job_create" });
417
- const jobId = docRef.id;
399
+ source: "mcp_sync"
400
+ });
418
401
  console.error(`[Premortem DEBUG] Step 4 done: jobId=${jobId}`);
419
402
  console.error(`[Premortem] Job queued: ${jobId}, polling for completion...`);
420
403
  const maxWaitMs = 10 * 60 * 1e3;
@@ -423,8 +406,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
423
406
  let kickedFallback = false;
424
407
  while (Date.now() - startTime < maxWaitMs) {
425
408
  await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
426
- const snap = await docRef.get();
427
- const data = snap.data() || {};
409
+ const data = await getPremortem(jobId) || {};
428
410
  if (!kickedFallback && data.status === "queued" && Date.now() - startTime > 1e4) {
429
411
  console.error(`[Premortem] Job still queued after 10s, kicking via Cloud Function...`);
430
412
  kickedFallback = true;
@@ -475,31 +457,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
475
457
  const { input, auth_token } = args;
476
458
  const decoded = await requirePremiumWithAutoAuth(auth_token);
477
459
  const parsedInput = RunInputSchema.parse(input);
478
- const app = getApps().length > 0 ? getApp() : void 0;
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({
460
+ const { id: queuedJobId } = await createPremortem({
483
461
  status: "queued",
484
462
  payload: parsedInput,
485
- createdAt: admin.firestore.FieldValue.serverTimestamp(),
486
463
  uid: decoded?.uid || null
487
- }), { operationName: "premortem_queue_create" });
464
+ });
488
465
  return {
489
- content: [{ type: "text", text: JSON.stringify({ jobId: docRef.id }) }]
466
+ content: [{ type: "text", text: JSON.stringify({ jobId: queuedJobId }) }]
490
467
  };
491
468
  }
492
469
  case "premortem_status": {
493
470
  const { jobId, auth_token } = args;
494
471
  const { effectiveUid } = await resolveAuthContext(auth_token);
495
- const app = getApps().length > 0 ? getApp() : void 0;
496
- if (!app)
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)
472
+ const data = await getPremortem(jobId);
473
+ if (!data)
501
474
  throw new McpError(ErrorCode.InvalidRequest, "Job not found");
502
- const data = snap.data() || {};
503
475
  const allowPublic = process.env.ALLOW_PUBLIC_PREMORTEM === "true";
504
476
  if (!allowPublic && effectiveUid && data.uid && data.uid !== effectiveUid) {
505
477
  throw new McpError(ErrorCode.InvalidRequest, "You do not have permission to access this job");
@@ -566,27 +538,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
566
538
  case "premortem_list": {
567
539
  const { limit = 10, auth_token } = args;
568
540
  const { effectiveUid } = await resolveAuthContext(auth_token);
569
- const app = getApps().length > 0 ? getApp() : void 0;
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
- });
541
+ const premortems = await listPremortems({ limit });
590
542
  return {
591
543
  content: [{ type: "text", text: JSON.stringify(premortems, null, 2) }]
592
544
  };
@@ -603,10 +555,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
603
555
  throw new McpError(ErrorCode.InvalidParams, "conversational_context is required");
604
556
  }
605
557
  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
558
  const { buildRunInput, validateForGraduation, buildGraduationMetadata } = await import("./premortem-handoff-XT4K3YDJ.js");
611
559
  const validation = validateForGraduation(conversational_context);
612
560
  if (!validation.valid) {
@@ -624,24 +572,22 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
624
572
  conversational_context.verdict ? 5 : 4
625
573
  // Estimate current act
626
574
  );
627
- const docRef = await firestoreRetry(() => db.collection("premortem_jobs").add({
575
+ const { id: gradJobId } = await createPremortem({
628
576
  status: "queued",
629
577
  payload: parsedInput,
630
- createdAt: admin.firestore.FieldValue.serverTimestamp(),
631
578
  uid: decoded?.uid || null,
632
579
  source: "mcp_graduate",
633
580
  graduation: graduationMetadata
634
- }), { operationName: "premortem_graduate_create" });
635
- console.error(`[Premortem Graduate] Created job ${docRef.id} from session ${session_id}`);
581
+ });
582
+ console.error(`[Premortem Graduate] Created job ${gradJobId} from session ${session_id}`);
636
583
  try {
637
- const sessionRef = db.collection("premortem_jobs").doc(session_id);
638
- const sessionSnap = await sessionRef.get();
639
- if (sessionSnap.exists) {
640
- await firestoreRetry(() => sessionRef.update({
641
- linkedJobId: docRef.id,
642
- graduatedAt: admin.firestore.FieldValue.serverTimestamp()
643
- }), { operationName: "premortem_session_link" });
644
- console.error(`[Premortem Graduate] Linked session ${session_id} to job ${docRef.id}`);
584
+ const sessionData = await getPremortem(session_id);
585
+ if (sessionData) {
586
+ await updatePremortem(session_id, {
587
+ linkedJobId: gradJobId,
588
+ graduatedAt: (/* @__PURE__ */ new Date()).toISOString()
589
+ });
590
+ console.error(`[Premortem Graduate] Linked session ${session_id} to job ${gradJobId}`);
645
591
  }
646
592
  } catch (linkError) {
647
593
  console.error(`[Premortem Graduate] Failed to link session:`, linkError);
@@ -649,11 +595,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
649
595
  return {
650
596
  content: [{ type: "text", text: JSON.stringify({
651
597
  success: true,
652
- jobId: docRef.id,
598
+ jobId: gradJobId,
653
599
  sourceSessionId: session_id,
654
600
  projectName: project_name,
655
601
  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 "${docRef.id}" to check progress.`
602
+ 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
603
  }, null, 2) }]
658
604
  };
659
605
  }
@@ -856,10 +802,6 @@ Let's start with the basics. Tell me more about what you're building:
856
802
  if (!graduationStatus.canGraduate) {
857
803
  throw new McpError(ErrorCode.InvalidRequest, `Cannot graduate: ${graduationStatus.reason}`);
858
804
  }
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
805
  let productContext = session.productContext;
864
806
  if (!productContext?.brief) {
865
807
  console.error("[PremortemGraduate] Building fallback product context from initialInput");
@@ -890,7 +832,7 @@ Let's start with the basics. Tell me more about what you're building:
890
832
  conversationalContext: conversational_context
891
833
  });
892
834
  const parsedInput = RunInputSchema.parse(runInput);
893
- const docRef = await firestoreRetry(() => db.collection("premortem_jobs").add({
835
+ const { id: chatGradJobId } = await createPremortem({
894
836
  status: "queued",
895
837
  payload: parsedInput,
896
838
  uid: decoded?.uid || null,
@@ -898,31 +840,30 @@ Let's start with the basics. Tell me more about what you're building:
898
840
  metadata: buildGraduationMetadata({
899
841
  sessionId: session_id,
900
842
  conversationalContext: conversational_context
901
- }),
902
- createdAt: admin.firestore.FieldValue.serverTimestamp()
903
- }), { operationName: "premortem_chat_graduate" });
843
+ })
844
+ });
904
845
  session.conversationHistory.push({
905
846
  role: "assistant",
906
847
  content: `\u{1F393} **Graduated to Full Analysis!**
907
848
 
908
849
  \u2615 This will take a minute\u2014go grab a cup of coffee while our 9 AI agents stress-test your idea.
909
850
 
910
- Job ID: \`${docRef.id}\`
851
+ Job ID: \`${chatGradJobId}\`
911
852
 
912
853
  The full pipeline includes deeper competitor research, market sizing, financial modeling, and more.`
913
854
  });
914
855
  await saveChatSession(session);
915
856
  try {
916
- await firestoreRetry(() => db.collection("premortem_chats").doc(session_id).update({
917
- linkedJobId: docRef.id,
918
- graduatedAt: admin.firestore.FieldValue.serverTimestamp()
919
- }), { operationName: "premortem_chat_link" });
857
+ await updateChat(session_id, {
858
+ linkedJobId: chatGradJobId,
859
+ graduatedAt: (/* @__PURE__ */ new Date()).toISOString()
860
+ });
920
861
  } catch {
921
862
  }
922
863
  return {
923
864
  content: [{ type: "text", text: JSON.stringify({
924
865
  success: true,
925
- jobId: docRef.id,
866
+ jobId: chatGradJobId,
926
867
  sourceSessionId: session_id,
927
868
  projectName: project_name,
928
869
  warnings: validation.warnings,
@@ -932,7 +873,7 @@ The full pipeline includes deeper competitor research, market sizing, financial
932
873
  competitors: session.competitors.length,
933
874
  has_verdict: !!session.verdict
934
875
  },
935
- message: `\u2615 Full analysis queued! This will take a minute\u2014go grab a cup of coffee. Use premortem_status with jobId "${docRef.id}" to check progress.`
876
+ 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
877
  }, null, 2) }]
937
878
  };
938
879
  }
@@ -11,20 +11,21 @@ import {
11
11
  getWikiMarkdown,
12
12
  listPersonas,
13
13
  saveWikiMarkdown
14
- } from "./chunk-IVWF7VYZ.js";
14
+ } from "./chunk-M37M2UA4.js";
15
+ import "./chunk-JBJYSV4P.js";
15
16
  import {
16
17
  guardBoundary,
17
18
  guardOutput,
18
- initFirebase,
19
+ withPerfTracking
20
+ } from "./chunk-OP4EO6FV.js";
21
+ import {
19
22
  mapErrorToMcp,
20
23
  requirePremiumWithAutoAuth,
21
24
  resolveAuthContext,
22
25
  validateAuth,
23
- validateRequestSize,
24
- withPerfTracking
25
- } from "./chunk-PD2HN2R5.js";
26
+ validateRequestSize
27
+ } from "./chunk-NUBIEJTU.js";
26
28
  import "./chunk-7FHM2GD3.js";
27
- import "./chunk-JBJYSV4P.js";
28
29
 
29
30
  // ../mcp/dist/mcp/src/tools-server.js
30
31
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -184,7 +185,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
184
185
  throw new McpError(ErrorCode.InvalidParams, "Missing arguments");
185
186
  validateRequestSize(rawArgs);
186
187
  const { args } = guardBoundary(name, rawArgs);
187
- initFirebase();
188
188
  const rawResponse = await withPerfTracking(name, async () => {
189
189
  if (isWriteTool(name)) {
190
190
  const peekToken = args.auth_token;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kylewadegrove/cutline-mcp-cli",
3
- "version": "0.5.1",
3
+ "version": "0.6.1",
4
4
  "description": "CLI and MCP servers for Cutline — authenticate, then run constraint-aware MCP servers in Cursor or any MCP client.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",