@danielmarbach/mnemonic-mcp 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ All notable changes to `mnemonic` will be documented in this file.
4
4
 
5
5
  The format is loosely based on Keep a Changelog and uses semver-style version headings.
6
6
 
7
+ ## [0.6.0] - 2026-03-13
8
+
9
+ ### Added
10
+
11
+ - Protected-branch policy support for automatic `remember` project-vault commits. `ProjectMemoryPolicy` now supports `protectedBranchBehavior` (`ask` | `block` | `allow`) and `protectedBranchPatterns` (glob-like patterns). Built-in patterns are `main`, `master`, and `release*`.
12
+ - `remember` now accepts `allowProtectedBranch` as a one-time override when a protected-branch policy would otherwise ask or block.
13
+
14
+ ### Changed
15
+
16
+ - `set_project_memory_policy` and `get_project_memory_policy` now manage and display protected-branch settings in addition to write-scope and consolidation defaults.
17
+ - `remember` now checks the current local git branch before automatic project-vault commits (scope omitted) and follows protected-branch policy with actionable guidance.
18
+
7
19
  ## [0.5.1] - 2026-03-12
8
20
 
9
21
  ### Fixed
package/README.md CHANGED
@@ -208,6 +208,7 @@ User-tunable fields:
208
208
  | `mutationPushMode` | `"main-only"` | When to auto-push after a write: `"all"`, `"main-only"`, or `"none"` |
209
209
 
210
210
  `projectMemoryPolicies` and `projectIdentityOverrides` are written automatically by `set_project_memory_policy` and `set_project_identity` — no need to edit them by hand.
211
+ Project memory policies can include protected-branch settings (`protectedBranchBehavior`, `protectedBranchPatterns`) used by `remember` when writing to project vaults.
211
212
 
212
213
  Example — raise concurrency on a fast machine and disable auto-push everywhere:
213
214
 
@@ -255,7 +256,14 @@ Two vault types store notes:
255
256
  - `cwd` + `scope: "global"` → main vault, with project association in frontmatter
256
257
  - no `cwd` → main vault as a plain global memory
257
258
 
258
- Use `set_project_memory_policy` to save a per-project default. Supported values: `project`, `global`, `ask`. When policy is `ask`, `remember` returns a clear choice instead of guessing.
259
+ Use `set_project_memory_policy` to save per-project defaults:
260
+
261
+ - write scope (`project`, `global`, `ask`)
262
+ - consolidation mode (`supersedes`, `delete`)
263
+ - protected-branch behavior for project-vault writes (`ask`, `block`, `allow`)
264
+ - protected-branch patterns (glob strings; defaults are `main`, `master`, `release*`)
265
+
266
+ When write scope policy is `ask`, `remember` returns a clear storage choice instead of guessing. When protected-branch behavior is `ask`, `remember` returns a one-time override option plus instructions to persist `block`/`allow`.
259
267
 
260
268
  ### Project identity
261
269
 
@@ -370,7 +378,7 @@ Imported notes are written to the main vault with `lifecycle: permanent` and `sc
370
378
  | `forget` | Delete note + embedding, git commit + push, cleanup relationships |
371
379
  | `get` | Fetch one or more notes by exact id |
372
380
  | `get_project_identity` | Show effective project identity and remote override |
373
- | `get_project_memory_policy` | Show saved default write scope |
381
+ | `get_project_memory_policy` | Show saved write scope, consolidation mode, and protected-branch settings |
374
382
  | `list` | List notes filtered by scope/tags/storage |
375
383
  | `list_migrations` | List available migrations and pending count |
376
384
  | `memory_graph` | Show compact adjacency list of relationships |
@@ -381,7 +389,7 @@ Imported notes are written to the main vault with `lifecycle: permanent` and `sc
381
389
  | `remember` | Write note + embedding; `cwd` sets context, `scope` picks storage, `lifecycle` picks temporary vs permanent |
