Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/8BitTacoSupreme/flowstate/llms.txt

Use this file to discover all available pages before exploring further.

Every pipeline step beyond context generation is implemented as a tool adapter — a class that wraps a bridge call, file I/O, or a pure Python check behind a consistent ToolResult return value. The orchestrator never calls subprocess or writes files directly; it calls an adapter method and checks result.success. This makes adapters independently testable and swappable.

ToolAdapter Base Class

All adapters extend ToolAdapter from flowstate/tools/base.py. The constructor accepts four parameters:
class ToolAdapter:
    name: str = "base"

    def __init__(
        self,
        root: Path,
        dry_run: bool = False,
        bridge: ClaudeBridge | None = None,
        memory: MemoryStore | None = None,
    ):
        self.root = root
        self.dry_run = dry_run
        self._bridge = bridge
        self.memory = memory
The bridge property is lazy — if no bridge is injected at construction time, one is created on first access from a default BridgeConfig. In practice, the orchestrator always injects the shared bridge instance.

bridge_to_result()

A convenience method that converts a BridgeResult from a bridge call into the adapter’s ToolResult contract:
def bridge_to_result(self, br: BridgeResult, artifacts: list[str] | None = None) -> ToolResult:
    return ToolResult(
        success=br.success,
        output=br.output,
        artifacts=artifacts or [],
        error=br.error,
    )

get_memory_context()

Queries the MemoryStore for prior knowledge relevant to a topic and returns a formatted string ready for prompt injection:
def get_memory_context(self, query: str, max_tokens: int = 1500) -> str:
    if self.memory is None:
        return ""
    return self.memory.get_context(query, max_tokens=max_tokens)
If memory is None (e.g., in a test with no store), an empty string is returned and the adapter proceeds without prior context.

ToolResult Dataclass

All adapter methods return a ToolResult:
@dataclass
class ToolResult:
    success: bool
    output: str
    artifacts: list[str] = field(default_factory=list)
    error: str | None = None
FieldTypeDescription
successboolWhether the step succeeded
outputstrHuman-readable status line (printed by _run_step())
artifactslist[str]Absolute file paths to created output files
errorstr | NoneError message stored in flowstate.json if success=False

ResearchAdapter

ResearchAdapter in flowstate/tools/research.py performs split-topic research by running one short bridge call per topic and merging the sections into research/report.md. Class name: ResearchAdapter | Adapter name: "research"

execute(answers: InterviewAnswers) -> ToolResult

def execute(self, answers: InterviewAnswers) -> ToolResult:
    report_dir = self.root / "research"
    report_dir.mkdir(exist_ok=True)
    report_path = report_dir / "report.md"
    topics = _split_topics(answers.research_focus)
    ...
_split_topics() splits answers.research_focus by comma, strips whitespace, and falls back to ["general research"] if the field is empty. For each topic, the adapter:
  1. Builds a focused prompt with _build_topic_prompt(topic, answers) — includes core_problem and architecture_pattern as context
  2. Prepends prior memory context via get_memory_context(topic) if any exists
  3. Calls self.bridge.run(...) with these parameters:
br = self.bridge.run(
    prompt,
    system_prompt=RESEARCH_SYSTEM_PROMPT,
    allowed_tools=["WebSearch", "WebFetch"],
    max_turns=3,
    model="sonnet",
)
All sections are merged into a single # Research Report document separated by --- dividers and written to research/report.md.

RESEARCH_SYSTEM_PROMPT

You are an expert technical researcher. Produce a concise Markdown research
section — under 50 lines. Do at most 2 web searches. For the given topic:
1. One targeted search for current best practice.
2. Pros/cons of top 2 approaches (brief table).
3. One concrete recommendation with rationale.
Output ONLY the Markdown. No preamble, no summaries, no follow-up offers.

Dry-run mock

When dry_run=True, the adapter writes MOCK_REPORT to research/report.md and returns immediately without calling the bridge:
# Research Report

## Focus Areas
{focus}

## Findings
- **Libraries**: Analysis pending research integration.
- **Edge Cases**: Identified areas requiring deeper investigation.
- **References**: Queued for search.

## Recommendations
1. Proceed with initial architecture while research continues.
2. Flag blocking unknowns for async resolution.

---
*Generated by FlowState (mock mode)*

StrategyAdapter

StrategyAdapter in flowstate/tools/strategy.py runs a single strategic pressure-test call and writes the result to research/strategy.md. Class name: StrategyAdapter | Adapter name: "strategy"

pressure_test(answers: InterviewAnswers) -> ToolResult

_build_pressure_test_prompt() constructs a structured markdown prompt from core_problem, ten_x_vision, milestones, architecture_pattern, and test_coverage. Prior memory context for "{core_problem} {ten_x_vision}" is prepended if available. The bridge call uses:
br = self.bridge.run(
    prompt,
    system_prompt=STRATEGY_SYSTEM_PROMPT,
    allowed_tools=["WebSearch"],
    max_turns=5,
    model="sonnet",
)
On success, br.output is written directly to research/strategy.md and the path is returned as an artifact. On failure, bridge_to_result(br) is returned with success=False.

STRATEGY_SYSTEM_PROMPT

You are a startup advisor running a strategic pressure-test.
Your job is to evaluate the idea honestly and produce a strategic assessment.

