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,345 +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 { createTmpProject, teardown } = require('./helpers');
6
-
7
- describe('autoDetectStack', () => {
8
- const { autoDetectStack } = require('../src/detector');
9
-
10
- describe('Next.js detection', () => {
11
- it('detects package.json with "next" in dependencies', () => {
12
- const dir = createTmpProject({
13
- 'package.json': JSON.stringify({ dependencies: { next: '14.0.0' } }),
14
- });
15
- try {
16
- const config = autoDetectStack(dir);
17
- assert.strictEqual(config.projectType, 'frontend');
18
- assert.strictEqual(config.framework, 'Next.js (App Router)');
19
- assert.strictEqual(config.testRunner, 'Vitest / Jest');
20
- } finally {
21
- teardown(dir);
22
- }
23
- });
24
-
25
- it('detects package.json with "next" in devDependencies', () => {
26
- const dir = createTmpProject({
27
- 'package.json': JSON.stringify({ devDependencies: { next: '15.0.0' } }),
28
- });
29
- try {
30
- const config = autoDetectStack(dir);
31
- assert.strictEqual(config.projectType, 'frontend');
32
- assert.strictEqual(config.framework, 'Next.js (App Router)');
33
- } finally {
34
- teardown(dir);
35
- }
36
- });
37
- });
38
-
39
- describe('Django detection', () => {
40
- it('detects manage.py presence', () => {
41
- const dir = createTmpProject({ 'manage.py': '' });
42
- try {
43
- const config = autoDetectStack(dir);
44
- assert.strictEqual(config.projectType, 'backend');
45
- assert.strictEqual(config.framework, 'Django Framework');
46
- } finally {
47
- teardown(dir);
48
- }
49
- });
50
-
51
- it('detects requirements.txt with django', () => {
52
- const dir = createTmpProject({
53
- 'requirements.txt': 'django>=4.0\ndjangorestframework\n',
54
- });
55
- try {
56
- const config = autoDetectStack(dir);
57
- assert.strictEqual(config.projectType, 'backend');
58
- assert.strictEqual(config.framework, 'Django Framework');
59
- } finally {
60
- teardown(dir);
61
- }
62
- });
63
-
64
- it('detects pyproject.toml with django', () => {
65
- const dir = createTmpProject({
66
- 'pyproject.toml': '[project]\nname = "myapp"\ndependencies = ["django>=4.0"]\n',
67
- });
68
- try {
69
- const config = autoDetectStack(dir);
70
- assert.strictEqual(config.projectType, 'backend');
71
- assert.strictEqual(config.framework, 'Django Framework');
72
- } finally {
73
- teardown(dir);
74
- }
75
- });
76
- });
77
-
78
- describe('Go detection', () => {
79
- it('detects go.mod presence', () => {
80
- const dir = createTmpProject({
81
- 'go.mod': 'module example.com/myapp\n\ngo 1.21\n',
82
- });
83
- try {
84
- const config = autoDetectStack(dir);
85
- assert.strictEqual(config.projectType, 'backend');
86
- assert.strictEqual(config.framework, 'Go (Golang)');
87
- assert.strictEqual(config.testRunner, 'go test');
88
- } finally {
89
- teardown(dir);
90
- }
91
- });
92
- });
93
-
94
- describe('FastAPI detection', () => {
95
- it('detects pyproject.toml with fastapi', () => {
96
- const dir = createTmpProject({
97
- 'pyproject.toml': '[project]\nname = "myapi"\ndependencies = ["fastapi"]\n',
98
- });
99
- try {
100
- const config = autoDetectStack(dir);
101
- assert.strictEqual(config.projectType, 'backend');
102
- assert.strictEqual(config.framework, 'FastAPI Framework');
103
- } finally {
104
- teardown(dir);
105
- }
106
- });
107
- });
108
-
109
- describe('Astro detection', () => {
110
- it('detects package.json with astro', () => {
111
- const dir = createTmpProject({
112
- 'package.json': JSON.stringify({ dependencies: { astro: '3.0.0' } }),
113
- });
114
- try {
115
- const config = autoDetectStack(dir);
116
- assert.strictEqual(config.projectType, 'frontend');
117
- assert.strictEqual(config.framework, 'Astro Framework');
118
- } finally {
119
- teardown(dir);
120
- }
121
- });
122
- });
123
-
124
- describe('Rails detection', () => {
125
- it('detects Gemfile with rails and config/routes.rb', () => {
126
- const dir = createTmpProject({
127
- 'Gemfile': "source 'https://rubygems.org'\ngem 'rails', '~> 7.0'\n",
128
- 'config/routes.rb': "Rails.application.routes.draw do\nend\n",
129
- });
130
- try {
131
- const config = autoDetectStack(dir);
132
- assert.strictEqual(config.projectType, 'backend');
133
- assert.strictEqual(config.framework, 'Ruby on Rails');
134
- } finally {
135
- teardown(dir);
136
- }
137
- });
138
- });
139
-
140
- describe('Rust detection', () => {
141
- it('detects Cargo.toml with [package]', () => {
142
- const dir = createTmpProject({
143
- 'Cargo.toml': '[package]\nname = "myapp"\nversion = "0.1.0"\nedition = "2021"\n',
144
- });
145
- try {
146
- const config = autoDetectStack(dir);
147
- assert.strictEqual(config.projectType, 'backend');
148
- assert.strictEqual(config.framework, 'Rust Engine');
149
- } finally {
150
- teardown(dir);
151
- }
152
- });
153
- });
154
-
155
- describe('Laravel detection', () => {
156
- it('detects artisan and composer.json', () => {
157
- const dir = createTmpProject({
158
- artisan: '',
159
- 'composer.json': JSON.stringify({ require: { 'laravel/framework': '^10.0' } }),
160
- });
161
- try {
162
- const config = autoDetectStack(dir);
163
- assert.strictEqual(config.projectType, 'backend');
164
- assert.strictEqual(config.framework, 'PHP / Laravel');
165
- } finally {
166
- teardown(dir);
167
- }
168
- });
169
- });
170
-
171
- describe('Fallback detection', () => {
172
- it('returns frontend/Next.js fallback when src/app exists but no adapter matches', () => {
173
- const dir = createTmpProject({
174
- 'src/app/page.tsx': '',
175
- });
176
- try {
177
- const config = autoDetectStack(dir);
178
- assert.strictEqual(config.projectType, 'frontend');
179
- assert.strictEqual(config.framework, 'Next.js');
180
- assert.strictEqual(config.testRunner, 'Vitest');
181
- assert.strictEqual(config.adapter, null);
182
- } finally {
183
- teardown(dir);
184
- }
185
- });
186
-
187
- it('returns frontend/Next.js fallback when src/pages exists but no adapter matches', () => {
188
- const dir = createTmpProject({
189
- 'src/pages/index.tsx': '',
190
- });
191
- try {
192
- const config = autoDetectStack(dir);
193
- assert.strictEqual(config.projectType, 'frontend');
194
- assert.strictEqual(config.framework, 'Next.js');
195
- assert.strictEqual(config.adapter, null);
196
- } finally {
197
- teardown(dir);
198
- }
199
- });
200
-
201
- it('returns backend/FastAPI fallback for empty directory', () => {
202
- const dir = createTmpProject({});
203
- try {
204
- const config = autoDetectStack(dir);
205
- assert.strictEqual(config.projectType, 'backend');
206
- assert.strictEqual(config.framework, 'FastAPI');
207
- assert.strictEqual(config.testRunner, 'Pytest');
208
- assert.strictEqual(config.adapter, null);
209
- } finally {
210
- teardown(dir);
211
- }
212
- });
213
-
214
- it('returns fallback backend config when package.json has unknown dependencies', () => {
215
- const dir = createTmpProject({
216
- 'package.json': JSON.stringify({ dependencies: { lodash: '4.17.21' } }),
217
- });
218
- try {
219
- const config = autoDetectStack(dir);
220
- assert.strictEqual(config.adapter, null);
221
- assert.strictEqual(config.projectType, 'backend');
222
- assert.strictEqual(config.framework, 'FastAPI');
223
- } finally {
224
- teardown(dir);
225
- }
226
- });
227
- });
228
-
229
- describe('Husky detection', () => {
230
- it('returns gitHooks=husky when .husky directory exists', () => {
231
- const dir = createTmpProject({
232
- 'package.json': JSON.stringify({ dependencies: { next: '14.0.0' } }),
233
- '.husky/pre-commit': '#!/bin/sh\nnpm test\n',
234
- });
235
- try {
236
- const config = autoDetectStack(dir);
237
- assert.strictEqual(config.gitHooks, 'husky');
238
- } finally {
239
- teardown(dir);
240
- }
241
- });
242
-
243
- it('returns gitHooks=githooks when .husky directory is absent', () => {
244
- const dir = createTmpProject({
245
- 'package.json': JSON.stringify({ dependencies: { next: '14.0.0' } }),
246
- });
247
- try {
248
- const config = autoDetectStack(dir);
249
- assert.strictEqual(config.gitHooks, 'githooks');
250
- } finally {
251
- teardown(dir);
252
- }
253
- });
254
-
255
- it('returns gitHooks=husky in fallback when .husky exists', () => {
256
- const dir = createTmpProject({
257
- '.husky/pre-commit': '',
258
- });
259
- try {
260
- const config = autoDetectStack(dir);
261
- assert.strictEqual(config.gitHooks, 'husky');
262
- } finally {
263
- teardown(dir);
264
- }
265
- });
266
- });
267
-
268
- describe('config object shape', () => {
269
- it('returns all required fields for a matched adapter', () => {
270
- const dir = createTmpProject({
271
- 'package.json': JSON.stringify({ dependencies: { next: '14.0.0' } }),
272
- });
273
- try {
274
- const config = autoDetectStack(dir);
275
- assert.ok('projectType' in config, 'missing projectType');
276
- assert.ok('framework' in config, 'missing framework');
277
- assert.ok('styling' in config, 'missing styling');
278
- assert.ok('uiLibrary' in config, 'missing uiLibrary');
279
- assert.ok('testRunner' in config, 'missing testRunner');
280
- assert.ok('gitHooks' in config, 'missing gitHooks');
281
- assert.ok('orm' in config, 'missing orm');
282
- assert.ok('adapter' in config, 'missing adapter');
283
- assert.ok(config.adapter !== null, 'adapter should be set');
284
- } finally {
285
- teardown(dir);
286
- }
287
- });
288
-
289
- it('sets styling and uiLibrary for frontend adapters', () => {
290
- const dir = createTmpProject({
291
- 'package.json': JSON.stringify({ dependencies: { next: '14.0.0' } }),
292
- });
293
- try {
294
- const config = autoDetectStack(dir);
295
- assert.strictEqual(config.styling, 'Tailwind CSS v4');
296
- assert.strictEqual(config.uiLibrary, 'Component Primitives');
297
- } finally {
298
- teardown(dir);
299
- }
300
- });
301
-
302
- it('sets N/A for styling and uiLibrary on backend adapters', () => {
303
- const dir = createTmpProject({ 'go.mod': 'module example.com\n' });
304
- try {
305
- const config = autoDetectStack(dir);
306
- assert.strictEqual(config.styling, 'N/A');
307
- assert.strictEqual(config.uiLibrary, 'N/A');
308
- } finally {
309
- teardown(dir);
310
- }
311
- });
312
-
313
- it('returns all required fields for fallback config', () => {
314
- const dir = createTmpProject({});
315
- try {
316
- const config = autoDetectStack(dir);
317
- assert.ok('projectType' in config);
318
- assert.ok('framework' in config);
319
- assert.ok('styling' in config);
320
- assert.ok('uiLibrary' in config);
321
- assert.ok('testRunner' in config);
322
- assert.ok('gitHooks' in config);
323
- assert.ok('orm' in config);
324
- assert.strictEqual(config.adapter, null);
325
- } finally {
326
- teardown(dir);
327
- }
328
- });
329
- });
330
-
331
- describe('adapter priority', () => {
332
- it('Next.js matches before Django when both signals exist', () => {
333
- const dir = createTmpProject({
334
- 'package.json': JSON.stringify({ dependencies: { next: '14.0.0' } }),
335
- 'manage.py': '',
336
- });
337
- try {
338
- const config = autoDetectStack(dir);
339
- assert.strictEqual(config.framework, 'Next.js (App Router)');
340
- } finally {
341
- teardown(dir);
342
- }
343
- });
344
- });
345
- });
@@ -1,302 +0,0 @@
1
- const { describe, it, beforeEach, afterEach } = require('node:test');
2
- const assert = require('node:assert');
3
- const path = require('path');
4
- const fs = require('fs');
5
- const { createTmpProject, teardown } = require('./helpers');
6
-
7
- describe('runDoctor', () => {
8
- const { runDoctor } = require('../src/doctor');
9
-
10
- /**
11
- * Helper: create a fully healthy project with all required structures.
12
- */
13
- function createHealthyProject() {
14
- return createTmpProject({
15
- 'AGENTS.md': '# AGENTS.md\nProject governance rules.\n',
16
- 'sdds/.gitkeep': '',
17
- 'docs/Journal/001-adr.md': '# ADR 001\n',
18
- 'tools/audit.js': "const scan = require('./check_scan');\nmodule.exports = { scan };\n",
19
- 'tools/check_scan.js': 'module.exports = function check() { return true; };\n',
20
- 'tools/check_lint.py': 'def check():\n return True\n',
21
- '.husky/pre-commit': '#!/usr/bin/env sh\nnpm test\n',
22
- });
23
- }
24
-
25
- let capturedOutput;
26
- let originalLog;
27
-
28
- beforeEach(() => {
29
- capturedOutput = [];
30
- originalLog = console.log;
31
- console.log = (...args) => capturedOutput.push(args.join(' '));
32
- });
33
-
34
- afterEach(() => {
35
- console.log = originalLog;
36
- });
37
-
38
- describe('healthy project (all checks pass)', () => {
39
- it('reports HEALTHY when all files and tools are present', () => {
40
- const dir = createHealthyProject();
41
- try {
42
- runDoctor(dir);
43
- const output = capturedOutput.join('\n');
44
- assert.ok(output.includes('HEALTHY'), 'output should contain HEALTHY');
45
- assert.ok(output.includes('[PASS]'), 'output should contain PASS markers');
46
- assert.ok(!output.includes('[FAIL]'), 'should have no FAIL markers');
47
- } finally {
48
- teardown(dir);
49
- }
50
- });
51
-
52
- it('has PASS status for AGENTS.md check', () => {
53
- const dir = createHealthyProject();
54
- try {
55
- runDoctor(dir);
56
- const output = capturedOutput.join('\n');
57
- assert.ok(output.includes('AGENTS.md law document'), 'should check AGENTS.md');
58
- // The PASS line for AGENTS.md
59
- assert.ok(output.includes('[PASS]') && output.includes('AGENTS.md'), 'AGENTS.md should be PASS');
60
- } finally {
61
- teardown(dir);
62
- }
63
- });
64
-
65
- it('has PASS status for tools/ check', () => {
66
- const dir = createHealthyProject();
67
- try {
68
- runDoctor(dir);
69
- const output = capturedOutput.join('\n');
70
- assert.ok(output.includes('tools/ safeguard suite'), 'should check tools/');
71
- assert.ok(output.includes('[PASS]') && output.includes('tools/ safeguard suite'), 'tools/ should be PASS');
72
- } finally {
73
- teardown(dir);
74
- }
75
- });
76
-
77
- it('validates JS tool loads successfully', () => {
78
- const dir = createHealthyProject();
79
- try {
80
- runDoctor(dir);
81
- const output = capturedOutput.join('\n');
82
- assert.ok(output.includes('tool/check_scan.js loads'), 'should validate check_scan.js');
83
- assert.ok(output.includes('[PASS]') && output.includes('check_scan.js loads'), 'check_scan.js should PASS');
84
- } finally {
85
- teardown(dir);
86
- }
87
- });
88
-
89
- it('validates Python tool parses successfully', () => {
90
- const dir = createHealthyProject();
91
- try {
92
- runDoctor(dir);
93
- const output = capturedOutput.join('\n');
94
- assert.ok(output.includes('tool/check_lint.py parses'), 'should validate check_lint.py');
95
- assert.ok(output.includes('[PASS]') && output.includes('check_lint.py parses'), 'check_lint.py should PASS');
96
- } finally {
97
- teardown(dir);
98
- }
99
- });
100
-
101
- it('validates audit.js import alignment', () => {
102
- const dir = createHealthyProject();
103
- try {
104
- runDoctor(dir);
105
- const output = capturedOutput.join('\n');
106
- assert.ok(output.includes('audit.js import ./check_scan'), 'should check audit.js import');
107
- assert.ok(output.includes('[PASS]') && output.includes('audit.js import ./check_scan'), 'audit.js import should PASS');
108
- } finally {
109
- teardown(dir);
110
- }
111
- });
112
- });
113
-
114
- describe('missing AGENTS.md', () => {
115
- it('marks AGENTS.md check as FAIL', () => {
116
- const dir = createTmpProject({
117
- 'sdds/.gitkeep': '',
118
- 'docs/Journal/001-adr.md': '# ADR\n',
119
- 'tools/check_scan.js': 'module.exports = function() {};\n',
120
- '.husky/pre-commit': '#!/usr/bin/env sh\necho test\n',
121
- });
122
- try {
123
- runDoctor(dir);
124
- const output = capturedOutput.join('\n');
125
- assert.ok(output.includes('[FAIL]') && output.includes('AGENTS.md law document'), 'AGENTS.md should be FAIL');
126
- assert.ok(!output.includes('HEALTHY'), 'should not be HEALTHY');
127
- } finally {
128
- teardown(dir);
129
- }
130
- });
131
- });
132
-
133
- describe('missing sdds/ directory', () => {
134
- it('marks sdds/ check as FAIL', () => {
135
- const dir = createTmpProject({
136
- 'AGENTS.md': '# AGENTS\n',
137
- 'docs/Journal/001-adr.md': '# ADR\n',
138
- 'tools/check_scan.js': 'module.exports = function() {};\n',
139
- '.husky/pre-commit': '#!/usr/bin/env sh\necho test\n',
140
- });
141
- try {
142
- runDoctor(dir);
143
- const output = capturedOutput.join('\n');
144
- assert.ok(output.includes('[FAIL]') && output.includes('sdds/'), 'sdds/ should be FAIL');
145
- assert.ok(!output.includes('HEALTHY'), 'should not be HEALTHY');
146
- } finally {
147
- teardown(dir);
148
- }
149
- });
150
- });
151
-
152
- describe('missing tools/ directory', () => {
153
- it('marks tools/ safeguard suite as FAIL', () => {
154
- const dir = createTmpProject({
155
- 'AGENTS.md': '# AGENTS\n',
156
- 'sdds/.gitkeep': '',
157
- 'docs/Journal/001-adr.md': '# ADR\n',
158
- '.husky/pre-commit': '#!/usr/bin/env sh\necho test\n',
159
- });
160
- try {
161
- runDoctor(dir);
162
- const output = capturedOutput.join('\n');
163
- assert.ok(output.includes('[FAIL]') && output.includes('tools/ safeguard suite'), 'tools/ should be FAIL');
164
- assert.ok(!output.includes('HEALTHY'), 'should not be HEALTHY');
165
- } finally {
166
- teardown(dir);
167
- }
168
- });
169
- });
170
-
171
- describe('missing pre-commit hooks', () => {
172
- it('marks pre-commit git hooks as FAIL when neither .husky nor .githooks exist', () => {
173
- const dir = createTmpProject({
174
- 'AGENTS.md': '# AGENTS\n',
175
- 'sdds/.gitkeep': '',
176
- 'docs/Journal/001-adr.md': '# ADR\n',
177
- 'tools/check_scan.js': 'module.exports = function() {};\n',
178
- });
179
- try {
180
- runDoctor(dir);
181
- const output = capturedOutput.join('\n');
182
- assert.ok(output.includes('[FAIL]') && output.includes('pre-commit git hooks'), 'pre-commit hooks should be FAIL');
183
- assert.ok(!output.includes('HEALTHY'), 'should not be HEALTHY');
184
- } finally {
185
- teardown(dir);
186
- }
187
- });
188
-
189
- it('marks pre-commit git hooks as PASS when .githooks/pre-commit exists', () => {
190
- const dir = createTmpProject({
191
- 'AGENTS.md': '# AGENTS\n',
192
- 'sdds/.gitkeep': '',
193
- 'docs/Journal/001-adr.md': '# ADR\n',
194
- 'tools/check_scan.js': 'module.exports = function() {};\n',
195
- '.githooks/pre-commit': '#!/usr/bin/env bash\necho test\n',
196
- });
197
- try {
198
- runDoctor(dir);
199
- const output = capturedOutput.join('\n');
200
- assert.ok(output.includes('[PASS]') && output.includes('pre-commit git hooks'), '.githooks should be PASS');
201
- } finally {
202
- teardown(dir);
203
- }
204
- });
205
- });
206
-
207
- describe('broken JS tool (require fails)', () => {
208
- it('marks tool/check_broken.js loads as FAIL when require fails', () => {
209
- const dir = createTmpProject({
210
- 'AGENTS.md': '# AGENTS\n',
211
- 'sdds/.gitkeep': '',
212
- 'docs/Journal/001-adr.md': '# ADR\n',
213
- 'tools/check_broken.js': "const missing = require('./nonexistent');\nmodule.exports = {};\n",
214
- '.husky/pre-commit': '#!/usr/bin/env sh\necho test\n',
215
- });
216
- try {
217
- runDoctor(dir);
218
- const output = capturedOutput.join('\n');
219
- assert.ok(output.includes('[FAIL]') && output.includes('tool/check_broken.js loads'), 'broken JS tool should FAIL');
220
- assert.ok(!output.includes('HEALTHY'), 'should not be HEALTHY');
221
- } finally {
222
- teardown(dir);
223
- }
224
- });
225
- });
226
-
227
- describe('broken Python tool (ast.parse fails)', () => {
228
- it('marks tool/check_bad.py parses as FAIL when syntax is invalid', () => {
229
- const dir = createTmpProject({
230
- 'AGENTS.md': '# AGENTS\n',
231
- 'sdds/.gitkeep': '',
232
- 'docs/Journal/001-adr.md': '# ADR\n',
233
- 'tools/check_bad.py': 'def broken(\n return True\n', // Missing closing paren
234
- '.husky/pre-commit': '#!/usr/bin/env sh\necho test\n',
235
- });
236
- try {
237
- runDoctor(dir);
238
- const output = capturedOutput.join('\n');
239
- assert.ok(output.includes('[FAIL]') && output.includes('tool/check_bad.py parses'), 'broken Python tool should FAIL');
240
- assert.ok(!output.includes('HEALTHY'), 'should not be HEALTHY');
241
- } finally {
242
- teardown(dir);
243
- }
244
- });
245
- });
246
-
247
- describe('English output', () => {
248
- it('outputs all messages in English (no Spanish)', () => {
249
- const dir = createHealthyProject();
250
- try {
251
- runDoctor(dir);
252
- const output = capturedOutput.join('\n');
253
- // Key English phrases that should be present
254
- assert.ok(output.includes('HEALTHY'), 'should output HEALTHY');
255
- assert.ok(output.includes('CLC KERNEL DOCTOR') || output.includes('CLC FORGE DOCTOR'), 'should output doctor header');
256
- assert.ok(output.includes('PASS'), 'should output PASS');
257
- // Spanish phrases that should NOT be present
258
- const spanishWords = ['éxito', 'éxito', 'falló', 'problema', 'instalado', 'salvaguardas', 'leyes'];
259
- for (const word of spanishWords) {
260
- assert.ok(!output.toLowerCase().includes(word), `should not contain Spanish word: ${word}`);
261
- }
262
- } finally {
263
- teardown(dir);
264
- }
265
- });
266
- });
267
-
268
- describe('audit.js import alignment failure', () => {
269
- it('marks audit.js import as FAIL when required module is missing', () => {
270
- const dir = createTmpProject({
271
- 'AGENTS.md': '# AGENTS\n',
272
- 'sdds/.gitkeep': '',
273
- 'docs/Journal/001-adr.md': '# ADR\n',
274
- 'tools/audit.js': "const checker = require('./check_nonexistent');\nmodule.exports = {};\n",
275
- '.husky/pre-commit': '#!/usr/bin/env sh\necho test\n',
276
- });
277
- try {
278
- runDoctor(dir);
279
- const output = capturedOutput.join('\n');
280
- assert.ok(output.includes('[FAIL]') && output.includes('audit.js import ./check_nonexistent'), 'audit.js import should FAIL');
281
- assert.ok(output.includes('Module not found in tools/'), 'should show detail message');
282
- assert.ok(!output.includes('HEALTHY'), 'should not be HEALTHY');
283
- } finally {
284
- teardown(dir);
285
- }
286
- });
287
- });
288
-
289
- describe('multiple issues detected', () => {
290
- it('reports correct count of issues', () => {
291
- const dir = createTmpProject({}); // Empty project — many checks fail
292
- try {
293
- runDoctor(dir);
294
- const output = capturedOutput.join('\n');
295
- assert.ok(output.includes('issue(s) detected'), 'should report issues detected');
296
- assert.ok(!output.includes('HEALTHY'), 'should not be HEALTHY');
297
- } finally {
298
- teardown(dir);
299
- }
300
- });
301
- });
302
- });