edsger 0.53.0 → 0.54.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.
Files changed (148) 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/sync-github-issues/index.d.ts +11 -0
  20. package/dist/commands/sync-github-issues/index.js +42 -0
  21. package/dist/commands/sync-sentry-issues/index.d.ts +14 -0
  22. package/dist/commands/sync-sentry-issues/index.js +73 -0
  23. package/dist/commands/workflow/executors/phase-executor.js +6 -4
  24. package/dist/commands/workflow/phase-orchestrator.js +0 -1
  25. package/dist/config/issue-status.d.ts +18 -45
  26. package/dist/config/issue-status.js +21 -107
  27. package/dist/index.js +97 -3
  28. package/dist/phases/app-store-generation/agent.js +2 -1
  29. package/dist/phases/app-store-generation/index.js +11 -3
  30. package/dist/phases/autonomous/index.js +9 -6
  31. package/dist/phases/branch-planning/index.js +1 -2
  32. package/dist/phases/branch-planning/prompts.d.ts +1 -1
  33. package/dist/phases/branch-planning/prompts.js +3 -2
  34. package/dist/phases/bug-fixing/analyzer.js +6 -3
  35. package/dist/phases/bug-fixing/mcp-server.d.ts +18 -1
  36. package/dist/phases/bug-fixing/mcp-server.js +19 -76
  37. package/dist/phases/chat-processor/product-tools.d.ts +5 -8
  38. package/dist/phases/chat-processor/product-tools.js +6 -512
  39. package/dist/phases/chat-processor/tools.d.ts +5 -9
  40. package/dist/phases/chat-processor/tools.js +6 -704
  41. package/dist/phases/code-implementation/branch-pr-creator.js +7 -5
  42. package/dist/phases/code-implementation/index.js +6 -3
  43. package/dist/phases/code-implementation-verification/agent.js +6 -1
  44. package/dist/phases/code-refine/index.js +8 -6
  45. package/dist/phases/code-refine/refine-iteration.js +2 -1
  46. package/dist/phases/code-review/index.js +8 -6
  47. package/dist/phases/code-testing/analyzer.js +11 -8
  48. package/dist/phases/financing-deck/agent.d.ts +1 -0
  49. package/dist/phases/financing-deck/agent.js +96 -0
  50. package/dist/phases/financing-deck/context.d.ts +13 -0
  51. package/dist/phases/financing-deck/context.js +69 -0
  52. package/dist/phases/financing-deck/index.d.ts +15 -0
  53. package/dist/phases/financing-deck/index.js +89 -0
  54. package/dist/phases/financing-deck/prompts.d.ts +2 -0
  55. package/dist/phases/financing-deck/prompts.js +94 -0
  56. package/dist/phases/find-architecture/index.d.ts +44 -0
  57. package/dist/phases/find-architecture/index.js +248 -0
  58. package/dist/phases/find-architecture/prompts.d.ts +31 -0
  59. package/dist/phases/find-architecture/prompts.js +128 -0
  60. package/dist/phases/find-architecture/state.d.ts +21 -0
  61. package/dist/phases/find-architecture/state.js +17 -0
  62. package/dist/phases/find-architecture/types.d.ts +55 -0
  63. package/dist/phases/find-architecture/types.js +69 -0
  64. package/dist/phases/find-bugs/index.js +13 -4
  65. package/dist/phases/find-features/index.js +10 -5
  66. package/dist/phases/find-smells/index.js +10 -3
  67. package/dist/phases/functional-testing/analyzer.js +27 -17
  68. package/dist/phases/functional-testing/http-fallback.d.ts +1 -1
  69. package/dist/phases/functional-testing/http-fallback.js +32 -16
  70. package/dist/phases/functional-testing/mcp-server.d.ts +9 -1
  71. package/dist/phases/functional-testing/mcp-server.js +13 -132
  72. package/dist/phases/growth-analysis/agent.js +2 -2
  73. package/dist/phases/growth-analysis/index.js +9 -3
  74. package/dist/phases/intelligence-analysis/agent.js +2 -2
  75. package/dist/phases/intelligence-analysis/index.js +9 -2
  76. package/dist/phases/issue-analysis/agent.d.ts +9 -1
  77. package/dist/phases/issue-analysis/agent.js +68 -27
  78. package/dist/phases/issue-analysis/context.d.ts +5 -9
  79. package/dist/phases/issue-analysis/context.js +31 -76
  80. package/dist/phases/issue-analysis/index.js +32 -84
  81. package/dist/phases/issue-analysis/outcome.d.ts +3 -33
  82. package/dist/phases/issue-analysis/outcome.js +15 -253
  83. package/dist/phases/issue-analysis/prompts.d.ts +3 -5
  84. package/dist/phases/issue-analysis/prompts.js +45 -158
  85. package/dist/phases/issue-analysis-verification/agent.d.ts +4 -4
  86. package/dist/phases/issue-analysis-verification/agent.js +5 -5
  87. package/dist/phases/issue-analysis-verification/index.d.ts +4 -2
  88. package/dist/phases/issue-analysis-verification/index.js +9 -22
  89. package/dist/phases/issue-analysis-verification/prompts.d.ts +1 -2
  90. package/dist/phases/issue-analysis-verification/prompts.js +21 -46
  91. package/dist/phases/output-contracts.js +66 -78
  92. package/dist/phases/pr-execution/context.d.ts +2 -0
  93. package/dist/phases/pr-execution/context.js +1 -0
  94. package/dist/phases/pr-execution/index.js +28 -19
  95. package/dist/phases/pr-execution/prompts.d.ts +2 -1
  96. package/dist/phases/pr-execution/prompts.js +12 -10
  97. package/dist/phases/pr-resolve/index.js +2 -8
  98. package/dist/phases/pr-splitting/index.js +3 -3
  99. package/dist/phases/pr-splitting/prompts.d.ts +1 -1
  100. package/dist/phases/pr-splitting/prompts.js +3 -2
  101. package/dist/phases/pull-request/creator.js +10 -7
  102. package/dist/phases/pull-request/handler.js +3 -1
  103. package/dist/phases/release-sync/index.js +52 -43
  104. package/dist/phases/run-sheet/index.js +2 -1
  105. package/dist/phases/smoke-test/agent.js +2 -1
  106. package/dist/phases/smoke-test/index.js +4 -1
  107. package/dist/phases/sync-github-issues/index.d.ts +41 -0
  108. package/dist/phases/sync-github-issues/index.js +187 -0
  109. package/dist/phases/sync-github-issues/state.d.ts +26 -0
  110. package/dist/phases/sync-github-issues/state.js +18 -0
  111. package/dist/phases/sync-github-issues/types.d.ts +35 -0
  112. package/dist/phases/sync-github-issues/types.js +6 -0
  113. package/dist/phases/sync-sentry-issues/index.d.ts +29 -0
  114. package/dist/phases/sync-sentry-issues/index.js +153 -0
  115. package/dist/phases/sync-sentry-issues/sentry-client.d.ts +66 -0
  116. package/dist/phases/sync-sentry-issues/sentry-client.js +221 -0
  117. package/dist/phases/sync-sentry-issues/state.d.ts +23 -0
  118. package/dist/phases/sync-sentry-issues/state.js +18 -0
  119. package/dist/phases/sync-sentry-issues/types.d.ts +46 -0
  120. package/dist/phases/sync-sentry-issues/types.js +6 -0
  121. package/dist/phases/sync-shared/mcp.d.ts +81 -0
  122. package/dist/phases/sync-shared/mcp.js +111 -0
  123. package/dist/phases/technical-design/index.js +0 -1
  124. package/dist/phases/test-cases-analysis/agent.js +2 -1
  125. package/dist/phases/test-cases-analysis/index.js +0 -1
  126. package/dist/phases/user-stories-analysis/agent.js +2 -1
  127. package/dist/phases/user-stories-analysis/index.js +0 -1
  128. package/dist/services/coaching/coaching-agent.js +29 -4
  129. package/dist/services/feedbacks.d.ts +1 -1
  130. package/dist/services/repo-config.d.ts +17 -0
  131. package/dist/services/repo-config.js +50 -0
  132. package/dist/skills/phase/issue-analysis/SKILL.md +48 -92
  133. package/dist/skills/phase/issue-analysis-verification/SKILL.md +46 -31
  134. package/dist/tools/bootstrap.d.ts +45 -0
  135. package/dist/tools/bootstrap.js +50 -0
  136. package/dist/types/external-sources.d.ts +22 -0
  137. package/dist/types/external-sources.js +23 -0
  138. package/dist/types/index.d.ts +5 -10
  139. package/dist/types/issues.d.ts +2 -0
  140. package/dist/types/llm-responses.d.ts +1 -14
  141. package/dist/utils/formatters.js +1 -7
  142. package/dist/utils/git-branch-manager-async.js +9 -5
  143. package/dist/utils/git-branch-manager.js +17 -7
  144. package/dist/workspace/workspace-manager.d.ts +10 -0
  145. package/dist/workspace/workspace-manager.js +22 -1
  146. package/package.json +6 -2
  147. package/vitest.config.ts +4 -0
  148. package/.env.local +0 -12
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { Octokit } from '@octokit/rest';
6
6
  import { gitPush } from '../../utils/git-push.js';
