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.
Files changed (142) 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/issue-analysis/index.d.ts +5 -0
  20. package/dist/commands/issue-analysis/index.js +9 -0
  21. package/dist/commands/sync-github-issues/index.d.ts +11 -0
  22. package/dist/commands/sync-github-issues/index.js +42 -0
  23. package/dist/commands/sync-sentry-issues/index.d.ts +14 -0
  24. package/dist/commands/sync-sentry-issues/index.js +73 -0
  25. package/dist/commands/technical-design/index.d.ts +5 -0
  26. package/dist/commands/technical-design/index.js +9 -0
  27. package/dist/commands/test-cases-analysis/index.d.ts +5 -0
  28. package/dist/commands/test-cases-analysis/index.js +9 -0
  29. package/dist/commands/user-stories-analysis/index.d.ts +5 -0
  30. package/dist/commands/user-stories-analysis/index.js +9 -0
  31. package/dist/commands/workflow/executors/phase-executor.js +6 -4
  32. package/dist/commands/workflow/phase-orchestrator.js +0 -1
  33. package/dist/config/issue-status.d.ts +18 -45
  34. package/dist/config/issue-status.js +21 -107
  35. package/dist/index.js +176 -4
  36. package/dist/phases/app-store-generation/agent.js +2 -1
  37. package/dist/phases/app-store-generation/index.js +11 -3
  38. package/dist/phases/branch-planning/index.js +0 -1
  39. package/dist/phases/bug-fixing/analyzer.js +0 -1
  40. package/dist/phases/bug-fixing/mcp-server.d.ts +18 -1
  41. package/dist/phases/bug-fixing/mcp-server.js +19 -76
  42. package/dist/phases/chat-processor/product-tools.d.ts +5 -8
  43. package/dist/phases/chat-processor/product-tools.js +6 -512
  44. package/dist/phases/chat-processor/tools.d.ts +5 -9
  45. package/dist/phases/chat-processor/tools.js +6 -704
  46. package/dist/phases/code-implementation/index.js +0 -1
  47. package/dist/phases/code-implementation-verification/agent.js +6 -1
  48. package/dist/phases/code-refine/index.js +0 -1
  49. package/dist/phases/code-refine/refine-iteration.js +2 -1
  50. package/dist/phases/code-review/index.js +0 -1
  51. package/dist/phases/code-testing/analyzer.js +0 -1
  52. package/dist/phases/financing-deck/agent.d.ts +1 -0
  53. package/dist/phases/financing-deck/agent.js +96 -0
  54. package/dist/phases/financing-deck/context.d.ts +13 -0
  55. package/dist/phases/financing-deck/context.js +69 -0
  56. package/dist/phases/financing-deck/index.d.ts +15 -0
  57. package/dist/phases/financing-deck/index.js +89 -0
  58. package/dist/phases/financing-deck/prompts.d.ts +2 -0
  59. package/dist/phases/financing-deck/prompts.js +94 -0
  60. package/dist/phases/find-architecture/index.d.ts +44 -0
  61. package/dist/phases/find-architecture/index.js +248 -0
  62. package/dist/phases/find-architecture/prompts.d.ts +31 -0
  63. package/dist/phases/find-architecture/prompts.js +128 -0
  64. package/dist/phases/find-architecture/state.d.ts +21 -0
  65. package/dist/phases/find-architecture/state.js +17 -0
  66. package/dist/phases/find-architecture/types.d.ts +55 -0
  67. package/dist/phases/find-architecture/types.js +69 -0
  68. package/dist/phases/find-bugs/index.js +13 -4
  69. package/dist/phases/find-features/index.js +10 -5
  70. package/dist/phases/find-smells/index.js +10 -3
  71. package/dist/phases/functional-testing/analyzer.js +27 -17
  72. package/dist/phases/functional-testing/http-fallback.d.ts +1 -1
  73. package/dist/phases/functional-testing/http-fallback.js +32 -16
  74. package/dist/phases/functional-testing/mcp-server.d.ts +9 -1
  75. package/dist/phases/functional-testing/mcp-server.js +13 -132
  76. package/dist/phases/growth-analysis/agent.js +2 -2
  77. package/dist/phases/growth-analysis/index.js +9 -3
  78. package/dist/phases/intelligence-analysis/agent.js +2 -2
  79. package/dist/phases/intelligence-analysis/index.js +9 -2
  80. package/dist/phases/issue-analysis/agent.d.ts +9 -1
  81. package/dist/phases/issue-analysis/agent.js +68 -27
  82. package/dist/phases/issue-analysis/context.d.ts +5 -9
  83. package/dist/phases/issue-analysis/context.js +31 -76
  84. package/dist/phases/issue-analysis/index.js +32 -84
  85. package/dist/phases/issue-analysis/outcome.d.ts +3 -33
  86. package/dist/phases/issue-analysis/outcome.js +15 -253
  87. package/dist/phases/issue-analysis/prompts.d.ts +3 -5
  88. package/dist/phases/issue-analysis/prompts.js +45 -158
  89. package/dist/phases/issue-analysis-verification/agent.d.ts +4 -4
  90. package/dist/phases/issue-analysis-verification/agent.js +5 -5
  91. package/dist/phases/issue-analysis-verification/index.d.ts +4 -2
  92. package/dist/phases/issue-analysis-verification/index.js +9 -22
  93. package/dist/phases/issue-analysis-verification/prompts.d.ts +1 -2
  94. package/dist/phases/issue-analysis-verification/prompts.js +21 -46
  95. package/dist/phases/output-contracts.js +66 -78
  96. package/dist/phases/pr-execution/index.js +2 -2
  97. package/dist/phases/pr-resolve/index.js +2 -8
  98. package/dist/phases/pr-splitting/index.js +2 -2
  99. package/dist/phases/release-sync/index.js +52 -43
  100. package/dist/phases/run-sheet/index.js +2 -1
  101. package/dist/phases/smoke-test/agent.js +2 -1
  102. package/dist/phases/smoke-test/index.js +4 -1
  103. package/dist/phases/sync-github-issues/index.d.ts +41 -0
  104. package/dist/phases/sync-github-issues/index.js +187 -0
  105. package/dist/phases/sync-github-issues/state.d.ts +26 -0
  106. package/dist/phases/sync-github-issues/state.js +18 -0
  107. package/dist/phases/sync-github-issues/types.d.ts +35 -0
  108. package/dist/phases/sync-github-issues/types.js +6 -0
  109. package/dist/phases/sync-sentry-issues/index.d.ts +29 -0
  110. package/dist/phases/sync-sentry-issues/index.js +153 -0
  111. package/dist/phases/sync-sentry-issues/sentry-client.d.ts +66 -0
  112. package/dist/phases/sync-sentry-issues/sentry-client.js +221 -0
  113. package/dist/phases/sync-sentry-issues/state.d.ts +23 -0
  114. package/dist/phases/sync-sentry-issues/state.js +18 -0
  115. package/dist/phases/sync-sentry-issues/types.d.ts +46 -0
  116. package/dist/phases/sync-sentry-issues/types.js +6 -0
  117. package/dist/phases/sync-shared/mcp.d.ts +81 -0
  118. package/dist/phases/sync-shared/mcp.js +111 -0
  119. package/dist/phases/technical-design/index.js +0 -1
  120. package/dist/phases/test-cases-analysis/agent.js +2 -1
  121. package/dist/phases/test-cases-analysis/index.js +0 -1
  122. package/dist/phases/user-stories-analysis/agent.js +2 -1
  123. package/dist/phases/user-stories-analysis/index.js +0 -1
  124. package/dist/services/coaching/coaching-agent.js +29 -4
  125. package/dist/services/feedbacks.d.ts +1 -1
  126. package/dist/skills/phase/issue-analysis/SKILL.md +48 -92
  127. package/dist/skills/phase/issue-analysis-verification/SKILL.md +46 -31
  128. package/dist/tools/bootstrap.d.ts +45 -0
  129. package/dist/tools/bootstrap.js +50 -0
  130. package/dist/types/external-sources.d.ts +22 -0
  131. package/dist/types/external-sources.js +23 -0
  132. package/dist/types/index.d.ts +5 -10
  133. package/dist/types/issues.d.ts +2 -0
  134. package/dist/types/llm-responses.d.ts +1 -14
  135. package/dist/utils/formatters.js +1 -7
  136. package/dist/utils/issue-phase-cli.d.ts +26 -0
  137. package/dist/utils/issue-phase-cli.js +44 -0
  138. package/dist/workspace/workspace-manager.d.ts +10 -0
  139. package/dist/workspace/workspace-manager.js +22 -1
  140. package/package.json +6 -2
  141. package/vitest.config.ts +4 -0
  142. 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,5 @@
1
+ export interface TechnicalDesignCliOptions {
2
+ issueId: string;
3
+ verbose?: boolean;
4
+ }
5
+ export declare const runTechnicalDesignCommand: (options: TechnicalDesignCliOptions) => Promise<void>;
@@ -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,5 @@
1
+ export interface TestCasesAnalysisCliOptions {
2
+ issueId: string;
3
+ verbose?: boolean;
4
+ }
5
+ export declare const runTestCasesAnalysisCommand: (options: TestCasesAnalysisCliOptions) => Promise<void>;
@@ -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,5 @@
1
+ export interface UserStoriesAnalysisCliOptions {
2
+ issueId: string;
3
+ verbose?: boolean;
4
+ }
5
+ export declare const runUserStoriesAnalysisCommand: (options: UserStoriesAnalysisCliOptions) => Promise<void>;
@@ -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
- 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,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 <productId>
273
+ // Subcommand: edsger checklists [productId] [--team-id <id>]
244
274
  // ============================================================
245
275
  program
246
- .command('checklists <productId>')
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 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
  }