clckernel 1.2.6 → 1.2.9

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 (47) hide show
  1. package/README.es.md +103 -110
  2. package/README.md +100 -108
  3. package/package.json +14 -2
  4. package/src/adapters/agent-os.js +206 -0
  5. package/src/catalog.js +16 -0
  6. package/src/detector.js +2 -0
  7. package/src/generator.js +39 -62
  8. package/src/index.js +11 -0
  9. package/src/ui.js +79 -35
  10. package/tools/audit.js +0 -0
  11. package/tools/audit.py +0 -0
  12. package/tools/check_custom.js +0 -0
  13. package/tools/check_custom.py +0 -0
  14. package/tools/check_db_efficiency.py +0 -0
  15. package/tools/check_domain_data.js +100 -0
  16. package/tools/check_hitl.js +93 -0
  17. package/tools/check_responsive.js +0 -0
  18. package/tools/check_seo.js +1 -1
  19. package/tools/check_symlinks.js +76 -0
  20. package/tools/check_test_calendar_sync.js +111 -0
  21. package/tools/scan_secrets.js +12 -1
  22. package/tools/scan_secrets.py +0 -0
  23. package/AGENTS.md +0 -124
  24. package/docs/Journal/001-adr-clckernel-governance.md +0 -18
  25. package/test/adapters/astro.test.js +0 -102
  26. package/test/adapters/base.test.js +0 -86
  27. package/test/adapters/django.test.js +0 -95
  28. package/test/adapters/fastapi.test.js +0 -88
  29. package/test/adapters/go.test.js +0 -81
  30. package/test/adapters/laravel.test.js +0 -106
  31. package/test/adapters/nextjs.test.js +0 -113
  32. package/test/adapters/rails.test.js +0 -108
  33. package/test/adapters/rust.test.js +0 -83
  34. package/test/catalog.test.js +0 -170
  35. package/test/config.test.js +0 -512
  36. package/test/detector.test.js +0 -345
  37. package/test/doctor.test.js +0 -302
  38. package/test/generator.test.js +0 -419
  39. package/test/guards_new.test.js +0 -95
  40. package/test/helpers.js +0 -36
  41. package/test/integration/cli-flow.test.js +0 -270
  42. package/test/technologies/celery_guard.test.js +0 -185
  43. package/test/technologies/detector.test.js +0 -342
  44. package/test/technologies/docker_guard.test.js +0 -187
  45. package/test/technologies/integration.test.js +0 -123
  46. package/test/technologies/postgres_guard.test.js +0 -87
  47. package/test/technologies/redis_guard.test.js +0 -128