382
390
  | `relate` | Create typed relationship between notes (bidirectional) |
383
391
  | `set_project_identity` | Save which git remote defines project identity |
384
- | `set_project_memory_policy` | Save default write scope for project (`project`, `global`, `ask`) |
392
+ | `set_project_memory_policy` | Save project policy defaults (scope, consolidation mode, protected-branch behavior/patterns) |
385
393
  | `sync` | Git sync when remote exists plus embedding backfill always; `force=true` rebuilds all embeddings |
386
394
  | `unrelate` | Remove relationship between notes |
387
395
  | `update` | Update note content/title/tags/lifecycle, re-embeds always |
package/build/config.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ProjectMemoryPolicy } from "./project-memory-policy.js";
1
+ import { type ProjectMemoryPolicy } from "./project-memory-policy.js";
2
2
  import type { ProjectIdentityOverride } from "./project.js";
3
3
  export type MutationPushMode = "all" | "main-only" | "none";
4
4
  export interface MnemonicConfig {
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAE5D,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,WAAW,GAAG,MAAM,CAAC;AAE5D,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,EAAE,MAAM,CAAC;IAChC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC3D,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;CACnE;AAmED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAS/E;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWrG;AAED,qBAAa,mBAAmB;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,aAAa,EAAE,MAAM;IAI3B,IAAI,IAAI,OAAO,CAAC,cAAc,CAAC;IAI/B,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IAK7E,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC;IAK3F,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAM5D,0BAA0B,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAM/F,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAM9C,OAAO;YAgBP,QAAQ;CAGvB"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAE5D,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,WAAW,GAAG,MAAM,CAAC;AAE5D,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,EAAE,MAAM,CAAC;IAChC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC3D,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;CACnE;AAkID;;;;GAIG;AACH,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAS/E;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWrG;AAED,qBAAa,mBAAmB;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,aAAa,EAAE,MAAM;IAI3B,IAAI,IAAI,OAAO,CAAC,cAAc,CAAC;IAI/B,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IAK7E,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC;IAK3F,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAM5D,0BAA0B,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAM/F,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAM9C,OAAO;YAgBP,QAAQ;CAGvB"}
package/build/config.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import fs from "fs/promises";
2
2
  import path from "path";
3
+ import { CONSOLIDATION_MODES, PROJECT_POLICY_SCOPES, PROTECTED_BRANCH_BEHAVIORS, } from "./project-memory-policy.js";
3
4
  const defaultConfig = {
4
5
  // Keep this at the latest schema version. When adding a new latest-schema
5
6
  // migration, bump this value in the same change so fresh installs start at
@@ -54,6 +55,46 @@ function normalizeProjectIdentityOverrides(value) {
54
55
  }
55
56
  return normalized;
56
57
  }