For each idea, evaluate:
1. **Problem clarity**: Is the core problem real and urgent?
2. **10x potential**: Does the vision represent a 10x improvement?
3. **Feasibility**: Can this be built with the stated architecture?
4. **Risks**: What are the top 3 risks and mitigations?
5. **Recommendation**: Ship, pivot, or kill — with rationale.

Output a structured Markdown document. Be direct and honest.

Dry-run mock

When dry_run=True, the adapter writes MOCK_STRATEGY filled with core_problem and ten_x_vision values from answers:
# Strategy: Pressure Test

## Core Problem
{problem}

## 10x Vision
{vision}

## Assessment
- **Market fit**: Validated against the core problem statement.
- **Scalability**: Architecture supports 10x growth path.
- **Risk**: Key risks identified and mitigated in milestone planning.

---
*Generated by FlowState (mock mode)*

GSDAdapter

GSDAdapter in flowstate/tools/gsd_adapter.py handles Step 4 (Management). Unlike the research and strategy adapters, it does not make any bridge calls in live mode — it delegates entirely to write_context_files() from context.py. Class name: GSDAdapter | Adapter name: "gsd"

new_project(state: FlowStateModel) -> ToolResult

def new_project(self, state: FlowStateModel) -> ToolResult:
    if self.dry_run:
        # writes MOCK_ROADMAP to .planning/ROADMAP.md
        ...
        return ToolResult(
            success=True,
            output="[dry-run] Context files written to .planning/",
            artifacts=[str(roadmap_path)],
        )

    created = write_context_files(state, self.root)
    artifacts = [str(p) for p in created]

    return ToolResult(
        success=True,
        output=f"Context files written: {len(created)} files to .planning/, .claude/, research/",
        artifacts=artifacts,
    )
In dry-run mode, only ROADMAP.md is written using the MOCK_ROADMAP template (with milestones from interview answers). In live mode, all five context files are written by write_context_files().
The full GSD skill system (/gsd:plan-phase, /gsd:execute-phase, /gsd:progress) runs natively inside Claude Code sessions, not through this adapter. Use flowstate launch gsd <N> to get the exact commands for native GSD execution.

DisciplineAudit

The discipline step does not use a ToolAdapter subclass. check_setup() in flowstate/discipline.py is a standalone function that returns an AuditResult — a pure Python project health check with no LLM calls.

check_setup(root: Path) -> AuditResult

def check_setup(root: Path) -> AuditResult:
    checks: dict[str, bool] = {}
    checks["git_repo"]          = (root / ".git").is_dir()
    checks["pytest_config"]     = (
        (root / "pyproject.toml").exists()
        or (root / "pytest.ini").exists()
        or (root / "setup.cfg").exists()
    )
    checks["pre_commit_config"] = (root / ".pre-commit-config.yaml").exists()
    checks["git_hooks"]         = (root / ".git" / "hooks" / "pre-commit").exists()
    checks["tests_dir"]         = (root / "tests").is_dir()
    checks["src_dir"]           = (root / "src").is_dir() or _has_python_package(root)
    checks["planning_dir"]      = (root / ".planning").is_dir()
    ...
The seven checks and what they verify:
CheckPasses when
git_repo.git/ directory exists
pytest_configpyproject.toml, pytest.ini, or setup.cfg exists
pre_commit_config.pre-commit-config.yaml exists
git_hooks.git/hooks/pre-commit exists
tests_dirtests/ directory exists
src_dirsrc/ exists or any directory contains __init__.py
planning_dir.planning/ directory exists (written by GSD step)

AuditResult fields

@dataclass
class AuditResult:
    success: bool         # always True — audit itself never fails
    checks: dict[str, bool]  # map of check name → passed
    summary: str          # formatted multi-line string for console output
The summary string renders as:
Audit: 6/7 checks passed
  [+] Git Repo
  [+] Pytest Config
  [-] Pre Commit Config
  [+] Git Hooks
  [+] Tests Dir
  [+] Src Dir
  [+] Planning Dir

Creating a Custom Adapter

Subclass ToolAdapter, set a name, implement one method, and return a ToolResult:
from pathlib import Path
from flowstate.tools.base import ToolAdapter, ToolResult
from flowstate.state import FlowStateModel


class DocgenAdapter(ToolAdapter):
    name = "docgen"

    def generate(self, state: FlowStateModel) -> ToolResult:
        if self.dry_run:
            return ToolResult(
                success=True,
                output="[dry-run] Would generate docs",
                artifacts=[],
            )

        # Inject relevant prior knowledge into the prompt
        prior = self.get_memory_context("documentation best practices")
        prompt = f"{prior}\n\nGenerate API docs for this project."

        br = self.bridge.run(
            prompt,
            system_prompt="You are a technical writer.",
            allowed_tools=["Read"],
            max_turns=5,
            model="sonnet",
        )

        if br.success:
            out = self.root / "docs" / "api.md"
            out.parent.mkdir(exist_ok=True)
            out.write_text(br.output)
            return ToolResult(success=True, output="Docs written", artifacts=[str(out)])

        return self.bridge_to_result(br)
To wire it into the pipeline, instantiate it in run_pipeline() and dispatch it through _run_step() with the shared bridge and memory instances.

Build docs developers (and LLMs) love