edsger 0.51.0 → 0.53.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 (188) hide show
  1. package/.claude/settings.local.json +23 -3
  2. package/.env.local +12 -0
  3. package/dist/commands/find-smells/index.d.ts +21 -0
  4. package/dist/commands/find-smells/index.js +65 -0
  5. package/dist/index.js +29 -0
  6. package/dist/phases/find-bugs/index.js +7 -92
  7. package/dist/phases/find-bugs/state.d.ts +10 -35
  8. package/dist/phases/find-bugs/state.js +12 -120
  9. package/dist/phases/find-features/index.js +16 -83
  10. package/dist/phases/find-features/prompts.d.ts +7 -1
  11. package/dist/phases/find-features/prompts.js +31 -11
  12. package/dist/phases/find-features/state.d.ts +15 -19
  13. package/dist/phases/find-features/state.js +17 -89
  14. package/dist/phases/find-features/types.d.ts +1 -1
  15. package/dist/phases/find-shared/git.d.ts +24 -0
  16. package/dist/phases/find-shared/git.js +60 -0
  17. package/dist/phases/find-shared/mcp.d.ts +33 -0
  18. package/dist/phases/find-shared/mcp.js +69 -0
  19. package/dist/phases/find-shared/scan-state.d.ts +33 -0
  20. package/dist/phases/find-shared/scan-state.js +112 -0
  21. package/dist/phases/find-smells/index.d.ts +47 -0
  22. package/dist/phases/find-smells/index.js +278 -0
  23. package/dist/phases/find-smells/prompts.d.ts +30 -0
  24. package/dist/phases/find-smells/prompts.js +129 -0
  25. package/dist/phases/find-smells/state.d.ts +21 -0
  26. package/dist/phases/find-smells/state.js +17 -0
  27. package/dist/phases/find-smells/types.d.ts +51 -0
  28. package/dist/phases/find-smells/types.js +64 -0
  29. package/dist/phases/pr-execution/context.js +40 -32
  30. package/dist/phases/pr-splitting/context.js +18 -13
  31. package/dist/utils/github-repo-info.d.ts +13 -1
  32. package/dist/utils/github-repo-info.js +32 -6
  33. package/package.json +1 -1
  34. package/vitest.config.ts +2 -0
  35. package/dist/api/__tests__/app-store.test.d.ts +0 -7
  36. package/dist/api/__tests__/app-store.test.js +0 -60
  37. package/dist/api/__tests__/intelligence.test.d.ts +0 -11
  38. package/dist/api/__tests__/intelligence.test.js +0 -315
  39. package/dist/api/features/__tests__/feature-utils.test.d.ts +0 -4
  40. package/dist/api/features/__tests__/feature-utils.test.js +0 -370
  41. package/dist/api/features/__tests__/status-updater.test.d.ts +0 -4
  42. package/dist/api/features/__tests__/status-updater.test.js +0 -88
  43. package/dist/api/features/approval-checker.d.ts +0 -20
  44. package/dist/api/features/approval-checker.js +0 -152
  45. package/dist/api/features/batch-operations.d.ts +0 -17
  46. package/dist/api/features/batch-operations.js +0 -100
  47. package/dist/api/features/feature-utils.d.ts +0 -23
  48. package/dist/api/features/feature-utils.js +0 -80
  49. package/dist/api/features/get-feature.d.ts +0 -5
  50. package/dist/api/features/get-feature.js +0 -21
  51. package/dist/api/features/index.d.ts +0 -8
  52. package/dist/api/features/index.js +0 -10
  53. package/dist/api/features/status-updater.d.ts +0 -41
  54. package/dist/api/features/status-updater.js +0 -122
  55. package/dist/api/features/test-cases.d.ts +0 -29
  56. package/dist/api/features/test-cases.js +0 -110
  57. package/dist/api/features/update-feature.d.ts +0 -20
  58. package/dist/api/features/update-feature.js +0 -83
  59. package/dist/api/features/user-stories.d.ts +0 -21
  60. package/dist/api/features/user-stories.js +0 -88
  61. package/dist/commands/agent-workflow/feature-worker.d.ts +0 -14
  62. package/dist/commands/agent-workflow/feature-worker.js +0 -65
  63. package/dist/commands/build/__tests__/build.test.d.ts +0 -5
  64. package/dist/commands/build/__tests__/build.test.js +0 -206
  65. package/dist/commands/build/__tests__/detect-project.test.d.ts +0 -6
  66. package/dist/commands/build/__tests__/detect-project.test.js +0 -160
  67. package/dist/commands/build/__tests__/run-build.test.d.ts +0 -6
  68. package/dist/commands/build/__tests__/run-build.test.js +0 -433
  69. package/dist/commands/intelligence/__tests__/command.test.d.ts +0 -4
  70. package/dist/commands/intelligence/__tests__/command.test.js +0 -48
  71. package/dist/commands/workflow/core/__tests__/feature-filter.test.d.ts +0 -5
  72. package/dist/commands/workflow/core/__tests__/feature-filter.test.js +0 -316
  73. package/dist/commands/workflow/core/__tests__/pipeline-evaluator.test.d.ts +0 -4
  74. package/dist/commands/workflow/core/__tests__/pipeline-evaluator.test.js +0 -397
  75. package/dist/commands/workflow/core/__tests__/state-manager.test.d.ts +0 -4
  76. package/dist/commands/workflow/core/__tests__/state-manager.test.js +0 -384
  77. package/dist/commands/workflow/core/feature-filter.d.ts +0 -16
  78. package/dist/commands/workflow/core/feature-filter.js +0 -47
  79. package/dist/commands/workflow/feature-coordinator.d.ts +0 -18
  80. package/dist/commands/workflow/feature-coordinator.js +0 -161
  81. package/dist/config/__tests__/config.test.d.ts +0 -4
  82. package/dist/config/__tests__/config.test.js +0 -286
  83. package/dist/config/__tests__/feature-status.test.d.ts +0 -4
  84. package/dist/config/__tests__/feature-status.test.js +0 -111
  85. package/dist/config/feature-status.d.ts +0 -56
  86. package/dist/config/feature-status.js +0 -130
  87. package/dist/errors/__tests__/index.test.d.ts +0 -4
  88. package/dist/errors/__tests__/index.test.js +0 -349
  89. package/dist/phases/app-store-generation/__tests__/agent.test.d.ts +0 -5
  90. package/dist/phases/app-store-generation/__tests__/agent.test.js +0 -142
  91. package/dist/phases/app-store-generation/__tests__/context.test.d.ts +0 -4
  92. package/dist/phases/app-store-generation/__tests__/context.test.js +0 -284
  93. package/dist/phases/app-store-generation/__tests__/prompts.test.d.ts +0 -4
  94. package/dist/phases/app-store-generation/__tests__/prompts.test.js +0 -122
  95. package/dist/phases/app-store-generation/__tests__/screenshot-composer.test.d.ts +0 -5
  96. package/dist/phases/app-store-generation/__tests__/screenshot-composer.test.js +0 -826
  97. package/dist/phases/code-review/__tests__/diff-utils.test.d.ts +0 -1
  98. package/dist/phases/code-review/__tests__/diff-utils.test.js +0 -101
  99. package/dist/phases/feature-analysis/agent.d.ts +0 -13
  100. package/dist/phases/feature-analysis/agent.js +0 -112
  101. package/dist/phases/feature-analysis/context.d.ts +0 -24
  102. package/dist/phases/feature-analysis/context.js +0 -138
  103. package/dist/phases/feature-analysis/index.d.ts +0 -8
  104. package/dist/phases/feature-analysis/index.js +0 -199
  105. package/dist/phases/feature-analysis/outcome.d.ts +0 -40
  106. package/dist/phases/feature-analysis/outcome.js +0 -280
  107. package/dist/phases/feature-analysis/prompts.d.ts +0 -10
  108. package/dist/phases/feature-analysis/prompts.js +0 -212
  109. package/dist/phases/feature-analysis-verification/agent.d.ts +0 -33
  110. package/dist/phases/feature-analysis-verification/agent.js +0 -124
  111. package/dist/phases/feature-analysis-verification/index.d.ts +0 -25
  112. package/dist/phases/feature-analysis-verification/index.js +0 -92
  113. package/dist/phases/feature-analysis-verification/prompts.d.ts +0 -10
  114. package/dist/phases/feature-analysis-verification/prompts.js +0 -100
  115. package/dist/phases/intelligence-analysis/__tests__/context.test.d.ts +0 -4
  116. package/dist/phases/intelligence-analysis/__tests__/context.test.js +0 -192
  117. package/dist/phases/intelligence-analysis/__tests__/matching.test.d.ts +0 -13
  118. package/dist/phases/intelligence-analysis/__tests__/matching.test.js +0 -154
  119. package/dist/phases/intelligence-analysis/__tests__/orchestration.test.d.ts +0 -5
  120. package/dist/phases/intelligence-analysis/__tests__/orchestration.test.js +0 -378
  121. package/dist/phases/intelligence-analysis/__tests__/prompts.test.d.ts +0 -4
  122. package/dist/phases/intelligence-analysis/__tests__/prompts.test.js +0 -33
  123. package/dist/phases/pr-execution/__tests__/file-assigner.test.d.ts +0 -1
  124. package/dist/phases/pr-execution/__tests__/file-assigner.test.js +0 -303
  125. package/dist/phases/pr-resolve/__tests__/checklist-learner.test.d.ts +0 -1
  126. package/dist/phases/pr-resolve/__tests__/checklist-learner.test.js +0 -157
  127. package/dist/phases/pr-resolve/__tests__/prompts.test.d.ts +0 -1
  128. package/dist/phases/pr-resolve/__tests__/prompts.test.js +0 -116
  129. package/dist/phases/pr-resolve/__tests__/resolve-mapping.test.d.ts +0 -1
  130. package/dist/phases/pr-resolve/__tests__/resolve-mapping.test.js +0 -138
  131. package/dist/phases/pr-resolve/__tests__/types.test.d.ts +0 -1
  132. package/dist/phases/pr-resolve/__tests__/types.test.js +0 -43
  133. package/dist/phases/pr-resolve/__tests__/workspace.test.d.ts +0 -1
  134. package/dist/phases/pr-resolve/__tests__/workspace.test.js +0 -111
  135. package/dist/phases/pr-review/__tests__/prompts.test.d.ts +0 -1
  136. package/dist/phases/pr-review/__tests__/prompts.test.js +0 -49
  137. package/dist/phases/pr-review/__tests__/review-comments.test.d.ts +0 -1
  138. package/dist/phases/pr-review/__tests__/review-comments.test.js +0 -110
  139. package/dist/phases/pr-shared/__tests__/agent-utils.test.d.ts +0 -1
  140. package/dist/phases/pr-shared/__tests__/agent-utils.test.js +0 -91
  141. package/dist/phases/pr-shared/__tests__/context.test.d.ts +0 -1
  142. package/dist/phases/pr-shared/__tests__/context.test.js +0 -94
  143. package/dist/phases/pr-splitting/__tests__/import-dep-validator.test.d.ts +0 -1
  144. package/dist/phases/pr-splitting/__tests__/import-dep-validator.test.js +0 -331
  145. package/dist/phases/run-sheet/render.d.ts +0 -60
  146. package/dist/phases/run-sheet/render.js +0 -297
  147. package/dist/phases/smoke-test/__tests__/agent.test.d.ts +0 -4
  148. package/dist/phases/smoke-test/__tests__/agent.test.js +0 -84
  149. package/dist/phases/smoke-test/__tests__/github.test.d.ts +0 -9
  150. package/dist/phases/smoke-test/__tests__/github.test.js +0 -120
  151. package/dist/phases/smoke-test/__tests__/snapshot.test.d.ts +0 -8
  152. package/dist/phases/smoke-test/__tests__/snapshot.test.js +0 -93
  153. package/dist/phases/smoke-test/github.d.ts +0 -54
  154. package/dist/phases/smoke-test/github.js +0 -101
  155. package/dist/phases/smoke-test/snapshot.d.ts +0 -27
  156. package/dist/phases/smoke-test/snapshot.js +0 -157
  157. package/dist/services/coaching/__tests__/coaching-agent.test.d.ts +0 -1
  158. package/dist/services/coaching/__tests__/coaching-agent.test.js +0 -74
  159. package/dist/services/coaching/__tests__/coaching-loop.test.d.ts +0 -1
  160. package/dist/services/coaching/__tests__/coaching-loop.test.js +0 -59
  161. package/dist/services/coaching/__tests__/self-rating.test.d.ts +0 -1
  162. package/dist/services/coaching/__tests__/self-rating.test.js +0 -188
  163. package/dist/services/lifecycle-agent/__tests__/phase-criteria.test.d.ts +0 -4
  164. package/dist/services/lifecycle-agent/__tests__/phase-criteria.test.js +0 -133
  165. package/dist/services/lifecycle-agent/__tests__/transition-rules.test.d.ts +0 -4
  166. package/dist/services/lifecycle-agent/__tests__/transition-rules.test.js +0 -336
  167. package/dist/services/lifecycle-agent/index.d.ts +0 -24
  168. package/dist/services/lifecycle-agent/index.js +0 -25
  169. package/dist/services/lifecycle-agent/phase-criteria.d.ts +0 -57
  170. package/dist/services/lifecycle-agent/phase-criteria.js +0 -335
  171. package/dist/services/lifecycle-agent/transition-rules.d.ts +0 -60
  172. package/dist/services/lifecycle-agent/transition-rules.js +0 -184
  173. package/dist/services/lifecycle-agent/types.d.ts +0 -190
  174. package/dist/services/lifecycle-agent/types.js +0 -12
  175. package/dist/services/phase-hooks/__tests__/bindings-fetcher.test.d.ts +0 -1
  176. package/dist/services/phase-hooks/__tests__/bindings-fetcher.test.js +0 -122
  177. package/dist/services/phase-hooks/__tests__/hook-executor.test.d.ts +0 -1
  178. package/dist/services/phase-hooks/__tests__/hook-executor.test.js +0 -321
  179. package/dist/services/phase-hooks/__tests__/hook-runner.test.d.ts +0 -1
  180. package/dist/services/phase-hooks/__tests__/hook-runner.test.js +0 -261
  181. package/dist/services/phase-hooks/__tests__/plugin-loader.test.d.ts +0 -1
  182. package/dist/services/phase-hooks/__tests__/plugin-loader.test.js +0 -158
  183. package/dist/services/video/__tests__/video-pipeline.test.d.ts +0 -6
  184. package/dist/services/video/__tests__/video-pipeline.test.js +0 -249
  185. package/dist/types/features.d.ts +0 -35
  186. package/dist/types/features.js +0 -1
  187. package/dist/workspace/__tests__/workspace-manager.test.d.ts +0 -7
  188. package/dist/workspace/__tests__/workspace-manager.test.js +0 -52
