@kevin0181/memoc 1.1.11 → 1.2.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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/bin/cli.js +69 -22
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -144,7 +144,7 @@ Run it from the project root. It preserves existing project memory, including:
144
144
  - `.memoc/systems/`
145
145
  - `.memoc/wiki/`
146
146
 
147
- It refreshes the managed blocks, project-local wrappers, runtime copy, PATH helpers, and memoc-owned protocol templates. User-owned memory files such as `session-summary.md`, `03-decisions.md`, `04-handoff.md`, `06-project-rules.md`, and wiki topic/source pages are preserved. If `memoc` is not on PATH after upgrading, keep using:
147
+ It refreshes the managed blocks, project-local wrappers, runtime copy, PATH helpers, and memoc-owned protocol templates. User-owned memory files such as `session-summary.md`, `03-decisions.md`, `04-handoff.md`, `06-project-rules.md`, and wiki topic/source pages are preserved. Upgrade also runs the `trim-summary` compaction pass so startup memory stays small. If `memoc` is not on PATH after upgrading, keep using:
148
148
 
149
149
  ```bash
150
150
  # Windows
package/bin/cli.js CHANGED
@@ -246,6 +246,47 @@ function archiveLegacyLog(dir, mark) {
246
246
  mark('move', `${path.relative(dir, logPath)} -> ${path.relative(dir, archivePath)}`);
247
247
  }
248
248
 
249
+ function summarySectionBulletCounts(src) {
250
+ const counts = {};
251
+ let current = '';
252
+ for (const line of String(src || '').split(/\r?\n/)) {
253
+ const heading = line.match(/^##\s+(.+?)\s*$/);
254
+ if (heading) {
255
+ current = heading[1].trim();
256
+ counts[current] = counts[current] || 0;
257
+ continue;
258
+ }
259
+ if (current && /^-\s+/.test(line)) counts[current] = (counts[current] || 0) + 1;
260
+ }
261
+ return counts;
262
+ }
263
+
264
+ function trimSummaryFile(dir) {
265
+ const summaryPath = path.join(dir, '.memoc', 'session-summary.md');
266
+ const archivePath = path.join(dir, '.memoc', 'session-summary-archive.md');
267
+ if (!fs.existsSync(summaryPath)) {
268
+ write(summaryPath, tplSessionSummary());
269
+ return { action: 'add' };
270
+ }
271
+
272
+ const src = fs.readFileSync(summaryPath, 'utf8');
273
+ const beforeBytes = Buffer.byteLength(src, 'utf8');
274
+ const counts = summarySectionBulletCounts(src);
275
+ const tooManyBullets = Object.values(counts).some(count => count > 3);
276
+ if (beforeBytes <= 800 && !tooManyBullets) {
277
+ return { action: 'skip', beforeBytes };
278
+ }
279
+
280
+ const compact = compactSessionSummary(src);
281
+ const afterBytes = Buffer.byteLength(compact, 'utf8');
282
+ const archiveHeader = fs.existsSync(archivePath)
283
+ ? ''
284
+ : '# Session Summary Archive\n\nOlder oversized startup summaries moved by `memoc trim-summary`.\n';
285
+ fs.appendFileSync(archivePath, `${archiveHeader}\n## [${nowISO()}] archived summary (${beforeBytes}B)\n\n${src.trimEnd()}\n`, 'utf8');
286
+ write(summaryPath, compact);
287
+ return { action: 'trim', beforeBytes, afterBytes };
288
+ }
289
+
249
290
  function migrateLegacyLogReferences(filePath) {
250
291
  if (!fs.existsSync(filePath)) return false;
251
292
  const before = fs.readFileSync(filePath, 'utf8');
@@ -940,7 +981,9 @@ function collectMemocMarkdownFiles(dir) {
940
981
  }
941
982
  walk(path.join(dir, '.memoc'));
942
983
  walk(path.join(dir, 'skills', 'project-memory-maintainer'));
943
- return files.sort();
984
+ return files
985
+ .filter(fp => !/session-summary-archive(?:-\d+)?\.md$/.test(path.basename(fp)))
986
+ .sort();
944
987
  }
945
988
 
