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.
- package/README.md +20 -0
- package/dist/api/financing.d.ts +47 -0
- package/dist/api/financing.js +37 -0
- package/dist/api/issues/approval-checker.d.ts +11 -9
- package/dist/api/issues/approval-checker.js +30 -41
- package/dist/api/issues/status-updater.d.ts +47 -20
- package/dist/api/issues/status-updater.js +114 -46
- package/dist/api/issues/update-issue.d.ts +5 -0
- package/dist/api/issues/update-issue.js +6 -0
- package/dist/commands/agent-workflow/processor.js +5 -1
- package/dist/commands/checklists/index.d.ts +5 -2
- package/dist/commands/checklists/index.js +73 -12
- package/dist/commands/checklists/tools.d.ts +14 -7
- package/dist/commands/checklists/tools.js +15 -208
- package/dist/commands/financing-deck/index.d.ts +8 -0
- package/dist/commands/financing-deck/index.js +66 -0
- package/dist/commands/find-architecture/index.d.ts +13 -0
- package/dist/commands/find-architecture/index.js +41 -0
- package/dist/commands/sync-github-issues/index.d.ts +11 -0
- package/dist/commands/sync-github-issues/index.js +42 -0
- package/dist/commands/sync-sentry-issues/index.d.ts +14 -0
- package/dist/commands/sync-sentry-issues/index.js +73 -0
- package/dist/commands/workflow/executors/phase-executor.js +6 -4
- package/dist/commands/workflow/phase-orchestrator.js +0 -1
- package/dist/config/issue-status.d.ts +18 -45
- package/dist/config/issue-status.js +21 -107
- package/dist/index.js +97 -3
- package/dist/phases/app-store-generation/agent.js +2 -1
- package/dist/phases/app-store-generation/index.js +11 -3
- package/dist/phases/autonomous/index.js +9 -6
- package/dist/phases/branch-planning/index.js +1 -2
- package/dist/phases/branch-planning/prompts.d.ts +1 -1
- package/dist/phases/branch-planning/prompts.js +3 -2
- package/dist/phases/bug-fixing/analyzer.js +6 -3
- package/dist/phases/bug-fixing/mcp-server.d.ts +18 -1
- package/dist/phases/bug-fixing/mcp-server.js +19 -76
- package/dist/phases/chat-processor/product-tools.d.ts +5 -8
- package/dist/phases/chat-processor/product-tools.js +6 -512
- package/dist/phases/chat-processor/tools.d.ts +5 -9
- package/dist/phases/chat-processor/tools.js +6 -704
- package/dist/phases/code-implementation/branch-pr-creator.js +7 -5
- package/dist/phases/code-implementation/index.js +6 -3
- package/dist/phases/code-implementation-verification/agent.js +6 -1
- package/dist/phases/code-refine/index.js +8 -6
- package/dist/phases/code-refine/refine-iteration.js +2 -1
- package/dist/phases/code-review/index.js +8 -6
- package/dist/phases/code-testing/analyzer.js +11 -8
- package/dist/phases/financing-deck/agent.d.ts +1 -0
- package/dist/phases/financing-deck/agent.js +96 -0
- package/dist/phases/financing-deck/context.d.ts +13 -0
- package/dist/phases/financing-deck/context.js +69 -0
- package/dist/phases/financing-deck/index.d.ts +15 -0
- package/dist/phases/financing-deck/index.js +89 -0
- package/dist/phases/financing-deck/prompts.d.ts +2 -0
- package/dist/phases/financing-deck/prompts.js +94 -0
- package/dist/phases/find-architecture/index.d.ts +44 -0
- package/dist/phases/find-architecture/index.js +248 -0
- package/dist/phases/find-architecture/prompts.d.ts +31 -0
- package/dist/phases/find-architecture/prompts.js +128 -0
- package/dist/phases/find-architecture/state.d.ts +21 -0
- package/dist/phases/find-architecture/state.js +17 -0
- package/dist/phases/find-architecture/types.d.ts +55 -0
- package/dist/phases/find-architecture/types.js +69 -0
- package/dist/phases/find-bugs/index.js +13 -4
- package/dist/phases/find-features/index.js +10 -5
- package/dist/phases/find-smells/index.js +10 -3
- package/dist/phases/functional-testing/analyzer.js +27 -17
- package/dist/phases/functional-testing/http-fallback.d.ts +1 -1
- package/dist/phases/functional-testing/http-fallback.js +32 -16
- package/dist/phases/functional-testing/mcp-server.d.ts +9 -1
- package/dist/phases/functional-testing/mcp-server.js +13 -132
- package/dist/phases/growth-analysis/agent.js +2 -2
- package/dist/phases/growth-analysis/index.js +9 -3
- package/dist/phases/intelligence-analysis/agent.js +2 -2
- package/dist/phases/intelligence-analysis/index.js +9 -2
- package/dist/phases/issue-analysis/agent.d.ts +9 -1
- package/dist/phases/issue-analysis/agent.js +68 -27
- package/dist/phases/issue-analysis/context.d.ts +5 -9
- package/dist/phases/issue-analysis/context.js +31 -76
- package/dist/phases/issue-analysis/index.js +32 -84
- package/dist/phases/issue-analysis/outcome.d.ts +3 -33
- package/dist/phases/issue-analysis/outcome.js +15 -253
- package/dist/phases/issue-analysis/prompts.d.ts +3 -5
- package/dist/phases/issue-analysis/prompts.js +45 -158
- package/dist/phases/issue-analysis-verification/agent.d.ts +4 -4
- package/dist/phases/issue-analysis-verification/agent.js +5 -5
- package/dist/phases/issue-analysis-verification/index.d.ts +4 -2
- package/dist/phases/issue-analysis-verification/index.js +9 -22
- package/dist/phases/issue-analysis-verification/prompts.d.ts +1 -2
- package/dist/phases/issue-analysis-verification/prompts.js +21 -46
- package/dist/phases/output-contracts.js +66 -78
- package/dist/phases/pr-execution/context.d.ts +2 -0
- package/dist/phases/pr-execution/context.js +1 -0
- package/dist/phases/pr-execution/index.js +28 -19
- package/dist/phases/pr-execution/prompts.d.ts +2 -1
- package/dist/phases/pr-execution/prompts.js +12 -10
- package/dist/phases/pr-resolve/index.js +2 -8
- package/dist/phases/pr-splitting/index.js +3 -3
- package/dist/phases/pr-splitting/prompts.d.ts +1 -1
- package/dist/phases/pr-splitting/prompts.js +3 -2
- package/dist/phases/pull-request/creator.js +10 -7
- package/dist/phases/pull-request/handler.js +3 -1
- package/dist/phases/release-sync/index.js +52 -43
- package/dist/phases/run-sheet/index.js +2 -1
- package/dist/phases/smoke-test/agent.js +2 -1
- package/dist/phases/smoke-test/index.js +4 -1
- package/dist/phases/sync-github-issues/index.d.ts +41 -0
- package/dist/phases/sync-github-issues/index.js +187 -0
- package/dist/phases/sync-github-issues/state.d.ts +26 -0
- package/dist/phases/sync-github-issues/state.js +18 -0
- package/dist/phases/sync-github-issues/types.d.ts +35 -0
- package/dist/phases/sync-github-issues/types.js +6 -0
- package/dist/phases/sync-sentry-issues/index.d.ts +29 -0
- package/dist/phases/sync-sentry-issues/index.js +153 -0
- package/dist/phases/sync-sentry-issues/sentry-client.d.ts +66 -0
- package/dist/phases/sync-sentry-issues/sentry-client.js +221 -0
- package/dist/phases/sync-sentry-issues/state.d.ts +23 -0
- package/dist/phases/sync-sentry-issues/state.js +18 -0
- package/dist/phases/sync-sentry-issues/types.d.ts +46 -0
- package/dist/phases/sync-sentry-issues/types.js +6 -0
- package/dist/phases/sync-shared/mcp.d.ts +81 -0
- package/dist/phases/sync-shared/mcp.js +111 -0
- package/dist/phases/technical-design/index.js +0 -1
- package/dist/phases/test-cases-analysis/agent.js +2 -1
- package/dist/phases/test-cases-analysis/index.js +0 -1
- package/dist/phases/user-stories-analysis/agent.js +2 -1
- package/dist/phases/user-stories-analysis/index.js +0 -1
- package/dist/services/coaching/coaching-agent.js +29 -4
- package/dist/services/feedbacks.d.ts +1 -1
- package/dist/services/repo-config.d.ts +17 -0
- package/dist/services/repo-config.js +50 -0
- package/dist/skills/phase/issue-analysis/SKILL.md +48 -92
- package/dist/skills/phase/issue-analysis-verification/SKILL.md +46 -31
- package/dist/tools/bootstrap.d.ts +45 -0
- package/dist/tools/bootstrap.js +50 -0
- package/dist/types/external-sources.d.ts +22 -0
- package/dist/types/external-sources.js +23 -0
- package/dist/types/index.d.ts +5 -10
- package/dist/types/issues.d.ts +2 -0
- package/dist/types/llm-responses.d.ts +1 -14
- package/dist/utils/formatters.js +1 -7
- package/dist/utils/git-branch-manager-async.js +9 -5
- package/dist/utils/git-branch-manager.js +17 -7
- package/dist/workspace/workspace-manager.d.ts +10 -0
- package/dist/workspace/workspace-manager.js +22 -1
- package/package.json +6 -2
- package/vitest.config.ts +4 -0
- 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
|
|
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
|
|
80
|
+
// Get the base branch SHA
|
|
79
81
|
const { data: baseBranchData } = await octokit.repos.getBranch({
|
|
80
82
|
owner,
|
|
81
83
|
repo,
|
|
82
|
-
branch:
|
|
84
|
+
branch: resolvedBaseBranch,
|
|
83
85
|
});
|
|
84
|
-
// Create the feat/ branch from
|
|
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 ${
|
|
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
|
|
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
|
|
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(
|
|
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 =
|
|
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,
|
|
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
|
|
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:
|
|
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,
|
|
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
|
-
|
|
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 =
|
|
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,
|
|
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
|
|
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:
|
|
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,
|
|
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
|
|
41
|
-
|
|
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
|
+
}
|