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 +9 -10
- package/dist/index.cjs +60 -73
- package/dist/mcp.cjs +155 -69
- package/package.json +1 -1
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
|
|
32
|
+
npx decision-memory init
|
|
35
33
|
```
|
|
36
34
|
|
|
37
|
-
This
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
42
|
-
npx decision-memory init
|
|
43
|
-
```
|
|
42
|
+
Then restart Claude Code — the MCP server connects automatically.
|
|
44
43
|
|
|
45
|
-
###
|
|
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
|
|
3112
|
+
function parseDecisionsHeader(line) {
|
|
3113
3113
|
const match = line.match(/^decisions\[(\d+)\]\{([^}]+)\}:$/);
|
|
3114
3114
|
if (!match) return null;
|
|
3115
|
-
return {
|
|
3116
|
-
|
|
3117
|
-
|
|
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
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
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
|
|
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
|
-
|
|
3165
|
+
inDecisions = false;
|
|
3166
|
+
inChanges = false;
|
|
3160
3167
|
inSummary = true;
|
|
3161
3168
|
continue;
|
|
3162
3169
|
}
|
|
3163
3170
|
if (inSummary) continue;
|
|
3164
|
-
const
|
|
3165
|
-
if (
|
|
3166
|
-
tableFields =
|
|
3167
|
-
|
|
3171
|
+
const decisionsHeader = parseDecisionsHeader(line);
|
|
3172
|
+
if (decisionsHeader) {
|
|
3173
|
+
tableFields = decisionsHeader.fields;
|
|
3174
|
+
inDecisions = true;
|
|
3175
|
+
inChanges = false;
|
|
3168
3176
|
continue;
|
|
3169
3177
|
}
|
|
3170
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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]
|
|
3390
|
-
|
|
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,
|
|
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.
|
|
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
|
|
20985
|
+
function parseDecisionsHeader(line) {
|
|
20978
20986
|
const match = line.match(/^decisions\[(\d+)\]\{([^}]+)\}:$/);
|
|
20979
20987
|
if (!match) return null;
|
|
20980
|
-
return {
|
|
20981
|
-
|
|
20982
|
-
|
|
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
|
-
|
|
21000
|
-
|
|
21001
|
-
|
|
21002
|
-
|
|
21003
|
-
|
|
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
|
|
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
|
-
|
|
21038
|
+
inDecisions = false;
|
|
21039
|
+
inChanges = false;
|
|
21025
21040
|
inSummary = true;
|
|
21026
21041
|
continue;
|
|
21027
21042
|
}
|
|
21028
21043
|
if (inSummary) continue;
|
|
21029
|
-
const
|
|
21030
|
-
if (
|
|
21031
|
-
tableFields =
|
|
21032
|
-
|
|
21044
|
+
const decisionsHeader = parseDecisionsHeader(line);
|
|
21045
|
+
if (decisionsHeader) {
|
|
21046
|
+
tableFields = decisionsHeader.fields;
|
|
21047
|
+
inDecisions = true;
|
|
21048
|
+
inChanges = false;
|
|
21033
21049
|
continue;
|
|
21034
21050
|
}
|
|
21035
|
-
|
|
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
|
-
|
|
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
|
-
|
|
21064
|
-
|
|
21065
|
-
|
|
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
|
-
|
|
21184
|
-
|
|
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
|
-
|
|
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]
|
|
21237
|
-
|
|
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,
|
|
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);
|