braingent 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- braingent-0.1.0/.gitignore +14 -0
- braingent-0.1.0/LICENSE +21 -0
- braingent-0.1.0/PKG-INFO +125 -0
- braingent-0.1.0/README.md +89 -0
- braingent-0.1.0/pyproject.toml +99 -0
- braingent-0.1.0/release/denylist.yml +22 -0
- braingent-0.1.0/scripts/release-scan.py +218 -0
- braingent-0.1.0/src/braingent/__init__.py +5 -0
- braingent-0.1.0/src/braingent/__main__.py +6 -0
- braingent-0.1.0/src/braingent/cli.py +7 -0
- braingent-0.1.0/src/braingent/compressors/__init__.py +31 -0
- braingent-0.1.0/src/braingent/compressors/base.py +12 -0
- braingent-0.1.0/src/braingent/compressors/dedupe.py +23 -0
- braingent-0.1.0/src/braingent/compressors/filters.py +42 -0
- braingent-0.1.0/src/braingent/compressors/group.py +29 -0
- braingent-0.1.0/src/braingent/compressors/truncate.py +29 -0
- braingent-0.1.0/src/braingent/core.py +2759 -0
- braingent-0.1.0/src/braingent/mcp_server.py +57 -0
- braingent-0.1.0/src/braingent/mcp_tools.py +234 -0
- braingent-0.1.0/src/braingent/py.typed +1 -0
- braingent-0.1.0/src/braingent/qa/__init__.py +0 -0
- braingent-0.1.0/src/braingent/qa/prompts/black-box.md +23 -0
- braingent-0.1.0/src/braingent/qa/prompts/critic.md +23 -0
- braingent-0.1.0/src/braingent/qa/prompts/merge.md +19 -0
- braingent-0.1.0/src/braingent/qa/prompts/white-box.md +23 -0
- braingent-0.1.0/src/braingent/qa/qa-evidence.schema.json +139 -0
- braingent-0.1.0/src/braingent/qa/qa_evidence.py +489 -0
- braingent-0.1.0/src/braingent/qa/templates/test-plan.md +97 -0
- braingent-0.1.0/src/braingent/qa/test_plan.py +1428 -0
- braingent-0.1.0/src/braingent/templates/starter/.python-version +1 -0
- braingent-0.1.0/src/braingent/templates/starter/AGENTS.md +118 -0
- braingent-0.1.0/src/braingent/templates/starter/CHATGPT_PROJECT_BRIEF.md +57 -0
- braingent-0.1.0/src/braingent/templates/starter/CLAUDE.md +109 -0
- braingent-0.1.0/src/braingent/templates/starter/CURRENT_STATE.md +43 -0
- braingent-0.1.0/src/braingent/templates/starter/FILE-TREE.md +115 -0
- braingent-0.1.0/src/braingent/templates/starter/INDEX.md +93 -0
- braingent-0.1.0/src/braingent/templates/starter/README.md +89 -0
- braingent-0.1.0/src/braingent/templates/starter/imports/README.md +19 -0
- braingent-0.1.0/src/braingent/templates/starter/imports/raw/README.md +14 -0
- braingent-0.1.0/src/braingent/templates/starter/imports/summaries/README.md +8 -0
- braingent-0.1.0/src/braingent/templates/starter/inbox/README.md +11 -0
- braingent-0.1.0/src/braingent/templates/starter/indexes/README.md +22 -0
- braingent-0.1.0/src/braingent/templates/starter/orgs/org--example/README.md +33 -0
- braingent-0.1.0/src/braingent/templates/starter/orgs/org--example/projects/project--example--memory/README.md +37 -0
- braingent-0.1.0/src/braingent/templates/starter/orgs/org--example/projects/project--example--memory/records/README.md +31 -0
- braingent-0.1.0/src/braingent/templates/starter/people/README.md +18 -0
- braingent-0.1.0/src/braingent/templates/starter/preferences/agent-task-protocol.md +78 -0
- braingent-0.1.0/src/braingent/templates/starter/preferences/agent-workflow.md +82 -0
- braingent-0.1.0/src/braingent/templates/starter/preferences/capture-policy.md +110 -0
- braingent-0.1.0/src/braingent/templates/starter/preferences/code-review.md +66 -0
- braingent-0.1.0/src/braingent/templates/starter/preferences/content-style.md +31 -0
- braingent-0.1.0/src/braingent/templates/starter/preferences/engineering-defaults.md +36 -0
- braingent-0.1.0/src/braingent/templates/starter/preferences/naming.md +83 -0
- braingent-0.1.0/src/braingent/templates/starter/preferences/note-taking-and-ai-memory.md +216 -0
- braingent-0.1.0/src/braingent/templates/starter/preferences/planning.md +42 -0
- braingent-0.1.0/src/braingent/templates/starter/preferences/pr-and-commit.md +39 -0
- braingent-0.1.0/src/braingent/templates/starter/preferences/privacy-and-safety.md +52 -0
- braingent-0.1.0/src/braingent/templates/starter/preferences/project-conventions.md +44 -0
- braingent-0.1.0/src/braingent/templates/starter/preferences/search-recipes.md +77 -0
- braingent-0.1.0/src/braingent/templates/starter/preferences/taxonomy.md +88 -0
- braingent-0.1.0/src/braingent/templates/starter/preferences/taxonomy.yml +273 -0
- braingent-0.1.0/src/braingent/templates/starter/repositories/repo--example--owner--repo/README.md +52 -0
- braingent-0.1.0/src/braingent/templates/starter/requirements-dev.txt +4 -0
- braingent-0.1.0/src/braingent/templates/starter/requirements.txt +4 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/__init__.py +1 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/braingent.py +29 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/cleanup.sh +88 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/doctor.sh +16 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/eval_tokens.py +88 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/find.sh +16 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/mcp_server.py +25 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/mcp_tools.py +16 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/new-record.sh +224 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/qa-generate.sh +9 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/recall.sh +16 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/reindex.sh +16 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/release-scan.py +218 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/synthesize.sh +16 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/task-archive.sh +16 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/task-claim.sh +16 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/task-comment.sh +16 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/task-dashboard.sh +7 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/task-list.sh +16 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/task-new.sh +16 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/task-status.sh +16 -0
- braingent-0.1.0/src/braingent/templates/starter/scripts/validate.sh +16 -0
- braingent-0.1.0/src/braingent/templates/starter/tasks/CLAUDE.md +26 -0
- braingent-0.1.0/src/braingent/templates/starter/tasks/INDEX.md +11 -0
- braingent-0.1.0/src/braingent/templates/starter/tasks/README.md +26 -0
- braingent-0.1.0/src/braingent/templates/starter/tasks/active/BGT-0001--example-review-task.md +80 -0
- braingent-0.1.0/src/braingent/templates/starter/tasks/archive/README.md +11 -0
- braingent-0.1.0/src/braingent/templates/starter/templates/agent-task.md +76 -0
- braingent-0.1.0/src/braingent/templates/starter/templates/code-review-record.md +59 -0
- braingent-0.1.0/src/braingent/templates/starter/templates/decision-record.md +52 -0
- braingent-0.1.0/src/braingent/templates/starter/templates/import-summary-record.md +52 -0
- braingent-0.1.0/src/braingent/templates/starter/templates/learning-record.md +50 -0
- braingent-0.1.0/src/braingent/templates/starter/templates/note-record.md +32 -0
- braingent-0.1.0/src/braingent/templates/starter/templates/person-interaction-record.md +40 -0
- braingent-0.1.0/src/braingent/templates/starter/templates/repository-profile.md +48 -0
- braingent-0.1.0/src/braingent/templates/starter/templates/task-record-minimal.md +41 -0
- braingent-0.1.0/src/braingent/templates/starter/templates/task-record.md +89 -0
- braingent-0.1.0/src/braingent/templates/starter/templates/ticket-stub.md +36 -0
- braingent-0.1.0/src/braingent/templates/starter/templates/tool-version-record.md +37 -0
- braingent-0.1.0/src/braingent/templates/starter/tickets/README.md +14 -0
- braingent-0.1.0/src/braingent/templates/starter/tools/README.md +23 -0
- braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/README.md +110 -0
- braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/SCHEMA_GAP_ANALYSIS.md +35 -0
- braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/SKILL.md +52 -0
- braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/examples/synthetic-output.md +179 -0
- braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/examples/synthetic-ticket.md +19 -0
- braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/prompts/black-box.md +23 -0
- braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/prompts/critic.md +23 -0
- braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/prompts/merge.md +19 -0
- braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/prompts/white-box.md +23 -0
- braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/qa-evidence.schema.json +139 -0
- braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/records/.gitkeep +1 -0
- braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/templates/test-plan.md +97 -0
- braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/test-plan.sh +9 -0
- braingent-0.1.0/src/braingent/templates/starter/topics/topic--ai-memory/README.md +33 -0
- braingent-0.1.0/src/braingent/templates/starter/workflows/cleanup-braingent.md +424 -0
- braingent-0.1.0/src/braingent/templates/starter/workflows/index-repo.md +151 -0
- braingent-0.1.0/src/braingent/templates/starter/workflows/retrieve-context.md +128 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
.DS_Store
|
|
2
|
+
__pycache__/
|
|
3
|
+
.venv/
|
|
4
|
+
*.swp
|
|
5
|
+
*.tmp
|
|
6
|
+
.braingent.db
|
|
7
|
+
.braingent.db-*
|
|
8
|
+
.test-plans/
|
|
9
|
+
uv.lock
|
|
10
|
+
dashboard/tasks/.tanstack/
|
|
11
|
+
dashboard/tasks/dist/
|
|
12
|
+
dashboard/tasks/node_modules/
|
|
13
|
+
dashboard/tasks/playwright-report/
|
|
14
|
+
dashboard/tasks/test-results/
|
braingent-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 JJ Adonis
|
|
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.
|
braingent-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: braingent
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Durable engineering memory CLI: doctor, reindex, find, recall, synthesize, MCP, and QA test-plan generation.
|
|
5
|
+
Project-URL: Homepage, https://github.com/thedoublejay/braingent-manifesto
|
|
6
|
+
Project-URL: Source, https://github.com/thedoublejay/braingent-manifesto
|
|
7
|
+
Project-URL: Issues, https://github.com/thedoublejay/braingent-manifesto/issues
|
|
8
|
+
Author: JJ Adonis
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: cli,mcp,memory,qa,test-plans
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: pyyaml<7,>=6.0.3
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: build>=1.5.0; extra == 'dev'
|
|
25
|
+
Requires-Dist: hatchling>=1.30.1; extra == 'dev'
|
|
26
|
+
Requires-Dist: mypy>=2.1.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=9.1.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: ruff>=0.15.17; extra == 'dev'
|
|
29
|
+
Requires-Dist: twine>=6.2.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: types-pyyaml>=6.0.12.20260518; extra == 'dev'
|
|
31
|
+
Provides-Extra: mcp
|
|
32
|
+
Requires-Dist: mcp<2,>=1.27.2; extra == 'mcp'
|
|
33
|
+
Provides-Extra: tokens
|
|
34
|
+
Requires-Dist: tiktoken<1,>=0.13.0; extra == 'tokens'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# Braingent Memory Repo
|
|
38
|
+
|
|
39
|
+
This is a Markdown-first engineering memory repository based on Braingent, created by JJ Adonis.
|
|
40
|
+
|
|
41
|
+
Use it as a durable memory layer for AI-assisted software engineering work. Claude, Codex, ChatGPT, and other tools should read this repo before planning meaningful work and should capture important outcomes after the work is done.
|
|
42
|
+
|
|
43
|
+
## What Goes Here
|
|
44
|
+
|
|
45
|
+
- Project and repository profiles.
|
|
46
|
+
- Task records.
|
|
47
|
+
- Code review records.
|
|
48
|
+
- Decision records.
|
|
49
|
+
- Reusable learnings.
|
|
50
|
+
- Tool and version notes.
|
|
51
|
+
- Ticket stubs for cross-cutting work.
|
|
52
|
+
- Optional live `BGT-NNNN` agent tasks for active coordination.
|
|
53
|
+
- Optional dashboard docs for a read-only live task UI.
|
|
54
|
+
- An installable `braingent` helper CLI for search, validation, reindexing,
|
|
55
|
+
task files, MCP retrieval, and QA generation.
|
|
56
|
+
- The packaged QA generator for Markdown, Xray JSON, TestRail CSV, and Gherkin
|
|
57
|
+
outputs.
|
|
58
|
+
- Raw imports before they are summarized.
|
|
59
|
+
- Preferences that guide future AI agents.
|
|
60
|
+
|
|
61
|
+
## Read First
|
|
62
|
+
|
|
63
|
+
Agents should start here:
|
|
64
|
+
|
|
65
|
+
1. `AGENTS.md` or `CLAUDE.md`, depending on the tool.
|
|
66
|
+
2. `INDEX.md`.
|
|
67
|
+
3. `CURRENT_STATE.md`.
|
|
68
|
+
4. `preferences/`.
|
|
69
|
+
5. `tasks/INDEX.md` if live tasks are enabled and the work may already be active.
|
|
70
|
+
6. Relevant organization, project, repository, topic, tool, ticket, or person records.
|
|
71
|
+
|
|
72
|
+
## Core Workflow
|
|
73
|
+
|
|
74
|
+
Before work:
|
|
75
|
+
|
|
76
|
+
- Read the root instructions.
|
|
77
|
+
- Search memory for relevant context.
|
|
78
|
+
- Check live tasks before creating overlapping active work.
|
|
79
|
+
- Reuse prior decisions and conventions.
|
|
80
|
+
|
|
81
|
+
During work:
|
|
82
|
+
|
|
83
|
+
- Track decisions, versions, commands, failures, fixes, tickets, PRs, branches, and follow-ups.
|
|
84
|
+
- Append activity to a live `BGT-NNNN` task when coordination or handoff matters.
|
|
85
|
+
|
|
86
|
+
After work:
|
|
87
|
+
|
|
88
|
+
- Create or update a durable record.
|
|
89
|
+
- Link completed live tasks to durable records with `agent_task: BGT-NNNN`.
|
|
90
|
+
- Update indexes or current state if needed.
|
|
91
|
+
- Commit the memory change.
|
|
92
|
+
|
|
93
|
+
## Search
|
|
94
|
+
|
|
95
|
+
Start with free-text search:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
rg -n "<query>" .
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Use the packaged structured search helper:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
braingent find kind=decision
|
|
105
|
+
braingent recall repo=repo--example--owner--repo
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
You can also search frontmatter fields directly:
|
|
109
|
+
|
|
110
|
+
- `record_kind`
|
|
111
|
+
- `status`
|
|
112
|
+
- `organization`
|
|
113
|
+
- `project`
|
|
114
|
+
- `repositories`
|
|
115
|
+
- `ticket`
|
|
116
|
+
- `topics`
|
|
117
|
+
- `tools`
|
|
118
|
+
- `people`
|
|
119
|
+
- `ai_tools`
|
|
120
|
+
|
|
121
|
+
## Safety
|
|
122
|
+
|
|
123
|
+
Never store secrets, credentials, tokens, private keys, customer secrets, or sensitive personal data in this repo.
|
|
124
|
+
|
|
125
|
+
Use placeholders and links instead of copying sensitive evidence.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Braingent Memory Repo
|
|
2
|
+
|
|
3
|
+
This is a Markdown-first engineering memory repository based on Braingent, created by JJ Adonis.
|
|
4
|
+
|
|
5
|
+
Use it as a durable memory layer for AI-assisted software engineering work. Claude, Codex, ChatGPT, and other tools should read this repo before planning meaningful work and should capture important outcomes after the work is done.
|
|
6
|
+
|
|
7
|
+
## What Goes Here
|
|
8
|
+
|
|
9
|
+
- Project and repository profiles.
|
|
10
|
+
- Task records.
|
|
11
|
+
- Code review records.
|
|
12
|
+
- Decision records.
|
|
13
|
+
- Reusable learnings.
|
|
14
|
+
- Tool and version notes.
|
|
15
|
+
- Ticket stubs for cross-cutting work.
|
|
16
|
+
- Optional live `BGT-NNNN` agent tasks for active coordination.
|
|
17
|
+
- Optional dashboard docs for a read-only live task UI.
|
|
18
|
+
- An installable `braingent` helper CLI for search, validation, reindexing,
|
|
19
|
+
task files, MCP retrieval, and QA generation.
|
|
20
|
+
- The packaged QA generator for Markdown, Xray JSON, TestRail CSV, and Gherkin
|
|
21
|
+
outputs.
|
|
22
|
+
- Raw imports before they are summarized.
|
|
23
|
+
- Preferences that guide future AI agents.
|
|
24
|
+
|
|
25
|
+
## Read First
|
|
26
|
+
|
|
27
|
+
Agents should start here:
|
|
28
|
+
|
|
29
|
+
1. `AGENTS.md` or `CLAUDE.md`, depending on the tool.
|
|
30
|
+
2. `INDEX.md`.
|
|
31
|
+
3. `CURRENT_STATE.md`.
|
|
32
|
+
4. `preferences/`.
|
|
33
|
+
5. `tasks/INDEX.md` if live tasks are enabled and the work may already be active.
|
|
34
|
+
6. Relevant organization, project, repository, topic, tool, ticket, or person records.
|
|
35
|
+
|
|
36
|
+
## Core Workflow
|
|
37
|
+
|
|
38
|
+
Before work:
|
|
39
|
+
|
|
40
|
+
- Read the root instructions.
|
|
41
|
+
- Search memory for relevant context.
|
|
42
|
+
- Check live tasks before creating overlapping active work.
|
|
43
|
+
- Reuse prior decisions and conventions.
|
|
44
|
+
|
|
45
|
+
During work:
|
|
46
|
+
|
|
47
|
+
- Track decisions, versions, commands, failures, fixes, tickets, PRs, branches, and follow-ups.
|
|
48
|
+
- Append activity to a live `BGT-NNNN` task when coordination or handoff matters.
|
|
49
|
+
|
|
50
|
+
After work:
|
|
51
|
+
|
|
52
|
+
- Create or update a durable record.
|
|
53
|
+
- Link completed live tasks to durable records with `agent_task: BGT-NNNN`.
|
|
54
|
+
- Update indexes or current state if needed.
|
|
55
|
+
- Commit the memory change.
|
|
56
|
+
|
|
57
|
+
## Search
|
|
58
|
+
|
|
59
|
+
Start with free-text search:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
rg -n "<query>" .
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Use the packaged structured search helper:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
braingent find kind=decision
|
|
69
|
+
braingent recall repo=repo--example--owner--repo
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
You can also search frontmatter fields directly:
|
|
73
|
+
|
|
74
|
+
- `record_kind`
|
|
75
|
+
- `status`
|
|
76
|
+
- `organization`
|
|
77
|
+
- `project`
|
|
78
|
+
- `repositories`
|
|
79
|
+
- `ticket`
|
|
80
|
+
- `topics`
|
|
81
|
+
- `tools`
|
|
82
|
+
- `people`
|
|
83
|
+
- `ai_tools`
|
|
84
|
+
|
|
85
|
+
## Safety
|
|
86
|
+
|
|
87
|
+
Never store secrets, credentials, tokens, private keys, customer secrets, or sensitive personal data in this repo.
|
|
88
|
+
|
|
89
|
+
Use placeholders and links instead of copying sensitive evidence.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.30.1"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "braingent"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Durable engineering memory CLI: doctor, reindex, find, recall, synthesize, MCP, and QA test-plan generation."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{ name = "JJ Adonis" }]
|
|
13
|
+
keywords = ["memory", "cli", "mcp", "qa", "test-plans"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Topic :: Software Development",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"PyYAML>=6.0.3,<7",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
mcp = [
|
|
31
|
+
"mcp>=1.27.2,<2",
|
|
32
|
+
]
|
|
33
|
+
tokens = [
|
|
34
|
+
"tiktoken>=0.13.0,<1",
|
|
35
|
+
]
|
|
36
|
+
dev = [
|
|
37
|
+
"build>=1.5.0",
|
|
38
|
+
"hatchling>=1.30.1",
|
|
39
|
+
"mypy>=2.1.0",
|
|
40
|
+
"pytest>=9.1.0",
|
|
41
|
+
"ruff>=0.15.17",
|
|
42
|
+
"twine>=6.2.0",
|
|
43
|
+
"types-PyYAML>=6.0.12.20260518",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[project.urls]
|
|
47
|
+
Homepage = "https://github.com/thedoublejay/braingent-manifesto"
|
|
48
|
+
Source = "https://github.com/thedoublejay/braingent-manifesto"
|
|
49
|
+
Issues = "https://github.com/thedoublejay/braingent-manifesto/issues"
|
|
50
|
+
|
|
51
|
+
[project.scripts]
|
|
52
|
+
braingent = "braingent.cli:main"
|
|
53
|
+
braingent-mcp = "braingent.mcp_server:main"
|
|
54
|
+
|
|
55
|
+
[tool.hatch.build.targets.wheel]
|
|
56
|
+
packages = ["src/braingent"]
|
|
57
|
+
|
|
58
|
+
[tool.hatch.build.targets.sdist]
|
|
59
|
+
include = [
|
|
60
|
+
"/LICENSE",
|
|
61
|
+
"/README.md",
|
|
62
|
+
"/pyproject.toml",
|
|
63
|
+
"/src",
|
|
64
|
+
"/release",
|
|
65
|
+
"/scripts/release-scan.py",
|
|
66
|
+
]
|
|
67
|
+
exclude = [
|
|
68
|
+
".git/",
|
|
69
|
+
".gitignore",
|
|
70
|
+
".venv/",
|
|
71
|
+
"__pycache__/",
|
|
72
|
+
"*.pyc",
|
|
73
|
+
".braingent.db",
|
|
74
|
+
".test-plans/",
|
|
75
|
+
"dashboard/",
|
|
76
|
+
"indexes/records*.json",
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
[tool.ruff]
|
|
80
|
+
line-length = 120
|
|
81
|
+
target-version = "py311"
|
|
82
|
+
src = ["src", "scripts", "tests", "tools"]
|
|
83
|
+
exclude = ["src/braingent/templates/starter"]
|
|
84
|
+
|
|
85
|
+
[tool.ruff.lint]
|
|
86
|
+
select = ["E", "F", "I", "UP", "B", "SIM", "RUF", "A"]
|
|
87
|
+
ignore = ["D", "E402", "E501"]
|
|
88
|
+
|
|
89
|
+
[tool.mypy]
|
|
90
|
+
python_version = "3.11"
|
|
91
|
+
files = ["src", "scripts", "tests", "tools/tool--test-plan/test_plan.py"]
|
|
92
|
+
mypy_path = ["src", "."]
|
|
93
|
+
explicit_package_bases = true
|
|
94
|
+
namespace_packages = true
|
|
95
|
+
warn_unused_configs = true
|
|
96
|
+
ignore_missing_imports = false
|
|
97
|
+
disable_error_code = ["attr-defined", "assignment", "import-not-found"]
|
|
98
|
+
exclude = ["build", "dist", "__pycache__", "src/braingent/templates/starter"]
|
|
99
|
+
plugins = []
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
patterns:
|
|
2
|
+
- id: private-customer-reference
|
|
3
|
+
regex: '(?i)\br[e]gask\b'
|
|
4
|
+
description: Private customer/project name must not ship in public artifacts.
|
|
5
|
+
- id: jira-ticket-reference
|
|
6
|
+
regex: '\bREG-[0-9]+\b'
|
|
7
|
+
description: Private Jira ticket keys must not ship in public artifacts.
|
|
8
|
+
- id: private-local-user-path
|
|
9
|
+
regex: '/Users/(?!you\b)[A-Za-z0-9._-]+/'
|
|
10
|
+
description: Local developer paths must not ship in public artifacts.
|
|
11
|
+
- id: atlassian-host
|
|
12
|
+
regex: '(?i)\b[a-z0-9.-]+\.atlassian\.net\b'
|
|
13
|
+
description: Atlassian tenant hosts must not ship in public artifacts.
|
|
14
|
+
- id: slack-host
|
|
15
|
+
regex: '(?i)\b[a-z0-9.-]+\.slack\.com\b'
|
|
16
|
+
description: Slack workspace hosts must not ship in public artifacts.
|
|
17
|
+
- id: sentry-dsn
|
|
18
|
+
regex: 'https://[A-Za-z0-9]+@[A-Za-z0-9.-]+/[0-9]+'
|
|
19
|
+
description: Sentry DSNs must not ship in public artifacts.
|
|
20
|
+
- id: api-key-assignment
|
|
21
|
+
regex: '(?i)\b(api[_-]?key|secret|password|private[_-]?key)\s*[:=]\s*["''][^"'']{8,}["'']'
|
|
22
|
+
description: Secret-like assignments must not ship in public artifacts.
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
import shutil
|
|
8
|
+
import subprocess
|
|
9
|
+
import sys
|
|
10
|
+
import tarfile
|
|
11
|
+
import tempfile
|
|
12
|
+
import zipfile
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
import yaml
|
|
19
|
+
except ImportError as exc: # pragma: no cover - local environment issue.
|
|
20
|
+
raise SystemExit("Missing PyYAML. Install package dev dependencies before running release-scan.") from exc
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
TEXT_SUFFIXES = {
|
|
24
|
+
"",
|
|
25
|
+
".cfg",
|
|
26
|
+
".css",
|
|
27
|
+
".csv",
|
|
28
|
+
".html",
|
|
29
|
+
".ini",
|
|
30
|
+
".js",
|
|
31
|
+
".json",
|
|
32
|
+
".md",
|
|
33
|
+
".py",
|
|
34
|
+
".sh",
|
|
35
|
+
".toml",
|
|
36
|
+
".txt",
|
|
37
|
+
".xml",
|
|
38
|
+
".yaml",
|
|
39
|
+
".yml",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
FORBIDDEN_PARTS = {
|
|
43
|
+
".git",
|
|
44
|
+
".mypy_cache",
|
|
45
|
+
".ruff_cache",
|
|
46
|
+
".venv",
|
|
47
|
+
"__pycache__",
|
|
48
|
+
".test-plans",
|
|
49
|
+
"dashboard",
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass(frozen=True)
|
|
54
|
+
class DenyPattern:
|
|
55
|
+
ident: str
|
|
56
|
+
regex: re.Pattern[str]
|
|
57
|
+
description: str
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def load_denylist(path: Path) -> list[DenyPattern]:
|
|
61
|
+
raw = yaml.safe_load(path.read_text(encoding="utf-8"))
|
|
62
|
+
patterns = raw.get("patterns") if isinstance(raw, dict) else None
|
|
63
|
+
if not isinstance(patterns, list):
|
|
64
|
+
raise SystemExit(f"{path} must contain a patterns list")
|
|
65
|
+
result: list[DenyPattern] = []
|
|
66
|
+
for item in patterns:
|
|
67
|
+
if not isinstance(item, dict):
|
|
68
|
+
raise SystemExit(f"{path} contains a non-object pattern entry")
|
|
69
|
+
result.append(
|
|
70
|
+
DenyPattern(
|
|
71
|
+
ident=str(item["id"]),
|
|
72
|
+
regex=re.compile(str(item["regex"])),
|
|
73
|
+
description=str(item.get("description") or item["id"]),
|
|
74
|
+
)
|
|
75
|
+
)
|
|
76
|
+
return result
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def run(command: list[str], cwd: Path) -> None:
|
|
80
|
+
completed = subprocess.run(command, cwd=cwd, check=False)
|
|
81
|
+
if completed.returncode != 0:
|
|
82
|
+
raise SystemExit(completed.returncode)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def build_artifacts(package_root: Path, out_dir: Path) -> None:
|
|
86
|
+
run([sys.executable, "-m", "build", str(package_root), "--outdir", str(out_dir)], package_root)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def extract_artifacts(dist_dir: Path, extract_dir: Path) -> list[Path]:
|
|
90
|
+
roots: list[Path] = []
|
|
91
|
+
for artifact in sorted(dist_dir.iterdir()):
|
|
92
|
+
target = extract_dir / artifact.name
|
|
93
|
+
target.mkdir(parents=True, exist_ok=True)
|
|
94
|
+
if artifact.suffix == ".whl":
|
|
95
|
+
with zipfile.ZipFile(artifact) as wheel:
|
|
96
|
+
wheel.extractall(target)
|
|
97
|
+
elif artifact.name.endswith(".tar.gz"):
|
|
98
|
+
with tarfile.open(artifact, "r:gz") as sdist:
|
|
99
|
+
sdist.extractall(target, filter="data")
|
|
100
|
+
else:
|
|
101
|
+
raise SystemExit(f"Unexpected artifact: {artifact.name}")
|
|
102
|
+
roots.append(target)
|
|
103
|
+
return roots
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def normalized_artifact_path(path: Path, root: Path) -> str:
|
|
107
|
+
rel = path.relative_to(root).as_posix()
|
|
108
|
+
parts = rel.split("/", 1)
|
|
109
|
+
if len(parts) == 2 and parts[0].startswith("braingent-") and (root / parts[0] / "pyproject.toml").exists():
|
|
110
|
+
return parts[1]
|
|
111
|
+
return rel
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def is_allowed_artifact_path(path: str) -> bool:
|
|
115
|
+
parts = set(path.split("/"))
|
|
116
|
+
if parts & FORBIDDEN_PARTS:
|
|
117
|
+
return False
|
|
118
|
+
if path.endswith(".pyc") or "/indexes/records" in path:
|
|
119
|
+
return False
|
|
120
|
+
if path in {".gitignore", "LICENSE", "PKG-INFO", "README.md", "pyproject.toml", "release/denylist.yml", "scripts/release-scan.py"}:
|
|
121
|
+
return True
|
|
122
|
+
return (path.startswith(("src/", "braingent/", "braingent-")) and ".dist-info/" in path) or path.startswith(
|
|
123
|
+
("src/braingent/", "braingent/")
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def iter_files(roots: list[Path]) -> list[tuple[Path, Path, str]]:
|
|
128
|
+
files: list[tuple[Path, Path, str]] = []
|
|
129
|
+
for root in roots:
|
|
130
|
+
for path in root.rglob("*"):
|
|
131
|
+
if path.is_file():
|
|
132
|
+
files.append((root, path, normalized_artifact_path(path, root)))
|
|
133
|
+
return files
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def scan_allowed_files(files: list[tuple[Path, Path, str]]) -> list[str]:
|
|
137
|
+
issues: list[str] = []
|
|
138
|
+
for _, _, rel in files:
|
|
139
|
+
if not is_allowed_artifact_path(rel):
|
|
140
|
+
issues.append(f"unexpected artifact file: {rel}")
|
|
141
|
+
return issues
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def scan_denylist(files: list[tuple[Path, Path, str]], patterns: list[DenyPattern]) -> list[str]:
|
|
145
|
+
issues: list[str] = []
|
|
146
|
+
for _, path, rel in files:
|
|
147
|
+
if path.suffix not in TEXT_SUFFIXES:
|
|
148
|
+
continue
|
|
149
|
+
try:
|
|
150
|
+
text = path.read_text(encoding="utf-8")
|
|
151
|
+
except UnicodeDecodeError:
|
|
152
|
+
continue
|
|
153
|
+
for pattern in patterns:
|
|
154
|
+
match = pattern.regex.search(text)
|
|
155
|
+
if match:
|
|
156
|
+
issues.append(f"{rel}: {pattern.ident}: {pattern.description}: {match.group(0)!r}")
|
|
157
|
+
return issues
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def run_gitleaks(source: Path, require: bool) -> list[str]:
|
|
161
|
+
binary = shutil.which("gitleaks")
|
|
162
|
+
if not binary:
|
|
163
|
+
if require:
|
|
164
|
+
return ["gitleaks is required but was not found on PATH"]
|
|
165
|
+
print("release-scan: gitleaks not found; skipped external secret scan", file=sys.stderr)
|
|
166
|
+
return []
|
|
167
|
+
completed = subprocess.run([binary, "detect", "--source", str(source), "--no-git", "--redact"], check=False)
|
|
168
|
+
if completed.returncode == 0:
|
|
169
|
+
return []
|
|
170
|
+
return [f"gitleaks detected potential secrets under {source}"]
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def main(argv: list[str] | None = None) -> int:
|
|
174
|
+
parser = argparse.ArgumentParser(description="Build and scan Braingent release artifacts.")
|
|
175
|
+
parser.add_argument("--package-root", default=".", help="package root containing pyproject.toml")
|
|
176
|
+
parser.add_argument("--denylist", default="release/denylist.yml", help="denylist YAML path")
|
|
177
|
+
parser.add_argument("--require-gitleaks", action="store_true", help="fail if gitleaks is unavailable")
|
|
178
|
+
parser.add_argument("--json", action="store_true", help="emit JSON summary")
|
|
179
|
+
args = parser.parse_args(argv)
|
|
180
|
+
|
|
181
|
+
package_root = Path(args.package_root).expanduser().resolve()
|
|
182
|
+
denylist = Path(args.denylist)
|
|
183
|
+
if not denylist.is_absolute():
|
|
184
|
+
denylist = package_root / denylist
|
|
185
|
+
patterns = load_denylist(denylist)
|
|
186
|
+
|
|
187
|
+
with tempfile.TemporaryDirectory(prefix="braingent-release-scan-") as tmp:
|
|
188
|
+
tmp_path = Path(tmp)
|
|
189
|
+
dist_dir = tmp_path / "dist"
|
|
190
|
+
extract_dir = tmp_path / "extract"
|
|
191
|
+
dist_dir.mkdir()
|
|
192
|
+
extract_dir.mkdir()
|
|
193
|
+
build_artifacts(package_root, dist_dir)
|
|
194
|
+
roots = extract_artifacts(dist_dir, extract_dir)
|
|
195
|
+
files = iter_files(roots)
|
|
196
|
+
issues = [
|
|
197
|
+
*scan_allowed_files(files),
|
|
198
|
+
*scan_denylist(files, patterns),
|
|
199
|
+
*run_gitleaks(extract_dir, args.require_gitleaks),
|
|
200
|
+
]
|
|
201
|
+
summary: dict[str, Any] = {
|
|
202
|
+
"artifacts": sorted(path.name for path in dist_dir.iterdir()),
|
|
203
|
+
"file_count": len(files),
|
|
204
|
+
"issues": issues,
|
|
205
|
+
}
|
|
206
|
+
if args.json:
|
|
207
|
+
print(json.dumps(summary, indent=2, sort_keys=True))
|
|
208
|
+
elif issues:
|
|
209
|
+
print("release-scan failed:", file=sys.stderr)
|
|
210
|
+
for issue in issues:
|
|
211
|
+
print(f"- {issue}", file=sys.stderr)
|
|
212
|
+
else:
|
|
213
|
+
print(f"release-scan passed for {', '.join(summary['artifacts'])}")
|
|
214
|
+
return 1 if issues else 0
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
if __name__ == "__main__":
|
|
218
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Compression pipeline for token-efficient Braingent hydration."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from . import dedupe, filters, group, truncate
|
|
6
|
+
from .base import Depth
|
|
7
|
+
|
|
8
|
+
VALID_DEPTHS = {"full", "summary", "frontmatter"}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def apply_pipeline(
|
|
12
|
+
text: str,
|
|
13
|
+
*,
|
|
14
|
+
record_kind: str,
|
|
15
|
+
depth: str,
|
|
16
|
+
) -> str:
|
|
17
|
+
if depth not in VALID_DEPTHS:
|
|
18
|
+
raise ValueError(f"unknown depth: {depth}")
|
|
19
|
+
if depth == "frontmatter":
|
|
20
|
+
return filters.frontmatter_only(text)
|
|
21
|
+
if depth == "full":
|
|
22
|
+
return text
|
|
23
|
+
|
|
24
|
+
out = filters.drop_archived_sections(text, record_kind=record_kind)
|
|
25
|
+
out = group.collapse_link_lists(out)
|
|
26
|
+
out = dedupe.collapse_repeated_lines(out)
|
|
27
|
+
out = truncate.head_per_section(out, max_lines_per_section=20)
|
|
28
|
+
return out
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
__all__ = ["VALID_DEPTHS", "Depth", "apply_pipeline"]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Shared compressor types."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Literal, Protocol
|
|
6
|
+
|
|
7
|
+
Depth = Literal["full", "summary", "frontmatter"]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Compressor(Protocol):
|
|
11
|
+
def apply(self, text: str, *, record_kind: str, depth: Depth) -> str:
|
|
12
|
+
"""Return compressed Markdown for one record."""
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Deduplication compressors."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def collapse_repeated_lines(text: str) -> str:
|
|
7
|
+
out: list[str] = []
|
|
8
|
+
previous: str | None = None
|
|
9
|
+
repeat_count = 0
|
|
10
|
+
|
|
11
|
+
for line in text.splitlines(keepends=True):
|
|
12
|
+
if line == previous:
|
|
13
|
+
repeat_count += 1
|
|
14
|
+
continue
|
|
15
|
+
if previous is not None and repeat_count > 0:
|
|
16
|
+
out[-1] = out[-1].rstrip("\n") + f" (repeated {repeat_count + 1}x)\n"
|
|
17
|
+
out.append(line)
|
|
18
|
+
previous = line
|
|
19
|
+
repeat_count = 0
|
|
20
|
+
|
|
21
|
+
if previous is not None and repeat_count > 0:
|
|
22
|
+
out[-1] = out[-1].rstrip("\n") + f" (repeated {repeat_count + 1}x)\n"
|
|
23
|
+
return "".join(out)
|