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
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Symlink & AI IDE Interoperability Guard
4
+ * Verifies that AI agent rule mirrors (CLAUDE.md, GEMINI.md, .cursorrules, .windsurfrules)
5
+ * exist and resolve to valid files without broken symlinks.
6
+ * Self-contained: uses only Node.js built-ins.
7
+ *
8
+ * @param {string} targetDir - Directory to scan (defaults to cwd)
9
+ * @returns {{ exitCode: number, skipped: boolean, name: string, data: object }}
10
+ */
11
+ const check = async function checkSymlinks(targetDir) {
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+
15
+ const dir = targetDir || process.cwd();
16
+ const rules = ['CLAUDE.md', 'GEMINI.md', '.cursorrules', '.windsurfrules'];
17
+ const violations = [];
18
+
19
+ const agentsPath = path.join(dir, 'AGENTS.md');
20
+ const hasAgents = fs.existsSync(agentsPath);
21
+
22
+ if (!hasAgents) {
23
+ return {
24
+ exitCode: 0,
25
+ skipped: true,
26
+ name: 'check_symlinks',
27
+ data: { violations: [], message: 'No AGENTS.md present in project' }
28
+ };
29
+ }
30
+
31
+ for (const rule of rules) {
32
+ const filePath = path.join(dir, rule);
33
+ try {
34
+ const lstat = fs.lstatSync(filePath);
35
+ if (lstat.isSymbolicLink()) {
36
+ const target = fs.readlinkSync(filePath);
37
+ const resolved = path.isAbsolute(target) ? target : path.resolve(dir, target);
38
+ if (!fs.existsSync(resolved)) {
39
+ violations.push({
40
+ file: rule,
41
+ message: `Broken symlink: points to non-existent target "${target}"`
42
+ });
43
+ }
44
+ }
45
+ } catch (err) {
46
+ if (err.code !== 'ENOENT') {
47
+ violations.push({
48
+ file: rule,
49
+ message: `Error reading symlink: ${err.message}`
50
+ });
51
+ }
52
+ }
53
+ }
54
+
55
+ const exitCode = violations.length > 0 ? 1 : 0;
56
+ return {
57
+ exitCode,
58
+ skipped: false,
59
+ name: 'check_symlinks',
60
+ data: {
61
+ violations,
62
+ message: violations.length > 0
63
+ ? `${violations.length} broken or invalid symlink(s) detected`
64
+ : 'All AI IDE rule mirrors & symlinks verified'
65
+ }
66
+ };
67
+ };
68
+
69
+ module.exports = check;
70
+
71
+ if (require.main === module) {
72
+ check(process.argv[2]).then(result => {
73
+ console.log(JSON.stringify(result, null, 2));
74
+ process.exit(result.exitCode);
75
+ });
76
+ }
@@ -62,7 +62,18 @@ const check = async function scanSecrets(targetDir) {
62
62
 
63
63
  let content;
64
64
  try {
65
- content = fs.readFileSync(filePath, 'utf-8');
65
+ const buffer = fs.readFileSync(filePath);
66
+ // Skip binary files (check for null byte in first 512 bytes)
67
+ let isBinary = false;
68
+ const checkLen = Math.min(buffer.length, 512);
69
+ for (let b = 0; b < checkLen; b++) {
70
+ if (buffer[b] === 0) {
71
+ isBinary = true;
72
+ break;
73
+ }
74
+ }
75
+ if (isBinary) continue;
76
+ content = buffer.toString('utf-8');
66
77
  } catch {
67
78
  continue;
68
79
  }
File without changes
package/AGENTS.md DELETED
@@ -1,124 +0,0 @@
1
- # 🔨 CLC Kernel (Node.js / Polyglot Core) — AGENTS
2
-
3
- This document is the **authoritative law** for AI agents working in this repository.
4
- Forged by **CLC Kernel: The AI Agent Governance Engine**.
5
-
6
- ---
7
-
8
- ## 🎯 Project Identity & Category
9
- **CLC Kernel (`clckernel`)** is the Universal Polyglot AI Agent Governance Engine & AIUP Orchestrator.
10
- It creates and owns a new category in software engineering: **Deterministic AI Agent Governance**.
11
-
12
- Unlike prompt-based rules (`.cursorrules`) or backend agent runtimes (LangGraph), CLC Kernel provides an **in-repo developer harness** that deterministically forces AI coding agents to follow strict engineering lifecycles (SDD, TDD, Clean Architecture AST Linters) across multiple tech stacks (Next.js, FastAPI, Rails, Go, Rust, Laravel, Django, Astro).
13
-
14
- ---
15
-
16
- > 🚨 **RULE #0: MANDATORY EXECUTION OVERRIDE RULE (UNBYPASSABLE)**
17
- > Even when the user issues a direct or urgent fix request ("fix this bug", "fix this error", "quick fix"):
18
- > YOU ARE STRICTLY FORBIDDEN from modifying source code directly without completing the full quality harness:
19
- > 1. **Research & Root Cause Analysis:** Investigate tracebacks and inspect affected files before editing.
20
- > 2. **TDD Verification (Red Phase):** Write a failing regression test first (`npm test`).
21
- > 3. **Clean Architecture Implementation (Green Phase):** Make the test pass maintaining layer isolation.
22
- > 4. **Mandatory Educational Audit Gate:** Execute `npm test` and verify zero lint/test regressions.
23
- > NEVER declare success or skip verification commands for quick fixes.
24
-
25
- ---
26
-
27
- ## 1. The Engineering Loop (Every Task)
28
- 1. **Research** — Inspect codebase / docs / existing AST linters before writing code.
29
- 2. **Plan** — Write an SDD under `sdds/{change-name}/` when introducing features or architectural refactors.
30
- 3. **Test (TDD)** — Write failing tests first in `test/`.
31
- 4. **Implement** — Write modular, zero-external-dependency code in `src/` or `tools/`.
32
- 5. **Verify & Audit** — Run `npm test` (all 236+ test cases across catalog, adapters, generator, and detector).
33
- 6. **DoD (Definition of Done)** — Clean code, zero regressions, full test coverage, docs updated in English and Spanish.
34
- 7. **Commit** — Pre-commit hook runs automated guards. Use Conventional Commits (`feat:`, `fix:`, `docs:`, `refactor:`, `test:`). Never add AI co-authorship.
35
-
36
- ---
37
-
38
- ## 2. Core Repository Architecture & Layout
39
-
40
- ```
41
- clckernel/
42
- ├── bin/
43
- │ └── cli.js # Executable CLI entrypoint (clckernel, clc-kernel, clc-forge)
44
- ├── src/
45
- │ ├── index.js # CLI controller orchestration
46
- │ ├── catalog.js # Central 26-guard registry (Single Source of Truth)
47
- │ ├── config.js # .clckernel.yml loader & validator (ConfigError)
48
- │ ├── detector.js # Stack auto-detection heuristics
49
- │ ├── doctor.js # Repository health check engine (CLC KERNEL DOCTOR)
50
- │ ├── generator.js # Universal harness, hook, and symlink provisioner
51
- │ ├── ui.js # Terminal UI, ASCII banner & ANSI renderer
52
- │ ├── yaml.js # Zero-dependency YAML parser & glob-to-regex engine
53
- │ ├── adapters/ # Polyglot stack adapters (Next.js, FastAPI, Rails, Go, Rust, Laravel, Django, Astro)
54
- │ └── technologies/ # Technology-specific detectors (Celery, Redis, Postgres, Docker)
55
- ├── tools/ # Deterministic AST linters & safeguard scripts (Node.js & Python stdlib)
56
- │ ├── audit.js / audit.py # Multi-guard orchestrator gates
57
- │ ├── scan_secrets.js / .py # Secret leak scanners
58
- │ ├── check_architecture.* # Clean Architecture layer isolation checkers
59
- │ ├── check_custom.js / .py # User-defined regex rules from .clckernel.yml
60
- │ ├── check_a11y.js # ARIA & accessibility linter
61
- │ ├── check_ui_reuse.js # UI primitive reuse enforcer
62
- │ ├── check_performance.js # Next/Image & tree-shaking linter
63
- │ ├── check_responsive.js # Mobile-first & touch target linter
64
- │ ├── check_seo.js # OpenGraph, SEO & Core Web Vitals linter
65
- │ ├── check_storybook.js # Storybook coverage guard
66
- │ ├── check_api_contracts.js # Zod schema validation guard
67
- │ ├── check_migrations.py # Alembic / Django migration idempotency guard
68
- │ ├── check_db_efficiency.py # ORM N+1 & query efficiency guard
69
- │ ├── check_scope.py # Git diff vs SDD authorized scope guard
70
- │ ├── verify_tdd.py # Red-to-Green transition proof checker
71
- │ ├── celery_guard.py # Celery broker & result backend safety guard
72
- │ ├── redis_guard.py # Redis decode_responses & timeout guard
73
- │ ├── postgres_guard.py # PostgreSQL raw query & injection guard
74
- │ └── docker_guard.py # Docker non-root USER & healthcheck guard
75
- ├── test/ # Comprehensive node:test runner suites
76
- ├── README.md # Primary documentation (English)
77
- ├── README.es.md # Primary documentation (Spanish)
78
- └── package.json # Package manifest (@clckernel)
79
- ```
80
-
81
- ---
82
-
83
- ## 3. Active Safeguard Catalog & Deterministic Rules
84
-
85
- All AI agents operating in repositories provisioned by CLC Kernel are deterministically governed by the following safeguard layers:
86
-
87
- ### 📱 Frontend & UX Quality Safeguards
88
- - **`check_responsive.js`**: Enforces mobile-first responsive design, warns against fixed-width pixel overflows (`width: >400px` without media queries), and requires minimum touch targets (44x44px or 48x48px) for buttons and interactive controls.
89
- - **`check_seo.js`**: Validates essential SEO title/description tags, OpenGraph (`og:title`, `og:image`), GEO tags (`geo.position`, `ICBM`), heading hierarchy (`<h1>` uniqueness), and LCP image `priority` / `fetchpriority="high"`.
90
- - **`check_a11y.js`**: Audits ARIA roles, missing `alt` attributes on images, and keyboard navigable interactive controls.
91
- - **`check_ui_reuse.js`**: Prevents duplicate UI classes and arbitrary styling by enforcing the reuse of design system primitives in `components/ui/`.
92
- - **`check_performance.js`**: Enforces framework-native image optimization (`next/image`, `astro:assets`) and prevents barrel-file imports that break tree-shaking.
93
- - **`check_api_contracts.js`**: Enforces runtime schema validation (Zod) on all external HTTP requests and API boundaries.
94
- - **`check_storybook.js`**: Audits Storybook `.stories.tsx` coverage for all new UI primitives.
95
-
96
- ### 🏛️ Backend, Architecture & Data Safeguards
97
- - **`check_architecture.js / .py`**: Validates Clean Architecture layer boundaries (Domain -> Use Case -> Interface -> Infrastructure) and prevents presentation/route handlers from calling DB queries directly.
98
- - **`check_db_efficiency.py`**: Detects N+1 query patterns in loops and enforces eager loading (`select_related`, `prefetch_related` in Django; `joinedload` in SQLAlchemy).
99
- - **`check_migrations.py`**: Ensures database migrations (Alembic / Django) are idempotent, reversible, and do not drop tables/columns destructively without down-revisions.
100
- - **`scan_secrets.js / .py`**: Scans diffs for private keys, AWS/Stripe credentials, JWT secrets, and hardcoded connection strings.
101
- - **`check_scope.py`**: Compares modified files against the authorized scope defined in the local SDD specification.
102
- - **`verify_tdd.py`**: Validates the transition proof from a failing regression test (Red Phase) to a passing implementation (Green Phase).
103
-
104
- ### 🐳 Infrastructure & Technology Guards
105
- - **`docker_guard.py`**: Checks Dockerfiles for non-root `USER` directives, prevents `ENV` secret exposures, and requires `HEALTHCHECK` definitions.
106
- - **`celery_guard.py`**: Enforces broker URL isolation via environment variables, `@shared_task(ignore_result=True)` defaults, and secure backend configs.
107
- - **`redis_guard.py`**: Enforces `decode_responses=True`, mandatory `socket_connect_timeout`, and prevents hardcoded Redis host strings.
108
- - **`postgres_guard.py`**: Blocks raw SQL string formatting / Python f-strings in queries to prevent SQL injection vulnerabilities.
109
-
110
- ---
111
-
112
- ## 4. Recommended Ecosystem Companions
113
- CLC Kernel is strictly focused on **Governance, Process & Quality Guards**. It relies modularly on companion tools with graceful degradation:
114
- - 🧠 **Engram (MCP):** Long-term memory, session state, and architectural decision records (ADRs).
115
- - 🕸️ **Graphify:** Repository knowledge graphs and visual topology mapping.
116
- - 🛡️ **Gentle AI (RDD):** Review-Driven Development with adversarial dual-lens review gates.
117
-
118
- ---
119
-
120
- ## 5. Coding & Design Principles
121
- - **CONCEPTS > CODE:** Never add boilerplate without understanding architectural layer boundaries.
122
- - **Zero-Dependency Core:** The CLI engine and tool scripts must use native Node.js / Python built-ins wherever possible (zero runtime npm dependencies for the parser and linters).
123
- - **Polyglot Fidelity:** Native guards must be written in the ecosystem's native idioms (Go AST for Go, Python AST for Python, JS/TS AST for Node).
124
- - **Graceful Fallbacks:** If optional companion tools or optional linters are missing from the host machine, guards must degrade gracefully with advisory warnings rather than hard crashes.
@@ -1,18 +0,0 @@
1
- # ADR 001: Deterministic AI Agent Governance Engine
2
-
3
- ## Status
4
- Accepted
5
-
6
- ## Context
7
- AI coding agents generate code rapidly but introduce technical debt, layer violations, and false-positive test transitions when operating without deterministic guardrails.
8
-
9
- ## Decision
10
- Establish **CLC Kernel** (`clckernel`) as a polyglot AI Agent Governance harness implementing the AI Unified Process (AIUP):
11
- 1. **Spec-Driven Development (SDD)** with Human-in-the-Loop gates.
12
- 2. **Test-Driven Development (TDD)** requiring verified failing tests prior to implementation.
13
- 3. **Deterministic AST Linters** tailored per tech stack (Go, Python, Rust, Ruby, JS/TS, PHP).
14
- 4. **Universal IDE Symlinking** from `AGENTS.md` to all AI IDEs (Cursor, Claude, Gemini, Copilot, Antigravity).
15
-
16
- ## Consequences
17
- - Guarantees architectural integrity and zero secret leaks.
18
- - Seamlessly integrates with Engram (memory) and Graphify (knowledge graph).
@@ -1,102 +0,0 @@
1
- const { describe, it } = require('node:test');
2
- const assert = require('node:assert');
3
- const { createTmpProject, teardown } = require('../helpers');
4
-
5
- describe('AstroAdapter', () => {
6
- const AstroAdapter = require('../../src/adapters/astro');
7
-
8
- describe('constructor', () => {
9
- it('creates adapter with correct name and projectType', () => {
10
- const adapter = new AstroAdapter();
11
- assert.strictEqual(adapter.name, 'Astro Framework');
12
- assert.strictEqual(adapter.projectType, 'frontend');
13
- assert.strictEqual(adapter.testRunner, 'Vitest / Playwright');
14
- });
15
- });
16
-
17
- describe('detect()', () => {
18
- it('returns true when package.json has astro dependency', () => {
19
- const dir = createTmpProject({
20
- 'package.json': JSON.stringify({ dependencies: { astro: '4.0.0' } }),
21
- });
22
- try {
23
- const adapter = new AstroAdapter();
24
- assert.strictEqual(adapter.detect(dir), true);
25
- } finally {
26
- teardown(dir);
27
- }
28
- });
29
-
30
- it('returns true when astro.config.mjs exists', () => {
31
- const dir = createTmpProject({
32
- 'astro.config.mjs': 'export default {}',
33
- 'package.json': JSON.stringify({ dependencies: {} }),
34
- });
35
- try {
36
- const adapter = new AstroAdapter();
37
- assert.strictEqual(adapter.detect(dir), true);
38
- } finally {
39
- teardown(dir);
40
- }
41
- });
42
-
43
- it('returns true when astro.config.js exists', () => {
44
- const dir = createTmpProject({
45
- 'astro.config.js': 'module.exports = {}',
46
- 'package.json': JSON.stringify({ dependencies: {} }),
47
- });
48
- try {
49
- const adapter = new AstroAdapter();
50
- assert.strictEqual(adapter.detect(dir), true);
51
- } finally {
52
- teardown(dir);
53
- }
54
- });
55
-
56
- it('returns false when no package.json exists', () => {
57
- const dir = createTmpProject({});
58
- try {
59
- const adapter = new AstroAdapter();
60
- assert.strictEqual(adapter.detect(dir), false);
61
- } finally {
62
- teardown(dir);
63
- }
64
- });
65
-
66
- it('returns false when package.json has no astro and no config file', () => {
67
- const dir = createTmpProject({
68
- 'package.json': JSON.stringify({ dependencies: { react: '18.0.0' } }),
69
- });
70
- try {
71
- const adapter = new AstroAdapter();
72
- assert.strictEqual(adapter.detect(dir), false);
73
- } finally {
74
- teardown(dir);
75
- }
76
- });
77
- });
78
-
79
- describe('getTools()', () => {
80
- it('returns array of tool objects with required fields', () => {
81
- const adapter = new AstroAdapter();
82
- const tools = adapter.getTools();
83
- assert.ok(Array.isArray(tools));
84
- assert.ok(tools.length > 0);
85
- for (const tool of tools) {
86
- assert.ok(tool.name);
87
- assert.ok(tool.language);
88
- assert.ok(typeof tool.command === 'string');
89
- assert.ok(tool.description);
90
- }
91
- });
92
- });
93
-
94
- describe('getSafeguards()', () => {
95
- it('returns non-empty array of strings', () => {
96
- const adapter = new AstroAdapter();
97
- const guards = adapter.getSafeguards();
98
- assert.ok(Array.isArray(guards));
99
- assert.ok(guards.length >= 5);
100
- });
101
- });
102
- });
@@ -1,86 +0,0 @@
1
- const { describe, it } = require('node:test');
2
- const assert = require('node:assert');
3
- const { createTmpProject, teardown } = require('../helpers');
4
-
5
- describe('BaseAdapter', () => {
6
- const BaseAdapter = require('../../src/adapters/base');
7
-
8
- describe('constructor', () => {
9
- it('stores name, projectType, and testRunner', () => {
10
- const adapter = new BaseAdapter('Test', 'frontend', 'Jest');
11
- assert.strictEqual(adapter.name, 'Test');
12
- assert.strictEqual(adapter.projectType, 'frontend');
13
- assert.strictEqual(adapter.testRunner, 'Jest');
14
- });
15
- });
16
-
17
- describe('detect()', () => {
18
- it('returns false by default', () => {
19
- const adapter = new BaseAdapter('Test', 'frontend', 'Jest');
20
- assert.strictEqual(adapter.detect('/tmp'), false);
21
- });
22
- });
23
-
24
- describe('getSafeguards()', () => {
25
- it('returns empty array by default', () => {
26
- const adapter = new BaseAdapter('Test', 'frontend', 'Jest');
27
- assert.deepStrictEqual(adapter.getSafeguards(), []);
28
- });
29
- });
30
-
31
- describe('getTools()', () => {
32
- it('returns empty array by default', () => {
33
- const adapter = new BaseAdapter('Test', 'frontend', 'Jest');
34
- assert.deepStrictEqual(adapter.getTools(), []);
35
- });
36
- });
37
-
38
- describe('provision()', () => {
39
- it('creates docs/, sdds/, tools/ directories', () => {
40
- const adapter = new BaseAdapter('Test', 'frontend', 'Jest');
41
- const dir = createTmpProject({});
42
- try {
43
- adapter.provision(dir, {});
44
-
45
- assert.ok(require('fs').existsSync(require('path').join(dir, 'docs', 'Journal')));
46
- assert.ok(require('fs').existsSync(require('path').join(dir, 'docs', 'Architecture')));
47
- assert.ok(require('fs').existsSync(require('path').join(dir, 'docs', 'Development')));
48
- assert.ok(require('fs').existsSync(require('path').join(dir, 'sdds')));
49
- assert.ok(require('fs').existsSync(require('path').join(dir, 'tools')));
50
- } finally {
51
- teardown(dir);
52
- }
53
- });
54
-
55
- it('writes .gitignore with sdds/* entry', () => {
56
- const adapter = new BaseAdapter('Test', 'frontend', 'Jest');
57
- const dir = createTmpProject({});
58
- try {
59
- adapter.provision(dir, {});
60
-
61
- const gitignore = require('fs').readFileSync(
62
- require('path').join(dir, '.gitignore'), 'utf-8'
63
- );
64
- assert.ok(gitignore.includes('sdds/*'));
65
- } finally {
66
- teardown(dir);
67
- }
68
- });
69
-
70
- it('writes AGENTS.md with adapter name and safeguards', () => {
71
- const adapter = new BaseAdapter('TestStack', 'backend', 'Pytest');
72
- const dir = createTmpProject({});
73
- try {
74
- adapter.provision(dir, {});
75
-
76
- const agents = require('fs').readFileSync(
77
- require('path').join(dir, 'AGENTS.md'), 'utf-8'
78
- );
79
- assert.ok(agents.includes('TestStack'));
80
- assert.ok(agents.includes('BACKEND'));
81
- } finally {
82
- teardown(dir);
83
- }
84
- });
85
- });
86
- });
@@ -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
- });