@@ -1,25 +0,0 @@
1
- import { type ChecklistPhaseContext } from '../../services/checklist.js';
2
- import { type EdsgerConfig } from '../../types/index.js';
3
- import { type FeatureAnalysisContext } from '../feature-analysis/context.js';
4
- import { type ChecklistVerificationResult } from './agent.js';
5
- interface VerificationCycleResult {
6
- passed: boolean;
7
- verificationResult: ChecklistVerificationResult | null;
8
- nextPrompt?: string;
9
- }
10
- /**
11
- * Perform verification and determine if iteration should continue
12
- * This function only verifies - it does NOT manage artifacts (save/delete/update)
13
- */
14
- export interface FeatureAnalysisVerificationOptions {
15
- structuredAnalysisResult: Record<string, unknown>;
16
- checklistContext: ChecklistPhaseContext | null;
17
- context: FeatureAnalysisContext;
18
- config: EdsgerConfig;
19
- currentIteration: number;
20
- maxIterations: number;
21
- featureId: string;
22
- verbose?: boolean;
23
- }
24
- export declare function performVerificationCycle(opts: FeatureAnalysisVerificationOptions): Promise<VerificationCycleResult>;
25
- export {};
@@ -1,92 +0,0 @@
1
- import { logFeatureVerificationEvent } from '../../services/audit-logs.js';
2
- import { logError, logInfo } from '../../utils/logger.js';
3
- import { createImprovementPrompt } from '../feature-analysis/prompts.js';
4
- import { verifyChecklistCompliance, } from './agent.js';
5
- export async function performVerificationCycle(opts) {
6
- const { structuredAnalysisResult, checklistContext, context, config, currentIteration, maxIterations, featureId, verbose, } = opts;
7
- // No verification needed if no checklist context
8
- if (!checklistContext || checklistContext.checklists.length === 0) {
9
- return { passed: true, verificationResult: null };
10
- }
11
- const createdUserStories = (structuredAnalysisResult.created_user_stories ||
12
- []);
13
- const createdTestCases = (structuredAnalysisResult.created_test_cases ||
14
- []);
15
- if (verbose) {
16
- logInfo('🔍 Starting checklist verification...');
17
- }
18
- const verificationResult = await verifyChecklistCompliance({
19
- checklistContext,
20
- analysisContext: context,
21
- createdUserStories: createdUserStories || [],
22
- createdTestCases: createdTestCases || [],
23
- verbose,
24
- }, config);
25
- // Verification passed
26
- if (verificationResult.rejected_count === 0) {
27
- if (verbose) {
28
- logInfo(`✅ Checklist verification passed: ${verificationResult.confirmed_count} confirmed, ${verificationResult.uncertain_count} uncertain`);
29
- }
30
- // Log verification success
31
- await logFeatureVerificationEvent({
32
- featureId,
33
- phase: 'feature_analysis',
34
- iteration: currentIteration,
35
- result: 'success',
36
- verificationData: {
37
- confirmed_count: verificationResult.confirmed_count,
38
- rejected_count: verificationResult.rejected_count,
39
- uncertain_count: verificationResult.uncertain_count,
40
- summary: verificationResult.summary,
41
- },
42
- }, verbose);
43
- return { passed: true, verificationResult };
44
- }
45
- // Verification failed
46
- logError(`❌ Iteration ${currentIteration}: Checklist verification FAILED - ${verificationResult.rejected_count} items rejected, ${verificationResult.uncertain_count} uncertain`);
47
- // Log verification failure with improvement suggestions
48
- await logFeatureVerificationEvent({
49
- featureId,
50
- phase: 'feature_analysis',
51
- iteration: currentIteration,
52
- result: 'error',
53
- verificationData: {
54
- confirmed_count: verificationResult.confirmed_count,
55
- rejected_count: verificationResult.rejected_count,
56
- uncertain_count: verificationResult.uncertain_count,
57
- rejected_items: verificationResult.item_verifications
58
- .filter((item) => item.verification_status === 'rejected')
59
- .map((item) => ({
60
- checklist_item_id: item.checklist_item_id,
61
- reason: item.verification_reason || 'No reason provided',
62
- concerns: item.concerns || [],
63
- improvement_suggestions: item.improvement_suggestions || [],
64
- })),
65
- uncertain_items: verificationResult.item_verifications
66
- .filter((item) => item.verification_status === 'uncertain')
67
- .map((item) => ({
68
- checklist_item_id: item.checklist_item_id,
69
- reason: item.verification_reason || 'No reason provided',
70
- concerns: item.concerns || [],
71
- improvement_suggestions: item.improvement_suggestions || [],
72
- })),
73
- summary: verificationResult.summary,
74
- overall_suggestions: verificationResult.overall_suggestions || [],
75
- },
76
- }, verbose);
77
- // Check if we can iterate again
78
- if (currentIteration < maxIterations) {
79
- if (verbose) {
80
- logInfo(` Will retry with improvement feedback (${maxIterations - currentIteration} attempts remaining)`);
81
- }
82
- // Create improvement prompt for next iteration
83
- const nextPrompt = createImprovementPrompt(verificationResult, {
84
- created_user_stories: createdUserStories || [],
85
- created_test_cases: createdTestCases || [],
86
- });
87
- return { passed: false, verificationResult, nextPrompt };
88
- }
89
- // Max iterations reached
90
- logError(`❌ Maximum iterations (${maxIterations}) reached. Checklist verification still failing.`);
91
- return { passed: false, verificationResult };
92
- }
@@ -1,10 +0,0 @@
1
- import { type ChecklistPhaseContext } from '../../services/checklist.js';
2
- import { type FeatureAnalysisContext } from '../feature-analysis/context.js';
3
- export declare const createChecklistVerificationSystemPrompt: (projectDir?: string) => Promise<string>;
4
- export interface ChecklistVerificationPromptOptions {
5
- checklistContext: ChecklistPhaseContext;
6
- analysisContext: FeatureAnalysisContext;
7
- createdUserStories: any[];
8
- createdTestCases: any[];
9
- }
10
- export declare const createChecklistVerificationPrompt: (options: ChecklistVerificationPromptOptions) => string;
@@ -1,100 +0,0 @@
1
- import { resolveSkill } from '../../services/skill-resolver.js';
2
- import { OUTPUT_CONTRACTS } from '../output-contracts.js';
3
- export const createChecklistVerificationSystemPrompt = async (projectDir) => {
4
- const skill = await resolveSkill('phase/feature-analysis-verification', {
5
- projectDir,
6
- });
7
- if (!skill) {
8
- throw new Error('Failed to load skill: phase/feature-analysis-verification');
9
- }
10
- return `${skill.prompt}\n\n${OUTPUT_CONTRACTS['feature-analysis-verification']}`;
11
- };
12
- export const createChecklistVerificationPrompt = (options) => {
13
- const { checklistContext, analysisContext, createdUserStories, createdTestCases, } = options;
14
- // Format all checklist items with their requirements
15
- const checklistItemsSection = checklistContext.checklists
16
- .flatMap((checklist) => checklist.items.map((item) => {
17
- return `### Checklist Item: ${item.title}
18
- **ID**: ${item.id}
19
- **Checklist**: ${checklist.name} (${checklist.role})
20
- **Requirement**: ${item.description}
21
- **Required**: ${item.is_required ? 'Yes' : 'No'}
22
- **Type**: ${item.item_type}
23
- `;
24
- }))
25
- .join('\n---\n');
26
- // Format created user stories
27
- const userStoriesSection = createdUserStories.length > 0
28
- ? createdUserStories
29
- .map((story, index) => `${index + 1}. **${story.title}**
30
- ${story.description}
31
- Status: ${story.status || 'draft'}`)
32
- .join('\n\n')
33
- : 'No user stories were created';
34
- // Format created test cases
35
- const testCasesSection = createdTestCases.length > 0
36
- ? createdTestCases
37
- .map((testCase, index) => `${index + 1}. **${testCase.name}** ${testCase.is_critical ? '[CRITICAL]' : '[OPTIONAL]'}
38
- ${testCase.description}`)
39
- .join('\n\n')
40
- : 'No test cases were created';
41
- const totalChecklistItems = checklistContext.checklists.reduce((sum, checklist) => sum + checklist.items.length, 0);
42
- return `# Checklist Verification Task
43
-
44
- You are verifying that generated artifacts (user stories and test cases) satisfy checklist requirements.
45
-
46
- ## Feature Context
47
-
48
- **Feature**: ${analysisContext.feature.name}
49
- **Feature ID**: ${analysisContext.feature.id}
50
- **Description**: ${analysisContext.feature.description || 'No description'}
51
- **Product**: ${analysisContext.product.name}
52
-
53
- ## Generated Artifacts
54
-
55
- ### User Stories (${createdUserStories.length})
56
- ${userStoriesSection}
57
-
58
- ### Test Cases (${createdTestCases.length})
59
- ${testCasesSection}
60
-
61
- ## Checklist Requirements to Verify (${totalChecklistItems})
62
-
63
- ${checklistItemsSection}
64
-
65
- ---
66
-
67
- ## Your Verification Task
68
-
69
- For EACH checklist item above:
70
-
71
- 1. **Understand the Requirement**: What does this checklist item require?
72
-
73
- 2. **Examine the Artifacts**: Look at the user stories and test cases above
74
-
75
- 3. **Determine Coverage**: Do the artifacts adequately address this requirement?
76
- - Do they directly address the requirement?
77
- - Is the coverage concrete and specific?
78
- - Is there sufficient quality and depth?
79
- - Are there any gaps or missing elements?
80
-
81
- **Note**: Verify the actual quality/result, not whether the artifacts mention the requirement.
82
- Example: For "check duplicates" → verify if artifacts are actually duplicate-free, not if they mention checking duplicates.
83
-
84
- 4. **Make Your Determination**:
85
- - **CONFIRMED** (is_satisfied: true): The artifacts clearly and adequately satisfy this requirement
86
- - **REJECTED** (is_satisfied: false): The requirement is not addressed, or addressed insufficiently
87
- - **UNCERTAIN** (is_satisfied: false): The artifacts partially address the requirement but with gaps
88
-
89
- 5. **Document Your Reasoning**: Provide specific, detailed reasons for your decision
90
-
91
- 6. **Provide Improvement Suggestions** (for rejected/uncertain items):
92
- - Be specific and actionable
93
- - Suggest what user stories or test cases should be added
94
- - Indicate what should be expanded or modified
95
- - Give concrete examples when possible
96
-
97
- Remember: Focus on what the artifacts actually contain, not on claims. Be thorough and fair.
98
-
99
- Provide your verification results in the required JSON format with improvement_suggestions for any rejected or uncertain items.`;
100
- };
@@ -1,4 +0,0 @@
1
- /**
2
- * Unit tests for intelligence analysis context formatting.
3
- */
4
- export {};
@@ -1,192 +0,0 @@
1
- /**
2
- * Unit tests for intelligence analysis context formatting.
3
- */
4
- import assert from 'node:assert';
5
- import { describe, it } from 'node:test';
6
- import { formatContextForPrompt, } from '../context.js';
7
- const makeContext = (overrides = {}) => ({
8
- product: {
9
- id: 'prod-001',
10
- name: 'Edsger',
11
- description: 'AI-powered software development automation.',
12
- features: [
13
- {
14
- name: 'Code Generation',
15
- description: 'AI writes code',
16
- status: 'shipped',
17
- },
18
- {
19
- name: 'Growth Analysis',
20
- description: 'Marketing automation',
21
- status: 'shipped',
22
- },
23
- ],
24
- },
25
- confirmedCompetitors: [],
26
- suggestedCompetitors: [],
27
- previousSnapshots: new Map(),
28
- ...overrides,
29
- });
30
- const makeCompetitor = (name, id = `comp-${name.toLowerCase()}`) => ({
31
- id,
32
- product_id: 'prod-001',
33
- name,
34
- url: `https://${name.toLowerCase()}.com`,
35
- app_store_url: null,
36
- play_store_url: null,
37
- description: `${name} description`,
38
- category: 'developer-tools',
39
- status: 'confirmed',
40
- discovery_source: 'ai_discovery',
41
- discovery_reason: 'Frequently compared',
42
- notes: null,
43
- created_by: 'user-1',
44
- created_at: '2026-03-25',
45
- updated_at: '2026-03-25',
46
- });
47
- // ============================================================
48
- // Product info in context
49
- // ============================================================
50
- void describe('formatContextForPrompt — product info', () => {
51
- void it('should include product name', () => {
52
- const result = formatContextForPrompt(makeContext());
53
- assert.ok(result.includes('Edsger'));
54
- });
55
- void it('should include product description', () => {
56
- const result = formatContextForPrompt(makeContext());
57
- assert.ok(result.includes('AI-powered software development automation'));
58
- });
59
- void it('should include product ID', () => {
60
- const result = formatContextForPrompt(makeContext());
61
- assert.ok(result.includes('prod-001'));
62
- });
63
- void it('should list features with bold names', () => {
64
- const result = formatContextForPrompt(makeContext());
65
- assert.ok(result.includes('**Code Generation**'));
66
- assert.ok(result.includes('**Growth Analysis**'));
67
- });
68
- void it('should show feature count', () => {
69
- const result = formatContextForPrompt(makeContext());
70
- assert.ok(result.includes('Product Features (2)'));
71
- });
72
- void it('should handle empty features', () => {
73
- const ctx = makeContext({
74
- product: { id: 'p1', name: 'X', description: '', features: [] },
75
- });
76
- const result = formatContextForPrompt(ctx);
77
- assert.ok(result.includes('No features listed'));
78
- });
79
- void it('should handle missing description', () => {
80
- const ctx = makeContext({
81
- product: {
82
- id: 'p1',
83
- name: 'X',
84
- description: undefined,
85
- features: [],
86
- },
87
- });
88
- const result = formatContextForPrompt(ctx);
89
- assert.ok(result.includes('No description provided'));
90
- });
91
- });
92
- // ============================================================
93
- // No competitors — discovery prompt
94
- // ============================================================
95
- void describe('formatContextForPrompt — no competitors', () => {
96
- void it('should indicate no competitors registered', () => {
97
- const result = formatContextForPrompt(makeContext());
98
- assert.ok(result.includes('No competitors registered yet'));
99
- });
100
- void it('should instruct to discover competitors', () => {
101
- const result = formatContextForPrompt(makeContext());
102
- assert.ok(result.includes('discover'));
103
- });
104
- });
105
- // ============================================================
106
- // With confirmed competitors
107
- // ============================================================
108
- void describe('formatContextForPrompt — with confirmed competitors', () => {
109
- const competitor = makeCompetitor('Linear');
110
- void it('should show competitor name as heading', () => {
111
- const ctx = makeContext({ confirmedCompetitors: [competitor] });
112
- const result = formatContextForPrompt(ctx);
113
- assert.ok(result.includes('### Linear'));
114
- });
115
- void it('should show competitor ID', () => {
116
- const ctx = makeContext({ confirmedCompetitors: [competitor] });
117
- const result = formatContextForPrompt(ctx);
118
- assert.ok(result.includes('comp-linear'));
119
- });
120
- void it('should show competitor URL', () => {
121
- const ctx = makeContext({ confirmedCompetitors: [competitor] });
122
- const result = formatContextForPrompt(ctx);
123
- assert.ok(result.includes('https://linear.com'));
124
- });
125
- void it('should show confirmed count', () => {
126
- const ctx = makeContext({
127
- confirmedCompetitors: [makeCompetitor('A'), makeCompetitor('B')],
128
- });
129
- const result = formatContextForPrompt(ctx);
130
- assert.ok(result.includes('Confirmed Competitors (2)'));
131
- });
132
- void it('should show N/A for missing app store URLs', () => {
133
- const ctx = makeContext({ confirmedCompetitors: [competitor] });
134
- const result = formatContextForPrompt(ctx);
135
- assert.ok(result.includes('App Store**: N/A'));
136
- });
137
- void it('should include snapshot data when available', () => {
138
- const snapshots = new Map();
139
- snapshots.set('comp-linear', [
140
- {
141
- id: 'snap-1',
142
- competitor_id: 'comp-linear',
143
- product_id: 'prod-001',
144
- features: [
145
- { name: 'Issues', description: 'Issue tracking', is_new: false },
146
- ],
147
- pricing: { model: 'freemium' },
148
- tech_stack: [],
149
- app_rating: 4.5,
150
- app_review_count: 1200,
151
- app_version: '2.0',
152
- app_last_updated: null,
153
- recent_reviews: [],
154
- social_mentions: {},
155
- changes_detected: [],
156
- source: 'ai_analysis',
157
- raw_data: null,
158
- created_at: '2026-03-25',
159
- },
160
- ]);
161
- const ctx = makeContext({
162
- confirmedCompetitors: [competitor],
163
- previousSnapshots: snapshots,
164
- });
165
- const result = formatContextForPrompt(ctx);
166
- assert.ok(result.includes('Rating: 4.5'));
167
- assert.ok(result.includes('Reviews: 1200'));
168
- assert.ok(result.includes('Pricing model: freemium'));
169
- });
170
- void it('should show "No previous data" without snapshots', () => {
171
- const ctx = makeContext({ confirmedCompetitors: [competitor] });
172
- const result = formatContextForPrompt(ctx);
173
- assert.ok(result.includes('No previous data'));
174
- });
175
- });
176
- // ============================================================
177
- // Human guidance
178
- // ============================================================
179
- void describe('formatContextForPrompt — guidance', () => {
180
- void it('should include guidance when provided', () => {
181
- const result = formatContextForPrompt(makeContext(), 'Focus on pricing changes');
182
- assert.ok(result.includes('Focus on pricing changes'));
183
- });
184
- void it('should have priority callout for guidance', () => {
185
- const result = formatContextForPrompt(makeContext(), 'Focus on pricing');
186
- assert.ok(result.includes('Follow this guidance closely'));
187
- });
188
- void it('should not include guidance section when absent', () => {
189
- const result = formatContextForPrompt(makeContext());
190
- assert.ok(!result.includes('Human Analysis Guidance'));
191
- });
192
- });
@@ -1,13 +0,0 @@
1
- /**
2
- * Unit tests for competitor snapshot matching logic.
3
- *
4
- * Tests the PRODUCTION resolveCompetitorId and buildCompetitorIndex
5
- * functions exported from index.ts.
6
- *
7
- * The matching uses a 4-tier strategy:
8
- * 1. Direct competitor_id match
9
- * 2. Exact name match (case-insensitive)
10
- * 3. Normalized name match (strip suffixes like "App", "Inc")
11
- * 4. Substring match
12
- */
13
- export {};
@@ -1,154 +0,0 @@
1
- /**
2
- * Unit tests for competitor snapshot matching logic.
3
- *
4
- * Tests the PRODUCTION resolveCompetitorId and buildCompetitorIndex
5
- * functions exported from index.ts.
6
- *
7
- * The matching uses a 4-tier strategy:
8
- * 1. Direct competitor_id match
9
- * 2. Exact name match (case-insensitive)
10
- * 3. Normalized name match (strip suffixes like "App", "Inc")
11
- * 4. Substring match
12
- */
13
- import assert from 'node:assert';
14
- import { describe, it } from 'node:test';
15
- import { buildCompetitorIndex, resolveCompetitorId } from '../index.js';
16
- const competitors = [
17
- { id: 'id-linear', name: 'Linear' },
18
- { id: 'id-cursor', name: 'Cursor' },
19
- { id: 'id-github-copilot', name: 'GitHub Copilot' },
20
- { id: 'id-vercel', name: 'Vercel Inc' },
21
- ];
22
- const { idSet, nameToId } = buildCompetitorIndex(competitors);
23
- // ============================================================
24
- // buildCompetitorIndex
25
- // ============================================================
26
- void describe('buildCompetitorIndex', () => {
27
- void it('should populate idSet with all competitor IDs', () => {
28
- assert.strictEqual(idSet.size, 4);
29
- assert.ok(idSet.has('id-linear'));
30
- assert.ok(idSet.has('id-cursor'));
31
- assert.ok(idSet.has('id-github-copilot'));
32
- assert.ok(idSet.has('id-vercel'));
33
- });
34
- void it('should index exact names (lowercased)', () => {
35
- assert.strictEqual(nameToId.get('linear'), 'id-linear');
36
- assert.strictEqual(nameToId.get('cursor'), 'id-cursor');
37
- assert.strictEqual(nameToId.get('github copilot'), 'id-github-copilot');
38
- });
39
- void it('should index normalized names (suffix stripped)', () => {
40
- // "Vercel Inc" → "vercel" should also be indexed
41
- assert.strictEqual(nameToId.get('vercel'), 'id-vercel');
42
- });
43
- void it('should not double-index when name has no suffix', () => {
44
- // "Linear" has no suffix to strip, so only "linear" should be in map
45
- // Count entries for id-linear
46
- let count = 0;
47
- for (const [, id] of nameToId) {
48
- if (id === 'id-linear') {
49
- count++;
50
- }
51
- }
52
- assert.strictEqual(count, 1);
53
- });
54
- void it('should handle empty list', () => {
55
- const { idSet: emptyIds, nameToId: emptyNames } = buildCompetitorIndex([]);
56
- assert.strictEqual(emptyIds.size, 0);
57
- assert.strictEqual(emptyNames.size, 0);
58
- });
59
- });
60
- // ============================================================
61
- // Tier 1: Direct ID match
62
- // ============================================================
63
- void describe('resolveCompetitorId — direct ID match', () => {
64
- void it('should match by exact competitor_id', () => {
65
- const result = resolveCompetitorId({ competitor_id: 'id-linear', competitor_name: 'Whatever' }, idSet, nameToId);
66
- assert.strictEqual(result, 'id-linear');
67
- });
68
- void it('should ignore invalid competitor_id and fall through to name', () => {
69
- const result = resolveCompetitorId({ competitor_id: 'id-nonexistent', competitor_name: 'Linear' }, idSet, nameToId);
70
- assert.strictEqual(result, 'id-linear');
71
- });
72
- void it('should handle undefined competitor_id', () => {
73
- const result = resolveCompetitorId({ competitor_name: 'Linear' }, idSet, nameToId);
74
- assert.strictEqual(result, 'id-linear');
75
- });
76
- void it('should prefer ID over name when both match different competitors', () => {
77
- // If AI returns id for Cursor but name "Linear", ID wins
78
- const result = resolveCompetitorId({ competitor_id: 'id-cursor', competitor_name: 'Linear' }, idSet, nameToId);
79
- assert.strictEqual(result, 'id-cursor');
80
- });
81
- });
82
- // ============================================================
83
- // Tier 2: Exact name match (case-insensitive)
84
- // ============================================================
85
- void describe('resolveCompetitorId — exact name match', () => {
86
- void it('should match exact name', () => {
87
- assert.strictEqual(resolveCompetitorId({ competitor_name: 'Linear' }, idSet, nameToId), 'id-linear');
88
- });
89
- void it('should match case-insensitively', () => {
90
- assert.strictEqual(resolveCompetitorId({ competitor_name: 'linear' }, idSet, nameToId), 'id-linear');
91
- assert.strictEqual(resolveCompetitorId({ competitor_name: 'LINEAR' }, idSet, nameToId), 'id-linear');
92
- });
93
- void it('should match multi-word names', () => {
94
- assert.strictEqual(resolveCompetitorId({ competitor_name: 'GitHub Copilot' }, idSet, nameToId), 'id-github-copilot');
95
- });
96
- void it('should match multi-word names case-insensitively', () => {
97
- assert.strictEqual(resolveCompetitorId({ competitor_name: 'github copilot' }, idSet, nameToId), 'id-github-copilot');
98
- });
99
- });
100
- // ============================================================
101
- // Tier 3: Normalized name match (strip suffixes)
102
- // ============================================================
103
- void describe('resolveCompetitorId — normalized name match', () => {
104
- void it('should match "Vercel" against "Vercel Inc"', () => {
105
- assert.strictEqual(resolveCompetitorId({ competitor_name: 'Vercel' }, idSet, nameToId), 'id-vercel');
106
- });
107
- void it('should match "Linear App" against "Linear"', () => {
108
- assert.strictEqual(resolveCompetitorId({ competitor_name: 'Linear App' }, idSet, nameToId), 'id-linear');
109
- });
110
- void it('should match with ".io" suffix stripped', () => {
111
- const { idSet: ids, nameToId: names } = buildCompetitorIndex([
112
- { id: 'id-render', name: 'Render.io' },
113
- ]);
114
- assert.strictEqual(resolveCompetitorId({ competitor_name: 'Render' }, ids, names), 'id-render');
115
- });
116
- void it('should match with ".com" suffix stripped', () => {
117
- const { idSet: ids, nameToId: names } = buildCompetitorIndex([
118
- { id: 'id-netlify', name: 'Netlify.com' },
119
- ]);
120
- assert.strictEqual(resolveCompetitorId({ competitor_name: 'Netlify' }, ids, names), 'id-netlify');
121
- });
122
- void it('should match with "Ltd" suffix stripped', () => {
123
- const { idSet: ids, nameToId: names } = buildCompetitorIndex([
124
- { id: 'id-acme', name: 'Acme Ltd' },
125
- ]);
126
- assert.strictEqual(resolveCompetitorId({ competitor_name: 'Acme' }, ids, names), 'id-acme');
127
- });
128
- });
129
- // ============================================================
130
- // Tier 4: Substring match
131
- // ============================================================
132
- void describe('resolveCompetitorId — substring match', () => {
133
- void it('should match when snapshot name contains competitor name', () => {
134
- assert.strictEqual(resolveCompetitorId({ competitor_name: 'Cursor AI Editor' }, idSet, nameToId), 'id-cursor');
135
- });
136
- void it('should match when competitor name contains snapshot name', () => {
137
- const { idSet: ids, nameToId: names } = buildCompetitorIndex([
138
- { id: 'id-ghc', name: 'GitHub Copilot Enterprise' },
139
- ]);
140
- assert.strictEqual(resolveCompetitorId({ competitor_name: 'github copilot' }, ids, names), 'id-ghc');
141
- });
142
- });
143
- // ============================================================
144
- // No match
145
- // ============================================================
146
- void describe('resolveCompetitorId — no match', () => {
147
- void it('should return undefined for unknown competitor', () => {
148
- assert.strictEqual(resolveCompetitorId({ competitor_name: 'Completely Unknown Tool' }, idSet, nameToId), undefined);
149
- });
150
- void it('should return undefined with empty index', () => {
151
- const { idSet: ids, nameToId: names } = buildCompetitorIndex([]);
152
- assert.strictEqual(resolveCompetitorId({ competitor_name: 'Linear' }, ids, names), undefined);
153
- });
154
- });
@@ -1,5 +0,0 @@
1
- /**
2
- * Integration tests for analyseIntelligence orchestration.
3
- * Uses dependency injection to mock API calls and AI agent.
4
- */
5
- export {};