@xultrax-web/agent-memory-mcp 0.11.0 → 0.11.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 +13 -3
- package/dist/index.js +160 -15
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -27,14 +27,24 @@ agent-memory save-rule no-emojis-ever \
|
|
|
27
27
|
--enforce-on commits,chat_responses \
|
|
28
28
|
--content "No emojis. Anywhere. Ever."
|
|
29
29
|
|
|
30
|
-
agent-memory emit-companions
|
|
30
|
+
agent-memory emit-companions
|
|
31
|
+
# writes AGENTS.md + CLAUDE.md + .cursor/rules/*.mdc + .gemini/instructions.md
|
|
32
|
+
# (v0.11.1 — all four targets · use --target agents,claude to filter)
|
|
31
33
|
```
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
Companion file targets (v0.11.1):
|
|
36
|
+
|
|
37
|
+
| Target | Path | Auto-loaded by |
|
|
38
|
+
| -------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- |
|
|
39
|
+
| `agents` | `AGENTS.md` | Claude Code, Codex CLI, Cursor, Aider, Devin, Copilot, Gemini CLI, Windsurf, Amazon Q |
|
|
40
|
+
| `claude` | `CLAUDE.md` | Claude Code (5-level hierarchy · managed/global/project/local/subdir) |
|
|
41
|
+
| `cursor` | `.cursor/rules/operator-hard.mdc` (`alwaysApply: true`) + `operator-conventions.mdc` (agent-requested) | Cursor (MDC format) |
|
|
42
|
+
| `gemini` | `.gemini/instructions.md` | Gemini CLI |
|
|
43
|
+
|
|
44
|
+
Set `AGENT_MEMORY_AUTO_EMIT_DIR=/path/to/project` to auto-regenerate all companions on every rule save.
|
|
34
45
|
|
|
35
46
|
Roadmap for the v0.11.x series:
|
|
36
47
|
|
|
37
|
-
- `CLAUDE.md` + `.cursor/rules/*.mdc` + `.gemini/instructions.md` emitters (per-tool native formats)
|
|
38
48
|
- Compliance Receipts (Macaroon-style HMAC tokens · protocol-level enforcement of our own destructive tools)
|
|
39
49
|
- `check_action` tool (deterministic rule matching · optional Sampling enrichment where clients support it)
|
|
40
50
|
- `audit` command (rule conflicts · staleness · receipt-denial log)
|
package/dist/index.js
CHANGED
|
@@ -1022,6 +1022,7 @@ function buildAgentsMdContent(rules) {
|
|
|
1022
1022
|
}
|
|
1023
1023
|
return parts.join("\n");
|
|
1024
1024
|
}
|
|
1025
|
+
export const ALL_COMPANION_TARGETS = ["agents", "claude", "cursor", "gemini"];
|
|
1025
1026
|
function resolveCompanionDir(explicit) {
|
|
1026
1027
|
if (explicit && explicit.trim().length > 0)
|
|
1027
1028
|
return explicit.trim();
|
|
@@ -1030,22 +1031,137 @@ function resolveCompanionDir(explicit) {
|
|
|
1030
1031
|
return envOverride.trim();
|
|
1031
1032
|
return process.cwd();
|
|
1032
1033
|
}
|
|
1034
|
+
// CLAUDE.md content · same body as AGENTS.md but with a Claude-Code-specific
|
|
1035
|
+
// header. Claude Code's 5-level hierarchy (managed/global/project/local/subdir)
|
|
1036
|
+
// reads any CLAUDE.md it finds; we generate the project-root file by default.
|
|
1037
|
+
function buildClaudeMdContent(rules) {
|
|
1038
|
+
const body = buildAgentsMdContent(rules);
|
|
1039
|
+
// Replace the AGENTS.md-specific header sentence with a CLAUDE.md one
|
|
1040
|
+
return body.replace(/^# Operator rules\n/, `# Operator rules · Claude Code\n\n> This is your CLAUDE.md — Claude Code reads it on session start.\n\n`);
|
|
1041
|
+
}
|
|
1042
|
+
// .gemini/instructions.md content · same body as AGENTS.md, slightly different
|
|
1043
|
+
// header.
|
|
1044
|
+
function buildGeminiInstructionsContent(rules) {
|
|
1045
|
+
const body = buildAgentsMdContent(rules);
|
|
1046
|
+
return body.replace(/^# Operator rules\n/, `# Operator rules · Gemini CLI\n\n> Loaded by Gemini CLI from .gemini/instructions.md on session start.\n\n`);
|
|
1047
|
+
}
|
|
1048
|
+
// Cursor consumes .cursor/rules/*.mdc files with their own YAML frontmatter.
|
|
1049
|
+
// Per spec: each file <150 lines, alwaysApply file <50 lines, dir total <500.
|
|
1050
|
+
// Strategy: one file per severity (hard / soft) — hard is alwaysApply, soft
|
|
1051
|
+
// is description-driven so the agent pulls it in when relevant.
|
|
1052
|
+
function buildCursorMdcFiles(rules) {
|
|
1053
|
+
const hard = rules.filter((r) => r.severity === "hard");
|
|
1054
|
+
const soft = rules.filter((r) => r.severity !== "hard");
|
|
1055
|
+
const files = [];
|
|
1056
|
+
if (hard.length > 0) {
|
|
1057
|
+
const fm = [
|
|
1058
|
+
"---",
|
|
1059
|
+
`description: "Operator hard rules · always obey · auto-generated from agent-memory-mcp"`,
|
|
1060
|
+
"alwaysApply: true",
|
|
1061
|
+
"---",
|
|
1062
|
+
"",
|
|
1063
|
+
"# Operator hard rules",
|
|
1064
|
+
"",
|
|
1065
|
+
"These rules MUST be obeyed. Violations should be flagged and blocked.",
|
|
1066
|
+
"",
|
|
1067
|
+
].join("\n");
|
|
1068
|
+
files.push({
|
|
1069
|
+
filename: "operator-hard.mdc",
|
|
1070
|
+
content: fm + hard.map((r) => formatRuleAsMarkdown(r)).join("\n"),
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
if (soft.length > 0) {
|
|
1074
|
+
const fm = [
|
|
1075
|
+
"---",
|
|
1076
|
+
`description: "Operator conventions · prefer to obey · pulled in by agent on relevance"`,
|
|
1077
|
+
"alwaysApply: false",
|
|
1078
|
+
"---",
|
|
1079
|
+
"",
|
|
1080
|
+
"# Operator conventions",
|
|
1081
|
+
"",
|
|
1082
|
+
"Soft rules · prefer to obey. The agent may consult these when the context warrants.",
|
|
1083
|
+
"",
|
|
1084
|
+
].join("\n");
|
|
1085
|
+
files.push({
|
|
1086
|
+
filename: "operator-conventions.mdc",
|
|
1087
|
+
content: fm + soft.map((r) => formatRuleAsMarkdown(r)).join("\n"),
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
return files;
|
|
1091
|
+
}
|
|
1033
1092
|
function emitCompanions(opts = {}) {
|
|
1034
1093
|
const outDir = resolveCompanionDir(opts.outDir);
|
|
1094
|
+
const targets = opts.targets && opts.targets.length > 0 ? opts.targets : ALL_COMPANION_TARGETS;
|
|
1035
1095
|
const rules = loadAllRules();
|
|
1036
|
-
|
|
1037
|
-
const content = buildAgentsMdContent(rules);
|
|
1038
|
-
// Best-effort directory creation (companion dir may not exist if user
|
|
1039
|
-
// passes a fresh path); mkdirSync is idempotent with recursive:true.
|
|
1096
|
+
// Best-effort directory creation; mkdirSync recursive is idempotent.
|
|
1040
1097
|
try {
|
|
1041
1098
|
mkdirSync(outDir, { recursive: true });
|
|
1042
1099
|
}
|
|
1043
1100
|
catch {
|
|
1044
|
-
// Ignore — atomicWriteFile
|
|
1101
|
+
// Ignore — atomicWriteFile surfaces a clearer error if needed.
|
|
1102
|
+
}
|
|
1103
|
+
const emitted = [];
|
|
1104
|
+
if (targets.includes("agents")) {
|
|
1105
|
+
const fp = join(outDir, "AGENTS.md");
|
|
1106
|
+
atomicWriteFile(fp, buildAgentsMdContent(rules));
|
|
1107
|
+
emitted.push(fp);
|
|
1108
|
+
}
|
|
1109
|
+
if (targets.includes("claude")) {
|
|
1110
|
+
const fp = join(outDir, "CLAUDE.md");
|
|
1111
|
+
atomicWriteFile(fp, buildClaudeMdContent(rules));
|
|
1112
|
+
emitted.push(fp);
|
|
1113
|
+
}
|
|
1114
|
+
if (targets.includes("cursor")) {
|
|
1115
|
+
const cursorDir = join(outDir, ".cursor", "rules");
|
|
1116
|
+
try {
|
|
1117
|
+
mkdirSync(cursorDir, { recursive: true });
|
|
1118
|
+
}
|
|
1119
|
+
catch {
|
|
1120
|
+
// Ignore — atomicWriteFile surfaces clearer error if needed.
|
|
1121
|
+
}
|
|
1122
|
+
const files = buildCursorMdcFiles(rules);
|
|
1123
|
+
if (files.length === 0) {
|
|
1124
|
+
// No rules yet — drop a placeholder so the tool knows where to put them
|
|
1125
|
+
const placeholderPath = join(cursorDir, "operator-rules.mdc");
|
|
1126
|
+
const placeholder = [
|
|
1127
|
+
"---",
|
|
1128
|
+
`description: "Operator rules · auto-generated · no rules defined yet"`,
|
|
1129
|
+
"alwaysApply: false",
|
|
1130
|
+
"---",
|
|
1131
|
+
"",
|
|
1132
|
+
"No rules defined yet. Run `agent-memory save-rule` to add the first one.",
|
|
1133
|
+
"",
|
|
1134
|
+
].join("\n");
|
|
1135
|
+
atomicWriteFile(placeholderPath, placeholder);
|
|
1136
|
+
emitted.push(placeholderPath);
|
|
1137
|
+
}
|
|
1138
|
+
else {
|
|
1139
|
+
for (const f of files) {
|
|
1140
|
+
const fp = join(cursorDir, f.filename);
|
|
1141
|
+
atomicWriteFile(fp, f.content);
|
|
1142
|
+
emitted.push(fp);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1045
1145
|
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1146
|
+
if (targets.includes("gemini")) {
|
|
1147
|
+
const geminiDir = join(outDir, ".gemini");
|
|
1148
|
+
try {
|
|
1149
|
+
mkdirSync(geminiDir, { recursive: true });
|
|
1150
|
+
}
|
|
1151
|
+
catch {
|
|
1152
|
+
// Ignore — atomicWriteFile surfaces clearer error if needed.
|
|
1153
|
+
}
|
|
1154
|
+
const fp = join(geminiDir, "instructions.md");
|
|
1155
|
+
atomicWriteFile(fp, buildGeminiInstructionsContent(rules));
|
|
1156
|
+
emitted.push(fp);
|
|
1157
|
+
}
|
|
1158
|
+
logEvent("emit_companions", {
|
|
1159
|
+
outDir,
|
|
1160
|
+
rules_count: rules.length,
|
|
1161
|
+
targets,
|
|
1162
|
+
files: emitted.map((p) => p.replace(outDir, "").replace(/^[\\/]/, "")),
|
|
1163
|
+
});
|
|
1164
|
+
return { outDir, emitted, rules_count: rules.length, targets };
|
|
1049
1165
|
}
|
|
1050
1166
|
function maybeAutoEmitCompanions() {
|
|
1051
1167
|
const autoDir = process.env.AGENT_MEMORY_AUTO_EMIT_DIR;
|
|
@@ -1063,11 +1179,20 @@ function maybeAutoEmitCompanions() {
|
|
|
1063
1179
|
}
|
|
1064
1180
|
function toolEmitCompanions(args) {
|
|
1065
1181
|
const outDir = typeof args.out_dir === "string" ? args.out_dir : undefined;
|
|
1066
|
-
|
|
1182
|
+
let targets;
|
|
1183
|
+
if (Array.isArray(args.targets)) {
|
|
1184
|
+
targets = args.targets
|
|
1185
|
+
.filter((t) => typeof t === "string")
|
|
1186
|
+
.filter((t) => ALL_COMPANION_TARGETS.includes(t));
|
|
1187
|
+
if (targets.length === 0)
|
|
1188
|
+
targets = undefined;
|
|
1189
|
+
}
|
|
1190
|
+
const r = emitCompanions({ outDir, targets });
|
|
1191
|
+
const files = r.emitted.map((p) => p.replace(r.outDir, "").replace(/^[\\/]/, "")).join(", ");
|
|
1067
1192
|
if (r.rules_count === 0) {
|
|
1068
|
-
return `Emitted ${r.emitted
|
|
1193
|
+
return `Emitted ${r.emitted.length} placeholder file(s) to ${r.outDir} (${files}) · no rules yet · run save_rule to add the first one.`;
|
|
1069
1194
|
}
|
|
1070
|
-
return `Emitted ${r.rules_count} rule${r.rules_count === 1 ? "" : "s"}
|
|
1195
|
+
return `Emitted ${r.rules_count} rule${r.rules_count === 1 ? "" : "s"} across ${r.targets.length} target${r.targets.length === 1 ? "" : "s"} (${r.targets.join(", ")}) → ${r.emitted.length} file${r.emitted.length === 1 ? "" : "s"} at ${r.outDir}: ${files}`;
|
|
1071
1196
|
}
|
|
1072
1197
|
function toolListRules(_args) {
|
|
1073
1198
|
const rules = loadAllRules();
|
|
@@ -1466,7 +1591,7 @@ function actionColor(action) {
|
|
|
1466
1591
|
// -------------------------------------------------------------
|
|
1467
1592
|
// Server wiring
|
|
1468
1593
|
// -------------------------------------------------------------
|
|
1469
|
-
const server = new Server({ name: "agent-memory", version: "0.11.
|
|
1594
|
+
const server = new Server({ name: "agent-memory", version: "0.11.1" }, { capabilities: { tools: {}, resources: {}, prompts: {} } });
|
|
1470
1595
|
// -------------------------------------------------------------
|
|
1471
1596
|
// Resource URI scheme
|
|
1472
1597
|
// -------------------------------------------------------------
|
|
@@ -1964,13 +2089,25 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
1964
2089
|
},
|
|
1965
2090
|
{
|
|
1966
2091
|
name: "emit_companions",
|
|
1967
|
-
description: "Regenerate companion rule files
|
|
2092
|
+
description: "Regenerate companion rule files from the current rule memories. " +
|
|
2093
|
+
"Writes one or more of: AGENTS.md (universal cross-tool standard, Linux Foundation), " +
|
|
2094
|
+
"CLAUDE.md (Claude Code's 5-level hierarchy), .cursor/rules/*.mdc (Cursor's MDC format · hard rules get alwaysApply:true, soft rules become description-driven), " +
|
|
2095
|
+
".gemini/instructions.md (Gemini CLI). " +
|
|
2096
|
+
"Default writes ALL four targets. Use `targets` to filter. Output dir resolves from `out_dir`, then AGENT_MEMORY_COMPANION_DIR env, then process.cwd().",
|
|
1968
2097
|
inputSchema: {
|
|
1969
2098
|
type: "object",
|
|
1970
2099
|
properties: {
|
|
1971
2100
|
out_dir: {
|
|
1972
2101
|
type: "string",
|
|
1973
|
-
description: "Optional output directory. Defaults to AGENT_MEMORY_COMPANION_DIR env
|
|
2102
|
+
description: "Optional output directory. Defaults to AGENT_MEMORY_COMPANION_DIR env, then process.cwd().",
|
|
2103
|
+
},
|
|
2104
|
+
targets: {
|
|
2105
|
+
type: "array",
|
|
2106
|
+
items: {
|
|
2107
|
+
type: "string",
|
|
2108
|
+
enum: ["agents", "claude", "cursor", "gemini"],
|
|
2109
|
+
},
|
|
2110
|
+
description: "Which companion files to emit. Omit (or pass empty) to emit all four. Examples: ['agents'] for AGENTS.md only, ['claude','cursor'] for Claude Code + Cursor.",
|
|
1974
2111
|
},
|
|
1975
2112
|
},
|
|
1976
2113
|
},
|
|
@@ -2312,7 +2449,15 @@ async function cliMain(command, rest) {
|
|
|
2312
2449
|
}
|
|
2313
2450
|
case "emit-companions": {
|
|
2314
2451
|
const out = flags.out ? String(flags.out) : undefined;
|
|
2315
|
-
|
|
2452
|
+
const target = flags.target;
|
|
2453
|
+
let targets;
|
|
2454
|
+
if (typeof target === "string" && target.length > 0) {
|
|
2455
|
+
targets = target
|
|
2456
|
+
.split(",")
|
|
2457
|
+
.map((t) => t.trim())
|
|
2458
|
+
.filter((t) => t.length > 0);
|
|
2459
|
+
}
|
|
2460
|
+
process.stdout.write(toolEmitCompanions({ out_dir: out, targets }) + "\n");
|
|
2316
2461
|
return 0;
|
|
2317
2462
|
}
|
|
2318
2463
|
case "ui": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xultrax-web/agent-memory-mcp",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"mcpName": "io.github.xultrax-web/agent-memory-mcp",
|
|
5
5
|
"description": "Markdown memory for AI agents. Plain files you can read, edit, grep, and commit. Operator-grade storage with atomic writes, file locking, tags, [[wiki-links]], find_related, git-backed multi-machine sync, and an Ink-based TUI.",
|
|
6
6
|
"type": "module",
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"format:check": "prettier --check .",
|
|
22
22
|
"test": "vitest run",
|
|
23
23
|
"test:watch": "vitest",
|
|
24
|
-
"prepublishOnly": "npm run build"
|
|
24
|
+
"prepublishOnly": "npm run build",
|
|
25
|
+
"prepare": "husky"
|
|
25
26
|
},
|
|
26
27
|
"keywords": [
|
|
27
28
|
"mcp",
|
|
@@ -65,11 +66,18 @@
|
|
|
65
66
|
"@types/node": "^22.10.2",
|
|
66
67
|
"@types/proper-lockfile": "^4.1.4",
|
|
67
68
|
"@types/react": "^19.2.15",
|
|
69
|
+
"husky": "^9.1.7",
|
|
70
|
+
"lint-staged": "^17.0.5",
|
|
68
71
|
"prettier": "^3.8.3",
|
|
69
72
|
"typescript": "^5.7.2",
|
|
70
73
|
"vitest": "^4.1.7"
|
|
71
74
|
},
|
|
72
75
|
"engines": {
|
|
73
76
|
"node": ">=20"
|
|
77
|
+
},
|
|
78
|
+
"lint-staged": {
|
|
79
|
+
"*.{ts,tsx,js,mjs,cjs,json,jsonc,md,yml,yaml}": [
|
|
80
|
+
"prettier --write"
|
|
81
|
+
]
|
|
74
82
|
}
|
|
75
83
|
}
|