oc-solomemory-dev 1.0.6-dev.70f1ae5
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.
Potentially problematic release.
This version of oc-solomemory-dev might be problematic. Click here for more details.
- package/LICENSE +661 -0
- package/README.md +71 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/install.d.ts +2 -0
- package/dist/cli/install.d.ts.map +1 -0
- package/dist/cli/templates.d.ts +3 -0
- package/dist/cli/templates.d.ts.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +553 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13864 -0
- package/dist/services/auth.d.ts +10 -0
- package/dist/services/auth.d.ts.map +1 -0
- package/dist/services/client-types.d.ts +123 -0
- package/dist/services/client-types.d.ts.map +1 -0
- package/dist/services/client.d.ts +29 -0
- package/dist/services/client.d.ts.map +1 -0
- package/dist/services/context.d.ts +23 -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/logger.d.ts +2 -0
- package/dist/services/logger.d.ts.map +1 -0
- package/dist/services/messages.d.ts +33 -0
- package/dist/services/messages.d.ts.map +1 -0
- package/dist/services/privacy.d.ts +4 -0
- package/dist/services/privacy.d.ts.map +1 -0
- package/dist/services/tags.d.ts +51 -0
- package/dist/services/tags.d.ts.map +1 -0
- package/dist/services/workspace.d.ts +7 -0
- package/dist/services/workspace.d.ts.map +1 -0
- package/dist/state.d.ts +2 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/sync.d.ts +16 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/tools.d.ts +10 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/types/index.d.ts +52 -0
- package/dist/types/index.d.ts.map +1 -0
- package/package.json +76 -0
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<picture>
|
|
2
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/solomemory-ai/.github/main/brand/logo-mark-white-transparent.png">
|
|
3
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/solomemory-ai/.github/main/brand/logo-mark-black-transparent.png">
|
|
4
|
+
<img src="https://raw.githubusercontent.com/solomemory-ai/.github/main/brand/logo-mark-black-transparent.png" alt="Solo Memory" width="120">
|
|
5
|
+
</picture>
|
|
6
|
+
|
|
7
|
+
# Solo Memory for OpenCode
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/oc-solomemory)
|
|
10
|
+
[](https://www.npmjs.com/package/oc-solomemory)
|
|
11
|
+
[](LICENSE)
|
|
12
|
+
[](https://www.typescriptlang.org/)
|
|
13
|
+
|
|
14
|
+
Persistent memory plugin for [OpenCode](https://opencode.ai). Your AI agent remembers across sessions and projects.
|
|
15
|
+
|
|
16
|
+
[Solo Memory](https://solomemory.com) gives your coding agent long-term memory — preferences you've shared, decisions you've made, context from past sessions. It all carries forward automatically.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
Get your API key at [solomemory.com](https://solomemory.com), then:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx oc-solomemory@latest install --api-key=your-key
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Restart OpenCode. Done.
|
|
29
|
+
|
|
30
|
+
## What It Does
|
|
31
|
+
|
|
32
|
+
- **Remembers context** — Your preferences, project decisions, and past conversations are injected into every new session
|
|
33
|
+
- **Syncs conversations** — Sessions are synced to Solo Memory, building knowledge over time
|
|
34
|
+
- **Scopes memory** — Memories are organized by user, project, repo, and branch
|
|
35
|
+
- **Keyword detection** — Say "remember this" and it saves to memory
|
|
36
|
+
- **Codebase indexing** — Run `/solomemory-init` to memorize your project structure
|
|
37
|
+
|
|
38
|
+
## How It Works
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
You start a session
|
|
42
|
+
→ Plugin fetches your profile + relevant memories
|
|
43
|
+
→ Agent has full context from day one
|
|
44
|
+
|
|
45
|
+
You work with the agent
|
|
46
|
+
→ Session syncs to Solo Memory when idle
|
|
47
|
+
|
|
48
|
+
Next session
|
|
49
|
+
→ Agent already knows what you discussed
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Memory Scopes
|
|
53
|
+
|
|
54
|
+
| Scope | What it remembers | Example |
|
|
55
|
+
| ----------- | ------------------------------------ | ----------------------------------- |
|
|
56
|
+
| **User** | Your preferences across all projects | "Prefers TypeScript strict mode" |
|
|
57
|
+
| **Project** | Project-specific context | "Uses Bun, not Node" |
|
|
58
|
+
| **Repo** | Repository knowledge | "Auth uses JWT with refresh tokens" |
|
|
59
|
+
| **Branch** | Branch-specific work | "Working on dark mode feature" |
|
|
60
|
+
|
|
61
|
+
## Configuration
|
|
62
|
+
|
|
63
|
+
See [docs/CONFIG.md](docs/CONFIG.md) for advanced options.
|
|
64
|
+
|
|
65
|
+
## Contributing
|
|
66
|
+
|
|
67
|
+
See [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md).
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
[AGPL-3.0](LICENSE)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAoCA,wBAAgB,IAAI,IAAI,IAAI,CAe3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/cli/install.ts"],"names":[],"mappings":"AAqMA,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAK1D"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const SOLOMEMORY_INIT_COMMAND = "---\ndescription: Initialize Solo Memory with comprehensive codebase knowledge\n---\n\n# Initializing Solo Memory\n\nYou are initializing persistent memory for this codebase. This is not just data collection - you're building context that will make you significantly more effective across all future sessions.\n\n## Understanding Context\n\nYou are a **stateful** coding agent. Users expect to work with you over extended periods - potentially the entire lifecycle of a project. Your memory is how you get better over time and maintain continuity.\n\n## What to Remember\n\n### 1. Procedures (Rules & Workflows)\nExplicit rules that should always be followed:\n- \"Never commit directly to main - always use feature branches\"\n- \"Always run lint before tests\"\n- \"Use conventional commits format\"\n\n### 2. Preferences (Style & Conventions) \nProject and user coding style:\n- \"Prefer functional components over class components\"\n- \"Use early returns instead of nested conditionals\"\n- \"Always add JSDoc to exported functions\"\n\n### 3. Architecture & Context\nHow the codebase works and why:\n- \"Auth system was refactored in v2.0 - old patterns deprecated\"\n- \"The monorepo used to have 3 modules before consolidation\"\n- \"This pagination bug was fixed before - similar to PR #234\"\n\n## Memory Scopes\n\n**Project-scoped** (`scope: \"project\"`):\n- Build/test/lint commands\n- Architecture and key directories\n- Team conventions specific to this codebase\n- Technology stack and framework choices\n- Known issues and their solutions\n\n**User-scoped** (`scope: \"user\"`):\n- Personal coding preferences across all projects\n- Communication style preferences\n- General workflow habits\n\n## Research Approach\n\nThis is a **deep research** initialization. Take your time and be thorough (~50+ tool calls). The goal is to genuinely understand the project, not just collect surface-level facts.\n\n**What to uncover:**\n- Tech stack and dependencies (explicit and implicit)\n- Project structure and architecture\n- Build/test/deploy commands and workflows\n- Contributors & team dynamics (who works on what?)\n- Commit conventions and branching strategy\n- Code evolution (major refactors, architecture changes)\n- Pain points (areas with lots of bug fixes)\n- Implicit conventions not documented anywhere\n\n## Research Techniques\n\n### File-based\n- README.md, CONTRIBUTING.md, AGENTS.md, CLAUDE.md\n- Package manifests (package.json, Cargo.toml, pyproject.toml, go.mod)\n- Config files (.eslintrc, tsconfig.json, .prettierrc)\n- CI/CD configs (.github/workflows/)\n\n### Git-based\n- `git log --oneline -20` - Recent history\n- `git branch -a` - Branching strategy \n- `git log --format=\"%s\" -50` - Commit conventions\n- `git shortlog -sn --all | head -10` - Main contributors\n\n### Explore Agent\nFire parallel explore queries for broad understanding:\n```\nTask(explore, \"What is the tech stack and key dependencies?\")\nTask(explore, \"What is the project structure? Key directories?\")\nTask(explore, \"How do you build, test, and run this project?\")\nTask(explore, \"What are the main architectural patterns?\")\nTask(explore, \"What conventions or patterns are used?\")\n```\n\n## How to Do Thorough Research\n\n**Don't just collect data - analyze and cross-reference.**\n\nBad (shallow):\n- Run commands, copy output\n- List facts without understanding\n\nGood (thorough):\n- Cross-reference findings (if inconsistent, dig deeper)\n- Resolve ambiguities (don't leave questions unanswered)\n- Read actual file content, not just names\n- Look for patterns (what do commits tell you about workflow?)\n- Think like a new team member - what would you want to know?\n\n## Saving Memories\n\nUse the `solomemory` tool for each distinct insight:\n\n```\nsolomemory(mode: \"add\", content: \"...\", type: \"...\", scope: \"project\")\n```\n\n**Types:**\n- `project-config` - tech stack, commands, tooling\n- `architecture` - codebase structure, key components, data flow\n- `learned-pattern` - conventions specific to this codebase\n- `error-solution` - known issues and their fixes\n- `preference` - coding style preferences (use with user scope)\n\n**Guidelines:**\n- Save each distinct insight as a separate memory\n- Be concise but include enough context to be useful\n- Include the \"why\" not just the \"what\" when relevant\n- Update memories incrementally as you research (don't wait until the end)\n\n**Good memories:**\n- \"Uses Bun runtime and package manager. Commands: bun install, bun run dev, bun test\"\n- \"API routes in src/routes/, handlers in src/handlers/. Hono framework.\"\n- \"Auth uses Redis sessions, not JWT. Implementation in src/lib/auth.ts\"\n- \"Never use `any` type - strict TypeScript. Use `unknown` and narrow.\"\n- \"Database migrations must be backward compatible - we do rolling deploys\"\n\n## Upfront Questions\n\nBefore diving in, ask:\n1. \"Any specific rules I should always follow?\"\n2. \"Preferences for how I communicate? (terse/detailed)\"\n\n## Reflection Phase\n\nBefore finishing, reflect:\n1. **Completeness**: Did you cover commands, architecture, conventions, gotchas?\n2. **Quality**: Are memories concise and searchable?\n3. **Scope**: Did you correctly separate project vs user knowledge?\n\nThen ask: \"I've initialized memory with X insights. Want me to continue refining, or is this good?\"\n\n## Your Task\n\n1. Ask upfront questions (research depth, rules, preferences)\n2. Check existing memories: `solomemory(mode: \"list\", scope: \"project\")`\n3. Research based on chosen depth\n4. Save memories incrementally as you discover insights\n5. Reflect and verify completeness\n6. Summarize what was learned and ask if user wants refinement\n";
|
|
2
|
+
export declare const SOLOMEMORY_LOGIN_COMMAND = "---\ndescription: Configure Solo Memory API key\n---\n\n# Solo Memory Setup\n\nTo use Solo Memory, you need to set your API key.\n\n## Option 1: Environment Variable (Recommended)\n\n```bash\nexport SOLOMEMORY_API_KEY=\"your-api-key\"\n```\n\nAdd this to your shell profile (~/.bashrc, ~/.zshrc, etc.) for persistence.\n\n## Option 2: Config File\n\nCreate `~/.config/opencode/solomemory.jsonc`:\n\n```jsonc\n{\n \"apiKey\": \"your-api-key\",\n // Optional: Custom API URL (default: https://api.solomemory.com)\n \"apiUrl\": \"https://api.solomemory.com\"\n}\n```\n\n## Option 3: Credentials File\n\nSave credentials using the CLI:\n\n```bash\nnpx oc-solomemory@latest login\n```\n\nThis will prompt for your API key and save it securely.\n\n## Verify Setup\n\nRestart OpenCode and run:\n\n```bash\nopencode -c\n```\n\nYou should see `solomemory` in the tools list.\n";
|
|
3
|
+
//# sourceMappingURL=templates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/cli/templates.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,uBAAuB,0mLAsJnC,CAAC;AAEF,eAAO,MAAM,wBAAwB,22BA+CpC,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli/install.ts
|
|
4
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
5
|
+
import { homedir as homedir2 } from "node:os";
|
|
6
|
+
import path2 from "node:path";
|
|
7
|
+
|
|
8
|
+
// src/services/auth.ts
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
var CREDENTIALS_DIR = path.join(homedir(), ".solomemory-opencode");
|
|
13
|
+
var CREDENTIALS_FILE = path.join(CREDENTIALS_DIR, "credentials.json");
|
|
14
|
+
function saveCredentials(apiKey) {
|
|
15
|
+
mkdirSync(CREDENTIALS_DIR, { recursive: true, mode: 448 });
|
|
16
|
+
const credentials = {
|
|
17
|
+
apiKey,
|
|
18
|
+
createdAt: new Date().toISOString()
|
|
19
|
+
};
|
|
20
|
+
writeFileSync(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2), { mode: 384 });
|
|
21
|
+
}
|
|
22
|
+
function getCredentialsDir() {
|
|
23
|
+
return CREDENTIALS_DIR;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/services/jsonc.ts
|
|
27
|
+
function processStringChar(content, state) {
|
|
28
|
+
const char = content[state.i];
|
|
29
|
+
if (char === undefined)
|
|
30
|
+
return;
|
|
31
|
+
let backslashCount = 0;
|
|
32
|
+
let j = state.i - 1;
|
|
33
|
+
while (j >= 0 && content[j] === "\\") {
|
|
34
|
+
backslashCount++;
|
|
35
|
+
j--;
|
|
36
|
+
}
|
|
37
|
+
if (backslashCount % 2 === 0) {
|
|
38
|
+
state.inString = !state.inString;
|
|
39
|
+
}
|
|
40
|
+
state.result += char;
|
|
41
|
+
state.i++;
|
|
42
|
+
}
|
|
43
|
+
function processSingleLineComment(content, state) {
|
|
44
|
+
const char = content[state.i];
|
|
45
|
+
if (char === undefined)
|
|
46
|
+
return;
|
|
47
|
+
if (char === `
|
|
48
|
+
`) {
|
|
49
|
+
state.inSingleLineComment = false;
|
|
50
|
+
state.result += char;
|
|
51
|
+
}
|
|
52
|
+
state.i++;
|
|
53
|
+
}
|
|
54
|
+
function processMultiLineComment(content, state) {
|
|
55
|
+
const char = content[state.i];
|
|
56
|
+
const nextChar = content[state.i + 1];
|
|
57
|
+
if (char === "*" && nextChar === "/") {
|
|
58
|
+
state.inMultiLineComment = false;
|
|
59
|
+
state.i += 2;
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (char === `
|
|
63
|
+
`) {
|
|
64
|
+
state.result += char;
|
|
65
|
+
}
|
|
66
|
+
state.i++;
|
|
67
|
+
}
|
|
68
|
+
function handleCommentStart(char, nextChar, state) {
|
|
69
|
+
if (char === "/" && nextChar === "/") {
|
|
70
|
+
state.inSingleLineComment = true;
|
|
71
|
+
state.i += 2;
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
if (char === "/" && nextChar === "*") {
|
|
75
|
+
state.inMultiLineComment = true;
|
|
76
|
+
state.i += 2;
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
function handleStringState(content, state, char) {
|
|
82
|
+
if (!state.inSingleLineComment && !state.inMultiLineComment && char === '"') {
|
|
83
|
+
processStringChar(content, state);
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
if (state.inString) {
|
|
87
|
+
state.result += char;
|
|
88
|
+
state.i++;
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
function handleCommentState(content, state) {
|
|
94
|
+
if (state.inSingleLineComment) {
|
|
95
|
+
processSingleLineComment(content, state);
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
if (state.inMultiLineComment) {
|
|
99
|
+
processMultiLineComment(content, state);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
function processCharacter(content, state) {
|
|
105
|
+
const char = content[state.i];
|
|
106
|
+
const nextChar = content[state.i + 1];
|
|
107
|
+
if (handleStringState(content, state, char ?? "")) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (handleCommentState(content, state)) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (handleCommentStart(char, nextChar, state)) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (char !== undefined) {
|
|
117
|
+
state.result += char;
|
|
118
|
+
}
|
|
119
|
+
state.i++;
|
|
120
|
+
}
|
|
121
|
+
function stripJsoncComments(content) {
|
|
122
|
+
const state = {
|
|
123
|
+
result: "",
|
|
124
|
+
i: 0,
|
|
125
|
+
inString: false,
|
|
126
|
+
inSingleLineComment: false,
|
|
127
|
+
inMultiLineComment: false
|
|
128
|
+
};
|
|
129
|
+
while (state.i < content.length) {
|
|
130
|
+
processCharacter(content, state);
|
|
131
|
+
}
|
|
132
|
+
return state.result.replaceAll(/,\s*([}\]])/g, "$1");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/cli/templates.ts
|
|
136
|
+
var SOLOMEMORY_INIT_COMMAND = `---
|
|
137
|
+
description: Initialize Solo Memory with comprehensive codebase knowledge
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
# Initializing Solo Memory
|
|
141
|
+
|
|
142
|
+
You are initializing persistent memory for this codebase. This is not just data collection - you're building context that will make you significantly more effective across all future sessions.
|
|
143
|
+
|
|
144
|
+
## Understanding Context
|
|
145
|
+
|
|
146
|
+
You are a **stateful** coding agent. Users expect to work with you over extended periods - potentially the entire lifecycle of a project. Your memory is how you get better over time and maintain continuity.
|
|
147
|
+
|
|
148
|
+
## What to Remember
|
|
149
|
+
|
|
150
|
+
### 1. Procedures (Rules & Workflows)
|
|
151
|
+
Explicit rules that should always be followed:
|
|
152
|
+
- "Never commit directly to main - always use feature branches"
|
|
153
|
+
- "Always run lint before tests"
|
|
154
|
+
- "Use conventional commits format"
|
|
155
|
+
|
|
156
|
+
### 2. Preferences (Style & Conventions)
|
|
157
|
+
Project and user coding style:
|
|
158
|
+
- "Prefer functional components over class components"
|
|
159
|
+
- "Use early returns instead of nested conditionals"
|
|
160
|
+
- "Always add JSDoc to exported functions"
|
|
161
|
+
|
|
162
|
+
### 3. Architecture & Context
|
|
163
|
+
How the codebase works and why:
|
|
164
|
+
- "Auth system was refactored in v2.0 - old patterns deprecated"
|
|
165
|
+
- "The monorepo used to have 3 modules before consolidation"
|
|
166
|
+
- "This pagination bug was fixed before - similar to PR #234"
|
|
167
|
+
|
|
168
|
+
## Memory Scopes
|
|
169
|
+
|
|
170
|
+
**Project-scoped** (\`scope: "project"\`):
|
|
171
|
+
- Build/test/lint commands
|
|
172
|
+
- Architecture and key directories
|
|
173
|
+
- Team conventions specific to this codebase
|
|
174
|
+
- Technology stack and framework choices
|
|
175
|
+
- Known issues and their solutions
|
|
176
|
+
|
|
177
|
+
**User-scoped** (\`scope: "user"\`):
|
|
178
|
+
- Personal coding preferences across all projects
|
|
179
|
+
- Communication style preferences
|
|
180
|
+
- General workflow habits
|
|
181
|
+
|
|
182
|
+
## Research Approach
|
|
183
|
+
|
|
184
|
+
This is a **deep research** initialization. Take your time and be thorough (~50+ tool calls). The goal is to genuinely understand the project, not just collect surface-level facts.
|
|
185
|
+
|
|
186
|
+
**What to uncover:**
|
|
187
|
+
- Tech stack and dependencies (explicit and implicit)
|
|
188
|
+
- Project structure and architecture
|
|
189
|
+
- Build/test/deploy commands and workflows
|
|
190
|
+
- Contributors & team dynamics (who works on what?)
|
|
191
|
+
- Commit conventions and branching strategy
|
|
192
|
+
- Code evolution (major refactors, architecture changes)
|
|
193
|
+
- Pain points (areas with lots of bug fixes)
|
|
194
|
+
- Implicit conventions not documented anywhere
|
|
195
|
+
|
|
196
|
+
## Research Techniques
|
|
197
|
+
|
|
198
|
+
### File-based
|
|
199
|
+
- README.md, CONTRIBUTING.md, AGENTS.md, CLAUDE.md
|
|
200
|
+
- Package manifests (package.json, Cargo.toml, pyproject.toml, go.mod)
|
|
201
|
+
- Config files (.eslintrc, tsconfig.json, .prettierrc)
|
|
202
|
+
- CI/CD configs (.github/workflows/)
|
|
203
|
+
|
|
204
|
+
### Git-based
|
|
205
|
+
- \`git log --oneline -20\` - Recent history
|
|
206
|
+
- \`git branch -a\` - Branching strategy
|
|
207
|
+
- \`git log --format="%s" -50\` - Commit conventions
|
|
208
|
+
- \`git shortlog -sn --all | head -10\` - Main contributors
|
|
209
|
+
|
|
210
|
+
### Explore Agent
|
|
211
|
+
Fire parallel explore queries for broad understanding:
|
|
212
|
+
\`\`\`
|
|
213
|
+
Task(explore, "What is the tech stack and key dependencies?")
|
|
214
|
+
Task(explore, "What is the project structure? Key directories?")
|
|
215
|
+
Task(explore, "How do you build, test, and run this project?")
|
|
216
|
+
Task(explore, "What are the main architectural patterns?")
|
|
217
|
+
Task(explore, "What conventions or patterns are used?")
|
|
218
|
+
\`\`\`
|
|
219
|
+
|
|
220
|
+
## How to Do Thorough Research
|
|
221
|
+
|
|
222
|
+
**Don't just collect data - analyze and cross-reference.**
|
|
223
|
+
|
|
224
|
+
Bad (shallow):
|
|
225
|
+
- Run commands, copy output
|
|
226
|
+
- List facts without understanding
|
|
227
|
+
|
|
228
|
+
Good (thorough):
|
|
229
|
+
- Cross-reference findings (if inconsistent, dig deeper)
|
|
230
|
+
- Resolve ambiguities (don't leave questions unanswered)
|
|
231
|
+
- Read actual file content, not just names
|
|
232
|
+
- Look for patterns (what do commits tell you about workflow?)
|
|
233
|
+
- Think like a new team member - what would you want to know?
|
|
234
|
+
|
|
235
|
+
## Saving Memories
|
|
236
|
+
|
|
237
|
+
Use the \`solomemory\` tool for each distinct insight:
|
|
238
|
+
|
|
239
|
+
\`\`\`
|
|
240
|
+
solomemory(mode: "add", content: "...", type: "...", scope: "project")
|
|
241
|
+
\`\`\`
|
|
242
|
+
|
|
243
|
+
**Types:**
|
|
244
|
+
- \`project-config\` - tech stack, commands, tooling
|
|
245
|
+
- \`architecture\` - codebase structure, key components, data flow
|
|
246
|
+
- \`learned-pattern\` - conventions specific to this codebase
|
|
247
|
+
- \`error-solution\` - known issues and their fixes
|
|
248
|
+
- \`preference\` - coding style preferences (use with user scope)
|
|
249
|
+
|
|
250
|
+
**Guidelines:**
|
|
251
|
+
- Save each distinct insight as a separate memory
|
|
252
|
+
- Be concise but include enough context to be useful
|
|
253
|
+
- Include the "why" not just the "what" when relevant
|
|
254
|
+
- Update memories incrementally as you research (don't wait until the end)
|
|
255
|
+
|
|
256
|
+
**Good memories:**
|
|
257
|
+
- "Uses Bun runtime and package manager. Commands: bun install, bun run dev, bun test"
|
|
258
|
+
- "API routes in src/routes/, handlers in src/handlers/. Hono framework."
|
|
259
|
+
- "Auth uses Redis sessions, not JWT. Implementation in src/lib/auth.ts"
|
|
260
|
+
- "Never use \`any\` type - strict TypeScript. Use \`unknown\` and narrow."
|
|
261
|
+
- "Database migrations must be backward compatible - we do rolling deploys"
|
|
262
|
+
|
|
263
|
+
## Upfront Questions
|
|
264
|
+
|
|
265
|
+
Before diving in, ask:
|
|
266
|
+
1. "Any specific rules I should always follow?"
|
|
267
|
+
2. "Preferences for how I communicate? (terse/detailed)"
|
|
268
|
+
|
|
269
|
+
## Reflection Phase
|
|
270
|
+
|
|
271
|
+
Before finishing, reflect:
|
|
272
|
+
1. **Completeness**: Did you cover commands, architecture, conventions, gotchas?
|
|
273
|
+
2. **Quality**: Are memories concise and searchable?
|
|
274
|
+
3. **Scope**: Did you correctly separate project vs user knowledge?
|
|
275
|
+
|
|
276
|
+
Then ask: "I've initialized memory with X insights. Want me to continue refining, or is this good?"
|
|
277
|
+
|
|
278
|
+
## Your Task
|
|
279
|
+
|
|
280
|
+
1. Ask upfront questions (research depth, rules, preferences)
|
|
281
|
+
2. Check existing memories: \`solomemory(mode: "list", scope: "project")\`
|
|
282
|
+
3. Research based on chosen depth
|
|
283
|
+
4. Save memories incrementally as you discover insights
|
|
284
|
+
5. Reflect and verify completeness
|
|
285
|
+
6. Summarize what was learned and ask if user wants refinement
|
|
286
|
+
`;
|
|
287
|
+
var SOLOMEMORY_LOGIN_COMMAND = `---
|
|
288
|
+
description: Configure Solo Memory API key
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
# Solo Memory Setup
|
|
292
|
+
|
|
293
|
+
To use Solo Memory, you need to set your API key.
|
|
294
|
+
|
|
295
|
+
## Option 1: Environment Variable (Recommended)
|
|
296
|
+
|
|
297
|
+
\`\`\`bash
|
|
298
|
+
export SOLOMEMORY_API_KEY="your-api-key"
|
|
299
|
+
\`\`\`
|
|
300
|
+
|
|
301
|
+
Add this to your shell profile (~/.bashrc, ~/.zshrc, etc.) for persistence.
|
|
302
|
+
|
|
303
|
+
## Option 2: Config File
|
|
304
|
+
|
|
305
|
+
Create \`~/.config/opencode/solomemory.jsonc\`:
|
|
306
|
+
|
|
307
|
+
\`\`\`jsonc
|
|
308
|
+
{
|
|
309
|
+
"apiKey": "your-api-key",
|
|
310
|
+
// Optional: Custom API URL (default: https://api.solomemory.com)
|
|
311
|
+
"apiUrl": "https://api.solomemory.com"
|
|
312
|
+
}
|
|
313
|
+
\`\`\`
|
|
314
|
+
|
|
315
|
+
## Option 3: Credentials File
|
|
316
|
+
|
|
317
|
+
Save credentials using the CLI:
|
|
318
|
+
|
|
319
|
+
\`\`\`bash
|
|
320
|
+
npx oc-solomemory@latest login
|
|
321
|
+
\`\`\`
|
|
322
|
+
|
|
323
|
+
This will prompt for your API key and save it securely.
|
|
324
|
+
|
|
325
|
+
## Verify Setup
|
|
326
|
+
|
|
327
|
+
Restart OpenCode and run:
|
|
328
|
+
|
|
329
|
+
\`\`\`bash
|
|
330
|
+
opencode -c
|
|
331
|
+
\`\`\`
|
|
332
|
+
|
|
333
|
+
You should see \`solomemory\` in the tools list.
|
|
334
|
+
`;
|
|
335
|
+
|
|
336
|
+
// src/cli/install.ts
|
|
337
|
+
var OPENCODE_CONFIG_DIR = path2.join(homedir2(), ".config", "opencode");
|
|
338
|
+
var OPENCODE_COMMAND_DIR = path2.join(OPENCODE_CONFIG_DIR, "command");
|
|
339
|
+
var JSON_INDENT_SPACES = 2;
|
|
340
|
+
var SEPARATOR_WIDTH = 50;
|
|
341
|
+
function getPluginTag() {
|
|
342
|
+
return "1.0.6-dev.70f1ae5".includes("-dev.") ? "dev" : "latest";
|
|
343
|
+
}
|
|
344
|
+
function getPluginName() {
|
|
345
|
+
return `oc-solomemory@${getPluginTag()}`;
|
|
346
|
+
}
|
|
347
|
+
function isOpencodeConfig(value) {
|
|
348
|
+
return typeof value === "object" && value !== null;
|
|
349
|
+
}
|
|
350
|
+
function parseOpencodeConfig(content) {
|
|
351
|
+
const jsonContent = stripJsoncComments(content);
|
|
352
|
+
try {
|
|
353
|
+
const parsed = JSON.parse(jsonContent);
|
|
354
|
+
if (!isOpencodeConfig(parsed)) {
|
|
355
|
+
console.error("✗ Invalid config file format");
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
return parsed;
|
|
359
|
+
} catch {
|
|
360
|
+
console.error("✗ Failed to parse config file");
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function insertPluginIntoExistingArray(content) {
|
|
365
|
+
const match = /("plugin"\s*:\s*\[)([^\]]*?)(\])/.exec(content);
|
|
366
|
+
if (match === null)
|
|
367
|
+
return content;
|
|
368
|
+
const fullMatch = match[0];
|
|
369
|
+
const start = match[1] ?? "";
|
|
370
|
+
const middle = match[2] ?? "";
|
|
371
|
+
const end = match[JSON_INDENT_SPACES + 1] ?? "";
|
|
372
|
+
const trimmed = middle.trim();
|
|
373
|
+
const replacement = trimmed === "" ? `${start}
|
|
374
|
+
"${getPluginName()}"
|
|
375
|
+
${end}` : `${start}${middle.trimEnd()},
|
|
376
|
+
"${getPluginName()}"
|
|
377
|
+
${end}`;
|
|
378
|
+
return content.replace(fullMatch, replacement);
|
|
379
|
+
}
|
|
380
|
+
function writePluginToJsonc(configPath, content) {
|
|
381
|
+
if (content.includes('"plugin"')) {
|
|
382
|
+
writeFileSync2(configPath, insertPluginIntoExistingArray(content));
|
|
383
|
+
} else {
|
|
384
|
+
const updated = content.replace(/^(\s*\{)/, `$1
|
|
385
|
+
"plugin": ["${getPluginName()}"],`);
|
|
386
|
+
writeFileSync2(configPath, updated);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function updateConfigWithPlugin(configPath, content) {
|
|
390
|
+
if (configPath.endsWith(".jsonc")) {
|
|
391
|
+
writePluginToJsonc(configPath, content);
|
|
392
|
+
} else {
|
|
393
|
+
const config = parseOpencodeConfig(content);
|
|
394
|
+
if (config === null)
|
|
395
|
+
return;
|
|
396
|
+
const plugins = config.plugin ?? [];
|
|
397
|
+
plugins.push(getPluginName());
|
|
398
|
+
config.plugin = plugins;
|
|
399
|
+
writeFileSync2(configPath, JSON.stringify(config, null, JSON_INDENT_SPACES));
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
function replaceExistingPlugin(content) {
|
|
403
|
+
return content.replaceAll(/"oc-solomemory@[^"]*"/g, `"${getPluginName()}"`);
|
|
404
|
+
}
|
|
405
|
+
function addPluginToConfig(configPath) {
|
|
406
|
+
try {
|
|
407
|
+
const content = readFileSync2(configPath, "utf8");
|
|
408
|
+
if (content.includes("oc-solomemory")) {
|
|
409
|
+
const updated = replaceExistingPlugin(content);
|
|
410
|
+
writeFileSync2(configPath, updated);
|
|
411
|
+
console.log(`✓ Plugin updated to ${getPluginName()}`);
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
const config = parseOpencodeConfig(content);
|
|
415
|
+
if (config === null)
|
|
416
|
+
return false;
|
|
417
|
+
updateConfigWithPlugin(configPath, content);
|
|
418
|
+
console.log(`✓ Added plugin to ${configPath}`);
|
|
419
|
+
return true;
|
|
420
|
+
} catch (error) {
|
|
421
|
+
console.error("✗ Failed to update config:", error);
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
function findOpencodeConfig() {
|
|
426
|
+
const candidates = [
|
|
427
|
+
path2.join(OPENCODE_CONFIG_DIR, "opencode.jsonc"),
|
|
428
|
+
path2.join(OPENCODE_CONFIG_DIR, "opencode.json")
|
|
429
|
+
];
|
|
430
|
+
for (const filePath of candidates) {
|
|
431
|
+
if (existsSync2(filePath)) {
|
|
432
|
+
return filePath;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
function createNewConfig() {
|
|
438
|
+
const configPath = path2.join(OPENCODE_CONFIG_DIR, "opencode.jsonc");
|
|
439
|
+
mkdirSync2(OPENCODE_CONFIG_DIR, { recursive: true });
|
|
440
|
+
const config = `{
|
|
441
|
+
"plugin": ["${getPluginName()}"]
|
|
442
|
+
}
|
|
443
|
+
`;
|
|
444
|
+
writeFileSync2(configPath, config);
|
|
445
|
+
console.log(`✓ Created ${configPath}`);
|
|
446
|
+
}
|
|
447
|
+
function createCommands() {
|
|
448
|
+
mkdirSync2(OPENCODE_COMMAND_DIR, { recursive: true });
|
|
449
|
+
const initPath = path2.join(OPENCODE_COMMAND_DIR, "solomemory-init.md");
|
|
450
|
+
writeFileSync2(initPath, SOLOMEMORY_INIT_COMMAND);
|
|
451
|
+
console.log(`✓ Created /solomemory-init command`);
|
|
452
|
+
const loginPath = path2.join(OPENCODE_COMMAND_DIR, "solomemory-login.md");
|
|
453
|
+
writeFileSync2(loginPath, SOLOMEMORY_LOGIN_COMMAND);
|
|
454
|
+
console.log(`✓ Created /solomemory-login command`);
|
|
455
|
+
}
|
|
456
|
+
function stepRegisterPlugin() {
|
|
457
|
+
console.log("Step 1: Register plugin in OpenCode config");
|
|
458
|
+
const configPath = findOpencodeConfig();
|
|
459
|
+
if (configPath === null) {
|
|
460
|
+
createNewConfig();
|
|
461
|
+
} else {
|
|
462
|
+
addPluginToConfig(configPath);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
function stepCreateCommands() {
|
|
466
|
+
console.log(`
|
|
467
|
+
Step 2: Create /solomemory-init and /solomemory-login commands`);
|
|
468
|
+
createCommands();
|
|
469
|
+
}
|
|
470
|
+
function printApiKeyRequired() {
|
|
471
|
+
console.error(`✗ API key is required.
|
|
472
|
+
`);
|
|
473
|
+
console.error("Usage:");
|
|
474
|
+
console.error(" npx oc-solomemory@latest install <api-key>");
|
|
475
|
+
console.error(` npx oc-solomemory@latest install --api-key=<key>
|
|
476
|
+
`);
|
|
477
|
+
console.error("Get your API key at https://solomemory.com");
|
|
478
|
+
return 1;
|
|
479
|
+
}
|
|
480
|
+
function saveApiKey(apiKey) {
|
|
481
|
+
saveCredentials(apiKey);
|
|
482
|
+
console.log(`✓ API key saved to ${getCredentialsDir()}`);
|
|
483
|
+
console.log(`
|
|
484
|
+
✓ Setup complete! Restart OpenCode to activate.
|
|
485
|
+
`);
|
|
486
|
+
}
|
|
487
|
+
function runInstall(apiKey) {
|
|
488
|
+
console.log(`
|
|
489
|
+
\uD83E\uDDE0 oc-solomemory installer
|
|
490
|
+
`);
|
|
491
|
+
stepRegisterPlugin();
|
|
492
|
+
stepCreateCommands();
|
|
493
|
+
console.log(`
|
|
494
|
+
` + "─".repeat(SEPARATOR_WIDTH));
|
|
495
|
+
console.log(`
|
|
496
|
+
\uD83D\uDD11 Final step: Configure API key
|
|
497
|
+
`);
|
|
498
|
+
saveApiKey(apiKey);
|
|
499
|
+
return 0;
|
|
500
|
+
}
|
|
501
|
+
function install(apiKey) {
|
|
502
|
+
if (typeof apiKey === "string") {
|
|
503
|
+
return runInstall(apiKey);
|
|
504
|
+
}
|
|
505
|
+
return printApiKeyRequired();
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// src/cli/index.ts
|
|
509
|
+
function printHelp() {
|
|
510
|
+
console.log(`
|
|
511
|
+
oc-solomemory - Persistent memory for OpenCode agents
|
|
512
|
+
|
|
513
|
+
Usage:
|
|
514
|
+
npx oc-solomemory@latest install <api-key>
|
|
515
|
+
npx oc-solomemory@latest install --api-key=<key>
|
|
516
|
+
|
|
517
|
+
Get your API key at https://solomemory.com
|
|
518
|
+
`);
|
|
519
|
+
}
|
|
520
|
+
function shouldShowHelp(command) {
|
|
521
|
+
return command === undefined || command === "help" || command === "--help" || command === "-h";
|
|
522
|
+
}
|
|
523
|
+
function parseApiKey(args) {
|
|
524
|
+
const flag = args.find((a) => a.startsWith("--api-key="));
|
|
525
|
+
if (flag !== undefined)
|
|
526
|
+
return flag.split("=")[1];
|
|
527
|
+
const positional = args.find((a) => !a.startsWith("-"));
|
|
528
|
+
return positional;
|
|
529
|
+
}
|
|
530
|
+
function handleCommand(command, args) {
|
|
531
|
+
if (command === "install") {
|
|
532
|
+
return install(parseApiKey(args.slice(1)));
|
|
533
|
+
}
|
|
534
|
+
console.error(`Unknown command: ${command}`);
|
|
535
|
+
printHelp();
|
|
536
|
+
return 1;
|
|
537
|
+
}
|
|
538
|
+
function main() {
|
|
539
|
+
const args = process.argv.slice(2);
|
|
540
|
+
const command = args[0];
|
|
541
|
+
if (shouldShowHelp(command)) {
|
|
542
|
+
printHelp();
|
|
543
|
+
process.exit(0);
|
|
544
|
+
}
|
|
545
|
+
if (command === undefined) {
|
|
546
|
+
process.exit(1);
|
|
547
|
+
}
|
|
548
|
+
const exitCode = handleCommand(command, args);
|
|
549
|
+
process.exit(exitCode);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// src/cli.ts
|
|
553
|
+
main();
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare function getApiKey(): string | undefined;
|
|
2
|
+
export declare function getApiUrl(): string;
|
|
3
|
+
export declare function isConfigured(): boolean;
|
|
4
|
+
export interface RuntimeConfig {
|
|
5
|
+
readonly maxMemories: number;
|
|
6
|
+
readonly maxProjectMemories: number;
|
|
7
|
+
readonly maxProfileItems: number;
|
|
8
|
+
readonly injectProfile: boolean;
|
|
9
|
+
readonly containerTagPrefix: string;
|
|
10
|
+
readonly platformIdentifier: string;
|
|
11
|
+
readonly autoSyncConversations: boolean;
|
|
12
|
+
readonly filterPrompt: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function getConfig(): RuntimeConfig;
|
|
15
|
+
export declare const CONFIG: RuntimeConfig;
|
|
16
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAuGA,wBAAgB,SAAS,IAAI,MAAM,GAAG,SAAS,CAG9C;AAED,wBAAgB,SAAS,IAAI,MAAM,CAGlC;AAED,wBAAgB,YAAY,IAAI,OAAO,CAEtC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,qBAAqB,EAAE,OAAO,CAAC;IACxC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,wBAAgB,SAAS,IAAI,aAAa,CAMzC;AAoCD,eAAO,MAAM,MAAM,EAAE,aAAoE,CAAC"}
|
package/dist/index.d.ts
ADDED