clckernel 1.2.13 → 1.2.18
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/.agents/skills/clckernel_cli/SKILL.md +46 -0
- package/.agents/skills/clckernel_cli/reference/audit.md +16 -0
- package/.agents/skills/clckernel_cli/reference/commit.md +23 -0
- package/.agents/skills/clckernel_cli/reference/doctor.md +10 -0
- package/.agents/skills/clckernel_cli/reference/feature.md +38 -0
- package/.agents/skills/clckernel_cli/reference/fix.md +24 -0
- package/.agents/skills/clckernel_cli/reference/guard.md +23 -0
- package/.agents/skills/clckernel_cli/reference/phase.md +18 -0
- package/.agents/skills/clckernel_cli/reference/start.md +28 -0
- package/.agents/skills/clckernel_cli/reference/test.md +22 -0
- package/.agents/skills/clckernel_cli/reference/update.md +33 -0
- package/README.es.md +86 -0
- package/README.md +86 -0
- package/package.json +2 -1
- package/src/adapters/agent-os.js +11 -11
- package/src/catalog.js +45 -29
- package/src/config.js +1 -1
- package/src/generator.js +86 -22
- package/src/index.js +92 -7
- package/tools/audit.js +197 -38
- package/tools/audit.py +151 -25
- package/tools/check_custom.js +1 -1
- package/tools/check_hitl.js +16 -4
- /package/{src → tools}/yaml.js +0 -0
package/tools/audit.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Modular Audit Orchestrator — JS
|
|
4
4
|
* Discovers check_*.js guard scripts at runtime, runs each with graceful
|
|
5
|
-
* degradation,
|
|
5
|
+
* degradation, categorizes results into the 4 Canonical RDD Lenses,
|
|
6
|
+
* and generates both an English audit report (05-audit-report.md) and
|
|
7
|
+
* an immutable, machine-readable audit receipt (audit-receipt.json).
|
|
6
8
|
* Self-contained: uses only Node.js built-ins.
|
|
7
9
|
*/
|
|
8
10
|
|
|
@@ -11,10 +13,59 @@ const path = require('path');
|
|
|
11
13
|
|
|
12
14
|
const REPO_ROOT = path.resolve(__dirname, '..');
|
|
13
15
|
|
|
16
|
+
// ── 4 Canonical RDD Lenses Mapping ──────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
const LENS_MAP = {
|
|
19
|
+
// Correctness
|
|
20
|
+
verify_tdd: 'correctness',
|
|
21
|
+
check_scope: 'correctness',
|
|
22
|
+
check_a11y: 'correctness',
|
|
23
|
+
check_api_contracts: 'correctness',
|
|
24
|
+
check_openapi_drift: 'correctness',
|
|
25
|
+
pytest: 'correctness',
|
|
26
|
+
tsc: 'correctness',
|
|
27
|
+
lint: 'correctness',
|
|
28
|
+
ruff: 'correctness',
|
|
29
|
+
// Security
|
|
30
|
+
scan_secrets: 'security',
|
|
31
|
+
docker_guard: 'security',
|
|
32
|
+
redis_guard: 'security',
|
|
33
|
+
celery_guard: 'security',
|
|
34
|
+
postgres_guard: 'security',
|
|
35
|
+
// Architecture
|
|
36
|
+
check_architecture: 'architecture',
|
|
37
|
+
check_custom: 'architecture',
|
|
38
|
+
check_ui_reuse: 'architecture',
|
|
39
|
+
check_symlinks: 'architecture',
|
|
40
|
+
check_hitl: 'architecture',
|
|
41
|
+
check_domain_data: 'architecture',
|
|
42
|
+
check_migrations: 'architecture',
|
|
43
|
+
graphify: 'architecture',
|
|
44
|
+
gentle_ai_review: 'architecture',
|
|
45
|
+
// Performance
|
|
46
|
+
check_db_efficiency: 'performance',
|
|
47
|
+
check_performance: 'performance',
|
|
48
|
+
check_seo: 'performance',
|
|
49
|
+
check_responsive: 'performance',
|
|
50
|
+
check_storybook: 'performance',
|
|
51
|
+
check_test_calendar_sync: 'performance',
|
|
52
|
+
check_observability: 'performance',
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
function getLensForGuard(guardName) {
|
|
56
|
+
try {
|
|
57
|
+
const { lensFor } = require('../src/catalog');
|
|
58
|
+
const lens = lensFor(guardName);
|
|
59
|
+
if (lens) return lens;
|
|
60
|
+
} catch {}
|
|
61
|
+
return LENS_MAP[guardName] || 'architecture';
|
|
62
|
+
}
|
|
63
|
+
|
|
14
64
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
15
65
|
|
|
16
|
-
function getActiveSddDir() {
|
|
17
|
-
const
|
|
66
|
+
function getActiveSddDir(baseDir) {
|
|
67
|
+
const root = baseDir || REPO_ROOT;
|
|
68
|
+
const sddsDir = path.join(root, 'sdds');
|
|
18
69
|
if (!fs.existsSync(sddsDir)) {
|
|
19
70
|
fs.mkdirSync(sddsDir, { recursive: true });
|
|
20
71
|
}
|
|
@@ -25,7 +76,7 @@ function getActiveSddDir() {
|
|
|
25
76
|
if (dirs.length > 0) {
|
|
26
77
|
return path.join(sddsDir, dirs[0]);
|
|
27
78
|
}
|
|
28
|
-
const defaultDir = path.join(sddsDir, '
|
|
79
|
+
const defaultDir = path.join(sddsDir, 'audit-run');
|
|
29
80
|
fs.mkdirSync(defaultDir, { recursive: true });
|
|
30
81
|
return defaultDir;
|
|
31
82
|
}
|
|
@@ -34,8 +85,9 @@ function getActiveSddDir() {
|
|
|
34
85
|
* Discover all check_*.js guard scripts in the tools/ directory.
|
|
35
86
|
* Returns absolute paths sorted alphabetically for deterministic ordering.
|
|
36
87
|
*/
|
|
37
|
-
function discoverGuards() {
|
|
38
|
-
const toolsDir = __dirname;
|
|
88
|
+
function discoverGuards(toolsDirParam) {
|
|
89
|
+
const toolsDir = toolsDirParam || __dirname;
|
|
90
|
+
if (!fs.existsSync(toolsDir)) return [];
|
|
39
91
|
return fs.readdirSync(toolsDir)
|
|
40
92
|
.filter(f => (f.startsWith('check_') || f.startsWith('scan_')) && f.endsWith('.js') && f !== path.basename(__filename || 'audit.js'))
|
|
41
93
|
.sort()
|
|
@@ -50,12 +102,13 @@ function discoverGuards() {
|
|
|
50
102
|
*/
|
|
51
103
|
async function runGuard(guardPath, targetDir) {
|
|
52
104
|
const guardName = path.basename(guardPath, '.js');
|
|
105
|
+
const lens = getLensForGuard(guardName);
|
|
53
106
|
try {
|
|
54
107
|
const mod = require(guardPath);
|
|
55
108
|
const fn = typeof mod === 'function' ? mod : Object.values(mod)[0];
|
|
56
109
|
if (typeof fn !== 'function') {
|
|
57
110
|
return {
|
|
58
|
-
exitCode: 0, skipped: true, name: guardName,
|
|
111
|
+
exitCode: 0, skipped: true, name: guardName, lens,
|
|
59
112
|
data: { message: `Guard ${guardName} does not export a function` },
|
|
60
113
|
};
|
|
61
114
|
}
|
|
@@ -65,11 +118,12 @@ async function runGuard(guardPath, targetDir) {
|
|
|
65
118
|
exitCode: result.exitCode || 0,
|
|
66
119
|
skipped: Boolean(result.skipped),
|
|
67
120
|
name: result.name || guardName,
|
|
121
|
+
lens,
|
|
68
122
|
data: result.data || result,
|
|
69
123
|
};
|
|
70
124
|
} catch (e) {
|
|
71
125
|
return {
|
|
72
|
-
exitCode: 0, skipped: true, name: guardName,
|
|
126
|
+
exitCode: 0, skipped: true, name: guardName, lens,
|
|
73
127
|
data: { message: `Guard unavailable: ${e.message}` },
|
|
74
128
|
};
|
|
75
129
|
}
|
|
@@ -84,8 +138,9 @@ function formatGuardSection(name, result) {
|
|
|
84
138
|
const lines = [];
|
|
85
139
|
const icon = result.skipped ? '⏭️' : result.exitCode === 0 ? '✅' : '❌';
|
|
86
140
|
const status = result.skipped ? 'SKIPPED' : result.exitCode === 0 ? 'PASSED' : 'FAILED';
|
|
141
|
+
const lensBadge = (result.lens || 'architecture').toUpperCase();
|
|
87
142
|
|
|
88
|
-
lines.push(`### ${icon} ${name.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase())} — ${status}`);
|
|
143
|
+
lines.push(`### ${icon} ${name.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase())} [${lensBadge}] — ${status}`);
|
|
89
144
|
lines.push('');
|
|
90
145
|
|
|
91
146
|
if (result.skipped) {
|
|
@@ -96,15 +151,21 @@ function formatGuardSection(name, result) {
|
|
|
96
151
|
lines.push(`> ${result.data?.message || 'Issues found'}`);
|
|
97
152
|
lines.push('');
|
|
98
153
|
const violations = result.data?.violations || result.data?.findings || {};
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
154
|
+
if (Array.isArray(violations)) {
|
|
155
|
+
for (const item of violations) {
|
|
156
|
+
lines.push(`- ${item.file ? `\`${item.file}\`: ` : ''}${item.message || item.reason || JSON.stringify(item)}`);
|
|
157
|
+
}
|
|
158
|
+
} else {
|
|
159
|
+
const files = Object.keys(violations);
|
|
160
|
+
if (files.length > 0) {
|
|
161
|
+
for (const file of files) {
|
|
162
|
+
const items = violations[file];
|
|
163
|
+
for (const item of items) {
|
|
164
|
+
const loc = item.lineNo ? ` (line ${item.lineNo})` : '';
|
|
165
|
+
const label = item.label || item.rule || item.module || '';
|
|
166
|
+
const msg = item.message || item.reason || '';
|
|
167
|
+
lines.push(`- \`${file}\`${loc}: ${label ? `[${label}] ` : ''}${msg}`);
|
|
168
|
+
}
|
|
108
169
|
}
|
|
109
170
|
}
|
|
110
171
|
}
|
|
@@ -127,8 +188,8 @@ function generateReport(results, sddDir) {
|
|
|
127
188
|
|
|
128
189
|
sections.push(`# Code Audit Report: ${taskName}`);
|
|
129
190
|
sections.push('');
|
|
130
|
-
sections.push('> **
|
|
131
|
-
sections.push('>
|
|
191
|
+
sections.push('> **Review-Driven Development (RDD) Report:**');
|
|
192
|
+
sections.push('> Segmented across the 4 Canonical Review Lenses (Correctness, Security, Architecture, Performance).');
|
|
132
193
|
sections.push('');
|
|
133
194
|
sections.push('---');
|
|
134
195
|
sections.push('');
|
|
@@ -144,9 +205,26 @@ function generateReport(results, sddDir) {
|
|
|
144
205
|
sections.push('---');
|
|
145
206
|
sections.push('');
|
|
146
207
|
|
|
147
|
-
|
|
148
|
-
|
|
208
|
+
const lenses = ['correctness', 'security', 'architecture', 'performance'];
|
|
209
|
+
const lensHeaders = {
|
|
210
|
+
correctness: '## 🎯 Correctness Lens',
|
|
211
|
+
security: '## 🛡️ Security Lens',
|
|
212
|
+
architecture: '## 🏛️ Architecture Lens',
|
|
213
|
+
performance: '## ⚡ Performance Lens',
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
for (const lens of lenses) {
|
|
217
|
+
sections.push(lensHeaders[lens]);
|
|
149
218
|
sections.push('');
|
|
219
|
+
const lensResults = results.filter(r => r.lens === lens);
|
|
220
|
+
if (lensResults.length === 0) {
|
|
221
|
+
sections.push('_No guards registered for this lens._\n');
|
|
222
|
+
} else {
|
|
223
|
+
for (const result of lensResults) {
|
|
224
|
+
sections.push(formatGuardSection(result.name, result));
|
|
225
|
+
sections.push('');
|
|
226
|
+
}
|
|
227
|
+
}
|
|
150
228
|
sections.push('---');
|
|
151
229
|
sections.push('');
|
|
152
230
|
}
|
|
@@ -154,33 +232,107 @@ function generateReport(results, sddDir) {
|
|
|
154
232
|
return sections.join('\n');
|
|
155
233
|
}
|
|
156
234
|
|
|
235
|
+
/**
|
|
236
|
+
* Generate an immutable machine-readable audit receipt (audit-receipt.json).
|
|
237
|
+
*/
|
|
238
|
+
function generateReceipt(results, targetDir) {
|
|
239
|
+
const lenses = ['correctness', 'security', 'architecture', 'performance'];
|
|
240
|
+
const lensSummary = {};
|
|
241
|
+
|
|
242
|
+
let hasFailure = false;
|
|
243
|
+
for (const lens of lenses) {
|
|
244
|
+
const lensResults = results.filter(r => r.lens === lens);
|
|
245
|
+
const passed = lensResults.filter(r => !r.skipped && r.exitCode === 0).length;
|
|
246
|
+
const failed = lensResults.filter(r => !r.skipped && r.exitCode !== 0).length;
|
|
247
|
+
const skipped = lensResults.filter(r => r.skipped).length;
|
|
248
|
+
if (failed > 0) hasFailure = true;
|
|
249
|
+
|
|
250
|
+
lensSummary[lens] = {
|
|
251
|
+
status: failed > 0 ? 'FAILED' : 'PASSED',
|
|
252
|
+
passed,
|
|
253
|
+
failed,
|
|
254
|
+
skipped,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
let commitHash = 'UNKNOWN';
|
|
259
|
+
let gentleAiDetected = false;
|
|
260
|
+
try {
|
|
261
|
+
const { execSync } = require('child_process');
|
|
262
|
+
commitHash = execSync('git rev-parse HEAD', { cwd: targetDir, stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf-8' }).trim();
|
|
263
|
+
} catch {}
|
|
264
|
+
|
|
265
|
+
try {
|
|
266
|
+
const { execSync } = require('child_process');
|
|
267
|
+
execSync('which gentle-ai', { stdio: 'ignore' });
|
|
268
|
+
gentleAiDetected = true;
|
|
269
|
+
} catch {}
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
$schema: 'https://clckernel.dev/schemas/audit-receipt.v1.json',
|
|
273
|
+
contract: 'clckernel.audit-receipt/v1',
|
|
274
|
+
timestamp: new Date().toISOString(),
|
|
275
|
+
commitHash,
|
|
276
|
+
verdict: hasFailure ? 'FAILED' : 'PASSED',
|
|
277
|
+
lenses: lensSummary,
|
|
278
|
+
generator: 'clckernel',
|
|
279
|
+
gentle_ai: {
|
|
280
|
+
compatible: true,
|
|
281
|
+
rdd_ready: true,
|
|
282
|
+
cli_installed: gentleAiDetected,
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
157
287
|
// ── Console Summary ──────────────────────────────────────────────────────────
|
|
158
288
|
|
|
159
|
-
function printSummary(results, reportPath) {
|
|
289
|
+
function printSummary(results, reportPath, receiptPath) {
|
|
160
290
|
console.log('\n=================================================================');
|
|
161
|
-
console.log(' CLC
|
|
291
|
+
console.log(' CLC KERNEL — 4-LENS RDD AUDIT SUMMARY');
|
|
162
292
|
console.log('=================================================================\n');
|
|
163
293
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
294
|
+
const lenses = ['correctness', 'security', 'architecture', 'performance'];
|
|
295
|
+
const lensLabels = {
|
|
296
|
+
correctness: '🎯 CORRECTNESS LENS',
|
|
297
|
+
security: '🛡️ SECURITY LENS',
|
|
298
|
+
architecture: '🏛️ ARCHITECTURE LENS',
|
|
299
|
+
performance: '⚡ PERFORMANCE LENS',
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
for (const lens of lenses) {
|
|
303
|
+
const lensResults = results.filter(r => r.lens === lens);
|
|
304
|
+
console.log(` ${lensLabels[lens]}:`);
|
|
305
|
+
if (lensResults.length === 0) {
|
|
306
|
+
console.log(' — No guards active for this lens');
|
|
307
|
+
} else {
|
|
308
|
+
for (const result of lensResults) {
|
|
309
|
+
const icon = result.skipped ? '⏭️' : result.exitCode === 0 ? '✅' : '❌';
|
|
310
|
+
const label = result.name.replace(/_/g, ' ');
|
|
311
|
+
const msg = result.data?.message || (result.exitCode === 0 ? 'Passed' : 'Failed');
|
|
312
|
+
console.log(` ${icon} ${label}: ${msg}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
console.log('');
|
|
169
316
|
}
|
|
170
317
|
|
|
171
|
-
console.log(
|
|
318
|
+
console.log(`📄 Full report: ${reportPath}`);
|
|
319
|
+
if (receiptPath) {
|
|
320
|
+
console.log(`🧾 Audit receipt: ${receiptPath}`);
|
|
321
|
+
}
|
|
172
322
|
console.log('=================================================================\n');
|
|
173
323
|
}
|
|
174
324
|
|
|
175
325
|
// ── Main ─────────────────────────────────────────────────────────────────────
|
|
176
326
|
|
|
177
327
|
async function main(targetDir) {
|
|
178
|
-
const dir = targetDir
|
|
179
|
-
const sddDir = getActiveSddDir();
|
|
328
|
+
const dir = targetDir ? path.resolve(targetDir) : process.cwd();
|
|
329
|
+
const sddDir = getActiveSddDir(dir);
|
|
180
330
|
|
|
181
|
-
console.log('\nRunning CLC
|
|
331
|
+
console.log('\nRunning CLC Kernel audit...\n');
|
|
182
332
|
|
|
183
|
-
const
|
|
333
|
+
const targetTools = path.join(dir, 'tools');
|
|
334
|
+
const toolsDir = fs.existsSync(targetTools) ? targetTools : __dirname;
|
|
335
|
+
const guardPaths = discoverGuards(toolsDir);
|
|
184
336
|
|
|
185
337
|
if (guardPaths.length === 0) {
|
|
186
338
|
console.log('No guard tools found in tools/. Nothing to audit.');
|
|
@@ -200,20 +352,27 @@ async function main(targetDir) {
|
|
|
200
352
|
const reportPath = path.join(sddDir, '05-audit-report.md');
|
|
201
353
|
fs.writeFileSync(reportPath, reportContent, 'utf-8');
|
|
202
354
|
|
|
355
|
+
// Generate receipt
|
|
356
|
+
const receipt = generateReceipt(results, dir);
|
|
357
|
+
const receiptPath = path.join(sddDir, 'audit-receipt.json');
|
|
358
|
+
fs.writeFileSync(receiptPath, JSON.stringify(receipt, null, 2) + '\n', 'utf-8');
|
|
359
|
+
|
|
203
360
|
// Print summary
|
|
204
|
-
printSummary(results, reportPath);
|
|
361
|
+
printSummary(results, reportPath, receiptPath);
|
|
205
362
|
|
|
206
363
|
// Exit with failure if any guard failed (not skipped)
|
|
207
364
|
const hasFailure = results.some(r => !r.skipped && r.exitCode !== 0);
|
|
208
|
-
|
|
365
|
+
if (hasFailure) {
|
|
366
|
+
process.exit(1);
|
|
367
|
+
}
|
|
209
368
|
}
|
|
210
369
|
|
|
211
370
|
// Support both require() and direct execution
|
|
212
371
|
if (require.main === module) {
|
|
213
372
|
main(process.argv[2]).catch(err => {
|
|
214
373
|
console.error(`Audit orchestrator error: ${err.message}`);
|
|
215
|
-
process.exit(
|
|
374
|
+
process.exit(1);
|
|
216
375
|
});
|
|
217
376
|
}
|
|
218
377
|
|
|
219
|
-
module.exports = { main, getActiveSddDir, discoverGuards, runGuard };
|
|
378
|
+
module.exports = { main, getActiveSddDir, discoverGuards, runGuard, generateReceipt };
|
package/tools/audit.py
CHANGED
|
@@ -17,6 +17,48 @@ import traceback
|
|
|
17
17
|
|
|
18
18
|
REPO_ROOT = pathlib.Path(__file__).resolve().parent.parent
|
|
19
19
|
|
|
20
|
+
# ── 4 Canonical RDD Lenses Mapping ──────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
LENS_MAP = {
|
|
23
|
+
# Correctness
|
|
24
|
+
"verify_tdd": "correctness",
|
|
25
|
+
"check_scope": "correctness",
|
|
26
|
+
"check_a11y": "correctness",
|
|
27
|
+
"check_api_contracts": "correctness",
|
|
28
|
+
"check_openapi_drift": "correctness",
|
|
29
|
+
"pytest": "correctness",
|
|
30
|
+
"tsc": "correctness",
|
|
31
|
+
"lint": "correctness",
|
|
32
|
+
"ruff": "correctness",
|
|
33
|
+
# Security
|
|
34
|
+
"scan_secrets": "security",
|
|
35
|
+
"docker_guard": "security",
|
|
36
|
+
"redis_guard": "security",
|
|
37
|
+
"celery_guard": "security",
|
|
38
|
+
"postgres_guard": "security",
|
|
39
|
+
# Architecture
|
|
40
|
+
"check_architecture": "architecture",
|
|
41
|
+
"check_custom": "architecture",
|
|
42
|
+
"check_ui_reuse": "architecture",
|
|
43
|
+
"check_symlinks": "architecture",
|
|
44
|
+
"check_hitl": "architecture",
|
|
45
|
+
"check_domain_data": "architecture",
|
|
46
|
+
"check_migrations": "architecture",
|
|
47
|
+
"graphify": "architecture",
|
|
48
|
+
"gentle_ai_review": "architecture",
|
|
49
|
+
# Performance
|
|
50
|
+
"check_db_efficiency": "performance",
|
|
51
|
+
"check_performance": "performance",
|
|
52
|
+
"check_seo": "performance",
|
|
53
|
+
"check_responsive": "performance",
|
|
54
|
+
"check_storybook": "performance",
|
|
55
|
+
"check_test_calendar_sync": "performance",
|
|
56
|
+
"check_observability": "performance",
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
def get_lens(guard_name: str) -> str:
|
|
60
|
+
return LENS_MAP.get(guard_name, "architecture")
|
|
61
|
+
|
|
20
62
|
|
|
21
63
|
# ── Helpers ──────────────────────────────────────────────────────────────────
|
|
22
64
|
|
|
@@ -87,12 +129,14 @@ def run_guard(guard_path: pathlib.Path, target_dir: str) -> dict:
|
|
|
87
129
|
"exit_code": result.get("exit_code", 0),
|
|
88
130
|
"skipped": bool(result.get("skipped", False)),
|
|
89
131
|
"name": result.get("name", guard_name),
|
|
132
|
+
"lens": get_lens(guard_name),
|
|
90
133
|
"data": result.get("data", result),
|
|
91
134
|
}
|
|
92
135
|
|
|
93
136
|
except Exception as exc:
|
|
94
137
|
return {
|
|
95
138
|
"exit_code": 0, "skipped": True, "name": guard_name,
|
|
139
|
+
"lens": get_lens(guard_name),
|
|
96
140
|
"data": {"message": f"Guard unavailable: {exc}"},
|
|
97
141
|
}
|
|
98
142
|
|
|
@@ -151,7 +195,7 @@ def _format_guard_section(name: str, result: dict) -> str:
|
|
|
151
195
|
|
|
152
196
|
|
|
153
197
|
def generate_report(results: list[dict], sdd_dir: pathlib.Path) -> str:
|
|
154
|
-
"""Generate the 05-audit-report.md from aggregated guard results."""
|
|
198
|
+
"""Generate the 05-audit-report.md from aggregated guard results grouped by 4 RDD lenses."""
|
|
155
199
|
task_name = sdd_dir.name
|
|
156
200
|
passed = sum(1 for r in results if not r.get("skipped") and r.get("exit_code") == 0)
|
|
157
201
|
failed = sum(1 for r in results if not r.get("skipped") and r.get("exit_code") != 0)
|
|
@@ -161,8 +205,8 @@ def generate_report(results: list[dict], sdd_dir: pathlib.Path) -> str:
|
|
|
161
205
|
sections: list[str] = []
|
|
162
206
|
sections.append(f"# Code Audit Report: {task_name}")
|
|
163
207
|
sections.append("")
|
|
164
|
-
sections.append("> **
|
|
165
|
-
sections.append(">
|
|
208
|
+
sections.append("> **Review-Driven Development (RDD) Report:**")
|
|
209
|
+
sections.append("> Segmented across the 4 Canonical Review Lenses (Correctness, Security, Architecture, Performance).")
|
|
166
210
|
sections.append("")
|
|
167
211
|
sections.append("---")
|
|
168
212
|
sections.append("")
|
|
@@ -178,39 +222,116 @@ def generate_report(results: list[dict], sdd_dir: pathlib.Path) -> str:
|
|
|
178
222
|
sections.append("---")
|
|
179
223
|
sections.append("")
|
|
180
224
|
|
|
181
|
-
|
|
182
|
-
|
|
225
|
+
lenses = ["correctness", "security", "architecture", "performance"]
|
|
226
|
+
headers = {
|
|
227
|
+
"correctness": "## 🎯 Correctness Lens",
|
|
228
|
+
"security": "## 🛡️ Security Lens",
|
|
229
|
+
"architecture": "## 🏛️ Architecture Lens",
|
|
230
|
+
"performance": "## ⚡ Performance Lens",
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
for lens in lenses:
|
|
234
|
+
sections.append(headers[lens])
|
|
183
235
|
sections.append("")
|
|
236
|
+
lens_results = [r for r in results if r.get("lens") == lens]
|
|
237
|
+
if not lens_results:
|
|
238
|
+
sections.append("_No guards registered for this lens._\n")
|
|
239
|
+
else:
|
|
240
|
+
for result in lens_results:
|
|
241
|
+
sections.append(_format_guard_section(result.get("name", "unknown"), result))
|
|
242
|
+
sections.append("")
|
|
184
243
|
sections.append("---")
|
|
185
244
|
sections.append("")
|
|
186
245
|
|
|
187
246
|
return "\n".join(sections)
|
|
188
247
|
|
|
189
248
|
|
|
249
|
+
def generate_receipt(results: list[dict], target_dir: str) -> dict:
|
|
250
|
+
"""Generate an immutable machine-readable audit receipt (audit-receipt.json)."""
|
|
251
|
+
import datetime
|
|
252
|
+
|
|
253
|
+
lenses = ["correctness", "security", "architecture", "performance"]
|
|
254
|
+
lens_summary = {}
|
|
255
|
+
has_failure = False
|
|
256
|
+
|
|
257
|
+
for lens in lenses:
|
|
258
|
+
lens_results = [r for r in results if r.get("lens") == lens]
|
|
259
|
+
passed = sum(1 for r in lens_results if not r.get("skipped") and r.get("exit_code") == 0)
|
|
260
|
+
failed = sum(1 for r in lens_results if not r.get("skipped") and r.get("exit_code") != 0)
|
|
261
|
+
skipped = sum(1 for r in lens_results if r.get("skipped"))
|
|
262
|
+
if failed > 0:
|
|
263
|
+
has_failure = True
|
|
264
|
+
lens_summary[lens] = {
|
|
265
|
+
"status": "FAILED" if failed > 0 else "PASSED",
|
|
266
|
+
"passed": passed,
|
|
267
|
+
"failed": failed,
|
|
268
|
+
"skipped": skipped,
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
commit_hash = "UNKNOWN"
|
|
272
|
+
try:
|
|
273
|
+
commit_hash = subprocess.check_output(
|
|
274
|
+
["git", "rev-parse", "HEAD"],
|
|
275
|
+
cwd=target_dir,
|
|
276
|
+
stderr=subprocess.DEVNULL,
|
|
277
|
+
encoding="utf-8"
|
|
278
|
+
).strip()
|
|
279
|
+
except Exception:
|
|
280
|
+
pass
|
|
281
|
+
|
|
282
|
+
import shutil
|
|
283
|
+
gentle_ai_detected = shutil.which("gentle-ai") is not None
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
"$schema": "https://clckernel.dev/schemas/audit-receipt.v1.json",
|
|
287
|
+
"contract": "clckernel.audit-receipt/v1",
|
|
288
|
+
"timestamp": datetime.datetime.now(datetime.timezone.utc).isoformat(),
|
|
289
|
+
"commitHash": commit_hash,
|
|
290
|
+
"verdict": "FAILED" if has_failure else "PASSED",
|
|
291
|
+
"lenses": lens_summary,
|
|
292
|
+
"generator": "clckernel",
|
|
293
|
+
"gentle_ai": {
|
|
294
|
+
"compatible": True,
|
|
295
|
+
"rdd_ready": True,
|
|
296
|
+
"cli_installed": gentle_ai_detected,
|
|
297
|
+
},
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
|
|
190
301
|
# ── Console Summary ──────────────────────────────────────────────────────────
|
|
191
302
|
|
|
192
|
-
def print_summary(results: list[dict], report_path: pathlib.Path) -> None:
|
|
303
|
+
def print_summary(results: list[dict], report_path: pathlib.Path, receipt_path: pathlib.Path | None = None) -> None:
|
|
193
304
|
"""Print an educational summary to the terminal."""
|
|
194
305
|
print("\n" + "=" * 65)
|
|
195
|
-
print(" CLC
|
|
306
|
+
print(" CLC KERNEL — 4-LENS RDD AUDIT SUMMARY")
|
|
196
307
|
print("=" * 65 + "\n")
|
|
197
308
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
309
|
+
lenses = ["correctness", "security", "architecture", "performance"]
|
|
310
|
+
labels = {
|
|
311
|
+
"correctness": "🎯 CORRECTNESS LENS",
|
|
312
|
+
"security": "🛡️ SECURITY LENS",
|
|
313
|
+
"architecture": "🏛️ ARCHITECTURE LENS",
|
|
314
|
+
"performance": "⚡ PERFORMANCE LENS",
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
for lens in lenses:
|
|
318
|
+
lens_results = [r for r in results if r.get("lens") == lens]
|
|
319
|
+
print(f" {labels[lens]}:")
|
|
320
|
+
if not lens_results:
|
|
321
|
+
print(" — No guards active for this lens")
|
|
208
322
|
else:
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
323
|
+
for result in lens_results:
|
|
324
|
+
skipped = result.get("skipped", False)
|
|
325
|
+
exit_code = result.get("exit_code", 0)
|
|
326
|
+
name = result.get("name", "unknown").replace("_", " ")
|
|
327
|
+
msg = result.get("data", {}).get("message", "Passed" if exit_code == 0 else "Failed")
|
|
328
|
+
icon = "⏭️" if skipped else ("✅" if exit_code == 0 else "❌")
|
|
329
|
+
print(f" {icon} {name}: {msg}")
|
|
330
|
+
print("")
|
|
331
|
+
|
|
332
|
+
print(f"📄 Full report: {report_path}")
|
|
333
|
+
if receipt_path:
|
|
334
|
+
print(f"🧾 Audit receipt: {receipt_path}")
|
|
214
335
|
print("=" * 65 + "\n")
|
|
215
336
|
|
|
216
337
|
|
|
@@ -229,7 +350,7 @@ def main(target_dir: str | None = None) -> int:
|
|
|
229
350
|
sdd_dir = sdds_dir / "audit-run"
|
|
230
351
|
sdd_dir.mkdir(parents=True, exist_ok=True)
|
|
231
352
|
|
|
232
|
-
print(f"\nRunning CLC
|
|
353
|
+
print(f"\nRunning CLC Kernel audit for: {sdd_dir.name}\n")
|
|
233
354
|
|
|
234
355
|
guard_paths = discover_guards(tools_dir)
|
|
235
356
|
|
|
@@ -253,8 +374,13 @@ def main(target_dir: str | None = None) -> int:
|
|
|
253
374
|
report_path = sdd_dir / "05-audit-report.md"
|
|
254
375
|
report_path.write_text(report_content, encoding="utf-8")
|
|
255
376
|
|
|
377
|
+
# Generate and write receipt
|
|
378
|
+
receipt = generate_receipt(results, dir_path)
|
|
379
|
+
receipt_path = sdd_dir / "audit-receipt.json"
|
|
380
|
+
receipt_path.write_text(json.dumps(receipt, indent=2) + "\n", encoding="utf-8")
|
|
381
|
+
|
|
256
382
|
# Print summary
|
|
257
|
-
print_summary(results, report_path)
|
|
383
|
+
print_summary(results, report_path, receipt_path)
|
|
258
384
|
|
|
259
385
|
# Exit with failure if any guard failed (not skipped)
|
|
260
386
|
has_failure = any(
|
|
@@ -270,4 +396,4 @@ if __name__ == "__main__":
|
|
|
270
396
|
sys.exit(main(target))
|
|
271
397
|
except Exception:
|
|
272
398
|
traceback.print_exc()
|
|
273
|
-
sys.exit(
|
|
399
|
+
sys.exit(1)
|
package/tools/check_custom.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @param {string} targetDir - Directory to scan (defaults to cwd)
|
|
8
8
|
* @returns {{ exitCode: number, skipped: boolean, name: string, data: object }}
|
|
9
9
|
*/
|
|
10
|
-
const { parseYaml, globToRegex } = require('
|
|
10
|
+
const { parseYaml, globToRegex } = require('./yaml.js');
|
|
11
11
|
const check = async function checkCustom(targetDir) {
|
|
12
12
|
const fs = require('fs');
|
|
13
13
|
const path = require('path');
|
package/tools/check_hitl.js
CHANGED
|
@@ -22,10 +22,22 @@ const check = async function checkHitl(targetDir) {
|
|
|
22
22
|
for (const sDir of searchDirs) {
|
|
23
23
|
if (fs.existsSync(sDir)) {
|
|
24
24
|
try {
|
|
25
|
-
const
|
|
26
|
-
for (const
|
|
27
|
-
if (
|
|
28
|
-
skillFiles.push(path.join(sDir,
|
|
25
|
+
const entries = fs.readdirSync(sDir, { withFileTypes: true });
|
|
26
|
+
for (const entry of entries) {
|
|
27
|
+
if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
28
|
+
skillFiles.push(path.join(sDir, entry.name));
|
|
29
|
+
} else if (entry.isDirectory()) {
|
|
30
|
+
// Exclude framework CLI scaffolding and tool plugins
|
|
31
|
+
if (entry.name === 'clckernel_cli' || entry.name === 'clc_forge_cli' || entry.name === 'impeccable') {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const subDirPath = path.join(sDir, entry.name);
|
|
35
|
+
const subEntries = fs.readdirSync(subDirPath, { withFileTypes: true });
|
|
36
|
+
for (const sub of subEntries) {
|
|
37
|
+
if (sub.isFile() && sub.name.endsWith('.md')) {
|
|
38
|
+
skillFiles.push(path.join(subDirPath, sub.name));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
29
41
|
}
|
|
30
42
|
}
|
|
31
43
|
} catch {}
|
/package/{src → tools}/yaml.js
RENAMED
|
File without changes
|