feature-map-cli 1.0.0__tar.gz → 1.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/.gitignore +4 -0
- feature_map_cli-1.1.0/CHANGELOG.md +72 -0
- feature_map_cli-1.1.0/Formula/feature-map.rb +26 -0
- feature_map_cli-1.1.0/GUIDE.md +550 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/PKG-INFO +34 -13
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/README.md +33 -12
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/pyproject.toml +3 -1
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/share/feature_map/skill/SKILL.md +4 -2
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/share/feature_map/skill/references/authoring.md +3 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/share/feature_map/skill/references/commands.md +8 -2
- feature_map_cli-1.1.0/share/feature_map/skill/references/example-map.md +69 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/share/feature_map/skill/references/existing-repos.md +3 -2
- feature_map_cli-1.1.0/src/feature_map/_version.py +1 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/bootstrap.py +119 -32
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/cli.py +51 -2
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/commands/init_cmd.py +9 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/commands/install_cmd.py +24 -7
- feature_map_cli-1.1.0/src/feature_map/commands/update_cmd.py +389 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/config.py +4 -0
- feature_map_cli-1.1.0/src/feature_map/harness.py +100 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/paths.py +8 -0
- feature_map_cli-1.1.0/src/feature_map/prompt.py +99 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/tests/helpers.py +7 -4
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/tests/test_bootstrap.py +107 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/tests/test_cli.py +5 -2
- feature_map_cli-1.1.0/tests/test_harness.py +113 -0
- feature_map_cli-1.1.0/tests/test_npm_wrapper.py +31 -0
- feature_map_cli-1.1.0/tests/test_update.py +316 -0
- feature_map_cli-1.0.0/CHANGELOG.md +0 -37
- feature_map_cli-1.0.0/Formula/feature-map.rb +0 -31
- feature_map_cli-1.0.0/src/feature_map/_version.py +0 -1
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/LICENSE +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/share/feature_map/schema/feature-map.schema.json +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/share/feature_map/templates/feature.yaml.tpl +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/__init__.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/__main__.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/commands/__init__.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/commands/check_cmd.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/commands/find_cmd.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/commands/graph_cmd.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/commands/impact_cmd.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/commands/list_cmd.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/commands/search_cmd.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/commands/show_cmd.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/commands/stats_cmd.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/commands/validate_cmd.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/discover.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/errors.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/graph.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/loader.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/output.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/path_extract.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/path_normalize.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/path_resolve.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/text_index.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/src/feature_map/validate.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/tests/fixtures/.features/auth.yaml +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/tests/fixtures/.features/billing.yaml +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/tests/fixtures/.features/notifications.yaml +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/tests/fixtures/repo/src/app.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/tests/fixtures/repo/src/auth/session.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/tests/fixtures/repo/src/billing/plans.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/tests/fixtures/repo/src/notify/send.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/tests/test_paths.py +0 -0
- {feature_map_cli-1.0.0 → feature_map_cli-1.1.0}/tests/test_validate.py +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [1.1.0] — 2026-08-30
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `feature-map update` upgrades the installed CLI (or reports that it is
|
|
15
|
+
already latest). Detects pip, npm, Homebrew, pipx, and uv from the
|
|
16
|
+
running executable and uses that package manager.
|
|
17
|
+
- `init` now mirrors the agent skill into every *known* harness skill directory
|
|
18
|
+
the repo already uses, alongside the unchanged primary target. Known set:
|
|
19
|
+
`.agents/skills`, `.claude/skills`, `.grok/skills`. A harness is mirrored only
|
|
20
|
+
when its config directory exists, so `init` never seeds an agent tree nobody
|
|
21
|
+
asked for, and no harness is privileged over another.
|
|
22
|
+
- `--skill-dir DIR` (repeatable) and `skill_dirs:` in `.feature-map.yaml` mirror
|
|
23
|
+
the skill into directories for harnesses the CLI does not know about. These
|
|
24
|
+
are always written.
|
|
25
|
+
- The `AGENTS.md` block now names the deployed skill by path, so an agent that
|
|
26
|
+
only reads `AGENTS.md` can find it without guessing.
|
|
27
|
+
- `references/example-map.md`: a complete feature map with the reasoning for
|
|
28
|
+
every line, plus the same feature written badly for contrast.
|
|
29
|
+
- `GUIDE.md` ships inside the package, so the skill's cross-references resolve
|
|
30
|
+
for pip/npm/brew installs. `install` reports its path (`--json` → `.guide`).
|
|
31
|
+
- `install` reports every known skill location (`--json` → `.skill_locations`).
|
|
32
|
+
- After bootstrapping, `feature-map init` offers to launch your agent harness
|
|
33
|
+
(`claude`, `cursor-agent`, `opencode`, `grok`, `codex`, `gemini`, `pi`) to
|
|
34
|
+
scour the repo and author the first maps, with an arrow-key picker when
|
|
35
|
+
multiple harnesses are on PATH. Shortcut with `-y/--yes` and
|
|
36
|
+
`-h/--harness <name>`; prompts are skipped for `--json` and non-TTY runs.
|
|
37
|
+
- npm package `feature-map-cli` (`npm install -g feature-map-cli`) wraps the
|
|
38
|
+
PyPI CLI in a local virtualenv. Still requires Python 3.8+.
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
|
|
42
|
+
- CLI `--help` describes the tool as a cross-app feature index for agents.
|
|
43
|
+
|
|
44
|
+
## [1.0.0] — 2026-08-29
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
|
|
48
|
+
- `feature-map` CLI
|
|
49
|
+
- Full command surface: `list`, `show`, `search`, `find`, `graph`, `validate`,
|
|
50
|
+
`check`, `impact`, `stats`, `init`, `install`, `--json`, `--version`
|
|
51
|
+
- `feature-map init` bootstraps any repo: `.features/`, agent skill, `.feature-map.yaml`,
|
|
52
|
+
`AGENTS.md` snippet, and `bin/feature-map` shim
|
|
53
|
+
- `feature-map init <name>` scaffolds a new map from the bundled template
|
|
54
|
+
- `feature-map init --upgrade-skill` refreshes the agent skill from the installed package
|
|
55
|
+
- Fixture-based test suite
|
|
56
|
+
- Homebrew formula in `Formula/feature-map.rb` (tap: `markschellhas/tap`)
|
|
57
|
+
- pip-installable entrypoint via `pyproject.toml` (`feature-map-cli`)
|
|
58
|
+
|
|
59
|
+
### Changed
|
|
60
|
+
|
|
61
|
+
- Public CLI and Homebrew name is `feature-map` (`brew install markschellhas/tap/feature-map`).
|
|
62
|
+
The PyPI project is `feature-map-cli` (`pip install feature-map-cli`) because
|
|
63
|
+
PyPI treats `feature-map` as too similar to the existing biology package
|
|
64
|
+
`featuremap`. The Python import is `feature_map`.
|
|
65
|
+
- Repo-local `bin/feature-map` shim execs `feature-map` without recursing if
|
|
66
|
+
`bin/` is on PATH.
|
|
67
|
+
- `init` writes `<!-- feature-map:start -->` in `AGENTS.md` and migrates the
|
|
68
|
+
older `<!-- featuremap:start -->` block.
|
|
69
|
+
|
|
70
|
+
[Unreleased]: https://github.com/markschellhas/feature-map/compare/v1.1.0...HEAD
|
|
71
|
+
[1.1.0]: https://github.com/markschellhas/feature-map/releases/tag/v1.1.0
|
|
72
|
+
[1.0.0]: https://github.com/markschellhas/feature-map/releases/tag/v1.0.0
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
class FeatureMap < Formula
|
|
2
|
+
include Language::Python::Virtualenv
|
|
3
|
+
|
|
4
|
+
desc "Cross-app architecture research CLI"
|
|
5
|
+
homepage "https://github.com/markschellhas/feature-map"
|
|
6
|
+
url "https://files.pythonhosted.org/packages/1b/9b/4e4305e888854994e104e28da63d5d2d68dc40eb3b331ce37fa493c9fc65/feature_map_cli-1.0.0.tar.gz"
|
|
7
|
+
sha256 "fdb211503d71408f8aad68712666fdd0bdeab4676ab3f585935416fd69c7f7bc"
|
|
8
|
+
license "MIT"
|
|
9
|
+
head "https://github.com/markschellhas/feature-map.git", branch: "master"
|
|
10
|
+
|
|
11
|
+
depends_on "libyaml"
|
|
12
|
+
depends_on "python@3.12"
|
|
13
|
+
|
|
14
|
+
resource "pyyaml" do
|
|
15
|
+
url "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz"
|
|
16
|
+
sha256 "d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def install
|
|
20
|
+
virtualenv_install_with_resources
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
test do
|
|
24
|
+
assert_match version.to_s, shell_output("#{bin}/feature-map --version")
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
# Feature Map — development, packaging, and adoption
|
|
2
|
+
|
|
3
|
+
This is the operator's guide for `feature-map`: how it was built, how to
|
|
4
|
+
publish it so anyone can install it, and how a developer (or an agent)
|
|
5
|
+
adopts it in another repository — including existing codebases that do
|
|
6
|
+
not yet have maps.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Why this, not a wiki
|
|
11
|
+
|
|
12
|
+
A Karpathy-style (or any LLM-written) wiki works: agents dump what they
|
|
13
|
+
learned into markdown and the next session can read it. The failure mode
|
|
14
|
+
is **shape**. Language models write prose. A wiki page on "signup" becomes
|
|
15
|
+
a narrative — history, asides, duplicated README, a little architecture —
|
|
16
|
+
and the next agent has to **ingest the whole page** to find the three
|
|
17
|
+
files that matter. That is slow in wall time and expensive in tokens.
|
|
18
|
+
|
|
19
|
+
Feature Map stores the same knowledge as **fields**, not essays:
|
|
20
|
+
|
|
21
|
+
| Need | Wiki | Feature Map |
|
|
22
|
+
|------|------|-------------|
|
|
23
|
+
| What is this? | Buried in a paragraph | `purpose` |
|
|
24
|
+
| Where do I start? | Maybe a link, maybe not | `entry_points` (real paths) |
|
|
25
|
+
| Which apps? | Prose | `apps` |
|
|
26
|
+
| How does a user move? | A story | `user_flow` |
|
|
27
|
+
| What else is coupled? | "See also" if you are lucky | `related_features` → `graph` |
|
|
28
|
+
| Is this stale? | You re-read it | `check` (paths on disk), `validate` |
|
|
29
|
+
|
|
30
|
+
**How an agent finds a feature (cheap path):**
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
feature-map list # names only
|
|
34
|
+
feature-map search billing # slugs + short snippets
|
|
35
|
+
feature-map show billing --section entry_points
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`list` is a few dozen tokens. `search` returns hits, not chapters.
|
|
39
|
+
`--section` loads **one key** from one YAML file. The agent opens the
|
|
40
|
+
code at those paths instead of reading a 2k-word wiki page and then
|
|
41
|
+
still grepping.
|
|
42
|
+
|
|
43
|
+
That is why it is more efficient:
|
|
44
|
+
|
|
45
|
+
1. **Addressable.** The CLI is an index (`list`, `search`, `find`,
|
|
46
|
+
`impact`). A wiki is a pile of documents you `cat`.
|
|
47
|
+
2. **Bounded reads.** Structured keys + `--section` cap context. A wiki
|
|
48
|
+
has no equivalent of "give me only the files."
|
|
49
|
+
3. **Checkable.** `validate` and `check` catch missing fields and dead
|
|
50
|
+
paths. A wiki cannot tell you the controller it mentions was renamed.
|
|
51
|
+
4. **Stable voice.** YAML fields do not grow a new introduction every
|
|
52
|
+
time an agent "updates the docs."
|
|
53
|
+
|
|
54
|
+
**Value to the developer** (human, not just the agent):
|
|
55
|
+
|
|
56
|
+
- You get a **directory of the product** — one slug per capability —
|
|
57
|
+
without maintaining a second documentation site.
|
|
58
|
+
- Reviews stay honest: if `entry_points` does not list the file you
|
|
59
|
+
changed, the map is wrong and you fix it in the same PR.
|
|
60
|
+
- Onboarding is `feature-map list` then `show`, not "read the wiki and"
|
|
61
|
+
hope the architecture section is current."
|
|
62
|
+
- You still write PRDs and plans for *what to build*. The map only
|
|
63
|
+
answers *where it lives and what it touches*. That split keeps both
|
|
64
|
+
artifacts small.
|
|
65
|
+
|
|
66
|
+
A wiki can still hold narrative (vision, history, runbooks). Feature Map
|
|
67
|
+
is the **index the agent is required to hit first** so it does not spend
|
|
68
|
+
the budget re-discovering the tree.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 1. How we developed this
|
|
73
|
+
|
|
74
|
+
### Why it exists
|
|
75
|
+
|
|
76
|
+
Taptics is a multi-app monorepo (`rails/`, `mobile_app/`, `worldcuppuzzles-web/`,
|
|
77
|
+
and others). Agents and engineers kept guessing which files belonged to which
|
|
78
|
+
product feature. The answer we wanted was a **per-repo, human-authored
|
|
79
|
+
architecture index** — YAML maps in `.features/` — plus a small CLI so agents
|
|
80
|
+
can `list` / `search` / `show` / `validate` instead of grepping the tree
|
|
81
|
+
blindly.
|
|
82
|
+
|
|
83
|
+
Maps are research infrastructure, not product features. They do not replace
|
|
84
|
+
PRDs or implementation plans. They tell you *where the code lives and how
|
|
85
|
+
the pieces connect* before you touch anything.
|
|
86
|
+
|
|
87
|
+
### Two-phase delivery
|
|
88
|
+
|
|
89
|
+
The work is specified in playbook-app `docs/PRDs/prd-feature-map-cli.md`.
|
|
90
|
+
|
|
91
|
+
| Phase | Where | Goal |
|
|
92
|
+
|-------|--------|------|
|
|
93
|
+
| **1** | Inside playbook-app | Dogfood a complete CLI and agent skill against real maps |
|
|
94
|
+
| **2** | This `feature-map` package | Extract that CLI so other repos can install it |
|
|
95
|
+
|
|
96
|
+
Phase 1 shipped first under `.agents/skills/feature-map/` with a repo-root
|
|
97
|
+
`./bin/feature-map` shim. Commands, schema, discovery (walk up from `cwd` to
|
|
98
|
+
the git root), JSON output, and a bash harness were proven against the
|
|
99
|
+
monorepo's maps.
|
|
100
|
+
|
|
101
|
+
Phase 2 is this directory. The Python package was moved, not rewritten:
|
|
102
|
+
|
|
103
|
+
- Import package `feature_map`; public CLI / Homebrew name `feature-map`;
|
|
104
|
+
PyPI project `feature-map-cli`
|
|
105
|
+
- Assets (schema, template, agent skill) live in `share/feature_map/` and
|
|
106
|
+
ship inside the wheel
|
|
107
|
+
- Tests use `tests/fixtures/` only — no dependency on Taptics maps
|
|
108
|
+
- Defaults are generic (`apps: []`); Rails-style path fallbacks apply only
|
|
109
|
+
when `rails` is listed in `.feature-map.yaml`
|
|
110
|
+
|
|
111
|
+
### Design rules that made extraction cheap
|
|
112
|
+
|
|
113
|
+
1. **No imports from the host repo.** The CLI only reads `.features/`,
|
|
114
|
+
`.feature-map.yaml`, and the filesystem.
|
|
115
|
+
2. **Package-relative assets.** Schema and templates resolve via
|
|
116
|
+
`feature_map.paths.assets_root()`, not hardcoded monorepo paths.
|
|
117
|
+
3. **Thin entrypoint.** `python -m feature_map` and the `feature-map` console
|
|
118
|
+
script both call `feature_map.cli:main`.
|
|
119
|
+
4. **Two meanings of `init`.** `feature-map init` bootstraps a consumer repo.
|
|
120
|
+
`feature-map init <name>` scaffolds one map. Both are required for
|
|
121
|
+
distribution: the first installs the workflow, the second authors data.
|
|
122
|
+
|
|
123
|
+
### What a feature map is
|
|
124
|
+
|
|
125
|
+
A map is a YAML file `.features/<slug>.yaml` with at least:
|
|
126
|
+
|
|
127
|
+
- `feature_name` — matches the filename stem
|
|
128
|
+
- `purpose` — what it does and why
|
|
129
|
+
- `entry_points` — real file paths (and routes if useful)
|
|
130
|
+
|
|
131
|
+
Recommended: `apps`, `user_flow`, `related_features`, `notes`. Cross-app
|
|
132
|
+
features get one map, not one map per app. `related_features` is how
|
|
133
|
+
`graph` and `impact --transitive` work.
|
|
134
|
+
|
|
135
|
+
### What we did *not* build
|
|
136
|
+
|
|
137
|
+
- No web UI or database of maps
|
|
138
|
+
- No auto-generator that invents maps from the AST (maps stay authored;
|
|
139
|
+
agents may draft them after reading the code — see §3)
|
|
140
|
+
- No coupling to Rails, Flutter, or Taptics runtime
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## 2. How to package it so anybody can use it
|
|
145
|
+
|
|
146
|
+
The product that other people install is **this directory**, not playbook-app.
|
|
147
|
+
Map *data* never ships in the package. Each consumer repo keeps its own
|
|
148
|
+
`.features/*.yaml`.
|
|
149
|
+
|
|
150
|
+
### 2.1 Split out of playbook-app (first publish)
|
|
151
|
+
|
|
152
|
+
This tree started as `playbook-app/featuremap/`. Cloud Agent tokens cannot
|
|
153
|
+
create GitHub repositories. Create `markschellhas/feature-map` (public, MIT)
|
|
154
|
+
in the GitHub UI, then:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
cd /path/to/playbook-app
|
|
158
|
+
git subtree split --prefix=featuremap -b featuremap-split
|
|
159
|
+
git push git@github.com:markschellhas/feature-map.git featuremap-split:main
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
After that, develop and tag in the standalone repo. See `EXTRACT.md`.
|
|
163
|
+
|
|
164
|
+
### 2.2 pip (primary for Linux / CI)
|
|
165
|
+
|
|
166
|
+
From the `feature-map` repo root:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
python3 -m pip install build twine
|
|
170
|
+
python3 -m build # sdist + wheel in dist/
|
|
171
|
+
python3 -m twine check dist/*
|
|
172
|
+
python3 -m twine upload dist/ # once PyPI project "feature-map-cli" exists
|
|
173
|
+
# Preferred: Trusted Publishing. Full steps: PUBLISH.md
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`pyproject.toml` already declares:
|
|
177
|
+
|
|
178
|
+
- distribution name `feature-map-cli`; import package `feature_map` under `src/feature_map`
|
|
179
|
+
- console script `feature-map = feature_map.cli:main`
|
|
180
|
+
- runtime dep `pyyaml>=6.0`
|
|
181
|
+
- wheel force-include of `share/feature_map` → `feature_map/share`
|
|
182
|
+
|
|
183
|
+
Until PyPI is live, anyone can install from git:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
pip install "git+https://github.com/markschellhas/feature-map.git"
|
|
187
|
+
# or a local checkout:
|
|
188
|
+
pip install -e /path/to/feature-map
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### 2.3 Homebrew (primary for macOS)
|
|
192
|
+
|
|
193
|
+
`Formula/feature-map.rb` is a draft. After the GitHub repo exists
|
|
194
|
+
(full steps: `PUBLISH.md`):
|
|
195
|
+
|
|
196
|
+
1. Tag a release: `git tag v1.0.0 && git push origin v1.0.0`
|
|
197
|
+
2. Fill `url` + `sha256` on the formula (or keep `head` for `--HEAD`)
|
|
198
|
+
3. Add the formula to a tap (`homebrew-taptics` or a personal tap)
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
brew tap markschellhas/tap
|
|
202
|
+
brew install feature-map
|
|
203
|
+
# or during bring-up:
|
|
204
|
+
brew install --HEAD markschellhas/tap/feature-map
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
The formula should leave `feature-map` on PATH and, when possible, install
|
|
208
|
+
`share/feature_map/{schema,templates,skill}` for `init`.
|
|
209
|
+
|
|
210
|
+
### 2.4 Versioning
|
|
211
|
+
|
|
212
|
+
- Semver on the CLI (`feature-map --version`, today `1.1.0`)
|
|
213
|
+
- Skill frontmatter `version:` should match the CLI version
|
|
214
|
+
- Record changes in `CHANGELOG.md`
|
|
215
|
+
- Consumer repos may pin `min_cli_version` in `.feature-map.yaml`
|
|
216
|
+
|
|
217
|
+
### 2.5 What a release contains vs what it does not
|
|
218
|
+
|
|
219
|
+
| Ships in the package | Stays in each consumer repo |
|
|
220
|
+
|----------------------|-----------------------------|
|
|
221
|
+
| `feature-map` binary | `.features/*.yaml` (the maps) |
|
|
222
|
+
| Schema + map template | `.feature-map.yaml` |
|
|
223
|
+
| Agent skill (`SKILL.md` + references) | `AGENTS.md` snippet (written by `init`) |
|
|
224
|
+
| Homebrew formula / CI for the tool | `bin/feature-map` shim (written by `init`) |
|
|
225
|
+
|
|
226
|
+
### 2.6 Verify a package before you publish
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
cd feature-map
|
|
230
|
+
pip install -e ".[dev]"
|
|
231
|
+
python -m pytest -q
|
|
232
|
+
feature-map --version
|
|
233
|
+
feature-map --help
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
CI (`.github/workflows/ci.yml`) runs the same tests on 3.8 and 3.12.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## 3. Install and use it in another repository
|
|
241
|
+
|
|
242
|
+
This section is for a developer adopting Feature Map in **their** repo,
|
|
243
|
+
greenfield or existing. It is also the contract agents must follow once
|
|
244
|
+
`feature-map init` has run.
|
|
245
|
+
|
|
246
|
+
### 3.1 Install the CLI
|
|
247
|
+
|
|
248
|
+
Pick one:
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
# once published
|
|
252
|
+
brew install markschellhas/tap/feature-map
|
|
253
|
+
pip install feature-map-cli
|
|
254
|
+
npm install -g feature-map-cli
|
|
255
|
+
|
|
256
|
+
# until then
|
|
257
|
+
pip install "git+https://github.com/markschellhas/feature-map.git"
|
|
258
|
+
pip install -e /path/to/playbook-app/featuremap
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Confirm:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
feature-map --version
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### 3.2 Bootstrap the repo
|
|
268
|
+
|
|
269
|
+
From the consumer repo root (must be a git checkout, or `init` still works
|
|
270
|
+
if you are already at the intended root):
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
feature-map init
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
This is idempotent. It:
|
|
277
|
+
|
|
278
|
+
1. Creates `.features/`
|
|
279
|
+
2. Copies the agent skill to `.agents/skills/feature-map/`
|
|
280
|
+
(or `.grok/skills/feature-map/` if that tree already exists), and
|
|
281
|
+
mirrors it into every other known harness directory the repo already
|
|
282
|
+
uses, plus any `--skill-dir` / `skill_dirs:` target
|
|
283
|
+
3. Writes `.feature-map.yaml` if missing
|
|
284
|
+
4. Appends (or refreshes) an `AGENTS.md` block that **requires** agents
|
|
285
|
+
to use Feature Map before feature work, and names the deployed skill
|
|
286
|
+
by path so an agent that only reads `AGENTS.md` can still find it
|
|
287
|
+
5. Writes `bin/feature-map` — a shim that execs `feature-map` on PATH
|
|
288
|
+
|
|
289
|
+
Skill directories are harness-neutral. `.agents/skills/` stays the primary
|
|
290
|
+
target; a known harness is *additionally* mirrored only when the repo already
|
|
291
|
+
uses it (its config directory exists), so `init` never seeds an agent tree
|
|
292
|
+
nobody asked for. Known: `.agents/skills`, `.claude/skills`, `.grok/skills`.
|
|
293
|
+
For anything else, name it yourself — `--skill-dir .my-agent/skills`, or
|
|
294
|
+
`skill_dirs:` in `.feature-map.yaml` — and it is always written.
|
|
295
|
+
|
|
296
|
+
Useful flags: `--no-agents`, `--no-shim`, `--upgrade-skill`, `--force`,
|
|
297
|
+
`--skill-dir DIR` (repeatable).
|
|
298
|
+
Also: `-y/--yes` skips the "start authoring maps?" prompt and
|
|
299
|
+
`-h/--harness <name>` picks the agent harness non-interactively
|
|
300
|
+
(`claude|cursor-agent|opencode|grok|codex|gemini|pi`). On an empty repo
|
|
301
|
+
`init` then offers to launch your harness to scour the code and author the
|
|
302
|
+
first maps; answer `n` or pick `skip` to do it yourself.
|
|
303
|
+
|
|
304
|
+
Then declare this repo's apps so `check` can resolve paths:
|
|
305
|
+
|
|
306
|
+
```yaml
|
|
307
|
+
# .feature-map.yaml
|
|
308
|
+
features_dir: .features
|
|
309
|
+
apps:
|
|
310
|
+
- api
|
|
311
|
+
- web
|
|
312
|
+
- mobile
|
|
313
|
+
required_sections:
|
|
314
|
+
- purpose
|
|
315
|
+
- entry_points
|
|
316
|
+
min_cli_version: "1.0.0"
|
|
317
|
+
# Extra skill mirror targets for harnesses this CLI does not know about:
|
|
318
|
+
# skill_dirs:
|
|
319
|
+
# - .my-agent/skills
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### 3.3 Instruction for agents — always use the feature map
|
|
323
|
+
|
|
324
|
+
`feature-map init` writes this mandate into `AGENTS.md`. Keep it. Do not
|
|
325
|
+
weaken it. Downstream skills (plans, PRDs, debugging) should say:
|
|
326
|
+
|
|
327
|
+
> **REQUIRED SUB-SKILL:** Use `feature-map` before proceeding.
|
|
328
|
+
|
|
329
|
+
**Agents must:**
|
|
330
|
+
|
|
331
|
+
1. **Before any feature work, debug, PRD, or plan** — run
|
|
332
|
+
`feature-map list` (or `./bin/feature-map list`).
|
|
333
|
+
2. If the feature name is unclear — `feature-map search <keyword>` and
|
|
334
|
+
`feature-map find <path-fragment>`.
|
|
335
|
+
3. Read the map — `feature-map <slug>` or
|
|
336
|
+
`feature-map show <slug> --section entry_points` to save tokens.
|
|
337
|
+
4. For cross-cutting work — `feature-map graph <slug>`.
|
|
338
|
+
5. After shipping architecture changes — update the relevant
|
|
339
|
+
`.features/*.yaml`, then `feature-map validate` and `feature-map check`.
|
|
340
|
+
|
|
341
|
+
**Agents must not** implement, plan, or "just grep the repo" when a map
|
|
342
|
+
exists for that area. The map is authoritative; the tree is how you
|
|
343
|
+
verify the map.
|
|
344
|
+
|
|
345
|
+
If `list` is empty, or search/find miss the area you are about to touch,
|
|
346
|
+
**stop and author maps first** (§3.4). Do not invent architecture in a
|
|
347
|
+
PRD or plan without writing the map.
|
|
348
|
+
|
|
349
|
+
Day-to-day commands:
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
feature-map list
|
|
353
|
+
feature-map search billing
|
|
354
|
+
feature-map find src/invoices
|
|
355
|
+
feature-map billing
|
|
356
|
+
feature-map show billing --section entry_points
|
|
357
|
+
feature-map graph billing --format mermaid
|
|
358
|
+
feature-map validate
|
|
359
|
+
feature-map check
|
|
360
|
+
feature-map impact src/invoices/create.py
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
`./bin/feature-map` is the same CLI if the global binary is not on PATH
|
|
364
|
+
in a given environment.
|
|
365
|
+
|
|
366
|
+
### 3.4 Existing repos — scour the code, then write maps
|
|
367
|
+
|
|
368
|
+
`init` does **not** generate maps from source. On a repo that already has
|
|
369
|
+
code, an agent (or a developer) must **read the tree and author maps**.
|
|
370
|
+
This is expected and should be the first Feature Map task in that repo.
|
|
371
|
+
|
|
372
|
+
Do not emit one map per file. Cluster by *user-visible capability* or
|
|
373
|
+
*deployed subsystem*.
|
|
374
|
+
|
|
375
|
+
#### Step A — inventory the repo
|
|
376
|
+
|
|
377
|
+
```bash
|
|
378
|
+
# top-level apps / packages
|
|
379
|
+
ls
|
|
380
|
+
# language / package manifests
|
|
381
|
+
# Gemfile, package.json, pyproject.toml, go.mod, pubspec.yaml, apps/, packages/
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
Write the `apps:` list in `.feature-map.yaml` from that inventory
|
|
385
|
+
(`api`, `web`, `ios`, `worker`, …). Prefer directory names that `check`
|
|
386
|
+
can prefix onto paths.
|
|
387
|
+
|
|
388
|
+
#### Step B — find seams (where features show up)
|
|
389
|
+
|
|
390
|
+
Scour, in order, whatever exists:
|
|
391
|
+
|
|
392
|
+
| Signal | Typical paths |
|
|
393
|
+
|--------|----------------|
|
|
394
|
+
| HTTP routes | `config/routes.rb`, `**/routes.*`, OpenAPI, `app/controllers` |
|
|
395
|
+
| App shells | `src/App.svelte`, `lib/main.dart`, `cmd/*/main.go`, `app/page.tsx` |
|
|
396
|
+
| Domain models | `app/models`, `src/domain`, `internal/` |
|
|
397
|
+
| Background jobs | `app/jobs`, `workers/`, queues |
|
|
398
|
+
| CLIs | `bin/`, `cmd/`, `cli/` |
|
|
399
|
+
| Docs / PRDs | `docs/`, `README*`, existing architecture notes |
|
|
400
|
+
| Tests | test names often name the feature better than folders |
|
|
401
|
+
|
|
402
|
+
Use the CLI as you go:
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
feature-map find <controller_or_package>
|
|
406
|
+
feature-map search <domain word>
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
On a first pass those will miss (no maps yet). That is the signal to
|
|
410
|
+
**draft** maps, not to skip them.
|
|
411
|
+
|
|
412
|
+
#### Step C — cluster into features
|
|
413
|
+
|
|
414
|
+
A feature is a slice a human would name in a standup ("signup",
|
|
415
|
+
"billing", "push notifications"), usually spanning several files and
|
|
416
|
+
sometimes several apps.
|
|
417
|
+
|
|
418
|
+
Cluster when they share:
|
|
419
|
+
|
|
420
|
+
- the same user journey
|
|
421
|
+
- the same primary models / tables
|
|
422
|
+
- the same entry controllers or screens
|
|
423
|
+
- a documented product name
|
|
424
|
+
|
|
425
|
+
Split when journeys, owners, or deployables diverge. Cross-app flows
|
|
426
|
+
(one API + one mobile screen + one worker) are **one** map with multiple
|
|
427
|
+
`apps` and `entry_points`, not three maps.
|
|
428
|
+
|
|
429
|
+
#### Step D — author each map
|
|
430
|
+
|
|
431
|
+
```bash
|
|
432
|
+
feature-map init signup # scaffold .features/signup.yaml
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
Then replace placeholders by reading the real code. Required:
|
|
436
|
+
|
|
437
|
+
```yaml
|
|
438
|
+
feature_name: signup
|
|
439
|
+
purpose: "How a new account is created and reaches the first authenticated screen."
|
|
440
|
+
entry_points:
|
|
441
|
+
- api/src/signup/handler.go
|
|
442
|
+
- web/src/routes/signup/+page.svelte
|
|
443
|
+
apps:
|
|
444
|
+
- api
|
|
445
|
+
- web
|
|
446
|
+
user_flow:
|
|
447
|
+
primary: "Email submit → API creates user → redirect to /app"
|
|
448
|
+
related_features:
|
|
449
|
+
- billing (plan attached after first login)
|
|
450
|
+
notes: >
|
|
451
|
+
Status: mapped from existing code on 2026-08-27. Confirm webhook path
|
|
452
|
+
before changing Stripe.
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
Rules while scouring:
|
|
456
|
+
|
|
457
|
+
- Prefer **paths that exist on disk**. `feature-map check` will flag
|
|
458
|
+
missing ones; invented paths train agents to look in the wrong place.
|
|
459
|
+
- Routes (`GET /signup`) may appear in `entry_points` as extra context,
|
|
460
|
+
but put the implementing file next to them.
|
|
461
|
+
- `related_features` entries must start with a real slug once that map
|
|
462
|
+
exists (`billing (note)`, not a sentence).
|
|
463
|
+
- Record uncertainty in `notes` instead of guessing.
|
|
464
|
+
|
|
465
|
+
Work in batches: scaffold 5–10 obvious features, `validate`, then fill
|
|
466
|
+
gaps `check` and `stats` report.
|
|
467
|
+
|
|
468
|
+
```bash
|
|
469
|
+
feature-map validate
|
|
470
|
+
feature-map check --json
|
|
471
|
+
feature-map stats --json
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
`validate` without `--strict` treats missing recommended sections as
|
|
475
|
+
warnings (exit 0). Use `--strict` in CI once the first pass is honest.
|
|
476
|
+
|
|
477
|
+
#### Step E — when to stop the first pass
|
|
478
|
+
|
|
479
|
+
Stop when:
|
|
480
|
+
|
|
481
|
+
- Every app in `.feature-map.yaml` appears on at least one map
|
|
482
|
+
- The main user journeys (signup, core loop, billing/admin if any) have maps
|
|
483
|
+
- `feature-map search <product noun>` hits something for the nouns in the README
|
|
484
|
+
- `check` only reports paths you *intend* to add, not typos
|
|
485
|
+
|
|
486
|
+
You do not need 100% file coverage. You need **named doors** into the
|
|
487
|
+
system so the next agent does not start from zero.
|
|
488
|
+
|
|
489
|
+
#### Step F — keep maps alive
|
|
490
|
+
|
|
491
|
+
After that first scour:
|
|
492
|
+
|
|
493
|
+
- New feature → `feature-map init <slug>` in the same PR as the code
|
|
494
|
+
- Moved/renamed files → update `entry_points`, run `check`
|
|
495
|
+
- Cross-feature work → update `related_features` and re-read `graph`
|
|
496
|
+
|
|
497
|
+
### 3.5 Greenfield repos
|
|
498
|
+
|
|
499
|
+
If there is almost no code yet, still `feature-map init`, then add a map
|
|
500
|
+
when the first vertical slice lands. Empty `.features/` plus the
|
|
501
|
+
`AGENTS.md` mandate is enough: the next agent is required to author the
|
|
502
|
+
map with the feature, not after the fact.
|
|
503
|
+
|
|
504
|
+
### 3.6 CI in the consumer repo
|
|
505
|
+
|
|
506
|
+
```yaml
|
|
507
|
+
# example GitHub Actions step
|
|
508
|
+
- run: pip install feature-map-cli
|
|
509
|
+
- run: feature-map validate
|
|
510
|
+
# optional, once maps are trusted:
|
|
511
|
+
# - run: feature-map validate --strict
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
Trigger on changes to `.features/**` and, if you want a backstop, on
|
|
515
|
+
every PR.
|
|
516
|
+
|
|
517
|
+
### 3.7 Upgrade
|
|
518
|
+
|
|
519
|
+
```bash
|
|
520
|
+
feature-map update
|
|
521
|
+
# or, by hand:
|
|
522
|
+
# pip install -U feature-map-cli
|
|
523
|
+
# npm install -g feature-map-cli@latest
|
|
524
|
+
# brew upgrade markschellhas/tap/feature-map
|
|
525
|
+
feature-map init --upgrade-skill
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
`feature-map update` detects whether this copy came from pip, npm, Homebrew,
|
|
529
|
+
pipx, or uv and upgrades through that channel. If you are already on the
|
|
530
|
+
latest version it says so and exits 0. A source/editable checkout cannot
|
|
531
|
+
be upgraded this way — install a released package instead.
|
|
532
|
+
|
|
533
|
+
`--upgrade-skill` overwrites the deployed `SKILL.md` and references from
|
|
534
|
+
the installed package version. Re-run `feature-map init` to refresh the
|
|
535
|
+
`AGENTS.md` marked block.
|
|
536
|
+
|
|
537
|
+
---
|
|
538
|
+
|
|
539
|
+
## Related files in this package
|
|
540
|
+
|
|
541
|
+
| File | Role |
|
|
542
|
+
|------|------|
|
|
543
|
+
| `README.md` | Short install + command list |
|
|
544
|
+
| `PUBLISH.md` | Submit to PyPI and Homebrew |
|
|
545
|
+
| `EXTRACT.md` | Split this directory into its own GitHub repo |
|
|
546
|
+
| `CHANGELOG.md` | Released CLI versions |
|
|
547
|
+
| `share/feature_map/skill/SKILL.md` | Agent skill copied by `init` |
|
|
548
|
+
| `share/feature_map/skill/references/existing-repos.md` | Scour-the-code playbook for agents |
|
|
549
|
+
| `share/feature_map/skill/references/authoring.md` | Map shape and conventions |
|
|
550
|
+
| `share/feature_map/skill/references/commands.md` | Full CLI reference |
|