mirror of
https://github.com/alirezarezvani/ClaudeForge.git
synced 2026-07-03 02:13:15 -04:00
feat: ClaudeForge 2.1.0 — installable plugin with 150-line cap, forked audit skills, /sync --weekly, and AGENTS.md export (#26)
This commit is contained in:
+114
-15
@@ -27,16 +27,48 @@ class ContentGenerator:
|
||||
"""
|
||||
Generate root CLAUDE.md file (navigation hub).
|
||||
|
||||
When the surrounding project already contains ``AGENTS.md``,
|
||||
``.cursorrules``, or ``.windsurfrules`` (passed via
|
||||
``project_context['existing_instruction_files']``), the generated root
|
||||
file prepends ``@`` imports for them so Claude inherits their content
|
||||
instead of duplicating it.
|
||||
|
||||
Returns:
|
||||
Complete CLAUDE.md content as string
|
||||
Complete CLAUDE.md content as string.
|
||||
"""
|
||||
template = self.template_selector.select_template()
|
||||
|
||||
# Use template selector's customization
|
||||
if template.get('modular_recommended'):
|
||||
return self._generate_modular_root(template)
|
||||
body = self._generate_modular_root(template)
|
||||
else:
|
||||
return self._generate_standalone_file(template)
|
||||
body = self._generate_standalone_file(template)
|
||||
|
||||
return self._prepend_existing_instruction_imports(body)
|
||||
|
||||
def _prepend_existing_instruction_imports(self, body: str) -> str:
|
||||
"""Insert ``@`` import lines for any AGENTS.md / cursor / windsurf files."""
|
||||
existing = self.project_context.get('existing_instruction_files') or []
|
||||
supported = ('AGENTS.md', '.cursorrules', '.windsurfrules')
|
||||
imports = [name for name in supported if name in existing]
|
||||
if not imports:
|
||||
return body
|
||||
|
||||
# Insert right after the intro paragraph (first blank line after the H1).
|
||||
lines = body.split('\n')
|
||||
out: List[str] = []
|
||||
inserted = False
|
||||
for i, line in enumerate(lines):
|
||||
out.append(line)
|
||||
if not inserted and i > 0 and line == '' and lines[i - 1].strip():
|
||||
out.append('## External Instructions')
|
||||
out.append('')
|
||||
out.append('Chained from sibling instruction files in this repo:')
|
||||
out.append('')
|
||||
for name in imports:
|
||||
out.append(f"@{name}")
|
||||
out.append('')
|
||||
inserted = True
|
||||
return '\n'.join(out) if inserted else body
|
||||
|
||||
def _generate_modular_root(self, template: Dict[str, Any]) -> str:
|
||||
"""Generate root file for modular architecture (navigation hub)."""
|
||||
@@ -99,7 +131,8 @@ class ContentGenerator:
|
||||
context: Context name ('backend', 'frontend', 'database', etc.)
|
||||
|
||||
Returns:
|
||||
Context-specific CLAUDE.md content
|
||||
Context-specific CLAUDE.md content with a back-link to the root
|
||||
CLAUDE.md so Claude can navigate back up the chain.
|
||||
"""
|
||||
generators = {
|
||||
'backend': self._generate_backend_file,
|
||||
@@ -110,7 +143,59 @@ class ContentGenerator:
|
||||
}
|
||||
|
||||
generator = generators.get(context, self._generate_generic_context_file)
|
||||
return generator()
|
||||
body = generator()
|
||||
|
||||
# Depth-aware back-link: most context dirs are one level deep, but
|
||||
# ``.github`` may sit at the repo root next to the root CLAUDE.md.
|
||||
rel_root = "../CLAUDE.md"
|
||||
backlink = (
|
||||
"> Parent context: see the root [CLAUDE.md]"
|
||||
f"({rel_root}) for project-wide guidelines and behavioural rules.\n"
|
||||
f"> Chained import: `@{rel_root}`\n\n"
|
||||
)
|
||||
return backlink + body
|
||||
|
||||
def generate_rules_file(
|
||||
self,
|
||||
name: str,
|
||||
description: str,
|
||||
paths: List[str],
|
||||
body: str,
|
||||
) -> str:
|
||||
"""Emit a path-scoped ``.claude/rules/*.md`` instruction file.
|
||||
|
||||
Anthropic loads these conditionally when Claude accesses files matching
|
||||
any of the ``paths:`` globs. Use this for sections that would otherwise
|
||||
bloat the root CLAUDE.md — e.g. backend-only standards that only matter
|
||||
when editing files under ``src/backend/**``.
|
||||
|
||||
Args:
|
||||
name: Skill-style identifier, kebab-case (e.g. ``backend-rules``).
|
||||
description: Single-sentence summary (used by Claude for matching).
|
||||
paths: List of glob patterns that trigger the load.
|
||||
body: Markdown content (excluding frontmatter and title).
|
||||
|
||||
Returns:
|
||||
Complete file content ready to write to ``.claude/rules/<name>.md``.
|
||||
Stays within the 150-line cap unless the caller passes a body that
|
||||
already exceeds it; the cap is enforced by the validator hook.
|
||||
"""
|
||||
if not paths:
|
||||
raise ValueError("paths must be a non-empty list of glob patterns")
|
||||
|
||||
lines: List[str] = ["---"]
|
||||
lines.append(f"name: {name}")
|
||||
lines.append(f"description: {description}")
|
||||
lines.append("paths:")
|
||||
for glob in paths:
|
||||
lines.append(f" - {glob}")
|
||||
lines.append("---")
|
||||
lines.append("")
|
||||
lines.append(f"# {name.replace('-', ' ').title()}")
|
||||
lines.append("")
|
||||
lines.append(body.strip())
|
||||
lines.append("")
|
||||
return "\n".join(lines)
|
||||
|
||||
def _generate_backend_file(self) -> str:
|
||||
"""Generate backend-specific CLAUDE.md."""
|
||||
@@ -412,22 +497,36 @@ class ContentGenerator:
|
||||
return sections
|
||||
|
||||
def _generate_navigation_section(self, template: Dict[str, Any]) -> List[str]:
|
||||
"""Generate navigation section for modular architecture."""
|
||||
"""Generate navigation section for modular architecture.
|
||||
|
||||
Emits both human-readable markdown links and Claude Code ``@`` imports
|
||||
so the chained CLAUDE.md files are loaded automatically when the root
|
||||
file is read.
|
||||
"""
|
||||
project_type = self.project_context.get('type')
|
||||
links = []
|
||||
targets: List[tuple] = [] # (label, relative_path)
|
||||
|
||||
if project_type == 'fullstack':
|
||||
links.append("- [Backend Guidelines](backend/CLAUDE.md)")
|
||||
links.append("- [Frontend Guidelines](frontend/CLAUDE.md)")
|
||||
links.append("- [Database Operations](database/CLAUDE.md)")
|
||||
targets.append(("Backend Guidelines", "backend/CLAUDE.md"))
|
||||
targets.append(("Frontend Guidelines", "frontend/CLAUDE.md"))
|
||||
targets.append(("Database Operations", "database/CLAUDE.md"))
|
||||
|
||||
if 'cicd' in self.project_context.get('workflows', []):
|
||||
links.append("- [CI/CD Workflows](.github/CLAUDE.md)")
|
||||
targets.append(("CI/CD Workflows", ".github/CLAUDE.md"))
|
||||
|
||||
if not links:
|
||||
links.append("- [Add links to context-specific CLAUDE.md files]")
|
||||
if not targets:
|
||||
return ["- [Add links to context-specific CLAUDE.md files]"]
|
||||
|
||||
return links
|
||||
lines: List[str] = []
|
||||
for label, path in targets:
|
||||
lines.append(f"- [{label}]({path})")
|
||||
lines.append("")
|
||||
lines.append("Chained context (Claude Code auto-imports these):")
|
||||
lines.append("")
|
||||
for _, path in targets:
|
||||
lines.append(f"@{path}")
|
||||
|
||||
return lines
|
||||
|
||||
def _generate_core_principles(self, template: Dict[str, Any], max_count: int = 7) -> List[str]:
|
||||
"""Generate core principles list."""
|
||||
|
||||
Reference in New Issue
Block a user