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
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI command: `edsger sync-sentry-issues <productId>`
|
|
3
|
+
*
|
|
4
|
+
* Reads Sentry credentials from process.env (populated by `edsger config
|
|
5
|
+
* set` via ~/.edsger/env.json or by real environment variables). For an
|
|
6
|
+
* MVP we intentionally don't put per-product Sentry config in the
|
|
7
|
+
* database — the auth token is user-scoped, and org/project rarely
|
|
8
|
+
* change. If the friction shows up, follow up by adding columns on the
|
|
9
|
+
* products table and a settings UI.
|
|
10
|
+
*/
|
|
11
|
+
import { syncSentryIssues } from '../../phases/sync-sentry-issues/index.js';
|
|
12
|
+
import { logError, logInfo, logSuccess } from '../../utils/logger.js';
|
|
13
|
+
/**
|
|
14
|
+
* Pull Sentry creds from env. Returns a list of missing variable names so
|
|
15
|
+
* the CLI can give one clear "set these" message instead of dribbling
|
|
16
|
+
* errors.
|
|
17
|
+
*/
|
|
18
|
+
function readSentryEnv() {
|
|
19
|
+
const authToken = process.env.SENTRY_AUTH_TOKEN ?? '';
|
|
20
|
+
const org = process.env.SENTRY_ORG ?? '';
|
|
21
|
+
const project = process.env.SENTRY_PROJECT ?? '';
|
|
22
|
+
// SENTRY_URL is the official sentry-cli convention. SENTRY_BASE_URL is
|
|
23
|
+
// the older name we shipped first; accept both with the new one winning
|
|
24
|
+
// so users with the old desktop install + new CLI keep working.
|
|
25
|
+
const url = process.env.SENTRY_URL || process.env.SENTRY_BASE_URL || undefined;
|
|
26
|
+
const missing = [];
|
|
27
|
+
if (!authToken) {
|
|
28
|
+
missing.push('SENTRY_AUTH_TOKEN');
|
|
29
|
+
}
|
|
30
|
+
if (!org) {
|
|
31
|
+
missing.push('SENTRY_ORG');
|
|
32
|
+
}
|
|
33
|
+
if (!project) {
|
|
34
|
+
missing.push('SENTRY_PROJECT');
|
|
35
|
+
}
|
|
36
|
+
if (missing.length > 0) {
|
|
37
|
+
return { ok: false, missing };
|
|
38
|
+
}
|
|
39
|
+
return { ok: true, env: { authToken, org, project, baseUrl: url } };
|
|
40
|
+
}
|
|
41
|
+
export async function runSyncSentryIssues(productId, options = {}) {
|
|
42
|
+
const { verbose } = options;
|
|
43
|
+
logInfo(`Starting Sentry issue sync for product ${productId}`);
|
|
44
|
+
const env = readSentryEnv();
|
|
45
|
+
if (!env.ok) {
|
|
46
|
+
logError(`Sentry not configured. Missing: ${env.missing.join(', ')}. ` +
|
|
47
|
+
`Configure in the desktop app (Product Settings → Credentials) or ` +
|
|
48
|
+
`set them with \`edsger config set <KEY>=<VALUE>\`. ` +
|
|
49
|
+
`Optional: SENTRY_URL for self-hosted Sentry.`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
const result = await syncSentryIssues({
|
|
53
|
+
productId,
|
|
54
|
+
authToken: env.env.authToken,
|
|
55
|
+
org: env.env.org,
|
|
56
|
+
project: env.env.project,
|
|
57
|
+
baseUrl: env.env.baseUrl,
|
|
58
|
+
verbose,
|
|
59
|
+
});
|
|
60
|
+
if (result.status === 'success') {
|
|
61
|
+
logSuccess(`Sentry sync completed: ${result.message}`);
|
|
62
|
+
if (result.source) {
|
|
63
|
+
logInfo(`Source: ${result.source}`);
|
|
64
|
+
}
|
|
65
|
+
if (result.fetchedCount !== undefined) {
|
|
66
|
+
logInfo(`Fetched ${result.fetchedCount} · created ${result.createdCount ?? 0} · updated ${result.updatedCount ?? 0} · refreshed ${result.refreshedCount ?? 0} · deleted ${result.deletedCount ?? 0}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
logError(`Sentry sync failed: ${result.message}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { generateTechnicalDesign } from '../../phases/technical-design/index.js';
|
|
2
|
+
import { runIssuePhaseCli } from '../../utils/issue-phase-cli.js';
|
|
3
|
+
export const runTechnicalDesignCommand = (options) => runIssuePhaseCli({
|
|
4
|
+
command: 'technical-design',
|
|
5
|
+
description: 'Technical design',
|
|
6
|
+
issueId: options.issueId,
|
|
7
|
+
verbose: options.verbose,
|
|
8
|
+
execute: generateTechnicalDesign,
|
|
9
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { analyseTestCases } from '../../phases/test-cases-analysis/index.js';
|
|
2
|
+
import { runIssuePhaseCli } from '../../utils/issue-phase-cli.js';
|
|
3
|
+
export const runTestCasesAnalysisCommand = (options) => runIssuePhaseCli({
|
|
4
|
+
command: 'test-cases-analysis',
|
|
5
|
+
description: 'Test-cases analysis',
|
|
6
|
+
issueId: options.issueId,
|
|
7
|
+
verbose: options.verbose,
|
|
8
|
+
execute: analyseTestCases,
|
|
9
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { analyseUserStories } from '../../phases/user-stories-analysis/index.js';
|
|
2
|
+
import { runIssuePhaseCli } from '../../utils/issue-phase-cli.js';
|
|
3
|
+
export const runUserStoriesAnalysisCommand = (options) => runIssuePhaseCli({
|
|
4
|
+
command: 'user-stories-analysis',
|
|
5
|
+
description: 'User-stories analysis',
|
|
6
|
+
issueId: options.issueId,
|
|
7
|
+
verbose: options.verbose,
|
|
8
|
+
execute: analyseUserStories,
|
|
9
|
+
});
|
|
@@ -130,9 +130,7 @@ async function validateAndLogChecklists(options, name, checklistContext, issueId
|
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
// Higher-order function for phase execution
|
|
133
|
-
export const createPhaseRunner = (phaseConfig) =>
|
|
134
|
-
// eslint-disable-next-line complexity
|
|
135
|
-
async (options, config) => {
|
|
133
|
+
export const createPhaseRunner = (phaseConfig) => async (options, config) => {
|
|
136
134
|
const { issueId, verbose } = options;
|
|
137
135
|
const { name, execute } = phaseConfig;
|
|
138
136
|
// Track phase duration for logging
|
|
@@ -143,7 +141,11 @@ async (options, config) => {
|
|
|
143
141
|
if (verbose) {
|
|
144
142
|
logDebug(`Checking approval before executing ${name} phase...`, verbose);
|
|
145
143
|
}
|
|
146
|
-
|
|
144
|
+
// The approval system keys configs on snake_case phase names
|
|
145
|
+
// (matching the workflow JSONB vocabulary), so convert from the
|
|
146
|
+
// kebab-case orchestrator name before checking.
|
|
147
|
+
const phaseSnakeCase = name.replace(/-/g, '_');
|
|
148
|
+
const approvalCheck = await checkApprovalBeforePhaseExecution(issueId, phaseSnakeCase, verbose);
|
|
147
149
|
if (!approvalCheck.canProceed) {
|
|
148
150
|
// Current status requires approval but hasn't been approved yet
|
|
149
151
|
// Block phase execution and return blocked result
|
|
@@ -36,7 +36,6 @@ const logAndMarkPhaseCompleted = async (result, verbose) => {
|
|
|
36
36
|
* Orchestrate phase execution based on execution mode
|
|
37
37
|
* Routes to appropriate phase sequence based on mode (only_*, from_*, full_pipeline)
|
|
38
38
|
*/
|
|
39
|
-
// eslint-disable-next-line complexity
|
|
40
39
|
export const runPipelineByMode = async (options, config, executionMode) => {
|
|
41
40
|
const { issueId, verbose } = options;
|
|
42
41
|
if (verbose) {
|
|
@@ -1,56 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Issue status configuration
|
|
3
|
-
* Defines the progression order and mappings for issue statuses
|
|
4
|
-
*/
|
|
5
|
-
import type { IssueStatus } from '../types/index.js';
|
|
6
|
-
/**
|
|
7
|
-
* Status progression order - higher index means more advanced status
|
|
8
|
-
* This defines the allowed forward progression through the issue workflow
|
|
9
|
-
*
|
|
10
|
-
* Business Logic Rules:
|
|
11
|
-
* - Issues can only move forward or stay at the same status level
|
|
12
|
-
* - No regression to earlier stages is allowed (e.g., cannot go back to 'backlog' from 'code_implementation')
|
|
13
|
-
* - Status updates preserve development momentum and prevent accidental rollbacks
|
|
2
|
+
* Issue lifecycle status configuration.
|
|
14
3
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* - Same status → same status: Allowed for retries, reprocessing, or status confirmation
|
|
18
|
-
* - code_refine_verification → code_refine: Handled by specific retry logic, not this progression
|
|
4
|
+
* Under the 2D model, issues.status carries 7 lifecycle values. Phase
|
|
5
|
+
* progress lives in the workflow JSONB column instead.
|
|
19
6
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
7
|
+
* The map kebab-case→workflow used by the worker is in
|
|
8
|
+
* ../api/issues/status-updater.ts (PHASE_NAME_TO_WORKFLOW)
|
|
9
|
+
* — this file only owns the lifecycle status enum + ordering used by
|
|
10
|
+
* isForwardProgression().
|
|
23
11
|
*/
|
|
24
|
-
|
|
12
|
+
import type { IssueStatus } from '../types/index.js';
|
|
25
13
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* This mapping ensures that each pipeline phase updates the issue to the appropriate
|
|
30
|
-
* status. Phase names use kebab-case (e.g., 'issue-analysis') while status values
|
|
31
|
-
* use snake_case (e.g., 'issue_analysis') to match database schema.
|
|
14
|
+
* Lifecycle progression order. Used by isForwardProgression() to reject
|
|
15
|
+
* regressions — e.g. you can move backlog → ready_for_ai but not the
|
|
16
|
+
* other way around.
|
|
32
17
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* and skip the status update to prevent unintended regression to 'backlog'.
|
|
18
|
+
* Special-cases (handled in isForwardProgression):
|
|
19
|
+
* - any status → archived: always allowed
|
|
20
|
+
* - archived → backlog: always allowed (unarchive)
|
|
37
21
|
*/
|
|
38
|
-
export declare const
|
|
22
|
+
export declare const STATUS_PROGRESSION_ORDER: readonly IssueStatus[];
|
|
39
23
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* Excluded statuses (system-managed):
|
|
44
|
-
* - *_verification statuses: Automatically entered after completing the corresponding phase
|
|
45
|
-
* - testing_in_progress: Automatically set when functional testing begins
|
|
46
|
-
* - testing_passed: Automatically set when all tests pass
|
|
47
|
-
* - testing_failed: Automatically set when tests fail
|
|
48
|
-
*
|
|
49
|
-
* These excluded statuses should not be shown in UI dropdowns for manual status changes
|
|
50
|
-
* because they represent intermediate states that are managed by the workflow system.
|
|
24
|
+
* Statuses a human can pick from a dropdown. AI-managed states
|
|
25
|
+
* (assigned_to_ai, in_progress) are written by the worker and not
|
|
26
|
+
* exposed for manual selection.
|
|
51
27
|
*/
|
|
52
28
|
export declare const HUMAN_SELECTABLE_STATUSES: readonly IssueStatus[];
|
|
53
|
-
/**
|
|
54
|
-
* Check if a status can be manually selected by a human user
|
|
55
|
-
*/
|
|
56
29
|
export declare function isHumanSelectableStatus(status: IssueStatus): boolean;
|
|
@@ -1,130 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Issue status configuration
|
|
3
|
-
* Defines the progression order and mappings for issue statuses
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* Status progression order - higher index means more advanced status
|
|
7
|
-
* This defines the allowed forward progression through the issue workflow
|
|
2
|
+
* Issue lifecycle status configuration.
|
|
8
3
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* - No regression to earlier stages is allowed (e.g., cannot go back to 'backlog' from 'code_implementation')
|
|
12
|
-
* - Status updates preserve development momentum and prevent accidental rollbacks
|
|
4
|
+
* Under the 2D model, issues.status carries 7 lifecycle values. Phase
|
|
5
|
+
* progress lives in the workflow JSONB column instead.
|
|
13
6
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
7
|
+
* The map kebab-case→workflow used by the worker is in
|
|
8
|
+
* ../api/issues/status-updater.ts (PHASE_NAME_TO_WORKFLOW)
|
|
9
|
+
* — this file only owns the lifecycle status enum + ordering used by
|
|
10
|
+
* isForwardProgression().
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Lifecycle progression order. Used by isForwardProgression() to reject
|
|
14
|
+
* regressions — e.g. you can move backlog → ready_for_ai but not the
|
|
15
|
+
* other way around.
|
|
18
16
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
17
|
+
* Special-cases (handled in isForwardProgression):
|
|
18
|
+
* - any status → archived: always allowed
|
|
19
|
+
* - archived → backlog: always allowed (unarchive)
|
|
22
20
|
*/
|
|
23
21
|
export const STATUS_PROGRESSION_ORDER = [
|
|
24
22
|
'backlog',
|
|
25
23
|
'ready_for_ai',
|
|
26
24
|
'assigned_to_ai',
|
|
27
|
-
'
|
|
28
|
-
'issue_analysis_verification',
|
|
29
|
-
'user_stories_analysis',
|
|
30
|
-
'user_stories_analysis_verification',
|
|
31
|
-
'test_cases_analysis',
|
|
32
|
-
'test_cases_analysis_verification',
|
|
33
|
-
'technical_design',
|
|
34
|
-
'technical_design_verification',
|
|
35
|
-
'branch_planning',
|
|
36
|
-
'branch_planning_verification',
|
|
37
|
-
'code_implementation',
|
|
38
|
-
'code_implementation_verification',
|
|
39
|
-
'pr_splitting',
|
|
40
|
-
'pr_splitting_verification',
|
|
41
|
-
'pr_execution',
|
|
42
|
-
'code_refine',
|
|
43
|
-
'code_refine_verification',
|
|
44
|
-
'bug_fixing',
|
|
45
|
-
'code_review',
|
|
46
|
-
'functional_testing',
|
|
47
|
-
'testing_in_progress',
|
|
48
|
-
'testing_passed',
|
|
49
|
-
'testing_failed',
|
|
50
|
-
'ready_for_review',
|
|
25
|
+
'in_progress',
|
|
51
26
|
'shipped',
|
|
27
|
+
'failed',
|
|
52
28
|
'archived',
|
|
53
29
|
];
|
|
54
30
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* This mapping ensures that each pipeline phase updates the issue to the appropriate
|
|
59
|
-
* status. Phase names use kebab-case (e.g., 'issue-analysis') while status values
|
|
60
|
-
* use snake_case (e.g., 'issue_analysis') to match database schema.
|
|
61
|
-
*
|
|
62
|
-
* All status values must match the database constraint in migration 20251019000000_update_feature_status_constraint.sql
|
|
63
|
-
*
|
|
64
|
-
* If a phase is not found in this mapping, updateIssueStatusForPhase will return null
|
|
65
|
-
* and skip the status update to prevent unintended regression to 'backlog'.
|
|
66
|
-
*/
|
|
67
|
-
export const PHASE_STATUS_MAP = {
|
|
68
|
-
'issue-analysis': 'issue_analysis',
|
|
69
|
-
'issue-analysis-verification': 'issue_analysis_verification',
|
|
70
|
-
'user-stories-analysis': 'user_stories_analysis',
|
|
71
|
-
'user-stories-analysis-verification': 'user_stories_analysis_verification',
|
|
72
|
-
'test-cases-analysis': 'test_cases_analysis',
|
|
73
|
-
'test-cases-analysis-verification': 'test_cases_analysis_verification',
|
|
74
|
-
'technical-design': 'technical_design',
|
|
75
|
-
'technical-design-verification': 'technical_design_verification',
|
|
76
|
-
'branch-planning': 'branch_planning',
|
|
77
|
-
'branch-planning-verification': 'branch_planning_verification',
|
|
78
|
-
'code-implementation': 'code_implementation',
|
|
79
|
-
'code-implementation-verification': 'code_implementation_verification',
|
|
80
|
-
'pr-splitting': 'pr_splitting',
|
|
81
|
-
'pr-splitting-verification': 'pr_splitting_verification',
|
|
82
|
-
'pr-execution': 'pr_execution',
|
|
83
|
-
'code-refine': 'code_refine',
|
|
84
|
-
'code-refine-verification': 'code_refine_verification',
|
|
85
|
-
'bug-fixing': 'bug_fixing',
|
|
86
|
-
'code-review': 'code_review',
|
|
87
|
-
'functional-testing': 'functional_testing',
|
|
88
|
-
'testing-in-progress': 'testing_in_progress',
|
|
89
|
-
'testing-passed': 'testing_passed',
|
|
90
|
-
'testing-failed': 'testing_failed',
|
|
91
|
-
'ready-for-review': 'ready_for_review',
|
|
92
|
-
};
|
|
93
|
-
/**
|
|
94
|
-
* Human-selectable statuses
|
|
95
|
-
* These are the statuses that a human user can manually set.
|
|
96
|
-
*
|
|
97
|
-
* Excluded statuses (system-managed):
|
|
98
|
-
* - *_verification statuses: Automatically entered after completing the corresponding phase
|
|
99
|
-
* - testing_in_progress: Automatically set when functional testing begins
|
|
100
|
-
* - testing_passed: Automatically set when all tests pass
|
|
101
|
-
* - testing_failed: Automatically set when tests fail
|
|
102
|
-
*
|
|
103
|
-
* These excluded statuses should not be shown in UI dropdowns for manual status changes
|
|
104
|
-
* because they represent intermediate states that are managed by the workflow system.
|
|
31
|
+
* Statuses a human can pick from a dropdown. AI-managed states
|
|
32
|
+
* (assigned_to_ai, in_progress) are written by the worker and not
|
|
33
|
+
* exposed for manual selection.
|
|
105
34
|
*/
|
|
106
35
|
export const HUMAN_SELECTABLE_STATUSES = [
|
|
107
36
|
'backlog',
|
|
108
37
|
'ready_for_ai',
|
|
109
|
-
'issue_analysis',
|
|
110
|
-
'user_stories_analysis',
|
|
111
|
-
'test_cases_analysis',
|
|
112
|
-
'technical_design',
|
|
113
|
-
'branch_planning',
|
|
114
|
-
'code_implementation',
|
|
115
|
-
'pr_splitting',
|
|
116
|
-
'pr_execution',
|
|
117
|
-
'code_refine',
|
|
118
|
-
'bug_fixing',
|
|
119
|
-
'code_review',
|
|
120
|
-
'functional_testing',
|
|
121
|
-
'ready_for_review',
|
|
122
38
|
'shipped',
|
|
123
39
|
'archived',
|
|
40
|
+
'failed',
|
|
124
41
|
];
|
|
125
|
-
/**
|
|
126
|
-
* Check if a status can be manually selected by a human user
|
|
127
|
-
*/
|
|
128
42
|
export function isHumanSelectableStatus(status) {
|
|
129
43
|
return HUMAN_SELECTABLE_STATUSES.includes(status);
|
|
130
44
|
}
|
package/dist/index.js
CHANGED
|
@@ -13,20 +13,29 @@ import { runBuild } from './commands/build/index.js';
|
|
|
13
13
|
import { runChecklists } from './commands/checklists/index.js';
|
|
14
14
|
import { runCodeReview } from './commands/code-review/index.js';
|
|
15
15
|
import { runConfigGet, runConfigList, runConfigSet, runConfigUnset, } from './commands/config/index.js';
|
|
16
|
+
import { runFinancingDeck } from './commands/financing-deck/index.js';
|
|
17
|
+
import { runFindArchitecture } from './commands/find-architecture/index.js';
|
|
16
18
|
import { runFindBugs } from './commands/find-bugs/index.js';
|
|
17
19
|
import { runFindFeatures } from './commands/find-features/index.js';
|
|
18
20
|
import { parseCategoriesOption, runFindSmells, } from './commands/find-smells/index.js';
|
|
19
21
|
import { runGrowthAnalysis } from './commands/growth-analysis/index.js';
|
|
20
22
|
import { runInit } from './commands/init/index.js';
|
|
21
23
|
import { runIntelligence } from './commands/intelligence/index.js';
|
|
24
|
+
import { runIssueAnalysisCommand } from './commands/issue-analysis/index.js';
|
|
22
25
|
import { runPRResolve } from './commands/pr-resolve/index.js';
|
|
23
26
|
import { runPRReview } from './commands/pr-review/index.js';
|
|
24
27
|
import { runRefactor } from './commands/refactor/refactor.js';
|
|
25
28
|
import { runReleaseSyncCommand } from './commands/release-sync/index.js';
|
|
26
29
|
import { runRunSheetCommand } from './commands/run-sheet/index.js';
|
|
27
30
|
import { runSmokeTestCommand } from './commands/smoke-test/index.js';
|
|
31
|
+
import { runSyncGithubIssues } from './commands/sync-github-issues/index.js';
|
|
32
|
+
import { runSyncSentryIssues } from './commands/sync-sentry-issues/index.js';
|
|
28
33
|
import { runTaskWorker } from './commands/task-worker/index.js';
|
|
34
|
+
import { runTechnicalDesignCommand } from './commands/technical-design/index.js';
|
|
35
|
+
import { runTestCasesAnalysisCommand } from './commands/test-cases-analysis/index.js';
|
|
36
|
+
import { runUserStoriesAnalysisCommand } from './commands/user-stories-analysis/index.js';
|
|
29
37
|
import { runWorkflow } from './commands/workflow/index.js';
|
|
38
|
+
import { DEFAULT_MAX_FILES as FIND_ARCHITECTURE_DEFAULT_MAX_FILES } from './phases/find-architecture/index.js';
|
|
30
39
|
import { DEFAULT_MAX_FILES as FIND_SMELLS_DEFAULT_MAX_FILES } from './phases/find-smells/index.js';
|
|
31
40
|
import { SMELL_CATEGORIES, } from './phases/find-smells/types.js';
|
|
32
41
|
import { logError, logInfo } from './utils/logger.js';
|
|
@@ -147,6 +156,27 @@ program
|
|
|
147
156
|
}
|
|
148
157
|
});
|
|
149
158
|
// ============================================================
|
|
159
|
+
// Subcommand: edsger financing-deck <productId>
|
|
160
|
+
// ============================================================
|
|
161
|
+
program
|
|
162
|
+
.command('financing-deck <productId>')
|
|
163
|
+
.description('Draft an investor pitch deck for a product (AI-generated)')
|
|
164
|
+
.requiredOption('--target-stage <stage>', 'Target stage: pre_seed | seed | series_a | series_b | series_c | growth')
|
|
165
|
+
.option('-v, --verbose', 'Verbose output')
|
|
166
|
+
.action(async (productId, opts) => {
|
|
167
|
+
try {
|
|
168
|
+
await runFinancingDeck({
|
|
169
|
+
financingDeck: productId,
|
|
170
|
+
financingTargetStage: opts.targetStage,
|
|
171
|
+
verbose: opts.verbose,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
// ============================================================
|
|
150
180
|
// Subcommand: edsger intel <productId>
|
|
151
181
|
// ============================================================
|
|
152
182
|
program
|
|
@@ -240,18 +270,26 @@ program
|
|
|
240
270
|
}
|
|
241
271
|
});
|
|
242
272
|
// ============================================================
|
|
243
|
-
// Subcommand: edsger checklists <
|
|
273
|
+
// Subcommand: edsger checklists [productId] [--team-id <id>]
|
|
244
274
|
// ============================================================
|
|
245
275
|
program
|
|
246
|
-
.command('checklists
|
|
247
|
-
.description('Analyse a local directory and sync product checklists (create/update/delete) using AI')
|
|
276
|
+
.command('checklists [productId]')
|
|
277
|
+
.description('Analyse a local directory and sync product OR team checklists (create/update/delete) using AI. Pass a product ID positionally for product scope, or --team-id for team scope.')
|
|
248
278
|
.option('-v, --verbose', 'Verbose output')
|
|
249
279
|
.option('--dir <path>', 'Directory to analyse (defaults to current working directory)')
|
|
250
280
|
.option('--prompt <text>', 'Additional instructions for the AI agent')
|
|
281
|
+
.option('--team-id <id>', 'Sync TEAM-level checklists for this team instead of a product. The directory is treated as team standards / reference material.')
|
|
251
282
|
.action(async (productId, opts) => {
|
|
252
283
|
try {
|
|
284
|
+
if (!productId && !opts.teamId) {
|
|
285
|
+
throw new Error('Provide a product ID positionally or pass --team-id <id> for team scope.');
|
|
286
|
+
}
|
|
287
|
+
if (productId && opts.teamId) {
|
|
288
|
+
throw new Error('Pass either a product ID or --team-id, not both.');
|
|
289
|
+
}
|
|
253
290
|
await runChecklists({
|
|
254
291
|
checklistsProductId: productId,
|
|
292
|
+
checklistsTeamId: opts.teamId,
|
|
255
293
|
checklistsDir: opts.dir,
|
|
256
294
|
checklistsPrompt: opts.prompt,
|
|
257
295
|
verbose: opts.verbose,
|
|
@@ -301,6 +339,70 @@ program
|
|
|
301
339
|
}
|
|
302
340
|
});
|
|
303
341
|
// ============================================================
|
|
342
|
+
// Subcommand: edsger issue-analysis <issueId>
|
|
343
|
+
// ============================================================
|
|
344
|
+
program
|
|
345
|
+
.command('issue-analysis <issueId>')
|
|
346
|
+
.description('Generate or refine the execution plan for an issue (issue_analysis phase, with verification loop)')
|
|
347
|
+
.option('-v, --verbose', 'Verbose output')
|
|
348
|
+
.action(async (issueId, opts) => {
|
|
349
|
+
try {
|
|
350
|
+
await runIssueAnalysisCommand({ issueId, verbose: opts.verbose });
|
|
351
|
+
}
|
|
352
|
+
catch (error) {
|
|
353
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
354
|
+
process.exit(1);
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
// ============================================================
|
|
358
|
+
// Subcommand: edsger user-stories-analysis <issueId>
|
|
359
|
+
// ============================================================
|
|
360
|
+
program
|
|
361
|
+
.command('user-stories-analysis <issueId>')
|
|
362
|
+
.description('Generate or refine user stories for an issue (user_stories_analysis phase, with verification loop)')
|
|
363
|
+
.option('-v, --verbose', 'Verbose output')
|
|
364
|
+
.action(async (issueId, opts) => {
|
|
365
|
+
try {
|
|
366
|
+
await runUserStoriesAnalysisCommand({ issueId, verbose: opts.verbose });
|
|
367
|
+
}
|
|
368
|
+
catch (error) {
|
|
369
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
370
|
+
process.exit(1);
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
// ============================================================
|
|
374
|
+
// Subcommand: edsger test-cases-analysis <issueId>
|
|
375
|
+
// ============================================================
|
|
376
|
+
program
|
|
377
|
+
.command('test-cases-analysis <issueId>')
|
|
378
|
+
.description('Generate or refine test cases for an issue (test_cases_analysis phase, with verification loop)')
|
|
379
|
+
.option('-v, --verbose', 'Verbose output')
|
|
380
|
+
.action(async (issueId, opts) => {
|
|
381
|
+
try {
|
|
382
|
+
await runTestCasesAnalysisCommand({ issueId, verbose: opts.verbose });
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
386
|
+
process.exit(1);
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
// ============================================================
|
|
390
|
+
// Subcommand: edsger technical-design <issueId>
|
|
391
|
+
// ============================================================
|
|
392
|
+
program
|
|
393
|
+
.command('technical-design <issueId>')
|
|
394
|
+
.description('Generate or refine the technical design for an issue (technical_design phase, with verification loop)')
|
|
395
|
+
.option('-v, --verbose', 'Verbose output')
|
|
396
|
+
.action(async (issueId, opts) => {
|
|
397
|
+
try {
|
|
398
|
+
await runTechnicalDesignCommand({ issueId, verbose: opts.verbose });
|
|
399
|
+
}
|
|
400
|
+
catch (error) {
|
|
401
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
402
|
+
process.exit(1);
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
// ============================================================
|
|
304
406
|
// Subcommand: edsger smoke-test <releaseId>
|
|
305
407
|
// ============================================================
|
|
306
408
|
program
|
|
@@ -384,6 +486,41 @@ program
|
|
|
384
486
|
}
|
|
385
487
|
});
|
|
386
488
|
// ============================================================
|
|
489
|
+
// Subcommand: edsger sync-github-issues <productId>
|
|
490
|
+
// ============================================================
|
|
491
|
+
program
|
|
492
|
+
.command('sync-github-issues <productId>')
|
|
493
|
+
.description("Mirror the product's connected GitHub repo issues into the local issues list (handles new, updated, closed, and deleted)")
|
|
494
|
+
.option('-v, --verbose', 'Verbose output')
|
|
495
|
+
.action(async (productId, opts) => {
|
|
496
|
+
try {
|
|
497
|
+
await runSyncGithubIssues(productId, opts);
|
|
498
|
+
}
|
|
499
|
+
catch (error) {
|
|
500
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
501
|
+
process.exit(1);
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
// ============================================================
|
|
505
|
+
// Subcommand: edsger sync-sentry-issues <productId>
|
|
506
|
+
//
|
|
507
|
+
// Requires SENTRY_AUTH_TOKEN, SENTRY_ORG, SENTRY_PROJECT in env (set via
|
|
508
|
+
// `edsger config set`). Optional SENTRY_BASE_URL for self-hosted Sentry.
|
|
509
|
+
// ============================================================
|
|
510
|
+
program
|
|
511
|
+
.command('sync-sentry-issues <productId>')
|
|
512
|
+
.description('Mirror Sentry project issues into the local issues list (requires SENTRY_AUTH_TOKEN, SENTRY_ORG, SENTRY_PROJECT env)')
|
|
513
|
+
.option('-v, --verbose', 'Verbose output')
|
|
514
|
+
.action(async (productId, opts) => {
|
|
515
|
+
try {
|
|
516
|
+
await runSyncSentryIssues(productId, opts);
|
|
517
|
+
}
|
|
518
|
+
catch (error) {
|
|
519
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
520
|
+
process.exit(1);
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
// ============================================================
|
|
387
524
|
// Subcommand: edsger find-features <productId>
|
|
388
525
|
// ============================================================
|
|
389
526
|
program
|
|
@@ -443,6 +580,31 @@ program
|
|
|
443
580
|
}
|
|
444
581
|
});
|
|
445
582
|
// ============================================================
|
|
583
|
+
// Subcommand: edsger find-architecture <productId>
|
|
584
|
+
// ============================================================
|
|
585
|
+
program
|
|
586
|
+
.command('find-architecture <productId>')
|
|
587
|
+
.description("AI-audit a product's repository for architectural concerns (layering, coupling, cycles, missing/duplicated abstractions, responsibility creep) and file each new finding as an issue")
|
|
588
|
+
.option('--full', 'Force a full scan even if previous-scan state exists')
|
|
589
|
+
.option('--branch <name>', 'Branch to scan (defaults to repo default branch)')
|
|
590
|
+
.option('--max-files <n>', `Upper bound on files the auditor may Read (default ${FIND_ARCHITECTURE_DEFAULT_MAX_FILES})`, (value) => {
|
|
591
|
+
const n = parseInt(value, 10);
|
|
592
|
+
if (Number.isNaN(n) || n <= 0) {
|
|
593
|
+
throw new Error('--max-files must be a positive integer');
|
|
594
|
+
}
|
|
595
|
+
return n;
|
|
596
|
+
})
|
|
597
|
+
.option('-v, --verbose', 'Verbose output')
|
|
598
|
+
.action(async (productId, opts) => {
|
|
599
|
+
try {
|
|
600
|
+
await runFindArchitecture(productId, opts);
|
|
601
|
+
}
|
|
602
|
+
catch (error) {
|
|
603
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
604
|
+
process.exit(1);
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
// ============================================================
|
|
446
608
|
// Subcommand: edsger pr-resolve <productId>
|
|
447
609
|
// ============================================================
|
|
448
610
|
program
|
|
@@ -475,7 +637,17 @@ program
|
|
|
475
637
|
.option('--concurrency <number>', 'Max concurrent issues to process (default: 3)', parseInt)
|
|
476
638
|
.option('-c, --config <path>', 'Path to config file')
|
|
477
639
|
.option('-v, --verbose', 'Verbose output');
|
|
478
|
-
program.action(async (options) => {
|
|
640
|
+
program.action(async (options, command) => {
|
|
641
|
+
// Commander silently falls through to this default action when the user
|
|
642
|
+
// passes a subcommand name that isn't registered (e.g. an old binary that
|
|
643
|
+
// doesn't yet know about `sync-sentry-issues`). That makes version-skew
|
|
644
|
+
// bugs look like the agent inexplicably running with the wrong intent.
|
|
645
|
+
// Treat any leftover positional arg as an unknown command and abort.
|
|
646
|
+
if (command.args.length > 0) {
|
|
647
|
+
logError(`Unknown command: ${command.args[0]}`);
|
|
648
|
+
logError(`Run 'edsger --help' to see available commands.`);
|
|
649
|
+
process.exit(1);
|
|
650
|
+
}
|
|
479
651
|
try {
|
|
480
652
|
await runEdsger(options);
|
|
481
653
|
}
|
|
@@ -94,7 +94,8 @@ export async function executeAppStoreQuery(currentPrompt, systemPrompt, config,
|
|
|
94
94
|
logDebug(`${content.text}`, verbose);
|
|
95
95
|
}
|
|
96
96
|
else if (content.type === 'tool_use') {
|
|
97
|
-
const
|
|
97
|
+
const input = (content.input ?? {});
|
|
98
|
+
const desc = input.description ?? input.command ?? 'Running...';
|
|
98
99
|
logInfo(`[Turn ${turnCount}] ${content.name}: ${typeof desc === 'string' ? desc.slice(0, 120) : 'Running...'}`);
|
|
99
100
|
}
|
|
100
101
|
}
|