doctracker 0.1.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.
- doctracker/__init__.py +5 -0
- doctracker/__main__.py +4 -0
- doctracker/cli.py +55 -0
- doctracker/config.py +65 -0
- doctracker/extraction/__init__.py +5 -0
- doctracker/extraction/griffe_loader.py +148 -0
- doctracker/server/__init__.py +1 -0
- doctracker/server/app.py +78 -0
- doctracker/server/guide.md +62 -0
- doctracker/server/guide.py +68 -0
- doctracker/server/state.py +32 -0
- doctracker/server/tools.py +76 -0
- doctracker/server/watcher.py +63 -0
- doctracker/server/web.py +120 -0
- doctracker/wiki/__init__.py +1 -0
- doctracker/wiki/pages.py +356 -0
- doctracker/wiki/static/htmx-LICENSE.txt +13 -0
- doctracker/wiki/static/htmx.min.js +1 -0
- doctracker/wiki/static/live.js +178 -0
- doctracker/wiki/static/style.css +424 -0
- doctracker/wiki/templates/base.html +33 -0
- doctracker/wiki/templates/entity.html +97 -0
- doctracker/wiki/templates/fragment.html +3 -0
- doctracker/wiki/templates/fragment_content.html +5 -0
- doctracker/wiki/templates/header.html +20 -0
- doctracker/wiki/templates/loading.html +2 -0
- doctracker/wiki/templates/members.html +18 -0
- doctracker/wiki/templates/module.html +19 -0
- doctracker/wiki/templates/not_found.html +6 -0
- doctracker/wiki/templates/search.html +21 -0
- doctracker/wiki/templates/source.html +1 -0
- doctracker/wiki/templates/tree.html +31 -0
- doctracker-0.1.0.dist-info/METADATA +234 -0
- doctracker-0.1.0.dist-info/RECORD +38 -0
- doctracker-0.1.0.dist-info/WHEEL +4 -0
- doctracker-0.1.0.dist-info/entry_points.txt +3 -0
- doctracker-0.1.0.dist-info/licenses/LICENSE +21 -0
- doctracker-0.1.0.dist-info/licenses/src/doctracker/wiki/static/htmx-LICENSE.txt +13 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{% include "header.html" %}
|
|
2
|
+
{% set role = page_role(state, entity) | replace(' ', '-') %}
|
|
3
|
+
|
|
4
|
+
<details class="block divider block-description" data-section="description" open>
|
|
5
|
+
<summary class="badge badge-description">Description</summary>
|
|
6
|
+
{% set texts = other_sections(entity) | selectattr("kind", "equalto", "text") | list %}
|
|
7
|
+
{% if texts %}
|
|
8
|
+
<div class="docstring">{% for section in texts %}{{ section.text | markdown | safe }}{% endfor %}</div>
|
|
9
|
+
{% else %}
|
|
10
|
+
<p class="empty">No docstring.</p>
|
|
11
|
+
{% endif %}
|
|
12
|
+
</details>
|
|
13
|
+
|
|
14
|
+
<details class="block divider block-{{ role }}" data-section="signature" open>
|
|
15
|
+
<summary class="badge badge-{{ role }}">Signature</summary>
|
|
16
|
+
{% if entity.decorators %}
|
|
17
|
+
<p class="chips decorators">{% for decorator in entity.decorators %}<code class="chip">@{{ decorator }}</code>{% endfor %}</p>
|
|
18
|
+
{% endif %}
|
|
19
|
+
<pre class="signature code-block"><code>{{ signature(entity) | highlight }}</code></pre>
|
|
20
|
+
{% if entity.kind == "class" and entity.bases %}
|
|
21
|
+
<p class="bases">Bases: {% for base in entity.bases %}<code class="chip">{{ base }}</code>{% endfor %}</p>
|
|
22
|
+
{% endif %}
|
|
23
|
+
</details>
|
|
24
|
+
|
|
25
|
+
{% set rows = parameter_rows(entity) %}
|
|
26
|
+
{% if rows %}
|
|
27
|
+
<details class="block divider block-{{ role }}" data-section="parameters" open>
|
|
28
|
+
<summary class="badge badge-{{ role }}">Parameters <span class="count">{{ rows | length }}</span></summary>
|
|
29
|
+
<table>
|
|
30
|
+
<thead><tr><th>Name</th><th>Type</th><th>Default</th><th>Description</th></tr></thead>
|
|
31
|
+
<tbody>
|
|
32
|
+
{% for row in rows %}
|
|
33
|
+
<tr>
|
|
34
|
+
<td><code class="param-name">{{ row.name }}</code></td>
|
|
35
|
+
<td>{% if row.annotation %}<code>{{ row.annotation }}</code>{% else %}<span class="empty">-</span>{% endif %}</td>
|
|
36
|
+
<td>{% if row.default %}<code>{{ row.default }}</code>{% else %}<span class="empty">-</span>{% endif %}</td>
|
|
37
|
+
<td>{% if row.description %}{{ row.description | markdown | safe }}{% else %}<span class="empty">-</span>{% endif %}</td>
|
|
38
|
+
</tr>
|
|
39
|
+
{% endfor %}
|
|
40
|
+
</tbody>
|
|
41
|
+
</table>
|
|
42
|
+
</details>
|
|
43
|
+
{% endif %}
|
|
44
|
+
|
|
45
|
+
{% set documented = other_sections(entity) | rejectattr("kind", "equalto", "text") | list %}
|
|
46
|
+
{% if entity.kind == "function" and entity.returns and not documented | selectattr("kind", "equalto", "returns") | list %}
|
|
47
|
+
<details class="block divider block-returns" data-section="returns" open>
|
|
48
|
+
<summary class="badge badge-returns">Returns</summary>
|
|
49
|
+
<p><code>{{ entity.returns }}</code></p>
|
|
50
|
+
</details>
|
|
51
|
+
{% endif %}
|
|
52
|
+
|
|
53
|
+
{% for section in documented %}
|
|
54
|
+
{% set kind = section.kind | replace(' ', '-') %}
|
|
55
|
+
<details class="block divider block-{{ kind }}" data-section="{{ kind }}" open>
|
|
56
|
+
<summary class="badge badge-{{ kind }}">{{ section.title or section.kind }}{% if section["items"] | length > 1 %} <span class="count">{{ section["items"] | length }}</span>{% endif %}</summary>
|
|
57
|
+
{% if section.text %}{{ section.text | markdown | safe }}{% endif %}
|
|
58
|
+
{% if section["items"] %}
|
|
59
|
+
<ul class="items">
|
|
60
|
+
{% for item in section["items"] %}
|
|
61
|
+
<li>
|
|
62
|
+
{% if item.is_code %}
|
|
63
|
+
<pre class="code-block"><code>{{ item.description | highlight }}</code></pre>
|
|
64
|
+
{% else %}
|
|
65
|
+
{% if item.name or item.annotation %}
|
|
66
|
+
<code class="item-head">
|
|
67
|
+
{%- if item.name %}<span class="param-name">{{ item.name }}</span>{% endif %}
|
|
68
|
+
{%- if item.name and item.annotation %}<span class="type">: {{ item.annotation }}</span>
|
|
69
|
+
{%- elif item.annotation %}<span class="param-name">{{ item.annotation }}</span>{% endif %}
|
|
70
|
+
</code>
|
|
71
|
+
{% endif %}
|
|
72
|
+
{{ item.description | markdown | safe }}
|
|
73
|
+
{% endif %}
|
|
74
|
+
</li>
|
|
75
|
+
{% endfor %}
|
|
76
|
+
</ul>
|
|
77
|
+
{% endif %}
|
|
78
|
+
</details>
|
|
79
|
+
{% endfor %}
|
|
80
|
+
|
|
81
|
+
{% if entity.kind == "class" %}
|
|
82
|
+
{% with divider = true %}{% include "members.html" %}{% endwith %}
|
|
83
|
+
{% endif %}
|
|
84
|
+
|
|
85
|
+
{# Open by default; the code is fetched right after the page is shown, so the page itself stays light. #}
|
|
86
|
+
<details class="block divider block-source" data-section="source" open
|
|
87
|
+
hx-get="/source/{{ entity.path }}" hx-trigger="load" hx-target="find .code">
|
|
88
|
+
<summary class="badge badge-source">Source</summary>
|
|
89
|
+
{# Changing the theme saves it to doc/config/config.json. #}
|
|
90
|
+
<label class="theme-picker">Code theme
|
|
91
|
+
<select class="code-theme-select" name="code_theme"
|
|
92
|
+
hx-post="/settings/code-theme" hx-trigger="change" hx-swap="none">
|
|
93
|
+
{% for name in code_themes %}<option value="{{ name }}"{% if name == code_theme %} selected{% endif %}>{{ name }}</option>{% endfor %}
|
|
94
|
+
</select>
|
|
95
|
+
</label>
|
|
96
|
+
<div class="code"><p class="empty">Loading…</p></div>
|
|
97
|
+
</details>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
{# Reloads itself when the server reports a change in the code.
|
|
2
|
+
hx-disinherit keeps links inside the page from inheriting this element's hx-swap. #}
|
|
3
|
+
<article hx-get="/wiki/{{ path }}" hx-trigger="docs-changed from:body" hx-swap="outerHTML" hx-disinherit="*">
|
|
4
|
+
{% include content %}
|
|
5
|
+
</article>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{% set role = page_role(state, entity) %}
|
|
2
|
+
<header class="page-header">
|
|
3
|
+
<p class="crumbs">
|
|
4
|
+
{% for crumb in breadcrumbs %}
|
|
5
|
+
<a href="/wiki/{{ crumb.path }}" hx-get="/wiki/{{ crumb.path }}" hx-target="#content" hx-push-url="true">{{ crumb.name }}</a>
|
|
6
|
+
<span class="sep">›</span>
|
|
7
|
+
{% endfor %}
|
|
8
|
+
</p>
|
|
9
|
+
<h1>
|
|
10
|
+
<span class="icon icon-{{ role | replace(' ', '-') }}">{{ symbols.get(role, "") }}</span>
|
|
11
|
+
<span class="name role-{{ role | replace(' ', '-') }}">{{ entity.name }}</span>
|
|
12
|
+
<span class="kind">{{ role }}</span>
|
|
13
|
+
</h1>
|
|
14
|
+
<p class="meta">
|
|
15
|
+
<code class="path">{{ entity.path }}</code>
|
|
16
|
+
{% if entity.filepath %}
|
|
17
|
+
<span class="location">{{ entity.filepath }}{% if entity.lineno and entity.kind != "module" %}:{{ entity.lineno }}–{{ entity.endlineno }}{% endif %}</span>
|
|
18
|
+
{% endif %}
|
|
19
|
+
</p>
|
|
20
|
+
</header>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{% for group in members_by_group(state, entity) %}
|
|
2
|
+
<details class="block{% if divider %} divider{% endif %} block-{{ group.role | replace(' ', '-') }}" data-section="{{ group.label | lower }}" open>
|
|
3
|
+
<summary class="badge badge-{{ group.role | replace(' ', '-') }}">{{ group.label }} <span class="count">{{ group["items"] | length }}</span></summary>
|
|
4
|
+
<ul class="members">
|
|
5
|
+
{% for item in group["items"] %}
|
|
6
|
+
<li>
|
|
7
|
+
<a class="member" href="/wiki/{{ item.path }}" hx-get="/wiki/{{ item.path }}" hx-target="#content" hx-push-url="true">
|
|
8
|
+
<span class="icon icon-{{ item.role | replace(' ', '-') }}">{{ item.symbol }}</span>
|
|
9
|
+
<span class="member-body">
|
|
10
|
+
<code class="member-signature"><span class="name role-{{ item.role | replace(' ', '-') }}">{{ item.name }}</span><span class="details">{{ item.details }}</span></code>
|
|
11
|
+
{% if item.summary %}<span class="summary">{{ item.summary }}</span>{% endif %}
|
|
12
|
+
</span>
|
|
13
|
+
</a>
|
|
14
|
+
</li>
|
|
15
|
+
{% endfor %}
|
|
16
|
+
</ul>
|
|
17
|
+
</details>
|
|
18
|
+
{% endfor %}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{% include "header.html" %}
|
|
2
|
+
|
|
3
|
+
<details class="block block-description" data-section="description" open>
|
|
4
|
+
<summary class="badge badge-description">Description</summary>
|
|
5
|
+
{% if entity.docstring %}
|
|
6
|
+
<div class="docstring">{{ entity.docstring | markdown | safe }}</div>
|
|
7
|
+
{% else %}
|
|
8
|
+
<p class="empty">No docstring.</p>
|
|
9
|
+
{% endif %}
|
|
10
|
+
</details>
|
|
11
|
+
|
|
12
|
+
{% if entity.exports %}
|
|
13
|
+
<details class="block block-exports" data-section="exports" open>
|
|
14
|
+
<summary class="badge badge-exports">Public API <code>__all__</code></summary>
|
|
15
|
+
<p class="chips">{% for name in entity.exports %}<code class="chip">{{ name }}</code>{% endfor %}</p>
|
|
16
|
+
</details>
|
|
17
|
+
{% endif %}
|
|
18
|
+
|
|
19
|
+
{% include "members.html" %}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{% if query.strip() %}
|
|
2
|
+
<ul class="search-options" role="listbox">
|
|
3
|
+
{% for item in results %}
|
|
4
|
+
<li role="option" aria-selected="false">
|
|
5
|
+
<a class="search-link" href="/wiki/{{ item.path }}" hx-get="/wiki/{{ item.path }}" hx-target="#content" hx-push-url="true">
|
|
6
|
+
<span class="icon icon-{{ item.role | replace(' ', '-') }}">{{ item.symbol }}</span>
|
|
7
|
+
<span class="search-text">
|
|
8
|
+
<span class="search-name">{{ item.before }}<mark>{{ item.match }}</mark>{{ item.after }}</span>
|
|
9
|
+
<span class="search-path">{{ item.path }}</span>
|
|
10
|
+
{% if item.snippet %}
|
|
11
|
+
<span class="search-snippet">{{ item.snippet.before }}<mark>{{ item.snippet.match }}</mark>{{ item.snippet.after }}</span>
|
|
12
|
+
{% endif %}
|
|
13
|
+
</span>
|
|
14
|
+
<span class="search-kind">{{ item.role }}</span>
|
|
15
|
+
</a>
|
|
16
|
+
</li>
|
|
17
|
+
{% else %}
|
|
18
|
+
<li class="search-empty">No results</li>
|
|
19
|
+
{% endfor %}
|
|
20
|
+
</ul>
|
|
21
|
+
{% endif %}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<pre class="code-block"><code>{{ code | highlight }}</code></pre>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{# The icon comes before the link: on branches it opens/closes the branch, the name navigates.
|
|
2
|
+
Packages show a folder, modules (files) a file, other entities their kind symbol. #}
|
|
3
|
+
{% macro icon(item) %}
|
|
4
|
+
<span class="icon icon-{{ item.role }}">{{ item.symbol }}</span>
|
|
5
|
+
{% endmacro %}
|
|
6
|
+
{% macro link(item) %}
|
|
7
|
+
<a href="/wiki/{{ item.path }}" hx-get="/wiki/{{ item.path }}" hx-target="#content" hx-push-url="true"
|
|
8
|
+
class="role-{{ item.role }}{% if item.current %} current{% endif %}" {% if item.current %}aria-current="page"{% endif %}>
|
|
9
|
+
{{- item.name -}}
|
|
10
|
+
</a>
|
|
11
|
+
{% endmacro %}
|
|
12
|
+
{% macro node(item) %}
|
|
13
|
+
<li>
|
|
14
|
+
{% if item.children %}
|
|
15
|
+
{# Packages and modules start open; classes open only on the path to the current page. #}
|
|
16
|
+
<details data-path="{{ item.path }}" {% if item.open or item.role != "class" %}open{% endif %}>
|
|
17
|
+
<summary>{{ icon(item) }}{{ link(item) }}</summary>
|
|
18
|
+
<ul>
|
|
19
|
+
{% for child in item.children %}{{ node(child) }}{% endfor %}
|
|
20
|
+
</ul>
|
|
21
|
+
</details>
|
|
22
|
+
{% else %}
|
|
23
|
+
<div class="leaf">{{ icon(item) }}{{ link(item) }}</div>
|
|
24
|
+
{% endif %}
|
|
25
|
+
</li>
|
|
26
|
+
{% endmacro %}
|
|
27
|
+
<nav id="tree" {% if oob %}hx-swap-oob="true"{% endif %}>
|
|
28
|
+
<ul>
|
|
29
|
+
{% for item in tree %}{{ node(item) }}{% endfor %}
|
|
30
|
+
</ul>
|
|
31
|
+
</nav>
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: doctracker
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server that centralizes the documentation of Python projects developed with LLM agents.
|
|
5
|
+
Author: Carlos Filipe de Castro Lemos
|
|
6
|
+
Author-email: Carlos Filipe de Castro Lemos <carlos.lemos.dev@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
License-File: src/doctracker/wiki/static/htmx-LICENSE.txt
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Classifier: Topic :: Software Development :: Documentation
|
|
19
|
+
Requires-Dist: click>=8.5.0
|
|
20
|
+
Requires-Dist: griffe>=2.4.1
|
|
21
|
+
Requires-Dist: jinja2>=3.1.6
|
|
22
|
+
Requires-Dist: markdown-it-py>=4.2.0
|
|
23
|
+
Requires-Dist: mcp>=2.3.0,<3
|
|
24
|
+
Requires-Dist: pygments>=2.21.0
|
|
25
|
+
Requires-Dist: starlette>=1.10.0
|
|
26
|
+
Requires-Dist: uvicorn>=0.54.0
|
|
27
|
+
Requires-Python: >=3.11
|
|
28
|
+
Project-URL: Homepage, https://github.com/cfclemos/doctracker
|
|
29
|
+
Project-URL: Repository, https://github.com/cfclemos/doctracker
|
|
30
|
+
Project-URL: Issues, https://github.com/cfclemos/doctracker/issues
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# DOCUMENTATION TRACKER (DOCTRACKER)
|
|
34
|
+
|
|
35
|
+
**One place for the documentation of a Python project developed with LLM agents.**
|
|
36
|
+
|
|
37
|
+
When agents work on a codebase, documentation ends up scattered: notes in many
|
|
38
|
+
Markdown files, memories, code comments and commit messages, all drifting out of
|
|
39
|
+
sync. Documentation Tracker (doctracker) centralizes it. It runs an [MCP](https://modelcontextprotocol.io)
|
|
40
|
+
server that any agent (Claude Code, OpenCode, …) can connect to, and serves a live
|
|
41
|
+
wiki of the project in your browser.
|
|
42
|
+
|
|
43
|
+
> **Status: alpha (0.1).** This first version builds the technical reference of the
|
|
44
|
+
> code. Notes, commit history and LLM summaries come in the next versions (see
|
|
45
|
+
> [Roadmap](#roadmap)).
|
|
46
|
+
|
|
47
|
+
## Features
|
|
48
|
+
|
|
49
|
+
- **Technical reference built from the code** — modules, classes, functions,
|
|
50
|
+
methods and attributes, with signatures, type hints, decorators and parsed
|
|
51
|
+
docstrings (Google, NumPy or Sphinx style).
|
|
52
|
+
- **Static analysis only** — the code is read with [griffe](https://mkdocstrings.github.io/griffe/),
|
|
53
|
+
never imported or executed.
|
|
54
|
+
- **Live wiki** — edit a file and the open page updates by itself.
|
|
55
|
+
- **MCP server** — agents can list and read the documentation, and get a guide on
|
|
56
|
+
how to document the code so the wiki stays complete.
|
|
57
|
+
- **Search** — by name, path and description, with keyboard navigation.
|
|
58
|
+
- **Everything in one folder** — doctracker keeps its files in `doc/` inside your
|
|
59
|
+
project and never touches anything else.
|
|
60
|
+
- **Cross-platform** — Windows and Linux, Python 3.11+.
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
Install it as a development dependency of the project you want to document:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
uv add --dev doctracker
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
or with pip:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pip install doctracker
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Quick start
|
|
77
|
+
|
|
78
|
+
From the root of your project:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
doctracker init # creates doc/config/config.json (packages are detected automatically)
|
|
82
|
+
doctracker start # starts the server
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
With uv, prefix the commands with `uv run` (e.g. `uv run doctracker start`).
|
|
86
|
+
`start` runs `init` on its own if the project has no configuration yet.
|
|
87
|
+
|
|
88
|
+
The server prints its two addresses:
|
|
89
|
+
|
|
90
|
+
```text
|
|
91
|
+
Wiki: http://127.0.0.1:8765/
|
|
92
|
+
MCP: http://127.0.0.1:8765/mcp
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Open the wiki in your browser. Keep the server running while you work on the code;
|
|
96
|
+
stop it with `Ctrl+C`.
|
|
97
|
+
|
|
98
|
+
## Connecting an agent
|
|
99
|
+
|
|
100
|
+
The MCP server uses the Streamable HTTP transport at `http://127.0.0.1:8765/mcp`.
|
|
101
|
+
The server must be running before the agent connects.
|
|
102
|
+
|
|
103
|
+
**Claude Code** — add a `.mcp.json` file at the root of your project:
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"mcpServers": {
|
|
108
|
+
"doctracker": { "type": "http", "url": "http://127.0.0.1:8765/mcp" }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
or register it from the terminal:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
claude mcp add --transport http doctracker http://127.0.0.1:8765/mcp
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**OpenCode** — in `opencode.json`:
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"mcp": {
|
|
124
|
+
"doctracker": { "type": "remote", "url": "http://127.0.0.1:8765/mcp" }
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Other agents** — any MCP client that supports Streamable HTTP can connect to the
|
|
130
|
+
same URL.
|
|
131
|
+
|
|
132
|
+
### What the agent gets
|
|
133
|
+
|
|
134
|
+
| Name | Kind | What it does |
|
|
135
|
+
| --- | --- | --- |
|
|
136
|
+
| `list_entities` | tool | Lists documented entities, filtered by path prefix and kind. |
|
|
137
|
+
| `get_entity` | tool | Full technical reference of one entity. |
|
|
138
|
+
| `status` | tool | Whether the documentation is built, and loading errors. |
|
|
139
|
+
| `documentation_guide` | tool | How to document this project's code (adapted to its settings). |
|
|
140
|
+
| `doctracker://guide` | resource | The same guide, as a Markdown resource. |
|
|
141
|
+
|
|
142
|
+
The server's instructions tell the agent to read the guide before writing or
|
|
143
|
+
changing docstrings.
|
|
144
|
+
|
|
145
|
+
## The wiki
|
|
146
|
+
|
|
147
|
+
- **Tree** — packages (📦/📁), modules (📄), classes (**C**), functions (**f**) and
|
|
148
|
+
methods (**m**). Branches collapse; the tree can be resized or hidden (☰).
|
|
149
|
+
- **Module pages** — description, public API (`__all__`) and members grouped by kind.
|
|
150
|
+
- **Entity pages** — description, signature, parameters table, returns, raises,
|
|
151
|
+
examples, notes, members (for classes) and the source code with syntax
|
|
152
|
+
highlighting. Every section can be collapsed from its badge.
|
|
153
|
+
- **Code theme** — choose among the Pygments themes in the Source section; the
|
|
154
|
+
choice is saved in `config.json`.
|
|
155
|
+
- **Search** — press `/` anywhere, type, use ↑/↓ and Enter.
|
|
156
|
+
|
|
157
|
+
## Configuration
|
|
158
|
+
|
|
159
|
+
`doc/config/config.json` (created by `doctracker init`):
|
|
160
|
+
|
|
161
|
+
| Key | Default | Description |
|
|
162
|
+
| --- | --- | --- |
|
|
163
|
+
| `packages` | detected | Top-level packages to document. |
|
|
164
|
+
| `search_paths` | `["src", "."]` | Folders where the packages are found, relative to the project root. |
|
|
165
|
+
| `host` | `"127.0.0.1"` | Address the server listens on. |
|
|
166
|
+
| `port` | `8765` | Port of the server (also `doctracker start --port`). |
|
|
167
|
+
| `docstring_style` | `"google"` | `google`, `numpy` or `sphinx`. |
|
|
168
|
+
| `show_private` | `true` | Show `_private` members. |
|
|
169
|
+
| `code_theme` | `"default"` | Pygments theme of the source code. |
|
|
170
|
+
| `ssl_certfile` / `ssl_keyfile` | `null` | Certificate and key files; when both are set, the server uses HTTPS. |
|
|
171
|
+
| `watch_interval` | `1.0` | Seconds between checks for changed files. |
|
|
172
|
+
|
|
173
|
+
### The `doc/` folder
|
|
174
|
+
|
|
175
|
+
```text
|
|
176
|
+
doc/
|
|
177
|
+
├── .gitignore # keeps local data out of git
|
|
178
|
+
└── config/
|
|
179
|
+
└── config.json # commit this file
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Other folders (local data, models) are created only when a feature needs them, and
|
|
183
|
+
are ignored by `doc/.gitignore`.
|
|
184
|
+
|
|
185
|
+
## Documenting your code
|
|
186
|
+
|
|
187
|
+
doctracker shows what the code says, so the wiki is as good as the docstrings. In
|
|
188
|
+
short:
|
|
189
|
+
|
|
190
|
+
- a docstring for every module, public class, function and method;
|
|
191
|
+
- the first line is a one-sentence summary (it is used in lists and search);
|
|
192
|
+
- type hints on every parameter and return value;
|
|
193
|
+
- `Args`, `Returns`, `Raises`, `Attributes` and `Examples` sections as needed;
|
|
194
|
+
- constants documented with a string on the line after the assignment.
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
def get_weather(city: str, units: str = "metric") -> Forecast:
|
|
198
|
+
"""Fetch the forecast for a city.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
city: City name, e.g. `Lisbon`.
|
|
202
|
+
units: `metric` or `imperial`.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Forecast data for the city.
|
|
206
|
+
|
|
207
|
+
Raises:
|
|
208
|
+
CityNotFound: When the city is unknown.
|
|
209
|
+
"""
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Agents get the complete guide from the `documentation_guide` tool.
|
|
213
|
+
|
|
214
|
+
## Security
|
|
215
|
+
|
|
216
|
+
- By default the server listens only on `127.0.0.1`, and the MCP endpoint
|
|
217
|
+
validates the `Host` and `Origin` headers (protection against DNS rebinding).
|
|
218
|
+
- The wiki only accepts changes (such as the code theme) from its own pages.
|
|
219
|
+
- Exposing the server on a network is not supported yet: there is no
|
|
220
|
+
authentication in this version.
|
|
221
|
+
|
|
222
|
+
## Roadmap
|
|
223
|
+
|
|
224
|
+
- Notes recorded by the agent through the MCP server, turned into atomic commits.
|
|
225
|
+
- Commit history linked to each function and class, with LLM summaries
|
|
226
|
+
(llama.cpp or vLLM).
|
|
227
|
+
- Knowledge graph of the code: callers, callees, dead code.
|
|
228
|
+
- Semantic search.
|
|
229
|
+
- Editing the documentation from the wiki.
|
|
230
|
+
|
|
231
|
+
## License
|
|
232
|
+
|
|
233
|
+
[MIT](LICENSE). The bundled [htmx](https://htmx.org) is distributed under the
|
|
234
|
+
Zero-Clause BSD license (`src/doctracker/wiki/static/htmx-LICENSE.txt`).
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
doctracker/__init__.py,sha256=OPHbolX0pWdelvIIY955T7zRPyOfB0BUYsx-B0Pt-p0,168
|
|
2
|
+
doctracker/__main__.py,sha256=X2wMUJYRd25PjHhdVo7VesexL5SdCQu8K0p2qPsRWnc,71
|
|
3
|
+
doctracker/cli.py,sha256=yatqx4J73OOdWcCqScJtmF8h7hLgbbAUDWQ4LSjcoy8,2115
|
|
4
|
+
doctracker/config.py,sha256=aUCAocPHfaQK8JWwDmP8B5e435bNjzwpXL_nqwzVe5s,2459
|
|
5
|
+
doctracker/extraction/__init__.py,sha256=ZJr6nwoYDrR3tlM_1Qb2_H-0k6ado2AUINVt2ysdg3Y,156
|
|
6
|
+
doctracker/extraction/griffe_loader.py,sha256=_Ev-n-V44AP2yCthVKCdeKBRQB4KQOWlhFRLZg3lkSk,5876
|
|
7
|
+
doctracker/server/__init__.py,sha256=XhKWcquD0benxpMuuBt4rwK2XG1lVKz8jysxI65mdeo,67
|
|
8
|
+
doctracker/server/app.py,sha256=HpedKlRwFxXNVWwBL6DZdCgI4Rz7Dh_T4gZdHanByOI,2778
|
|
9
|
+
doctracker/server/guide.md,sha256=YzoDKvs145a18MXY-untYNzSIzoYu_2Yn3aAyyxEiM0,2622
|
|
10
|
+
doctracker/server/guide.py,sha256=UvKj9Z-eex2aa6ltT5urhS7cK-qV9fxRFQAd-mYCBhc,1810
|
|
11
|
+
doctracker/server/state.py,sha256=dpC8Yem-B5MHAvkOWyXfQzJ99IwoFi_RA6-h6P3wV1Y,1088
|
|
12
|
+
doctracker/server/tools.py,sha256=LbZDcTvKvDVX0_6wvj8XL0_XcisqXLsfzIoMOTgB8HA,2854
|
|
13
|
+
doctracker/server/watcher.py,sha256=Yw4Nd-mqwmw_4nO7SYIv0hcgWlwSZaOt-tg9QdvwUyg,2292
|
|
14
|
+
doctracker/server/web.py,sha256=2SApeHwbpN-zdf0ZCuNxLE_A2qRmd6Pg4hNp5mUN6Sg,5289
|
|
15
|
+
doctracker/wiki/__init__.py,sha256=0buyZNaGj4oACwT_SWXCnsalTE8OCPjHlwQPKXus6So,48
|
|
16
|
+
doctracker/wiki/pages.py,sha256=rIwOgsV1x4cOb0epa9ZSs41aZsC2ihNKNmi5wAk1BKI,13592
|
|
17
|
+
doctracker/wiki/static/htmx-LICENSE.txt,sha256=09JFb3ZBTyRWEEZg69Za_xwEzXlmuUK9q9Y_PNsxajg,642
|
|
18
|
+
doctracker/wiki/static/htmx.min.js,sha256=1v3HXyBOa976mbab8ebUrGm4o2T3eSn0XBNHa0AA9xc,52182
|
|
19
|
+
doctracker/wiki/static/live.js,sha256=bdqNCfyBRoqnUZgTk6f-DeRT4s3TvqNUWAFmi_Qc28M,6141
|
|
20
|
+
doctracker/wiki/static/style.css,sha256=TBz9o36vMqrVl1Kp_6pXEgODVTslh-svaY9jMEftOMk,16029
|
|
21
|
+
doctracker/wiki/templates/base.html,sha256=ixIRxNIcEUUu3VediP5u9ocM9X6smDQFe6UUGRzwrvI,1463
|
|
22
|
+
doctracker/wiki/templates/entity.html,sha256=gVTBXMY1jr82LwanZU5tsMdDC9glzbp1DDDYo94sc1E,4528
|
|
23
|
+
doctracker/wiki/templates/fragment.html,sha256=cWMUrPQdRHvpgYe_DQLecu3JqvJ47sjueFMqGzKNujk,173
|
|
24
|
+
doctracker/wiki/templates/fragment_content.html,sha256=rerxgekXdfq36g4D7ZFme0Qk9p-bw4kknwZgd9UljWE,297
|
|
25
|
+
doctracker/wiki/templates/header.html,sha256=8a1ER8hdWx8ODw_EbsTGiuPiwxlJLXCnAjWTZzslpCw,839
|
|
26
|
+
doctracker/wiki/templates/loading.html,sha256=nBiMvmX392O8tclG3wi6-SmufhGFd4QWmBEQkAo3taU,119
|
|
27
|
+
doctracker/wiki/templates/members.html,sha256=kppY02QYP2ozCnWyyeCGSPtpM1UBK3v3Ko-GyfgpzpE,1028
|
|
28
|
+
doctracker/wiki/templates/module.html,sha256=JVmREILC4bfz3MDhYvKQbStwNWhzqHDxMNLEcdPeZR4,672
|
|
29
|
+
doctracker/wiki/templates/not_found.html,sha256=6GTjW1Eyj87SKJ4RFfylDKw6Jxz4vfWmV30tCH3hjWs,226
|
|
30
|
+
doctracker/wiki/templates/search.html,sha256=OIEvlbzvONPHphrQeOgMREaWXfi6EDZtusmrfJ2wURM,906
|
|
31
|
+
doctracker/wiki/templates/source.html,sha256=-fFyb5gbUzk4bqGlVFdpEqVk9-KJnjtwuZMCrfbYj2w,66
|
|
32
|
+
doctracker/wiki/templates/tree.html,sha256=LDmst4sprNRojUiQBOwKqUX6HQHwa0TkDNhxcmrdOnA,1218
|
|
33
|
+
doctracker-0.1.0.dist-info/licenses/LICENSE,sha256=EPwZHQ2im2N4aU6nrT6NuQ_I4X4JnkFYu80yqiAILhI,1086
|
|
34
|
+
doctracker-0.1.0.dist-info/licenses/src/doctracker/wiki/static/htmx-LICENSE.txt,sha256=09JFb3ZBTyRWEEZg69Za_xwEzXlmuUK9q9Y_PNsxajg,642
|
|
35
|
+
doctracker-0.1.0.dist-info/WHEEL,sha256=9IUYslYuG_asT4WlWvfk_K76nveuLvSTKDoLeL0cV-Q,80
|
|
36
|
+
doctracker-0.1.0.dist-info/entry_points.txt,sha256=zg9ymdE9eqS9HdygNKpTg-uXu03AvfUUMs5atlp3lx4,52
|
|
37
|
+
doctracker-0.1.0.dist-info/METADATA,sha256=lhXtfz8ElyCUgCjSLFzUfx-j1_P6cTdXONEQjRJatlQ,8319
|
|
38
|
+
doctracker-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Carlos Filipe de Castro Lemos
|
|
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,13 @@
|
|
|
1
|
+
Zero-Clause BSD
|
|
2
|
+
=============
|
|
3
|
+
|
|
4
|
+
Permission to use, copy, modify, and/or distribute this software for
|
|
5
|
+
any purpose with or without fee is hereby granted.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL
|
|
8
|
+
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
|
|
9
|
+
OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
|
|
10
|
+
FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
|
|
11
|
+
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
|
12
|
+
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
|
13
|
+
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|