livespec 0.24.0__py3-none-any.whl
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.
- livespec-0.24.0.dist-info/METADATA +703 -0
- livespec-0.24.0.dist-info/RECORD +53 -0
- livespec-0.24.0.dist-info/WHEEL +4 -0
- livespec-0.24.0.dist-info/entry_points.txt +3 -0
- livespec_mcp/__init__.py +22 -0
- livespec_mcp/cli.py +185 -0
- livespec_mcp/config.py +377 -0
- livespec_mcp/domain/__init__.py +0 -0
- livespec_mcp/domain/extractors.py +1945 -0
- livespec_mcp/domain/graph.py +242 -0
- livespec_mcp/domain/indexer.py +708 -0
- livespec_mcp/domain/languages.py +72 -0
- livespec_mcp/domain/matcher.py +222 -0
- livespec_mcp/domain/md_specs.py +455 -0
- livespec_mcp/domain/openspec_changes.py +311 -0
- livespec_mcp/domain/openspec_discover.py +80 -0
- livespec_mcp/domain/openspec_export.py +212 -0
- livespec_mcp/domain/openspec_validate.py +199 -0
- livespec_mcp/domain/rag.py +660 -0
- livespec_mcp/domain/specs_sync.py +428 -0
- livespec_mcp/domain/watcher.py +188 -0
- livespec_mcp/error_middleware.py +73 -0
- livespec_mcp/explorer/__init__.py +23 -0
- livespec_mcp/explorer/asgi.py +162 -0
- livespec_mcp/explorer/autowire.py +105 -0
- livespec_mcp/explorer/fastapi.py +105 -0
- livespec_mcp/explorer/install.py +147 -0
- livespec_mcp/instrumentation.py +174 -0
- livespec_mcp/plugin_visibility.py +153 -0
- livespec_mcp/prompts.py +162 -0
- livespec_mcp/resources.py +226 -0
- livespec_mcp/server.py +59 -0
- livespec_mcp/state.py +252 -0
- livespec_mcp/storage/__init__.py +0 -0
- livespec_mcp/storage/db.py +597 -0
- livespec_mcp/storage/schema.sql +349 -0
- livespec_mcp/storage/trends.py +110 -0
- livespec_mcp/templates/AGENT_PLAYBOOK.md +321 -0
- livespec_mcp/templates/fastapi/SESSION_PROMPT.md +20 -0
- livespec_mcp/templates/fastapi/cursor/rules/livespec-fastapi.mdc +44 -0
- livespec_mcp/templates/fastapi/cursor/skills/livespec-fastapi/SKILL.md +63 -0
- livespec_mcp/tools/__init__.py +0 -0
- livespec_mcp/tools/_errors.py +31 -0
- livespec_mcp/tools/analysis.py +3718 -0
- livespec_mcp/tools/docs.py +384 -0
- livespec_mcp/tools/explorer.py +2678 -0
- livespec_mcp/tools/indexing.py +227 -0
- livespec_mcp/tools/plugins/__init__.py +189 -0
- livespec_mcp/tools/plugins/docs.py +24 -0
- livespec_mcp/tools/plugins/spec.py +33 -0
- livespec_mcp/tools/search.py +88 -0
- livespec_mcp/tools/specs.py +1488 -0
- livespec_mcp/workspace_param.py +57 -0
|
@@ -0,0 +1,703 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: livespec
|
|
3
|
+
Version: 0.24.0
|
|
4
|
+
Summary: Code intelligence for AI agents: call graph, impact analysis, and bidirectional Spec <-> code traceability
|
|
5
|
+
Author-email: Juan Pablo Diaz <juanpablo516@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: fastmcp<4,>=3
|
|
9
|
+
Requires-Dist: networkx>=3.2
|
|
10
|
+
Requires-Dist: pathspec>=0.12
|
|
11
|
+
Requires-Dist: pydantic>=2.7
|
|
12
|
+
Requires-Dist: tomli>=2; python_full_version < '3.11'
|
|
13
|
+
Requires-Dist: tree-sitter-language-pack<1.6.3,>=0.7
|
|
14
|
+
Requires-Dist: tree-sitter>=0.25
|
|
15
|
+
Requires-Dist: watchdog>=4.0
|
|
16
|
+
Requires-Dist: xxhash>=3.4
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: hypothesis>=6.100; extra == 'dev'
|
|
19
|
+
Requires-Dist: psutil>=5.9; extra == 'dev'
|
|
20
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
21
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
22
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
23
|
+
Provides-Extra: embeddings
|
|
24
|
+
Requires-Dist: fastembed>=0.4; extra == 'embeddings'
|
|
25
|
+
Requires-Dist: sqlite-vec>=0.1.6; extra == 'embeddings'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# livespec
|
|
29
|
+
|
|
30
|
+
**Code intelligence for AI agents** — call graph, impact analysis, and
|
|
31
|
+
bidirectional **Spec ↔ code** traceability (functional requirements, ADRs,
|
|
32
|
+
NFRs, and other spec kinds). Local-first, zero external services.
|
|
33
|
+
|
|
34
|
+
Ships as a **Claude Code plugin** bundling three things: the **MCP server**
|
|
35
|
+
(the tools), a specialized **subagent**, and a preloaded **Skill** (the
|
|
36
|
+
operating manual). Everything an agent sees — the plugin, subagent, Skill, and
|
|
37
|
+
MCP tool namespace — is `livespec`. The MCP server is still distributed as the
|
|
38
|
+
`livespec-mcp` package (`uvx livespec-mcp` / `pip install livespec-mcp`); that
|
|
39
|
+
package/command name is unchanged.
|
|
40
|
+
|
|
41
|
+
Battle-tested on real codebases. Four releases of compounding wins
|
|
42
|
+
on the same Django 5.1.4 queries:
|
|
43
|
+
|
|
44
|
+
| Tool on Django (40K symbols) | v0.8 | v0.9 | v0.10 | v0.14 |
|
|
45
|
+
|---|---:|---:|---:|---:|
|
|
46
|
+
| `find_dead_code` candidates | 824 | 514 | 348 | **344** (−58% cumulative) |
|
|
47
|
+
| `find_endpoints(framework='django')` | 20 | **162** (+8×) | 162 | 162 |
|
|
48
|
+
| `quick_orient` p95 | ~60 ms | ~60 ms | ~60 ms | ~60 ms |
|
|
49
|
+
| Partial reindex (touch 1 file, Django) | — | ~7 s | — | **1.4 s** |
|
|
50
|
+
|
|
51
|
+
Validated across 5 distinct agent profiles (exploration, refactor,
|
|
52
|
+
Spec flow, Django bugfix, TypeScript feature) — see
|
|
53
|
+
[`docs/AGENT_USAGE_DATA.md`](docs/AGENT_USAGE_DATA.md).
|
|
54
|
+
|
|
55
|
+
> Want the agentic flow without reading further?
|
|
56
|
+
> - **MCP prompt `agent_playbook`** — how to use tools *and* annotate code (`@spec:`).
|
|
57
|
+
> - [`docs/AGENT_QUICKSTART.md`](docs/AGENT_QUICKSTART.md) — 10-step cold-open flow.
|
|
58
|
+
> - [`docs/AGENT_PLAYBOOK.md`](docs/AGENT_PLAYBOOK.md) — same content as the prompt.
|
|
59
|
+
|
|
60
|
+
## 30-second tour
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Wire as an MCP server next to your editor (claude.ai/code, Cursor, ...).
|
|
64
|
+
# Every tool call carries workspace="/abs/path" — one server, N repos.
|
|
65
|
+
livespec-mcp
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```jsonc
|
|
69
|
+
// First call: cold-index the workspace once
|
|
70
|
+
> index_project()
|
|
71
|
+
{ "files_total": 2898, "symbols_total": 39789, "edges_total": 465179,
|
|
72
|
+
"languages": {"python": 2786, "javascript": 112}, "watcher_started": false }
|
|
73
|
+
|
|
74
|
+
// Composite first-contact on an unfamiliar symbol
|
|
75
|
+
> quick_orient(qname="django.contrib.auth.middleware.AuthenticationMiddleware.process_request")
|
|
76
|
+
{ "kind": "method", "is_entry_point": false,
|
|
77
|
+
"callers_count": 56, "callees_count": 3,
|
|
78
|
+
"top_callees": [
|
|
79
|
+
{"qualified_name": "django.contrib.auth.middleware.get_user", "pagerank": 0.000024},
|
|
80
|
+
{"qualified_name": "django.utils.functional.SimpleLazyObject", "pagerank": 0.000209},
|
|
81
|
+
{"qualified_name": "django.core.exceptions.ImproperlyConfigured", "pagerank": 0.000872}
|
|
82
|
+
],
|
|
83
|
+
"specs": [] }
|
|
84
|
+
|
|
85
|
+
// Wider blast radius on a Spec-active codebase
|
|
86
|
+
> analyze_impact(target_type="spec", target="SPEC-042")
|
|
87
|
+
{ "spec_id": "SPEC-042", "implementing_symbols": [...],
|
|
88
|
+
"dependent_specs": ["SPEC-088", "SPEC-091"],
|
|
89
|
+
"impacted_callers": [...] }
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Built for the questions an agent asks on an unfamiliar codebase:
|
|
93
|
+
|
|
94
|
+
- ¿Qué código implementa el SPEC-042?
|
|
95
|
+
- Si modifico `auth.verify`, ¿qué Specs y qué llamadores se ven afectados?
|
|
96
|
+
- ¿Qué módulos no tienen ningún Spec asociado?
|
|
97
|
+
- ¿Qué Specs dependen de SPEC-042 transitivamente?
|
|
98
|
+
|
|
99
|
+
Spec traceability is the differentiator. Most code-intel tools stop at "what
|
|
100
|
+
calls this function?". livespec layers Spec ↔ code links (functional
|
|
101
|
+
requirements, ADRs, NFRs, and other kinds) on top so an agent on a
|
|
102
|
+
serious-software-shop codebase can answer *"changing this function affects
|
|
103
|
+
SPEC-042, SPEC-088 and 3 dependent Specs"* in one round-trip. Spec agentic
|
|
104
|
+
tools ship in the default surface; Spec mutation/management tools live in
|
|
105
|
+
the `livespec-spec` plugin. Plugins register at boot (multi-tenant: every
|
|
106
|
+
`workspace=` has its own DB), but **`PluginVisibilityMiddleware`** hides
|
|
107
|
+
mutation/doc tools from `tools/list` until that workspace has `spec`/`doc`
|
|
108
|
+
rows — or you set `LIVESPEC_PLUGINS=spec` / `=all` in MCP config.
|
|
109
|
+
|
|
110
|
+
### What "living" actually means here
|
|
111
|
+
|
|
112
|
+
| Layer | Lives | How |
|
|
113
|
+
|---|---|---|
|
|
114
|
+
| Symbol index | ✅ | xxh3 content-hash incremental, run `index_project` on demand |
|
|
115
|
+
| Call graph + edges | ✅ | re-resolved on every change; persistent `symbol_ref` |
|
|
116
|
+
| Spec ↔ code links | ✅ | auto-scan of `@spec:` annotations after every `index_project` |
|
|
117
|
+
| Spec ↔ Spec graph | ✅ | explicit, cycle-checked; `link_spec_dependency` (plugin) |
|
|
118
|
+
| Drift detection | ✅ | body_hash + signature_hash on every symbol; `list_docs(only_stale=True)` (plugin) |
|
|
119
|
+
| **Generated docs content** | ❌ on-demand | `generate_docs` (plugin) needs an LLM-capable caller or an MCP host that supports sampling. Drift is *detected*, not *fixed*. |
|
|
120
|
+
|
|
121
|
+
So: traceability is live, docs are not. If your workflow is "give me an
|
|
122
|
+
agent that always knows which code implements which spec, and which
|
|
123
|
+
tests probably break when X changes" — this is exactly what the project is
|
|
124
|
+
good at. If you wanted "writes my doc comments while I sleep" — not yet.
|
|
125
|
+
|
|
126
|
+
## Stack
|
|
127
|
+
|
|
128
|
+
- **FastMCP 2.14** (stdio transport)
|
|
129
|
+
- **SQLite** (single `docs.db` file, ACID, WAL, explicit migration framework)
|
|
130
|
+
- **tree-sitter + tree-sitter-language-pack** for multi-language parsing
|
|
131
|
+
- **Python `ast`** for high-precision Python extraction
|
|
132
|
+
- **NetworkX** for call graph and topological impact analysis (cached per
|
|
133
|
+
index run)
|
|
134
|
+
|
|
135
|
+
100% local, zero external services, zero API keys required.
|
|
136
|
+
|
|
137
|
+
## Language support
|
|
138
|
+
|
|
139
|
+
Honest table — only languages with a passing test suite are claimed.
|
|
140
|
+
|
|
141
|
+
| Language | Status | What's covered |
|
|
142
|
+
|----------|--------|----------------|
|
|
143
|
+
| **Python** | ✅ Tested | Functions, classes, methods, decorators, calls — uses `ast` for full precision. Imports drive scoped resolution (P0.4). |
|
|
144
|
+
| **Go** | ✅ Tested | Functions, struct types via `type_spec`, struct methods, calls. **Scoped resolution** via `import` + alias (P1.A2 v0.4). |
|
|
145
|
+
| **Java** | ✅ Tested | Classes, methods, calls (`method_invocation`) |
|
|
146
|
+
| **JavaScript** | ✅ Tested | Function declarations, **arrow functions** assigned to const/let, classes, methods. **Scoped resolution** via ES6 `import` and CommonJS `require` (P1.A1 v0.4). |
|
|
147
|
+
| **TypeScript** | ✅ Tested | Same as JS plus typed signatures (`.ts` and `.tsx`). **Scoped resolution** via ES6 `import` (P1.A1 v0.4). |
|
|
148
|
+
| **Rust** | ✅ Tested | Free functions, struct/enum types, **`impl` block methods** as `Type::method`, traits. **Scoped resolution** via `use` declarations (P4.A3 v0.5). |
|
|
149
|
+
| **Ruby** | ✅ Tested | `def`, `class`, `module`, `singleton_method`, calls. Best-effort scoped resolution via `require_relative` + receiver field (P1.A4 v0.4). |
|
|
150
|
+
| **PHP** | ✅ Tested | Classes, methods, function/method/scoped call expressions. Best-effort scoped resolution via `use Namespace\X` for `Class::method()` (P1.A4 v0.4); instance-method calls are not scoped. |
|
|
151
|
+
| C, C++, C#, Kotlin, Swift, Scala | ⚠️ Untested | The generic tree-sitter extractor will *attempt* to parse these (they're listed in `EXT_LANGUAGE`) but no test suite covers them. Symbol coverage may be partial — open an issue with a fixture if you need a specific language hardened. |
|
|
152
|
+
|
|
153
|
+
The extractor is a heuristic over hardcoded tree-sitter node types
|
|
154
|
+
(`_DEF_NODE_TYPES`, `_CALL_NODE_TYPES` in `extractors.py`); it intentionally
|
|
155
|
+
trades completeness for simplicity. Use the per-language tests in
|
|
156
|
+
`tests/test_extractors.py` as the contract.
|
|
157
|
+
|
|
158
|
+
## Install
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
uv venv --python 3.12
|
|
162
|
+
uv pip install -e ".[dev]"
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Run as MCP server
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
livespec-mcp # stdio MCP server (same as `livespec-mcp serve`)
|
|
169
|
+
livespec # same command — `livespec` is an alias of `livespec-mcp`
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
> The **product** is `livespec`; the **package/command** stays `livespec-mcp`
|
|
173
|
+
> (`pip install livespec-mcp`, `uvx livespec-mcp`) for back-compat. After a
|
|
174
|
+
> local install both `livespec` and `livespec-mcp` invoke the same entry point.
|
|
175
|
+
|
|
176
|
+
Every tool call requires `workspace="/abs/path"` (no cwd or env fallback
|
|
177
|
+
since v0.12). Persistent state lives in `<workspace>/.mcp-docs/docs.db`.
|
|
178
|
+
|
|
179
|
+
### Headless CLI (v0.14)
|
|
180
|
+
|
|
181
|
+
The same indexing pipeline without an MCP host — for cron, systemd
|
|
182
|
+
timers, pre-commit hooks, CI:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
livespec-mcp index /path/to/repo [--force] [--embed] # JSON stats to stdout
|
|
186
|
+
livespec-mcp status /path/to/repo # index status JSON
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Claude Code / Cursor wiring
|
|
190
|
+
|
|
191
|
+
```json
|
|
192
|
+
{
|
|
193
|
+
"mcpServers": {
|
|
194
|
+
"livespec": {
|
|
195
|
+
"command": "uv",
|
|
196
|
+
"args": ["--directory", "/path/to/livespec", "run", "livespec-mcp"]
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Multi-repo:** pass `workspace="/abs/path/to/project"` on **every** tool call (required).
|
|
203
|
+
No `LIVESPEC_WORKSPACE` — switching repos is only a different `workspace=` value.
|
|
204
|
+
The server caches up to 8 workspaces (LRU); no MCP restart.
|
|
205
|
+
|
|
206
|
+
### Keep the index fresh (Claude Code hook)
|
|
207
|
+
|
|
208
|
+
The watcher (`index_project(watch=True)`) works but is a race trap when
|
|
209
|
+
multiple agents write concurrently. For single-user Claude Code sessions
|
|
210
|
+
a `PostToolUse` hook is simpler and exact — re-index incrementally after
|
|
211
|
+
every file edit (hash-skip makes no-op runs cheap):
|
|
212
|
+
|
|
213
|
+
```json
|
|
214
|
+
{
|
|
215
|
+
"hooks": {
|
|
216
|
+
"PostToolUse": [
|
|
217
|
+
{
|
|
218
|
+
"matcher": "Edit|Write|NotebookEdit",
|
|
219
|
+
"hooks": [
|
|
220
|
+
{
|
|
221
|
+
"type": "command",
|
|
222
|
+
"command": "livespec-mcp index \"$CLAUDE_PROJECT_DIR\" >/dev/null 2>&1 &"
|
|
223
|
+
}
|
|
224
|
+
]
|
|
225
|
+
}
|
|
226
|
+
]
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
The trailing `&` keeps the hook non-blocking; the next tool call sees a
|
|
232
|
+
fresh index.
|
|
233
|
+
|
|
234
|
+
### FastAPI integration
|
|
235
|
+
|
|
236
|
+
Three steps to serve the Spec Explorer from your API — no manual wiring unless
|
|
237
|
+
you disable autowire:
|
|
238
|
+
|
|
239
|
+
1. **Install** livespec-mcp in the same environment as your app
|
|
240
|
+
(`pip install livespec-mcp` or `uv add livespec-mcp`).
|
|
241
|
+
|
|
242
|
+
2. **Index once** (MCP tool or CLI). If the repo has `app = FastAPI(...)` in
|
|
243
|
+
`main.py` or `app.py`, `index_project` auto-builds `.mcp-docs/explorer/`
|
|
244
|
+
and appends `mount_explorer(app)` when `[explorer] auto_mount = true`
|
|
245
|
+
(default in `.livespec.toml`).
|
|
246
|
+
|
|
247
|
+
3. **Open** `http://localhost:<port>/explorer/` — the Overview tab shows
|
|
248
|
+
spec status, coverage trend, and navigation into API / Changes /
|
|
249
|
+
Coverage gaps. For a quick try without your app:
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
livespec-mcp index /path/to/your/repo
|
|
253
|
+
livespec-mcp explorer serve /path/to/your/repo
|
|
254
|
+
# → http://127.0.0.1:8765/explorer/
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Set `[explorer] auto_mount = false` in `.livespec.toml` if you prefer runtime mount:
|
|
258
|
+
|
|
259
|
+
```python
|
|
260
|
+
from livespec_mcp.explorer import enable_explorer
|
|
261
|
+
enable_explorer(app) # no main.py patch
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
The API tab **Try it** sends `fetch` to your base URL (CORS must allow the Explorer origin).
|
|
265
|
+
|
|
266
|
+
### One-shot install (rule + skill + index + autowire)
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
livespec-mcp fastapi init /path/to/your/fastapi-repo
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Writes:
|
|
273
|
+
|
|
274
|
+
| Path | Purpose |
|
|
275
|
+
|------|---------|
|
|
276
|
+
| `.livespec.toml` | `[explorer]` defaults |
|
|
277
|
+
| `.mcp-docs/explorer/` | Spec Explorer bundle |
|
|
278
|
+
| `.cursor/rules/livespec-fastapi.mdc` | Agent rule (FastAPI globs) |
|
|
279
|
+
| `.cursor/skills/livespec-fastapi/SKILL.md` | Session workflow skill |
|
|
280
|
+
| `.livespec/SESSION_PROMPT.md` | Copy-paste chat opener |
|
|
281
|
+
|
|
282
|
+
Appends `mount_explorer(app, prefix="/explorer")` to `main.py`/`app.py` when found.
|
|
283
|
+
Flags: `--no-index`, `--no-wire`, `--no-cursor`.
|
|
284
|
+
|
|
285
|
+
## Workflows — livespec + OpenSpec
|
|
286
|
+
|
|
287
|
+
[OpenSpec](https://github.com/Fission-AI/OpenSpec) is a spec-driven-development
|
|
288
|
+
**framework**: you author the intent (requirements + scenarios) in an
|
|
289
|
+
`openspec/` tree before writing code. **livespec is the code-graph and
|
|
290
|
+
traceability layer beneath it** — it does not replace OpenSpec authoring; it
|
|
291
|
+
links those specs to the code that implements them and keeps the link live.
|
|
292
|
+
Since v0.22 livespec reads *and* writes the OpenSpec format (round-trip).
|
|
293
|
+
|
|
294
|
+
**The pieces you invoke** (Claude Code plugin):
|
|
295
|
+
|
|
296
|
+
| Invocation | What it is | When |
|
|
297
|
+
|---|---|---|
|
|
298
|
+
| `/livespec-onboard <path>` | slash command → delegates to the subagent (cold open) | start on any repo |
|
|
299
|
+
| **`livespec`** subagent | specialized agent, preloads the Skill | heavy exploration / spec work |
|
|
300
|
+
| `/openspec_workflow` | MCP prompt: the sync → trace → validate → export loop | a repo with `openspec/` |
|
|
301
|
+
| `/onboard_project`, `/extract_specs_from_module`, `/audit_spec_coverage` | MCP prompts | brownfield |
|
|
302
|
+
|
|
303
|
+
> **The one rule:** every tool takes `workspace="/abs/repo/root"` (no cwd/env fallback).
|
|
304
|
+
|
|
305
|
+
### Case 1 — New project from scratch (spec-first)
|
|
306
|
+
|
|
307
|
+
Intent leads (OpenSpec); livespec ties code↔spec as you build.
|
|
308
|
+
|
|
309
|
+
1. **Author capabilities as specs** — `openspec/specs/<capability>/spec.md` with
|
|
310
|
+
`## Purpose` / `### Requirement:` / `#### Scenario:` (WHEN/THEN). Ask the
|
|
311
|
+
`livespec` subagent to draft them.
|
|
312
|
+
2. **Ingest + gate the design before coding** —
|
|
313
|
+
`sync_openspec()` → `validate_openspec(strict=True)` (mirrors
|
|
314
|
+
`openspec validate --strict`: every requirement needs ≥1 scenario).
|
|
315
|
+
3. **Implement feature by feature**, annotating docstrings with `@spec:SPEC-NNN`;
|
|
316
|
+
run `index_project()` after each batch (auto-scans annotations).
|
|
317
|
+
4. **Trace tests to individual scenarios** —
|
|
318
|
+
`link_scenario_symbol(spec_id, scenario_name, symbol_qname, relation="tests")`.
|
|
319
|
+
5. **Track coverage live** — `audit_coverage()` and `get_spec_implementation(spec_id)`
|
|
320
|
+
(per-scenario `verified` flag).
|
|
321
|
+
6. **Behaviour changes = an OpenSpec change** — author `openspec/changes/<name>/`,
|
|
322
|
+
then `sync_openspec()` → `apply_spec_change(name, dry_run=True)` → `apply_spec_change(name)`
|
|
323
|
+
→ `archive_spec_change(name)`.
|
|
324
|
+
7. **Before a PR** — `git_diff_impact(base, head)`; cite spec ids.
|
|
325
|
+
|
|
326
|
+
*Shortcut:* `/openspec_workflow` loads this whole loop.
|
|
327
|
+
|
|
328
|
+
### Case 2 — Undocumented project (brownfield)
|
|
329
|
+
|
|
330
|
+
livespec reverse-engineers the structure and proposes specs; you curate and link.
|
|
331
|
+
|
|
332
|
+
1. **Onboard** — `/livespec-onboard /abs/path` (subagent runs `index_project`
|
|
333
|
+
→ `get_project_overview` → `list_specs`).
|
|
334
|
+
2. **Understand the shape** — `quick_orient`, `who_calls`, `find_endpoints`,
|
|
335
|
+
`find_dead_code`.
|
|
336
|
+
3. **Propose specs from the code** (the brownfield killer feature) —
|
|
337
|
+
`propose_specs_from_codebase()` + `scan_docstrings_for_spec_hints`; per-module
|
|
338
|
+
`/extract_specs_from_module <module>`.
|
|
339
|
+
4. **Curate + persist** (human in the loop) — `create_spec(...)`, or write
|
|
340
|
+
`docs/specs.md` and `import_specs_from_markdown`.
|
|
341
|
+
5. **Link code↔spec** — `bulk_link_spec_symbols(mappings=[...])`, or add
|
|
342
|
+
`@spec:SPEC-NNN` annotations and re-`index_project`. Tests via `relation="tests"`.
|
|
343
|
+
6. **Audit + iterate** — `/audit_spec_coverage` (orphan specs, uncovered modules).
|
|
344
|
+
7. **Graduate to OpenSpec (optional)** — `export_openspec()` writes a real
|
|
345
|
+
`openspec/` tree, then `validate_openspec(strict=True)`. A repo that had zero
|
|
346
|
+
docs now has a maintainable spec-driven structure.
|
|
347
|
+
|
|
348
|
+
**In one line each:** *new* → OpenSpec defines the contract, `validate_openspec`
|
|
349
|
+
gates it, you build with live links; *brownfield* → `/livespec-onboard` orients,
|
|
350
|
+
`propose_specs_from_codebase` reconstructs the intent, `export_openspec` graduates
|
|
351
|
+
it to spec-driven.
|
|
352
|
+
|
|
353
|
+
## Tools (44 total: 29 core + 12 Spec plugin + 3 docs plugin)
|
|
354
|
+
|
|
355
|
+
Every tool requires `workspace` (absolute project root). Pass it on each call;
|
|
356
|
+
omitting it is an error (no env fallback). LRU cache (8 workspaces) — one MCP
|
|
357
|
+
server, many projects, no restart.
|
|
358
|
+
|
|
359
|
+
**Menu (v0.19):** plugins register at boot but `PluginVisibilityMiddleware`
|
|
360
|
+
hides Spec mutation / doc tools from `tools/list` until the workspace has
|
|
361
|
+
`spec` rows, a `.mcp-docs/explorer/` bundle (docs plugin), or you set
|
|
362
|
+
`LIVESPEC_PLUGINS`. **`import_specs_from_markdown`** and
|
|
363
|
+
**`export_explorer`** are always visible (brownfield bootstrap — no
|
|
364
|
+
chicken-and-egg). After the first `workspace=` call on a repo with Specs +
|
|
365
|
+
explorer, the menu grows to **44** tools. Reconnect the MCP host if your
|
|
366
|
+
client cached an old tool list.
|
|
367
|
+
|
|
368
|
+
### Default surface — code intel + Spec agentic (29)
|
|
369
|
+
|
|
370
|
+
These tools answer the questions an agent ASKS on an unfamiliar codebase.
|
|
371
|
+
Always registered (including markdown Spec import + Explorer export).
|
|
372
|
+
|
|
373
|
+
#### Indexing (1)
|
|
374
|
+
- `index_project(force=False, watch=False, embed=False, explorer=False)` — walk, parse,
|
|
375
|
+
persist. Also rebuilds search chunks idempotently. Respects
|
|
376
|
+
`.gitignore` (root + nested, negations included) on top of the
|
|
377
|
+
built-in ignore list (v0.14). Pass `embed=True` to populate vector
|
|
378
|
+
embeddings (requires `[embeddings]` extra). Auto-builds the Spec Explorer
|
|
379
|
+
bundle when `.mcp-docs/explorer/` already exists, `explorer=True`, or a
|
|
380
|
+
FastAPI entry is detected (see **FastAPI integration** above). Read the
|
|
381
|
+
`project://index/status` resource for current status (the legacy
|
|
382
|
+
`get_index_status` tool was dropped in v0.9).
|
|
383
|
+
|
|
384
|
+
Optional per-repo tuning via `.livespec.toml` at the workspace root
|
|
385
|
+
(v0.14) — config patterns outrank `.gitignore`:
|
|
386
|
+
|
|
387
|
+
```toml
|
|
388
|
+
[index]
|
|
389
|
+
ignore = ["assets/", "*.min.js"] # gitignore syntax, "!" re-includes
|
|
390
|
+
languages = ["python", "typescript"] # allow-list; absent = all 9
|
|
391
|
+
max_file_bytes = 2000000 # skip files larger than this
|
|
392
|
+
|
|
393
|
+
[specs]
|
|
394
|
+
sync_from = ["docs/REQUISITOS_FUNCIONALES.md"] # re-import after every index_project
|
|
395
|
+
links_seed = "docs/requirements/livespec-spec-links.json" # optional bulk_link seed
|
|
396
|
+
|
|
397
|
+
[workspace]
|
|
398
|
+
group_db = "../.livespec-group/docs.db" # cross-project: several repo roots
|
|
399
|
+
# share one DB so a Spec can link
|
|
400
|
+
# symbols across repos (each keeps
|
|
401
|
+
# its own project_id). Absent = the
|
|
402
|
+
# per-repo .mcp-docs/docs.db.
|
|
403
|
+
|
|
404
|
+
[explorer]
|
|
405
|
+
mount_path = "/explorer" # FastAPI mount prefix for autowire
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
#### Search (1, v0.12)
|
|
409
|
+
- `search(query, scope='all'|'code'|'specs', limit=20)` — hybrid
|
|
410
|
+
retrieval over AST-aware chunks of symbols + Specs. FTS5 keyword lane
|
|
411
|
+
always live (splits `snake_case` into OR tokens); vector lane fuses via
|
|
412
|
+
Reciprocal Rank Fusion (k=60) when `[embeddings]` extra is installed and
|
|
413
|
+
chunks are embedded — falls back to FTS-only if the embedder is offline.
|
|
414
|
+
Use when you want "code that talks about X" without an exact
|
|
415
|
+
symbol-name match. Companion tool `embed_chunks()` (also default
|
|
416
|
+
surface) populates vec0 tables on demand; ~200MB model download on
|
|
417
|
+
first run, cached afterwards in `~/.cache/livespec-mcp/fastembed`
|
|
418
|
+
(shared across workspaces; override with `FASTEMBED_CACHE_PATH`).
|
|
419
|
+
The download shows no progress under MCP — run
|
|
420
|
+
`livespec-mcp index <repo> --embed` in a terminal to watch it.
|
|
421
|
+
|
|
422
|
+
#### Code intelligence (14)
|
|
423
|
+
- `find_symbol(query, kind, limit)` — separator-agnostic name lookup.
|
|
424
|
+
- `get_symbol_source(qname)` — body slice only (lighter than full info).
|
|
425
|
+
- `who_calls(qname, max_depth=1)` — backward cone, slim agentic alias. Adds
|
|
426
|
+
`route_callers` when the symbol is an HTTP endpoint that a frontend calls
|
|
427
|
+
over a route (cross-repo via `group_db`) — *"I changed this endpoint, what
|
|
428
|
+
frontend breaks?"*.
|
|
429
|
+
- `who_does_this_call(qname, max_depth=1)` — forward-direction counterpart.
|
|
430
|
+
Adds `invokes_endpoints`: backend routes this symbol hits via
|
|
431
|
+
`fetch`/`axios`/`requests`.
|
|
432
|
+
- `quick_orient(qname)` — composite snapshot: metadata + docstring lead +
|
|
433
|
+
top-5 callers/callees by PageRank + linked Specs + entry-point flag.
|
|
434
|
+
Replaces 3-4 calls with one when an agent first lands on a symbol.
|
|
435
|
+
- `analyze_impact(target_type, target, max_depth)` — symbol/file/Spec blast
|
|
436
|
+
radius. `max_depth=1` covers the old "find references" use case.
|
|
437
|
+
- `get_project_overview(include_infrastructure=False)` — top symbols by
|
|
438
|
+
PageRank; infra noise filtered by default.
|
|
439
|
+
- `git_diff_impact(base_ref='HEAD~1', head_ref='HEAD', max_depth=5)` —
|
|
440
|
+
changed files → impacted callers → affected Specs → suggested test files.
|
|
441
|
+
PR-review entry point.
|
|
442
|
+
- `find_dead_code(include_infrastructure=False)` — symbols with zero
|
|
443
|
+
callers and zero Spec links. Skips entry-point paths, framework
|
|
444
|
+
decorators, `__main__` guards, list-stored callbacks.
|
|
445
|
+
- `find_orphan_tests(max_depth=10)` — test functions whose forward cone
|
|
446
|
+
never reaches a non-test symbol.
|
|
447
|
+
- `find_endpoints(framework=None)` — framework entry points. Decorator
|
|
448
|
+
markers (`framework` ∈ {flask, fastapi, click, pytest, fastmcp, celery,
|
|
449
|
+
django, spring, angular}), filesystem routing ({nextjs, fresh, sveltekit,
|
|
450
|
+
remix}), Django CBV bases, and call-style routing (`hono`:
|
|
451
|
+
`app.get('/users', handler)` with method + path per route).
|
|
452
|
+
**(v0.19)** FastAPI/Flask decorators also yield `http_method` +
|
|
453
|
+
`http_path` in Explorer and MCP payloads. Default sweep **excludes**
|
|
454
|
+
`tests/**` and `@pytest.fixture` handlers (use `framework='pytest'` for
|
|
455
|
+
fixtures).
|
|
456
|
+
- `grep_in_indexed_files(pattern, path_glob?, kind?, limit=50)` — search
|
|
457
|
+
only files present in the index (avoids `node_modules` / `.venv`).
|
|
458
|
+
- `agent_scratch(qname, note)` / `agent_scratch_clear(qname?)` — ephemeral
|
|
459
|
+
agent notes per project (SQLite, not Specs). Omit `qname` on clear to wipe all.
|
|
460
|
+
- `audit_coverage()` — Spec coverage report: modules without direct Spec,
|
|
461
|
+
modules implicitly covered (transitively reached), modules truly orphan,
|
|
462
|
+
modules in languages whose annotation extractor isn't wired yet
|
|
463
|
+
(`modules_unsupported_language`), Specs without implementation, Specs with
|
|
464
|
+
low avg confidence. **(v0.16)** Also reports auto-derived per-Spec **test
|
|
465
|
+
coverage** (`spec_coverage`, `avg_test_coverage`, `specs_with_any_test_coverage`):
|
|
466
|
+
an implementing symbol counts as tested when a test's forward call-cone
|
|
467
|
+
reaches it (depth 3) or an explicit `relation='tests'` link exists — so
|
|
468
|
+
Spec test-coverage works with no hand-linking for projects whose tests
|
|
469
|
+
call the code directly. The Spec Explorer renders this as a per-Spec test
|
|
470
|
+
coverage meter with a `coverage_source` badge.
|
|
471
|
+
|
|
472
|
+
#### Spec agentic — query + bootstrap (5)
|
|
473
|
+
- `bulk_link_spec_symbols(mappings)` — batch-link N (spec_id, symbol_qname)
|
|
474
|
+
pairs in one transaction. Escape hatch for files/languages where the
|
|
475
|
+
in-source annotation extractor doesn't reach (configs, SQL, YAML).
|
|
476
|
+
Idempotent: re-linking an existing pair is a no-op. Test symbols must be
|
|
477
|
+
**functions** (`tests.pkg.test_mod.test_fn`), not modules (`tests.pkg.test_mod`).
|
|
478
|
+
- `import_specs_from_markdown(path, fmt="auto")` — bulk-create/update Specs
|
|
479
|
+
from Markdown. Auto-detects two dialects: the native `## SPEC-NNN: Title`
|
|
480
|
+
format and **OpenSpec** (Fission-AI) `### Requirement:` / `#### Scenario:`
|
|
481
|
+
specs — point `path` at a single file or at an `openspec/` directory to walk
|
|
482
|
+
its whole tree. Positions livespec as the traceability/graph layer beneath
|
|
483
|
+
spec-driven-development authoring tools. Always visible; warns on duplicate
|
|
484
|
+
Spec headings; idempotent.
|
|
485
|
+
- `list_specs(status, module, priority, kind, has_implementation)` —
|
|
486
|
+
Spec discovery surface.
|
|
487
|
+
- `get_spec_implementation(spec_id)` — answers
|
|
488
|
+
*"¿qué código implementa el SPEC-042?"*.
|
|
489
|
+
- `propose_specs_from_codebase(module_depth=2, min_symbols_per_group=3,
|
|
490
|
+
max_proposals=30, skip_already_covered=True)` — heuristic Spec discovery
|
|
491
|
+
on a Spec-empty repo. Groups symbols by module + PageRank, proposes
|
|
492
|
+
Spec candidates with humanized title + suggested_symbols.
|
|
493
|
+
|
|
494
|
+
#### OpenSpec interop — round-trip + change lifecycle (5, v0.22)
|
|
495
|
+
|
|
496
|
+
Make livespec a first-class citizen of an [OpenSpec](https://github.com/Fission-AI/OpenSpec)
|
|
497
|
+
`openspec/` repo. Scenarios (`#### Scenario:` WHEN/THEN) are first-class rows
|
|
498
|
+
since v0.22 — `get_spec_implementation` returns them (with per-scenario linked
|
|
499
|
+
`symbols` + a `verified` flag) and `list_specs` counts them. Link code/tests to
|
|
500
|
+
an individual scenario with `link_scenario_symbol` (Spec plugin).
|
|
501
|
+
|
|
502
|
+
- `sync_openspec(openspec_dir?)` — import an entire OpenSpec tree in one call:
|
|
503
|
+
canonical requirements from `specs/` **and** every change under `changes/`
|
|
504
|
+
(proposed) / `archive/` (archived); reads `openspec.json`. Always visible
|
|
505
|
+
(bootstrap). For a single file use `import_specs_from_markdown`.
|
|
506
|
+
- `export_openspec(out_dir="openspec", include_changes=True)` — the inverse:
|
|
507
|
+
write the DB back to `specs/<capability>/spec.md` (+ `changes/`, `archive/`).
|
|
508
|
+
Closes the round-trip. Capability == the spec's `module`.
|
|
509
|
+
- `validate_openspec(strict=False)` — mirror `openspec validate [--strict]`;
|
|
510
|
+
the load-bearing check is *every requirement MUST have ≥1 scenario*.
|
|
511
|
+
- `list_spec_changes(status?)` / `get_spec_change(name)` — inspect change
|
|
512
|
+
proposals (proposal/design/tasks prose + ADD/MODIFY/REMOVE/RENAME deltas).
|
|
513
|
+
|
|
514
|
+
#### Spec Explorer (1, always visible)
|
|
515
|
+
- `export_explorer(base?, head?, generated_at?)` — writes
|
|
516
|
+
`.mcp-docs/explorer/` (`data.json` + `index.html`). Swagger-style view by
|
|
517
|
+
Spec; **v0.19** HTTP Try-it for routes with method/path; FastAPI
|
|
518
|
+
autowire on export/index. Preview: `livespec-mcp explorer serve` →
|
|
519
|
+
`http://127.0.0.1:8765/explorer/`.
|
|
520
|
+
|
|
521
|
+
### `livespec-spec` plugin — Spec mutation (12)
|
|
522
|
+
|
|
523
|
+
Visible in `tools/list` when the workspace DB has `spec` rows, or when
|
|
524
|
+
`LIVESPEC_PLUGINS` includes `spec`. Tools an *operator* runs to mutate Spec state.
|
|
525
|
+
|
|
526
|
+
`bulk_link_spec_symbols` and `import_specs_from_markdown` live in the
|
|
527
|
+
**default surface** (always visible) so brownfield repos can bootstrap without
|
|
528
|
+
setting `LIVESPEC_PLUGINS`.
|
|
529
|
+
|
|
530
|
+
- `create_spec(title, ...)`, `update_spec(spec_id, ...)`,
|
|
531
|
+
`delete_spec(spec_id)` — cascade-removes spec_symbol links.
|
|
532
|
+
- `link_spec_symbol(spec_id, symbol_qname, relation, confidence, source, unlink)` —
|
|
533
|
+
link / unlink a single Spec↔symbol pair.
|
|
534
|
+
- `link_scenario_symbol(spec_id, scenario_name, symbol_qname, ...)` —
|
|
535
|
+
scenario-level traceability: link code/tests to an individual OpenSpec
|
|
536
|
+
`#### Scenario:` (finer than the whole requirement). Surfaced per-scenario in
|
|
537
|
+
`get_spec_implementation` (`verified` + linked `symbols`).
|
|
538
|
+
- `link_spec_dependency(parent_spec_id, child_spec_id, kind='requires')` /
|
|
539
|
+
`unlink_spec_dependency` / `get_spec_dependency_graph` — Spec→Spec graph.
|
|
540
|
+
`kind` ∈ {requires, extends, conflicts}; cycles rejected at insert time.
|
|
541
|
+
- `scan_spec_annotations()` — two-level matcher (`@spec:SPEC-NNN` vs.
|
|
542
|
+
verb-anchored); auto-runs after every `index_project`.
|
|
543
|
+
- `scan_docstrings_for_spec_hints()` — surfaces Spec candidates from existing
|
|
544
|
+
docstrings (first sentence, leading verb). Returns
|
|
545
|
+
`verb_histogram_top` for noticing dominant action verbs.
|
|
546
|
+
- `apply_spec_change(name, dry_run=False)` / `archive_spec_change(name)` —
|
|
547
|
+
OpenSpec change lifecycle: fold a change's deltas into the canonical Spec set
|
|
548
|
+
(ADDED/MODIFIED upsert+activate, REMOVED deprecate, **RENAMED** moves the old
|
|
549
|
+
requirement's traceability links onto the new name and drops the old spec),
|
|
550
|
+
then mark it archived. `dry_run=True` returns the plan + applicability
|
|
551
|
+
`warnings` (missing target, would-overwrite) without mutating.
|
|
552
|
+
|
|
553
|
+
### Brownfield bootstrap (no Python one-liner)
|
|
554
|
+
|
|
555
|
+
```text
|
|
556
|
+
index_project(workspace=..., explorer=True)
|
|
557
|
+
→ import_specs_from_markdown(path="docs/REQUISITOS_FUNCIONALES.md")
|
|
558
|
+
→ bulk_link_spec_symbols(mappings=[...])
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
Or add `[specs].sync_from` to `.livespec.toml` and run
|
|
562
|
+
`uv run python scripts/sync_livespec_specs.py /path/to/repo` after editing the
|
|
563
|
+
spec without a full re-index.
|
|
564
|
+
|
|
565
|
+
### `livespec-docs` plugin — doc generation (3)
|
|
566
|
+
|
|
567
|
+
Visible when the workspace has `doc` rows, a `.mcp-docs/explorer/` bundle, or
|
|
568
|
+
`LIVESPEC_PLUGINS` includes `docs`. Human-tier ceremony for managing generated docs.
|
|
569
|
+
|
|
570
|
+
- `generate_docs(target_type, identifier, content?, max_tokens?)` —
|
|
571
|
+
three modes: caller_supplied / sampling / needs_caller_content. Works
|
|
572
|
+
in Claude Code (caller mode) and Cursor/Desktop (sampling mode).
|
|
573
|
+
- `list_docs(target_type, only_stale=False)` — list or surface drifted
|
|
574
|
+
docs (drift triggers on body_hash OR signature_hash mismatch).
|
|
575
|
+
- `export_documentation(format, out_subdir)` — markdown or JSON.
|
|
576
|
+
|
|
577
|
+
### Migrating from older versions
|
|
578
|
+
|
|
579
|
+
| Removed | Use instead |
|
|
580
|
+
|---|---|
|
|
581
|
+
| `find_references` (v0.1) | `analyze_impact(target_type='symbol', target=qname, max_depth=1)` |
|
|
582
|
+
| `get_symbol_info` (v0.7) | `quick_orient` (composite) + `get_symbol_source` (body) |
|
|
583
|
+
| `get_call_graph` (v0.7) | `who_calls` + `who_does_this_call` |
|
|
584
|
+
| `search`, `rebuild_chunks` (v0.7, dropped v0.8) | `search` is **back in v0.12** wired to a real chunk pipeline + vector lane via Reciprocal Rank Fusion. `rebuild_chunks` is now auto-run inside `index_project` (no separate tool). `find_symbol` + `quick_orient` still cover exact-name lookup. |
|
|
585
|
+
| `list_files` (v0.7) | grep / ripgrep host with path glob |
|
|
586
|
+
| `start_watcher` / `stop_watcher` / `watcher_status` (v0.7) | re-run `index_project` on demand (watcher race-condition trap for editing agents) |
|
|
587
|
+
| `link_requirement_to_code` (v0.6 alias) | `link_spec_symbol` |
|
|
588
|
+
| `link_requirements` / `unlink_requirements` (v0.6 alias) | `link_spec_dependency` / `unlink_spec_dependency` |
|
|
589
|
+
| `get_requirement_dependencies` (v0.6 alias) | `get_spec_dependency_graph` |
|
|
590
|
+
| `get_index_status` (v0.9, deprecated in v0.8) | read the `project://index/status` resource |
|
|
591
|
+
| `list_requirements` / `get_requirement_implementation` / `create_requirement` / etc. (RF nomenclature, removed v0.20 — hard cut) | `list_specs` / `get_spec_implementation` / `create_spec` / etc. |
|
|
592
|
+
|
|
593
|
+
## Resources
|
|
594
|
+
|
|
595
|
+
- `project://overview`
|
|
596
|
+
- `project://index/status`
|
|
597
|
+
- `project://specs`
|
|
598
|
+
- `project://specs/{spec_id}`
|
|
599
|
+
- `project://files/{path*}`
|
|
600
|
+
- `project://symbols/{qname*}`
|
|
601
|
+
- `doc://symbol/{qname*}`
|
|
602
|
+
- `doc://spec/{spec_id}`
|
|
603
|
+
- `code://symbol/{qname*}` — raw symbol source slice
|
|
604
|
+
|
|
605
|
+
## Prompts (slash commands)
|
|
606
|
+
|
|
607
|
+
- **`agent_playbook`** — primary agent guide: tool tiers, call patterns, `@spec:` commenting, brownfield Spec workflow, anti-patterns
|
|
608
|
+
- `onboard_project`
|
|
609
|
+
- `analyze_change_impact`
|
|
610
|
+
- `audit_spec_coverage`
|
|
611
|
+
- `extract_specs_from_module`
|
|
612
|
+
- `document_undocumented_symbols`
|
|
613
|
+
- `refresh_stale_docs`
|
|
614
|
+
- `explain_symbol`
|
|
615
|
+
|
|
616
|
+
## Performance
|
|
617
|
+
|
|
618
|
+
Numbers from the v0.8/v0.9 battle-test harness (4 sessions / 4 profiles
|
|
619
|
+
/ 65+ logged calls in [`docs/AGENT_USAGE_DATA.md`](docs/AGENT_USAGE_DATA.md)).
|
|
620
|
+
Cold = first run; warm = cached run on the same workspace. Latency p95
|
|
621
|
+
measured with the in-process middleware
|
|
622
|
+
(`src/livespec_mcp/instrumentation.py`).
|
|
623
|
+
|
|
624
|
+
| Repo | Files / Symbols | `index_project` cold | `quick_orient` p95 | `get_project_overview` p95 |
|
|
625
|
+
|---|---:|---:|---:|---:|
|
|
626
|
+
| url-shortener-demo (Python) | 4 / 23 | ~50 ms | <5 ms | ~10 ms |
|
|
627
|
+
| livespec itself (Python+8 langs) | 84 / 495 | ~400 ms | ~60 ms | ~75 ms |
|
|
628
|
+
| jig (Python) | 130 / 1173 | ~600 ms | ~50 ms | ~80 ms |
|
|
629
|
+
| Django (Python, stress) | 2898 / 39789 | ~25 s | <100 ms | ~250 ms |
|
|
630
|
+
| warp subset (Rust, stress) | 5K / 50K | ~30 s | <100 ms | ~300 ms |
|
|
631
|
+
|
|
632
|
+
For repos > 30K symbols, pass `summary_only=True` on aggregator and
|
|
633
|
+
traversal tools (`audit_coverage`, `find_dead_code`, `find_orphan_tests`,
|
|
634
|
+
`find_endpoints`, `git_diff_impact`, `who_calls`, `who_does_this_call`,
|
|
635
|
+
`analyze_impact`) to keep payloads under ~200 KB. Counts stay exact
|
|
636
|
+
regardless of pagination — see `bench/run.py --large` for the Django
|
|
637
|
+
stress profile.
|
|
638
|
+
|
|
639
|
+
### Django precision series (same queries, across releases)
|
|
640
|
+
|
|
641
|
+
| Tool | v0.8 | v0.9 | v0.11 | v0.14 | Why |
|
|
642
|
+
|---|---:|---:|---:|---:|---|
|
|
643
|
+
| `find_dead_code` | 824 | 514 | 348 | **344** | non-Python skip → dotted-path string refs → runtime registration → closure-capture |
|
|
644
|
+
| `find_endpoints(django)` | 20 | **162** | 162 | 162 | class-based view detection via inheritance from `View` / mixins |
|
|
645
|
+
| Partial reindex (Django, 1 file) | — | ~7 s | — | **1.4 s** | targeted `_resolve_refs` walk |
|
|
646
|
+
| `index_project` cold (Django) | — | ~148 s | — | **54 s** | (v0.14 re-run on Ryzen 7 4800H; edges 1.05M → 465K from scoped-resolution precision, DB 124 → 71 MB, RSS post-PageRank 609 → 294 MB — those three are machine-independent) |
|
|
647
|
+
|
|
648
|
+
## Tests
|
|
649
|
+
|
|
650
|
+
```bash
|
|
651
|
+
uv run pytest -q
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
In-memory FastMCP `Client(mcp)` so tests run without subprocess or network.
|
|
655
|
+
|
|
656
|
+
## Agent vs human user
|
|
657
|
+
|
|
658
|
+
livespec ships two user shapes deliberately:
|
|
659
|
+
|
|
660
|
+
- **Agents** see the 24-tool default surface and the agentic-read Spec tools
|
|
661
|
+
(`list_specs`, `get_spec_implementation`,
|
|
662
|
+
`propose_specs_from_codebase`, `audit_coverage`). The composite
|
|
663
|
+
`quick_orient` is the canonical first-contact tool — it returns
|
|
664
|
+
metadata, docstring lead, top callers/callees by PageRank, linked Specs,
|
|
665
|
+
and entry-point flags in one call.
|
|
666
|
+
- **Humans** (or operator scripts) reach for the plugin tools to mutate
|
|
667
|
+
Spec state and manage docs. Auto-load happens once the DB shows real Spec
|
|
668
|
+
or doc rows; before that, set `LIVESPEC_PLUGINS=all` (or `=spec` /
|
|
669
|
+
`=docs`) to bootstrap.
|
|
670
|
+
|
|
671
|
+
This is why dropping `search`/`get_symbol_info` was safe: the battle-test
|
|
672
|
+
harness logged zero agent calls to those tools across 3 codebases. The
|
|
673
|
+
data trumped the prior intuition.
|
|
674
|
+
|
|
675
|
+
## Roadmap
|
|
676
|
+
|
|
677
|
+
| Fase | Estado | Contenido |
|
|
678
|
+
|------|--------|-----------|
|
|
679
|
+
| 0 — Bootstrap | ✅ | FastMCP server, project structure |
|
|
680
|
+
| 1 — Indexing | ✅ | tree-sitter + Python AST, file-incremental, call graph |
|
|
681
|
+
| 2 — Analysis | ✅ | NetworkX, impact, PageRank |
|
|
682
|
+
| 3 — Requirements | ✅ | CRUD + linking + annotation matcher |
|
|
683
|
+
| 4 — RAG/Embeddings | ✅ | AST chunking, FTS5, fastembed + sqlite-vec optional, RRF |
|
|
684
|
+
| 5 — Doc generation | ✅ | `generate_docs` (dual-mode), drift detect (body+signature), export |
|
|
685
|
+
| 6 — Polish | ✅ | 7 prompts, doc:// resources, two-level @rf: matcher with negation guard |
|
|
686
|
+
| 7 — v0.2 | ✅ | Multi-tenant state, tool consolidation 25→23, persistent refs, watcher, bench suite |
|
|
687
|
+
| 8 — v0.3 | ✅ | Auto-scan post-index, PageRank infra filter, scoped resolution by imports, `git_diff_impact`, embeddings smoke real, Ruby+PHP fixtures, hypothesis property tests, memory bench, GitHub Actions CI, `code://` resource, `delete_requirement`, markdown RF importer |
|
|
688
|
+
| 9 — v0.4 | ✅ | Scoped resolution for TS/JS/Go/Ruby/PHP, `find_dead_code` / `audit_coverage` / `find_orphan_tests`, `did_you_mean` on Symbol-not-found errors, watcher `atexit` cleanup, CI venv fix |
|
|
689
|
+
| 10 — v0.5 | ✅ | Bug fixes from real-repo demo, decorators as first-class field + `find_endpoints`, RF dependency graph (requires/extends/conflicts) with `analyze_impact` cascade, matcher multi-RF + confidence override + `@not_rf:` negation + golden dataset, Rust `use` scoped resolution |
|
|
690
|
+
| 11 — v0.6 | ✅ | Hardening: explicit migration framework, unified error shape, RF link tools renamed, deprecated `use_workspace` removed, Django stress test (40K symbols), graph cache, README pitch reframe |
|
|
691
|
+
| 12 — v0.7 | ✅ | Brownfield onboarding: `propose_requirements_from_codebase`, `bulk_link_rf_symbols`, `scan_docstrings_for_rf_hints`. Pagination on aggregator tools. Rust `pub` visibility-aware dead-code filter. `find_symbol` separator-agnostic |
|
|
692
|
+
| 13 — v0.8 | ✅ | Curation pass driven by 3-session battle-test data: 4 quick-win agentic tools (`quick_orient`, `who_calls`, `who_does_this_call`, `get_symbol_source`). 11 P2 bug fixes on `find_dead_code`, `audit_coverage`, `git_diff_impact`, `propose_requirements_from_codebase`. Plugin auto-detect framework — RF mutation (11 tools) and doc management (3 tools) move into auto-loading plugins. Tier-4 drops: `list_files`, `search`, `rebuild_chunks`, `get_call_graph`, `get_symbol_info`, watcher trio. Default surface 39 → 17 tools |
|
|
693
|
+
| 14 — v0.9 | ✅ | Django readiness: targeted `_resolve_refs` walk on partial reindex (closes v0.7 deferred). Pagination on `who_calls` / `who_does_this_call` / `analyze_impact`. `min_weight=0.6` filter mutes resolver fan-out. Django dead-code accuracy (skip non-Python, recognize dotted-path strings + `class Meta:`). Django CBV detection in `find_endpoints` (LoginView/FormView/LoginRequiredMixin/etc.). Drop `get_index_status`. Default surface 17 → 16. Wire-validated: Django `find_dead_code` 824 → 514, `find_endpoints(django)` 20 → 162 |
|
|
694
|
+
| 15 — v0.10 | ✅ | Library codebase release: `from .x import Y` re-exports + `__all__` lists protect names from `find_dead_code` (closes the largest remaining false-positive bucket on Django). README lift — Django numbers above the fold + 30-second tour. Battle-test session 05 against Deno Fresh (TS/TSX/JS) — 5/5 profiles covered. Wire-validated: Django `find_dead_code` 514 → 348 (−58% cumulative from v0.8 baseline of 824) |
|
|
695
|
+
| 16 — v0.11 | ✅ | TS framework readiness: bundler/build dir filter (`_fresh/`, `dist/`, `build/`, `.next/`, `out/`, `node_modules/`, `.svelte-kit/`, `target/`, …), TS framework entry-point detection (Fresh `islands/`, Next `pages/` + `app/`, SvelteKit `routes/`, Remix `app/routes/`), JSX element refs as call-graph edges, runtime-registration name protection (`Field.register_lookup` / `signal.connect` / `add_middleware`). Closes session-05 bugs #18-#20. Wire-validated: Fresh `find_dead_code` 974 → **0** (default), 974 → 118 (`include_non_python=True`); `find_endpoints(fresh)` returns **340** entry points; `top_symbols` from `_fresh/` 18/20 → **0/20**. Three sonnet subagents in parallel worktrees |
|
|
696
|
+
| 17 — v0.12 | ✅ | Multi-repo workspace: `workspace` required on every call, one server instance serves N repos (LRU per-workspace state). RAG layer wired: `index_project` runs AST-aware chunking, `search` (FTS5 + optional sqlite-vec via RRF) + `embed_chunks` exposed. JSDoc extraction for TS/JS (`@rf:` annotations in JSDoc now scanned). `bulk_link_rf_symbols` promoted out of the RF plugin. Banner-comment filter. `force=True` preserves manual RF links |
|
|
697
|
+
| 18 — v0.13 | ✅ | Framework coverage sprint: Spring Boot (Java annotations → endpoints + DI-aware dead-code), Angular (TS decorators, template-reachability method protection, lifecycle hooks), Hono (call-style route extraction with method+path, named-handler callback refs, module-level registration scan — also covers Express-style apps). TS decorator + Java annotation extraction (migration v8 auto re-extract). Dual-decorator alias fix: `find_dead_code` on livespec-mcp itself 22 → **0** |
|
|
698
|
+
| 19 — v0.14 | ✅ | Personal-fit sprint: gitignore-aware indexing (root + nested + negations via `pathspec`), `.livespec.toml` per-repo config (ignore/languages/max_file_bytes, outranks .gitignore), headless CLI (`livespec-mcp index|status` — cron/systemd/pre-commit sin host MCP), fix resources rotos bajo multi-tenant (MRU binding + `mcp_error` shape), `languages_unsupported` reporting, closure-capture TS/JS/Rust, embed cache persistente XDG, Django re-validation: dead-code **344** (serie 824→514→348→344), partial reindex **1.4s** |
|
|
699
|
+
| 20 — v0.15–0.17 | ✅ | RF Explorer static bundle (`export_explorer`), derived RF test coverage + explorer meters, Changes/drill-down/trend/freshness, reproducible self-RFs (`livespec-rf-links.json`) |
|
|
700
|
+
| 21 — v0.18 | ✅ | `PluginVisibilityMiddleware` (per-workspace tool menu, 19→33 after index). `livespec-mcp explorer serve` + FastAPI `mount_explorer` autowire. Explorer landing + Swagger API tab. Search: FTS snake_case + offline vector fallback. **342** default tests |
|
|
701
|
+
| 22 — v0.19 | ✅ | FastAPI HTTP paths + Explorer Try-it; `fastapi init`; brownfield RF bootstrap (`import_requirements_from_markdown` always visible, `[requirements].sync_from`, duplicate-spec warnings); agent tools + PR RF comment CI. **368** default tests |
|
|
702
|
+
| 23 — v0.20 | ✅ | **Breaking (hard cut):** RF → Spec nomenclature + taxonomy. `rf`/`rf_symbol`/`rf_dependency` tables renamed to `spec`/`spec_symbol`/`spec_dependency` with a new `kind` column (`functional_requirement`, `non_functional_requirement`, `adr`, `design`, `constraint`, `epic`, `other`); migration v11 preserves existing `RF-NNN` ids. `@rf:`/`@not_rf` annotations renamed to `@spec:`/`@not_spec`. All RF-prefixed tools renamed (`list_requirements`→`list_specs`, `create_requirement`→`create_spec`, etc.), `livespec-rf` plugin renamed to `livespec-spec`. No aliases — single breaking release. Followed by an 8-dimension audit whose ~50 fixes landed across six batches (packaging, storage/concurrency, domain correctness, tools, performance, docs). **403** default tests |
|
|
703
|
+
| 24 — v0.23 | ✅ | Cross-repo route edges (`route_ref`, mig v14 — `who_calls.route_callers` / `who_does_this_call.invokes_endpoints`), grouped DB (`[workspace] group_db`), Python callback-arg edges. **Full OpenSpec (Fission-AI) compatibility:** scenarios first-class (migs v15/v17 `spec_scenario`/`scenario_symbol`), `export_openspec` round-trip, `validate_openspec`, change lifecycle (mig v16 `spec_change`/`spec_change_delta`; `sync_openspec`/`apply_spec_change`/`archive_spec_change` — RENAMED FROM/TO + `dry_run`/warnings, mig v18), scenario-level traceability (`link_scenario_symbol`), Purpose round-trip, and agent discoverability (`openspec_workflow` prompt). **Battle-tested** against the real Fission-AI/OpenSpec tree (2 layout bugs fixed). Tools 36 → 44. Rebrand: product name `livespec` + `livespec` command alias (dist/package stay `livespec-mcp`). Trusted-Publishing release workflow. (Supersedes the tag-only, unpublished v0.22.0.) |
|