@@ -1,95 +0,0 @@
1
- const { describe, it } = require('node:test');
2
- const assert = require('node:assert');
3
- const { createTmpProject, teardown } = require('../helpers');
4
-
5
- describe('DjangoAdapter', () => {
6
- const DjangoAdapter = require('../../src/adapters/django');
7
-
8
- describe('constructor', () => {
9
- it('creates adapter with correct name and projectType', () => {
10
- const adapter = new DjangoAdapter();
11
- assert.strictEqual(adapter.name, 'Django Framework');
12
- assert.strictEqual(adapter.projectType, 'backend');
13
- });
14
- });
15
-
16
- describe('detect()', () => {
17
- it('returns true when manage.py exists', () => {
18
- const dir = createTmpProject({ 'manage.py': '#!/usr/bin/env python' });
19
- try {
20
- const adapter = new DjangoAdapter();
21
- assert.strictEqual(adapter.detect(dir), true);
22
- } finally {
23
- teardown(dir);
24
- }
25
- });
26
-
27
- it('returns true when requirements.txt contains django', () => {
28
- const dir = createTmpProject({ 'requirements.txt': 'django>=4.0\npsycopg2' });
29
- try {
30
- const adapter = new DjangoAdapter();
31
- assert.strictEqual(adapter.detect(dir), true);
32
- } finally {
33
- teardown(dir);
34
- }
35
- });
36
-
37
- it('returns true when pyproject.toml contains django', () => {
38
- const dir = createTmpProject({
39
- 'pyproject.toml': '[project]\nname = "myapp"\ndependencies = ["django>=4.0"]',
40
- });
41
- try {
42
- const adapter = new DjangoAdapter();
43
- assert.strictEqual(adapter.detect(dir), true);
44
- } finally {
45
- teardown(dir);
46
- }
47
- });
48
-
49
- it('returns false when no django indicators present', () => {
50
- const dir = createTmpProject({
51
- 'requirements.txt': 'flask>=2.0',
52
- });
53
- try {
54
- const adapter = new DjangoAdapter();
55
- assert.strictEqual(adapter.detect(dir), false);
56
- } finally {
57
- teardown(dir);
58
- }
59
- });
60
-
61
- it('returns false when directory is empty', () => {
62
- const dir = createTmpProject({});
63
- try {
64
- const adapter = new DjangoAdapter();
65
- assert.strictEqual(adapter.detect(dir), false);
66
- } finally {
67
- teardown(dir);
68
- }
69
- });
70
- });
71
-
72
- describe('getTools()', () => {
73
- it('returns array of tool objects with required fields', () => {
74
- const adapter = new DjangoAdapter();
75
- const tools = adapter.getTools();
76
- assert.ok(Array.isArray(tools));
77
- assert.ok(tools.length > 0);
78
- for (const tool of tools) {
79
- assert.ok(tool.name);
80
- assert.ok(tool.language);
81
- assert.ok(typeof tool.command === 'string');
82
- assert.ok(tool.description);
83
- }
84
- });
85
- });
86
-
87
- describe('getSafeguards()', () => {
88
- it('returns non-empty array of strings', () => {
89
- const adapter = new DjangoAdapter();
90
- const guards = adapter.getSafeguards();
91
- assert.ok(Array.isArray(guards));
92
- assert.ok(guards.length >= 5);
93
- });
94
- });
95
- });
@@ -1,88 +0,0 @@
1
- const { describe, it } = require('node:test');
2
- const assert = require('node:assert');
3
- const { createTmpProject, teardown } = require('../helpers');
4
-
5
- describe('FastApiAdapter', () => {
6
- const FastApiAdapter = require('../../src/adapters/fastapi');
7
-
8
- describe('constructor', () => {
9
- it('creates adapter with correct name and projectType', () => {
10
- const adapter = new FastApiAdapter();
11
- assert.strictEqual(adapter.name, 'FastAPI Framework');
12
- assert.strictEqual(adapter.projectType, 'backend');
13
- assert.strictEqual(adapter.testRunner, 'Pytest');
14
- });
15
- });
16
-
17
- describe('detect()', () => {
18
- it('returns true when pyproject.toml contains fastapi', () => {
19
- const dir = createTmpProject({
20
- 'pyproject.toml': '[project]\nname = "api"\ndependencies = ["fastapi>=0.100"]',
21
- });
22
- try {
23
- const adapter = new FastApiAdapter();
24
- assert.strictEqual(adapter.detect(dir), true);
25
- } finally {
26
- teardown(dir);
27
- }
28
- });
29
-
30
- it('returns true when requirements.txt contains fastapi', () => {
31
- const dir = createTmpProject({
32
- 'requirements.txt': 'fastapi>=0.100\nuvicorn',
33
- });
34
- try {
35
- const adapter = new FastApiAdapter();
36
- assert.strictEqual(adapter.detect(dir), true);
37
- } finally {
38
- teardown(dir);
39
- }
40
- });
41
-
42
- it('returns false when no fastapi indicators present', () => {
43
- const dir = createTmpProject({
44
- 'requirements.txt': 'flask>=2.0',
45
- });
46
- try {
47
- const adapter = new FastApiAdapter();
48
- assert.strictEqual(adapter.detect(dir), false);
49
- } finally {
50
- teardown(dir);
51
- }
52
- });
53
-
54
- it('returns false when directory is empty', () => {
55
- const dir = createTmpProject({});
56
- try {
57
- const adapter = new FastApiAdapter();
58
- assert.strictEqual(adapter.detect(dir), false);
59
- } finally {
60
- teardown(dir);
61
- }
62
- });
63
- });
64
-
65
- describe('getTools()', () => {
66
- it('returns array of tool objects with required fields', () => {
67
- const adapter = new FastApiAdapter();
68
- const tools = adapter.getTools();
69
- assert.ok(Array.isArray(tools));
70
- assert.ok(tools.length > 0);
71
- for (const tool of tools) {
72
- assert.ok(tool.name);
73
- assert.ok(tool.language);
74
- assert.ok(typeof tool.command === 'string');
75
- assert.ok(tool.description);
76
- }
77
- });
78
- });
79
-
80
- describe('getSafeguards()', () => {
81
- it('returns non-empty array of strings', () => {
82
- const adapter = new FastApiAdapter();
83
- const guards = adapter.getSafeguards();
84
- assert.ok(Array.isArray(guards));
85
- assert.ok(guards.length >= 5);
86
- });
87
- });
88
- });
@@ -1,81 +0,0 @@
1
- const { describe, it } = require('node:test');
2
- const assert = require('node:assert');
3
- const { createTmpProject, teardown } = require('../helpers');
4
-
5
- describe('GoAdapter', () => {
6
- const GoAdapter = require('../../src/adapters/go');
7
-
8
- describe('constructor', () => {
9
- it('creates adapter with correct name and projectType', () => {
10
- const adapter = new GoAdapter();
11
- assert.strictEqual(adapter.name, 'Go (Golang)');
12
- assert.strictEqual(adapter.projectType, 'backend');
13
- assert.strictEqual(adapter.testRunner, 'go test');
14
- });
15
- });
16
-
17
- describe('detect()', () => {
18
- it('returns true when go.mod exists', () => {
19
- const dir = createTmpProject({ 'go.mod': 'module example.com/myapp\ngo 1.21' });
20
- try {
21
- const adapter = new GoAdapter();
22
- assert.strictEqual(adapter.detect(dir), true);
23
- } finally {
24
- teardown(dir);
25
- }
26
- });
27
-
28
- it('returns false when go.mod does not exist', () => {
29
- const dir = createTmpProject({});
30
- try {
31
- const adapter = new GoAdapter();
32
- assert.strictEqual(adapter.detect(dir), false);
33
- } finally {
34
- teardown(dir);
35
- }
36
- });
37
- });
38
-
39
- describe('getTools()', () => {
40
- it('returns array of tool objects with required fields', () => {
41
- const adapter = new GoAdapter();
42
- const tools = adapter.getTools();
43
- assert.ok(Array.isArray(tools));
44
- assert.ok(tools.length > 0);
45
- for (const tool of tools) {
46
- assert.ok(tool.name);
47
- assert.ok(tool.language);
48
- assert.ok(typeof tool.command === 'string');
49
- assert.ok(tool.description);
50
- }
51
- });
52
-
53
- it('all external tools have language=external and empty path', () => {
54
- const adapter = new GoAdapter();
55
- const tools = adapter.getTools();
56
- const externalTools = tools.filter((t) => t.language === 'external');
57
- for (const tool of externalTools) {
58
- assert.strictEqual(tool.language, 'external');
59
- assert.strictEqual(tool.path, '');
60
- }
61
- });
62
-
63
- it('includes custom rules guard', () => {
64
- const adapter = new GoAdapter();
65
- const tools = adapter.getTools();
66
- const customTool = tools.find((t) => t.name === 'check_custom');
67
- assert.ok(customTool, 'check_custom tool should exist');
68
- assert.strictEqual(customTool.language, 'js');
69
- assert.strictEqual(customTool.path, 'check_custom.js');
70
- });
71
- });
72
-
73
- describe('getSafeguards()', () => {
74
- it('returns non-empty array of strings', () => {
75
- const adapter = new GoAdapter();
76
- const guards = adapter.getSafeguards();
77
- assert.ok(Array.isArray(guards));
78
- assert.ok(guards.length >= 5);
79
- });
80
- });
81
- });
@@ -1,106 +0,0 @@
1
- const { describe, it } = require('node:test');
2
- const assert = require('node:assert');
3
- const { createTmpProject, teardown } = require('../helpers');
4
-
5
- describe('LaravelAdapter', () => {
6
- const LaravelAdapter = require('../../src/adapters/laravel');
7
-
8
- describe('constructor', () => {
9
- it('creates adapter with correct name and projectType', () => {
10
- const adapter = new LaravelAdapter();
11
- assert.strictEqual(adapter.name, 'PHP / Laravel');
12
- assert.strictEqual(adapter.projectType, 'backend');
13
- assert.strictEqual(adapter.testRunner, 'Pest / PHPUnit');
14
- });
15
- });
16
-
17
- describe('detect()', () => {
18
- it('returns true when both composer.json and artisan exist', () => {
19
- const dir = createTmpProject({
20
- 'composer.json': JSON.stringify({ require: { 'laravel/framework': '^10.0' } }),
21
- 'artisan': '#!/usr/bin/env php',
22
- });
23
- try {
24
- const adapter = new LaravelAdapter();
25
- assert.strictEqual(adapter.detect(dir), true);
26
- } finally {
27
- teardown(dir);
28
- }
29
- });
30
-
31
- it('returns false when only composer.json exists', () => {
32
- const dir = createTmpProject({
33
- 'composer.json': JSON.stringify({ require: {} }),
34
- });
35
- try {
36
- const adapter = new LaravelAdapter();
37
- assert.strictEqual(adapter.detect(dir), false);
38
- } finally {
39
- teardown(dir);
40
- }
41
- });
42
-
43
- it('returns false when only artisan exists', () => {
44
- const dir = createTmpProject({ 'artisan': '#!/usr/bin/env php' });
45
- try {
46
- const adapter = new LaravelAdapter();
47
- assert.strictEqual(adapter.detect(dir), false);
48
- } finally {
49
- teardown(dir);
50
- }
51
- });
52
-
53
- it('returns false when directory is empty', () => {
54
- const dir = createTmpProject({});
55
- try {
56
- const adapter = new LaravelAdapter();
57
- assert.strictEqual(adapter.detect(dir), false);
58
- } finally {
59
- teardown(dir);
60
- }
61
- });
62
- });
63
-
64
- describe('getTools()', () => {
65
- it('returns array of tool objects with required fields', () => {
66
- const adapter = new LaravelAdapter();
67
- const tools = adapter.getTools();
68
- assert.ok(Array.isArray(tools));
69
- assert.ok(tools.length > 0);
70
- for (const tool of tools) {
71
- assert.ok(tool.name);
72
- assert.ok(tool.language);
73
- assert.ok(typeof tool.command === 'string');
74
- assert.ok(tool.description);
75
- }
76
- });
77
-
78
- it('all external tools have language=external and empty path', () => {
79
- const adapter = new LaravelAdapter();
80
- const tools = adapter.getTools();
81
- const externalTools = tools.filter((t) => t.language === 'external');
82
- for (const tool of externalTools) {
83
- assert.strictEqual(tool.language, 'external');
84
- assert.strictEqual(tool.path, '');
85
- }
86
- });
87
-
88
- it('includes custom rules guard', () => {
89
- const adapter = new LaravelAdapter();
90
- const tools = adapter.getTools();
91
- const customTool = tools.find((t) => t.name === 'check_custom');
92
- assert.ok(customTool, 'check_custom tool should exist');
93
- assert.strictEqual(customTool.language, 'python');
94
- assert.strictEqual(customTool.path, 'check_custom.py');
95
- });
96
- });
97
-
98
- describe('getSafeguards()', () => {
99
- it('returns non-empty array of strings', () => {
100
- const adapter = new LaravelAdapter();
101
- const guards = adapter.getSafeguards();
102
- assert.ok(Array.isArray(guards));
103
- assert.ok(guards.length >= 5);
104
- });
105
- });
106
- });
@@ -1,113 +0,0 @@
1
- const { describe, it } = require('node:test');
2
- const assert = require('node:assert');
3
- const { createTmpProject, teardown } = require('../helpers');
4
-
5
- describe('NextjsAdapter', () => {
6
- const NextjsAdapter = require('../../src/adapters/nextjs');
7
-
8
- describe('constructor', () => {
9
- it('creates adapter with correct name and projectType', () => {
10
- const adapter = new NextjsAdapter();
11
- assert.strictEqual(adapter.name, 'Next.js (App Router)');
12
- assert.strictEqual(adapter.projectType, 'frontend');
13
- assert.strictEqual(adapter.testRunner, 'Vitest / Jest');
14
- });
15
- });
16
-
17
- describe('detect()', () => {
18
- it('returns true when package.json has next dependency', () => {
19
- const dir = createTmpProject({
20
- 'package.json': JSON.stringify({ dependencies: { next: '14.0.0' } }),
21
- });
22
- try {
23
- const adapter = new NextjsAdapter();
24
- assert.strictEqual(adapter.detect(dir), true);
25
- } finally {
26
- teardown(dir);
27
- }
28
- });
29
-
30
- it('returns true when next is in devDependencies', () => {
31
- const dir = createTmpProject({
32
- 'package.json': JSON.stringify({ devDependencies: { next: '14.0.0' } }),
33
- });
34
- try {
35
- const adapter = new NextjsAdapter();
36
- assert.strictEqual(adapter.detect(dir), true);
37
- } finally {
38
- teardown(dir);
39
- }
40
- });
41
-
42
- it('returns false when package.json has no next dependency', () => {
43
- const dir = createTmpProject({
44
- 'package.json': JSON.stringify({ dependencies: { react: '18.0.0' } }),
45
- });
46
- try {
47
- const adapter = new NextjsAdapter();
48
- assert.strictEqual(adapter.detect(dir), false);
49
- } finally {
50
- teardown(dir);
51
- }
52
- });
53
-
54
- it('returns false when no package.json exists', () => {
55
- const dir = createTmpProject({});
56
- try {
57
- const adapter = new NextjsAdapter();
58
- assert.strictEqual(adapter.detect(dir), false);
59
- } finally {
60
- teardown(dir);
61
- }
62
- });
63
-
64
- it('returns false when package.json is malformed JSON', () => {
65
- const dir = createTmpProject({
66
- 'package.json': '{invalid json',
67
- });
68
- try {
69
- const adapter = new NextjsAdapter();
70
- assert.strictEqual(adapter.detect(dir), false);
71
- } finally {
72
- teardown(dir);
73
- }
74
- });
75
- });
76
-
77
- describe('getTools()', () => {
78
- it('returns array of tool objects with required fields', () => {
79
- const adapter = new NextjsAdapter();
80
- const tools = adapter.getTools();
81
- assert.ok(Array.isArray(tools));
82
- assert.ok(tools.length > 0);
83
- for (const tool of tools) {
84
- assert.ok(tool.name, 'tool must have name');
85
- assert.ok(tool.language, 'tool must have language');
86
- assert.ok(typeof tool.command === 'string', 'tool must have command string');
87
- assert.ok(tool.description, 'tool must have description');
88
- }
89
- });
90
-
91
- it('external tools have language=external and empty path', () => {
92
- const adapter = new NextjsAdapter();
93
- const tools = adapter.getTools();
94
- const external = tools.filter((t) => t.language === 'external');
95
- assert.ok(external.length > 0, 'should have external tools');
96
- for (const tool of external) {
97
- assert.strictEqual(tool.path, '');
98
- }
99
- });
100
- });
101
-
102
- describe('getSafeguards()', () => {
103
- it('returns non-empty array of strings', () => {
104
- const adapter = new NextjsAdapter();
105
- const guards = adapter.getSafeguards();
106
- assert.ok(Array.isArray(guards));
107
- assert.ok(guards.length >= 5, 'should have at least 5 safeguards');
108
- for (const g of guards) {
109
- assert.strictEqual(typeof g, 'string');
110
- }
111
- });
112
- });
113
- });
@@ -1,108 +0,0 @@
1
- const { describe, it } = require('node:test');
2
- const assert = require('node:assert');
3
- const { createTmpProject, teardown } = require('../helpers');
4
-
5
- describe('RailsAdapter', () => {
6
- const RailsAdapter = require('../../src/adapters/rails');
7
-
8
- describe('constructor', () => {
9
- it('creates adapter with correct name and projectType', () => {
10
- const adapter = new RailsAdapter();
11
- assert.strictEqual(adapter.name, 'Ruby on Rails');
12
- assert.strictEqual(adapter.projectType, 'backend');
13
- assert.strictEqual(adapter.testRunner, 'RSpec / Minitest');
14
- });
15
- });
16
-
17
- describe('detect()', () => {
18
- it('returns true when Gemfile and config/routes.rb exist', () => {
19
- const dir = createTmpProject({
20
- 'Gemfile': "source 'https://rubygems.org'\ngem 'rails', '~> 7.0'",
21
- 'config/routes.rb': 'Rails.application.routes.draw do end',
22
- });
23
- try {
24
- const adapter = new RailsAdapter();
25
- assert.strictEqual(adapter.detect(dir), true);
26
- } finally {
27
- teardown(dir);
28
- }
29
- });
30
-
31
- it('returns false when only Gemfile exists (no routes.rb)', () => {
32
- const dir = createTmpProject({
33
- 'Gemfile': "source 'https://rubygems.org'",
34
- });
35
- try {
36
- const adapter = new RailsAdapter();
37
- assert.strictEqual(adapter.detect(dir), false);
38
- } finally {
39
- teardown(dir);
40
- }
41
- });
42
-
43
- it('returns false when only routes.rb exists (no Gemfile)', () => {
44
- const dir = createTmpProject({
45
- 'config/routes.rb': 'Rails.application.routes.draw do end',
46
- });
47
- try {
48
- const adapter = new RailsAdapter();
49
- assert.strictEqual(adapter.detect(dir), false);
50
- } finally {
51
- teardown(dir);
52
- }
53
- });
54
-
55
- it('returns false when directory is empty', () => {
56
- const dir = createTmpProject({});
57
- try {
58
- const adapter = new RailsAdapter();
59
- assert.strictEqual(adapter.detect(dir), false);
60
- } finally {
61
- teardown(dir);
62
- }
63
- });
64
- });
65
-
66
- describe('getTools()', () => {
67
- it('returns array of tool objects with required fields', () => {
68
- const adapter = new RailsAdapter();
69
- const tools = adapter.getTools();
70
- assert.ok(Array.isArray(tools));
71
- assert.ok(tools.length > 0);
72
- for (const tool of tools) {
73
- assert.ok(tool.name);
74
- assert.ok(tool.language);
75
- assert.ok(typeof tool.command === 'string');
76
- assert.ok(tool.description);
77
- }
78
- });
79
-
80
- it('all external tools have language=external and empty path', () => {
81
- const adapter = new RailsAdapter();
82
- const tools = adapter.getTools();
83
- const externalTools = tools.filter((t) => t.language === 'external');
84
- for (const tool of externalTools) {
85
- assert.strictEqual(tool.language, 'external');
86
- assert.strictEqual(tool.path, '');
87
- }
88
- });
89
-
90
- it('includes custom rules guard', () => {
91
- const adapter = new RailsAdapter();
92
- const tools = adapter.getTools();
93
- const customTool = tools.find((t) => t.name === 'check_custom');
94
- assert.ok(customTool, 'check_custom tool should exist');
95
- assert.strictEqual(customTool.language, 'js');
96
- assert.strictEqual(customTool.path, 'check_custom.js');
97
- });
98
- });
99
-
100
- describe('getSafeguards()', () => {
101
- it('returns non-empty array of strings', () => {
102
- const adapter = new RailsAdapter();
103
- const guards = adapter.getSafeguards();
104
- assert.ok(Array.isArray(guards));
105
- assert.ok(guards.length >= 5);
106
- });
107
- });
108
- });
@@ -1,83 +0,0 @@
1
- const { describe, it } = require('node:test');
2
- const assert = require('node:assert');
3
- const { createTmpProject, teardown } = require('../helpers');
4
-
5
- describe('RustAdapter', () => {
6
- const RustAdapter = require('../../src/adapters/rust');
7
-
8
- describe('constructor', () => {
9
- it('creates adapter with correct name and projectType', () => {
10
- const adapter = new RustAdapter();
11
- assert.strictEqual(adapter.name, 'Rust Engine');
12
- assert.strictEqual(adapter.projectType, 'backend');
13
- assert.strictEqual(adapter.testRunner, 'cargo test');
14
- });
15
- });
16
-
17
- describe('detect()', () => {
18
- it('returns true when Cargo.toml exists', () => {
19
- const dir = createTmpProject({
20
- 'Cargo.toml': '[package]\nname = "myapp"\nversion = "0.1.0"\nedition = "2021"',
21
- });
22
- try {
23
- const adapter = new RustAdapter();
24
- assert.strictEqual(adapter.detect(dir), true);
25
- } finally {
26
- teardown(dir);
27
- }
28
- });
29
-
30
- it('returns false when Cargo.toml does not exist', () => {
31
- const dir = createTmpProject({});
32
- try {
33
- const adapter = new RustAdapter();
34
- assert.strictEqual(adapter.detect(dir), false);
35
- } finally {
36
- teardown(dir);
37
- }
38
- });
39
- });
40
-
41
- describe('getTools()', () => {
42
- it('returns array of tool objects with required fields', () => {
43
- const adapter = new RustAdapter();
44
- const tools = adapter.getTools();
45
- assert.ok(Array.isArray(tools));
46
- assert.ok(tools.length > 0);
47
- for (const tool of tools) {
48
- assert.ok(tool.name);
49
- assert.ok(tool.language);
50
- assert.ok(typeof tool.command === 'string');
51
- assert.ok(tool.description);
52
- }
53
- });
54
-
55
- it('all external tools have language=external and empty path', () => {
56
- const adapter = new RustAdapter();
57
- const tools = adapter.getTools();
58
- const externalTools = tools.filter((t) => t.language === 'external');
59
- for (const tool of externalTools) {
60
- assert.strictEqual(tool.language, 'external');
61
- assert.strictEqual(tool.path, '');
62
- }
63
- });
64
-
65
- it('includes custom rules guard', () => {
66
- const adapter = new RustAdapter();
67
- const tools = adapter.getTools();
68
- const customTool = tools.find((t) => t.name === 'check_custom');
69
- assert.ok(customTool, 'check_custom tool should exist');
70
- assert.strictEqual(customTool.language, 'js');
71
- assert.strictEqual(customTool.path, 'check_custom.js');
72
- });
73
- });
74
-
75
- describe('getSafeguards()', () => {
76
- it('returns non-empty array of strings', () => {
77
- const adapter = new RustAdapter();
78
- const guards = adapter.getSafeguards();
79
- assert.ok(Array.isArray(guards));
80
- assert.ok(guards.length >= 5);
81
- });
82
- });
83
- });