edsger 0.53.0 → 0.54.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.md +20 -0
  2. package/dist/api/financing.d.ts +47 -0
  3. package/dist/api/financing.js +37 -0
  4. package/dist/api/issues/approval-checker.d.ts +11 -9
  5. package/dist/api/issues/approval-checker.js +30 -41
  6. package/dist/api/issues/status-updater.d.ts +47 -20
  7. package/dist/api/issues/status-updater.js +114 -46
  8. package/dist/api/issues/update-issue.d.ts +5 -0
  9. package/dist/api/issues/update-issue.js +6 -0
  10. package/dist/commands/agent-workflow/processor.js +5 -1
  11. package/dist/commands/checklists/index.d.ts +5 -2
  12. package/dist/commands/checklists/index.js +73 -12
  13. package/dist/commands/checklists/tools.d.ts +14 -7
  14. package/dist/commands/checklists/tools.js +15 -208
  15. package/dist/commands/financing-deck/index.d.ts +8 -0
  16. package/dist/commands/financing-deck/index.js +66 -0
  17. package/dist/commands/find-architecture/index.d.ts +13 -0
  18. package/dist/commands/find-architecture/index.js +41 -0
  19. package/dist/commands/sync-github-issues/index.d.ts +11 -0
  20. package/dist/commands/sync-github-issues/index.js +42 -0
  21. package/dist/commands/sync-sentry-issues/index.d.ts +14 -0
  22. package/dist/commands/sync-sentry-issues/index.js +73 -0
  23. package/dist/commands/workflow/executors/phase-executor.js +6 -4
  24. package/dist/commands/workflow/phase-orchestrator.js +0 -1
  25. package/dist/config/issue-status.d.ts +18 -45
  26. package/dist/config/issue-status.js +21 -107
  27. package/dist/index.js +97 -3
  28. package/dist/phases/app-store-generation/agent.js +2 -1
  29. package/dist/phases/app-store-generation/index.js +11 -3
  30. package/dist/phases/autonomous/index.js +9 -6
  31. package/dist/phases/branch-planning/index.js +1 -2
  32. package/dist/phases/branch-planning/prompts.d.ts +1 -1
  33. package/dist/phases/branch-planning/prompts.js +3 -2
  34. package/dist/phases/bug-fixing/analyzer.js +6 -3
  35. package/dist/phases/bug-fixing/mcp-server.d.ts +18 -1
  36. package/dist/phases/bug-fixing/mcp-server.js +19 -76
  37. package/dist/phases/chat-processor/product-tools.d.ts +5 -8
  38. package/dist/phases/chat-processor/product-tools.js +6 -512
  39. package/dist/phases/chat-processor/tools.d.ts +5 -9
  40. package/dist/phases/chat-processor/tools.js +6 -704
  41. package/dist/phases/code-implementation/branch-pr-creator.js +7 -5
  42. package/dist/phases/code-implementation/index.js +6 -3
  43. package/dist/phases/code-implementation-verification/agent.js +6 -1
  44. package/dist/phases/code-refine/index.js +8 -6
  45. package/dist/phases/code-refine/refine-iteration.js +2 -1
  46. package/dist/phases/code-review/index.js +8 -6
  47. package/dist/phases/code-testing/analyzer.js +11 -8
  48. package/dist/phases/financing-deck/agent.d.ts +1 -0
  49. package/dist/phases/financing-deck/agent.js +96 -0
  50. package/dist/phases/financing-deck/context.d.ts +13 -0
  51. package/dist/phases/financing-deck/context.js +69 -0
  52. package/dist/phases/financing-deck/index.d.ts +15 -0
  53. package/dist/phases/financing-deck/index.js +89 -0
  54. package/dist/phases/financing-deck/prompts.d.ts +2 -0
  55. package/dist/phases/financing-deck/prompts.js +94 -0
  56. package/dist/phases/find-architecture/index.d.ts +44 -0
  57. package/dist/phases/find-architecture/index.js +248 -0
  58. package/dist/phases/find-architecture/prompts.d.ts +31 -0
  59. package/dist/phases/find-architecture/prompts.js +128 -0
  60. package/dist/phases/find-architecture/state.d.ts +21 -0
  61. package/dist/phases/find-architecture/state.js +17 -0
  62. package/dist/phases/find-architecture/types.d.ts +55 -0
  63. package/dist/phases/find-architecture/types.js +69 -0
  64. package/dist/phases/find-bugs/index.js +13 -4
  65. package/dist/phases/find-features/index.js +10 -5
  66. package/dist/phases/find-smells/index.js +10 -3
  67. package/dist/phases/functional-testing/analyzer.js +27 -17
  68. package/dist/phases/functional-testing/http-fallback.d.ts +1 -1
  69. package/dist/phases/functional-testing/http-fallback.js +32 -16
  70. package/dist/phases/functional-testing/mcp-server.d.ts +9 -1
  71. package/dist/phases/functional-testing/mcp-server.js +13 -132
  72. package/dist/phases/growth-analysis/agent.js +2 -2
  73. package/dist/phases/growth-analysis/index.js +9 -3
  74. package/dist/phases/intelligence-analysis/agent.js +2 -2
  75. package/dist/phases/intelligence-analysis/index.js +9 -2
  76. package/dist/phases/issue-analysis/agent.d.ts +9 -1
  77. package/dist/phases/issue-analysis/agent.js +68 -27
  78. package/dist/phases/issue-analysis/context.d.ts +5 -9
  79. package/dist/phases/issue-analysis/context.js +31 -76
  80. package/dist/phases/issue-analysis/index.js +32 -84
  81. package/dist/phases/issue-analysis/outcome.d.ts +3 -33
  82. package/dist/phases/issue-analysis/outcome.js +15 -253
  83. package/dist/phases/issue-analysis/prompts.d.ts +3 -5
  84. package/dist/phases/issue-analysis/prompts.js +45 -158
  85. package/dist/phases/issue-analysis-verification/agent.d.ts +4 -4
  86. package/dist/phases/issue-analysis-verification/agent.js +5 -5
  87. package/dist/phases/issue-analysis-verification/index.d.ts +4 -2
  88. package/dist/phases/issue-analysis-verification/index.js +9 -22
  89. package/dist/phases/issue-analysis-verification/prompts.d.ts +1 -2
  90. package/dist/phases/issue-analysis-verification/prompts.js +21 -46
  91. package/dist/phases/output-contracts.js +66 -78
  92. package/dist/phases/pr-execution/context.d.ts +2 -0
  93. package/dist/phases/pr-execution/context.js +1 -0
  94. package/dist/phases/pr-execution/index.js +28 -19
  95. package/dist/phases/pr-execution/prompts.d.ts +2 -1
  96. package/dist/phases/pr-execution/prompts.js +12 -10
  97. package/dist/phases/pr-resolve/index.js +2 -8
  98. package/dist/phases/pr-splitting/index.js +3 -3
  99. package/dist/phases/pr-splitting/prompts.d.ts +1 -1
  100. package/dist/phases/pr-splitting/prompts.js +3 -2
  101. package/dist/phases/pull-request/creator.js +10 -7
  102. package/dist/phases/pull-request/handler.js +3 -1
  103. package/dist/phases/release-sync/index.js +52 -43
  104. package/dist/phases/run-sheet/index.js +2 -1
  105. package/dist/phases/smoke-test/agent.js +2 -1
  106. package/dist/phases/smoke-test/index.js +4 -1
  107. package/dist/phases/sync-github-issues/index.d.ts +41 -0
  108. package/dist/phases/sync-github-issues/index.js +187 -0
  109. package/dist/phases/sync-github-issues/state.d.ts +26 -0
  110. package/dist/phases/sync-github-issues/state.js +18 -0
  111. package/dist/phases/sync-github-issues/types.d.ts +35 -0
  112. package/dist/phases/sync-github-issues/types.js +6 -0
  113. package/dist/phases/sync-sentry-issues/index.d.ts +29 -0
  114. package/dist/phases/sync-sentry-issues/index.js +153 -0
  115. package/dist/phases/sync-sentry-issues/sentry-client.d.ts +66 -0
  116. package/dist/phases/sync-sentry-issues/sentry-client.js +221 -0
  117. package/dist/phases/sync-sentry-issues/state.d.ts +23 -0
  118. package/dist/phases/sync-sentry-issues/state.js +18 -0
  119. package/dist/phases/sync-sentry-issues/types.d.ts +46 -0
  120. package/dist/phases/sync-sentry-issues/types.js +6 -0
  121. package/dist/phases/sync-shared/mcp.d.ts +81 -0
  122. package/dist/phases/sync-shared/mcp.js +111 -0
  123. package/dist/phases/technical-design/index.js +0 -1
  124. package/dist/phases/test-cases-analysis/agent.js +2 -1
  125. package/dist/phases/test-cases-analysis/index.js +0 -1
  126. package/dist/phases/user-stories-analysis/agent.js +2 -1
  127. package/dist/phases/user-stories-analysis/index.js +0 -1
  128. package/dist/services/coaching/coaching-agent.js +29 -4
  129. package/dist/services/feedbacks.d.ts +1 -1
  130. package/dist/services/repo-config.d.ts +17 -0
  131. package/dist/services/repo-config.js +50 -0
  132. package/dist/skills/phase/issue-analysis/SKILL.md +48 -92
  133. package/dist/skills/phase/issue-analysis-verification/SKILL.md +46 -31
  134. package/dist/tools/bootstrap.d.ts +45 -0
  135. package/dist/tools/bootstrap.js +50 -0
  136. package/dist/types/external-sources.d.ts +22 -0
  137. package/dist/types/external-sources.js +23 -0
  138. package/dist/types/index.d.ts +5 -10
  139. package/dist/types/issues.d.ts +2 -0
  140. package/dist/types/llm-responses.d.ts +1 -14
  141. package/dist/utils/formatters.js +1 -7
  142. package/dist/utils/git-branch-manager-async.js +9 -5
  143. package/dist/utils/git-branch-manager.js +17 -7
  144. package/dist/workspace/workspace-manager.d.ts +10 -0
  145. package/dist/workspace/workspace-manager.js +22 -1
  146. package/package.json +6 -2
  147. package/vitest.config.ts +4 -0
  148. package/.env.local +0 -12
