greenbump 0.2.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/README.md +2 -0
  3. package/action-dist/index.cjs +1054 -19
  4. package/competitive-analysis.md +304 -0
  5. package/dist/cli.js +8 -0
  6. package/dist/cli.js.map +1 -1
  7. package/dist/engine/change-detector.js +152 -0
  8. package/dist/engine/change-detector.js.map +1 -0
  9. package/dist/engine/dep-graph.js +101 -0
  10. package/dist/engine/dep-graph.js.map +1 -0
  11. package/dist/engine/git.js +10 -0
  12. package/dist/engine/git.js.map +1 -1
  13. package/dist/engine/perf/metrics.js +171 -0
  14. package/dist/engine/perf/metrics.js.map +1 -0
  15. package/dist/engine/perf/regression.js +115 -0
  16. package/dist/engine/perf/regression.js.map +1 -0
  17. package/dist/engine/run.js +71 -2
  18. package/dist/engine/run.js.map +1 -1
  19. package/dist/engine/sandbox/docker-manager.js +215 -0
  20. package/dist/engine/sandbox/docker-manager.js.map +1 -0
  21. package/dist/engine/sandbox/orchestrator.js +107 -0
  22. package/dist/engine/sandbox/orchestrator.js.map +1 -0
  23. package/dist/engine/sandbox/service-detector.js +147 -0
  24. package/dist/engine/sandbox/service-detector.js.map +1 -0
  25. package/dist/engine/sandbox/template-generator.js +241 -0
  26. package/dist/engine/sandbox/template-generator.js.map +1 -0
  27. package/dist/engine/staged-fix.js +108 -0
  28. package/dist/engine/staged-fix.js.map +1 -0
  29. package/dist/engine/stages.js +125 -0
  30. package/dist/engine/stages.js.map +1 -0
  31. package/dist/engine/verify.js +121 -0
  32. package/dist/engine/verify.js.map +1 -0
  33. package/dist/format.js +20 -0
  34. package/dist/format.js.map +1 -1
  35. package/optimization-roadmap.md +772 -0
  36. package/package.json +4 -1
