mneme-ai 0.8.3
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/README.md +45 -0
- package/bin/mneme.js +2 -0
- package/dist/commands/adapt.d.ts +6 -0
- package/dist/commands/adapt.d.ts.map +1 -0
- package/dist/commands/adapt.js +219 -0
- package/dist/commands/adapt.js.map +1 -0
- package/dist/commands/ask.d.ts +8 -0
- package/dist/commands/ask.d.ts.map +1 -0
- package/dist/commands/ask.js +76 -0
- package/dist/commands/ask.js.map +1 -0
- package/dist/commands/blast.d.ts +8 -0
- package/dist/commands/blast.d.ts.map +1 -0
- package/dist/commands/blast.js +132 -0
- package/dist/commands/blast.js.map +1 -0
- package/dist/commands/clones.d.ts +19 -0
- package/dist/commands/clones.d.ts.map +1 -0
- package/dist/commands/clones.js +155 -0
- package/dist/commands/clones.js.map +1 -0
- package/dist/commands/conscience.d.ts +15 -0
- package/dist/commands/conscience.d.ts.map +1 -0
- package/dist/commands/conscience.js +202 -0
- package/dist/commands/conscience.js.map +1 -0
- package/dist/commands/correlate.d.ts +18 -0
- package/dist/commands/correlate.d.ts.map +1 -0
- package/dist/commands/correlate.js +173 -0
- package/dist/commands/correlate.js.map +1 -0
- package/dist/commands/echo.d.ts +16 -0
- package/dist/commands/echo.d.ts.map +1 -0
- package/dist/commands/echo.js +175 -0
- package/dist/commands/echo.js.map +1 -0
- package/dist/commands/genius.d.ts +13 -0
- package/dist/commands/genius.d.ts.map +1 -0
- package/dist/commands/genius.js +213 -0
- package/dist/commands/genius.js.map +1 -0
- package/dist/commands/heal.d.ts +22 -0
- package/dist/commands/heal.d.ts.map +1 -0
- package/dist/commands/heal.js +160 -0
- package/dist/commands/heal.js.map +1 -0
- package/dist/commands/index-cmd.d.ts +9 -0
- package/dist/commands/index-cmd.d.ts.map +1 -0
- package/dist/commands/index-cmd.js +74 -0
- package/dist/commands/index-cmd.js.map +1 -0
- package/dist/commands/init.d.ts +6 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +34 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/mcp.d.ts +4 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +10 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/palimpsest.d.ts +22 -0
- package/dist/commands/palimpsest.d.ts.map +1 -0
- package/dist/commands/palimpsest.js +164 -0
- package/dist/commands/palimpsest.js.map +1 -0
- package/dist/commands/status.d.ts +4 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +49 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/teach.d.ts +16 -0
- package/dist/commands/teach.d.ts.map +1 -0
- package/dist/commands/teach.js +237 -0
- package/dist/commands/teach.js.map +1 -0
- package/dist/commands/teach.test.d.ts +2 -0
- package/dist/commands/teach.test.d.ts.map +1 -0
- package/dist/commands/teach.test.js +44 -0
- package/dist/commands/teach.test.js.map +1 -0
- package/dist/commands/why.d.ts +12 -0
- package/dist/commands/why.d.ts.map +1 -0
- package/dist/commands/why.js +85 -0
- package/dist/commands/why.js.map +1 -0
- package/dist/commands/wild-features.d.ts +42 -0
- package/dist/commands/wild-features.d.ts.map +1 -0
- package/dist/commands/wild-features.js +483 -0
- package/dist/commands/wild-features.js.map +1 -0
- package/dist/commands/wild-stubs.d.ts +6 -0
- package/dist/commands/wild-stubs.d.ts.map +1 -0
- package/dist/commands/wild-stubs.js +112 -0
- package/dist/commands/wild-stubs.js.map +1 -0
- package/dist/commands/wisdom.d.ts +13 -0
- package/dist/commands/wisdom.d.ts.map +1 -0
- package/dist/commands/wisdom.js +94 -0
- package/dist/commands/wisdom.js.map +1 -0
- package/dist/config.d.ts +26 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +28 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +371 -0
- package/dist/index.js.map +1 -0
- package/dist/meditations.d.ts +24 -0
- package/dist/meditations.d.ts.map +1 -0
- package/dist/meditations.js +120 -0
- package/dist/meditations.js.map +1 -0
- package/dist/meditations.test.d.ts +2 -0
- package/dist/meditations.test.d.ts.map +1 -0
- package/dist/meditations.test.js +70 -0
- package/dist/meditations.test.js.map +1 -0
- package/dist/paths.d.ts +7 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +14 -0
- package/dist/paths.js.map +1 -0
- package/dist/ui.d.ts +12 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +30 -0
- package/dist/ui.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { entities, git, store } from "@mneme-ai/core";
|
|
2
|
+
import { resolveEmbedder } from "@mneme-ai/embeddings";
|
|
3
|
+
import kleur from "kleur";
|
|
4
|
+
import { dbPath } from "../paths.js";
|
|
5
|
+
import { readConfig } from "../config.js";
|
|
6
|
+
import { ui, formatProgress } from "../ui.js";
|
|
7
|
+
/**
|
|
8
|
+
* `mneme entities` — parse repo, embed, persist.
|
|
9
|
+
*/
|
|
10
|
+
export async function entitiesCommand(opts) {
|
|
11
|
+
ui.banner();
|
|
12
|
+
if (!(await git.isGitRepo(opts.cwd))) {
|
|
13
|
+
ui.error("Not in a git repo. Run `mneme init` first.");
|
|
14
|
+
return 1;
|
|
15
|
+
}
|
|
16
|
+
const meta = await git.getRepoMeta(opts.cwd);
|
|
17
|
+
const cfg = readConfig(meta.rootPath);
|
|
18
|
+
const s = new store.MnemeStore(dbPath(meta.rootPath));
|
|
19
|
+
// Set up TS/JS parser (always — it's the primary supported language).
|
|
20
|
+
const tsParser = new entities.TypeScriptParser();
|
|
21
|
+
try {
|
|
22
|
+
await tsParser.preload();
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
ui.error(err.message);
|
|
26
|
+
s.close();
|
|
27
|
+
return 1;
|
|
28
|
+
}
|
|
29
|
+
// Set up Python parser if `python3` (or `python`) is on PATH.
|
|
30
|
+
let pyParser = null;
|
|
31
|
+
try {
|
|
32
|
+
const p = new entities.PythonParser();
|
|
33
|
+
await p.preload();
|
|
34
|
+
pyParser = p;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Python not available — skip silently. We're not adding a hard dep.
|
|
38
|
+
}
|
|
39
|
+
const supported = ["TypeScript / JavaScript (compiler API)"];
|
|
40
|
+
if (pyParser)
|
|
41
|
+
supported.push("Python (ast)");
|
|
42
|
+
ui.step("parsers", supported.join(" + "));
|
|
43
|
+
ui.step("embedder", "resolving …");
|
|
44
|
+
const embedder = await resolveEmbedder({
|
|
45
|
+
provider: cfg.embeddings.provider,
|
|
46
|
+
model: cfg.embeddings.model,
|
|
47
|
+
baseUrl: cfg.embeddings.baseUrl,
|
|
48
|
+
});
|
|
49
|
+
ui.dim(` using ${embedder.name} (${embedder.dimensions} dims)`);
|
|
50
|
+
ui.step("walking", `tracked .ts / .tsx / .js / .jsx${pyParser ? " / .py" : ""} files`);
|
|
51
|
+
const collected = [];
|
|
52
|
+
let filesParsed = 0;
|
|
53
|
+
let tsFiles = 0;
|
|
54
|
+
let pyFiles = 0;
|
|
55
|
+
for await (const e of tsParser.parseRepo({
|
|
56
|
+
cwd: meta.rootPath,
|
|
57
|
+
onProgress: (n) => {
|
|
58
|
+
filesParsed = n;
|
|
59
|
+
tsFiles = n;
|
|
60
|
+
if (n % 25 === 0)
|
|
61
|
+
ui.raw(`\r${kleur.gray("›")} parsed ${n} TS/JS files `);
|
|
62
|
+
},
|
|
63
|
+
})) {
|
|
64
|
+
collected.push(e);
|
|
65
|
+
}
|
|
66
|
+
if (pyParser) {
|
|
67
|
+
for await (const e of pyParser.parseRepo({
|
|
68
|
+
cwd: meta.rootPath,
|
|
69
|
+
onProgress: (n) => {
|
|
70
|
+
pyFiles = n;
|
|
71
|
+
if (n % 25 === 0)
|
|
72
|
+
ui.raw(`\r${kleur.gray("›")} parsed ${tsFiles} TS/JS + ${n} Python files `);
|
|
73
|
+
},
|
|
74
|
+
})) {
|
|
75
|
+
collected.push(e);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
ui.raw("\n");
|
|
79
|
+
filesParsed = tsFiles + pyFiles;
|
|
80
|
+
ui.success(`Parsed ${filesParsed} files (${tsFiles} TS/JS, ${pyFiles} Python) → ${collected.length} entities`);
|
|
81
|
+
if (collected.length === 0) {
|
|
82
|
+
ui.warn("No entities extracted. Either no source files are tracked, or all are tests/declarations.");
|
|
83
|
+
s.close();
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
// Embed in batches.
|
|
87
|
+
const batchSize = opts.embedBatchSize ?? 32;
|
|
88
|
+
for (let i = 0; i < collected.length; i += batchSize) {
|
|
89
|
+
const batch = collected.slice(i, i + batchSize);
|
|
90
|
+
const texts = batch.map((e) => entities.entityEmbeddingText(e));
|
|
91
|
+
const vecs = await embedder.embed(texts);
|
|
92
|
+
for (let j = 0; j < batch.length; j++)
|
|
93
|
+
batch[j].embedding = vecs[j];
|
|
94
|
+
const bar = formatProgress(Math.min(i + batchSize, collected.length), collected.length);
|
|
95
|
+
ui.raw(`\r${kleur.gray("›")} embedding ${bar} `);
|
|
96
|
+
}
|
|
97
|
+
ui.raw("\n");
|
|
98
|
+
s.upsertEntities(collected, embedder.name);
|
|
99
|
+
ui.success(`Indexed ${collected.length} entities (${embedder.name})`);
|
|
100
|
+
// Quick summary by language + kind.
|
|
101
|
+
const byLang = s.countEntitiesByLanguage();
|
|
102
|
+
process.stdout.write("\n By language:\n");
|
|
103
|
+
for (const row of byLang) {
|
|
104
|
+
process.stdout.write(` ${row.language.padEnd(12)} ${row.n}\n`);
|
|
105
|
+
}
|
|
106
|
+
s.close();
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* `mneme clones` — detect semantic clones from indexed entities.
|
|
111
|
+
*/
|
|
112
|
+
export async function clonesCommand(opts) {
|
|
113
|
+
if (!(await git.isGitRepo(opts.cwd))) {
|
|
114
|
+
ui.error("Not in a git repo. Run `mneme init` first.");
|
|
115
|
+
return 1;
|
|
116
|
+
}
|
|
117
|
+
const meta = await git.getRepoMeta(opts.cwd);
|
|
118
|
+
const s = new store.MnemeStore(dbPath(meta.rootPath));
|
|
119
|
+
if (s.countEntitiesWithEmbedding() < 2) {
|
|
120
|
+
ui.error("Not enough indexed entities. Run `mneme entities` first to parse and embed your codebase.");
|
|
121
|
+
s.close();
|
|
122
|
+
return 1;
|
|
123
|
+
}
|
|
124
|
+
const all = Array.from(s.iterEmbeddedEntities());
|
|
125
|
+
s.close();
|
|
126
|
+
const detector = new entities.CosineCloneDetector();
|
|
127
|
+
const clusters = await detector.detect({
|
|
128
|
+
entities: all,
|
|
129
|
+
threshold: opts.threshold,
|
|
130
|
+
maxClusterSize: 12,
|
|
131
|
+
});
|
|
132
|
+
if (opts.json) {
|
|
133
|
+
process.stdout.write(JSON.stringify(clusters.slice(0, opts.topN ?? 20), null, 2) + "\n");
|
|
134
|
+
return 0;
|
|
135
|
+
}
|
|
136
|
+
ui.banner();
|
|
137
|
+
process.stdout.write(kleur.bold().magenta("Clone clusters") +
|
|
138
|
+
kleur.gray(` (threshold ${opts.threshold ?? entities.DEFAULT_CLONE_THRESHOLD} · ${all.length} entities · ${clusters.length} clusters)`) +
|
|
139
|
+
"\n\n");
|
|
140
|
+
if (clusters.length === 0) {
|
|
141
|
+
ui.warn("No clusters above threshold. Try lowering with --threshold 0.7.");
|
|
142
|
+
return 0;
|
|
143
|
+
}
|
|
144
|
+
for (const c of clusters.slice(0, opts.topN ?? 20)) {
|
|
145
|
+
process.stdout.write(` ${kleur.bold().cyan("●")} ${kleur.bold(c.id)} ` +
|
|
146
|
+
kleur.gray(`cohesion ${c.cohesion.toFixed(3)} · ${c.members.length} members`) +
|
|
147
|
+
"\n");
|
|
148
|
+
for (const m of c.members) {
|
|
149
|
+
process.stdout.write(` ${kleur.gray(m.kind.padEnd(8))} ${kleur.white(m.name)} ${kleur.gray(`(${m.filePath}:${m.startLine})`)}\n`);
|
|
150
|
+
}
|
|
151
|
+
process.stdout.write("\n");
|
|
152
|
+
}
|
|
153
|
+
return 0;
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=clones.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clones.js","sourceRoot":"","sources":["../../src/commands/clones.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAe,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,EAAE,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAO9C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAA0B;IAC9D,EAAE,CAAC,MAAM,EAAE,CAAC;IACZ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QACrC,EAAE,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;QACvD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7C,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,CAAC,GAAG,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEtD,sEAAsE;IACtE,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,gBAAgB,EAAE,CAAC;IACjD,IAAI,CAAC;QACH,MAAM,QAAQ,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,EAAE,CAAC,KAAK,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;QACjC,CAAC,CAAC,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,CAAC;IACX,CAAC;IAED,8DAA8D;IAC9D,IAAI,QAAQ,GAAiC,IAAI,CAAC;IAClD,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,QAAQ,CAAC,YAAY,EAAE,CAAC;QACtC,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC;QAClB,QAAQ,GAAG,CAAC,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QACP,qEAAqE;IACvE,CAAC;IAED,MAAM,SAAS,GAAG,CAAC,wCAAwC,CAAC,CAAC;IAC7D,IAAI,QAAQ;QAAE,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC7C,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAE1C,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;IACnC,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC;QACrC,QAAQ,EAAE,GAAG,CAAC,UAAU,CAAC,QAAQ;QACjC,KAAK,EAAE,GAAG,CAAC,UAAU,CAAC,KAAK;QAC3B,OAAO,EAAE,GAAG,CAAC,UAAU,CAAC,OAAO;KAChC,CAAC,CAAC;IACH,EAAE,CAAC,GAAG,CAAC,qBAAqB,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,UAAU,QAAQ,CAAC,CAAC;IAE3E,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,kCAAkC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IACvF,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,KAAK,EAAE,MAAM,CAAC,IAAI,QAAQ,CAAC,SAAS,CAAC;QACvC,GAAG,EAAE,IAAI,CAAC,QAAQ;QAClB,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE;YAChB,WAAW,GAAG,CAAC,CAAC;YAChB,OAAO,GAAG,CAAC,CAAC;YACZ,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC;gBAAE,EAAE,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC;QAChF,CAAC;KACF,CAAC,EAAE,CAAC;QACH,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IACD,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,KAAK,EAAE,MAAM,CAAC,IAAI,QAAQ,CAAC,SAAS,CAAC;YACvC,GAAG,EAAE,IAAI,CAAC,QAAQ;YAClB,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE;gBAChB,OAAO,GAAG,CAAC,CAAC;gBACZ,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC;oBAAE,EAAE,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,OAAO,YAAY,CAAC,oBAAoB,CAAC,CAAC;YACpG,CAAC;SACF,CAAC,EAAE,CAAC;YACH,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACb,WAAW,GAAG,OAAO,GAAG,OAAO,CAAC;IAChC,EAAE,CAAC,OAAO,CAAC,UAAU,WAAW,WAAW,OAAO,WAAW,OAAO,cAAc,SAAS,CAAC,MAAM,WAAW,CAAC,CAAC;IAE/G,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,EAAE,CAAC,IAAI,CAAC,2FAA2F,CAAC,CAAC;QACrG,CAAC,CAAC,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,CAAC;IACX,CAAC;IAED,oBAAoB;IACpB,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,IAAI,EAAE,CAAC;IAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,IAAI,SAAS,EAAE,CAAC;QACrD,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC;QAChD,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;QAChE,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE;YAAE,KAAK,CAAC,CAAC,CAAE,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACrE,MAAM,GAAG,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,SAAS,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;QACxF,EAAE,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,eAAe,GAAG,OAAO,CAAC,CAAC;IACxD,CAAC;IACD,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAEb,CAAC,CAAC,cAAc,CAAC,SAAS,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;IAE3C,EAAE,CAAC,OAAO,CAAC,WAAW,SAAS,CAAC,MAAM,cAAc,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAC;IAEtE,oCAAoC;IACpC,MAAM,MAAM,GAAG,CAAC,CAAC,uBAAuB,EAAE,CAAC;IAC3C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAC3C,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;IACpE,CAAC;IAED,CAAC,CAAC,KAAK,EAAE,CAAC;IACV,OAAO,CAAC,CAAC;AACX,CAAC;AASD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAA0B;IAC5D,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QACrC,EAAE,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;QACvD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7C,MAAM,CAAC,GAAG,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEtD,IAAI,CAAC,CAAC,0BAA0B,EAAE,GAAG,CAAC,EAAE,CAAC;QACvC,EAAE,CAAC,KAAK,CACN,2FAA2F,CAC5F,CAAC;QACF,CAAC,CAAC,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,oBAAoB,EAAE,CAAC,CAAC;IACjD,CAAC,CAAC,KAAK,EAAE,CAAC;IAEV,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,mBAAmB,EAAE,CAAC;IACpD,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC;QACrC,QAAQ,EAAE,GAAG;QACb,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,cAAc,EAAE,EAAE;KACnB,CAAC,CAAC;IAEH,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QACzF,OAAO,CAAC,CAAC;IACX,CAAC;IAED,EAAE,CAAC,MAAM,EAAE,CAAC;IACZ,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,gBAAgB,CAAC;QACpC,KAAK,CAAC,IAAI,CACR,gBAAgB,IAAI,CAAC,SAAS,IAAI,QAAQ,CAAC,uBAAuB,QAAQ,GAAG,CAAC,MAAM,iBAAiB,QAAQ,CAAC,MAAM,YAAY,CACjI;QACD,MAAM,CACT,CAAC;IAEF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,EAAE,CAAC,IAAI,CAAC,iEAAiE,CAAC,CAAC;QAC3E,OAAO,CAAC,CAAC;IACX,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI;YACjD,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,MAAM,UAAU,CAAC;YAC/E,IAAI,CACP,CAAC;QACF,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;YAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,SAAS,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,SAAS,GAAG,CAAC,IAAI,CACjH,CAAC;QACJ,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface ConscienceCommandOptions {
|
|
2
|
+
cwd: string;
|
|
3
|
+
/** Explicit file list (relative to repo root). */
|
|
4
|
+
files?: string[];
|
|
5
|
+
/** Path to a unified diff file (e.g. exported PR). */
|
|
6
|
+
diffFile?: string;
|
|
7
|
+
/** "-" reads diff from stdin. */
|
|
8
|
+
stdin?: boolean;
|
|
9
|
+
/** How recent past commits must be to count, in days. */
|
|
10
|
+
recencyDays?: number;
|
|
11
|
+
topN?: number;
|
|
12
|
+
json?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare function conscienceCommand(opts: ConscienceCommandOptions): Promise<number>;
|
|
15
|
+
//# sourceMappingURL=conscience.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conscience.d.ts","sourceRoot":"","sources":["../../src/commands/conscience.ts"],"names":[],"mappings":"AAyBA,MAAM,WAAW,wBAAwB;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAYD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwEvF"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mneme conscience` — review co-pilot powered by your repo's own history.
|
|
3
|
+
*
|
|
4
|
+
* Given a set of files about to change (typed in, fed via stdin, or extracted
|
|
5
|
+
* from a unified diff file), Mneme finds:
|
|
6
|
+
*
|
|
7
|
+
* 1. Past commits that touched the same files
|
|
8
|
+
* 2. Whether those commits were correlated with incidents
|
|
9
|
+
* 3. A risk score = f(incident count, recency, files-overlap depth)
|
|
10
|
+
*
|
|
11
|
+
* The reviewer reads:
|
|
12
|
+
*
|
|
13
|
+
* ● PR #98 (3 of 4 files match) RISK: 0.78
|
|
14
|
+
* "introduce idempotency"
|
|
15
|
+
* → 1 incident within 48h: INC-1287
|
|
16
|
+
*
|
|
17
|
+
* Phase-2 hook: when entity-level diffs are available, similarity becomes
|
|
18
|
+
* semantic instead of file-overlap. The contract here doesn't change.
|
|
19
|
+
*/
|
|
20
|
+
import { readFileSync } from "node:fs";
|
|
21
|
+
import kleur from "kleur";
|
|
22
|
+
import { git, store, util } from "@mneme-ai/core";
|
|
23
|
+
import { dbPath } from "../paths.js";
|
|
24
|
+
import { ui } from "../ui.js";
|
|
25
|
+
export async function conscienceCommand(opts) {
|
|
26
|
+
if (!(await git.isGitRepo(opts.cwd))) {
|
|
27
|
+
ui.error("Not in a git repo. Run `mneme init` first.");
|
|
28
|
+
return 1;
|
|
29
|
+
}
|
|
30
|
+
const meta = await git.getRepoMeta(opts.cwd);
|
|
31
|
+
const s = new store.MnemeStore(dbPath(meta.rootPath));
|
|
32
|
+
// 1. Resolve the set of files this PR is changing.
|
|
33
|
+
const files = await resolveFileSet(opts);
|
|
34
|
+
if (files.length === 0) {
|
|
35
|
+
ui.error("No files supplied. Pass file paths as args, --diff-file <path>, or pipe a diff via --stdin.");
|
|
36
|
+
s.close();
|
|
37
|
+
return 1;
|
|
38
|
+
}
|
|
39
|
+
// 2. Load all commits, score each by:
|
|
40
|
+
// - file overlap (Jaccard against the changed set)
|
|
41
|
+
// - recency (within `recencyDays`)
|
|
42
|
+
// - incident correlations (any commit→incident edge in store)
|
|
43
|
+
const all = util.loadAllCommits(s);
|
|
44
|
+
const recencyDays = opts.recencyDays ?? 365;
|
|
45
|
+
const cutoff = Date.now() - recencyDays * 24 * 60 * 60 * 1000;
|
|
46
|
+
const fileSet = new Set(files.map(normalizePath));
|
|
47
|
+
const related = [];
|
|
48
|
+
for (const c of all) {
|
|
49
|
+
const t = Date.parse(c.authorDate);
|
|
50
|
+
if (!Number.isFinite(t) || t < cutoff)
|
|
51
|
+
continue;
|
|
52
|
+
const commitFiles = new Set(c.files.map(normalizePath));
|
|
53
|
+
const overlap = countOverlap(commitFiles, fileSet);
|
|
54
|
+
if (overlap === 0)
|
|
55
|
+
continue;
|
|
56
|
+
const overlapRatio = overlap / fileSet.size;
|
|
57
|
+
const recencyScore = Math.max(0, 1 - (Date.now() - t) / (recencyDays * 24 * 60 * 60 * 1000));
|
|
58
|
+
const incidents = findCorrelatedIncidents(s, c.hash);
|
|
59
|
+
const incidentScore = Math.min(1, incidents.length * 0.4);
|
|
60
|
+
// Composite risk: overlap matters most, incidents next, recency last.
|
|
61
|
+
const risk = clamp01(0.5 * overlapRatio + 0.35 * incidentScore + 0.15 * recencyScore);
|
|
62
|
+
related.push({
|
|
63
|
+
commit: c,
|
|
64
|
+
fileOverlap: overlap,
|
|
65
|
+
fileOverlapRatio: overlapRatio,
|
|
66
|
+
recencyScore,
|
|
67
|
+
incidentIds: incidents.map((i) => i.id),
|
|
68
|
+
incidentCount: incidents.length,
|
|
69
|
+
riskScore: risk,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
related.sort((a, b) => b.riskScore - a.riskScore);
|
|
73
|
+
const top = related.slice(0, opts.topN ?? 8);
|
|
74
|
+
if (opts.json) {
|
|
75
|
+
process.stdout.write(JSON.stringify({ files, top }, null, 2) + "\n");
|
|
76
|
+
s.close();
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
printConscience(files, top, s);
|
|
80
|
+
s.close();
|
|
81
|
+
return 0;
|
|
82
|
+
}
|
|
83
|
+
function findCorrelatedIncidents(s, commitHash) {
|
|
84
|
+
const rows = s.db
|
|
85
|
+
.prepare(`SELECT i.id AS id, i.title AS title FROM correlations c
|
|
86
|
+
JOIN incidents i ON i.id = c.to_id
|
|
87
|
+
WHERE c.from_kind = 'commit' AND c.from_id = ?`)
|
|
88
|
+
.all(commitHash);
|
|
89
|
+
return rows;
|
|
90
|
+
}
|
|
91
|
+
async function resolveFileSet(opts) {
|
|
92
|
+
if (opts.files && opts.files.length > 0)
|
|
93
|
+
return opts.files;
|
|
94
|
+
if (opts.diffFile) {
|
|
95
|
+
const text = readFileSync(opts.diffFile, "utf8");
|
|
96
|
+
return parseDiffFiles(text);
|
|
97
|
+
}
|
|
98
|
+
if (opts.stdin) {
|
|
99
|
+
const text = await readStdin();
|
|
100
|
+
return parseDiffFiles(text);
|
|
101
|
+
}
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
function parseDiffFiles(diff) {
|
|
105
|
+
const out = new Set();
|
|
106
|
+
for (const line of diff.split("\n")) {
|
|
107
|
+
// Match "diff --git a/path b/path" and "+++ b/path".
|
|
108
|
+
const m1 = line.match(/^diff --git a\/(.+?) b\/(.+)$/);
|
|
109
|
+
if (m1) {
|
|
110
|
+
out.add(m1[2]);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const m2 = line.match(/^\+\+\+ b\/(.+)$/);
|
|
114
|
+
if (m2)
|
|
115
|
+
out.add(m2[1]);
|
|
116
|
+
}
|
|
117
|
+
return Array.from(out);
|
|
118
|
+
}
|
|
119
|
+
function readStdin() {
|
|
120
|
+
return new Promise((resolve) => {
|
|
121
|
+
let data = "";
|
|
122
|
+
process.stdin.setEncoding("utf8");
|
|
123
|
+
process.stdin.on("data", (chunk) => (data += chunk));
|
|
124
|
+
process.stdin.on("end", () => resolve(data));
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
function normalizePath(p) {
|
|
128
|
+
return p.replace(/\\/g, "/").replace(/^\.\//, "").toLowerCase();
|
|
129
|
+
}
|
|
130
|
+
function countOverlap(a, b) {
|
|
131
|
+
let n = 0;
|
|
132
|
+
for (const x of a)
|
|
133
|
+
if (b.has(x))
|
|
134
|
+
n++;
|
|
135
|
+
return n;
|
|
136
|
+
}
|
|
137
|
+
function clamp01(n) {
|
|
138
|
+
return Math.min(1, Math.max(0, n));
|
|
139
|
+
}
|
|
140
|
+
function riskColor(risk) {
|
|
141
|
+
if (risk >= 0.7)
|
|
142
|
+
return kleur.red;
|
|
143
|
+
if (risk >= 0.4)
|
|
144
|
+
return kleur.yellow;
|
|
145
|
+
return kleur.green;
|
|
146
|
+
}
|
|
147
|
+
function riskLabel(risk) {
|
|
148
|
+
if (risk >= 0.7)
|
|
149
|
+
return "HIGH";
|
|
150
|
+
if (risk >= 0.4)
|
|
151
|
+
return "MED";
|
|
152
|
+
return "LOW";
|
|
153
|
+
}
|
|
154
|
+
function printConscience(files, top, s) {
|
|
155
|
+
ui.banner();
|
|
156
|
+
process.stdout.write(`${kleur.bold().cyan("Conscience")} ${kleur.gray("PR risk co-pilot")}\n\n`);
|
|
157
|
+
process.stdout.write(` ${kleur.bold("changing")} ${files.length} file(s):\n`);
|
|
158
|
+
for (const f of files.slice(0, 12)) {
|
|
159
|
+
process.stdout.write(` ${kleur.gray("·")} ${f}\n`);
|
|
160
|
+
}
|
|
161
|
+
if (files.length > 12)
|
|
162
|
+
process.stdout.write(` ${kleur.gray(`…and ${files.length - 12} more`)}\n`);
|
|
163
|
+
process.stdout.write("\n");
|
|
164
|
+
if (top.length === 0) {
|
|
165
|
+
ui.success("No historically related commits in the recency window. Looks safe.");
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
process.stdout.write(`${kleur.bold().magenta("Past commits that touched the same files")}\n\n`);
|
|
169
|
+
for (const r of top) {
|
|
170
|
+
const c = r.commit;
|
|
171
|
+
const color = riskColor(r.riskScore);
|
|
172
|
+
const date = c.authorDate.slice(0, 10);
|
|
173
|
+
const ref = c.prNumber ? `PR #${c.prNumber}` : c.shortHash;
|
|
174
|
+
process.stdout.write(` ${color("●")} ${kleur.bold(ref)} ${kleur.gray(`[${date} · ${c.authorName}]`)} ` +
|
|
175
|
+
`${color(`RISK ${riskLabel(r.riskScore)}`)} ${kleur.gray(`(${r.riskScore.toFixed(2)})`)}\n` +
|
|
176
|
+
` ${kleur.white(c.subject)}\n` +
|
|
177
|
+
` ${kleur.gray(`${r.fileOverlap}/${files.length} files match`)}` +
|
|
178
|
+
(r.incidentCount > 0
|
|
179
|
+
? ` ${kleur.red(`⚠ ${r.incidentCount} incident(s) correlated`)}`
|
|
180
|
+
: "") +
|
|
181
|
+
"\n");
|
|
182
|
+
if (r.incidentCount > 0) {
|
|
183
|
+
for (const id of r.incidentIds.slice(0, 3)) {
|
|
184
|
+
const inc = s.db
|
|
185
|
+
.prepare("SELECT title, severity FROM incidents WHERE id = ?")
|
|
186
|
+
.get(id);
|
|
187
|
+
if (inc) {
|
|
188
|
+
process.stdout.write(` ${kleur.gray("→")} ${kleur.bold(id)} ${kleur.gray(`[${inc.severity}]`)} ${inc.title}\n`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
process.stdout.write("\n");
|
|
193
|
+
}
|
|
194
|
+
const high = top.filter((r) => r.riskScore >= 0.7).length;
|
|
195
|
+
if (high > 0) {
|
|
196
|
+
process.stdout.write(kleur.red(` ${high} HIGH-RISK historical match${high === 1 ? "" : "es"} — recommend a careful review pass before merge.\n`));
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
process.stdout.write(kleur.gray(` No HIGH risk found. Consider this an advisory: review with normal rigor.\n`));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=conscience.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conscience.js","sourceRoot":"","sources":["../../src/commands/conscience.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
type CorrelateSource = "pager" | "manual";
|
|
2
|
+
export interface CorrelateCommandOptions {
|
|
3
|
+
cwd: string;
|
|
4
|
+
source?: CorrelateSource;
|
|
5
|
+
org?: string;
|
|
6
|
+
project?: string;
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
file?: string;
|
|
9
|
+
since?: string;
|
|
10
|
+
until?: string;
|
|
11
|
+
windowDays?: number;
|
|
12
|
+
threshold?: number;
|
|
13
|
+
topN?: number;
|
|
14
|
+
json?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function correlateCommand(opts: CorrelateCommandOptions): Promise<number>;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=correlate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"correlate.d.ts","sourceRoot":"","sources":["../../src/commands/correlate.ts"],"names":[],"mappings":"AAWA,KAAK,eAAe,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE1C,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,eAAe,CAAC;IAEzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqGrF"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import kleur from "kleur";
|
|
2
|
+
import { git, store } from "@mneme-ai/core";
|
|
3
|
+
import { TemporalCorrelationEngine, SentryAdapter, ManualJsonAdapter, } from "@mneme-ai/correlator";
|
|
4
|
+
import { dbPath } from "../paths.js";
|
|
5
|
+
import { readConfig } from "../config.js";
|
|
6
|
+
import { ui } from "../ui.js";
|
|
7
|
+
export async function correlateCommand(opts) {
|
|
8
|
+
if (!opts.source) {
|
|
9
|
+
return printPlanned();
|
|
10
|
+
}
|
|
11
|
+
if (!(await git.isGitRepo(opts.cwd))) {
|
|
12
|
+
ui.error("Not in a git repo. Run `mneme init` first.");
|
|
13
|
+
return 1;
|
|
14
|
+
}
|
|
15
|
+
const meta = await git.getRepoMeta(opts.cwd);
|
|
16
|
+
const cfg = readConfig(meta.rootPath);
|
|
17
|
+
const s = new store.MnemeStore(dbPath(meta.rootPath));
|
|
18
|
+
if (s.countCommits() === 0) {
|
|
19
|
+
ui.error("Memory is empty. Run `mneme index` first.");
|
|
20
|
+
s.close();
|
|
21
|
+
return 1;
|
|
22
|
+
}
|
|
23
|
+
ui.banner();
|
|
24
|
+
process.stdout.write(`${kleur.bold().magenta("Correlate")} ${kleur.gray(`source=${opts.source}`)}\n\n`);
|
|
25
|
+
// 1. Pull commits within the window from the store.
|
|
26
|
+
const sinceMs = Date.parse(opts.since ?? "") ||
|
|
27
|
+
Date.now() - (opts.windowDays ?? 90) * 24 * 60 * 60 * 1000;
|
|
28
|
+
const sinceIso = new Date(sinceMs).toISOString();
|
|
29
|
+
const commits = loadCommitsSince(s, sinceIso);
|
|
30
|
+
ui.step("commits", `${commits.length} loaded since ${sinceIso.slice(0, 10)}`);
|
|
31
|
+
// 2. Fetch incidents from the chosen source.
|
|
32
|
+
let incidents = [];
|
|
33
|
+
if (opts.source === "pager") {
|
|
34
|
+
if (!opts.org || !opts.project) {
|
|
35
|
+
ui.error("`--source pager` requires `--org <slug>` and `--project <slug>`.");
|
|
36
|
+
s.close();
|
|
37
|
+
return 1;
|
|
38
|
+
}
|
|
39
|
+
const apiToken = process.env["MNEME_PAGER_TOKEN"] ?? process.env["SENTRY_AUTH_TOKEN"];
|
|
40
|
+
if (!apiToken) {
|
|
41
|
+
ui.error("Set MNEME_PAGER_TOKEN in your environment.");
|
|
42
|
+
ui.dim("(Currently the pager adapter speaks the Sentry REST API; SENTRY_AUTH_TOKEN also works.)");
|
|
43
|
+
s.close();
|
|
44
|
+
return 1;
|
|
45
|
+
}
|
|
46
|
+
const adapter = new SentryAdapter({
|
|
47
|
+
orgSlug: opts.org,
|
|
48
|
+
projectSlug: opts.project,
|
|
49
|
+
apiToken,
|
|
50
|
+
baseUrl: opts.baseUrl,
|
|
51
|
+
});
|
|
52
|
+
ui.step("pager", `fetching incidents from ${opts.org}/${opts.project} …`);
|
|
53
|
+
incidents = await adapter.fetch({ since: sinceIso, until: opts.until });
|
|
54
|
+
}
|
|
55
|
+
else if (opts.source === "manual") {
|
|
56
|
+
if (!opts.file) {
|
|
57
|
+
ui.error("`--source manual` requires `--file <path-to-incidents.json>`.");
|
|
58
|
+
s.close();
|
|
59
|
+
return 1;
|
|
60
|
+
}
|
|
61
|
+
const adapter = new ManualJsonAdapter(opts.file);
|
|
62
|
+
ui.step("manual", `loading from ${opts.file}`);
|
|
63
|
+
incidents = await adapter.fetch({ since: sinceIso, until: opts.until });
|
|
64
|
+
}
|
|
65
|
+
ui.step("incidents", `${incidents.length} loaded`);
|
|
66
|
+
if (incidents.length === 0) {
|
|
67
|
+
ui.warn("No incidents in range. Try widening --since or --window-days.");
|
|
68
|
+
s.close();
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
// 3. Run the engine.
|
|
72
|
+
const windowMs = (opts.windowDays ?? 7) * 24 * 60 * 60 * 1000;
|
|
73
|
+
const engine = new TemporalCorrelationEngine();
|
|
74
|
+
const correlations = await engine.correlate({
|
|
75
|
+
commits,
|
|
76
|
+
incidents,
|
|
77
|
+
windowMs,
|
|
78
|
+
});
|
|
79
|
+
// 4. Persist + filter to top-N at threshold.
|
|
80
|
+
s.upsertIncidents(incidents);
|
|
81
|
+
s.upsertCorrelations(correlations);
|
|
82
|
+
const threshold = opts.threshold ?? 0.3;
|
|
83
|
+
const filtered = correlations
|
|
84
|
+
.filter((c) => c.weight >= threshold)
|
|
85
|
+
.sort((a, b) => b.weight - a.weight)
|
|
86
|
+
.slice(0, opts.topN ?? 20);
|
|
87
|
+
if (opts.json) {
|
|
88
|
+
process.stdout.write(JSON.stringify(filtered, null, 2) + "\n");
|
|
89
|
+
s.close();
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
printCorrelations(filtered, incidents, commits, meta);
|
|
93
|
+
s.close();
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
function loadCommitsSince(s, sinceIso) {
|
|
97
|
+
const rows = s.db
|
|
98
|
+
.prepare(`SELECT * FROM commits WHERE author_date >= ? ORDER BY author_date ASC`)
|
|
99
|
+
.all(sinceIso);
|
|
100
|
+
return rows.map((r) => ({
|
|
101
|
+
hash: String(r.hash),
|
|
102
|
+
shortHash: String(r.short_hash),
|
|
103
|
+
authorName: String(r.author_name),
|
|
104
|
+
authorEmail: String(r.author_email),
|
|
105
|
+
authorDate: String(r.author_date),
|
|
106
|
+
committerDate: String(r.committer_date),
|
|
107
|
+
subject: String(r.subject),
|
|
108
|
+
body: String(r.body),
|
|
109
|
+
parents: String(r.parents).split(/\s+/).filter(Boolean),
|
|
110
|
+
files: filesForCommit(s, String(r.hash)),
|
|
111
|
+
prNumber: typeof r.pr_number === "number" ? r.pr_number : undefined,
|
|
112
|
+
prTitle: r.pr_title ? String(r.pr_title) : undefined,
|
|
113
|
+
prBody: r.pr_body ? String(r.pr_body) : undefined,
|
|
114
|
+
issueRefs: r.issue_refs ? JSON.parse(String(r.issue_refs)) : undefined,
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
function filesForCommit(s, hash) {
|
|
118
|
+
return s.filesForCommit(hash);
|
|
119
|
+
}
|
|
120
|
+
function printCorrelations(rows, incidents, commits, repo) {
|
|
121
|
+
const incidentById = new Map(incidents.map((i) => [i.id, i]));
|
|
122
|
+
const commitByHash = new Map(commits.map((c) => [c.hash, c]));
|
|
123
|
+
if (rows.length === 0) {
|
|
124
|
+
ui.warn("No correlations above threshold. Try `--threshold 0.15` or widen `--window-days`.");
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
process.stdout.write(`\n${kleur.bold().magenta("Top correlations")} ${kleur.gray(`(${rows.length})`)}\n\n`);
|
|
128
|
+
for (const r of rows) {
|
|
129
|
+
const commit = commitByHash.get(r.fromId);
|
|
130
|
+
const incident = incidentById.get(r.toId);
|
|
131
|
+
if (!commit || !incident)
|
|
132
|
+
continue;
|
|
133
|
+
const conf = (r.weight * 100).toFixed(0) + "%";
|
|
134
|
+
const date = commit.authorDate.slice(0, 10);
|
|
135
|
+
process.stdout.write(` ${kleur.green("●")} ${kleur.bold(commit.shortHash)} ${kleur.gray(`[${date} · ${commit.authorName}]`)}\n` +
|
|
136
|
+
` ${kleur.white(commit.subject)}\n` +
|
|
137
|
+
` ${kleur.cyan("→ incident:")} ${kleur.bold(incident.id)} ${kleur.gray(`[${incident.severity}]`)} ${incident.title}\n` +
|
|
138
|
+
` ${kleur.yellow("confidence:")} ${conf} ${kleur.gray(r.reason)}\n\n`);
|
|
139
|
+
}
|
|
140
|
+
process.stdout.write(kleur.gray(` Tip: stored ${rows.length} correlation rows in .mneme/mneme.db. Re-run anytime; the engine is deterministic.\n`));
|
|
141
|
+
}
|
|
142
|
+
async function printPlanned() {
|
|
143
|
+
ui.banner();
|
|
144
|
+
process.stdout.write(`${kleur.bold().magenta("Correlate")} ${kleur.gray("(Phase 3)")}\n\n`);
|
|
145
|
+
process.stdout.write([
|
|
146
|
+
"Pull error/incident data from a source and correlate it with the commits",
|
|
147
|
+
"that likely caused it.",
|
|
148
|
+
"",
|
|
149
|
+
kleur.bold("Sources available now:"),
|
|
150
|
+
" mneme correlate --source pager --org <slug> --project <slug>",
|
|
151
|
+
" mneme correlate --source manual --file ./incidents.json",
|
|
152
|
+
"",
|
|
153
|
+
kleur.bold("Common options:"),
|
|
154
|
+
" --since <iso> only consider incidents/commits since this date",
|
|
155
|
+
" --window-days <n> correlation window (default 7)",
|
|
156
|
+
" --threshold <0..1> minimum confidence (default 0.30)",
|
|
157
|
+
" --top <n> top-N rows (default 20)",
|
|
158
|
+
" --json machine-readable output",
|
|
159
|
+
"",
|
|
160
|
+
kleur.bold("Auth:"),
|
|
161
|
+
" Pager: set MNEME_PAGER_TOKEN in your environment",
|
|
162
|
+
"",
|
|
163
|
+
kleur.bold("Output (sample):"),
|
|
164
|
+
" ● a1b2c3d [2025-09-01 · alice]",
|
|
165
|
+
" Refactor payment flow",
|
|
166
|
+
" → incident: INC-1287 [error] Stripe webhook 500",
|
|
167
|
+
" confidence: 87% reason: file overlap + temporal proximity",
|
|
168
|
+
"",
|
|
169
|
+
kleur.gray("Track progress in ROADMAP.md → Phase 3."),
|
|
170
|
+
].join("\n") + "\n");
|
|
171
|
+
return 0;
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=correlate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"correlate.js","sourceRoot":"","sources":["../../src/commands/correlate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface EchoCommandOptions {
|
|
2
|
+
cwd: string;
|
|
3
|
+
/** Look up by stored incident id (e.g. "sentry:12345"). */
|
|
4
|
+
id?: string;
|
|
5
|
+
/** Or freeform — describe the incident in words. */
|
|
6
|
+
query?: string;
|
|
7
|
+
topK?: number;
|
|
8
|
+
json?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* `mneme echo` — given an incident (id or freeform), find the most similar
|
|
12
|
+
* past incidents in the store. Helps on-call answer "have we seen this before?"
|
|
13
|
+
* before they spend an hour reinventing the postmortem.
|
|
14
|
+
*/
|
|
15
|
+
export declare function echoCommand(opts: EchoCommandOptions): Promise<number>;
|
|
16
|
+
//# sourceMappingURL=echo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"echo.d.ts","sourceRoot":"","sources":["../../src/commands/echo.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,2DAA2D;IAC3D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CA2D3E"}
|