Skip to content

Scaffold API

The fcc.scaffold package provides the documentation generator, the Jinja2 template engine, and CLI commands for project scaffolding and doc generation. All CLI operations have programmatic equivalents.

flowchart TD
    CLI[fcc CLI] --> init[fcc init]
    CLI --> add[fcc add-persona]
    CLI --> sim[fcc simulate]
    CLI --> gen[fcc generate-docs]
    CLI --> val[fcc validate-docs]
    CLI --> dash[fcc dashboard]
    gen --> DG[DocGenerator]
    DG --> J2[Jinja2 Templates]
    DG --> PR[PersonaRegistry]
    J2 --> OUT[Generated Markdown Files]
    PR --> OUT

DocGenerator

DocGenerator is the core engine behind the fcc generate-docs CLI command. It accepts a PersonaRegistry, resolves template topics from YAML data, and writes documentation files via Jinja2 templates.

Generating All Documentation

from pathlib import Path
from fcc._resources import get_data_dir, get_personas_dir
from fcc.personas.registry import PersonaRegistry
from fcc.scaffold.doc_generator import DocGenerator

registry = PersonaRegistry.from_yaml_directory(get_personas_dir())
gen = DocGenerator(registry, data_dir=get_data_dir())

output_dir = Path("docs")
total = gen.generate_all_docs(output_dir)
print(f"Generated {total} files")

generate_all_docs produces:

  • Per-persona documentation (index, specification, constitution, coordination, prompts, tutorials, workflows, package) -- approximately 56 files per persona
  • Cross-reference documentation (matrix, coordination methods, constitution framework) -- 3 files
  • SITEMAP.md -- 1 file

Generating for a Subset of Personas

Pass a list of persona IDs to restrict output.

total = gen.generate_all_docs(
    output_dir=Path("docs"),
    persona_ids=["RC", "BC", "DE"],
)
print(f"Generated {total} files for 3 personas")

Generating Individual Components

from pathlib import Path
from fcc._resources import get_data_dir, get_personas_dir
from fcc.personas.registry import PersonaRegistry
from fcc.personas.cross_reference import CrossReferenceMatrix
from fcc.scaffold.doc_generator import DocGenerator

registry = PersonaRegistry.from_yaml_directory(get_personas_dir())
gen = DocGenerator(registry, data_dir=get_data_dir())
output_dir = Path("docs")

# Single persona documentation
file_count = gen.generate_persona_docs("RC", output_dir)
print(f"RC docs: {file_count} files")

# Cross-reference documentation (requires a matrix)
matrix = CrossReferenceMatrix.from_yaml(
    get_personas_dir() / "cross_reference.yaml"
)
file_count = gen.generate_cross_reference(matrix, output_dir)
print(f"Cross-reference: {file_count} files")

# Sitemap only
file_count = gen.generate_sitemap(output_dir)
print(f"Sitemap: {file_count} file")

Constructor Parameters

Parameter Type Default Description
registry PersonaRegistry (required) The personas to generate docs for
templates_dir Path or None Bundled templates Override the Jinja2 templates directory
data_dir Path or None Bundled data Override the data directory for topics and constitution

Custom Templates Directory

You can override the template directory to use your own Jinja2 templates while keeping the same generation logic.

from pathlib import Path
from fcc.personas.registry import PersonaRegistry
from fcc.scaffold.doc_generator import DocGenerator

registry = PersonaRegistry.from_yaml_directory("my_project/personas/")
gen = DocGenerator(
    registry,
    templates_dir=Path("my_project/templates"),
    data_dir=Path("my_project/data"),
)
total = gen.generate_all_docs(Path("my_project/docs"))

Template Engine

The fcc.scaffold.engine module provides lower-level Jinja2 template rendering functions.

get_jinja_env

Creates a configured Jinja2 Environment pointing at the specified templates directory. The environment uses trim_blocks and lstrip_blocks for clean output.

from fcc._resources import get_templates_dir
from fcc.scaffold.engine import get_jinja_env

env = get_jinja_env(get_templates_dir())

# List available template names
print(env.list_templates())

render_template

Renders a single template file with a context dictionary.