7
+ import { resolveDefaultBranch } from '../../utils/github-repo-info.js';
7
8
  import { logError, logInfo } from '../../utils/logger.js';
8
9
  // GitHub PR title best practice: keep under 72 characters
9
10
  const MAX_PR_TITLE_LENGTH = 72;
@@ -44,7 +45,8 @@ export function devBranchToFeatBranch(devBranchName) {
44
45
  * Create a pull request from a dev/ branch to its corresponding feat/ branch
45
46
  * This is used after code implementation in multi-branch issues
46
47
  */
47
- export async function createBranchPullRequest(config, devBranchName, issueName, branchDescription, baseBranch = 'main') {
48
+ export async function createBranchPullRequest(config, devBranchName, issueName, branchDescription, baseBranch) {
49
+ const resolvedBaseBranch = baseBranch ?? resolveDefaultBranch();
48
50
  const { githubToken, owner, repo, verbose } = config;
49
51
  if (!devBranchName.startsWith('dev/')) {
50
52
  return {
@@ -75,13 +77,13 @@ export async function createBranchPullRequest(config, devBranchName, issueName,
75
77
  logInfo(`📝 Ensuring target branch ${featBranchName} exists...`);
76
78
  }
77
79
  try {
78
- // Get the base branch (main) SHA
80
+ // Get the base branch SHA
79
81
  const { data: baseBranchData } = await octokit.repos.getBranch({
80
82
  owner,
81
83
  repo,
82
- branch: baseBranch,
84
+ branch: resolvedBaseBranch,
83
85
  });
84
- // Create the feat/ branch from main
86
+ // Create the feat/ branch from the resolved base
85
87
  await octokit.git.createRef({
86
88
  owner,
87
89
  repo,
@@ -89,7 +91,7 @@ export async function createBranchPullRequest(config, devBranchName, issueName,
89
91
  sha: baseBranchData.commit.sha,
90
92
  });
91
93
  if (verbose) {
92
- logInfo(`✅ Created target branch ${featBranchName} from ${baseBranch}`);
94
+ logInfo(`✅ Created target branch ${featBranchName} from ${resolvedBaseBranch}`);
93
95
  }
94
96
  }
95
97
  catch (error) {
@@ -8,6 +8,7 @@ import { createBranches, getBaseBranchInfo, getBranches, getCurrentBranch, updat
8
8
  import { formatChecklistsForContext, } from '../../services/checklist.js';
9
9
  import { extractChecklistItems, runPhaseCoaching, } from '../../services/coaching/index.js';
10
10
  import { formatFeedbacksForContext, getFeedbacksForPhase, } from '../../services/feedbacks.js';
11
+ import { getDefaultBranchForIssue } from '../../services/repo-config.js';
11
12
  import { prepareCustomBranchGitEnvironmentAsync, syncFeatBranchWithMain, } from '../../utils/git-branch-manager.js';
12
13
  import { gitPush } from '../../utils/git-push.js';
13
14
  import { extractJsonFromResponse } from '../../utils/json-extractor.js';
@@ -170,9 +171,11 @@ message, lastAssistantResponse, issueId, verbose
170
171
  }
171
172
  return null;
172
173
  }
173
- // eslint-disable-next-line complexity
174
174
  export const implementIssueCode = async (options, config, checklistContext) => {
175
- const { issueId, verbose, baseBranch = 'main' } = options;
175
+ const { issueId, verbose } = options;
176
+ // Resolve the repo's default branch (handles forks where default is master, etc.).
177
+ // Caller may override via options.baseBranch (e.g. branch-chained PR base).
178
+ const baseBranch = options.baseBranch ?? (await getDefaultBranchForIssue(issueId, verbose));
176
179
  if (verbose) {
177
180
  logInfo(`Starting code implementation for issue ID: ${issueId}`);
178
181
  logInfo(`Base branch: ${baseBranch}`);
@@ -322,7 +325,7 @@ export const implementIssueCode = async (options, config, checklistContext) => {
322
325
  githubToken: githubConfig.token,
323
326
  owner: githubConfig.owner,
324
327
  repo: githubConfig.repo,
325
- baseBranch: 'main',
328
+ baseBranch,
326
329
  verbose,
327
330
  });
328
331
  }
@@ -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);
@@ -6,6 +6,7 @@
6
6
  import { getGitHubConfig } from '../../api/github.js';
7
7
  import { DEFAULT_MODEL } from '../../constants.js';
8
8
  import { getBaseBranchInfo, getBranches, updateBranch, } from '../../services/branches.js';
9
+ import { getDefaultBranchForIssue } from '../../services/repo-config.js';
9
10
  import { getUncommittedFiles, hasUncommittedChanges, prepareCustomBranchGitEnvironmentAsync, preparePhaseGitEnvironmentAsync, syncFeatBranchWithMain, } from '../../utils/git-branch-manager.js';
10
11
  import { gitPushCurrentBranch } from '../../utils/git-push.js';
11
12
  import { logError, logInfo } from '../../utils/logger.js';
@@ -41,18 +42,19 @@ export const MAX_REFINE_ITERATIONS = 10;
41
42
  * Similar to technical-design, this includes an iterative improvement cycle:
42
43
  * refine → verification → improve → re-refine (if needed)
43
44
  */
44
- // eslint-disable-next-line complexity
45
45
  export const refineCodeFromPRFeedback = async (options, config, checklistContext) => {
46
46
  const { issueId, githubToken, verbose } = options;
47
47
  if (verbose) {
48
48
  logInfo(`Starting code refine for issue ID: ${issueId}`);
49
49
  }
50
+ // Resolve the repo's default branch (handles forks where default is master, etc.)
51
+ const defaultBranch = await getDefaultBranchForIssue(issueId, verbose);
50
52
  // For multi-branch issues, find the branch that has been reviewed
51
53
  // (status = 'reviewed' after code-review phase) and use its branch_name for refine
52
54
  let branchName = `dev/${issueId}`; // Default for single-branch issues
53
55
  let currentBranch = null;
54
56
  let allBranches = [];
55
- let baseBranchForRebase = 'main'; // Default base branch for rebase
57
+ let baseBranchForRebase = defaultBranch; // Default base branch for rebase
56
58
  let rebaseTargetBranchForRebase; // Target for --onto rebase (e.g., main)
57
59
  let originalBaseBranchForRebase; // For --onto when base was squash-merged
58
60
  let baseBranchCompletedForRebase = false;
@@ -68,7 +70,7 @@ export const refineCodeFromPRFeedback = async (options, config, checklistContext
68
70
  logInfo(` Using dev branch: ${branchName}`);
69
71
  }
70
72
  // Determine the correct base branch for rebase using base_branch_id
71
- const baseBranchInfo = getBaseBranchInfo(currentBranch, allBranches, 'main');
73
+ const baseBranchInfo = getBaseBranchInfo(currentBranch, allBranches, defaultBranch);
72
74
  // Always use the baseBranch from getBaseBranchInfo - it handles all cases:
73
75
  // - No base branch: returns 'main'
74
76
  // - Base branch merged: returns feat branch (new branches created from feat/xxx)
@@ -114,14 +116,14 @@ export const refineCodeFromPRFeedback = async (options, config, checklistContext
114
116
  githubConfig.repo) {
115
117
  const featBranchName = branchName.replace(/^dev\//, 'feat/');
116
118
  if (verbose) {
117
- logInfo(`📥 Syncing ${featBranchName} with main before rebase...`);
119
+ logInfo(`📥 Syncing ${featBranchName} with ${defaultBranch} before rebase...`);
118
120
  }
119
121
  featSyncedToMain = await syncFeatBranchWithMain({
120
122
  featBranch: featBranchName,
121
123
  githubToken: githubConfig.token,
122
124
  owner: githubConfig.owner,
123
125
  repo: githubConfig.repo,
124
- baseBranch: 'main',
126
+ baseBranch: defaultBranch,
125
127
  verbose,
126
128
  });
127
129
  }
@@ -150,7 +152,7 @@ export const refineCodeFromPRFeedback = async (options, config, checklistContext
150
152
  baseBranchCompleted: baseBranchCompletedForRebase,
151
153
  githubToken,
152
154
  })
153
- : await preparePhaseGitEnvironmentAsync(issueId, 'main', verbose, true, {
155
+ : await preparePhaseGitEnvironmentAsync(issueId, defaultBranch, verbose, true, {
154
156
  model: DEFAULT_MODEL,
155
157
  });
156
158
  const cleanupGit = gitEnvResult.cleanup;
@@ -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;
@@ -10,6 +10,7 @@ import { DEFAULT_MODEL } from '../../constants.js';
10
10
  import { getBaseBranchInfo, getBranches, updateBranch, } from '../../services/branches.js';
11
11
  import { formatChecklistsForContext, } from '../../services/checklist.js';
12
12
  import { formatFeedbacksForContext, getFeedbacksForPhase, } from '../../services/feedbacks.js';
13
+ import { getDefaultBranchForIssue } from '../../services/repo-config.js';
13
14
  import { prepareCustomBranchGitEnvironmentAsync, preparePhaseGitEnvironmentAsync, syncFeatBranchWithMain, } from '../../utils/git-branch-manager.js';
14
15
  import { logError, logInfo } from '../../utils/logger.js';
15
16
  import { createPromptGenerator, extractTextFromContent, tryExtractResult, } from '../pr-shared/agent-utils.js';
@@ -67,18 +68,19 @@ baseBranchInfo, baseBranchForRebase, originalBaseBranchForRebase, verbose) {
67
68
  /**
68
69
  * Main code review function
69
70
  */
70
- // eslint-disable-next-line complexity
71
71
  export const reviewPullRequest = async (options, config, checklistContext) => {
72
72
  const { issueId, githubToken, verbose } = options;
73
73
  if (verbose) {
74
74
  logInfo(`Starting code review for issue ID: ${issueId}`);
75
75
  }
76
+ // Resolve the repo's default branch (handles forks where default is master, etc.)
77
+ const defaultBranch = await getDefaultBranchForIssue(issueId, verbose);
76
78
  // For multi-branch issues, find the branch that is ready for review
77
79
  // and use its branch_name for review (branch_name is stored as dev/...)
78
80
  let branchName = `dev/${issueId}`; // Default for single-branch issues
79
81
  let currentBranch = null;
80
82
  let allBranches = [];
81
- let baseBranchForRebase = 'main'; // Default base branch for rebase
83
+ let baseBranchForRebase = defaultBranch; // Default base branch for rebase
82
84
  let rebaseTargetBranchForRebase; // Target for --onto rebase (e.g., main)
83
85
  let originalBaseBranchForRebase; // For --onto when base was squash-merged
84
86
  let baseBranchCompletedForRebase = false;
@@ -95,7 +97,7 @@ export const reviewPullRequest = async (options, config, checklistContext) => {
95
97
  logInfo(` Using dev branch: ${branchName}`);
96
98
  }
97
99
  // Determine the correct base branch for rebase using base_branch_id
98
- const baseBranchInfo = getBaseBranchInfo(currentBranch, allBranches, 'main');
100
+ const baseBranchInfo = getBaseBranchInfo(currentBranch, allBranches, defaultBranch);
99
101
  // Always use the baseBranch from getBaseBranchInfo - it handles all cases:
100
102
  // - No base branch: returns 'main'
101
103
  // - Base branch merged: returns feat branch (new branches created from feat/xxx)
@@ -141,14 +143,14 @@ export const reviewPullRequest = async (options, config, checklistContext) => {
141
143
  githubConfig.repo) {
142
144
  const featBranchName = branchName.replace(/^dev\//, 'feat/');
143
145
  if (verbose) {
144
- logInfo(`📥 Syncing ${featBranchName} with main before rebase...`);
146
+ logInfo(`📥 Syncing ${featBranchName} with ${defaultBranch} before rebase...`);
145
147
  }
146
148
  featSyncedToMain = await syncFeatBranchWithMain({
147
149
  featBranch: featBranchName,
148
150
  githubToken: githubConfig.token,
149
151
  owner: githubConfig.owner,
150
152
  repo: githubConfig.repo,
151
- baseBranch: 'main',
153
+ baseBranch: defaultBranch,
152
154
  verbose,
153
155
  });
154
156
  }
@@ -177,7 +179,7 @@ export const reviewPullRequest = async (options, config, checklistContext) => {
177
179
  baseBranchCompleted: baseBranchCompletedForRebase,
178
180
  githubToken,
179
181
  })
180
- : await preparePhaseGitEnvironmentAsync(issueId, 'main', verbose, true, {
182
+ : await preparePhaseGitEnvironmentAsync(issueId, defaultBranch, verbose, true, {
181
183
  model: DEFAULT_MODEL,
182
184
  });
183
185
  const cleanupGit = gitEnvResult.cleanup;
@@ -1,5 +1,11 @@
1
1
  import { query } from '@anthropic-ai/claude-agent-sdk';
2
2
  import { DEFAULT_MODEL } from '../../constants.js';
3
+ import { getDefaultBranchForIssue } from '../../services/repo-config.js';
4
+ import { preparePhaseGitEnvironment } from '../../utils/git-branch-manager.js';
5
+ import { extractJsonFromResponse } from '../../utils/json-extractor.js';
6
+ import { logDebug, logError, logInfo } from '../../utils/logger.js';
7
+ import { fetchCodeTestingContext, formatContextForPrompt, } from './context-fetcher.js';
8
+ import { createCodeTestingPromptWithContext, createCodeTestingSystemPrompt, } from './prompts.js';
3
9
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
4
10
  function processAssistantContent(content, verbose) {
5
11
  let text = '';
@@ -14,11 +20,6 @@ function processAssistantContent(content, verbose) {
14
20
  }
15
21
  return text;
16
22
  }
17
- import { preparePhaseGitEnvironment } from '../../utils/git-branch-manager.js';
18
- import { extractJsonFromResponse } from '../../utils/json-extractor.js';
19
- import { logDebug, logError, logInfo } from '../../utils/logger.js';
20
- import { fetchCodeTestingContext, formatContextForPrompt, } from './context-fetcher.js';
21
- import { createCodeTestingPromptWithContext, createCodeTestingSystemPrompt, } from './prompts.js';
22
23
  function userMessage(content) {
23
24
  return {
24
25
  type: 'user',
@@ -31,14 +32,16 @@ async function* prompt(testingPrompt) {
31
32
  setTimeout(res, 10000);
32
33
  });
33
34
  }
34
- // eslint-disable-next-line complexity
35
35
  export const writeCodeTests = async (options, config) => {
36
36
  const { issueId, verbose } = options;
37
37
  if (verbose) {
38
38
  logInfo(`Starting code testing phase for issue ID: ${issueId}`);
39
39
  }
40
- // Prepare git environment: switch to issue branch and rebase with main
41
- const cleanupGit = preparePhaseGitEnvironment(issueId, 'main', verbose);
40
+ // Prepare git environment: switch to issue branch and rebase with the
41
+ // repo's default branch (resolved via GitHub API for consistency with
42
+ // other phases — same source of truth for forks where origin/HEAD may lag).
43
+ const defaultBranch = await getDefaultBranchForIssue(issueId, verbose);
44
+ const cleanupGit = preparePhaseGitEnvironment(issueId, defaultBranch, verbose);
42
45
  try {
43
46
  // Fetch all required context information via MCP endpoints
44
47
  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;