edsger 0.77.0 → 0.78.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/LICENSE +21 -0
- package/README.md +95 -210
- package/assets/LICENSE +21 -0
- package/assets/README.md +104 -0
- package/assets/benchmark/base.yaml +286 -0
- package/assets/benchmark/csharp.yaml +68 -0
- package/assets/benchmark/dart.yaml +70 -0
- package/assets/benchmark/go.yaml +71 -0
- package/assets/benchmark/java.yaml +68 -0
- package/assets/benchmark/python.yaml +72 -0
- package/assets/benchmark/typescript.yaml +71 -0
- package/assets/languages.yaml +35 -0
- package/assets/manifest.yaml +39 -0
- package/assets/review/base.yaml +111 -0
- package/assets/review/csharp.yaml +38 -0
- package/assets/review/dart.yaml +40 -0
- package/assets/review/go.yaml +40 -0
- package/assets/review/java.yaml +40 -0
- package/assets/review/python.yaml +41 -0
- package/assets/review/typescript.yaml +40 -0
- package/assets/schema/benchmark.schema.json +87 -0
- package/assets/schema/review.schema.json +73 -0
- package/assets/skills/benchmark/SKILL.md +66 -0
- package/assets/skills/pr-resolve/SKILL.md +58 -0
- package/assets/skills/pr-review/SKILL.md +66 -0
- package/dist/index.js +1339 -1247
- package/dist/index.js.map +1 -0
- package/package.json +58 -53
- package/dist/api/adr.d.ts +0 -48
- package/dist/api/adr.js +0 -139
- package/dist/api/app-store-iap.d.ts +0 -60
- package/dist/api/app-store-iap.js +0 -145
- package/dist/api/app-store.d.ts +0 -93
- package/dist/api/app-store.js +0 -206
- package/dist/api/chat.d.ts +0 -72
- package/dist/api/chat.js +0 -272
- package/dist/api/cross-product.d.ts +0 -33
- package/dist/api/cross-product.js +0 -127
- package/dist/api/financing.d.ts +0 -47
- package/dist/api/financing.js +0 -37
- package/dist/api/github.d.ts +0 -86
- package/dist/api/github.js +0 -203
- package/dist/api/growth.d.ts +0 -81
- package/dist/api/growth.js +0 -75
- package/dist/api/intelligence-normalize.d.ts +0 -25
- package/dist/api/intelligence-normalize.js +0 -103
- package/dist/api/intelligence.d.ts +0 -187
- package/dist/api/intelligence.js +0 -402
- package/dist/api/issues/approval-checker.d.ts +0 -22
- package/dist/api/issues/approval-checker.js +0 -141
- package/dist/api/issues/batch-operations.d.ts +0 -17
- package/dist/api/issues/batch-operations.js +0 -100
- package/dist/api/issues/get-issue.d.ts +0 -5
- package/dist/api/issues/get-issue.js +0 -47
- package/dist/api/issues/index.d.ts +0 -8
- package/dist/api/issues/index.js +0 -10
- package/dist/api/issues/issue-utils.d.ts +0 -25
- package/dist/api/issues/issue-utils.js +0 -133
- package/dist/api/issues/status-updater.d.ts +0 -68
- package/dist/api/issues/status-updater.js +0 -190
- package/dist/api/issues/test-cases.d.ts +0 -29
- package/dist/api/issues/test-cases.js +0 -185
- package/dist/api/issues/update-issue.d.ts +0 -25
- package/dist/api/issues/update-issue.js +0 -127
- package/dist/api/issues/user-stories.d.ts +0 -21
- package/dist/api/issues/user-stories.js +0 -163
- package/dist/api/mcp-client.d.ts +0 -22
- package/dist/api/mcp-client.js +0 -75
- package/dist/api/products/test-cases.d.ts +0 -18
- package/dist/api/products/test-cases.js +0 -51
- package/dist/api/products.d.ts +0 -15
- package/dist/api/products.js +0 -45
- package/dist/api/release-test-cases.d.ts +0 -7
- package/dist/api/release-test-cases.js +0 -59
- package/dist/api/releases.d.ts +0 -41
- package/dist/api/releases.js +0 -117
- package/dist/api/run-sheets.d.ts +0 -22
- package/dist/api/run-sheets.js +0 -13
- package/dist/api/storage.d.ts +0 -13
- package/dist/api/storage.js +0 -29
- package/dist/api/tasks.d.ts +0 -26
- package/dist/api/tasks.js +0 -81
- package/dist/api/test-reports.d.ts +0 -9
- package/dist/api/test-reports.js +0 -43
- package/dist/api/user-psychology.d.ts +0 -101
- package/dist/api/user-psychology.js +0 -143
- package/dist/api/web-deploy.d.ts +0 -67
- package/dist/api/web-deploy.js +0 -199
- package/dist/auth/auth-store.d.ts +0 -98
- package/dist/auth/auth-store.js +0 -194
- package/dist/auth/env-store.d.ts +0 -41
- package/dist/auth/env-store.js +0 -127
- package/dist/auth/login.d.ts +0 -18
- package/dist/auth/login.js +0 -179
- package/dist/commands/adr/index.d.ts +0 -13
- package/dist/commands/adr/index.js +0 -31
- package/dist/commands/agent-workflow/chat-worker.d.ts +0 -16
- package/dist/commands/agent-workflow/chat-worker.js +0 -465
- package/dist/commands/agent-workflow/index.d.ts +0 -13
- package/dist/commands/agent-workflow/index.js +0 -107
- package/dist/commands/agent-workflow/issue-worker.d.ts +0 -14
- package/dist/commands/agent-workflow/issue-worker.js +0 -65
- package/dist/commands/agent-workflow/processor.d.ts +0 -95
- package/dist/commands/agent-workflow/processor.js +0 -519
- package/dist/commands/analyze-logs/index.d.ts +0 -7
- package/dist/commands/analyze-logs/index.js +0 -34
- package/dist/commands/api-docs/index.d.ts +0 -18
- package/dist/commands/api-docs/index.js +0 -41
- package/dist/commands/app-store/index.d.ts +0 -12
- package/dist/commands/app-store/index.js +0 -69
- package/dist/commands/architecture-diagram/index.d.ts +0 -8
- package/dist/commands/architecture-diagram/index.js +0 -10
- package/dist/commands/build/detect-project.d.ts +0 -52
- package/dist/commands/build/detect-project.js +0 -277
- package/dist/commands/build/index.d.ts +0 -66
- package/dist/commands/build/index.js +0 -544
- package/dist/commands/chat-serve/chat-server.d.ts +0 -114
- package/dist/commands/chat-serve/chat-server.js +0 -339
- package/dist/commands/chat-serve/index.d.ts +0 -20
- package/dist/commands/chat-serve/index.js +0 -58
- package/dist/commands/checklists/index.d.ts +0 -16
- package/dist/commands/checklists/index.js +0 -187
- package/dist/commands/checklists/tools.d.ts +0 -17
- package/dist/commands/checklists/tools.js +0 -19
- package/dist/commands/class-diagram/index.d.ts +0 -7
- package/dist/commands/class-diagram/index.js +0 -9
- package/dist/commands/code-review/index.d.ts +0 -2
- package/dist/commands/code-review/index.js +0 -37
- package/dist/commands/code-review/reviewer.d.ts +0 -3
- package/dist/commands/code-review/reviewer.js +0 -205
- package/dist/commands/config/index.d.ts +0 -16
- package/dist/commands/config/index.js +0 -60
- package/dist/commands/data-flow/index.d.ts +0 -19
- package/dist/commands/data-flow/index.js +0 -55
- package/dist/commands/diagram-shared/index.d.ts +0 -21
- package/dist/commands/diagram-shared/index.js +0 -37
- package/dist/commands/discover/index.d.ts +0 -14
- package/dist/commands/discover/index.js +0 -29
- package/dist/commands/er-diagram/index.d.ts +0 -19
- package/dist/commands/er-diagram/index.js +0 -55
- package/dist/commands/features/index.d.ts +0 -15
- package/dist/commands/features/index.js +0 -34
- package/dist/commands/financing-deck/index.d.ts +0 -8
- package/dist/commands/financing-deck/index.js +0 -66
- package/dist/commands/find-architecture/index.d.ts +0 -15
- package/dist/commands/find-architecture/index.js +0 -50
- package/dist/commands/find-bugs/index.d.ts +0 -13
- package/dist/commands/find-bugs/index.js +0 -48
- package/dist/commands/find-features/index.d.ts +0 -14
- package/dist/commands/find-features/index.js +0 -42
- package/dist/commands/find-smells/index.d.ts +0 -23
- package/dist/commands/find-smells/index.js +0 -74
- package/dist/commands/flowchart/index.d.ts +0 -8
- package/dist/commands/flowchart/index.js +0 -10
- package/dist/commands/growth-analysis/index.d.ts +0 -7
- package/dist/commands/growth-analysis/index.js +0 -89
- package/dist/commands/init/index.d.ts +0 -5
- package/dist/commands/init/index.js +0 -152
- package/dist/commands/init/prompts.d.ts +0 -9
- package/dist/commands/init/prompts.js +0 -128
- package/dist/commands/init/templates.d.ts +0 -9
- package/dist/commands/init/templates.js +0 -264
- package/dist/commands/intelligence/index.d.ts +0 -15
- package/dist/commands/intelligence/index.js +0 -138
- package/dist/commands/issue-analysis/index.d.ts +0 -5
- package/dist/commands/issue-analysis/index.js +0 -9
- package/dist/commands/pr-resolve/index.d.ts +0 -13
- package/dist/commands/pr-resolve/index.js +0 -50
- package/dist/commands/pr-review/index.d.ts +0 -12
- package/dist/commands/pr-review/index.js +0 -47
- package/dist/commands/product-techniques/index.d.ts +0 -15
- package/dist/commands/product-techniques/index.js +0 -37
- package/dist/commands/product-test-cases/index.d.ts +0 -12
- package/dist/commands/product-test-cases/index.js +0 -40
- package/dist/commands/quality-benchmark/index.d.ts +0 -45
- package/dist/commands/quality-benchmark/index.js +0 -247
- package/dist/commands/recipes/index.d.ts +0 -17
- package/dist/commands/recipes/index.js +0 -40
- package/dist/commands/refactor/refactor.d.ts +0 -2
- package/dist/commands/refactor/refactor.js +0 -134
- package/dist/commands/release-sync/index.d.ts +0 -5
- package/dist/commands/release-sync/index.js +0 -38
- package/dist/commands/run-sheet/index.d.ts +0 -6
- package/dist/commands/run-sheet/index.js +0 -48
- package/dist/commands/screen-flow/index.d.ts +0 -18
- package/dist/commands/screen-flow/index.js +0 -54
- package/dist/commands/sequence-diagram/index.d.ts +0 -19
- package/dist/commands/sequence-diagram/index.js +0 -55
- package/dist/commands/session-serve/index.d.ts +0 -32
- package/dist/commands/session-serve/index.js +0 -100
- package/dist/commands/session-turn/index.d.ts +0 -73
- package/dist/commands/session-turn/index.js +0 -254
- package/dist/commands/smoke-test/index.d.ts +0 -5
- package/dist/commands/smoke-test/index.js +0 -40
- package/dist/commands/state-diagram/index.d.ts +0 -7
- package/dist/commands/state-diagram/index.js +0 -9
- package/dist/commands/sync-aws/index.d.ts +0 -16
- package/dist/commands/sync-aws/index.js +0 -214
- package/dist/commands/sync-datadog/index.d.ts +0 -16
- package/dist/commands/sync-datadog/index.js +0 -236
- package/dist/commands/sync-github-issues/index.d.ts +0 -11
- package/dist/commands/sync-github-issues/index.js +0 -42
- package/dist/commands/sync-github-pull-requests/index.d.ts +0 -11
- package/dist/commands/sync-github-pull-requests/index.js +0 -42
- package/dist/commands/sync-org-repos/index.d.ts +0 -12
- package/dist/commands/sync-org-repos/index.js +0 -60
- package/dist/commands/sync-sentry-issues/index.d.ts +0 -14
- package/dist/commands/sync-sentry-issues/index.js +0 -73
- package/dist/commands/sync-terraform/index.d.ts +0 -16
- package/dist/commands/sync-terraform/index.js +0 -248
- package/dist/commands/task-worker/index.d.ts +0 -8
- package/dist/commands/task-worker/index.js +0 -34
- package/dist/commands/task-worker/processor.d.ts +0 -23
- package/dist/commands/task-worker/processor.js +0 -84
- package/dist/commands/technical-design/index.d.ts +0 -5
- package/dist/commands/technical-design/index.js +0 -9
- package/dist/commands/test-cases-analysis/index.d.ts +0 -5
- package/dist/commands/test-cases-analysis/index.js +0 -9
- package/dist/commands/user-psychology/index.d.ts +0 -7
- package/dist/commands/user-psychology/index.js +0 -51
- package/dist/commands/user-stories-analysis/index.d.ts +0 -5
- package/dist/commands/user-stories-analysis/index.js +0 -9
- package/dist/commands/workflow/config/phase-configs.d.ts +0 -5
- package/dist/commands/workflow/config/phase-configs.js +0 -132
- package/dist/commands/workflow/core/index.d.ts +0 -8
- package/dist/commands/workflow/core/index.js +0 -12
- package/dist/commands/workflow/core/issue-filter.d.ts +0 -16
- package/dist/commands/workflow/core/issue-filter.js +0 -47
- package/dist/commands/workflow/core/pipeline-evaluator.d.ts +0 -24
- package/dist/commands/workflow/core/pipeline-evaluator.js +0 -32
- package/dist/commands/workflow/core/state-manager.d.ts +0 -24
- package/dist/commands/workflow/core/state-manager.js +0 -42
- package/dist/commands/workflow/core/workflow-logger.d.ts +0 -23
- package/dist/commands/workflow/core/workflow-logger.js +0 -77
- package/dist/commands/workflow/executors/phase-executor.d.ts +0 -8
- package/dist/commands/workflow/executors/phase-executor.js +0 -338
- package/dist/commands/workflow/index.d.ts +0 -2
- package/dist/commands/workflow/index.js +0 -34
- package/dist/commands/workflow/issue-coordinator.d.ts +0 -18
- package/dist/commands/workflow/issue-coordinator.js +0 -161
- package/dist/commands/workflow/phase-orchestrator.d.ts +0 -26
- package/dist/commands/workflow/phase-orchestrator.js +0 -699
- package/dist/commands/workflow/processor.d.ts +0 -47
- package/dist/commands/workflow/processor.js +0 -149
- package/dist/config/issue-status.d.ts +0 -29
- package/dist/config/issue-status.js +0 -44
- package/dist/config.d.ts +0 -3
- package/dist/config.js +0 -79
- package/dist/constants.d.ts +0 -10
- package/dist/constants.js +0 -36
- package/dist/errors/index.d.ts +0 -68
- package/dist/errors/index.js +0 -129
- package/dist/index.d.ts +0 -4
- package/dist/phases/adr-generation/agent.d.ts +0 -6
- package/dist/phases/adr-generation/agent.js +0 -69
- package/dist/phases/adr-generation/index.d.ts +0 -15
- package/dist/phases/adr-generation/index.js +0 -66
- package/dist/phases/adr-generation/parse.d.ts +0 -12
- package/dist/phases/adr-generation/parse.js +0 -123
- package/dist/phases/adr-generation/prompts.d.ts +0 -8
- package/dist/phases/adr-generation/prompts.js +0 -35
- package/dist/phases/analyze-logs/agent.d.ts +0 -11
- package/dist/phases/analyze-logs/agent.js +0 -66
- package/dist/phases/analyze-logs/index.d.ts +0 -23
- package/dist/phases/analyze-logs/index.js +0 -152
- package/dist/phases/analyze-logs/prompts.d.ts +0 -3
- package/dist/phases/analyze-logs/prompts.js +0 -27
- package/dist/phases/api-docs/index.d.ts +0 -47
- package/dist/phases/api-docs/index.js +0 -254
- package/dist/phases/api-docs/mcp-server.d.ts +0 -25
- package/dist/phases/api-docs/mcp-server.js +0 -82
- package/dist/phases/api-docs/prompts.d.ts +0 -16
- package/dist/phases/api-docs/prompts.js +0 -65
- package/dist/phases/api-docs/types.d.ts +0 -22
- package/dist/phases/api-docs/types.js +0 -10
- package/dist/phases/app-store-generation/agent.d.ts +0 -2
- package/dist/phases/app-store-generation/agent.js +0 -177
- package/dist/phases/app-store-generation/assets/inter-latin-ext.woff2 +0 -0
- package/dist/phases/app-store-generation/assets/inter-latin.woff2 +0 -0
- package/dist/phases/app-store-generation/context.d.ts +0 -21
- package/dist/phases/app-store-generation/context.js +0 -78
- package/dist/phases/app-store-generation/index.d.ts +0 -20
- package/dist/phases/app-store-generation/index.js +0 -157
- package/dist/phases/app-store-generation/inter-font.d.ts +0 -20
- package/dist/phases/app-store-generation/inter-font.js +0 -49
- package/dist/phases/app-store-generation/prompts.d.ts +0 -43
- package/dist/phases/app-store-generation/prompts.js +0 -73
- package/dist/phases/app-store-generation/screenshot-composer.d.ts +0 -41
- package/dist/phases/app-store-generation/screenshot-composer.js +0 -516
- package/dist/phases/app-store-generation/uploader.d.ts +0 -19
- package/dist/phases/app-store-generation/uploader.js +0 -150
- package/dist/phases/architecture-diagram/index.d.ts +0 -15
- package/dist/phases/architecture-diagram/index.js +0 -51
- package/dist/phases/autonomous/index.d.ts +0 -32
- package/dist/phases/autonomous/index.js +0 -412
- package/dist/phases/autonomous/prompts.d.ts +0 -5
- package/dist/phases/autonomous/prompts.js +0 -31
- package/dist/phases/branch-planning/context.d.ts +0 -14
- package/dist/phases/branch-planning/context.js +0 -43
- package/dist/phases/branch-planning/index.d.ts +0 -25
- package/dist/phases/branch-planning/index.js +0 -404
- package/dist/phases/branch-planning/outcome.d.ts +0 -28
- package/dist/phases/branch-planning/outcome.js +0 -115
- package/dist/phases/branch-planning/prompts.d.ts +0 -41
- package/dist/phases/branch-planning/prompts.js +0 -157
- package/dist/phases/bug-fixing/analyzer.d.ts +0 -17
- package/dist/phases/bug-fixing/analyzer.js +0 -247
- package/dist/phases/bug-fixing/context-fetcher.d.ts +0 -22
- package/dist/phases/bug-fixing/context-fetcher.js +0 -137
- package/dist/phases/bug-fixing/index.d.ts +0 -7
- package/dist/phases/bug-fixing/index.js +0 -7
- package/dist/phases/bug-fixing/mcp-server.d.ts +0 -18
- package/dist/phases/bug-fixing/mcp-server.js +0 -19
- package/dist/phases/bug-fixing/prompts.d.ts +0 -2
- package/dist/phases/bug-fixing/prompts.js +0 -9
- package/dist/phases/chat-processor/context.d.ts +0 -38
- package/dist/phases/chat-processor/context.js +0 -91
- package/dist/phases/chat-processor/index.d.ts +0 -49
- package/dist/phases/chat-processor/index.js +0 -482
- package/dist/phases/chat-processor/product-context.d.ts +0 -36
- package/dist/phases/chat-processor/product-context.js +0 -113
- package/dist/phases/chat-processor/product-prompts.d.ts +0 -4
- package/dist/phases/chat-processor/product-prompts.js +0 -65
- package/dist/phases/chat-processor/product-tools.d.ts +0 -8
- package/dist/phases/chat-processor/product-tools.js +0 -11
- package/dist/phases/chat-processor/prompts.d.ts +0 -35
- package/dist/phases/chat-processor/prompts.js +0 -213
- package/dist/phases/chat-processor/tools.d.ts +0 -8
- package/dist/phases/chat-processor/tools.js +0 -11
- package/dist/phases/class-diagram/index.d.ts +0 -14
- package/dist/phases/class-diagram/index.js +0 -76
- package/dist/phases/code-implementation/branch-pr-creator.d.ts +0 -31
- package/dist/phases/code-implementation/branch-pr-creator.js +0 -172
- package/dist/phases/code-implementation/context.d.ts +0 -17
- package/dist/phases/code-implementation/context.js +0 -88
- package/dist/phases/code-implementation/index.d.ts +0 -33
- package/dist/phases/code-implementation/index.js +0 -926
- package/dist/phases/code-implementation/outcome.d.ts +0 -47
- package/dist/phases/code-implementation/outcome.js +0 -53
- package/dist/phases/code-implementation/prompts.d.ts +0 -8
- package/dist/phases/code-implementation/prompts.js +0 -166
- package/dist/phases/code-implementation-verification/agent.d.ts +0 -32
- package/dist/phases/code-implementation-verification/agent.js +0 -225
- package/dist/phases/code-implementation-verification/index.d.ts +0 -27
- package/dist/phases/code-implementation-verification/index.js +0 -121
- package/dist/phases/code-implementation-verification/prompts.d.ts +0 -10
- package/dist/phases/code-implementation-verification/prompts.js +0 -90
- package/dist/phases/code-refine/context.d.ts +0 -99
- package/dist/phases/code-refine/context.js +0 -457
- package/dist/phases/code-refine/index.d.ts +0 -59
- package/dist/phases/code-refine/index.js +0 -348
- package/dist/phases/code-refine/prompts.d.ts +0 -5
- package/dist/phases/code-refine/prompts.js +0 -162
- package/dist/phases/code-refine/refine-iteration.d.ts +0 -33
- package/dist/phases/code-refine/refine-iteration.js +0 -166
- package/dist/phases/code-refine/retry-handler.d.ts +0 -17
- package/dist/phases/code-refine/retry-handler.js +0 -87
- package/dist/phases/code-refine-verification/github.d.ts +0 -54
- package/dist/phases/code-refine-verification/github.js +0 -328
- package/dist/phases/code-refine-verification/index.d.ts +0 -11
- package/dist/phases/code-refine-verification/index.js +0 -260
- package/dist/phases/code-refine-verification/llm-analyzer.d.ts +0 -22
- package/dist/phases/code-refine-verification/llm-analyzer.js +0 -152
- package/dist/phases/code-refine-verification/prompts.d.ts +0 -5
- package/dist/phases/code-refine-verification/prompts.js +0 -119
- package/dist/phases/code-refine-verification/types.d.ts +0 -74
- package/dist/phases/code-refine-verification/types.js +0 -4
- package/dist/phases/code-review/context.d.ts +0 -96
- package/dist/phases/code-review/context.js +0 -278
- package/dist/phases/code-review/diff-utils.d.ts +0 -36
- package/dist/phases/code-review/diff-utils.js +0 -100
- package/dist/phases/code-review/index.d.ts +0 -40
- package/dist/phases/code-review/index.js +0 -627
- package/dist/phases/code-testing/analyzer.d.ts +0 -19
- package/dist/phases/code-testing/analyzer.js +0 -215
- package/dist/phases/code-testing/context-fetcher.d.ts +0 -16
- package/dist/phases/code-testing/context-fetcher.js +0 -83
- package/dist/phases/code-testing/index.d.ts +0 -2
- package/dist/phases/code-testing/index.js +0 -2
- package/dist/phases/code-testing/prompts.d.ts +0 -3
- package/dist/phases/code-testing/prompts.js +0 -63
- package/dist/phases/data-flow/index.d.ts +0 -28
- package/dist/phases/data-flow/index.js +0 -286
- package/dist/phases/data-flow/mcp-server.d.ts +0 -85
- package/dist/phases/data-flow/mcp-server.js +0 -140
- package/dist/phases/data-flow/prompts.d.ts +0 -14
- package/dist/phases/data-flow/prompts.js +0 -36
- package/dist/phases/data-flow/types.d.ts +0 -71
- package/dist/phases/data-flow/types.js +0 -86
- package/dist/phases/diagram-shared/clone-repos.d.ts +0 -63
- package/dist/phases/diagram-shared/clone-repos.js +0 -153
- package/dist/phases/diagram-shared/generate.d.ts +0 -42
- package/dist/phases/diagram-shared/generate.js +0 -162
- package/dist/phases/diagram-shared/graph.d.ts +0 -62
- package/dist/phases/diagram-shared/graph.js +0 -169
- package/dist/phases/diagram-shared/mcp.d.ts +0 -35
- package/dist/phases/diagram-shared/mcp.js +0 -68
- package/dist/phases/diagram-shared/prompts.d.ts +0 -23
- package/dist/phases/diagram-shared/prompts.js +0 -35
- package/dist/phases/discover-services/index.d.ts +0 -29
- package/dist/phases/discover-services/index.js +0 -528
- package/dist/phases/er-diagram/index.d.ts +0 -28
- package/dist/phases/er-diagram/index.js +0 -290
- package/dist/phases/er-diagram/mcp-server.d.ts +0 -77
- package/dist/phases/er-diagram/mcp-server.js +0 -144
- package/dist/phases/er-diagram/prompts.d.ts +0 -14
- package/dist/phases/er-diagram/prompts.js +0 -36
- package/dist/phases/er-diagram/types.d.ts +0 -76
- package/dist/phases/er-diagram/types.js +0 -84
- package/dist/phases/features/index.d.ts +0 -65
- package/dist/phases/features/index.js +0 -292
- package/dist/phases/features/mcp-server.d.ts +0 -61
- package/dist/phases/features/mcp-server.js +0 -165
- package/dist/phases/features/prompts.d.ts +0 -32
- package/dist/phases/features/prompts.js +0 -92
- package/dist/phases/features/types.d.ts +0 -34
- package/dist/phases/features/types.js +0 -15
- package/dist/phases/financing-deck/agent.d.ts +0 -1
- package/dist/phases/financing-deck/agent.js +0 -96
- package/dist/phases/financing-deck/context.d.ts +0 -13
- package/dist/phases/financing-deck/context.js +0 -69
- package/dist/phases/financing-deck/index.d.ts +0 -15
- package/dist/phases/financing-deck/index.js +0 -89
- package/dist/phases/financing-deck/prompts.d.ts +0 -2
- package/dist/phases/financing-deck/prompts.js +0 -94
- package/dist/phases/find-architecture/index.d.ts +0 -47
- package/dist/phases/find-architecture/index.js +0 -268
- package/dist/phases/find-architecture/prompts.d.ts +0 -32
- package/dist/phases/find-architecture/prompts.js +0 -129
- package/dist/phases/find-architecture/state.d.ts +0 -21
- package/dist/phases/find-architecture/state.js +0 -17
- package/dist/phases/find-architecture/types.d.ts +0 -55
- package/dist/phases/find-architecture/types.js +0 -69
- package/dist/phases/find-bugs/index.d.ts +0 -33
- package/dist/phases/find-bugs/index.js +0 -238
- package/dist/phases/find-bugs/prompts.d.ts +0 -22
- package/dist/phases/find-bugs/prompts.js +0 -101
- package/dist/phases/find-bugs/state.d.ts +0 -19
- package/dist/phases/find-bugs/state.js +0 -13
- package/dist/phases/find-bugs/types.d.ts +0 -21
- package/dist/phases/find-bugs/types.js +0 -16
- package/dist/phases/find-features/index.d.ts +0 -40
- package/dist/phases/find-features/index.js +0 -328
- package/dist/phases/find-features/prompts.d.ts +0 -43
- package/dist/phases/find-features/prompts.js +0 -138
- package/dist/phases/find-features/state.d.ts +0 -25
- package/dist/phases/find-features/state.js +0 -22
- package/dist/phases/find-features/types.d.ts +0 -27
- package/dist/phases/find-features/types.js +0 -16
- package/dist/phases/find-shared/baseline.d.ts +0 -45
- package/dist/phases/find-shared/baseline.js +0 -56
- package/dist/phases/find-shared/custom-rules.d.ts +0 -39
- package/dist/phases/find-shared/custom-rules.js +0 -75
- package/dist/phases/find-shared/detect-context.d.ts +0 -40
- package/dist/phases/find-shared/detect-context.js +0 -247
- package/dist/phases/find-shared/git.d.ts +0 -24
- package/dist/phases/find-shared/git.js +0 -60
- package/dist/phases/find-shared/mcp.d.ts +0 -54
- package/dist/phases/find-shared/mcp.js +0 -127
- package/dist/phases/find-shared/rule-config.d.ts +0 -37
- package/dist/phases/find-shared/rule-config.js +0 -67
- package/dist/phases/find-shared/rule-packs.d.ts +0 -65
- package/dist/phases/find-shared/rule-packs.js +0 -124
- package/dist/phases/find-shared/scan-state.d.ts +0 -33
- package/dist/phases/find-shared/scan-state.js +0 -112
- package/dist/phases/find-shared/scoped-read.d.ts +0 -12
- package/dist/phases/find-shared/scoped-read.js +0 -33
- package/dist/phases/find-smells/index.d.ts +0 -50
- package/dist/phases/find-smells/index.js +0 -305
- package/dist/phases/find-smells/prompts.d.ts +0 -31
- package/dist/phases/find-smells/prompts.js +0 -130
- package/dist/phases/find-smells/state.d.ts +0 -21
- package/dist/phases/find-smells/state.js +0 -17
- package/dist/phases/find-smells/types.d.ts +0 -51
- package/dist/phases/find-smells/types.js +0 -64
- package/dist/phases/flow-shared/clone-repos.d.ts +0 -63
- package/dist/phases/flow-shared/clone-repos.js +0 -153
- package/dist/phases/flowchart/index.d.ts +0 -15
- package/dist/phases/flowchart/index.js +0 -50
- package/dist/phases/functional-testing/analyzer.d.ts +0 -46
- package/dist/phases/functional-testing/analyzer.js +0 -622
- package/dist/phases/functional-testing/context-fetcher.d.ts +0 -16
- package/dist/phases/functional-testing/context-fetcher.js +0 -83
- package/dist/phases/functional-testing/http-fallback.d.ts +0 -10
- package/dist/phases/functional-testing/http-fallback.js +0 -156
- package/dist/phases/functional-testing/index.d.ts +0 -4
- package/dist/phases/functional-testing/index.js +0 -4
- package/dist/phases/functional-testing/mcp-server.d.ts +0 -9
- package/dist/phases/functional-testing/mcp-server.js +0 -13
- package/dist/phases/functional-testing/prompts.d.ts +0 -2
- package/dist/phases/functional-testing/prompts.js +0 -58
- package/dist/phases/functional-testing/test-report-creator.d.ts +0 -68
- package/dist/phases/functional-testing/test-report-creator.js +0 -306
- package/dist/phases/functional-testing/test-retry-handler.d.ts +0 -16
- package/dist/phases/functional-testing/test-retry-handler.js +0 -70
- package/dist/phases/growth-analysis/agent.d.ts +0 -2
- package/dist/phases/growth-analysis/agent.js +0 -107
- package/dist/phases/growth-analysis/context.d.ts +0 -23
- package/dist/phases/growth-analysis/context.js +0 -137
- package/dist/phases/growth-analysis/index.d.ts +0 -32
- package/dist/phases/growth-analysis/index.js +0 -226
- package/dist/phases/growth-analysis/prompts.d.ts +0 -2
- package/dist/phases/growth-analysis/prompts.js +0 -45
- package/dist/phases/intelligence-analysis/agent.d.ts +0 -2
- package/dist/phases/intelligence-analysis/agent.js +0 -338
- package/dist/phases/intelligence-analysis/context.d.ts +0 -23
- package/dist/phases/intelligence-analysis/context.js +0 -142
- package/dist/phases/intelligence-analysis/index.d.ts +0 -58
- package/dist/phases/intelligence-analysis/index.js +0 -244
- package/dist/phases/intelligence-analysis/prompts.d.ts +0 -2
- package/dist/phases/intelligence-analysis/prompts.js +0 -62
- package/dist/phases/issue-analysis/agent.d.ts +0 -21
- package/dist/phases/issue-analysis/agent.js +0 -153
- package/dist/phases/issue-analysis/context.d.ts +0 -20
- package/dist/phases/issue-analysis/context.js +0 -93
- package/dist/phases/issue-analysis/index.d.ts +0 -8
- package/dist/phases/issue-analysis/index.js +0 -147
- package/dist/phases/issue-analysis/outcome.d.ts +0 -10
- package/dist/phases/issue-analysis/outcome.js +0 -42
- package/dist/phases/issue-analysis/prompts.d.ts +0 -8
- package/dist/phases/issue-analysis/prompts.js +0 -99
- package/dist/phases/issue-analysis-verification/agent.d.ts +0 -33
- package/dist/phases/issue-analysis-verification/agent.js +0 -124
- package/dist/phases/issue-analysis-verification/index.d.ts +0 -27
- package/dist/phases/issue-analysis-verification/index.js +0 -79
- package/dist/phases/issue-analysis-verification/prompts.d.ts +0 -9
- package/dist/phases/issue-analysis-verification/prompts.js +0 -75
- package/dist/phases/output-contracts.d.ts +0 -7
- package/dist/phases/output-contracts.js +0 -1201
- package/dist/phases/pr-execution/context.d.ts +0 -32
- package/dist/phases/pr-execution/context.js +0 -246
- package/dist/phases/pr-execution/file-assigner.d.ts +0 -57
- package/dist/phases/pr-execution/file-assigner.js +0 -287
- package/dist/phases/pr-execution/index.d.ts +0 -22
- package/dist/phases/pr-execution/index.js +0 -401
- package/dist/phases/pr-execution/outcome.d.ts +0 -24
- package/dist/phases/pr-execution/outcome.js +0 -34
- package/dist/phases/pr-execution/pr-executor.d.ts +0 -42
- package/dist/phases/pr-execution/pr-executor.js +0 -78
- package/dist/phases/pr-execution/prompts.d.ts +0 -27
- package/dist/phases/pr-execution/prompts.js +0 -131
- package/dist/phases/pr-resolve/checklist-learner.d.ts +0 -28
- package/dist/phases/pr-resolve/checklist-learner.js +0 -153
- package/dist/phases/pr-resolve/github-reply.d.ts +0 -16
- package/dist/phases/pr-resolve/github-reply.js +0 -75
- package/dist/phases/pr-resolve/index.d.ts +0 -33
- package/dist/phases/pr-resolve/index.js +0 -272
- package/dist/phases/pr-resolve/prompts.d.ts +0 -17
- package/dist/phases/pr-resolve/prompts.js +0 -107
- package/dist/phases/pr-resolve/types.d.ts +0 -18
- package/dist/phases/pr-resolve/types.js +0 -14
- package/dist/phases/pr-resolve/workspace.d.ts +0 -45
- package/dist/phases/pr-resolve/workspace.js +0 -174
- package/dist/phases/pr-review/index.d.ts +0 -27
- package/dist/phases/pr-review/index.js +0 -209
- package/dist/phases/pr-review/prompts.d.ts +0 -5
- package/dist/phases/pr-review/prompts.js +0 -87
- package/dist/phases/pr-shared/agent-utils.d.ts +0 -47
- package/dist/phases/pr-shared/agent-utils.js +0 -112
- package/dist/phases/pr-shared/context.d.ts +0 -24
- package/dist/phases/pr-shared/context.js +0 -78
- package/dist/phases/pr-shared/status.d.ts +0 -18
- package/dist/phases/pr-shared/status.js +0 -37
- package/dist/phases/pr-splitting/context.d.ts +0 -28
- package/dist/phases/pr-splitting/context.js +0 -182
- package/dist/phases/pr-splitting/import-dep-validator.d.ts +0 -67
- package/dist/phases/pr-splitting/import-dep-validator.js +0 -255
- package/dist/phases/pr-splitting/index.d.ts +0 -36
- package/dist/phases/pr-splitting/index.js +0 -413
- package/dist/phases/pr-splitting/outcome.d.ts +0 -24
- package/dist/phases/pr-splitting/outcome.js +0 -114
- package/dist/phases/pr-splitting/prompts.d.ts +0 -43
- package/dist/phases/pr-splitting/prompts.js +0 -154
- package/dist/phases/product-techniques/index.d.ts +0 -52
- package/dist/phases/product-techniques/index.js +0 -268
- package/dist/phases/product-techniques/mcp-server.d.ts +0 -41
- package/dist/phases/product-techniques/mcp-server.js +0 -96
- package/dist/phases/product-techniques/prompts.d.ts +0 -19
- package/dist/phases/product-techniques/prompts.js +0 -66
- package/dist/phases/product-techniques/types.d.ts +0 -13
- package/dist/phases/product-techniques/types.js +0 -13
- package/dist/phases/product-test-cases/index.d.ts +0 -25
- package/dist/phases/product-test-cases/index.js +0 -174
- package/dist/phases/product-test-cases/prompts.d.ts +0 -24
- package/dist/phases/product-test-cases/prompts.js +0 -80
- package/dist/phases/product-test-cases/types.d.ts +0 -17
- package/dist/phases/product-test-cases/types.js +0 -27
- package/dist/phases/pull-request/creator.d.ts +0 -30
- package/dist/phases/pull-request/creator.js +0 -319
- package/dist/phases/pull-request/handler.d.ts +0 -15
- package/dist/phases/pull-request/handler.js +0 -92
- package/dist/phases/quality-benchmark/gate.d.ts +0 -50
- package/dist/phases/quality-benchmark/gate.js +0 -91
- package/dist/phases/quality-benchmark/index.d.ts +0 -65
- package/dist/phases/quality-benchmark/index.js +0 -208
- package/dist/phases/quality-benchmark/mcp-server.d.ts +0 -46
- package/dist/phases/quality-benchmark/mcp-server.js +0 -252
- package/dist/phases/quality-benchmark/parsers.d.ts +0 -45
- package/dist/phases/quality-benchmark/parsers.js +0 -1311
- package/dist/phases/quality-benchmark/prompts.d.ts +0 -31
- package/dist/phases/quality-benchmark/prompts.js +0 -154
- package/dist/phases/quality-benchmark/rubric.md +0 -1211
- package/dist/phases/quality-benchmark/tool-catalog.d.ts +0 -33
- package/dist/phases/quality-benchmark/tool-catalog.js +0 -693
- package/dist/phases/quality-benchmark/tool-runner.d.ts +0 -69
- package/dist/phases/quality-benchmark/tool-runner.js +0 -399
- package/dist/phases/quality-benchmark/types.d.ts +0 -319
- package/dist/phases/quality-benchmark/types.js +0 -23
- package/dist/phases/quality-benchmark/workspace.d.ts +0 -31
- package/dist/phases/quality-benchmark/workspace.js +0 -110
- package/dist/phases/recipes/index.d.ts +0 -63
- package/dist/phases/recipes/index.js +0 -358
- package/dist/phases/recipes/mcp-server.d.ts +0 -66
- package/dist/phases/recipes/mcp-server.js +0 -229
- package/dist/phases/recipes/prompts.d.ts +0 -35
- package/dist/phases/recipes/prompts.js +0 -105
- package/dist/phases/recipes/types.d.ts +0 -42
- package/dist/phases/recipes/types.js +0 -16
- package/dist/phases/release-sync/github.d.ts +0 -66
- package/dist/phases/release-sync/github.js +0 -140
- package/dist/phases/release-sync/index.d.ts +0 -24
- package/dist/phases/release-sync/index.js +0 -156
- package/dist/phases/release-sync/snapshot.d.ts +0 -27
- package/dist/phases/release-sync/snapshot.js +0 -158
- package/dist/phases/run-sheet/agent.d.ts +0 -49
- package/dist/phases/run-sheet/agent.js +0 -156
- package/dist/phases/run-sheet/index.d.ts +0 -57
- package/dist/phases/run-sheet/index.js +0 -392
- package/dist/phases/screen-flow/index.d.ts +0 -26
- package/dist/phases/screen-flow/index.js +0 -349
- package/dist/phases/screen-flow/mcp-server.d.ts +0 -201
- package/dist/phases/screen-flow/mcp-server.js +0 -272
- package/dist/phases/screen-flow/prompts.d.ts +0 -19
- package/dist/phases/screen-flow/prompts.js +0 -43
- package/dist/phases/screen-flow/theme.d.ts +0 -19
- package/dist/phases/screen-flow/theme.js +0 -193
- package/dist/phases/screen-flow/types.d.ts +0 -137
- package/dist/phases/screen-flow/types.js +0 -81
- package/dist/phases/sequence-diagram/index.d.ts +0 -30
- package/dist/phases/sequence-diagram/index.js +0 -290
- package/dist/phases/sequence-diagram/mcp-server.d.ts +0 -64
- package/dist/phases/sequence-diagram/mcp-server.js +0 -134
- package/dist/phases/sequence-diagram/prompts.d.ts +0 -14
- package/dist/phases/sequence-diagram/prompts.js +0 -36
- package/dist/phases/sequence-diagram/types.d.ts +0 -52
- package/dist/phases/sequence-diagram/types.js +0 -93
- package/dist/phases/smoke-test/agent.d.ts +0 -12
- package/dist/phases/smoke-test/agent.js +0 -93
- package/dist/phases/smoke-test/index.d.ts +0 -22
- package/dist/phases/smoke-test/index.js +0 -241
- package/dist/phases/smoke-test/prompts.d.ts +0 -15
- package/dist/phases/smoke-test/prompts.js +0 -35
- package/dist/phases/state-diagram/index.d.ts +0 -15
- package/dist/phases/state-diagram/index.js +0 -53
- package/dist/phases/sync-github-issues/index.d.ts +0 -41
- package/dist/phases/sync-github-issues/index.js +0 -187
- package/dist/phases/sync-github-issues/state.d.ts +0 -26
- package/dist/phases/sync-github-issues/state.js +0 -18
- package/dist/phases/sync-github-issues/types.d.ts +0 -35
- package/dist/phases/sync-github-issues/types.js +0 -6
- package/dist/phases/sync-github-pull-requests/index.d.ts +0 -23
- package/dist/phases/sync-github-pull-requests/index.js +0 -210
- package/dist/phases/sync-github-pull-requests/state.d.ts +0 -24
- package/dist/phases/sync-github-pull-requests/state.js +0 -16
- package/dist/phases/sync-github-pull-requests/types.d.ts +0 -22
- package/dist/phases/sync-github-pull-requests/types.js +0 -1
- package/dist/phases/sync-org-repos/index.d.ts +0 -28
- package/dist/phases/sync-org-repos/index.js +0 -133
- package/dist/phases/sync-sentry-issues/index.d.ts +0 -29
- package/dist/phases/sync-sentry-issues/index.js +0 -153
- package/dist/phases/sync-sentry-issues/sentry-client.d.ts +0 -66
- package/dist/phases/sync-sentry-issues/sentry-client.js +0 -221
- package/dist/phases/sync-sentry-issues/state.d.ts +0 -23
- package/dist/phases/sync-sentry-issues/state.js +0 -18
- package/dist/phases/sync-sentry-issues/types.d.ts +0 -46
- package/dist/phases/sync-sentry-issues/types.js +0 -6
- package/dist/phases/sync-shared/mcp.d.ts +0 -81
- package/dist/phases/sync-shared/mcp.js +0 -111
- package/dist/phases/task/agent.d.ts +0 -10
- package/dist/phases/task/agent.js +0 -83
- package/dist/phases/task/context.d.ts +0 -16
- package/dist/phases/task/context.js +0 -50
- package/dist/phases/task/index.d.ts +0 -17
- package/dist/phases/task/index.js +0 -54
- package/dist/phases/task/prompts.d.ts +0 -9
- package/dist/phases/task/prompts.js +0 -21
- package/dist/phases/technical-design/context.d.ts +0 -12
- package/dist/phases/technical-design/context.js +0 -39
- package/dist/phases/technical-design/index.d.ts +0 -21
- package/dist/phases/technical-design/index.js +0 -456
- package/dist/phases/technical-design/outcome.d.ts +0 -25
- package/dist/phases/technical-design/outcome.js +0 -41
- package/dist/phases/technical-design/prompts.d.ts +0 -8
- package/dist/phases/technical-design/prompts.js +0 -210
- package/dist/phases/technical-design-verification/agent.d.ts +0 -53
- package/dist/phases/technical-design-verification/agent.js +0 -186
- package/dist/phases/technical-design-verification/index.d.ts +0 -38
- package/dist/phases/technical-design-verification/index.js +0 -152
- package/dist/phases/technical-design-verification/prompts.d.ts +0 -12
- package/dist/phases/technical-design-verification/prompts.js +0 -143
- package/dist/phases/test-cases-analysis/agent.d.ts +0 -9
- package/dist/phases/test-cases-analysis/agent.js +0 -105
- package/dist/phases/test-cases-analysis/context.d.ts +0 -15
- package/dist/phases/test-cases-analysis/context.js +0 -86
- package/dist/phases/test-cases-analysis/formatters.d.ts +0 -6
- package/dist/phases/test-cases-analysis/formatters.js +0 -35
- package/dist/phases/test-cases-analysis/index.d.ts +0 -9
- package/dist/phases/test-cases-analysis/index.js +0 -175
- package/dist/phases/test-cases-analysis/outcome.d.ts +0 -27
- package/dist/phases/test-cases-analysis/outcome.js +0 -146
- package/dist/phases/test-cases-analysis/prompts.d.ts +0 -7
- package/dist/phases/test-cases-analysis/prompts.js +0 -171
- package/dist/phases/user-psychology/agent.d.ts +0 -16
- package/dist/phases/user-psychology/agent.js +0 -105
- package/dist/phases/user-psychology/context.d.ts +0 -10
- package/dist/phases/user-psychology/context.js +0 -65
- package/dist/phases/user-psychology/index.d.ts +0 -18
- package/dist/phases/user-psychology/index.js +0 -96
- package/dist/phases/user-psychology/prompts.d.ts +0 -2
- package/dist/phases/user-psychology/prompts.js +0 -41
- package/dist/phases/user-stories-analysis/agent.d.ts +0 -9
- package/dist/phases/user-stories-analysis/agent.js +0 -105
- package/dist/phases/user-stories-analysis/context.d.ts +0 -15
- package/dist/phases/user-stories-analysis/context.js +0 -86
- package/dist/phases/user-stories-analysis/formatters.d.ts +0 -6
- package/dist/phases/user-stories-analysis/formatters.js +0 -32
- package/dist/phases/user-stories-analysis/index.d.ts +0 -9
- package/dist/phases/user-stories-analysis/index.js +0 -175
- package/dist/phases/user-stories-analysis/outcome.d.ts +0 -27
- package/dist/phases/user-stories-analysis/outcome.js +0 -146
- package/dist/phases/user-stories-analysis/prompts.d.ts +0 -7
- package/dist/phases/user-stories-analysis/prompts.js +0 -194
- package/dist/services/audit-logs.d.ts +0 -73
- package/dist/services/audit-logs.js +0 -174
- package/dist/services/branches.d.ts +0 -103
- package/dist/services/branches.js +0 -391
- package/dist/services/checklist.d.ts +0 -100
- package/dist/services/checklist.js +0 -259
- package/dist/services/coaching/coaching-agent.d.ts +0 -39
- package/dist/services/coaching/coaching-agent.js +0 -308
- package/dist/services/coaching/coaching-loop.d.ts +0 -62
- package/dist/services/coaching/coaching-loop.js +0 -265
- package/dist/services/coaching/index.d.ts +0 -8
- package/dist/services/coaching/index.js +0 -8
- package/dist/services/coaching/phase-coaching.d.ts +0 -47
- package/dist/services/coaching/phase-coaching.js +0 -60
- package/dist/services/coaching/self-rating.d.ts +0 -26
- package/dist/services/coaching/self-rating.js +0 -153
- package/dist/services/feedbacks.d.ts +0 -57
- package/dist/services/feedbacks.js +0 -264
- package/dist/services/phase-hooks/bindings-fetcher.d.ts +0 -21
- package/dist/services/phase-hooks/bindings-fetcher.js +0 -53
- package/dist/services/phase-hooks/hook-executor.d.ts +0 -36
- package/dist/services/phase-hooks/hook-executor.js +0 -167
- package/dist/services/phase-hooks/hook-logging.d.ts +0 -14
- package/dist/services/phase-hooks/hook-logging.js +0 -41
- package/dist/services/phase-hooks/hook-runner.d.ts +0 -19
- package/dist/services/phase-hooks/hook-runner.js +0 -83
- package/dist/services/phase-hooks/index.d.ts +0 -10
- package/dist/services/phase-hooks/index.js +0 -9
- package/dist/services/phase-hooks/plugin-loader.d.ts +0 -31
- package/dist/services/phase-hooks/plugin-loader.js +0 -207
- package/dist/services/phase-hooks/types.d.ts +0 -69
- package/dist/services/phase-hooks/types.js +0 -8
- package/dist/services/phase-ratings.d.ts +0 -70
- package/dist/services/phase-ratings.js +0 -143
- package/dist/services/product-logs.d.ts +0 -31
- package/dist/services/product-logs.js +0 -94
- package/dist/services/pull-requests.d.ts +0 -57
- package/dist/services/pull-requests.js +0 -186
- package/dist/services/quality-reports.d.ts +0 -45
- package/dist/services/quality-reports.js +0 -93
- package/dist/services/repo-config.d.ts +0 -17
- package/dist/services/repo-config.js +0 -50
- package/dist/services/skill-resolver.d.ts +0 -46
- package/dist/services/skill-resolver.js +0 -123
- package/dist/services/video/device-frames.d.ts +0 -34
- package/dist/services/video/device-frames.js +0 -513
- package/dist/services/video/index.d.ts +0 -66
- package/dist/services/video/index.js +0 -238
- package/dist/services/video/retry.d.ts +0 -20
- package/dist/services/video/retry.js +0 -76
- package/dist/services/video/screenshot-generator.d.ts +0 -69
- package/dist/services/video/screenshot-generator.js +0 -259
- package/dist/services/video/tts-generator.d.ts +0 -40
- package/dist/services/video/tts-generator.js +0 -121
- package/dist/services/video/video-assembler.d.ts +0 -45
- package/dist/services/video/video-assembler.js +0 -351
- package/dist/services/video/video-uploader.d.ts +0 -28
- package/dist/services/video/video-uploader.js +0 -108
- package/dist/skills/phase/adr-generation/SKILL.md +0 -51
- package/dist/skills/phase/analyze-logs/SKILL.md +0 -24
- package/dist/skills/phase/app-store-generation/SKILL.md +0 -60
- package/dist/skills/phase/architecture-diagram/SKILL.md +0 -41
- package/dist/skills/phase/autonomous/SKILL.md +0 -52
- package/dist/skills/phase/branch-planning/SKILL.md +0 -55
- package/dist/skills/phase/bug-fixing/SKILL.md +0 -47
- package/dist/skills/phase/class-diagram/SKILL.md +0 -44
- package/dist/skills/phase/code-implementation/SKILL.md +0 -68
- package/dist/skills/phase/code-implementation-verification/SKILL.md +0 -76
- package/dist/skills/phase/code-refine/SKILL.md +0 -62
- package/dist/skills/phase/code-testing/SKILL.md +0 -78
- package/dist/skills/phase/data-flow/SKILL.md +0 -82
- package/dist/skills/phase/er-diagram/SKILL.md +0 -71
- package/dist/skills/phase/flowchart/SKILL.md +0 -38
- package/dist/skills/phase/functional-testing/SKILL.md +0 -62
- package/dist/skills/phase/growth-analysis/SKILL.md +0 -97
- package/dist/skills/phase/incremental-sync/SKILL.md +0 -59
- package/dist/skills/phase/intelligence-analysis/SKILL.md +0 -52
- package/dist/skills/phase/issue-analysis/SKILL.md +0 -73
- package/dist/skills/phase/issue-analysis-verification/SKILL.md +0 -61
- package/dist/skills/phase/pr-execution/SKILL.md +0 -69
- package/dist/skills/phase/pr-splitting/SKILL.md +0 -78
- package/dist/skills/phase/screen-flow/SKILL.md +0 -78
- package/dist/skills/phase/sequence-diagram/SKILL.md +0 -67
- package/dist/skills/phase/smoke-test/SKILL.md +0 -80
- package/dist/skills/phase/state-diagram/SKILL.md +0 -38
- package/dist/skills/phase/task/SKILL.md +0 -19
- package/dist/skills/phase/technical-design/SKILL.md +0 -75
- package/dist/skills/phase/technical-design-verification/SKILL.md +0 -43
- package/dist/skills/phase/test-cases-analysis/SKILL.md +0 -88
- package/dist/skills/phase/user-psychology/SKILL.md +0 -135
- package/dist/skills/phase/user-stories-analysis/SKILL.md +0 -118
- package/dist/supabase/client.d.ts +0 -34
- package/dist/supabase/client.js +0 -123
- package/dist/system/session-manager.d.ts +0 -67
- package/dist/system/session-manager.js +0 -300
- package/dist/system/sleep-notification.d.ts +0 -15
- package/dist/system/sleep-notification.js +0 -108
- package/dist/system/sleep-prevention.d.ts +0 -20
- package/dist/system/sleep-prevention.js +0 -112
- package/dist/tools/bootstrap.d.ts +0 -45
- package/dist/tools/bootstrap.js +0 -50
- package/dist/types/external-sources.d.ts +0 -22
- package/dist/types/external-sources.js +0 -23
- package/dist/types/index.d.ts +0 -206
- package/dist/types/index.js +0 -1
- package/dist/types/issues.d.ts +0 -37
- package/dist/types/issues.js +0 -1
- package/dist/types/llm-responses.d.ts +0 -114
- package/dist/types/llm-responses.js +0 -9
- package/dist/types/pipeline.d.ts +0 -28
- package/dist/types/pipeline.js +0 -4
- package/dist/updater/auto-updater.d.ts +0 -39
- package/dist/updater/auto-updater.js +0 -134
- package/dist/utils/conflict-resolver.d.ts +0 -49
- package/dist/utils/conflict-resolver.js +0 -201
- package/dist/utils/formatters.d.ts +0 -42
- package/dist/utils/formatters.js +0 -166
- package/dist/utils/git-branch-manager-async.d.ts +0 -127
- package/dist/utils/git-branch-manager-async.js +0 -425
- package/dist/utils/git-branch-manager.d.ts +0 -160
- package/dist/utils/git-branch-manager.js +0 -613
- package/dist/utils/git-push.d.ts +0 -43
- package/dist/utils/git-push.js +0 -136
- package/dist/utils/github-repo-info.d.ts +0 -26
- package/dist/utils/github-repo-info.js +0 -45
- package/dist/utils/image-downloader.d.ts +0 -32
- package/dist/utils/image-downloader.js +0 -152
- package/dist/utils/issue-phase-cli.d.ts +0 -26
- package/dist/utils/issue-phase-cli.js +0 -44
- package/dist/utils/json-extract.d.ts +0 -6
- package/dist/utils/json-extract.js +0 -44
- package/dist/utils/json-extractor.d.ts +0 -27
- package/dist/utils/json-extractor.js +0 -102
- package/dist/utils/logger.d.ts +0 -35
- package/dist/utils/logger.js +0 -167
- package/dist/utils/pipeline-logger.d.ts +0 -8
- package/dist/utils/pipeline-logger.js +0 -38
- package/dist/utils/validation.d.ts +0 -68
- package/dist/utils/validation.js +0 -229
- package/dist/utils/workflow-utils.d.ts +0 -22
- package/dist/utils/workflow-utils.js +0 -48
- package/dist/workspace/session-workspace.d.ts +0 -43
- package/dist/workspace/session-workspace.js +0 -90
- package/dist/workspace/workspace-manager.d.ts +0 -105
- package/dist/workspace/workspace-manager.js +0 -280
package/dist/index.js
CHANGED
|
@@ -1,1271 +1,1363 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
import
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
import {
|
|
61
|
-
import
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
.
|
|
210
|
-
.
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
.
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
.
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
.
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
await runDataFlow(productId, {
|
|
283
|
-
diagramId: opts.diagramId,
|
|
284
|
-
repoId: opts.repoId,
|
|
285
|
-
guidance: opts.guidance,
|
|
286
|
-
verbose: opts.verbose,
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
catch (error) {
|
|
290
|
-
exitWithError(error);
|
|
291
|
-
}
|
|
292
|
-
});
|
|
293
|
-
// ============================================================
|
|
294
|
-
// Subcommand: edsger er-diagram <productId>
|
|
295
|
-
// ============================================================
|
|
296
|
-
program
|
|
297
|
-
.command('er-diagram [productId]')
|
|
298
|
-
.description('Generate a structured entity-relationship diagram (tables, views, enums, junctions + their relationships) for a product (or standalone repository) from its schema source')
|
|
299
|
-
.requiredOption('--diagram-id <id>', 'Pending diagrams row id to populate')
|
|
300
|
-
.option('--repo-id <id>', 'Run in repo-only mode against a single repositories row (no product context)')
|
|
301
|
-
.option('-g, --guidance <text>', 'Human direction for the AI (focus areas, exclusions)')
|
|
302
|
-
.option('-v, --verbose', 'Verbose output')
|
|
303
|
-
.action(async (productId, opts) => {
|
|
304
|
-
try {
|
|
305
|
-
if (!productId && !opts.repoId) {
|
|
306
|
-
throw new Error('Provide a productId or --repo-id (repo-only mode) for er-diagram');
|
|
307
|
-
}
|
|
308
|
-
await runErDiagram(productId, {
|
|
309
|
-
diagramId: opts.diagramId,
|
|
310
|
-
repoId: opts.repoId,
|
|
311
|
-
guidance: opts.guidance,
|
|
312
|
-
verbose: opts.verbose,
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
catch (error) {
|
|
316
|
-
exitWithError(error);
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
// ============================================================
|
|
320
|
-
// Subcommand: edsger sequence-diagram <productId>
|
|
321
|
-
// ============================================================
|
|
322
|
-
program
|
|
323
|
-
.command('sequence-diagram [productId]')
|
|
324
|
-
.description('Generate a structured sequence diagram (participants + ordered messages for one scenario) for a product (or standalone repository) from its source code')
|
|
325
|
-
.requiredOption('--diagram-id <id>', 'Pending diagrams row id to populate')
|
|
326
|
-
.option('--repo-id <id>', 'Run in repo-only mode against a single repositories row (no product context)')
|
|
327
|
-
.option('-g, --guidance <text>', 'Human direction for the AI — the scenario to map (e.g. "user checkout")')
|
|
328
|
-
.option('-v, --verbose', 'Verbose output')
|
|
329
|
-
.action(async (productId, opts) => {
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { resolve as resolve2 } from "path";
|
|
5
|
+
import { Command, Option } from "commander";
|
|
6
|
+
import pc6 from "picocolors";
|
|
7
|
+
|
|
8
|
+
// src/commands/benchmark.ts
|
|
9
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
10
|
+
import { basename, join as join3 } from "path";
|
|
11
|
+
import pc2 from "picocolors";
|
|
12
|
+
|
|
13
|
+
// src/core/assets.ts
|
|
14
|
+
import { execFileSync } from "child_process";
|
|
15
|
+
import { existsSync, readdirSync, statSync } from "fs";
|
|
16
|
+
import { homedir } from "os";
|
|
17
|
+
import { dirname, isAbsolute, join, resolve } from "path";
|
|
18
|
+
import { fileURLToPath } from "url";
|
|
19
|
+
|
|
20
|
+
// src/core/logger.ts
|
|
21
|
+
import pc from "picocolors";
|
|
22
|
+
var verbose = false;
|
|
23
|
+
function setVerbose(v) {
|
|
24
|
+
verbose = v;
|
|
25
|
+
}
|
|
26
|
+
var log = {
|
|
27
|
+
info(msg) {
|
|
28
|
+
process.stderr.write(`${pc.cyan("\u2022")} ${msg}
|
|
29
|
+
`);
|
|
30
|
+
},
|
|
31
|
+
success(msg) {
|
|
32
|
+
process.stderr.write(`${pc.green("\u2713")} ${msg}
|
|
33
|
+
`);
|
|
34
|
+
},
|
|
35
|
+
warn(msg) {
|
|
36
|
+
process.stderr.write(`${pc.yellow("!")} ${msg}
|
|
37
|
+
`);
|
|
38
|
+
},
|
|
39
|
+
error(msg) {
|
|
40
|
+
process.stderr.write(`${pc.red("\u2717")} ${msg}
|
|
41
|
+
`);
|
|
42
|
+
},
|
|
43
|
+
step(msg) {
|
|
44
|
+
process.stderr.write(`${pc.dim(" \u2192")} ${pc.dim(msg)}
|
|
45
|
+
`);
|
|
46
|
+
},
|
|
47
|
+
debug(msg) {
|
|
48
|
+
if (verbose) process.stderr.write(`${pc.dim(` ${msg}`)}
|
|
49
|
+
`);
|
|
50
|
+
},
|
|
51
|
+
/** Print a heading to stderr so structured stdout stays clean. */
|
|
52
|
+
heading(msg) {
|
|
53
|
+
process.stderr.write(`
|
|
54
|
+
${pc.bold(msg)}
|
|
55
|
+
`);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// src/util/yaml.ts
|
|
60
|
+
import { readFileSync } from "fs";
|
|
61
|
+
import yaml from "js-yaml";
|
|
62
|
+
function loadStructured(path) {
|
|
63
|
+
const raw = readFileSync(path, "utf8");
|
|
64
|
+
return yaml.load(raw);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/core/assets.ts
|
|
68
|
+
var moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
69
|
+
function findBundledAssetsDir() {
|
|
70
|
+
if (process.env.EDSGER_ASSETS_DIR && existsSync(process.env.EDSGER_ASSETS_DIR)) {
|
|
71
|
+
return process.env.EDSGER_ASSETS_DIR;
|
|
72
|
+
}
|
|
73
|
+
let dir = moduleDir;
|
|
74
|
+
for (let i = 0; i < 8; i++) {
|
|
75
|
+
const candidate = join(dir, "assets");
|
|
76
|
+
if (existsSync(join(candidate, "manifest.yaml"))) return candidate;
|
|
77
|
+
if (existsSync(join(dir, "manifest.yaml")) && existsSync(join(dir, "benchmark"))) {
|
|
78
|
+
return dir;
|
|
79
|
+
}
|
|
80
|
+
const parent = dirname(dir);
|
|
81
|
+
if (parent === dir) break;
|
|
82
|
+
dir = parent;
|
|
83
|
+
}
|
|
84
|
+
throw new Error("Could not locate bundled assets. Reinstall edsger or set EDSGER_ASSETS_DIR.");
|
|
85
|
+
}
|
|
86
|
+
function loadLangConfig(assetsRoot) {
|
|
87
|
+
const cfgPath = join(assetsRoot, "languages.yaml");
|
|
88
|
+
if (!existsSync(cfgPath)) return null;
|
|
89
|
+
return loadStructured(cfgPath);
|
|
90
|
+
}
|
|
91
|
+
function scoreLanguages(names, cfg, minScore) {
|
|
92
|
+
const exts = new Set(names.map(extOf));
|
|
93
|
+
const scored = [];
|
|
94
|
+
for (const lang of cfg.languages) {
|
|
95
|
+
let score = 0;
|
|
96
|
+
for (const m of lang.markers) {
|
|
97
|
+
if (m.includes("*")) {
|
|
98
|
+
const re = globToRegExp(m);
|
|
99
|
+
if (names.some((f) => re.test(f))) score += 3;
|
|
100
|
+
} else if (names.includes(m)) {
|
|
101
|
+
score += 3;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
let extHits = 0;
|
|
105
|
+
for (const e of lang.extensions) if (exts.has(e)) extHits++;
|
|
106
|
+
score += Math.min(extHits, cfg.maxExtensionMatches);
|
|
107
|
+
if (score >= minScore) scored.push({ id: lang.id, score });
|
|
108
|
+
}
|
|
109
|
+
return scored.sort((a, b) => b.score - a.score).map((s) => s.id);
|
|
110
|
+
}
|
|
111
|
+
function detectLanguages(cwd, assetsRoot) {
|
|
112
|
+
const cfg = loadLangConfig(assetsRoot);
|
|
113
|
+
if (!cfg) return [];
|
|
114
|
+
const names = readShallowTree(cwd, 3).map((e) => e.name);
|
|
115
|
+
return scoreLanguages(names, cfg, cfg.minScore);
|
|
116
|
+
}
|
|
117
|
+
function inferLanguagesFromFiles(paths, assetsRoot, minScore = 1) {
|
|
118
|
+
const cfg = loadLangConfig(assetsRoot);
|
|
119
|
+
if (!cfg) return [];
|
|
120
|
+
const names = paths.flatMap((p) => [p, p.split("/").pop() ?? p]);
|
|
121
|
+
return scoreLanguages(names, cfg, minScore);
|
|
122
|
+
}
|
|
123
|
+
function resolveStandard(kind, opts, config, configDir, forcedLanguages) {
|
|
124
|
+
const bundled = opts.assetsPath ? resolveRootPath(opts.assetsPath, opts.cwd) : findBundledAssetsDir();
|
|
125
|
+
const languages = opts.lang ? [normaliseLang(opts.lang)] : config.language ? [normaliseLang(config.language)] : pickDetected(detectLanguages(opts.cwd, bundled), forcedLanguages);
|
|
126
|
+
const roots = [
|
|
127
|
+
{ path: bundled, label: opts.assetsPath ? `assets:${opts.assetsPath}` : "bundled" }
|
|
128
|
+
];
|
|
129
|
+
const overrideSpec = (kind === "benchmark" ? config.benchmark?.standards : config.review?.standards) ?? config.standards;
|
|
130
|
+
if (overrideSpec && !opts.assetsPath) {
|
|
131
|
+
roots.push({ path: resolveRootPath(overrideSpec, configDir), label: `config:${overrideSpec}` });
|
|
132
|
+
}
|
|
133
|
+
const layers = [];
|
|
134
|
+
for (const root of roots) {
|
|
135
|
+
pushIfExists(layers, join(root.path, kind, "base.yaml"), `${root.label}/base`);
|
|
136
|
+
for (const lang of languages) {
|
|
137
|
+
pushIfExists(layers, join(root.path, kind, `${lang}.yaml`), `${root.label}/${lang}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const repoLocal = join(opts.cwd, ".edsger", kind);
|
|
141
|
+
if (existsSync(repoLocal)) {
|
|
142
|
+
pushIfExists(layers, join(repoLocal, "base.yaml"), `repo:.edsger/${kind}/base`);
|
|
143
|
+
for (const lang of languages) {
|
|
144
|
+
pushIfExists(layers, join(repoLocal, `${lang}.yaml`), `repo:.edsger/${kind}/${lang}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (opts.standardPath) {
|
|
148
|
+
const p = resolveRootPath(opts.standardPath, opts.cwd);
|
|
149
|
+
if (existsSync(p) && statSync(p).isDirectory()) {
|
|
150
|
+
pushIfExists(layers, join(p, kind, "base.yaml"), `--standard/base`);
|
|
151
|
+
for (const lang of languages) {
|
|
152
|
+
pushIfExists(layers, join(p, kind, `${lang}.yaml`), `--standard/${lang}`);
|
|
153
|
+
}
|
|
154
|
+
pushIfExists(layers, join(p, "base.yaml"), `--standard/base`);
|
|
155
|
+
} else if (existsSync(p)) {
|
|
156
|
+
pushIfExists(layers, p, `--standard:${opts.standardPath}`);
|
|
157
|
+
} else {
|
|
158
|
+
throw new Error(`--standard path not found: ${p}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (layers.length === 0) {
|
|
162
|
+
throw new Error(`No ${kind} standard found in any source.`);
|
|
163
|
+
}
|
|
164
|
+
const merged = mergeLayers(
|
|
165
|
+
kind,
|
|
166
|
+
layers.map((l) => l.data)
|
|
167
|
+
);
|
|
168
|
+
return { standard: merged, layers: layers.map((l) => l.label), languages };
|
|
169
|
+
}
|
|
170
|
+
function mergeLayers(kind, layers) {
|
|
171
|
+
if (kind === "benchmark") {
|
|
172
|
+
return layers.reduce(
|
|
173
|
+
(acc, layer) => mergeBenchmark(acc, layer),
|
|
174
|
+
{}
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
return layers.reduce(
|
|
178
|
+
(acc, layer) => mergeReview(acc, layer),
|
|
179
|
+
{}
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
function mergeBenchmark(base, overlay) {
|
|
183
|
+
const categories = mergeById(
|
|
184
|
+
base.categories ?? [],
|
|
185
|
+
overlay.categories ?? [],
|
|
186
|
+
(b, o) => ({
|
|
187
|
+
...b,
|
|
188
|
+
...o,
|
|
189
|
+
criteria: mergeById(b.criteria ?? [], o.criteria ?? [], (bc, oc) => ({
|
|
190
|
+
...bc,
|
|
191
|
+
...oc
|
|
192
|
+
}))
|
|
193
|
+
})
|
|
194
|
+
);
|
|
195
|
+
return {
|
|
196
|
+
...base,
|
|
197
|
+
...overlay,
|
|
198
|
+
// Preserve the base layer's identity rather than the last overlay's.
|
|
199
|
+
id: base.id ?? overlay.id,
|
|
200
|
+
name: base.name ?? overlay.name,
|
|
201
|
+
version: base.version ?? overlay.version,
|
|
202
|
+
kind: "benchmark",
|
|
203
|
+
scoreScale: overlay.scoreScale ?? base.scoreScale ?? 4,
|
|
204
|
+
categories
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function mergeReview(base, overlay) {
|
|
208
|
+
const focusAreas = mergeById(
|
|
209
|
+
base.focusAreas ?? [],
|
|
210
|
+
overlay.focusAreas ?? [],
|
|
211
|
+
(b, o) => ({
|
|
212
|
+
...b,
|
|
213
|
+
...o
|
|
214
|
+
})
|
|
215
|
+
);
|
|
216
|
+
return {
|
|
217
|
+
...base,
|
|
218
|
+
...overlay,
|
|
219
|
+
id: base.id ?? overlay.id,
|
|
220
|
+
name: base.name ?? overlay.name,
|
|
221
|
+
version: base.version ?? overlay.version,
|
|
222
|
+
kind: "review",
|
|
223
|
+
policy: { ...base.policy ?? {}, ...overlay.policy ?? {} },
|
|
224
|
+
focusAreas
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function mergeById(base, overlay, merge) {
|
|
228
|
+
const out = base.map((x) => ({ ...x }));
|
|
229
|
+
const index = new Map(out.map((x, i) => [x.id, i]));
|
|
230
|
+
for (const item of overlay) {
|
|
231
|
+
const at = index.get(item.id);
|
|
232
|
+
if (at === void 0) {
|
|
233
|
+
index.set(item.id, out.length);
|
|
234
|
+
out.push({ ...item });
|
|
235
|
+
} else {
|
|
236
|
+
out[at] = merge(out[at], item);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return out;
|
|
240
|
+
}
|
|
241
|
+
function pushIfExists(layers, path, label) {
|
|
242
|
+
if (existsSync(path)) {
|
|
243
|
+
layers.push({ data: loadStructured(path), label });
|
|
244
|
+
log.debug(`standard layer: ${label} (${path})`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function resolveRootPath(spec, baseDir) {
|
|
248
|
+
if (spec.startsWith("git+") || /^(https?|git|ssh):\/\//.test(spec)) {
|
|
249
|
+
return cloneToCache(spec.replace(/^git\+/, ""));
|
|
250
|
+
}
|
|
251
|
+
return isAbsolute(spec) ? spec : resolve(baseDir, spec);
|
|
252
|
+
}
|
|
253
|
+
function cloneToCache(url) {
|
|
254
|
+
const [repo, ref] = url.split("#");
|
|
255
|
+
const safe = repo.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
256
|
+
const dest = join(homedir(), ".cache", "edsger", "standards", safe);
|
|
257
|
+
if (!existsSync(dest)) {
|
|
258
|
+
log.step(`cloning standards repo ${repo}`);
|
|
259
|
+
execFileSync("git", ["clone", "--depth", "1", ...ref ? ["--branch", ref] : [], repo, dest], {
|
|
260
|
+
stdio: "ignore"
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
return dest;
|
|
264
|
+
}
|
|
265
|
+
function readShallowTree(root, depth) {
|
|
266
|
+
const skip = /* @__PURE__ */ new Set([
|
|
267
|
+
"node_modules",
|
|
268
|
+
".git",
|
|
269
|
+
"dist",
|
|
270
|
+
"build",
|
|
271
|
+
"out",
|
|
272
|
+
"target",
|
|
273
|
+
"vendor",
|
|
274
|
+
".venv",
|
|
275
|
+
"venv",
|
|
276
|
+
"__pycache__",
|
|
277
|
+
".dart_tool"
|
|
278
|
+
]);
|
|
279
|
+
const out = [];
|
|
280
|
+
const walk = (dir, level) => {
|
|
281
|
+
let items;
|
|
330
282
|
try {
|
|
331
|
-
|
|
332
|
-
|
|
283
|
+
items = readdirSync(dir);
|
|
284
|
+
} catch {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
for (const name of items) {
|
|
288
|
+
if (skip.has(name)) continue;
|
|
289
|
+
out.push({ name });
|
|
290
|
+
if (level < depth) {
|
|
291
|
+
const full = join(dir, name);
|
|
292
|
+
try {
|
|
293
|
+
if (statSync(full).isDirectory()) walk(full, level + 1);
|
|
294
|
+
} catch {
|
|
333
295
|
}
|
|
334
|
-
|
|
335
|
-
diagramId: opts.diagramId,
|
|
336
|
-
repoId: opts.repoId,
|
|
337
|
-
guidance: opts.guidance,
|
|
338
|
-
verbose: opts.verbose,
|
|
339
|
-
});
|
|
296
|
+
}
|
|
340
297
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
298
|
+
};
|
|
299
|
+
walk(root, 1);
|
|
300
|
+
return out;
|
|
301
|
+
}
|
|
302
|
+
function extOf(name) {
|
|
303
|
+
const i = name.lastIndexOf(".");
|
|
304
|
+
return i < 0 ? "" : name.slice(i);
|
|
305
|
+
}
|
|
306
|
+
function globToRegExp(glob) {
|
|
307
|
+
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
308
|
+
return new RegExp(`^${escaped}$`);
|
|
309
|
+
}
|
|
310
|
+
function pickDetected(detected, fallback) {
|
|
311
|
+
return detected.length ? detected : fallback ?? [];
|
|
312
|
+
}
|
|
313
|
+
function normaliseLang(input) {
|
|
314
|
+
const v = input.trim().toLowerCase();
|
|
315
|
+
const aliases = {
|
|
316
|
+
ts: "typescript",
|
|
317
|
+
js: "typescript",
|
|
318
|
+
javascript: "typescript",
|
|
319
|
+
node: "typescript",
|
|
320
|
+
nodejs: "typescript",
|
|
321
|
+
py: "python",
|
|
322
|
+
golang: "go",
|
|
323
|
+
cs: "csharp",
|
|
324
|
+
"c#": "csharp",
|
|
325
|
+
dotnet: "csharp",
|
|
326
|
+
jvm: "java",
|
|
327
|
+
kotlin: "java",
|
|
328
|
+
flutter: "dart"
|
|
329
|
+
};
|
|
330
|
+
return aliases[v] ?? v;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// src/core/config.ts
|
|
334
|
+
import { existsSync as existsSync2 } from "fs";
|
|
335
|
+
import { join as join2 } from "path";
|
|
336
|
+
var CONFIG_FILENAMES = [
|
|
337
|
+
"edsger.config.yaml",
|
|
338
|
+
"edsger.config.yml",
|
|
339
|
+
"edsger.config.json",
|
|
340
|
+
".edsgerrc.yaml",
|
|
341
|
+
".edsgerrc.yml",
|
|
342
|
+
".edsgerrc.json",
|
|
343
|
+
".edsgerrc"
|
|
370
344
|
];
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
.option('-g, --guidance <text>', sub.guidance)
|
|
378
|
-
.option('-v, --verbose', 'Verbose output')
|
|
379
|
-
.action(async (productId, opts) => {
|
|
345
|
+
function loadConfig(cwd) {
|
|
346
|
+
const searchDirs = [cwd, join2(cwd, ".edsger")];
|
|
347
|
+
for (const dir of searchDirs) {
|
|
348
|
+
for (const name of CONFIG_FILENAMES) {
|
|
349
|
+
const p = join2(dir, name);
|
|
350
|
+
if (existsSync2(p)) {
|
|
380
351
|
try {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
await sub.run(productId, {
|
|
385
|
-
diagramId: opts.diagramId,
|
|
386
|
-
repoId: opts.repoId,
|
|
387
|
-
guidance: opts.guidance,
|
|
388
|
-
verbose: opts.verbose,
|
|
389
|
-
});
|
|
390
|
-
}
|
|
391
|
-
catch (error) {
|
|
392
|
-
exitWithError(error);
|
|
352
|
+
const config = loadStructured(p) ?? {};
|
|
353
|
+
return { config, path: p };
|
|
354
|
+
} catch {
|
|
393
355
|
}
|
|
394
|
-
|
|
395
|
-
}
|
|
396
|
-
// ============================================================
|
|
397
|
-
// Subcommand: edsger user-psychology <productId>
|
|
398
|
-
// ============================================================
|
|
399
|
-
program
|
|
400
|
-
.command('user-psychology <productId>')
|
|
401
|
-
.description('Research the product target users and produce a structured psychographic profile (personas + JTBD + Fogg + messaging angles)')
|
|
402
|
-
.option('-v, --verbose', 'Verbose output')
|
|
403
|
-
.option('-g, --guidance <text>', 'Human direction for the AI (audience focus, market, segment)')
|
|
404
|
-
.requiredOption('--analysis-id <id>', 'ID of the pending analysis row to fill in (reserved by the desktop UI)')
|
|
405
|
-
.action(async (productId, opts) => {
|
|
406
|
-
try {
|
|
407
|
-
await runUserPsychologyAnalysis({
|
|
408
|
-
userPsychology: productId,
|
|
409
|
-
verbose: opts.verbose,
|
|
410
|
-
userPsychologyGuidance: opts.guidance,
|
|
411
|
-
userPsychologyAnalysisId: opts.analysisId,
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
catch (error) {
|
|
415
|
-
exitWithError(error);
|
|
416
|
-
}
|
|
417
|
-
});
|
|
418
|
-
// ============================================================
|
|
419
|
-
// Subcommand: edsger financing-deck <productId>
|
|
420
|
-
// ============================================================
|
|
421
|
-
program
|
|
422
|
-
.command('financing-deck <productId>')
|
|
423
|
-
.description('Draft an investor pitch deck for a product (AI-generated)')
|
|
424
|
-
.requiredOption('--target-stage <stage>', 'Target stage: pre_seed | seed | series_a | series_b | series_c | growth')
|
|
425
|
-
.option('-v, --verbose', 'Verbose output')
|
|
426
|
-
.action(async (productId, opts) => {
|
|
427
|
-
try {
|
|
428
|
-
await runFinancingDeck({
|
|
429
|
-
financingDeck: productId,
|
|
430
|
-
financingTargetStage: opts.targetStage,
|
|
431
|
-
verbose: opts.verbose,
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
catch (error) {
|
|
435
|
-
exitWithError(error);
|
|
436
|
-
}
|
|
437
|
-
});
|
|
438
|
-
// ============================================================
|
|
439
|
-
// Subcommand: edsger intel <productId>
|
|
440
|
-
// ============================================================
|
|
441
|
-
program
|
|
442
|
-
.command('intel <productId>')
|
|
443
|
-
.description('Run AI-powered competitive intelligence analysis for a product')
|
|
444
|
-
.option('-v, --verbose', 'Verbose output')
|
|
445
|
-
.option('-t, --type <type>', 'Report type: competitive, market, pmf, research, comprehensive', 'competitive')
|
|
446
|
-
.option('-g, --guidance <text>', 'Human direction for the analysis focus')
|
|
447
|
-
.option('--report-id <id>', 'Update an existing pending report instead of creating new')
|
|
448
|
-
.option('--list-competitors', 'List discovered and confirmed competitors')
|
|
449
|
-
.option('--confirm-competitor <id>', 'Confirm a suggested competitor for tracking')
|
|
450
|
-
.option('--dismiss-competitor <id>', 'Dismiss a suggested competitor')
|
|
451
|
-
.action(async (productId, opts) => {
|
|
452
|
-
try {
|
|
453
|
-
await runIntelligence({
|
|
454
|
-
productId,
|
|
455
|
-
reportType: opts.type,
|
|
456
|
-
verbose: opts.verbose,
|
|
457
|
-
guidance: opts.guidance,
|
|
458
|
-
reportId: opts.reportId,
|
|
459
|
-
listCompetitors: opts.listCompetitors,
|
|
460
|
-
confirmCompetitor: opts.confirmCompetitor,
|
|
461
|
-
dismissCompetitor: opts.dismissCompetitor,
|
|
462
|
-
});
|
|
463
|
-
}
|
|
464
|
-
catch (error) {
|
|
465
|
-
exitWithError(error);
|
|
466
|
-
}
|
|
467
|
-
});
|
|
468
|
-
// ============================================================
|
|
469
|
-
// Subcommand: edsger adr <adrId>
|
|
470
|
-
// ============================================================
|
|
471
|
-
program
|
|
472
|
-
.command('adr <adrId>')
|
|
473
|
-
.description('Generate decision options (pros/cons) for a draft Architecture Decision Record')
|
|
474
|
-
.option('-v, --verbose', 'Verbose output')
|
|
475
|
-
.action(async (adrId, opts) => {
|
|
476
|
-
try {
|
|
477
|
-
await runAdr({ adrId, verbose: opts.verbose });
|
|
478
|
-
}
|
|
479
|
-
catch (error) {
|
|
480
|
-
exitWithError(error);
|
|
481
|
-
}
|
|
482
|
-
});
|
|
483
|
-
// ============================================================
|
|
484
|
-
// Subcommand: edsger app-store <productId>
|
|
485
|
-
// ============================================================
|
|
486
|
-
program
|
|
487
|
-
.command('app-store <productId>')
|
|
488
|
-
.description('Generate professional App Store / Play Store screenshots and listing content')
|
|
489
|
-
.option('-v, --verbose', 'Verbose output')
|
|
490
|
-
.option('--screenshots-only', 'Only generate screenshots')
|
|
491
|
-
.option('--listings-only', 'Only generate listing content')
|
|
492
|
-
.option('--store <type>', 'Target store: apple, google, or all (default: all)', 'all')
|
|
493
|
-
.option('--locale <locale>', 'Target locale (auto-detected from App Store Connect if omitted)')
|
|
494
|
-
.action(async (productId, opts) => {
|
|
495
|
-
try {
|
|
496
|
-
await runAppStoreGeneration({
|
|
497
|
-
appStoreProductId: productId,
|
|
498
|
-
verbose: opts.verbose,
|
|
499
|
-
screenshotsOnly: opts.screenshotsOnly,
|
|
500
|
-
listingsOnly: opts.listingsOnly,
|
|
501
|
-
store: opts.store,
|
|
502
|
-
locale: opts.locale,
|
|
503
|
-
});
|
|
504
|
-
}
|
|
505
|
-
catch (error) {
|
|
506
|
-
exitWithError(error);
|
|
507
|
-
}
|
|
508
|
-
});
|
|
509
|
-
// ============================================================
|
|
510
|
-
// Subcommand: edsger build <productId>
|
|
511
|
-
// ============================================================
|
|
512
|
-
program
|
|
513
|
-
.command('build <productId>')
|
|
514
|
-
.description('Build iOS/macOS app and optionally upload to App Store Connect')
|
|
515
|
-
.option('-v, --verbose', 'Verbose output')
|
|
516
|
-
.option('--scheme <name>', 'Xcode scheme (auto-detected if omitted)')
|
|
517
|
-
.option('--project <path>', 'Path to .xcworkspace or .xcodeproj (relative to repo root)')
|
|
518
|
-
.option('--configuration <name>', 'Build configuration (default: Release)', 'Release')
|
|
519
|
-
.option('--platform <platform>', 'Target platform: ios or macos (default: ios)', 'ios')
|
|
520
|
-
.option('--upload', 'Upload IPA to App Store Connect after building')
|
|
521
|
-
.option('--reselect', 'Re-discover project and scheme (ignore saved config)')
|
|
522
|
-
.option('--skip-archive', 'Skip build, only upload existing IPA')
|
|
523
|
-
.option('--ipa <path>', 'Upload a specific IPA file directly')
|
|
524
|
-
.action(async (productId, opts) => {
|
|
525
|
-
try {
|
|
526
|
-
await runBuild({
|
|
527
|
-
buildProductId: productId,
|
|
528
|
-
verbose: opts.verbose,
|
|
529
|
-
scheme: opts.scheme,
|
|
530
|
-
project: opts.project,
|
|
531
|
-
configuration: opts.configuration,
|
|
532
|
-
platform: opts.platform,
|
|
533
|
-
upload: opts.upload,
|
|
534
|
-
reselect: opts.reselect,
|
|
535
|
-
skipArchive: opts.skipArchive,
|
|
536
|
-
ipa: opts.ipa,
|
|
537
|
-
});
|
|
538
|
-
}
|
|
539
|
-
catch (error) {
|
|
540
|
-
exitWithError(error);
|
|
541
|
-
}
|
|
542
|
-
});
|
|
543
|
-
// ============================================================
|
|
544
|
-
// Subcommand: edsger checklists [productId] [--team-id <id>]
|
|
545
|
-
// ============================================================
|
|
546
|
-
program
|
|
547
|
-
.command('checklists [productId]')
|
|
548
|
-
.description('Analyse a local directory and sync product OR team checklists (create/update/delete) using AI. Pass a product ID positionally for product scope, or --team-id for team scope.')
|
|
549
|
-
.option('-v, --verbose', 'Verbose output')
|
|
550
|
-
.option('--dir <path>', 'Directory to analyse (defaults to current working directory)')
|
|
551
|
-
.option('--prompt <text>', 'Additional instructions for the AI agent')
|
|
552
|
-
.option('--team-id <id>', 'Sync TEAM-level checklists for this team instead of a product. The directory is treated as team standards / reference material.')
|
|
553
|
-
.action(async (productId, opts) => {
|
|
554
|
-
try {
|
|
555
|
-
if (!productId && !opts.teamId) {
|
|
556
|
-
throw new Error('Provide a product ID positionally or pass --team-id <id> for team scope.');
|
|
557
|
-
}
|
|
558
|
-
if (productId && opts.teamId) {
|
|
559
|
-
throw new Error('Pass either a product ID or --team-id, not both.');
|
|
560
|
-
}
|
|
561
|
-
await runChecklists({
|
|
562
|
-
checklistsProductId: productId,
|
|
563
|
-
checklistsTeamId: opts.teamId,
|
|
564
|
-
checklistsDir: opts.dir,
|
|
565
|
-
checklistsPrompt: opts.prompt,
|
|
566
|
-
verbose: opts.verbose,
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
catch (error) {
|
|
570
|
-
exitWithError(error);
|
|
571
|
-
}
|
|
572
|
-
});
|
|
573
|
-
// ============================================================
|
|
574
|
-
// Subcommand: edsger analyze-logs <productId>
|
|
575
|
-
// ============================================================
|
|
576
|
-
program
|
|
577
|
-
.command('analyze-logs <productId>')
|
|
578
|
-
.description('Analyze pending product logs and create feedback for issues found')
|
|
579
|
-
.option('-v, --verbose', 'Verbose output')
|
|
580
|
-
.action(async (productId, opts) => {
|
|
581
|
-
try {
|
|
582
|
-
await runAnalyzeLogs({
|
|
583
|
-
analyzeLogs: productId,
|
|
584
|
-
verbose: opts.verbose,
|
|
585
|
-
});
|
|
586
|
-
}
|
|
587
|
-
catch (error) {
|
|
588
|
-
exitWithError(error);
|
|
589
|
-
}
|
|
590
|
-
});
|
|
591
|
-
// ============================================================
|
|
592
|
-
// Subcommand: edsger release-sync <productId>
|
|
593
|
-
// ============================================================
|
|
594
|
-
program
|
|
595
|
-
.command('release-sync <productId>')
|
|
596
|
-
.description('Sync GitHub releases (and any unreleased snapshot version) into the releases table for a product')
|
|
597
|
-
.option('-v, --verbose', 'Verbose output')
|
|
598
|
-
.action(async (productId, opts) => {
|
|
599
|
-
try {
|
|
600
|
-
await runReleaseSyncCommand({
|
|
601
|
-
productId,
|
|
602
|
-
verbose: opts.verbose,
|
|
603
|
-
});
|
|
604
|
-
}
|
|
605
|
-
catch (error) {
|
|
606
|
-
exitWithError(error);
|
|
607
|
-
}
|
|
608
|
-
});
|
|
609
|
-
// ============================================================
|
|
610
|
-
// Subcommand: edsger issue-analysis <issueId>
|
|
611
|
-
// ============================================================
|
|
612
|
-
program
|
|
613
|
-
.command('issue-analysis <issueId>')
|
|
614
|
-
.description('Generate or refine the execution plan for an issue (issue_analysis phase, with verification loop)')
|
|
615
|
-
.option('-v, --verbose', 'Verbose output')
|
|
616
|
-
.action(async (issueId, opts) => {
|
|
617
|
-
try {
|
|
618
|
-
await runIssueAnalysisCommand({ issueId, verbose: opts.verbose });
|
|
619
|
-
}
|
|
620
|
-
catch (error) {
|
|
621
|
-
exitWithError(error);
|
|
622
|
-
}
|
|
623
|
-
});
|
|
624
|
-
// ============================================================
|
|
625
|
-
// Subcommand: edsger session-turn <channelId>
|
|
626
|
-
// ============================================================
|
|
627
|
-
program
|
|
628
|
-
.command('session-turn <channelId>')
|
|
629
|
-
.description('Run one conversational agent turn for a chat session (ai_assistant channel)')
|
|
630
|
-
.requiredOption('--product <productId>', 'Product the session is bound to')
|
|
631
|
-
.option('--repos <ids>', 'Comma-separated repository IDs the agent may touch', '')
|
|
632
|
-
.option('--resume <sessionId>', 'SDK session id from a previous turn to resume the same conversation')
|
|
633
|
-
.option('--run-finished <runId>', 'Follow-up turn: report the results of a background cli_* run that finished')
|
|
634
|
-
.option('--run-command <command>', 'The command name of the finished run (used with --run-finished)')
|
|
635
|
-
.option('-v, --verbose', 'Verbose output')
|
|
636
|
-
.action(async (channelId, opts) => {
|
|
637
|
-
try {
|
|
638
|
-
await runSessionTurnCommand({
|
|
639
|
-
channelId,
|
|
640
|
-
productId: opts.product,
|
|
641
|
-
repositoryIds: (opts.repos ?? '')
|
|
642
|
-
.split(',')
|
|
643
|
-
.map((s) => s.trim())
|
|
644
|
-
.filter(Boolean),
|
|
645
|
-
resumeSessionId: opts.resume,
|
|
646
|
-
runFinished: opts.runFinished
|
|
647
|
-
? { runId: opts.runFinished, command: opts.runCommand }
|
|
648
|
-
: undefined,
|
|
649
|
-
verbose: opts.verbose,
|
|
650
|
-
});
|
|
651
|
-
}
|
|
652
|
-
catch (error) {
|
|
653
|
-
exitWithError(error);
|
|
654
|
-
}
|
|
655
|
-
});
|
|
656
|
-
// ============================================================
|
|
657
|
-
// Subcommand: edsger session-serve <channelId>
|
|
658
|
-
// ============================================================
|
|
659
|
-
program
|
|
660
|
-
.command('session-serve <channelId>')
|
|
661
|
-
.description('Run a long-lived conversational agent for a chat session, taking one turn per command read from stdin')
|
|
662
|
-
.requiredOption('--product <productId>', 'Product the session is bound to')
|
|
663
|
-
.option('--repos <ids>', 'Comma-separated repository IDs the agent may touch', '')
|
|
664
|
-
.option('--resume <sessionId>', 'SDK session id to resume on the first turn (e.g. after a crash-restart)')
|
|
665
|
-
.option('-v, --verbose', 'Verbose output')
|
|
666
|
-
.action(async (channelId, opts) => {
|
|
667
|
-
try {
|
|
668
|
-
await runSessionServeCommand({
|
|
669
|
-
channelId,
|
|
670
|
-
productId: opts.product,
|
|
671
|
-
repositoryIds: (opts.repos ?? '')
|
|
672
|
-
.split(',')
|
|
673
|
-
.map((s) => s.trim())
|
|
674
|
-
.filter(Boolean),
|
|
675
|
-
resumeSessionId: opts.resume,
|
|
676
|
-
verbose: opts.verbose,
|
|
677
|
-
});
|
|
678
|
-
}
|
|
679
|
-
catch (error) {
|
|
680
|
-
exitWithError(error);
|
|
681
|
-
}
|
|
682
|
-
});
|
|
683
|
-
// ============================================================
|
|
684
|
-
// Subcommand: edsger chat-serve
|
|
685
|
-
// ============================================================
|
|
686
|
-
program
|
|
687
|
-
.command('chat-serve')
|
|
688
|
-
.description('Run a long-lived daemon that answers human messages on issue and product group chat channels')
|
|
689
|
-
.option('-v, --verbose', 'Verbose output')
|
|
690
|
-
.action(async (opts) => {
|
|
691
|
-
try {
|
|
692
|
-
await runChatServeCommand({ verbose: opts.verbose });
|
|
693
|
-
}
|
|
694
|
-
catch (error) {
|
|
695
|
-
exitWithError(error);
|
|
696
|
-
}
|
|
697
|
-
});
|
|
698
|
-
// ============================================================
|
|
699
|
-
// Subcommand: edsger user-stories-analysis <issueId>
|
|
700
|
-
// ============================================================
|
|
701
|
-
program
|
|
702
|
-
.command('user-stories-analysis <issueId>')
|
|
703
|
-
.description('Generate or refine user stories for an issue (user_stories_analysis phase, with verification loop)')
|
|
704
|
-
.option('-v, --verbose', 'Verbose output')
|
|
705
|
-
.action(async (issueId, opts) => {
|
|
706
|
-
try {
|
|
707
|
-
await runUserStoriesAnalysisCommand({ issueId, verbose: opts.verbose });
|
|
708
|
-
}
|
|
709
|
-
catch (error) {
|
|
710
|
-
exitWithError(error);
|
|
711
|
-
}
|
|
712
|
-
});
|
|
713
|
-
// ============================================================
|
|
714
|
-
// Subcommand: edsger test-cases-analysis <issueId>
|
|
715
|
-
// ============================================================
|
|
716
|
-
program
|
|
717
|
-
.command('test-cases-analysis <issueId>')
|
|
718
|
-
.description('Generate or refine test cases for an issue (test_cases_analysis phase, with verification loop)')
|
|
719
|
-
.option('-v, --verbose', 'Verbose output')
|
|
720
|
-
.action(async (issueId, opts) => {
|
|
721
|
-
try {
|
|
722
|
-
await runTestCasesAnalysisCommand({ issueId, verbose: opts.verbose });
|
|
723
|
-
}
|
|
724
|
-
catch (error) {
|
|
725
|
-
exitWithError(error);
|
|
726
|
-
}
|
|
727
|
-
});
|
|
728
|
-
// ============================================================
|
|
729
|
-
// Subcommand: edsger technical-design <issueId>
|
|
730
|
-
// ============================================================
|
|
731
|
-
program
|
|
732
|
-
.command('technical-design <issueId>')
|
|
733
|
-
.description('Generate or refine the technical design for an issue (technical_design phase, with verification loop)')
|
|
734
|
-
.option('-v, --verbose', 'Verbose output')
|
|
735
|
-
.action(async (issueId, opts) => {
|
|
736
|
-
try {
|
|
737
|
-
await runTechnicalDesignCommand({ issueId, verbose: opts.verbose });
|
|
738
|
-
}
|
|
739
|
-
catch (error) {
|
|
740
|
-
exitWithError(error);
|
|
741
|
-
}
|
|
742
|
-
});
|
|
743
|
-
// ============================================================
|
|
744
|
-
// Subcommand: edsger smoke-test <releaseId>
|
|
745
|
-
// ============================================================
|
|
746
|
-
program
|
|
747
|
-
.command('smoke-test <releaseId>')
|
|
748
|
-
.description('Generate smoke-test cases for a specific release (requires the release to be synced first — see `edsger release-sync`)')
|
|
749
|
-
.option('-v, --verbose', 'Verbose output')
|
|
750
|
-
.action(async (releaseId, opts) => {
|
|
751
|
-
try {
|
|
752
|
-
await runSmokeTestCommand({
|
|
753
|
-
releaseId,
|
|
754
|
-
verbose: opts.verbose,
|
|
755
|
-
});
|
|
756
|
-
}
|
|
757
|
-
catch (error) {
|
|
758
|
-
exitWithError(error);
|
|
356
|
+
}
|
|
759
357
|
}
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
.
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
358
|
+
}
|
|
359
|
+
return { config: {} };
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// src/core/agent.ts
|
|
363
|
+
import {
|
|
364
|
+
createSdkMcpServer,
|
|
365
|
+
query,
|
|
366
|
+
tool
|
|
367
|
+
} from "@anthropic-ai/claude-agent-sdk";
|
|
368
|
+
var READ_ONLY_DISALLOWED = ["Edit", "Write", "NotebookEdit"];
|
|
369
|
+
async function runAgent(opts) {
|
|
370
|
+
assertAuth();
|
|
371
|
+
let captured;
|
|
372
|
+
const submitTool = tool(
|
|
373
|
+
"submit_result",
|
|
374
|
+
"Submit the final structured result. Call this exactly once when done.",
|
|
375
|
+
opts.resultShape,
|
|
376
|
+
async (args) => {
|
|
377
|
+
captured = args;
|
|
378
|
+
return { content: [{ type: "text", text: "Result recorded. You may stop now." }] };
|
|
379
|
+
}
|
|
380
|
+
);
|
|
381
|
+
const outServer = createSdkMcpServer({
|
|
382
|
+
name: "edsger_out",
|
|
383
|
+
version: "1.0.0",
|
|
384
|
+
tools: [submitTool]
|
|
385
|
+
});
|
|
386
|
+
const allowedTools = [
|
|
387
|
+
"Read",
|
|
388
|
+
"Grep",
|
|
389
|
+
"Glob",
|
|
390
|
+
"Bash",
|
|
391
|
+
"mcp__edsger_out__submit_result",
|
|
392
|
+
...opts.allowWrite ? ["Edit", "Write", "MultiEdit"] : [],
|
|
393
|
+
...opts.extraAllowedTools ?? []
|
|
394
|
+
];
|
|
395
|
+
const mcpServers = {
|
|
396
|
+
edsger_out: outServer,
|
|
397
|
+
...opts.extraMcpServers ?? {}
|
|
398
|
+
};
|
|
399
|
+
let costUsd = 0;
|
|
400
|
+
let numTurns = 0;
|
|
401
|
+
const controller = new AbortController();
|
|
402
|
+
const timer = opts.timeoutMs ? setTimeout(() => controller.abort(), opts.timeoutMs) : void 0;
|
|
403
|
+
const stream = query({
|
|
404
|
+
prompt: opts.prompt,
|
|
405
|
+
options: {
|
|
406
|
+
model: opts.model,
|
|
407
|
+
cwd: opts.cwd,
|
|
408
|
+
maxTurns: opts.maxTurns,
|
|
409
|
+
// Reproducible runs: do not load the target repo's settings/CLAUDE.md.
|
|
410
|
+
settingSources: [],
|
|
411
|
+
systemPrompt: {
|
|
412
|
+
type: "preset",
|
|
413
|
+
preset: "claude_code",
|
|
414
|
+
append: opts.systemPromptAppend
|
|
415
|
+
},
|
|
416
|
+
// The user explicitly invoked an agent to inspect/modify their own repo.
|
|
417
|
+
// Capability is shaped by allowedTools/disallowedTools rather than prompts:
|
|
418
|
+
// read-only runs have edit tools removed from context entirely (below).
|
|
419
|
+
permissionMode: "bypassPermissions",
|
|
420
|
+
allowedTools,
|
|
421
|
+
disallowedTools: opts.allowWrite ? [] : READ_ONLY_DISALLOWED,
|
|
422
|
+
mcpServers,
|
|
423
|
+
abortController: controller
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
try {
|
|
427
|
+
for await (const message of stream) {
|
|
428
|
+
if (message.type === "assistant") {
|
|
429
|
+
for (const block of message.message?.content ?? []) {
|
|
430
|
+
if (block.type === "tool_use") {
|
|
431
|
+
log.debug(`tool: ${block.name}`);
|
|
432
|
+
} else if (block.type === "text" && opts.verbose && block.text?.trim()) {
|
|
433
|
+
log.debug(block.text.trim().split("\n")[0] ?? "");
|
|
434
|
+
}
|
|
795
435
|
}
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
});
|
|
802
|
-
// ============================================================
|
|
803
|
-
// Subcommand: edsger find-bugs <productId>
|
|
804
|
-
// ============================================================
|
|
805
|
-
program
|
|
806
|
-
.command('find-bugs [productId]')
|
|
807
|
-
.description("AI-audit a product's (or a standalone repository's) repo for bugs and file each new finding as an issue")
|
|
808
|
-
.option('--repo-id <id>', 'Run in repo-only mode against a single repositories row (no product context)')
|
|
809
|
-
.option('--full', 'Force a full scan even if previous-scan state exists')
|
|
810
|
-
.option('--branch <name>', 'Branch to scan (defaults to repo default branch)')
|
|
811
|
-
.option('--max-files <n>', 'Upper bound on files the auditor may Read (default 200)', (value) => {
|
|
812
|
-
const n = parseInt(value, 10);
|
|
813
|
-
if (Number.isNaN(n) || n <= 0) {
|
|
814
|
-
throw new Error('--max-files must be a positive integer');
|
|
815
|
-
}
|
|
816
|
-
return n;
|
|
817
|
-
})
|
|
818
|
-
.option('-v, --verbose', 'Verbose output')
|
|
819
|
-
.action(async (productId, opts) => {
|
|
820
|
-
try {
|
|
821
|
-
await runFindBugs(productId, opts);
|
|
822
|
-
}
|
|
823
|
-
catch (error) {
|
|
824
|
-
exitWithError(error);
|
|
825
|
-
}
|
|
826
|
-
});
|
|
827
|
-
// ============================================================
|
|
828
|
-
// Subcommand: edsger quality-benchmark <productId>
|
|
829
|
-
// ============================================================
|
|
830
|
-
program
|
|
831
|
-
.command('quality-benchmark [productId]')
|
|
832
|
-
.description("Run an industrial-grade code quality benchmark against the product's (or a standalone repository's) GitHub repo")
|
|
833
|
-
.option('--repo <path>', "Override the auto-clone with a local checkout (default: clone the product's repo into ~/edsger/quality-<owner>-<repo>)")
|
|
834
|
-
.option('--repo-id <id>', 'Benchmark a specific repository (id). With a productId, targets that linked repo; without a productId, runs in repo-only mode (no product)')
|
|
835
|
-
.option('--branch <name>', 'Override the detected default branch on the report envelope')
|
|
836
|
-
.option('--pkg-manager <name>', 'npm | pnpm | yarn (auto-detected if absent)')
|
|
837
|
-
.option('--no-install', 'Refuse to install missing tools; mark them unmeasured')
|
|
838
|
-
.option('--output <path>', 'Where to write the JSON report (default: ./quality-report-<commit>.json)')
|
|
839
|
-
.option('--no-save', 'Skip the quality_reports/save MCP call (local-only run)')
|
|
840
|
-
.option('--force', 'Overwrite any existing report row for this (product, commit, rubric)')
|
|
841
|
-
.option('--gate', "Evaluate the scope's quality gate against the report and exit non-zero if it fails (for CI). No-op when no gate is configured")
|
|
842
|
-
.option('-v, --verbose', 'Print every progress event')
|
|
843
|
-
.action(async (productId, opts) => {
|
|
844
|
-
try {
|
|
845
|
-
if (!productId && !opts.repoId) {
|
|
846
|
-
throw new Error('Provide a productId or --repo-id (repo-only mode) for quality-benchmark');
|
|
436
|
+
} else if (message.type === "result") {
|
|
437
|
+
costUsd = message.total_cost_usd ?? 0;
|
|
438
|
+
numTurns = message.num_turns ?? 0;
|
|
439
|
+
if (message.subtype && message.subtype !== "success") {
|
|
440
|
+
log.warn(`agent ended with: ${message.subtype}`);
|
|
847
441
|
}
|
|
848
|
-
|
|
849
|
-
}
|
|
850
|
-
catch (error) {
|
|
851
|
-
exitWithError(error);
|
|
852
|
-
}
|
|
853
|
-
});
|
|
854
|
-
// ============================================================
|
|
855
|
-
// Subcommand: edsger sync-github-issues <productId>
|
|
856
|
-
// ============================================================
|
|
857
|
-
program
|
|
858
|
-
.command('sync-github-issues <productId>')
|
|
859
|
-
.description("Mirror the product's connected GitHub repo issues into the local issues list (handles new, updated, closed, and deleted)")
|
|
860
|
-
.option('-v, --verbose', 'Verbose output')
|
|
861
|
-
.action(async (productId, opts) => {
|
|
862
|
-
try {
|
|
863
|
-
await runSyncGithubIssues(productId, opts);
|
|
864
|
-
}
|
|
865
|
-
catch (error) {
|
|
866
|
-
exitWithError(error);
|
|
867
|
-
}
|
|
868
|
-
});
|
|
869
|
-
// ============================================================
|
|
870
|
-
// Subcommand: edsger sync-github-pull-requests <repoId>
|
|
871
|
-
// ============================================================
|
|
872
|
-
program
|
|
873
|
-
.command('sync-github-pull-requests <repoId>')
|
|
874
|
-
.description("Mirror a connected GitHub repo's pull requests into the local pull_requests list for the repo detail page (idempotent — refreshes, never duplicates)")
|
|
875
|
-
.option('-v, --verbose', 'Verbose output')
|
|
876
|
-
.action(async (repoId, opts) => {
|
|
877
|
-
try {
|
|
878
|
-
await runSyncGithubPullRequests(repoId, opts);
|
|
879
|
-
}
|
|
880
|
-
catch (error) {
|
|
881
|
-
exitWithError(error);
|
|
882
|
-
}
|
|
883
|
-
});
|
|
884
|
-
// ============================================================
|
|
885
|
-
// Subcommand: edsger sync-org-repos <teamId>
|
|
886
|
-
// ============================================================
|
|
887
|
-
program
|
|
888
|
-
.command('sync-org-repos <teamId>')
|
|
889
|
-
.description("Sync a GitHub org's repos under a team via the Edsger GitHub App, falling back to your personal GitHub token.")
|
|
890
|
-
.option('-v, --verbose', 'Verbose output')
|
|
891
|
-
.option('--org <name>', "GitHub org name (defaults to the team's configured github_org)")
|
|
892
|
-
.action(async (teamId, opts) => {
|
|
893
|
-
try {
|
|
894
|
-
await runSyncOrgRepos(teamId, opts);
|
|
895
|
-
}
|
|
896
|
-
catch (error) {
|
|
897
|
-
exitWithError(error);
|
|
898
|
-
}
|
|
899
|
-
});
|
|
900
|
-
// ============================================================
|
|
901
|
-
// Subcommand: edsger sync-sentry-issues <productId>
|
|
902
|
-
//
|
|
903
|
-
// Requires SENTRY_AUTH_TOKEN, SENTRY_ORG, SENTRY_PROJECT in env (set via
|
|
904
|
-
// `edsger config set`). Optional SENTRY_BASE_URL for self-hosted Sentry.
|
|
905
|
-
// ============================================================
|
|
906
|
-
program
|
|
907
|
-
.command('sync-sentry-issues <productId>')
|
|
908
|
-
.description('Mirror Sentry project issues into the local issues list (requires SENTRY_AUTH_TOKEN, SENTRY_ORG, SENTRY_PROJECT env)')
|
|
909
|
-
.option('-v, --verbose', 'Verbose output')
|
|
910
|
-
.action(async (productId, opts) => {
|
|
911
|
-
try {
|
|
912
|
-
await runSyncSentryIssues(productId, opts);
|
|
913
|
-
}
|
|
914
|
-
catch (error) {
|
|
915
|
-
exitWithError(error);
|
|
916
|
-
}
|
|
917
|
-
});
|
|
918
|
-
// ============================================================
|
|
919
|
-
// Subcommand: edsger sync-datadog <teamId>
|
|
920
|
-
//
|
|
921
|
-
// Requires DD_API_KEY, DD_APP_KEY in env (set via `edsger config set`
|
|
922
|
-
// or desktop Settings → Credentials → Datadog). Optional DD_SITE.
|
|
923
|
-
// ============================================================
|
|
924
|
-
program
|
|
925
|
-
.command('sync-datadog <teamId>')
|
|
926
|
-
.description('Import services from Datadog Service Catalog into the edsger service catalog (requires DD_API_KEY, DD_APP_KEY env)')
|
|
927
|
-
.option('-v, --verbose', 'Verbose output')
|
|
928
|
-
.action(async (teamId, opts) => {
|
|
929
|
-
try {
|
|
930
|
-
await runSyncDatadog(teamId, opts);
|
|
931
|
-
}
|
|
932
|
-
catch (error) {
|
|
933
|
-
exitWithError(error);
|
|
442
|
+
}
|
|
934
443
|
}
|
|
935
|
-
})
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
// Subcommand: edsger sync-aws <teamId>
|
|
939
|
-
//
|
|
940
|
-
// Uses the standard AWS credential chain to discover services by
|
|
941
|
-
// their Service tag and optionally pull cost data.
|
|
942
|
-
// ============================================================
|
|
943
|
-
program
|
|
944
|
-
.command('sync-aws <teamId>')
|
|
945
|
-
.description('Discover services from AWS by Service tag and pull cost data from Cost Explorer')
|
|
946
|
-
.option('-v, --verbose', 'Verbose output')
|
|
947
|
-
.option('--region <region>', 'AWS region (default: AWS_REGION env or us-east-1)')
|
|
948
|
-
.option('--no-cost', 'Skip Cost Explorer query')
|
|
949
|
-
.action(async (teamId, opts) => {
|
|
950
|
-
try {
|
|
951
|
-
await runSyncAws(teamId, {
|
|
952
|
-
verbose: opts.verbose,
|
|
953
|
-
region: opts.region,
|
|
954
|
-
noCost: opts.cost === false,
|
|
955
|
-
});
|
|
956
|
-
}
|
|
957
|
-
catch (error) {
|
|
958
|
-
exitWithError(error);
|
|
959
|
-
}
|
|
960
|
-
});
|
|
961
|
-
// ============================================================
|
|
962
|
-
// Subcommand: edsger discover <teamId> [runId]
|
|
963
|
-
//
|
|
964
|
-
// Scans every repo in the team's connected GitHub org and upserts an
|
|
965
|
-
// inferred service for each into the service catalog. Drives an existing
|
|
966
|
-
// discovery_runs row (created by the desktop) to running → success/failed.
|
|
967
|
-
// ============================================================
|
|
968
|
-
program
|
|
969
|
-
.command('discover <teamId> [runId]')
|
|
970
|
-
.description('Discover services by scanning every repo in the team’s connected GitHub organisation')
|
|
971
|
-
.option('-v, --verbose', 'Verbose output')
|
|
972
|
-
.option('--org <name>', 'Override the GitHub organisation to scan')
|
|
973
|
-
.action(async (teamId, runId, opts) => {
|
|
974
|
-
try {
|
|
975
|
-
await runDiscover(teamId, runId, opts);
|
|
976
|
-
}
|
|
977
|
-
catch (error) {
|
|
978
|
-
exitWithError(error);
|
|
444
|
+
} catch (err) {
|
|
445
|
+
if (controller.signal.aborted) {
|
|
446
|
+
throw new Error(`Agent run timed out after ${Math.round((opts.timeoutMs ?? 0) / 1e3)}s.`);
|
|
979
447
|
}
|
|
448
|
+
throw err;
|
|
449
|
+
} finally {
|
|
450
|
+
if (timer) clearTimeout(timer);
|
|
451
|
+
}
|
|
452
|
+
return { result: captured, costUsd, numTurns };
|
|
453
|
+
}
|
|
454
|
+
function assertAuth() {
|
|
455
|
+
if (!process.env.ANTHROPIC_API_KEY && !process.env.CLAUDE_CODE_OAUTH_TOKEN) {
|
|
456
|
+
throw new Error(
|
|
457
|
+
"No Anthropic credentials found. Set ANTHROPIC_API_KEY (or CLAUDE_CODE_OAUTH_TOKEN) before running edsger."
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// src/core/report.ts
|
|
463
|
+
function scoreBenchmark(standard, result) {
|
|
464
|
+
const scale = standard.scoreScale || 4;
|
|
465
|
+
const totalWeight = standard.categories.reduce((s, c) => s + c.weight, 0) || 1;
|
|
466
|
+
const scoreLookup = /* @__PURE__ */ new Map();
|
|
467
|
+
for (const cat of result.categories) {
|
|
468
|
+
for (const cr of cat.criteria) scoreLookup.set(`${cat.id}/${cr.id}`, cr.score);
|
|
469
|
+
}
|
|
470
|
+
const justifyLookup = /* @__PURE__ */ new Map();
|
|
471
|
+
for (const cat of result.categories) {
|
|
472
|
+
for (const cr of cat.criteria) {
|
|
473
|
+
justifyLookup.set(`${cat.id}/${cr.id}`, {
|
|
474
|
+
justification: cr.justification,
|
|
475
|
+
evidence: cr.evidence ?? []
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
const categories = standard.categories.map((cat) => {
|
|
480
|
+
const critWeightTotal = cat.criteria.reduce((s, c) => s + (c.weight ?? 1), 0) || 1;
|
|
481
|
+
const scored = cat.criteria.map((cr) => {
|
|
482
|
+
const raw = clamp(scoreLookup.get(`${cat.id}/${cr.id}`) ?? 0, 0, scale);
|
|
483
|
+
const meta = justifyLookup.get(`${cat.id}/${cr.id}`);
|
|
484
|
+
return {
|
|
485
|
+
id: cr.id,
|
|
486
|
+
title: cr.title,
|
|
487
|
+
weight: cr.weight ?? 1,
|
|
488
|
+
rawScore: raw,
|
|
489
|
+
maxScore: scale,
|
|
490
|
+
fraction: raw / scale,
|
|
491
|
+
justification: meta?.justification ?? "Not assessed.",
|
|
492
|
+
evidence: meta?.evidence ?? []
|
|
493
|
+
};
|
|
494
|
+
});
|
|
495
|
+
const fraction = scored.reduce((s, c) => s + c.fraction * c.weight, 0) / critWeightTotal;
|
|
496
|
+
const normWeight = cat.weight / totalWeight * 100;
|
|
497
|
+
return {
|
|
498
|
+
id: cat.id,
|
|
499
|
+
name: cat.name,
|
|
500
|
+
weight: round(normWeight, 1),
|
|
501
|
+
fraction,
|
|
502
|
+
points: round(normWeight * fraction, 1),
|
|
503
|
+
criteria: scored
|
|
504
|
+
};
|
|
505
|
+
});
|
|
506
|
+
const overall = round(
|
|
507
|
+
categories.reduce((s, c) => s + c.points, 0),
|
|
508
|
+
1
|
|
509
|
+
);
|
|
510
|
+
return {
|
|
511
|
+
standardName: standard.name,
|
|
512
|
+
standardVersion: standard.version,
|
|
513
|
+
overall,
|
|
514
|
+
grade: gradeFor(overall),
|
|
515
|
+
categories,
|
|
516
|
+
summary: result.summary,
|
|
517
|
+
strengths: result.strengths ?? [],
|
|
518
|
+
weaknesses: result.weaknesses ?? [],
|
|
519
|
+
recommendations: result.recommendations ?? []
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
function gradeFor(score) {
|
|
523
|
+
if (score >= 90) return "A";
|
|
524
|
+
if (score >= 80) return "B";
|
|
525
|
+
if (score >= 70) return "C";
|
|
526
|
+
if (score >= 60) return "D";
|
|
527
|
+
return "F";
|
|
528
|
+
}
|
|
529
|
+
function renderBenchmarkMarkdown(scored, meta) {
|
|
530
|
+
const lines = [];
|
|
531
|
+
lines.push(`# Edsger Benchmark Report`);
|
|
532
|
+
lines.push("");
|
|
533
|
+
lines.push(`**Score: ${scored.overall} / 100 (Grade ${scored.grade})**`);
|
|
534
|
+
lines.push("");
|
|
535
|
+
lines.push(`> ${scored.summary}`);
|
|
536
|
+
lines.push("");
|
|
537
|
+
lines.push(`| Field | Value |`);
|
|
538
|
+
lines.push(`| --- | --- |`);
|
|
539
|
+
lines.push(`| Repository | \`${meta.repo}\` |`);
|
|
540
|
+
lines.push(`| Languages | ${meta.languages.join(", ") || "n/a"} |`);
|
|
541
|
+
lines.push(`| Standard | ${scored.standardName} v${scored.standardVersion} |`);
|
|
542
|
+
lines.push(`| Sources | ${meta.layers.join(" \u2192 ")} |`);
|
|
543
|
+
lines.push(`| Model | ${meta.model} |`);
|
|
544
|
+
lines.push(`| Generated | ${meta.generatedAt} |`);
|
|
545
|
+
lines.push("");
|
|
546
|
+
lines.push(`## Category scores`);
|
|
547
|
+
lines.push("");
|
|
548
|
+
lines.push(`| Category | Weight | Score | Weighted |`);
|
|
549
|
+
lines.push(`| --- | ---: | ---: | ---: |`);
|
|
550
|
+
for (const c of scored.categories) {
|
|
551
|
+
lines.push(`| ${c.name} | ${c.weight}% | ${Math.round(c.fraction * 100)}% | ${c.points} |`);
|
|
552
|
+
}
|
|
553
|
+
lines.push(`| **Overall** | **100%** | | **${scored.overall}** |`);
|
|
554
|
+
lines.push("");
|
|
555
|
+
if (scored.strengths.length) {
|
|
556
|
+
lines.push(`## Strengths`);
|
|
557
|
+
lines.push("");
|
|
558
|
+
for (const s of scored.strengths) lines.push(`- ${s}`);
|
|
559
|
+
lines.push("");
|
|
560
|
+
}
|
|
561
|
+
if (scored.weaknesses.length) {
|
|
562
|
+
lines.push(`## Weaknesses`);
|
|
563
|
+
lines.push("");
|
|
564
|
+
for (const w of scored.weaknesses) lines.push(`- ${w}`);
|
|
565
|
+
lines.push("");
|
|
566
|
+
}
|
|
567
|
+
if (scored.recommendations.length) {
|
|
568
|
+
lines.push(`## Recommendations`);
|
|
569
|
+
lines.push("");
|
|
570
|
+
for (const r of scored.recommendations) {
|
|
571
|
+
lines.push(`- **[${r.priority}] ${r.title}** \u2014 ${r.detail}`);
|
|
572
|
+
}
|
|
573
|
+
lines.push("");
|
|
574
|
+
}
|
|
575
|
+
lines.push(`## Detailed findings`);
|
|
576
|
+
lines.push("");
|
|
577
|
+
for (const c of scored.categories) {
|
|
578
|
+
lines.push(`### ${c.name} \u2014 ${Math.round(c.fraction * 100)}%`);
|
|
579
|
+
lines.push("");
|
|
580
|
+
for (const cr of c.criteria) {
|
|
581
|
+
lines.push(`- **${cr.title}** (${cr.rawScore}/${cr.maxScore}) \u2014 ${cr.justification}`);
|
|
582
|
+
for (const e of cr.evidence) lines.push(` - \`${e}\``);
|
|
583
|
+
}
|
|
584
|
+
lines.push("");
|
|
585
|
+
}
|
|
586
|
+
lines.push(`---`);
|
|
587
|
+
lines.push(`<sub>Generated by [edsger](https://www.npmjs.com/package/edsger).</sub>`);
|
|
588
|
+
return lines.join("\n");
|
|
589
|
+
}
|
|
590
|
+
function clamp(n, lo, hi) {
|
|
591
|
+
return Math.max(lo, Math.min(hi, n));
|
|
592
|
+
}
|
|
593
|
+
function round(n, dp) {
|
|
594
|
+
const f = 10 ** dp;
|
|
595
|
+
return Math.round(n * f) / f;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// src/core/schema.ts
|
|
599
|
+
import { z } from "zod";
|
|
600
|
+
var CriterionScoreSchema = z.object({
|
|
601
|
+
id: z.string().describe("Criterion id from the rubric"),
|
|
602
|
+
score: z.number().describe("Score for this criterion, 0..scoreScale"),
|
|
603
|
+
justification: z.string().describe("Why this score, in one or two sentences"),
|
|
604
|
+
evidence: z.array(z.string()).default([]).describe("File paths / snippets supporting the score")
|
|
980
605
|
});
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
// and lets the agent analyse .tf files to extract services, resources,
|
|
985
|
-
// and dependencies. Alternatively, --dir for a local directory.
|
|
986
|
-
// ============================================================
|
|
987
|
-
program
|
|
988
|
-
.command('sync-terraform <teamId>')
|
|
989
|
-
.description('Scan a Terraform repo to discover services, AWS resources, and infrastructure dependencies')
|
|
990
|
-
.option('-v, --verbose', 'Verbose output')
|
|
991
|
-
.option('--dir <path>', 'Local directory to scan instead of cloning from GitHub')
|
|
992
|
-
.action(async (teamId, opts) => {
|
|
993
|
-
try {
|
|
994
|
-
await runSyncTerraform(teamId, opts);
|
|
995
|
-
}
|
|
996
|
-
catch (error) {
|
|
997
|
-
exitWithError(error);
|
|
998
|
-
}
|
|
606
|
+
var CategoryScoreSchema = z.object({
|
|
607
|
+
id: z.string().describe("Category id from the rubric"),
|
|
608
|
+
criteria: z.array(CriterionScoreSchema)
|
|
999
609
|
});
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
.
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
}
|
|
1013
|
-
return n;
|
|
1014
|
-
})
|
|
1015
|
-
.option('--max-suggestions <n>', 'Cap on opportunities the agent may surface (default 10)', (value) => {
|
|
1016
|
-
const n = parseInt(value, 10);
|
|
1017
|
-
if (Number.isNaN(n) || n <= 0) {
|
|
1018
|
-
throw new Error('--max-suggestions must be a positive integer');
|
|
1019
|
-
}
|
|
1020
|
-
return n;
|
|
1021
|
-
})
|
|
1022
|
-
.option('--against-report <id>', 'Narrow the run to opportunities implied by a specific intelligence report')
|
|
1023
|
-
.option('-v, --verbose', 'Verbose output')
|
|
1024
|
-
.action(async (productId, opts) => {
|
|
1025
|
-
try {
|
|
1026
|
-
await runFindFeatures(productId, opts);
|
|
1027
|
-
}
|
|
1028
|
-
catch (error) {
|
|
1029
|
-
exitWithError(error);
|
|
1030
|
-
}
|
|
610
|
+
var BenchmarkResultSchema = z.object({
|
|
611
|
+
categories: z.array(CategoryScoreSchema),
|
|
612
|
+
summary: z.string().describe("2-4 sentence overall assessment"),
|
|
613
|
+
strengths: z.array(z.string()).default([]),
|
|
614
|
+
weaknesses: z.array(z.string()).default([]),
|
|
615
|
+
recommendations: z.array(
|
|
616
|
+
z.object({
|
|
617
|
+
priority: z.enum(["high", "medium", "low"]),
|
|
618
|
+
title: z.string(),
|
|
619
|
+
detail: z.string()
|
|
620
|
+
})
|
|
621
|
+
).default([])
|
|
1031
622
|
});
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
.option('--max-files <n>', `Upper bound on files the auditor may Read (default ${FIND_SMELLS_DEFAULT_MAX_FILES})`, (value) => {
|
|
1042
|
-
const n = parseInt(value, 10);
|
|
1043
|
-
if (Number.isNaN(n) || n <= 0) {
|
|
1044
|
-
throw new Error('--max-files must be a positive integer');
|
|
1045
|
-
}
|
|
1046
|
-
return n;
|
|
1047
|
-
})
|
|
1048
|
-
.option('--categories <list>', `Comma-separated category filter (${SMELL_CATEGORIES.join(',')})`, parseCategoriesOption)
|
|
1049
|
-
.option('-v, --verbose', 'Verbose output')
|
|
1050
|
-
.action(async (productId, opts) => {
|
|
1051
|
-
try {
|
|
1052
|
-
await runFindSmells(productId, opts);
|
|
1053
|
-
}
|
|
1054
|
-
catch (error) {
|
|
1055
|
-
exitWithError(error);
|
|
1056
|
-
}
|
|
623
|
+
var SeveritySchema = z.enum(["critical", "high", "medium", "low", "info"]);
|
|
624
|
+
var ReviewFindingSchema = z.object({
|
|
625
|
+
focusArea: z.string().describe("Focus area id this finding relates to"),
|
|
626
|
+
severity: SeveritySchema,
|
|
627
|
+
file: z.string().describe("Repo-relative file path, or '-' if general"),
|
|
628
|
+
line: z.number().nullable().default(null).describe("1-based line in the new file, if applicable"),
|
|
629
|
+
title: z.string(),
|
|
630
|
+
detail: z.string().describe("What is wrong and why it matters"),
|
|
631
|
+
suggestion: z.string().nullable().default(null).describe("Concrete suggested fix")
|
|
1057
632
|
});
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
.command('find-architecture [productId]')
|
|
1063
|
-
.description("AI-audit a product's (or a standalone repository's) repo for architectural concerns (layering, coupling, cycles, missing/duplicated abstractions, responsibility creep) and file each new finding as an issue")
|
|
1064
|
-
.option('--repo-id <id>', 'Run in repo-only mode against a single repositories row (no product context)')
|
|
1065
|
-
.option('--full', 'Force a full scan even if previous-scan state exists')
|
|
1066
|
-
.option('--branch <name>', 'Branch to scan (defaults to repo default branch)')
|
|
1067
|
-
.option('--max-files <n>', `Upper bound on files the auditor may Read (default ${FIND_ARCHITECTURE_DEFAULT_MAX_FILES})`, (value) => {
|
|
1068
|
-
const n = parseInt(value, 10);
|
|
1069
|
-
if (Number.isNaN(n) || n <= 0) {
|
|
1070
|
-
throw new Error('--max-files must be a positive integer');
|
|
1071
|
-
}
|
|
1072
|
-
return n;
|
|
1073
|
-
})
|
|
1074
|
-
.option('-v, --verbose', 'Verbose output')
|
|
1075
|
-
.action(async (productId, opts) => {
|
|
1076
|
-
try {
|
|
1077
|
-
await runFindArchitecture(productId, opts);
|
|
1078
|
-
}
|
|
1079
|
-
catch (error) {
|
|
1080
|
-
exitWithError(error);
|
|
1081
|
-
}
|
|
633
|
+
var ReviewResultSchema = z.object({
|
|
634
|
+
summary: z.string().describe("Overall summary of the change and review"),
|
|
635
|
+
verdict: z.enum(["approve", "request_changes", "comment"]),
|
|
636
|
+
findings: z.array(ReviewFindingSchema).default([])
|
|
1082
637
|
});
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
.description("Generate product-level regression test cases by exploring the product's repo. Existing approved cases are preserved; replaceable (draft / pending_approval) ones may be deleted and replaced.")
|
|
1089
|
-
.option('--branch <name>', 'Branch to scan (defaults to repo default branch)')
|
|
1090
|
-
.option('-v, --verbose', 'Verbose output')
|
|
1091
|
-
.action(async (productId, opts) => {
|
|
1092
|
-
try {
|
|
1093
|
-
await runProductTestCases(productId, opts);
|
|
1094
|
-
}
|
|
1095
|
-
catch (error) {
|
|
1096
|
-
exitWithError(error);
|
|
1097
|
-
}
|
|
638
|
+
var ResolveResultSchema = z.object({
|
|
639
|
+
summary: z.string().describe("What was changed and why"),
|
|
640
|
+
changes: z.array(z.object({ file: z.string(), description: z.string() })).default([]),
|
|
641
|
+
testsRun: z.string().nullable().default(null).describe("Command used to verify, and its outcome"),
|
|
642
|
+
remaining: z.array(z.string()).default([]).describe("Anything still unresolved")
|
|
1098
643
|
});
|
|
1099
|
-
|
|
1100
|
-
//
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
.
|
|
1108
|
-
|
|
1109
|
-
|
|
644
|
+
|
|
645
|
+
// src/commands/benchmark.ts
|
|
646
|
+
async function runBenchmark(opts) {
|
|
647
|
+
const { config, path: configPath } = loadConfig(opts.cwd);
|
|
648
|
+
const configDir = configPath ? join3(configPath, "..") : opts.cwd;
|
|
649
|
+
const resolved = resolveStandard("benchmark", opts, config, configDir);
|
|
650
|
+
const standard = resolved.standard;
|
|
651
|
+
log.info(
|
|
652
|
+
`Benchmarking ${pc2.bold(basename(opts.cwd))} against ${pc2.bold(standard.name)} (languages: ${resolved.languages.join(", ") || "generic"})`
|
|
653
|
+
);
|
|
654
|
+
log.debug(`standard layers: ${resolved.layers.join(" \u2192 ")}`);
|
|
655
|
+
const prompt = buildPrompt(standard, opts.cwd);
|
|
656
|
+
const { result, costUsd, numTurns } = await runAgent({
|
|
657
|
+
prompt,
|
|
658
|
+
cwd: opts.cwd,
|
|
659
|
+
model: opts.model,
|
|
660
|
+
maxTurns: opts.maxTurns,
|
|
661
|
+
timeoutMs: opts.timeoutMs,
|
|
662
|
+
allowWrite: false,
|
|
663
|
+
verbose: opts.verbose,
|
|
664
|
+
resultShape: BenchmarkResultSchema.shape,
|
|
665
|
+
systemPromptAppend: "You are Edsger, a meticulous staff-level engineer performing an objective repository assessment. Inspect the actual code and configuration before scoring. Be evidence-driven and fair; cite concrete files. Do not modify any files."
|
|
666
|
+
});
|
|
667
|
+
if (!result) {
|
|
668
|
+
log.error(
|
|
669
|
+
"The agent did not produce a result. Try increasing --max-turns or check credentials."
|
|
670
|
+
);
|
|
671
|
+
return 1;
|
|
672
|
+
}
|
|
673
|
+
const scored = scoreBenchmark(standard, result);
|
|
674
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
675
|
+
const md = renderBenchmarkMarkdown(scored, {
|
|
676
|
+
repo: basename(opts.cwd),
|
|
677
|
+
languages: resolved.languages,
|
|
678
|
+
layers: resolved.layers,
|
|
679
|
+
generatedAt,
|
|
680
|
+
model: opts.model
|
|
681
|
+
});
|
|
682
|
+
const jsonReport = {
|
|
683
|
+
schema: "edsger.benchmark/1",
|
|
684
|
+
generatedAt,
|
|
685
|
+
model: opts.model,
|
|
686
|
+
repo: basename(opts.cwd),
|
|
687
|
+
languages: resolved.languages,
|
|
688
|
+
standard: { name: standard.name, version: standard.version, layers: resolved.layers },
|
|
689
|
+
overall: scored.overall,
|
|
690
|
+
grade: scored.grade,
|
|
691
|
+
categories: scored.categories,
|
|
692
|
+
summary: scored.summary,
|
|
693
|
+
strengths: scored.strengths,
|
|
694
|
+
weaknesses: scored.weaknesses,
|
|
695
|
+
recommendations: scored.recommendations,
|
|
696
|
+
costUsd
|
|
697
|
+
};
|
|
698
|
+
if (!opts.noWrite && !opts.dryRun) {
|
|
699
|
+
const outDir = join3(opts.cwd, opts.out);
|
|
700
|
+
mkdirSync(outDir, { recursive: true });
|
|
701
|
+
writeFileSync(join3(outDir, "report.md"), md + "\n", "utf8");
|
|
702
|
+
writeFileSync(join3(outDir, "report.json"), JSON.stringify(jsonReport, null, 2) + "\n", "utf8");
|
|
703
|
+
log.success(`Report written to ${pc2.bold(join3(opts.out, "report.md"))}`);
|
|
704
|
+
}
|
|
705
|
+
printSummary(scored, numTurns, costUsd);
|
|
706
|
+
if (opts.json) process.stdout.write(JSON.stringify(jsonReport, null, 2) + "\n");
|
|
707
|
+
return 0;
|
|
708
|
+
}
|
|
709
|
+
function printSummary(scored, numTurns, costUsd) {
|
|
710
|
+
log.heading(`Score: ${scored.overall}/100 (Grade ${scored.grade})`);
|
|
711
|
+
for (const c of scored.categories) {
|
|
712
|
+
const pct = Math.round(c.fraction * 100);
|
|
713
|
+
process.stderr.write(` ${bar(pct)} ${String(pct).padStart(3)}% ${c.name}
|
|
714
|
+
`);
|
|
715
|
+
}
|
|
716
|
+
log.step(`${numTurns} turns \xB7 ~$${costUsd.toFixed(3)}`);
|
|
717
|
+
}
|
|
718
|
+
function bar(pct) {
|
|
719
|
+
const width = 20;
|
|
720
|
+
const filled = Math.round(pct / 100 * width);
|
|
721
|
+
const color = pct >= 80 ? pc2.green : pct >= 60 ? pc2.yellow : pc2.red;
|
|
722
|
+
return color("\u2588".repeat(filled)) + pc2.dim("\u2591".repeat(width - filled));
|
|
723
|
+
}
|
|
724
|
+
function buildPrompt(standard, cwd) {
|
|
725
|
+
const rubric = standard.categories.map((cat) => {
|
|
726
|
+
const crits = cat.criteria.map((cr) => {
|
|
727
|
+
const anchors = cr.rubric ? Object.entries(cr.rubric).map(([k, v]) => ` - ${k}: ${v}`).join("\n") : "";
|
|
728
|
+
return ` - id: ${cat.id}/${cr.id}
|
|
729
|
+
title: ${cr.title}
|
|
730
|
+
guidance: ${cr.guidance.trim()}` + (anchors ? `
|
|
731
|
+
anchors:
|
|
732
|
+
${anchors}` : "");
|
|
733
|
+
}).join("\n");
|
|
734
|
+
return `- category: ${cat.id} (${cat.name}, weight ${cat.weight})
|
|
735
|
+
${crits}`;
|
|
736
|
+
}).join("\n");
|
|
737
|
+
return [
|
|
738
|
+
`Assess the repository located at: ${cwd}`,
|
|
739
|
+
``,
|
|
740
|
+
`Score it against the rubric below. Each criterion is scored from 0 to ${standard.scoreScale}`,
|
|
741
|
+
`(0 = absent/harmful, ${standard.scoreScale} = exemplary), using the level anchors as a guide.`,
|
|
742
|
+
``,
|
|
743
|
+
`Method:`,
|
|
744
|
+
`1. Explore the repository structure, key source files, configuration, CI, tests, and docs.`,
|
|
745
|
+
` Use Read/Grep/Glob and read-only Bash (e.g. ls, cat, git log). Do NOT modify anything.`,
|
|
746
|
+
`2. For each criterion, gather concrete evidence (file paths) before deciding a score.`,
|
|
747
|
+
`3. Be objective and consistent. Absence of evidence for a practice means a low score.`,
|
|
748
|
+
`4. When finished, call submit_result exactly once with a score for EVERY criterion id below,`,
|
|
749
|
+
` grouped by category. Include a short justification and evidence paths for each.`,
|
|
750
|
+
``,
|
|
751
|
+
`Rubric:`,
|
|
752
|
+
rubric
|
|
753
|
+
].join("\n");
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// src/commands/pr-review.ts
|
|
757
|
+
import pc3 from "picocolors";
|
|
758
|
+
|
|
759
|
+
// src/core/gh.ts
|
|
760
|
+
import { execFile } from "child_process";
|
|
761
|
+
import { mkdtempSync, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
762
|
+
import { tmpdir } from "os";
|
|
763
|
+
import { join as join4 } from "path";
|
|
764
|
+
import { promisify } from "util";
|
|
765
|
+
var exec = promisify(execFile);
|
|
766
|
+
async function gh(args, cwd) {
|
|
767
|
+
const { stdout } = await exec("gh", args, {
|
|
768
|
+
cwd,
|
|
769
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
770
|
+
env: process.env
|
|
771
|
+
});
|
|
772
|
+
return stdout;
|
|
773
|
+
}
|
|
774
|
+
async function ensureGhReady() {
|
|
775
|
+
try {
|
|
776
|
+
await exec("gh", ["--version"]);
|
|
777
|
+
} catch {
|
|
778
|
+
throw new Error(
|
|
779
|
+
"GitHub CLI (gh) not found. Install it from https://cli.github.com and run `gh auth login`."
|
|
780
|
+
);
|
|
781
|
+
}
|
|
782
|
+
try {
|
|
783
|
+
await exec("gh", ["auth", "status"]);
|
|
784
|
+
} catch {
|
|
785
|
+
throw new Error("Not authenticated with GitHub. Run `gh auth login` first.");
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
async function getPrContext(ref, repo, cwd) {
|
|
789
|
+
const repoArgs = repo ? ["--repo", repo] : [];
|
|
790
|
+
const fields = "number,title,body,author,baseRefName,headRefName,state,url,isCrossRepository,files";
|
|
791
|
+
const metaRaw = await gh(["pr", "view", ref, ...repoArgs, "--json", fields], cwd);
|
|
792
|
+
const meta = JSON.parse(metaRaw);
|
|
793
|
+
const diff = await gh(["pr", "diff", ref, ...repoArgs], cwd);
|
|
794
|
+
return {
|
|
795
|
+
number: meta.number,
|
|
796
|
+
title: meta.title,
|
|
797
|
+
body: meta.body ?? "",
|
|
798
|
+
author: meta.author?.login ?? "unknown",
|
|
799
|
+
baseRefName: meta.baseRefName,
|
|
800
|
+
headRefName: meta.headRefName,
|
|
801
|
+
state: meta.state,
|
|
802
|
+
url: meta.url,
|
|
803
|
+
isCrossRepository: Boolean(meta.isCrossRepository),
|
|
804
|
+
files: meta.files ?? [],
|
|
805
|
+
diff
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
async function checkoutPr(ref, repo, cwd) {
|
|
809
|
+
const repoArgs = repo ? ["--repo", repo] : [];
|
|
810
|
+
await gh(["pr", "checkout", ref, ...repoArgs], cwd);
|
|
811
|
+
}
|
|
812
|
+
async function postReview(ctx, repo, event, body, inline, cwd) {
|
|
813
|
+
const target = repo ?? await currentRepoSlug(cwd);
|
|
814
|
+
if (inline.length === 0) {
|
|
815
|
+
const flag = event === "APPROVE" ? "--approve" : event === "REQUEST_CHANGES" ? "--request-changes" : "--comment";
|
|
816
|
+
await gh(["pr", "review", String(ctx.number), "--repo", target, flag, "--body", body], cwd);
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
const payload = {
|
|
820
|
+
body,
|
|
821
|
+
event,
|
|
822
|
+
comments: inline.map((c) => ({ path: c.path, line: c.line, side: "RIGHT", body: c.body }))
|
|
823
|
+
};
|
|
824
|
+
const dir = mkdtempSync(join4(tmpdir(), "edsger-review-"));
|
|
825
|
+
const file = join4(dir, "review.json");
|
|
826
|
+
writeFileSync2(file, JSON.stringify(payload), "utf8");
|
|
827
|
+
try {
|
|
828
|
+
await gh(
|
|
829
|
+
["api", "--method", "POST", `repos/${target}/pulls/${ctx.number}/reviews`, "--input", file],
|
|
830
|
+
cwd
|
|
831
|
+
);
|
|
832
|
+
} catch {
|
|
833
|
+
const flag = event === "APPROVE" ? "--approve" : event === "REQUEST_CHANGES" ? "--request-changes" : "--comment";
|
|
834
|
+
await gh(["pr", "review", String(ctx.number), "--repo", target, flag, "--body", body], cwd);
|
|
835
|
+
} finally {
|
|
836
|
+
rmSync(dir, { recursive: true, force: true });
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
async function currentRepoSlug(cwd) {
|
|
840
|
+
const raw = await gh(["repo", "view", "--json", "nameWithOwner"], cwd);
|
|
841
|
+
return JSON.parse(raw).nameWithOwner;
|
|
842
|
+
}
|
|
843
|
+
async function getPrFeedback(ctx, repo, cwd) {
|
|
844
|
+
const target = repo ?? await currentRepoSlug(cwd);
|
|
845
|
+
const reviewRaw = await gh(
|
|
846
|
+
["api", `repos/${target}/pulls/${ctx.number}/comments`, "--paginate"],
|
|
847
|
+
cwd
|
|
848
|
+
).catch(() => "[]");
|
|
849
|
+
const issueRaw = await gh(
|
|
850
|
+
["api", `repos/${target}/issues/${ctx.number}/comments`, "--paginate"],
|
|
851
|
+
cwd
|
|
852
|
+
).catch(() => "[]");
|
|
853
|
+
const review = JSON.parse(reviewRaw);
|
|
854
|
+
const issue = JSON.parse(issueRaw);
|
|
855
|
+
return {
|
|
856
|
+
reviewComments: review.map((c) => ({
|
|
857
|
+
author: c.user?.login ?? "unknown",
|
|
858
|
+
path: c.path,
|
|
859
|
+
line: c.line,
|
|
860
|
+
body: c.body
|
|
861
|
+
})),
|
|
862
|
+
issueComments: issue.map((c) => ({ author: c.user?.login ?? "unknown", body: c.body }))
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// src/commands/pr-review.ts
|
|
867
|
+
var SEVERITY_RANK = {
|
|
868
|
+
critical: 4,
|
|
869
|
+
high: 3,
|
|
870
|
+
medium: 2,
|
|
871
|
+
low: 1,
|
|
872
|
+
info: 0
|
|
873
|
+
};
|
|
874
|
+
async function runPrReview(opts) {
|
|
875
|
+
await ensureGhReady();
|
|
876
|
+
log.info(`Fetching PR ${pc3.bold(opts.pr)}${opts.repo ? ` from ${opts.repo}` : ""}`);
|
|
877
|
+
const ctx = await getPrContext(opts.pr, opts.repo, opts.cwd);
|
|
878
|
+
const { config, path: configPath } = loadConfig(opts.cwd);
|
|
879
|
+
const configDir = configPath ? `${configPath}/..` : opts.cwd;
|
|
880
|
+
const fileHint = inferLanguagesFromFiles(
|
|
881
|
+
ctx.files.map((f) => f.path),
|
|
882
|
+
findBundledAssetsDir()
|
|
883
|
+
);
|
|
884
|
+
const resolved = resolveStandard("review", opts, config, configDir, fileHint);
|
|
885
|
+
const standard = resolved.standard;
|
|
886
|
+
log.info(
|
|
887
|
+
`Reviewing "${pc3.bold(ctx.title)}" (#${ctx.number}, ${ctx.files.length} files) against ${pc3.bold(standard.name)}`
|
|
888
|
+
);
|
|
889
|
+
const { result, costUsd, numTurns } = await runAgent({
|
|
890
|
+
prompt: buildPrompt2(standard, ctx),
|
|
891
|
+
cwd: opts.cwd,
|
|
892
|
+
model: opts.model,
|
|
893
|
+
maxTurns: opts.maxTurns,
|
|
894
|
+
timeoutMs: opts.timeoutMs,
|
|
895
|
+
allowWrite: false,
|
|
896
|
+
verbose: opts.verbose,
|
|
897
|
+
resultShape: ReviewResultSchema.shape,
|
|
898
|
+
systemPromptAppend: "You are Edsger, a senior code reviewer. Review only what the diff changes, but read surrounding code for context. Be specific, kind, and actionable; prefer high-signal findings over nitpicks. Do not modify any files."
|
|
899
|
+
});
|
|
900
|
+
if (!result) {
|
|
901
|
+
log.error("The agent did not produce a review.");
|
|
902
|
+
return 1;
|
|
903
|
+
}
|
|
904
|
+
const event = decideEvent(result, standard);
|
|
905
|
+
const body = renderBody(result, ctx, standard, resolved.layers);
|
|
906
|
+
const inline = buildInlineComments(result, ctx, standard);
|
|
907
|
+
printPreview(result, event, numTurns, costUsd);
|
|
908
|
+
if (opts.json) {
|
|
909
|
+
process.stdout.write(
|
|
910
|
+
JSON.stringify(
|
|
911
|
+
{ verdict: result.verdict, event, findings: result.findings, summary: result.summary },
|
|
912
|
+
null,
|
|
913
|
+
2
|
|
914
|
+
) + "\n"
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
if (opts.post && !opts.dryRun) {
|
|
918
|
+
await postReview(ctx, opts.repo, event, body, inline, opts.cwd);
|
|
919
|
+
log.success(`Posted review (${event}) to ${ctx.url}`);
|
|
920
|
+
} else {
|
|
921
|
+
log.warn(`Preview only. Re-run with ${pc3.bold("--post")} to submit this review to GitHub.`);
|
|
922
|
+
process.stderr.write("\n" + body + "\n");
|
|
923
|
+
}
|
|
924
|
+
return 0;
|
|
925
|
+
}
|
|
926
|
+
function decideEvent(result, standard) {
|
|
927
|
+
const blocking = new Set(standard.policy.blockingSeverities);
|
|
928
|
+
const hasBlocking = result.findings.some((f) => blocking.has(f.severity));
|
|
929
|
+
if (hasBlocking) return "REQUEST_CHANGES";
|
|
930
|
+
if (result.verdict === "approve") return "APPROVE";
|
|
931
|
+
if (result.verdict === "request_changes") return "REQUEST_CHANGES";
|
|
932
|
+
return "COMMENT";
|
|
933
|
+
}
|
|
934
|
+
function buildInlineComments(result, ctx, standard) {
|
|
935
|
+
const changed = new Set(ctx.files.map((f) => f.path));
|
|
936
|
+
const sorted = [...result.findings].sort(
|
|
937
|
+
(a, b) => SEVERITY_RANK[b.severity] - SEVERITY_RANK[a.severity]
|
|
938
|
+
);
|
|
939
|
+
const out = [];
|
|
940
|
+
for (const f of sorted) {
|
|
941
|
+
if (out.length >= standard.policy.maxComments) break;
|
|
942
|
+
if (f.line && f.file && f.file !== "-" && changed.has(f.file)) {
|
|
943
|
+
out.push({ path: f.file, line: f.line, body: renderFinding(f) });
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
return out;
|
|
947
|
+
}
|
|
948
|
+
function renderFinding(f) {
|
|
949
|
+
const head = `**[${f.severity}] ${f.title}**`;
|
|
950
|
+
const fix = f.suggestion ? `
|
|
951
|
+
|
|
952
|
+
_Suggestion:_ ${f.suggestion}` : "";
|
|
953
|
+
return `${head}
|
|
954
|
+
|
|
955
|
+
${f.detail}${fix}`;
|
|
956
|
+
}
|
|
957
|
+
function renderBody(result, ctx, standard, layers) {
|
|
958
|
+
const counts = countBySeverity(result.findings);
|
|
959
|
+
const lines = [];
|
|
960
|
+
lines.push(`## Edsger review \u2014 ${verdictLabel(result.verdict)}`);
|
|
961
|
+
lines.push("");
|
|
962
|
+
lines.push(result.summary);
|
|
963
|
+
lines.push("");
|
|
964
|
+
lines.push(
|
|
965
|
+
`**Findings:** ` + ["critical", "high", "medium", "low", "info"].filter((s) => counts[s]).map((s) => `${counts[s]} ${s}`).join(", ") || "no blocking issues"
|
|
966
|
+
);
|
|
967
|
+
lines.push("");
|
|
968
|
+
const general = result.findings.filter((f) => !f.line || f.file === "-");
|
|
969
|
+
if (general.length) {
|
|
970
|
+
lines.push(`### General comments`);
|
|
971
|
+
lines.push("");
|
|
972
|
+
for (const f of general) lines.push(`- ${renderFinding(f).replace(/\n+/g, " ")}`);
|
|
973
|
+
lines.push("");
|
|
974
|
+
}
|
|
975
|
+
lines.push("---");
|
|
976
|
+
lines.push(
|
|
977
|
+
`<sub>Reviewed against ${standard.name} v${standard.version} (${layers.join(" \u2192 ")}) by [edsger](https://www.npmjs.com/package/edsger).</sub>`
|
|
978
|
+
);
|
|
979
|
+
void ctx;
|
|
980
|
+
return lines.join("\n");
|
|
981
|
+
}
|
|
982
|
+
function printPreview(result, event, numTurns, costUsd) {
|
|
983
|
+
log.heading(`Verdict: ${event}`);
|
|
984
|
+
const counts = countBySeverity(result.findings);
|
|
985
|
+
for (const s of ["critical", "high", "medium", "low", "info"]) {
|
|
986
|
+
if (counts[s]) process.stderr.write(` ${sevColor(s)(s.padEnd(8))} ${counts[s]}
|
|
987
|
+
`);
|
|
988
|
+
}
|
|
989
|
+
log.step(`${numTurns} turns \xB7 ~$${costUsd.toFixed(3)}`);
|
|
990
|
+
}
|
|
991
|
+
function countBySeverity(findings) {
|
|
992
|
+
const c = { critical: 0, high: 0, medium: 0, low: 0, info: 0 };
|
|
993
|
+
for (const f of findings) c[f.severity]++;
|
|
994
|
+
return c;
|
|
995
|
+
}
|
|
996
|
+
function sevColor(s) {
|
|
997
|
+
if (s === "critical" || s === "high") return pc3.red;
|
|
998
|
+
if (s === "medium") return pc3.yellow;
|
|
999
|
+
return pc3.dim;
|
|
1000
|
+
}
|
|
1001
|
+
function verdictLabel(v) {
|
|
1002
|
+
return v === "approve" ? "Approve" : v === "request_changes" ? "Request changes" : "Comment";
|
|
1003
|
+
}
|
|
1004
|
+
function buildPrompt2(standard, ctx) {
|
|
1005
|
+
const focus = standard.focusAreas.map((fa) => {
|
|
1006
|
+
const checklist = (fa.checklist ?? []).map((c) => ` - ${c}`).join("\n");
|
|
1007
|
+
return `- id: ${fa.id} (${fa.title}, default severity ${fa.defaultSeverity ?? "medium"})
|
|
1008
|
+
guidance: ${fa.guidance.trim()}` + (checklist ? `
|
|
1009
|
+
checklist:
|
|
1010
|
+
${checklist}` : "");
|
|
1011
|
+
}).join("\n");
|
|
1012
|
+
return [
|
|
1013
|
+
`Review the following pull request.`,
|
|
1014
|
+
``,
|
|
1015
|
+
`Title: ${ctx.title}`,
|
|
1016
|
+
`Author: ${ctx.author}`,
|
|
1017
|
+
`Base: ${ctx.baseRefName} Head: ${ctx.headRefName}`,
|
|
1018
|
+
`Description:`,
|
|
1019
|
+
ctx.body ? indent(ctx.body) : " (no description)",
|
|
1020
|
+
``,
|
|
1021
|
+
`Changed files: ${ctx.files.map((f) => f.path).join(", ")}`,
|
|
1022
|
+
``,
|
|
1023
|
+
`Method:`,
|
|
1024
|
+
`1. Read the diff below. Use Read/Grep/Glob to inspect surrounding code in the working`,
|
|
1025
|
+
` directory for context when needed. Do NOT modify files.`,
|
|
1026
|
+
`2. Evaluate the change against the focus areas. Assign each finding a severity`,
|
|
1027
|
+
` (critical/high/medium/low/info) and, where it maps to a changed line, the file path`,
|
|
1028
|
+
` and the 1-based line number in the NEW version of the file.`,
|
|
1029
|
+
`3. Verify the change does what its description claims; flag missing tests for new logic.`,
|
|
1030
|
+
`4. Choose a verdict: approve, request_changes, or comment. Then call submit_result once.`,
|
|
1031
|
+
``,
|
|
1032
|
+
`Focus areas:`,
|
|
1033
|
+
focus,
|
|
1034
|
+
``,
|
|
1035
|
+
`Unified diff:`,
|
|
1036
|
+
"```diff",
|
|
1037
|
+
truncate(ctx.diff, 18e4),
|
|
1038
|
+
"```"
|
|
1039
|
+
].join("\n");
|
|
1040
|
+
}
|
|
1041
|
+
function indent(s) {
|
|
1042
|
+
return s.split("\n").map((l) => ` ${l}`).join("\n");
|
|
1043
|
+
}
|
|
1044
|
+
function truncate(s, max) {
|
|
1045
|
+
return s.length > max ? s.slice(0, max) + "\n... [diff truncated] ..." : s;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// src/commands/pr-resolve.ts
|
|
1049
|
+
import { execFile as execFile2 } from "child_process";
|
|
1050
|
+
import { promisify as promisify2 } from "util";
|
|
1051
|
+
import pc4 from "picocolors";
|
|
1052
|
+
var exec2 = promisify2(execFile2);
|
|
1053
|
+
async function runPrResolve(opts) {
|
|
1054
|
+
await ensureGhReady();
|
|
1055
|
+
log.info(`Fetching PR ${pc4.bold(opts.pr)}${opts.repo ? ` from ${opts.repo}` : ""}`);
|
|
1056
|
+
const ctx = await getPrContext(opts.pr, opts.repo, opts.cwd);
|
|
1057
|
+
const feedback = await getPrFeedback(ctx, opts.repo, opts.cwd).catch(() => ({
|
|
1058
|
+
reviewComments: [],
|
|
1059
|
+
issueComments: []
|
|
1060
|
+
}));
|
|
1061
|
+
if (opts.checkout && !opts.dryRun) {
|
|
1062
|
+
log.step(`checking out PR branch ${ctx.headRefName}`);
|
|
1063
|
+
await checkoutPr(opts.pr, opts.repo, opts.cwd);
|
|
1064
|
+
}
|
|
1065
|
+
log.info(
|
|
1066
|
+
`Resolving "${pc4.bold(ctx.title)}" (#${ctx.number}) \u2014 ${feedback.reviewComments.length} review comments`
|
|
1067
|
+
);
|
|
1068
|
+
const { result, costUsd, numTurns } = await runAgent({
|
|
1069
|
+
prompt: buildPrompt3(ctx, feedback, opts.instruction),
|
|
1070
|
+
cwd: opts.cwd,
|
|
1071
|
+
model: opts.model,
|
|
1072
|
+
maxTurns: Math.max(opts.maxTurns, 30),
|
|
1073
|
+
timeoutMs: Math.max(opts.timeoutMs, 20 * 6e4),
|
|
1074
|
+
allowWrite: true,
|
|
1075
|
+
verbose: opts.verbose,
|
|
1076
|
+
resultShape: ResolveResultSchema.shape,
|
|
1077
|
+
systemPromptAppend: "You are Edsger, a careful senior engineer fixing a pull request. Make the smallest correct change that addresses the feedback. Match the existing code style. After editing, run the project's tests/linters to verify. Never weaken tests to make them pass, and never commit secrets."
|
|
1078
|
+
});
|
|
1079
|
+
if (!result) {
|
|
1080
|
+
log.error("The agent did not produce a result.");
|
|
1081
|
+
return 1;
|
|
1082
|
+
}
|
|
1083
|
+
printSummary2(result, numTurns, costUsd);
|
|
1084
|
+
if (opts.json) process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
1085
|
+
if (opts.push && !opts.dryRun) {
|
|
1086
|
+
const pushed = await commitAndPush(ctx, result, opts.cwd, opts.checkout);
|
|
1087
|
+
if (pushed) log.success(`Committed and pushed to ${ctx.headRefName}`);
|
|
1088
|
+
else log.warn("Nothing to commit \u2014 working tree is clean.");
|
|
1089
|
+
} else {
|
|
1090
|
+
log.warn(
|
|
1091
|
+
`Changes left in the working tree. Review them, then commit/push (or re-run with ${pc4.bold("--push")}).`
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
1094
|
+
return 0;
|
|
1095
|
+
}
|
|
1096
|
+
async function commitAndPush(ctx, result, cwd, checkedOut) {
|
|
1097
|
+
const status = await exec2("git", ["status", "--porcelain"], { cwd });
|
|
1098
|
+
if (!status.stdout.trim()) return false;
|
|
1099
|
+
await exec2("git", ["add", "-A"], { cwd });
|
|
1100
|
+
const message = `fix: address review feedback on #${ctx.number}
|
|
1101
|
+
|
|
1102
|
+
${result.summary}`;
|
|
1103
|
+
await exec2("git", ["commit", "-m", message], { cwd });
|
|
1104
|
+
if (checkedOut) {
|
|
1110
1105
|
try {
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1106
|
+
await exec2("git", ["push"], { cwd });
|
|
1107
|
+
return true;
|
|
1108
|
+
} catch (err) {
|
|
1109
|
+
if (ctx.isCrossRepository) {
|
|
1110
|
+
throw new Error(
|
|
1111
|
+
`Could not push to the fork's branch automatically: ${errText(err)}
|
|
1112
|
+
Push manually, or ensure the PR branch was checked out with gh.`
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
if (ctx.isCrossRepository) {
|
|
1118
|
+
throw new Error(
|
|
1119
|
+
"This PR is from a fork. Re-run without --no-checkout so gh can configure the push target, or commit and push to the fork manually."
|
|
1120
|
+
);
|
|
1121
|
+
}
|
|
1122
|
+
await exec2("git", ["push", "origin", `HEAD:${ctx.headRefName}`], { cwd });
|
|
1123
|
+
return true;
|
|
1124
|
+
}
|
|
1125
|
+
function errText(err) {
|
|
1126
|
+
return err instanceof Error ? err.message.split("\n")[0] ?? "" : String(err);
|
|
1127
|
+
}
|
|
1128
|
+
function printSummary2(result, numTurns, costUsd) {
|
|
1129
|
+
log.heading("Resolution summary");
|
|
1130
|
+
process.stderr.write(` ${result.summary}
|
|
1131
|
+
|
|
1132
|
+
`);
|
|
1133
|
+
if (result.changes.length) {
|
|
1134
|
+
for (const c of result.changes)
|
|
1135
|
+
process.stderr.write(` ${pc4.green("~")} ${c.file} \u2014 ${c.description}
|
|
1136
|
+
`);
|
|
1137
|
+
}
|
|
1138
|
+
if (result.testsRun) log.step(`verification: ${result.testsRun}`);
|
|
1139
|
+
if (result.remaining.length) {
|
|
1140
|
+
log.warn("Remaining items:");
|
|
1141
|
+
for (const r of result.remaining) process.stderr.write(` - ${r}
|
|
1142
|
+
`);
|
|
1143
|
+
}
|
|
1144
|
+
log.step(`${numTurns} turns \xB7 ~$${costUsd.toFixed(3)}`);
|
|
1145
|
+
}
|
|
1146
|
+
function buildPrompt3(ctx, feedback, instruction) {
|
|
1147
|
+
const reviewComments = feedback.reviewComments.length ? feedback.reviewComments.map((c) => ` - ${c.path}${c.line ? `:${c.line}` : ""} (${c.author}): ${c.body}`).join("\n") : " (none)";
|
|
1148
|
+
const issueComments = feedback.issueComments.length ? feedback.issueComments.map((c) => ` - ${c.author}: ${c.body}`).join("\n") : " (none)";
|
|
1149
|
+
return [
|
|
1150
|
+
`Resolve the open items on this pull request by editing the code in the working directory.`,
|
|
1151
|
+
``,
|
|
1152
|
+
`PR #${ctx.number}: ${ctx.title}`,
|
|
1153
|
+
`Base: ${ctx.baseRefName} Head: ${ctx.headRefName}`,
|
|
1154
|
+
``,
|
|
1155
|
+
instruction ? `Primary instruction: ${instruction}` : `Primary goal: address the reviewer feedback below.`,
|
|
1156
|
+
``,
|
|
1157
|
+
`Inline review comments:`,
|
|
1158
|
+
reviewComments,
|
|
1159
|
+
``,
|
|
1160
|
+
`Conversation comments:`,
|
|
1161
|
+
issueComments,
|
|
1162
|
+
``,
|
|
1163
|
+
`Method:`,
|
|
1164
|
+
`1. Understand each requested change. Use Read/Grep/Glob to locate the relevant code.`,
|
|
1165
|
+
`2. Make the minimal, correct edits with Edit/Write. Match existing conventions.`,
|
|
1166
|
+
`3. Run the project's tests and linters (via Bash) to verify your changes pass.`,
|
|
1167
|
+
`4. Do not weaken or delete tests to make them pass; do not introduce secrets.`,
|
|
1168
|
+
`5. Call submit_result once with a summary, the list of files changed, how you verified,`,
|
|
1169
|
+
` and anything you could not resolve.`
|
|
1170
|
+
].join("\n");
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
// src/commands/skills.ts
|
|
1174
|
+
import {
|
|
1175
|
+
cpSync,
|
|
1176
|
+
existsSync as existsSync3,
|
|
1177
|
+
mkdirSync as mkdirSync2,
|
|
1178
|
+
readFileSync as readFileSync2,
|
|
1179
|
+
readdirSync as readdirSync2,
|
|
1180
|
+
rmSync as rmSync2,
|
|
1181
|
+
statSync as statSync2,
|
|
1182
|
+
writeFileSync as writeFileSync3
|
|
1183
|
+
} from "fs";
|
|
1184
|
+
import { homedir as homedir2 } from "os";
|
|
1185
|
+
import { join as join5 } from "path";
|
|
1186
|
+
import pc5 from "picocolors";
|
|
1187
|
+
var MANAGED = ["benchmark", "pr-review", "pr-resolve"];
|
|
1188
|
+
function targetSkillsDir(scope) {
|
|
1189
|
+
return scope.global ? join5(homedir2(), ".claude", "skills") : join5(scope.cwd, ".claude", "skills");
|
|
1190
|
+
}
|
|
1191
|
+
function sourceSkillsDir() {
|
|
1192
|
+
return join5(findBundledAssetsDir(), "skills");
|
|
1193
|
+
}
|
|
1194
|
+
async function runSkillsInstall(opts) {
|
|
1195
|
+
const assetsDir = findBundledAssetsDir();
|
|
1196
|
+
const src = sourceSkillsDir();
|
|
1197
|
+
if (!existsSync3(src)) {
|
|
1198
|
+
log.error(`Bundled skills not found at ${src}. Reinstall edsger.`);
|
|
1199
|
+
return 1;
|
|
1200
|
+
}
|
|
1201
|
+
const dest = targetSkillsDir(opts);
|
|
1202
|
+
mkdirSync2(dest, { recursive: true });
|
|
1203
|
+
const available = readdirSync2(src).filter((n) => existsSync3(join5(src, n, "SKILL.md")));
|
|
1204
|
+
let installed = 0;
|
|
1205
|
+
for (const name of available) {
|
|
1206
|
+
const outDir = join5(dest, name);
|
|
1207
|
+
if (existsSync3(outDir) && !opts.force) {
|
|
1208
|
+
log.warn(`skill "${name}" already exists \u2014 use --force to overwrite`);
|
|
1209
|
+
continue;
|
|
1210
|
+
}
|
|
1211
|
+
rmSync2(outDir, { recursive: true, force: true });
|
|
1212
|
+
cpSync(join5(src, name), outDir, { recursive: true });
|
|
1213
|
+
const skillFile = join5(outDir, "SKILL.md");
|
|
1214
|
+
const rewritten = readFileSync2(skillFile, "utf8").split("${CLAUDE_PLUGIN_ROOT}").join(assetsDir);
|
|
1215
|
+
writeFileSync3(skillFile, rewritten, "utf8");
|
|
1216
|
+
installed++;
|
|
1217
|
+
log.success(`installed /${name}`);
|
|
1218
|
+
}
|
|
1219
|
+
if (installed === 0) {
|
|
1220
|
+
log.info("Nothing installed.");
|
|
1221
|
+
return 0;
|
|
1222
|
+
}
|
|
1223
|
+
log.heading(`Installed ${installed} skill(s) into ${pc5.bold(dest)}`);
|
|
1224
|
+
log.step(`scope: ${opts.global ? "user (all projects)" : "this project"}`);
|
|
1225
|
+
log.step(`standards: ${assetsDir}`);
|
|
1226
|
+
log.step("Open Claude Code and run /benchmark, /pr-review, or /pr-resolve.");
|
|
1227
|
+
return 0;
|
|
1228
|
+
}
|
|
1229
|
+
async function runSkillsUninstall(opts) {
|
|
1230
|
+
const dest = targetSkillsDir(opts);
|
|
1231
|
+
let removed = 0;
|
|
1232
|
+
for (const name of MANAGED) {
|
|
1233
|
+
const dir = join5(dest, name);
|
|
1234
|
+
if (existsSync3(dir)) {
|
|
1235
|
+
rmSync2(dir, { recursive: true, force: true });
|
|
1236
|
+
removed++;
|
|
1237
|
+
log.success(`removed /${name}`);
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
log.info(removed ? `Removed ${removed} skill(s) from ${dest}` : "No edsger skills found.");
|
|
1241
|
+
return 0;
|
|
1242
|
+
}
|
|
1243
|
+
async function runSkillsList(opts) {
|
|
1244
|
+
for (const [label, scope] of [
|
|
1245
|
+
["user", { ...opts, global: true }],
|
|
1246
|
+
["project", { ...opts, global: false }]
|
|
1247
|
+
]) {
|
|
1248
|
+
const dir = targetSkillsDir(scope);
|
|
1249
|
+
const present = MANAGED.filter((n) => {
|
|
1250
|
+
const p = join5(dir, n, "SKILL.md");
|
|
1251
|
+
return existsSync3(p) && statSync2(p).isFile();
|
|
1252
|
+
});
|
|
1253
|
+
log.heading(`${label}: ${dir}`);
|
|
1254
|
+
if (present.length) for (const n of present) process.stderr.write(` ${pc5.green("\u2713")} /${n}
|
|
1255
|
+
`);
|
|
1256
|
+
else process.stderr.write(` ${pc5.dim("(none installed)")}
|
|
1257
|
+
`);
|
|
1258
|
+
}
|
|
1259
|
+
return 0;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
// src/index.ts
|
|
1263
|
+
var VERSION = "0.78.0";
|
|
1264
|
+
var DEFAULT_MODEL = "claude-sonnet-4-6";
|
|
1265
|
+
function toGlobal(raw) {
|
|
1266
|
+
setVerbose(Boolean(raw.verbose));
|
|
1267
|
+
return {
|
|
1268
|
+
cwd: resolve2(raw.cwd ?? process.cwd()),
|
|
1269
|
+
model: raw.model ?? process.env.EDSGER_MODEL ?? DEFAULT_MODEL,
|
|
1270
|
+
standardPath: raw.standard,
|
|
1271
|
+
assetsPath: raw.assets,
|
|
1272
|
+
lang: raw.lang,
|
|
1273
|
+
json: Boolean(raw.json),
|
|
1274
|
+
verbose: Boolean(raw.verbose),
|
|
1275
|
+
dryRun: Boolean(raw.dryRun),
|
|
1276
|
+
maxTurns: raw.maxTurns ? Number.parseInt(raw.maxTurns, 10) : 20,
|
|
1277
|
+
timeoutMs: (raw.timeout ? Number.parseFloat(raw.timeout) : 15) * 6e4
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
function addGlobalOptions(cmd) {
|
|
1281
|
+
return cmd.option("-C, --cwd <dir>", "directory of the repository to operate on").addOption(new Option("-m, --model <model>", "model id for the agent").default(void 0)).option("--standard <path>", "path to a custom standards dir or file (overrides bundled)").option("--assets <path>", "override the entire assets root").option("--lang <id>", "force a language id, skipping detection").option("--max-turns <n>", "max agentic turns").option("--timeout <minutes>", "hard wall-clock limit per agent run (default 15)").option("--json", "print machine-readable JSON to stdout").option("-v, --verbose", "verbose logging").option("--dry-run", "run the agent but make no persistent/external changes");
|
|
1282
|
+
}
|
|
1283
|
+
async function guard(fn) {
|
|
1284
|
+
try {
|
|
1285
|
+
const code = await fn();
|
|
1286
|
+
process.exitCode = code;
|
|
1287
|
+
} catch (err) {
|
|
1288
|
+
log.error(err instanceof Error ? err.message : String(err));
|
|
1289
|
+
if (process.env.EDSGER_DEBUG && err instanceof Error)
|
|
1290
|
+
process.stderr.write((err.stack ?? "") + "\n");
|
|
1291
|
+
process.exitCode = 1;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
var program = new Command();
|
|
1295
|
+
program.name("edsger").description(
|
|
1296
|
+
"Agentic CLI to benchmark repositories and review/resolve pull requests\nagainst industrial-grade standards (from edsger-assets, fully overridable)."
|
|
1297
|
+
).version(VERSION, "-V, --version");
|
|
1298
|
+
addGlobalOptions(
|
|
1299
|
+
program.command("benchmark").description("Score a repository against a benchmark rubric and write a report").option("--out <dir>", "report output directory (relative to repo)", ".edsger/benchmark").option("--no-write", "do not write report files into the repo")
|
|
1300
|
+
).action(async (opts) => {
|
|
1301
|
+
await guard(
|
|
1302
|
+
() => runBenchmark({ ...toGlobal(opts), out: opts.out, noWrite: opts.write === false })
|
|
1303
|
+
);
|
|
1124
1304
|
});
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
.
|
|
1130
|
-
|
|
1131
|
-
.requiredOption('--scan-id <id>', 'Pending feature_scans row id to drive (created by the desktop UI before invocation)')
|
|
1132
|
-
.option('-g, --guidance <text>', 'Human direction for the AI (focus areas, exclusions)')
|
|
1133
|
-
.option('-v, --verbose', 'Verbose output')
|
|
1134
|
-
.action(async (productId, opts) => {
|
|
1135
|
-
try {
|
|
1136
|
-
await runFeatures(productId, {
|
|
1137
|
-
scanId: opts.scanId,
|
|
1138
|
-
guidance: opts.guidance,
|
|
1139
|
-
verbose: opts.verbose,
|
|
1140
|
-
});
|
|
1141
|
-
}
|
|
1142
|
-
catch (error) {
|
|
1143
|
-
exitWithError(error);
|
|
1144
|
-
}
|
|
1305
|
+
addGlobalOptions(
|
|
1306
|
+
program.command("pr-review").description("Review a pull request against the review standard (requires gh)").argument("<pr>", "PR number, URL, or branch").option("--repo <owner/repo>", "target repository (defaults to current)").option("--post", "submit the review to GitHub (otherwise preview only)")
|
|
1307
|
+
).action(async (pr, opts) => {
|
|
1308
|
+
await guard(
|
|
1309
|
+
() => runPrReview({ ...toGlobal(opts), pr, repo: opts.repo, post: Boolean(opts.post) })
|
|
1310
|
+
);
|
|
1145
1311
|
});
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
}
|
|
1312
|
+
addGlobalOptions(
|
|
1313
|
+
program.command("pr-resolve").description("Resolve review feedback / fix a pull request (requires gh)").argument("<pr>", "PR number, URL, or branch").option("--repo <owner/repo>", "target repository (defaults to current)").option("--instruction <text>", "what to do (defaults to addressing review comments)").option("--no-checkout", "do not check out the PR branch first").option("--push", "commit and push the changes when done")
|
|
1314
|
+
).action(
|
|
1315
|
+
async (pr, opts) => {
|
|
1316
|
+
await guard(
|
|
1317
|
+
() => runPrResolve({
|
|
1318
|
+
...toGlobal(opts),
|
|
1319
|
+
pr,
|
|
1320
|
+
repo: opts.repo,
|
|
1321
|
+
instruction: opts.instruction,
|
|
1322
|
+
checkout: opts.checkout !== false,
|
|
1323
|
+
push: Boolean(opts.push)
|
|
1324
|
+
})
|
|
1325
|
+
);
|
|
1326
|
+
}
|
|
1327
|
+
);
|
|
1328
|
+
var skills = program.command("skills").description("install edsger as Claude Code skills (/benchmark, /pr-review, /pr-resolve)");
|
|
1329
|
+
skills.command("install").description("install the skills into Claude Code").option("--global", "install for the current user (~/.claude/skills) [default]").option("--project", "install into ./.claude/skills instead").option("-C, --cwd <dir>", "project directory (with --project)").option("--force", "overwrite existing skills").action(async (opts) => {
|
|
1330
|
+
await guard(
|
|
1331
|
+
() => runSkillsInstall({
|
|
1332
|
+
cwd: resolve2(opts.cwd ?? process.cwd()),
|
|
1333
|
+
global: opts.project ? false : true,
|
|
1334
|
+
force: Boolean(opts.force)
|
|
1335
|
+
})
|
|
1336
|
+
);
|
|
1172
1337
|
});
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
.option('--pr-id <id>', 'Pull request record ID in database')
|
|
1181
|
-
.option('--repo-id <id>', 'Run in repo-only mode against a single repositories row (no product context)')
|
|
1182
|
-
.option('--no-learn', 'Skip checklist learning after resolve')
|
|
1183
|
-
.option('-v, --verbose', 'Verbose output')
|
|
1184
|
-
.action(async (productId, opts) => {
|
|
1185
|
-
try {
|
|
1186
|
-
if (!productId && !opts.repoId) {
|
|
1187
|
-
throw new Error('Provide a productId or --repo-id (repo-only mode) for pr-resolve');
|
|
1188
|
-
}
|
|
1189
|
-
await runPRResolve(productId, opts);
|
|
1190
|
-
}
|
|
1191
|
-
catch (error) {
|
|
1192
|
-
exitWithError(error);
|
|
1193
|
-
}
|
|
1338
|
+
skills.command("uninstall").description("remove the edsger skills from Claude Code").option("--global", "remove from the user scope (~/.claude/skills) [default]").option("--project", "remove from ./.claude/skills instead").option("-C, --cwd <dir>", "project directory (with --project)").action(async (opts) => {
|
|
1339
|
+
await guard(
|
|
1340
|
+
() => runSkillsUninstall({
|
|
1341
|
+
cwd: resolve2(opts.cwd ?? process.cwd()),
|
|
1342
|
+
global: opts.project ? false : true
|
|
1343
|
+
})
|
|
1344
|
+
);
|
|
1194
1345
|
});
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
// ============================================================
|
|
1198
|
-
program
|
|
1199
|
-
.option('-r, --review', 'Review code changes')
|
|
1200
|
-
.option('-s, --staged', 'Review only staged changes')
|
|
1201
|
-
.option('-f, --files <patterns...>', 'Review specific file patterns')
|
|
1202
|
-
.option('--refactor', 'Refactor code in current directory')
|
|
1203
|
-
.option('--init', 'Initialize .edsger directory with project templates')
|
|
1204
|
-
.option('--product-level', 'Use legacy product-level workflow (requires EDSGER_PRODUCT_ID, EDSGER_MCP_SERVER_URL, EDSGER_MCP_TOKEN env vars)')
|
|
1205
|
-
.option('--watch-tasks <productId>', 'Watch and execute pending tasks for a product')
|
|
1206
|
-
.option('--concurrency <number>', 'Max concurrent issues to process (default: 3)', parseInt)
|
|
1207
|
-
.option('-c, --config <path>', 'Path to config file')
|
|
1208
|
-
.option('-v, --verbose', 'Verbose output');
|
|
1209
|
-
program.action(async (options, command) => {
|
|
1210
|
-
// Commander silently falls through to this default action when the user
|
|
1211
|
-
// passes a subcommand name that isn't registered (e.g. an old binary that
|
|
1212
|
-
// doesn't yet know about `sync-sentry-issues`). That makes version-skew
|
|
1213
|
-
// bugs look like the agent inexplicably running with the wrong intent.
|
|
1214
|
-
// Treat any leftover positional arg as an unknown command and abort.
|
|
1215
|
-
if (command.args.length > 0) {
|
|
1216
|
-
logError(`Run 'edsger --help' to see available commands.`);
|
|
1217
|
-
exitWithError(new Error(`Unknown command: ${command.args[0]}`));
|
|
1218
|
-
return;
|
|
1219
|
-
}
|
|
1220
|
-
try {
|
|
1221
|
-
await runEdsger(options);
|
|
1222
|
-
}
|
|
1223
|
-
catch (error) {
|
|
1224
|
-
exitWithError(error);
|
|
1225
|
-
}
|
|
1346
|
+
skills.command("list").description("show which edsger skills are installed").option("-C, --cwd <dir>", "project directory").action(async (opts) => {
|
|
1347
|
+
await guard(() => runSkillsList({ cwd: resolve2(opts.cwd ?? process.cwd()), global: true }));
|
|
1226
1348
|
});
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
}
|
|
1243
|
-
// Handle code review mode
|
|
1244
|
-
if (options.review || options.staged || options.files) {
|
|
1245
|
-
await runCodeReview(options);
|
|
1246
|
-
return;
|
|
1247
|
-
}
|
|
1248
|
-
// Handle legacy product-level workflow mode
|
|
1249
|
-
if (options.productLevel) {
|
|
1250
|
-
logInfo('Running in legacy product-level mode...');
|
|
1251
|
-
await runWorkflow(options);
|
|
1252
|
-
return;
|
|
1253
|
-
}
|
|
1254
|
-
// Auto-detect: if EDSGER_PRODUCT_ID is set, use legacy mode for backward compatibility
|
|
1255
|
-
if (process.env.EDSGER_PRODUCT_ID) {
|
|
1256
|
-
logInfo('EDSGER_PRODUCT_ID detected. Using legacy product-level mode. Use `edsger login` for the new cross-product mode.');
|
|
1257
|
-
await runWorkflow(options);
|
|
1258
|
-
return;
|
|
1259
|
-
}
|
|
1260
|
-
// Default: new agent workflow mode (cross-product)
|
|
1261
|
-
await runAgentWorkflow(options);
|
|
1262
|
-
};
|
|
1263
|
-
// Only parse when this file is run directly (not imported as module)
|
|
1264
|
-
// Check if this is the main module being executed
|
|
1265
|
-
const isMainModule = import.meta.url === `file://${process.argv[1]}` ||
|
|
1266
|
-
process.argv[1]?.endsWith('/edsger') ||
|
|
1267
|
-
process.argv[1]?.endsWith('\\edsger') ||
|
|
1268
|
-
process.argv[1]?.endsWith('index.js');
|
|
1269
|
-
if (isMainModule) {
|
|
1270
|
-
program.parse();
|
|
1271
|
-
}
|
|
1349
|
+
program.addHelpText(
|
|
1350
|
+
"after",
|
|
1351
|
+
`
|
|
1352
|
+
Examples:
|
|
1353
|
+
${pc6.dim("$")} edsger benchmark
|
|
1354
|
+
${pc6.dim("$")} edsger benchmark -C ./my-repo --json
|
|
1355
|
+
${pc6.dim("$")} edsger pr-review 123 --post
|
|
1356
|
+
${pc6.dim("$")} edsger pr-resolve 123 --instruction "fix the failing test" --push
|
|
1357
|
+
${pc6.dim("$")} edsger skills install ${pc6.dim("# enable /benchmark etc. in Claude Code")}
|
|
1358
|
+
|
|
1359
|
+
Auth: set ANTHROPIC_API_KEY for the agent; run \`gh auth login\` for PR commands.
|
|
1360
|
+
`
|
|
1361
|
+
);
|
|
1362
|
+
program.parseAsync(process.argv);
|
|
1363
|
+
//# sourceMappingURL=index.js.map
|