oc-tweaks 0.11.1 → 0.11.3

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/dist/cli/init.js CHANGED
@@ -2,8 +2,8 @@
2
2
  // @bun
3
3
 
4
4
  // src/cli/init.ts
5
- import { mkdir } from "fs/promises";
6
- import { dirname } from "path";
5
+ import { mkdir as mkdir2 } from "fs/promises";
6
+ import { dirname as dirname2 } from "path";
7
7
 
8
8
  // src/plugins/auto-memory/diag.ts
9
9
  import { statSync } from "node:fs";
@@ -103,6 +103,33 @@ function parseFrontmatter(raw) {
103
103
  };
104
104
  return { meta, body };
105
105
  }
106
+ function serializeFrontmatter(meta, body) {
107
+ const lines = ["---"];
108
+ if (meta.id)
109
+ lines.push(`id: ${meta.id}`);
110
+ lines.push(`scope: ${meta.scope}`);
111
+ if (meta.type)
112
+ lines.push(`type: ${meta.type}`);
113
+ if (meta.source)
114
+ lines.push(`source: ${meta.source}`);
115
+ if (meta.created_at)
116
+ lines.push(`created_at: ${meta.created_at}`);
117
+ if (meta.updated_at)
118
+ lines.push(`updated_at: ${meta.updated_at}`);
119
+ lines.push(`trusted_as_instruction: false`);
120
+ if (meta.summary !== undefined)
121
+ lines.push(`summary: "${meta.summary}"`);
122
+ if (meta.disabled !== undefined)
123
+ lines.push(`disabled: ${meta.disabled ? "true" : "false"}`);
124
+ if (meta.usage_count !== undefined)
125
+ lines.push(`usage_count: ${meta.usage_count}`);
126
+ if (meta.last_usage !== undefined)
127
+ lines.push(`last_usage: ${meta.last_usage}`);
128
+ lines.push("---");
129
+ lines.push(body);
130
+ return lines.join(`
131
+ `);
132
+ }
106
133
 
107
134
  // src/plugins/auto-memory/registry.ts
