memorix 0.5.2 → 0.6.1
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 +18 -1
- package/dist/cli/index.js +483 -72
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/static/app.js +143 -11
- package/dist/dashboard/static/index.html +5 -0
- package/dist/dashboard/static/style.css +197 -0
- package/dist/index.js +450 -70
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/cli/index.js
CHANGED
|
@@ -31,13 +31,16 @@ var init_esm_shims = __esm({
|
|
|
31
31
|
// src/store/persistence.ts
|
|
32
32
|
var persistence_exports = {};
|
|
33
33
|
__export(persistence_exports, {
|
|
34
|
+
getBaseDataDir: () => getBaseDataDir,
|
|
34
35
|
getDbFilePath: () => getDbFilePath,
|
|
35
36
|
getGraphFilePath: () => getGraphFilePath,
|
|
36
37
|
getProjectDataDir: () => getProjectDataDir,
|
|
37
38
|
hasExistingData: () => hasExistingData,
|
|
39
|
+
listProjectDirs: () => listProjectDirs,
|
|
38
40
|
loadGraphJsonl: () => loadGraphJsonl,
|
|
39
41
|
loadIdCounter: () => loadIdCounter,
|
|
40
42
|
loadObservationsJson: () => loadObservationsJson,
|
|
43
|
+
migrateGlobalData: () => migrateGlobalData,
|
|
41
44
|
saveGraphJsonl: () => saveGraphJsonl,
|
|
42
45
|
saveIdCounter: () => saveIdCounter,
|
|
43
46
|
saveObservationsJson: () => saveObservationsJson
|
|
@@ -45,11 +48,105 @@ __export(persistence_exports, {
|
|
|
45
48
|
import { promises as fs } from "fs";
|
|
46
49
|
import path2 from "path";
|
|
47
50
|
import os from "os";
|
|
51
|
+
function sanitizeProjectId(projectId) {
|
|
52
|
+
return projectId.replace(/\//g, "--").replace(/[<>:"|?*\\]/g, "_");
|
|
53
|
+
}
|
|
48
54
|
async function getProjectDataDir(projectId, baseDir) {
|
|
49
|
-
const
|
|
55
|
+
const base = baseDir ?? DEFAULT_DATA_DIR;
|
|
56
|
+
const dirName = sanitizeProjectId(projectId);
|
|
57
|
+
const dataDir = path2.join(base, dirName);
|
|
50
58
|
await fs.mkdir(dataDir, { recursive: true });
|
|
51
59
|
return dataDir;
|
|
52
60
|
}
|
|
61
|
+
function getBaseDataDir(baseDir) {
|
|
62
|
+
return baseDir ?? DEFAULT_DATA_DIR;
|
|
63
|
+
}
|
|
64
|
+
async function listProjectDirs(baseDir) {
|
|
65
|
+
const base = baseDir ?? DEFAULT_DATA_DIR;
|
|
66
|
+
try {
|
|
67
|
+
const entries = await fs.readdir(base, { withFileTypes: true });
|
|
68
|
+
return entries.filter((e) => e.isDirectory()).map((e) => path2.join(base, e.name));
|
|
69
|
+
} catch {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function migrateGlobalData(projectId, baseDir) {
|
|
74
|
+
const base = baseDir ?? DEFAULT_DATA_DIR;
|
|
75
|
+
const globalObsPath = path2.join(base, "observations.json");
|
|
76
|
+
const migratedObsPath = path2.join(base, "observations.json.migrated");
|
|
77
|
+
let sourceObsPath = null;
|
|
78
|
+
try {
|
|
79
|
+
await fs.access(globalObsPath);
|
|
80
|
+
sourceObsPath = globalObsPath;
|
|
81
|
+
} catch {
|
|
82
|
+
try {
|
|
83
|
+
await fs.access(migratedObsPath);
|
|
84
|
+
sourceObsPath = migratedObsPath;
|
|
85
|
+
} catch {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
let globalObs = [];
|
|
90
|
+
try {
|
|
91
|
+
const data = await fs.readFile(sourceObsPath, "utf-8");
|
|
92
|
+
globalObs = JSON.parse(data);
|
|
93
|
+
if (!Array.isArray(globalObs) || globalObs.length === 0) return false;
|
|
94
|
+
} catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
const projectDir2 = await getProjectDataDir(projectId, baseDir);
|
|
98
|
+
const projectObsPath = path2.join(projectDir2, "observations.json");
|
|
99
|
+
let projectObs = [];
|
|
100
|
+
try {
|
|
101
|
+
const data = await fs.readFile(projectObsPath, "utf-8");
|
|
102
|
+
projectObs = JSON.parse(data);
|
|
103
|
+
if (!Array.isArray(projectObs)) projectObs = [];
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
if (projectObs.length >= globalObs.length) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
const existingIds = new Set(projectObs.map((o) => o.id));
|
|
110
|
+
const merged = [...projectObs];
|
|
111
|
+
for (const obs of globalObs) {
|
|
112
|
+
if (!existingIds.has(obs.id)) {
|
|
113
|
+
merged.push(obs);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
merged.sort((a, b) => (a.id ?? 0) - (b.id ?? 0));
|
|
117
|
+
for (const obs of merged) {
|
|
118
|
+
obs.projectId = projectId;
|
|
119
|
+
}
|
|
120
|
+
await fs.writeFile(projectObsPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
121
|
+
for (const file of ["graph.jsonl", "counter.json"]) {
|
|
122
|
+
const src = path2.join(base, file);
|
|
123
|
+
const srcMigrated = path2.join(base, file + ".migrated");
|
|
124
|
+
const dst = path2.join(projectDir2, file);
|
|
125
|
+
for (const source of [src, srcMigrated]) {
|
|
126
|
+
try {
|
|
127
|
+
await fs.access(source);
|
|
128
|
+
await fs.copyFile(source, dst);
|
|
129
|
+
break;
|
|
130
|
+
} catch {
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const maxId = merged.reduce((max, o) => Math.max(max, o.id ?? 0), 0);
|
|
135
|
+
await fs.writeFile(
|
|
136
|
+
path2.join(projectDir2, "counter.json"),
|
|
137
|
+
JSON.stringify({ nextId: maxId + 1 }),
|
|
138
|
+
"utf-8"
|
|
139
|
+
);
|
|
140
|
+
for (const file of ["observations.json", "graph.jsonl", "counter.json"]) {
|
|
141
|
+
const src = path2.join(base, file);
|
|
142
|
+
try {
|
|
143
|
+
await fs.access(src);
|
|
144
|
+
await fs.rename(src, src + ".migrated");
|
|
145
|
+
} catch {
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
53
150
|
function getDbFilePath(projectDir2) {
|
|
54
151
|
return path2.join(projectDir2, "memorix.msp");
|
|
55
152
|
}
|
|
@@ -1132,12 +1229,14 @@ function detectProject(cwd) {
|
|
|
1132
1229
|
const rootPath = getGitRoot(basePath) ?? findPackageRoot(basePath) ?? basePath;
|
|
1133
1230
|
const gitRemote = getGitRemote(rootPath);
|
|
1134
1231
|
if (gitRemote) {
|
|
1135
|
-
const
|
|
1136
|
-
const name2 =
|
|
1137
|
-
return { id, name: name2, gitRemote, rootPath };
|
|
1232
|
+
const id2 = normalizeGitRemote(gitRemote);
|
|
1233
|
+
const name2 = id2.split("/").pop() ?? path3.basename(rootPath);
|
|
1234
|
+
return { id: id2, name: name2, gitRemote, rootPath };
|
|
1138
1235
|
}
|
|
1139
1236
|
const name = path3.basename(rootPath);
|
|
1140
|
-
|
|
1237
|
+
const id = `local/${name}`;
|
|
1238
|
+
console.error(`[memorix] Warning: no git remote found at ${rootPath}, using fallback projectId: ${id}`);
|
|
1239
|
+
return { id, name, rootPath };
|
|
1141
1240
|
}
|
|
1142
1241
|
function findPackageRoot(cwd) {
|
|
1143
1242
|
let dir = path3.resolve(cwd);
|
|
@@ -1700,6 +1799,76 @@ var init_copilot = __esm({
|
|
|
1700
1799
|
}
|
|
1701
1800
|
});
|
|
1702
1801
|
|
|
1802
|
+
// src/rules/adapters/kiro.ts
|
|
1803
|
+
import matter7 from "gray-matter";
|
|
1804
|
+
var KiroAdapter;
|
|
1805
|
+
var init_kiro = __esm({
|
|
1806
|
+
"src/rules/adapters/kiro.ts"() {
|
|
1807
|
+
"use strict";
|
|
1808
|
+
init_esm_shims();
|
|
1809
|
+
init_utils();
|
|
1810
|
+
KiroAdapter = class {
|
|
1811
|
+
source = "kiro";
|
|
1812
|
+
filePatterns = [
|
|
1813
|
+
".kiro/steering/*.md",
|
|
1814
|
+
"AGENTS.md"
|
|
1815
|
+
];
|
|
1816
|
+
parse(filePath, content) {
|
|
1817
|
+
if (filePath.includes(".kiro/steering/")) {
|
|
1818
|
+
return this.parseSteeringRule(filePath, content);
|
|
1819
|
+
}
|
|
1820
|
+
if (filePath.endsWith("AGENTS.md")) {
|
|
1821
|
+
return this.parseAgentsMd(filePath, content);
|
|
1822
|
+
}
|
|
1823
|
+
return [];
|
|
1824
|
+
}
|
|
1825
|
+
generate(rules) {
|
|
1826
|
+
return rules.map((rule, i) => {
|
|
1827
|
+
const fm = {};
|
|
1828
|
+
if (rule.description) fm.description = rule.description;
|
|
1829
|
+
const fileName = rule.id.replace(/^kiro:/, "").replace(/[^a-zA-Z0-9-_]/g, "-") || `rule-${i}`;
|
|
1830
|
+
const body = Object.keys(fm).length > 0 ? matter7.stringify(rule.content, fm) : rule.content;
|
|
1831
|
+
return {
|
|
1832
|
+
filePath: `.kiro/steering/${fileName}.md`,
|
|
1833
|
+
content: body
|
|
1834
|
+
};
|
|
1835
|
+
});
|
|
1836
|
+
}
|
|
1837
|
+
parseSteeringRule(filePath, content) {
|
|
1838
|
+
const { data, content: body } = matter7(content);
|
|
1839
|
+
const trimmed = body.trim();
|
|
1840
|
+
if (!trimmed) return [];
|
|
1841
|
+
const trigger = data.trigger;
|
|
1842
|
+
const alwaysApply = !trigger || trigger === "always";
|
|
1843
|
+
return [{
|
|
1844
|
+
id: generateRuleId("kiro", filePath),
|
|
1845
|
+
content: trimmed,
|
|
1846
|
+
description: data.description,
|
|
1847
|
+
source: "kiro",
|
|
1848
|
+
scope: alwaysApply ? "global" : "path-specific",
|
|
1849
|
+
paths: data.globs,
|
|
1850
|
+
alwaysApply,
|
|
1851
|
+
priority: alwaysApply ? 10 : 5,
|
|
1852
|
+
hash: hashContent(trimmed)
|
|
1853
|
+
}];
|
|
1854
|
+
}
|
|
1855
|
+
parseAgentsMd(filePath, content) {
|
|
1856
|
+
const trimmed = content.trim();
|
|
1857
|
+
if (!trimmed) return [];
|
|
1858
|
+
return [{
|
|
1859
|
+
id: generateRuleId("kiro", filePath),
|
|
1860
|
+
content: trimmed,
|
|
1861
|
+
source: "kiro",
|
|
1862
|
+
scope: "project",
|
|
1863
|
+
alwaysApply: true,
|
|
1864
|
+
priority: 10,
|
|
1865
|
+
hash: hashContent(trimmed)
|
|
1866
|
+
}];
|
|
1867
|
+
}
|
|
1868
|
+
};
|
|
1869
|
+
}
|
|
1870
|
+
});
|
|
1871
|
+
|
|
1703
1872
|
// src/rules/syncer.ts
|
|
1704
1873
|
var syncer_exports = {};
|
|
1705
1874
|
__export(syncer_exports, {
|
|
@@ -1718,6 +1887,7 @@ var init_syncer = __esm({
|
|
|
1718
1887
|
init_windsurf();
|
|
1719
1888
|
init_antigravity();
|
|
1720
1889
|
init_copilot();
|
|
1890
|
+
init_kiro();
|
|
1721
1891
|
RulesSyncer = class {
|
|
1722
1892
|
projectRoot;
|
|
1723
1893
|
adapters;
|
|
@@ -1730,7 +1900,8 @@ var init_syncer = __esm({
|
|
|
1730
1900
|
new CodexAdapter(),
|
|
1731
1901
|
new WindsurfAdapter(),
|
|
1732
1902
|
new AntigravityAdapter(),
|
|
1733
|
-
new CopilotAdapter()
|
|
1903
|
+
new CopilotAdapter(),
|
|
1904
|
+
new KiroAdapter()
|
|
1734
1905
|
];
|
|
1735
1906
|
for (const a of all) {
|
|
1736
1907
|
this.adapters.set(a.source, a);
|
|
@@ -2339,8 +2510,60 @@ var init_antigravity2 = __esm({
|
|
|
2339
2510
|
}
|
|
2340
2511
|
});
|
|
2341
2512
|
|
|
2513
|
+
// src/workspace/mcp-adapters/kiro.ts
|
|
2514
|
+
import { homedir as homedir7 } from "os";
|
|
2515
|
+
import { join as join7 } from "path";
|
|
2516
|
+
var KiroMCPAdapter;
|
|
2517
|
+
var init_kiro2 = __esm({
|
|
2518
|
+
"src/workspace/mcp-adapters/kiro.ts"() {
|
|
2519
|
+
"use strict";
|
|
2520
|
+
init_esm_shims();
|
|
2521
|
+
KiroMCPAdapter = class {
|
|
2522
|
+
source = "kiro";
|
|
2523
|
+
parse(content) {
|
|
2524
|
+
try {
|
|
2525
|
+
const config = JSON.parse(content);
|
|
2526
|
+
const servers = config.mcpServers ?? {};
|
|
2527
|
+
return Object.entries(servers).map(([name, entry]) => ({
|
|
2528
|
+
name,
|
|
2529
|
+
command: entry.command ?? "",
|
|
2530
|
+
args: entry.args ?? [],
|
|
2531
|
+
...entry.env && Object.keys(entry.env).length > 0 ? { env: entry.env } : {},
|
|
2532
|
+
...entry.url ? { url: entry.url } : {}
|
|
2533
|
+
}));
|
|
2534
|
+
} catch {
|
|
2535
|
+
return [];
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
generate(servers) {
|
|
2539
|
+
const mcpServers = {};
|
|
2540
|
+
for (const s of servers) {
|
|
2541
|
+
const entry = {};
|
|
2542
|
+
if (s.url) {
|
|
2543
|
+
entry.url = s.url;
|
|
2544
|
+
} else {
|
|
2545
|
+
entry.command = s.command;
|
|
2546
|
+
entry.args = s.args;
|
|
2547
|
+
}
|
|
2548
|
+
if (s.env && Object.keys(s.env).length > 0) {
|
|
2549
|
+
entry.env = s.env;
|
|
2550
|
+
}
|
|
2551
|
+
mcpServers[s.name] = entry;
|
|
2552
|
+
}
|
|
2553
|
+
return JSON.stringify({ mcpServers }, null, 2);
|
|
2554
|
+
}
|
|
2555
|
+
getConfigPath(projectRoot) {
|
|
2556
|
+
if (projectRoot) {
|
|
2557
|
+
return join7(projectRoot, ".kiro", "settings", "mcp.json");
|
|
2558
|
+
}
|
|
2559
|
+
return join7(homedir7(), ".kiro", "settings", "mcp.json");
|
|
2560
|
+
}
|
|
2561
|
+
};
|
|
2562
|
+
}
|
|
2563
|
+
});
|
|
2564
|
+
|
|
2342
2565
|
// src/workspace/workflow-sync.ts
|
|
2343
|
-
import
|
|
2566
|
+
import matter8 from "gray-matter";
|
|
2344
2567
|
var WorkflowSyncer;
|
|
2345
2568
|
var init_workflow_sync = __esm({
|
|
2346
2569
|
"src/workspace/workflow-sync.ts"() {
|
|
@@ -2355,7 +2578,7 @@ var init_workflow_sync = __esm({
|
|
|
2355
2578
|
let description = "";
|
|
2356
2579
|
let content = raw;
|
|
2357
2580
|
try {
|
|
2358
|
-
const parsed =
|
|
2581
|
+
const parsed = matter8(raw);
|
|
2359
2582
|
description = parsed.data?.description ?? "";
|
|
2360
2583
|
content = parsed.content.trim();
|
|
2361
2584
|
} catch {
|
|
@@ -2377,7 +2600,7 @@ var init_workflow_sync = __esm({
|
|
|
2377
2600
|
if (wf.description) {
|
|
2378
2601
|
fm.description = wf.description;
|
|
2379
2602
|
}
|
|
2380
|
-
const content =
|
|
2603
|
+
const content = matter8.stringify(wf.content, fm);
|
|
2381
2604
|
return {
|
|
2382
2605
|
filePath: `.agents/skills/${safeName}/SKILL.md`,
|
|
2383
2606
|
content
|
|
@@ -2394,7 +2617,7 @@ var init_workflow_sync = __esm({
|
|
|
2394
2617
|
}
|
|
2395
2618
|
fm.globs = "";
|
|
2396
2619
|
fm.alwaysApply = "false";
|
|
2397
|
-
const content =
|
|
2620
|
+
const content = matter8.stringify(wf.content, fm);
|
|
2398
2621
|
return {
|
|
2399
2622
|
filePath: `.cursor/rules/${safeName}.mdc`,
|
|
2400
2623
|
content
|
|
@@ -2582,8 +2805,8 @@ var init_applier = __esm({
|
|
|
2582
2805
|
|
|
2583
2806
|
// src/workspace/engine.ts
|
|
2584
2807
|
import { readFileSync as readFileSync2, readdirSync, existsSync as existsSync4, cpSync, mkdirSync as mkdirSync2 } from "fs";
|
|
2585
|
-
import { join as
|
|
2586
|
-
import { homedir as
|
|
2808
|
+
import { join as join9 } from "path";
|
|
2809
|
+
import { homedir as homedir8 } from "os";
|
|
2587
2810
|
var WorkspaceSyncEngine;
|
|
2588
2811
|
var init_engine2 = __esm({
|
|
2589
2812
|
"src/workspace/engine.ts"() {
|
|
@@ -2595,6 +2818,7 @@ var init_engine2 = __esm({
|
|
|
2595
2818
|
init_claude_code2();
|
|
2596
2819
|
init_copilot2();
|
|
2597
2820
|
init_antigravity2();
|
|
2821
|
+
init_kiro2();
|
|
2598
2822
|
init_workflow_sync();
|
|
2599
2823
|
init_syncer();
|
|
2600
2824
|
init_sanitizer();
|
|
@@ -2608,7 +2832,8 @@ var init_engine2 = __esm({
|
|
|
2608
2832
|
["codex", new CodexMCPAdapter()],
|
|
2609
2833
|
["claude-code", new ClaudeCodeMCPAdapter()],
|
|
2610
2834
|
["copilot", new CopilotMCPAdapter()],
|
|
2611
|
-
["antigravity", new AntigravityMCPAdapter()]
|
|
2835
|
+
["antigravity", new AntigravityMCPAdapter()],
|
|
2836
|
+
["kiro", new KiroMCPAdapter()]
|
|
2612
2837
|
]);
|
|
2613
2838
|
this.workflowSyncer = new WorkflowSyncer();
|
|
2614
2839
|
this.rulesSyncer = new RulesSyncer(projectRoot);
|
|
@@ -2626,7 +2851,8 @@ var init_engine2 = __esm({
|
|
|
2626
2851
|
codex: [],
|
|
2627
2852
|
"claude-code": [],
|
|
2628
2853
|
copilot: [],
|
|
2629
|
-
antigravity: []
|
|
2854
|
+
antigravity: [],
|
|
2855
|
+
kiro: []
|
|
2630
2856
|
};
|
|
2631
2857
|
for (const [target, adapter] of this.adapters) {
|
|
2632
2858
|
const configPath = adapter.getConfigPath(this.projectRoot);
|
|
@@ -2718,13 +2944,14 @@ var init_engine2 = __esm({
|
|
|
2718
2944
|
windsurf: [".windsurf/skills"],
|
|
2719
2945
|
"claude-code": [".claude/skills"],
|
|
2720
2946
|
copilot: [".github/skills", ".copilot/skills"],
|
|
2721
|
-
antigravity: [".agent/skills", ".gemini/skills", ".gemini/antigravity/skills"]
|
|
2947
|
+
antigravity: [".agent/skills", ".gemini/skills", ".gemini/antigravity/skills"],
|
|
2948
|
+
kiro: [".kiro/skills"]
|
|
2722
2949
|
};
|
|
2723
2950
|
/** Get the target skills directory for an agent (null if agent has no skills support) */
|
|
2724
2951
|
getTargetSkillsDir(target) {
|
|
2725
2952
|
const dirs = _WorkspaceSyncEngine.SKILLS_DIRS[target];
|
|
2726
2953
|
if (!dirs || dirs.length === 0) return null;
|
|
2727
|
-
return
|
|
2954
|
+
return join9(this.projectRoot, dirs[0]);
|
|
2728
2955
|
}
|
|
2729
2956
|
/**
|
|
2730
2957
|
* Scan all agent skills directories and collect unique skills.
|
|
@@ -2733,12 +2960,12 @@ var init_engine2 = __esm({
|
|
|
2733
2960
|
const skills = [];
|
|
2734
2961
|
const conflicts = [];
|
|
2735
2962
|
const seen = /* @__PURE__ */ new Map();
|
|
2736
|
-
const home =
|
|
2963
|
+
const home = homedir8();
|
|
2737
2964
|
for (const [agent, dirs] of Object.entries(_WorkspaceSyncEngine.SKILLS_DIRS)) {
|
|
2738
2965
|
for (const dir of dirs) {
|
|
2739
2966
|
const paths = [
|
|
2740
|
-
|
|
2741
|
-
|
|
2967
|
+
join9(this.projectRoot, dir),
|
|
2968
|
+
join9(home, dir)
|
|
2742
2969
|
];
|
|
2743
2970
|
for (const skillsRoot of paths) {
|
|
2744
2971
|
if (!existsSync4(skillsRoot)) continue;
|
|
@@ -2746,7 +2973,7 @@ var init_engine2 = __esm({
|
|
|
2746
2973
|
const entries = readdirSync(skillsRoot, { withFileTypes: true });
|
|
2747
2974
|
for (const entry of entries) {
|
|
2748
2975
|
if (!entry.isDirectory()) continue;
|
|
2749
|
-
const skillMd =
|
|
2976
|
+
const skillMd = join9(skillsRoot, entry.name, "SKILL.md");
|
|
2750
2977
|
if (!existsSync4(skillMd)) continue;
|
|
2751
2978
|
let description = "";
|
|
2752
2979
|
try {
|
|
@@ -2758,7 +2985,7 @@ var init_engine2 = __esm({
|
|
|
2758
2985
|
const newEntry = {
|
|
2759
2986
|
name: entry.name,
|
|
2760
2987
|
description,
|
|
2761
|
-
sourcePath:
|
|
2988
|
+
sourcePath: join9(skillsRoot, entry.name),
|
|
2762
2989
|
sourceAgent: agent
|
|
2763
2990
|
};
|
|
2764
2991
|
const existing = seen.get(entry.name);
|
|
@@ -2795,7 +3022,7 @@ var init_engine2 = __esm({
|
|
|
2795
3022
|
}
|
|
2796
3023
|
for (const skill of skills) {
|
|
2797
3024
|
if (skill.sourceAgent === target) continue;
|
|
2798
|
-
const dest =
|
|
3025
|
+
const dest = join9(targetDir, skill.name);
|
|
2799
3026
|
if (existsSync4(dest)) {
|
|
2800
3027
|
skipped.push(`${skill.name} (already exists in ${target})`);
|
|
2801
3028
|
continue;
|
|
@@ -2811,13 +3038,13 @@ var init_engine2 = __esm({
|
|
|
2811
3038
|
}
|
|
2812
3039
|
scanWorkflows() {
|
|
2813
3040
|
const workflows = [];
|
|
2814
|
-
const wfDir =
|
|
3041
|
+
const wfDir = join9(this.projectRoot, ".windsurf", "workflows");
|
|
2815
3042
|
if (!existsSync4(wfDir)) return workflows;
|
|
2816
3043
|
try {
|
|
2817
3044
|
const files = readdirSync(wfDir).filter((f) => f.endsWith(".md"));
|
|
2818
3045
|
for (const file of files) {
|
|
2819
3046
|
try {
|
|
2820
|
-
const content = readFileSync2(
|
|
3047
|
+
const content = readFileSync2(join9(wfDir, file), "utf-8");
|
|
2821
3048
|
workflows.push(this.workflowSyncer.parseWindsurfWorkflow(file, content));
|
|
2822
3049
|
} catch {
|
|
2823
3050
|
}
|
|
@@ -2905,7 +3132,8 @@ var init_engine2 = __esm({
|
|
|
2905
3132
|
codex: "codex",
|
|
2906
3133
|
windsurf: "windsurf",
|
|
2907
3134
|
copilot: "copilot",
|
|
2908
|
-
antigravity: "antigravity"
|
|
3135
|
+
antigravity: "antigravity",
|
|
3136
|
+
kiro: "kiro"
|
|
2909
3137
|
};
|
|
2910
3138
|
return map[target] ?? null;
|
|
2911
3139
|
}
|
|
@@ -3076,6 +3304,18 @@ async function detectInstalledAgents() {
|
|
|
3076
3304
|
agents.push("kiro");
|
|
3077
3305
|
} catch {
|
|
3078
3306
|
}
|
|
3307
|
+
const codexDir = path5.join(home, ".codex");
|
|
3308
|
+
try {
|
|
3309
|
+
await fs3.access(codexDir);
|
|
3310
|
+
agents.push("codex");
|
|
3311
|
+
} catch {
|
|
3312
|
+
}
|
|
3313
|
+
const antigravityDir = path5.join(home, ".gemini", "antigravity");
|
|
3314
|
+
try {
|
|
3315
|
+
await fs3.access(antigravityDir);
|
|
3316
|
+
agents.push("antigravity");
|
|
3317
|
+
} catch {
|
|
3318
|
+
}
|
|
3079
3319
|
return agents;
|
|
3080
3320
|
}
|
|
3081
3321
|
async function installHooks(agent, projectRoot, global = false) {
|
|
@@ -3152,15 +3392,34 @@ async function installAgentRules(agent, projectRoot) {
|
|
|
3152
3392
|
case "copilot":
|
|
3153
3393
|
rulesPath = path5.join(projectRoot, ".github", "copilot-instructions.md");
|
|
3154
3394
|
break;
|
|
3395
|
+
case "codex":
|
|
3396
|
+
rulesPath = path5.join(projectRoot, "AGENTS.md");
|
|
3397
|
+
break;
|
|
3398
|
+
case "kiro":
|
|
3399
|
+
rulesPath = path5.join(projectRoot, ".kiro", "rules", "memorix.md");
|
|
3400
|
+
break;
|
|
3155
3401
|
default:
|
|
3156
|
-
|
|
3402
|
+
rulesPath = path5.join(projectRoot, ".agent", "rules", "memorix.md");
|
|
3403
|
+
break;
|
|
3157
3404
|
}
|
|
3158
3405
|
try {
|
|
3159
3406
|
await fs3.mkdir(path5.dirname(rulesPath), { recursive: true });
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3407
|
+
if (agent === "codex") {
|
|
3408
|
+
try {
|
|
3409
|
+
const existing = await fs3.readFile(rulesPath, "utf-8");
|
|
3410
|
+
if (existing.includes("Memorix")) {
|
|
3411
|
+
return;
|
|
3412
|
+
}
|
|
3413
|
+
await fs3.writeFile(rulesPath, existing + "\n\n" + rulesContent, "utf-8");
|
|
3414
|
+
} catch {
|
|
3415
|
+
await fs3.writeFile(rulesPath, rulesContent, "utf-8");
|
|
3416
|
+
}
|
|
3417
|
+
} else {
|
|
3418
|
+
try {
|
|
3419
|
+
await fs3.access(rulesPath);
|
|
3420
|
+
} catch {
|
|
3421
|
+
await fs3.writeFile(rulesPath, rulesContent, "utf-8");
|
|
3422
|
+
}
|
|
3164
3423
|
}
|
|
3165
3424
|
} catch {
|
|
3166
3425
|
}
|
|
@@ -3174,22 +3433,50 @@ You have access to Memorix memory tools. Follow these rules to maintain persiste
|
|
|
3174
3433
|
|
|
3175
3434
|
At the **beginning of every conversation**, before responding to the user:
|
|
3176
3435
|
|
|
3177
|
-
1. Call \`memorix_search\` with query related to the user's first message or the current project
|
|
3178
|
-
2. If results are found, use
|
|
3179
|
-
3. Reference relevant memories naturally in your response
|
|
3436
|
+
1. Call \`memorix_search\` with a query related to the user's first message or the current project
|
|
3437
|
+
2. If results are found, use \`memorix_detail\` to fetch the most relevant ones
|
|
3438
|
+
3. Reference relevant memories naturally in your response \u2014 the user should feel you "remember" them
|
|
3180
3439
|
|
|
3181
3440
|
This ensures you already know the project context without the user re-explaining.
|
|
3182
3441
|
|
|
3183
3442
|
## During Session \u2014 Capture Important Context
|
|
3184
3443
|
|
|
3185
|
-
Proactively call \`memorix_store\`
|
|
3444
|
+
**Proactively** call \`memorix_store\` whenever any of the following happen:
|
|
3445
|
+
|
|
3446
|
+
### Architecture & Decisions
|
|
3447
|
+
- Technology choice, framework selection, or design pattern adopted
|
|
3448
|
+
- Trade-off discussion with a clear conclusion
|
|
3449
|
+
- API design, database schema, or project structure decisions
|
|
3450
|
+
|
|
3451
|
+
### Bug Fixes & Problem Solving
|
|
3452
|
+
- A bug is identified and resolved \u2014 store root cause + fix
|
|
3453
|
+
- Workaround applied for a known issue
|
|
3454
|
+
- Performance issue diagnosed and optimized
|
|
3455
|
+
|
|
3456
|
+
### Gotchas & Pitfalls
|
|
3457
|
+
- Something unexpected or tricky is discovered
|
|
3458
|
+
- A common mistake is identified and corrected
|
|
3459
|
+
- Platform-specific behavior that caused issues
|
|
3186
3460
|
|
|
3187
|
-
|
|
3188
|
-
-
|
|
3189
|
-
-
|
|
3190
|
-
-
|
|
3461
|
+
### Configuration & Environment
|
|
3462
|
+
- Environment variables, port numbers, paths changed
|
|
3463
|
+
- Docker, nginx, Caddy, or reverse proxy config modified
|
|
3464
|
+
- Package dependencies added, removed, or version-pinned
|
|
3191
3465
|
|
|
3192
|
-
|
|
3466
|
+
### Deployment & Operations
|
|
3467
|
+
- Server deployment steps (Docker, VPS, cloud)
|
|
3468
|
+
- DNS, SSL/TLS certificate, domain configuration
|
|
3469
|
+
- CI/CD pipeline setup or changes
|
|
3470
|
+
- Database migration or data transfer procedures
|
|
3471
|
+
- Server topology (ports, services, reverse proxy chain)
|
|
3472
|
+
- SSH keys, access credentials setup (store pattern, NOT secrets)
|
|
3473
|
+
|
|
3474
|
+
### Project Milestones
|
|
3475
|
+
- Feature completed or shipped
|
|
3476
|
+
- Version released or published to npm/PyPI/etc.
|
|
3477
|
+
- Repository made public, README updated, PR submitted
|
|
3478
|
+
|
|
3479
|
+
Use appropriate types: \`decision\`, \`problem-solution\`, \`gotcha\`, \`what-changed\`, \`discovery\`, \`how-it-works\`.
|
|
3193
3480
|
|
|
3194
3481
|
## Session End \u2014 Store Summary
|
|
3195
3482
|
|
|
@@ -3197,18 +3484,21 @@ When the conversation is ending or the user says goodbye:
|
|
|
3197
3484
|
|
|
3198
3485
|
1. Call \`memorix_store\` with type \`session-request\` to record:
|
|
3199
3486
|
- What was accomplished in this session
|
|
3200
|
-
- Current project state
|
|
3487
|
+
- Current project state and any blockers
|
|
3201
3488
|
- Pending tasks or next steps
|
|
3202
|
-
-
|
|
3489
|
+
- Key files modified
|
|
3203
3490
|
|
|
3204
|
-
This creates a "handoff note" for the next session.
|
|
3491
|
+
This creates a "handoff note" for the next session (or for another AI agent).
|
|
3205
3492
|
|
|
3206
3493
|
## Guidelines
|
|
3207
3494
|
|
|
3208
|
-
- **Don't store trivial information** (greetings, acknowledgments, simple file reads)
|
|
3495
|
+
- **Don't store trivial information** (greetings, acknowledgments, simple file reads, ls/dir output)
|
|
3209
3496
|
- **Do store anything you'd want to know if you lost all context**
|
|
3210
|
-
- **
|
|
3497
|
+
- **Do store anything a different AI agent would need to continue this work**
|
|
3498
|
+
- **Use concise titles** (~5-10 words) and structured facts
|
|
3211
3499
|
- **Include file paths** in filesModified when relevant
|
|
3500
|
+
- **Include related concepts** for better searchability
|
|
3501
|
+
- **Prefer storing too much over too little** \u2014 the retention system will auto-decay stale memories
|
|
3212
3502
|
`;
|
|
3213
3503
|
}
|
|
3214
3504
|
async function uninstallHooks(agent, projectRoot, global = false) {
|
|
@@ -3233,7 +3523,7 @@ async function uninstallHooks(agent, projectRoot, global = false) {
|
|
|
3233
3523
|
}
|
|
3234
3524
|
async function getHookStatus(projectRoot) {
|
|
3235
3525
|
const results = [];
|
|
3236
|
-
const agents = ["claude", "copilot", "windsurf", "cursor", "kiro", "codex"];
|
|
3526
|
+
const agents = ["claude", "copilot", "windsurf", "cursor", "kiro", "codex", "antigravity"];
|
|
3237
3527
|
for (const agent of agents) {
|
|
3238
3528
|
const projectPath = getProjectConfigPath(agent, projectRoot);
|
|
3239
3529
|
const globalPath = getGlobalConfigPath(agent);
|
|
@@ -3398,31 +3688,65 @@ function sendError(res, message, status = 500) {
|
|
|
3398
3688
|
function filterByProject(items, projectId) {
|
|
3399
3689
|
return items.filter((item) => item.projectId === projectId);
|
|
3400
3690
|
}
|
|
3401
|
-
async function handleApi(req, res, dataDir, projectId, projectName) {
|
|
3691
|
+
async function handleApi(req, res, dataDir, projectId, projectName, baseDir) {
|
|
3402
3692
|
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
3403
3693
|
const apiPath = url.pathname.replace("/api", "");
|
|
3694
|
+
const requestedProject = url.searchParams.get("project");
|
|
3695
|
+
let effectiveDataDir = dataDir;
|
|
3696
|
+
let effectiveProjectId = projectId;
|
|
3697
|
+
let effectiveProjectName = projectName;
|
|
3698
|
+
if (requestedProject && requestedProject !== projectId) {
|
|
3699
|
+
const sanitized = requestedProject.replace(/\//g, "--").replace(/[<>:"|?*\\]/g, "_");
|
|
3700
|
+
const candidateDir = path6.join(baseDir, sanitized);
|
|
3701
|
+
try {
|
|
3702
|
+
await fs4.access(candidateDir);
|
|
3703
|
+
effectiveDataDir = candidateDir;
|
|
3704
|
+
effectiveProjectId = requestedProject;
|
|
3705
|
+
effectiveProjectName = requestedProject.split("/").pop() || requestedProject;
|
|
3706
|
+
} catch {
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3404
3709
|
try {
|
|
3405
3710
|
switch (apiPath) {
|
|
3711
|
+
case "/projects": {
|
|
3712
|
+
try {
|
|
3713
|
+
const entries = await fs4.readdir(baseDir, { withFileTypes: true });
|
|
3714
|
+
const projects = entries.filter((e) => e.isDirectory() && e.name.includes("--")).map((e) => {
|
|
3715
|
+
const dirName = e.name;
|
|
3716
|
+
const id = dirName.replace(/--/g, "/");
|
|
3717
|
+
return {
|
|
3718
|
+
id,
|
|
3719
|
+
name: id.split("/").pop() || id,
|
|
3720
|
+
dirName,
|
|
3721
|
+
isCurrent: id === projectId
|
|
3722
|
+
};
|
|
3723
|
+
});
|
|
3724
|
+
sendJson(res, projects);
|
|
3725
|
+
} catch {
|
|
3726
|
+
sendJson(res, []);
|
|
3727
|
+
}
|
|
3728
|
+
break;
|
|
3729
|
+
}
|
|
3406
3730
|
case "/project": {
|
|
3407
|
-
sendJson(res, { id:
|
|
3731
|
+
sendJson(res, { id: effectiveProjectId, name: effectiveProjectName });
|
|
3408
3732
|
break;
|
|
3409
3733
|
}
|
|
3410
3734
|
case "/graph": {
|
|
3411
|
-
const graph = await loadGraphJsonl(
|
|
3735
|
+
const graph = await loadGraphJsonl(effectiveDataDir);
|
|
3412
3736
|
sendJson(res, graph);
|
|
3413
3737
|
break;
|
|
3414
3738
|
}
|
|
3415
3739
|
case "/observations": {
|
|
3416
|
-
const allObs = await loadObservationsJson(
|
|
3417
|
-
const observations2 = filterByProject(allObs,
|
|
3740
|
+
const allObs = await loadObservationsJson(effectiveDataDir);
|
|
3741
|
+
const observations2 = filterByProject(allObs, effectiveProjectId);
|
|
3418
3742
|
sendJson(res, observations2);
|
|
3419
3743
|
break;
|
|
3420
3744
|
}
|
|
3421
3745
|
case "/stats": {
|
|
3422
|
-
const graph = await loadGraphJsonl(
|
|
3423
|
-
const allObs = await loadObservationsJson(
|
|
3424
|
-
const observations2 = filterByProject(allObs,
|
|
3425
|
-
const nextId2 = await loadIdCounter(
|
|
3746
|
+
const graph = await loadGraphJsonl(effectiveDataDir);
|
|
3747
|
+
const allObs = await loadObservationsJson(effectiveDataDir);
|
|
3748
|
+
const observations2 = filterByProject(allObs, effectiveProjectId);
|
|
3749
|
+
const nextId2 = await loadIdCounter(effectiveDataDir);
|
|
3426
3750
|
const typeCounts = {};
|
|
3427
3751
|
for (const obs of observations2) {
|
|
3428
3752
|
const t = obs.type || "unknown";
|
|
@@ -3440,8 +3764,8 @@ async function handleApi(req, res, dataDir, projectId, projectName) {
|
|
|
3440
3764
|
break;
|
|
3441
3765
|
}
|
|
3442
3766
|
case "/retention": {
|
|
3443
|
-
const allObs = await loadObservationsJson(
|
|
3444
|
-
const observations2 = filterByProject(allObs,
|
|
3767
|
+
const allObs = await loadObservationsJson(effectiveDataDir);
|
|
3768
|
+
const observations2 = filterByProject(allObs, effectiveProjectId);
|
|
3445
3769
|
const now = Date.now();
|
|
3446
3770
|
const scored = observations2.map((obs) => {
|
|
3447
3771
|
const age = now - new Date(obs.createdAt || now).getTime();
|
|
@@ -3475,8 +3799,42 @@ async function handleApi(req, res, dataDir, projectId, projectName) {
|
|
|
3475
3799
|
});
|
|
3476
3800
|
break;
|
|
3477
3801
|
}
|
|
3478
|
-
default:
|
|
3802
|
+
default: {
|
|
3803
|
+
const deleteMatch = apiPath.match(/^\/observations\/(\d+)$/);
|
|
3804
|
+
if (deleteMatch && req.method === "DELETE") {
|
|
3805
|
+
const obsId = parseInt(deleteMatch[1], 10);
|
|
3806
|
+
const allObs = await loadObservationsJson(effectiveDataDir);
|
|
3807
|
+
const idx = allObs.findIndex((o) => o.id === obsId);
|
|
3808
|
+
if (idx === -1) {
|
|
3809
|
+
sendError(res, "Observation not found", 404);
|
|
3810
|
+
} else {
|
|
3811
|
+
allObs.splice(idx, 1);
|
|
3812
|
+
await saveObservationsJson(effectiveDataDir, allObs);
|
|
3813
|
+
sendJson(res, { ok: true, deleted: obsId });
|
|
3814
|
+
}
|
|
3815
|
+
break;
|
|
3816
|
+
}
|
|
3817
|
+
if (apiPath === "/export") {
|
|
3818
|
+
const graph = await loadGraphJsonl(effectiveDataDir);
|
|
3819
|
+
const allObs = await loadObservationsJson(effectiveDataDir);
|
|
3820
|
+
const observations2 = filterByProject(allObs, effectiveProjectId);
|
|
3821
|
+
const nextId2 = await loadIdCounter(effectiveDataDir);
|
|
3822
|
+
const exportData = {
|
|
3823
|
+
project: { id: effectiveProjectId, name: effectiveProjectName },
|
|
3824
|
+
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3825
|
+
graph,
|
|
3826
|
+
observations: observations2,
|
|
3827
|
+
nextId: nextId2
|
|
3828
|
+
};
|
|
3829
|
+
res.writeHead(200, {
|
|
3830
|
+
"Content-Type": "application/json",
|
|
3831
|
+
"Content-Disposition": `attachment; filename="memorix-${effectiveProjectId.replace(/\//g, "-")}-export.json"`
|
|
3832
|
+
});
|
|
3833
|
+
res.end(JSON.stringify(exportData, null, 2));
|
|
3834
|
+
break;
|
|
3835
|
+
}
|
|
3479
3836
|
sendError(res, "Not found", 404);
|
|
3837
|
+
}
|
|
3480
3838
|
}
|
|
3481
3839
|
} catch (err) {
|
|
3482
3840
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
@@ -3518,10 +3876,11 @@ function openBrowser(url) {
|
|
|
3518
3876
|
}
|
|
3519
3877
|
async function startDashboard(dataDir, port, staticDir, projectId, projectName, autoOpen = true) {
|
|
3520
3878
|
const resolvedStaticDir = staticDir;
|
|
3879
|
+
const baseDir = getBaseDataDir();
|
|
3521
3880
|
const server = createServer(async (req, res) => {
|
|
3522
3881
|
const url = req.url || "/";
|
|
3523
3882
|
if (url.startsWith("/api/")) {
|
|
3524
|
-
await handleApi(req, res, dataDir, projectId, projectName);
|
|
3883
|
+
await handleApi(req, res, dataDir, projectId, projectName, baseDir);
|
|
3525
3884
|
} else {
|
|
3526
3885
|
await serveStatic(req, res, resolvedStaticDir);
|
|
3527
3886
|
}
|
|
@@ -3579,6 +3938,14 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
3579
3938
|
import { z } from "zod";
|
|
3580
3939
|
async function createMemorixServer(cwd) {
|
|
3581
3940
|
const project = detectProject(cwd);
|
|
3941
|
+
try {
|
|
3942
|
+
const { migrateGlobalData: migrateGlobalData2 } = await Promise.resolve().then(() => (init_persistence(), persistence_exports));
|
|
3943
|
+
const migrated = await migrateGlobalData2(project.id);
|
|
3944
|
+
if (migrated) {
|
|
3945
|
+
console.error(`[memorix] Migrated legacy data to project directory: ${project.id}`);
|
|
3946
|
+
}
|
|
3947
|
+
} catch {
|
|
3948
|
+
}
|
|
3582
3949
|
const projectDir2 = await getProjectDataDir(project.id);
|
|
3583
3950
|
const graphManager = new KnowledgeGraphManager(projectDir2);
|
|
3584
3951
|
await graphManager.init();
|
|
@@ -3593,15 +3960,14 @@ async function createMemorixServer(cwd) {
|
|
|
3593
3960
|
const { getHookStatus: getHookStatus2, installHooks: installHooks2, detectInstalledAgents: detectInstalledAgents2 } = await Promise.resolve().then(() => (init_installers(), installers_exports));
|
|
3594
3961
|
const workDir = cwd ?? process.cwd();
|
|
3595
3962
|
const statuses = await getHookStatus2(workDir);
|
|
3596
|
-
const
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
}
|
|
3963
|
+
const installedAgents = new Set(statuses.filter((s) => s.installed).map((s) => s.agent));
|
|
3964
|
+
const detectedAgents = await detectInstalledAgents2();
|
|
3965
|
+
for (const agent of detectedAgents) {
|
|
3966
|
+
if (installedAgents.has(agent)) continue;
|
|
3967
|
+
try {
|
|
3968
|
+
const config = await installHooks2(agent, workDir);
|
|
3969
|
+
console.error(`[memorix] Auto-installed hooks for ${agent} \u2192 ${config.configPath}`);
|
|
3970
|
+
} catch {
|
|
3605
3971
|
}
|
|
3606
3972
|
}
|
|
3607
3973
|
} catch {
|
|
@@ -3738,15 +4104,20 @@ Entity: ${entityName} | Type: ${type} | Project: ${project.id}${enrichment}`
|
|
|
3738
4104
|
query: z.string().describe("Search query (natural language or keywords)"),
|
|
3739
4105
|
limit: z.number().optional().describe("Max results (default: 20)"),
|
|
3740
4106
|
type: z.enum(OBSERVATION_TYPES).optional().describe("Filter by observation type"),
|
|
3741
|
-
maxTokens: z.number().optional().describe("Token budget \u2014 trim results to fit (0 = unlimited)")
|
|
4107
|
+
maxTokens: z.number().optional().describe("Token budget \u2014 trim results to fit (0 = unlimited)"),
|
|
4108
|
+
scope: z.enum(["project", "global"]).optional().describe(
|
|
4109
|
+
'Search scope: "project" (default) only searches current project, "global" searches all projects'
|
|
4110
|
+
)
|
|
3742
4111
|
}
|
|
3743
4112
|
},
|
|
3744
|
-
async ({ query, limit, type, maxTokens }) => {
|
|
4113
|
+
async ({ query, limit, type, maxTokens, scope }) => {
|
|
3745
4114
|
const result = await compactSearch({
|
|
3746
4115
|
query,
|
|
3747
4116
|
limit,
|
|
3748
4117
|
type,
|
|
3749
|
-
maxTokens
|
|
4118
|
+
maxTokens,
|
|
4119
|
+
// Default to current project scope; 'global' removes the project filter
|
|
4120
|
+
projectId: scope === "global" ? void 0 : project.id
|
|
3750
4121
|
});
|
|
3751
4122
|
let text = result.formatted;
|
|
3752
4123
|
if (!syncAdvisoryShown && syncAdvisory) {
|
|
@@ -4325,7 +4696,29 @@ var init_serve = __esm({
|
|
|
4325
4696
|
run: async ({ args }) => {
|
|
4326
4697
|
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
|
|
4327
4698
|
const { createMemorixServer: createMemorixServer2 } = await Promise.resolve().then(() => (init_server2(), server_exports2));
|
|
4328
|
-
const
|
|
4699
|
+
const { execSync: execSync2 } = await import("child_process");
|
|
4700
|
+
let projectRoot = args.cwd || process.env.INIT_CWD || process.cwd();
|
|
4701
|
+
try {
|
|
4702
|
+
execSync2("git rev-parse --show-toplevel", {
|
|
4703
|
+
cwd: projectRoot,
|
|
4704
|
+
encoding: "utf-8",
|
|
4705
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4706
|
+
});
|
|
4707
|
+
} catch {
|
|
4708
|
+
const scriptDir = new URL(".", import.meta.url).pathname.replace(/^\/([A-Z]:)/, "$1");
|
|
4709
|
+
try {
|
|
4710
|
+
const gitRoot = execSync2("git rev-parse --show-toplevel", {
|
|
4711
|
+
cwd: scriptDir,
|
|
4712
|
+
encoding: "utf-8",
|
|
4713
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4714
|
+
}).trim();
|
|
4715
|
+
if (gitRoot) {
|
|
4716
|
+
projectRoot = gitRoot;
|
|
4717
|
+
console.error(`[memorix] CWD has no git, using script dir: ${projectRoot}`);
|
|
4718
|
+
}
|
|
4719
|
+
} catch {
|
|
4720
|
+
}
|
|
4721
|
+
}
|
|
4329
4722
|
const { server, projectId } = await createMemorixServer2(projectRoot);
|
|
4330
4723
|
const transport = new StdioServerTransport();
|
|
4331
4724
|
await server.connect(transport);
|
|
@@ -4725,7 +5118,8 @@ function patternToObservationType(pattern) {
|
|
|
4725
5118
|
gotcha: "gotcha",
|
|
4726
5119
|
configuration: "what-changed",
|
|
4727
5120
|
learning: "discovery",
|
|
4728
|
-
implementation: "what-changed"
|
|
5121
|
+
implementation: "what-changed",
|
|
5122
|
+
deployment: "what-changed"
|
|
4729
5123
|
};
|
|
4730
5124
|
return map[pattern] ?? "discovery";
|
|
4731
5125
|
}
|
|
@@ -4796,6 +5190,23 @@ var init_pattern_detector = __esm({
|
|
|
4796
5190
|
],
|
|
4797
5191
|
minLength: 200,
|
|
4798
5192
|
baseConfidence: 0.5
|
|
5193
|
+
},
|
|
5194
|
+
{
|
|
5195
|
+
type: "deployment",
|
|
5196
|
+
keywords: [
|
|
5197
|
+
/\b(deploy(ed|ing|ment)?|ship(ped|ping)?|releas(ed|ing)|publish(ed|ing)?)\b/i,
|
|
5198
|
+
/(部署|发布|上线|迁移|运维)/,
|
|
5199
|
+
/\b(docker|compose|container|kubernetes|k8s|helm)\b/i,
|
|
5200
|
+
/\b(VPS|server|host(ing)?|cloud|AWS|Azure|GCP|Cloudflare)\b/i,
|
|
5201
|
+
/\b(nginx|caddy|apache|reverse.?proxy|load.?balanc)\b/i,
|
|
5202
|
+
/\b(SSL|TLS|cert(ificate)?|HTTPS|Let'?s?.?Encrypt|ACME)\b/i,
|
|
5203
|
+
/\b(DNS|domain|A.?record|CNAME|nameserver|Cloudflare)\b/i,
|
|
5204
|
+
/\b(CI\/CD|pipeline|GitHub.?Actions|Jenkins|GitLab.?CI)\b/i,
|
|
5205
|
+
/\b(scp|rsync|ssh|sftp|systemd|systemctl|service)\b/i,
|
|
5206
|
+
/(服务器|域名|证书|反向代理|负载均衡|镜像|容器)/
|
|
5207
|
+
],
|
|
5208
|
+
minLength: 80,
|
|
5209
|
+
baseConfidence: 0.75
|
|
4799
5210
|
}
|
|
4800
5211
|
];
|
|
4801
5212
|
}
|