from fcc._resources import get_templates_dir, get_personas_dir
from fcc.personas.registry import PersonaRegistry
from fcc.scaffold.engine import render_template

registry = PersonaRegistry.from_yaml_directory(get_personas_dir())
persona = registry.get("RC")

content = render_template(
    template_path="docs/persona/specification.md.j2",
    context={
        "persona": persona,
        "doc_context": persona.doc_context or {},
        "default_constitution": {},
        "tutorial_count": 21,
        "prompt_count": 21,
    },
    templates_dir=get_templates_dir(),
)
print(content[:200])

render_to_file

Renders a template and writes the result to a file, creating parent directories as needed.

from pathlib import Path
from fcc.scaffold.engine import render_to_file

render_to_file(
    template_path="docs/persona/index.md.j2",
    output_path=Path("output/rc/index.md"),
    context={"persona": persona, "doc_context": {}, "default_constitution": {},
             "tutorial_count": 21, "prompt_count": 21},
)

CLI Programmatic Equivalents

Every fcc CLI command has a direct programmatic equivalent. Here is a mapping of CLI commands to Python API calls.

fcc init

fcc init --name "My Project" --dir ./my-project
from pathlib import Path
from fcc.scaffold.project import init_project

config = init_project(Path("./my-project"), "My Project")
print(f"Personas: {len(config.personas)}")

fcc add-persona

fcc add-persona "Custom Analyst" --phase Find --id CA --dir ./my-project
from pathlib import Path
from fcc.scaffold.project import add_persona

persona = add_persona(Path("./my-project"), "CA", "Custom Analyst", "Find")
print(f"Added: {persona.name} ({persona.id})")

fcc simulate

fcc simulate --scenario GEN-001 --mock --dir .
from pathlib import Path
from fcc.simulation.ai_client import AIClient, AIProvider
from fcc.simulation.ai_engine import AISimulationEngine, write_ai_traces
from fcc.workflow.graph import WorkflowGraph

graph = WorkflowGraph.from_json(Path("data/workflows/base_sequence.json"))
client = AIClient(provider=AIProvider.MOCK)
engine = AISimulationEngine(graph, ai_client=client, max_steps=20)

result = engine.run(start_node="RC", scenario_id="GEN-001")
write_ai_traces([result], "traces_ai.json")
print(f"Steps: {result.total_steps}, AI calls: {result.total_ai_calls}")

fcc generate-docs

fcc generate-docs --dir docs --personas all
from pathlib import Path
from fcc._resources import get_data_dir, get_personas_dir
from fcc.personas.registry import PersonaRegistry
from fcc.scaffold.doc_generator import DocGenerator

registry = PersonaRegistry.from_yaml_directory(get_personas_dir())
gen = DocGenerator(registry, data_dir=get_data_dir())
total = gen.generate_all_docs(Path("docs"))
print(f"Generated {total} files")

fcc generate-docs --personas core

from pathlib import Path
from fcc._resources import get_data_dir, get_personas_dir
from fcc.personas.registry import PersonaRegistry
from fcc.scaffold.doc_generator import DocGenerator

registry = PersonaRegistry.from_yaml_directory(get_personas_dir())
core_ids = [p.id for p in registry.by_category("core")]

gen = DocGenerator(registry, data_dir=get_data_dir())
total = gen.generate_all_docs(Path("docs"), persona_ids=core_ids)
print(f"Generated {total} files for core personas")

fcc validate-docs

fcc validate-docs --dir docs
from pathlib import Path

doc_dir = Path("docs")
persona_dir = doc_dir / "fcc" / "personas"
md_files = list(persona_dir.rglob("*.md"))
print(f"Found {len(md_files)} documentation files")

issues = [f for f in md_files if not f.read_text().strip()]
if issues:
    print(f"Empty files: {len(issues)}")
else:
    print("Validation passed")

fcc sitemap

fcc sitemap --dir docs
from pathlib import Path
from fcc._resources import get_data_dir, get_personas_dir
from fcc.personas.registry import PersonaRegistry
from fcc.scaffold.doc_generator import DocGenerator

registry = PersonaRegistry.from_yaml_directory(get_personas_dir())
gen = DocGenerator(registry, data_dir=get_data_dir())
gen.generate_sitemap(Path("docs"))