@yasserkhanorg/impact-gate 2.1.3 → 2.1.5
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/dist/esm/qa-agent/phase25/fix_loop.js +1 -0
- package/dist/esm/qa-agent/phase25/fix_tools.js +21 -2
- package/dist/qa-agent/phase25/fix_loop.d.ts.map +1 -1
- package/dist/qa-agent/phase25/fix_loop.js +1 -0
- package/dist/qa-agent/phase25/fix_tools.d.ts +2 -0
- package/dist/qa-agent/phase25/fix_tools.d.ts.map +1 -1
- package/dist/qa-agent/phase25/fix_tools.js +21 -2
- package/package.json +1 -1
|
@@ -94,6 +94,7 @@ export async function runFixLoop(config, findings, browser, projectRoot) {
|
|
|
94
94
|
screenshotDir,
|
|
95
95
|
screenshotCounter: 100, // Start at 100 to avoid collisions with Phase 2 screenshots
|
|
96
96
|
qaCommitHashes: new Set(),
|
|
97
|
+
pendingWrittenFiles: new Set(),
|
|
97
98
|
};
|
|
98
99
|
for (const finding of fixable) {
|
|
99
100
|
if (wtf.shouldStop()) {
|
|
@@ -159,8 +159,21 @@ export function executeFixTool(ctx, name, input) {
|
|
|
159
159
|
if (!isPathSafe(ctx.projectRoot, filePath)) {
|
|
160
160
|
return { output: `Blocked: "${filePath}" is outside the project or a restricted path.` };
|
|
161
161
|
}
|
|
162
|
+
// Refuse to overwrite files that already have uncommitted user changes
|
|
163
|
+
if (!ctx.pendingWrittenFiles.has(filePath)) {
|
|
164
|
+
try {
|
|
165
|
+
const status = execFileSync('git', ['status', '--porcelain', '--', filePath], { cwd: ctx.projectRoot, encoding: 'utf-8' }).trim();
|
|
166
|
+
if (status.length > 0) {
|
|
167
|
+
return { output: `Blocked: "${filePath}" has uncommitted user changes. Choose a different file or skip this fix.` };
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
// git status failed — allow the write (file may be new/untracked)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
162
174
|
const fullPath = resolve(ctx.projectRoot, filePath);
|
|
163
175
|
writeFileSync(fullPath, String(input.content), 'utf-8');
|
|
176
|
+
ctx.pendingWrittenFiles.add(filePath);
|
|
164
177
|
return { output: `Written: ${filePath}`, filesChanged: [filePath] };
|
|
165
178
|
}
|
|
166
179
|
case 'search_code': {
|
|
@@ -227,6 +240,7 @@ export function executeFixTool(ctx, name, input) {
|
|
|
227
240
|
execFileSync('git', ['commit', '-m', message], { cwd: ctx.projectRoot, encoding: 'utf-8' });
|
|
228
241
|
const hash = execFileSync('git', ['rev-parse', '--short', 'HEAD'], { cwd: ctx.projectRoot, encoding: 'utf-8' }).trim();
|
|
229
242
|
ctx.qaCommitHashes.add(hash);
|
|
243
|
+
ctx.pendingWrittenFiles.clear();
|
|
230
244
|
return { output: `Committed: ${hash} — ${message}`, commitHash: hash, filesChanged: files };
|
|
231
245
|
}
|
|
232
246
|
catch (err) {
|
|
@@ -252,9 +266,14 @@ export function executeFixTool(ctx, name, input) {
|
|
|
252
266
|
}
|
|
253
267
|
}
|
|
254
268
|
case 'git_restore': {
|
|
269
|
+
const filesToRestore = [...ctx.pendingWrittenFiles];
|
|
270
|
+
if (filesToRestore.length === 0) {
|
|
271
|
+
return { output: 'No pending edits to restore.' };
|
|
272
|
+
}
|
|
255
273
|
try {
|
|
256
|
-
execFileSync('git', ['checkout', '--',
|
|
257
|
-
|
|
274
|
+
execFileSync('git', ['checkout', '--', ...filesToRestore], { cwd: ctx.projectRoot, encoding: 'utf-8' });
|
|
275
|
+
ctx.pendingWrittenFiles.clear();
|
|
276
|
+
return { output: `Restored ${filesToRestore.length} file(s): ${filesToRestore.join(', ')}` };
|
|
258
277
|
}
|
|
259
278
|
catch (err) {
|
|
260
279
|
const error = err;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fix_loop.d.ts","sourceRoot":"","sources":["../../../src/qa-agent/phase25/fix_loop.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,OAAO,EAAqC,aAAa,EAAE,QAAQ,EAAC,MAAM,aAAa,CAAC;AACrG,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,4BAA4B,CAAC;AAyD7D,wBAAsB,UAAU,CAC5B,MAAM,EAAE,QAAQ,EAChB,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"fix_loop.d.ts","sourceRoot":"","sources":["../../../src/qa-agent/phase25/fix_loop.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,OAAO,EAAqC,aAAa,EAAE,QAAQ,EAAC,MAAM,aAAa,CAAC;AACrG,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,4BAA4B,CAAC;AAyD7D,wBAAsB,UAAU,CAC5B,MAAM,EAAE,QAAQ,EAChB,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,aAAa,CAAC,CA6FxB"}
|
|
@@ -100,6 +100,7 @@ async function runFixLoop(config, findings, browser, projectRoot) {
|
|
|
100
100
|
screenshotDir,
|
|
101
101
|
screenshotCounter: 100, // Start at 100 to avoid collisions with Phase 2 screenshots
|
|
102
102
|
qaCommitHashes: new Set(),
|
|
103
|
+
pendingWrittenFiles: new Set(),
|
|
103
104
|
};
|
|
104
105
|
for (const finding of fixable) {
|
|
105
106
|
if (wtf.shouldStop()) {
|
|
@@ -9,6 +9,8 @@ export interface FixToolContext {
|
|
|
9
9
|
screenshotCounter: number;
|
|
10
10
|
/** Commit hashes created by the fix loop. Only these can be reverted. */
|
|
11
11
|
qaCommitHashes: Set<string>;
|
|
12
|
+
/** Files written by the current fix attempt. Only these are restored on failure. */
|
|
13
|
+
pendingWrittenFiles: Set<string>;
|
|
12
14
|
}
|
|
13
15
|
export interface FixToolResult {
|
|
14
16
|
output: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fix_tools.d.ts","sourceRoot":"","sources":["../../../src/qa-agent/phase25/fix_tools.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAE/C,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,4BAA4B,CAAC;AAM7D,eAAO,MAAM,oBAAoB,EAAE,SAAS,CAAC,IAAI,EAgGhD,CAAC;AAMF,MAAM,WAAW,cAAc;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,yEAAyE;IACzE,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"fix_tools.d.ts","sourceRoot":"","sources":["../../../src/qa-agent/phase25/fix_tools.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAE/C,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,4BAA4B,CAAC;AAM7D,eAAO,MAAM,oBAAoB,EAAE,SAAS,CAAC,IAAI,EAgGhD,CAAC;AAMF,MAAM,WAAW,cAAc;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,yEAAyE;IACzE,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,oFAAoF;IACpF,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,aAAa,CAAC,EAAE,OAAO,CAAC;CAC3B;AA2CD,wBAAgB,cAAc,CAC1B,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,aAAa,CAmLf"}
|
|
@@ -163,8 +163,21 @@ function executeFixTool(ctx, name, input) {
|
|
|
163
163
|
if (!isPathSafe(ctx.projectRoot, filePath)) {
|
|
164
164
|
return { output: `Blocked: "${filePath}" is outside the project or a restricted path.` };
|
|
165
165
|
}
|
|
166
|
+
// Refuse to overwrite files that already have uncommitted user changes
|
|
167
|
+
if (!ctx.pendingWrittenFiles.has(filePath)) {
|
|
168
|
+
try {
|
|
169
|
+
const status = (0, child_process_1.execFileSync)('git', ['status', '--porcelain', '--', filePath], { cwd: ctx.projectRoot, encoding: 'utf-8' }).trim();
|
|
170
|
+
if (status.length > 0) {
|
|
171
|
+
return { output: `Blocked: "${filePath}" has uncommitted user changes. Choose a different file or skip this fix.` };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
// git status failed — allow the write (file may be new/untracked)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
166
178
|
const fullPath = (0, path_1.resolve)(ctx.projectRoot, filePath);
|
|
167
179
|
(0, fs_1.writeFileSync)(fullPath, String(input.content), 'utf-8');
|
|
180
|
+
ctx.pendingWrittenFiles.add(filePath);
|
|
168
181
|
return { output: `Written: ${filePath}`, filesChanged: [filePath] };
|
|
169
182
|
}
|
|
170
183
|
case 'search_code': {
|
|
@@ -231,6 +244,7 @@ function executeFixTool(ctx, name, input) {
|
|
|
231
244
|
(0, child_process_1.execFileSync)('git', ['commit', '-m', message], { cwd: ctx.projectRoot, encoding: 'utf-8' });
|
|
232
245
|
const hash = (0, child_process_1.execFileSync)('git', ['rev-parse', '--short', 'HEAD'], { cwd: ctx.projectRoot, encoding: 'utf-8' }).trim();
|
|
233
246
|
ctx.qaCommitHashes.add(hash);
|
|
247
|
+
ctx.pendingWrittenFiles.clear();
|
|
234
248
|
return { output: `Committed: ${hash} — ${message}`, commitHash: hash, filesChanged: files };
|
|
235
249
|
}
|
|
236
250
|
catch (err) {
|
|
@@ -256,9 +270,14 @@ function executeFixTool(ctx, name, input) {
|
|
|
256
270
|
}
|
|
257
271
|
}
|
|
258
272
|
case 'git_restore': {
|
|
273
|
+
const filesToRestore = [...ctx.pendingWrittenFiles];
|
|
274
|
+
if (filesToRestore.length === 0) {
|
|
275
|
+
return { output: 'No pending edits to restore.' };
|
|
276
|
+
}
|
|
259
277
|
try {
|
|
260
|
-
(0, child_process_1.execFileSync)('git', ['checkout', '--',
|
|
261
|
-
|
|
278
|
+
(0, child_process_1.execFileSync)('git', ['checkout', '--', ...filesToRestore], { cwd: ctx.projectRoot, encoding: 'utf-8' });
|
|
279
|
+
ctx.pendingWrittenFiles.clear();
|
|
280
|
+
return { output: `Restored ${filesToRestore.length} file(s): ${filesToRestore.join(', ')}` };
|
|
262
281
|
}
|
|
263
282
|
catch (err) {
|
|
264
283
|
const error = err;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yasserkhanorg/impact-gate",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"description": "Diff-aware E2E impact analysis and coverage gating for Playwright/Cypress teams. Optional AI features can suggest, generate, and heal tests once your project has a route-families.json manifest.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|