@@ -0,0 +1,107 @@
1
+ import { join } from "path";
2
+ import { writeFile } from "fs/promises";
3
+ import { checkDockerAvailable, buildDockerImage, runVerificationInContainer, startComposeServices, waitForServices, cleanupDocker, } from "./docker-manager.js";
4
+ import { detectProjectType, generateDockerfile, generateComposeFile, } from "./template-generator.js";
5
+ import { detectRequiredServices, getServiceDisplayName, } from "./service-detector.js";
6
+ /**
7
+ * Run verification in isolated Docker sandbox
8
+ */
9
+ export async function runInSandbox(cwd, options) {
10
+ // Check if Docker is available
11
+ const dockerInfo = await checkDockerAvailable();
12
+ if (!dockerInfo.available) {
13
+ console.warn("⚠️ Docker not available, skipping sandbox verification");
14
+ console.warn(" Install Docker for isolated testing: https://docs.docker.com/get-docker/");
15
+ return {
16
+ passed: false,
17
+ buildSuccess: false,
18
+ testsPassed: false,
19
+ services: [],
20
+ logs: "",
21
+ exitCode: -1,
22
+ skipped: true,
23
+ skipReason: "docker_unavailable",
24
+ };
25
+ }
26
+ console.log("🐳 Starting sandbox environment...");
27
+ console.log(` Docker version: ${dockerInfo.version}`);
28
+ // Detect project type
29
+ const projectType = await detectProjectType(cwd);
30
+ console.log(` Project type: ${projectType}`);
31
+ // Detect or use specified services
32
+ const services = options.services || await detectRequiredServices(cwd);
33
+ if (services.length > 0) {
34
+ console.log(` Required services: ${services.map(getServiceDisplayName).join(", ")}`);
35
+ }
36
+ const imageName = options.dockerImage || "greenbump-verify";
37
+ const dockerfilePath = join(cwd, ".greenbump-Dockerfile");
38
+ const composeFilePath = join(cwd, ".greenbump-compose.yml");
39
+ let projectName;
40
+ const serviceHealths = [];
41
+ try {
42
+ // Generate Dockerfile
43
+ const dockerfile = await generateDockerfile(projectType, cwd);
44
+ await writeFile(dockerfilePath, dockerfile);
45
+ // Generate docker-compose.yml if services needed
46
+ let composeFile = "";
47
+ if (services.length > 0) {
48
+ composeFile = await generateComposeFile(services);
49
+ if (composeFile) {
50
+ await writeFile(composeFilePath, composeFile);
51
+ }
52
+ }
53
+ // Build Docker image
54
+ await buildDockerImage(cwd, imageName, dockerfilePath);
55
+ // Start services if needed
56
+ if (services.length > 0 && composeFile && dockerInfo.composeAvailable) {
57
+ projectName = await startComposeServices(cwd, composeFilePath);
58
+ // Wait for services to be healthy
59
+ await waitForServices(projectName, services, options.timeout ? options.timeout / 1000 : 60);
60
+ // Record service health
61
+ for (const service of services) {
62
+ serviceHealths.push({
63
+ name: service,
64
+ healthy: true,
65
+ displayName: getServiceDisplayName(service),
66
+ });
67
+ }
68
+ }
69
+ else if (services.length > 0 && !dockerInfo.composeAvailable) {
70
+ console.warn("⚠️ docker-compose not available, skipping service startup");
71
+ }
72
+ // Run verification in container
73
+ console.log("🔍 Running verification in container...");
74
+ const networkMode = projectName ? `${projectName}_default` : "bridge";
75
+ const result = await runVerificationInContainer(cwd, {
76
+ image: imageName,
77
+ networkMode,
78
+ timeout: options.timeout,
79
+ });
80
+ // Display results
81
+ if (result.exitCode === 0) {
82
+ console.log("✅ Verification passed in sandbox");
83
+ }
84
+ else {
85
+ console.error("❌ Verification failed in sandbox");
86
+ console.error(result.stderr);
87
+ }
88
+ return {
89
+ passed: result.exitCode === 0,
90
+ buildSuccess: result.buildSuccess,
91
+ testsPassed: result.testsPassed,
92
+ services: serviceHealths,
93
+ logs: result.stdout + "\n" + result.stderr,
94
+ exitCode: result.exitCode,
95
+ };
96
+ }
97
+ finally {
98
+ // Cleanup unless keepContainer is true
99
+ if (!options.keepContainer) {
100
+ await cleanupDocker(cwd, imageName, composeFilePath, projectName);
101
+ }
102
+ else {
103
+ console.log("🔧 Container kept for debugging (use --keep-container=false to cleanup)");
104
+ }
105
+ }
106
+ }
107
+ //# sourceMappingURL=orchestrator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../../src/engine/sandbox/orchestrator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,147 @@
1
+ import { existsSync } from "fs";
2
+ import { join } from "path";
3
+ import { readFile } from "fs/promises";
4
+ /**
5
+ * Detect required database/cache services for the project
6
+ */
7
+ export async function detectRequiredServices(cwd) {
8
+ const services = new Set();
9
+ // Check package.json dependencies
10
+ await detectFromPackageJson(cwd, services);
11
+ // Check docker-compose files
12
+ await detectFromDockerCompose(cwd, services);
13
+ // Check environment files
14
+ await detectFromEnvFiles(cwd, services);
15
+ return Array.from(services);
16
+ }
17
+ /**
18
+ * Detect services from package.json dependencies
19
+ */
20
+ async function detectFromPackageJson(cwd, services) {
21
+ const pkgPath = join(cwd, "package.json");
22
+ if (!existsSync(pkgPath))
23
+ return;
24
+ try {
25
+ const content = await readFile(pkgPath, "utf-8");
26
+ const pkg = JSON.parse(content);
27
+ const allDeps = {
28
+ ...pkg.dependencies,
29
+ ...pkg.devDependencies,
30
+ };
31
+ // PostgreSQL
32
+ if (allDeps.pg || allDeps["pg-promise"] || allDeps.postgres || allDeps.sequelize) {
33
+ services.add("postgres");
34
+ }
35
+ // MySQL
36
+ if (allDeps.mysql || allDeps.mysql2) {
37
+ services.add("mysql");
38
+ }
39
+ // Redis
40
+ if (allDeps.redis || allDeps.ioredis) {
41
+ services.add("redis");
42
+ }
43
+ // MongoDB
44
+ if (allDeps.mongodb || allDeps.mongoose) {
45
+ services.add("mongodb");
46
+ }
47
+ }
48
+ catch {
49
+ // Ignore errors reading package.json
50
+ }
51
+ }
52
+ /**
53
+ * Detect services from docker-compose files
54
+ */
55
+ async function detectFromDockerCompose(cwd, services) {
56
+ const composeFiles = [
57
+ "docker-compose.yml",
58
+ "docker-compose.yaml",
59
+ "docker-compose.test.yml",
60
+ "docker-compose.test.yaml",
61
+ ];
62
+ for (const file of composeFiles) {
63
+ const composePath = join(cwd, file);
64
+ if (!existsSync(composePath))
65
+ continue;
66
+ try {
67
+ const content = await readFile(composePath, "utf-8");
68
+ // Simple text search for service images
69
+ if (/image:\s*postgres/i.test(content)) {
70
+ services.add("postgres");
71
+ }
72
+ if (/image:\s*mysql/i.test(content)) {
73
+ services.add("mysql");
74
+ }
75
+ if (/image:\s*redis/i.test(content)) {
76
+ services.add("redis");
77
+ }
78
+ if (/image:\s*mongo/i.test(content)) {
79
+ services.add("mongodb");
80
+ }
81
+ }
82
+ catch {
83
+ // Ignore errors reading compose files
84
+ }
85
+ }
86
+ }
87
+ /**
88
+ * Detect services from .env files
89
+ */
90
+ async function detectFromEnvFiles(cwd, services) {
91
+ const envFiles = [
92
+ ".env",
93
+ ".env.test",
94
+ ".env.local",
95
+ ".env.development",
96
+ ];
97
+ for (const file of envFiles) {
98
+ const envPath = join(cwd, file);
99
+ if (!existsSync(envPath))
100
+ continue;
101
+ try {
102
+ const content = await readFile(envPath, "utf-8");
103
+ // Check for database connection strings
104
+ if (/postgres:\/\//i.test(content) || /postgresql:\/\//i.test(content)) {
105
+ services.add("postgres");
106
+ }
107
+ if (/mysql:\/\//i.test(content)) {
108
+ services.add("mysql");
109
+ }
110
+ if (/redis:\/\//i.test(content)) {
111
+ services.add("redis");
112
+ }
113
+ if (/mongodb:\/\//i.test(content)) {
114
+ services.add("mongodb");
115
+ }
116
+ // Check for database host environment variables
117
+ if (/POSTGRES_HOST|PGHOST|DATABASE_URL.*postgres/i.test(content)) {
118
+ services.add("postgres");
119
+ }
120
+ if (/MYSQL_HOST|DATABASE_URL.*mysql/i.test(content)) {
121
+ services.add("mysql");
122
+ }
123
+ if (/REDIS_HOST|REDIS_URL/i.test(content)) {
124
+ services.add("redis");
125
+ }
126
+ if (/MONGO_HOST|MONGODB_URL|MONGO_URL/i.test(content)) {
127
+ services.add("mongodb");
128
+ }
129
+ }
130
+ catch {
131
+ // Ignore errors reading env files
132
+ }
133
+ }
134
+ }
135
+ /**
136
+ * Get display name for service
137
+ */
138
+ export function getServiceDisplayName(service) {
139
+ const names = {
140
+ postgres: "PostgreSQL",
141
+ mysql: "MySQL",
142
+ redis: "Redis",
143
+ mongodb: "MongoDB",
144
+ };
145
+ return names[service] || service;
146
+ }
147
+ //# sourceMappingURL=service-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-detector.js","sourceRoot":"","sources":["../../../src/engine/sandbox/service-detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,241 @@
1
+ import { existsSync } from "fs";
2
+ import { join } from "path";
3
+ import { readFile } from "fs/promises";
4
+ /**
5
+ * Detect project type based on files present
6
+ */
7
+ export async function detectProjectType(cwd) {
8
+ if (existsSync(join(cwd, "package.json"))) {
9
+ return "nodejs";
10
+ }
11
+ if (existsSync(join(cwd, "requirements.txt")) ||
12
+ existsSync(join(cwd, "pyproject.toml")) ||
13
+ existsSync(join(cwd, "setup.py"))) {
14
+ return "python";
15
+ }
16
+ return "unknown";
17
+ }
18
+ /**
19
+ * Generate Dockerfile for project
20
+ */
21
+ export async function generateDockerfile(projectType, cwd) {
22
+ if (projectType === "nodejs") {
23
+ return await generateNodeJsDockerfile(cwd);
24
+ }
25
+ if (projectType === "python") {
26
+ return await generatePythonDockerfile(cwd);
27
+ }
28
+ // Fallback to Node.js
29
+ return await generateNodeJsDockerfile(cwd);
30
+ }
31
+ /**
32
+ * Generate Node.js Dockerfile
33
+ */
34
+ async function generateNodeJsDockerfile(cwd) {
35
+ const hasPnpm = existsSync(join(cwd, "pnpm-lock.yaml"));
36
+ const hasYarn = existsSync(join(cwd, "yarn.lock"));
37
+ let installCmd = "npm ci";
38
+ let copyLockFile = "COPY package*.json ./";
39
+ if (hasPnpm) {
40
+ installCmd = "corepack enable && pnpm install --frozen-lockfile";
41
+ copyLockFile = "COPY package*.json pnpm-lock.yaml ./";
42
+ }
43
+ else if (hasYarn) {
44
+ installCmd = "yarn install --frozen-lockfile";
45
+ copyLockFile = "COPY package*.json yarn.lock ./";
46
+ }
47
+ return `FROM node:20-slim
48
+
49
+ WORKDIR /workspace
50
+
51
+ # Copy package files
52
+ ${copyLockFile}
53
+
54
+ # Install dependencies
55
+ RUN ${installCmd}
56
+
57
+ # Copy source code
58
+ COPY . .
59
+
60
+ # Build if build script exists
61
+ RUN if grep -q '"build":' package.json; then npm run build || true; fi
62
+
63
+ # Default command: run tests
64
+ CMD ["npm", "test"]
65
+ `;
66
+ }
67
+ /**
68
+ * Generate Python Dockerfile
69
+ */
70
+ async function generatePythonDockerfile(cwd) {
71
+ const hasRequirements = existsSync(join(cwd, "requirements.txt"));
72
+ const hasPyproject = existsSync(join(cwd, "pyproject.toml"));
73
+ let installCmd = "";
74
+ let copyFiles = "";
75
+ if (hasRequirements) {
76
+ copyFiles = "COPY requirements*.txt ./";
77
+ installCmd = "pip install --no-cache-dir -r requirements.txt";
78
+ }
79
+ else if (hasPyproject) {
80
+ copyFiles = "COPY pyproject.toml setup.py* ./";
81
+ installCmd = "pip install --no-cache-dir -e .";
82
+ }
83
+ return `FROM python:3.11-slim
84
+
85
+ WORKDIR /workspace
86
+
87
+ # Copy dependency files
88
+ ${copyFiles}
89
+
90
+ # Install dependencies
91
+ RUN ${installCmd}
92
+
93
+ # Copy source code
94
+ COPY . .
95
+
96
+ # Default command: run tests
97
+ CMD ["pytest"]
98
+ `;
99
+ }
100
+ /**
101
+ * Generate docker-compose.yml for services
102
+ */
103
+ export async function generateComposeFile(services) {
104
+ const serviceConfigs = services.map(getServiceConfig).filter((config) => config !== null);
105
+ if (serviceConfigs.length === 0) {
106
+ return "";
107
+ }
108
+ const servicesYaml = serviceConfigs.map(formatServiceConfig).join("\n");
109
+ return `version: '3.8'
110
+
111
+ services:
112
+ ${servicesYaml}
113
+
114
+ networks:
115
+ default:
116
+ name: greenbump_network
117
+ `;
118
+ }
119
+ /**
120
+ * Get service configuration
121
+ */
122
+ function getServiceConfig(serviceName) {
123
+ const configs = {
124
+ postgres: {
125
+ name: "postgres",
126
+ image: "postgres:16-alpine",
127
+ ports: ["5432:5432"],
128
+ environment: {
129
+ POSTGRES_USER: "test",
130
+ POSTGRES_PASSWORD: "test",
131
+ POSTGRES_DB: "test",
132
+ },
133
+ healthcheck: {
134
+ test: ["CMD-SHELL", "pg_isready -U test"],
135
+ interval: "5s",
136
+ timeout: "5s",
137
+ retries: 5,
138
+ },
139
+ },
140
+ mysql: {
141
+ name: "mysql",
142
+ image: "mysql:8",
143
+ ports: ["3306:3306"],
144
+ environment: {
145
+ MYSQL_ROOT_PASSWORD: "test",
146
+ MYSQL_DATABASE: "test",
147
+ },
148
+ healthcheck: {
149
+ test: ["CMD", "mysqladmin", "ping", "-h", "localhost"],
150
+ interval: "5s",
151
+ timeout: "5s",
152
+ retries: 5,
153
+ },
154
+ },
155
+ redis: {
156
+ name: "redis",
157
+ image: "redis:7-alpine",
158
+ ports: ["6379:6379"],
159
+ healthcheck: {
160
+ test: ["CMD", "redis-cli", "ping"],
161
+ interval: "5s",
162
+ timeout: "5s",
163
+ retries: 5,
164
+ },
165
+ },
166
+ mongodb: {
167
+ name: "mongodb",
168
+ image: "mongo:7",
169
+ ports: ["27017:27017"],
170
+ environment: {
171
+ MONGO_INITDB_ROOT_USERNAME: "test",
172
+ MONGO_INITDB_ROOT_PASSWORD: "test",
173
+ },
174
+ healthcheck: {
175
+ test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"],
176
+ interval: "5s",
177
+ timeout: "5s",
178
+ retries: 5,
179
+ },
180
+ },
181
+ };
182
+ return configs[serviceName] || null;
183
+ }
184
+ /**
185
+ * Format service config as YAML
186
+ */
187
+ function formatServiceConfig(config) {
188
+ const lines = [` ${config.name}:`];
189
+ lines.push(` image: ${config.image}`);
190
+ if (config.environment) {
191
+ lines.push(" environment:");
192
+ for (const [key, value] of Object.entries(config.environment)) {
193
+ lines.push(` ${key}: ${value}`);
194
+ }
195
+ }
196
+ if (config.ports.length > 0) {
197
+ lines.push(" ports:");
198
+ for (const port of config.ports) {
199
+ lines.push(` - "${port}"`);
200
+ }
201
+ }
202
+ lines.push(" healthcheck:");
203
+ lines.push(` test: ${JSON.stringify(config.healthcheck.test)}`);
204
+ lines.push(` interval: ${config.healthcheck.interval}`);
205
+ lines.push(` timeout: ${config.healthcheck.timeout}`);
206
+ lines.push(` retries: ${config.healthcheck.retries}`);
207
+ return lines.join("\n");
208
+ }
209
+ /**
210
+ * Check if project has build script
211
+ */
212
+ export async function hasBuildScript(cwd) {
213
+ const pkgPath = join(cwd, "package.json");
214
+ if (!existsSync(pkgPath))
215
+ return false;
216
+ try {
217
+ const content = await readFile(pkgPath, "utf-8");
218
+ const pkg = JSON.parse(content);
219
+ return pkg.scripts?.build !== undefined;
220
+ }
221
+ catch {
222
+ return false;
223
+ }
224
+ }
225
+ /**
226
+ * Check if project has test script
227
+ */
228
+ export async function hasTestScript(cwd) {
229
+ const pkgPath = join(cwd, "package.json");
230
+ if (!existsSync(pkgPath))
231
+ return false;
232
+ try {
233
+ const content = await readFile(pkgPath, "utf-8");
234
+ const pkg = JSON.parse(content);
235
+ return pkg.scripts?.test !== undefined;
236
+ }
237
+ catch {
238
+ return false;
239
+ }
240
+ }
241
+ //# sourceMappingURL=template-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-generator.js","sourceRoot":"","sources":["../../../src/engine/sandbox/template-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Staged fix orchestrator for v0.4.0
3
+ *
4
+ * This module provides a multi-stage fix strategy that:
5
+ * 1. Builds a dependency graph to identify affected files
6
+ * 2. Categorizes files into stages (config → types → source)
7
+ * 3. Fixes each stage independently with incremental commits
8
+ * 4. Allows rollback to the last successful stage on failure
9
+ *
10
+ * Usage: pass --staged flag to enable staged fix mode
11
+ */
12
+ import { buildDependencyGraph, organizeLayers } from "./dep-graph.js";
13
+ import { COMMON_STAGES, categorizeFilesByStage } from "./stages.js";
14
+ import { commitStage } from "./git.js";
15
+ import { runFixLoop } from "../agent/fixer.js";
16
+ import { runChecks } from "./checks.js";
17
+ /**
18
+ * Run a staged fix: break the fix into multiple stages, fix each independently,
19
+ * and commit after each successful stage.
20
+ */
21
+ export async function runStagedFix(opts) {
22
+ const log = opts.onLog ?? (() => { });
23
+ const { cwd, pm, dep } = opts;
24
+ // 1. Build dependency graph
25
+ log("📊 building dependency graph…");
26
+ const nodes = await buildDependencyGraph(cwd, dep);
27
+ if (nodes.length === 0) {
28
+ log("⚠️ dependency graph empty, falling back to single-stage fix");
29
+ return await runSingleStageFix(opts);
30
+ }
31
+ const layers = organizeLayers(nodes);
32
+ log(`📊 found ${layers.length} dependency layers, ${nodes.length} files total`);
33
+ // 2. Categorize files by stage
34
+ const stageFiles = await categorizeFilesByStage(cwd, COMMON_STAGES);
35
+ const stages = COMMON_STAGES.filter((stage) => {
36
+ const files = stageFiles.get(stage.name) || [];
37
+ return files.length > 0;
38
+ });
39
+ if (stages.length === 0) {
40
+ log("⚠️ no files matched any stage patterns, falling back to single-stage fix");
41
+ return await runSingleStageFix(opts);
42
+ }
43
+ log(`🔧 identified ${stages.length} stages: ${stages.map(s => s.name).join(" → ")}`);
44
+ // 3. Fix each stage
45
+ let totalRounds = 0;
46
+ let totalInputTokens = 0;
47
+ let totalOutputTokens = 0;
48
+ const allEditedFiles = [];
49
+ for (let i = 0; i < stages.length; i++) {
50
+ const stage = stages[i];
51
+ const files = stageFiles.get(stage.name) || [];
52
+ log(`\n🔧 Stage ${i + 1}/${stages.length}: ${stage.name} (${files.length} files)`);
53
+ // Run fix loop for this stage only
54
+ // (In a full implementation, we'd pass a file filter to the fix loop)
55
+ const stageResult = await runFixLoop({
56
+ ...opts,
57
+ onLog: (msg) => log(` ${msg}`),
58
+ });
59
+ totalRounds += stageResult.rounds;
60
+ totalInputTokens += stageResult.usage.inputTokens;
61
+ totalOutputTokens += stageResult.usage.outputTokens;
62
+ allEditedFiles.push(...stageResult.editedFiles);
63
+ // Validate stage
64
+ const passed = await stage.validator(cwd);
65
+ if (!passed) {
66
+ log(`❌ Stage ${i + 1} validation failed`);
67
+ return {
68
+ fixed: false,
69
+ completedStages: i,
70
+ totalStages: stages.length,
71
+ failedStage: stage.name,
72
+ rounds: totalRounds,
73
+ usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens },
74
+ editedFiles: allEditedFiles,
75
+ budgetExceeded: stageResult.budgetExceeded,
76
+ };
77
+ }
78
+ // Commit this stage
79
+ await commitStage(cwd, stage.name, i + 1, stages.length);
80
+ log(`✓ Stage ${i + 1} completed and committed`);
81
+ }
82
+ // Final validation
83
+ const finalCheck = await runChecks(pm, cwd, opts.checkOverrides);
84
+ return {
85
+ fixed: finalCheck.ok,
86
+ completedStages: stages.length,
87
+ totalStages: stages.length,
88
+ rounds: totalRounds,
89
+ usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens },
90
+ editedFiles: allEditedFiles,
91
+ };
92
+ }
93
+ /**
94
+ * Fallback: run a single fix loop without staging (same as v0.3.0 behavior)
95
+ */
96
+ async function runSingleStageFix(opts) {
97
+ const result = await runFixLoop(opts);
98
+ return {
99
+ fixed: result.fixed,
100
+ completedStages: result.fixed ? 1 : 0,
101
+ totalStages: 1,
102
+ rounds: result.rounds,
103
+ usage: result.usage,
104
+ editedFiles: result.editedFiles,
105
+ budgetExceeded: result.budgetExceeded,
106
+ };
107
+ }
108
+ //# sourceMappingURL=staged-fix.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staged-fix.js","sourceRoot":"","sources":["../../src/engine/staged-fix.ts"],"names":[],"mappings":"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"}