polymath-nexus 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- polymath_nexus-0.2.0/LICENSE +21 -0
- polymath_nexus-0.2.0/PKG-INFO +166 -0
- polymath_nexus-0.2.0/README.md +134 -0
- polymath_nexus-0.2.0/nexus/__init__.py +104 -0
- polymath_nexus-0.2.0/nexus/cli.py +223 -0
- polymath_nexus-0.2.0/nexus/config.py +45 -0
- polymath_nexus-0.2.0/nexus/discovery.py +77 -0
- polymath_nexus-0.2.0/nexus/index.py +689 -0
- polymath_nexus-0.2.0/nexus/memory/__init__.py +3 -0
- polymath_nexus-0.2.0/nexus/memory/chunker.py +151 -0
- polymath_nexus-0.2.0/nexus/memory/embedder.py +212 -0
- polymath_nexus-0.2.0/nexus/memory/migrate.py +128 -0
- polymath_nexus-0.2.0/nexus/memory/retriever.py +139 -0
- polymath_nexus-0.2.0/nexus/memory/store.py +779 -0
- polymath_nexus-0.2.0/nexus/memory/sync.py +79 -0
- polymath_nexus-0.2.0/nexus/memory/writer.py +295 -0
- polymath_nexus-0.2.0/nexus/mirrors.py +258 -0
- polymath_nexus-0.2.0/nexus/types.py +82 -0
- polymath_nexus-0.2.0/polymath_nexus.egg-info/PKG-INFO +166 -0
- polymath_nexus-0.2.0/polymath_nexus.egg-info/SOURCES.txt +30 -0
- polymath_nexus-0.2.0/polymath_nexus.egg-info/dependency_links.txt +1 -0
- polymath_nexus-0.2.0/polymath_nexus.egg-info/entry_points.txt +2 -0
- polymath_nexus-0.2.0/polymath_nexus.egg-info/requires.txt +10 -0
- polymath_nexus-0.2.0/polymath_nexus.egg-info/top_level.txt +1 -0
- polymath_nexus-0.2.0/pyproject.toml +68 -0
- polymath_nexus-0.2.0/setup.cfg +4 -0
- polymath_nexus-0.2.0/tests/test_brain_cli.py +197 -0
- polymath_nexus-0.2.0/tests/test_brain_decoupling.py +126 -0
- polymath_nexus-0.2.0/tests/test_longterm_brain.py +481 -0
- polymath_nexus-0.2.0/tests/test_memory_store.py +129 -0
- polymath_nexus-0.2.0/tests/test_mirrors.py +233 -0
- polymath_nexus-0.2.0/tests/test_timezone.py +94 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ayushi Gupta
|
|
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,166 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: polymath-nexus
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Long-term team brain — markdown + JSONL, date-stamped, owner-attributed, append-only. Auto-generates CLAUDE.md / AGENTS.md / .cursor / Copilot mirrors so every AI tool reads it for free.
|
|
5
|
+
Author: Ayushi Gupta
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ayushigupta-29/nexus
|
|
8
|
+
Project-URL: Repository, https://github.com/ayushigupta-29/nexus
|
|
9
|
+
Project-URL: Format spec, https://github.com/ayushigupta-29/nexus/blob/main/SPEC.md
|
|
10
|
+
Keywords: ai,memory,context,team-brain,claude,cursor,codex,copilot
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: httpx>=0.27.0
|
|
24
|
+
Provides-Extra: embed
|
|
25
|
+
Requires-Dist: google-genai>=1.0.0; extra == "embed"
|
|
26
|
+
Requires-Dist: openai>=1.50.0; extra == "embed"
|
|
27
|
+
Requires-Dist: sqlite-vec>=0.1.6; extra == "embed"
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# nexus — long-term team brain
|
|
34
|
+
|
|
35
|
+
> *nexus* (Greek: νοῦς) — mind, intellect, intuition.
|
|
36
|
+
> The faculty that sees things as they are.
|
|
37
|
+
|
|
38
|
+
`nexus` is a long-lived, team-shareable project memory. It is:
|
|
39
|
+
|
|
40
|
+
- **Just markdown files in a folder.** No DB to run, no server to host.
|
|
41
|
+
- **Date-stamped and owner-attributed.** Every entry carries
|
|
42
|
+
`[YYYY-MM-DD owner]`. Grep works forever.
|
|
43
|
+
- **Append-only.** Nothing is ever auto-deleted. Old engineering rules
|
|
44
|
+
are load-bearing, not stale.
|
|
45
|
+
- **Tool-agnostic.** Auto-generates `CLAUDE.md`, `AGENTS.md`,
|
|
46
|
+
`.cursor/rules/brain.mdc`, `.github/copilot-instructions.md` —
|
|
47
|
+
every AI tool you use reads the brain through its native discovery,
|
|
48
|
+
no extra config.
|
|
49
|
+
- **Forward-compatible.** Schema versioned. A `nexus` from 2046 will
|
|
50
|
+
still read a brain written in 2026, and vice versa.
|
|
51
|
+
|
|
52
|
+
## Why
|
|
53
|
+
|
|
54
|
+
Teams accumulate knowledge faster than they document it. The brain is
|
|
55
|
+
where that knowledge goes to live for decades — surviving turnover,
|
|
56
|
+
tool churn, and reorgs.
|
|
57
|
+
|
|
58
|
+
When someone leaves: their `brain/rules/<them>.md` stays. Their
|
|
59
|
+
decisions, their patterns, their gotchas — all dated, all attributed.
|
|
60
|
+
The next person reads `brain/` on day one and inherits the team's
|
|
61
|
+
mental model.
|
|
62
|
+
|
|
63
|
+
## Quickstart
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install nexus
|
|
67
|
+
cd ~/my-repo
|
|
68
|
+
nexus init # creates brain/ + AI tool mirrors
|
|
69
|
+
nexus learn-from "always use httpx, never requests — async-first codebase"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
That's it. Open Claude Code / Cursor / Codex / Copilot in this repo —
|
|
73
|
+
all four now read this rule automatically via their native discovery
|
|
74
|
+
files.
|
|
75
|
+
|
|
76
|
+
## Folder structure
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
brain/
|
|
80
|
+
├── README.md intro for humans
|
|
81
|
+
├── rules/<owner>.md hard constraints, never break these
|
|
82
|
+
├── logic/<owner>.md business/domain logic
|
|
83
|
+
├── code/<owner>.md patterns, conventions, architecture
|
|
84
|
+
├── stack/<owner>.md tech stack, versions
|
|
85
|
+
├── data/<owner>.md schemas, field definitions
|
|
86
|
+
├── goals/<owner>.md project objectives, scope
|
|
87
|
+
├── decisions/<owner>.md ADR-style: chosen approach + why
|
|
88
|
+
├── glossary/<owner>.md domain term definitions
|
|
89
|
+
└── personas/<owner>.md tone, behavior, communication style
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Per-owner sub-files mean two teammates writing simultaneously **never
|
|
93
|
+
conflict at the file level**. You can also use single-file mode
|
|
94
|
+
(`brain/rules.md`); the reader handles both.
|
|
95
|
+
|
|
96
|
+
## CLI
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
nexus init # create brain/ in cwd
|
|
100
|
+
nexus learn-from "<rule>" # append to rules/<you>.md, dated
|
|
101
|
+
nexus show [bucket] # grep-first search
|
|
102
|
+
--since 2026-01-01
|
|
103
|
+
--until 2026-05-31
|
|
104
|
+
--by ayushi
|
|
105
|
+
-q pattern
|
|
106
|
+
--json
|
|
107
|
+
nexus usage # counts per bucket + owner
|
|
108
|
+
nexus own # print your owner slug
|
|
109
|
+
nexus sync # regenerate AI-tool mirrors
|
|
110
|
+
nexus unmirror # remove mirrors (opt-out)
|
|
111
|
+
nexus spec # print SPEC.md
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Auto-generated mirrors
|
|
115
|
+
|
|
116
|
+
After every `nexus` write, four files at repo root are regenerated:
|
|
117
|
+
|
|
118
|
+
| Mirror | Tool | Auto-discovered |
|
|
119
|
+
|---|---|---|
|
|
120
|
+
| `CLAUDE.md` | Claude Code | ✓ |
|
|
121
|
+
| `AGENTS.md` | Codex CLI | ✓ |
|
|
122
|
+
| `.cursor/rules/brain.mdc` | Cursor | ✓ |
|
|
123
|
+
| `.github/copilot-instructions.md` | GitHub Copilot | ✓ |
|
|
124
|
+
|
|
125
|
+
Each carries `<!-- GENERATED — DO NOT EDIT -->`. Edit `brain/`; mirrors
|
|
126
|
+
follow. To opt out: `nexus unmirror`.
|
|
127
|
+
|
|
128
|
+
## Per-team use cases
|
|
129
|
+
|
|
130
|
+
**Engineering** — Drop your rules in `brain/rules/<you>.md`. Cursor /
|
|
131
|
+
Claude Code / Codex see them all automatically.
|
|
132
|
+
|
|
133
|
+
**Data** — `brain/glossary/<you>.md` is the canonical home for metric
|
|
134
|
+
definitions. "What does 'active user' mean?" gets answered once,
|
|
135
|
+
forever.
|
|
136
|
+
|
|
137
|
+
**Analytics** — `brain/decisions/<you>.md` for revenue recognition,
|
|
138
|
+
attribution choices. Date stamps make the rationale auditable.
|
|
139
|
+
|
|
140
|
+
**Business / PMs** — Edit any `.md` via GitHub web UI; commits become
|
|
141
|
+
PRs through your normal review flow. No CLI required.
|
|
142
|
+
|
|
143
|
+
## Long-term contracts
|
|
144
|
+
|
|
145
|
+
- **No auto-delete.** Ever. The brain only grows. Old rules are
|
|
146
|
+
load-bearing.
|
|
147
|
+
- **Append-only history.** When a rule changes, write a new chunk that
|
|
148
|
+
`supersedes` the old one; the old one stays on disk forever.
|
|
149
|
+
- **Forward-compat.** Unknown fields in chunk JSONL are dropped on
|
|
150
|
+
read, not crashed. Your 2026 install reads 2046 brains and vice
|
|
151
|
+
versa.
|
|
152
|
+
- **No required services.** A brain is markdown files. Anything that
|
|
153
|
+
reads files reads the brain.
|
|
154
|
+
|
|
155
|
+
Full format contract: see [`SPEC.md`](./SPEC.md).
|
|
156
|
+
|
|
157
|
+
## License
|
|
158
|
+
|
|
159
|
+
MIT.
|
|
160
|
+
|
|
161
|
+
## Related
|
|
162
|
+
|
|
163
|
+
Built originally as the substrate layer of
|
|
164
|
+
[polymath](https://github.com/ayushigupta-29/polymath), a multi-model
|
|
165
|
+
AI orchestrator. Extracted into its own repo so any AI tool, not just
|
|
166
|
+
Polymath, can use the format.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# nexus — long-term team brain
|
|
2
|
+
|
|
3
|
+
> *nexus* (Greek: νοῦς) — mind, intellect, intuition.
|
|
4
|
+
> The faculty that sees things as they are.
|
|
5
|
+
|
|
6
|
+
`nexus` is a long-lived, team-shareable project memory. It is:
|
|
7
|
+
|
|
8
|
+
- **Just markdown files in a folder.** No DB to run, no server to host.
|
|
9
|
+
- **Date-stamped and owner-attributed.** Every entry carries
|
|
10
|
+
`[YYYY-MM-DD owner]`. Grep works forever.
|
|
11
|
+
- **Append-only.** Nothing is ever auto-deleted. Old engineering rules
|
|
12
|
+
are load-bearing, not stale.
|
|
13
|
+
- **Tool-agnostic.** Auto-generates `CLAUDE.md`, `AGENTS.md`,
|
|
14
|
+
`.cursor/rules/brain.mdc`, `.github/copilot-instructions.md` —
|
|
15
|
+
every AI tool you use reads the brain through its native discovery,
|
|
16
|
+
no extra config.
|
|
17
|
+
- **Forward-compatible.** Schema versioned. A `nexus` from 2046 will
|
|
18
|
+
still read a brain written in 2026, and vice versa.
|
|
19
|
+
|
|
20
|
+
## Why
|
|
21
|
+
|
|
22
|
+
Teams accumulate knowledge faster than they document it. The brain is
|
|
23
|
+
where that knowledge goes to live for decades — surviving turnover,
|
|
24
|
+
tool churn, and reorgs.
|
|
25
|
+
|
|
26
|
+
When someone leaves: their `brain/rules/<them>.md` stays. Their
|
|
27
|
+
decisions, their patterns, their gotchas — all dated, all attributed.
|
|
28
|
+
The next person reads `brain/` on day one and inherits the team's
|
|
29
|
+
mental model.
|
|
30
|
+
|
|
31
|
+
## Quickstart
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install nexus
|
|
35
|
+
cd ~/my-repo
|
|
36
|
+
nexus init # creates brain/ + AI tool mirrors
|
|
37
|
+
nexus learn-from "always use httpx, never requests — async-first codebase"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
That's it. Open Claude Code / Cursor / Codex / Copilot in this repo —
|
|
41
|
+
all four now read this rule automatically via their native discovery
|
|
42
|
+
files.
|
|
43
|
+
|
|
44
|
+
## Folder structure
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
brain/
|
|
48
|
+
├── README.md intro for humans
|
|
49
|
+
├── rules/<owner>.md hard constraints, never break these
|
|
50
|
+
├── logic/<owner>.md business/domain logic
|
|
51
|
+
├── code/<owner>.md patterns, conventions, architecture
|
|
52
|
+
├── stack/<owner>.md tech stack, versions
|
|
53
|
+
├── data/<owner>.md schemas, field definitions
|
|
54
|
+
├── goals/<owner>.md project objectives, scope
|
|
55
|
+
├── decisions/<owner>.md ADR-style: chosen approach + why
|
|
56
|
+
├── glossary/<owner>.md domain term definitions
|
|
57
|
+
└── personas/<owner>.md tone, behavior, communication style
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Per-owner sub-files mean two teammates writing simultaneously **never
|
|
61
|
+
conflict at the file level**. You can also use single-file mode
|
|
62
|
+
(`brain/rules.md`); the reader handles both.
|
|
63
|
+
|
|
64
|
+
## CLI
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
nexus init # create brain/ in cwd
|
|
68
|
+
nexus learn-from "<rule>" # append to rules/<you>.md, dated
|
|
69
|
+
nexus show [bucket] # grep-first search
|
|
70
|
+
--since 2026-01-01
|
|
71
|
+
--until 2026-05-31
|
|
72
|
+
--by ayushi
|
|
73
|
+
-q pattern
|
|
74
|
+
--json
|
|
75
|
+
nexus usage # counts per bucket + owner
|
|
76
|
+
nexus own # print your owner slug
|
|
77
|
+
nexus sync # regenerate AI-tool mirrors
|
|
78
|
+
nexus unmirror # remove mirrors (opt-out)
|
|
79
|
+
nexus spec # print SPEC.md
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Auto-generated mirrors
|
|
83
|
+
|
|
84
|
+
After every `nexus` write, four files at repo root are regenerated:
|
|
85
|
+
|
|
86
|
+
| Mirror | Tool | Auto-discovered |
|
|
87
|
+
|---|---|---|
|
|
88
|
+
| `CLAUDE.md` | Claude Code | ✓ |
|
|
89
|
+
| `AGENTS.md` | Codex CLI | ✓ |
|
|
90
|
+
| `.cursor/rules/brain.mdc` | Cursor | ✓ |
|
|
91
|
+
| `.github/copilot-instructions.md` | GitHub Copilot | ✓ |
|
|
92
|
+
|
|
93
|
+
Each carries `<!-- GENERATED — DO NOT EDIT -->`. Edit `brain/`; mirrors
|
|
94
|
+
follow. To opt out: `nexus unmirror`.
|
|
95
|
+
|
|
96
|
+
## Per-team use cases
|
|
97
|
+
|
|
98
|
+
**Engineering** — Drop your rules in `brain/rules/<you>.md`. Cursor /
|
|
99
|
+
Claude Code / Codex see them all automatically.
|
|
100
|
+
|
|
101
|
+
**Data** — `brain/glossary/<you>.md` is the canonical home for metric
|
|
102
|
+
definitions. "What does 'active user' mean?" gets answered once,
|
|
103
|
+
forever.
|
|
104
|
+
|
|
105
|
+
**Analytics** — `brain/decisions/<you>.md` for revenue recognition,
|
|
106
|
+
attribution choices. Date stamps make the rationale auditable.
|
|
107
|
+
|
|
108
|
+
**Business / PMs** — Edit any `.md` via GitHub web UI; commits become
|
|
109
|
+
PRs through your normal review flow. No CLI required.
|
|
110
|
+
|
|
111
|
+
## Long-term contracts
|
|
112
|
+
|
|
113
|
+
- **No auto-delete.** Ever. The brain only grows. Old rules are
|
|
114
|
+
load-bearing.
|
|
115
|
+
- **Append-only history.** When a rule changes, write a new chunk that
|
|
116
|
+
`supersedes` the old one; the old one stays on disk forever.
|
|
117
|
+
- **Forward-compat.** Unknown fields in chunk JSONL are dropped on
|
|
118
|
+
read, not crashed. Your 2026 install reads 2046 brains and vice
|
|
119
|
+
versa.
|
|
120
|
+
- **No required services.** A brain is markdown files. Anything that
|
|
121
|
+
reads files reads the brain.
|
|
122
|
+
|
|
123
|
+
Full format contract: see [`SPEC.md`](./SPEC.md).
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
MIT.
|
|
128
|
+
|
|
129
|
+
## Related
|
|
130
|
+
|
|
131
|
+
Built originally as the substrate layer of
|
|
132
|
+
[polymath](https://github.com/ayushigupta-29/polymath), a multi-model
|
|
133
|
+
AI orchestrator. Extracted into its own repo so any AI tool, not just
|
|
134
|
+
Polymath, can use the format.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Brain — long-term project memory.
|
|
3
|
+
|
|
4
|
+
This is the public API surface of the brain. Anything not exported here is
|
|
5
|
+
internal to brain and may change without notice. The surface is intentionally
|
|
6
|
+
small — the brain's value is in the *file format* (markdown + JSONL + date
|
|
7
|
+
stamps + owner attribution), not in any specific Python API.
|
|
8
|
+
|
|
9
|
+
Long-term contract: nothing inside `nexus.*` imports anything from
|
|
10
|
+
`polymath.*` outside the brain. Enforced by tests/test_brain_decoupling.py.
|
|
11
|
+
|
|
12
|
+
This boundary is what makes the brain extractable into its own package
|
|
13
|
+
later — when that happens, this exact `__init__.py` becomes the top-level
|
|
14
|
+
of the new `brain` distribution.
|
|
15
|
+
|
|
16
|
+
────────────────────────────────────────────────────────────────────────
|
|
17
|
+
Quick reference
|
|
18
|
+
|
|
19
|
+
# Discovery
|
|
20
|
+
from nexus import find_brain_root, find_repo_root, current_owner_slug
|
|
21
|
+
|
|
22
|
+
# Init
|
|
23
|
+
from nexus import init_brain_root
|
|
24
|
+
|
|
25
|
+
# Reading
|
|
26
|
+
from nexus import read_context, build_context_injection
|
|
27
|
+
from nexus import brain_grep, brain_usage
|
|
28
|
+
|
|
29
|
+
# Writing
|
|
30
|
+
from nexus import append_context, write_context
|
|
31
|
+
|
|
32
|
+
# Types
|
|
33
|
+
from nexus import TaskType, CONTEXT_FILES, AuthExpiredError
|
|
34
|
+
|
|
35
|
+
# Memory (chunked, embedded — optional, opt-in)
|
|
36
|
+
from nexus import Chunk, MemoryStore, harvest_session
|
|
37
|
+
"""
|
|
38
|
+
from __future__ import annotations
|
|
39
|
+
|
|
40
|
+
# ── Discovery ────────────────────────────────────────────────────────────────
|
|
41
|
+
from nexus.discovery import (
|
|
42
|
+
current_owner_slug,
|
|
43
|
+
find_brain_root,
|
|
44
|
+
find_repo_root,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# ── Types (canonical home) ───────────────────────────────────────────────────
|
|
48
|
+
from nexus.types import (
|
|
49
|
+
ALWAYS_INJECT,
|
|
50
|
+
CONTEXT_DESCRIPTIONS,
|
|
51
|
+
CONTEXT_FILES,
|
|
52
|
+
TASK_CONTEXT_MAP,
|
|
53
|
+
AuthExpiredError,
|
|
54
|
+
TaskType,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# ── Config ───────────────────────────────────────────────────────────────────
|
|
58
|
+
from nexus.config import (
|
|
59
|
+
BRAIN_CACHE_DIR,
|
|
60
|
+
BRAIN_DIR_NAME,
|
|
61
|
+
BRAIN_HOME,
|
|
62
|
+
BRAIN_PROJECTS_DIR,
|
|
63
|
+
MIRROR_FILES,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# ── Index (read/write) ───────────────────────────────────────────────────────
|
|
67
|
+
from nexus.index import (
|
|
68
|
+
append_context,
|
|
69
|
+
brain_grep,
|
|
70
|
+
brain_usage,
|
|
71
|
+
build_context_injection,
|
|
72
|
+
context_dir,
|
|
73
|
+
create_project,
|
|
74
|
+
init_brain_root,
|
|
75
|
+
list_context_files,
|
|
76
|
+
list_projects,
|
|
77
|
+
read_context,
|
|
78
|
+
write_context,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# ── Memory (chunked store, optional) ─────────────────────────────────────────
|
|
82
|
+
from nexus.memory.store import Chunk, MemoryStore
|
|
83
|
+
from nexus.memory.writer import (
|
|
84
|
+
HarvestResult,
|
|
85
|
+
harvest_session,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
__all__ = [
|
|
89
|
+
# Discovery
|
|
90
|
+
"current_owner_slug", "find_brain_root", "find_repo_root",
|
|
91
|
+
# Types
|
|
92
|
+
"TaskType", "AuthExpiredError",
|
|
93
|
+
"CONTEXT_FILES", "CONTEXT_DESCRIPTIONS", "TASK_CONTEXT_MAP", "ALWAYS_INJECT",
|
|
94
|
+
# Config
|
|
95
|
+
"BRAIN_HOME", "BRAIN_PROJECTS_DIR", "BRAIN_CACHE_DIR", "BRAIN_DIR_NAME",
|
|
96
|
+
"MIRROR_FILES",
|
|
97
|
+
# Index
|
|
98
|
+
"init_brain_root", "create_project", "list_projects",
|
|
99
|
+
"context_dir", "list_context_files",
|
|
100
|
+
"read_context", "write_context", "append_context",
|
|
101
|
+
"build_context_injection", "brain_grep", "brain_usage",
|
|
102
|
+
# Memory
|
|
103
|
+
"Chunk", "MemoryStore", "HarvestResult", "harvest_session",
|
|
104
|
+
]
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"""
|
|
2
|
+
`nexus` — standalone CLI for the long-term brain.
|
|
3
|
+
|
|
4
|
+
No LLM dependencies. Pure stdlib + nexus. Works with `pip install nexus`
|
|
5
|
+
(brain-only) or as a dependency of polymath.
|
|
6
|
+
|
|
7
|
+
Subcommands:
|
|
8
|
+
nexus init Create brain/ in cwd
|
|
9
|
+
nexus show [bucket] [--since DATE] [--until DATE] [--by OWNER] [-q PATTERN]
|
|
10
|
+
Search the brain. Grep-first.
|
|
11
|
+
nexus usage Counts per bucket + per owner
|
|
12
|
+
nexus own Print which slug you'll write as
|
|
13
|
+
nexus learn-from "<correction>" Append to rules/<you>.md (deliberate)
|
|
14
|
+
nexus sync Regenerate the AI-tool mirror files
|
|
15
|
+
nexus unmirror Remove the AI-tool mirror files
|
|
16
|
+
nexus spec Print the SPEC.md format contract
|
|
17
|
+
"""
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import argparse
|
|
21
|
+
import json
|
|
22
|
+
import sys
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
from nexus.discovery import current_owner_slug, find_brain_root, find_repo_root
|
|
26
|
+
from nexus.index import (
|
|
27
|
+
append_context, brain_grep, brain_usage, context_dir, create_project,
|
|
28
|
+
init_brain_root,
|
|
29
|
+
)
|
|
30
|
+
from nexus.mirrors import regenerate_mirrors, remove_mirrors
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _cmd_init(args) -> int:
|
|
34
|
+
base = init_brain_root(Path(args.cwd) if args.cwd else None)
|
|
35
|
+
repo = find_repo_root() or base.parent
|
|
36
|
+
report = regenerate_mirrors(repo_root=repo, force=getattr(args, "force", False))
|
|
37
|
+
print(f"✓ Brain initialised at {base}")
|
|
38
|
+
if report.written:
|
|
39
|
+
for tool, path in report.written.items():
|
|
40
|
+
print(f" ↳ mirrored to {path.relative_to(repo)} (for {tool})")
|
|
41
|
+
elif not report.skipped:
|
|
42
|
+
print(" (brain is empty; mirrors will be generated after first write)")
|
|
43
|
+
_print_skipped(report, repo)
|
|
44
|
+
print(f"\nYour owner slug: {current_owner_slug()}")
|
|
45
|
+
print("\nNext: `nexus learn-from \"your first rule\"`")
|
|
46
|
+
return 0
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _cmd_show(args) -> int:
|
|
50
|
+
project = args.project or ""
|
|
51
|
+
hits = brain_grep(
|
|
52
|
+
project,
|
|
53
|
+
pattern=args.pattern or "",
|
|
54
|
+
since=args.since or "",
|
|
55
|
+
until=args.until or "",
|
|
56
|
+
by=args.by or "",
|
|
57
|
+
ctx_name=args.bucket or "",
|
|
58
|
+
)
|
|
59
|
+
if not hits:
|
|
60
|
+
print("(no matches)")
|
|
61
|
+
return 0
|
|
62
|
+
if args.json:
|
|
63
|
+
print(json.dumps(hits, indent=2))
|
|
64
|
+
return 0
|
|
65
|
+
for h in hits:
|
|
66
|
+
print(f"{h['file']:40s} {h['text']}")
|
|
67
|
+
return 0
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _cmd_usage(args) -> int:
|
|
71
|
+
project = args.project or ""
|
|
72
|
+
stats = brain_usage(project)
|
|
73
|
+
if args.json:
|
|
74
|
+
print(json.dumps(stats, indent=2))
|
|
75
|
+
return 0
|
|
76
|
+
print(f"Total entries: {stats['total_entries']}\n")
|
|
77
|
+
if stats["buckets"]:
|
|
78
|
+
print("By bucket:")
|
|
79
|
+
for k, v in sorted(stats["buckets"].items(), key=lambda kv: -kv[1]):
|
|
80
|
+
print(f" {k:12s} {v}")
|
|
81
|
+
if stats["owners"]:
|
|
82
|
+
print("\nBy owner:")
|
|
83
|
+
for k, v in sorted(stats["owners"].items(), key=lambda kv: -kv[1]):
|
|
84
|
+
print(f" {k:12s} {v}")
|
|
85
|
+
return 0
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _cmd_own(args) -> int:
|
|
89
|
+
"""Print owner slug and the timezone we'll record on writes."""
|
|
90
|
+
from nexus.memory.store import _local_tz_name
|
|
91
|
+
print(f"owner: {current_owner_slug()}")
|
|
92
|
+
print(f"tz: {_local_tz_name()}")
|
|
93
|
+
return 0
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _cmd_learn_from(args) -> int:
|
|
97
|
+
text = args.text or ""
|
|
98
|
+
if not text:
|
|
99
|
+
print("usage: nexus learn-from \"<rule>\"", file=sys.stderr)
|
|
100
|
+
return 2
|
|
101
|
+
project = args.project or ""
|
|
102
|
+
brain = find_brain_root()
|
|
103
|
+
if brain is None and not project:
|
|
104
|
+
# Need either a brain/ in cwd or a project name for personal scope.
|
|
105
|
+
print("error: no brain/ found in this repo. Run `nexus init` first,\n"
|
|
106
|
+
"or use --project <name> for personal-scope brains.", file=sys.stderr)
|
|
107
|
+
return 1
|
|
108
|
+
if not brain and project:
|
|
109
|
+
create_project(project)
|
|
110
|
+
owner = current_owner_slug()
|
|
111
|
+
written = append_context(project, "rules", text, by_owner=True, author=owner)
|
|
112
|
+
print(f"✓ Logged to {written.relative_to(written.parents[2]) if len(written.parents) >= 3 else written}")
|
|
113
|
+
_dt = __import__("datetime").datetime
|
|
114
|
+
today = _dt.now().astimezone().strftime("%Y-%m-%d")
|
|
115
|
+
print(f" - [{today} {owner}] {text}")
|
|
116
|
+
return 0
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _print_skipped(report, repo: Path) -> None:
|
|
120
|
+
"""Tell the user about mirror files we refused to overwrite. Silence
|
|
121
|
+
here would look like the mirror simply worked."""
|
|
122
|
+
for tool, path in report.backed_up.items():
|
|
123
|
+
print(f" ↳ saved your old {tool} file to {path.relative_to(repo)}")
|
|
124
|
+
if not report.skipped:
|
|
125
|
+
return
|
|
126
|
+
for tool, path in report.skipped.items():
|
|
127
|
+
print(f" skipped {path.relative_to(repo)} — you wrote this, not nexus")
|
|
128
|
+
print(" Move its content into brain/, or run with --force to hand it "
|
|
129
|
+
"over (your version is saved as a .bak first).")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _cmd_sync(args) -> int:
|
|
133
|
+
repo = find_repo_root() or (find_brain_root() or Path.cwd()).parent
|
|
134
|
+
report = regenerate_mirrors(args.project or "", repo_root=repo, force=args.force)
|
|
135
|
+
if not report.written and not report.skipped:
|
|
136
|
+
print("(brain is empty; nothing to mirror)")
|
|
137
|
+
return 0
|
|
138
|
+
for tool, path in report.written.items():
|
|
139
|
+
print(f"✓ {tool}: {path.relative_to(repo)}")
|
|
140
|
+
_print_skipped(report, repo)
|
|
141
|
+
return 0
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _cmd_unmirror(args) -> int:
|
|
145
|
+
report = remove_mirrors(force=args.force)
|
|
146
|
+
for p in report.removed:
|
|
147
|
+
print(f"removed {p}")
|
|
148
|
+
for p in report.skipped:
|
|
149
|
+
print(f"kept {p} — you wrote this, not nexus")
|
|
150
|
+
if report.skipped:
|
|
151
|
+
print("Pass --force to delete it anyway.")
|
|
152
|
+
if not report.removed and not report.skipped:
|
|
153
|
+
print("(no mirror files present)")
|
|
154
|
+
return 0
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _cmd_spec(args) -> int:
|
|
158
|
+
spec = Path(__file__).resolve().parent / "SPEC.md"
|
|
159
|
+
if spec.exists():
|
|
160
|
+
print(spec.read_text())
|
|
161
|
+
return 0
|
|
162
|
+
print(f"SPEC.md not found at {spec}", file=sys.stderr)
|
|
163
|
+
return 1
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
167
|
+
p = argparse.ArgumentParser(
|
|
168
|
+
prog="nexus",
|
|
169
|
+
description="Long-term, team-shareable project brain.",
|
|
170
|
+
)
|
|
171
|
+
p.add_argument("--project", default="", help="project name (personal scope)")
|
|
172
|
+
sub = p.add_subparsers(dest="cmd", required=True)
|
|
173
|
+
|
|
174
|
+
p_init = sub.add_parser("init", help="create brain/ in cwd")
|
|
175
|
+
p_init.add_argument("--cwd", default="", help="target directory (default: cwd)")
|
|
176
|
+
p_init.add_argument("--force", action="store_true",
|
|
177
|
+
help="take over mirror files you wrote by hand (saves a .bak)")
|
|
178
|
+
p_init.set_defaults(func=_cmd_init)
|
|
179
|
+
|
|
180
|
+
p_show = sub.add_parser("show", help="search the brain (grep-first)")
|
|
181
|
+
p_show.add_argument("bucket", nargs="?", default="", help="one of: rules logic code stack data goals decisions glossary personas")
|
|
182
|
+
p_show.add_argument("--since", default="", help="YYYY-MM-DD lower bound")
|
|
183
|
+
p_show.add_argument("--until", default="", help="YYYY-MM-DD upper bound")
|
|
184
|
+
p_show.add_argument("--by", default="", help="owner slug")
|
|
185
|
+
p_show.add_argument("-q", "--pattern", default="", help="substring filter (case-insensitive)")
|
|
186
|
+
p_show.add_argument("--json", action="store_true", help="emit JSON")
|
|
187
|
+
p_show.set_defaults(func=_cmd_show)
|
|
188
|
+
|
|
189
|
+
p_usage = sub.add_parser("usage", help="counts per bucket and per owner (read-only)")
|
|
190
|
+
p_usage.add_argument("--json", action="store_true")
|
|
191
|
+
p_usage.set_defaults(func=_cmd_usage)
|
|
192
|
+
|
|
193
|
+
p_own = sub.add_parser("own", help="print which owner slug you'll write as")
|
|
194
|
+
p_own.set_defaults(func=_cmd_own)
|
|
195
|
+
|
|
196
|
+
p_learn = sub.add_parser("learn-from", help="log a correction to rules/<you>.md")
|
|
197
|
+
p_learn.add_argument("text", nargs="?", default="", help="the rule to log")
|
|
198
|
+
p_learn.set_defaults(func=_cmd_learn_from)
|
|
199
|
+
|
|
200
|
+
p_sync = sub.add_parser("sync", help="regenerate AI-tool mirror files")
|
|
201
|
+
p_sync.add_argument("--force", action="store_true",
|
|
202
|
+
help="take over mirror files you wrote by hand (saves a .bak)")
|
|
203
|
+
p_sync.set_defaults(func=_cmd_sync)
|
|
204
|
+
|
|
205
|
+
p_unmirror = sub.add_parser("unmirror", help="remove AI-tool mirror files")
|
|
206
|
+
p_unmirror.add_argument("--force", action="store_true",
|
|
207
|
+
help="also delete mirror files nexus did not write")
|
|
208
|
+
p_unmirror.set_defaults(func=_cmd_unmirror)
|
|
209
|
+
|
|
210
|
+
p_spec = sub.add_parser("spec", help="print the brain SPEC.md")
|
|
211
|
+
p_spec.set_defaults(func=_cmd_spec)
|
|
212
|
+
|
|
213
|
+
return p
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def main(argv: list[str] | None = None) -> int:
|
|
217
|
+
parser = build_parser()
|
|
218
|
+
args = parser.parse_args(argv)
|
|
219
|
+
return args.func(args)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
if __name__ == "__main__":
|
|
223
|
+
sys.exit(main())
|