@@ -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
+ }
@@ -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
- const approvalCheck = await checkApprovalBeforePhaseExecution(issueId, verbose);
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
- * Special Cases:
16
- * - testing_failed testing_in_progress: Allowed for retry attempts after fixing issues
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
- * Note: The order reflects the typical development workflow from initial planning
21
- * through to final shipping. All status values must match the database constraint
22
- * in migration 20251019000000_update_feature_status_constraint.sql
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
- export declare const STATUS_PROGRESSION_ORDER: readonly IssueStatus[];
12
+ import type { IssueStatus } from '../types/index.js';
25
13
  /**
26
- * Phase to status mapping
27
- * Maps pipeline phase names to their corresponding issue statuses
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
- * All status values must match the database constraint in migration 20251019000000_update_feature_status_constraint.sql
34
- *
35
- * If a phase is not found in this mapping, updateIssueStatusForPhase will return null
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 PHASE_STATUS_MAP: Record<string, IssueStatus>;
22
+ export declare const STATUS_PROGRESSION_ORDER: readonly IssueStatus[];
39
23
  /**
40
- * Human-selectable statuses
41
- * These are the statuses that a human user can manually set.
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
- * Business Logic Rules:
10
- * - Issues can only move forward or stay at the same status level
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
- * Special Cases:
15
- * - testing_failed → testing_in_progress: Allowed for retry attempts after fixing issues
16
- * - Same status same status: Allowed for retries, reprocessing, or status confirmation
17
- * - code_refine_verification → code_refine: Handled by specific retry logic, not this progression
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
- * Note: The order reflects the typical development workflow from initial planning
20
- * through to final shipping. All status values must match the database constraint
21
- * in migration 20251019000000_update_feature_status_constraint.sql
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
- 'issue_analysis',
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
- * Phase to status mapping
56
- * Maps pipeline phase names to their corresponding issue statuses
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,6 +13,8 @@ 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';
@@ -25,8 +27,11 @@ import { runRefactor } from './commands/refactor/refactor.js';
25
27
  import { runReleaseSyncCommand } from './commands/release-sync/index.js';
26
28
  import { runRunSheetCommand } from './commands/run-sheet/index.js';
27
29
  import { runSmokeTestCommand } from './commands/smoke-test/index.js';
30
+ import { runSyncGithubIssues } from './commands/sync-github-issues/index.js';
31
+ import { runSyncSentryIssues } from './commands/sync-sentry-issues/index.js';
28
32
  import { runTaskWorker } from './commands/task-worker/index.js';
29
33
  import { runWorkflow } from './commands/workflow/index.js';
34
+ import { DEFAULT_MAX_FILES as FIND_ARCHITECTURE_DEFAULT_MAX_FILES } from './phases/find-architecture/index.js';
30
35
  import { DEFAULT_MAX_FILES as FIND_SMELLS_DEFAULT_MAX_FILES } from './phases/find-smells/index.js';
31
36
  import { SMELL_CATEGORIES, } from './phases/find-smells/types.js';
32
37
  import { logError, logInfo } from './utils/logger.js';
@@ -147,6 +152,27 @@ program
147
152
  }
148
153
  });
149
154
  // ============================================================
155
+ // Subcommand: edsger financing-deck <productId>
156
+ // ============================================================
157
+ program
158
+ .command('financing-deck <productId>')
159
+ .description('Draft an investor pitch deck for a product (AI-generated)')
160
+ .requiredOption('--target-stage <stage>', 'Target stage: pre_seed | seed | series_a | series_b | series_c | growth')
161
+ .option('-v, --verbose', 'Verbose output')
162
+ .action(async (productId, opts) => {
163
+ try {
164
+ await runFinancingDeck({
165
+ financingDeck: productId,
166
+ financingTargetStage: opts.targetStage,
167
+ verbose: opts.verbose,
168
+ });
169
+ }
170
+ catch (error) {
171
+ logError(error instanceof Error ? error.message : String(error));
172
+ process.exit(1);
173
+ }
174
+ });
175
+ // ============================================================
150
176
  // Subcommand: edsger intel <productId>
151
177
  // ============================================================
152
178
  program
@@ -240,18 +266,26 @@ program
240
266
  }
241
267
  });
242
268
  // ============================================================
243
- // Subcommand: edsger checklists <productId>
269
+ // Subcommand: edsger checklists [productId] [--team-id <id>]
244
270
  // ============================================================
245
271
  program
246
- .command('checklists <productId>')
247
- .description('Analyse a local directory and sync product checklists (create/update/delete) using AI')
272
+ .command('checklists [productId]')
273
+ .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
274
  .option('-v, --verbose', 'Verbose output')
249
275
  .option('--dir <path>', 'Directory to analyse (defaults to current working directory)')
250
276
  .option('--prompt <text>', 'Additional instructions for the AI agent')
277
+ .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
278
  .action(async (productId, opts) => {
252
279
  try {
280
+ if (!productId && !opts.teamId) {
281
+ throw new Error('Provide a product ID positionally or pass --team-id <id> for team scope.');
282
+ }
283
+ if (productId && opts.teamId) {
284
+ throw new Error('Pass either a product ID or --team-id, not both.');
285
+ }
253
286
  await runChecklists({
254
287
  checklistsProductId: productId,
288
+ checklistsTeamId: opts.teamId,
255
289
  checklistsDir: opts.dir,
256
290
  checklistsPrompt: opts.prompt,
257
291
  verbose: opts.verbose,
@@ -384,6 +418,41 @@ program
384
418
  }
385
419
  });
386
420
  // ============================================================
421
+ // Subcommand: edsger sync-github-issues <productId>
422
+ // ============================================================
423
+ program
424
+ .command('sync-github-issues <productId>')
425
+ .description("Mirror the product's connected GitHub repo issues into the local issues list (handles new, updated, closed, and deleted)")
426
+ .option('-v, --verbose', 'Verbose output')
427
+ .action(async (productId, opts) => {
428
+ try {
429
+ await runSyncGithubIssues(productId, opts);
430
+ }
431
+ catch (error) {
432
+ logError(error instanceof Error ? error.message : String(error));
433
+ process.exit(1);
434
+ }
435
+ });
436
+ // ============================================================
437
+ // Subcommand: edsger sync-sentry-issues <productId>
438
+ //
439
+ // Requires SENTRY_AUTH_TOKEN, SENTRY_ORG, SENTRY_PROJECT in env (set via
440
+ // `edsger config set`). Optional SENTRY_BASE_URL for self-hosted Sentry.
441
+ // ============================================================
442
+ program
443
+ .command('sync-sentry-issues <productId>')
444
+ .description('Mirror Sentry project issues into the local issues list (requires SENTRY_AUTH_TOKEN, SENTRY_ORG, SENTRY_PROJECT env)')
445
+ .option('-v, --verbose', 'Verbose output')
446
+ .action(async (productId, opts) => {
447
+ try {
448
+ await runSyncSentryIssues(productId, opts);
449
+ }
450
+ catch (error) {
451
+ logError(error instanceof Error ? error.message : String(error));
452
+ process.exit(1);
453
+ }
454
+ });
455
+ // ============================================================
387
456
  // Subcommand: edsger find-features <productId>
388
457
  // ============================================================
389
458
  program
@@ -443,6 +512,31 @@ program
443
512
  }
444
513
  });
445
514
  // ============================================================
515
+ // Subcommand: edsger find-architecture <productId>
516
+ // ============================================================
517
+ program
518
+ .command('find-architecture <productId>')
519
+ .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")
520
+ .option('--full', 'Force a full scan even if previous-scan state exists')
521
+ .option('--branch <name>', 'Branch to scan (defaults to repo default branch)')
522
+ .option('--max-files <n>', `Upper bound on files the auditor may Read (default ${FIND_ARCHITECTURE_DEFAULT_MAX_FILES})`, (value) => {
523
+ const n = parseInt(value, 10);
524
+ if (Number.isNaN(n) || n <= 0) {
525
+ throw new Error('--max-files must be a positive integer');
526
+ }
527
+ return n;
528
+ })
529
+ .option('-v, --verbose', 'Verbose output')
530
+ .action(async (productId, opts) => {
531
+ try {
532
+ await runFindArchitecture(productId, opts);
533
+ }
534
+ catch (error) {
535
+ logError(error instanceof Error ? error.message : String(error));
536
+ process.exit(1);
537
+ }
538
+ });
539
+ // ============================================================
446
540
  // Subcommand: edsger pr-resolve <productId>
447
541
  // ============================================================
448
542
  program
@@ -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 desc = content.input?.description || content.input?.command || 'Running...';
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
  }
@@ -1,7 +1,7 @@
1
1
  import { getAppStorePrimaryLocale, saveAppStoreListings, upsertAppStoreConfig, } from '../../api/app-store.js';
2
2
  import { getGitHubConfigByProduct } from '../../api/github.js';
3
3
  import { logError, logInfo, logSuccess } from '../../utils/logger.js';
4
- import { cloneIssueRepo, ensureWorkspaceDir, } from '../../workspace/workspace-manager.js';
4
+ import { cleanupIssueRepo, cloneIssueRepo, ensureWorkspaceDir, } from '../../workspace/workspace-manager.js';
5
5
  import { executeAppStoreQuery } from './agent.js';
6
6
  import { prepareAppStoreContext } from './context.js';
7
7
  import { createAppStoreSystemPrompt } from './prompts.js';
@@ -13,13 +13,14 @@ function truncateKeywords(keywords) {
13
13
  }
14
14
  return keywords.slice(0, 100).replace(/,[^,]*$/, '');
15
15
  }
16
- // eslint-disable-next-line complexity
17
16
  export const generateAppStoreAssets = async (options, config) => {
18
17
  const { productId, targetStore, screenshotsOnly, listingsOnly, verbose } = options;
19
18
  if (verbose) {
20
19
  logInfo(`Starting app store generation for product: ${productId}`);
21
20
  logInfo(` Target store: ${targetStore}`);
22
21
  }
22
+ let repoCwd;
23
+ let generationSucceeded = false;
23
24
  try {
24
25
  // Resolve locale: use explicit param, or fetch from Apple App Store Connect
25
26
  let { locale } = options;
@@ -48,7 +49,8 @@ export const generateAppStoreAssets = async (options, config) => {
48
49
  'Please configure a GitHub repo in the product settings before generating assets.');
49
50
  }
50
51
  const workspaceRoot = ensureWorkspaceDir();
51
- const { repoPath: repoCwd } = cloneIssueRepo(workspaceRoot, `app-store-${productId}`, githubConfig.owner, githubConfig.repo, githubConfig.token);
52
+ const cloned = cloneIssueRepo(workspaceRoot, `app-store-${productId}`, githubConfig.owner, githubConfig.repo, githubConfig.token);
53
+ repoCwd = cloned.repoPath;
52
54
  logInfo(`Repository cloned to: ${repoCwd}`);
53
55
  // Prepare context and prompt
54
56
  const { context, prompt: analysisPrompt } = await prepareAppStoreContext(productId, targetStore, locale, verbose, true);
@@ -126,6 +128,7 @@ export const generateAppStoreAssets = async (options, config) => {
126
128
  }
127
129
  }
128
130
  const totalScreenshots = Object.values(savedScreenshots).flat().length;
131
+ generationSucceeded = true;
129
132
  return {
130
133
  productId,
131
134
  status: 'success',
@@ -146,4 +149,9 @@ export const generateAppStoreAssets = async (options, config) => {
146
149
  configIds: [],
147
150
  };
148
151
  }
152
+ finally {
153
+ if (generationSucceeded) {
154
+ cleanupIssueRepo(repoCwd);
155
+ }
156
+ }
149
157
  };
@@ -12,6 +12,7 @@ import { getIssue } from '../../api/issues/get-issue.js';
12
12
  import { DEFAULT_MODEL } from '../../constants.js';
13
13
  import { logIssuePhaseEvent } from '../../services/audit-logs.js';
14
14
  import { createBranches, getCurrentBranch, updateBranch, } from '../../services/branches.js';
15
+ import { getDefaultBranchForIssue } from '../../services/repo-config.js';
15
16
  import { prepareCustomBranchGitEnvironmentAsync, resetUncommittedChanges, } from '../../utils/git-branch-manager.js';
16
17
  import { gitPush } from '../../utils/git-push.js';
17
18
  import { logDebug, logError, logInfo } from '../../utils/logger.js';
@@ -128,7 +129,7 @@ function parseIterationResult(responseText) {
128
129
  * Handle a successful iteration: push, create PR, and log
129
130
  */
