@momomemory/opencode-momo 0.1.1 → 0.1.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/README.md CHANGED
@@ -156,10 +156,27 @@ Configuration is stored in `~/.config/opencode/momo.jsonc`:
156
156
  // Momo server URL
157
157
  "baseUrl": "http://localhost:3000",
158
158
  // API key for authentication
159
- "apiKey": "your-key-here"
159
+ "apiKey": "your-key-here",
160
+ // Optional override for user memory container tag
161
+ "containerTagUser": "",
162
+ // Optional override for project memory container tag
163
+ "containerTagProject": ""
160
164
  }
161
165
  ```
162
166
 
167
+ ### Project-local config (optional)
168
+
169
+ You can also place config in your project root as `.momo.jsonc` (preferred) or `momo.jsonc`.
170
+ This is useful when you want a stable `containerTagProject` for a specific repo.
171
+
172
+ ### Config precedence
173
+
174
+ Highest to lowest precedence:
175
+ 1. Environment variables (`MOMO_*`)
176
+ 2. Project-local config (`.momo.jsonc` / `momo.jsonc`)
177
+ 3. Global config (`~/.config/opencode/momo.jsonc`)
178
+ 4. Built-in defaults
179
+
163
180
  ### Environment Variables
164
181
 
165
182
  The following variables can be used to override configuration:
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAyIA;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CA8CjE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG;IACpD,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAWA;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7C,IAAI,CA2CN"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AA2IA;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CA+CjE;AA0ID;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG;IACpD,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAWA;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7C,IAAI,CA6CN"}
package/dist/cli.js CHANGED
@@ -17,74 +17,73 @@ import { join } from "node:path";
17
17
  import { homedir } from "node:os";
18
18
 
19
19
  // src/services/jsonc.ts
20
- function stripJsoncComments(content) {
21
- let result = "";
22
- let i = 0;
23
- let inString = false;
24
- let inSingleLineComment = false;
25
- let inMultiLineComment = false;
26
- while (i < content.length) {
27
- const char = content[i];
28
- const nextChar = content[i + 1];
29
- if (!inSingleLineComment && !inMultiLineComment) {
30
- if (char === '"') {
31
- let backslashCount = 0;
32
- let j = i - 1;
33
- while (j >= 0 && content[j] === "\\") {
34
- backslashCount++;
35
- j--;
36
- }
37
- if (backslashCount % 2 === 0) {
38
- inString = !inString;
20
+ function stripJsoncComments(input) {
21
+ var Mode;
22
+ ((Mode2) => {
23
+ Mode2[Mode2["Code"] = 0] = "Code";
24
+ Mode2[Mode2["String"] = 1] = "String";
25
+ Mode2[Mode2["LineComment"] = 2] = "LineComment";
26
+ Mode2[Mode2["BlockComment"] = 3] = "BlockComment";
27
+ })(Mode ||= {});
28
+ const out = [];
29
+ let mode = 0 /* Code */;
30
+ for (let i = 0;i < input.length; i++) {
31
+ const ch = input[i];
32
+ const next = input[i + 1];
33
+ if (mode === 1 /* String */) {
34
+ out.push(ch);
35
+ if (ch === "\\") {
36
+ if (next !== undefined) {
37
+ out.push(next);
38
+ i++;
39
39
  }
40
- result += char;
41
- i++;
42
40
  continue;
43
41
  }
44
- }
45
- if (inString) {
46
- result += char;
47
- i++;
48
- continue;
49
- }
50
- if (!inSingleLineComment && !inMultiLineComment) {
51
- if (char === "/" && nextChar === "/") {
52
- inSingleLineComment = true;
53
- i += 2;
54
- continue;
55
- }
56
- if (char === "/" && nextChar === "*") {
57
- inMultiLineComment = true;
58
- i += 2;
59
- continue;
42
+ if (ch === '"') {
43
+ mode = 0 /* Code */;
60
44
  }
45
+ continue;
61
46
  }
62
- if (inSingleLineComment) {
63
- if (char === `
47
+ if (mode === 2 /* LineComment */) {
48
+ if (ch === `
64
49
  `) {
65
- inSingleLineComment = false;
66
- result += char;
50
+ mode = 0 /* Code */;
51
+ out.push(`
52
+ `);
67
53
  }
68
- i++;
69
54
  continue;
70
55
  }