946
989
  function ensureMemocFrontmatter(filePath, dir) {
@@ -2392,6 +2435,16 @@ function run(dir, forceUpdate, action = 'update') {
2392
2435
 
2393
2436
  archiveLegacyLog(dir, mark);
2394
2437
 
2438
+ const trim = trimSummaryFile(dir);
2439
+ if (trim.action === 'trim') {
2440
+ mark('update', `.memoc/session-summary.md (trimmed ${trim.beforeBytes}B -> ${trim.afterBytes}B)`);
2441
+ mark('update', '.memoc/session-summary-archive.md');
2442
+ } else if (trim.action === 'add') {
2443
+ mark('add', '.memoc/session-summary.md');
2444
+ } else {
2445
+ mark('skip', `.memoc/session-summary.md (compact ${trim.beforeBytes || 0}B)`);
2446
+ }
2447
+
2395
2448
  // Obsidian graph filters — add/merge memoc tags for existing installs too
2396
2449
  ensureObsidianFrontmatter(dir, mark);
2397
2450
  }
@@ -3392,37 +3445,24 @@ function runCompress(dir) {
3392
3445
  // ═══════════════════════════════════════════════════════════════════
3393
3446
 
3394
3447
  function runTrimSummary(dir) {
3395
- const summaryPath = path.join(dir, '.memoc', 'session-summary.md');
3396
- const archivePath = path.join(dir, '.memoc', 'session-summary-archive.md');
3397
- if (!fs.existsSync(summaryPath)) {
3398
- write(summaryPath, tplSessionSummary());
3448
+ const result = trimSummaryFile(dir);
3449
+ if (result.action === 'add') {
3399
3450
  console.log('\n memoc trim-summary\n');
3400
3451
  console.log(' Added .memoc/session-summary.md');
3401
3452
  console.log('\n Done.\n');
3402
3453
  return;
3403
3454
  }
3404
3455
 
3405
- const src = fs.readFileSync(summaryPath, 'utf8');
3406
- const beforeBytes = Buffer.byteLength(src, 'utf8');
3407
- const compact = compactSessionSummary(src);
3408
- const afterBytes = Buffer.byteLength(compact, 'utf8');
3409
-
3410
- if (src === compact && beforeBytes <= 800) {
3456
+ if (result.action === 'skip') {
3411
3457
  console.log('\n memoc trim-summary\n');
3412
- console.log(` session-summary.md is already compact (${beforeBytes}B).`);
3458
+ console.log(` session-summary.md is already compact (${result.beforeBytes}B).`);
3413
3459
  console.log('\n Done.\n');
3414
3460
  return;
3415
3461
  }
3416
3462
 
3417
- const archiveHeader = fs.existsSync(archivePath)
3418
- ? ''
3419
- : '# Session Summary Archive\n\nOlder oversized startup summaries moved by `memoc trim-summary`.\n';
3420
- fs.appendFileSync(archivePath, `${archiveHeader}\n## [${nowISO()}] archived summary (${beforeBytes}B)\n\n${src.trimEnd()}\n`, 'utf8');
3421
- write(summaryPath, compact);
3422
-
3423
3463
  console.log('\n memoc trim-summary\n');
3424
3464
  console.log(` Archived .memoc/session-summary-archive.md`);
3425
- console.log(` Rewrote .memoc/session-summary.md (${beforeBytes}B → ${afterBytes}B)`);
3465
+ console.log(` Rewrote .memoc/session-summary.md (${result.beforeBytes}B → ${result.afterBytes}B)`);
3426
3466
  console.log(' Reminder Completed history belongs in worklog; resume details belong in 04-handoff.md.');
3427
3467
  console.log('\n Done.\n');
3428
3468
  }
@@ -3432,8 +3472,8 @@ function compactSessionSummary(src) {
3432
3472
  const lines = [
3433
3473
  '# Session Summary',
3434
3474
  `Last: ${nowISO()}`,
3435
- 'Replace this file instead of appending to it. Keep total size <800B and each section ≤3 bullets.',
3436
- 'Completed history belongs in actor worklogs; incomplete/risky resume detail belongs in `04-handoff.md`.',
3475
+ 'Replace, do not append. Keep <800B.',
3476
+ 'History: worklog. Resume risks: 04-handoff.md.',
3437
3477
  '',
3438
3478
  ];
3439
3479
 
@@ -3455,6 +3495,7 @@ function sectionText(src, heading) {
3455
3495
  }
3456
3496
 
3457
3497
  function compactSummaryBullets(text) {
3498
+ const maxLine = 72;
3458
3499
  return String(text || '')
3459
3500
  .split(/\r?\n/)
3460
3501
  .map(line => line.trim())
@@ -3462,7 +3503,13 @@ function compactSummaryBullets(text) {
3462
3503
  .map(line => line.replace(/^[-*]\s+/, '').replace(/^\d+[.)]\s+/, '').trim())
3463
3504
  .filter(Boolean)
3464
3505
  .slice(0, 3)
3465
- .map(line => `- ${line.length > 140 ? `${line.slice(0, 137)}...` : line}`);
3506
+ .map(line => {
3507
+ const compact = line
3508
+ .replace(/`/g, '')
3509
+ .replace(/\s+/g, ' ')
3510
+ .trim();
3511
+ return `- ${compact.length > maxLine ? `${compact.slice(0, maxLine - 3)}...` : compact}`;
3512
+ });
3466
3513
  }
3467
3514
 
3468
3515
  function summaryPlaceholder(heading) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kevin0181/memoc",
3
- "version": "1.1.11",
3
+ "version": "1.2.1",
4
4
  "description": "Give AI agents a memory. Scaffolds session-to-session context for Claude Code, Codex, Cursor, and more.",
5
5
  "keywords": [
6
6
  "ai",