130
131
  async function handleSuccessfulIteration(opts) {
131
- const { issueId, devBranchName, currentBranch, iterationResult, totalIterations, iterationDuration, startTime, endTime, existingPrUrl, verbose, } = opts;
132
+ const { issueId, devBranchName, currentBranch, iterationResult, totalIterations, iterationDuration, startTime, endTime, existingPrUrl, defaultBranch, verbose, } = opts;
132
133
  // Get GitHub config for push authentication and PR creation
133
134
  const githubConfig = await getGitHubConfig(issueId, verbose);
134
135
  // Push to remote with authentication
@@ -144,7 +145,7 @@ async function handleSuccessfulIteration(opts) {
144
145
  let prUrl = existingPrUrl;
145
146
  let prNumber;
146
147
  if (!prUrl) {
147
- const prInfo = await createAutonomousPR(githubConfig, devBranchName, currentBranch, verbose);
148
+ const prInfo = await createAutonomousPR(githubConfig, devBranchName, currentBranch, defaultBranch, verbose);
148
149
  ({ prUrl, prNumber } = prInfo);
149
150
  }
150
151
  // Log iteration completion to audit logs
@@ -173,7 +174,7 @@ async function handleSuccessfulIteration(opts) {
173
174
  */
174
175
  async function createAutonomousPR(
175
176
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
176
- githubConfig, devBranchName, currentBranch, verbose) {
177
+ githubConfig, devBranchName, currentBranch, defaultBranch, verbose) {
177
178
  if (!githubConfig.configured ||
178
179
  !githubConfig.token ||
179
180
  !githubConfig.owner ||
@@ -190,7 +191,7 @@ githubConfig, devBranchName, currentBranch, verbose) {
190
191
  verbose,
191
192
  };
192
193
  logInfo(`📝 Creating pull request for autonomous work...`);
193
- const prResult = await createBranchPullRequest(prConfig, devBranchName, currentBranch.name, currentBranch.description || 'Autonomous development implementation', 'main');
194
+ const prResult = await createBranchPullRequest(prConfig, devBranchName, currentBranch.name, currentBranch.description || 'Autonomous development implementation', defaultBranch);
194
195
  if (prResult.success) {
195
196
  logInfo(`✅ Pull request created: ${prResult.pullRequestUrl}`);
196
197
  return {
@@ -299,10 +300,11 @@ export async function runAutonomousDevelopment(options, config, _checklistContex
299
300
  }
300
301
  }
301
302
  }
302
- // 4. Prepare git environment (checkout branch + rebase with main)
303
+ // 4. Prepare git environment (checkout branch + rebase with default branch)
304
+ const defaultBranch = await getDefaultBranchForIssue(issueId, verbose);
303
305
  const { cleanup: cleanupGit } = await prepareCustomBranchGitEnvironmentAsync({
304
306
  issueBranch: devBranchName,
305
- baseBranch: 'main',
307
+ baseBranch: defaultBranch,
306
308
  verbose,
307
309
  resolveConflicts: true,
308
310
  conflictResolverConfig: {
@@ -334,6 +336,7 @@ export async function runAutonomousDevelopment(options, config, _checklistContex
334
336
  startTime,
335
337
  endTime,
336
338
  existingPrUrl: prUrl,
339
+ defaultBranch,
337
340
  verbose,
338
341
  });
339
342
  prUrl = prInfo.prUrl || prUrl;
@@ -116,7 +116,6 @@ async function persistBranches(sortedBranches, issueId, verbose) {
116
116
  }
117
117
  }
118
118
  }
119
- // eslint-disable-next-line complexity
120
119
  export const planIssueBranches = async (options, config) => {
121
120
  const { issueId, verbose, replaceExisting } = options;
122
121
  if (verbose) {
@@ -169,7 +168,7 @@ export const planIssueBranches = async (options, config) => {
169
168
  // Build the user prompt based on mode
170
169
  const contextInfo = formatContextForPrompt(context.issue, context.product, context.user_stories, context.test_cases);
171
170
  const existingBranchesInfo = formatExistingBranchesForPrompt(context.existing_branches);
172
- const systemPrompt = await createBranchPlanningSystemPrompt(config, issueId);
171
+ const systemPrompt = await createBranchPlanningSystemPrompt(config, issueId, undefined, verbose);
173
172
  const userPrompt = await buildUserPrompt({
174
173
  isIncrementalUpdate,
175
174
  feedbacksContext,
@@ -2,7 +2,7 @@ import { type EdsgerConfig } from '../../types/index.js';
2
2
  /**
3
3
  * Create the system prompt for branch planning
4
4
  */
5
- export declare function createBranchPlanningSystemPrompt(config: EdsgerConfig, issueId: string, projectDir?: string): Promise<string>;
5
+ export declare function createBranchPlanningSystemPrompt(config: EdsgerConfig, issueId: string, projectDir?: string, verbose?: boolean): Promise<string>;
6
6
  /**
7
7
  * Create the user prompt with context for branch planning
8
8
  */