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.
Files changed (122) hide show
  1. braingent-0.1.0/.gitignore +14 -0
  2. braingent-0.1.0/LICENSE +21 -0
  3. braingent-0.1.0/PKG-INFO +125 -0
  4. braingent-0.1.0/README.md +89 -0
  5. braingent-0.1.0/pyproject.toml +99 -0
  6. braingent-0.1.0/release/denylist.yml +22 -0
  7. braingent-0.1.0/scripts/release-scan.py +218 -0
  8. braingent-0.1.0/src/braingent/__init__.py +5 -0
  9. braingent-0.1.0/src/braingent/__main__.py +6 -0
  10. braingent-0.1.0/src/braingent/cli.py +7 -0
  11. braingent-0.1.0/src/braingent/compressors/__init__.py +31 -0
  12. braingent-0.1.0/src/braingent/compressors/base.py +12 -0
  13. braingent-0.1.0/src/braingent/compressors/dedupe.py +23 -0
  14. braingent-0.1.0/src/braingent/compressors/filters.py +42 -0
  15. braingent-0.1.0/src/braingent/compressors/group.py +29 -0
  16. braingent-0.1.0/src/braingent/compressors/truncate.py +29 -0
  17. braingent-0.1.0/src/braingent/core.py +2759 -0
  18. braingent-0.1.0/src/braingent/mcp_server.py +57 -0
  19. braingent-0.1.0/src/braingent/mcp_tools.py +234 -0
  20. braingent-0.1.0/src/braingent/py.typed +1 -0
  21. braingent-0.1.0/src/braingent/qa/__init__.py +0 -0
  22. braingent-0.1.0/src/braingent/qa/prompts/black-box.md +23 -0
  23. braingent-0.1.0/src/braingent/qa/prompts/critic.md +23 -0
  24. braingent-0.1.0/src/braingent/qa/prompts/merge.md +19 -0
  25. braingent-0.1.0/src/braingent/qa/prompts/white-box.md +23 -0
  26. braingent-0.1.0/src/braingent/qa/qa-evidence.schema.json +139 -0
  27. braingent-0.1.0/src/braingent/qa/qa_evidence.py +489 -0
  28. braingent-0.1.0/src/braingent/qa/templates/test-plan.md +97 -0
  29. braingent-0.1.0/src/braingent/qa/test_plan.py +1428 -0
  30. braingent-0.1.0/src/braingent/templates/starter/.python-version +1 -0
  31. braingent-0.1.0/src/braingent/templates/starter/AGENTS.md +118 -0
  32. braingent-0.1.0/src/braingent/templates/starter/CHATGPT_PROJECT_BRIEF.md +57 -0
  33. braingent-0.1.0/src/braingent/templates/starter/CLAUDE.md +109 -0
  34. braingent-0.1.0/src/braingent/templates/starter/CURRENT_STATE.md +43 -0
  35. braingent-0.1.0/src/braingent/templates/starter/FILE-TREE.md +115 -0
  36. braingent-0.1.0/src/braingent/templates/starter/INDEX.md +93 -0
  37. braingent-0.1.0/src/braingent/templates/starter/README.md +89 -0
  38. braingent-0.1.0/src/braingent/templates/starter/imports/README.md +19 -0
  39. braingent-0.1.0/src/braingent/templates/starter/imports/raw/README.md +14 -0
  40. braingent-0.1.0/src/braingent/templates/starter/imports/summaries/README.md +8 -0
  41. braingent-0.1.0/src/braingent/templates/starter/inbox/README.md +11 -0
  42. braingent-0.1.0/src/braingent/templates/starter/indexes/README.md +22 -0
  43. braingent-0.1.0/src/braingent/templates/starter/orgs/org--example/README.md +33 -0
  44. braingent-0.1.0/src/braingent/templates/starter/orgs/org--example/projects/project--example--memory/README.md +37 -0
  45. braingent-0.1.0/src/braingent/templates/starter/orgs/org--example/projects/project--example--memory/records/README.md +31 -0
  46. braingent-0.1.0/src/braingent/templates/starter/people/README.md +18 -0
  47. braingent-0.1.0/src/braingent/templates/starter/preferences/agent-task-protocol.md +78 -0
  48. braingent-0.1.0/src/braingent/templates/starter/preferences/agent-workflow.md +82 -0
  49. braingent-0.1.0/src/braingent/templates/starter/preferences/capture-policy.md +110 -0
  50. braingent-0.1.0/src/braingent/templates/starter/preferences/code-review.md +66 -0
  51. braingent-0.1.0/src/braingent/templates/starter/preferences/content-style.md +31 -0
  52. braingent-0.1.0/src/braingent/templates/starter/preferences/engineering-defaults.md +36 -0
  53. braingent-0.1.0/src/braingent/templates/starter/preferences/naming.md +83 -0
  54. braingent-0.1.0/src/braingent/templates/starter/preferences/note-taking-and-ai-memory.md +216 -0
  55. braingent-0.1.0/src/braingent/templates/starter/preferences/planning.md +42 -0
  56. braingent-0.1.0/src/braingent/templates/starter/preferences/pr-and-commit.md +39 -0
  57. braingent-0.1.0/src/braingent/templates/starter/preferences/privacy-and-safety.md +52 -0
  58. braingent-0.1.0/src/braingent/templates/starter/preferences/project-conventions.md +44 -0
  59. braingent-0.1.0/src/braingent/templates/starter/preferences/search-recipes.md +77 -0
  60. braingent-0.1.0/src/braingent/templates/starter/preferences/taxonomy.md +88 -0
  61. braingent-0.1.0/src/braingent/templates/starter/preferences/taxonomy.yml +273 -0
  62. braingent-0.1.0/src/braingent/templates/starter/repositories/repo--example--owner--repo/README.md +52 -0
  63. braingent-0.1.0/src/braingent/templates/starter/requirements-dev.txt +4 -0
  64. braingent-0.1.0/src/braingent/templates/starter/requirements.txt +4 -0
  65. braingent-0.1.0/src/braingent/templates/starter/scripts/__init__.py +1 -0
  66. braingent-0.1.0/src/braingent/templates/starter/scripts/braingent.py +29 -0
  67. braingent-0.1.0/src/braingent/templates/starter/scripts/cleanup.sh +88 -0
  68. braingent-0.1.0/src/braingent/templates/starter/scripts/doctor.sh +16 -0
  69. braingent-0.1.0/src/braingent/templates/starter/scripts/eval_tokens.py +88 -0
  70. braingent-0.1.0/src/braingent/templates/starter/scripts/find.sh +16 -0
  71. braingent-0.1.0/src/braingent/templates/starter/scripts/mcp_server.py +25 -0
  72. braingent-0.1.0/src/braingent/templates/starter/scripts/mcp_tools.py +16 -0
  73. braingent-0.1.0/src/braingent/templates/starter/scripts/new-record.sh +224 -0
  74. braingent-0.1.0/src/braingent/templates/starter/scripts/qa-generate.sh +9 -0
  75. braingent-0.1.0/src/braingent/templates/starter/scripts/recall.sh +16 -0
  76. braingent-0.1.0/src/braingent/templates/starter/scripts/reindex.sh +16 -0
  77. braingent-0.1.0/src/braingent/templates/starter/scripts/release-scan.py +218 -0
  78. braingent-0.1.0/src/braingent/templates/starter/scripts/synthesize.sh +16 -0
  79. braingent-0.1.0/src/braingent/templates/starter/scripts/task-archive.sh +16 -0
  80. braingent-0.1.0/src/braingent/templates/starter/scripts/task-claim.sh +16 -0
  81. braingent-0.1.0/src/braingent/templates/starter/scripts/task-comment.sh +16 -0
  82. braingent-0.1.0/src/braingent/templates/starter/scripts/task-dashboard.sh +7 -0
  83. braingent-0.1.0/src/braingent/templates/starter/scripts/task-list.sh +16 -0
  84. braingent-0.1.0/src/braingent/templates/starter/scripts/task-new.sh +16 -0
  85. braingent-0.1.0/src/braingent/templates/starter/scripts/task-status.sh +16 -0
  86. braingent-0.1.0/src/braingent/templates/starter/scripts/validate.sh +16 -0
  87. braingent-0.1.0/src/braingent/templates/starter/tasks/CLAUDE.md +26 -0
  88. braingent-0.1.0/src/braingent/templates/starter/tasks/INDEX.md +11 -0
  89. braingent-0.1.0/src/braingent/templates/starter/tasks/README.md +26 -0
  90. braingent-0.1.0/src/braingent/templates/starter/tasks/active/BGT-0001--example-review-task.md +80 -0
  91. braingent-0.1.0/src/braingent/templates/starter/tasks/archive/README.md +11 -0
  92. braingent-0.1.0/src/braingent/templates/starter/templates/agent-task.md +76 -0
  93. braingent-0.1.0/src/braingent/templates/starter/templates/code-review-record.md +59 -0
  94. braingent-0.1.0/src/braingent/templates/starter/templates/decision-record.md +52 -0
  95. braingent-0.1.0/src/braingent/templates/starter/templates/import-summary-record.md +52 -0
  96. braingent-0.1.0/src/braingent/templates/starter/templates/learning-record.md +50 -0
  97. braingent-0.1.0/src/braingent/templates/starter/templates/note-record.md +32 -0
  98. braingent-0.1.0/src/braingent/templates/starter/templates/person-interaction-record.md +40 -0
  99. braingent-0.1.0/src/braingent/templates/starter/templates/repository-profile.md +48 -0
  100. braingent-0.1.0/src/braingent/templates/starter/templates/task-record-minimal.md +41 -0
  101. braingent-0.1.0/src/braingent/templates/starter/templates/task-record.md +89 -0
  102. braingent-0.1.0/src/braingent/templates/starter/templates/ticket-stub.md +36 -0
  103. braingent-0.1.0/src/braingent/templates/starter/templates/tool-version-record.md +37 -0
  104. braingent-0.1.0/src/braingent/templates/starter/tickets/README.md +14 -0
  105. braingent-0.1.0/src/braingent/templates/starter/tools/README.md +23 -0
  106. braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/README.md +110 -0
  107. braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/SCHEMA_GAP_ANALYSIS.md +35 -0
  108. braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/SKILL.md +52 -0
  109. braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/examples/synthetic-output.md +179 -0
  110. braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/examples/synthetic-ticket.md +19 -0
  111. braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/prompts/black-box.md +23 -0
  112. braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/prompts/critic.md +23 -0
  113. braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/prompts/merge.md +19 -0
  114. braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/prompts/white-box.md +23 -0
  115. braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/qa-evidence.schema.json +139 -0
  116. braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/records/.gitkeep +1 -0
  117. braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/templates/test-plan.md +97 -0
  118. braingent-0.1.0/src/braingent/templates/starter/tools/tool--test-plan/test-plan.sh +9 -0
  119. braingent-0.1.0/src/braingent/templates/starter/topics/topic--ai-memory/README.md +33 -0
  120. braingent-0.1.0/src/braingent/templates/starter/workflows/cleanup-braingent.md +424 -0
  121. braingent-0.1.0/src/braingent/templates/starter/workflows/index-repo.md +151 -0
  122. 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/
@@ -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.
@@ -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,5 @@
1
+ """Braingent engineering-memory tooling."""
2
+
3
+ from __future__ import annotations
4
+
5
+ __version__ = "0.1.0"
@@ -0,0 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+ from braingent.cli import main
4
+
5
+ if __name__ == "__main__":
6
+ raise SystemExit(main())
@@ -0,0 +1,7 @@
1
+ """Console entry point for Braingent."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from braingent.core import main
6
+
7
+ __all__ = ["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)