clckernel 1.2.6 → 1.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.
Files changed (45) hide show
  1. package/README.es.md +51 -118
  2. package/README.md +48 -116
  3. package/package.json +14 -2
  4. package/src/adapters/agent-os.js +183 -0
  5. package/src/catalog.js +16 -0
  6. package/src/detector.js +2 -0
  7. package/src/index.js +11 -0
  8. package/src/ui.js +79 -35
  9. package/tools/audit.js +0 -0
  10. package/tools/audit.py +0 -0
  11. package/tools/check_custom.js +0 -0
  12. package/tools/check_custom.py +0 -0
  13. package/tools/check_db_efficiency.py +0 -0
  14. package/tools/check_domain_data.js +100 -0
  15. package/tools/check_hitl.js +93 -0
  16. package/tools/check_responsive.js +0 -0
  17. package/tools/check_seo.js +1 -1
  18. package/tools/check_symlinks.js +76 -0
  19. package/tools/scan_secrets.js +12 -1
  20. package/tools/scan_secrets.py +0 -0
  21. package/AGENTS.md +0 -124
  22. package/docs/Journal/001-adr-clckernel-governance.md +0 -18
  23. package/test/adapters/astro.test.js +0 -102
  24. package/test/adapters/base.test.js +0 -86
  25. package/test/adapters/django.test.js +0 -95
  26. package/test/adapters/fastapi.test.js +0 -88
  27. package/test/adapters/go.test.js +0 -81
  28. package/test/adapters/laravel.test.js +0 -106
  29. package/test/adapters/nextjs.test.js +0 -113
  30. package/test/adapters/rails.test.js +0 -108
  31. package/test/adapters/rust.test.js +0 -83
  32. package/test/catalog.test.js +0 -170
  33. package/test/config.test.js +0 -512
  34. package/test/detector.test.js +0 -345
  35. package/test/doctor.test.js +0 -302
  36. package/test/generator.test.js +0 -419
  37. package/test/guards_new.test.js +0 -95
  38. package/test/helpers.js +0 -36
  39. package/test/integration/cli-flow.test.js +0 -270
  40. package/test/technologies/celery_guard.test.js +0 -185
  41. package/test/technologies/detector.test.js +0 -342
  42. package/test/technologies/docker_guard.test.js +0 -187
  43. package/test/technologies/integration.test.js +0 -123
  44. package/test/technologies/postgres_guard.test.js +0 -87
  45. package/test/technologies/redis_guard.test.js +0 -128
