Metadata-Version: 2.5
Name: flo-pm
Version: 0.3.1
Summary: Static sites and reports from a repo's .issues/ and .context/ markdown
Project-URL: Repository, https://github.com/BramEsposito/flo-pm
Project-URL: Issues, https://github.com/BramEsposito/flo-pm/issues
Requires-Python: >=3.11
Requires-Dist: markdown>=3.5
Requires-Dist: pyyaml>=6.0
Description-Content-Type: text/markdown

# flo

Static sites and reports from a repository's own markdown: an issue backlog
in `.issues/`, the project's design context in `.context/`, and release
reports read off both. No server and no database: the issue files live next
to the code they are about, and every page works straight from `file://`.

| Command | Builds | From |
|---|---|---|
| `flo issues` | a filterable issues site, one page per issue | `.issues/*.md`, `[labels]` |
| `flo context` | a site of the project's scope files, checked for broken deep links | `.context/**/*.md` |
| `flo burnup` | a burnup chart: scope against closed, per release | the issues |
| `flo roadmap` | when each release can ship, theme by theme, at the planned pace | the issues + `[roadmap]` |
| `flo workpackages` | the open work grouped into packages, and each one's progress | the issues + `[workpackages]` |
| `flo epics` | every issue ever filed grouped by the part of the product it belongs to, with each epic's points left, pace and history | the issues + `[epics]` |
| `flo delivery` | what stands between a release and shipping, with code checks | the issues + `[delivery]` |
| `flo build` | all of the above that the project has inputs for | |
| `flo init` | sets a project up for all of it | |
| `flo migrate` | folds an older project's `labels.toml`, `roadmap.toml`, … into `flo.toml` | |
| `flo docs` | flo's own documentation: the topics, or one printed as markdown (`flo docs epics`) | |

`examples/kettle/` is a complete project to look at: a pickup-ordering app
for a fictional bakery chain, with eighteen issues, a context directory and
every report configured. `cd examples/kettle && flo build` and open
`dist/issues-site/index.html`.

## Install

Globally, as a command available in every project:

```sh
uv tool install flo-pm                 # or: pipx install flo-pm
```

Or per project, pinned with the rest of the dev tooling:

```sh
uv add --dev flo-pm                    # then: uv run flo build
```

The package is `flo-pm` on PyPI (`flo` there is an unrelated project);
either way the command is `flo`, and it reads the `flo.toml` of whatever
project you run it in. Python 3.11+.

flo's documentation is installed with it, one document per command: `flo
docs` lists the topics and `flo docs <topic>` prints one. A project's own
markdown can point at "`flo docs roadmap`" rather than a link into the
package, whose path differs per machine (the sources are in `src/flo/docs/`).

## Start a project

```sh
flo init --name Kettle --prefix KET --reports
flo build
```

`init` writes `flo.toml` — flo's settings and the `[labels]` every issue is
checked against — an issues directory and a context directory, each with a
README that explains its conventions, a `TEMPLATE.md` to copy and an icon, and
a starter issue and scope file. `--reports` adds the `[roadmap]`,
`[workpackages]` and `[delivery]` blocks. It only ever adds — files, and
blocks to an existing `flo.toml` — so it is safe to run in a project that
already has some of them.

Both directories are [Open Knowledge Format](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md)
bundles as they stand: every markdown file `init` writes carries a `type`
(`Issue`, `Context`, `Guide`), the one key OKF requires, and the sites warn
about a file without one. `index.md` and `log.md`, the names OKF reserves,
are never read as issues or scopes.

The conventions are the substance: **one markdown file per issue, which never
moves; `status` is the only thing that says where it stands; one context file
per scope, split into AS IS and TO BE, with no history in it.** The two READMEs
`init` writes spell them out, and the sites warn when a file breaks them.

## `flo.toml`

Everything flo needs to know about a project, in one file with one clearly
marked block per concern: flo's own settings, then the project's data — the
label vocabulary and the plans the reports are drawn from. Found by walking up
from the working directory, so `flo` works from anywhere in the project. Paths
are relative to the file.

```toml
# ══ flo — where things are, what issues are called, how the sites look ══════
[project]
name = "Kettle"          # shown on every page (default: the directory name)
id_prefix = "KET"        # issues are KET-1, KET-2, … (default: ISS)

[paths]
issues = ".issues"
context = ".context"
out = "dist"             # each site is a directory under it

[urls]                   # where a dev server serves each site, if one does
issues = "/issues/"      # (defaults: /issues/, /context/, /burnup/, …)

[theme]
css = "flo-theme.css"    # inlined at the end of every page's <head>
fonts = "https://fonts.googleapis.com/css2?family=…"

[menu]                   # the strip of links across the pages, in order
tools = ["issues", "context", "burnup", "roadmap", "workpackages", "epics", "delivery"]
home = { label = "Home", url = "/" }

# ══ labels — the vocabulary every issue is checked against ══════════════════
[labels.issue]           # which frontmatter keys every issue must answer
[labels.status]          # one section per label field, with its allowed values
[labels.assignee]
[labels.tags]            # … and any field of your own: version, points, theme

# ══ roadmap — the plan `flo roadmap` lays the releases out by ═══════════════
[roadmap.plan]           # start, pace, baseline
[[roadmap.release]]      # each release, its reserve…
[[roadmap.release.theme]]  # …and the themes it is scheduled in

# ══ workpackages — what each package of work is ═════════════════════════════
[workpackages]           # the releases in scope, the baseline
[[workpackages.kind]]
[[workpackages.package]]
# ══ epics — what each epic is ════════════════════════════════════════════════
[[epics.epic]]           # key, name, blurb; an issue's `epic` names one

# ══ delivery — the recommended path the delivery report follows ═════════════
[delivery.review]        # the review date and the release it was for
[[delivery.lane]]        # the order to clear the work in
```

`examples/kettle/flo.toml` is a complete one, every block filled in. A project
without a `[roadmap]` simply has no roadmap — `flo build` skips it — and the
same goes for the other two report blocks.

Older projects kept the data blocks as files of their own in the issues
directory (`labels.toml`, `roadmap.toml`, `workpackages.toml`, `epics.toml`,
`delivery-review.toml`). flo still reads those, with a note, and `flo migrate`
folds them into `flo.toml` — comments and all — checking that each block reads
back as its file did before deleting the file.

Every command's flags default to these values, and still override them: `flo
roadmap --as-of 2026-11-01`, `flo issues --watch`. `flo <command> --help`
lists them.

## Theming

Every page ships flo's neutral theme — system fonts, a palette declared as
custom properties on `:root`, redefined for dark mode. A project's `[theme]
css` comes after it, so redefining a property wins. Define dark-mode values
with the pages' own selectors (`@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) { … } }` and `:root[data-theme="dark"]`);
they are more specific than a bare `:root`. `examples/kettle/flo-theme.css`
covers the tokens each page reads.

## Development

```sh
uv venv && uv pip install -e . pytest
.venv/bin/python -m pytest
```

CI runs the tests on every push and pull request
(`.github/workflows/ci.yml`). A version tag publishes to PyPI: bump `version`
in `pyproject.toml`, commit, then `git tag v<version> && git push origin
v<version>` (`.github/workflows/publish.yml`, through PyPI trusted
publishing — no token is stored).

The test suite builds the Kettle example and a freshly initialised project,
and fails on any build warning — so a change to a generator, a template or the
example is checked end to end.
