decision-memory 0.1.6 → 0.1.7

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 CHANGED
@@ -28,21 +28,20 @@ No manual intervention needed.
28
28
 
29
29
  ## Quickstart (Claude Code)
30
30
 
31
- ### 1. Copy integration files to your project
32
-
33
31
  ```bash
34
- npx degit baadir/decisionmemo/integrations/claude-code . --force
32
+ npx decision-memory init
35
33
  ```
36
34
 
37
- This pulls `.mcp.json`, `CLAUDE.md` and `.claude/` directly into your project root — no cloning, no manual copying.
38
-
39
- ### 2. Initialize DECISIONS.toon
35
+ This single command:
36
+ - Creates `DECISIONS.toon` in your project root
37
+ - Copies `.mcp.json` (MCP server config)
38
+ - Copies `CLAUDE.md` (session-start instructions for Claude)
39
+ - Copies `.claude/hooks/` (auto-trigger hook)
40
+ - Updates `.gitattributes`
40
41
 
41
- ```bash
42
- npx decision-memory init
43
- ```
42
+ Then restart Claude Code — the MCP server connects automatically.
44
43
 
45
- ### 3. Start Claude Code
44
+ ### Start Claude Code
46
45
 
47
46
  Claude will automatically:
48
47
  - Call `get_context_summary` at session start
package/dist/index.cjs CHANGED
@@ -3109,13 +3109,15 @@ function splitCsvRow(line) {
3109
3109
  fields.push(current);
3110
3110
  return fields;
3111
3111
  }
3112
- function parseTableHeader(line) {
3112
+ function parseDecisionsHeader(line) {
3113
3113
  const match = line.match(/^decisions\[(\d+)\]\{([^}]+)\}:$/);
3114
3114
  if (!match) return null;
3115
- return {
3116
- count: parseInt(match[1], 10),
3117
- fields: match[2].split(",")
3118
- };
3115
+ return { count: parseInt(match[1], 10), fields: match[2].split(",") };
3116
+ }
3117
+ function parseChangesHeader(line) {
3118
+ const match = line.match(/^changes\[(\d+)\]\{([^}]+)\}:$/);
3119
+ if (!match) return null;
3120
+ return { count: parseInt(match[1], 10), fields: match[2].split(",") };
3119
3121
  }
