edsger 0.50.0 → 0.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +23 -3
- package/.env.local +12 -0
- package/README.md +25 -25
- package/dist/api/chat.d.ts +7 -7
- package/dist/api/chat.js +13 -13
- package/dist/api/cross-product.d.ts +10 -10
- package/dist/api/cross-product.js +30 -30
- package/dist/api/github.d.ts +5 -5
- package/dist/api/github.js +10 -10
- package/dist/api/intelligence.d.ts +3 -3
- package/dist/api/issues/approval-checker.d.ts +20 -0
- package/dist/api/{features → issues}/approval-checker.js +16 -16
- package/dist/api/issues/get-issue.d.ts +5 -0
- package/dist/api/issues/get-issue.js +21 -0
- package/dist/api/issues/index.d.ts +8 -0
- package/dist/api/issues/index.js +10 -0
- package/dist/api/issues/issue-utils.d.ts +23 -0
- package/dist/api/issues/issue-utils.js +80 -0
- package/dist/api/issues/status-updater.d.ts +41 -0
- package/dist/api/{features → issues}/status-updater.js +23 -23
- package/dist/api/{features → issues}/test-cases.d.ts +7 -7
- package/dist/api/{features → issues}/test-cases.js +12 -12
- package/dist/api/issues/update-issue.d.ts +20 -0
- package/dist/api/{features/update-feature.js → issues/update-issue.js} +22 -22
- package/dist/api/{features → issues}/user-stories.d.ts +5 -5
- package/dist/api/{features → issues}/user-stories.js +8 -8
- package/dist/api/products.d.ts +1 -1
- package/dist/api/tasks.d.ts +1 -1
- package/dist/api/test-reports.d.ts +2 -2
- package/dist/api/test-reports.js +4 -4
- package/dist/auth/login.js +1 -1
- package/dist/commands/agent-workflow/chat-worker.d.ts +7 -7
- package/dist/commands/agent-workflow/chat-worker.js +50 -50
- package/dist/commands/agent-workflow/index.d.ts +2 -2
- package/dist/commands/agent-workflow/index.js +3 -3
- package/dist/commands/agent-workflow/{feature-worker.d.ts → issue-worker.d.ts} +4 -4
- package/dist/commands/agent-workflow/{feature-worker.js → issue-worker.js} +12 -12
- package/dist/commands/agent-workflow/processor.d.ts +9 -9
- package/dist/commands/agent-workflow/processor.js +90 -90
- package/dist/commands/build/index.js +2 -2
- package/dist/commands/find-bugs/index.d.ts +11 -0
- package/dist/commands/find-bugs/index.js +39 -0
- package/dist/commands/find-features/index.d.ts +14 -0
- package/dist/commands/find-features/index.js +42 -0
- package/dist/commands/find-smells/index.d.ts +21 -0
- package/dist/commands/find-smells/index.js +65 -0
- package/dist/commands/init/prompts.js +1 -1
- package/dist/commands/init/templates.d.ts +1 -1
- package/dist/commands/init/templates.js +4 -4
- package/dist/commands/workflow/config/phase-configs.js +17 -17
- package/dist/commands/workflow/core/index.d.ts +1 -1
- package/dist/commands/workflow/core/index.js +2 -2
- package/dist/commands/workflow/core/issue-filter.d.ts +16 -0
- package/dist/commands/workflow/core/issue-filter.js +47 -0
- package/dist/commands/workflow/core/state-manager.d.ts +10 -10
- package/dist/commands/workflow/core/state-manager.js +10 -10
- package/dist/commands/workflow/core/workflow-logger.d.ts +9 -9
- package/dist/commands/workflow/core/workflow-logger.js +21 -21
- package/dist/commands/workflow/executors/phase-executor.d.ts +2 -2
- package/dist/commands/workflow/executors/phase-executor.js +32 -32
- package/dist/commands/workflow/issue-coordinator.d.ts +18 -0
- package/dist/commands/workflow/{feature-coordinator.js → issue-coordinator.js} +29 -29
- package/dist/commands/workflow/phase-orchestrator.d.ts +2 -2
- package/dist/commands/workflow/phase-orchestrator.js +82 -82
- package/dist/commands/workflow/processor.d.ts +7 -7
- package/dist/commands/workflow/processor.js +44 -44
- package/dist/config/{feature-status.d.ts → issue-status.d.ts} +14 -14
- package/dist/config/{feature-status.js → issue-status.js} +14 -14
- package/dist/errors/index.d.ts +6 -6
- package/dist/errors/index.js +11 -11
- package/dist/index.js +90 -1
- package/dist/phases/app-store-generation/context.js +6 -6
- package/dist/phases/app-store-generation/index.js +2 -2
- package/dist/phases/app-store-generation/prompts.js +2 -2
- package/dist/phases/autonomous/index.d.ts +3 -3
- package/dist/phases/autonomous/index.js +37 -37
- package/dist/phases/autonomous/prompts.d.ts +2 -2
- package/dist/phases/autonomous/prompts.js +4 -4
- package/dist/phases/branch-planning/context.d.ts +3 -3
- package/dist/phases/branch-planning/context.js +12 -12
- package/dist/phases/branch-planning/index.d.ts +3 -3
- package/dist/phases/branch-planning/index.js +32 -32
- package/dist/phases/branch-planning/outcome.d.ts +5 -5
- package/dist/phases/branch-planning/outcome.js +12 -12
- package/dist/phases/branch-planning/prompts.d.ts +3 -3
- package/dist/phases/branch-planning/prompts.js +13 -13
- package/dist/phases/bug-fixing/analyzer.d.ts +2 -2
- package/dist/phases/bug-fixing/analyzer.js +13 -13
- package/dist/phases/bug-fixing/context-fetcher.d.ts +3 -3
- package/dist/phases/bug-fixing/context-fetcher.js +18 -18
- package/dist/phases/bug-fixing/mcp-server.js +17 -18
- package/dist/phases/chat-processor/context.d.ts +5 -5
- package/dist/phases/chat-processor/context.js +17 -17
- package/dist/phases/chat-processor/index.d.ts +4 -4
- package/dist/phases/chat-processor/index.js +17 -17
- package/dist/phases/chat-processor/product-context.d.ts +3 -3
- package/dist/phases/chat-processor/product-context.js +16 -16
- package/dist/phases/chat-processor/product-prompts.d.ts +1 -1
- package/dist/phases/chat-processor/product-prompts.js +10 -10
- package/dist/phases/chat-processor/product-tools.d.ts +2 -2
- package/dist/phases/chat-processor/product-tools.js +33 -33
- package/dist/phases/chat-processor/prompts.d.ts +3 -3
- package/dist/phases/chat-processor/prompts.js +22 -22
- package/dist/phases/chat-processor/tools.js +46 -46
- package/dist/phases/code-implementation/branch-pr-creator.d.ts +3 -3
- package/dist/phases/code-implementation/branch-pr-creator.js +5 -5
- package/dist/phases/code-implementation/context.d.ts +3 -3
- package/dist/phases/code-implementation/context.js +18 -18
- package/dist/phases/code-implementation/index.d.ts +4 -4
- package/dist/phases/code-implementation/index.js +88 -88
- package/dist/phases/code-implementation/outcome.d.ts +3 -3
- package/dist/phases/code-implementation/outcome.js +6 -6
- package/dist/phases/code-implementation/prompts.d.ts +1 -1
- package/dist/phases/code-implementation/prompts.js +6 -6
- package/dist/phases/code-implementation-verification/agent.d.ts +3 -3
- package/dist/phases/code-implementation-verification/agent.js +5 -5
- package/dist/phases/code-implementation-verification/index.d.ts +3 -3
- package/dist/phases/code-implementation-verification/index.js +11 -11
- package/dist/phases/code-implementation-verification/prompts.d.ts +3 -3
- package/dist/phases/code-implementation-verification/prompts.js +7 -7
- package/dist/phases/code-refine/context.d.ts +8 -8
- package/dist/phases/code-refine/context.js +29 -29
- package/dist/phases/code-refine/index.d.ts +2 -2
- package/dist/phases/code-refine/index.js +20 -20
- package/dist/phases/code-refine/prompts.d.ts +1 -1
- package/dist/phases/code-refine/prompts.js +3 -3
- package/dist/phases/code-refine/refine-iteration.d.ts +1 -1
- package/dist/phases/code-refine/refine-iteration.js +4 -4
- package/dist/phases/code-refine/retry-handler.js +2 -2
- package/dist/phases/code-refine-verification/index.js +10 -10
- package/dist/phases/code-refine-verification/types.d.ts +2 -2
- package/dist/phases/code-review/context.d.ts +8 -8
- package/dist/phases/code-review/context.js +25 -25
- package/dist/phases/code-review/diff-utils.d.ts +1 -1
- package/dist/phases/code-review/diff-utils.js +1 -1
- package/dist/phases/code-review/index.d.ts +2 -2
- package/dist/phases/code-review/index.js +26 -26
- package/dist/phases/code-testing/analyzer.d.ts +2 -2
- package/dist/phases/code-testing/analyzer.js +18 -18
- package/dist/phases/code-testing/context-fetcher.d.ts +3 -3
- package/dist/phases/code-testing/context-fetcher.js +16 -16
- package/dist/phases/code-testing/prompts.d.ts +1 -1
- package/dist/phases/code-testing/prompts.js +5 -5
- package/dist/phases/find-bugs/index.d.ts +30 -0
- package/dist/phases/find-bugs/index.js +216 -0
- package/dist/phases/find-bugs/prompts.d.ts +22 -0
- package/dist/phases/find-bugs/prompts.js +101 -0
- package/dist/phases/find-bugs/state.d.ts +19 -0
- package/dist/phases/find-bugs/state.js +13 -0
- package/dist/phases/find-bugs/types.d.ts +21 -0
- package/dist/phases/find-bugs/types.js +16 -0
- package/dist/phases/find-features/index.d.ts +40 -0
- package/dist/phases/find-features/index.js +279 -0
- package/dist/phases/find-features/prompts.d.ts +43 -0
- package/dist/phases/find-features/prompts.js +138 -0
- package/dist/phases/find-features/state.d.ts +25 -0
- package/dist/phases/find-features/state.js +22 -0
- package/dist/phases/find-features/types.d.ts +27 -0
- package/dist/phases/find-features/types.js +16 -0
- package/dist/phases/find-shared/git.d.ts +24 -0
- package/dist/phases/find-shared/git.js +60 -0
- package/dist/phases/find-shared/mcp.d.ts +33 -0
- package/dist/phases/find-shared/mcp.js +69 -0
- package/dist/phases/find-shared/scan-state.d.ts +33 -0
- package/dist/phases/find-shared/scan-state.js +112 -0
- package/dist/phases/find-smells/index.d.ts +47 -0
- package/dist/phases/find-smells/index.js +278 -0
- package/dist/phases/find-smells/prompts.d.ts +30 -0
- package/dist/phases/find-smells/prompts.js +129 -0
- package/dist/phases/find-smells/state.d.ts +21 -0
- package/dist/phases/find-smells/state.js +17 -0
- package/dist/phases/find-smells/types.d.ts +51 -0
- package/dist/phases/find-smells/types.js +64 -0
- package/dist/phases/functional-testing/analyzer.d.ts +2 -2
- package/dist/phases/functional-testing/analyzer.js +40 -40
- package/dist/phases/functional-testing/context-fetcher.d.ts +3 -3
- package/dist/phases/functional-testing/context-fetcher.js +16 -16
- package/dist/phases/functional-testing/http-fallback.d.ts +2 -2
- package/dist/phases/functional-testing/http-fallback.js +9 -9
- package/dist/phases/functional-testing/mcp-server.js +23 -24
- package/dist/phases/functional-testing/prompts.d.ts +1 -1
- package/dist/phases/functional-testing/prompts.js +4 -4
- package/dist/phases/functional-testing/test-report-creator.d.ts +2 -2
- package/dist/phases/functional-testing/test-report-creator.js +10 -10
- package/dist/phases/functional-testing/test-retry-handler.js +3 -3
- package/dist/phases/growth-analysis/context.js +6 -6
- package/dist/phases/growth-analysis/index.js +2 -2
- package/dist/phases/growth-analysis/prompts.js +2 -2
- package/dist/phases/intelligence-analysis/context.js +7 -7
- package/dist/phases/intelligence-analysis/index.js +3 -3
- package/dist/phases/{feature-analysis → issue-analysis}/agent.js +1 -1
- package/dist/phases/issue-analysis/context.d.ts +24 -0
- package/dist/phases/{feature-analysis → issue-analysis}/context.js +30 -30
- package/dist/phases/issue-analysis/index.d.ts +8 -0
- package/dist/phases/{feature-analysis → issue-analysis}/index.js +29 -29
- package/dist/phases/issue-analysis/outcome.d.ts +40 -0
- package/dist/phases/{feature-analysis → issue-analysis}/outcome.js +17 -17
- package/dist/phases/{feature-analysis → issue-analysis}/prompts.d.ts +3 -3
- package/dist/phases/{feature-analysis → issue-analysis}/prompts.js +12 -12
- package/dist/phases/{feature-analysis-verification → issue-analysis-verification}/agent.d.ts +2 -2
- package/dist/phases/{feature-analysis-verification → issue-analysis-verification}/agent.js +1 -1
- package/dist/phases/{feature-analysis-verification → issue-analysis-verification}/index.d.ts +5 -5
- package/dist/phases/{feature-analysis-verification → issue-analysis-verification}/index.js +9 -9
- package/dist/phases/{feature-analysis-verification → issue-analysis-verification}/prompts.d.ts +2 -2
- package/dist/phases/{feature-analysis-verification → issue-analysis-verification}/prompts.js +7 -7
- package/dist/phases/output-contracts.js +37 -37
- package/dist/phases/pr-execution/context.d.ts +3 -3
- package/dist/phases/pr-execution/context.js +14 -14
- package/dist/phases/pr-execution/index.d.ts +2 -2
- package/dist/phases/pr-execution/index.js +22 -22
- package/dist/phases/pr-execution/outcome.d.ts +4 -4
- package/dist/phases/pr-execution/outcome.js +6 -6
- package/dist/phases/pr-execution/prompts.d.ts +4 -4
- package/dist/phases/pr-execution/prompts.js +6 -6
- package/dist/phases/pr-resolve/checklist-learner.js +2 -2
- package/dist/phases/pr-review/index.d.ts +1 -1
- package/dist/phases/pr-review/index.js +1 -1
- package/dist/phases/pr-review/prompts.d.ts +1 -1
- package/dist/phases/pr-review/prompts.js +1 -1
- package/dist/phases/pr-shared/context.d.ts +3 -3
- package/dist/phases/pr-shared/context.js +3 -3
- package/dist/phases/pr-splitting/context.d.ts +3 -3
- package/dist/phases/pr-splitting/context.js +16 -16
- package/dist/phases/pr-splitting/index.d.ts +4 -4
- package/dist/phases/pr-splitting/index.js +29 -29
- package/dist/phases/pr-splitting/outcome.d.ts +3 -3
- package/dist/phases/pr-splitting/outcome.js +7 -7
- package/dist/phases/pr-splitting/prompts.d.ts +3 -3
- package/dist/phases/pr-splitting/prompts.js +11 -11
- package/dist/phases/pull-request/creator.d.ts +4 -4
- package/dist/phases/pull-request/creator.js +25 -25
- package/dist/phases/pull-request/handler.d.ts +3 -3
- package/dist/phases/pull-request/handler.js +16 -16
- package/dist/phases/release-sync/index.js +2 -2
- package/dist/phases/run-sheet/agent.js +1 -2
- package/dist/phases/run-sheet/index.js +3 -3
- package/dist/phases/smoke-test/index.js +2 -2
- package/dist/phases/technical-design/context.d.ts +3 -3
- package/dist/phases/technical-design/context.js +11 -11
- package/dist/phases/technical-design/index.d.ts +2 -2
- package/dist/phases/technical-design/index.js +27 -27
- package/dist/phases/technical-design/outcome.d.ts +4 -4
- package/dist/phases/technical-design/outcome.js +6 -6
- package/dist/phases/technical-design/prompts.d.ts +2 -2
- package/dist/phases/technical-design/prompts.js +10 -10
- package/dist/phases/technical-design-verification/agent.d.ts +3 -3
- package/dist/phases/technical-design-verification/agent.js +4 -4
- package/dist/phases/technical-design-verification/index.d.ts +4 -4
- package/dist/phases/technical-design-verification/index.js +12 -12
- package/dist/phases/technical-design-verification/prompts.d.ts +3 -3
- package/dist/phases/technical-design-verification/prompts.js +6 -6
- package/dist/phases/test-cases-analysis/context.d.ts +5 -5
- package/dist/phases/test-cases-analysis/context.js +18 -18
- package/dist/phases/test-cases-analysis/formatters.js +7 -7
- package/dist/phases/test-cases-analysis/index.d.ts +1 -1
- package/dist/phases/test-cases-analysis/index.js +21 -21
- package/dist/phases/test-cases-analysis/outcome.d.ts +7 -7
- package/dist/phases/test-cases-analysis/outcome.js +13 -13
- package/dist/phases/test-cases-analysis/prompts.d.ts +3 -3
- package/dist/phases/test-cases-analysis/prompts.js +6 -6
- package/dist/phases/user-stories-analysis/context.d.ts +5 -5
- package/dist/phases/user-stories-analysis/context.js +18 -18
- package/dist/phases/user-stories-analysis/formatters.js +7 -7
- package/dist/phases/user-stories-analysis/index.d.ts +1 -1
- package/dist/phases/user-stories-analysis/index.js +21 -21
- package/dist/phases/user-stories-analysis/outcome.d.ts +7 -7
- package/dist/phases/user-stories-analysis/outcome.js +13 -13
- package/dist/phases/user-stories-analysis/prompts.d.ts +3 -3
- package/dist/phases/user-stories-analysis/prompts.js +10 -10
- package/dist/services/audit-logs.d.ts +10 -10
- package/dist/services/audit-logs.js +12 -12
- package/dist/services/branches.d.ts +6 -6
- package/dist/services/branches.js +16 -16
- package/dist/services/checklist.d.ts +3 -3
- package/dist/services/checklist.js +11 -11
- package/dist/services/coaching/coaching-agent.js +2 -2
- package/dist/services/coaching/coaching-loop.d.ts +1 -1
- package/dist/services/coaching/coaching-loop.js +2 -2
- package/dist/services/coaching/phase-coaching.d.ts +2 -2
- package/dist/services/coaching/phase-coaching.js +3 -3
- package/dist/services/coaching/self-rating.js +1 -1
- package/dist/services/feedbacks.d.ts +4 -4
- package/dist/services/feedbacks.js +8 -8
- package/dist/services/phase-hooks/bindings-fetcher.d.ts +4 -4
- package/dist/services/phase-hooks/bindings-fetcher.js +8 -8
- package/dist/services/phase-hooks/hook-executor.js +1 -1
- package/dist/services/phase-hooks/hook-logging.d.ts +2 -2
- package/dist/services/phase-hooks/hook-logging.js +4 -4
- package/dist/services/phase-hooks/hook-runner.d.ts +1 -1
- package/dist/services/phase-hooks/hook-runner.js +4 -4
- package/dist/services/phase-hooks/types.d.ts +3 -3
- package/dist/services/phase-ratings.d.ts +7 -7
- package/dist/services/phase-ratings.js +8 -8
- package/dist/services/pull-requests.d.ts +4 -4
- package/dist/services/pull-requests.js +11 -11
- package/dist/services/skill-resolver.d.ts +1 -1
- package/dist/services/skill-resolver.js +1 -1
- package/dist/skills/phase/app-store-generation/SKILL.md +9 -9
- package/dist/skills/phase/autonomous/SKILL.md +2 -2
- package/dist/skills/phase/branch-planning/SKILL.md +12 -12
- package/dist/skills/phase/bug-fixing/SKILL.md +1 -1
- package/dist/skills/phase/code-implementation/SKILL.md +6 -6
- package/dist/skills/phase/code-implementation-verification/SKILL.md +3 -3
- package/dist/skills/phase/code-testing/SKILL.md +5 -5
- package/dist/skills/phase/functional-testing/SKILL.md +3 -3
- package/dist/skills/phase/growth-analysis/SKILL.md +8 -8
- package/dist/skills/phase/incremental-sync/SKILL.md +6 -6
- package/dist/skills/phase/intelligence-analysis/SKILL.md +7 -7
- package/dist/skills/phase/{feature-analysis → issue-analysis}/SKILL.md +8 -8
- package/dist/skills/phase/pr-execution/SKILL.md +7 -7
- package/dist/skills/phase/pr-splitting/SKILL.md +14 -14
- package/dist/skills/phase/smoke-test/SKILL.md +1 -1
- package/dist/skills/phase/technical-design/SKILL.md +5 -5
- package/dist/skills/phase/test-cases-analysis/SKILL.md +4 -4
- package/dist/skills/phase/user-stories-analysis/SKILL.md +13 -13
- package/dist/system/session-manager.d.ts +3 -3
- package/dist/system/session-manager.js +3 -3
- package/dist/system/sleep-notification.js +2 -2
- package/dist/system/sleep-prevention.js +1 -1
- package/dist/types/index.d.ts +21 -21
- package/dist/types/{features.d.ts → issues.d.ts} +3 -3
- package/dist/types/pipeline.d.ts +4 -4
- package/dist/updater/auto-updater.d.ts +2 -2
- package/dist/updater/auto-updater.js +3 -3
- package/dist/utils/conflict-resolver.d.ts +1 -1
- package/dist/utils/conflict-resolver.js +5 -5
- package/dist/utils/formatters.d.ts +4 -4
- package/dist/utils/formatters.js +29 -29
- package/dist/utils/git-branch-manager-async.d.ts +6 -6
- package/dist/utils/git-branch-manager-async.js +41 -41
- package/dist/utils/git-branch-manager.d.ts +11 -11
- package/dist/utils/git-branch-manager.js +42 -42
- package/dist/utils/image-downloader.d.ts +4 -4
- package/dist/utils/image-downloader.js +17 -17
- package/dist/utils/pipeline-logger.d.ts +1 -1
- package/dist/utils/pipeline-logger.js +5 -5
- package/dist/workspace/workspace-manager.d.ts +17 -17
- package/dist/workspace/workspace-manager.js +21 -21
- package/package.json +1 -1
- package/vitest.config.ts +4 -0
- package/dist/api/__tests__/app-store.test.d.ts +0 -7
- package/dist/api/__tests__/app-store.test.js +0 -60
- package/dist/api/__tests__/intelligence.test.d.ts +0 -11
- package/dist/api/__tests__/intelligence.test.js +0 -315
- package/dist/api/features/__tests__/feature-utils.test.d.ts +0 -4
- package/dist/api/features/__tests__/feature-utils.test.js +0 -370
- package/dist/api/features/__tests__/status-updater.test.d.ts +0 -4
- package/dist/api/features/__tests__/status-updater.test.js +0 -88
- package/dist/api/features/approval-checker.d.ts +0 -20
- package/dist/api/features/feature-utils.d.ts +0 -23
- package/dist/api/features/feature-utils.js +0 -80
- package/dist/api/features/get-feature.d.ts +0 -5
- package/dist/api/features/get-feature.js +0 -21
- package/dist/api/features/index.d.ts +0 -8
- package/dist/api/features/index.js +0 -10
- package/dist/api/features/status-updater.d.ts +0 -41
- package/dist/api/features/update-feature.d.ts +0 -20
- package/dist/commands/build/__tests__/build.test.d.ts +0 -5
- package/dist/commands/build/__tests__/build.test.js +0 -206
- package/dist/commands/build/__tests__/detect-project.test.d.ts +0 -6
- package/dist/commands/build/__tests__/detect-project.test.js +0 -160
- package/dist/commands/build/__tests__/run-build.test.d.ts +0 -6
- package/dist/commands/build/__tests__/run-build.test.js +0 -433
- package/dist/commands/intelligence/__tests__/command.test.d.ts +0 -4
- package/dist/commands/intelligence/__tests__/command.test.js +0 -48
- package/dist/commands/workflow/core/__tests__/feature-filter.test.d.ts +0 -5
- package/dist/commands/workflow/core/__tests__/feature-filter.test.js +0 -316
- package/dist/commands/workflow/core/__tests__/pipeline-evaluator.test.d.ts +0 -4
- package/dist/commands/workflow/core/__tests__/pipeline-evaluator.test.js +0 -397
- package/dist/commands/workflow/core/__tests__/state-manager.test.d.ts +0 -4
- package/dist/commands/workflow/core/__tests__/state-manager.test.js +0 -384
- package/dist/commands/workflow/core/feature-filter.d.ts +0 -16
- package/dist/commands/workflow/core/feature-filter.js +0 -47
- package/dist/commands/workflow/feature-coordinator.d.ts +0 -18
- package/dist/config/__tests__/config.test.d.ts +0 -4
- package/dist/config/__tests__/config.test.js +0 -286
- package/dist/config/__tests__/feature-status.test.d.ts +0 -4
- package/dist/config/__tests__/feature-status.test.js +0 -111
- package/dist/errors/__tests__/index.test.d.ts +0 -4
- package/dist/errors/__tests__/index.test.js +0 -349
- package/dist/phases/app-store-generation/__tests__/agent.test.d.ts +0 -5
- package/dist/phases/app-store-generation/__tests__/agent.test.js +0 -142
- package/dist/phases/app-store-generation/__tests__/context.test.d.ts +0 -4
- package/dist/phases/app-store-generation/__tests__/context.test.js +0 -284
- package/dist/phases/app-store-generation/__tests__/prompts.test.d.ts +0 -4
- package/dist/phases/app-store-generation/__tests__/prompts.test.js +0 -122
- package/dist/phases/app-store-generation/__tests__/screenshot-composer.test.d.ts +0 -5
- package/dist/phases/app-store-generation/__tests__/screenshot-composer.test.js +0 -826
- package/dist/phases/code-review/__tests__/diff-utils.test.js +0 -101
- package/dist/phases/feature-analysis/context.d.ts +0 -24
- package/dist/phases/feature-analysis/index.d.ts +0 -8
- package/dist/phases/feature-analysis/outcome.d.ts +0 -40
- package/dist/phases/intelligence-analysis/__tests__/context.test.d.ts +0 -4
- package/dist/phases/intelligence-analysis/__tests__/context.test.js +0 -192
- package/dist/phases/intelligence-analysis/__tests__/matching.test.d.ts +0 -13
- package/dist/phases/intelligence-analysis/__tests__/matching.test.js +0 -154
- package/dist/phases/intelligence-analysis/__tests__/orchestration.test.d.ts +0 -5
- package/dist/phases/intelligence-analysis/__tests__/orchestration.test.js +0 -378
- package/dist/phases/intelligence-analysis/__tests__/prompts.test.d.ts +0 -4
- package/dist/phases/intelligence-analysis/__tests__/prompts.test.js +0 -33
- package/dist/phases/pr-execution/__tests__/file-assigner.test.d.ts +0 -1
- package/dist/phases/pr-execution/__tests__/file-assigner.test.js +0 -303
- package/dist/phases/pr-resolve/__tests__/checklist-learner.test.d.ts +0 -1
- package/dist/phases/pr-resolve/__tests__/checklist-learner.test.js +0 -157
- package/dist/phases/pr-resolve/__tests__/prompts.test.d.ts +0 -1
- package/dist/phases/pr-resolve/__tests__/prompts.test.js +0 -116
- package/dist/phases/pr-resolve/__tests__/resolve-mapping.test.d.ts +0 -1
- package/dist/phases/pr-resolve/__tests__/resolve-mapping.test.js +0 -138
- package/dist/phases/pr-resolve/__tests__/types.test.d.ts +0 -1
- package/dist/phases/pr-resolve/__tests__/types.test.js +0 -43
- package/dist/phases/pr-resolve/__tests__/workspace.test.d.ts +0 -1
- package/dist/phases/pr-resolve/__tests__/workspace.test.js +0 -111
- package/dist/phases/pr-review/__tests__/prompts.test.d.ts +0 -1
- package/dist/phases/pr-review/__tests__/prompts.test.js +0 -49
- package/dist/phases/pr-review/__tests__/review-comments.test.d.ts +0 -1
- package/dist/phases/pr-review/__tests__/review-comments.test.js +0 -110
- package/dist/phases/pr-shared/__tests__/agent-utils.test.d.ts +0 -1
- package/dist/phases/pr-shared/__tests__/agent-utils.test.js +0 -91
- package/dist/phases/pr-shared/__tests__/context.test.d.ts +0 -1
- package/dist/phases/pr-shared/__tests__/context.test.js +0 -94
- package/dist/phases/pr-splitting/__tests__/import-dep-validator.test.d.ts +0 -1
- package/dist/phases/pr-splitting/__tests__/import-dep-validator.test.js +0 -331
- package/dist/phases/run-sheet/render.d.ts +0 -60
- package/dist/phases/run-sheet/render.js +0 -297
- package/dist/phases/smoke-test/__tests__/agent.test.d.ts +0 -4
- package/dist/phases/smoke-test/__tests__/agent.test.js +0 -84
- package/dist/phases/smoke-test/__tests__/github.test.d.ts +0 -9
- package/dist/phases/smoke-test/__tests__/github.test.js +0 -120
- package/dist/phases/smoke-test/__tests__/snapshot.test.d.ts +0 -8
- package/dist/phases/smoke-test/__tests__/snapshot.test.js +0 -93
- package/dist/phases/smoke-test/github.d.ts +0 -54
- package/dist/phases/smoke-test/github.js +0 -101
- package/dist/phases/smoke-test/snapshot.d.ts +0 -27
- package/dist/phases/smoke-test/snapshot.js +0 -157
- package/dist/services/coaching/__tests__/coaching-agent.test.d.ts +0 -1
- package/dist/services/coaching/__tests__/coaching-agent.test.js +0 -74
- package/dist/services/coaching/__tests__/coaching-loop.test.d.ts +0 -1
- package/dist/services/coaching/__tests__/coaching-loop.test.js +0 -59
- package/dist/services/coaching/__tests__/self-rating.test.d.ts +0 -1
- package/dist/services/coaching/__tests__/self-rating.test.js +0 -188
- package/dist/services/lifecycle-agent/__tests__/phase-criteria.test.d.ts +0 -4
- package/dist/services/lifecycle-agent/__tests__/phase-criteria.test.js +0 -133
- package/dist/services/lifecycle-agent/__tests__/transition-rules.test.d.ts +0 -4
- package/dist/services/lifecycle-agent/__tests__/transition-rules.test.js +0 -336
- package/dist/services/lifecycle-agent/index.d.ts +0 -24
- package/dist/services/lifecycle-agent/index.js +0 -25
- package/dist/services/lifecycle-agent/phase-criteria.d.ts +0 -57
- package/dist/services/lifecycle-agent/phase-criteria.js +0 -335
- package/dist/services/lifecycle-agent/transition-rules.d.ts +0 -60
- package/dist/services/lifecycle-agent/transition-rules.js +0 -184
- package/dist/services/lifecycle-agent/types.d.ts +0 -190
- package/dist/services/lifecycle-agent/types.js +0 -12
- package/dist/services/phase-hooks/__tests__/bindings-fetcher.test.d.ts +0 -1
- package/dist/services/phase-hooks/__tests__/bindings-fetcher.test.js +0 -122
- package/dist/services/phase-hooks/__tests__/hook-executor.test.d.ts +0 -1
- package/dist/services/phase-hooks/__tests__/hook-executor.test.js +0 -321
- package/dist/services/phase-hooks/__tests__/hook-runner.test.d.ts +0 -1
- package/dist/services/phase-hooks/__tests__/hook-runner.test.js +0 -261
- package/dist/services/phase-hooks/__tests__/plugin-loader.test.d.ts +0 -1
- package/dist/services/phase-hooks/__tests__/plugin-loader.test.js +0 -158
- package/dist/services/video/__tests__/video-pipeline.test.d.ts +0 -6
- package/dist/services/video/__tests__/video-pipeline.test.js +0 -249
- package/dist/types/features.js +0 -1
- package/dist/workspace/__tests__/workspace-manager.test.d.ts +0 -7
- package/dist/workspace/__tests__/workspace-manager.test.js +0 -52
- /package/dist/api/{features → issues}/batch-operations.d.ts +0 -0
- /package/dist/api/{features → issues}/batch-operations.js +0 -0
- /package/dist/phases/{feature-analysis → issue-analysis}/agent.d.ts +0 -0
- /package/dist/skills/phase/{feature-analysis-verification → issue-analysis-verification}/SKILL.md +0 -0
- /package/dist/{phases/code-review/__tests__/diff-utils.test.d.ts → types/issues.js} +0 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP helpers shared by find-bugs / find-features / find-smells.
|
|
3
|
+
*
|
|
4
|
+
* All three load a product's basics (name, description) for prompt context,
|
|
5
|
+
* pull the open-issue list for dedup, and file findings via `issues/create`.
|
|
6
|
+
* Centralising the calls means a schema change in MCP only needs touching one
|
|
7
|
+
* place, and the per-phase orchestrators stay focused on their own logic.
|
|
8
|
+
*/
|
|
9
|
+
import { type IssueInfo } from '../../types/issues.js';
|
|
10
|
+
export interface ProductBasics {
|
|
11
|
+
name: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function fetchProductBasics(productId: string): Promise<ProductBasics>;
|
|
15
|
+
/**
|
|
16
|
+
* Fetch the product's open issues for dedup context. Filters out terminal
|
|
17
|
+
* statuses (shipped/archived/closed/...) since those can't conflict with new
|
|
18
|
+
* findings the agent might surface.
|
|
19
|
+
*/
|
|
20
|
+
export declare function fetchOpenIssues(productId: string): Promise<IssueInfo[]>;
|
|
21
|
+
export declare function isTerminalStatus(status: string): boolean;
|
|
22
|
+
export interface CreateIssueInput {
|
|
23
|
+
productId: string;
|
|
24
|
+
/** Issue title for `name`. */
|
|
25
|
+
title: string;
|
|
26
|
+
/** Already-formatted markdown body. The caller decides the layout. */
|
|
27
|
+
description: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* File a new issue via MCP. Returns the new issue id, or null if MCP returned
|
|
31
|
+
* an error / unexpected shape (already logged).
|
|
32
|
+
*/
|
|
33
|
+
export declare function createIssue(input: CreateIssueInput): Promise<string | null>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP helpers shared by find-bugs / find-features / find-smells.
|
|
3
|
+
*
|
|
4
|
+
* All three load a product's basics (name, description) for prompt context,
|
|
5
|
+
* pull the open-issue list for dedup, and file findings via `issues/create`.
|
|
6
|
+
* Centralising the calls means a schema change in MCP only needs touching one
|
|
7
|
+
* place, and the per-phase orchestrators stay focused on their own logic.
|
|
8
|
+
*/
|
|
9
|
+
import { callMcpEndpoint } from '../../api/mcp-client.js';
|
|
10
|
+
import { logError, logWarning } from '../../utils/logger.js';
|
|
11
|
+
export async function fetchProductBasics(productId) {
|
|
12
|
+
try {
|
|
13
|
+
const result = (await callMcpEndpoint('resources/read', {
|
|
14
|
+
uri: `product://${productId}`,
|
|
15
|
+
}));
|
|
16
|
+
const text = result.contents?.[0]?.text || '{}';
|
|
17
|
+
const parsed = JSON.parse(text);
|
|
18
|
+
return {
|
|
19
|
+
name: parsed.name || productId,
|
|
20
|
+
description: parsed.description,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return { name: productId };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Fetch the product's open issues for dedup context. Filters out terminal
|
|
29
|
+
* statuses (shipped/archived/closed/...) since those can't conflict with new
|
|
30
|
+
* findings the agent might surface.
|
|
31
|
+
*/
|
|
32
|
+
export async function fetchOpenIssues(productId) {
|
|
33
|
+
try {
|
|
34
|
+
const result = (await callMcpEndpoint('issues/list', {
|
|
35
|
+
product_id: productId,
|
|
36
|
+
}));
|
|
37
|
+
const all = result.issues || [];
|
|
38
|
+
return all.filter((i) => !isTerminalStatus(i.status));
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
logWarning(`Could not load existing issues for dedup: ${error instanceof Error ? error.message : String(error)}`);
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function isTerminalStatus(status) {
|
|
46
|
+
return (status === 'shipped' ||
|
|
47
|
+
status === 'archived' ||
|
|
48
|
+
status === 'cancelled' ||
|
|
49
|
+
status === 'closed' ||
|
|
50
|
+
status === 'completed');
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* File a new issue via MCP. Returns the new issue id, or null if MCP returned
|
|
54
|
+
* an error / unexpected shape (already logged).
|
|
55
|
+
*/
|
|
56
|
+
export async function createIssue(input) {
|
|
57
|
+
try {
|
|
58
|
+
const result = (await callMcpEndpoint('issues/create', {
|
|
59
|
+
product_id: input.productId,
|
|
60
|
+
name: input.title,
|
|
61
|
+
description: input.description,
|
|
62
|
+
}));
|
|
63
|
+
return result.issue?.id || result.id || null;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
logError(`Failed to create issue for "${input.title}": ${error instanceof Error ? error.message : String(error)}`);
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-product scan state, generic across the find-* phases. Each phase has
|
|
3
|
+
* its own state schema (find-bugs tracks a commit sha, find-features tracks
|
|
4
|
+
* the most-recent-seen feedback id, etc.) so the type is parametric, but the
|
|
5
|
+
* file layout, atomic-write, and lock semantics are identical and live here.
|
|
6
|
+
*
|
|
7
|
+
* Storage layout: `~/.edsger/<dirName>/<productId>.json` plus a sibling
|
|
8
|
+
* `.lock` file. The lockfile uses O_EXCL so concurrent acquirers race on the
|
|
9
|
+
* filesystem, not on application code — atomic on local POSIX filesystems
|
|
10
|
+
* (good enough; the workspace clone is also machine-local).
|
|
11
|
+
*
|
|
12
|
+
* Known limitation: state is machine-local. A scan started on machine A and
|
|
13
|
+
* next on machine B will look like a first run.
|
|
14
|
+
*/
|
|
15
|
+
export interface LockHandle {
|
|
16
|
+
release: () => void;
|
|
17
|
+
}
|
|
18
|
+
export interface ScanStateModule<T extends object> {
|
|
19
|
+
load: (productId: string) => T;
|
|
20
|
+
save: (productId: string, state: T) => void;
|
|
21
|
+
/** Merge `patch` into the stored state. Fields not mentioned are preserved. */
|
|
22
|
+
update: (productId: string, patch: Partial<T>) => T;
|
|
23
|
+
/**
|
|
24
|
+
* Acquire an exclusive lock for this product's state. Returns null if held.
|
|
25
|
+
* Stale locks older than `staleAfterMs` (default 1h) are reclaimed.
|
|
26
|
+
*/
|
|
27
|
+
acquireLock: (productId: string, staleAfterMs?: number) => LockHandle | null;
|
|
28
|
+
}
|
|
29
|
+
export interface CreateScanStateModuleOptions {
|
|
30
|
+
/** Subdirectory under ~/.edsger/. Example: 'find-bugs-state'. */
|
|
31
|
+
dirName: string;
|
|
32
|
+
}
|
|
33
|
+
export declare function createScanStateModule<T extends object>(opts: CreateScanStateModuleOptions): ScanStateModule<T>;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-product scan state, generic across the find-* phases. Each phase has
|
|
3
|
+
* its own state schema (find-bugs tracks a commit sha, find-features tracks
|
|
4
|
+
* the most-recent-seen feedback id, etc.) so the type is parametric, but the
|
|
5
|
+
* file layout, atomic-write, and lock semantics are identical and live here.
|
|
6
|
+
*
|
|
7
|
+
* Storage layout: `~/.edsger/<dirName>/<productId>.json` plus a sibling
|
|
8
|
+
* `.lock` file. The lockfile uses O_EXCL so concurrent acquirers race on the
|
|
9
|
+
* filesystem, not on application code — atomic on local POSIX filesystems
|
|
10
|
+
* (good enough; the workspace clone is also machine-local).
|
|
11
|
+
*
|
|
12
|
+
* Known limitation: state is machine-local. A scan started on machine A and
|
|
13
|
+
* next on machine B will look like a first run.
|
|
14
|
+
*/
|
|
15
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, unlinkSync, writeFileSync, } from 'fs';
|
|
16
|
+
import { homedir } from 'os';
|
|
17
|
+
import { join } from 'path';
|
|
18
|
+
const DEFAULT_STALE_AFTER_MS = 60 * 60 * 1000;
|
|
19
|
+
export function createScanStateModule(opts) {
|
|
20
|
+
const { dirName } = opts;
|
|
21
|
+
// Resolve at call time, not module-load time, so test setups that swap
|
|
22
|
+
// process.env.HOME between cases see the change.
|
|
23
|
+
function stateDir() {
|
|
24
|
+
return join(homedir(), '.edsger', dirName);
|
|
25
|
+
}
|
|
26
|
+
function safeProductId(productId) {
|
|
27
|
+
return productId.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
28
|
+
}
|
|
29
|
+
function statePath(productId) {
|
|
30
|
+
return join(stateDir(), `${safeProductId(productId)}.json`);
|
|
31
|
+
}
|
|
32
|
+
function lockPath(productId) {
|
|
33
|
+
return join(stateDir(), `${safeProductId(productId)}.lock`);
|
|
34
|
+
}
|
|
35
|
+
function ensureStateDir() {
|
|
36
|
+
const dir = stateDir();
|
|
37
|
+
if (!existsSync(dir)) {
|
|
38
|
+
mkdirSync(dir, { recursive: true });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function load(productId) {
|
|
42
|
+
const p = statePath(productId);
|
|
43
|
+
if (!existsSync(p)) {
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const raw = readFileSync(p, 'utf-8');
|
|
48
|
+
const parsed = JSON.parse(raw);
|
|
49
|
+
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** Atomic write: stage to a tempfile, then rename. */
|
|
56
|
+
function save(productId, state) {
|
|
57
|
+
ensureStateDir();
|
|
58
|
+
const finalPath = statePath(productId);
|
|
59
|
+
const tmpPath = `${finalPath}.${process.pid}.tmp`;
|
|
60
|
+
writeFileSync(tmpPath, JSON.stringify(state, null, 2), 'utf-8');
|
|
61
|
+
renameSync(tmpPath, finalPath);
|
|
62
|
+
}
|
|
63
|
+
function update(productId, patch) {
|
|
64
|
+
const merged = { ...load(productId), ...patch };
|
|
65
|
+
save(productId, merged);
|
|
66
|
+
return merged;
|
|
67
|
+
}
|
|
68
|
+
function acquireLock(productId, staleAfterMs = DEFAULT_STALE_AFTER_MS) {
|
|
69
|
+
ensureStateDir();
|
|
70
|
+
const lock = lockPath(productId);
|
|
71
|
+
if (existsSync(lock)) {
|
|
72
|
+
try {
|
|
73
|
+
const raw = readFileSync(lock, 'utf-8');
|
|
74
|
+
const parsed = JSON.parse(raw);
|
|
75
|
+
const age = Date.now() - new Date(parsed.acquiredAt ?? 0).getTime();
|
|
76
|
+
if (age < staleAfterMs) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
// Stale or corrupt — reclaim. Only reachable if a previous run died
|
|
80
|
+
// without releasing (SIGKILL, OOM).
|
|
81
|
+
rmSync(lock, { force: true });
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
rmSync(lock, { force: true });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
// `flag: 'wx'` = open with O_CREAT | O_EXCL — atomic against a racing
|
|
89
|
+
// peer, and writeFileSync(path, ...) closes the fd internally so no
|
|
90
|
+
// manual closeSync is needed (vs. the openSync + writeFileSync(fd)
|
|
91
|
+
// form, which leaks the fd).
|
|
92
|
+
writeFileSync(lock, JSON.stringify({
|
|
93
|
+
acquiredAt: new Date().toISOString(),
|
|
94
|
+
pid: process.pid,
|
|
95
|
+
}), { flag: 'wx' });
|
|
96
|
+
return {
|
|
97
|
+
release: () => {
|
|
98
|
+
try {
|
|
99
|
+
unlinkSync(lock);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// Already gone — fine.
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return { load, save, update, acquireLock };
|
|
112
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find-smells phase: clone the product's repo, ask Claude to audit code for
|
|
3
|
+
* code smells (refactor candidates, perf cliffs, dead code, etc.), and file
|
|
4
|
+
* each new finding as an issue. Subsequent runs are incremental, scoped to
|
|
5
|
+
* commits since the previous successful scan.
|
|
6
|
+
*
|
|
7
|
+
* Companion to find-bugs (real defects) and find-features (missing user
|
|
8
|
+
* capability). The three phases share the same workspace + state pattern.
|
|
9
|
+
*/
|
|
10
|
+
import { type SmellCategory } from './types.js';
|
|
11
|
+
export interface FindSmellsOptions {
|
|
12
|
+
productId: string;
|
|
13
|
+
githubToken: string;
|
|
14
|
+
owner: string;
|
|
15
|
+
repo: string;
|
|
16
|
+
/** Force a full scan even if previous-scan state exists. */
|
|
17
|
+
full?: boolean;
|
|
18
|
+
/** Branch to scan; defaults to the repo's default branch. */
|
|
19
|
+
branch?: string;
|
|
20
|
+
/** Upper bound on files the auditor may Read. Keeps token cost predictable. */
|
|
21
|
+
maxFiles?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Optional category filter. Agent is asked to honour it via the prompt
|
|
24
|
+
* AND its output is re-filtered after parsing — both are required because
|
|
25
|
+
* the agent can't be trusted to obey perfectly.
|
|
26
|
+
*/
|
|
27
|
+
categories?: SmellCategory[];
|
|
28
|
+
verbose?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface FindSmellsResult {
|
|
31
|
+
status: 'success' | 'error';
|
|
32
|
+
message: string;
|
|
33
|
+
scannedCommitSha?: string;
|
|
34
|
+
smellsFound?: number;
|
|
35
|
+
issuesCreated?: number;
|
|
36
|
+
/** Findings the agent passed off to find-bugs (not filed by this run). */
|
|
37
|
+
deferredToBugs?: number;
|
|
38
|
+
/** Findings the agent passed off to find-features (not filed by this run). */
|
|
39
|
+
deferredToFeatures?: number;
|
|
40
|
+
summary?: string;
|
|
41
|
+
}
|
|
42
|
+
/** Single source of truth — referenced by the CLI help string too. */
|
|
43
|
+
export declare const DEFAULT_MAX_FILES = 200;
|
|
44
|
+
/**
|
|
45
|
+
* Scan a product's repository for code smells and file each new finding as an issue.
|
|
46
|
+
*/
|
|
47
|
+
export declare function scanForSmells(options: FindSmellsOptions): Promise<FindSmellsResult>;
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find-smells phase: clone the product's repo, ask Claude to audit code for
|
|
3
|
+
* code smells (refactor candidates, perf cliffs, dead code, etc.), and file
|
|
4
|
+
* each new finding as an issue. Subsequent runs are incremental, scoped to
|
|
5
|
+
* commits since the previous successful scan.
|
|
6
|
+
*
|
|
7
|
+
* Companion to find-bugs (real defects) and find-features (missing user
|
|
8
|
+
* capability). The three phases share the same workspace + state pattern.
|
|
9
|
+
*/
|
|
10
|
+
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
11
|
+
import { DEFAULT_MODEL } from '../../constants.js';
|
|
12
|
+
import { logError, logInfo, logSuccess, logWarning, } from '../../utils/logger.js';
|
|
13
|
+
import { cloneIssueRepo, ensureWorkspaceDir, syncRepoToRef, } from '../../workspace/workspace-manager.js';
|
|
14
|
+
import { detectDefaultBranch, gitRevParse, isAncestor, listChangedPaths, } from '../find-shared/git.js';
|
|
15
|
+
import { createIssue, fetchOpenIssues, fetchProductBasics, } from '../find-shared/mcp.js';
|
|
16
|
+
import { createPromptGenerator, extractTextFromContent, tryExtractResult, } from '../pr-shared/agent-utils.js';
|
|
17
|
+
import { createFindSmellsSystemPrompt, createFindSmellsUserPrompt, } from './prompts.js';
|
|
18
|
+
import { acquireFindSmellsLock, loadFindSmellsState, updateFindSmellsState, } from './state.js';
|
|
19
|
+
import { isScanResult, isSmellCategory, } from './types.js';
|
|
20
|
+
const WORKSPACE_KEY = 'find-smells';
|
|
21
|
+
/** Single source of truth — referenced by the CLI help string too. */
|
|
22
|
+
export const DEFAULT_MAX_FILES = 200;
|
|
23
|
+
/**
|
|
24
|
+
* Upper bound on turns for the in-CLI Claude session. Same rationale as
|
|
25
|
+
* find-bugs: read-only but open-scope audit needs headroom for repo-wide
|
|
26
|
+
* Glob/Read/Grep without runaway exploration.
|
|
27
|
+
*/
|
|
28
|
+
const MAX_TURNS = 200;
|
|
29
|
+
/**
|
|
30
|
+
* Scan a product's repository for code smells and file each new finding as an issue.
|
|
31
|
+
*/
|
|
32
|
+
// eslint-disable-next-line complexity
|
|
33
|
+
export async function scanForSmells(options) {
|
|
34
|
+
const { productId, githubToken, owner, repo, full, maxFiles, categories, verbose, } = options;
|
|
35
|
+
logInfo(`Starting smell scan for product ${productId} (${owner}/${repo})`);
|
|
36
|
+
const lock = acquireFindSmellsLock(productId);
|
|
37
|
+
if (!lock) {
|
|
38
|
+
logWarning(`Another smell scan is already in progress for product ${productId}; skipping.`);
|
|
39
|
+
return {
|
|
40
|
+
status: 'error',
|
|
41
|
+
message: 'Another smell scan is already in progress for this product',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
updateFindSmellsState(productId, {
|
|
46
|
+
lastAttemptedAt: new Date().toISOString(),
|
|
47
|
+
});
|
|
48
|
+
const workspaceRoot = ensureWorkspaceDir();
|
|
49
|
+
const repoKey = `${WORKSPACE_KEY}-${productId}`;
|
|
50
|
+
const cloned = cloneIssueRepo(workspaceRoot, repoKey, owner, repo, githubToken);
|
|
51
|
+
const { repoPath } = cloned;
|
|
52
|
+
const branch = options.branch ?? detectDefaultBranch(repoPath);
|
|
53
|
+
logInfo(`Syncing ${owner}/${repo} to branch ${branch}`);
|
|
54
|
+
syncRepoToRef(repoPath, { branch }, githubToken);
|
|
55
|
+
const headSha = gitRevParse(repoPath, 'HEAD');
|
|
56
|
+
const state = loadFindSmellsState(productId);
|
|
57
|
+
const baseSha = full ? undefined : state.lastScannedCommitSha;
|
|
58
|
+
let scope = 'full';
|
|
59
|
+
let changedPaths;
|
|
60
|
+
if (baseSha && baseSha !== headSha) {
|
|
61
|
+
const sameTree = isAncestor(repoPath, baseSha, headSha);
|
|
62
|
+
if (sameTree) {
|
|
63
|
+
scope = 'incremental';
|
|
64
|
+
changedPaths = listChangedPaths(repoPath, baseSha, headSha);
|
|
65
|
+
logInfo(`Incremental scan: ${changedPaths.length} files changed since ${baseSha.slice(0, 8)}`);
|
|
66
|
+
if (changedPaths.length === 0) {
|
|
67
|
+
logSuccess('No code changes since last scan; nothing to do.');
|
|
68
|
+
updateFindSmellsState(productId, {
|
|
69
|
+
lastScannedCommitSha: headSha,
|
|
70
|
+
lastScannedAt: new Date().toISOString(),
|
|
71
|
+
lastError: undefined,
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
status: 'success',
|
|
75
|
+
message: 'No changes since last scan',
|
|
76
|
+
scannedCommitSha: headSha,
|
|
77
|
+
smellsFound: 0,
|
|
78
|
+
issuesCreated: 0,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
logWarning(`Last scanned sha ${baseSha.slice(0, 8)} not reachable from HEAD; falling back to full scan.`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else if (baseSha === headSha) {
|
|
87
|
+
// Symmetric with the "no changed files" early return above: even though
|
|
88
|
+
// the sha didn't move, advance lastScannedAt + clear lastError so the
|
|
89
|
+
// state file accurately reflects when we last verified the repo.
|
|
90
|
+
logSuccess('HEAD unchanged since last scan; nothing to do.');
|
|
91
|
+
updateFindSmellsState(productId, {
|
|
92
|
+
lastScannedAt: new Date().toISOString(),
|
|
93
|
+
lastError: undefined,
|
|
94
|
+
});
|
|
95
|
+
return {
|
|
96
|
+
status: 'success',
|
|
97
|
+
message: 'HEAD unchanged since last scan',
|
|
98
|
+
scannedCommitSha: headSha,
|
|
99
|
+
smellsFound: 0,
|
|
100
|
+
issuesCreated: 0,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
const product = await fetchProductBasics(productId);
|
|
104
|
+
const existingIssues = await fetchOpenIssues(productId);
|
|
105
|
+
logInfo(`Loaded ${existingIssues.length} existing issues for dedup context`);
|
|
106
|
+
const systemPrompt = createFindSmellsSystemPrompt();
|
|
107
|
+
const userPrompt = createFindSmellsUserPrompt({
|
|
108
|
+
productName: product.name,
|
|
109
|
+
productDescription: product.description,
|
|
110
|
+
scope,
|
|
111
|
+
baseSha,
|
|
112
|
+
headSha,
|
|
113
|
+
changedPaths,
|
|
114
|
+
maxFiles: maxFiles ?? DEFAULT_MAX_FILES,
|
|
115
|
+
categories,
|
|
116
|
+
existingIssues: existingIssues.map((i) => ({
|
|
117
|
+
id: i.id,
|
|
118
|
+
name: i.name,
|
|
119
|
+
description: i.description,
|
|
120
|
+
})),
|
|
121
|
+
});
|
|
122
|
+
let lastAssistantResponse = '';
|
|
123
|
+
let scanResult = null;
|
|
124
|
+
logInfo('Running Claude code-smell audit...');
|
|
125
|
+
for await (const message of query({
|
|
126
|
+
prompt: createPromptGenerator(userPrompt),
|
|
127
|
+
options: {
|
|
128
|
+
systemPrompt: {
|
|
129
|
+
type: 'preset',
|
|
130
|
+
preset: 'claude_code',
|
|
131
|
+
append: systemPrompt,
|
|
132
|
+
},
|
|
133
|
+
model: DEFAULT_MODEL,
|
|
134
|
+
maxTurns: MAX_TURNS,
|
|
135
|
+
permissionMode: 'bypassPermissions',
|
|
136
|
+
cwd: repoPath,
|
|
137
|
+
},
|
|
138
|
+
})) {
|
|
139
|
+
if (message.type === 'assistant') {
|
|
140
|
+
lastAssistantResponse += extractTextFromContent(message.message?.content ?? [], verbose);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (message.type !== 'result') {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const responseText = message.subtype === 'success'
|
|
147
|
+
? message.result || lastAssistantResponse
|
|
148
|
+
: lastAssistantResponse;
|
|
149
|
+
const parsed = tryExtractResult(responseText, 'scan_result');
|
|
150
|
+
if (isScanResult(parsed)) {
|
|
151
|
+
scanResult = parsed;
|
|
152
|
+
}
|
|
153
|
+
else if (message.subtype !== 'success') {
|
|
154
|
+
logError(`Audit incomplete: ${message.subtype}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (!scanResult) {
|
|
158
|
+
const msg = 'Audit failed: could not parse a scan_result from the agent';
|
|
159
|
+
updateFindSmellsState(productId, { lastError: msg });
|
|
160
|
+
return {
|
|
161
|
+
status: 'error',
|
|
162
|
+
message: msg,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
const { summary, smells } = scanResult;
|
|
166
|
+
logInfo(`Audit produced ${smells.length} candidate smells. ${summary}`);
|
|
167
|
+
const deferredBugs = scanResult.deferred_to_bugs ?? [];
|
|
168
|
+
const deferredFeatures = scanResult.deferred_to_features ?? [];
|
|
169
|
+
if (deferredBugs.length > 0) {
|
|
170
|
+
logInfo(`${deferredBugs.length} finding(s) deferred to find-bugs — run \`edsger find-bugs ${productId}\` to pick them up:`);
|
|
171
|
+
for (const d of deferredBugs) {
|
|
172
|
+
const loc = d.file ? ` (${d.file}${d.line ? `:${d.line}` : ''})` : '';
|
|
173
|
+
logInfo(` • ${d.title}${loc} — ${d.reason}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (deferredFeatures.length > 0) {
|
|
177
|
+
logInfo(`${deferredFeatures.length} finding(s) deferred to find-features — run \`edsger find-features ${productId}\` to pick them up:`);
|
|
178
|
+
for (const d of deferredFeatures) {
|
|
179
|
+
const loc = d.file ? ` (${d.file}${d.line ? `:${d.line}` : ''})` : '';
|
|
180
|
+
logInfo(` • ${d.title}${loc} — ${d.reason}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const { kept: filteredSmells, droppedCategories } = filterByCategories(smells, categories);
|
|
184
|
+
if (droppedCategories.length > 0) {
|
|
185
|
+
const summaryLine = droppedCategories
|
|
186
|
+
.map(({ category, count }) => `${category}×${count}`)
|
|
187
|
+
.join(', ');
|
|
188
|
+
logWarning(`Dropped findings outside the requested categories or with unknown category: ${summaryLine}.`);
|
|
189
|
+
}
|
|
190
|
+
let created = 0;
|
|
191
|
+
for (const smell of filteredSmells) {
|
|
192
|
+
const issueId = await createIssueForSmell(productId, smell);
|
|
193
|
+
if (issueId) {
|
|
194
|
+
created++;
|
|
195
|
+
logSuccess(`Filed issue ${issueId}: ${smell.title}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
updateFindSmellsState(productId, {
|
|
199
|
+
lastScannedCommitSha: headSha,
|
|
200
|
+
lastScannedAt: new Date().toISOString(),
|
|
201
|
+
lastError: undefined,
|
|
202
|
+
});
|
|
203
|
+
const droppedCount = droppedCategories.reduce((acc, d) => acc + d.count, 0);
|
|
204
|
+
return {
|
|
205
|
+
status: 'success',
|
|
206
|
+
message: `Filed ${created} of ${filteredSmells.length} candidate smells (${droppedCount} dropped by category filter; ${deferredBugs.length} deferred to bugs, ${deferredFeatures.length} to features)`,
|
|
207
|
+
scannedCommitSha: headSha,
|
|
208
|
+
smellsFound: filteredSmells.length,
|
|
209
|
+
issuesCreated: created,
|
|
210
|
+
deferredToBugs: deferredBugs.length,
|
|
211
|
+
deferredToFeatures: deferredFeatures.length,
|
|
212
|
+
summary,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
217
|
+
logError(`Smell scan failed: ${errorMessage}`);
|
|
218
|
+
updateFindSmellsState(productId, { lastError: errorMessage });
|
|
219
|
+
return {
|
|
220
|
+
status: 'error',
|
|
221
|
+
message: `Smell scan failed: ${errorMessage}`,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
lock.release();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async function createIssueForSmell(productId, smell) {
|
|
229
|
+
return createIssue({
|
|
230
|
+
productId,
|
|
231
|
+
title: smell.title,
|
|
232
|
+
description: formatIssueDescription(smell),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Filter the agent's findings down to the requested categories. The system
|
|
237
|
+
* prompt asks the agent to honour the filter, but trusting the prompt alone
|
|
238
|
+
* lets bad output through; we re-check here. Items with an unknown category
|
|
239
|
+
* are also dropped — the type system can't enforce that across the JSON
|
|
240
|
+
* boundary.
|
|
241
|
+
*
|
|
242
|
+
* Returns per-category drop counts so the user-facing warning can name the
|
|
243
|
+
* specific categories that were filtered out (more actionable than a bare
|
|
244
|
+
* total).
|
|
245
|
+
*/
|
|
246
|
+
function filterByCategories(smells, categories) {
|
|
247
|
+
const allow = categories?.length ? new Set(categories) : null;
|
|
248
|
+
const kept = [];
|
|
249
|
+
const droppedTally = new Map();
|
|
250
|
+
for (const s of smells) {
|
|
251
|
+
const known = isSmellCategory(s.category);
|
|
252
|
+
const inAllow = !allow || (known && allow.has(s.category));
|
|
253
|
+
if (known && inAllow) {
|
|
254
|
+
kept.push(s);
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
const key = known ? s.category : '<unknown>';
|
|
258
|
+
droppedTally.set(key, (droppedTally.get(key) ?? 0) + 1);
|
|
259
|
+
}
|
|
260
|
+
const droppedCategories = Array.from(droppedTally.entries())
|
|
261
|
+
.map(([category, count]) => ({ category, count }))
|
|
262
|
+
.sort((a, b) => b.count - a.count);
|
|
263
|
+
return { kept, droppedCategories };
|
|
264
|
+
}
|
|
265
|
+
function formatIssueDescription(smell) {
|
|
266
|
+
const location = smell.line ? `${smell.file}:${smell.line}` : smell.file;
|
|
267
|
+
const lines = [
|
|
268
|
+
`**Severity**: ${smell.severity}`,
|
|
269
|
+
`**Category**: ${smell.category}`,
|
|
270
|
+
`**Location**: \`${location}\``,
|
|
271
|
+
'',
|
|
272
|
+
smell.description,
|
|
273
|
+
'',
|
|
274
|
+
'---',
|
|
275
|
+
'_Filed automatically by `edsger find-smells`._',
|
|
276
|
+
];
|
|
277
|
+
return lines.join('\n');
|
|
278
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompts for the find-smells phase. The agent runs with cwd=repo checkout
|
|
3
|
+
* and has Read/Grep/Glob/Bash available — it explores the code itself
|
|
4
|
+
* rather than receiving a pre-baked diff.
|
|
5
|
+
*
|
|
6
|
+
* Boundary against neighbouring phases:
|
|
7
|
+
* - find-bugs handles real defects (security, correctness, races, etc.)
|
|
8
|
+
* - find-features handles missing user-facing capability
|
|
9
|
+
* - find-smells (this phase) handles "the code would be better if changed":
|
|
10
|
+
* refactor candidates, dead code, perf cliffs, type-safety gaps, etc.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createFindSmellsSystemPrompt(): string;
|
|
13
|
+
export interface FindSmellsUserPromptParams {
|
|
14
|
+
productName: string;
|
|
15
|
+
productDescription?: string;
|
|
16
|
+
scope: 'full' | 'incremental';
|
|
17
|
+
baseSha?: string;
|
|
18
|
+
headSha: string;
|
|
19
|
+
changedPaths?: string[];
|
|
20
|
+
existingIssues: {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
}[];
|
|
25
|
+
/** Upper bound on files the auditor may Read. Keeps token cost predictable. */
|
|
26
|
+
maxFiles?: number;
|
|
27
|
+
/** Optional comma-separated category filter from the CLI. */
|
|
28
|
+
categories?: string[];
|
|
29
|
+
}
|
|
30
|
+
export declare function createFindSmellsUserPrompt(params: FindSmellsUserPromptParams): string;
|