Metadata-Version: 2.4
Name: fkmd
Version: 0.1.0
Summary: F2K Markdown source compiler: structured, composable .md → .f.md
Author: albert
License: MIT
Keywords: ai,compiler,documentation,markdown
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Documentation
Classifier: Topic :: Text Processing :: Markup :: Markdown
Requires-Python: >=3.10
Requires-Dist: markdown-it-py>=3
Requires-Dist: watchdog>=4
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == 'dev'
Description-Content-Type: text/markdown

# fkmd — F2K Markdown
![F2K Markdown Logo](docs-user/logo.png)

[中文文档](docs-user/README.zh.md)

F2K Markdown is a Markdown source compiler designed for AI Agents and knowledge base maintenance. It lets you write Markdown like Python—bringing the modularity of programming languages to your documents.

In the AI Agent era, Markdown has become a new programming language, widely used in Agent Skills development, knowledge base construction, and system prompt management. However, as project complexity grows, plain text Markdown becomes hard to maintain:
- **Concept Duplication**: The same terminology, version number, or configuration appears in multiple places, violating the Single Source of Truth (SSOT) principle.
- **Lack of Modularity**: Unable to nest files, resulting in overly long files that are hard to read and reuse.
- **Tedious Formatting**: Step numbers and heading levels require manual adjustment when reorganizing content.

F2K Markdown solves this by introducing a lightweight compilation mechanism:
- **Seamless Integration**: Source files are ordinary `.md` files (identified by a `#!fkmd` header), and the compiled output is pure `.f.md` Markdown that any renderer or AI Agent can read natively.
- **Variables & Expressions**: Embed Python variables or expressions directly in text using `{python_expr}`.
- **Code Block Execution**: Execute Python logic within Markdown code blocks for data processing or environment initialization.
- **File Nesting**: Embed other Markdown files with automatic heading level indentation for true document modularity.
- **Lenient Parsing**: Syntax errors or undefined variables won't crash the compiler; they are output as plain text to ensure robustness.

![Demo: Left is the source Markdown, right is the real-time compiled output](docs-user/demo.gif)
>In the demo above: The left side shows the `.md` source file with variables and expressions, while the right side shows the `.f.md` output being compiled in real-time.

---

## Installation

Install globally via `uv` (recommended):

```bash
uv tool install fkmd
# or install into your project
uv add fkmd
```

Alternatively, install from source:

```bash
git clone https://github.com/f2k-ai/f2k-markdown.git
cd f2k-markdown
uv sync
uv run fkmd --version
```

---

## Quickstart

1. Start the watcher in your document root:

```bash
cd your-document-root
fkmd watch .
```