71
- if (inMultiLineComment) {
72
- if (char === "*" && nextChar === "/") {
73
- inMultiLineComment = false;
74
- i += 2;
56
+ if (mode === 3 /* BlockComment */) {
57
+ if (ch === "*" && next === "/") {
58
+ mode = 0 /* Code */;
59
+ i++;
75
60
  continue;
76
61
  }
77
- if (char === `
62
+ if (ch === `
78
63
  `) {
79
- result += char;
64
+ out.push(`
65
+ `);
80
66
  }
67
+ continue;
68
+ }
69
+ if (ch === '"') {
70
+ mode = 1 /* String */;
71
+ out.push(ch);
72
+ continue;
73
+ }
74
+ if (ch === "/" && next === "/") {
75
+ mode = 2 /* LineComment */;
76
+ i++;
77
+ continue;
78
+ }
79
+ if (ch === "/" && next === "*") {
80
+ mode = 3 /* BlockComment */;
81
81
  i++;
82
82
  continue;
83
83
  }
84
- result += char;
85
- i++;
84
+ out.push(ch);
86
85
  }
87
- return result.replace(/,\s*([}\]])/g, "$1");
86
+ return out.join("").replace(/,\s*([}\]])/g, "$1");
88
87
  }
89
88
 
90
89
  // src/config.ts
@@ -159,6 +158,8 @@ bunx @momomemory/opencode-momo configure
159
158
  Or set environment variables:
160
159
  - \`MOMO_BASE_URL\` - Momo server URL (default: http://localhost:3000)
161
160
  - \`MOMO_API_KEY\` - API key for authentication
161
+ - \`MOMO_CONTAINER_TAG_USER\` - Optional user container tag override
162
+ - \`MOMO_CONTAINER_TAG_PROJECT\` - Optional project container tag override
162
163
 
163
164
  After configuration, restart OpenCode to activate.
164
165
  `;
@@ -206,53 +207,186 @@ async function promptInput(question, defaultValue) {
206
207
  }
207
208
  function registerPluginInConfig(configDir) {
208
209
  const configPath = join2(configDir, "opencode.jsonc");
209
- if (existsSync(configPath)) {
210
- const content = readFileSync(configPath, "utf-8");
211
- if (content.includes(PLUGIN_NAME)) {
212
- return false;
213
- }
214
- if (content.includes('"plugin"')) {
215
- const newContent = content.replace(/("plugin"\s*:\s*\[)([^\]]*?)(\])/, (_match, start, middle, end) => {
216
- const trimmed = middle.trim();
217
- if (trimmed === "") {
218
- return `${start}
210
+ if (!existsSync(configPath)) {
211
+ mkdirSync(configDir, { recursive: true });
212
+ const newConfig = `{
213
+ "plugin": [
219
214
  "${PLUGIN_NAME}"
220
- ${end}`;
221
- }
222
- return `${start}${middle.trimEnd()},
215
+ ]
216
+ }
217
+ `;
218
+ writeFileSync(configPath, newConfig, "utf-8");
219
+ return true;
220
+ }
221
+ const content = readFileSync(configPath, "utf-8");
222
+ if (content.includes(PLUGIN_NAME)) {
223
+ return false;
224
+ }
225
+ const pluginArrayStart = findPluginArrayStart(content);
226
+ if (pluginArrayStart !== -1) {
227
+ const pluginArrayEnd = findMatchingBracket(content, pluginArrayStart);
228
+ if (pluginArrayEnd !== -1) {
229
+ const before = content.slice(0, pluginArrayStart + 1);
230
+ const middle = content.slice(pluginArrayStart + 1, pluginArrayEnd);
231
+ const after = content.slice(pluginArrayEnd);
232
+ const middleTrimmed = middle.trim();
233
+ const appended = middleTrimmed.length === 0 ? `
234
+ "${PLUGIN_NAME}"
235
+ ` : `${middle.replace(/\s*$/, "")},
223
236
  "${PLUGIN_NAME}"
224
- ${end}`;
225
- });
226
- writeFileSync(configPath, newContent, "utf-8");
237
+ `;
238
+ writeFileSync(configPath, `${before}${appended}${after}`, "utf-8");
227
239
  return true;
228
240
  }
229
- const beforeBrace = content.trimEnd().slice(0, -1).trimEnd();
230
- if (beforeBrace.endsWith(",") || beforeBrace.endsWith("{")) {
231
- const insertContent = content.replace(/\}\s*$/, ` "plugin": [
232
- "${PLUGIN_NAME}"
233
- ]
234
- }`);
235
- writeFileSync(configPath, insertContent, "utf-8");
236
- } else {
237
- const withComma = content.replace(/(\S)\s*\}\s*$/, `$1,
241
+ }
242
+ const rootClose = findRootObjectClose(content);
243
+ if (rootClose === -1) {
244
+ const rebuilt = `{
238
245
  "plugin": [
239
246
  "${PLUGIN_NAME}"
240
247
  ]
241
- }`);
242
- writeFileSync(configPath, withComma, "utf-8");
243
- }
248
+ }
249
+ `;
250
+ writeFileSync(configPath, rebuilt, "utf-8");
244
251
  return true;
245
252
  }
246
- mkdirSync(configDir, { recursive: true });
247
- const newConfig = `{
253
+ const head = content.slice(0, rootClose).replace(/\s*$/, "");
254
+ const needsComma = !head.endsWith("{") && !head.endsWith(",");
255
+ const insertion = `${needsComma ? "," : ""}
248
256
  "plugin": [
249
257
  "${PLUGIN_NAME}"
250
258
  ]
251
- }
252
259
  `;
253
- writeFileSync(configPath, newConfig, "utf-8");
260
+ const tail = content.slice(rootClose);
261
+ writeFileSync(configPath, `${head}${insertion}${tail}`, "utf-8");
254
262
  return true;
255
263
  }
264
+ function findPluginArrayStart(content) {
265
+ const keyMatch = /"plugin"\s*:/.exec(content);
266
+ if (!keyMatch)
267
+ return -1;
268
+ const start = keyMatch.index + keyMatch[0].length;
269
+ for (let i = start;i < content.length; i++) {
270
+ const ch = content[i];
271
+ if (!ch || /\s/.test(ch))
272
+ continue;
273
+ return ch === "[" ? i : -1;
274
+ }
275
+ return -1;
276
+ }
277
+ function findMatchingBracket(content, openIndex) {
278
+ let depth = 0;
279
+ let inString = false;
280
+ let inLineComment = false;
281
+ let inBlockComment = false;
282
+ for (let i = openIndex;i < content.length; i++) {
283
+ const ch = content[i];
284
+ const next = content[i + 1];
285
+ if (inLineComment) {
286
+ if (ch === `
287
+ `)
288
+ inLineComment = false;
289
+ continue;
290
+ }
291
+ if (inBlockComment) {
292
+ if (ch === "*" && next === "/") {
293
+ inBlockComment = false;
294
+ i++;
295
+ }
296
+ continue;
297
+ }
298
+ if (inString) {
299
+ if (ch === "\\") {
300
+ i++;
301
+ continue;
302
+ }
303
+ if (ch === '"')
304
+ inString = false;
305
+ continue;
306
+ }
307
+ if (ch === '"') {
308
+ inString = true;
309
+ continue;
310
+ }
311
+ if (ch === "/" && next === "/") {
312
+ inLineComment = true;
313
+ i++;
314
+ continue;
315
+ }
316
+ if (ch === "/" && next === "*") {
317
+ inBlockComment = true;
318
+ i++;
319
+ continue;
320
+ }
321
+ if (ch === "[") {
322
+ depth++;
323
+ continue;
324
+ }
325
+ if (ch === "]") {
326
+ depth--;
327
+ if (depth === 0)
328
+ return i;
329
+ }
330
+ }
331
+ return -1;
332
+ }
333
+ function findRootObjectClose(content) {
334
+ let depth = 0;
335
+ let inString = false;
336
+ let inLineComment = false;
337
+ let inBlockComment = false;
338
+ let rootClose = -1;
339
+ for (let i = 0;i < content.length; i++) {
340
+ const ch = content[i];
341
+ const next = content[i + 1];
342
+ if (inLineComment) {
343
+ if (ch === `
344
+ `)
345
+ inLineComment = false;
346
+ continue;
347
+ }
348
+ if (inBlockComment) {
349
+ if (ch === "*" && next === "/") {
350
+ inBlockComment = false;
351
+ i++;
352
+ }
353
+ continue;
354
+ }
355
+ if (inString) {
356
+ if (ch === "\\") {
357
+ i++;
358
+ continue;
359
+ }
360
+ if (ch === '"')
361
+ inString = false;
362
+ continue;
363
+ }
364
+ if (ch === '"') {
365
+ inString = true;
366
+ continue;
367
+ }
368
+ if (ch === "/" && next === "/") {
369
+ inLineComment = true;
370
+ i++;
371
+ continue;
372
+ }
373
+ if (ch === "/" && next === "*") {
374
+ inBlockComment = true;
375
+ i++;
376
+ continue;
377
+ }
378
+ if (ch === "{") {
379
+ depth++;
380
+ continue;
381
+ }
382
+ if (ch === "}") {
383
+ depth--;
384
+ if (depth === 0)
385
+ rootClose = i;
386
+ }
387
+ }
388
+ return rootClose;
389
+ }
256
390
  function writeCommandFiles(configDir) {
257
391
  const commandDir = join2(configDir, "command");
258
392
  mkdirSync(commandDir, { recursive: true });
@@ -286,7 +420,7 @@ function writeMomoConfig(configDir, options) {
286
420
  containerTagProject: priorContainerTagProject ?? ""
287
421
  };
288
422
  const lines = ["{"];
289
- lines.push(" // Momo server URL");
423
+ lines.push(" // Momo server URL (default: http://localhost:3000)");
290
424
  lines.push(` "baseUrl": ${JSON.stringify(config.baseUrl)},`);
291
425
  lines.push(" // API key for authentication (leave empty if your server has auth disabled)");
292
426
  lines.push(` "apiKey": ${JSON.stringify(config.apiKey)},`);
@@ -294,6 +428,8 @@ function writeMomoConfig(configDir, options) {
294
428
  lines.push(` "containerTagUser": ${JSON.stringify(config.containerTagUser)},`);
295
429
  lines.push(" // Optional override for project memory container tag (default: auto-derived from project directory)");
296
430
  lines.push(` "containerTagProject": ${JSON.stringify(config.containerTagProject)}`);
431
+ lines.push(" // Note: project-local config files (.momo.jsonc / momo.jsonc) override this global file,");
432
+ lines.push(" // and MOMO_* environment variables override both.");
297
433
  lines.push("}");
298
434
  lines.push("");
299
435
  writeFileSync(configPath, lines.join(`
@@ -331,6 +467,14 @@ async function runInstall(flags) {
331
467
  writeCommandFiles(configDir);
332
468
  console.log(` ✓ Created ${join2(configDir, "command", "momo-init.md")}`);
333
469
  console.log(` ✓ Created ${join2(configDir, "command", "momo-configure.md")}`);
470
+ const momoConfigPath = join2(configDir, "momo.jsonc");
471
+ const hadMomoConfig = existsSync(momoConfigPath);
472
+ writeMomoConfig(configDir, {});
473
+ if (hadMomoConfig) {
474
+ console.log(` ✓ Updated ${momoConfigPath}`);
475
+ } else {
476
+ console.log(` ✓ Created ${momoConfigPath}`);
477
+ }
334
478
  console.log(`
335
479
  ✅ Installation complete!
336
480
 
@@ -375,7 +519,7 @@ async function runConfigure(flags) {
375
519
  Restart OpenCode to apply changes.`);
376
520
  }
377
521
  function printHelp() {
378
- console.log(`opencode-momo v0.1.1
522
+ console.log(`opencode-momo v0.1.3
379
523
 
380
524
  OpenCode plugin that gives coding agents persistent memory using Momo.
381
525
 
package/dist/config.d.ts CHANGED
@@ -5,6 +5,6 @@ export interface MomoConfig {
5
5
  containerTagProject?: string;
6
6
  }
7
7
  export declare function getConfigDir(): string;
8
- export declare function loadConfig(): MomoConfig;
9
- export declare function isConfigured(): boolean;
8
+ export declare function loadConfig(directory?: string): MomoConfig;
9
+ export declare function isConfigured(directory?: string): boolean;
10
10
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAWD,wBAAgB,YAAY,IAAI,MAAM,CAMrC;AAaD,wBAAgB,UAAU,IAAI,UAAU,CAYvC;AAED,wBAAgB,YAAY,IAAI,OAAO,CAGtC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAWD,wBAAgB,YAAY,IAAI,MAAM,CAMrC;AAiCD,wBAAgB,UAAU,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,CAmBzD;AAED,wBAAgB,YAAY,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAGxD"}
package/dist/index.js CHANGED
@@ -13416,79 +13416,78 @@ class MomoClient {
13416
13416
  }
13417
13417
  }
13418
13418
  // src/config.ts
13419
- import { readFileSync } from "node:fs";
13419
+ import { readFileSync, existsSync } from "node:fs";
13420
13420
  import { join } from "node:path";
13421
13421
  import { homedir } from "node:os";
13422
13422
 
13423
13423
  // src/services/jsonc.ts
13424
- function stripJsoncComments(content) {
13425
- let result = "";
13426
- let i = 0;
13427
- let inString = false;
13428
- let inSingleLineComment = false;
13429
- let inMultiLineComment = false;
13430
- while (i < content.length) {
13431
- const char = content[i];
13432
- const nextChar = content[i + 1];
13433
- if (!inSingleLineComment && !inMultiLineComment) {
13434
- if (char === '"') {
13435
- let backslashCount = 0;
13436
- let j = i - 1;
13437
- while (j >= 0 && content[j] === "\\") {
13438
- backslashCount++;
13439
- j--;
13440
- }
13441
- if (backslashCount % 2 === 0) {
13442
- inString = !inString;
13443
- }
13444
- result += char;
13445
- i++;
13446
- continue;
13447
- }
13448
- }
13449
- if (inString) {
13450
- result += char;
13451
- i++;
13452
- continue;
13453
- }
13454
- if (!inSingleLineComment && !inMultiLineComment) {
13455
- if (char === "/" && nextChar === "/") {
13456
- inSingleLineComment = true;
13457
- i += 2;
13424
+ function stripJsoncComments(input) {
13425
+ var Mode;
13426
+ ((Mode2) => {
13427
+ Mode2[Mode2["Code"] = 0] = "Code";
13428
+ Mode2[Mode2["String"] = 1] = "String";
13429
+ Mode2[Mode2["LineComment"] = 2] = "LineComment";
13430
+ Mode2[Mode2["BlockComment"] = 3] = "BlockComment";
13431
+ })(Mode ||= {});
13432
+ const out = [];
13433
+ let mode = 0 /* Code */;
13434
+ for (let i = 0;i < input.length; i++) {
13435
+ const ch = input[i];
13436
+ const next = input[i + 1];
13437
+ if (mode === 1 /* String */) {
13438
+ out.push(ch);
13439
+ if (ch === "\\") {
13440
+ if (next !== undefined) {
13441
+ out.push(next);
13442
+ i++;
13443
+ }
13458
13444
  continue;
13459
13445
  }
13460
- if (char === "/" && nextChar === "*") {
13461
- inMultiLineComment = true;
13462
- i += 2;
13463
- continue;
13446
+ if (ch === '"') {
13447
+ mode = 0 /* Code */;
13464
13448
  }
13449
+ continue;
13465
13450
  }
13466
- if (inSingleLineComment) {
13467
- if (char === `
13451
+ if (mode === 2 /* LineComment */) {
13452
+ if (ch === `
13468
13453
  `) {
13469
- inSingleLineComment = false;
13470
- result += char;
13454
+ mode = 0 /* Code */;
13455
+ out.push(`
13456
+ `);
13471
13457
  }
13472
- i++;
13473
13458
  continue;
13474
13459
  }
13475
- if (inMultiLineComment) {
13476
- if (char === "*" && nextChar === "/") {
13477
- inMultiLineComment = false;
13478
- i += 2;
13460
+ if (mode === 3 /* BlockComment */) {
13461
+ if (ch === "*" && next === "/") {
13462
+ mode = 0 /* Code */;
13463
+ i++;
13479
13464
  continue;
13480
13465
  }
13481
- if (char === `
13466
+ if (ch === `
13482
13467
  `) {
13483
- result += char;
13468
+ out.push(`
13469
+ `);
13484
13470
  }
13471
+ continue;
13472
+ }
13473
+ if (ch === '"') {
13474
+ mode = 1 /* String */;
13475
+ out.push(ch);
13476
+ continue;
13477
+ }
13478
+ if (ch === "/" && next === "/") {
13479
+ mode = 2 /* LineComment */;
13480
+ i++;
13481
+ continue;
13482
+ }
13483
+ if (ch === "/" && next === "*") {
13484
+ mode = 3 /* BlockComment */;
13485
13485
  i++;
13486
13486
  continue;
13487
13487
  }
13488
- result += char;
13489
- i++;
13488
+ out.push(ch);
13490
13489
  }
13491
- return result.replace(/,\s*([}\]])/g, "$1");
13490
+ return out.join("").replace(/,\s*([}\]])/g, "$1");
13492
13491
  }
13493
13492
 
13494
13493
  // src/config.ts
@@ -13502,6 +13501,9 @@ function getConfigDir() {
13502
13501
  }
13503
13502
  function readConfigFile() {
13504
13503
  const configPath = join(getConfigDir(), "momo.jsonc");
13504
+ return readJsoncConfigFile(configPath);
13505
+ }
13506
+ function readJsoncConfigFile(configPath) {
13505
13507
  try {
13506
13508
  const raw = readFileSync(configPath, "utf-8");
13507
13509
  const stripped = stripJsoncComments(raw);
@@ -13510,19 +13512,35 @@ function readConfigFile() {
13510
13512
  return {};
13511
13513
  }
13512
13514
  }
13513
- function loadConfig() {
13514
- const fileConfig = readConfigFile();
13515
+ function readProjectConfigFile(directory) {
13516
+ if (!directory)
13517
+ return {};
13518
+ const dotPath = join(directory, ".momo.jsonc");
13519
+ if (existsSync(dotPath)) {
13520
+ return readJsoncConfigFile(dotPath);
13521
+ }
13522
+ const plainPath = join(directory, "momo.jsonc");
13523
+ if (existsSync(plainPath)) {
13524
+ return readJsoncConfigFile(plainPath);
13525
+ }
13526
+ return {};
13527
+ }
13528
+ function loadConfig(directory) {
13529
+ const globalConfig2 = readConfigFile();
13530
+ const projectConfig = readProjectConfigFile(directory);
13515
13531
  const envApiKey = process.env.MOMO_API_KEY;
13516
13532
  const envBaseUrl = process.env.MOMO_BASE_URL;
13533
+ const envContainerTagUser = process.env.MOMO_CONTAINER_TAG_USER;
13534
+ const envContainerTagProject = process.env.MOMO_CONTAINER_TAG_PROJECT;
13517
13535
  return {
13518
- apiKey: envApiKey ?? fileConfig.apiKey,
13519
- baseUrl: envBaseUrl ?? fileConfig.baseUrl ?? DEFAULT_BASE_URL,
13520
- containerTagUser: fileConfig.containerTagUser,
13521
- containerTagProject: fileConfig.containerTagProject
13536
+ apiKey: envApiKey ?? projectConfig.apiKey ?? globalConfig2.apiKey,
13537
+ baseUrl: envBaseUrl ?? projectConfig.baseUrl ?? globalConfig2.baseUrl ?? DEFAULT_BASE_URL,
13538
+ containerTagUser: envContainerTagUser ?? projectConfig.containerTagUser ?? globalConfig2.containerTagUser,
13539
+ containerTagProject: envContainerTagProject ?? projectConfig.containerTagProject ?? globalConfig2.containerTagProject
13522
13540
  };
13523
13541
  }
13524
- function isConfigured() {
13525
- const config2 = loadConfig();
13542
+ function isConfigured(directory) {
13543
+ const config2 = loadConfig(directory);
13526
13544
  return config2.apiKey != null && config2.apiKey.length > 0;
13527
13545
  }
13528
13546
 
@@ -13701,8 +13719,8 @@ var HELP_TEXT = `**Momo Memory Commands**
13701
13719
  - \`momo({ mode: "forget", memoryId: "mem_abc123" })\`
13702
13720
  `;
13703
13721
  var MomoPlugin = async (ctx) => {
13704
- const config2 = loadConfig();
13705
- const configured = isConfigured();
13722
+ const config2 = loadConfig(ctx.directory);
13723
+ const configured = isConfigured(ctx.directory);
13706
13724
  let momo;
13707
13725
  if (configured) {
13708
13726
  momo = new MomoClient({
@@ -13716,7 +13734,7 @@ var MomoPlugin = async (ctx) => {
13716
13734
  });
13717
13735
  function requireMomo() {
13718
13736
  if (!momo) {
13719
- throw new Error("Momo is not configured. Set MOMO_API_KEY environment variable or create ~/.config/opencode/momo.jsonc");
13737
+ throw new Error("Momo is not configured. Set MOMO_API_KEY or configure ~/.config/opencode/momo.jsonc (or project-local .momo.jsonc).");
13720
13738
  }
13721
13739
  return momo;
13722
13740
  }
@@ -1,7 +1,8 @@
1
1
  /**
2
- * Strips comments from JSONC content while respecting string boundaries.
3
- * Handles // and /* comments, URLs in strings, and escaped quotes.
4
- * Also removes trailing commas to support more relaxed JSONC format.
2
+ * Remove JSONC comments and trailing commas.
3
+ *
4
+ * This parser is intentionally lightweight and keeps string literals intact,
5
+ * including escaped quotes and URLs.
5
6
  */
6
- export declare function stripJsoncComments(content: string): string;
7
+ export declare function stripJsoncComments(input: string): string;
7
8
  //# sourceMappingURL=jsonc.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"jsonc.d.ts","sourceRoot":"","sources":["../../src/services/jsonc.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA0E1D"}
1
+ {"version":3,"file":"jsonc.d.ts","sourceRoot":"","sources":["../../src/services/jsonc.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAgFxD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momomemory/opencode-momo",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "OpenCode plugin that gives coding agents persistent memory using Momo",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",