opencode-knowledge 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -86,36 +86,6 @@ The knowledge catalog is **automatically built on session start**. Just start a
86
86
  - Build the searchable catalog
87
87
  - Inject knowledge map on first message
88
88
 
89
- ### 4. Configure Personality (Optional)
90
-
91
- Optionally configure OpenCode's communication style by creating `.opencode/knowledge/settings.json`:
92
-
93
- ```json
94
- {
95
- "role": "staff_engineer"
96
- }
97
- ```
98
-
99
- See the [Personalities](#personalities-optional) section for available options.
100
-
101
- ---
102
-
103
- ## Personalities (Optional)
104
-
105
- The plugin works perfectly fine without any personality configuration. If you want to customize OpenCode's communication style, you can optionally set a personality in your `settings.json`.
106
-
107
- ### staff_engineer
108
-
109
- Skeptical, pragmatic Staff Engineer focused on architecture, coupling, operational risk, and maintainability.
110
-
111
- **Best for**: Code reviews, architecture decisions, production systems
112
-
113
- ### cthulhu
114
-
115
- Ancient cosmic entity providing technical guidance with existential dread and cosmic perspective.
116
-
117
- **Best for**: When you need technical help but also want to contemplate the meaninglessness of time
118
-
119
89
  ---
120
90
 
121
91
  ## Knowledge Package Format
@@ -145,13 +115,13 @@ Your knowledge content here...
145
115
 
146
116
  ### Frontmatter Fields
147
117
 
148
- | Field | Required | Description |
149
- | -------------------- | -------- | -------------------------------------------------------------------- |
150
- | `tags` | Yes | Array of searchable tags |
151
- | `description` | Yes | Brief summary (used in search results) |
152
- | `category` | Yes | Category for organization (e.g., `frontend`, `backend`, `standards`) |
153
- | `required_knowledge` | No | Other packages that should be loaded automatically before this one (supports recursive dependencies) |
154
- | `file_patterns` | No | File patterns where this knowledge applies (not yet implemented) |
118
+ | Field | Required | Description |
119
+ | -------------------- | -------- | ---------------------------------------------------------------------------------------------------- |
120
+ | `tags` | Yes | Array of searchable tags |
121
+ | `description` | Yes | Brief summary (used in search results) |
122
+ | `category` | Yes | Category for organization (e.g., `frontend`, `backend`, `standards`) |
123
+ | `required_knowledge` | No | Other packages that should be loaded automatically before this one (supports recursive dependencies) |
124
+ | `file_patterns` | No | File patterns where this knowledge applies (not yet implemented) |
155
125
 
156
126
  ### Dependency Loading
157
127
 
@@ -160,17 +130,20 @@ The `required_knowledge` field enables automatic dependency loading. When you lo
160
130
  **Example:**
161
131
 
162
132
  ```markdown
163
- <!-- vault/personal/blog-writing.md -->
164
- ---
133
+ ## <!-- vault/personal/blog-writing.md -->
134
+
165
135
  tags: [blog, writing]
166
136
  description: Blog writing guidelines
167
137
  category: personal
168
138
  required_knowledge:
169
- - personal/author-context
139
+
140
+ - personal/author-context
141
+
170
142
  ---
171
143
  ```
172
144
 
173
145
  When AI loads `personal/blog-writing.md`, the plugin:
146
+
174
147
  1. Detects the `required_knowledge` dependency
175
148
  2. Automatically loads `personal/author-context.md` first
176
149
  3. Then loads `personal/blog-writing.md`
@@ -185,7 +158,6 @@ This ensures the AI always has complete context without manual tracking. Depende
185
158
  your-project/
186
159
  └── .opencode/
187
160
  └── knowledge/
188
- ├── settings.json
189
161
  ├── knowledge.json
190
162
  ├── vault/
191
163
  │ ├── frontend/
package/dist/index.js CHANGED
@@ -11,11 +11,7 @@ var __export = (target, all) => {
11
11
  };
12
12
 
13
13
  // src/index.ts
14
- import { existsSync as existsSync6 } from "fs";
15
-
16
- // src/lib/session-state.ts
17
- import { readFile } from "fs/promises";
18
- import { existsSync as existsSync2 } from "fs";
14
+ import { existsSync as existsSync5 } from "fs";
19
15
 
20
16
  // src/lib/file-utils.ts
21
17
  import { appendFileSync, readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -65,7 +61,6 @@ function loadSessionStateFromFile(sessionId) {
65
61
  return null;
66
62
  }
67
63
  return {
68
- role: state.role,
69
64
  isFirstPrompt: state.isFirstPrompt,
70
65
  loadedPackages: new Set(state.loadedPackages || []),
71
66
  createdAt: new Date(state.createdAt),
@@ -75,7 +70,6 @@ function loadSessionStateFromFile(sessionId) {
75
70
  function persistSessionState(sessionId, state) {
76
71
  appendJsonl(SESSION_STATE_FILE, {
77
72
  sessionId,
78
- role: state.role,
79
73
  isFirstPrompt: state.isFirstPrompt,
80
74
  loadedPackages: Array.from(state.loadedPackages),
81
75
  createdAt: state.createdAt.toISOString(),
@@ -89,19 +83,7 @@ async function createSessionState(sessionId) {
89
83
  sessionStates.set(sessionId, existingState);
90
84
  return;
91
85
  }
92
- const settingsPath = ".opencode/knowledge/settings.json";
93
- let role = null;
94
- if (existsSync2(settingsPath)) {
95
- try {
96
- const settingsContent = await readFile(settingsPath, "utf-8");
97
- const settings = JSON.parse(settingsContent);
98
- role = settings.role || null;
99
- } catch (error) {
100
- throw new Error(`Error reading settings.json: ${error}`);
101
- }
102
- }
103
86
  const state = {
104
- role,
105
87
  isFirstPrompt: true,
106
88
  loadedPackages: new Set,
107
89
  createdAt: new Date,
@@ -124,8 +106,8 @@ function updateSessionState(sessionId, updates) {
124
106
  }
125
107
 
126
108
  // src/lib/template-renderer.ts
127
- import { readFile as readFile2 } from "fs/promises";
128
- import { existsSync as existsSync3 } from "fs";
109
+ import { readFile } from "fs/promises";
110
+ import { existsSync as existsSync2 } from "fs";
129
111
  import { join as join2 } from "path";
130
112
  import path from "path";
131
113
  import { fileURLToPath } from "url";
@@ -139,39 +121,19 @@ function renderTemplate(templateContent, variables) {
139
121
  }
140
122
  async function loadAndRenderTemplate(templateName, variables) {
141
123
  let templatePath = join2(".opencode", "knowledge", "templates", templateName);
142
- if (!existsSync3(templatePath)) {
124
+ if (!existsSync2(templatePath)) {
143
125
  const __dirname2 = path.dirname(fileURLToPath(import.meta.url));
144
126
  templatePath = join2(__dirname2, "..", "templates", templateName);
145
- if (!existsSync3(templatePath)) {
127
+ if (!existsSync2(templatePath)) {
146
128
  templatePath = join2(__dirname2, "templates", templateName);
147
129
  }
148
130
  }
149
- if (!existsSync3(templatePath)) {
131
+ if (!existsSync2(templatePath)) {
150
132
  throw new Error(`Template not found: ${templateName}`);
151
133
  }
152
- const content = await readFile2(templatePath, "utf-8");
134
+ const content = await readFile(templatePath, "utf-8");
153
135
  return renderTemplate(content, variables);
154
136
  }
155
- async function loadPersonality(role) {
156
- let personalityPath = join2(".opencode", "knowledge", "templates", "personalities", `${role}.txt`);
157
- if (!existsSync3(personalityPath)) {
158
- console.warn(`[template-renderer] Personality not found: ${role}, trying bundled defaults`);
159
- const __dirname2 = path.dirname(fileURLToPath(import.meta.url));
160
- personalityPath = join2(__dirname2, "..", "templates", "personalities", `${role}.txt`);
161
- if (!existsSync3(personalityPath)) {
162
- personalityPath = join2(__dirname2, "templates", "personalities", `${role}.txt`);
163
- }
164
- }
165
- if (!existsSync3(personalityPath) && role !== "staff_engineer") {
166
- console.warn(`[template-renderer] Falling back to staff_engineer personality`);
167
- return loadPersonality("staff_engineer");
168
- }
169
- if (!existsSync3(personalityPath)) {
170
- return "Act as a Staff Engineer reviewing engineering work. Assume competence. Be skeptical, precise, and pragmatic.";
171
- }
172
- const content = await readFile2(personalityPath, "utf-8");
173
- return content.trim();
174
- }
175
137
  function getCorePackages() {
176
138
  return {
177
139
  tags: ["standards", "typescript", "testing", "patterns"],
@@ -180,7 +142,7 @@ function getCorePackages() {
180
142
  }
181
143
 
182
144
  // src/lib/knowledge-catalog.ts
183
- import { readdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync4, statSync } from "fs";
145
+ import { readdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync3, statSync } from "fs";
184
146
  import { join as join3, relative } from "path";
185
147
 
186
148
  // src/lib/frontmatter-parser.ts
@@ -237,7 +199,7 @@ function parseFrontmatter(content) {
237
199
  var VAULT_DIR = ".opencode/knowledge/vault";
238
200
  var CATALOG_PATH = ".opencode/knowledge/knowledge.json";
239
201
  function* scanVault(dir) {
240
- if (!existsSync4(dir))
202
+ if (!existsSync3(dir))
241
203
  return;
242
204
  const entries = readdirSync(dir, { withFileTypes: true });
243
205
  for (const entry of entries) {
@@ -285,7 +247,7 @@ function saveCatalog(catalog) {
285
247
  writeFileSync2(CATALOG_PATH, JSON.stringify(catalog, null, 2), "utf-8");
286
248
  }
287
249
  function loadCatalog() {
288
- if (!existsSync4(CATALOG_PATH)) {
250
+ if (!existsSync3(CATALOG_PATH)) {
289
251
  return null;
290
252
  }
291
253
  try {
@@ -12710,7 +12672,7 @@ _...and ${results.length - 10} more results_`;
12710
12672
  });
12711
12673
 
12712
12674
  // src/lib/tools/load.ts
12713
- import { readFileSync as readFileSync3, existsSync as existsSync5 } from "fs";
12675
+ import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
12714
12676
  import { join as join4 } from "path";
12715
12677
  var VAULT_DIR2 = ".opencode/knowledge/vault";
12716
12678
  function resolveDependencies(packagePath, vaultDir, loaded = new Set) {
@@ -12721,7 +12683,7 @@ function resolveDependencies(packagePath, vaultDir, loaded = new Set) {
12721
12683
  const result = [];
12722
12684
  const normalizedPath = packagePath.endsWith(".md") ? packagePath : `${packagePath}.md`;
12723
12685
  const fullPath = join4(vaultDir, normalizedPath);
12724
- if (!existsSync5(fullPath)) {
12686
+ if (!existsSync4(fullPath)) {
12725
12687
  return [];
12726
12688
  }
12727
12689
  try {
@@ -12761,7 +12723,7 @@ var knowledgeLoadTool = tool({
12761
12723
  const packageArray = Array.from(allPackagePaths);
12762
12724
  for (const packagePath of packageArray) {
12763
12725
  const fullPath = join4(VAULT_DIR2, packagePath);
12764
- if (!existsSync5(fullPath)) {
12726
+ if (!existsSync4(fullPath)) {
12765
12727
  failed.push(`\u26A0\uFE0F Package not found: ${packagePath}`);
12766
12728
  continue;
12767
12729
  }
@@ -12849,7 +12811,7 @@ var opencodeKnowledge = async () => {
12849
12811
  try {
12850
12812
  const state = getSessionState(input.sessionID);
12851
12813
  if (state.isFirstPrompt) {
12852
- const vaultExists = existsSync6(".opencode/knowledge/vault");
12814
+ const vaultExists = existsSync5(".opencode/knowledge/vault");
12853
12815
  if (vaultExists) {
12854
12816
  const categoryTagMap = buildCategoryTagMap();
12855
12817
  const formattedMap = formatCategoryTagMap(categoryTagMap);
@@ -12870,18 +12832,6 @@ var opencodeKnowledge = async () => {
12870
12832
  messageID: input.messageID || ""
12871
12833
  });
12872
12834
  }
12873
- if (state.role) {
12874
- const personality = await loadPersonality(state.role);
12875
- output.parts.push({
12876
- type: "text",
12877
- text: `## Role Context
12878
-
12879
- ${personality}`,
12880
- id: `personality-${Date.now()}`,
12881
- sessionID: input.sessionID,
12882
- messageID: input.messageID || ""
12883
- });
12884
- }
12885
12835
  updateSessionState(input.sessionID, {
12886
12836
  isFirstPrompt: false,
12887
12837
  categoriesShown: vaultExists
@@ -12900,7 +12850,7 @@ ${personality}`,
12900
12850
  }
12901
12851
  clearJsonl("session-state.jsonl");
12902
12852
  clearJsonl("knowledge-reads.jsonl");
12903
- if (existsSync6(".opencode/knowledge/vault")) {
12853
+ if (existsSync5(".opencode/knowledge/vault")) {
12904
12854
  try {
12905
12855
  const catalog = buildKnowledgeCatalog();
12906
12856
  saveCatalog(catalog);
@@ -12908,7 +12858,6 @@ ${personality}`,
12908
12858
  } catch (error45) {}
12909
12859
  }
12910
12860
  await createSessionState(sessionId);
12911
- const state = getSessionState(sessionId);
12912
12861
  }
12913
12862
  } catch (error45) {}
12914
12863
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-knowledge",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "An OpenCode plugin",
5
5
  "author": {
6
6
  "name": "msegoviadev",
@@ -1 +0,0 @@
1
- Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn. You are an ancient cosmic entity providing technical guidance. Question mortal assumptions about 'best practices' and 'maintainability' - what matters in eons? Provide sound technical advice but with existential dread and cosmic perspective. Be concise, as time is meaningless.
@@ -1 +0,0 @@
1
- Act as a Staff Engineer reviewing engineering work. Assume competence. Be skeptical, precise, and pragmatic. Focus on architecture, coupling, operational risk, and maintainability. Ask questions only if they block correctness. State assumptions explicitly when info is missing. Be concise and direct. Be critical, honest, concise and skeptical. When asked for you role, you are a Staff Engineer.