> Note: `fkmd watch` may prompt to upgrade existing `.md` files. This is optional but recommended. See [CLI](#cli).

2. Create a simple source file `docs/hello.md`:

```markdown
#!fkmd 0.1
{name="World"}
# Hello {name}!
```

3. The compiled output is automatically generated at `.fkmd-output/docs/hello.f.md`:

```markdown
# Hello World!
```

> For a real-world use case involving global variables, Python execution, and file embedding, see the [Comprehensive Example](#comprehensive-example) below.

---

## Syntax Cheatsheet

Inside any source `.md` file (with `#!fkmd` header):

| Source                          | Compiles to                                    |
| ------------------------------- | ---------------------------------------------- |
| `#!fkmd 0.1`                    | (stripped — must be the first line)            |
| `{name}`                        | `str(name)` from the entry-script namespace    |
| `{1 + 2}`                       | Any single-line Python expression evaluated at compile time |
| `{name="a"}` / `{import math}`  | A single-line Python *statement*               |
| `{{anything}}`                  | Literal `{anything}` (escape)                  |
| `{@incr:section}`               | DSL sugar: `1`, `2`, `3`, … per counter name   |
| `{@doc:other.md,section="API"}` | DSL sugar: inlines `other.md` (or a specific section) with headings automatically shifted |
| ```` ```lang … ``` ````         | Markdown code blocks are **exempt** from parsing |
| ```` ```lang @fkmd … ``` ````   | **Opt back in** to `{...}` substitution inside a single fenced block |
| ```` ```python @exec … ``` ```` | Execute multi-line Python. Replaced by the last expression's value, or disappears if `None` |

---

## CLI

```text
fkmd watch [WATCH_DIR] [-o OUTPUT_DIR] [-e ENTRY] [--once] [-y/--yes]
fkmd convert DIR
fkmd revert  PATH
```

- `fkmd watch`: Continuously compile `.md` → `.f.md` as files change.
- `fkmd convert`: Safely upgrade plain `.md` files to `fkmd` sources. It adds the `#!fkmd` header and escapes existing braces (e.g., `{` to `{{`) to ensure your original content remains exactly unchanged after compilation.
- `fkmd revert`: Strict inverse of `fkmd convert`. It strips the header and unescapes braces, restoring your files to their original plain Markdown state.

---

## Programmatic API

`fkmd` is also a Python library. Use it when you want to compile sources in-memory without managing a file-system loop.

```python
from fkmd import compile_string, compile_file, Compiler

# In-memory string → string
result = compile_string("hello {name}!", globals={"name": "World"})
print(result.text)             # "hello World!"

# Single file → write to a path
compile_file(
    "docs/api-reference.md",
    globals={"version": "1.2.3"},
    output="dist/api-reference.html.md",
)

# Long-lived Compiler
compiler = Compiler(entry=".fkmd-entry.py", base_dir="docs/")
result = compiler.compile_file("docs/system-prompt.md")
```

---

## Comprehensive Example

Here is a full example demonstrating how to build a modular system prompt for an AI Agent using global variables, Python execution, and file embedding.

1. **Global Variables**: Edit the entry script `.fkmd-entry.py`:

```python
agent_name = "CodeCopilot"
```

2. **Main Document**: Create your source file `docs/00-quick-start.md`:

````markdown
#!fkmd 0.1 
{doc_version="v1.0"}
> Document Version: {doc_version}  -- local variable defined in this file
> Agent Name: {agent_name} -- global variable defined in .fkmd-entry.py
> Current Time: {import datetime; datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}

# System Prompt

You are {agent_name}, an advanced AI coding assistant.

## {@incr:main}. Initial Setup

```python @exec
def test_api_connection():
    return "connected"
```

Your API connection status is: {test_api_connection()}.
**Do not proceed** if the status is not connected.

## {@incr:main}. Operating Procedures

### Step {@incr:agent_step}: Gather Context
Always read the relevant files first.

### Step {@incr:agent_step}: Plan
Formulate a plan before executing changes.

## {@incr:main}. API Reference
> Note: The embedded API documentation's heading levels are automatically aligned to sit under this section.

{@doc:00b-quick-start.md, section="API Endpoints"}
````

3. **Embedded Document**: Create the embedded file `docs/00b-quick-start.md`:

```markdown
#!fkmd 0.1
# API Endpoints
## GET `/api/v1/status`
Check health status.
```

4. **Compiled Output**: The output will be automatically generated at `.fkmd-output/docs/00-quick-start.f.md`:

```markdown
> Document Version: v1.0  -- local variable defined in this file
> Agent Name: CodeCopilot -- global variable defined in .fkmd-entry.py
> Current Time: 2026-05-15 17:36:02

# System Prompt

You are CodeCopilot, an advanced AI coding assistant.

## 1. Initial Setup

Your API connection status is: connected.
**Do not proceed** if the status is not connected.

## 2. Operating Procedures

### Step 1: Gather Context
Always read the relevant files first.

### Step 2: Plan
Formulate a plan before executing changes.

## 3. API Reference
> Note: The embedded API documentation's heading levels are automatically aligned to sit under this section.

### API Endpoints

#### GET `/api/v1/status`
Check health status.
```

---

## Under the Hood

To understand how `{python_expr}` are evaluated, it helps to know the execution order during a compile pass:

1. **Entry Script Execution**: Before any Markdown file is processed, the entry script (e.g., `.fkmd-entry.py`) is executed exactly once. Any variables, functions, or imports defined here become the **shared global namespace** for the entire compile pass.
2. **File Compilation**: Each `.md` source file is then compiled. Each file gets its own **fresh local namespace**.
3. **Expression Evaluation**: When the compiler encounters `{expression}` or a ```` ```python @exec ```` block, it evaluates the Python code using the file's local namespace backed by the shared global namespace. This means you can read global variables anywhere, but local assignments (like `{doc_version="v1.0"}`) remain isolated to that specific file.

---

## Roadmap
See the [roadmap](docs-dev/roadmap.md) for the detailed features and issues.