@10kdevs/matha 0.1.0

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bhupesh
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.
package/README.md ADDED
@@ -0,0 +1,222 @@
1
+ # MATHA
2
+
3
+ **The persistent cognitive layer for AI-assisted development.**
4
+
5
+ ---
6
+
7
+ You gave the AI your entire codebase. You explained the business logic for two hours. You did a Q&A, walked through every edge case, corrected every misunderstanding.
8
+
9
+ Then you closed the session.
10
+
11
+ The next day you opened a new one. And it remembered nothing.
12
+
13
+ Not the HWM calculation that took three sessions to get right. Not the rule about deposit events not triggering profit cycles. Not the assumption that broke everything in V1 and cost you twelve days to fix. Gone. Cold start. Again.
14
+
15
+ This is not an AI intelligence problem. Every tool — Cursor, Copilot, Claude Code, Windsurf — is stateless by design. They are powerful and amnesiac in equal measure. The larger your project, the more painful that amnesia becomes.
16
+
17
+ MATHA fixes this.
18
+
19
+ ---
20
+
21
+ ## What MATHA Does
22
+
23
+ MATHA is a local MCP server that runs alongside your project and gives any AI agent the context that currently only exists inside a senior engineer's head.
24
+
25
+ It captures three things that no markdown file can hold:
26
+
27
+ **Intent** — why the project exists, what the non-negotiable rules are, what it explicitly does not do. Not features. The reasoning behind them.
28
+
29
+ **Decisions** — every assumption that broke, every correction that was made, every "no that's wrong" moment that cost you days. Captured automatically from the work itself, not from humans writing docs after the fact.
30
+
31
+ **Behaviour contracts** — what the system must do, written before code is written. Machine-verifiable. Validated after every session. Violations recorded and surfaced automatically next time.
32
+
33
+ Every session writes back what it learned. Every session starts warmer than the last. The brain stays on.
34
+
35
+ ---
36
+
37
+ ## How It Works
38
+
39
+ Three commands. That's the entire interface.
40
+
41
+ ```bash
42
+ # Once per project — seeds the brain from your project's intent and git history
43
+ matha init
44
+
45
+ # Before every AI session — surfaces what the brain knows, fires warnings,
46
+ # writes behaviour contract before a single line of code is written
47
+ matha before
48
+
49
+ # After every AI session — captures what was learned, records violations,
50
+ # updates the brain so the next session starts with full context
51
+ matha after
52
+ ```
53
+
54
+ The session brief produced by `matha before` is copy-pasteable directly into any AI agent. It tells the agent what it needs to know before it touches anything — danger zones, prior decisions, frozen files, behaviour contract.
55
+
56
+ The write-back from `matha after` means that correction never has to be made twice.
57
+
58
+ ---
59
+
60
+ ## Installation
61
+
62
+ ```bash
63
+ npm install -g matha
64
+ ```
65
+
66
+ Zero-install first run:
67
+ ```bash
68
+ npx matha init
69
+ ```
70
+
71
+ Requires Node.js 20+. No API key. No cloud dependency. All data stays in your repository.
72
+
73
+ ---
74
+
75
+ ## Connecting To Your IDE
76
+
77
+ After `matha init`, connect MATHA to your IDE via MCP.
78
+
79
+ The init command writes `.matha/mcp-config.json` with the exact config for your machine. Add it to your IDE's MCP configuration.
80
+
81
+ **Claude Code:**
82
+ ```bash
83
+ # .matha/mcp-config.json contains the correct config
84
+ # Add to your Claude Code MCP settings
85
+ ```
86
+
87
+ **Cursor:**
88
+ ```json
89
+ {
90
+ "mcpServers": {
91
+ "matha": {
92
+ "command": "node",
93
+ "args": ["/path/to/your/project/node_modules/.bin/matha", "serve"]
94
+ }
95
+ }
96
+ }
97
+ ```
98
+
99
+ Once connected, your AI agent can call `matha_brief()` as its first action in any session — receiving the full project context before writing a line.
100
+
101
+ ---
102
+
103
+ ## The Eight Gates
104
+
105
+ `matha before` runs eight structured gates before allowing the AI to build. Not as a prompt. As enforced infrastructure.
106
+
107
+ ```
108
+ GATE 01 UNDERSTAND What is the WHY of this change?
109
+ GATE 02 BOUND What are the non-negotiable rules?
110
+ GATE 03 ORIENT What exists? What is stable, frozen, volatile?
111
+ GATE 04 SURFACE DANGER Any prior failures in this area?
112
+ GATE 05 CONTRACT What must be true after? Written before code.
113
+ GATE 06 COST CHECK What model tier? What token budget?
114
+ GATE 07 BUILD AI is now allowed to generate code.
115
+ GATE 08 WRITE BACK What was learned? Captured. Never lost.
116
+ ```
117
+
118
+ Gate 07 does not open until Gates 01 through 05 are complete.
119
+
120
+ ---
121
+
122
+ ## The Brain
123
+
124
+ MATHA's knowledge lives in `.matha/` in your repository. Committed to version control. Owned by your team. Never sent anywhere.
125
+
126
+ ```
127
+ .matha/
128
+ ├── hippocampus/ intent, rules, decisions, danger zones
129
+ ├── cerebellum/ behaviour contracts, violation log
130
+ ├── cortex/ stability map, co-change graph, boundaries
131
+ ├── dopamine/ session history, routing rules, deltas
132
+ └── sessions/ session briefs
133
+ ```
134
+
135
+ The cortex builds itself from git history. Files that change together are linked. Files with low churn and high connectivity are classified frozen — AI agents are warned before touching them.
136
+
137
+ The dopamine loop learns from every session. If business logic changes in your project consistently burn three times the predicted token budget, MATHA adjusts its recommendation automatically. It tells you why.
138
+
139
+ ---
140
+
141
+ ## MCP Tools
142
+
143
+ AI agents connected via MCP have access to:
144
+
145
+ ```
146
+ matha_brief(scope?, directory?) Full session context
147
+ matha_get_rules() Non-negotiable business rules
148
+ matha_get_danger_zones(context?) Known failure patterns
149
+ matha_get_decisions(component?) Decision history
150
+ matha_get_stability(files[]) Stability classification per file
151
+ matha_match(scope, intent) Full cerebellum match — what does
152
+ the brain know about this operation?
153
+ matha_record_decision(...) Write a decision back to the brain
154
+ matha_record_danger(...) Flag a new danger zone
155
+ matha_record_contract(...) Store a behaviour contract
156
+ matha_refresh_cortex() Rebuild from git history
157
+ matha_get_routing(operationType?) Learned model routing rules
158
+ ```
159
+
160
+ ---
161
+
162
+ ## Initialising From An Existing Document
163
+
164
+ If your project already has a BRD, spec, or requirements document:
165
+
166
+ ```bash
167
+ matha init --from requirements.md
168
+ ```
169
+
170
+ MATHA parses business rules, boundaries, and intent from the document and pre-fills the init prompts. You review and confirm. Nothing is written without your sign-off.
171
+
172
+ ---
173
+
174
+ ## What MATHA Is Not
175
+
176
+ MATHA does not generate code.
177
+
178
+ MATHA does not replace your IDE, your AI model, or your version control.
179
+
180
+ MATHA does not require a specific model, a specific IDE, or a specific language. If your tool supports MCP, it connects to MATHA on day one.
181
+
182
+ MATHA does not send your data anywhere. The brain lives in your repository.
183
+
184
+ ---
185
+
186
+ ## The Real Problem It Solves
187
+
188
+ There is a moment every developer who uses AI tools eventually hits.
189
+
190
+ The project is large enough that you cannot hold all of it in your head. The AI cannot hold all of it in its context window. You find yourself explaining the same decision three times across three sessions. You find a bug and you are not confident which part of the codebase is responsible. You realise you have forgotten the core logic you wrote four weeks ago.
191
+
192
+ At that moment, the AI is not the problem. The absence of accumulated understanding is the problem.
193
+
194
+ MATHA is the accumulated understanding.
195
+
196
+ It is what the senior engineer carries in their head after three years on a project — the why behind every decision, the scars from every production incident, the rules that cannot be broken and the code that cannot be touched without knowing why it works the way it does.
197
+
198
+ That knowledge survives sessions. It survives team changes. It survives the moment you come back to a project six months after you last touched it.
199
+
200
+ The code gets better. The context never resets. The brain stays on.
201
+
202
+ ---
203
+
204
+ ## Case Study
205
+
206
+ [How MATHA would have saved 12 days on a real PAMM trading platform →](docs/pamm-case-study.md)
207
+
208
+ ---
209
+
210
+ ## Contributing
211
+
212
+ [CONTRIBUTING.md](CONTRIBUTING.md)
213
+
214
+ ---
215
+
216
+ ## License
217
+
218
+ MIT — owned by nobody, usable by everyone.
219
+
220
+ ---
221
+
222
+ *Built because the problem was real.*
@@ -0,0 +1,223 @@
1
+ import * as path from 'path';
2
+ import * as fs from 'fs/promises';
3
+ import { getDangerZones, getDecisions } from '../brain/hippocampus.js';
4
+ import { getSnapshot } from '../brain/cortex.js';
5
+ const STOP_WORDS = new Set([
6
+ 'should', 'never', 'always', 'before', 'after', 'every', 'which',
7
+ 'their', 'there', 'where', 'when', 'would', 'could', 'might',
8
+ 'using', 'being', 'having'
9
+ ]);
10
+ export function extractKeywords(text) {
11
+ const words = text
12
+ .toLowerCase()
13
+ .replace(/[^\w\s]/g, ' ')
14
+ .split(/\s+/);
15
+ return words.filter(word => word.length > 4 && !STOP_WORDS.has(word));
16
+ }
17
+ export function matchDangerZones(context, dangerZones) {
18
+ const results = [];
19
+ const scopeLower = context.scope.toLowerCase();
20
+ const intentLower = context.intent.toLowerCase();
21
+ for (const zone of dangerZones) {
22
+ const compLower = (zone.component || '').toLowerCase();
23
+ // Check scope match
24
+ let matched = scopeLower.includes(compLower);
25
+ // Check intent match
26
+ if (!matched) {
27
+ matched = intentLower.includes(compLower);
28
+ }
29
+ // Check keyword match
30
+ if (!matched && zone.description) {
31
+ const zoneKeywords = extractKeywords(zone.description);
32
+ matched = zoneKeywords.some(kw => intentLower.includes(kw));
33
+ }
34
+ if (matched) {
35
+ results.push({
36
+ matchType: 'danger_zone',
37
+ severity: 'critical',
38
+ title: `Danger Zone: ${zone.component}`,
39
+ description: zone.description || '',
40
+ source: 'danger-zones.json',
41
+ component: zone.component,
42
+ recommendation: 'Review danger zone before proceeding. Consider matha_record_decision after session.',
43
+ });
44
+ }
45
+ }
46
+ return results;
47
+ }
48
+ export function matchContracts(context, contracts) {
49
+ const results = [];
50
+ const scopeLower = context.scope.toLowerCase();
51
+ for (const [key, contract] of Object.entries(contracts)) {
52
+ const compLower = (contract.component || '').toLowerCase();
53
+ if (scopeLower.includes(compLower)) {
54
+ const violations = (contract.assertions || []).filter((a) => a.violation_count > 0);
55
+ const hasViolations = violations.length > 0;
56
+ results.push({
57
+ matchType: 'contract',
58
+ severity: hasViolations ? 'critical' : 'info',
59
+ title: `Contract: ${contract.component}`,
60
+ description: hasViolations
61
+ ? `Previously violated ${violations.length} times.`
62
+ : 'Contract is currently clean.',
63
+ source: 'contracts',
64
+ component: contract.component,
65
+ recommendation: `Verify all ${(contract.assertions || []).length} contract assertions pass after changes.`,
66
+ });
67
+ }
68
+ }
69
+ return results;
70
+ }
71
+ export function matchFrozenFiles(context, stabilityRecords) {
72
+ const results = [];
73
+ // Create a map of frozen files for quick lookup
74
+ const frozenFiles = stabilityRecords.filter(r => r.stability === 'frozen');
75
+ if (context.filepaths && context.filepaths.length > 0) {
76
+ for (const filepath of context.filepaths) {
77
+ // Find matching frozen record by exact path
78
+ const record = frozenFiles.find(r => r.filepath === filepath);
79
+ if (record) {
80
+ results.push({
81
+ matchType: 'frozen_file',
82
+ severity: 'critical',
83
+ title: `Frozen File: ${filepath}`,
84
+ description: record.reason || 'No reason provided',
85
+ source: 'cortex/stability.json',
86
+ component: filepath,
87
+ recommendation: 'This file is classified FROZEN. Confirm owner approval before modifying.',
88
+ });
89
+ }
90
+ }
91
+ }
92
+ else {
93
+ // Fall back to scope substring match
94
+ const scopeLower = context.scope.toLowerCase();
95
+ const scopeParts = scopeLower.split(',').map(s => s.trim()).filter(Boolean);
96
+ for (const record of frozenFiles) {
97
+ const recordFpLower = record.filepath.toLowerCase();
98
+ // Match if the scope part is a substring of the filepath, OR if filepath is a substring of the scope part
99
+ const isMatched = scopeParts.some(part => recordFpLower.includes(part) || part.includes(recordFpLower));
100
+ if (isMatched) {
101
+ results.push({
102
+ matchType: 'frozen_file',
103
+ severity: 'critical',
104
+ title: `Frozen File: ${record.filepath}`,
105
+ description: record.reason || 'No reason provided',
106
+ source: 'cortex/stability.json',
107
+ component: record.filepath,
108
+ recommendation: 'This file is classified FROZEN. Confirm owner approval before modifying.',
109
+ });
110
+ }
111
+ }
112
+ }
113
+ return results;
114
+ }
115
+ export function matchDecisionPatterns(context, decisions) {
116
+ const results = [];
117
+ const scopeLower = context.scope.toLowerCase();
118
+ // Sort decisions by timestamp descending
119
+ const sortedDecisions = [...decisions].sort((a, b) => {
120
+ return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
121
+ });
122
+ // Filter only active decisions
123
+ const activeDecisions = sortedDecisions.filter(d => d.status === 'active');
124
+ let matchCount = 0;
125
+ for (const decision of activeDecisions) {
126
+ if (matchCount >= 3)
127
+ break;
128
+ const compLower = (decision.component || '').toLowerCase();
129
+ if (scopeLower.includes(compLower)) {
130
+ results.push({
131
+ matchType: 'decision_pattern',
132
+ severity: 'warning',
133
+ title: `Prior Decision: ${decision.component}`,
134
+ description: `Previous assumption: ${decision.previous_assumption}. Correction: ${decision.correction}.`,
135
+ source: 'hippocampus/decisions',
136
+ component: decision.component,
137
+ recommendation: 'Be aware of this prior correction when working in this area.',
138
+ });
139
+ matchCount++;
140
+ }
141
+ }
142
+ return results;
143
+ }
144
+ // Helper to determine sort order
145
+ const SEVERITY_ORDER = {
146
+ critical: 0,
147
+ warning: 1,
148
+ info: 2,
149
+ };
150
+ export async function matchAll(context, mathaDir) {
151
+ try {
152
+ const allResults = [];
153
+ // 1. Fetch Danger Zones
154
+ try {
155
+ const dangerZones = await getDangerZones(mathaDir).catch(() => []);
156
+ allResults.push(...matchDangerZones(context, dangerZones));
157
+ }
158
+ catch {
159
+ // Ignore errors for danger zones
160
+ }
161
+ // 2. Fetch Contracts
162
+ try {
163
+ const contractsDir = path.join(mathaDir, 'cerebellum', 'contracts');
164
+ const contractsFiles = await fs.readdir(contractsDir).catch(() => []);
165
+ const contracts = {};
166
+ for (const file of contractsFiles) {
167
+ if (file.endsWith('.json')) {
168
+ try {
169
+ const content = await fs.readFile(path.join(contractsDir, file), 'utf-8');
170
+ const parsed = JSON.parse(content);
171
+ if (parsed.component) {
172
+ contracts[parsed.component] = parsed;
173
+ }
174
+ }
175
+ catch {
176
+ // ignore bad files
177
+ }
178
+ }
179
+ }
180
+ allResults.push(...matchContracts(context, contracts));
181
+ }
182
+ catch {
183
+ // Ignore errors for contracts
184
+ }
185
+ // 3. Fetch Frozen Files
186
+ try {
187
+ const snapshot = await getSnapshot(mathaDir).catch(() => null);
188
+ if (snapshot && snapshot.stability) {
189
+ allResults.push(...matchFrozenFiles(context, snapshot.stability));
190
+ }
191
+ }
192
+ catch {
193
+ // Ignore errors for frozen files
194
+ }
195
+ // 4. Fetch Decisions
196
+ try {
197
+ const decisions = await getDecisions(mathaDir).catch(() => []);
198
+ allResults.push(...matchDecisionPatterns(context, decisions));
199
+ }
200
+ catch {
201
+ // Ignore errors for decisions
202
+ }
203
+ // Deduplicate by matchType + component
204
+ const seen = new Set();
205
+ const deduplicated = [];
206
+ for (const result of allResults) {
207
+ const key = `${result.matchType}:${result.component.toLowerCase()}`;
208
+ if (!seen.has(key)) {
209
+ seen.add(key);
210
+ deduplicated.push(result);
211
+ }
212
+ }
213
+ // Sort by severity (critical -> warning -> info)
214
+ deduplicated.sort((a, b) => {
215
+ return SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity];
216
+ });
217
+ return deduplicated;
218
+ }
219
+ catch {
220
+ // Never throw - return empty array on catastrophic failure
221
+ return [];
222
+ }
223
+ }