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.
- 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/issue-analysis/index.d.ts +5 -0
- package/dist/commands/issue-analysis/index.js +9 -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/technical-design/index.d.ts +5 -0
- package/dist/commands/technical-design/index.js +9 -0
- package/dist/commands/test-cases-analysis/index.d.ts +5 -0
- package/dist/commands/test-cases-analysis/index.js +9 -0
- package/dist/commands/user-stories-analysis/index.d.ts +5 -0
- package/dist/commands/user-stories-analysis/index.js +9 -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 +176 -4
- package/dist/phases/app-store-generation/agent.js +2 -1
- package/dist/phases/app-store-generation/index.js +11 -3
- package/dist/phases/branch-planning/index.js +0 -1
- package/dist/phases/bug-fixing/analyzer.js +0 -1
- 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/index.js +0 -1
- package/dist/phases/code-implementation-verification/agent.js +6 -1
- package/dist/phases/code-refine/index.js +0 -1
- package/dist/phases/code-refine/refine-iteration.js +2 -1
- package/dist/phases/code-review/index.js +0 -1
- package/dist/phases/code-testing/analyzer.js +0 -1
- 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/index.js +2 -2
- package/dist/phases/pr-resolve/index.js +2 -8
- package/dist/phases/pr-splitting/index.js +2 -2
- 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/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/issue-phase-cli.d.ts +26 -0
- package/dist/utils/issue-phase-cli.js +44 -0
- 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
package/README.md
CHANGED
|
@@ -229,6 +229,26 @@ npx edsger --review --staged
|
|
|
229
229
|
- MCP server for advanced issue operations (analysis, design, implementation, testing)
|
|
230
230
|
- Playwright for functional testing (when using `--test` flag)
|
|
231
231
|
|
|
232
|
+
## Releasing
|
|
233
|
+
|
|
234
|
+
Published from this directory using
|
|
235
|
+
[semantic-release](https://github.com/semantic-release/semantic-release),
|
|
236
|
+
driven by [Conventional Commit](https://www.conventionalcommits.org/)
|
|
237
|
+
messages on `main` (`fix:` → patch, `feat:` → minor, `feat!:` /
|
|
238
|
+
`BREAKING CHANGE:` → major).
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
cd packages/edsger
|
|
242
|
+
npm run release:preview # dry-run, see the next version + notes
|
|
243
|
+
npm run release # publish to npm + push the git tag
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
> **Important:** the `edsger-contract` and `edsger-tools` versions
|
|
247
|
+
> referenced in `package.json` must already be on the npm registry
|
|
248
|
+
> before this CLI can be installed. Always run the Changesets flow for
|
|
249
|
+
> those two packages first — see [`RELEASING.md`](../../RELEASING.md)
|
|
250
|
+
> in the repo root for the full sequence.
|
|
251
|
+
|
|
232
252
|
## License
|
|
233
253
|
|
|
234
254
|
ISC
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin MCP wrappers for the Financing tab. Mirror the shape of the
|
|
3
|
+
* `growth.ts` and `intelligence.ts` siblings.
|
|
4
|
+
*/
|
|
5
|
+
export type FinancingStage = 'pre_seed' | 'seed' | 'series_a' | 'series_b' | 'series_c' | 'growth';
|
|
6
|
+
export interface FinancingDeckSlide {
|
|
7
|
+
slide_id: string;
|
|
8
|
+
title: string;
|
|
9
|
+
body: string;
|
|
10
|
+
ai_rationale?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface FinancingRedFlag {
|
|
13
|
+
metric: string;
|
|
14
|
+
severity: 'low' | 'medium' | 'high';
|
|
15
|
+
explanation: string;
|
|
16
|
+
fix: string;
|
|
17
|
+
}
|
|
18
|
+
export interface FinancingContext {
|
|
19
|
+
product: {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
description: string | null;
|
|
23
|
+
created_at: string | null;
|
|
24
|
+
} | null;
|
|
25
|
+
profile: Record<string, unknown> | null;
|
|
26
|
+
metrics: Record<string, unknown> | null;
|
|
27
|
+
target_stage: FinancingStage | null;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Fetch the founder-supplied financing profile + metrics. The agent is
|
|
31
|
+
* expected to WebSearch current industry benchmarks itself rather than
|
|
32
|
+
* relying on a stale server-side table.
|
|
33
|
+
*/
|
|
34
|
+
export declare function getFinancingContext(productId: string, targetStage: FinancingStage, verbose?: boolean): Promise<FinancingContext>;
|
|
35
|
+
/**
|
|
36
|
+
* Persist a generated pitch deck draft to financing_ai_analyses.
|
|
37
|
+
*/
|
|
38
|
+
export declare function saveFinancingAnalysis(payload: {
|
|
39
|
+
product_id: string;
|
|
40
|
+
target_stage: FinancingStage;
|
|
41
|
+
deck_slides: FinancingDeckSlide[];
|
|
42
|
+
stage_reasoning?: string;
|
|
43
|
+
red_flags?: FinancingRedFlag[];
|
|
44
|
+
source_summary?: Record<string, unknown>;
|
|
45
|
+
}, verbose?: boolean): Promise<{
|
|
46
|
+
analysis_id: string;
|
|
47
|
+
}>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { logError, logInfo } from '../utils/logger.js';
|
|
2
|
+
import { callMcpEndpoint } from './mcp-client.js';
|
|
3
|
+
/**
|
|
4
|
+
* Fetch the founder-supplied financing profile + metrics. The agent is
|
|
5
|
+
* expected to WebSearch current industry benchmarks itself rather than
|
|
6
|
+
* relying on a stale server-side table.
|
|
7
|
+
*/
|
|
8
|
+
export async function getFinancingContext(productId, targetStage, verbose) {
|
|
9
|
+
if (verbose) {
|
|
10
|
+
logInfo(`Fetching financing context for product=${productId} stage=${targetStage}`);
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
const result = (await callMcpEndpoint('financing/get_context', {
|
|
14
|
+
product_id: productId,
|
|
15
|
+
target_stage: targetStage,
|
|
16
|
+
}));
|
|
17
|
+
const text = result.content?.[0]?.text || '{}';
|
|
18
|
+
return JSON.parse(text);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
if (verbose) {
|
|
22
|
+
logError(`Failed to fetch financing context: ${error instanceof Error ? error.message : String(error)}`);
|
|
23
|
+
}
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Persist a generated pitch deck draft to financing_ai_analyses.
|
|
29
|
+
*/
|
|
30
|
+
export async function saveFinancingAnalysis(payload, verbose) {
|
|
31
|
+
if (verbose) {
|
|
32
|
+
logInfo(`Saving financing analysis: ${payload.deck_slides.length} slides, ${(payload.red_flags ?? []).length} red flags`);
|
|
33
|
+
}
|
|
34
|
+
const result = (await callMcpEndpoint('financing/save_analysis', payload));
|
|
35
|
+
const text = result.content?.[0]?.text || '{}';
|
|
36
|
+
return JSON.parse(text);
|
|
37
|
+
}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Approval workflow integration for issue phases
|
|
3
|
-
*
|
|
2
|
+
* Approval workflow integration for issue phases.
|
|
3
|
+
*
|
|
4
|
+
* Under the 2D model, approvals are gated on the PHASE the worker is
|
|
5
|
+
* about to enter (e.g. "approve before technical_design runs"), not on
|
|
6
|
+
* the issue's lifecycle status. The phase-executor passes the target
|
|
7
|
+
* phase name; we look up the approval config for that phase and block
|
|
8
|
+
* execution if pending approval is required.
|
|
4
9
|
*/
|
|
5
10
|
interface ApprovalCheckResult {
|
|
6
11
|
canProceed: boolean;
|
|
@@ -9,12 +14,9 @@ interface ApprovalCheckResult {
|
|
|
9
14
|
message?: string;
|
|
10
15
|
}
|
|
11
16
|
/**
|
|
12
|
-
* Check if
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* @param issueId - Issue ID
|
|
16
|
-
* @param verbose - Verbose logging
|
|
17
|
-
* @returns Promise<ApprovalCheckResult> - Whether the phase can proceed
|
|
17
|
+
* Check if a phase requires approval before the worker enters it.
|
|
18
|
+
* Called by phase-executor with the snake_case phase name (e.g.
|
|
19
|
+
* 'technical_design').
|
|
18
20
|
*/
|
|
19
|
-
export declare function checkApprovalBeforePhaseExecution(issueId: string, verbose?: boolean): Promise<ApprovalCheckResult>;
|
|
21
|
+
export declare function checkApprovalBeforePhaseExecution(issueId: string, phase: string, verbose?: boolean): Promise<ApprovalCheckResult>;
|
|
20
22
|
export {};
|
|
@@ -1,32 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Approval workflow integration for issue phases
|
|
3
|
-
*
|
|
2
|
+
* Approval workflow integration for issue phases.
|
|
3
|
+
*
|
|
4
|
+
* Under the 2D model, approvals are gated on the PHASE the worker is
|
|
5
|
+
* about to enter (e.g. "approve before technical_design runs"), not on
|
|
6
|
+
* the issue's lifecycle status. The phase-executor passes the target
|
|
7
|
+
* phase name; we look up the approval config for that phase and block
|
|
8
|
+
* execution if pending approval is required.
|
|
4
9
|
*/
|
|
5
10
|
import { logError, logInfo, logWarning } from '../../utils/logger.js';
|
|
6
11
|
import { callMcpEndpoint } from '../mcp-client.js';
|
|
7
12
|
import { getIssue } from './get-issue.js';
|
|
8
13
|
/**
|
|
9
|
-
* Check if
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* @param issueId - Issue ID
|
|
13
|
-
* @param verbose - Verbose logging
|
|
14
|
-
* @returns Promise<ApprovalCheckResult> - Whether the phase can proceed
|
|
14
|
+
* Check if a phase requires approval before the worker enters it.
|
|
15
|
+
* Called by phase-executor with the snake_case phase name (e.g.
|
|
16
|
+
* 'technical_design').
|
|
15
17
|
*/
|
|
16
|
-
export async function checkApprovalBeforePhaseExecution(issueId, verbose = false) {
|
|
18
|
+
export async function checkApprovalBeforePhaseExecution(issueId, phase, verbose = false) {
|
|
17
19
|
try {
|
|
18
|
-
// 1. Get current issue to check its status and product_id
|
|
19
20
|
const issue = await getIssue(issueId, verbose);
|
|
20
|
-
const currentStatus = issue.status;
|
|
21
21
|
const productId = issue.product_id;
|
|
22
22
|
if (verbose) {
|
|
23
|
-
logInfo(`🔍 Checking approval for
|
|
23
|
+
logInfo(`🔍 Checking approval for phase: ${phase}`);
|
|
24
24
|
logInfo(`📦 Product ID: ${productId}`);
|
|
25
25
|
}
|
|
26
|
-
// 2. Check if current status requires approval
|
|
27
26
|
const requiresApprovalResult = (await callMcpEndpoint('approvals/requires_approval', {
|
|
28
27
|
product_id: productId,
|
|
29
|
-
issue_status:
|
|
28
|
+
issue_status: phase,
|
|
30
29
|
}));
|
|
31
30
|
if (verbose) {
|
|
32
31
|
logInfo(`📋 MCP requires_approval response: ${JSON.stringify(requiresApprovalResult)}`);
|
|
@@ -34,26 +33,24 @@ export async function checkApprovalBeforePhaseExecution(issueId, verbose = false
|
|
|
34
33
|
const requiresApproval = requiresApprovalResult?.requires_approval === true;
|
|
35
34
|
if (!requiresApproval) {
|
|
36
35
|
if (verbose) {
|
|
37
|
-
logInfo(`✅
|
|
36
|
+
logInfo(`✅ Phase ${phase} does not require approval. Proceeding.`);
|
|
38
37
|
}
|
|
39
38
|
return {
|
|
40
39
|
canProceed: true,
|
|
41
40
|
requiresApproval: false,
|
|
42
41
|
};
|
|
43
42
|
}
|
|
44
|
-
// 3. Status requires approval - check if already approved
|
|
45
43
|
if (verbose) {
|
|
46
|
-
logInfo(`🔒
|
|
44
|
+
logInfo(`🔒 Phase ${phase} requires approval`);
|
|
47
45
|
}
|
|
48
46
|
const existingApprovals = (await callMcpEndpoint('approvals/issue_approvals', {
|
|
49
47
|
issue_id: issueId,
|
|
50
48
|
}));
|
|
51
|
-
|
|
52
|
-
const approvedApproval = existingApprovals?.approvals?.find((approval) => approval.requested_status === currentStatus &&
|
|
49
|
+
const approvedApproval = existingApprovals?.approvals?.find((approval) => approval.requested_status === phase &&
|
|
53
50
|
approval.approval_status === 'approved');
|
|
54
51
|
if (approvedApproval) {
|
|
55
52
|
if (verbose) {
|
|
56
|
-
logInfo(`✅ Found approved approval for ${
|
|
53
|
+
logInfo(`✅ Found approved approval for ${phase}. Proceeding.`);
|
|
57
54
|
}
|
|
58
55
|
return {
|
|
59
56
|
canProceed: true,
|
|
@@ -61,39 +58,36 @@ export async function checkApprovalBeforePhaseExecution(issueId, verbose = false
|
|
|
61
58
|
approvalId: approvedApproval.id,
|
|
62
59
|
};
|
|
63
60
|
}
|
|
64
|
-
|
|
65
|
-
const pendingApproval = existingApprovals?.approvals?.find((approval) => approval.requested_status === currentStatus &&
|
|
61
|
+
const pendingApproval = existingApprovals?.approvals?.find((approval) => approval.requested_status === phase &&
|
|
66
62
|
approval.approval_status === 'pending');
|
|
67
63
|
if (pendingApproval) {
|
|
68
64
|
if (verbose) {
|
|
69
|
-
logWarning(`⏳ Approval already pending for ${
|
|
65
|
+
logWarning(`⏳ Approval already pending for ${phase}. Waiting.`);
|
|
70
66
|
}
|
|
71
67
|
return {
|
|
72
68
|
canProceed: false,
|
|
73
69
|
requiresApproval: true,
|
|
74
70
|
approvalId: pendingApproval.id,
|
|
75
|
-
message: `Waiting for approval of
|
|
71
|
+
message: `Waiting for approval of phase: ${phase}`,
|
|
76
72
|
};
|
|
77
73
|
}
|
|
78
|
-
// No pending or approved approval - need to create one
|
|
79
74
|
if (verbose) {
|
|
80
|
-
logInfo(`🔔 No approval exists for ${
|
|
75
|
+
logInfo(`🔔 No approval exists for ${phase}. Creating approval request...`);
|
|
81
76
|
}
|
|
82
|
-
|
|
83
|
-
const approvalId = await createApprovalRequestWithEmail(issueId, productId, currentStatus, verbose);
|
|
77
|
+
const approvalId = await createApprovalRequestWithEmail(issueId, productId, phase, verbose);
|
|
84
78
|
if (approvalId) {
|
|
85
79
|
return {
|
|
86
80
|
canProceed: false,
|
|
87
81
|
requiresApproval: true,
|
|
88
82
|
approvalId,
|
|
89
|
-
message: `Approval request created for
|
|
83
|
+
message: `Approval request created for phase: ${phase}`,
|
|
90
84
|
};
|
|
91
85
|
}
|
|
92
86
|
logError('Failed to create approval request');
|
|
93
87
|
return {
|
|
94
88
|
canProceed: false,
|
|
95
89
|
requiresApproval: true,
|
|
96
|
-
message: `Failed to create approval request for
|
|
90
|
+
message: `Failed to create approval request for phase: ${phase}`,
|
|
97
91
|
};
|
|
98
92
|
}
|
|
99
93
|
catch (error) {
|
|
@@ -108,22 +102,18 @@ export async function checkApprovalBeforePhaseExecution(issueId, verbose = false
|
|
|
108
102
|
}
|
|
109
103
|
}
|
|
110
104
|
/**
|
|
111
|
-
* Create an approval request
|
|
112
|
-
*
|
|
105
|
+
* Create an approval request for a phase. The approval is asking:
|
|
106
|
+
* "Can we proceed into this phase?"
|
|
113
107
|
*/
|
|
114
|
-
async function createApprovalRequestWithEmail(issueId, productId,
|
|
108
|
+
async function createApprovalRequestWithEmail(issueId, productId, phase, verbose = false) {
|
|
115
109
|
try {
|
|
116
110
|
if (verbose) {
|
|
117
|
-
logInfo(`Creating approval request for
|
|
111
|
+
logInfo(`Creating approval request for phase: ${phase}`);
|
|
118
112
|
}
|
|
119
|
-
// Create approval request via MCP endpoint
|
|
120
|
-
// Note: We're requesting approval for the current status
|
|
121
|
-
// The approval is asking: "Can we proceed from this status?"
|
|
122
|
-
// Email notifications are sent automatically by the MCP handler
|
|
123
113
|
const result = (await callMcpEndpoint('approvals/create', {
|
|
124
114
|
issue_id: issueId,
|
|
125
|
-
requested_status:
|
|
126
|
-
previous_status: null,
|
|
115
|
+
requested_status: phase,
|
|
116
|
+
previous_status: null,
|
|
127
117
|
}));
|
|
128
118
|
const approvalId = result?.approval_id;
|
|
129
119
|
if (!approvalId) {
|
|
@@ -132,7 +122,6 @@ async function createApprovalRequestWithEmail(issueId, productId, currentStatus,
|
|
|
132
122
|
}
|
|
133
123
|
if (verbose) {
|
|
134
124
|
logInfo(`✅ Approval request created: ${approvalId}`);
|
|
135
|
-
// Log email results if available
|
|
136
125
|
if (result?.emails_sent) {
|
|
137
126
|
const sentCount = result.emails_sent.filter((e) => e.status === 'sent').length;
|
|
138
127
|
const failedCount = result.emails_sent.filter((e) => e.status !== 'sent').length;
|
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Worker-side phase progress writer.
|
|
3
|
+
*
|
|
4
|
+
* Under the 2D state model, phase progress lives in the workflow JSONB
|
|
5
|
+
* column, not in issues.status. The lifecycle column changes only at:
|
|
6
|
+
* - claim time (ready_for_ai → assigned_to_ai, via claim_next_ready_issue)
|
|
7
|
+
* - first phase entry (assigned_to_ai → in_progress, automatic in enter_phase)
|
|
8
|
+
* - terminal completion (handled by orchestrator after all phases done)
|
|
9
|
+
*
|
|
10
|
+
* For each phase transition, we call the enter_phase RPC which atomically
|
|
11
|
+
* closes any prior active phase and sets the target phase to running or
|
|
12
|
+
* verifying. For non-active terminal states (completed/failed/skipped on a
|
|
13
|
+
* specific phase, e.g. functional-testing setting itself to completed/failed
|
|
14
|
+
* when the test result arrives), we call set_phase_state.
|
|
15
|
+
*
|
|
16
|
+
* Public exports:
|
|
17
|
+
* enterPhase(issueId, phase, state) — primary write path
|
|
18
|
+
* setPhaseState(issueId, phase, state) — terminal phase outcomes
|
|
19
|
+
* updateIssueStatus({issueId, status}) — direct lifecycle changes
|
|
20
|
+
* updateIssueStatusForPhase(issueId, phaseName, verbose)
|
|
21
|
+
* Stable signature for the orchestrator's 9 call sites; internally
|
|
22
|
+
* dispatches to enterPhase or setPhaseState based on the phase name.
|
|
4
23
|
*/
|
|
5
24
|
import type { IssueStatus } from '../../types/index.js';
|
|
6
25
|
interface StatusUpdateOptions {
|
|
@@ -9,33 +28,41 @@ interface StatusUpdateOptions {
|
|
|
9
28
|
readonly verbose?: boolean;
|
|
10
29
|
}
|
|
11
30
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* Special cases for archived status:
|
|
15
|
-
* - Any status → archived: Always allowed (archiving from any state)
|
|
16
|
-
* - Archived → backlog: Always allowed (unarchiving restores to backlog)
|
|
31
|
+
* Forward-progression check for the legacy status enum. Retained for
|
|
32
|
+
* lifecycle-only updates; phase progress no longer flows through here.
|
|
17
33
|
*/
|
|
18
34
|
export declare function isForwardProgression(currentStatus: IssueStatus, newStatus: IssueStatus): boolean;
|
|
19
35
|
/**
|
|
20
|
-
*
|
|
36
|
+
* Direct lifecycle status update. Phase-driven callers should use
|
|
37
|
+
* `enterPhase` / `setPhaseState` instead — this is for true lifecycle
|
|
38
|
+
* writes only (e.g. shipping, archiving, failing terminally).
|
|
21
39
|
*/
|
|
22
40
|
export declare function updateIssueStatus({ issueId, status, verbose, }: StatusUpdateOptions): Promise<boolean>;
|
|
23
41
|
/**
|
|
24
|
-
*
|
|
42
|
+
* Atomic phase transition. Closes any prior running/verifying phase and
|
|
43
|
+
* marks the target phase as running or verifying. Bumps issues.status to
|
|
44
|
+
* in_progress automatically when needed. Use this for active phase entry.
|
|
45
|
+
*
|
|
46
|
+
* Throws on RPC failure so callers can react (retry, escalate, abort).
|
|
47
|
+
* Old boolean-returning behavior is preserved by updateIssueStatusForPhase
|
|
48
|
+
* which wraps this call.
|
|
25
49
|
*/
|
|
26
|
-
export declare
|
|
50
|
+
export declare function enterPhase(issueId: string, phase: string, state: 'running' | 'verifying', verbose?: boolean): Promise<void>;
|
|
27
51
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
|
|
52
|
+
* Atomic phase state update — typically used to mark a phase as completed,
|
|
53
|
+
* failed, or skipped without affecting other phases or issues.status.
|
|
54
|
+
* For example, functional-testing/analyzer uses this to record
|
|
55
|
+
* `functional_testing → completed` (testing passed) or
|
|
56
|
+
* `functional_testing → failed` (testing failed).
|
|
57
|
+
*/
|
|
58
|
+
export declare function setPhaseState(issueId: string, phase: string, state: 'pending' | 'running' | 'verifying' | 'completed' | 'failed' | 'skipped', verbose?: boolean): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Update issue progress for a pipeline phase.
|
|
34
61
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
62
|
+
* Signature is preserved (issueId, phase, verbose) so all 9+ orchestrator
|
|
63
|
+
* call sites continue to work without modification. The internal
|
|
64
|
+
* implementation has been rewritten to call enter_phase, which writes to
|
|
65
|
+
* workflow JSONB instead of issues.status.
|
|
39
66
|
*/
|
|
40
67
|
export declare const updateIssueStatusForPhase: (issueId: string, phase: string, verbose?: boolean) => Promise<boolean>;
|
|
41
68
|
export {};
|
|
@@ -1,41 +1,83 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Worker-side phase progress writer.
|
|
3
|
+
*
|
|
4
|
+
* Under the 2D state model, phase progress lives in the workflow JSONB
|
|
5
|
+
* column, not in issues.status. The lifecycle column changes only at:
|
|
6
|
+
* - claim time (ready_for_ai → assigned_to_ai, via claim_next_ready_issue)
|
|
7
|
+
* - first phase entry (assigned_to_ai → in_progress, automatic in enter_phase)
|
|
8
|
+
* - terminal completion (handled by orchestrator after all phases done)
|
|
9
|
+
*
|
|
10
|
+
* For each phase transition, we call the enter_phase RPC which atomically
|
|
11
|
+
* closes any prior active phase and sets the target phase to running or
|
|
12
|
+
* verifying. For non-active terminal states (completed/failed/skipped on a
|
|
13
|
+
* specific phase, e.g. functional-testing setting itself to completed/failed
|
|
14
|
+
* when the test result arrives), we call set_phase_state.
|
|
15
|
+
*
|
|
16
|
+
* Public exports:
|
|
17
|
+
* enterPhase(issueId, phase, state) — primary write path
|
|
18
|
+
* setPhaseState(issueId, phase, state) — terminal phase outcomes
|
|
19
|
+
* updateIssueStatus({issueId, status}) — direct lifecycle changes
|
|
20
|
+
* updateIssueStatusForPhase(issueId, phaseName, verbose)
|
|
21
|
+
* Stable signature for the orchestrator's 9 call sites; internally
|
|
22
|
+
* dispatches to enterPhase or setPhaseState based on the phase name.
|
|
4
23
|
*/
|
|
5
|
-
import {
|
|
24
|
+
import { STATUS_PROGRESSION_ORDER } from '../../config/issue-status.js';
|
|
6
25
|
import { logError, logInfo } from '../../utils/logger.js';
|
|
7
26
|
import { callMcpEndpoint } from '../mcp-client.js';
|
|
8
27
|
import { getIssue } from './get-issue.js';
|
|
28
|
+
const PHASE_NAME_TO_WORKFLOW = {
|
|
29
|
+
// Active phase transitions (call enter_phase)
|
|
30
|
+
'issue-analysis': { kind: 'enter', phase: 'issue_analysis', state: 'running' },
|
|
31
|
+
'issue-analysis-verification': { kind: 'enter', phase: 'issue_analysis', state: 'verifying' },
|
|
32
|
+
'user-stories-analysis': { kind: 'enter', phase: 'user_stories_analysis', state: 'running' },
|
|
33
|
+
'user-stories-analysis-verification': { kind: 'enter', phase: 'user_stories_analysis', state: 'verifying' },
|
|
34
|
+
'test-cases-analysis': { kind: 'enter', phase: 'test_cases_analysis', state: 'running' },
|
|
35
|
+
'test-cases-analysis-verification': { kind: 'enter', phase: 'test_cases_analysis', state: 'verifying' },
|
|
36
|
+
'technical-design': { kind: 'enter', phase: 'technical_design', state: 'running' },
|
|
37
|
+
'technical-design-verification': { kind: 'enter', phase: 'technical_design', state: 'verifying' },
|
|
38
|
+
'branch-planning': { kind: 'enter', phase: 'branch_planning', state: 'running' },
|
|
39
|
+
'branch-planning-verification': { kind: 'enter', phase: 'branch_planning', state: 'verifying' },
|
|
40
|
+
'code-implementation': { kind: 'enter', phase: 'code_implementation', state: 'running' },
|
|
41
|
+
'code-implementation-verification': { kind: 'enter', phase: 'code_implementation', state: 'verifying' },
|
|
42
|
+
'pr-splitting': { kind: 'enter', phase: 'pr_splitting', state: 'running' },
|
|
43
|
+
'pr-splitting-verification': { kind: 'enter', phase: 'pr_splitting', state: 'verifying' },
|
|
44
|
+
'pr-execution': { kind: 'enter', phase: 'pr_execution', state: 'running' },
|
|
45
|
+
'functional-testing': { kind: 'enter', phase: 'functional_testing', state: 'running' },
|
|
46
|
+
'code-review': { kind: 'enter', phase: 'code_review', state: 'running' },
|
|
47
|
+
'ready-for-review': { kind: 'enter', phase: 'code_review', state: 'verifying' },
|
|
48
|
+
'code-refine': { kind: 'enter', phase: 'code_refine', state: 'running' },
|
|
49
|
+
'code-refine-verification': { kind: 'enter', phase: 'code_refine', state: 'verifying' },
|
|
50
|
+
'bug-fixing': { kind: 'enter', phase: 'bug_fixing', state: 'running' },
|
|
51
|
+
// Terminal phase outcomes (call set_phase_state)
|
|
52
|
+
'testing-passed': { kind: 'terminal', phase: 'functional_testing', state: 'completed' },
|
|
53
|
+
'testing-failed': { kind: 'terminal', phase: 'functional_testing', state: 'failed' },
|
|
54
|
+
'testing-in-progress': { kind: 'enter', phase: 'functional_testing', state: 'running' },
|
|
55
|
+
};
|
|
9
56
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* Special cases for archived status:
|
|
13
|
-
* - Any status → archived: Always allowed (archiving from any state)
|
|
14
|
-
* - Archived → backlog: Always allowed (unarchiving restores to backlog)
|
|
57
|
+
* Forward-progression check for the legacy status enum. Retained for
|
|
58
|
+
* lifecycle-only updates; phase progress no longer flows through here.
|
|
15
59
|
*/
|
|
16
60
|
export function isForwardProgression(currentStatus, newStatus) {
|
|
17
|
-
// Any status can transition to archived
|
|
18
61
|
if (newStatus === 'archived') {
|
|
19
62
|
return true;
|
|
20
63
|
}
|
|
21
|
-
// Archived can only transition back to backlog (unarchive)
|
|
22
64
|
if (currentStatus === 'archived') {
|
|
23
65
|
return newStatus === 'backlog';
|
|
24
66
|
}
|
|
25
67
|
const currentIndex = STATUS_PROGRESSION_ORDER.indexOf(currentStatus);
|
|
26
68
|
const newIndex = STATUS_PROGRESSION_ORDER.indexOf(newStatus);
|
|
27
|
-
// Allow moving forward or staying at same level (for retries, etc.)
|
|
28
69
|
return newIndex >= currentIndex;
|
|
29
70
|
}
|
|
30
71
|
/**
|
|
31
|
-
*
|
|
72
|
+
* Direct lifecycle status update. Phase-driven callers should use
|
|
73
|
+
* `enterPhase` / `setPhaseState` instead — this is for true lifecycle
|
|
74
|
+
* writes only (e.g. shipping, archiving, failing terminally).
|
|
32
75
|
*/
|
|
33
76
|
export async function updateIssueStatus({ issueId, status, verbose = false, }) {
|
|
34
77
|
try {
|
|
35
78
|
if (verbose) {
|
|
36
79
|
logInfo(`Updating issue ${issueId} status to: ${status}`);
|
|
37
80
|
}
|
|
38
|
-
// Get current issue status to check for regression
|
|
39
81
|
let issue;
|
|
40
82
|
let currentStatus;
|
|
41
83
|
try {
|
|
@@ -45,14 +87,11 @@ export async function updateIssueStatus({ issueId, status, verbose = false, }) {
|
|
|
45
87
|
catch (error) {
|
|
46
88
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
47
89
|
logError(`Failed to get current issue status for ${issueId}: ${errorMessage}`);
|
|
48
|
-
// If we can't get the current status, we can't safely check for regression
|
|
49
|
-
// so we'll skip the update to prevent potential regression
|
|
50
90
|
if (verbose) {
|
|
51
91
|
logInfo('⚠️ Skipping status update due to inability to verify current status');
|
|
52
92
|
}
|
|
53
93
|
return false;
|
|
54
94
|
}
|
|
55
|
-
// Check if this would be a regression
|
|
56
95
|
if (!isForwardProgression(currentStatus, status)) {
|
|
57
96
|
const message = `⚠️ Skipping status regression: ${currentStatus} → ${status}. Only forward progression allowed.`;
|
|
58
97
|
if (verbose) {
|
|
@@ -60,18 +99,13 @@ export async function updateIssueStatus({ issueId, status, verbose = false, }) {
|
|
|
60
99
|
}
|
|
61
100
|
return false;
|
|
62
101
|
}
|
|
63
|
-
// Only proceed if status actually changed
|
|
64
102
|
if (currentStatus === status) {
|
|
65
103
|
if (verbose) {
|
|
66
104
|
logInfo(`Status already at ${status}, no update needed`);
|
|
67
105
|
}
|
|
68
106
|
return true;
|
|
69
107
|
}
|
|
70
|
-
|
|
71
|
-
await callMcpEndpoint('issues/update', {
|
|
72
|
-
issue_id: issueId,
|
|
73
|
-
status,
|
|
74
|
-
});
|
|
108
|
+
await callMcpEndpoint('issues/update', { issue_id: issueId, status });
|
|
75
109
|
if (verbose) {
|
|
76
110
|
logInfo(`✅ Issue status updated successfully from ${currentStatus} to: ${status}`);
|
|
77
111
|
}
|
|
@@ -86,37 +120,71 @@ export async function updateIssueStatus({ issueId, status, verbose = false, }) {
|
|
|
86
120
|
}
|
|
87
121
|
}
|
|
88
122
|
/**
|
|
89
|
-
*
|
|
123
|
+
* Atomic phase transition. Closes any prior running/verifying phase and
|
|
124
|
+
* marks the target phase as running or verifying. Bumps issues.status to
|
|
125
|
+
* in_progress automatically when needed. Use this for active phase entry.
|
|
126
|
+
*
|
|
127
|
+
* Throws on RPC failure so callers can react (retry, escalate, abort).
|
|
128
|
+
* Old boolean-returning behavior is preserved by updateIssueStatusForPhase
|
|
129
|
+
* which wraps this call.
|
|
90
130
|
*/
|
|
91
|
-
export
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
131
|
+
export async function enterPhase(issueId, phase, state, verbose = false) {
|
|
132
|
+
await callMcpEndpoint('issues/enter_phase', {
|
|
133
|
+
issue_id: issueId,
|
|
134
|
+
phase,
|
|
135
|
+
state,
|
|
136
|
+
});
|
|
137
|
+
if (verbose) {
|
|
138
|
+
logInfo(`✅ entered phase ${phase} (${state})`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
95
141
|
/**
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
|
|
142
|
+
* Atomic phase state update — typically used to mark a phase as completed,
|
|
143
|
+
* failed, or skipped without affecting other phases or issues.status.
|
|
144
|
+
* For example, functional-testing/analyzer uses this to record
|
|
145
|
+
* `functional_testing → completed` (testing passed) or
|
|
146
|
+
* `functional_testing → failed` (testing failed).
|
|
147
|
+
*/
|
|
148
|
+
export async function setPhaseState(issueId, phase, state, verbose = false) {
|
|
149
|
+
await callMcpEndpoint('issues/set_phase_state', {
|
|
150
|
+
issue_id: issueId,
|
|
151
|
+
phase,
|
|
152
|
+
state,
|
|
153
|
+
});
|
|
154
|
+
if (verbose) {
|
|
155
|
+
logInfo(`✅ set phase ${phase} → ${state}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Update issue progress for a pipeline phase.
|
|
102
160
|
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
161
|
+
* Signature is preserved (issueId, phase, verbose) so all 9+ orchestrator
|
|
162
|
+
* call sites continue to work without modification. The internal
|
|
163
|
+
* implementation has been rewritten to call enter_phase, which writes to
|
|
164
|
+
* workflow JSONB instead of issues.status.
|
|
107
165
|
*/
|
|
108
166
|
export const updateIssueStatusForPhase = async (issueId, phase, verbose) => {
|
|
109
|
-
const
|
|
110
|
-
if (!
|
|
111
|
-
const message = `⚠️ Unknown phase '${phase}' - skipping status update to prevent regression`;
|
|
167
|
+
const mapping = PHASE_NAME_TO_WORKFLOW[phase];
|
|
168
|
+
if (!mapping) {
|
|
112
169
|
if (verbose) {
|
|
113
|
-
logInfo(
|
|
170
|
+
logInfo(`⚠️ Unknown phase '${phase}' — skipping status update`);
|
|
171
|
+
}
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
if (mapping.kind === 'enter') {
|
|
176
|
+
await enterPhase(issueId, mapping.phase, mapping.state, verbose);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
await setPhaseState(issueId, mapping.phase, mapping.state, verbose);
|
|
180
|
+
}
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
185
|
+
if (verbose) {
|
|
186
|
+
logError(`Failed to ${mapping.kind === 'enter' ? 'enter' : 'set'} phase ${mapping.phase} (${mapping.state}): ${message}`);
|
|
114
187
|
}
|
|
115
188
|
return false;
|
|
116
189
|
}
|
|
117
|
-
return updateIssueStatus({
|
|
118
|
-
issueId,
|
|
119
|
-
status,
|
|
120
|
-
verbose,
|
|
121
|
-
});
|
|
122
190
|
};
|
|
@@ -4,6 +4,7 @@ import { type IssueWorkflow } from '../../types/pipeline.js';
|
|
|
4
4
|
*/
|
|
5
5
|
export declare function updateIssue(issueId: string, updates: {
|
|
6
6
|
technical_design?: string;
|
|
7
|
+
execution_plan?: string;
|
|
7
8
|
status?: string;
|
|
8
9
|
execution_mode?: string;
|
|
9
10
|
workflow?: IssueWorkflow;
|
|
@@ -12,6 +13,10 @@ export declare function updateIssue(issueId: string, updates: {
|
|
|
12
13
|
* Update technical design for an issue
|
|
13
14
|
*/
|
|
14
15
|
export declare function updateTechnicalDesign(issueId: string, technicalDesign: string, verbose?: boolean): Promise<boolean>;
|
|
16
|
+
/**
|
|
17
|
+
* Update execution plan for an issue
|
|
18
|
+
*/
|
|
19
|
+
export declare function updateExecutionPlan(issueId: string, executionPlan: string, verbose?: boolean): Promise<boolean>;
|
|
15
20
|
/**
|
|
16
21
|
* Mark a workflow phase as completed
|
|
17
22
|
* Fetches current workflow, updates the phase status, and saves back
|