where-are-we 0.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.
- where_are_we-0.1.0/.github/workflows/ci.yml +44 -0
- where_are_we-0.1.0/.github/workflows/release.yml +19 -0
- where_are_we-0.1.0/.gitignore +6 -0
- where_are_we-0.1.0/CHANGELOG.md +22 -0
- where_are_we-0.1.0/LICENSE +21 -0
- where_are_we-0.1.0/PKG-INFO +231 -0
- where_are_we-0.1.0/README.md +218 -0
- where_are_we-0.1.0/SCHEMA.md +52 -0
- where_are_we-0.1.0/pyproject.toml +25 -0
- where_are_we-0.1.0/src/where_are_we/__init__.py +4 -0
- where_are_we-0.1.0/src/where_are_we/mapper.py +2801 -0
- where_are_we-0.1.0/src/where_are_we/readmes.py +136 -0
- where_are_we-0.1.0/tests/test_smoke.py +99 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
smoke:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python: ["3.10", "3.12"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: ${{ matrix.python }}
|
|
19
|
+
- name: install
|
|
20
|
+
run: pip install . pytest
|
|
21
|
+
- name: tests
|
|
22
|
+
run: pytest tests -q
|
|
23
|
+
|
|
24
|
+
- name: map itself
|
|
25
|
+
run: where-are-we --repo . --out /tmp/map && test -s /tmp/map/framework_map_brief.md
|
|
26
|
+
- name: map a polyglot fixture
|
|
27
|
+
run: |
|
|
28
|
+
mkdir -p /tmp/fx/steps /tmp/fx/migrations
|
|
29
|
+
printf 'from behave import step\n@step("a user exists")\ndef f(c):\n pass\n' > /tmp/fx/steps/a.py
|
|
30
|
+
printf 'Given("a logged in user", async function () {});\n' > /tmp/fx/steps.ts
|
|
31
|
+
printf 'func TestFoo(t *testing.T) {}\n' > /tmp/fx/main_test.go
|
|
32
|
+
printf 'CREATE TABLE users (id int, email text);\n' > /tmp/fx/migrations/V1__init.sql
|
|
33
|
+
where-are-we --repo /tmp/fx --out /tmp/fxmap
|
|
34
|
+
grep -q "cucumber-js" /tmp/fxmap/framework_map_brief.md
|
|
35
|
+
grep -q "users" /tmp/fxmap/framework_map_brief.md
|
|
36
|
+
- name: manifest and hooks
|
|
37
|
+
run: |
|
|
38
|
+
where-are-we --repo /tmp/fx --init && test -f /tmp/fx/.framework-map.json
|
|
39
|
+
git -C /tmp/fx init -q && where-are-we --repo /tmp/fx --install-hook git
|
|
40
|
+
test -x /tmp/fx/.git/hooks/post-checkout
|
|
41
|
+
- name: idempotent
|
|
42
|
+
run: |
|
|
43
|
+
where-are-we --repo . --out /tmp/map | tee /tmp/first
|
|
44
|
+
where-are-we --repo . --out /tmp/map | grep -q "unchanged since it was built"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
- uses: actions/setup-python@v5
|
|
13
|
+
with:
|
|
14
|
+
python-version: "3.12"
|
|
15
|
+
- run: pip install build
|
|
16
|
+
- run: python -m build
|
|
17
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
18
|
+
with:
|
|
19
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
First release.
|
|
6
|
+
|
|
7
|
+
- Indexes any codebase into `framework_map.{json,md}` and a brief for a prompt:
|
|
8
|
+
languages, entry points, HTTP routes, data model, public surface, import and
|
|
9
|
+
cross-file call graphs, queues, gRPC, schedules, Kubernetes, Terraform, cache
|
|
10
|
+
keys, permissions, observability, error types, CLI, frontend, contracts
|
|
11
|
+
(OpenAPI, GraphQL, migrations, mocks, flags, i18n), ADRs, coverage, hotspots,
|
|
12
|
+
licenses, git history, blame owners, deprecations and documentation drift.
|
|
13
|
+
- Test suites across behave, pytest, jest, playwright, cypress, robot, JUnit,
|
|
14
|
+
TestNG, Cucumber in Java, Kotlin, Scala, TypeScript, JavaScript and Ruby,
|
|
15
|
+
rspec, go test, xUnit, NUnit, SpecFlow, PHPUnit, Behat, Rust, XCTest, karate,
|
|
16
|
+
gauge, k6 — plus the suite's own state: overlapping phrases, unused steps,
|
|
17
|
+
dead page-object methods, admitted debts, quarantine and past-run timings.
|
|
18
|
+
- `--init` writes a manifest a repository can correct; what it states wins.
|
|
19
|
+
- `--install-hook git|agent`, `--agent-file`, `--only`, `--skip`, `--max-lines`,
|
|
20
|
+
`--diff`, `--force`, `.wawe-ignore`.
|
|
21
|
+
- Rebuilds only when the tree has moved; JSON contract versioned as
|
|
22
|
+
`where-are-we/1` and documented in `SCHEMA.md`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BoberIT
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: where-are-we
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Answer 'where are we?' for a codebase: index a test suite and the product it tests into a map an agent can read instead of grepping.
|
|
5
|
+
Author: BoberIT
|
|
6
|
+
License: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: agents,behave,codebase-map,cucumber,pytest,testing
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Topic :: Software Development :: Testing
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
<div align="center">
|
|
15
|
+
|
|
16
|
+
# where-are-we
|
|
17
|
+
|
|
18
|
+
**The first question in an unfamiliar repository — answered in seconds, without a model.**
|
|
19
|
+
|
|
20
|
+
*Any codebase. Any language. Any agent.*
|
|
21
|
+
|
|
22
|
+
[](LICENSE)
|
|
23
|
+
[](https://www.python.org)
|
|
24
|
+
[](#frameworks)
|
|
25
|
+
[](pyproject.toml)
|
|
26
|
+
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
An agent opening a repository it has never seen spends its first forty tool
|
|
32
|
+
calls on the same questions. Where do the tests live. What may a step call. How
|
|
33
|
+
is a scenario launched. Which environment variables must be set. What does the
|
|
34
|
+
product actually expose.
|
|
35
|
+
|
|
36
|
+
At roughly a minute per turn that is half an hour, in every session, for answers
|
|
37
|
+
that are identical every time and derivable without a model.
|
|
38
|
+
|
|
39
|
+
`where-are-we` derives them by walking the tree, and writes them where the agent
|
|
40
|
+
will read them.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install git+https://github.com/ngavrish/where-are-we
|
|
44
|
+
|
|
45
|
+
where-are-we --repo ~/work/my-suite --product ~/work/my-app/src --out /tmp/map
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
framework map: 66 step modules, 1446 steps, 182 features, 1889 scenarios -> /tmp/map/framework_map.md
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## What you get
|
|
53
|
+
|
|
54
|
+
| file | what it is | typical size |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `framework_map_brief.md` | the digest to put in a prompt | ~40 KB |
|
|
57
|
+
| `framework_map.md` | the full map: every step phrase, every scenario with its line number | ~135 KB |
|
|
58
|
+
| `framework_map.json` | the same as data, for tooling | — |
|
|
59
|
+
|
|
60
|
+
Or straight into the file your harness already reads:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
where-are-we --repo . --agent-file AGENTS.md # also CLAUDE.md, .cursorrules, …
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The map is written between markers. Whatever else lives in that file survives.
|
|
67
|
+
|
|
68
|
+
## What it finds
|
|
69
|
+
|
|
70
|
+
Whatever the repository is. A service, a library, a monorepo, a test suite — it
|
|
71
|
+
is read the same way: what it is made of, where it starts, what it serves, what
|
|
72
|
+
it exposes, and how its parts depend on each other.
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
<table>
|
|
76
|
+
<tr><td width="50%" valign="top">
|
|
77
|
+
|
|
78
|
+
**Any codebase**
|
|
79
|
+
|
|
80
|
+
- languages, by file count
|
|
81
|
+
- where execution starts: entry points, `make` targets, npm scripts, container `CMD`
|
|
82
|
+
- the HTTP routes it serves — Flask, FastAPI, Django, Express, Go, Spring, Rails
|
|
83
|
+
- its data model — SQLAlchemy, Django, Prisma, TypeORM
|
|
84
|
+
- the public surface of each module
|
|
85
|
+
- how the top-level packages depend on each other
|
|
86
|
+
- monorepo layout, where there is one
|
|
87
|
+
- queues, topics and subjects — Kafka, RabbitMQ, SQS, pub/sub
|
|
88
|
+
- gRPC services and their methods, from `.proto`
|
|
89
|
+
- scheduled work — cron, Celery beat, Airflow, Kubernetes CronJobs
|
|
90
|
+
- Kubernetes and Helm manifests, and Terraform resources
|
|
91
|
+
- cache keys, permissions and roles
|
|
92
|
+
- metrics, spans and log fields
|
|
93
|
+
- the error types the code defines
|
|
94
|
+
- command-line commands — click, argparse, cobra
|
|
95
|
+
- frontend components, stores and hooks
|
|
96
|
+
- architecture decision records, coverage reports, largest files, declared licenses
|
|
97
|
+
|
|
98
|
+
**Shape of a test suite**
|
|
99
|
+
|
|
100
|
+
- how the suite is layered, and where each layer lives
|
|
101
|
+
- how a scenario is launched, from the runner scripts' own usage headers
|
|
102
|
+
- what a step may call — public methods, with signatures
|
|
103
|
+
- which step modules and page objects serve each feature
|
|
104
|
+
- what each step actually calls
|
|
105
|
+
|
|
106
|
+
**Facts you would otherwise grep for**
|
|
107
|
+
|
|
108
|
+
- every scenario, with its line number
|
|
109
|
+
- every environment variable, and the file that sets it
|
|
110
|
+
- locator constants, timeouts, budgets, retries
|
|
111
|
+
- module constants and helpers
|
|
112
|
+
- hooks, and what they do
|
|
113
|
+
- fixtures, test data, coverage documents
|
|
114
|
+
- code owners, per-directory READMEs
|
|
115
|
+
|
|
116
|
+
</td><td width="50%" valign="top">
|
|
117
|
+
|
|
118
|
+
**The product under test**
|
|
119
|
+
|
|
120
|
+
- routes, storage keys, API paths
|
|
121
|
+
- every `data-testid`, and which component owns it
|
|
122
|
+
- interface strings an assertion can match
|
|
123
|
+
|
|
124
|
+
**Contracts**
|
|
125
|
+
|
|
126
|
+
- OpenAPI parsed to method and path
|
|
127
|
+
- GraphQL to its types
|
|
128
|
+
- migrations to the tables and columns they create
|
|
129
|
+
- mock servers, feature flags, locale keys
|
|
130
|
+
- pinned image tags, secret paths (paths only)
|
|
131
|
+
|
|
132
|
+
**The state of the suite itself**
|
|
133
|
+
|
|
134
|
+
- step phrases that overlap — so a new one is not written when one exists
|
|
135
|
+
- phrases no feature uses
|
|
136
|
+
- page-object methods nothing calls
|
|
137
|
+
- the TODO and FIXME the code already admits to
|
|
138
|
+
- git history, and which ticket touched what
|
|
139
|
+
- quarantined scenarios, and the slow ones
|
|
140
|
+
|
|
141
|
+
</td></tr>
|
|
142
|
+
</table>
|
|
143
|
+
|
|
144
|
+
## Frameworks
|
|
145
|
+
|
|
146
|
+
behave · pytest · jest · playwright · cypress · robot · JUnit · TestNG ·
|
|
147
|
+
Cucumber-JVM (Java · Kotlin · Scala) · cucumber-js (TypeScript · JavaScript) ·
|
|
148
|
+
cucumber-ruby · rspec · go test · xUnit · NUnit · SpecFlow · PHPUnit · Behat ·
|
|
149
|
+
Rust · XCTest · karate · gauge · k6 · gatling
|
|
150
|
+
|
|
151
|
+
Detection is by shape, not by directory name: a page object is a class that owns
|
|
152
|
+
selectors — wherever it lives, whatever the team calls it. Run it on a
|
|
153
|
+
repository before you know what is in it.
|
|
154
|
+
|
|
155
|
+
## When the repository knows better
|
|
156
|
+
|
|
157
|
+
Autodetection gets the shape right and the vocabulary wrong. A repository states
|
|
158
|
+
its own, and what it states wins:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
where-are-we --repo . --init # writes a starter manifest from what was detected
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"name": "billing-e2e",
|
|
167
|
+
"purpose": "End-to-end tests for the billing portal.",
|
|
168
|
+
"layers": {
|
|
169
|
+
"steps": "steps/*.py — steps own no selectors, they call page objects"
|
|
170
|
+
},
|
|
171
|
+
"product_src": ["../billing-web/src"],
|
|
172
|
+
"conventions": ["After a fix, re-run only what failed."]
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`.framework-map.json` at the root, or the same block fenced as
|
|
177
|
+
` ```framework-map ` inside `README.md`.
|
|
178
|
+
|
|
179
|
+
Directories can explain themselves too:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
wawe-readmes --repo . # a README in every content directory that lacks one,
|
|
183
|
+
# derived from what is in it, with one TODO line for a human
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The map reads them back.
|
|
187
|
+
|
|
188
|
+
## Keeping it current
|
|
189
|
+
|
|
190
|
+
A map is worth rebuilding when the thing it describes has moved. The commit and
|
|
191
|
+
the newest file in the tree are recorded with it, so running the command on
|
|
192
|
+
every checkout costs a stat walk and nothing else. `--force` rebuilds regardless.
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
where-are-we --repo . --install-hook git # post-checkout, post-merge, post-commit
|
|
196
|
+
where-are-we --repo . --install-hook agent # SessionStart, before the first turn
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Options
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
--repo PATH the test repository to index
|
|
203
|
+
--product PATH,… source roots of the application under test
|
|
204
|
+
--out DIR where the three files land
|
|
205
|
+
--agent-file FILE also write the brief into AGENTS.md / CLAUDE.md / .cursorrules
|
|
206
|
+
--rules PATH a corpus of rules to list by name
|
|
207
|
+
--runs-api URL a runs database, to carry what earlier runs concluded
|
|
208
|
+
--init write a starter .framework-map.json
|
|
209
|
+
--install-hook git|agent
|
|
210
|
+
--force rebuild even when nothing moved
|
|
211
|
+
--only "routes,data model" keep only these sections in the brief
|
|
212
|
+
--skip "coverage,history" drop these
|
|
213
|
+
--max-lines N cap the brief; the full map is untouched
|
|
214
|
+
--diff what changed since the map already in --out
|
|
215
|
+
--quiet no summary line
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Why it exists
|
|
219
|
+
|
|
220
|
+
It was built for an agentic QA pipeline where seven branches ran at once and
|
|
221
|
+
each one opened the same way: forty greps to find where the steps live, which
|
|
222
|
+
page object owns the portal, how the driver is built, what `environment.py`
|
|
223
|
+
does. Identical in every branch, identical every run, and derivable without a
|
|
224
|
+
model — three runs died at their deadline with the branches still reading.
|
|
225
|
+
|
|
226
|
+
Nothing about it turned out to be specific to that pipeline, or to that agent,
|
|
227
|
+
or to Python. So it lives here.
|
|
228
|
+
|
|
229
|
+
## License
|
|
230
|
+
|
|
231
|
+
MIT
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# where-are-we
|
|
4
|
+
|
|
5
|
+
**The first question in an unfamiliar repository — answered in seconds, without a model.**
|
|
6
|
+
|
|
7
|
+
*Any codebase. Any language. Any agent.*
|
|
8
|
+
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
[](https://www.python.org)
|
|
11
|
+
[](#frameworks)
|
|
12
|
+
[](pyproject.toml)
|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
An agent opening a repository it has never seen spends its first forty tool
|
|
19
|
+
calls on the same questions. Where do the tests live. What may a step call. How
|
|
20
|
+
is a scenario launched. Which environment variables must be set. What does the
|
|
21
|
+
product actually expose.
|
|
22
|
+
|
|
23
|
+
At roughly a minute per turn that is half an hour, in every session, for answers
|
|
24
|
+
that are identical every time and derivable without a model.
|
|
25
|
+
|
|
26
|
+
`where-are-we` derives them by walking the tree, and writes them where the agent
|
|
27
|
+
will read them.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install git+https://github.com/ngavrish/where-are-we
|
|
31
|
+
|
|
32
|
+
where-are-we --repo ~/work/my-suite --product ~/work/my-app/src --out /tmp/map
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
framework map: 66 step modules, 1446 steps, 182 features, 1889 scenarios -> /tmp/map/framework_map.md
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## What you get
|
|
40
|
+
|
|
41
|
+
| file | what it is | typical size |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| `framework_map_brief.md` | the digest to put in a prompt | ~40 KB |
|
|
44
|
+
| `framework_map.md` | the full map: every step phrase, every scenario with its line number | ~135 KB |
|
|
45
|
+
| `framework_map.json` | the same as data, for tooling | — |
|
|
46
|
+
|
|
47
|
+
Or straight into the file your harness already reads:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
where-are-we --repo . --agent-file AGENTS.md # also CLAUDE.md, .cursorrules, …
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The map is written between markers. Whatever else lives in that file survives.
|
|
54
|
+
|
|
55
|
+
## What it finds
|
|
56
|
+
|
|
57
|
+
Whatever the repository is. A service, a library, a monorepo, a test suite — it
|
|
58
|
+
is read the same way: what it is made of, where it starts, what it serves, what
|
|
59
|
+
it exposes, and how its parts depend on each other.
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
<table>
|
|
63
|
+
<tr><td width="50%" valign="top">
|
|
64
|
+
|
|
65
|
+
**Any codebase**
|
|
66
|
+
|
|
67
|
+
- languages, by file count
|
|
68
|
+
- where execution starts: entry points, `make` targets, npm scripts, container `CMD`
|
|
69
|
+
- the HTTP routes it serves — Flask, FastAPI, Django, Express, Go, Spring, Rails
|
|
70
|
+
- its data model — SQLAlchemy, Django, Prisma, TypeORM
|
|
71
|
+
- the public surface of each module
|
|
72
|
+
- how the top-level packages depend on each other
|
|
73
|
+
- monorepo layout, where there is one
|
|
74
|
+
- queues, topics and subjects — Kafka, RabbitMQ, SQS, pub/sub
|
|
75
|
+
- gRPC services and their methods, from `.proto`
|
|
76
|
+
- scheduled work — cron, Celery beat, Airflow, Kubernetes CronJobs
|
|
77
|
+
- Kubernetes and Helm manifests, and Terraform resources
|
|
78
|
+
- cache keys, permissions and roles
|
|
79
|
+
- metrics, spans and log fields
|
|
80
|
+
- the error types the code defines
|
|
81
|
+
- command-line commands — click, argparse, cobra
|
|
82
|
+
- frontend components, stores and hooks
|
|
83
|
+
- architecture decision records, coverage reports, largest files, declared licenses
|
|
84
|
+
|
|
85
|
+
**Shape of a test suite**
|
|
86
|
+
|
|
87
|
+
- how the suite is layered, and where each layer lives
|
|
88
|
+
- how a scenario is launched, from the runner scripts' own usage headers
|
|
89
|
+
- what a step may call — public methods, with signatures
|
|
90
|
+
- which step modules and page objects serve each feature
|
|
91
|
+
- what each step actually calls
|
|
92
|
+
|
|
93
|
+
**Facts you would otherwise grep for**
|
|
94
|
+
|
|
95
|
+
- every scenario, with its line number
|
|
96
|
+
- every environment variable, and the file that sets it
|
|
97
|
+
- locator constants, timeouts, budgets, retries
|
|
98
|
+
- module constants and helpers
|
|
99
|
+
- hooks, and what they do
|
|
100
|
+
- fixtures, test data, coverage documents
|
|
101
|
+
- code owners, per-directory READMEs
|
|
102
|
+
|
|
103
|
+
</td><td width="50%" valign="top">
|
|
104
|
+
|
|
105
|
+
**The product under test**
|
|
106
|
+
|
|
107
|
+
- routes, storage keys, API paths
|
|
108
|
+
- every `data-testid`, and which component owns it
|
|
109
|
+
- interface strings an assertion can match
|
|
110
|
+
|
|
111
|
+
**Contracts**
|
|
112
|
+
|
|
113
|
+
- OpenAPI parsed to method and path
|
|
114
|
+
- GraphQL to its types
|
|
115
|
+
- migrations to the tables and columns they create
|
|
116
|
+
- mock servers, feature flags, locale keys
|
|
117
|
+
- pinned image tags, secret paths (paths only)
|
|
118
|
+
|
|
119
|
+
**The state of the suite itself**
|
|
120
|
+
|
|
121
|
+
- step phrases that overlap — so a new one is not written when one exists
|
|
122
|
+
- phrases no feature uses
|
|
123
|
+
- page-object methods nothing calls
|
|
124
|
+
- the TODO and FIXME the code already admits to
|
|
125
|
+
- git history, and which ticket touched what
|
|
126
|
+
- quarantined scenarios, and the slow ones
|
|
127
|
+
|
|
128
|
+
</td></tr>
|
|
129
|
+
</table>
|
|
130
|
+
|
|
131
|
+
## Frameworks
|
|
132
|
+
|
|
133
|
+
behave · pytest · jest · playwright · cypress · robot · JUnit · TestNG ·
|
|
134
|
+
Cucumber-JVM (Java · Kotlin · Scala) · cucumber-js (TypeScript · JavaScript) ·
|
|
135
|
+
cucumber-ruby · rspec · go test · xUnit · NUnit · SpecFlow · PHPUnit · Behat ·
|
|
136
|
+
Rust · XCTest · karate · gauge · k6 · gatling
|
|
137
|
+
|
|
138
|
+
Detection is by shape, not by directory name: a page object is a class that owns
|
|
139
|
+
selectors — wherever it lives, whatever the team calls it. Run it on a
|
|
140
|
+
repository before you know what is in it.
|
|
141
|
+
|
|
142
|
+
## When the repository knows better
|
|
143
|
+
|
|
144
|
+
Autodetection gets the shape right and the vocabulary wrong. A repository states
|
|
145
|
+
its own, and what it states wins:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
where-are-we --repo . --init # writes a starter manifest from what was detected
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"name": "billing-e2e",
|
|
154
|
+
"purpose": "End-to-end tests for the billing portal.",
|
|
155
|
+
"layers": {
|
|
156
|
+
"steps": "steps/*.py — steps own no selectors, they call page objects"
|
|
157
|
+
},
|
|
158
|
+
"product_src": ["../billing-web/src"],
|
|
159
|
+
"conventions": ["After a fix, re-run only what failed."]
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`.framework-map.json` at the root, or the same block fenced as
|
|
164
|
+
` ```framework-map ` inside `README.md`.
|
|
165
|
+
|
|
166
|
+
Directories can explain themselves too:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
wawe-readmes --repo . # a README in every content directory that lacks one,
|
|
170
|
+
# derived from what is in it, with one TODO line for a human
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
The map reads them back.
|
|
174
|
+
|
|
175
|
+
## Keeping it current
|
|
176
|
+
|
|
177
|
+
A map is worth rebuilding when the thing it describes has moved. The commit and
|
|
178
|
+
the newest file in the tree are recorded with it, so running the command on
|
|
179
|
+
every checkout costs a stat walk and nothing else. `--force` rebuilds regardless.
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
where-are-we --repo . --install-hook git # post-checkout, post-merge, post-commit
|
|
183
|
+
where-are-we --repo . --install-hook agent # SessionStart, before the first turn
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Options
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
--repo PATH the test repository to index
|
|
190
|
+
--product PATH,… source roots of the application under test
|
|
191
|
+
--out DIR where the three files land
|
|
192
|
+
--agent-file FILE also write the brief into AGENTS.md / CLAUDE.md / .cursorrules
|
|
193
|
+
--rules PATH a corpus of rules to list by name
|
|
194
|
+
--runs-api URL a runs database, to carry what earlier runs concluded
|
|
195
|
+
--init write a starter .framework-map.json
|
|
196
|
+
--install-hook git|agent
|
|
197
|
+
--force rebuild even when nothing moved
|
|
198
|
+
--only "routes,data model" keep only these sections in the brief
|
|
199
|
+
--skip "coverage,history" drop these
|
|
200
|
+
--max-lines N cap the brief; the full map is untouched
|
|
201
|
+
--diff what changed since the map already in --out
|
|
202
|
+
--quiet no summary line
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Why it exists
|
|
206
|
+
|
|
207
|
+
It was built for an agentic QA pipeline where seven branches ran at once and
|
|
208
|
+
each one opened the same way: forty greps to find where the steps live, which
|
|
209
|
+
page object owns the portal, how the driver is built, what `environment.py`
|
|
210
|
+
does. Identical in every branch, identical every run, and derivable without a
|
|
211
|
+
model — three runs died at their deadline with the branches still reading.
|
|
212
|
+
|
|
213
|
+
Nothing about it turned out to be specific to that pipeline, or to that agent,
|
|
214
|
+
or to Python. So it lives here.
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
MIT
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# `framework_map.json` — schema `where-are-we/1`
|
|
2
|
+
|
|
3
|
+
Stable contract. Sections may be added within a major version; a section that
|
|
4
|
+
exists keeps its shape. `schema` names the version; `fingerprint` is what the map
|
|
5
|
+
was built from (commit and newest mtime) and is how staleness is decided.
|
|
6
|
+
|
|
7
|
+
| key | shape | what it is |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `schema` | string | `where-are-we/<major>` |
|
|
10
|
+
| `repo` | string | absolute path indexed |
|
|
11
|
+
| `fingerprint` | string | `<commit>:<newest mtime>` |
|
|
12
|
+
| `stated` | object | what `.framework-map.json` declared, verbatim |
|
|
13
|
+
| `languages` | {lang: count} | files per language |
|
|
14
|
+
| `entry` | {what: [lines]} | entry points, make targets, npm scripts, container CMD |
|
|
15
|
+
| `exports` | {file: [names]} | public surface per module |
|
|
16
|
+
| `routes_served` | [string] | HTTP routes the code serves |
|
|
17
|
+
| `models` | {name: [fields]} | ORM models and their fields |
|
|
18
|
+
| `import_graph` | {package: [packages]} | dependencies between top-level packages |
|
|
19
|
+
| `call_graph_files` | {file:func: [callees]} | cross-file calls |
|
|
20
|
+
| `data_flow` | {file: {paths, tables}} | endpoints and tables co-located |
|
|
21
|
+
| `messaging` | {file: [topics]} | queues, topics, subjects |
|
|
22
|
+
| `grpc` | {service: [methods]} | from `.proto` |
|
|
23
|
+
| `schedules` | {file: [expressions]} | cron, beat, DAGs, CronJobs |
|
|
24
|
+
| `kubernetes` | {file: {kinds, names}} | manifests |
|
|
25
|
+
| `iac` | {file: [[type, name]]} | Terraform resources |
|
|
26
|
+
| `cache_keys` | [string] | cache keys the code uses |
|
|
27
|
+
| `permissions` | {file: [names]} | roles, scopes, guards |
|
|
28
|
+
| `observability` | {metrics, spans, log_fields} | names emitted |
|
|
29
|
+
| `error_types` | {class: file} | error and exception types |
|
|
30
|
+
| `cli_commands` | {file: [commands]} | click, argparse, cobra |
|
|
31
|
+
| `frontend` | {components, stores, hooks} | UI structure |
|
|
32
|
+
| `contracts` | {openapi, graphql, migrations, mocks, feature_flags, i18n, images, secret_paths} | files |
|
|
33
|
+
| `contract_details` | {endpoints, graphql, migration_tables, i18n_keys, flags} | parsed contents |
|
|
34
|
+
| `steps` | {file: [phrases]} | step definitions |
|
|
35
|
+
| `features` | {file: {scenarios: [{line, name}], tags}} | feature files |
|
|
36
|
+
| `page_objects`, `drivers`, `scripts` | [paths] | test layers |
|
|
37
|
+
| `public_api` | {file: [signatures]} | what a step may call |
|
|
38
|
+
| `feature_links` | {feature: {step_modules, page_objects}} | traceability |
|
|
39
|
+
| `near_duplicates` | [{a, a_in, b, b_in, similarity}] | overlapping phrases |
|
|
40
|
+
| `unused_steps`, `unused_api` | {file: [names]} | dead weight |
|
|
41
|
+
| `debts` | {file: [lines]} | TODO, FIXME, skip |
|
|
42
|
+
| `git_history` | {file: [commits]} | most-changed files |
|
|
43
|
+
| `ticket_links` | {ticket: {subject, files}} | what a ticket touched |
|
|
44
|
+
| `blame_owners` | {file: [people]} | who has been touching what |
|
|
45
|
+
| `coverage_by_file` | {file: percent} | from coverage reports |
|
|
46
|
+
| `deprecations`, `api_versions`, `doc_drift` | see README | decay signals |
|
|
47
|
+
| `scenario_history`, `slow_steps` | from junit | what past runs measured |
|
|
48
|
+
| `dir_readmes` | {dir: {path, summary, headings}} | directories explaining themselves |
|
|
49
|
+
| `counts` | {step_modules, steps, features, scenarios} | totals |
|
|
50
|
+
|
|
51
|
+
Everything is derived; nothing is inferred by a model. An absent section means
|
|
52
|
+
the repository has none of that, not that the tool failed.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "where-are-we"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Answer 'where are we?' for a codebase: index a test suite and the product it tests into a map an agent can read instead of grepping."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
license = {text = "MIT"}
|
|
8
|
+
authors = [{name = "BoberIT"}]
|
|
9
|
+
keywords = ["testing", "agents", "codebase-map", "behave", "pytest", "cucumber"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Programming Language :: Python :: 3",
|
|
12
|
+
"Topic :: Software Development :: Testing",
|
|
13
|
+
]
|
|
14
|
+
dependencies = []
|
|
15
|
+
|
|
16
|
+
[project.scripts]
|
|
17
|
+
where-are-we = "where_are_we.mapper:main"
|
|
18
|
+
wawe-readmes = "where_are_we.readmes:main"
|
|
19
|
+
|
|
20
|
+
[build-system]
|
|
21
|
+
requires = ["hatchling"]
|
|
22
|
+
build-backend = "hatchling.build"
|
|
23
|
+
|
|
24
|
+
[tool.hatch.build.targets.wheel]
|
|
25
|
+
packages = ["src/where_are_we"]
|