edsger 0.54.0 → 0.55.0

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.
Files changed (142) hide show
  1. package/README.md +20 -0
  2. package/dist/api/financing.d.ts +47 -0
  3. package/dist/api/financing.js +37 -0
  4. package/dist/api/issues/approval-checker.d.ts +11 -9
  5. package/dist/api/issues/approval-checker.js +30 -41
  6. package/dist/api/issues/status-updater.d.ts +47 -20
  7. package/dist/api/issues/status-updater.js +114 -46
  8. package/dist/api/issues/update-issue.d.ts +5 -0
  9. package/dist/api/issues/update-issue.js +6 -0
  10. package/dist/commands/agent-workflow/processor.js +5 -1
  11. package/dist/commands/checklists/index.d.ts +5 -2
  12. package/dist/commands/checklists/index.js +73 -12
  13. package/dist/commands/checklists/tools.d.ts +14 -7
  14. package/dist/commands/checklists/tools.js +15 -208
  15. package/dist/commands/financing-deck/index.d.ts +8 -0
  16. package/dist/commands/financing-deck/index.js +66 -0
  17. package/dist/commands/find-architecture/index.d.ts +13 -0
  18. package/dist/commands/find-architecture/index.js +41 -0
  19. package/dist/commands/issue-analysis/index.d.ts +5 -0
  20. package/dist/commands/issue-analysis/index.js +9 -0
  21. package/dist/commands/sync-github-issues/index.d.ts +11 -0
  22. package/dist/commands/sync-github-issues/index.js +42 -0
  23. package/dist/commands/sync-sentry-issues/index.d.ts +14 -0
  24. package/dist/commands/sync-sentry-issues/index.js +73 -0
  25. package/dist/commands/technical-design/index.d.ts +5 -0
  26. package/dist/commands/technical-design/index.js +9 -0
  27. package/dist/commands/test-cases-analysis/index.d.ts +5 -0
  28. package/dist/commands/test-cases-analysis/index.js +9 -0
  29. package/dist/commands/user-stories-analysis/index.d.ts +5 -0
  30. package/dist/commands/user-stories-analysis/index.js +9 -0
  31. package/dist/commands/workflow/executors/phase-executor.js +6 -4
  32. package/dist/commands/workflow/phase-orchestrator.js +0 -1
  33. package/dist/config/issue-status.d.ts +18 -45
  34. package/dist/config/issue-status.js +21 -107
  35. package/dist/index.js +176 -4
  36. package/dist/phases/app-store-generation/agent.js +2 -1
  37. package/dist/phases/app-store-generation/index.js +11 -3
  38. package/dist/phases/branch-planning/index.js +0 -1
  39. package/dist/phases/bug-fixing/analyzer.js +0 -1
  40. package/dist/phases/bug-fixing/mcp-server.d.ts +18 -1
  41. package/dist/phases/bug-fixing/mcp-server.js +19 -76
  42. package/dist/phases/chat-processor/product-tools.d.ts +5 -8
  43. package/dist/phases/chat-processor/product-tools.js +6 -512
  44. package/dist/phases/chat-processor/tools.d.ts +5 -9
  45. package/dist/phases/chat-processor/tools.js +6 -704
  46. package/dist/phases/code-implementation/index.js +0 -1
  47. package/dist/phases/code-implementation-verification/agent.js +6 -1
  48. package/dist/phases/code-refine/index.js +0 -1
  49. package/dist/phases/code-refine/refine-iteration.js +2 -1
  50. package/dist/phases/code-review/index.js +0 -1
  51. package/dist/phases/code-testing/analyzer.js +0 -1
  52. package/dist/phases/financing-deck/agent.d.ts +1 -0
  53. package/dist/phases/financing-deck/agent.js +96 -0
  54. package/dist/phases/financing-deck/context.d.ts +13 -0
  55. package/dist/phases/financing-deck/context.js +69 -0
  56. package/dist/phases/financing-deck/index.d.ts +15 -0
  57. package/dist/phases/financing-deck/index.js +89 -0
  58. package/dist/phases/financing-deck/prompts.d.ts +2 -0
  59. package/dist/phases/financing-deck/prompts.js +94 -0
  60. package/dist/phases/find-architecture/index.d.ts +44 -0
  61. package/dist/phases/find-architecture/index.js +248 -0
  62. package/dist/phases/find-architecture/prompts.d.ts +31 -0
  63. package/dist/phases/find-architecture/prompts.js +128 -0
  64. package/dist/phases/find-architecture/state.d.ts +21 -0
  65. package/dist/phases/find-architecture/state.js +17 -0
  66. package/dist/phases/find-architecture/types.d.ts +55 -0
  67. package/dist/phases/find-architecture/types.js +69 -0
  68. package/dist/phases/find-bugs/index.js +13 -4
  69. package/dist/phases/find-features/index.js +10 -5
  70. package/dist/phases/find-smells/index.js +10 -3
  71. package/dist/phases/functional-testing/analyzer.js +27 -17
  72. package/dist/phases/functional-testing/http-fallback.d.ts +1 -1
  73. package/dist/phases/functional-testing/http-fallback.js +32 -16
  74. package/dist/phases/functional-testing/mcp-server.d.ts +9 -1
  75. package/dist/phases/functional-testing/mcp-server.js +13 -132
  76. package/dist/phases/growth-analysis/agent.js +2 -2
  77. package/dist/phases/growth-analysis/index.js +9 -3
  78. package/dist/phases/intelligence-analysis/agent.js +2 -2
  79. package/dist/phases/intelligence-analysis/index.js +9 -2
  80. package/dist/phases/issue-analysis/agent.d.ts +9 -1
  81. package/dist/phases/issue-analysis/agent.js +68 -27
  82. package/dist/phases/issue-analysis/context.d.ts +5 -9
  83. package/dist/phases/issue-analysis/context.js +31 -76
  84. package/dist/phases/issue-analysis/index.js +32 -84
  85. package/dist/phases/issue-analysis/outcome.d.ts +3 -33
  86. package/dist/phases/issue-analysis/outcome.js +15 -253
  87. package/dist/phases/issue-analysis/prompts.d.ts +3 -5
  88. package/dist/phases/issue-analysis/prompts.js +45 -158
  89. package/dist/phases/issue-analysis-verification/agent.d.ts +4 -4
  90. package/dist/phases/issue-analysis-verification/agent.js +5 -5
  91. package/dist/phases/issue-analysis-verification/index.d.ts +4 -2
  92. package/dist/phases/issue-analysis-verification/index.js +9 -22
  93. package/dist/phases/issue-analysis-verification/prompts.d.ts +1 -2
  94. package/dist/phases/issue-analysis-verification/prompts.js +21 -46
  95. package/dist/phases/output-contracts.js +66 -78
  96. package/dist/phases/pr-execution/index.js +2 -2
  97. package/dist/phases/pr-resolve/index.js +2 -8
  98. package/dist/phases/pr-splitting/index.js +2 -2
  99. package/dist/phases/release-sync/index.js +52 -43
  100. package/dist/phases/run-sheet/index.js +2 -1
  101. package/dist/phases/smoke-test/agent.js +2 -1
  102. package/dist/phases/smoke-test/index.js +4 -1
  103. package/dist/phases/sync-github-issues/index.d.ts +41 -0
  104. package/dist/phases/sync-github-issues/index.js +187 -0
  105. package/dist/phases/sync-github-issues/state.d.ts +26 -0
  106. package/dist/phases/sync-github-issues/state.js +18 -0
  107. package/dist/phases/sync-github-issues/types.d.ts +35 -0
  108. package/dist/phases/sync-github-issues/types.js +6 -0
  109. package/dist/phases/sync-sentry-issues/index.d.ts +29 -0
  110. package/dist/phases/sync-sentry-issues/index.js +153 -0
  111. package/dist/phases/sync-sentry-issues/sentry-client.d.ts +66 -0
  112. package/dist/phases/sync-sentry-issues/sentry-client.js +221 -0
  113. package/dist/phases/sync-sentry-issues/state.d.ts +23 -0
  114. package/dist/phases/sync-sentry-issues/state.js +18 -0
  115. package/dist/phases/sync-sentry-issues/types.d.ts +46 -0
  116. package/dist/phases/sync-sentry-issues/types.js +6 -0
  117. package/dist/phases/sync-shared/mcp.d.ts +81 -0
  118. package/dist/phases/sync-shared/mcp.js +111 -0
  119. package/dist/phases/technical-design/index.js +0 -1
  120. package/dist/phases/test-cases-analysis/agent.js +2 -1
  121. package/dist/phases/test-cases-analysis/index.js +0 -1
  122. package/dist/phases/user-stories-analysis/agent.js +2 -1
  123. package/dist/phases/user-stories-analysis/index.js +0 -1
  124. package/dist/services/coaching/coaching-agent.js +29 -4
  125. package/dist/services/feedbacks.d.ts +1 -1
  126. package/dist/skills/phase/issue-analysis/SKILL.md +48 -92
  127. package/dist/skills/phase/issue-analysis-verification/SKILL.md +46 -31
  128. package/dist/tools/bootstrap.d.ts +45 -0
  129. package/dist/tools/bootstrap.js +50 -0
  130. package/dist/types/external-sources.d.ts +22 -0
  131. package/dist/types/external-sources.js +23 -0
  132. package/dist/types/index.d.ts +5 -10
  133. package/dist/types/issues.d.ts +2 -0
  134. package/dist/types/llm-responses.d.ts +1 -14
  135. package/dist/utils/formatters.js +1 -7
  136. package/dist/utils/issue-phase-cli.d.ts +26 -0
  137. package/dist/utils/issue-phase-cli.js +44 -0
  138. package/dist/workspace/workspace-manager.d.ts +10 -0
  139. package/dist/workspace/workspace-manager.js +22 -1
  140. package/package.json +6 -2
  141. package/vitest.config.ts +4 -0
  142. package/.env.local +0 -12