3120
3122
  function isSummaryLine(line) {
3121
3123
  return line.startsWith("summary{");
@@ -3130,15 +3132,17 @@ function parseDecisionRow(fields, values) {
3130
3132
  const impact = validImpacts.includes(impactRaw) ? impactRaw : "medium";
3131
3133
  const tagsRaw = get("tags");
3132
3134
  const tags = tagsRaw ? tagsRaw.split("|").filter(Boolean) : [];
3133
- return {
3134
- id: get("id"),
3135
- ts: get("ts"),
3136
- topic: get("topic"),
3137
- decision: get("decision"),
3138
- rationale: get("rationale"),
3139
- impact,
3140
- tags
3135
+ return { id: get("id"), ts: get("ts"), topic: get("topic"), decision: get("decision"), rationale: get("rationale"), impact, tags };
3136
+ }
3137
+ function parseChangeRow(fields, values) {
3138
+ const get = (name) => {
3139
+ const idx = fields.indexOf(name);
3140
+ return idx >= 0 ? values[idx] ?? "" : "";
3141
3141
  };
3142
+ const typeRaw = get("type");
3143
+ const validTypes = ["added", "modified", "removed", "refactored"];
3144
+ const type = validTypes.includes(typeRaw) ? typeRaw : "modified";
3145
+ return { id: get("id"), ts: get("ts"), file: get("file"), type, description: get("description") };
3142
3146
  }
3143
3147
  function parseDecisionFile(content) {
3144
3148
  const lines = content.split("\n");
@@ -3149,29 +3153,43 @@ function parseDecisionFile(content) {
3149
3153
  updated: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
3150
3154
  };
3151
3155
  const decisions = [];
3156
+ const changes = [];
3152
3157
  let tableFields = [];
3153
- let inTable = false;
3158
+ let inDecisions = false;
3159
+ let inChanges = false;
3154
3160
  let inSummary = false;
3155
3161
  for (const rawLine of lines) {
3156
3162
  const line = rawLine.trimEnd();
3157
3163
  if (!line || line.startsWith("#")) continue;
3158
3164
  if (isSummaryLine(line)) {
3159
- inTable = false;
3165
+ inDecisions = false;
3166
+ inChanges = false;
3160
3167
  inSummary = true;
3161
3168
  continue;
3162
3169
  }
3163
3170
  if (inSummary) continue;
3164
- const tableHeader = parseTableHeader(line);
3165
- if (tableHeader) {
3166
- tableFields = tableHeader.fields;
3167
- inTable = true;
3171
+ const decisionsHeader = parseDecisionsHeader(line);
3172
+ if (decisionsHeader) {
3173
+ tableFields = decisionsHeader.fields;
3174
+ inDecisions = true;
3175
+ inChanges = false;
3168
3176
  continue;
3169
3177
  }
3170
- if (inTable && tableFields.length > 0) {
3178
+ const changesHeader = parseChangesHeader(line);
3179
+ if (changesHeader) {
3180
+ tableFields = changesHeader.fields;
3181
+ inChanges = true;
3182
+ inDecisions = false;
3183
+ continue;
3184
+ }
3185
+ if (inDecisions && tableFields.length > 0) {
3171
3186
  const values = splitCsvRow(line);
3172
- if (values.length >= tableFields.length) {
3173
- decisions.push(parseDecisionRow(tableFields, values));
3174
- }
3187
+ if (values.length >= tableFields.length) decisions.push(parseDecisionRow(tableFields, values));
3188
+ continue;
3189
+ }
3190
+ if (inChanges && tableFields.length > 0) {
3191
+ const values = splitCsvRow(line);
3192
+ if (values.length >= tableFields.length) changes.push(parseChangeRow(tableFields, values));
3175
3193
  continue;
3176
3194
  }
3177
3195
  const colonIdx = line.indexOf(":");
@@ -3184,25 +3202,17 @@ function parseDecisionFile(content) {
3184
3202
  else if (key === "updated") meta.updated = value;
3185
3203
  }
3186
3204
  }
3187
- return { meta, decisions };
3205
+ return { meta, decisions, changes };
3188
3206
  }
3207
+ var escapeField = (s) => {
3208
+ if (s.includes(",") || s.includes('"') || s.includes("\n")) {
3209
+ return '"' + s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n") + '"';
3210
+ }
3211
+ return s;
3212
+ };
3189
3213
  function serializeDecisionRow(d) {
3190
- const escapeField = (s) => {
3191
- if (s.includes(",") || s.includes('"') || s.includes("\n")) {
3192
- return '"' + s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n") + '"';
3193
- }
3194
- return s;
3195
- };
3196
3214
  const tags = d.tags.join("|");
3197
- return [
3198
- d.id,
3199
- d.ts,
3200
- d.topic,
3201
- escapeField(d.decision),
3202
- escapeField(d.rationale),
3203
- d.impact,
3204
- tags
3205
- ].join(",");
3215
+ return [d.id, d.ts, d.topic, escapeField(d.decision), escapeField(d.rationale), d.impact, tags].join(",");
3206
3216
  }
3207
3217
 
3208
3218
  // packages/core/src/searcher.ts
@@ -3325,6 +3335,8 @@ updated: ${today}
3325
3335
 
3326
3336
  decisions[0]{id,ts,topic,decision,rationale,impact,tags}:
3327
3337
 
3338
+ changes[0]{id,ts,file,type,description}:
3339
+
3328
3340
  summary{total,high_impact,last_updated,top_topics}:
3329
3341
  0,0,${today},
3330
3342
  `;
@@ -3332,10 +3344,7 @@ summary{total,high_impact,last_updated,top_topics}:
3332
3344
  }
3333
3345
  function updateDecisionCount(filePath, newCount) {
3334
3346
  const content = fs.readFileSync(filePath, "utf-8");
3335
- const updated = content.replace(
3336
- /^decisions\[(\d+)\]\{/m,
3337
- `decisions[${newCount}]{`
3338
- );
3347
+ const updated = content.replace(/^decisions\[(\d+)\]\{/m, `decisions[${newCount}]{`);
3339
3348
  const tmpPath = filePath + ".tmp";
3340
3349
  fs.writeFileSync(tmpPath, updated, "utf-8");
3341
3350
  fs.renameSync(tmpPath, filePath);
@@ -3345,12 +3354,7 @@ function updateSummaryBlock(filePath) {
3345
3354
  const file = parseDecisionFile(content);
3346
3355
  const summaryBlock = buildSummaryBlock(file);
3347
3356
  const summaryStart = content.indexOf("\nsummary{");
3348
- let base;
3349
- if (summaryStart >= 0) {
3350
- base = content.slice(0, summaryStart);
3351
- } else {
3352
- base = content.trimEnd();
3353
- }
3357
+ const base = summaryStart >= 0 ? content.slice(0, summaryStart) : content.trimEnd();
3354
3358
  const newContent = base + "\n" + summaryBlock + "\n";
3355
3359
  const tmpPath = filePath + ".tmp";
3356
3360
  fs.writeFileSync(tmpPath, newContent, "utf-8");
@@ -3366,15 +3370,7 @@ function appendDecision(filePath, input2) {
3366
3370
  const currentCount = countMatch ? parseInt(countMatch[1], 10) : 0;
3367
3371
  const id = generateNextId(currentCount);
3368
3372
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z").replace(/:\d{2}Z$/, "Z");
3369
- const decision = {
3370
- id,
3371
- ts,
3372
- topic: input2.topic,
3373
- decision: input2.decision,
3374
- rationale: input2.rationale,
3375
- impact: input2.impact,
3376
- tags: input2.tags
3377
- };
3373
+ const decision = { id, ts, topic: input2.topic, decision: input2.decision, rationale: input2.rationale, impact: input2.impact, tags: input2.tags };
3378
3374
  const row = serializeDecisionRow(decision);
3379
3375
  const lines = content.split("\n");
3380
3376
  let tableEnd = -1;
@@ -3386,32 +3382,23 @@ function appendDecision(filePath, input2) {
3386
3382
  continue;
3387
3383
  }
3388
3384
  if (inTable) {
3389
- if (lines[i].startsWith("summary{") || lines[i] === "" && tableEnd >= 0) {
3390
- if (lines[i].startsWith("summary{")) {
3391
- tableEnd = i - 1;
3392
- } else {
3393
- tableEnd = i - 1;
3394
- }
3385
+ if (lines[i].startsWith("summary{") || lines[i].startsWith("changes[")) {
3386
+ tableEnd = i - 1;
3395
3387
  break;
3396
3388
  }
3397
- if (lines[i] !== "") {
3398
- tableEnd = i;
3399
- }
3389
+ if (lines[i] !== "") tableEnd = i;
3400
3390
  }
3401
3391
  }
3402
3392
  lines.splice(tableEnd + 1, 0, row);
3403
- const newContent = lines.join("\n");
3404
3393
  const tmpPath = filePath + ".tmp";
3405
- fs.writeFileSync(tmpPath, newContent, "utf-8");
3394
+ fs.writeFileSync(tmpPath, lines.join("\n"), "utf-8");
3406
3395
  fs.renameSync(tmpPath, filePath);
3407
3396
  updateDecisionCount(filePath, currentCount + 1);
3408
3397
  updateSummaryBlock(filePath);
3409
3398
  return decision;
3410
3399
  }
3411
3400
  function resolveDecisionFilePath(cwd) {
3412
- if (process.env.DECISION_MEMORY_FILE) {
3413
- return process.env.DECISION_MEMORY_FILE;
3414
- }
3401
+ if (process.env.DECISION_MEMORY_FILE) return process.env.DECISION_MEMORY_FILE;
3415
3402
  const dir = cwd ?? process.cwd();
3416
3403
  const local = path.join(dir, "DECISIONS.toon");
3417
3404
  if (fs.existsSync(local)) return local;
@@ -3563,7 +3550,7 @@ function summaryCommand(options) {
3563
3550
 
3564
3551
  // packages/cli/src/index.ts
3565
3552
  var program2 = new Command();
3566
- program2.name("decision-memory").description("Claude Code i\xE7in karar haf\u0131zas\u0131 arac\u0131").version("0.1.6");
3553
+ program2.name("decision-memory").description("Claude Code i\xE7in karar haf\u0131zas\u0131 arac\u0131").version("0.1.7");
3567
3554
  program2.command("init").description("Proje k\xF6k\xFCnde DECISIONS.toon dosyas\u0131 olu\u015Fturur").option("-p, --project <name>", "Proje ad\u0131").option("-d, --dir <path>", "Dizin (varsay\u0131lan: \xE7al\u0131\u015Fma dizini)").action((options) => initCommand(options));
3568
3555
  program2.command("log").description("Yeni bir karar ekler (interaktif veya flag'lerle)").option("-t, --topic <topic>", "Konu (\xF6rn: auth, database)").option("-d, --decision <text>", "Al\u0131nan karar").option("-r, --rationale <text>", "Gerek\xE7e").option("-i, --impact <level>", "Etki: low|medium|high|critical").option("--tags <tags>", "Etiketler (virg\xFClle ayr\u0131lm\u0131\u015F)").option("-f, --file <path>", "DECISIONS.toon dosya yolu").action(async (options) => {
3569
3556
  await logCommand(options);
package/dist/mcp.cjs CHANGED
@@ -20928,6 +20928,14 @@ function generateNextId(currentCount) {
20928
20928
  const encoded = encodeBase36(next).padStart(4, "0");
20929
20929
  return `D${encoded}`;
20930
20930
  }
20931
+ function generateNextChangeId(currentCount) {
20932
+ const next = currentCount + 1;
20933
+ if (next <= 999) {
20934
+ return `C${String(next).padStart(3, "0")}`;
20935
+ }
20936
+ const encoded = encodeBase36(next).padStart(4, "0");
20937
+ return `C${encoded}`;
20938
+ }
20931
20939
 
20932
20940
  // packages/core/src/parser.ts
20933
20941
  function splitCsvRow(line) {
@@ -20974,13 +20982,15 @@ function splitCsvRow(line) {
20974
20982
  fields.push(current);
20975
20983
  return fields;
20976
20984
  }
20977
- function parseTableHeader(line) {
20985
+ function parseDecisionsHeader(line) {
20978
20986
  const match = line.match(/^decisions\[(\d+)\]\{([^}]+)\}:$/);
20979
20987
  if (!match) return null;
20980
- return {
20981
- count: parseInt(match[1], 10),
20982
- fields: match[2].split(",")
20983
- };
20988
+ return { count: parseInt(match[1], 10), fields: match[2].split(",") };
20989
+ }
20990
+ function parseChangesHeader(line) {
20991
+ const match = line.match(/^changes\[(\d+)\]\{([^}]+)\}:$/);
20992
+ if (!match) return null;
20993
+ return { count: parseInt(match[1], 10), fields: match[2].split(",") };
20984
20994
  }
20985
20995
  function isSummaryLine(line) {
20986
20996
  return line.startsWith("summary{");
@@ -20995,15 +21005,17 @@ function parseDecisionRow(fields, values) {
20995
21005
  const impact = validImpacts.includes(impactRaw) ? impactRaw : "medium";
20996
21006
  const tagsRaw = get("tags");
20997
21007
  const tags = tagsRaw ? tagsRaw.split("|").filter(Boolean) : [];
20998
- return {
20999
- id: get("id"),
21000
- ts: get("ts"),
21001
- topic: get("topic"),
21002
- decision: get("decision"),
21003
- rationale: get("rationale"),
21004
- impact,
21005
- tags
21008
+ return { id: get("id"), ts: get("ts"), topic: get("topic"), decision: get("decision"), rationale: get("rationale"), impact, tags };
21009
+ }
21010
+ function parseChangeRow(fields, values) {
21011
+ const get = (name) => {
21012
+ const idx = fields.indexOf(name);
21013
+ return idx >= 0 ? values[idx] ?? "" : "";
21006
21014
  };
21015
+ const typeRaw = get("type");
21016
+ const validTypes = ["added", "modified", "removed", "refactored"];
21017
+ const type = validTypes.includes(typeRaw) ? typeRaw : "modified";
21018
+ return { id: get("id"), ts: get("ts"), file: get("file"), type, description: get("description") };
21007
21019
  }
21008
21020
  function parseDecisionFile(content) {
21009
21021
  const lines = content.split("\n");
@@ -21014,29 +21026,43 @@ function parseDecisionFile(content) {
21014
21026
  updated: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
21015
21027
  };
21016
21028
  const decisions = [];
21029
+ const changes = [];
21017
21030
  let tableFields = [];
21018
- let inTable = false;
21031
+ let inDecisions = false;
21032
+ let inChanges = false;
21019
21033
  let inSummary = false;
21020
21034
  for (const rawLine of lines) {
21021
21035
  const line = rawLine.trimEnd();
21022
21036
  if (!line || line.startsWith("#")) continue;
21023
21037
  if (isSummaryLine(line)) {
21024
- inTable = false;
21038
+ inDecisions = false;
21039
+ inChanges = false;
21025
21040
  inSummary = true;
21026
21041
  continue;
21027
21042
  }
21028
21043
  if (inSummary) continue;
21029
- const tableHeader = parseTableHeader(line);
21030
- if (tableHeader) {
21031
- tableFields = tableHeader.fields;
21032
- inTable = true;
21044
+ const decisionsHeader = parseDecisionsHeader(line);
21045
+ if (decisionsHeader) {
21046
+ tableFields = decisionsHeader.fields;
21047
+ inDecisions = true;
21048
+ inChanges = false;
21033
21049
  continue;
21034
21050
  }
21035
- if (inTable && tableFields.length > 0) {
21051
+ const changesHeader = parseChangesHeader(line);
21052
+ if (changesHeader) {
21053
+ tableFields = changesHeader.fields;
21054
+ inChanges = true;
21055
+ inDecisions = false;
21056
+ continue;
21057
+ }
21058
+ if (inDecisions && tableFields.length > 0) {
21036
21059
  const values = splitCsvRow(line);
21037
- if (values.length >= tableFields.length) {
21038
- decisions.push(parseDecisionRow(tableFields, values));
21039
- }
21060
+ if (values.length >= tableFields.length) decisions.push(parseDecisionRow(tableFields, values));
21061
+ continue;
21062
+ }
21063
+ if (inChanges && tableFields.length > 0) {
21064
+ const values = splitCsvRow(line);
21065
+ if (values.length >= tableFields.length) changes.push(parseChangeRow(tableFields, values));
21040
21066
  continue;
21041
21067
  }
21042
21068
  const colonIdx = line.indexOf(":");
@@ -21049,25 +21075,20 @@ function parseDecisionFile(content) {
21049
21075
  else if (key === "updated") meta.updated = value;
21050
21076
  }
21051
21077
  }
21052
- return { meta, decisions };
21078
+ return { meta, decisions, changes };
21053
21079
  }
21080
+ var escapeField = (s) => {
21081
+ if (s.includes(",") || s.includes('"') || s.includes("\n")) {
21082
+ return '"' + s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n") + '"';
21083
+ }
21084
+ return s;
21085
+ };
21054
21086
  function serializeDecisionRow(d) {
21055
- const escapeField = (s) => {
21056
- if (s.includes(",") || s.includes('"') || s.includes("\n")) {
21057
- return '"' + s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n") + '"';
21058
- }
21059
- return s;
21060
- };
21061
21087
  const tags = d.tags.join("|");
21062
- return [
21063
- d.id,
21064
- d.ts,
21065
- d.topic,
21066
- escapeField(d.decision),
21067
- escapeField(d.rationale),
21068
- d.impact,
21069
- tags
21070
- ].join(",");
21088
+ return [d.id, d.ts, d.topic, escapeField(d.decision), escapeField(d.rationale), d.impact, tags].join(",");
21089
+ }
21090
+ function serializeChangeRow(c) {
21091
+ return [c.id, c.ts, escapeField(c.file), c.type, escapeField(c.description)].join(",");
21071
21092
  }
21072
21093
 
21073
21094
  // packages/core/src/searcher.ts
@@ -21179,10 +21200,14 @@ var path = __toESM(require("path"), 1);
21179
21200
  var os = __toESM(require("os"), 1);
21180
21201
  function updateDecisionCount(filePath, newCount) {
21181
21202
  const content = fs.readFileSync(filePath, "utf-8");
21182
- const updated = content.replace(
21183
- /^decisions\[(\d+)\]\{/m,
21184
- `decisions[${newCount}]{`
21185
- );
21203
+ const updated = content.replace(/^decisions\[(\d+)\]\{/m, `decisions[${newCount}]{`);
21204
+ const tmpPath = filePath + ".tmp";
21205
+ fs.writeFileSync(tmpPath, updated, "utf-8");
21206
+ fs.renameSync(tmpPath, filePath);
21207
+ }
21208
+ function updateChangeCount(filePath, newCount) {
21209
+ const content = fs.readFileSync(filePath, "utf-8");
21210
+ const updated = content.replace(/^changes\[(\d+)\]\{/m, `changes[${newCount}]{`);
21186
21211
  const tmpPath = filePath + ".tmp";
21187
21212
  fs.writeFileSync(tmpPath, updated, "utf-8");
21188
21213
  fs.renameSync(tmpPath, filePath);
@@ -21192,12 +21217,7 @@ function updateSummaryBlock(filePath) {
21192
21217
  const file = parseDecisionFile(content);
21193
21218
  const summaryBlock = buildSummaryBlock(file);
21194
21219
  const summaryStart = content.indexOf("\nsummary{");
21195
- let base;
21196
- if (summaryStart >= 0) {
21197
- base = content.slice(0, summaryStart);
21198
- } else {
21199
- base = content.trimEnd();
21200
- }
21220
+ const base = summaryStart >= 0 ? content.slice(0, summaryStart) : content.trimEnd();
21201
21221
  const newContent = base + "\n" + summaryBlock + "\n";
21202
21222
  const tmpPath = filePath + ".tmp";
21203
21223
  fs.writeFileSync(tmpPath, newContent, "utf-8");
@@ -21213,15 +21233,7 @@ function appendDecision(filePath, input) {
21213
21233
  const currentCount = countMatch ? parseInt(countMatch[1], 10) : 0;
21214
21234
  const id = generateNextId(currentCount);
21215
21235
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z").replace(/:\d{2}Z$/, "Z");
21216
- const decision = {
21217
- id,
21218
- ts,
21219
- topic: input.topic,
21220
- decision: input.decision,
21221
- rationale: input.rationale,
21222
- impact: input.impact,
21223
- tags: input.tags
21224
- };
21236
+ const decision = { id, ts, topic: input.topic, decision: input.decision, rationale: input.rationale, impact: input.impact, tags: input.tags };
21225
21237
  const row = serializeDecisionRow(decision);
21226
21238
  const lines = content.split("\n");
21227
21239
  let tableEnd = -1;
@@ -21233,28 +21245,66 @@ function appendDecision(filePath, input) {
21233
21245
  continue;
21234
21246
  }
21235
21247
  if (inTable) {
21236
- if (lines[i].startsWith("summary{") || lines[i] === "" && tableEnd >= 0) {
21237
- if (lines[i].startsWith("summary{")) {
21238
- tableEnd = i - 1;
21239
- } else {
21240
- tableEnd = i - 1;
21241
- }
21248
+ if (lines[i].startsWith("summary{") || lines[i].startsWith("changes[")) {
21249
+ tableEnd = i - 1;
21242
21250
  break;
21243
21251
  }
21244
- if (lines[i] !== "") {
21245
- tableEnd = i;
21246
- }
21252
+ if (lines[i] !== "") tableEnd = i;
21247
21253
  }
21248
21254
  }
21249
21255
  lines.splice(tableEnd + 1, 0, row);
21250
- const newContent = lines.join("\n");
21251
21256
  const tmpPath = filePath + ".tmp";
21252
- fs.writeFileSync(tmpPath, newContent, "utf-8");
21257
+ fs.writeFileSync(tmpPath, lines.join("\n"), "utf-8");
21253
21258
  fs.renameSync(tmpPath, filePath);
21254
21259
  updateDecisionCount(filePath, currentCount + 1);
21255
21260
  updateSummaryBlock(filePath);
21256
21261
  return decision;
21257
21262
  }
21263
+ function appendChange(filePath, input) {
21264
+ if (!fs.existsSync(filePath)) {
21265
+ throw new Error(`DECISIONS.toon dosyas\u0131 bulunamad\u0131: ${filePath}
21266
+ \xD6nce 'decision-memory init' komutunu \xE7al\u0131\u015Ft\u0131r\u0131n.`);
21267
+ }
21268
+ let content = fs.readFileSync(filePath, "utf-8");
21269
+ if (!/^changes\[/m.test(content)) {
21270
+ const summaryStart = content.indexOf("\nsummary{");
21271
+ const insertPoint = summaryStart >= 0 ? summaryStart : content.length;
21272
+ content = content.slice(0, insertPoint) + "\nchanges[0]{id,ts,file,type,description}:\n" + content.slice(insertPoint);
21273
+ const tmpPath2 = filePath + ".tmp";
21274
+ fs.writeFileSync(tmpPath2, content, "utf-8");
21275
+ fs.renameSync(tmpPath2, filePath);
21276
+ content = fs.readFileSync(filePath, "utf-8");
21277
+ }
21278
+ const countMatch = content.match(/^changes\[(\d+)\]/m);
21279
+ const currentCount = countMatch ? parseInt(countMatch[1], 10) : 0;
21280
+ const id = generateNextChangeId(currentCount);
21281
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z").replace(/:\d{2}Z$/, "Z");
21282
+ const change = { id, ts, file: input.file, type: input.type, description: input.description };
21283
+ const row = serializeChangeRow(change);
21284
+ const lines = content.split("\n");
21285
+ let tableEnd = -1;
21286
+ let inTable = false;
21287
+ for (let i = 0; i < lines.length; i++) {
21288
+ if (/^changes\[\d+\]\{/.test(lines[i])) {
21289
+ inTable = true;
21290
+ tableEnd = i;
21291
+ continue;
21292
+ }
21293
+ if (inTable) {
21294
+ if (lines[i].startsWith("summary{")) {
21295
+ tableEnd = i - 1;
21296
+ break;
21297
+ }
21298
+ if (lines[i] !== "") tableEnd = i;
21299
+ }
21300
+ }
21301
+ lines.splice(tableEnd + 1, 0, row);
21302
+ const tmpPath = filePath + ".tmp";
21303
+ fs.writeFileSync(tmpPath, lines.join("\n"), "utf-8");
21304
+ fs.renameSync(tmpPath, filePath);
21305
+ updateChangeCount(filePath, currentCount + 1);
21306
+ return change;
21307
+ }
21258
21308
 
21259
21309
  // packages/mcp-server/src/config.ts
21260
21310
  var fs2 = __toESM(require("fs"), 1);
@@ -21400,6 +21450,34 @@ Yeni karar: ${entry.decision}`
21400
21450
  };
21401
21451
  }
21402
21452
 
21453
+ // packages/mcp-server/src/tools/log_change.ts
21454
+ var logChangeSchema = {
21455
+ file: external_exports.string().describe(
21456
+ "De\u011Fi\u015Ftirilen dosyan\u0131n yolu (\xF6rn: src/auth/login.ts)"
21457
+ ),
21458
+ type: external_exports.enum(["added", "modified", "removed", "refactored"]).describe(
21459
+ "added=yeni dosya eklendi, modified=mevcut dosya de\u011Fi\u015Ftirildi, removed=dosya silindi, refactored=yeniden yaz\u0131ld\u0131"
21460
+ ),
21461
+ description: external_exports.string().describe(
21462
+ "Ne de\u011Fi\u015Fti ve neden? (1-2 c\xFCmle)"
21463
+ )
21464
+ };
21465
+ async function logChangeHandler(input) {
21466
+ const filePath = resolveFilePath();
21467
+ const entry = appendChange(filePath, input);
21468
+ return {
21469
+ content: [
21470
+ {
21471
+ type: "text",
21472
+ text: `De\u011Fi\u015Fiklik kaydedildi: ${entry.id}
21473
+ Dosya: ${entry.file}
21474
+ T\xFCr: ${entry.type}
21475
+ A\xE7\u0131klama: ${entry.description}`
21476
+ }
21477
+ ]
21478
+ };
21479
+ }
21480
+
21403
21481
  // packages/mcp-server/src/index.ts
21404
21482
  var server = new McpServer({
21405
21483
  name: "decision-memory",
@@ -21437,6 +21515,14 @@ server.registerTool(
21437
21515
  },
21438
21516
  updateDecisionHandler
21439
21517
  );
21518
+ server.registerTool(
21519
+ "log_change",
21520
+ {
21521
+ description: "Kod de\u011Fi\u015Fikliklerini changelog olarak DECISIONS.toon'a kaydeder. Yeni dosya eklerken, \xF6nemli bir dosyay\u0131 de\u011Fi\u015Ftirirken, silerken veya yeniden yazarken \xE7a\u011F\u0131r\u0131n.",
21522
+ inputSchema: logChangeSchema
21523
+ },
21524
+ logChangeHandler
21525
+ );
21440
21526
  async function main() {
21441
21527
  const transport = new StdioServerTransport();
21442
21528
  await server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "decision-memory",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Claude Code için karar hafızası — CLI ve MCP server",
5
5
  "bin": {
6
6
  "decision-memory": "./dist/index.cjs",