58
+ function normalizeProjectMemoryPolicies(value) {
59
+ if (!value || typeof value !== "object") {
60
+ return {};
61
+ }
62
+ const normalized = {};
63
+ for (const [projectId, policy] of Object.entries(value)) {
64
+ if (!policy || typeof policy !== "object") {
65
+ continue;
66
+ }
67
+ const policyRecord = policy;
68
+ const normalizedProjectId = typeof policyRecord.projectId === "string" && policyRecord.projectId.trim().length > 0
69
+ ? policyRecord.projectId.trim()
70
+ : projectId;
71
+ const defaultScope = PROJECT_POLICY_SCOPES.includes(policyRecord.defaultScope)
72
+ ? policyRecord.defaultScope
73
+ : "project";
74
+ const consolidationMode = CONSOLIDATION_MODES.includes(policyRecord.consolidationMode)
75
+ ? policyRecord.consolidationMode
76
+ : undefined;
77
+ const protectedBranchBehavior = PROTECTED_BRANCH_BEHAVIORS.includes(policyRecord.protectedBranchBehavior)
78
+ ? policyRecord.protectedBranchBehavior
79
+ : undefined;
80
+ const protectedBranchPatterns = Array.isArray(policyRecord.protectedBranchPatterns)
81
+ ? policyRecord.protectedBranchPatterns
82
+ .filter((pattern) => typeof pattern === "string")
83
+ .map((pattern) => pattern.trim())
84
+ .filter((pattern) => pattern.length > 0)
85
+ : undefined;
86
+ normalized[projectId] = {
87
+ projectId: normalizedProjectId,
88
+ projectName: typeof policyRecord.projectName === "string" ? policyRecord.projectName : undefined,
89
+ defaultScope,
90
+ consolidationMode,
91
+ protectedBranchPatterns,
92
+ protectedBranchBehavior,
93
+ updatedAt: typeof policyRecord.updatedAt === "string" ? policyRecord.updatedAt : "",
94
+ };
95
+ }
96
+ return normalized;
97
+ }
57
98
  /**
58
99
  * Read the schema version from a vault's config.json.
59
100
  * Works for both main vault and project vaults.
@@ -126,7 +167,7 @@ export class MnemonicConfigStore {
126
167
  schemaVersion: normalizeSchemaVersion(parsed.schemaVersion),
127
168
  reindexEmbedConcurrency: normalizeConcurrency(parsed.reindexEmbedConcurrency),
128
169
  mutationPushMode: normalizeMutationPushMode(parsed.mutationPushMode),
129
- projectMemoryPolicies: parsed.projectMemoryPolicies ?? {},
170
+ projectMemoryPolicies: normalizeProjectMemoryPolicies(parsed.projectMemoryPolicies),
130
171
  projectIdentityOverrides: normalizeProjectIdentityOverrides(parsed.projectIdentityOverrides),
131
172
  };
132
173
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/build/index.js CHANGED
@@ -11,9 +11,9 @@ import { filterRelationships, mergeRelationshipsFromNotes, normalizeMergePlanSou
11
11
  import { selectRecallResults } from "./recall.js";
12
12
  import { cleanMarkdown } from "./markdown.js";
13
13
  import { MnemonicConfigStore, readVaultSchemaVersion } from "./config.js";
14
- import { CONSOLIDATION_MODES, PROJECT_POLICY_SCOPES, WRITE_SCOPES, resolveConsolidationMode, resolveWriteScope, } from "./project-memory-policy.js";
14
+ import { CONSOLIDATION_MODES, PROTECTED_BRANCH_BEHAVIORS, PROJECT_POLICY_SCOPES, WRITE_SCOPES, isProtectedBranch, resolveProtectedBranchBehavior, resolveProtectedBranchPatterns, resolveConsolidationMode, resolveWriteScope, } from "./project-memory-policy.js";
15
15
  import { classifyTheme, summarizePreview, titleCaseTheme } from "./project-introspection.js";
16
- import { detectProject, resolveProjectIdentity } from "./project.js";
16
+ import { detectProject, getCurrentGitBranch, resolveProjectIdentity } from "./project.js";
17
17
  import { VaultManager } from "./vault.js";
18
18
  import { Migrator } from "./migration.js";
19
19
  import { parseMemorySections } from "./import.js";
@@ -352,14 +352,6 @@ function formatProjectIdentityText(identity) {
352
352
  }
353
353
  return lines.join("\n");
354
354
  }
355
- async function getProjectPolicyScope(cwd) {
356
- const project = await resolveProject(cwd);
357
- if (!project) {
358
- return undefined;
359
- }
360
- const policy = await configStore.getProjectPolicy(project.id);
361
- return policy?.defaultScope;
362
- }
363
355
  function describeLifecycle(lifecycle) {
364
356
  return `lifecycle: ${lifecycle}`;
365
357
  }
@@ -454,6 +446,49 @@ function formatAskForWriteScope(project, unadopted = false) {
454
446
  "To avoid being asked again: call `set_project_memory_policy` with your preferred scope.",
455
447
  ].join("\n");
456
448
  }
449
+ function formatAskForProtectedBranch(project, branch, patterns) {
450
+ const projectLabel = project ? `${project.name} (${project.id})` : "this context";
451
+ return [
452
+ `Protected branch check for ${projectLabel}: current branch \`${branch}\` matches ${patterns.join(", ")}.`,
453
+ "Choose how to proceed:",
454
+ "- One-time override: call `remember` again with `allowProtectedBranch: true`",
455
+ "- Persist policy: call `set_project_memory_policy` with `protectedBranchBehavior: \"block\"`",
456
+ "- Persist policy: call `set_project_memory_policy` with `protectedBranchBehavior: \"allow\"`",
457
+ "",
458
+ "Optional: set `protectedBranchPatterns` to customize which branches are protected.",
459
+ ].join("\n");
460
+ }
461
+ function formatProtectedBranchBlocked(project, branch, patterns) {
462
+ const projectLabel = project ? `${project.name} (${project.id})` : "this context";
463
+ return [
464
+ `Auto-commit blocked for ${projectLabel}: current branch \`${branch}\` matches protected patterns ${patterns.join(", ")}.`,
465
+ "Policy is set to `protectedBranchBehavior: \"block\"`.",
466
+ "To proceed once, call `remember` again with `allowProtectedBranch: true`.",
467
+ "To change the default, call `set_project_memory_policy` with `protectedBranchBehavior: \"allow\"`.",
468
+ ].join("\n");
469
+ }
470
+ async function shouldBlockProtectedBranchCommit(options) {
471
+ const { cwd, writeScope, automaticCommit, project, policy, allowProtectedBranch } = options;
472
+ if (!cwd || writeScope !== "project" || !automaticCommit) {
473
+ return { blocked: false };
474
+ }
475
+ const branch = await getCurrentGitBranch(cwd);
476
+ if (!branch) {
477
+ return { blocked: false };
478
+ }
479
+ const patterns = resolveProtectedBranchPatterns(policy);
480
+ if (!isProtectedBranch(branch, patterns) || allowProtectedBranch) {
481
+ return { blocked: false };
482
+ }
483
+ const behavior = resolveProtectedBranchBehavior(policy);
484
+ if (behavior === "allow") {
485
+ return { blocked: false };
486
+ }
487
+ const message = behavior === "block"
488
+ ? formatProtectedBranchBlocked(project, branch, patterns)
489
+ : formatAskForProtectedBranch(project, branch, patterns);
490
+ return { blocked: true, message };
491
+ }
457
492
  async function embedMissingNotes(storage, noteIds, force = false) {
458
493
  const notes = noteIds
459
494
  ? (await Promise.all(noteIds.map((id) => storage.readNote(id)))).filter(Boolean)
@@ -1056,7 +1091,9 @@ server.registerTool("remember", {
1056
1091
  "- Multiple related notes have accumulated — use `consolidate` to merge them\n\n" +
1057
1092
  "After storing: consider whether this note relates to any note you recalled earlier in this session. " +
1058
1093
  "If so, call `relate` to link them while you still have context.\n\n" +
1059
- "Side effects: writes note + embedding files, git commits. Returns persistence status.",
1094
+ "Side effects: writes note + embedding files, git commits. " +
1095
+ "When project-vault writes target protected branches, policy may ask or block before writing. " +
1096
+ "Returns persistence status.",
1060
1097
  annotations: {
1061
1098
  readOnlyHint: false,
1062
1099
  destructiveHint: false,
@@ -1081,18 +1118,35 @@ server.registerTool("remember", {
1081
1118
  .optional()
1082
1119
  .describe("Where to store: 'project' = shared project vault (.mnemonic/), " +
1083
1120
  "'global' = private main vault. When omitted, uses the project's saved policy or defaults to 'project'."),
1121
+ allowProtectedBranch: z
1122
+ .boolean()
1123
+ .optional()
1124
+ .describe("One-time override for protected branch checks. " +
1125
+ "When true, remember can commit on a protected branch without changing project policy."),
1084
1126
  }),
1085
1127
  outputSchema: RememberResultSchema,
1086
- }, async ({ title, content, tags, lifecycle, summary, cwd, scope }) => {
1128
+ }, async ({ title, content, tags, lifecycle, summary, cwd, scope, allowProtectedBranch = false }) => {
1087
1129
  const project = await resolveProject(cwd);
1088
1130
  const cleanedContent = await cleanMarkdown(content);
1089
- const policyScope = await getProjectPolicyScope(cwd);
1131
+ const policy = project ? await configStore.getProjectPolicy(project.id) : undefined;
1132
+ const policyScope = policy?.defaultScope;
1090
1133
  const projectVaultExists = cwd ? Boolean(await vaultManager.getProjectVaultIfExists(cwd)) : true;
1091
1134
  const writeScope = resolveWriteScope(scope, policyScope, Boolean(project), projectVaultExists);
1092
1135
  if (writeScope === "ask") {
1093
1136
  const unadopted = !projectVaultExists && !policyScope;
1094
1137
  return { content: [{ type: "text", text: formatAskForWriteScope(project, unadopted) }], isError: true };
1095
1138
  }
1139
+ const protectedBranchCheck = await shouldBlockProtectedBranchCommit({
1140
+ cwd,
1141
+ writeScope,
1142
+ automaticCommit: scope === undefined,
1143
+ project,
1144
+ policy,
1145
+ allowProtectedBranch,
1146
+ });
1147
+ if (protectedBranchCheck.blocked) {
1148
+ return { content: [{ type: "text", text: protectedBranchCheck.message ?? "Protected branch policy blocked this commit." }], isError: true };
1149
+ }
1096
1150
  const vault = await resolveWriteVault(cwd, writeScope);
1097
1151
  const id = makeId(title);
1098
1152
  const now = new Date().toISOString();
@@ -1158,10 +1212,11 @@ server.registerTool("remember", {
1158
1212
  // ── set_project_memory_policy ─────────────────────────────────────────────────
1159
1213
  server.registerTool("set_project_memory_policy", {
1160
1214
  title: "Set Project Memory Policy",
1161
- description: "Set the default write scope and consolidation mode for a project.\n\n" +
1215
+ description: "Set the default write scope, consolidation mode, and protected-branch behavior for a project.\n\n" +
1162
1216
  "Use this when:\n" +
1163
1217
  "- The user wants all memories for a project to go to a specific vault by default\n" +
1164
- "- The current 'ask' behavior is inconvenient and should be automated\n\n" +
1218
+ "- The current 'ask' behavior is inconvenient and should be automated\n" +
1219
+ "- Protected branches should prompt, block, or allow project-vault commits by default\n\n" +
1165
1220
  "Do not use this when:\n" +
1166
1221
  "- You just need to store a single memory with a specific scope — pass `scope` to `remember` instead\n\n" +
1167
1222
  "Side effects: writes to main vault config.json, git commits and pushes.",
@@ -1173,42 +1228,79 @@ server.registerTool("set_project_memory_policy", {
1173
1228
  },
1174
1229
  inputSchema: z.object({
1175
1230
  cwd: z.string().describe("Absolute path to the project working directory"),
1176
- defaultScope: z.enum(PROJECT_POLICY_SCOPES).describe("Default storage: 'project' = shared .mnemonic/ vault, 'global' = private main vault, 'ask' = prompt each time"),
1231
+ defaultScope: z.enum(PROJECT_POLICY_SCOPES).optional().describe("Default storage: 'project' = shared .mnemonic/ vault, 'global' = private main vault, 'ask' = prompt each time"),
1177
1232
  consolidationMode: z.enum(CONSOLIDATION_MODES).optional().describe("Default consolidation mode: 'supersedes' preserves history (default), 'delete' removes sources immediately"),
1233
+ protectedBranchBehavior: z.enum(PROTECTED_BRANCH_BEHAVIORS).optional().describe("Behavior for protected-branch matches during project-vault remember writes: 'ask', 'block', or 'allow'"),
1234
+ protectedBranchPatterns: z.array(z.string()).optional().describe("Protected branch glob patterns. Defaults to [\"main\", \"master\", \"release*\"] when not set"),
1178
1235
  }),
1179
1236
  outputSchema: PolicyResultSchema,
1180
- }, async ({ cwd, defaultScope, consolidationMode }) => {
1237
+ }, async ({ cwd, defaultScope, consolidationMode, protectedBranchBehavior, protectedBranchPatterns }) => {
1181
1238
  const project = await resolveProject(cwd);
1182
1239
  if (!project) {
1183
1240
  return { content: [{ type: "text", text: `Could not detect a project for: ${cwd}` }], isError: true };
1184
1241
  }
1242
+ if (defaultScope === undefined
1243
+ && consolidationMode === undefined
1244
+ && protectedBranchBehavior === undefined
1245
+ && protectedBranchPatterns === undefined) {
1246
+ return {
1247
+ content: [{
1248
+ type: "text",
1249
+ text: "No policy fields provided. Set at least one of: defaultScope, consolidationMode, protectedBranchBehavior, protectedBranchPatterns.",
1250
+ }],
1251
+ isError: true,
1252
+ };
1253
+ }
1254
+ const existing = await configStore.getProjectPolicy(project.id);
1255
+ const effectiveDefaultScope = defaultScope ?? existing?.defaultScope ?? "project";
1256
+ const effectiveConsolidationMode = consolidationMode ?? existing?.consolidationMode;
1257
+ const effectiveProtectedBranchBehavior = protectedBranchBehavior ?? existing?.protectedBranchBehavior;
1258
+ const effectiveProtectedBranchPatterns = protectedBranchPatterns
1259
+ ? protectedBranchPatterns.map((pattern) => pattern.trim()).filter((pattern) => pattern.length > 0)
1260
+ : existing?.protectedBranchPatterns;
1185
1261
  const now = new Date().toISOString();
1186
1262
  const policy = {
1187
1263
  projectId: project.id,
1188
1264
  projectName: project.name,
1189
- defaultScope,
1190
- consolidationMode,
1265
+ defaultScope: effectiveDefaultScope,
1266
+ consolidationMode: effectiveConsolidationMode,
1267
+ protectedBranchBehavior: effectiveProtectedBranchBehavior,
1268
+ protectedBranchPatterns: effectiveProtectedBranchPatterns,
1191
1269
  updatedAt: now,
1192
1270
  };
1193
1271
  await configStore.setProjectPolicy(policy);
1194
- const modeStr = consolidationMode ? `, consolidationMode=${consolidationMode}` : "";
1272
+ const modeStr = effectiveConsolidationMode ? `, consolidationMode=${effectiveConsolidationMode}` : "";
1273
+ const branchBehaviorStr = effectiveProtectedBranchBehavior
1274
+ ? `, protectedBranchBehavior=${effectiveProtectedBranchBehavior}`
1275
+ : "";
1276
+ const branchPatternsStr = effectiveProtectedBranchPatterns && effectiveProtectedBranchPatterns.length > 0
1277
+ ? `, protectedBranchPatterns=${effectiveProtectedBranchPatterns.join("|")}`
1278
+ : "";
1195
1279
  const commitBody = formatCommitBody({
1196
1280
  projectName: project.name,
1197
- description: `Default scope: ${defaultScope}${modeStr ? `\nConsolidation mode: ${consolidationMode}` : ""}`,
1281
+ description: `Default scope: ${effectiveDefaultScope}` +
1282
+ `${effectiveConsolidationMode ? `\nConsolidation mode: ${effectiveConsolidationMode}` : ""}` +
1283
+ `${effectiveProtectedBranchBehavior ? `\nProtected branch behavior: ${effectiveProtectedBranchBehavior}` : ""}` +
1284
+ `${effectiveProtectedBranchPatterns && effectiveProtectedBranchPatterns.length > 0
1285
+ ? `\nProtected branch patterns: ${effectiveProtectedBranchPatterns.join(", ")}`
1286
+ : ""}`,
1198
1287
  });
1199
- await vaultManager.main.git.commit(`policy: ${project.name} default scope ${defaultScope}`, ["config.json"], commitBody);
1288
+ await vaultManager.main.git.commit(`policy: ${project.name} default scope ${effectiveDefaultScope}`, ["config.json"], commitBody);
1200
1289
  await pushAfterMutation(vaultManager.main);
1201
1290
  const structuredContent = {
1202
1291
  action: "policy_set",
1203
1292
  project: { id: project.id, name: project.name },
1204
- defaultScope,
1205
- consolidationMode,
1293
+ defaultScope: effectiveDefaultScope,
1294
+ consolidationMode: effectiveConsolidationMode,
1295
+ protectedBranchBehavior: effectiveProtectedBranchBehavior,
1296
+ protectedBranchPatterns: effectiveProtectedBranchPatterns,
1206
1297
  timestamp: now,
1207
1298
  };
1208
1299
  return {
1209
1300
  content: [{
1210
1301
  type: "text",
1211
- text: `Project memory policy set for ${project.name}: defaultScope=${defaultScope}${modeStr}`,
1302
+ text: `Project memory policy set for ${project.name}: defaultScope=${effectiveDefaultScope}` +
1303
+ `${modeStr}${branchBehaviorStr}${branchPatternsStr}`,
1212
1304
  }],
1213
1305
  structuredContent,
1214
1306
  };
@@ -1216,7 +1308,7 @@ server.registerTool("set_project_memory_policy", {
1216
1308
  // ── get_project_memory_policy ─────────────────────────────────────────────────
1217
1309
  server.registerTool("get_project_memory_policy", {
1218
1310
  title: "Get Project Memory Policy",
1219
- description: "Show the saved default write scope and consolidation mode for a project.\n\n" +
1311
+ description: "Show the saved default write scope, consolidation mode, and protected-branch settings for a project.\n\n" +
1220
1312
  "Use this when:\n" +
1221
1313
  "- Checking what the current storage default is before changing it\n" +
1222
1314
  "- Debugging why memories are landing in an unexpected vault\n\n" +
@@ -1254,12 +1346,22 @@ server.registerTool("get_project_memory_policy", {
1254
1346
  project: { id: project.id, name: project.name },
1255
1347
  defaultScope: policy.defaultScope,
1256
1348
  consolidationMode: policy.consolidationMode,
1349
+ protectedBranchBehavior: policy.protectedBranchBehavior,
1350
+ protectedBranchPatterns: policy.protectedBranchPatterns,
1257
1351
  updatedAt: policy.updatedAt,
1258
1352
  };
1353
+ const details = [
1354
+ `defaultScope=${policy.defaultScope}`,
1355
+ policy.consolidationMode ? `consolidationMode=${policy.consolidationMode}` : undefined,
1356
+ policy.protectedBranchBehavior ? `protectedBranchBehavior=${policy.protectedBranchBehavior}` : undefined,
1357
+ policy.protectedBranchPatterns && policy.protectedBranchPatterns.length > 0
1358
+ ? `protectedBranchPatterns=${policy.protectedBranchPatterns.join("|")}`
1359
+ : undefined,
1360
+ ].filter(Boolean).join(", ");
1259
1361
  return {
1260
1362
  content: [{
1261
1363
  type: "text",
1262
- text: `Project memory policy for ${project.name}: defaultScope=${policy.defaultScope} (updated ${policy.updatedAt})`,
1364
+ text: `Project memory policy for ${project.name}: ${details} (updated ${policy.updatedAt})`,
1263
1365
  }],
1264
1366
  structuredContent,
1265
1367
  };