opencode-knowledge 0.3.4 โ 0.4.0-next.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/LICENSE +1 -1
- package/README.md +404 -53
- package/dist/templates/first-prompt.template.md +17 -5
- package/package.json +1 -1
- package/dist/command/.gitkeep +0 -0
- package/dist/index.js +0 -221
- package/dist/templates/abbreviated.template.md +0 -8
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,87 +1,438 @@
|
|
|
1
1
|
# opencode-knowledge
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
A comprehensive knowledge management system for OpenCode that provides:
|
|
4
|
+
- **Role-based AI personalities** - Customize OpenCode's communication style
|
|
5
|
+
- **Knowledge vault** - Organize coding standards, patterns, and best practices
|
|
6
|
+
- **Tag-based search** - Quickly find relevant knowledge packages
|
|
7
|
+
- **Session management** - Track loaded packages and optimize token usage
|
|
6
8
|
|
|
7
9
|
## Features
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- ๐ฆ Bun/npm build tooling
|
|
12
|
-
- โจ ESLint + Prettier formatting
|
|
13
|
-
- ๐งช Vitest testing setup
|
|
14
|
-
- ๐ GitHub Actions CI/CD
|
|
15
|
-
- ๐ Release automation with release-please
|
|
11
|
+
### ๐ญ Personality System
|
|
12
|
+
Choose from different AI personas (staff engineer, frontend specialist, cthulhu, etc.) that influence how OpenCode communicates and approaches problems.
|
|
16
13
|
|
|
17
|
-
|
|
14
|
+
### ๐ Knowledge Vault
|
|
15
|
+
Create a structured library of markdown-based knowledge packages with frontmatter metadata for easy discovery and loading.
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
### ๐ Smart Search
|
|
18
|
+
Tag-based search system finds relevant knowledge packages based on your current task.
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
cd your-plugin-name
|
|
24
|
-
```
|
|
20
|
+
### ๐พ Session Persistence
|
|
21
|
+
Tracks loaded packages across sessions and provides token-optimized context injection.
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
- Change `name` to your plugin name
|
|
28
|
-
- Update `description`
|
|
29
|
-
- Update `repository.url`
|
|
23
|
+
---
|
|
30
24
|
|
|
31
|
-
|
|
25
|
+
## Installation
|
|
32
26
|
|
|
33
|
-
|
|
34
|
-
bun install
|
|
35
|
-
```
|
|
27
|
+
Add the plugin to your OpenCode config:
|
|
36
28
|
|
|
37
|
-
|
|
29
|
+
**Global config** (`~/.config/opencode/opencode.json` or `opencode.jsonc`):
|
|
38
30
|
|
|
39
|
-
|
|
40
|
-
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"plugin": ["opencode-knowledge"]
|
|
34
|
+
}
|
|
35
|
+
```
|
|
41
36
|
|
|
42
|
-
|
|
43
|
-
return {
|
|
44
|
-
tool: {
|
|
45
|
-
// Your plugin tools here
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
};
|
|
49
|
-
```
|
|
37
|
+
**Or per-project** (`opencode.json` or `opencode.jsonc` in your project root):
|
|
50
38
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"plugin": ["opencode-knowledge"]
|
|
42
|
+
}
|
|
43
|
+
```
|
|
55
44
|
|
|
56
|
-
|
|
45
|
+
---
|
|
57
46
|
|
|
58
|
-
|
|
59
|
-
- `mise run test` - Run tests
|
|
60
|
-
- `mise run lint` - Lint code
|
|
61
|
-
- `mise run lint:fix` - Fix linting issues
|
|
62
|
-
- `mise run format` - Format code with Prettier
|
|
47
|
+
## Quick Start
|
|
63
48
|
|
|
64
|
-
|
|
49
|
+
### 1. Create Settings File
|
|
65
50
|
|
|
66
|
-
Create
|
|
51
|
+
Create `.opencode/knowledge/settings.json` in your project:
|
|
67
52
|
|
|
68
53
|
```json
|
|
69
54
|
{
|
|
70
|
-
"
|
|
55
|
+
"role": "staff_engineer"
|
|
71
56
|
}
|
|
72
57
|
```
|
|
73
58
|
|
|
74
|
-
|
|
59
|
+
### 2. Create Knowledge Vault (Optional)
|
|
60
|
+
|
|
61
|
+
If you want to use the knowledge management features, create a vault structure:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
mkdir -p .opencode/knowledge/vault/standards
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 3. Create Your First Knowledge Package
|
|
68
|
+
|
|
69
|
+
Create `.opencode/knowledge/vault/standards/code-conventions.md`:
|
|
70
|
+
|
|
71
|
+
```markdown
|
|
72
|
+
---
|
|
73
|
+
tags:
|
|
74
|
+
- standards
|
|
75
|
+
- typescript
|
|
76
|
+
- conventions
|
|
77
|
+
description: Core code conventions and style guide
|
|
78
|
+
category: standards
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
# Code Conventions
|
|
82
|
+
|
|
83
|
+
## Naming
|
|
84
|
+
- Use camelCase for variables and functions
|
|
85
|
+
- Use PascalCase for classes and types
|
|
86
|
+
|
|
87
|
+
## Formatting
|
|
88
|
+
- Use single quotes for strings
|
|
89
|
+
- Line width: 100 characters
|
|
90
|
+
- Always use semicolons
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 4. Start OpenCode Session
|
|
94
|
+
|
|
95
|
+
The knowledge catalog is **automatically built on session start**. Just start a new session and the plugin will:
|
|
96
|
+
- Scan your vault for packages
|
|
97
|
+
- Build the searchable catalog
|
|
98
|
+
- Inject knowledge map on first message
|
|
99
|
+
|
|
100
|
+
### 5. Search and Load Knowledge
|
|
101
|
+
|
|
102
|
+
Search for packages by tags:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
knowledge_search [tags=typescript,conventions]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Load packages into your session:
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
knowledge_load [paths=standards/code-conventions.md]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Available Personalities
|
|
117
|
+
|
|
118
|
+
### staff_engineer
|
|
119
|
+
|
|
120
|
+
Skeptical, pragmatic Staff Engineer focused on architecture, coupling, operational risk, and maintainability.
|
|
121
|
+
|
|
122
|
+
**Best for**: Code reviews, architecture decisions, production systems
|
|
123
|
+
|
|
124
|
+
### cthulhu
|
|
125
|
+
|
|
126
|
+
Ancient cosmic entity providing technical guidance with existential dread and cosmic perspective.
|
|
127
|
+
|
|
128
|
+
**Best for**: When you need technical help but also want to contemplate the meaninglessness of time
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Tools
|
|
133
|
+
|
|
134
|
+
### knowledge_search
|
|
135
|
+
|
|
136
|
+
Search for knowledge packages by tags.
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
knowledge_search [tags=typescript,react,testing]
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Parameters**: Comma-separated list of tags
|
|
143
|
+
|
|
144
|
+
**Output**: Ranked list of matching packages with relevance scores
|
|
145
|
+
|
|
146
|
+
**Example**:
|
|
147
|
+
```
|
|
148
|
+
Found 5 packages matching [typescript, react]:
|
|
149
|
+
|
|
150
|
+
- **frontend/react-patterns.md** (75%)
|
|
151
|
+
Tags: typescript, react, patterns
|
|
152
|
+
Common React patterns and best practices
|
|
153
|
+
|
|
154
|
+
- **standards/typescript-conventions.md** (50%)
|
|
155
|
+
Tags: typescript, conventions
|
|
156
|
+
TypeScript coding standards
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
### knowledge_load
|
|
162
|
+
|
|
163
|
+
Load knowledge packages into the current session.
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
knowledge_load [paths=standards/code-conventions.md,frontend/react-patterns.md]
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Parameters**: Comma-separated list of package paths (relative to vault/)
|
|
170
|
+
|
|
171
|
+
**Output**: Package content injected into context
|
|
172
|
+
|
|
173
|
+
**Features**:
|
|
174
|
+
- Deduplication (won't load same package twice)
|
|
175
|
+
- Session tracking (remembers what's loaded)
|
|
176
|
+
- Error handling (warns about missing packages)
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
### knowledge_index
|
|
181
|
+
|
|
182
|
+
Manually rebuild the knowledge catalog from your vault.
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
knowledge_index
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Output**: Summary of categories and packages indexed.
|
|
189
|
+
|
|
190
|
+
**Note**: The catalog is **automatically built on session start**, so you rarely need this tool.
|
|
191
|
+
|
|
192
|
+
**When to use**:
|
|
193
|
+
- After adding packages mid-session
|
|
194
|
+
- If auto-build failed during session start
|
|
195
|
+
- To verify catalog contents
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Knowledge Package Format
|
|
75
200
|
|
|
76
|
-
|
|
201
|
+
Knowledge packages are markdown files with YAML frontmatter:
|
|
77
202
|
|
|
78
|
-
|
|
203
|
+
```markdown
|
|
204
|
+
---
|
|
205
|
+
tags:
|
|
206
|
+
- tag1
|
|
207
|
+
- tag2
|
|
208
|
+
- tag3
|
|
209
|
+
description: Brief description of this package
|
|
210
|
+
category: category-name
|
|
211
|
+
required_knowledge:
|
|
212
|
+
- other-package-1
|
|
213
|
+
- other-package-2
|
|
214
|
+
file_patterns:
|
|
215
|
+
- "*.tsx"
|
|
216
|
+
- "*.test.ts"
|
|
217
|
+
---
|
|
79
218
|
|
|
80
|
-
|
|
219
|
+
# Package Title
|
|
220
|
+
|
|
221
|
+
Your knowledge content here...
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Frontmatter Fields
|
|
225
|
+
|
|
226
|
+
| Field | Required | Description |
|
|
227
|
+
|-------|----------|-------------|
|
|
228
|
+
| `tags` | Yes | Array of searchable tags |
|
|
229
|
+
| `description` | Yes | Brief summary (used in search results) |
|
|
230
|
+
| `category` | Yes | Category for organization (e.g., `frontend`, `backend`, `standards`) |
|
|
231
|
+
| `required_knowledge` | No | Other packages that should be loaded first |
|
|
232
|
+
| `file_patterns` | No | File patterns where this knowledge applies |
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Directory Structure
|
|
237
|
+
|
|
238
|
+
```
|
|
239
|
+
your-project/
|
|
240
|
+
โโโ .opencode/
|
|
241
|
+
โโโ knowledge/
|
|
242
|
+
โโโ settings.json # Plugin configuration
|
|
243
|
+
โโโ knowledge.json # Auto-generated catalog (gitignored)
|
|
244
|
+
โโโ vault/ # Your knowledge packages
|
|
245
|
+
โ โโโ frontend/
|
|
246
|
+
โ โ โโโ react-patterns.md
|
|
247
|
+
โ โ โโโ state-management.md
|
|
248
|
+
โ โโโ backend/
|
|
249
|
+
โ โ โโโ api-design.md
|
|
250
|
+
โ โโโ standards/
|
|
251
|
+
โ โโโ code-conventions.md
|
|
252
|
+
โ โโโ testing-guide.md
|
|
253
|
+
โโโ tracker/ # Session state (gitignored)
|
|
254
|
+
โโโ session-state.jsonl
|
|
255
|
+
โโโ knowledge-reads.jsonl
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## Token Optimization
|
|
261
|
+
|
|
262
|
+
The plugin uses a single-phase approach for optimal token usage:
|
|
263
|
+
|
|
264
|
+
### First Message Only
|
|
265
|
+
- Shows full category-tag map (~500-1000 tokens depending on vault size)
|
|
266
|
+
- Injects personality
|
|
267
|
+
- Documents available tools with examples
|
|
268
|
+
- Creates session state
|
|
269
|
+
|
|
270
|
+
### Subsequent Messages
|
|
271
|
+
- No knowledge context injected
|
|
272
|
+
- LLM uses memory of first message
|
|
273
|
+
- **100% token savings** on subsequent messages
|
|
274
|
+
|
|
275
|
+
This approach provides significant token savings while ensuring the LLM has all the context it needs from the initial session setup.
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Example Vault Structure
|
|
280
|
+
|
|
281
|
+
Here's a recommended organization pattern:
|
|
282
|
+
|
|
283
|
+
```
|
|
284
|
+
vault/
|
|
285
|
+
โโโ frontend/
|
|
286
|
+
โ โโโ react-patterns.md # React best practices
|
|
287
|
+
โ โโโ state-management.md # Redux, Context, etc.
|
|
288
|
+
โ โโโ component-testing.md # Testing components
|
|
289
|
+
โ โโโ accessibility.md # A11y guidelines
|
|
290
|
+
โโโ backend/
|
|
291
|
+
โ โโโ api-design.md # REST/GraphQL patterns
|
|
292
|
+
โ โโโ database-patterns.md # ORM, migrations
|
|
293
|
+
โ โโโ error-handling.md # Error handling strategies
|
|
294
|
+
โโโ standards/
|
|
295
|
+
โ โโโ code-conventions.md # General coding standards
|
|
296
|
+
โ โโโ git-workflow.md # Branch strategy, commits
|
|
297
|
+
โ โโโ code-review.md # Review guidelines
|
|
298
|
+
โโโ infrastructure/
|
|
299
|
+
โโโ docker-patterns.md # Container best practices
|
|
300
|
+
โโโ ci-cd.md # Pipeline patterns
|
|
301
|
+
โโโ monitoring.md # Observability
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## Advanced Usage
|
|
307
|
+
|
|
308
|
+
### Creating Cross-Referenced Packages
|
|
309
|
+
|
|
310
|
+
Use `required_knowledge` to create dependency chains:
|
|
311
|
+
|
|
312
|
+
```markdown
|
|
313
|
+
---
|
|
314
|
+
tags:
|
|
315
|
+
- react
|
|
316
|
+
- advanced
|
|
317
|
+
- performance
|
|
318
|
+
description: Advanced React performance optimization
|
|
319
|
+
category: frontend
|
|
320
|
+
required_knowledge:
|
|
321
|
+
- frontend/react-patterns
|
|
322
|
+
- standards/code-conventions
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
# Advanced React Performance
|
|
326
|
+
|
|
327
|
+
This builds on basic patterns...
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### File Pattern Targeting
|
|
331
|
+
|
|
332
|
+
Specify when knowledge applies:
|
|
333
|
+
|
|
334
|
+
```markdown
|
|
335
|
+
---
|
|
336
|
+
tags:
|
|
337
|
+
- testing
|
|
338
|
+
- jest
|
|
339
|
+
description: Jest testing patterns
|
|
340
|
+
category: frontend
|
|
341
|
+
file_patterns:
|
|
342
|
+
- "*.test.ts"
|
|
343
|
+
- "*.test.tsx"
|
|
344
|
+
- "*.spec.ts"
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
# Jest Testing Guide
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## Troubleshooting
|
|
353
|
+
|
|
354
|
+
### Settings file not found
|
|
355
|
+
|
|
356
|
+
**Error**: `CONFIGURATION ERROR: Settings file not found`
|
|
357
|
+
|
|
358
|
+
**Solution**: Create `.opencode/knowledge/settings.json` with:
|
|
359
|
+
```json
|
|
360
|
+
{
|
|
361
|
+
"role": "staff_engineer"
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Personality not loading
|
|
366
|
+
|
|
367
|
+
**Error**: `CONFIGURATION ERROR: Personality file not found`
|
|
368
|
+
|
|
369
|
+
**Solution**: Verify the role name in `settings.json` matches an available personality (`staff_engineer` or `cthulhu`)
|
|
370
|
+
|
|
371
|
+
### Catalog not found
|
|
372
|
+
|
|
373
|
+
**Error**: `Knowledge catalog not found. Run knowledge_index first.`
|
|
374
|
+
|
|
375
|
+
**Solution**: Run `knowledge_index` to build the catalog from your vault
|
|
376
|
+
|
|
377
|
+
### Search returns no results
|
|
378
|
+
|
|
379
|
+
**Possible causes**:
|
|
380
|
+
1. Catalog is outdated โ Run `knowledge_index`
|
|
381
|
+
2. Tags don't match โ Check tag spelling
|
|
382
|
+
3. No packages with those tags โ Add packages or adjust search
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## Development
|
|
387
|
+
|
|
388
|
+
### Building
|
|
389
|
+
|
|
390
|
+
```bash
|
|
391
|
+
mise run build
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
Or directly with Bun:
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
bun build ./src/index.ts --outdir dist --target bun
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### Linting
|
|
401
|
+
|
|
402
|
+
```bash
|
|
403
|
+
mise run lint # Check for issues
|
|
404
|
+
mise run lint:fix # Auto-fix issues
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### Formatting
|
|
408
|
+
|
|
409
|
+
```bash
|
|
410
|
+
mise run format
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## Roadmap
|
|
416
|
+
|
|
417
|
+
- [ ] Auto-load packages based on file patterns
|
|
418
|
+
- [ ] Knowledge package dependencies resolution
|
|
419
|
+
- [ ] Usage analytics and metrics
|
|
420
|
+
- [ ] Export/import vault bundles
|
|
421
|
+
- [ ] Knowledge package templates
|
|
422
|
+
- [ ] VSCode extension for vault management
|
|
423
|
+
|
|
424
|
+
---
|
|
81
425
|
|
|
82
426
|
## Contributing
|
|
83
427
|
|
|
84
|
-
Contributions
|
|
428
|
+
Contributions welcome! Please:
|
|
429
|
+
|
|
430
|
+
1. Follow the code conventions in `AGENTS.md`
|
|
431
|
+
2. Run `mise run lint` before committing
|
|
432
|
+
3. Update documentation for new features
|
|
433
|
+
4. Add tests for new functionality
|
|
434
|
+
|
|
435
|
+
---
|
|
85
436
|
|
|
86
437
|
## License
|
|
87
438
|
|
|
@@ -9,10 +9,22 @@ You have access to a comprehensive knowledge base containing {{CATEGORIES_COUNT}
|
|
|
9
9
|
**Packages:**
|
|
10
10
|
{{CORE_PACKAGES_LIST}}
|
|
11
11
|
|
|
12
|
-
###
|
|
13
|
-
{{USER_PROMPT}}
|
|
12
|
+
### Available Knowledge Tools
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
You can load specific knowledge packages using the knowledge_load tool when relevant to the task at hand. Focus on packages that match the technical domain or problem you're solving.
|
|
14
|
+
You have access to the following tools for working with the knowledge vault:
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
**knowledge_search** - Search for packages by tags
|
|
17
|
+
- Use this to discover relevant knowledge packages
|
|
18
|
+
- Provide comma-separated tags (e.g., "typescript,react,testing")
|
|
19
|
+
- Returns ranked list of matching packages with relevance scores
|
|
20
|
+
|
|
21
|
+
**knowledge_load** - Load packages into context
|
|
22
|
+
- Use this to inject knowledge package content into the session
|
|
23
|
+
- Provide comma-separated paths (e.g., "standards/code-conventions.md")
|
|
24
|
+
- Returns package content and metadata for your reference
|
|
25
|
+
|
|
26
|
+
**knowledge_index** - Rebuild catalog (rarely needed)
|
|
27
|
+
- Use this if search results seem outdated or after adding packages
|
|
28
|
+
- Automatically runs on session start
|
|
29
|
+
|
|
30
|
+
Use these tools proactively when you need domain-specific guidance or best practices.
|
package/package.json
CHANGED
package/dist/command/.gitkeep
DELETED
|
File without changes
|
package/dist/index.js
DELETED
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
// src/index.ts
|
|
3
|
-
import path from "path";
|
|
4
|
-
import { fileURLToPath } from "url";
|
|
5
|
-
import { readFile as readFile2 } from "fs/promises";
|
|
6
|
-
import { existsSync as existsSync2 } from "fs";
|
|
7
|
-
|
|
8
|
-
// src/lib/session-state.ts
|
|
9
|
-
import { readFile } from "fs/promises";
|
|
10
|
-
import { existsSync } from "fs";
|
|
11
|
-
var sessionStates = new Map;
|
|
12
|
-
async function createSessionState(sessionId) {
|
|
13
|
-
if (sessionStates.has(sessionId)) {
|
|
14
|
-
throw new Error(`Session state already exists for session: ${sessionId}`);
|
|
15
|
-
}
|
|
16
|
-
const settingsPath = ".opencode/knowledge/settings.json";
|
|
17
|
-
if (!existsSync(settingsPath)) {
|
|
18
|
-
throw new Error(`CONFIGURATION ERROR: Cannot create session state - settings file not found at ${settingsPath}`);
|
|
19
|
-
}
|
|
20
|
-
let role;
|
|
21
|
-
try {
|
|
22
|
-
const settingsContent = await readFile(settingsPath, "utf-8");
|
|
23
|
-
const settings = JSON.parse(settingsContent);
|
|
24
|
-
if (!settings.role) {
|
|
25
|
-
throw new Error(`CONFIGURATION ERROR: Cannot create session state - missing 'role' field in ${settingsPath}`);
|
|
26
|
-
}
|
|
27
|
-
role = settings.role;
|
|
28
|
-
} catch (error) {
|
|
29
|
-
if (error instanceof Error && error.message.includes("CONFIGURATION ERROR")) {
|
|
30
|
-
throw error;
|
|
31
|
-
}
|
|
32
|
-
throw new Error(`Error reading settings.json: ${error}`);
|
|
33
|
-
}
|
|
34
|
-
const state = {
|
|
35
|
-
role,
|
|
36
|
-
isFirstPrompt: true,
|
|
37
|
-
loadedPackages: new Set,
|
|
38
|
-
createdAt: new Date
|
|
39
|
-
};
|
|
40
|
-
sessionStates.set(sessionId, state);
|
|
41
|
-
}
|
|
42
|
-
function getSessionState(sessionId) {
|
|
43
|
-
const state = sessionStates.get(sessionId);
|
|
44
|
-
if (!state) {
|
|
45
|
-
throw new Error(`Session state not found for session: ${sessionId}`);
|
|
46
|
-
}
|
|
47
|
-
return state;
|
|
48
|
-
}
|
|
49
|
-
function updateSessionState(sessionId, updates) {
|
|
50
|
-
const state = getSessionState(sessionId);
|
|
51
|
-
Object.assign(state, updates);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// src/index.ts
|
|
55
|
-
var logToFile = async (message) => {
|
|
56
|
-
try {
|
|
57
|
-
const logEntry = `${new Date().toISOString()}: ${message}
|
|
58
|
-
`;
|
|
59
|
-
let existing = "";
|
|
60
|
-
try {
|
|
61
|
-
existing = await Bun.file("/tmp/opencode-knowledge-debug.log").text();
|
|
62
|
-
} catch {}
|
|
63
|
-
await Bun.write("/tmp/opencode-knowledge-debug.log", existing + logEntry);
|
|
64
|
-
} catch (e) {}
|
|
65
|
-
};
|
|
66
|
-
function parseFrontmatter(content) {
|
|
67
|
-
const frontmatterRegex = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/;
|
|
68
|
-
const match = content.match(frontmatterRegex);
|
|
69
|
-
if (!match) {
|
|
70
|
-
return { frontmatter: {}, body: content.trim() };
|
|
71
|
-
}
|
|
72
|
-
const [, yamlContent, body] = match;
|
|
73
|
-
const frontmatter = {};
|
|
74
|
-
for (const line of yamlContent.split(`
|
|
75
|
-
`)) {
|
|
76
|
-
const colonIndex = line.indexOf(":");
|
|
77
|
-
if (colonIndex === -1)
|
|
78
|
-
continue;
|
|
79
|
-
const key = line.slice(0, colonIndex).trim();
|
|
80
|
-
const value = line.slice(colonIndex + 1).trim();
|
|
81
|
-
if (key === "description")
|
|
82
|
-
frontmatter.description = value;
|
|
83
|
-
if (key === "agent")
|
|
84
|
-
frontmatter.agent = value;
|
|
85
|
-
if (key === "model")
|
|
86
|
-
frontmatter.model = value;
|
|
87
|
-
if (key === "subtask")
|
|
88
|
-
frontmatter.subtask = value === "true";
|
|
89
|
-
}
|
|
90
|
-
return { frontmatter, body: body.trim() };
|
|
91
|
-
}
|
|
92
|
-
async function loadCommands() {
|
|
93
|
-
const commands = [];
|
|
94
|
-
const __dirname2 = path.dirname(fileURLToPath(import.meta.url));
|
|
95
|
-
const commandDir = path.join(__dirname2, "command");
|
|
96
|
-
if (!existsSync2(commandDir)) {
|
|
97
|
-
return commands;
|
|
98
|
-
}
|
|
99
|
-
const glob = new Bun.Glob("**/*.md");
|
|
100
|
-
for await (const file of glob.scan({ cwd: commandDir, absolute: true })) {
|
|
101
|
-
const content = await Bun.file(file).text();
|
|
102
|
-
const { frontmatter, body } = parseFrontmatter(content);
|
|
103
|
-
const relativePath = path.relative(commandDir, file);
|
|
104
|
-
const name = relativePath.replace(/\.md$/, "").replace(/\//g, "-");
|
|
105
|
-
commands.push({
|
|
106
|
-
name,
|
|
107
|
-
frontmatter,
|
|
108
|
-
template: body
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
return commands;
|
|
112
|
-
}
|
|
113
|
-
async function loadPersonality() {
|
|
114
|
-
const settingsPath = ".opencode/knowledge/settings.json";
|
|
115
|
-
if (!existsSync2(settingsPath)) {
|
|
116
|
-
const errorMsg = `\u274C CONFIGURATION ERROR: Settings file not found at ${settingsPath}. Please create this file with {"role": "your_role"}`;
|
|
117
|
-
await logToFile(errorMsg);
|
|
118
|
-
throw new Error(errorMsg);
|
|
119
|
-
}
|
|
120
|
-
let role;
|
|
121
|
-
try {
|
|
122
|
-
const settingsContent = await readFile2(settingsPath, "utf-8");
|
|
123
|
-
const settings = JSON.parse(settingsContent);
|
|
124
|
-
if (!settings.role) {
|
|
125
|
-
const errorMsg = `\u274C CONFIGURATION ERROR: Settings file exists but missing 'role' field. Please add {"role": "your_role"} to ${settingsPath}`;
|
|
126
|
-
await logToFile(errorMsg);
|
|
127
|
-
throw new Error(errorMsg);
|
|
128
|
-
}
|
|
129
|
-
role = settings.role;
|
|
130
|
-
await logToFile(`\u2705 Settings loaded: role="${role}" from ${settingsPath}`);
|
|
131
|
-
} catch (error) {
|
|
132
|
-
if (error instanceof Error && error.message.includes("CONFIGURATION ERROR")) {
|
|
133
|
-
throw error;
|
|
134
|
-
}
|
|
135
|
-
const errorMsg = `\u274C Error parsing settings.json: ${error}`;
|
|
136
|
-
await logToFile(errorMsg);
|
|
137
|
-
throw new Error(errorMsg);
|
|
138
|
-
}
|
|
139
|
-
const __dirname2 = path.dirname(fileURLToPath(import.meta.url));
|
|
140
|
-
let personalityPath = path.join(__dirname2, "..", "templates", "personalities", `${role}.txt`);
|
|
141
|
-
if (!existsSync2(personalityPath)) {
|
|
142
|
-
personalityPath = path.join(__dirname2, "templates", "personalities", `${role}.txt`);
|
|
143
|
-
}
|
|
144
|
-
if (!existsSync2(personalityPath)) {
|
|
145
|
-
const errorMsg = `\u274C CONFIGURATION ERROR: Personality file not found at ${personalityPath}. Available roles should have a corresponding .txt file in templates/personalities/`;
|
|
146
|
-
await logToFile(errorMsg);
|
|
147
|
-
throw new Error(errorMsg);
|
|
148
|
-
}
|
|
149
|
-
const content = await readFile2(personalityPath, "utf-8");
|
|
150
|
-
await logToFile(`\u2705 Personality loaded from: ${personalityPath}`);
|
|
151
|
-
return content.trim();
|
|
152
|
-
}
|
|
153
|
-
var opencodeKnowledge = async (ctx) => {
|
|
154
|
-
await logToFile("Plugin initialized");
|
|
155
|
-
const commands = await loadCommands();
|
|
156
|
-
return {
|
|
157
|
-
async config(config) {
|
|
158
|
-
config.command = config.command ?? {};
|
|
159
|
-
for (const cmd of commands) {
|
|
160
|
-
let template = cmd.template;
|
|
161
|
-
template = template.replace("{{CURRENT_TIME}}", new Date().toISOString());
|
|
162
|
-
config.command[cmd.name] = {
|
|
163
|
-
template,
|
|
164
|
-
description: cmd.frontmatter.description,
|
|
165
|
-
agent: cmd.frontmatter.agent,
|
|
166
|
-
model: cmd.frontmatter.model,
|
|
167
|
-
subtask: cmd.frontmatter.subtask
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
},
|
|
171
|
-
"chat.message": async (input, output) => {
|
|
172
|
-
try {
|
|
173
|
-
const state = getSessionState(input.sessionID);
|
|
174
|
-
if (state.isFirstPrompt) {
|
|
175
|
-
await logToFile(`\uD83C\uDFAF First message in session ${input.sessionID} - injecting personality`);
|
|
176
|
-
const personality = await loadPersonality();
|
|
177
|
-
const personalityPart = {
|
|
178
|
-
type: "text",
|
|
179
|
-
text: `## Role Context
|
|
180
|
-
|
|
181
|
-
${personality}`,
|
|
182
|
-
id: `personality-${Date.now()}`,
|
|
183
|
-
sessionID: input.sessionID,
|
|
184
|
-
messageID: input.messageID || ""
|
|
185
|
-
};
|
|
186
|
-
output.parts.push(personalityPart);
|
|
187
|
-
updateSessionState(input.sessionID, { isFirstPrompt: false });
|
|
188
|
-
await logToFile("\u2705 Personality injected on first message!");
|
|
189
|
-
} else {
|
|
190
|
-
await logToFile(`\u23ED\uFE0F Subsequent message - skipping personality injection`);
|
|
191
|
-
}
|
|
192
|
-
} catch (error) {
|
|
193
|
-
await logToFile(`\u274C Error in chat.message: ${error}`);
|
|
194
|
-
}
|
|
195
|
-
},
|
|
196
|
-
event: async ({ event }) => {
|
|
197
|
-
try {
|
|
198
|
-
if (event.type === "session.created") {
|
|
199
|
-
await logToFile("\uD83D\uDE80 session.created event");
|
|
200
|
-
const eventData = event;
|
|
201
|
-
const sessionId = eventData.properties?.info?.id;
|
|
202
|
-
if (!sessionId) {
|
|
203
|
-
const errorMsg = `\u274C Could not extract session ID from session.created event`;
|
|
204
|
-
await logToFile(errorMsg);
|
|
205
|
-
await logToFile(`Event structure: ${JSON.stringify(eventData)}`);
|
|
206
|
-
throw new Error(errorMsg);
|
|
207
|
-
}
|
|
208
|
-
await logToFile(`\u2705 Extracted session ID: ${sessionId}`);
|
|
209
|
-
await createSessionState(sessionId);
|
|
210
|
-
const state = getSessionState(sessionId);
|
|
211
|
-
await logToFile(`\u2705 Session state created for session ${sessionId} with role: ${state.role}`);
|
|
212
|
-
}
|
|
213
|
-
} catch (error) {
|
|
214
|
-
await logToFile(`\u274C Error in event: ${error}`);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
};
|
|
218
|
-
};
|
|
219
|
-
export {
|
|
220
|
-
opencodeKnowledge
|
|
221
|
-
};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
## Knowledge Context
|
|
2
|
-
|
|
3
|
-
You have access to an engineering knowledge base. Core areas include **{{CORE_TAGS}}** and key packages: **{{CORE_PACKAGES}}**.
|
|
4
|
-
|
|
5
|
-
### Your Task
|
|
6
|
-
{{USER_PROMPT}}
|
|
7
|
-
|
|
8
|
-
Use knowledge_load and knowledge_search tools to access relevant information as needed.
|