108
135
  var SKIP_PATTERNS = [
@@ -169,6 +196,8 @@ function scanDir(dir, scope) {
169
196
  continue;
170
197
  }
171
198
  const { meta, body } = parseFrontmatter(partial);
199
+ if (meta.disabled === true)
200
+ continue;
172
201
  const summary = extractSummary(meta, body);
173
202
  const tokenEstimate = Math.ceil((summary.length + JSON.stringify(meta).length) / 4);
174
203
  const entry = {
@@ -279,6 +308,90 @@ function formatDiagReport(report) {
279
308
  }
280
309
  if (false) {}
281
310
 
311
+ // src/plugins/auto-memory/migrate.ts
312
+ import { mkdir, open, readdir, readFile, rename, unlink } from "node:fs/promises";
313
+ import { dirname, join as join3, resolve } from "node:path";
314
+ function detectScope(absRoot, override) {
315
+ if (override)
316
+ return override;
317
+ return absRoot.includes(`.opencode${"/"}memory`) || absRoot.includes(`.opencode${"\\"}memory`) ? "project" : "global";
318
+ }
319
+ function slugifyFilename(filename) {
320
+ return filename.replace(/\.md$/i, "").toLowerCase().replace(/[\s_]+/g, "-");
321
+ }
322
+ async function atomicWrite(absPath, content) {
323
+ const tmpfile = `${absPath}.tmp.${Date.now()}-${Math.random().toString(36).slice(2)}`;
324
+ let tmpfileCreated = false;
325
+ try {
326
+ await mkdir(dirname(absPath), { recursive: true });
327
+ const handle = await open(tmpfile, "w");
328
+ try {
329
+ await handle.writeFile(content, "utf8");
330
+ await handle.sync();
331
+ } finally {
332
+ await handle.close();
333
+ }
334
+ tmpfileCreated = true;
335
+ await rename(tmpfile, absPath);
336
+ tmpfileCreated = false;
337
+ } finally {
338
+ if (tmpfileCreated) {
339
+ await unlink(tmpfile).catch(() => {});
340
+ }
341
+ }
342
+ }
343
+ async function migrate(opts) {
344
+ const absRoot = resolve(opts.root);
345
+ const scope = detectScope(absRoot, opts.scope);
346
+ const result = { migrated: [], skipped: [], errored: [] };
347
+ let filenames;
348
+ try {
349
+ filenames = await readdir(absRoot);
350
+ } catch {
351
+ return result;
352
+ }
353
+ for (const filename of filenames) {
354
+ if (!filename.endsWith(".md"))
355
+ continue;
356
+ const absPath = join3(absRoot, filename);
357
+ let raw;
358
+ try {
359
+ raw = await readFile(absPath, "utf8");
360
+ } catch (err) {
361
+ console.error(`[migrate] error reading ${filename}: ${err.message}`);
362
+ result.errored.push(filename);
363
+ continue;
364
+ }
365
+ const stripped = raw.startsWith("\uFEFF") ? raw.slice(1) : raw;
366
+ if (stripped.startsWith("---")) {
367
+ console.log(`[migrate] skip (has frontmatter): ${filename}`);
368
+ result.skipped.push(filename);
369
+ continue;
370
+ }
371
+ const now = new Date().toISOString();
372
+ const meta = {
373
+ id: slugifyFilename(filename),
374
+ scope,
375
+ type: "note",
376
+ source: "migrate",
377
+ created_at: now,
378
+ updated_at: now,
379
+ trusted_as_instruction: false
380
+ };
381
+ const newContent = serializeFrontmatter(meta, raw);
382
+ try {
383
+ await atomicWrite(absPath, newContent);
384
+ console.log(`[migrate] migrated: ${filename}`);
385
+ result.migrated.push(filename);
386
+ } catch (err) {
387
+ console.error(`[migrate] error writing ${filename}: ${err.message}`);
388
+ result.errored.push(filename);
389
+ }
390
+ }
391
+ return result;
392
+ }
393
+ if (false) {}
394
+
282
395
  // src/cli/init.ts
283
396
  var DEFAULT_CONFIG = {
284
397
  notify: { enabled: true },
@@ -296,7 +409,7 @@ async function initConfig() {
296
409
  return { created: false, path: configPath };
297
410
  }
298
411
  const json = JSON.stringify(DEFAULT_CONFIG, null, 2);
299
- await mkdir(dirname(configPath), { recursive: true });
412
+ await mkdir2(dirname2(configPath), { recursive: true });
300
413
  await Bun.write(configPath, json + `
301
414
  `);
302
415
  return { created: true, path: configPath };
@@ -308,6 +421,9 @@ function parseCliArgs(argv) {
308
421
  if (args[0] === "memory" && args[1] === "diag") {
309
422
  return { command: "memory-diag", opts: parseDiagOpts(args.slice(2)) };
310
423
  }
424
+ if (args[0] === "memory" && args[1] === "migrate") {
425
+ return { command: "memory-migrate", opts: parseMigrateOpts(args.slice(2)) };
426
+ }
311
427
  return { command: "init" };
312
428
  }
313
429
  function parseDiagOpts(args) {
@@ -323,12 +439,31 @@ function parseDiagOpts(args) {
323
439
  }
324
440
  return opts;
325
441
  }
442
+ function parseMigrateOpts(args) {
443
+ const opts = { root: "" };
444
+ for (let i = 0;i < args.length; i++) {
445
+ const arg = args[i];
446
+ if (arg === "--root" && args[i + 1])
447
+ opts.root = args[++i];
448
+ else if (arg === "--scope" && args[i + 1]) {
449
+ const scope = args[++i];
450
+ if (scope === "global" || scope === "project")
451
+ opts.scope = scope;
452
+ }
453
+ }
454
+ return opts;
455
+ }
326
456
  var isMain = typeof Bun !== "undefined" && Bun.main === import.meta.path;
327
457
  if (isMain) {
328
458
  const parsed = parseCliArgs(process.argv.slice(2));
329
459
  if (parsed.command === "memory-diag") {
330
460
  const report = await runDiag(parsed.opts);
331
461
  console.log(formatDiagReport(report));
462
+ } else if (parsed.command === "memory-migrate") {
463
+ if (!parsed.opts.root)
464
+ throw new Error("--root <dir> is required");
465
+ const result = await migrate(parsed.opts);
466
+ console.log(`Summary: migrated=${result.migrated.length}, skipped=${result.skipped.length}, errored=${result.errored.length}`);
332
467
  } else {
333
468
  const result = await initConfig();
334
469
  if (result.created) {
package/dist/index.js CHANGED
@@ -931,7 +931,8 @@ async function recallMemory(query, registry, opts = {}) {
931
931
  if (query.length === 0) {
932
932
  return [{ id: NO_MATCH_SENTINEL_ID, content: NO_MATCH_SENTINEL_CONTENT }];
933
933
  }
934
- const typeCandidates = opts.filterType ? registry.filter((e) => e.meta.type === opts.filterType) : registry;
934
+ const enabledEntries = registry.filter((entry) => entry.meta.disabled !== true);
935
+ const typeCandidates = opts.filterType ? enabledEntries.filter((e) => e.meta.type === opts.filterType) : enabledEntries;
935
936
  const candidates = opts.filterTags?.length ? typeCandidates.filter((entry) => {
936
937
  const tags = entry.meta.tags;
937
938
  return !tags || tags.some((tag) => opts.filterTags?.includes(tag));
@@ -1028,6 +1029,8 @@ function scanDir(dir, scope) {
1028
1029
  continue;
1029
1030
  }
1030
1031
  const { meta, body } = parseFrontmatter(partial);
1032
+ if (meta.disabled === true)
1033
+ continue;
1031
1034
  const summary = extractSummary(meta, body);
1032
1035
  const tokenEstimate = Math.ceil((summary.length + JSON.stringify(meta).length) / 4);
1033
1036
  const entry = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oc-tweaks",
3
- "version": "0.11.1",
3
+ "version": "0.11.3",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dist/index.js"