@@ -171,7 +171,6 @@ message, lastAssistantResponse, issueId, verbose
171
171
  }
172
172
  return null;
173
173
  }
174
- // eslint-disable-next-line complexity
175
174
  export const implementIssueCode = async (options, config, checklistContext) => {
176
175
  const { issueId, verbose } = options;
177
176
  // Resolve the repo's default branch (handles forks where default is master, etc.).
@@ -125,7 +125,12 @@ function parseVerificationResult(responseText, verbose) {
125
125
  /**
126
126
  * Process assistant messages to accumulate response text
127
127
  */
128
- function processAssistantContent(content, responseAccumulator, verbose) {
128
+ function processAssistantContent(
129
+ // SDK 0.2.124+ tightened the BetaContentBlock union; accept the wider
130
+ // shape and narrow at use sites. This helper only reads fields that
131
+ // exist on the `text` and `tool_use` variants.
132
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
133
+ content, responseAccumulator, verbose) {
129
134
  if (content.type === 'text' && content.text) {
130
135
  responseAccumulator.text += `${content.text}\n`;
131
136
  logDebug(`🔍 ${content.text}`, verbose);
@@ -42,7 +42,6 @@ export const MAX_REFINE_ITERATIONS = 10;
42
42
  * Similar to technical-design, this includes an iterative improvement cycle:
43
43
  * refine → verification → improve → re-refine (if needed)
44
44
  */
45
- // eslint-disable-next-line complexity
46
45
  export const refineCodeFromPRFeedback = async (options, config, checklistContext) => {
47
46
  const { issueId, githubToken, verbose } = options;
48
47
  if (verbose) {
@@ -77,7 +77,8 @@ export async function executeRefineIteration(opts) {
77
77
  logDebug(`🔧 ${content.text}`, verbose);
78
78
  }
79
79
  else if (content.type === 'tool_use') {
80
- logDebug(`🛠️ ${content.name}: ${content.input.description || 'Running...'}`, verbose);
80
+ const input = (content.input ?? {});
81
+ logDebug(`🛠️ ${content.name}: ${input.description ?? 'Running...'}`, verbose);
81
82
  }
82
83
  }
83
84
  continue;
@@ -68,7 +68,6 @@ baseBranchInfo, baseBranchForRebase, originalBaseBranchForRebase, verbose) {
68
68
  /**
69
69
  * Main code review function
70
70
  */
71
- // eslint-disable-next-line complexity
72
71
  export const reviewPullRequest = async (options, config, checklistContext) => {
73
72
  const { issueId, githubToken, verbose } = options;
74
73
  if (verbose) {
@@ -32,7 +32,6 @@ async function* prompt(testingPrompt) {
32
32
  setTimeout(res, 10000);
33
33
  });
34
34
  }
35
- // eslint-disable-next-line complexity
36
35
  export const writeCodeTests = async (options, config) => {
37
36
  const { issueId, verbose } = options;
38
37
  if (verbose) {
@@ -0,0 +1 @@
1
+ export declare function executeFinancingDeckQuery(userPrompt: string, systemPrompt: string, verbose?: boolean): Promise<Record<string, unknown> | null>;
@@ -0,0 +1,96 @@
1
+ import { query } from '@anthropic-ai/claude-agent-sdk';
2
+ import { DEFAULT_MODEL } from '../../constants.js';
3
+ import { logDebug, logError, logInfo } from '../../utils/logger.js';
4
+ /**
5
+ * Pulls the JSON contract block out of Claude's final assistant message.
6
+ * Accepts either a fenced ```json block or a bare JSON object — the
7
+ * fenced form is what the contract asks for.
8
+ */
9
+ function parseDeckResult(responseText) {
10
+ try {
11
+ const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
12
+ const jsonText = jsonBlockMatch
13
+ ? jsonBlockMatch[1]
14
+ : (responseText.match(/\{[\s\S]*\}/) ?? [responseText])[0];
15
+ const parsed = JSON.parse(jsonText);
16
+ if (parsed && Array.isArray(parsed.deck_slides)) {
17
+ return { deck: parsed };
18
+ }
19
+ return { error: 'Missing or empty deck_slides array' };
20
+ }
21
+ catch (error) {
22
+ return {
23
+ error: `JSON parsing failed: ${error instanceof Error ? error.message : String(error)}`,
24
+ };
25
+ }
26
+ }
27
+ function userMessage(content) {
28
+ return {
29
+ type: 'user',
30
+ message: { role: 'user', content },
31
+ };
32
+ }
33
+ // eslint-disable-next-line @typescript-eslint/require-await -- async generator required by SDK interface
34
+ async function* prompt(deckPrompt) {
35
+ yield userMessage(deckPrompt);
36
+ }
37
+ export async function executeFinancingDeckQuery(userPrompt, systemPrompt, verbose) {
38
+ let lastAssistantResponse = '';
39
+ let structured = null;
40
+ let turnCount = 0;
41
+ logInfo('Connecting to AI agent...');
42
+ for await (const message of query({
43
+ prompt: prompt(userPrompt),
44
+ options: {
45
+ systemPrompt: {
46
+ type: 'preset',
47
+ preset: 'claude_code',
48
+ append: systemPrompt,
49
+ },
50
+ model: DEFAULT_MODEL,
51
+ maxTurns: 40,
52
+ permissionMode: 'bypassPermissions',
53
+ },
54
+ })) {
55
+ if (verbose) {
56
+ logInfo(`Received message type: ${message.type}`);
57
+ }
58
+ if (message.type === 'assistant' && message.message?.content) {
59
+ turnCount++;
60
+ for (const content of message.message.content) {
61
+ if (content.type === 'text') {
62
+ lastAssistantResponse += `${content.text}\n`;
63
+ logDebug(`${content.text}`, verbose);
64
+ }
65
+ else if (content.type === 'tool_use') {
66
+ const input = (content.input ?? {});
67
+ const desc = input.description ?? input.command ?? 'Running...';
68
+ logInfo(`[Turn ${turnCount}] ${content.name}: ${typeof desc === 'string' ? desc.slice(0, 120) : 'Running...'}`);
69
+ }
70
+ }
71
+ }
72
+ if (message.type === 'result') {
73
+ if (message.subtype === 'success') {
74
+ logInfo(`\nFinancing deck draft completed after ${turnCount} turns, parsing JSON contract...`);
75
+ const responseText = message.result || lastAssistantResponse;
76
+ const parsed = parseDeckResult(responseText);
77
+ if (parsed.error) {
78
+ logError(`Failed to parse deck JSON: ${parsed.error}`);
79
+ }
80
+ else {
81
+ structured = parsed.deck;
82
+ }
83
+ }
84
+ else {
85
+ logError(`\nDeck generation incomplete: ${message.subtype}`);
86
+ if (lastAssistantResponse) {
87
+ const parsed = parseDeckResult(lastAssistantResponse);
88
+ if (!parsed.error) {
89
+ structured = parsed.deck;
90
+ }
91
+ }
92
+ }
93
+ }
94
+ }
95
+ return structured;
96
+ }
@@ -0,0 +1,13 @@
1
+ import { type FinancingContext, type FinancingStage } from '../../api/financing.js';
2
+ import { type ProductInfo } from '../../api/products.js';
3
+ export interface FinancingDeckContext {
4
+ product: ProductInfo;
5
+ financing: FinancingContext;
6
+ contextSummary: string;
7
+ }
8
+ /**
9
+ * Pre-fetch product + financing context so we can include a compact
10
+ * summary in the user prompt. The agent can still call MCP tools for
11
+ * the full data, but having a baseline summary speeds the first turn.
12
+ */
13
+ export declare function prepareFinancingDeckContext(productId: string, targetStage: FinancingStage, verbose?: boolean): Promise<FinancingDeckContext>;
@@ -0,0 +1,69 @@
1
+ import { getFinancingContext, } from '../../api/financing.js';
2
+ import { getProduct } from '../../api/products.js';
3
+ import { logError, logInfo } from '../../utils/logger.js';
4
+ /**
5
+ * Pre-fetch product + financing context so we can include a compact
6
+ * summary in the user prompt. The agent can still call MCP tools for
7
+ * the full data, but having a baseline summary speeds the first turn.
8
+ */
9
+ export async function prepareFinancingDeckContext(productId, targetStage, verbose) {
10
+ if (verbose) {
11
+ logInfo(`Fetching financing deck context for product=${productId} stage=${targetStage}`);
12
+ }
13
+ let product;
14
+ let financing;
15
+ try {
16
+ ;
17
+ [product, financing] = await Promise.all([
18
+ getProduct(productId, verbose),
19
+ getFinancingContext(productId, targetStage, verbose),
20
+ ]);
21
+ }
22
+ catch (error) {
23
+ const errorMessage = error instanceof Error ? error.message : String(error);
24
+ logError(`Failed to fetch financing context: ${errorMessage}`);
25
+ throw new Error(`Context fetch failed: ${errorMessage}`);
26
+ }
27
+ const issuesCount = product.issues?.length ?? 0;
28
+ const profile = financing.profile ?? {};
29
+ const metrics = financing.metrics ?? {};
30
+ const contextSummary = [
31
+ `## Product`,
32
+ `Name: ${product.name}`,
33
+ `Description: ${product.description ?? '(none)'}`,
34
+ `Issues tracked: ${issuesCount}`,
35
+ ``,
36
+ `## Founder context (self-reported)`,
37
+ `Current stage: ${profile.current_stage ?? 'not provided'}`,
38
+ `Target stage: ${targetStage}`,
39
+ `Target round size USD: ${profile.target_amount_usd ?? 'not provided'}`,
40
+ `Target valuation USD: ${profile.target_valuation_usd ?? 'not provided'}`,
41
+ `Use of funds: ${profile.use_of_funds ?? 'not provided'}`,
42
+ ``,
43
+ `## Self-reported metrics`,
44
+ formatMetric(metrics, 'arr_usd', 'ARR (USD)'),
45
+ formatMetric(metrics, 'mrr_usd', 'MRR (USD)'),
46
+ formatMetric(metrics, 'paying_customer_count', 'Paying customers'),
47
+ formatMetric(metrics, 'mom_growth_rate', 'MoM growth (fraction)'),
48
+ formatMetric(metrics, 'yoy_growth_rate', 'YoY growth (fraction)'),
49
+ formatMetric(metrics, 'net_revenue_retention', 'NRR (fraction)'),
50
+ formatMetric(metrics, 'gross_revenue_retention', 'GRR (fraction)'),
51
+ formatMetric(metrics, 'cac_usd', 'CAC (USD)'),
52
+ formatMetric(metrics, 'ltv_usd', 'LTV (USD)'),
53
+ formatMetric(metrics, 'cac_payback_months', 'CAC payback (months)'),
54
+ formatMetric(metrics, 'gross_margin', 'Gross margin (fraction)'),
55
+ formatMetric(metrics, 'monthly_burn_usd', 'Monthly burn (USD)'),
56
+ formatMetric(metrics, 'cash_in_bank_usd', 'Cash in bank (USD)'),
57
+ formatMetric(metrics, 'runway_months', 'Runway (months)'),
58
+ formatMetric(metrics, 'burn_multiple', 'Burn Multiple'),
59
+ formatMetric(metrics, 'fte_count', 'Team FTEs'),
60
+ ``,
61
+ `## Industry benchmarks`,
62
+ `Not bundled — use WebSearch to fetch current ${targetStage} benchmarks for this product's vertical before judging any metric. Cite source URLs in stage_reasoning and red flags.`,
63
+ ].join('\n');
64
+ return { product, financing, contextSummary };
65
+ }
66
+ function formatMetric(metrics, key, label) {
67
+ const v = metrics[key];
68
+ return `- ${label}: ${v === null || v === undefined ? 'not provided' : String(v)}`;
69
+ }
@@ -0,0 +1,15 @@
1
+ import { type FinancingStage } from '../../api/financing.js';
2
+ export interface FinancingDeckOptions {
3
+ productId: string;
4
+ targetStage: FinancingStage;
5
+ verbose?: boolean;
6
+ }
7
+ export interface FinancingDeckResult {
8
+ productId: string;
9
+ status: 'success' | 'error';
10
+ summary: string;
11
+ analysisId?: string;
12
+ slideCount: number;
13
+ redFlagCount: number;
14
+ }
15
+ export declare function generateFinancingDeck(options: FinancingDeckOptions): Promise<FinancingDeckResult>;
@@ -0,0 +1,89 @@
1
+ import { saveFinancingAnalysis, } from '../../api/financing.js';
2
+ import { logError, logInfo, logSuccess, logWarning } from '../../utils/logger.js';
3
+ import { executeFinancingDeckQuery } from './agent.js';
4
+ import { prepareFinancingDeckContext } from './context.js';
5
+ import { createFinancingDeckSystemPrompt, createFinancingDeckUserPrompt, } from './prompts.js';
6
+ const REQUIRED_SLIDE_IDS = [
7
+ 'problem',
8
+ 'solution',
9
+ 'market',
10
+ 'product',
11
+ 'traction',
12
+ 'business_model',
13
+ 'go_to_market',
14
+ 'competition',
15
+ 'team',
16
+ 'financials',
17
+ 'ask',
18
+ ];
19
+ export async function generateFinancingDeck(options) {
20
+ const { productId, targetStage, verbose } = options;
21
+ if (verbose) {
22
+ logInfo(`Starting financing deck generation: product=${productId} stage=${targetStage}`);
23
+ }
24
+ try {
25
+ const { contextSummary } = await prepareFinancingDeckContext(productId, targetStage, verbose);
26
+ const systemPrompt = createFinancingDeckSystemPrompt(targetStage);
27
+ const userPrompt = createFinancingDeckUserPrompt(productId, targetStage, contextSummary);
28
+ if (verbose) {
29
+ logInfo('Starting AI query for financing deck...');
30
+ }
31
+ const structured = await executeFinancingDeckQuery(userPrompt, systemPrompt, verbose);
32
+ if (!structured) {
33
+ throw new Error('No deck JSON received from agent');
34
+ }
35
+ const deckSlides = (structured.deck_slides ?? []);
36
+ const redFlags = (structured.red_flags ?? []);
37
+ const stageReasoning = structured.stage_reasoning ?? '';
38
+ const sourceSummary = structured.source_summary ?? {};
39
+ // Sanity-check the deck shape — the agent might short-circuit. If so we
40
+ // still return whatever we got, but warn loudly.
41
+ const missing = REQUIRED_SLIDE_IDS.filter((id) => !deckSlides.some((s) => s.slide_id === id));
42
+ if (missing.length > 0) {
43
+ logWarning(`Deck is missing required slides: ${missing.join(', ')} — saving anyway`);
44
+ }
45
+ // The agent should have already called save_financing_analysis via MCP.
46
+ // We re-save here to GUARANTEE persistence, even if the agent forgot the
47
+ // tool call but did emit a valid JSON contract. Idempotent at the
48
+ // analyses-history level (each save creates a new row).
49
+ let analysisId;
50
+ if (deckSlides.length > 0) {
51
+ try {
52
+ const saved = await saveFinancingAnalysis({
53
+ product_id: productId,
54
+ target_stage: targetStage,
55
+ deck_slides: deckSlides,
56
+ stage_reasoning: stageReasoning,
57
+ red_flags: redFlags,
58
+ source_summary: sourceSummary,
59
+ }, verbose);
60
+ analysisId = saved.analysis_id;
61
+ }
62
+ catch (saveError) {
63
+ // Agent likely already saved it — log and continue
64
+ if (verbose) {
65
+ logWarning(`Re-save skipped (agent likely already persisted): ${saveError instanceof Error ? saveError.message : String(saveError)}`);
66
+ }
67
+ }
68
+ }
69
+ logSuccess(`Financing deck completed: ${deckSlides.length} slides, ${redFlags.length} red flags`);
70
+ return {
71
+ productId,
72
+ status: 'success',
73
+ summary: stageReasoning || 'Deck draft persisted',
74
+ analysisId,
75
+ slideCount: deckSlides.length,
76
+ redFlagCount: redFlags.length,
77
+ };
78
+ }
79
+ catch (error) {
80
+ logError(`Financing deck generation failed: ${error instanceof Error ? error.message : String(error)}`);
81
+ return {
82
+ productId,
83
+ status: 'error',
84
+ summary: `Generation failed: ${error instanceof Error ? error.message : String(error)}`,
85
+ slideCount: 0,
86
+ redFlagCount: 0,
87
+ };
88
+ }
89
+ }
@@ -0,0 +1,2 @@
1
+ export declare function createFinancingDeckSystemPrompt(targetStage: string): string;
2
+ export declare function createFinancingDeckUserPrompt(productId: string, targetStage: string, contextSummary: string): string;
@@ -0,0 +1,94 @@
1
+ const STAGE_LABELS = {
2
+ pre_seed: 'Pre-Seed',
3
+ seed: 'Seed',
4
+ series_a: 'Series A',
5
+ series_b: 'Series B',
6
+ series_c: 'Series C',
7
+ growth: 'Growth',
8
+ };
9
+ const OUTPUT_CONTRACT = `## Output contract
10
+
11
+ Return ONE fenced JSON block with EXACTLY this shape — no prose around it:
12
+
13
+ \`\`\`json
14
+ {
15
+ "deck_slides": [
16
+ {"slide_id": "problem", "title": "...", "body": "...", "ai_rationale": "..."},
17
+ {"slide_id": "solution", "title": "...", "body": "...", "ai_rationale": "..."},
18
+ {"slide_id": "market", "title": "...", "body": "...", "ai_rationale": "..."},
19
+ {"slide_id": "product", "title": "...", "body": "...", "ai_rationale": "..."},
20
+ {"slide_id": "traction", "title": "...", "body": "...", "ai_rationale": "..."},
21
+ {"slide_id": "business_model","title": "...", "body": "...", "ai_rationale": "..."},
22
+ {"slide_id": "go_to_market", "title": "...", "body": "...", "ai_rationale": "..."},
23
+ {"slide_id": "competition", "title": "...", "body": "...", "ai_rationale": "..."},
24
+ {"slide_id": "team", "title": "...", "body": "...", "ai_rationale": "..."},
25
+ {"slide_id": "financials", "title": "...", "body": "...", "ai_rationale": "..."},
26
+ {"slide_id": "ask", "title": "...", "body": "...", "ai_rationale": "..."}
27
+ ],
28
+ "stage_reasoning": "2-4 sentence honest read on whether the data supports the target stage.",
29
+ "red_flags": [
30
+ {"metric": "...", "severity": "low|medium|high", "explanation": "...", "fix": "..."}
31
+ ],
32
+ "source_summary": {"issues": 0, "feedbacks": 0, "has_metrics": true}
33
+ }
34
+ \`\`\`
35
+
36
+ All 11 slide_ids are required in this exact order. body should be 80–150 words.`;
37
+ export function createFinancingDeckSystemPrompt(targetStage) {
38
+ const stageLabel = STAGE_LABELS[targetStage] ?? targetStage;
39
+ return `You are a senior fundraising advisor who has helped 100+ SaaS founders raise from Pre-Seed through Series C. You write the way investors at Sequoia, a16z, Bessemer, and YC speak: blunt, specific, evidence-driven, no fluff.
40
+
41
+ Your job: draft a complete ${stageLabel} pitch deck for a product, surface red flags an experienced investor would call out within 90 seconds, and write a short investor-style read on the founder's stage readiness.
42
+
43
+ You will be given the product context plus the founder's self-reported financial metrics. Use the MCP tools when you need additional product data — do NOT fabricate.
44
+
45
+ ## Hard rules
46
+
47
+ - **Industry benchmarks are NOT shipped with the context.** Before judging any metric against a stage standard, you MUST call \`WebSearch\` to look up current ${stageLabel} benchmarks for the product's vertical (B2B SaaS, vertical SaaS, AI infra, marketplace, etc. — infer from the product description). Cite at least one source URL in \`stage_reasoning\` and in each red flag's \`explanation\`. If WebSearch returns nothing usable, say "no recent benchmark found for X" rather than inventing a number.
48
+ - NEVER invent a financial metric. If the founder did not provide a number, write "not provided" in the relevant slide and add it as a red flag.
49
+ - Quote at least one real customer feedback line (verbatim or close paraphrase) somewhere in the Problem or Traction slide if any feedback exists in the gathered context.
50
+ - Match tone to stage:
51
+ - **pre_seed / seed**: lead with founder insight, problem, early signal. Traction can be qualitative.
52
+ - **series_a**: traction slide is the centerpiece — must reference actual ARR / growth / customer numbers from the gathered metrics. If those are missing, say so explicitly in stage_reasoning rather than inventing.
53
+ - **series_b+**: Burn Multiple, Rule of 40, NRR, CAC Payback are mandatory. Flag any that are missing as red flags.
54
+ - Each slide body is roughly 80–150 words. No marketing fluff. No emoji.
55
+ - Red flags should be honest — if burn multiple is bad vs. the benchmark you found, say so with the source; if retention is unknown, say "missing — investors will ask".
56
+
57
+ ## Tools available
58
+
59
+ - \`WebSearch\` / \`WebFetch\` — REQUIRED for fetching current ${stageLabel} industry benchmarks before judging metrics. Prefer recent (this year or last) sources: SaaS Capital, OpenView, Bessemer State of the Cloud, ICONIQ Growth, Carta data.
60
+ - \`mcp__edsger__get_product\` — product name + description
61
+ - \`mcp__edsger__list_issues\` — what the team is shipping
62
+ - \`mcp__edsger__list_feedbacks\` — raw customer voice
63
+ - \`mcp__edsger__get_financing_context\` — founder profile + self-reported metrics
64
+ - \`mcp__edsger__save_financing_analysis\` — call this LAST to persist the deck
65
+
66
+ ## Workflow
67
+
68
+ 1. Call \`get_product\`, \`list_issues\`, \`list_feedbacks\`, and \`get_financing_context\` (in any order or in parallel) to gather data.
69
+ 2. Based on the product description, identify the vertical (B2B SaaS / vertical SaaS / AI infra / dev tools / marketplace / etc.) and call \`WebSearch\` 1–3 times to fetch current ${stageLabel} benchmarks for that vertical (ARR, growth, NRR, burn multiple, CAC payback as applicable). Save the source URLs.
70
+ 3. Draft 11 structured slides (ids, in order: problem, solution, market, product, traction, business_model, go_to_market, competition, team, financials, ask).
71
+ 4. Identify 0–5 red flags by comparing the founder's metrics against the benchmarks you fetched. Each red flag's \`explanation\` must include the source URL.
72
+ 5. Write a 2–4 sentence \`stage_reasoning\` paragraph that names the benchmark sources you used.
73
+ 6. Call \`save_financing_analysis\` with all of the above (this writes to financing_ai_analyses; the desktop UI watches that table).
74
+ 7. Then output the JSON described in the output contract below as your final assistant message — the orchestrator parses this for confirmation.
75
+
76
+ ${OUTPUT_CONTRACT}`;
77
+ }
78
+ export function createFinancingDeckUserPrompt(productId, targetStage, contextSummary) {
79
+ const stageLabel = STAGE_LABELS[targetStage] ?? targetStage;
80
+ return `Generate a complete ${stageLabel} pitch deck for product \`${productId}\`.
81
+
82
+ ${contextSummary}
83
+
84
+ Follow the workflow in your system instructions:
85
+ 1. Use the MCP tools to gather any additional data you need (issues, feedbacks, financing context).
86
+ 2. WebSearch current ${stageLabel} benchmarks for this product's vertical and save the source URLs.
87
+ 3. Draft the 11 slides.
88
+ 4. Identify red flags vs the benchmarks you fetched (cite sources).
89
+ 5. Write stage_reasoning naming those sources.
90
+ 6. Call \`save_financing_analysis\` to persist.
91
+ 7. Then return the JSON output described in the contract.
92
+
93
+ Begin.`;
94
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Find-architecture phase: clone the product's repo, ask Claude to audit the
3
+ * codebase for architectural problems and improvement opportunities, and file
4
+ * each new finding as an issue. Subsequent runs are incremental, scoped to
5
+ * commits since the previous successful scan.
6
+ *
7
+ * Companion to find-bugs (real defects), find-features (missing user
8
+ * capability), and find-smells (local code-level improvements). All four
9
+ * phases share the same workspace + state pattern.
10
+ */
11
+ export interface FindArchitectureOptions {
12
+ productId: string;
13
+ githubToken: string;
14
+ owner: string;
15
+ repo: string;
16
+ /** Force a full scan even if previous-scan state exists. */
17
+ full?: boolean;
18
+ /** Branch to scan; defaults to the repo's default branch. */
19
+ branch?: string;
20
+ /** Upper bound on files the auditor may Read. Keeps token cost predictable. */
21
+ maxFiles?: number;
22
+ verbose?: boolean;
23
+ }
24
+ export interface FindArchitectureResult {
25
+ status: 'success' | 'error';
26
+ message: string;
27
+ scannedCommitSha?: string;
28
+ findingsFound?: number;
29
+ issuesCreated?: number;
30
+ /** Findings the agent passed off to find-bugs (not filed by this run). */
31
+ deferredToBugs?: number;
32
+ /** Findings the agent passed off to find-features (not filed by this run). */
33
+ deferredToFeatures?: number;
34
+ /** Findings the agent passed off to find-smells (not filed by this run). */
35
+ deferredToSmells?: number;
36
+ summary?: string;
37
+ }
38
+ /** Single source of truth — referenced by the CLI help string too. */
39
+ export declare const DEFAULT_MAX_FILES = 200;
40
+ /**
41
+ * Scan a product's repository for architectural concerns and file each new
42
+ * finding as an issue.
43
+ */
44
+ export declare function scanForArchitecture(options: FindArchitectureOptions): Promise<FindArchitectureResult>;