@@ -1,270 +0,0 @@
1
- const { describe, it } = require('node:test');
2
- const assert = require('node:assert');
3
- const path = require('path');
4
- const fs = require('fs');
5
- const { execSync } = require('child_process');
6
- const { createTmpProject, teardown } = require('../helpers');
7
-
8
- const CLI_PATH = path.join(__dirname, '..', '..', 'bin', 'cli.js');
9
-
10
- describe('Integration: Full CLI Flow', () => {
11
- describe('runCli() doctor command', () => {
12
- it('runs doctor and exits 0 on healthy project', () => {
13
- const dir = createTmpProject({
14
- 'AGENTS.md': '# AGENTS\n',
15
- 'sdds/.gitkeep': '',
16
- 'docs/Journal/001-adr.md': '# ADR\n',
17
- 'tools/audit.js': 'module.exports = {};\n',
18
- 'tools/check_scan.js': 'module.exports = function() {};\n',
19
- '.husky/pre-commit': '#!/usr/bin/env sh\necho test\n',
20
- });
21
- try {
22
- const result = execSync(`node "${CLI_PATH}" doctor "${dir}"`, {
23
- encoding: 'utf-8',
24
- timeout: 15000,
25
- });
26
- assert.ok(result.includes('HEALTHY'), 'doctor should report HEALTHY');
27
- assert.ok(result.includes('CLC KERNEL DOCTOR') || result.includes('CLC FORGE DOCTOR'), 'should show doctor header');
28
- assert.ok(result.includes('[PASS]'), 'should show PASS checks');
29
- } finally {
30
- teardown(dir);
31
- }
32
- });
33
-
34
- it('runs doctor and reports issues on unhealthy project', () => {
35
- const dir = createTmpProject({
36
- 'package.json': '{}',
37
- });
38
- try {
39
- const result = execSync(`node "${CLI_PATH}" doctor "${dir}"`, {
40
- encoding: 'utf-8',
41
- timeout: 15000,
42
- });
43
- assert.ok(result.includes('[FAIL]'), 'doctor should report FAIL checks');
44
- assert.ok(!result.includes('HEALTHY'), 'should not be HEALTHY');
45
- assert.ok(result.includes('issue(s) detected'), 'should report issue count');
46
- } finally {
47
- teardown(dir);
48
- }
49
- });
50
-
51
- it('resolves target directory from second argument', () => {
52
- const dir = createTmpProject({
53
- 'AGENTS.md': '# AGENTS\n',
54
- 'sdds/.gitkeep': '',
55
- 'docs/Journal/001-adr.md': '# ADR\n',
56
- 'tools/check_scan.js': 'module.exports = function() {};\n',
57
- '.husky/pre-commit': '#!/usr/bin/env sh\necho test\n',
58
- });
59
- try {
60
- const result = execSync(`node "${CLI_PATH}" doctor "${dir}"`, {
61
- encoding: 'utf-8',
62
- timeout: 15000,
63
- });
64
- // The output should show the target directory path
65
- assert.ok(result.includes(dir), 'output should show target directory');
66
- } finally {
67
- teardown(dir);
68
- }
69
- });
70
-
71
- it('uses cwd when no directory argument is provided', () => {
72
- // Create a healthy project in a temp dir and run from there
73
- const dir = createTmpProject({
74
- 'AGENTS.md': '# AGENTS\n',
75
- 'sdds/.gitkeep': '',
76
- 'docs/Journal/001-adr.md': '# ADR\n',
77
- 'tools/check_scan.js': 'module.exports = function() {};\n',
78
- '.husky/pre-commit': '#!/usr/bin/env sh\necho test\n',
79
- });
80
- try {
81
- const result = execSync(`node "${CLI_PATH}" doctor`, {
82
- encoding: 'utf-8',
83
- timeout: 15000,
84
- cwd: dir,
85
- });
86
- assert.ok(result.includes('HEALTHY'), 'doctor should use cwd');
87
- assert.ok(result.includes(dir), 'should show cwd path');
88
- } finally {
89
- teardown(dir);
90
- }
91
- });
92
- });
93
-
94
- describe('runCli() default flow (non-doctor)', () => {
95
- it('resolves target directory from first argument', () => {
96
- const dir = createTmpProject({
97
- 'package.json': JSON.stringify({ dependencies: { next: '14.0.0' } }),
98
- });
99
- try {
100
- // The default flow will try to printBanner → autoDetectStack → promptOptions
101
- // promptOptions reads stdin so it will hang — use timeout and check output
102
- try {
103
- execSync(`node "${CLI_PATH}" "${dir}"`, {
104
- encoding: 'utf-8',
105
- timeout: 3000,
106
- input: '1\n',
107
- });
108
- } catch (e) {
109
- // May exit non-zero due to prompt timeout, but we check the output
110
- const output = e.stdout || e.stderr || '';
111
- // Should at least detect the stack (Next.js)
112
- assert.ok(output.length > 0, 'should produce output');
113
- }
114
- } finally {
115
- teardown(dir);
116
- }
117
- });
118
- });
119
-
120
- describe('runCli() with --doctor flag', () => {
121
- it('accepts --doctor flag as alternative to doctor command', () => {
122
- const dir = createTmpProject({
123
- 'AGENTS.md': '# AGENTS\n',
124
- 'sdds/.gitkeep': '',
125
- 'docs/Journal/001-adr.md': '# ADR\n',
126
- 'tools/check_scan.js': 'module.exports = function() {};\n',
127
- '.husky/pre-commit': '#!/usr/bin/env sh\necho test\n',
128
- });
129
- try {
130
- const result = execSync(`node "${CLI_PATH}" --doctor "${dir}"`, {
131
- encoding: 'utf-8',
132
- timeout: 15000,
133
- });
134
- assert.ok(result.includes('HEALTHY'), '--doctor flag should run doctor');
135
- assert.ok(result.includes('CLC KERNEL DOCTOR') || result.includes('CLC FORGE DOCTOR'), 'should show doctor header');
136
- } finally {
137
- teardown(dir);
138
- }
139
- });
140
- });
141
-
142
- describe('doctor detects all hook types', () => {
143
- it('detects .githooks/pre-commit via CLI', () => {
144
- const dir = createTmpProject({
145
- 'AGENTS.md': '# AGENTS\n',
146
- 'sdds/.gitkeep': '',
147
- 'docs/Journal/001-adr.md': '# ADR\n',
148
- 'tools/check_lint.py': 'def check():\n return True\n',
149
- '.githooks/pre-commit': '#!/usr/bin/env bash\necho test\n',
150
- });
151
- try {
152
- const result = execSync(`node "${CLI_PATH}" doctor "${dir}"`, {
153
- encoding: 'utf-8',
154
- timeout: 15000,
155
- });
156
- // .githooks should satisfy the pre-commit check
157
- assert.ok(result.includes('[PASS]') && result.includes('pre-commit git hooks'), '.githooks should PASS');
158
- } finally {
159
- teardown(dir);
160
- }
161
- });
162
-
163
- it('validates Python tool parsing via CLI', () => {
164
- const dir = createTmpProject({
165
- 'AGENTS.md': '# AGENTS\n',
166
- 'sdds/.gitkeep': '',
167
- 'docs/Journal/001-adr.md': '# ADR\n',
168
- 'tools/check_syntax.py': 'x = 1\n',
169
- '.husky/pre-commit': '#!/usr/bin/env sh\necho test\n',
170
- });
171
- try {
172
- const result = execSync(`node "${CLI_PATH}" doctor "${dir}"`, {
173
- encoding: 'utf-8',
174
- timeout: 15000,
175
- });
176
- assert.ok(result.includes('tool/check_syntax.py parses'), 'should validate Python tool');
177
- assert.ok(result.includes('[PASS]') && result.includes('check_syntax.py parses'), 'valid Python should PASS');
178
- } finally {
179
- teardown(dir);
180
- }
181
- });
182
- });
183
- });
184
-
185
- describe('Integration: Component Composition', () => {
186
- it('autoDetectStack + generateHarness creates valid project structure', () => {
187
- const { autoDetectStack } = require('../../src/detector');
188
- const { generateHarness } = require('../../src/generator');
189
-
190
- const dir = createTmpProject({
191
- 'package.json': JSON.stringify({ dependencies: { next: '14.0.0' } }),
192
- });
193
- try {
194
- const config = autoDetectStack(dir);
195
- assert.strictEqual(config.framework, 'Next.js (App Router)', 'should detect Next.js');
196
-
197
- generateHarness(dir, config);
198
-
199
- // Verify all expected artifacts
200
- assert.ok(fs.existsSync(path.join(dir, 'AGENTS.md')), 'AGENTS.md should exist');
201
- assert.ok(fs.existsSync(path.join(dir, 'sdds')), 'sdds/ should exist');
202
- assert.ok(fs.existsSync(path.join(dir, 'docs', 'Journal')), 'docs/Journal/ should exist');
203
- assert.ok(fs.existsSync(path.join(dir, 'tools')), 'tools/ should exist');
204
- assert.ok(fs.existsSync(path.join(dir, '.husky', 'pre-commit')), '.husky/pre-commit should exist');
205
-
206
- // Verify AGENTS.md content
207
- const agents = fs.readFileSync(path.join(dir, 'AGENTS.md'), 'utf-8');
208
- assert.ok(agents.includes('Next.js'), 'AGENTS.md should mention Next.js');
209
- assert.ok(agents.includes('FRONTEND'), 'AGENTS.md should mention FRONTEND');
210
- } finally {
211
- teardown(dir);
212
- }
213
- });
214
-
215
- it('autoDetectStack fallback + generateHarness creates backend structure', () => {
216
- const { autoDetectStack } = require('../../src/detector');
217
- const { generateHarness } = require('../../src/generator');
218
-
219
- const dir = createTmpProject({
220
- 'go.mod': 'module example.com\n\ngo 1.21\n',
221
- });
222
- try {
223
- const config = autoDetectStack(dir);
224
- assert.strictEqual(config.framework, 'Go (Golang)', 'should detect Go');
225
-
226
- generateHarness(dir, config);
227
-
228
- assert.ok(fs.existsSync(path.join(dir, 'AGENTS.md')), 'AGENTS.md should exist');
229
- assert.ok(fs.existsSync(path.join(dir, 'sdds')), 'sdds/ should exist');
230
- assert.ok(fs.existsSync(path.join(dir, '.githooks', 'pre-commit')), '.githooks/pre-commit should exist');
231
-
232
- const agents = fs.readFileSync(path.join(dir, 'AGENTS.md'), 'utf-8');
233
- assert.ok(agents.includes('Go'), 'AGENTS.md should mention Go');
234
- assert.ok(agents.includes('BACKEND'), 'AGENTS.md should mention BACKEND');
235
- } finally {
236
- teardown(dir);
237
- }
238
- });
239
-
240
- it('detect → generate → doctor reports HEALTHY', () => {
241
- const { autoDetectStack } = require('../../src/detector');
242
- const { generateHarness } = require('../../src/generator');
243
- const { runDoctor } = require('../../src/doctor');
244
-
245
- const dir = createTmpProject({
246
- 'package.json': JSON.stringify({ dependencies: { next: '14.0.0' } }),
247
- });
248
- try {
249
- // Step 1: Detect
250
- const config = autoDetectStack(dir);
251
- assert.strictEqual(config.framework, 'Next.js (App Router)');
252
-
253
- // Step 2: Generate
254
- generateHarness(dir, config);
255
-
256
- // Step 3: Doctor should report HEALTHY
257
- let capturedOutput = '';
258
- const originalLog = console.log;
259
- console.log = (...args) => { capturedOutput += args.join(' ') + '\n'; };
260
- try {
261
- runDoctor(dir);
262
- assert.ok(capturedOutput.includes('HEALTHY'), 'after detect+generate, doctor should be HEALTHY');
263
- } finally {
264
- console.log = originalLog;
265
- }
266
- } finally {
267
- teardown(dir);
268
- }
269
- });
270
- });
@@ -1,185 +0,0 @@
1
- /**
2
- * CLC Forge — Celery guard integration tests.
3
- * Tests celery_guard.py via subprocess against fixture projects.
4
- */
5
-
6
- const { describe, it } = require('node:test');
7
- const assert = require('node:assert');
8
- const { spawn, spawnSync } = require('child_process');
9
- const path = require('path');
10
- const fs = require('fs');
11
- const os = require('os');
12
- const { createTmpProject, teardown } = require('../helpers');
13
-
14
- /**
15
- * Run celery_guard.py against a target directory.
16
- * Returns a promise resolving to the parsed JSON result.
17
- */
18
- function runCeleryGuard(targetDir) {
19
- return new Promise((resolve, reject) => {
20
- const script = path.join(__dirname, '../../tools/celery_guard.py');
21
- const proc = spawn('python3', [script, targetDir], { cwd: targetDir });
22
- let stdout = '';
23
- let stderr = '';
24
- proc.stdout.on('data', (d) => (stdout += d));
25
- proc.stderr.on('data', (d) => (stderr += d));
26
- proc.on('close', (code) => {
27
- try {
28
- const result = JSON.parse(stdout);
29
- resolve(result);
30
- } catch {
31
- reject(new Error(`Failed to parse guard output: ${stderr || stdout}`));
32
- }
33
- });
34
- proc.on('error', reject);
35
- });
36
- }
37
-
38
- /**
39
- * Create a git repo in dir and commit an initial empty file.
40
- */
41
- function initGitRepo(dir) {
42
- fs.writeFileSync(path.join(dir, '.gitkeep'), '');
43
- spawnSync('git', ['init'], { cwd: dir });
44
- spawnSync('git', ['config', 'user.email', 'test@test.com'], { cwd: dir });
45
- spawnSync('git', ['config', 'user.name', 'Test'], { cwd: dir });
46
- spawnSync('git', ['add', '.'], { cwd: dir });
47
- spawnSync('git', ['commit', '-m', 'initial'], { cwd: dir });
48
- }
49
-
50
- describe('celery_guard', () => {
51
- it('skips when no git diff is available (no git repo)', async () => {
52
- const dir = createTmpProject({});
53
- try {
54
- const result = await runCeleryGuard(dir);
55
- assert.strictEqual(result.skipped, true, 'should be skipped');
56
- assert.strictEqual(result.name, 'celery_guard');
57
- } finally {
58
- teardown(dir);
59
- }
60
- });
61
-
62
- it('finds violations: hardcoded broker_url in Celery()', async () => {
63
- const dir = createTmpProject({});
64
- initGitRepo(dir);
65
-
66
- // Create a new file with a violation
67
- fs.writeFileSync(
68
- path.join(dir, 'tasks.py'),
69
- `from celery import Celery\napp = Celery("myapp", broker_url="redis://localhost")\n`
70
- );
71
- spawnSync('git', ['add', 'tasks.py'], { cwd: dir });
72
-
73
- try {
74
- const result = await runCeleryGuard(dir);
75
- assert.strictEqual(result.exit_code, 1, 'should exit with error code');
76
- assert.strictEqual(result.skipped, false);
77
- assert.ok(result.data.findings.length > 0, 'should have findings');
78
- const issues = result.data.findings.map((f) => f.issue);
79
- assert.ok(issues.includes('hardcoded_broker_url'), 'should detect hardcoded broker_url');
80
- } finally {
81
- teardown(dir);
82
- }
83
- });
84
-
85
- it('finds violations: hardcoded result_backend', async () => {
86
- const dir = createTmpProject({});
87
- initGitRepo(dir);
88
-
89
- fs.writeFileSync(
90
- path.join(dir, 'tasks.py'),
91
- `from celery import Celery\napp = Celery(broker_url="redis://localhost", result_backend="redis://localhost")\n`
92
- );
93
- spawnSync('git', ['add', 'tasks.py'], { cwd: dir });
94
-
95
- try {
96
- const result = await runCeleryGuard(dir);
97
- assert.strictEqual(result.exit_code, 1);
98
- const issues = result.data.findings.map((f) => f.issue);
99
- assert.ok(issues.includes('hardcoded_result_backend'), 'should detect hardcoded result_backend');
100
- } finally {
101
- teardown(dir);
102
- }
103
- });
104
-
105
- it('finds violations: Celery with both broker_url and result_backend', async () => {
106
- const dir = createTmpProject({});
107
- initGitRepo(dir);
108
-
109
- fs.writeFileSync(
110
- path.join(dir, 'tasks.py'),
111
- `from celery import Celery\napp = Celery("myapp", broker_url="redis://localhost", result_backend="redis://localhost")\n`
112
- );
113
- spawnSync('git', ['add', 'tasks.py'], { cwd: dir });
114
-
115
- try {
116
- const result = await runCeleryGuard(dir);
117
- assert.strictEqual(result.exit_code, 1);
118
- const issues = result.data.findings.map((f) => f.issue);
119
- assert.ok(issues.includes('hardcoded_broker_url'), 'should detect hardcoded broker_url');
120
- assert.ok(issues.includes('hardcoded_result_backend'), 'should detect hardcoded result_backend');
121
- assert.strictEqual(result.data.findings.length, 2, 'should find both violations');
122
- } finally {
123
- teardown(dir);
124
- }
125
- });
126
-
127
- it('no violations: properly configured @shared_task with ignore_result=True', async () => {
128
- const dir = createTmpProject({});
129
- initGitRepo(dir);
130
-
131
- fs.writeFileSync(
132
- path.join(dir, 'tasks.py'),
133
- `from celery import shared_task\n@shared_task(ignore_result=True)\ndef add(x, y):\n return x + y\n`
134
- );
135
- spawnSync('git', ['add', 'tasks.py'], { cwd: dir });
136
-
137
- try {
138
- const result = await runCeleryGuard(dir);
139
- assert.strictEqual(result.exit_code, 0, 'should exit cleanly');
140
- assert.strictEqual(result.skipped, false);
141
- assert.strictEqual(result.data.findings.length, 0, 'should have no findings');
142
- } finally {
143
- teardown(dir);
144
- }
145
- });
146
-
147
- it('no violations: properly configured Celery without hardcoded URLs', async () => {
148
- const dir = createTmpProject({});
149
- initGitRepo(dir);
150
-
151
- fs.writeFileSync(
152
- path.join(dir, 'tasks.py'),
153
- `import os\nfrom celery import Celery\napp = Celery("myapp")\napp.conf.broker_url = os.environ.get("BROKER_URL")\napp.conf.result_backend = os.environ.get("RESULT_BACKEND")\n`
154
- );
155
- spawnSync('git', ['add', 'tasks.py'], { cwd: dir });
156
-
157
- try {
158
- const result = await runCeleryGuard(dir);
159
- assert.strictEqual(result.exit_code, 0, 'should exit cleanly');
160
- assert.strictEqual(result.data.findings.length, 0, 'should have no findings');
161
- } finally {
162
- teardown(dir);
163
- }
164
- });
165
-
166
- it('no violations: no celery in project (empty findings)', async () => {
167
- const dir = createTmpProject({});
168
- initGitRepo(dir);
169
-
170
- fs.writeFileSync(
171
- path.join(dir, 'main.py'),
172
- `def hello():\n return "world"\n`
173
- );
174
- spawnSync('git', ['add', 'main.py'], { cwd: dir });
175
-
176
- try {
177
- const result = await runCeleryGuard(dir);
178
- assert.strictEqual(result.exit_code, 0, 'should exit cleanly');
179
- assert.strictEqual(result.skipped, false);
180
- assert.strictEqual(result.data.findings.length, 0, 'should have no findings');
181
- } finally {
182
- teardown(dir);
183
- }
184
- });
185
- });