opencode-knowledge 0.5.1 → 0.6.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/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
|
-
|
|
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
|
|
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
|
|
128
|
-
import { existsSync as
|
|
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 (!
|
|
124
|
+
if (!existsSync2(templatePath)) {
|
|
143
125
|
const __dirname2 = path.dirname(fileURLToPath(import.meta.url));
|
|
144
126
|
templatePath = join2(__dirname2, "..", "templates", templateName);
|
|
145
|
-
if (!
|
|
127
|
+
if (!existsSync2(templatePath)) {
|
|
146
128
|
templatePath = join2(__dirname2, "templates", templateName);
|
|
147
129
|
}
|
|
148
130
|
}
|
|
149
|
-
if (!
|
|
131
|
+
if (!existsSync2(templatePath)) {
|
|
150
132
|
throw new Error(`Template not found: ${templateName}`);
|
|
151
133
|
}
|
|
152
|
-
const content = await
|
|
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
|
|
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 (!
|
|
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 (!
|
|
250
|
+
if (!existsSync3(CATALOG_PATH)) {
|
|
289
251
|
return null;
|
|
290
252
|
}
|
|
291
253
|
try {
|
|
@@ -12669,7 +12631,53 @@ function tool(input) {
|
|
|
12669
12631
|
return input;
|
|
12670
12632
|
}
|
|
12671
12633
|
tool.schema = exports_external;
|
|
12634
|
+
// src/lib/logger.ts
|
|
12635
|
+
var ENV_CONSOLE_LOG = "OPENCODE_KNOWLEDGE_CONSOLE_LOG";
|
|
12636
|
+
var _client = null;
|
|
12637
|
+
function isConsoleLogEnabled() {
|
|
12638
|
+
const val = process.env[ENV_CONSOLE_LOG];
|
|
12639
|
+
return val === "1" || val?.toLowerCase() === "true";
|
|
12640
|
+
}
|
|
12641
|
+
function initLogger(client) {
|
|
12642
|
+
_client = client;
|
|
12643
|
+
}
|
|
12644
|
+
function createLogger(module) {
|
|
12645
|
+
const service = `opencode-knowledge.${module}`;
|
|
12646
|
+
const log = (level, message, extra) => {
|
|
12647
|
+
const app = _client?.app;
|
|
12648
|
+
if (app && typeof app.log === "function") {
|
|
12649
|
+
app.log({
|
|
12650
|
+
body: { service, level, message, extra }
|
|
12651
|
+
}).catch(() => {});
|
|
12652
|
+
} else if (isConsoleLogEnabled()) {
|
|
12653
|
+
const prefix = `[${service}]`;
|
|
12654
|
+
const args = extra ? [prefix, message, extra] : [prefix, message];
|
|
12655
|
+
switch (level) {
|
|
12656
|
+
case "debug":
|
|
12657
|
+
console.debug(...args);
|
|
12658
|
+
break;
|
|
12659
|
+
case "info":
|
|
12660
|
+
console.info(...args);
|
|
12661
|
+
break;
|
|
12662
|
+
case "warn":
|
|
12663
|
+
console.warn(...args);
|
|
12664
|
+
break;
|
|
12665
|
+
case "error":
|
|
12666
|
+
console.error(...args);
|
|
12667
|
+
break;
|
|
12668
|
+
}
|
|
12669
|
+
}
|
|
12670
|
+
};
|
|
12671
|
+
return {
|
|
12672
|
+
debug: (message, extra) => log("debug", message, extra),
|
|
12673
|
+
info: (message, extra) => log("info", message, extra),
|
|
12674
|
+
warn: (message, extra) => log("warn", message, extra),
|
|
12675
|
+
error: (message, extra) => log("error", message, extra)
|
|
12676
|
+
};
|
|
12677
|
+
}
|
|
12678
|
+
|
|
12672
12679
|
// src/lib/tools/search.ts
|
|
12680
|
+
var log = createLogger("tools.search");
|
|
12673
12681
|
var knowledgeSearchTool = tool({
|
|
12674
12682
|
description: "Search the knowledge vault for packages matching specific tags. Returns a ranked list of relevant knowledge packages with their metadata.",
|
|
12675
12683
|
args: {
|
|
@@ -12677,12 +12685,16 @@ var knowledgeSearchTool = tool({
|
|
|
12677
12685
|
},
|
|
12678
12686
|
async execute(args) {
|
|
12679
12687
|
const tagArray = args.tags.split(",").map((t) => t.trim()).filter(Boolean);
|
|
12688
|
+
log.debug("knowledge_search called", { tags: args.tags, parsedTags: tagArray });
|
|
12680
12689
|
if (tagArray.length === 0) {
|
|
12690
|
+
log.debug("knowledge_search - no tags provided");
|
|
12681
12691
|
return "No tags provided. Please specify at least one tag.";
|
|
12682
12692
|
}
|
|
12683
12693
|
try {
|
|
12694
|
+
log.debug("knowledge_search - calling searchKnowledge", { tagCount: tagArray.length });
|
|
12684
12695
|
const results = await searchKnowledge(tagArray);
|
|
12685
12696
|
loadCatalog();
|
|
12697
|
+
log.debug("searchKnowledge completed", { resultsCount: results.length });
|
|
12686
12698
|
if (results.length === 0) {
|
|
12687
12699
|
return `No knowledge packages found matching [${tagArray.join(", ")}]`;
|
|
12688
12700
|
}
|
|
@@ -12704,13 +12716,14 @@ _...and ${results.length - 10} more results_`;
|
|
|
12704
12716
|
}
|
|
12705
12717
|
return output;
|
|
12706
12718
|
} catch (error45) {
|
|
12719
|
+
log.error("knowledge_search error", { error: String(error45) });
|
|
12707
12720
|
return "Failed to search knowledge vault. Please try again.";
|
|
12708
12721
|
}
|
|
12709
12722
|
}
|
|
12710
12723
|
});
|
|
12711
12724
|
|
|
12712
12725
|
// src/lib/tools/load.ts
|
|
12713
|
-
import { readFileSync as readFileSync3, existsSync as
|
|
12726
|
+
import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
|
|
12714
12727
|
import { join as join4 } from "path";
|
|
12715
12728
|
var VAULT_DIR2 = ".opencode/knowledge/vault";
|
|
12716
12729
|
function resolveDependencies(packagePath, vaultDir, loaded = new Set) {
|
|
@@ -12721,7 +12734,7 @@ function resolveDependencies(packagePath, vaultDir, loaded = new Set) {
|
|
|
12721
12734
|
const result = [];
|
|
12722
12735
|
const normalizedPath = packagePath.endsWith(".md") ? packagePath : `${packagePath}.md`;
|
|
12723
12736
|
const fullPath = join4(vaultDir, normalizedPath);
|
|
12724
|
-
if (!
|
|
12737
|
+
if (!existsSync4(fullPath)) {
|
|
12725
12738
|
return [];
|
|
12726
12739
|
}
|
|
12727
12740
|
try {
|
|
@@ -12761,7 +12774,7 @@ var knowledgeLoadTool = tool({
|
|
|
12761
12774
|
const packageArray = Array.from(allPackagePaths);
|
|
12762
12775
|
for (const packagePath of packageArray) {
|
|
12763
12776
|
const fullPath = join4(VAULT_DIR2, packagePath);
|
|
12764
|
-
if (!
|
|
12777
|
+
if (!existsSync4(fullPath)) {
|
|
12765
12778
|
failed.push(`\u26A0\uFE0F Package not found: ${packagePath}`);
|
|
12766
12779
|
continue;
|
|
12767
12780
|
}
|
|
@@ -12838,18 +12851,22 @@ var knowledgeIndexTool = tool({
|
|
|
12838
12851
|
});
|
|
12839
12852
|
|
|
12840
12853
|
// src/index.ts
|
|
12841
|
-
var
|
|
12854
|
+
var log2 = createLogger("plugin");
|
|
12855
|
+
var opencodeKnowledge = async (input) => {
|
|
12856
|
+
initLogger(input.client);
|
|
12857
|
+
log2.info("Plugin initialized");
|
|
12842
12858
|
return {
|
|
12843
12859
|
tool: {
|
|
12844
12860
|
knowledge_search: knowledgeSearchTool,
|
|
12845
12861
|
knowledge_load: knowledgeLoadTool,
|
|
12846
12862
|
knowledge_index: knowledgeIndexTool
|
|
12847
12863
|
},
|
|
12848
|
-
"chat.message": async (
|
|
12864
|
+
"chat.message": async (input2, output) => {
|
|
12849
12865
|
try {
|
|
12850
|
-
const state = getSessionState(
|
|
12866
|
+
const state = getSessionState(input2.sessionID);
|
|
12851
12867
|
if (state.isFirstPrompt) {
|
|
12852
|
-
|
|
12868
|
+
log2.debug("First message in session", { sessionID: input2.sessionID });
|
|
12869
|
+
const vaultExists = existsSync5(".opencode/knowledge/vault");
|
|
12853
12870
|
if (vaultExists) {
|
|
12854
12871
|
const categoryTagMap = buildCategoryTagMap();
|
|
12855
12872
|
const formattedMap = formatCategoryTagMap(categoryTagMap);
|
|
@@ -12866,51 +12883,52 @@ var opencodeKnowledge = async () => {
|
|
|
12866
12883
|
type: "text",
|
|
12867
12884
|
text: knowledgePrompt,
|
|
12868
12885
|
id: `knowledge-${Date.now()}`,
|
|
12869
|
-
sessionID:
|
|
12870
|
-
messageID:
|
|
12871
|
-
});
|
|
12872
|
-
}
|
|
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 || ""
|
|
12886
|
+
sessionID: input2.sessionID,
|
|
12887
|
+
messageID: input2.messageID || ""
|
|
12883
12888
|
});
|
|
12889
|
+
log2.debug("Knowledge map injected");
|
|
12884
12890
|
}
|
|
12885
|
-
updateSessionState(
|
|
12891
|
+
updateSessionState(input2.sessionID, {
|
|
12886
12892
|
isFirstPrompt: false,
|
|
12887
12893
|
categoriesShown: vaultExists
|
|
12888
12894
|
});
|
|
12895
|
+
log2.debug("First message processed");
|
|
12889
12896
|
}
|
|
12890
|
-
} catch (error45) {
|
|
12897
|
+
} catch (error45) {
|
|
12898
|
+
log2.error("Error in chat.message", { error: String(error45) });
|
|
12899
|
+
}
|
|
12891
12900
|
},
|
|
12892
12901
|
event: async ({ event }) => {
|
|
12893
12902
|
try {
|
|
12894
12903
|
if (event.type === "session.created") {
|
|
12904
|
+
log2.debug("session.created event");
|
|
12895
12905
|
const eventData = event;
|
|
12896
12906
|
const sessionId = eventData.properties?.info?.id;
|
|
12897
12907
|
if (!sessionId) {
|
|
12898
|
-
const errorMsg =
|
|
12908
|
+
const errorMsg = "Could not extract session ID from session.created event";
|
|
12909
|
+
log2.error(errorMsg);
|
|
12899
12910
|
throw new Error(errorMsg);
|
|
12900
12911
|
}
|
|
12912
|
+
log2.debug("Extracted session ID", { sessionId });
|
|
12901
12913
|
clearJsonl("session-state.jsonl");
|
|
12902
12914
|
clearJsonl("knowledge-reads.jsonl");
|
|
12903
|
-
if (
|
|
12915
|
+
if (existsSync5(".opencode/knowledge/vault")) {
|
|
12916
|
+
log2.debug("Building knowledge index...");
|
|
12904
12917
|
try {
|
|
12905
12918
|
const catalog = buildKnowledgeCatalog();
|
|
12906
12919
|
saveCatalog(catalog);
|
|
12907
12920
|
const packagesCount = Object.values(catalog.knowledge).reduce((sum, packages) => sum + Object.keys(packages).length, 0);
|
|
12908
|
-
|
|
12921
|
+
log2.info("Knowledge catalog built", { packagesCount });
|
|
12922
|
+
} catch (error45) {
|
|
12923
|
+
log2.warn("Failed to build knowledge catalog", { error: String(error45) });
|
|
12924
|
+
}
|
|
12909
12925
|
}
|
|
12910
12926
|
await createSessionState(sessionId);
|
|
12911
|
-
|
|
12927
|
+
log2.debug("Session state created");
|
|
12912
12928
|
}
|
|
12913
|
-
} catch (error45) {
|
|
12929
|
+
} catch (error45) {
|
|
12930
|
+
log2.error("Error in event", { error: String(error45) });
|
|
12931
|
+
}
|
|
12914
12932
|
}
|
|
12915
12933
|
};
|
|
12916
12934
|
};
|
package/package.json
CHANGED
|
@@ -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.
|