@momomemory/opencode-momo 0.1.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.
- package/README.md +202 -0
- package/dist/cli.d.ts +22 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +430 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13914 -0
- package/dist/services/compaction.d.ts +18 -0
- package/dist/services/compaction.d.ts.map +1 -0
- package/dist/services/context.d.ts +17 -0
- package/dist/services/context.d.ts.map +1 -0
- package/dist/services/jsonc.d.ts +7 -0
- package/dist/services/jsonc.d.ts.map +1 -0
- package/dist/services/privacy.d.ts +8 -0
- package/dist/services/privacy.d.ts.map +1 -0
- package/dist/services/tags.d.ts +22 -0
- package/dist/services/tags.d.ts.map +1 -0
- package/dist/test/chat-message.test.d.ts +2 -0
- package/dist/test/chat-message.test.d.ts.map +1 -0
- package/dist/test/cli.test.d.ts +2 -0
- package/dist/test/cli.test.d.ts.map +1 -0
- package/dist/test/config.test.d.ts +2 -0
- package/dist/test/config.test.d.ts.map +1 -0
- package/dist/test/placeholder.test.d.ts +2 -0
- package/dist/test/placeholder.test.d.ts.map +1 -0
- package/dist/test/tags-privacy.test.d.ts +2 -0
- package/dist/test/tags-privacy.test.d.ts.map +1 -0
- package/dist/test/tool.test.d.ts +2 -0
- package/dist/test/tool.test.d.ts.map +1 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# opencode-momo
|
|
2
|
+
|
|
3
|
+
OpenCode plugin for persistent memory using [Momo](https://github.com/momomemory/momo).
|
|
4
|
+
|
|
5
|
+
Your agent remembers what you tell it -- across sessions, across projects.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
### For Humans
|
|
10
|
+
|
|
11
|
+
1. Install the plugin:
|
|
12
|
+
```bash
|
|
13
|
+
bunx @momomemory/opencode-momo install
|
|
14
|
+
```
|
|
15
|
+
2. Configure your Momo connection:
|
|
16
|
+
```bash
|
|
17
|
+
opencode-momo configure --base-url http://localhost:3000 --api-key YOUR_KEY
|
|
18
|
+
```
|
|
19
|
+
Alternatively, set the following environment variables:
|
|
20
|
+
- `MOMO_BASE_URL` (default: `http://localhost:3000`)
|
|
21
|
+
- `MOMO_API_KEY`
|
|
22
|
+
|
|
23
|
+
**Or let your agent do it** -- paste this into OpenCode:
|
|
24
|
+
```
|
|
25
|
+
Install opencode-momo by following https://raw.githubusercontent.com/momomemory/opencode-momo/main/README.md
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### For LLM Agents
|
|
29
|
+
|
|
30
|
+
<details>
|
|
31
|
+
<summary>Instructions for LLM Agents (click to expand)</summary>
|
|
32
|
+
|
|
33
|
+
If you're an LLM agent helping set up opencode-momo, follow these steps:
|
|
34
|
+
|
|
35
|
+
#### Step 1: Run the installer
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
bunx @momomemory/opencode-momo install --yes
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
This will:
|
|
42
|
+
- Register the plugin in `~/.config/opencode/opencode.jsonc`
|
|
43
|
+
- Create the `/momo-init` and `/momo-configure` commands
|
|
44
|
+
|
|
45
|
+
#### Step 2: Verify the config
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
cat ~/.config/opencode/opencode.jsonc
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Should contain:
|
|
52
|
+
```jsonc
|
|
53
|
+
{
|
|
54
|
+
"plugin": [
|
|
55
|
+
"@momomemory/opencode-momo@latest"
|
|
56
|
+
// ... other plugins
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
If not, add it manually.
|
|
62
|
+
|
|
63
|
+
#### Step 3: Configure connection
|
|
64
|
+
|
|
65
|
+
Ask the user for their Momo server URL (default: `http://localhost:3000`) and optional API key.
|
|
66
|
+
|
|
67
|
+
Then configure via CLI:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
opencode-momo configure --base-url http://localhost:3000 --api-key YOUR_KEY
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Or set environment variables:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
export MOMO_BASE_URL=http://localhost:3000
|
|
77
|
+
export MOMO_API_KEY=your-key
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Or create `~/.config/opencode/momo.jsonc`:
|
|
81
|
+
|
|
82
|
+
```jsonc
|
|
83
|
+
{
|
|
84
|
+
"baseUrl": "http://localhost:3000",
|
|
85
|
+
"apiKey": "your-key"
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### Step 4: Verify setup
|
|
90
|
+
|
|
91
|
+
Tell the user to restart OpenCode. They should see `momo` in the tools list. If not, check:
|
|
92
|
+
1. Is `MOMO_API_KEY` set (or is the server running without auth)?
|
|
93
|
+
2. Is the plugin in `opencode.jsonc`?
|
|
94
|
+
|
|
95
|
+
#### Step 5: Initialize codebase memory (optional)
|
|
96
|
+
|
|
97
|
+
Run `/momo-init` to have the agent explore and memorize the codebase.
|
|
98
|
+
|
|
99
|
+
</details>
|
|
100
|
+
|
|
101
|
+
## Features
|
|
102
|
+
|
|
103
|
+
### Context Injection
|
|
104
|
+
Momo automatically injects relevant context into the first message of every chat session. This includes:
|
|
105
|
+
- **User Profile**: A computed narrative of your preferences and known facts.
|
|
106
|
+
- **User Memories**: Recent personal context across all projects.
|
|
107
|
+
- **Project Memories**: Specific knowledge related to the current codebase.
|
|
108
|
+
|
|
109
|
+
### Tool Modes
|
|
110
|
+
The `momo` tool supports 6 operational modes:
|
|
111
|
+
- **help**: Show usage information.
|
|
112
|
+
- **add**: Store a new memory with a specified scope and type.
|
|
113
|
+
- **search**: Perform a hybrid search across user and project memories.
|
|
114
|
+
- **profile**: View the computed user profile.
|
|
115
|
+
- **list**: List recent memories for a given scope.
|
|
116
|
+
- **forget**: Remove a specific memory by its ID.
|
|
117
|
+
|
|
118
|
+
### Codebase Indexing
|
|
119
|
+
The `/momo-init` command provides a structured workflow for agents to explore a new codebase and store its architecture, conventions, and key facts into project memory.
|
|
120
|
+
|
|
121
|
+
### Event Compaction
|
|
122
|
+
Momo listens for `session.compacted` events. When a session is compacted, the plugin automatically ingests the conversation history into Momo to ensure long-term retention of session insights. A 30-second cooldown is enforced per session to prevent redundant ingestion.
|
|
123
|
+
|
|
124
|
+
### Privacy
|
|
125
|
+
Momo supports the `<private>` tag. Any content wrapped in `<private>` tags is automatically stripped before being stored in memory. If a message consists entirely of private content, it will not be stored.
|
|
126
|
+
|
|
127
|
+
## Tool Usage
|
|
128
|
+
|
|
129
|
+
| Mode | Required Args | Optional Args | Description |
|
|
130
|
+
|------|--------------|---------------|-------------|
|
|
131
|
+
| help | — | — | Show help text |
|
|
132
|
+
| add | content | scope, memoryType | Store a memory |
|
|
133
|
+
| search | query | scope, limit | Search memories |
|
|
134
|
+
| profile | — | scope | View computed user profile |
|
|
135
|
+
| list | — | scope, limit | List recent memories |
|
|
136
|
+
| forget | memoryId | — | Forget a memory by ID |
|
|
137
|
+
|
|
138
|
+
**Scopes**: `user` (cross-project), `project` (current project, default)
|
|
139
|
+
**Memory Types**: `fact`, `preference`, `episode`
|
|
140
|
+
|
|
141
|
+
## Memory Scoping
|
|
142
|
+
|
|
143
|
+
Memories are isolated using container tags derived from your environment:
|
|
144
|
+
|
|
145
|
+
| Scope | Description | Tag Derivation |
|
|
146
|
+
|-------|-------------|-----------------------|
|
|
147
|
+
| **user** | Personal facts and preferences shared across all projects | `opencode-user-{hash(username)}` |
|
|
148
|
+
| **project** | Codebase-specific knowledge | `ocp-{slug(projectName)}-{hash(directory)}` |
|
|
149
|
+
|
|
150
|
+
## Configuration
|
|
151
|
+
|
|
152
|
+
Configuration is stored in `~/.config/opencode/momo.jsonc`:
|
|
153
|
+
|
|
154
|
+
```jsonc
|
|
155
|
+
{
|
|
156
|
+
// Momo server URL
|
|
157
|
+
"baseUrl": "http://localhost:3000",
|
|
158
|
+
// API key for authentication
|
|
159
|
+
"apiKey": "your-key-here"
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Environment Variables
|
|
164
|
+
|
|
165
|
+
The following variables can be used to override configuration:
|
|
166
|
+
- `MOMO_BASE_URL`: Overrides `baseUrl` in config.
|
|
167
|
+
- `MOMO_API_KEY`: Overrides `apiKey` in config.
|
|
168
|
+
- `MOMO_CONTAINER_TAG_USER`: Override the default user container tag.
|
|
169
|
+
- `MOMO_CONTAINER_TAG_PROJECT`: Override the default project container tag.
|
|
170
|
+
|
|
171
|
+
## Development
|
|
172
|
+
|
|
173
|
+
1. Install dependencies:
|
|
174
|
+
```bash
|
|
175
|
+
bun install
|
|
176
|
+
```
|
|
177
|
+
2. Build the plugin:
|
|
178
|
+
```bash
|
|
179
|
+
bun run build
|
|
180
|
+
```
|
|
181
|
+
3. Run typecheck:
|
|
182
|
+
```bash
|
|
183
|
+
bun run typecheck
|
|
184
|
+
```
|
|
185
|
+
4. Run tests:
|
|
186
|
+
```bash
|
|
187
|
+
bun test
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Local Installation
|
|
191
|
+
|
|
192
|
+
To test the plugin locally in OpenCode, add a file URL to your `opencode.jsonc`:
|
|
193
|
+
|
|
194
|
+
```jsonc
|
|
195
|
+
{
|
|
196
|
+
"plugin": ["file:///path/to/opencode-momo"]
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Register the plugin in OpenCode's opencode.jsonc config.
|
|
4
|
+
* Returns true if a change was made, false if already registered.
|
|
5
|
+
*/
|
|
6
|
+
export declare function registerPluginInConfig(configDir: string): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Write the markdown command files into the command directory.
|
|
9
|
+
* Returns an object indicating which files were written.
|
|
10
|
+
*/
|
|
11
|
+
export declare function writeCommandFiles(configDir: string): {
|
|
12
|
+
initWritten: boolean;
|
|
13
|
+
configureWritten: boolean;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Write or update the momo.jsonc config file.
|
|
17
|
+
*/
|
|
18
|
+
export declare function writeMomoConfig(configDir: string, options: {
|
|
19
|
+
baseUrl?: string;
|
|
20
|
+
apiKey?: string;
|
|
21
|
+
}): void;
|
|
22
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
4
|
+
|
|
5
|
+
// src/cli.ts
|
|
6
|
+
import {
|
|
7
|
+
readFileSync,
|
|
8
|
+
writeFileSync,
|
|
9
|
+
mkdirSync,
|
|
10
|
+
existsSync
|
|
11
|
+
} from "node:fs";
|
|
12
|
+
import { join as join2 } from "node:path";
|
|
13
|
+
import { createInterface } from "node:readline";
|
|
14
|
+
|
|
15
|
+
// src/config.ts
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
import { homedir } from "node:os";
|
|
18
|
+
|
|
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;
|
|
39
|
+
}
|
|
40
|
+
result += char;
|
|
41
|
+
i++;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
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;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (inSingleLineComment) {
|
|
63
|
+
if (char === `
|
|
64
|
+
`) {
|
|
65
|
+
inSingleLineComment = false;
|
|
66
|
+
result += char;
|
|
67
|
+
}
|
|
68
|
+
i++;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (inMultiLineComment) {
|
|
72
|
+
if (char === "*" && nextChar === "/") {
|
|
73
|
+
inMultiLineComment = false;
|
|
74
|
+
i += 2;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (char === `
|
|
78
|
+
`) {
|
|
79
|
+
result += char;
|
|
80
|
+
}
|
|
81
|
+
i++;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
result += char;
|
|
85
|
+
i++;
|
|
86
|
+
}
|
|
87
|
+
return result.replace(/,\s*([}\]])/g, "$1");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/config.ts
|
|
91
|
+
function getConfigDir() {
|
|
92
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
93
|
+
if (xdg) {
|
|
94
|
+
return join(xdg, "opencode");
|
|
95
|
+
}
|
|
96
|
+
return join(homedir(), ".config", "opencode");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/cli.ts
|
|
100
|
+
var PLUGIN_NAME = "@momomemory/opencode-momo@latest";
|
|
101
|
+
var MOMO_INIT_MD = `---
|
|
102
|
+
description: Initialize Momo with comprehensive codebase knowledge
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
# Initializing Momo Memory
|
|
106
|
+
|
|
107
|
+
You are initializing persistent memory for this codebase. Your goal is to explore the project thoroughly and store key information so that future sessions have full context without re-exploration.
|
|
108
|
+
|
|
109
|
+
## Steps
|
|
110
|
+
|
|
111
|
+
1. **Explore the project structure** — Understand the directory layout, key files, and architecture.
|
|
112
|
+
2. **Identify conventions** — Note coding style, naming patterns, testing approach, and tooling.
|
|
113
|
+
3. **Record architectural decisions** — Store how the codebase is organized and why.
|
|
114
|
+
4. **Save key facts** — Store important facts about the project using Momo.
|
|
115
|
+
|
|
116
|
+
## How to Store Memories
|
|
117
|
+
|
|
118
|
+
Use the \`momo\` tool to save what you learn:
|
|
119
|
+
|
|
120
|
+
\`\`\`
|
|
121
|
+
momo({ mode: "add", content: "This project uses a monorepo with Cargo workspaces", scope: "project", memoryType: "fact" })
|
|
122
|
+
momo({ mode: "add", content: "User prefers concise code comments", scope: "user", memoryType: "preference" })
|
|
123
|
+
\`\`\`
|
|
124
|
+
|
|
125
|
+
### Scopes
|
|
126
|
+
- **\`user\`** — Personal preferences and facts that apply across all projects
|
|
127
|
+
- **\`project\`** — Information specific to this codebase
|
|
128
|
+
|
|
129
|
+
### Memory Types
|
|
130
|
+
- **\`fact\`** — Objective information (architecture, tech stack, file locations)
|
|
131
|
+
- **\`preference\`** — Subjective preferences (coding style, tool choices)
|
|
132
|
+
- **\`episode\`** — Events or interactions worth remembering
|
|
133
|
+
|
|
134
|
+
## What to Capture
|
|
135
|
+
|
|
136
|
+
- Programming languages and frameworks used
|
|
137
|
+
- Build system and development commands
|
|
138
|
+
- Project structure and key directories
|
|
139
|
+
- Testing patterns and test locations
|
|
140
|
+
- Deployment and CI/CD configuration
|
|
141
|
+
- Important conventions and patterns
|
|
142
|
+
- Key dependencies and their purposes
|
|
143
|
+
- Any README or documentation highlights
|
|
144
|
+
|
|
145
|
+
Start by listing the top-level files and directories, then progressively explore the most important areas of the codebase. Store each significant finding as a memory.
|
|
146
|
+
`;
|
|
147
|
+
var MOMO_CONFIGURE_MD = `---
|
|
148
|
+
description: Configure Momo memory connection
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
# Configure Momo
|
|
152
|
+
|
|
153
|
+
Run this command to configure the Momo connection:
|
|
154
|
+
|
|
155
|
+
\`\`\`bash
|
|
156
|
+
bunx @momomemory/opencode-momo configure
|
|
157
|
+
\`\`\`
|
|
158
|
+
|
|
159
|
+
Or set environment variables:
|
|
160
|
+
- \`MOMO_BASE_URL\` - Momo server URL (default: http://localhost:3000)
|
|
161
|
+
- \`MOMO_API_KEY\` - API key for authentication
|
|
162
|
+
|
|
163
|
+
After configuration, restart OpenCode to activate.
|
|
164
|
+
`;
|
|
165
|
+
function parseArgs(argv) {
|
|
166
|
+
const flags = {};
|
|
167
|
+
let command;
|
|
168
|
+
for (let i = 0;i < argv.length; i++) {
|
|
169
|
+
const arg = argv[i];
|
|
170
|
+
if (arg === "--help" || arg === "-h") {
|
|
171
|
+
flags["help"] = true;
|
|
172
|
+
} else if (arg === "--yes" || arg === "-y") {
|
|
173
|
+
flags["yes"] = true;
|
|
174
|
+
} else if (arg === "--no-prompt") {
|
|
175
|
+
flags["no-prompt"] = true;
|
|
176
|
+
} else if (arg === "--base-url" && i + 1 < argv.length) {
|
|
177
|
+
i++;
|
|
178
|
+
flags["base-url"] = argv[i];
|
|
179
|
+
} else if (arg === "--api-key" && i + 1 < argv.length) {
|
|
180
|
+
i++;
|
|
181
|
+
flags["api-key"] = argv[i];
|
|
182
|
+
} else if (!arg.startsWith("-") && !command) {
|
|
183
|
+
command = arg;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return { command, flags };
|
|
187
|
+
}
|
|
188
|
+
async function confirm(question) {
|
|
189
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
190
|
+
return new Promise((resolve) => {
|
|
191
|
+
rl.question(`${question} (y/N) `, (answer) => {
|
|
192
|
+
rl.close();
|
|
193
|
+
resolve(answer.trim().toLowerCase() === "y");
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
async function promptInput(question, defaultValue) {
|
|
198
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
199
|
+
const suffix = defaultValue ? ` (${defaultValue})` : "";
|
|
200
|
+
return new Promise((resolve) => {
|
|
201
|
+
rl.question(`${question}${suffix}: `, (answer) => {
|
|
202
|
+
rl.close();
|
|
203
|
+
resolve(answer.trim() || defaultValue || "");
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
function registerPluginInConfig(configDir) {
|
|
208
|
+
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}
|
|
219
|
+
"${PLUGIN_NAME}"
|
|
220
|
+
${end}`;
|
|
221
|
+
}
|
|
222
|
+
return `${start}${middle.trimEnd()},
|
|
223
|
+
"${PLUGIN_NAME}"
|
|
224
|
+
${end}`;
|
|
225
|
+
});
|
|
226
|
+
writeFileSync(configPath, newContent, "utf-8");
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
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,
|
|
238
|
+
"plugin": [
|
|
239
|
+
"${PLUGIN_NAME}"
|
|
240
|
+
]
|
|
241
|
+
}`);
|
|
242
|
+
writeFileSync(configPath, withComma, "utf-8");
|
|
243
|
+
}
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
mkdirSync(configDir, { recursive: true });
|
|
247
|
+
const newConfig = `{
|
|
248
|
+
"plugin": [
|
|
249
|
+
"${PLUGIN_NAME}"
|
|
250
|
+
]
|
|
251
|
+
}
|
|
252
|
+
`;
|
|
253
|
+
writeFileSync(configPath, newConfig, "utf-8");
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
function writeCommandFiles(configDir) {
|
|
257
|
+
const commandDir = join2(configDir, "command");
|
|
258
|
+
mkdirSync(commandDir, { recursive: true });
|
|
259
|
+
const initPath = join2(commandDir, "momo-init.md");
|
|
260
|
+
const configurePath = join2(commandDir, "momo-configure.md");
|
|
261
|
+
writeFileSync(initPath, MOMO_INIT_MD, "utf-8");
|
|
262
|
+
writeFileSync(configurePath, MOMO_CONFIGURE_MD, "utf-8");
|
|
263
|
+
return { initWritten: true, configureWritten: true };
|
|
264
|
+
}
|
|
265
|
+
function writeMomoConfig(configDir, options) {
|
|
266
|
+
const configPath = join2(configDir, "momo.jsonc");
|
|
267
|
+
mkdirSync(configDir, { recursive: true });
|
|
268
|
+
let existing = {};
|
|
269
|
+
if (existsSync(configPath)) {
|
|
270
|
+
try {
|
|
271
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
272
|
+
const stripped = stripJsoncComments(raw);
|
|
273
|
+
existing = JSON.parse(stripped);
|
|
274
|
+
} catch {
|
|
275
|
+
existing = {};
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
const priorBaseUrl = typeof existing.baseUrl === "string" ? existing.baseUrl : undefined;
|
|
279
|
+
const priorApiKey = typeof existing.apiKey === "string" ? existing.apiKey : undefined;
|
|
280
|
+
const priorContainerTagUser = typeof existing.containerTagUser === "string" ? existing.containerTagUser : undefined;
|
|
281
|
+
const priorContainerTagProject = typeof existing.containerTagProject === "string" ? existing.containerTagProject : undefined;
|
|
282
|
+
const config = {
|
|
283
|
+
baseUrl: options.baseUrl ?? priorBaseUrl ?? "http://localhost:3000",
|
|
284
|
+
apiKey: options.apiKey ?? priorApiKey ?? "",
|
|
285
|
+
containerTagUser: priorContainerTagUser ?? "",
|
|
286
|
+
containerTagProject: priorContainerTagProject ?? ""
|
|
287
|
+
};
|
|
288
|
+
const lines = ["{"];
|
|
289
|
+
lines.push(" // Momo server URL");
|
|
290
|
+
lines.push(` "baseUrl": ${JSON.stringify(config.baseUrl)},`);
|
|
291
|
+
lines.push(" // API key for authentication (leave empty if your server has auth disabled)");
|
|
292
|
+
lines.push(` "apiKey": ${JSON.stringify(config.apiKey)},`);
|
|
293
|
+
lines.push(" // Optional override for user memory container tag (default: auto-derived from username)");
|
|
294
|
+
lines.push(` "containerTagUser": ${JSON.stringify(config.containerTagUser)},`);
|
|
295
|
+
lines.push(" // Optional override for project memory container tag (default: auto-derived from project directory)");
|
|
296
|
+
lines.push(` "containerTagProject": ${JSON.stringify(config.containerTagProject)}`);
|
|
297
|
+
lines.push("}");
|
|
298
|
+
lines.push("");
|
|
299
|
+
writeFileSync(configPath, lines.join(`
|
|
300
|
+
`), "utf-8");
|
|
301
|
+
}
|
|
302
|
+
async function runInstall(flags) {
|
|
303
|
+
const autoYes = flags["yes"] === true || flags["no-prompt"] === true;
|
|
304
|
+
const configDir = getConfigDir();
|
|
305
|
+
console.log(`
|
|
306
|
+
\uD83D\uDD27 Step 1: Register plugin in OpenCode config
|
|
307
|
+
`);
|
|
308
|
+
if (!autoYes) {
|
|
309
|
+
const ok = await confirm("Register @momomemory/opencode-momo in OpenCode config?");
|
|
310
|
+
if (!ok) {
|
|
311
|
+
console.log(" Skipped.");
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
const changed = registerPluginInConfig(configDir);
|
|
316
|
+
if (changed) {
|
|
317
|
+
console.log(` ✓ Plugin registered in ${join2(configDir, "opencode.jsonc")}`);
|
|
318
|
+
} else {
|
|
319
|
+
console.log(" ✓ Plugin already registered in config");
|
|
320
|
+
}
|
|
321
|
+
console.log(`
|
|
322
|
+
\uD83D\uDCDD Step 2: Create command files
|
|
323
|
+
`);
|
|
324
|
+
if (!autoYes) {
|
|
325
|
+
const ok = await confirm("Create momo-init.md and momo-configure.md command files?");
|
|
326
|
+
if (!ok) {
|
|
327
|
+
console.log(" Skipped.");
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
writeCommandFiles(configDir);
|
|
332
|
+
console.log(` ✓ Created ${join2(configDir, "command", "momo-init.md")}`);
|
|
333
|
+
console.log(` ✓ Created ${join2(configDir, "command", "momo-configure.md")}`);
|
|
334
|
+
console.log(`
|
|
335
|
+
✅ Installation complete!
|
|
336
|
+
|
|
337
|
+
Next steps:
|
|
338
|
+
1. Set your Momo API key:
|
|
339
|
+
$ opencode-momo configure --base-url http://localhost:3000 --api-key YOUR_KEY
|
|
340
|
+
|
|
341
|
+
Or set environment variables:
|
|
342
|
+
$ export MOMO_API_KEY=your-key
|
|
343
|
+
$ export MOMO_BASE_URL=http://localhost:3000
|
|
344
|
+
|
|
345
|
+
2. Restart OpenCode to activate the plugin.
|
|
346
|
+
|
|
347
|
+
3. Use /momo-init to initialize memory for your codebase.
|
|
348
|
+
`);
|
|
349
|
+
}
|
|
350
|
+
async function runConfigure(flags) {
|
|
351
|
+
const configDir = getConfigDir();
|
|
352
|
+
let baseUrl = typeof flags["base-url"] === "string" ? flags["base-url"] : undefined;
|
|
353
|
+
let apiKey = typeof flags["api-key"] === "string" ? flags["api-key"] : undefined;
|
|
354
|
+
if (baseUrl === undefined && apiKey === undefined) {
|
|
355
|
+
const isTTY = process.stdin.isTTY;
|
|
356
|
+
if (isTTY) {
|
|
357
|
+
baseUrl = await promptInput("Momo server URL", "http://localhost:3000");
|
|
358
|
+
apiKey = await promptInput("API key");
|
|
359
|
+
} else {
|
|
360
|
+
console.error(`No --base-url or --api-key provided, and stdin is not a TTY.
|
|
361
|
+
` + "Usage: opencode-momo configure --base-url URL --api-key KEY");
|
|
362
|
+
process.exit(1);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
writeMomoConfig(configDir, { baseUrl, apiKey });
|
|
366
|
+
console.log(`
|
|
367
|
+
✓ Configuration written to ${join2(configDir, "momo.jsonc")}`);
|
|
368
|
+
if (apiKey) {
|
|
369
|
+
console.log(" ✓ API key set");
|
|
370
|
+
}
|
|
371
|
+
if (baseUrl) {
|
|
372
|
+
console.log(` ✓ Base URL: ${baseUrl}`);
|
|
373
|
+
}
|
|
374
|
+
console.log(`
|
|
375
|
+
Restart OpenCode to apply changes.`);
|
|
376
|
+
}
|
|
377
|
+
function printHelp() {
|
|
378
|
+
console.log(`opencode-momo v0.1.1
|
|
379
|
+
|
|
380
|
+
OpenCode plugin that gives coding agents persistent memory using Momo.
|
|
381
|
+
|
|
382
|
+
USAGE:
|
|
383
|
+
opencode-momo <command> [options]
|
|
384
|
+
|
|
385
|
+
COMMANDS:
|
|
386
|
+
install Install and configure the plugin for OpenCode
|
|
387
|
+
configure Update Momo connection settings
|
|
388
|
+
help Show this help message
|
|
389
|
+
|
|
390
|
+
INSTALL OPTIONS:
|
|
391
|
+
--yes, -y Skip confirmation prompts
|
|
392
|
+
--no-prompt Same as --yes
|
|
393
|
+
|
|
394
|
+
CONFIGURE OPTIONS:
|
|
395
|
+
--base-url Momo server URL (default: http://localhost:3000)
|
|
396
|
+
--api-key API key for authentication
|
|
397
|
+
|
|
398
|
+
GENERAL OPTIONS:
|
|
399
|
+
-h, --help Show this help message
|
|
400
|
+
`);
|
|
401
|
+
}
|
|
402
|
+
async function main() {
|
|
403
|
+
const { command, flags } = parseArgs(process.argv.slice(2));
|
|
404
|
+
if (!command || command === "help" || flags["help"] === true) {
|
|
405
|
+
printHelp();
|
|
406
|
+
process.exit(0);
|
|
407
|
+
}
|
|
408
|
+
if (command === "install") {
|
|
409
|
+
await runInstall(flags);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
if (command === "configure") {
|
|
413
|
+
await runConfigure(flags);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
console.error(`Unknown command: ${command}`);
|
|
417
|
+
console.error('Run "opencode-momo --help" for usage.');
|
|
418
|
+
process.exit(1);
|
|
419
|
+
}
|
|
420
|
+
if (__require.main == __require.module) {
|
|
421
|
+
main().catch((err) => {
|
|
422
|
+
console.error("Error:", err instanceof Error ? err.message : err);
|
|
423
|
+
process.exit(1);
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
export {
|
|
427
|
+
writeMomoConfig,
|
|
428
|
+
writeCommandFiles,
|
|
429
|
+
registerPluginInConfig
|
|
430
|
+
};
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface MomoConfig {
|
|
2
|
+
apiKey?: string;
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
containerTagUser?: string;
|
|
5
|
+
containerTagProject?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function getConfigDir(): string;
|
|
8
|
+
export declare function loadConfig(): MomoConfig;
|
|
9
|
+
export declare function isConfigured(): boolean;
|
|
10
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAoC/D,eAAO,MAAM,UAAU,EAAE,MAmSxB,CAAC"}
|