aidos 0.0.1__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.
aidos-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yingding Wang
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,14 @@
1
+ include LICENSE
2
+ include projectdescription.md
3
+ include requirements.txt
4
+
5
+ recursive-include src/aidos *.py
6
+
7
+ recursive-exclude src/aidos/graphify-out *
8
+ recursive-exclude src/aidos __pycache__ *.pyc *.pyo
9
+
10
+ prune tests
11
+ prune scripts
12
+ prune docs
13
+ prune config
14
+ prune output
aidos-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,128 @@
1
+ Metadata-Version: 2.4
2
+ Name: aidos
3
+ Version: 0.0.1
4
+ Summary: aidos (AI Diary Operating System) — the control-plane CLI for an agentic harness: roadmap index, knowledge-graph reindex, and module setup.
5
+ Author: Yingding Wang
6
+ License-Expression: MIT
7
+ Keywords: aidos,ai-diary,harness,control-plane,knowledge-graph,roadmap
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Classifier: Topic :: Software Development :: Libraries
15
+ Requires-Python: >=3.12
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: typer>=0.15.1
19
+ Provides-Extra: mcp
20
+ Requires-Dist: mcp>=1.27.1; extra == "mcp"
21
+ Dynamic: license-file
22
+
23
+ # aidos
24
+
25
+ **AI diary OS** — the control-plane CLI for an agentic harness. Index your roadmaps, keep knowledge graphs fresh, and stand up new modules, all from one command.
26
+
27
+ ## What it does
28
+
29
+ `aidos` is the operations layer that sits *above* a multi-module workspace and manages the shared machinery your agent depends on:
30
+
31
+ - **Roadmap index** — parse a roadmap markdown into identified items (stable UUIDs), classify them done / active / deferred, and build a SQLite **FTS5** keyword index for fast search. The markdown stays the source of truth; the index is derived.
32
+ - **Graph reindex** — discover every subproject's knowledge graphs, report freshness, and rebuild them in the correct virtualenv. It never kills your MCP servers — it reports which ones need a restart.
33
+ - **Module setup** — a reproducible recipe (and script) to make a new Python module a first-class citizen: its own venv, its own code + docs graphs, and wiring into the shared portal.
34
+
35
+ It is **CLI-first**: all logic lives behind plain functions, so it is testable and scriptable without a server. A future control **MCP** wraps the same CLI.
36
+
37
+ The name is a nod to [`aidiary`](https://pypi.org/project/aidiary/) (the *AI diary* memory system): `aidos` is the **OS** layer around it.
38
+
39
+ ## Install
40
+
41
+ Requires: Python 3.12+
42
+
43
+ ```bash
44
+ pip install aidos
45
+ ```
46
+
47
+ Building a module's own knowledge graphs additionally uses `aidiary[graphs]` (installed editable in dev):
48
+
49
+ ```bash
50
+ pip install "aidiary[graphs]"
51
+ ```
52
+
53
+ ## Quick start
54
+
55
+ ### 1. Check graph freshness across the workspace
56
+
57
+ ```bash
58
+ aidos status
59
+ ```
60
+
61
+ Lists each subproject's graph layers (code / docs / …) with a ✓ fresh / ⚠ stale / ✗ missing badge based on `graph.json` mtime vs. newest source.
62
+
63
+ ### 2. Index a roadmap and search it
64
+
65
+ ```bash
66
+ aidos roadmap index path/to/ProjectRoadmap.md
67
+ aidos roadmap search "graph reindex" --db output/aidos/ProjectRoadmap.db
68
+ ```
69
+
70
+ `index` assigns a stable UUID to every item and writes a SQLite FTS5 index + a JSON sidecar. `search` is instant keyword retrieval — no embeddings, no LLM.
71
+
72
+ ### 3. Preview a done / active split (non-destructive)
73
+
74
+ ```bash
75
+ aidos roadmap split path/to/ProjectRoadmap.md
76
+ ```
77
+
78
+ Writes `*-archive.md` (done) and `*-active.md` (active + deferred) previews. **The original file is never modified** — you review first.
79
+
80
+ ### 4. Rebuild knowledge graphs
81
+
82
+ ```bash
83
+ aidos reindex --dry-run # show what would rebuild
84
+ aidos reindex --subproject aidos # rebuild one subproject's code graph
85
+ ```
86
+
87
+ Any rebuilt layer is reported as `restart_required` — restart those MCP servers through your editor (or your restart script), never by killing the process.
88
+
89
+ ## Directory layout
90
+
91
+ ```
92
+ my-module/
93
+ ├── pyproject.toml
94
+ ├── config/graphs.toml # graphs.code (src/, ast) + graphs.docs (docs/, semantic)
95
+ ├── src/<pkg>/ # your package
96
+ ├── docs/ # concept docs (feed the docs graph)
97
+ └── output/ # generated artifacts
98
+ ├── aidos/ # roadmap index db + json sidecar
99
+ └── graphs/{code,docs}/ # graph.json + graph.html
100
+ ```
101
+
102
+ ## Command reference
103
+
104
+ | Command | Purpose |
105
+ |---------|---------|
106
+ | `aidos status` | Report knowledge-graph freshness across subprojects |
107
+ | `aidos roadmap index <file>` | Build the SQLite FTS5 search index for a roadmap |
108
+ | `aidos roadmap search <query> --db <db>` | Keyword-search the index |
109
+ | `aidos roadmap split <file>` | Non-destructive done / active split preview |
110
+ | `aidos reindex [--subproject N] [--docs] [--dry-run]` | Rebuild per-subproject graphs |
111
+
112
+ ## Design principles
113
+
114
+ - **Markdown / files stay the source of truth** — every index, db, and graph is a *derived* artifact. Producers and consumers connect via files on disk, not imports.
115
+ - **Keyword over embeddings** — on a few-hundred-item corpus, SQLite FTS5 is instant and sufficient; vector search is deferred until the data demands it.
116
+ - **Never kill a stdio MCP server** — reindex reports `restart_required`; it does not respawn processes.
117
+
118
+ ## What's new
119
+
120
+ ### 0.0.1
121
+
122
+ **First cut — the Developer-Control plane CLI**
123
+
124
+ - **`roadmap`** — `index` (parse → stable UUIDs → done/active/deferred → SQLite FTS5 + JSON sidecar), `search` (FTS5 keyword), `split` (non-destructive done/active preview).
125
+ - **`reindex`** — discover subproject graphs, report freshness, rebuild via each subproject's `aidiary-graphs`; reports `restart_required` (never kills MCP servers).
126
+ - **`status`** — workspace-wide graph freshness.
127
+ - **Reproducible module setup** — idempotent `setup.sh` (venv + editable installs + graphs) and a documented "make a module a first-class harness citizen" guide.
128
+ - **Typer CLI**, stdlib-only core (plus Typer); builds to sdist + wheel.
aidos-0.0.1/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # aidos
2
+
3
+ > **aidos** = **AI diary OS** — the control-plane CLI for an agentic harness. Roadmap index, knowledge-graph reindex, and module setup. CLI-first; a control MCP wraps the CLI.
4
+
5
+ This is the **Developer-Control plane** of the harness control plane (see [ArchitectureReview-HarnessEstate-2026-06-24.md](../ArchitectureReview-HarnessEstate-2026-06-24.md) §5). It consolidates ad-hoc harness administration into one stdlib-only package. Roadmap: [#24](../ProjectRoadmap.md).
6
+
7
+ ## 📚 Contents
8
+
9
+ - [📝 Overview](#-overview)
10
+ - [🚀 Get Started](#-get-started)
11
+ - [📦 Commands](#-commands)
12
+ - [🤝 Author](#-author)
13
+ - [📄 License](#-license)
14
+
15
+ ## 📝 Overview
16
+
17
+ | Module | Responsibility |
18
+ |--------|----------------|
19
+ | `roadmap_tool` | Parse a roadmap markdown → stable per-item UUIDs → classify done/active/deferred → SQLite FTS5 search index → **non-destructive** done/active split preview |
20
+ | `reindex` | Discover per-subproject knowledge graphs, report freshness, rebuild in each subproject's venv (never kills MCP servers — reports `restart_required`) |
21
+ | `cli` | `aidos <command>` dispatch |
22
+
23
+ **Design rules:** markdown stays the source of truth (git-native); the index is a derived artifact. Keyword (FTS5) search only — embeddings deferred (keyword wins on a few-hundred-item corpus). Never `pkill` a stdio MCP server.
24
+
25
+ ## 🚀 Get Started
26
+
27
+ Run without installing:
28
+
29
+ ```bash
30
+ python aidos/src/aidos/cli.py status
31
+ ```
32
+
33
+ Or install editable for the `aidos` command:
34
+
35
+ ```bash
36
+ pip install -e aidos
37
+ aidos status
38
+ ```
39
+
40
+ ## 📦 Commands
41
+
42
+ ```bash
43
+ # Roadmap: index, search, non-destructive split preview
44
+ aidos roadmap index copilot-memory/ProjectRoadmap.md
45
+ aidos roadmap search "graph reindex" --db output/aidos/ProjectRoadmap.db
46
+ aidos roadmap split copilot-memory/ProjectRoadmap.md # writes *-archive.md + *-active.md previews
47
+
48
+ # Graphs: freshness + rebuild
49
+ aidos status
50
+ aidos reindex --dry-run
51
+ aidos reindex --subproject copilot-memory
52
+ ```
53
+
54
+ `reindex` reports `restart_required` for any rebuilt layer — restart those MCP servers via VS Code (MCP: Restart) or `./restart-mcps.sh`, never by killing the process.
55
+
56
+ ## 🤝 Author
57
+
58
+ **Yingding Wang**
59
+
60
+ ## 📄 License
61
+
62
+ [MIT](../LICENSE) — © 2026 Yingding Wang
@@ -0,0 +1,106 @@
1
+ # aidos
2
+
3
+ **AI diary OS** — the control-plane CLI for an agentic harness. Index your roadmaps, keep knowledge graphs fresh, and stand up new modules, all from one command.
4
+
5
+ ## What it does
6
+
7
+ `aidos` is the operations layer that sits *above* a multi-module workspace and manages the shared machinery your agent depends on:
8
+
9
+ - **Roadmap index** — parse a roadmap markdown into identified items (stable UUIDs), classify them done / active / deferred, and build a SQLite **FTS5** keyword index for fast search. The markdown stays the source of truth; the index is derived.
10
+ - **Graph reindex** — discover every subproject's knowledge graphs, report freshness, and rebuild them in the correct virtualenv. It never kills your MCP servers — it reports which ones need a restart.
11
+ - **Module setup** — a reproducible recipe (and script) to make a new Python module a first-class citizen: its own venv, its own code + docs graphs, and wiring into the shared portal.
12
+
13
+ It is **CLI-first**: all logic lives behind plain functions, so it is testable and scriptable without a server. A future control **MCP** wraps the same CLI.
14
+
15
+ The name is a nod to [`aidiary`](https://pypi.org/project/aidiary/) (the *AI diary* memory system): `aidos` is the **OS** layer around it.
16
+
17
+ ## Install
18
+
19
+ Requires: Python 3.12+
20
+
21
+ ```bash
22
+ pip install aidos
23
+ ```
24
+
25
+ Building a module's own knowledge graphs additionally uses `aidiary[graphs]` (installed editable in dev):
26
+
27
+ ```bash
28
+ pip install "aidiary[graphs]"
29
+ ```
30
+
31
+ ## Quick start
32
+
33
+ ### 1. Check graph freshness across the workspace
34
+
35
+ ```bash
36
+ aidos status
37
+ ```
38
+
39
+ Lists each subproject's graph layers (code / docs / …) with a ✓ fresh / ⚠ stale / ✗ missing badge based on `graph.json` mtime vs. newest source.
40
+
41
+ ### 2. Index a roadmap and search it
42
+
43
+ ```bash
44
+ aidos roadmap index path/to/ProjectRoadmap.md
45
+ aidos roadmap search "graph reindex" --db output/aidos/ProjectRoadmap.db
46
+ ```
47
+
48
+ `index` assigns a stable UUID to every item and writes a SQLite FTS5 index + a JSON sidecar. `search` is instant keyword retrieval — no embeddings, no LLM.
49
+
50
+ ### 3. Preview a done / active split (non-destructive)
51
+
52
+ ```bash
53
+ aidos roadmap split path/to/ProjectRoadmap.md
54
+ ```
55
+
56
+ Writes `*-archive.md` (done) and `*-active.md` (active + deferred) previews. **The original file is never modified** — you review first.
57
+
58
+ ### 4. Rebuild knowledge graphs
59
+
60
+ ```bash
61
+ aidos reindex --dry-run # show what would rebuild
62
+ aidos reindex --subproject aidos # rebuild one subproject's code graph
63
+ ```
64
+
65
+ Any rebuilt layer is reported as `restart_required` — restart those MCP servers through your editor (or your restart script), never by killing the process.
66
+
67
+ ## Directory layout
68
+
69
+ ```
70
+ my-module/
71
+ ├── pyproject.toml
72
+ ├── config/graphs.toml # graphs.code (src/, ast) + graphs.docs (docs/, semantic)
73
+ ├── src/<pkg>/ # your package
74
+ ├── docs/ # concept docs (feed the docs graph)
75
+ └── output/ # generated artifacts
76
+ ├── aidos/ # roadmap index db + json sidecar
77
+ └── graphs/{code,docs}/ # graph.json + graph.html
78
+ ```
79
+
80
+ ## Command reference
81
+
82
+ | Command | Purpose |
83
+ |---------|---------|
84
+ | `aidos status` | Report knowledge-graph freshness across subprojects |
85
+ | `aidos roadmap index <file>` | Build the SQLite FTS5 search index for a roadmap |
86
+ | `aidos roadmap search <query> --db <db>` | Keyword-search the index |
87
+ | `aidos roadmap split <file>` | Non-destructive done / active split preview |
88
+ | `aidos reindex [--subproject N] [--docs] [--dry-run]` | Rebuild per-subproject graphs |
89
+
90
+ ## Design principles
91
+
92
+ - **Markdown / files stay the source of truth** — every index, db, and graph is a *derived* artifact. Producers and consumers connect via files on disk, not imports.
93
+ - **Keyword over embeddings** — on a few-hundred-item corpus, SQLite FTS5 is instant and sufficient; vector search is deferred until the data demands it.
94
+ - **Never kill a stdio MCP server** — reindex reports `restart_required`; it does not respawn processes.
95
+
96
+ ## What's new
97
+
98
+ ### 0.0.1
99
+
100
+ **First cut — the Developer-Control plane CLI**
101
+
102
+ - **`roadmap`** — `index` (parse → stable UUIDs → done/active/deferred → SQLite FTS5 + JSON sidecar), `search` (FTS5 keyword), `split` (non-destructive done/active preview).
103
+ - **`reindex`** — discover subproject graphs, report freshness, rebuild via each subproject's `aidiary-graphs`; reports `restart_required` (never kills MCP servers).
104
+ - **`status`** — workspace-wide graph freshness.
105
+ - **Reproducible module setup** — idempotent `setup.sh` (venv + editable installs + graphs) and a documented "make a module a first-class harness citizen" guide.
106
+ - **Typer CLI**, stdlib-only core (plus Typer); builds to sdist + wheel.
@@ -0,0 +1,37 @@
1
+ [build-system]
2
+ requires = ["setuptools>=75.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "aidos"
7
+ version = "0.0.1"
8
+ description = "aidos (AI Diary Operating System) — the control-plane CLI for an agentic harness: roadmap index, knowledge-graph reindex, and module setup."
9
+ readme = "projectdescription.md"
10
+ requires-python = ">=3.12"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Yingding Wang" }]
14
+ keywords = ["aidos", "ai-diary", "harness", "control-plane", "knowledge-graph", "roadmap"]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Intended Audience :: Developers",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ "Programming Language :: Python :: 3.14",
22
+ "Topic :: Software Development :: Libraries",
23
+ ]
24
+ # Base tier is stdlib-only except the CLI framework. Keeps the control plane
25
+ # lightweight and installable everywhere. Version pins live in requirements.txt.
26
+ dependencies = ["typer>=0.15.1"]
27
+
28
+ [project.optional-dependencies]
29
+ # Roadmap #24 Phase 2 — control MCP wraps the CLI.
30
+ mcp = ["mcp>=1.27.1"]
31
+
32
+ [project.scripts]
33
+ aidos = "aidos.cli:main"
34
+
35
+ [tool.setuptools.packages.find]
36
+ where = ["src"]
37
+ include = ["aidos*"]
@@ -0,0 +1,4 @@
1
+ # aidos production dependencies (source of truth for version pins).
2
+ # Base tooling for the CLI. Keep this list minimal — the control plane should
3
+ # stay lightweight and installable everywhere.
4
+ typer>=0.26.7
aidos-0.0.1/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,27 @@
1
+ """aidos (AI diary OS) — workspace control-plane tooling for the harness.
2
+
3
+ This package is the **Developer-Control plane** of the harness control plane
4
+ (see workspace ``ArchitectureReview-HarnessEstate-2026-06-24.md`` §5). It
5
+ consolidates ad-hoc harness administration into a CLI-first, stdlib-only
6
+ package. A future control MCP (Roadmap #24 Phase 2) wraps this same CLI — the
7
+ CLI is the source of truth so the logic is testable without MCP.
8
+
9
+ Exports / submodules:
10
+
11
+ - :mod:`aidos.roadmap_tool` — parse a roadmap markdown into identified
12
+ items (stable UUIDs), classify done/active/deferred, build a SQLite FTS5
13
+ search index, and produce a **non-destructive** done/active split preview.
14
+ - :mod:`aidos.reindex` — discover per-subproject knowledge graphs,
15
+ report freshness/status, and orchestrate rebuilds in each subproject's venv
16
+ (never kills MCP servers — reports ``restart_required`` instead).
17
+ - :mod:`aidos.cli` — argparse dispatch (``aidos <command>``).
18
+
19
+ Consumers: the workspace operator (CLI) today; the ``aidos`` control MCP
20
+ (Phase 2) tomorrow.
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ __all__ = ["__version__"]
26
+
27
+ __version__ = "0.0.1"
@@ -0,0 +1,168 @@
1
+ """aidos command-line interface (Typer).
2
+
3
+ Usage::
4
+
5
+ aidos roadmap index <file> [--db PATH]
6
+ aidos roadmap search <query> [--db PATH] [-n N]
7
+ aidos roadmap split <file> [--out-dir DIR] # non-destructive preview
8
+ aidos status # graph freshness
9
+ aidos reindex [--subproject NAME] [--docs] [--dry-run]
10
+
11
+ The CLI is the source of truth; the future control MCP (Roadmap #24 Phase 2)
12
+ wraps these same functions.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from datetime import datetime, timezone
18
+ from pathlib import Path
19
+ from typing import Optional
20
+
21
+ import typer
22
+
23
+ from . import __version__, reindex as _reindex, roadmap_tool as _rt
24
+
25
+ app = typer.Typer(
26
+ no_args_is_help=True,
27
+ add_completion=False,
28
+ help="Workspace control-plane tooling for the harness.",
29
+ )
30
+ roadmap_app = typer.Typer(no_args_is_help=True, help="Parse / index / search / split a roadmap file.")
31
+ app.add_typer(roadmap_app, name="roadmap")
32
+
33
+
34
+ def _version_cb(value: bool) -> None:
35
+ if value:
36
+ typer.echo(f"aidos {__version__}")
37
+ raise typer.Exit()
38
+
39
+
40
+ @app.callback()
41
+ def _root(
42
+ _version: bool = typer.Option(
43
+ False, "--version", callback=_version_cb, is_eager=True, help="Show version and exit."
44
+ ),
45
+ ) -> None:
46
+ """Workspace control-plane tooling for the harness."""
47
+
48
+
49
+ def _find_root(start: Optional[Path] = None) -> Path:
50
+ """Walk up to the workspace root (marked by .vscode/ or .git/)."""
51
+ here = (start or Path.cwd()).resolve()
52
+ for cand in (here, *here.parents):
53
+ if (cand / ".vscode").is_dir() or (cand / ".git").is_dir():
54
+ return cand
55
+ return here
56
+
57
+
58
+ def _default_db(root: Path, file: Path) -> Path:
59
+ return root / "output" / "aidos" / f"{file.stem}.db"
60
+
61
+
62
+ @roadmap_app.command("index")
63
+ def roadmap_index(
64
+ file: Path = typer.Argument(..., help="Roadmap markdown file to index."),
65
+ db: Optional[Path] = typer.Option(None, help="Output SQLite path (default under output/aidos/)."),
66
+ ) -> None:
67
+ """Parse a roadmap and build the SQLite FTS5 search index."""
68
+ root = _find_root()
69
+ file = file.resolve()
70
+ items = _rt.parse_roadmap(file)
71
+ db_path = db.resolve() if db else _default_db(root, file)
72
+ _rt.build_index(items, db_path)
73
+ counts = _rt.summarize(items)
74
+ typer.echo(f"Indexed {counts['total']} items from {file.name} → {db_path}")
75
+ typer.echo(f" done={counts['done']} active={counts['active']} deferred={counts['deferred']}")
76
+ typer.echo(f" sidecar: {db_path.with_suffix('.json')}")
77
+
78
+
79
+ @roadmap_app.command("search")
80
+ def roadmap_search(
81
+ query: str = typer.Argument(..., help="FTS5 keyword query."),
82
+ db: Optional[Path] = typer.Option(None, help="Index SQLite path to search."),
83
+ n: int = typer.Option(20, "-n", help="Max results."),
84
+ ) -> None:
85
+ """Keyword-search the roadmap index."""
86
+ if db is None or not db.exists():
87
+ typer.echo("error: no index db found; run 'aidos roadmap index <file>' first "
88
+ "or pass --db", err=True)
89
+ raise typer.Exit(2)
90
+ rows = _rt.search_index(db, query, n)
91
+ if not rows:
92
+ typer.echo("(no matches)")
93
+ return
94
+ for r in rows:
95
+ typer.echo(f" [{r['status']:8}] {r['key']:8} {r['title']}")
96
+
97
+
98
+ @roadmap_app.command("split")
99
+ def roadmap_split(
100
+ file: Path = typer.Argument(..., help="Roadmap markdown file to split."),
101
+ out_dir: Optional[Path] = typer.Option(None, help="Preview output dir."),
102
+ ) -> None:
103
+ """Write a NON-DESTRUCTIVE done/active split preview (original untouched)."""
104
+ root = _find_root()
105
+ file = file.resolve()
106
+ items = _rt.parse_roadmap(file)
107
+ target = out_dir.resolve() if out_dir else root / "output" / "aidos" / "split-preview"
108
+ written = _rt.split_preview(file, items, target)
109
+ counts = _rt.summarize(items)
110
+ typer.echo(f"Split preview (non-destructive) for {file.name}:")
111
+ typer.echo(f" archive (done={counts['done']}): {written['archive']}")
112
+ typer.echo(f" active (active+deferred={counts['active'] + counts['deferred']}): {written['active']}")
113
+ typer.echo(" original file was NOT modified.")
114
+
115
+
116
+ @app.command()
117
+ def status() -> None:
118
+ """Report knowledge-graph freshness across subprojects."""
119
+ root = _find_root()
120
+ rows = _reindex.graph_status(root)
121
+ if not rows:
122
+ typer.echo("(no subprojects with output/graphs/ found)")
123
+ return
124
+ typer.echo(f"Graph status @ {root.name} ({datetime.now(timezone.utc):%Y-%m-%dT%H:%MZ})")
125
+ cur = None
126
+ for ls in rows:
127
+ if ls.subproject != cur:
128
+ cur = ls.subproject
129
+ typer.echo(f"\n{ls.subproject}")
130
+ mark = "✗ missing" if not ls.exists else ("⚠ stale" if ls.stale else "✓ fresh")
131
+ when = (datetime.fromtimestamp(ls.mtime, timezone.utc).strftime("%Y-%m-%d %H:%M")
132
+ if ls.mtime else "—")
133
+ typer.echo(f" {ls.layer:10} {mark:10} graph.json {when}")
134
+
135
+
136
+ @app.command()
137
+ def reindex(
138
+ subproject: Optional[str] = typer.Option(None, help="Only this subproject."),
139
+ docs: bool = typer.Option(False, help="Rebuild docs (heavier) instead of code-only."),
140
+ dry_run: bool = typer.Option(False, "--dry-run", help="Show what would run, don't execute."),
141
+ ) -> None:
142
+ """Rebuild per-subproject knowledge graphs (never restarts MCP servers)."""
143
+ root = _find_root()
144
+ results = _reindex.reindex(root, subproject=subproject, code_only=not docs, dry_run=dry_run)
145
+ if not results:
146
+ typer.echo("(nothing to reindex)")
147
+ return
148
+ failed = False
149
+ for r in results:
150
+ head = "would run" if not r.ran else f"rc={r.returncode}"
151
+ typer.echo(f"{r.subproject}: {head} — {' '.join(r.command)}")
152
+ if r.note:
153
+ typer.echo(f" {r.note}")
154
+ if r.restart_required:
155
+ typer.echo(f" restart_required: {', '.join(r.restart_required)} "
156
+ f"(restart via VS Code MCP or ./restart-mcps.sh — never pkill)")
157
+ if r.ran and r.returncode:
158
+ failed = True
159
+ if failed:
160
+ raise typer.Exit(1)
161
+
162
+
163
+ def main() -> None:
164
+ app()
165
+
166
+
167
+ if __name__ == "__main__":
168
+ main()
@@ -0,0 +1,139 @@
1
+ """reindex — discover and rebuild per-subproject knowledge graphs.
2
+
3
+ Part of the harness control plane (Roadmap #24 Phase 1). Orchestrates the
4
+ graph-rebuild ritual that is otherwise a manual per-subproject ``cd`` +
5
+ ``aidiary-graphs`` dance.
6
+
7
+ Hard constraint: this module **never** kills or restarts MCP servers
8
+ (externally killing a stdio MCP server is a recorded anti-pattern — VS Code
9
+ marks it permanently failed). A rebuild that changes ``graph.json`` reports
10
+ ``restart_required`` for the affected layers; the operator (or, later, the
11
+ control MCP) triggers the sanctioned restart via VS Code / ``restart-mcps.sh``.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import subprocess
17
+ from dataclasses import dataclass, field
18
+ from pathlib import Path
19
+
20
+ # Source directory that feeds each graph layer (for staleness comparison).
21
+ _LAYER_SOURCE = {
22
+ "code": ("src",),
23
+ "docs": ("docs",),
24
+ "frontend": ("dashboard/src", "src"),
25
+ "mem": ("memories",),
26
+ }
27
+ _SOURCE_SUFFIXES = {".py", ".ts", ".tsx", ".js", ".jsx", ".md"}
28
+
29
+
30
+ @dataclass
31
+ class Subproject:
32
+ name: str
33
+ path: Path
34
+ venv_python: Path
35
+ graphs_dir: Path
36
+
37
+ @property
38
+ def has_graphs(self) -> bool:
39
+ return self.graphs_dir.is_dir()
40
+
41
+
42
+ @dataclass
43
+ class LayerStatus:
44
+ subproject: str
45
+ layer: str
46
+ graph_json: Path
47
+ exists: bool
48
+ mtime: float | None
49
+ stale: bool
50
+ newest_source: float | None = None
51
+
52
+
53
+ @dataclass
54
+ class ReindexResult:
55
+ subproject: str
56
+ command: list[str]
57
+ ran: bool
58
+ returncode: int | None = None
59
+ restart_required: list[str] = field(default_factory=list)
60
+ note: str = ""
61
+
62
+
63
+ def discover_subprojects(root: Path) -> list[Subproject]:
64
+ """Find subprojects with a venv and an ``output/graphs/`` tree."""
65
+ found: list[Subproject] = []
66
+ for child in sorted(p for p in root.iterdir() if p.is_dir()):
67
+ venv_py = child / ".venv" / "bin" / "python"
68
+ graphs = child / "output" / "graphs"
69
+ if venv_py.exists() and graphs.is_dir():
70
+ found.append(Subproject(child.name, child, venv_py, graphs))
71
+ return found
72
+
73
+
74
+ def _newest_source_mtime(base: Path, layer: str) -> float | None:
75
+ newest: float | None = None
76
+ for rel in _LAYER_SOURCE.get(layer, ()):
77
+ src = base / rel
78
+ if not src.is_dir():
79
+ continue
80
+ for f in src.rglob("*"):
81
+ if f.is_file() and f.suffix in _SOURCE_SUFFIXES:
82
+ m = f.stat().st_mtime
83
+ if newest is None or m > newest:
84
+ newest = m
85
+ return newest
86
+
87
+
88
+ def graph_status(root: Path) -> list[LayerStatus]:
89
+ """Report each (subproject, layer) graph: existence, mtime, staleness."""
90
+ out: list[LayerStatus] = []
91
+ for sp in discover_subprojects(root):
92
+ for layer_dir in sorted(p for p in sp.graphs_dir.iterdir() if p.is_dir()):
93
+ layer = layer_dir.name
94
+ gj = layer_dir / "graph.json"
95
+ # Only report real graph layers (a directory that holds a graph.json).
96
+ # Sibling artifact dirs (analytics/, cache/) and backups are skipped.
97
+ if not gj.exists():
98
+ continue
99
+ mtime = gj.stat().st_mtime
100
+ newest = _newest_source_mtime(sp.path, layer)
101
+ stale = bool(newest is not None and newest > (mtime or 0))
102
+ out.append(LayerStatus(sp.name, layer, gj, True, mtime, stale, newest))
103
+ return out
104
+
105
+
106
+ def reindex(
107
+ root: Path,
108
+ subproject: str | None = None,
109
+ code_only: bool = True,
110
+ dry_run: bool = False,
111
+ ) -> list[ReindexResult]:
112
+ """Rebuild graphs by invoking each subproject's own ``aidiary-graphs``.
113
+
114
+ ``code_only`` runs the instant AST rebuild (no LLM). Docs rebuilds are
115
+ heavier and are left to an explicit ``--docs`` run (not yet wired).
116
+ """
117
+ results: list[ReindexResult] = []
118
+ for sp in discover_subprojects(root):
119
+ if subproject and sp.name != subproject:
120
+ continue
121
+ flag = "--code-only" if code_only else "--build-only"
122
+ cmd = [str(sp.venv_python), "-m", "aidiary.graphs.cli", flag]
123
+ # Layers that would be affected (for restart_required reporting).
124
+ affected = [
125
+ ls.layer for ls in graph_status(root)
126
+ if ls.subproject == sp.name and (not code_only or ls.layer in ("code", "frontend"))
127
+ ]
128
+ if dry_run:
129
+ results.append(ReindexResult(sp.name, cmd, ran=False,
130
+ restart_required=affected,
131
+ note="dry-run — would rebuild"))
132
+ continue
133
+ proc = subprocess.run(cmd, cwd=sp.path, capture_output=True, text=True)
134
+ results.append(ReindexResult(
135
+ sp.name, cmd, ran=True, returncode=proc.returncode,
136
+ restart_required=affected if proc.returncode == 0 else [],
137
+ note=(proc.stderr.strip().splitlines() or [""])[-1] if proc.returncode else "ok",
138
+ ))
139
+ return results
@@ -0,0 +1,225 @@
1
+ """roadmap_tool — parse, identify, index, and split roadmap markdown files.
2
+
3
+ Design (Roadmap #24 Phase 1):
4
+
5
+ - **Markdown stays the source of truth** (git-native, hand-editable, PR-reviewable).
6
+ This module derives a fast search index *from* it — it does not replace it.
7
+ - Each roadmap item gets a **stable UUID** (uuid5 over file + item key + title),
8
+ so re-runs are deterministic and an item keeps its id across edits to its body.
9
+ - Status is classified from each item's **own** signals (✅ / strikethrough /
10
+ "Shipped"/"Completed" / "Deferred") — never from a separate priority-matrix
11
+ marker (the matrix is known to lag real status).
12
+ - The done/active **split is a preview** written to a separate directory; the
13
+ original file is never mutated by this tool (operator reviews first).
14
+
15
+ Search uses SQLite **FTS5** (keyword). Embeddings are intentionally NOT used:
16
+ on a few-hundred-item corpus keyword search is instant, and the workspace
17
+ convention is that TF-IDF/keyword beats embeddings on small technical corpora.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import json
23
+ import re
24
+ import sqlite3
25
+ import uuid
26
+ from dataclasses import dataclass, field, asdict
27
+ from pathlib import Path
28
+
29
+ # Stable namespace so UUIDs are reproducible across runs and machines.
30
+ _NS = uuid.uuid5(uuid.NAMESPACE_URL, "aidos/roadmap")
31
+
32
+ # An item heading is a level-2/3 heading whose text starts with an identifier
33
+ # token containing a digit: ``#24``, ``24.``, ``NS-1.1``, ``GLM-1``, ``UI-15``,
34
+ # ``I1``, ``DOCS-2``. Group 1 = hashes, group 2 = optional strikethrough,
35
+ # group 3 = the item key token.
36
+ _ITEM_RE = re.compile(
37
+ r"^(#{2,3})\s+(~~)?\s*(#?[A-Za-z]{0,5}-?\d[\w.\-]*)\b(.*)$"
38
+ )
39
+
40
+ _DONE_RE = re.compile(
41
+ r"(✅|~~|\bShipped\b|\bSHIPPED\b|\bCompleted\b|\bDone\b|\bDONE\b)"
42
+ )
43
+ _DEFER_RE = re.compile(r"defer", re.IGNORECASE)
44
+
45
+ STATUSES = ("done", "deferred", "active")
46
+
47
+
48
+ @dataclass
49
+ class Item:
50
+ """A single roadmap item parsed from a markdown heading + its body."""
51
+
52
+ uuid: str
53
+ key: str # e.g. "#24", "NS-1.1", "GLM-2", "10"
54
+ title: str
55
+ status: str # one of STATUSES
56
+ level: int # heading level (2 or 3)
57
+ line_start: int # 1-based line of the heading
58
+ line_end: int # 1-based last line of the body (exclusive of next item)
59
+ tags: list[str] = field(default_factory=list)
60
+ body: str = ""
61
+
62
+ def metadata_block(self) -> str:
63
+ """Human-readable + machine-parseable block to stamp under a heading."""
64
+ return (
65
+ f"- **id:** `{self.uuid}`\n"
66
+ f"- **status:** {self.status}\n"
67
+ f"- **tags:** {', '.join(self.tags) or '—'}\n"
68
+ )
69
+
70
+
71
+ def _clean_title(key: str, trailing: str) -> str:
72
+ text = trailing.strip()
73
+ # Strip leading separators after the key (". ", ") ", "— ", "- ").
74
+ text = re.sub(r"^[.)\s—–-]+", "", text)
75
+ text = text.replace("~~", "").replace("✅", "").strip()
76
+ # Collapse a key that was split across the regex (e.g. "#24" + ". Title").
77
+ return text or key
78
+
79
+
80
+ def _classify(heading_line: str, body: str) -> str:
81
+ if _DEFER_RE.search(heading_line):
82
+ return "deferred"
83
+ if _DONE_RE.search(heading_line):
84
+ return "done"
85
+ # Only look at the first ~25 lines of the body for a status declaration,
86
+ # so a passing mention of "shipped" deep in prose does not mislabel.
87
+ head = "\n".join(body.splitlines()[:25])
88
+ if re.search(r"(\*\*✅|✅\s*Shipped|Status:.*?(Shipped|Completed|Done|✅))", head):
89
+ return "done"
90
+ if _DEFER_RE.search(head):
91
+ return "deferred"
92
+ return "active"
93
+
94
+
95
+ def _tags_for(key: str) -> list[str]:
96
+ m = re.match(r"#?([A-Za-z]+)", key)
97
+ return [m.group(1).upper()] if m and m.group(1) else []
98
+
99
+
100
+ def parse_roadmap(path: Path) -> list[Item]:
101
+ """Parse a roadmap markdown file into a list of :class:`Item`."""
102
+ lines = path.read_text(encoding="utf-8").splitlines()
103
+ # Locate every item heading first, then slice bodies between them.
104
+ heads: list[tuple[int, int, str, str, str]] = [] # (idx, level, key, hashes, trailing)
105
+ for i, line in enumerate(lines):
106
+ m = _ITEM_RE.match(line)
107
+ if not m:
108
+ continue
109
+ hashes, _strike, key, trailing = m.groups()
110
+ heads.append((i, len(hashes), key, line, trailing))
111
+
112
+ items: list[Item] = []
113
+ for n, (idx, level, key, heading_line, trailing) in enumerate(heads):
114
+ end = heads[n + 1][0] if n + 1 < len(heads) else len(lines)
115
+ body = "\n".join(lines[idx + 1:end]).strip()
116
+ title = _clean_title(key, trailing)
117
+ status = _classify(heading_line, body)
118
+ item_uuid = str(uuid.uuid5(_NS, f"{path.name}:{key}:{title}"))
119
+ items.append(
120
+ Item(
121
+ uuid=item_uuid,
122
+ key=key.lstrip("#") and key, # keep the literal token (e.g. "#24")
123
+ title=title,
124
+ status=status,
125
+ level=level,
126
+ line_start=idx + 1,
127
+ line_end=end,
128
+ tags=_tags_for(key),
129
+ body=body,
130
+ )
131
+ )
132
+ return items
133
+
134
+
135
+ def build_index(items: list[Item], db_path: Path) -> Path:
136
+ """Write a SQLite FTS5 keyword index + a JSON sidecar. Returns db_path."""
137
+ db_path.parent.mkdir(parents=True, exist_ok=True)
138
+ if db_path.exists():
139
+ db_path.unlink()
140
+ con = sqlite3.connect(db_path)
141
+ try:
142
+ con.execute(
143
+ "CREATE TABLE items (uuid TEXT PRIMARY KEY, key TEXT, title TEXT, "
144
+ "status TEXT, level INT, line_start INT, line_end INT, tags TEXT)"
145
+ )
146
+ con.execute(
147
+ "CREATE VIRTUAL TABLE roadmap_fts USING fts5("
148
+ "uuid UNINDEXED, key, title, status UNINDEXED, tags, body)"
149
+ )
150
+ for it in items:
151
+ con.execute(
152
+ "INSERT INTO items VALUES (?,?,?,?,?,?,?,?)",
153
+ (it.uuid, it.key, it.title, it.status, it.level,
154
+ it.line_start, it.line_end, ",".join(it.tags)),
155
+ )
156
+ con.execute(
157
+ "INSERT INTO roadmap_fts VALUES (?,?,?,?,?,?)",
158
+ (it.uuid, it.key, it.title, it.status, " ".join(it.tags), it.body),
159
+ )
160
+ con.commit()
161
+ finally:
162
+ con.close()
163
+ # JSON sidecar (bodies omitted to keep it small + diff-friendly).
164
+ sidecar = db_path.with_suffix(".json")
165
+ sidecar.write_text(
166
+ json.dumps(
167
+ [{k: v for k, v in asdict(it).items() if k != "body"} for it in items],
168
+ indent=2,
169
+ ),
170
+ encoding="utf-8",
171
+ )
172
+ return db_path
173
+
174
+
175
+ def search_index(db_path: Path, query: str, limit: int = 20) -> list[dict]:
176
+ """Run an FTS5 keyword search; return ranked item rows (no bodies)."""
177
+ con = sqlite3.connect(db_path)
178
+ try:
179
+ con.row_factory = sqlite3.Row
180
+ rows = con.execute(
181
+ "SELECT uuid, key, title, status FROM roadmap_fts "
182
+ "WHERE roadmap_fts MATCH ? ORDER BY rank LIMIT ?",
183
+ (query, limit),
184
+ ).fetchall()
185
+ return [dict(r) for r in rows]
186
+ finally:
187
+ con.close()
188
+
189
+
190
+ def split_preview(path: Path, items: list[Item], out_dir: Path) -> dict[str, Path]:
191
+ """Write NON-DESTRUCTIVE done/active+deferred split previews.
192
+
193
+ The original ``path`` is never modified. Returns the two written paths.
194
+ """
195
+ out_dir.mkdir(parents=True, exist_ok=True)
196
+ stem = path.stem
197
+ archive = out_dir / f"{stem}-archive.md"
198
+ active = out_dir / f"{stem}-active.md"
199
+
200
+ def _render(title: str, chosen: list[Item]) -> str:
201
+ out = [f"# {title}", "", f"> Generated by aidos from {path.name} "
202
+ f"(non-destructive preview). {len(chosen)} items.", ""]
203
+ for it in chosen:
204
+ out.append(f"## {it.key} — {it.title}")
205
+ out.append("")
206
+ out.append(it.metadata_block())
207
+ out.append(it.body)
208
+ out.append("")
209
+ out.append("---")
210
+ out.append("")
211
+ return "\n".join(out)
212
+
213
+ done = [it for it in items if it.status == "done"]
214
+ rest = [it for it in items if it.status != "done"]
215
+ archive.write_text(_render(f"{stem} — Archive (done)", done), encoding="utf-8")
216
+ active.write_text(_render(f"{stem} — Active / Deferred", rest), encoding="utf-8")
217
+ return {"archive": archive, "active": active}
218
+
219
+
220
+ def summarize(items: list[Item]) -> dict[str, int]:
221
+ counts = {s: 0 for s in STATUSES}
222
+ for it in items:
223
+ counts[it.status] = counts.get(it.status, 0) + 1
224
+ counts["total"] = len(items)
225
+ return counts
@@ -0,0 +1,128 @@
1
+ Metadata-Version: 2.4
2
+ Name: aidos
3
+ Version: 0.0.1
4
+ Summary: aidos (AI Diary Operating System) — the control-plane CLI for an agentic harness: roadmap index, knowledge-graph reindex, and module setup.
5
+ Author: Yingding Wang
6
+ License-Expression: MIT
7
+ Keywords: aidos,ai-diary,harness,control-plane,knowledge-graph,roadmap
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Classifier: Topic :: Software Development :: Libraries
15
+ Requires-Python: >=3.12
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: typer>=0.15.1
19
+ Provides-Extra: mcp
20
+ Requires-Dist: mcp>=1.27.1; extra == "mcp"
21
+ Dynamic: license-file
22
+
23
+ # aidos
24
+
25
+ **AI diary OS** — the control-plane CLI for an agentic harness. Index your roadmaps, keep knowledge graphs fresh, and stand up new modules, all from one command.
26
+
27
+ ## What it does
28
+
29
+ `aidos` is the operations layer that sits *above* a multi-module workspace and manages the shared machinery your agent depends on:
30
+
31
+ - **Roadmap index** — parse a roadmap markdown into identified items (stable UUIDs), classify them done / active / deferred, and build a SQLite **FTS5** keyword index for fast search. The markdown stays the source of truth; the index is derived.
32
+ - **Graph reindex** — discover every subproject's knowledge graphs, report freshness, and rebuild them in the correct virtualenv. It never kills your MCP servers — it reports which ones need a restart.
33
+ - **Module setup** — a reproducible recipe (and script) to make a new Python module a first-class citizen: its own venv, its own code + docs graphs, and wiring into the shared portal.
34
+
35
+ It is **CLI-first**: all logic lives behind plain functions, so it is testable and scriptable without a server. A future control **MCP** wraps the same CLI.
36
+
37
+ The name is a nod to [`aidiary`](https://pypi.org/project/aidiary/) (the *AI diary* memory system): `aidos` is the **OS** layer around it.
38
+
39
+ ## Install
40
+
41
+ Requires: Python 3.12+
42
+
43
+ ```bash
44
+ pip install aidos
45
+ ```
46
+
47
+ Building a module's own knowledge graphs additionally uses `aidiary[graphs]` (installed editable in dev):
48
+
49
+ ```bash
50
+ pip install "aidiary[graphs]"
51
+ ```
52
+
53
+ ## Quick start
54
+
55
+ ### 1. Check graph freshness across the workspace
56
+
57
+ ```bash
58
+ aidos status
59
+ ```
60
+
61
+ Lists each subproject's graph layers (code / docs / …) with a ✓ fresh / ⚠ stale / ✗ missing badge based on `graph.json` mtime vs. newest source.
62
+
63
+ ### 2. Index a roadmap and search it
64
+
65
+ ```bash
66
+ aidos roadmap index path/to/ProjectRoadmap.md
67
+ aidos roadmap search "graph reindex" --db output/aidos/ProjectRoadmap.db
68
+ ```
69
+
70
+ `index` assigns a stable UUID to every item and writes a SQLite FTS5 index + a JSON sidecar. `search` is instant keyword retrieval — no embeddings, no LLM.
71
+
72
+ ### 3. Preview a done / active split (non-destructive)
73
+
74
+ ```bash
75
+ aidos roadmap split path/to/ProjectRoadmap.md
76
+ ```
77
+
78
+ Writes `*-archive.md` (done) and `*-active.md` (active + deferred) previews. **The original file is never modified** — you review first.
79
+
80
+ ### 4. Rebuild knowledge graphs
81
+
82
+ ```bash
83
+ aidos reindex --dry-run # show what would rebuild
84
+ aidos reindex --subproject aidos # rebuild one subproject's code graph
85
+ ```
86
+
87
+ Any rebuilt layer is reported as `restart_required` — restart those MCP servers through your editor (or your restart script), never by killing the process.
88
+
89
+ ## Directory layout
90
+
91
+ ```
92
+ my-module/
93
+ ├── pyproject.toml
94
+ ├── config/graphs.toml # graphs.code (src/, ast) + graphs.docs (docs/, semantic)
95
+ ├── src/<pkg>/ # your package
96
+ ├── docs/ # concept docs (feed the docs graph)
97
+ └── output/ # generated artifacts
98
+ ├── aidos/ # roadmap index db + json sidecar
99
+ └── graphs/{code,docs}/ # graph.json + graph.html
100
+ ```
101
+
102
+ ## Command reference
103
+
104
+ | Command | Purpose |
105
+ |---------|---------|
106
+ | `aidos status` | Report knowledge-graph freshness across subprojects |
107
+ | `aidos roadmap index <file>` | Build the SQLite FTS5 search index for a roadmap |
108
+ | `aidos roadmap search <query> --db <db>` | Keyword-search the index |
109
+ | `aidos roadmap split <file>` | Non-destructive done / active split preview |
110
+ | `aidos reindex [--subproject N] [--docs] [--dry-run]` | Rebuild per-subproject graphs |
111
+
112
+ ## Design principles
113
+
114
+ - **Markdown / files stay the source of truth** — every index, db, and graph is a *derived* artifact. Producers and consumers connect via files on disk, not imports.
115
+ - **Keyword over embeddings** — on a few-hundred-item corpus, SQLite FTS5 is instant and sufficient; vector search is deferred until the data demands it.
116
+ - **Never kill a stdio MCP server** — reindex reports `restart_required`; it does not respawn processes.
117
+
118
+ ## What's new
119
+
120
+ ### 0.0.1
121
+
122
+ **First cut — the Developer-Control plane CLI**
123
+
124
+ - **`roadmap`** — `index` (parse → stable UUIDs → done/active/deferred → SQLite FTS5 + JSON sidecar), `search` (FTS5 keyword), `split` (non-destructive done/active preview).
125
+ - **`reindex`** — discover subproject graphs, report freshness, rebuild via each subproject's `aidiary-graphs`; reports `restart_required` (never kills MCP servers).
126
+ - **`status`** — workspace-wide graph freshness.
127
+ - **Reproducible module setup** — idempotent `setup.sh` (venv + editable installs + graphs) and a documented "make a module a first-class harness citizen" guide.
128
+ - **Typer CLI**, stdlib-only core (plus Typer); builds to sdist + wheel.
@@ -0,0 +1,16 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ projectdescription.md
5
+ pyproject.toml
6
+ requirements.txt
7
+ src/aidos/__init__.py
8
+ src/aidos/cli.py
9
+ src/aidos/reindex.py
10
+ src/aidos/roadmap_tool.py
11
+ src/aidos.egg-info/PKG-INFO
12
+ src/aidos.egg-info/SOURCES.txt
13
+ src/aidos.egg-info/dependency_links.txt
14
+ src/aidos.egg-info/entry_points.txt
15
+ src/aidos.egg-info/requires.txt
16
+ src/aidos.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ aidos = aidos.cli:main
@@ -0,0 +1,4 @@
1
+ typer>=0.15.1
2
+
3
+ [mcp]
4
+ mcp>=1.27.1
@@ -0,0 +1 @@
1
+ aidos