@chemx/starter-kit 26.9.25-943 → 26.9.26-253
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/cli/audit/prompts.js +1 -1
- package/cli/commands/cmd-router.js +20 -2
- package/cli/commands-schema.js +65 -6
- package/cli/generator-help.js +26 -6
- package/cli/generator-jig-cli.js +67 -0
- package/cli/generator-jig.js +479 -0
- package/cli/generator-jig.spec.js +148 -0
- package/cli/generator.js +15 -2
- package/cli/index.js +4 -1
- package/cli/installer-templates.js +8 -3
- package/cli/mcp/manifests.js +22 -4
- package/cli/mcp/tools-generate.js +22 -2
- package/cli/mcp/tools-read.js +50 -8
- package/cli/mcp/tools-verify.js +2 -0
- package/cli/mcp/tools.js +52 -3
- package/cli/project-detector.js +105 -2
- package/cli/reader-cli.js +146 -0
- package/cli/reader-logic.js +392 -0
- package/cli/reader-logic.spec.js +160 -0
- package/cli/reader.js +78 -171
- package/cli/search-commands-graph.js +125 -1
- package/cli/search-commands.js +7 -1
- package/cli/search-db.js +2 -0
- package/cli/search-queries-graph.js +294 -0
- package/cli/search-queries.js +6 -1
- package/cli/search.js +28 -30
- package/cli/team/team-memory.js +30 -7
- package/cli/team/team-memory.spec.js +24 -0
- package/cli/tesseract.spec.js +2 -2
- package/cli/trace.spec.js +92 -0
- package/cli/verify-helpers.js +46 -3
- package/cli/verify-lint.js +224 -0
- package/cli/verify-lint.spec.js +44 -0
- package/cli/verify.js +8 -1
- package/cli/verify.spec.js +17 -0
- package/package.json +1 -1
- package/scripts/pre-commit.sh +8 -3
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { executeBuild } from './build/executor.js';
|
|
2
|
+
import { findProjectRoot } from './build/detector.js';
|
|
3
|
+
import { resolvePackageManager, loadLocalPackageJson } from './build/detector.js';
|
|
4
|
+
import { ANSI } from './theme.js';
|
|
5
|
+
import { parseCommandFromArgs, stripAnsi, checkNodeModules } from './verify-helpers.js';
|
|
6
|
+
|
|
7
|
+
export const detectLintCommand = (customCmd, cwd = process.cwd(), isFix = false, targetPath = null) => {
|
|
8
|
+
if (customCmd && customCmd.trim().length > 0) return customCmd.trim();
|
|
9
|
+
const pkg = loadLocalPackageJson(cwd);
|
|
10
|
+
const scripts = (pkg && pkg.scripts) || {};
|
|
11
|
+
const pm = resolvePackageManager(cwd);
|
|
12
|
+
|
|
13
|
+
const cleanTarget = targetPath ? targetPath.trim() : null;
|
|
14
|
+
|
|
15
|
+
if (cleanTarget) {
|
|
16
|
+
return `npx eslint ${isFix ? '--fix ' : ''}${cleanTarget}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (isFix) {
|
|
20
|
+
if (scripts['lint:fix']) return `${pm} run lint:fix`;
|
|
21
|
+
if (scripts.lint) return `${pm} run lint -- --fix`;
|
|
22
|
+
return 'npx eslint --fix .';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (scripts.lint) return `${pm} run lint`;
|
|
26
|
+
return 'npx eslint .';
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const parseLintOutput = (stdout = '', stderr = '') => {
|
|
30
|
+
const combined = `${stdout}\n${stderr}`;
|
|
31
|
+
const lines = combined.split(/\r?\n/);
|
|
32
|
+
|
|
33
|
+
const errors = [];
|
|
34
|
+
const warnings = [];
|
|
35
|
+
let currentFile = null;
|
|
36
|
+
let fixableCount = 0;
|
|
37
|
+
|
|
38
|
+
// Pattern: /path/to/file.ext or file.ext (standalone line with no leading whitespace or : )
|
|
39
|
+
const fileHeaderRegex = /^([^\s].+\.[a-zA-Z0-9]+)$/;
|
|
40
|
+
// Pattern: 156:51 error message rule-name
|
|
41
|
+
const issueRegex = /^\s*([0-9]+):([0-9]+)\s+(error|warning)\s+(.+?)\s+([a-zA-Z0-9_/@-]+)\s*$/;
|
|
42
|
+
// Pattern: fixable count
|
|
43
|
+
const fixableRegex = /([0-9]+)\s+(?:errors?|warnings?|problems?).*potentially fixable with the `--fix` option/i;
|
|
44
|
+
|
|
45
|
+
for (const rawLine of lines) {
|
|
46
|
+
const clean = stripAnsi(rawLine);
|
|
47
|
+
const trimmed = clean.trim();
|
|
48
|
+
if (!trimmed) continue;
|
|
49
|
+
|
|
50
|
+
const fixMatch = clean.match(fixableRegex);
|
|
51
|
+
if (fixMatch) {
|
|
52
|
+
fixableCount = parseInt(fixMatch[1], 10);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const fileMatch = clean.match(fileHeaderRegex);
|
|
57
|
+
if (fileMatch && !clean.includes('✖') && !clean.includes('✔') && !clean.includes('problems')) {
|
|
58
|
+
currentFile = fileMatch[1].trim();
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const issueMatch = clean.match(issueRegex);
|
|
63
|
+
if (issueMatch) {
|
|
64
|
+
const issue = {
|
|
65
|
+
file: currentFile || 'unknown',
|
|
66
|
+
line: parseInt(issueMatch[1], 10),
|
|
67
|
+
column: parseInt(issueMatch[2], 10),
|
|
68
|
+
severity: issueMatch[3].toLowerCase(),
|
|
69
|
+
message: issueMatch[4].trim(),
|
|
70
|
+
ruleId: issueMatch[5].trim()
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
if (issue.severity === 'error') {
|
|
74
|
+
errors.push(issue);
|
|
75
|
+
} else {
|
|
76
|
+
warnings.push(issue);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
errors,
|
|
83
|
+
warnings,
|
|
84
|
+
errorCount: errors.length,
|
|
85
|
+
warningCount: warnings.length,
|
|
86
|
+
fixableCount
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const runLintAudit = async (rawArgs = [], isCli = false, options = {}) => {
|
|
91
|
+
if (rawArgs.includes('--help') || rawArgs.includes('-h') || rawArgs.includes('help')) {
|
|
92
|
+
const isJson = rawArgs.includes('--json');
|
|
93
|
+
if (isJson) {
|
|
94
|
+
process.stdout.write(JSON.stringify({ help: true, success: true }) + '\n');
|
|
95
|
+
} else {
|
|
96
|
+
process.stdout.write([
|
|
97
|
+
`${ANSI.BOLD}USAGE${ANSI.RESET}`,
|
|
98
|
+
` chemx lint [path] [options] [-- <command>]`,
|
|
99
|
+
'',
|
|
100
|
+
`${ANSI.BOLD}OPTIONS${ANSI.RESET}`,
|
|
101
|
+
` --fix Automatically fix fixable lint and tree-formatting errors`,
|
|
102
|
+
` --json Output structured diagnostics as JSON for AI agents`,
|
|
103
|
+
` --raw Do not capture or format output`,
|
|
104
|
+
` -h, --help Show this help message`,
|
|
105
|
+
'',
|
|
106
|
+
`${ANSI.BOLD}EXAMPLES${ANSI.RESET}`,
|
|
107
|
+
` chemx lint`,
|
|
108
|
+
` chemx lint --fix`,
|
|
109
|
+
` chemx lint apps/youmeos/components/blueprints/desktop/u-desktop.vue --fix`,
|
|
110
|
+
''
|
|
111
|
+
].join('\n'));
|
|
112
|
+
}
|
|
113
|
+
if (isCli) process.exit(0);
|
|
114
|
+
return { help: true, success: true };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const isJson = rawArgs.includes('--json') || options.json === true;
|
|
118
|
+
const isRaw = rawArgs.includes('--raw') || options.raw === true;
|
|
119
|
+
const isFix = rawArgs.includes('--fix') || options.fix === true;
|
|
120
|
+
const customCmd = parseCommandFromArgs(rawArgs) || options.command;
|
|
121
|
+
const cwd = findProjectRoot(options.cwd || process.cwd());
|
|
122
|
+
|
|
123
|
+
// Extract optional target path argument (first non-flag argument)
|
|
124
|
+
const targetPath = rawArgs.find((arg) => !arg.startsWith('-') && arg !== 'lint' && arg !== 'check:lint' && arg !== 'eslint') || null;
|
|
125
|
+
|
|
126
|
+
const nmStatus = checkNodeModules(cwd);
|
|
127
|
+
if (nmStatus) {
|
|
128
|
+
const friendlyMsg = nmStatus.msg('linting');
|
|
129
|
+
const report = {
|
|
130
|
+
success: false,
|
|
131
|
+
exitCode: 1,
|
|
132
|
+
command: customCmd || 'lint',
|
|
133
|
+
durationMs: 0,
|
|
134
|
+
errorCount: 1,
|
|
135
|
+
warningCount: 0,
|
|
136
|
+
fixableCount: 0,
|
|
137
|
+
executionError: friendlyMsg,
|
|
138
|
+
errors: [
|
|
139
|
+
{
|
|
140
|
+
file: 'package.json',
|
|
141
|
+
line: 1,
|
|
142
|
+
column: 1,
|
|
143
|
+
severity: 'error',
|
|
144
|
+
ruleId: 'MISSING_NODE_MODULES',
|
|
145
|
+
message: friendlyMsg
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
warnings: []
|
|
149
|
+
};
|
|
150
|
+
if (isJson) {
|
|
151
|
+
if (options.print !== false) {
|
|
152
|
+
process.stdout.write(JSON.stringify(report, null, 2) + '\n');
|
|
153
|
+
}
|
|
154
|
+
if (isCli) process.exit(1);
|
|
155
|
+
return report;
|
|
156
|
+
}
|
|
157
|
+
if (options.print !== false) {
|
|
158
|
+
process.stdout.write(`\n ${ANSI.RED}✖${ANSI.RESET} ${ANSI.BOLD}${friendlyMsg}${ANSI.RESET}\n\n`);
|
|
159
|
+
}
|
|
160
|
+
if (isCli) process.exit(1);
|
|
161
|
+
return report;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const command = detectLintCommand(customCmd, cwd, isFix, targetPath);
|
|
165
|
+
const execution = await executeBuild(command, cwd, { raw: isRaw });
|
|
166
|
+
const parsed = parseLintOutput(execution.stdout, execution.stderr);
|
|
167
|
+
|
|
168
|
+
// If exitCode is non-zero but regex didn't parse items (e.g. fatal syntax error), capture raw error
|
|
169
|
+
let executionError = null;
|
|
170
|
+
if (execution.exitCode !== 0 && parsed.errors.length === 0) {
|
|
171
|
+
const rawLines = `${execution.stderr}\n${execution.stdout}`.split('\n').map((l) => l.trim()).filter(Boolean);
|
|
172
|
+
executionError = rawLines.find((l) => /error|not found|failed/i.test(l)) || rawLines[0] || `Command exited with code ${execution.exitCode}`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const isSuccess = execution.exitCode === 0 && parsed.errors.length === 0;
|
|
176
|
+
|
|
177
|
+
const report = {
|
|
178
|
+
success: isSuccess,
|
|
179
|
+
exitCode: execution.exitCode,
|
|
180
|
+
command,
|
|
181
|
+
durationMs: execution.durationMs,
|
|
182
|
+
errorCount: parsed.errorCount,
|
|
183
|
+
warningCount: parsed.warningCount,
|
|
184
|
+
fixableCount: parsed.fixableCount,
|
|
185
|
+
executionError,
|
|
186
|
+
errors: parsed.errors,
|
|
187
|
+
warnings: parsed.warnings
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
if (isJson) {
|
|
191
|
+
if (options.print !== false) {
|
|
192
|
+
process.stdout.write(JSON.stringify(report, null, 2) + '\n');
|
|
193
|
+
}
|
|
194
|
+
if (isCli) process.exit(report.success ? 0 : 1);
|
|
195
|
+
return report;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (options.print !== false) {
|
|
199
|
+
if (report.success) {
|
|
200
|
+
const warnInfo = report.warningCount > 0 ? ` with ${report.warningCount} warning(s)` : '';
|
|
201
|
+
process.stdout.write(` ${ANSI.LIME}✔${ANSI.RESET} ${ANSI.BOLD}Linter passed${warnInfo}${ANSI.RESET} ${ANSI.DIM}(in ${report.durationMs}ms)${ANSI.RESET}\n`);
|
|
202
|
+
} else {
|
|
203
|
+
if (report.executionError) {
|
|
204
|
+
process.stdout.write(`\n ${ANSI.RED}✖${ANSI.RESET} ${ANSI.BOLD}Lint Execution Error:${ANSI.RESET} ${report.executionError}\n\n`);
|
|
205
|
+
} else {
|
|
206
|
+
process.stdout.write(`\n ${ANSI.RED}✖${ANSI.RESET} ${ANSI.BOLD}Lint Failures (${report.errorCount} error${report.errorCount === 1 ? '' : 's'}${report.warningCount > 0 ? `, ${report.warningCount} warning(s)` : ''})${ANSI.RESET}\n`);
|
|
207
|
+
const displayLimit = 10;
|
|
208
|
+
for (const err of report.errors.slice(0, displayLimit)) {
|
|
209
|
+
process.stdout.write(` ${ANSI.RED}✖${ANSI.RESET} ${ANSI.BOLD}${err.file}:${err.line}:${err.column}${ANSI.RESET} ${err.message} ${ANSI.DIM}(${err.ruleId})${ANSI.RESET}\n`);
|
|
210
|
+
}
|
|
211
|
+
if (report.errors.length > displayLimit) {
|
|
212
|
+
process.stdout.write(` ${ANSI.DIM}...and ${report.errors.length - displayLimit} more error(s)${ANSI.RESET}\n`);
|
|
213
|
+
}
|
|
214
|
+
if (report.fixableCount > 0) {
|
|
215
|
+
process.stdout.write(`\n ${ANSI.CYAN}💡 ${report.fixableCount} issue(s) potentially fixable with 'chemx lint --fix'${ANSI.RESET}\n`);
|
|
216
|
+
}
|
|
217
|
+
process.stdout.write('\n');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (isCli) process.exit(report.success ? 0 : 1);
|
|
223
|
+
return report;
|
|
224
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { detectLintCommand, parseLintOutput } from './verify-lint.js';
|
|
4
|
+
|
|
5
|
+
test('VerifyLint: detectLintCommand resolves custom command when provided', () => {
|
|
6
|
+
const cmd = detectLintCommand('pnpm custom:lint');
|
|
7
|
+
assert.strictEqual(cmd, 'pnpm custom:lint');
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test('VerifyLint: detectLintCommand handles targetPath and --fix', () => {
|
|
11
|
+
const cmd = detectLintCommand(null, process.cwd(), true, 'src/test.vue');
|
|
12
|
+
assert.strictEqual(cmd, 'npx eslint --fix src/test.vue');
|
|
13
|
+
|
|
14
|
+
const cmdNoFix = detectLintCommand(null, process.cwd(), false, 'src/test.vue');
|
|
15
|
+
assert.strictEqual(cmdNoFix, 'npx eslint src/test.vue');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('VerifyLint: detectLintCommand defaults to eslint or package script', () => {
|
|
19
|
+
const cmd = detectLintCommand(null, process.cwd(), false, null);
|
|
20
|
+
assert.ok(cmd.includes('eslint') || cmd.includes('lint'));
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('VerifyLint: parseLintOutput extracts errors, warnings, and fixable count', () => {
|
|
24
|
+
const sampleOutput = `
|
|
25
|
+
/path/to/component.vue
|
|
26
|
+
12:5 error 'unused' is defined but never used no-unused-vars
|
|
27
|
+
24:10 warning Unexpected console statement no-console
|
|
28
|
+
|
|
29
|
+
✖ 2 problems (1 error, 1 warning)
|
|
30
|
+
1 error and 0 warnings potentially fixable with the \`--fix\` option.
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
const parsed = parseLintOutput(sampleOutput);
|
|
34
|
+
assert.strictEqual(parsed.errorCount, 1);
|
|
35
|
+
assert.strictEqual(parsed.warningCount, 1);
|
|
36
|
+
assert.strictEqual(parsed.fixableCount, 1);
|
|
37
|
+
assert.strictEqual(parsed.errors.length, 1);
|
|
38
|
+
assert.strictEqual(parsed.warnings.length, 1);
|
|
39
|
+
assert.strictEqual(parsed.errors[0].file, '/path/to/component.vue');
|
|
40
|
+
assert.strictEqual(parsed.errors[0].line, 12);
|
|
41
|
+
assert.strictEqual(parsed.errors[0].column, 5);
|
|
42
|
+
assert.strictEqual(parsed.errors[0].ruleId, 'no-unused-vars');
|
|
43
|
+
assert.strictEqual(parsed.warnings[0].ruleId, 'no-console');
|
|
44
|
+
});
|
package/cli/verify.js
CHANGED
|
@@ -22,6 +22,7 @@ export {
|
|
|
22
22
|
parseTypecheckOutput,
|
|
23
23
|
parseTestOutput
|
|
24
24
|
} from './verify-helpers.js';
|
|
25
|
+
export { runLintAudit } from './verify-lint.js';
|
|
25
26
|
|
|
26
27
|
export const runTypecheckAudit = async (rawArgs = [], isCli = false, options = {}) => {
|
|
27
28
|
if (rawArgs.includes('--help') || rawArgs.includes('-h') || rawArgs.includes('help')) {
|
|
@@ -161,6 +162,12 @@ export const runTestAudit = async (rawArgs = [], isCli = false, options = {}) =>
|
|
|
161
162
|
const customCmd = parseCommandFromArgs(rawArgs) || options.command;
|
|
162
163
|
const cwd = findProjectRoot(options.cwd || process.cwd());
|
|
163
164
|
|
|
165
|
+
const targetFlag = (rawArgs.find((a) => a.startsWith('--target=')) || '').replace(/^--target=/, '');
|
|
166
|
+
const filterFlag = (rawArgs.find((a) => a.startsWith('--filter=') || a.startsWith('-t=')) || '').replace(/^--(filter|t)=/, '');
|
|
167
|
+
const positionalTarget = rawArgs.find((a) => !a.startsWith('-') && !['test', 'tests', 'check:test'].includes(a) && (a.endsWith('.js') || a.endsWith('.ts') || a.includes('/')));
|
|
168
|
+
const target = options.target || targetFlag || positionalTarget || null;
|
|
169
|
+
const filter = options.filter || filterFlag || null;
|
|
170
|
+
|
|
164
171
|
const nmStatus = checkNodeModules(cwd);
|
|
165
172
|
if (nmStatus) {
|
|
166
173
|
const friendlyMsg = nmStatus.msg('testing');
|
|
@@ -190,7 +197,7 @@ export const runTestAudit = async (rawArgs = [], isCli = false, options = {}) =>
|
|
|
190
197
|
return report;
|
|
191
198
|
}
|
|
192
199
|
|
|
193
|
-
const command = detectTestCommand(customCmd, cwd);
|
|
200
|
+
const command = detectTestCommand(customCmd, cwd, { target, filter });
|
|
194
201
|
const execution = await executeBuild(command, cwd, { raw: isRaw });
|
|
195
202
|
const parsed = parseTestOutput(execution.stdout, execution.stderr, execution.exitCode);
|
|
196
203
|
|
package/cli/verify.spec.js
CHANGED
|
@@ -28,6 +28,23 @@ test('Verify: detectTestCommand resolves custom test command when provided', ()
|
|
|
28
28
|
assert.strictEqual(cmd, 'vitest run test/unit');
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
+
test('Verify: detectTestCommand formats target file and filter options correctly', () => {
|
|
32
|
+
const cmd = detectTestCommand(null, process.cwd(), {
|
|
33
|
+
target: 'cli/generator-jig.spec.js',
|
|
34
|
+
filter: 'presets'
|
|
35
|
+
});
|
|
36
|
+
assert.ok(cmd.includes('cli/generator-jig.spec.js'));
|
|
37
|
+
assert.ok(cmd.includes('presets'));
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('Verify: detectTestCommand appends target to custom command', () => {
|
|
41
|
+
const cmd = detectTestCommand('npx vitest run', process.cwd(), {
|
|
42
|
+
target: 'src/services/billing.spec.ts',
|
|
43
|
+
filter: 'charge'
|
|
44
|
+
});
|
|
45
|
+
assert.strictEqual(cmd, 'npx vitest run src/services/billing.spec.ts -t "charge"');
|
|
46
|
+
});
|
|
47
|
+
|
|
31
48
|
test('Verify: parseTypecheckOutput extracts clean structured diagnostics', () => {
|
|
32
49
|
const sampleOutput = `
|
|
33
50
|
src/components/Card.tsx(14,5): error TS2322: Type 'string' is not assignable to type 'number'.
|
package/package.json
CHANGED
package/scripts/pre-commit.sh
CHANGED
|
@@ -118,17 +118,22 @@ elif command -v npx >/dev/null 2>&1; then
|
|
|
118
118
|
fi
|
|
119
119
|
|
|
120
120
|
if [ -n "$AUDIT_BIN" ]; then
|
|
121
|
-
|
|
121
|
+
if [ "$CHEMX_VERBOSE" = "1" ]; then
|
|
122
|
+
printf "%s[Chemical X] Verifying architectural health (Min Grade: %s, Min Score: %s)...%s\n" "$C_BLUE" "$MIN_GRADE" "$MIN_SCORE" "$C_RESET"
|
|
123
|
+
fi
|
|
122
124
|
|
|
123
125
|
AUDIT_CMD="$AUDIT_BIN audit --git --min-grade=$MIN_GRADE --min-score=$MIN_SCORE --non-interactive"
|
|
124
126
|
|
|
125
|
-
if ! eval "$AUDIT_CMD < /dev/null"; then
|
|
127
|
+
if ! AUDIT_OUT=$(eval "$AUDIT_CMD < /dev/null" 2>&1); then
|
|
126
128
|
printf "\n%s%s[Chemical X] Commit Blocked: Architectural health verification failed%s\n" "$C_BOLD" "$C_RED" "$C_RESET"
|
|
129
|
+
printf "%s\n\n" "$AUDIT_OUT"
|
|
127
130
|
printf "%s💡 Tip: Want crystalline drop-in templates to refactor in minutes?%s\n" "$C_CYAN" "$C_RESET"
|
|
128
131
|
printf " Run 'npm create chemx' or sponsor at https://github.com/sponsors/Chemical-X-Protocol\n\n"
|
|
129
132
|
exit 1
|
|
130
133
|
fi
|
|
131
134
|
fi
|
|
132
135
|
|
|
133
|
-
|
|
136
|
+
if [ "$CHEMX_VERBOSE" = "1" ]; then
|
|
137
|
+
printf "%s✔ [Chemical X] Pre-commit architectural guardrails passed.%s\n" "$C_GREEN" "$C_RESET"
|
|
138
|
+
fi
|
|
134
139
|
exit 0
|