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.
- package/README.es.md +51 -118
- package/README.md +48 -116
- package/package.json +14 -2
- package/src/adapters/agent-os.js +183 -0
- package/src/catalog.js +16 -0
- package/src/detector.js +2 -0
- package/src/index.js +11 -0
- package/src/ui.js +79 -35
- package/tools/audit.js +0 -0
- package/tools/audit.py +0 -0
- package/tools/check_custom.js +0 -0
- package/tools/check_custom.py +0 -0
- package/tools/check_db_efficiency.py +0 -0
- package/tools/check_domain_data.js +100 -0
- package/tools/check_hitl.js +93 -0
- package/tools/check_responsive.js +0 -0
- package/tools/check_seo.js +1 -1
- package/tools/check_symlinks.js +76 -0
- package/tools/scan_secrets.js +12 -1
- package/tools/scan_secrets.py +0 -0
- package/AGENTS.md +0 -124
- package/docs/Journal/001-adr-clckernel-governance.md +0 -18
- package/test/adapters/astro.test.js +0 -102
- package/test/adapters/base.test.js +0 -86
- package/test/adapters/django.test.js +0 -95
- package/test/adapters/fastapi.test.js +0 -88
- package/test/adapters/go.test.js +0 -81
- package/test/adapters/laravel.test.js +0 -106
- package/test/adapters/nextjs.test.js +0 -113
- package/test/adapters/rails.test.js +0 -108
- package/test/adapters/rust.test.js +0 -83
- package/test/catalog.test.js +0 -170
- package/test/config.test.js +0 -512
- package/test/detector.test.js +0 -345
- package/test/doctor.test.js +0 -302
- package/test/generator.test.js +0 -419
- package/test/guards_new.test.js +0 -95
- package/test/helpers.js +0 -36
- package/test/integration/cli-flow.test.js +0 -270
- package/test/technologies/celery_guard.test.js +0 -185
- package/test/technologies/detector.test.js +0 -342
- package/test/technologies/docker_guard.test.js +0 -187
- package/test/technologies/integration.test.js +0 -123
- package/test/technologies/postgres_guard.test.js +0 -87
- 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
|
+
}
|
package/tools/scan_secrets.js
CHANGED
|
@@ -62,7 +62,18 @@ const check = async function scanSecrets(targetDir) {
|
|
|
62
62
|
|
|
63
63
|
let content;
|
|
64
64
|
try {
|
|
65
|
-
|
|
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
|
}
|
package/tools/scan_secrets.py
CHANGED
|
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
|
-
});
|