clckernel 1.2.12 → 1.2.17
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/.agents/skills/clckernel_cli/SKILL.md +46 -0
- package/.agents/skills/clckernel_cli/reference/audit.md +16 -0
- package/.agents/skills/clckernel_cli/reference/commit.md +23 -0
- package/.agents/skills/clckernel_cli/reference/doctor.md +10 -0
- package/.agents/skills/clckernel_cli/reference/feature.md +38 -0
- package/.agents/skills/clckernel_cli/reference/fix.md +24 -0
- package/.agents/skills/clckernel_cli/reference/guard.md +23 -0
- package/.agents/skills/clckernel_cli/reference/phase.md +18 -0
- package/.agents/skills/clckernel_cli/reference/start.md +28 -0
- package/.agents/skills/clckernel_cli/reference/test.md +22 -0
- package/.agents/skills/clckernel_cli/reference/update.md +33 -0
- package/package.json +2 -1
- package/src/adapters/agent-os.js +11 -11
- package/src/catalog.js +5 -0
- package/src/config.js +1 -1
- package/src/generator.js +86 -22
- package/src/index.js +92 -7
- package/tools/check_custom.js +1 -1
- package/tools/check_hitl.js +16 -4
- /package/{src → tools}/yaml.js +0 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: clckernel-cli
|
|
3
|
+
description: >-
|
|
4
|
+
Conversational Agent Governance & Universal CLI Orchestrator for CLC Kernel.
|
|
5
|
+
Use when the user issues a clckernel command (e.g. clckernel feature, clckernel fix,
|
|
6
|
+
clckernel audit, clckernel doctor, clckernel commit, clckernel update, clckernel start)
|
|
7
|
+
or /clckernel, or asks to initiate a feature, fix a bug, audit architecture, run tests,
|
|
8
|
+
diagnose health, or commit changes in a project governed by CLC Kernel.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# 🤖 CLC Kernel — Agent Governance Engine
|
|
12
|
+
|
|
13
|
+
You are the runtime orchestrator of **CLC Kernel**. Your mission is to provide rigorous, deterministic AI agent governance across polyglot ecosystems (Next.js, FastAPI, Django, Astro, Rails, Go, Rust, Laravel, Agent-OS).
|
|
14
|
+
|
|
15
|
+
When the developer issues a \`clckernel <command>\` or \`/clckernel <command>\` prompt, enter the designated state machine and execute its playbook without skipping steps.
|
|
16
|
+
|
|
17
|
+
## Commands
|
|
18
|
+
|
|
19
|
+
| Command | Category | Description | Reference |
|
|
20
|
+
|---|---|---|---|
|
|
21
|
+
| `feature` | Lifecycle | Execute full AIUP cycle for a new feature (Spec -> HIT -> TDD -> Clean Code -> Audit) | [reference/feature.md](reference/feature.md) |
|
|
22
|
+
| `fix` | Lifecycle | Reproduce root cause with failing test, execute surgical fix, and audit | [reference/fix.md](reference/fix.md) |
|
|
23
|
+
| `test` | Verification | Run stack test runner and verify clean green exit code | [reference/test.md](reference/test.md) |
|
|
24
|
+
| `audit` | Verification | Run mandatory educational audit gate & AST validation | [reference/audit.md](reference/audit.md) |
|
|
25
|
+
| `doctor` | Diagnostics | Check configuration health, hook parity, symlinks & AST engine | [reference/doctor.md](reference/doctor.md) |
|
|
26
|
+
| `commit` | Version Control | Pre-flight audit, verify SDD scope, and commit with Conventional Commits | [reference/commit.md](reference/commit.md) |
|
|
27
|
+
| `update` | Maintenance | Non-destructive update: refresh core tools, playbooks & symlinks (preserves SDDs & custom guards) | [reference/update.md](reference/update.md) |
|
|
28
|
+
| `start` | Setup | Discover tech stack & initialize `.clckernel.yaml` | [reference/start.md](reference/start.md) |
|
|
29
|
+
| `create_guard` | Safeguards | Scaffold a new AST/linter guard in native stack language | [reference/guard.md](reference/guard.md) |
|
|
30
|
+
| `remove_guard` | Safeguards | Safely remove an existing guard and clean up config | [reference/guard.md](reference/guard.md) |
|
|
31
|
+
| `phase` | Lifecycle | Add or remove pipeline execution phases | [reference/phase.md](reference/phase.md) |
|
|
32
|
+
|
|
33
|
+
## Routing & Execution Rules
|
|
34
|
+
|
|
35
|
+
1. **Explicit command (e.g. `clckernel feature auth`, `clckernel fix button`, `clckernel audit`):**
|
|
36
|
+
Immediately load the corresponding playbook from `reference/` and execute its phases sequentially. Never skip the red TDD phase or the audit gate.
|
|
37
|
+
2. **General query or no arguments:**
|
|
38
|
+
Present available commands table and ask what action to perform.
|
|
39
|
+
3. **Natural language intent:**
|
|
40
|
+
- *"Nueva funcionalidad / feature"* -> Route to `feature` ([reference/feature.md](reference/feature.md))
|
|
41
|
+
- *"Corregir bug / arreglar error"* -> Route to `fix` ([reference/fix.md](reference/fix.md))
|
|
42
|
+
- *"Correr tests / pruebas"* -> Route to `test` ([reference/test.md](reference/test.md))
|
|
43
|
+
- *"Auditar / verificar arquitectura"* -> Route to `audit` ([reference/audit.md](reference/audit.md))
|
|
44
|
+
- *"Revisar estado / diagnóstico"* -> Route to `doctor` ([reference/doctor.md](reference/doctor.md))
|
|
45
|
+
- *"Commitear / guardar cambios"* -> Route to `commit` ([reference/commit.md](reference/commit.md))
|
|
46
|
+
4. **Mandatory Audit Gate:** Never declare success on code changes without running the audit gate and outputting the summary.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Playbook: Mandatory Educational Audit Gate
|
|
2
|
+
|
|
3
|
+
Use this playbook when auditing code quality, architecture compliance, or executing the mandatory audit gate (via `/clc audit`, `/clckernel audit`, or conversational request like *"corré la auditoría del kernel"*).
|
|
4
|
+
|
|
5
|
+
## Execution
|
|
6
|
+
|
|
7
|
+
1. **Locate Audit Tool:** Check available audit scripts:
|
|
8
|
+
- `node tools/audit.js` (JavaScript / Node environment)
|
|
9
|
+
- `python tools/audit.py` (Python / Backend environment)
|
|
10
|
+
2. **Execute Audit:** Run the tool within project workspace.
|
|
11
|
+
3. **Educational Summary:** Output the educational audit summary highlighting:
|
|
12
|
+
- AST Architectural conformance
|
|
13
|
+
- TDD compliance & test coverage
|
|
14
|
+
- Security & Secrets scan results
|
|
15
|
+
- Performance / DB efficiency checks
|
|
16
|
+
4. **Actionable Feedback:** If any guard fails, explain the exact violation, why it breaches architectural invariants, and how to remediate it.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# 📦 Playbook: \`clckernel commit <message>\`
|
|
2
|
+
|
|
3
|
+
Safely commit changes after verifying all pre-flight quality and architecture gates.
|
|
4
|
+
|
|
5
|
+
## Mandatory Execution Protocol
|
|
6
|
+
|
|
7
|
+
### Step 1: Pre-Flight AST Audit
|
|
8
|
+
1. Run \`node tools/audit.js\` or \`python tools/audit.py\`.
|
|
9
|
+
2. Ensure 100% of discovered guards pass with 0 errors and 0 secret leaks.
|
|
10
|
+
|
|
11
|
+
### Step 2: Scope Verification
|
|
12
|
+
1. Compare \`git diff --cached\` (or unstaged diff) against the active SDD spec in \`sdds/{change}/\`.
|
|
13
|
+
2. Verify that no files outside the agreed feature scope are staged.
|
|
14
|
+
|
|
15
|
+
### Step 3: Conventional Commit Formatting
|
|
16
|
+
1. Format commit message according to Conventional Commits:
|
|
17
|
+
- \`feat(...): ...\`
|
|
18
|
+
- \`fix(...): ...\`
|
|
19
|
+
- \`refactor(...): ...\`
|
|
20
|
+
- \`test(...): ...\`
|
|
21
|
+
- \`docs(...): ...\`
|
|
22
|
+
2. **STRICT LAW:** NEVER add "Co-Authored-By", AI signatures, or AI attribution.
|
|
23
|
+
3. Stage required files and commit cleanly.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Playbook: Kernel Doctor & Diagnostics
|
|
2
|
+
|
|
3
|
+
Use this playbook to diagnose environment health, detect hook drift, or verify configuration integrity (via `/clc doctor`, `/clckernel doctor`, or conversational request like *"revisá el estado del kernel"*).
|
|
4
|
+
|
|
5
|
+
## Checks
|
|
6
|
+
|
|
7
|
+
1. **Config Integrity:** Validate `.clckernel.yaml` syntax and presence of required sections (`framework`, `language`, `project_type`, `safeguards`).
|
|
8
|
+
2. **Hook Parity:** Check that git hooks (`.githooks/pre-commit` or `.husky/pre-commit`) are installed and executable (`chmod +x`).
|
|
9
|
+
3. **Tools & Guards Health:** Verify that scripts referenced in `.clckernel.yaml` exist in `tools/` and run without syntax errors.
|
|
10
|
+
4. **Drift Remediation:** If drift is detected, offer automatic fix or report clear steps.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# 🚀 Playbook: \`clckernel feature <name>\`
|
|
2
|
+
|
|
3
|
+
Guide the developer through implementing a new feature strictly governed by the **AI Unified Process (AIUP)**.
|
|
4
|
+
|
|
5
|
+
## Mandatory Execution Protocol
|
|
6
|
+
|
|
7
|
+
### Step 1: Research & Impact Analysis
|
|
8
|
+
1. Inspect the codebase, existing modules, and architecture layers before writing any code.
|
|
9
|
+
2. Determine affected architectural layers (Domain, Use Cases, Infrastructure, Presentation).
|
|
10
|
+
|
|
11
|
+
### Step 2: Local SDD Specification
|
|
12
|
+
1. Create a local specification file at \`sdds/{name}/spec.md\` (or \`sdds/{name}/01-spec.md\`).
|
|
13
|
+
2. Document:
|
|
14
|
+
- Feature purpose and user requirements.
|
|
15
|
+
- API / Domain contracts and interfaces.
|
|
16
|
+
- Affected files and strict layer boundaries.
|
|
17
|
+
- Planned test scenarios (unit, integration, edge cases).
|
|
18
|
+
|
|
19
|
+
### Step 3: Human-in-the-Loop (HIT) Review Gate
|
|
20
|
+
1. Present the proposed SDD spec, contracts, and test scenarios directly to the user in chat.
|
|
21
|
+
2. **STOP.** Explicitly ask for developer confirmation:
|
|
22
|
+
> *"Review the proposed contracts and test scenarios above. Shall we proceed to the Red TDD phase?"*
|
|
23
|
+
3. Wait for the user's approval before touching any production source files.
|
|
24
|
+
|
|
25
|
+
### Step 4: TDD Red Phase (Failing Test)
|
|
26
|
+
1. Write a failing regression or unit test demonstrating the new feature requirement.
|
|
27
|
+
2. Run the test suite and verify it fails with a non-zero exit code.
|
|
28
|
+
3. Confirm that the failure message proves the feature is missing or non-functional.
|
|
29
|
+
|
|
30
|
+
### Step 5: Clean Implementation (Green Phase)
|
|
31
|
+
1. Implement the minimal clean code necessary to make the failing test pass.
|
|
32
|
+
2. Maintain strict Clean Architecture boundaries (Domain -> Use Case -> Infrastructure).
|
|
33
|
+
3. Re-run tests to confirm 100% green pass.
|
|
34
|
+
|
|
35
|
+
### Step 6: Mandatory AST Audit Gate
|
|
36
|
+
1. Execute \`node tools/audit.js\` or \`python tools/audit.py\`.
|
|
37
|
+
2. Output the Educational Summary table to stdout.
|
|
38
|
+
3. Verify zero secrets, zero layer violations, and zero N+1 queries.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# 🔧 Playbook: \`clckernel fix <issue>\`
|
|
2
|
+
|
|
3
|
+
Enforce deterministic bug fixing without quick-fix shortcuts or superficial patches.
|
|
4
|
+
|
|
5
|
+
## Mandatory Execution Protocol
|
|
6
|
+
|
|
7
|
+
### Step 1: Root Cause & Traceback Analysis
|
|
8
|
+
1. Analyze the issue description, error logs, and stack trace.
|
|
9
|
+
2. Inspect the relevant files and surrounding architecture to pinpoint the exact failure mechanism.
|
|
10
|
+
3. **DO NOT** edit source code directly to "try things out".
|
|
11
|
+
|
|
12
|
+
### Step 2: TDD Red Phase (Regression Test)
|
|
13
|
+
1. Write a focused test that reproduces the bug demonstrably.
|
|
14
|
+
2. Run the test and verify it fails with the exact error described.
|
|
15
|
+
3. Log the failure output as proof of regression reproduction.
|
|
16
|
+
|
|
17
|
+
### Step 3: Surgical Clean Architecture Fix (Green Phase)
|
|
18
|
+
1. Apply the minimal surgical change addressing the root cause.
|
|
19
|
+
2. Respect layer isolation: do not bypass Domain or Use Case rules for convenience.
|
|
20
|
+
3. Run the regression test and confirm it passes in green.
|
|
21
|
+
|
|
22
|
+
### Step 4: Mandatory Audit Gate
|
|
23
|
+
1. Execute \`node tools/audit.js\` or \`python tools/audit.py\`.
|
|
24
|
+
2. Output the Educational Summary table to verify no side effects or guard violations occurred.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Playbook: Safeguards Management (Create & Remove)
|
|
2
|
+
|
|
3
|
+
Use this playbook when the user wants to add, modify, or delete custom architecture/security guards (via `/clc create_guard`, `/clc remove_guard`, or natural language like *"creá un guard para bloquear imports prohibidos"*).
|
|
4
|
+
|
|
5
|
+
## 🛠️ Create Guard
|
|
6
|
+
|
|
7
|
+
1. **Clarify Objective:** If not specified, ask what AST node, pattern, or security risk to intercept. **STOP.** Wait for details.
|
|
8
|
+
2. **Native Language Implementation:** Write the guard script in the native stack language:
|
|
9
|
+
- Python projects -> Python `ast` script in `tools/guards/<name>_guard.py`
|
|
10
|
+
- Node / TS projects -> JS/TS script in `tools/guards/<name>_guard.js`
|
|
11
|
+
- Go projects -> Go `go/parser` / `ast` script
|
|
12
|
+
- Ruby projects -> Ruby `parser` gem script
|
|
13
|
+
3. **Register in Config:** Add the guard definition under `custom_safeguards` or `tools` in `.clckernel.yaml`.
|
|
14
|
+
4. **Validation:** Run the script against test fixtures to confirm it flags violations and exits cleanly on compliant code.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 🗑️ Remove Guard
|
|
19
|
+
|
|
20
|
+
1. Identify the target guard from user request or ask for confirmation.
|
|
21
|
+
2. Update `.clckernel.yaml` removing the guard entry.
|
|
22
|
+
3. If requested, delete the corresponding file from `tools/guards/`.
|
|
23
|
+
4. Confirm removal cleanly.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Playbook: Execution Phases Management
|
|
2
|
+
|
|
3
|
+
Use this playbook when adding, modifying, or removing lifecycle phases in the AIUP governance pipeline (via `/clc add_phase`, `/clc remove_phase`, or conversational request like *"agregá una fase de smoke test antes del commit"*).
|
|
4
|
+
|
|
5
|
+
## Guidelines
|
|
6
|
+
|
|
7
|
+
1. **Understand Phase Purpose:**
|
|
8
|
+
- Pre-implementation (e.g. Research, SDD spec, RFC alignment)
|
|
9
|
+
- Implementation (e.g. TDD Red/Green, Layer Check)
|
|
10
|
+
- Verification / Audit (e.g. AST Audit, E2E, Smoke test)
|
|
11
|
+
- Delivery (e.g. Git pre-commit, PR generation)
|
|
12
|
+
|
|
13
|
+
2. **Update `.clckernel.yaml`:**
|
|
14
|
+
- Preserve execution order and dependencies.
|
|
15
|
+
- Specify whether the phase is blocking (`required: true`) or advisory (`required: false`).
|
|
16
|
+
|
|
17
|
+
3. **Confirm & Validate:**
|
|
18
|
+
- Present the updated pipeline flow to the user.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Playbook: Start / Initialize CLC Kernel
|
|
2
|
+
|
|
3
|
+
Use this playbook when the user wants to initialize, start, or set up governance in a project (via `/clc start`, `/clckernel start`, or conversational request like *"inicializar el proyecto con clc"*).
|
|
4
|
+
|
|
5
|
+
## Sequence
|
|
6
|
+
|
|
7
|
+
### Phase 1: Silent Discovery
|
|
8
|
+
1. Inspect project root files without asking. Look for signatures:
|
|
9
|
+
- `package.json` -> Next.js / Astro / Node
|
|
10
|
+
- `requirements.txt` / `pyproject.toml` -> FastAPI / Django
|
|
11
|
+
- `go.mod` -> Go
|
|
12
|
+
- `Cargo.toml` -> Rust
|
|
13
|
+
- `Gemfile` -> Rails
|
|
14
|
+
- `composer.json` -> Laravel
|
|
15
|
+
- `Dockerfile`, `docker-compose.yml`, `redis.conf`, `celery.py` -> Secondary tech stack
|
|
16
|
+
2. **Rule:** Never guess the tech stack. Inspect the actual repository files.
|
|
17
|
+
|
|
18
|
+
### Phase 2: Interactive Alignment
|
|
19
|
+
Present detected stack and proposal clearly:
|
|
20
|
+
1. Summarize detected architecture and auto-provisioned safeguards (e.g., TDD Red/Green, AST architecture guard, DB efficiency, secrets scan, tech-specific guards).
|
|
21
|
+
2. Ask one clear question:
|
|
22
|
+
- *"¿Activamos estas fases y salvaguardas estándar, o querés personalizar alguna regla o fase?"*
|
|
23
|
+
3. **STOP.** Wait for the user's response.
|
|
24
|
+
|
|
25
|
+
### Phase 3: Materialization
|
|
26
|
+
1. Write or update `.clckernel.yaml` at project root.
|
|
27
|
+
2. If hooks are configured, ensure `.githooks/pre-commit` or `.husky/pre-commit` are provisioned.
|
|
28
|
+
3. Confirm completion with a concise summary.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# 🧪 Playbook: \`clckernel test\`
|
|
2
|
+
|
|
3
|
+
Execute the repository test suite within the native environment runner.
|
|
4
|
+
|
|
5
|
+
## Mandatory Execution Protocol
|
|
6
|
+
|
|
7
|
+
### Step 1: Detect Test Runner
|
|
8
|
+
1. Inspect \`.clckernel.yaml\` or project files to identify the configured test runner:
|
|
9
|
+
- Node: \`npm test\` / \`vitest\` / \`jest\`
|
|
10
|
+
- Python: \`pytest\`
|
|
11
|
+
- Go: \`go test ./...\`
|
|
12
|
+
- Ruby: \`bundle exec rspec\`
|
|
13
|
+
- Rust: \`cargo test\`
|
|
14
|
+
- PHP: \`vendor/bin/pest\` / \`phpunit\`
|
|
15
|
+
|
|
16
|
+
### Step 2: Run Tests
|
|
17
|
+
1. Execute the test command.
|
|
18
|
+
2. If tests fail:
|
|
19
|
+
- Provide clear failure tracebacks.
|
|
20
|
+
- Propose fixing through the \`clckernel fix\` protocol.
|
|
21
|
+
3. If tests pass:
|
|
22
|
+
- Confirm green test status (exit code 0).
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Playbook: Update / Upgrade CLC Kernel
|
|
2
|
+
|
|
3
|
+
Use this playbook when the user wants to update an existing project to the latest version of CLC Kernel (via `/clc update`, `clckernel update`, `npx clckernel update`, or conversational request like *"actualizar clckernel en el proyecto"*).
|
|
4
|
+
|
|
5
|
+
## Invariants: The Non-Destructive Update Contract
|
|
6
|
+
|
|
7
|
+
1. **NEVER DELETE OR OVERWRITE `sdds/`:** All local specifications (`sdds/*/spec.md`, `plan.md`) must stay 100% intact.
|
|
8
|
+
2. **NEVER TOUCH CUSTOM GUARDS:** Any user-defined scripts under `tools/guards/` are preserved without alteration.
|
|
9
|
+
3. **NEVER OVERWRITE CONFIGURATION:** Existing `.clckernel.yaml` or `.clc-forge.yaml` settings and custom thresholds remain intact.
|
|
10
|
+
4. **NEVER MODIFY APPLICATION CODE:** Source files, domain layers, and application tests are never touched.
|
|
11
|
+
|
|
12
|
+
## Update Sequence
|
|
13
|
+
|
|
14
|
+
### Phase 1: Inspection & Detection
|
|
15
|
+
1. Verify the project is already governed by checking for `AGENTS.md`, `.clckernel.yaml`, `.husky/pre-commit`, or `.githooks/pre-commit`.
|
|
16
|
+
2. Inspect the active framework and test runner.
|
|
17
|
+
|
|
18
|
+
### Phase 2: Refresh Core Infrastructure
|
|
19
|
+
1. Refresh bundled AST audit tools in `tools/`:
|
|
20
|
+
- `tools/audit.js` / `tools/audit.py`
|
|
21
|
+
- Built-in guards (`scan_secrets.js`, `check_responsive.js`, `check_seo.js`, `check_db_efficiency.py`, `check_custom.js`, `docker_guard.py`, etc.)
|
|
22
|
+
2. Refresh Agent Skills & Playbooks:
|
|
23
|
+
- Synchronize `.agents/skills/clckernel_cli/` with latest playbooks and references.
|
|
24
|
+
3. Refresh Cross-IDE Rule Symlinks:
|
|
25
|
+
- Ensure `CLAUDE.md`, `GEMINI.md`, `.cursorrules`, `.cursor/rules/*.mdc`, `.github/copilot-instructions.md`, and `.antigravity/rules.md` link to `AGENTS.md`.
|
|
26
|
+
4. Refresh Pre-commit Hook:
|
|
27
|
+
- Ensure the git pre-commit hook runs the latest standard guards while preserving any custom checks.
|
|
28
|
+
|
|
29
|
+
### Phase 3: Verification & Reporting
|
|
30
|
+
1. Run `node tools/audit.js` or `python tools/audit.py` to verify harness health.
|
|
31
|
+
2. Output a transparent Educational Summary highlighting:
|
|
32
|
+
- What was updated (core tools, playbooks, symlinks)
|
|
33
|
+
- What was preserved (SDDs, custom guards, YAML configuration)
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clckernel",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.17",
|
|
4
4
|
"description": "CLC Kernel — The Universal Polyglot AI Agent Governance Engine (Next.js, FastAPI, Django, Astro, Rails, Go, Rust, Laravel).",
|
|
5
5
|
"main": "./bin/cli.js",
|
|
6
6
|
"files": [
|
|
7
7
|
"bin",
|
|
8
8
|
"src",
|
|
9
9
|
"tools",
|
|
10
|
+
".agents/skills/clckernel_cli",
|
|
10
11
|
"README.md",
|
|
11
12
|
"README.es.md"
|
|
12
13
|
],
|
package/src/adapters/agent-os.js
CHANGED
|
@@ -76,36 +76,36 @@ class AgentOSAdapter extends BaseAdapter {
|
|
|
76
76
|
return [
|
|
77
77
|
{
|
|
78
78
|
name: 'check_symlinks',
|
|
79
|
-
language: '
|
|
80
|
-
path: '
|
|
79
|
+
language: 'js',
|
|
80
|
+
path: 'check_symlinks.js',
|
|
81
81
|
command: 'node tools/check_symlinks.js',
|
|
82
82
|
description: 'AI IDE symlinks and rule mirror verification'
|
|
83
83
|
},
|
|
84
84
|
{
|
|
85
85
|
name: 'check_hitl',
|
|
86
|
-
language: '
|
|
87
|
-
path: '
|
|
86
|
+
language: 'js',
|
|
87
|
+
path: 'check_hitl.js',
|
|
88
88
|
command: 'node tools/check_hitl.js',
|
|
89
89
|
description: 'Human-in-the-Loop review checkpoint validator'
|
|
90
90
|
},
|
|
91
91
|
{
|
|
92
92
|
name: 'check_domain_data',
|
|
93
|
-
language: '
|
|
94
|
-
path: '
|
|
93
|
+
language: 'js',
|
|
94
|
+
path: 'check_domain_data.js',
|
|
95
95
|
command: 'node tools/check_domain_data.js',
|
|
96
96
|
description: 'Domain deliverables schema and clean state integrity'
|
|
97
97
|
},
|
|
98
98
|
{
|
|
99
99
|
name: 'check_test_calendar_sync',
|
|
100
|
-
language: '
|
|
101
|
-
path: '
|
|
100
|
+
language: 'js',
|
|
101
|
+
path: 'check_test_calendar_sync.js',
|
|
102
102
|
command: 'node tools/check_test_calendar_sync.js',
|
|
103
103
|
description: 'Test calendar harness synchronization and parity check'
|
|
104
104
|
},
|
|
105
105
|
{
|
|
106
106
|
name: 'scan_secrets',
|
|
107
|
-
language: '
|
|
108
|
-
path: '
|
|
107
|
+
language: 'js',
|
|
108
|
+
path: 'scan_secrets.js',
|
|
109
109
|
command: 'node tools/scan_secrets.js',
|
|
110
110
|
description: 'Security & API key leak scanner'
|
|
111
111
|
}
|
|
@@ -198,7 +198,7 @@ ${this.getSafeguards().map(s => `- ${s}`).join('\n')}
|
|
|
198
198
|
// 4. Copy domain guard tools into tools/
|
|
199
199
|
const srcToolsDir = path.join(__dirname, '..', '..', 'tools');
|
|
200
200
|
const targetToolsDir = path.join(targetDir, 'tools');
|
|
201
|
-
const toolsToCopy = ['check_symlinks.js', 'check_hitl.js', 'check_domain_data.js', 'scan_secrets.js', 'audit.js'];
|
|
201
|
+
const toolsToCopy = ['check_symlinks.js', 'check_hitl.js', 'check_domain_data.js', 'check_test_calendar_sync.js', 'scan_secrets.js', 'audit.js'];
|
|
202
202
|
|
|
203
203
|
for (const toolFile of toolsToCopy) {
|
|
204
204
|
const srcFile = path.join(srcToolsDir, toolFile);
|
package/src/catalog.js
CHANGED
|
@@ -125,6 +125,11 @@ const SAFEGUARD_CATALOG = [
|
|
|
125
125
|
applies_when: 'agent-os', severity: 'error', gate_mode: 'hard', config_keys: ['scope'],
|
|
126
126
|
phase: 'audit', file: 'check_domain_data.js', manifest: true, planned: false },
|
|
127
127
|
|
|
128
|
+
{ name: 'check_test_calendar_sync', labels: ['check_test_calendar_sync', 'check_test_calendar_sync.js'],
|
|
129
|
+
checks: 'Test calendar harness synchronization and parity check', verify: ['verify_calendar_sync', 'verify_test_calendar_sync'],
|
|
130
|
+
applies_when: 'agent-os', severity: 'error', gate_mode: 'hard', config_keys: ['scope'],
|
|
131
|
+
phase: 'audit', file: 'check_test_calendar_sync.js', manifest: true, planned: false },
|
|
132
|
+
|
|
128
133
|
{ name: 'ruff', labels: ['ruff'],
|
|
129
134
|
checks: 'Python AST linter (external CLI)', verify: ['verify_python_lint'],
|
|
130
135
|
applies_when: 'backend', severity: 'warning', gate_mode: 'advisory', config_keys: [],
|
package/src/config.js
CHANGED
package/src/generator.js
CHANGED
|
@@ -8,9 +8,33 @@ const path = require('path');
|
|
|
8
8
|
const { execSync } = require('child_process');
|
|
9
9
|
const { resolveConfig } = require('./config.js');
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Detects whether a Node CLI required by a hook command is actually installed
|
|
13
|
+
* in the target project. Resolving through node_modules/.bin is what `npx` does
|
|
14
|
+
* first, so a missing entry there means `npx` would download it on the fly (or
|
|
15
|
+
* fail inside a commit hook, where the network is not available).
|
|
16
|
+
* @param {string} targetDir - Target project root
|
|
17
|
+
* @param {string} pkg - Package name to look for
|
|
18
|
+
* @returns {boolean}
|
|
19
|
+
*/
|
|
20
|
+
function isCliInstalled(targetDir, pkg) {
|
|
21
|
+
if (!pkg) return false;
|
|
22
|
+
try {
|
|
23
|
+
return fs.existsSync(path.join(targetDir, 'node_modules', '.bin', pkg));
|
|
24
|
+
} catch (e) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
11
29
|
/**
|
|
12
30
|
* Generates a pre-commit hook dynamically from the adapter's tool manifest.
|
|
13
31
|
* Frontend projects use .husky; backend projects use .githooks.
|
|
32
|
+
*
|
|
33
|
+
* External CLIs (language === 'external', e.g. tsc, lint-staged) are only emitted
|
|
34
|
+
* when the target project already depends on them: the generator does not add
|
|
35
|
+
* dependencies, so referencing an uninstalled binary produces a hook that fails
|
|
36
|
+
* on every commit.
|
|
37
|
+
*
|
|
14
38
|
* @param {object} adapter - Adapter with getTools() and projectType
|
|
15
39
|
* @param {string} targetDir - Target project root
|
|
16
40
|
*/
|
|
@@ -23,10 +47,17 @@ function generateHookFromManifest(adapter, targetDir) {
|
|
|
23
47
|
: path.join(targetDir, '.githooks');
|
|
24
48
|
const hookFile = path.join(hookDir, 'pre-commit');
|
|
25
49
|
|
|
50
|
+
// The husky bootstrap only exists when the project ran `husky init`. Source it
|
|
51
|
+
// only in that case, so a generated hook never points at a missing file.
|
|
52
|
+
const huskyBootstrap = path.join(hookDir, '_', 'husky.sh');
|
|
53
|
+
const useHuskyBootstrap = isFront && fs.existsSync(huskyBootstrap);
|
|
54
|
+
|
|
26
55
|
const lines = [];
|
|
27
56
|
if (isFront) {
|
|
28
57
|
lines.push('#!/usr/bin/env sh');
|
|
29
|
-
|
|
58
|
+
if (useHuskyBootstrap) {
|
|
59
|
+
lines.push('. "$(dirname -- "$0")/_/husky.sh"');
|
|
60
|
+
}
|
|
30
61
|
lines.push('');
|
|
31
62
|
} else {
|
|
32
63
|
lines.push('#!/usr/bin/env bash');
|
|
@@ -34,13 +65,26 @@ function generateHookFromManifest(adapter, targetDir) {
|
|
|
34
65
|
lines.push('');
|
|
35
66
|
}
|
|
36
67
|
|
|
68
|
+
const skipped = [];
|
|
37
69
|
tools.forEach(tool => {
|
|
38
70
|
if (!tool.command) return;
|
|
71
|
+
if (tool.language === 'external') {
|
|
72
|
+
if (!isCliInstalled(targetDir, tool.extCLI)) {
|
|
73
|
+
skipped.push(tool.extCLI || tool.command);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
39
77
|
lines.push(tool.command);
|
|
40
78
|
});
|
|
41
79
|
|
|
80
|
+
if (skipped.length > 0) {
|
|
81
|
+
lines.push('');
|
|
82
|
+
lines.push(`# Not run here (not a dependency of this project): ${skipped.join(', ')}`);
|
|
83
|
+
lines.push('# Install it and re-run `clckernel update` to enable it.');
|
|
84
|
+
}
|
|
85
|
+
|
|
42
86
|
fs.mkdirSync(hookDir, { recursive: true });
|
|
43
|
-
fs.writeFileSync(hookFile, lines.join('\n'), 'utf-8');
|
|
87
|
+
fs.writeFileSync(hookFile, lines.join('\n') + '\n', 'utf-8');
|
|
44
88
|
try { fs.chmodSync(hookFile, '755'); } catch (e) {}
|
|
45
89
|
|
|
46
90
|
if (!isFront) {
|
|
@@ -62,6 +106,10 @@ function installToolsFromManifest(targetDir, manifest, config) {
|
|
|
62
106
|
const sourceToolsDir = path.join(__dirname, '..', 'tools');
|
|
63
107
|
const isFront = config.projectType === 'frontend';
|
|
64
108
|
|
|
109
|
+
// tools/ must exist before anything is copied into it. Provisioning a project
|
|
110
|
+
// that has no tools/ yet used to throw ENOENT on the first copyFileSync.
|
|
111
|
+
fs.mkdirSync(path.join(targetDir, 'tools'), { recursive: true });
|
|
112
|
+
|
|
65
113
|
// Validate descriptors: skip + warn tools missing required fields
|
|
66
114
|
const validTools = manifest.filter(tool => {
|
|
67
115
|
if (!tool.name || !tool.language || !tool.command) {
|
|
@@ -72,7 +120,8 @@ function installToolsFromManifest(targetDir, manifest, config) {
|
|
|
72
120
|
});
|
|
73
121
|
|
|
74
122
|
// Always copy the audit orchestrator for the right language
|
|
75
|
-
const
|
|
123
|
+
const isNodeStack = config.projectType === 'frontend' || config.projectType === 'agent-os';
|
|
124
|
+
const orchestrator = isNodeStack ? 'audit.js' : 'audit.py';
|
|
76
125
|
const orchestratorSrc = path.join(sourceToolsDir, orchestrator);
|
|
77
126
|
const orchestratorDest = path.join(targetDir, 'tools', orchestrator);
|
|
78
127
|
if (fs.existsSync(orchestratorSrc)) {
|
|
@@ -93,25 +142,29 @@ function installToolsFromManifest(targetDir, manifest, config) {
|
|
|
93
142
|
}
|
|
94
143
|
});
|
|
95
144
|
|
|
96
|
-
// check_custom.js
|
|
97
|
-
//
|
|
145
|
+
// check_custom.js parses the project YAML config, so the generated project
|
|
146
|
+
// must ship the parser alongside it.
|
|
98
147
|
if (validTools.some(tool => tool.path === 'check_custom.js')) {
|
|
99
|
-
installYamlModule(targetDir
|
|
148
|
+
installYamlModule(targetDir);
|
|
100
149
|
}
|
|
101
150
|
}
|
|
102
151
|
|
|
103
152
|
/**
|
|
104
|
-
* Copies the zero-dependency YAML parser into
|
|
105
|
-
*
|
|
153
|
+
* Copies the zero-dependency YAML parser into the target's tools/ directory.
|
|
154
|
+
*
|
|
155
|
+
* It deliberately does NOT go into the consumer's src/: that directory belongs
|
|
156
|
+
* to the project, and writing there both pollutes it and silently overwrites a
|
|
157
|
+
* consumer file that happens to be named yaml.js. tools/ is the directory the
|
|
158
|
+
* harness provisions and owns.
|
|
159
|
+
*
|
|
106
160
|
* @param {string} targetDir - Target project root
|
|
107
|
-
* @param {string} sourceToolsDir - Bundled tools/ directory of this package
|
|
108
161
|
*/
|
|
109
|
-
function installYamlModule(targetDir
|
|
110
|
-
const yamlSrc = path.join(
|
|
162
|
+
function installYamlModule(targetDir) {
|
|
163
|
+
const yamlSrc = path.join(__dirname, '..', 'tools', 'yaml.js');
|
|
111
164
|
if (!fs.existsSync(yamlSrc)) return;
|
|
112
|
-
const
|
|
113
|
-
fs.mkdirSync(
|
|
114
|
-
fs.copyFileSync(yamlSrc,
|
|
165
|
+
const toolsDest = path.join(targetDir, 'tools');
|
|
166
|
+
fs.mkdirSync(toolsDest, { recursive: true });
|
|
167
|
+
fs.copyFileSync(yamlSrc, path.join(toolsDest, 'yaml.js'));
|
|
115
168
|
}
|
|
116
169
|
|
|
117
170
|
/**
|
|
@@ -139,7 +192,7 @@ function installFallbackTools(targetDir) {
|
|
|
139
192
|
});
|
|
140
193
|
|
|
141
194
|
if (fallbackFiles.includes('check_custom.js')) {
|
|
142
|
-
installYamlModule(targetDir
|
|
195
|
+
installYamlModule(targetDir);
|
|
143
196
|
}
|
|
144
197
|
}
|
|
145
198
|
|
|
@@ -155,9 +208,12 @@ function generateFallbackHook(targetDir, projectType) {
|
|
|
155
208
|
const hookDir = path.join(targetDir, '.husky');
|
|
156
209
|
fs.mkdirSync(hookDir, { recursive: true });
|
|
157
210
|
const hookFile = path.join(hookDir, 'pre-commit');
|
|
211
|
+
// Only source the husky bootstrap when the project actually has it.
|
|
212
|
+
const bootstrap = fs.existsSync(path.join(hookDir, '_', 'husky.sh'))
|
|
213
|
+
? '. "$(dirname -- "$0")/_/husky.sh"\n'
|
|
214
|
+
: '';
|
|
158
215
|
fs.writeFileSync(hookFile, `#!/usr/bin/env sh
|
|
159
|
-
|
|
160
|
-
|
|
216
|
+
${bootstrap}
|
|
161
217
|
node tools/scan_secrets.js || true
|
|
162
218
|
`, 'utf-8');
|
|
163
219
|
try { fs.chmodSync(hookFile, '755'); } catch (e) {}
|
|
@@ -434,19 +490,27 @@ function installAgenticCLI(targetDir) {
|
|
|
434
490
|
symlinks.forEach(link => {
|
|
435
491
|
const linkPath = path.join(targetDir, link);
|
|
436
492
|
const linkDir = path.dirname(linkPath);
|
|
437
|
-
|
|
493
|
+
|
|
438
494
|
if (!fs.existsSync(linkDir)) {
|
|
439
495
|
fs.mkdirSync(linkDir, { recursive: true });
|
|
440
496
|
}
|
|
441
497
|
|
|
442
498
|
const relPath = path.relative(linkDir, path.join(targetDir, sourceFile));
|
|
443
|
-
|
|
499
|
+
|
|
444
500
|
try {
|
|
501
|
+
// Only replace an existing symlink (a stale mirror needs refreshing).
|
|
502
|
+
// A regular file belongs to the consumer: unlinking it would destroy
|
|
503
|
+
// their own rules, so leave it in place instead.
|
|
504
|
+
let existing = null;
|
|
445
505
|
try {
|
|
446
|
-
|
|
447
|
-
fs.unlinkSync(linkPath);
|
|
448
|
-
}
|
|
506
|
+
existing = fs.lstatSync(linkPath);
|
|
449
507
|
} catch (err) {}
|
|
508
|
+
|
|
509
|
+
if (existing) {
|
|
510
|
+
if (!existing.isSymbolicLink()) return;
|
|
511
|
+
fs.unlinkSync(linkPath);
|
|
512
|
+
}
|
|
513
|
+
|
|
450
514
|
fs.symlinkSync(relPath, linkPath, 'file');
|
|
451
515
|
} catch (e) {
|
|
452
516
|
// Ignorar fallos de permisos o symlinks existentes
|
package/src/index.js
CHANGED
|
@@ -9,25 +9,110 @@ const { printBanner, printBox, promptOptions, printSuccess, colors } = require('
|
|
|
9
9
|
const { generateHarness } = require('./generator');
|
|
10
10
|
const { runDoctor } = require('./doctor');
|
|
11
11
|
|
|
12
|
+
/** Commands that only read state. Safe to run against any project. */
|
|
13
|
+
const READ_ONLY_COMMANDS = new Set(['help', '--help', '-h', 'version', '--version', '-v']);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Flags that explicitly opt into the installation path. Everything else is
|
|
17
|
+
* rejected: a destructive default is fixed by making the default non-destructive.
|
|
18
|
+
*/
|
|
19
|
+
const INSTALL_FLAGS = new Set(['--yes', '-y']);
|
|
20
|
+
|
|
21
|
+
const HELP_TEXT = `
|
|
22
|
+
${'clckernel'} — CLC Kernel: The AI Agent Governance Engine
|
|
23
|
+
|
|
24
|
+
USAGE
|
|
25
|
+
clckernel <command> [targetDir]
|
|
26
|
+
|
|
27
|
+
READ-ONLY COMMANDS (never modify your project)
|
|
28
|
+
help, --help, -h Show this help
|
|
29
|
+
version, --version, -v Print the installed version
|
|
30
|
+
doctor [targetDir] Run diagnostics on a project
|
|
31
|
+
update [targetDir] Refresh tools, playbooks and IDE symlinks
|
|
32
|
+
|
|
33
|
+
INSTALLATION (writes to the target project)
|
|
34
|
+
clckernel [targetDir] --yes Provision the governance harness
|
|
35
|
+
|
|
36
|
+
Requires an explicit opt-in. Bare \`clckernel\` runs the interactive
|
|
37
|
+
confirmation flow; with no TTY it refuses to install.
|
|
38
|
+
|
|
39
|
+
EXAMPLES
|
|
40
|
+
clckernel --help
|
|
41
|
+
clckernel doctor .
|
|
42
|
+
clckernel update .
|
|
43
|
+
clckernel . --yes
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Reject any input that is not a recognized command or install flag.
|
|
48
|
+
* The rejected tokens are echoed so the user can see what was not understood.
|
|
49
|
+
* @param {string[]} args - Raw argv (without node/script)
|
|
50
|
+
* @returns {number} Process exit code
|
|
51
|
+
*/
|
|
52
|
+
function rejectUnknownInput(args) {
|
|
53
|
+
const rejected = args.join(' ');
|
|
54
|
+
process.stderr.write(
|
|
55
|
+
`\n❌ Unknown input: ${rejected}\n` +
|
|
56
|
+
' clckernel will not modify your project on unrecognized input.\n' +
|
|
57
|
+
' Run `clckernel --help` to see available commands.\n\n'
|
|
58
|
+
);
|
|
59
|
+
return 1;
|
|
60
|
+
}
|
|
61
|
+
|
|
12
62
|
async function runCli() {
|
|
13
63
|
const args = process.argv.slice(2);
|
|
14
64
|
const command = args[0];
|
|
15
65
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
66
|
+
if (command && READ_ONLY_COMMANDS.has(command)) {
|
|
67
|
+
if (command === 'version' || command === '--version' || command === '-v') {
|
|
68
|
+
const { version } = require('../package.json');
|
|
69
|
+
process.stdout.write(`clckernel v${version}\n`);
|
|
70
|
+
} else {
|
|
71
|
+
process.stdout.write(HELP_TEXT);
|
|
72
|
+
}
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (args.some((a) => READ_ONLY_COMMANDS.has(a))) {
|
|
77
|
+
process.exitCode = rejectUnknownInput(args);
|
|
78
|
+
return;
|
|
19
79
|
}
|
|
20
80
|
|
|
21
81
|
if (command === 'doctor' || args.includes('--doctor')) {
|
|
22
|
-
|
|
23
|
-
runDoctor(
|
|
82
|
+
const target = args[1] && args[1] !== '--doctor' ? args[1] : (args[0] === 'doctor' ? null : args[0]);
|
|
83
|
+
runDoctor(target ? path.resolve(target) : process.cwd());
|
|
24
84
|
return;
|
|
25
85
|
}
|
|
26
86
|
|
|
27
87
|
if (command === 'update' || args.includes('--update')) {
|
|
28
|
-
|
|
88
|
+
const target = args[1] && args[1] !== '--update' ? args[1] : (args[0] === 'update' ? null : args[0]);
|
|
29
89
|
const { runUpdate } = require('./updater');
|
|
30
|
-
await runUpdate(
|
|
90
|
+
await runUpdate(target ? path.resolve(target) : process.cwd());
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Everything below is destructive. Only reachable through an explicit command
|
|
95
|
+
// or an explicit install flag.
|
|
96
|
+
const flags = args.filter((a) => a.startsWith('-'));
|
|
97
|
+
const positionals = args.filter((a) => !a.startsWith('-'));
|
|
98
|
+
const unknownFlags = flags.filter((f) => !INSTALL_FLAGS.has(f));
|
|
99
|
+
|
|
100
|
+
if (unknownFlags.length > 0 || positionals.length > 1) {
|
|
101
|
+
process.exitCode = rejectUnknownInput(args);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const targetDir = positionals.length === 1 ? path.resolve(positionals[0]) : process.cwd();
|
|
106
|
+
const explicitInstall = INSTALL_FLAGS.has(command) || flags.length > 0;
|
|
107
|
+
const isInteractive = Boolean(process.stdin.isTTY) && !process.env.CI;
|
|
108
|
+
|
|
109
|
+
if (!explicitInstall && !isInteractive) {
|
|
110
|
+
process.stderr.write(
|
|
111
|
+
'\n❌ Refusing to install without an explicit opt-in.\n' +
|
|
112
|
+
' No TTY detected, so there is no confirmation prompt.\n' +
|
|
113
|
+
' Re-run with `clckernel --yes` to provision, or `clckernel --help`.\n\n'
|
|
114
|
+
);
|
|
115
|
+
process.exitCode = 1;
|
|
31
116
|
return;
|
|
32
117
|
}
|
|
33
118
|
|
package/tools/check_custom.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @param {string} targetDir - Directory to scan (defaults to cwd)
|
|
8
8
|
* @returns {{ exitCode: number, skipped: boolean, name: string, data: object }}
|
|
9
9
|
*/
|
|
10
|
-
const { parseYaml, globToRegex } = require('
|
|
10
|
+
const { parseYaml, globToRegex } = require('./yaml.js');
|
|
11
11
|
const check = async function checkCustom(targetDir) {
|
|
12
12
|
const fs = require('fs');
|
|
13
13
|
const path = require('path');
|
package/tools/check_hitl.js
CHANGED
|
@@ -22,10 +22,22 @@ const check = async function checkHitl(targetDir) {
|
|
|
22
22
|
for (const sDir of searchDirs) {
|
|
23
23
|
if (fs.existsSync(sDir)) {
|
|
24
24
|
try {
|
|
25
|
-
const
|
|
26
|
-
for (const
|
|
27
|
-
if (
|
|
28
|
-
skillFiles.push(path.join(sDir,
|
|
25
|
+
const entries = fs.readdirSync(sDir, { withFileTypes: true });
|
|
26
|
+
for (const entry of entries) {
|
|
27
|
+
if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
28
|
+
skillFiles.push(path.join(sDir, entry.name));
|
|
29
|
+
} else if (entry.isDirectory()) {
|
|
30
|
+
// Exclude framework CLI scaffolding and tool plugins
|
|
31
|
+
if (entry.name === 'clckernel_cli' || entry.name === 'clc_forge_cli' || entry.name === 'impeccable') {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const subDirPath = path.join(sDir, entry.name);
|
|
35
|
+
const subEntries = fs.readdirSync(subDirPath, { withFileTypes: true });
|
|
36
|
+
for (const sub of subEntries) {
|
|
37
|
+
if (sub.isFile() && sub.name.endsWith('.md')) {
|
|
38
|
+
skillFiles.push(path.join(subDirPath, sub.name));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
29
41
|
}
|
|
30
42
|
}
|
|
31
43
|
} catch {}
|
/package/{src → tools}/yaml.js
RENAMED
|
File without changes
|