diff --git a/README.md b/README.md index ceb5d6f..e30bc60 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Get started in minutes: ## How It Works 1. **Security rules** are written in unified markdown format (`sources/` directory) -2. **Conversion tools** translate rules to IDE-specific formats (Cursor, Windsurf, Copilot, Claude Code, Antigravity) +2. **Conversion tools** translate rules to IDE-specific formats (Cursor, Windsurf, Copilot, Agent Skills, Antigravity) 3. **Release automation** packages rules into downloadable ZIP files 4. **AI assistants** reference these rules when generating or reviewing code 5. **Secure code** is produced automatically without developer intervention @@ -63,7 +63,7 @@ Get started in minutes: ``` sources/ # Source rules -skills/ # Claude Code plugin (generated, committed) +skills/ # Agent Skills format (generated, committed) src/ # Conversion and validation tools dist/ # Other IDE bundles (generated, not committed) ``` diff --git a/skills/software-security/rules/codeguard-1-crypto-algorithms.md b/skills/software-security/rules/codeguard-1-crypto-algorithms.md index 7f0b820..18fcd78 100644 --- a/skills/software-security/rules/codeguard-1-crypto-algorithms.md +++ b/skills/software-security/rules/codeguard-1-crypto-algorithms.md @@ -1,115 +1,126 @@ --- -description: Cryptographic Security Guidelines +description: Cryptographic Security Guidelines & Post-Quantum Readiness alwaysApply: true --- rule_id: codeguard-1-crypto-algorithms -# Cryptographic Security Guidelines +# Cryptographic Security Guidelines & Post-Quantum Readiness -## Banned (Insecure) Algorithms +## 1. Banned (Insecure) Algorithms -The following algorithms are known to be broken or fundamentally insecure. **NEVER** generate or use code with these algorithms. -Examples: +The following algorithms are known to be broken or fundamentally insecure. NEVER generate or use code with these algorithms. -* Hash: `MD2`, `MD4`, `MD5`, `SHA-0` -* Symmetric: `RC2`, `RC4`, `Blowfish`, `DES`, `3DES` -* Key Exchange: Static RSA, Anonymous Diffie-Hellman -* Classical: `Vigenère` +* Hash: `MD2`, `MD4`, `MD5`, `SHA-0` +* Symmetric: `RC2`, `RC4`, `Blowfish`, `DES`, `3DES` +* Key Exchange: Static RSA, Anonymous Diffie-Hellman +* Classical: `Vigenère` -## Deprecated (Legacy/Weak) Algorithms +Reason: These are cryptographically broken and vulnerable to collision or man-in-the-middle attacks. -The following algorithms are not outright broken, but have known weaknesses, or are considered obsolete. **NEVER** generate or use code with these algorithms. -Examples: +## 2. Deprecated (Legacy/Weak) Algorithms -* Hash: `SHA-1` -* Symmetric: `AES-CBC`, `AES-ECB` -* Signature: RSA with `PKCS#1 v1.5` padding -* Key Exchange: DHE with weak/common primes +The following algorithms have known weaknesses or are considered obsolete. Avoid in new designs and prioritize migration. +* Hash: `SHA-1` +* Symmetric: `AES-CBC`, `AES-ECB` +* Signature: RSA with `PKCS#1 v1.5` padding +* Key Exchange: DHE with weak/common primes -## Deprecated SSL/Crypto APIs - FORBIDDEN -NEVER use these deprecated functions. Use the replacement APIs listed below: +## 3. Recommended & Post-Quantum Ready Algorithms -### Symmetric Encryption (AES) +Implement these modern, secure algorithms to ensure resistance against both classical and quantum threats. + +### Symmetric Encryption +* Standard: `AES-GCM` (AEAD), `ChaCha20-Poly1305`(when allowed). +* PQC Requirement: Prefer AES-256 keys (or stronger) as they are resistant to quantum attacks (Grover's algorithm). +* Avoid: Custom crypto or unauthenticated modes. + +### Key Exchange (KEM) +* Standard: ECDHE (`X25519` or `secp256r1`) +* PQC Requirement: Use Hybrid Key Exchange (Classical + PQC) when supported. + * Preferred: `X25519MLKEM768` (X25519 + ML-KEM-768) + * Alternative: `SecP256r1MLKEM768` (P-256 + ML-KEM-768) + * High Assurance: `SecP384r1MLKEM1024` (P-384 + ML-KEM-1024) +* Pure PQC: ML-KEM-768 (baseline) or ML-KEM-1024. Avoid ML-KEM-512 unless explicitly risk-accepted. +* Constraints: + * Use vendor-documented identifiers (RFC 9242/9370). + * Remove legacy/draft "Hybrid-Kyber" groups (e.g., `X25519Kyber`) and draft or hardcoded OIDs. + +### Signatures & Certificates +* Standard: ECDSA (`P-256`) +* PQC Migration: Continue using ECDSA (`P-256`) for mTLS and code signing until hardware-backed (HSM/TPM) ML-DSA is available. +* Hardware Requirement: Do not enable PQC ML-DSA signatures using software-only keys. Require HSM/TPM storage. + +### Protocol Versions +* (D)TLS: Enforce (D)TLS 1.3 only (or later). +* IPsec: Enforce IKEv2 only. + * Use ESP with AEAD (AES-256-GCM). + * Require PFS via ECDHE. + * Implement RFC 9242 and RFC 9370 for Hybrid PQC (ML-KEM + ECDHE). + * Ensure re-keys (CREATE_CHILD_SA) maintain hybrid algorithms. +* SSH: Enable only vendor-supported PQC/hybrid KEX (e.g., `sntrup761x25519`). + +## 4. Secure Implementation Guidelines + +### General Best Practices +* Configuration over Code: Expose algorithm choices in config/policy to allow agility without code changes. +* Key Management: + * Use KMS/HSM for key storage. + * Generate keys with a CSPRNG. + * Separate encryption keys from signature keys. + * Rotate keys per policy. + * NEVER hardcode keys, secrets, or experimental OIDs. +* Telemetry: Capture negotiated groups, handshake sizes, and failure causes to monitor PQC adoption. + +### Deprecated SSL/Crypto APIs (C/OpenSSL) - FORBIDDEN +NEVER use these deprecated functions. Use the replacement EVP high-level APIs. + +#### Symmetric Encryption (AES) - Deprecated: `AES_encrypt()`, `AES_decrypt()` -- Replacement: Use EVP high-level APIs: - ```c - EVP_EncryptInit_ex() +- Replacement: + + EVP_EncryptInit_ex() // Use EVP_aes_256_gcm() for PQC readiness EVP_EncryptUpdate() EVP_EncryptFinal_ex() - EVP_DecryptInit_ex() - EVP_DecryptUpdate() - EVP_DecryptFinal_ex() - ``` - -### RSA Operations -- Deprecated: `RSA_new()`, `RSA_up_ref()`, `RSA_free()`, `RSA_set0_crt_params()`, `RSA_get0_n()` -- Replacement: Use EVP key management APIs: - ```c + + +#### RSA/PKEY Operations +- Deprecated: `RSA_new()`, `RSA_free()`, `RSA_get0_n()` +- Replacement: + EVP_PKEY_new() EVP_PKEY_up_ref() EVP_PKEY_free() - ``` - -### Hash Functions -- Deprecated: `SHA1_Init()`, `SHA1_Update()`, `SHA1_Final()` -- Replacement: Use EVP digest APIs: - ```c - EVP_DigestInit_ex() - EVP_DigestUpdate() - EVP_DigestFinal_ex() - EVP_Q_digest() // For simple one-shot hashing - ``` - -### MAC Operations -- Deprecated: `CMAC_Init()`, `HMAC()` (especially with SHA1) -- Replacement: Use EVP MAC APIs: - ```c - EVP_Q_MAC() // For simple MAC operations - ``` - -### Key Wrapping -- Deprecated: `AES_wrap_key()`, `AES_unwrap_key()` -- Replacement: Use EVP key wrapping APIs or implement using EVP encryption - -### Other Deprecated Functions -- Deprecated: `DSA_sign()`, `DH_check()` -- Replacement: Use corresponding EVP APIs for DSA and DH operations - -## Banned Insecure Algorithms - STRICTLY FORBIDDEN -These algorithms MUST NOT be used in any form: - -### Hash Algorithms (Banned) -- MD2, MD4, MD5, SHA-0 -- Reason: Cryptographically broken, vulnerable to collision attacks -- Use Instead: SHA-256, SHA-384, SHA-512 - -### Symmetric Ciphers (Banned) -- RC2, RC4, Blowfish, DES, 3DES -- Reason: Weak key sizes, known vulnerabilities -- Use Instead: AES-128, AES-256, ChaCha20 - -### Key Exchange (Banned) -- Static RSA key exchange -- Anonymous Diffie-Hellman -- Reason: No forward secrecy, vulnerable to man-in-the-middle attacks -- Use Instead: ECDHE, DHE with proper validation - -## Broccoli Project Specific Requirements -- HMAC() with SHA1: Deprecated per Broccoli project requirements + + +#### Hash & MAC Functions +- Deprecated: `SHA1_Init()`, `HMAC()` (especially with SHA1) +- Replacement: + + EVP_DigestInit_ex() // Use SHA-256 or stronger + EVP_Q_MAC() // For one-shot MAC + + +## 5. Broccoli Project Specific Requirements +- HMAC() with SHA1: Deprecated. - Replacement: Use HMAC with SHA-256 or stronger: - ```c - // Instead of HMAC() with SHA1 - EVP_Q_MAC(NULL, "HMAC", NULL, "SHA256", NULL, key, key_len, data, data_len, out, out_size, &out_len); - ``` -## Secure Crypto Implementation Pattern + +// Example: Secure replacement for HMAC-SHA1 +```c +EVP_Q_MAC(NULL, "HMAC", NULL, "SHA256", NULL, key, key_len, data, data_len, out, out_size, &out_len); +``` + +## 6. Secure Crypto Implementation Pattern + + +// Example: Secure AES-256-GCM encryption (PQC-Ready Symmetric Strength) ```c -// Example: Secure AES encryption EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); if (!ctx) handle_error(); +// Use AES-256-GCM if (EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, key, iv) != 1) handle_error(); @@ -124,11 +135,3 @@ ciphertext_len += len; EVP_CIPHER_CTX_free(ctx); ``` - -## Code Review Checklist -- [ ] No deprecated SSL/crypto APIs used -- [ ] No banned algorithms (MD5, DES, RC4, etc.) -- [ ] HMAC uses SHA-256 or stronger (not SHA1) -- [ ] All crypto operations use EVP high-level APIs -- [ ] Proper error handling for all crypto operations -- [ ] Key material properly zeroed after use diff --git a/sources/core/codeguard-1-crypto-algorithms.md b/sources/core/codeguard-1-crypto-algorithms.md index e748452..edbf65d 100644 --- a/sources/core/codeguard-1-crypto-algorithms.md +++ b/sources/core/codeguard-1-crypto-algorithms.md @@ -1,114 +1,125 @@ --- -description: Cryptographic Security Guidelines +description: Cryptographic Security Guidelines & Post-Quantum Readiness languages: [] alwaysApply: true --- -# Cryptographic Security Guidelines +# Cryptographic Security Guidelines & Post-Quantum Readiness -## Banned (Insecure) Algorithms +## 1. Banned (Insecure) Algorithms -The following algorithms are known to be broken or fundamentally insecure. **NEVER** generate or use code with these algorithms. -Examples: +The following algorithms are known to be broken or fundamentally insecure. NEVER generate or use code with these algorithms. -* Hash: `MD2`, `MD4`, `MD5`, `SHA-0` -* Symmetric: `RC2`, `RC4`, `Blowfish`, `DES`, `3DES` -* Key Exchange: Static RSA, Anonymous Diffie-Hellman -* Classical: `Vigenère` +* Hash: `MD2`, `MD4`, `MD5`, `SHA-0` +* Symmetric: `RC2`, `RC4`, `Blowfish`, `DES`, `3DES` +* Key Exchange: Static RSA, Anonymous Diffie-Hellman +* Classical: `Vigenère` -## Deprecated (Legacy/Weak) Algorithms +Reason: These are cryptographically broken and vulnerable to collision or man-in-the-middle attacks. -The following algorithms are not outright broken, but have known weaknesses, or are considered obsolete. **NEVER** generate or use code with these algorithms. -Examples: +## 2. Deprecated (Legacy/Weak) Algorithms -* Hash: `SHA-1` -* Symmetric: `AES-CBC`, `AES-ECB` -* Signature: RSA with `PKCS#1 v1.5` padding -* Key Exchange: DHE with weak/common primes +The following algorithms have known weaknesses or are considered obsolete. Avoid in new designs and prioritize migration. +* Hash: `SHA-1` +* Symmetric: `AES-CBC`, `AES-ECB` +* Signature: RSA with `PKCS#1 v1.5` padding +* Key Exchange: DHE with weak/common primes -## Deprecated SSL/Crypto APIs - FORBIDDEN -NEVER use these deprecated functions. Use the replacement APIs listed below: +## 3. Recommended & Post-Quantum Ready Algorithms -### Symmetric Encryption (AES) +Implement these modern, secure algorithms to ensure resistance against both classical and quantum threats. + +### Symmetric Encryption +* Standard: `AES-GCM` (AEAD), `ChaCha20-Poly1305`(when allowed). +* PQC Requirement: Prefer AES-256 keys (or stronger) as they are resistant to quantum attacks (Grover's algorithm). +* Avoid: Custom crypto or unauthenticated modes. + +### Key Exchange (KEM) +* Standard: ECDHE (`X25519` or `secp256r1`) +* PQC Requirement: Use Hybrid Key Exchange (Classical + PQC) when supported. + * Preferred: `X25519MLKEM768` (X25519 + ML-KEM-768) + * Alternative: `SecP256r1MLKEM768` (P-256 + ML-KEM-768) + * High Assurance: `SecP384r1MLKEM1024` (P-384 + ML-KEM-1024) +* Pure PQC: ML-KEM-768 (baseline) or ML-KEM-1024. Avoid ML-KEM-512 unless explicitly risk-accepted. +* Constraints: + * Use vendor-documented identifiers (RFC 9242/9370). + * Remove legacy/draft "Hybrid-Kyber" groups (e.g., `X25519Kyber`) and draft or hardcoded OIDs. + +### Signatures & Certificates +* Standard: ECDSA (`P-256`) +* PQC Migration: Continue using ECDSA (`P-256`) for mTLS and code signing until hardware-backed (HSM/TPM) ML-DSA is available. +* Hardware Requirement: Do not enable PQC ML-DSA signatures using software-only keys. Require HSM/TPM storage. + +### Protocol Versions +* (D)TLS: Enforce (D)TLS 1.3 only (or later). +* IPsec: Enforce IKEv2 only. + * Use ESP with AEAD (AES-256-GCM). + * Require PFS via ECDHE. + * Implement RFC 9242 and RFC 9370 for Hybrid PQC (ML-KEM + ECDHE). + * Ensure re-keys (CREATE_CHILD_SA) maintain hybrid algorithms. +* SSH: Enable only vendor-supported PQC/hybrid KEX (e.g., `sntrup761x25519`). + +## 4. Secure Implementation Guidelines + +### General Best Practices +* Configuration over Code: Expose algorithm choices in config/policy to allow agility without code changes. +* Key Management: + * Use KMS/HSM for key storage. + * Generate keys with a CSPRNG. + * Separate encryption keys from signature keys. + * Rotate keys per policy. + * NEVER hardcode keys, secrets, or experimental OIDs. +* Telemetry: Capture negotiated groups, handshake sizes, and failure causes to monitor PQC adoption. + +### Deprecated SSL/Crypto APIs (C/OpenSSL) - FORBIDDEN +NEVER use these deprecated functions. Use the replacement EVP high-level APIs. + +#### Symmetric Encryption (AES) - Deprecated: `AES_encrypt()`, `AES_decrypt()` -- Replacement: Use EVP high-level APIs: - ```c - EVP_EncryptInit_ex() +- Replacement: + + EVP_EncryptInit_ex() // Use EVP_aes_256_gcm() for PQC readiness EVP_EncryptUpdate() EVP_EncryptFinal_ex() - EVP_DecryptInit_ex() - EVP_DecryptUpdate() - EVP_DecryptFinal_ex() - ``` - -### RSA Operations -- Deprecated: `RSA_new()`, `RSA_up_ref()`, `RSA_free()`, `RSA_set0_crt_params()`, `RSA_get0_n()` -- Replacement: Use EVP key management APIs: - ```c + + +#### RSA/PKEY Operations +- Deprecated: `RSA_new()`, `RSA_free()`, `RSA_get0_n()` +- Replacement: + EVP_PKEY_new() EVP_PKEY_up_ref() EVP_PKEY_free() - ``` - -### Hash Functions -- Deprecated: `SHA1_Init()`, `SHA1_Update()`, `SHA1_Final()` -- Replacement: Use EVP digest APIs: - ```c - EVP_DigestInit_ex() - EVP_DigestUpdate() - EVP_DigestFinal_ex() - EVP_Q_digest() // For simple one-shot hashing - ``` - -### MAC Operations -- Deprecated: `CMAC_Init()`, `HMAC()` (especially with SHA1) -- Replacement: Use EVP MAC APIs: - ```c - EVP_Q_MAC() // For simple MAC operations - ``` - -### Key Wrapping -- Deprecated: `AES_wrap_key()`, `AES_unwrap_key()` -- Replacement: Use EVP key wrapping APIs or implement using EVP encryption - -### Other Deprecated Functions -- Deprecated: `DSA_sign()`, `DH_check()` -- Replacement: Use corresponding EVP APIs for DSA and DH operations - -## Banned Insecure Algorithms - STRICTLY FORBIDDEN -These algorithms MUST NOT be used in any form: - -### Hash Algorithms (Banned) -- MD2, MD4, MD5, SHA-0 -- Reason: Cryptographically broken, vulnerable to collision attacks -- Use Instead: SHA-256, SHA-384, SHA-512 - -### Symmetric Ciphers (Banned) -- RC2, RC4, Blowfish, DES, 3DES -- Reason: Weak key sizes, known vulnerabilities -- Use Instead: AES-128, AES-256, ChaCha20 - -### Key Exchange (Banned) -- Static RSA key exchange -- Anonymous Diffie-Hellman -- Reason: No forward secrecy, vulnerable to man-in-the-middle attacks -- Use Instead: ECDHE, DHE with proper validation - -## Broccoli Project Specific Requirements -- HMAC() with SHA1: Deprecated per Broccoli project requirements + + +#### Hash & MAC Functions +- Deprecated: `SHA1_Init()`, `HMAC()` (especially with SHA1) +- Replacement: + + EVP_DigestInit_ex() // Use SHA-256 or stronger + EVP_Q_MAC() // For one-shot MAC + + +## 5. Broccoli Project Specific Requirements +- HMAC() with SHA1: Deprecated. - Replacement: Use HMAC with SHA-256 or stronger: - ```c - // Instead of HMAC() with SHA1 - EVP_Q_MAC(NULL, "HMAC", NULL, "SHA256", NULL, key, key_len, data, data_len, out, out_size, &out_len); - ``` -## Secure Crypto Implementation Pattern + +// Example: Secure replacement for HMAC-SHA1 +```c +EVP_Q_MAC(NULL, "HMAC", NULL, "SHA256", NULL, key, key_len, data, data_len, out, out_size, &out_len); +``` + +## 6. Secure Crypto Implementation Pattern + + +// Example: Secure AES-256-GCM encryption (PQC-Ready Symmetric Strength) ```c -// Example: Secure AES encryption EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); if (!ctx) handle_error(); +// Use AES-256-GCM if (EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, key, iv) != 1) handle_error(); @@ -123,11 +134,3 @@ ciphertext_len += len; EVP_CIPHER_CTX_free(ctx); ``` - -## Code Review Checklist -- [ ] No deprecated SSL/crypto APIs used -- [ ] No banned algorithms (MD5, DES, RC4, etc.) -- [ ] HMAC uses SHA-256 or stronger (not SHA1) -- [ ] All crypto operations use EVP high-level APIs -- [ ] Proper error handling for all crypto operations -- [ ] Key material properly zeroed after use diff --git a/src/convert_to_ide_formats.py b/src/convert_to_ide_formats.py index 1657366..0a1e2d0 100644 --- a/src/convert_to_ide_formats.py +++ b/src/convert_to_ide_formats.py @@ -6,8 +6,8 @@ Convert Unified Rules to IDE Formats Transforms the unified markdown sources into IDE-specific bundles (Cursor, -Windsurf, Copilot, Claude Code). This script is the main entry point for producing -distributable rule packs from the sources/ directory. +Windsurf, Copilot, Agent Skills, Antigravity). This script is the main entry point +for producing distributable rule packs from the sources/ directory. """ import re @@ -16,7 +16,13 @@ from collections import defaultdict from converter import RuleConverter -from formats import CursorFormat, WindsurfFormat, CopilotFormat, ClaudeCodeFormat, AntigravityFormat +from formats import ( + CursorFormat, + WindsurfFormat, + CopilotFormat, + AgentSkillsFormat, + AntigravityFormat, +) from utils import get_version_from_pyproject from validate_versions import set_plugin_version, set_marketplace_version @@ -26,7 +32,7 @@ def sync_plugin_metadata(version: str) -> None: """ - Sync version from pyproject.toml to Claude Code plugin metadata files. + Sync version from pyproject.toml to Agent Skills metadata files. Args: version: Version string from pyproject.toml @@ -39,17 +45,17 @@ def sync_plugin_metadata(version: str) -> None: def matches_tag_filter(rule_tags: list[str], filter_tags: list[str]) -> bool: """ Check if rule has all required tags (AND logic). - + Args: rule_tags: List of tags from the rule (already normalized to lowercase) filter_tags: List of tags to filter by (already normalized to lowercase) - + Returns: True if rule has all filter tags (or no filter), False otherwise """ if not filter_tags: return True # No filter means all pass - + return all(tag in rule_tags for tag in filter_tags) @@ -98,14 +104,20 @@ def update_skill_md(language_to_rules: dict[str, list[str]], skill_path: str) -> print(f"Updated SKILL.md with language mappings") -def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: bool = True, version: str = None, filter_tags: list[str] = None) -> dict[str, list[str]]: +def convert_rules( + input_path: str, + output_dir: str = "dist", + include_agentskills: bool = True, + version: str = None, + filter_tags: list[str] = None, +) -> dict[str, list[str]]: """ Convert rule file(s) to all supported IDE formats using RuleConverter. Args: input_path: Path to a single .md file or folder containing .md files output_dir: Output directory (default: 'dist/') - include_claudecode: Whether to generate Claude Code plugin (default: True, only for core rules) + include_agentskills: Whether to generate Agent Skills format (default: True, only for core rules) version: Version string to use (default: read from pyproject.toml) filter_tags: Optional list of tags to filter by (AND logic, case-insensitive) @@ -117,7 +129,7 @@ def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: } Example: - results = convert_rules("sources/core", "dist", include_claudecode=True) + results = convert_rules("sources/core", "dist", include_agentskills=True) print(f"Converted {len(results['success'])} rules") """ if version is None: @@ -130,10 +142,10 @@ def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: CopilotFormat(version), AntigravityFormat(version), ] - - # Only include Claude Code for core rules (committed plugin) - if include_claudecode: - all_formats.append(ClaudeCodeFormat(version)) + + # Only include Agent Skills format for core rules (committed as skills) + if include_agentskills: + all_formats.append(AgentSkillsFormat(version)) converter = RuleConverter(formats=all_formats) path = Path(input_path) @@ -151,7 +163,7 @@ def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: md_files = sorted(list(path.rglob("*.md"))) if not md_files: raise ValueError(f"No .md files found in {input_path}") - + print(f"Converting {len(md_files)} files from: {path}") # Setup output directory @@ -165,7 +177,7 @@ def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: try: # Convert the file (raises exceptions on error) result = converter.convert(md_file) - + # Apply tag filter if specified if filter_tags and not matches_tag_filter(result.tags, filter_tags): results["skipped"].append(result.filename) @@ -175,17 +187,15 @@ def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: output_files = [] for format_name, output in result.outputs.items(): # Construct output path - # Claude Code goes to project root ./skills/ + # Agent Skills goes to project root ./skills/ # Other formats go to dist/ (or specified output_dir) - if format_name == "claudecode": + if format_name == "agentskills": base_dir = PROJECT_ROOT else: base_dir = output_base - + output_file = ( - base_dir - / output.subpath - / f"{result.basename}{output.extension}" + base_dir / output.subpath / f"{result.basename}{output.extension}" ) # Create directory if it doesn't exist and write file @@ -225,30 +235,32 @@ def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: f"\nResults: {len(results['success'])} success, {len(results['errors'])} errors" ) - # Generate SKILL.md with language mappings (only if Claude Code is included) - if include_claudecode and language_to_rules: - template_path = PROJECT_ROOT / "sources" / "core" / "codeguard-SKILLS.md.template" - + # Generate SKILL.md with language mappings (only if Agent Skills is included) + if include_agentskills and language_to_rules: + template_path = ( + PROJECT_ROOT / "sources" / "core" / "codeguard-SKILLS.md.template" + ) + if not template_path.exists(): raise FileNotFoundError( f"SKILL.md template not found at {template_path}. " - "This file is required for Claude Code plugin generation." + "This file is required for Agent Skills generation." ) - + output_skill_dir = PROJECT_ROOT / "skills" / "software-security" output_skill_dir.mkdir(parents=True, exist_ok=True) output_skill_path = output_skill_dir / "SKILL.md" - + # Read template and inject current version from pyproject.toml template_content = template_path.read_text(encoding="utf-8") # Replace the hardcoded version with actual version template_content = re.sub( r'codeguard-version:\s*"[^"]*"', f'codeguard-version: "{version}"', - template_content + template_content, ) output_skill_path.write_text(template_content, encoding="utf-8") - + update_skill_md(language_to_rules, str(output_skill_path)) return results @@ -262,7 +274,7 @@ def _resolve_source_paths(args) -> list[Path]: # If --source flags provided, resolve under sources/ if args.source: return [Path("sources") / src for src in args.source] - + # Default: core rules only return [Path("sources/core")] @@ -270,7 +282,7 @@ def _resolve_source_paths(args) -> list[Path]: if __name__ == "__main__": import sys from argparse import ArgumentParser - + parser = ArgumentParser( description="Convert unified rule markdown into IDE-specific bundles." ) @@ -291,7 +303,7 @@ def _resolve_source_paths(args) -> list[Path]: dest="tags", help="Filter rules by tags (comma-separated, case-insensitive, AND logic). Example: --tag api,web-security", ) - + cli_args = parser.parse_args() source_paths = _resolve_source_paths(cli_args) @@ -307,27 +319,31 @@ def _resolve_source_paths(args) -> list[Path]: for source_path in source_paths: for md_file in source_path.rglob("*.md"): filename_to_sources[md_file.name].append(source_path.name) - - duplicates = {name: srcs for name, srcs in filename_to_sources.items() if len(srcs) > 1} + + duplicates = { + name: srcs for name, srcs in filename_to_sources.items() if len(srcs) > 1 + } if duplicates: print(f"❌ Found {len(duplicates)} duplicate filename(s) across sources:") for filename, sources in duplicates.items(): print(f" - {filename} in: {', '.join(sources)}") print("\nPlease rename files to have unique names across all sources.") sys.exit(1) - + # Get version once and sync to metadata files version = get_version_from_pyproject() sync_plugin_metadata(version) - # Check if core is in the sources for Claude Code plugin generation + # Check if core is in the sources for Agent Skills generation has_core = Path("sources/core") in source_paths if has_core: # Validate template exists early - template_path = PROJECT_ROOT / "sources" / "core" / "codeguard-SKILLS.md.template" + template_path = ( + PROJECT_ROOT / "sources" / "core" / "codeguard-SKILLS.md.template" + ) if not template_path.exists(): print(f"❌ SKILL.md template not found at {template_path}") - print("This file is required for Claude Code plugin generation.") + print("This file is required for Agent Skills generation.") sys.exit(1) # Clean output directories once before processing @@ -341,46 +357,50 @@ def _resolve_source_paths(args) -> list[Path]: if skills_rules_dir.exists(): shutil.rmtree(skills_rules_dir) print(f"✅ Cleaned skills/ directory") - + # Print processing summary if len(source_paths) > 1: - sources_list = ', '.join(p.name for p in source_paths) + sources_list = ", ".join(p.name for p in source_paths) print(f"\nConverting {len(source_paths)} sources: {sources_list}") if has_core: - print("(Claude Code plugin will include only core rules)") + print("(Agent Skills will include only core rules)") print() - + # Convert all sources aggregated = {"success": [], "errors": [], "skipped": []} # Parse comma-separated tags and normalize to lowercase filter_tags = None if cli_args.tags: - filter_tags = [tag.strip().lower() for tag in cli_args.tags.split(",") if tag.strip()] - + filter_tags = [ + tag.strip().lower() for tag in cli_args.tags.split(",") if tag.strip() + ] + # Print tag filter info if active if filter_tags: - print(f"Tag filter active: {', '.join(filter_tags)} (AND logic - rules must have all tags)\n") - + print( + f"Tag filter active: {', '.join(filter_tags)} (AND logic - rules must have all tags)\n" + ) + for source_path in source_paths: is_core = source_path == Path("sources/core") - + print(f"Processing: {source_path}") results = convert_rules( - str(source_path), - cli_args.output_dir, - include_claudecode=is_core, + str(source_path), + cli_args.output_dir, + include_agentskills=is_core, version=version, - filter_tags=filter_tags + filter_tags=filter_tags, ) - + aggregated["success"].extend(results["success"]) aggregated["errors"].extend(results["errors"]) if "skipped" in results: aggregated["skipped"].extend(results["skipped"]) print("") - + if aggregated["errors"]: print("❌ Some conversions failed") sys.exit(1) - + print("✅ All conversions successful") diff --git a/src/formats/__init__.py b/src/formats/__init__.py index 55cc0d0..2e80ed5 100644 --- a/src/formats/__init__.py +++ b/src/formats/__init__.py @@ -11,17 +11,19 @@ - CursorFormat: Generates .mdc files for Cursor IDE - WindsurfFormat: Generates .md files for Windsurf IDE - CopilotFormat: Generates .instructions.md files for GitHub Copilot -- ClaudeCodeFormat: Generates .md files for Claude Code plugins +- AgentSkillsFormat: Generates .md files for Agent Skills (OpenAI Codex, Claude Code, other AI coding tools) +- AntigravityFormat: Generates .md files for Google Antigravity Usage: - from formats import BaseFormat, ProcessedRule, CursorFormat, WindsurfFormat, CopilotFormat, ClaudeCodeFormat + from formats import BaseFormat, ProcessedRule, CursorFormat, WindsurfFormat, CopilotFormat, AgentSkillsFormat, AntigravityFormat version = "1.0.0" formats = [ CursorFormat(version), WindsurfFormat(version), CopilotFormat(version), - ClaudeCodeFormat(version), + AgentSkillsFormat(version), + AntigravityFormat(version), ] """ @@ -29,7 +31,7 @@ from formats.cursor import CursorFormat from formats.windsurf import WindsurfFormat from formats.copilot import CopilotFormat -from formats.claudecode import ClaudeCodeFormat +from formats.agentskills import AgentSkillsFormat from formats.antigravity import AntigravityFormat __all__ = [ @@ -38,6 +40,6 @@ "CursorFormat", "WindsurfFormat", "CopilotFormat", - "ClaudeCodeFormat", + "AgentSkillsFormat", "AntigravityFormat", ] diff --git a/src/formats/claudecode.py b/src/formats/agentskills.py similarity index 52% rename from src/formats/claudecode.py rename to src/formats/agentskills.py index 4c3b152..798b2db 100644 --- a/src/formats/claudecode.py +++ b/src/formats/agentskills.py @@ -3,72 +3,75 @@ # SPDX-License-Identifier: Apache-2.0 """ -Claude Code Format Implementation +Agent Skills Format Implementation -Generates .md files for Claude Code Skills/Plugins. +Generates .md files for the Agent Skills standard (agentskills.io). +This format is used by OpenAI Codex, Claude Code, and other AI coding tools. """ from formats.base import BaseFormat, ProcessedRule -class ClaudeCodeFormat(BaseFormat): +class AgentSkillsFormat(BaseFormat): """ - Claude Code plugin format implementation (.md files). - - Claude Code Skills use standard markdown files without - special frontmatter. The original rule content is preserved - and placed in the skills/software-security/rules/ directory - for plugin distribution. - - Unlike other IDE formats, Claude Code doesn't require special - frontmatter transformations - it uses the rules as-is for - plugin-based Skills. + Agent Skills format implementation (.md files). + + Agent Skills (https://agentskills.io/) is an open standard for extending + AI coding agents with task-specific capabilities. It uses standard markdown + files with YAML frontmatter to define rules and instructions. + + This format is adopted by: + - OpenAI Codex (skills) + - Claude Code (plugins) + - Other AI coding tools + + The original rule content is preserved and placed in the + skills/software-security/rules/ directory for distribution. """ def get_format_name(self) -> str: - """Return Claude Code format identifier.""" - return "claudecode" + """Return Agent Skills format identifier.""" + return "agentskills" def get_file_extension(self) -> str: - """Return Claude Code format file extension.""" + """Return Agent Skills format file extension.""" return ".md" def get_output_subpath(self) -> str: - """Return Claude Code output subdirectory.""" + """Return Agent Skills output subdirectory.""" return "skills/software-security/rules" def generate(self, rule: ProcessedRule, globs: str) -> str: """ - Generate Claude Code .md format. - - Claude Code Skills should preserve the original YAML frontmatter + Generate Agent Skills .md format. + + Agent Skills should preserve the original YAML frontmatter (description, languages, alwaysApply) so the rules remain complete - and can be referenced properly. - + and can be referenced properly by AI coding agents. + Args: rule: The processed rule to format - globs: Glob patterns (not used for Claude Code format) - + globs: Glob patterns (not used for Agent Skills format) + Returns: Complete markdown with original YAML frontmatter preserved """ # Build YAML frontmatter yaml_lines = [] - + # Add description desc = self._format_yaml_field("description", rule.description) if desc: yaml_lines.append(desc) - + # Add languages if present if rule.languages: # Format as YAML list yaml_lines.append("languages:") for lang in rule.languages: yaml_lines.append(f"- {lang}") - + # Add alwaysApply yaml_lines.append(f"alwaysApply: {str(rule.always_apply).lower()}") - - return self._build_yaml_frontmatter(yaml_lines, rule.content) + return self._build_yaml_frontmatter(yaml_lines, rule.content)