@syntesseraai/opencode-feature-factory 0.2.7 → 0.2.8
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/discovery.js +19 -16
- package/dist/discovery.test.js +2 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +53 -50
- package/dist/output.js +6 -2
- package/dist/output.test.js +30 -28
- package/dist/quality-gate-config.js +14 -6
- package/dist/quality-gate-config.test.js +24 -22
- package/dist/stop-quality-gate.d.ts +1 -1
- package/dist/stop-quality-gate.js +11 -5
- package/dist/stop-quality-gate.test.js +84 -82
- package/dist/types.js +2 -1
- package/package.json +1 -10
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Unit tests for quality-gate-config module
|
|
3
4
|
*
|
|
@@ -6,21 +7,22 @@
|
|
|
6
7
|
* - hasConfiguredCommands: checks if explicit commands are configured
|
|
7
8
|
* - DEFAULT_QUALITY_GATE: verify default values
|
|
8
9
|
*/
|
|
9
|
-
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
const quality_gate_config_1 = require("./quality-gate-config");
|
|
10
12
|
describe('DEFAULT_QUALITY_GATE', () => {
|
|
11
13
|
it('should have correct default values', () => {
|
|
12
|
-
expect(DEFAULT_QUALITY_GATE.steps).toEqual(['lint', 'build', 'test']);
|
|
13
|
-
expect(DEFAULT_QUALITY_GATE.useCiSh).toBe('auto');
|
|
14
|
-
expect(DEFAULT_QUALITY_GATE.packageManager).toBe('auto');
|
|
15
|
-
expect(DEFAULT_QUALITY_GATE.cacheSeconds).toBe(30);
|
|
16
|
-
expect(DEFAULT_QUALITY_GATE.maxOutputLines).toBe(160);
|
|
17
|
-
expect(DEFAULT_QUALITY_GATE.maxErrorLines).toBe(60);
|
|
18
|
-
expect(DEFAULT_QUALITY_GATE.include.rustClippy).toBe(true);
|
|
14
|
+
expect(quality_gate_config_1.DEFAULT_QUALITY_GATE.steps).toEqual(['lint', 'build', 'test']);
|
|
15
|
+
expect(quality_gate_config_1.DEFAULT_QUALITY_GATE.useCiSh).toBe('auto');
|
|
16
|
+
expect(quality_gate_config_1.DEFAULT_QUALITY_GATE.packageManager).toBe('auto');
|
|
17
|
+
expect(quality_gate_config_1.DEFAULT_QUALITY_GATE.cacheSeconds).toBe(30);
|
|
18
|
+
expect(quality_gate_config_1.DEFAULT_QUALITY_GATE.maxOutputLines).toBe(160);
|
|
19
|
+
expect(quality_gate_config_1.DEFAULT_QUALITY_GATE.maxErrorLines).toBe(60);
|
|
20
|
+
expect(quality_gate_config_1.DEFAULT_QUALITY_GATE.include.rustClippy).toBe(true);
|
|
19
21
|
});
|
|
20
22
|
});
|
|
21
23
|
describe('mergeQualityGateConfig', () => {
|
|
22
24
|
it('should return empty config when both inputs are undefined', () => {
|
|
23
|
-
const result = mergeQualityGateConfig(undefined, undefined);
|
|
25
|
+
const result = (0, quality_gate_config_1.mergeQualityGateConfig)(undefined, undefined);
|
|
24
26
|
expect(result).toEqual({ include: {} });
|
|
25
27
|
});
|
|
26
28
|
it('should return root config when dotOpencode is undefined', () => {
|
|
@@ -28,7 +30,7 @@ describe('mergeQualityGateConfig', () => {
|
|
|
28
30
|
lint: 'pnpm lint',
|
|
29
31
|
cacheSeconds: 60,
|
|
30
32
|
};
|
|
31
|
-
const result = mergeQualityGateConfig(root, undefined);
|
|
33
|
+
const result = (0, quality_gate_config_1.mergeQualityGateConfig)(root, undefined);
|
|
32
34
|
expect(result).toEqual({ lint: 'pnpm lint', cacheSeconds: 60, include: {} });
|
|
33
35
|
});
|
|
34
36
|
it('should return dotOpencode config when root is undefined', () => {
|
|
@@ -36,7 +38,7 @@ describe('mergeQualityGateConfig', () => {
|
|
|
36
38
|
build: 'npm run build',
|
|
37
39
|
maxOutputLines: 200,
|
|
38
40
|
};
|
|
39
|
-
const result = mergeQualityGateConfig(undefined, dotOpencode);
|
|
41
|
+
const result = (0, quality_gate_config_1.mergeQualityGateConfig)(undefined, dotOpencode);
|
|
40
42
|
expect(result).toEqual({ build: 'npm run build', maxOutputLines: 200, include: {} });
|
|
41
43
|
});
|
|
42
44
|
it('should override root values with dotOpencode values', () => {
|
|
@@ -49,7 +51,7 @@ describe('mergeQualityGateConfig', () => {
|
|
|
49
51
|
lint: 'npm run lint:strict',
|
|
50
52
|
cacheSeconds: 60,
|
|
51
53
|
};
|
|
52
|
-
const result = mergeQualityGateConfig(root, dotOpencode);
|
|
54
|
+
const result = (0, quality_gate_config_1.mergeQualityGateConfig)(root, dotOpencode);
|
|
53
55
|
expect(result.lint).toBe('npm run lint:strict');
|
|
54
56
|
expect(result.build).toBe('pnpm build');
|
|
55
57
|
expect(result.cacheSeconds).toBe(60);
|
|
@@ -65,7 +67,7 @@ describe('mergeQualityGateConfig', () => {
|
|
|
65
67
|
rustClippy: true,
|
|
66
68
|
},
|
|
67
69
|
};
|
|
68
|
-
const result = mergeQualityGateConfig(root, dotOpencode);
|
|
70
|
+
const result = (0, quality_gate_config_1.mergeQualityGateConfig)(root, dotOpencode);
|
|
69
71
|
expect(result.include?.rustClippy).toBe(true);
|
|
70
72
|
});
|
|
71
73
|
it('should preserve root include values not overridden by dotOpencode', () => {
|
|
@@ -78,7 +80,7 @@ describe('mergeQualityGateConfig', () => {
|
|
|
78
80
|
lint: 'custom lint',
|
|
79
81
|
// No include specified
|
|
80
82
|
};
|
|
81
|
-
const result = mergeQualityGateConfig(root, dotOpencode);
|
|
83
|
+
const result = (0, quality_gate_config_1.mergeQualityGateConfig)(root, dotOpencode);
|
|
82
84
|
expect(result.include?.rustClippy).toBe(true);
|
|
83
85
|
expect(result.lint).toBe('custom lint');
|
|
84
86
|
});
|
|
@@ -102,7 +104,7 @@ describe('mergeQualityGateConfig', () => {
|
|
|
102
104
|
useCiSh: 'never',
|
|
103
105
|
include: { rustClippy: true },
|
|
104
106
|
};
|
|
105
|
-
const result = mergeQualityGateConfig(root, dotOpencode);
|
|
107
|
+
const result = (0, quality_gate_config_1.mergeQualityGateConfig)(root, dotOpencode);
|
|
106
108
|
expect(result.lint).toBe('lint-override');
|
|
107
109
|
expect(result.build).toBe('build-root');
|
|
108
110
|
expect(result.test).toBe('test-root');
|
|
@@ -118,7 +120,7 @@ describe('mergeQualityGateConfig', () => {
|
|
|
118
120
|
});
|
|
119
121
|
describe('hasConfiguredCommands', () => {
|
|
120
122
|
it('should return false for empty config', () => {
|
|
121
|
-
expect(hasConfiguredCommands({})).toBe(false);
|
|
123
|
+
expect((0, quality_gate_config_1.hasConfiguredCommands)({})).toBe(false);
|
|
122
124
|
});
|
|
123
125
|
it('should return false when only non-command properties are set', () => {
|
|
124
126
|
const config = {
|
|
@@ -127,25 +129,25 @@ describe('hasConfiguredCommands', () => {
|
|
|
127
129
|
useCiSh: 'always',
|
|
128
130
|
packageManager: 'pnpm',
|
|
129
131
|
};
|
|
130
|
-
expect(hasConfiguredCommands(config)).toBe(false);
|
|
132
|
+
expect((0, quality_gate_config_1.hasConfiguredCommands)(config)).toBe(false);
|
|
131
133
|
});
|
|
132
134
|
it('should return true when lint is configured', () => {
|
|
133
135
|
const config = {
|
|
134
136
|
lint: 'pnpm lint',
|
|
135
137
|
};
|
|
136
|
-
expect(hasConfiguredCommands(config)).toBe(true);
|
|
138
|
+
expect((0, quality_gate_config_1.hasConfiguredCommands)(config)).toBe(true);
|
|
137
139
|
});
|
|
138
140
|
it('should return true when build is configured', () => {
|
|
139
141
|
const config = {
|
|
140
142
|
build: 'pnpm build',
|
|
141
143
|
};
|
|
142
|
-
expect(hasConfiguredCommands(config)).toBe(true);
|
|
144
|
+
expect((0, quality_gate_config_1.hasConfiguredCommands)(config)).toBe(true);
|
|
143
145
|
});
|
|
144
146
|
it('should return true when test is configured', () => {
|
|
145
147
|
const config = {
|
|
146
148
|
test: 'pnpm test',
|
|
147
149
|
};
|
|
148
|
-
expect(hasConfiguredCommands(config)).toBe(true);
|
|
150
|
+
expect((0, quality_gate_config_1.hasConfiguredCommands)(config)).toBe(true);
|
|
149
151
|
});
|
|
150
152
|
it('should return true when multiple commands are configured', () => {
|
|
151
153
|
const config = {
|
|
@@ -153,12 +155,12 @@ describe('hasConfiguredCommands', () => {
|
|
|
153
155
|
build: 'pnpm build',
|
|
154
156
|
test: 'pnpm test',
|
|
155
157
|
};
|
|
156
|
-
expect(hasConfiguredCommands(config)).toBe(true);
|
|
158
|
+
expect((0, quality_gate_config_1.hasConfiguredCommands)(config)).toBe(true);
|
|
157
159
|
});
|
|
158
160
|
it('should return false for empty string commands', () => {
|
|
159
161
|
const config = {
|
|
160
162
|
lint: '',
|
|
161
163
|
};
|
|
162
|
-
expect(hasConfiguredCommands(config)).toBe(false);
|
|
164
|
+
expect((0, quality_gate_config_1.hasConfiguredCommands)(config)).toBe(false);
|
|
163
165
|
});
|
|
164
166
|
});
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SECRET_PATTERNS = void 0;
|
|
4
|
+
exports.sanitizeOutput = sanitizeOutput;
|
|
5
|
+
exports.truncateOutput = truncateOutput;
|
|
6
|
+
exports.createQualityGateHooks = createQualityGateHooks;
|
|
1
7
|
const SERVICE_NAME = 'feature-factory';
|
|
2
8
|
const IDLE_DEBOUNCE_MS = 500;
|
|
3
9
|
const CI_TIMEOUT_MS = 300000; // 5 minutes
|
|
4
10
|
const SESSION_TTL_MS = 3600000; // 1 hour
|
|
5
11
|
const CLEANUP_INTERVAL_MS = 600000; // 10 minutes
|
|
6
|
-
|
|
12
|
+
exports.SECRET_PATTERNS = [
|
|
7
13
|
// AWS Access Key IDs
|
|
8
14
|
{ pattern: /AKIA[0-9A-Z]{16}/g, replacement: '[REDACTED_AWS_KEY]' },
|
|
9
15
|
// GitHub Personal Access Tokens (classic)
|
|
@@ -67,9 +73,9 @@ export const SECRET_PATTERNS = [
|
|
|
67
73
|
* Sanitizes CI output by redacting common secret patterns before sending to the LLM.
|
|
68
74
|
* This helps prevent accidental exposure of sensitive information in prompts.
|
|
69
75
|
*/
|
|
70
|
-
|
|
76
|
+
function sanitizeOutput(output) {
|
|
71
77
|
let sanitized = output;
|
|
72
|
-
for (const { pattern, replacement } of SECRET_PATTERNS) {
|
|
78
|
+
for (const { pattern, replacement } of exports.SECRET_PATTERNS) {
|
|
73
79
|
sanitized = sanitized.replace(pattern, replacement);
|
|
74
80
|
}
|
|
75
81
|
return sanitized;
|
|
@@ -78,7 +84,7 @@ export function sanitizeOutput(output) {
|
|
|
78
84
|
* Truncates CI output to the last N lines to reduce prompt size and focus on relevant errors.
|
|
79
85
|
* Adds a header indicating truncation if the output was longer than the limit.
|
|
80
86
|
*/
|
|
81
|
-
|
|
87
|
+
function truncateOutput(output, maxLines = 20) {
|
|
82
88
|
const lines = output.split('\n');
|
|
83
89
|
if (lines.length <= maxLines) {
|
|
84
90
|
return output;
|
|
@@ -154,7 +160,7 @@ async function log(client, level, message, extra) {
|
|
|
154
160
|
return undefined;
|
|
155
161
|
}
|
|
156
162
|
}
|
|
157
|
-
|
|
163
|
+
async function createQualityGateHooks(input) {
|
|
158
164
|
const { client, $, directory } = input;
|
|
159
165
|
async function ciShExists() {
|
|
160
166
|
try {
|