nia-opencode 0.1.0 → 0.1.2
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/dist/cli.js +59 -0
- package/dist/index.js +52 -13
- package/dist/keywords.d.ts +6 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,28 @@ import { homedir } from "node:os";
|
|
|
7
7
|
import * as readline from "node:readline";
|
|
8
8
|
var OPENCODE_CONFIG_DIR = join(homedir(), ".config", "opencode");
|
|
9
9
|
var NIA_CONFIG_PATH = join(OPENCODE_CONFIG_DIR, "nia.json");
|
|
10
|
+
var AGENTS_MD_PATH = join(OPENCODE_CONFIG_DIR, "AGENTS.md");
|
|
10
11
|
var PLUGIN_NAME = "nia-opencode@latest";
|
|
12
|
+
var NIA_AGENTS_INSTRUCTIONS = `
|
|
13
|
+
# How to use Nia
|
|
14
|
+
|
|
15
|
+
Nia provides tools for indexing and searching external repositories, research papers, local folders, documentation, packages, and performing AI-powered research. Its primary goal is to reduce hallucinations in LLMs and provide up-to-date context for AI agents.
|
|
16
|
+
|
|
17
|
+
## Deterministic Workflow
|
|
18
|
+
|
|
19
|
+
1. Check if the source is already indexed using manage_resource (when listing sources, use targeted query to save tokens since users can have multiple sources indexed) or check any nia.md files for already indexed sources.
|
|
20
|
+
2. If it is indexed, check the tree of the source or ls relevant directories.
|
|
21
|
+
3. After getting the grasp of the structure (tree), use 'search', 'nia_grep', 'nia_read' for targeted searches.
|
|
22
|
+
4. If helpful, use the context tool to save your research findings to make them reusable for future conversations.
|
|
23
|
+
5. Save your findings in an .md file to track: source indexed, used, its ID, and link so you won't have to list sources in the future and can get straight to work.
|
|
24
|
+
|
|
25
|
+
## Notes
|
|
26
|
+
|
|
27
|
+
- DO NOT USE WEB RESEARCH TOOLS IF INFORMATION IS INDEXED IN NIA BY USING 'manage_resource' tool.
|
|
28
|
+
- If the source isn't indexed, index it. Note that for docs you should always index the root link like docs.stripe.com so it will always scrape all pages.
|
|
29
|
+
- If you need to index something but don't know the link for that source, use nia_research (quick or deep modes).
|
|
30
|
+
- Once you use the index tool, do not expect it to finish in 1-3 seconds. Stop your work or do something that will make your work pause for 1-5 minutes until the source is indexed, then run manage_resource again to check its status. You can also prompt the user to wait if needed.
|
|
31
|
+
`;
|
|
11
32
|
function stripJsoncComments(content) {
|
|
12
33
|
return content.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "");
|
|
13
34
|
}
|
|
@@ -155,6 +176,32 @@ function createNiaConfig(apiKey) {
|
|
|
155
176
|
console.log(` Created ${NIA_CONFIG_PATH}`);
|
|
156
177
|
return true;
|
|
157
178
|
}
|
|
179
|
+
function updateAgentsMd() {
|
|
180
|
+
mkdirSync(OPENCODE_CONFIG_DIR, { recursive: true });
|
|
181
|
+
try {
|
|
182
|
+
if (existsSync(AGENTS_MD_PATH)) {
|
|
183
|
+
const content = readFileSync(AGENTS_MD_PATH, "utf-8");
|
|
184
|
+
if (content.includes("# How to use Nia")) {
|
|
185
|
+
console.log(" Nia instructions already in AGENTS.md");
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
const newContent = content.trimEnd() + `
|
|
189
|
+
|
|
190
|
+
` + NIA_AGENTS_INSTRUCTIONS.trim() + `
|
|
191
|
+
`;
|
|
192
|
+
writeFileSync(AGENTS_MD_PATH, newContent);
|
|
193
|
+
console.log(" Appended Nia instructions to AGENTS.md");
|
|
194
|
+
} else {
|
|
195
|
+
writeFileSync(AGENTS_MD_PATH, NIA_AGENTS_INSTRUCTIONS.trim() + `
|
|
196
|
+
`);
|
|
197
|
+
console.log(` Created ${AGENTS_MD_PATH} with Nia instructions`);
|
|
198
|
+
}
|
|
199
|
+
return true;
|
|
200
|
+
} catch (err) {
|
|
201
|
+
console.error(" Failed to update AGENTS.md:", err);
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
158
205
|
async function install(options) {
|
|
159
206
|
console.log(`
|
|
160
207
|
Nia OpenCode Plugin Installer
|
|
@@ -216,6 +263,18 @@ Step 3: Configure OpenCode`);
|
|
|
216
263
|
}
|
|
217
264
|
}
|
|
218
265
|
console.log(`
|
|
266
|
+
Step 4: Add Nia Instructions to AGENTS.md`);
|
|
267
|
+
if (options.tui && rl) {
|
|
268
|
+
const shouldUpdate = await confirm(rl, "Add Nia usage instructions to ~/.config/opencode/AGENTS.md?");
|
|
269
|
+
if (shouldUpdate) {
|
|
270
|
+
updateAgentsMd();
|
|
271
|
+
} else {
|
|
272
|
+
console.log(" Skipped.");
|
|
273
|
+
}
|
|
274
|
+
} else {
|
|
275
|
+
updateAgentsMd();
|
|
276
|
+
}
|
|
277
|
+
console.log(`
|
|
219
278
|
` + "-".repeat(50));
|
|
220
279
|
console.log(`
|
|
221
280
|
Setup Complete!
|
package/dist/index.js
CHANGED
|
@@ -46,7 +46,7 @@ function isConfigured() {
|
|
|
46
46
|
// src/keywords.ts
|
|
47
47
|
var CODE_BLOCK_PATTERN = /```[\s\S]*?```/g;
|
|
48
48
|
var INLINE_CODE_PATTERN = /`[^`]+`/g;
|
|
49
|
-
var
|
|
49
|
+
var RESEARCH_PATTERNS = [
|
|
50
50
|
/\b(research|look\s*up|find\s*docs?)\b/i,
|
|
51
51
|
/\b(search\s+for|search\s+codebase|search\s+repo|search\s+docs?)\b/i,
|
|
52
52
|
/\b(grep\s+for|grep\s+in)\b/i,
|
|
@@ -55,6 +55,14 @@ var DEFAULT_PATTERNS = [
|
|
|
55
55
|
/\bcheck\s+(the\s+)?(docs?|documentation)\s+(for|about|on)\b/i,
|
|
56
56
|
/\bfind\s+(examples?|usage)\s+(of|for)\b/i
|
|
57
57
|
];
|
|
58
|
+
var SAVE_PATTERNS = [
|
|
59
|
+
/\b(save\s+(this\s+)?(context|conversation|session|chat))\b/i,
|
|
60
|
+
/\b(continue\s+(this\s+)?(later|tomorrow|in\s+\w+))\b/i,
|
|
61
|
+
/\b(pick\s+(this\s+)?up\s+(later|tomorrow|in\s+\w+))\b/i,
|
|
62
|
+
/\b(hand\s*off|switch(ing)?\s+to)\s+(cursor|claude|windsurf|copilot|another\s+agent)\b/i,
|
|
63
|
+
/\b(save\s+for\s+later|bookmark\s+this)\b/i,
|
|
64
|
+
/\b(preserve|store)\s+(this\s+)?(context|conversation|session)\b/i
|
|
65
|
+
];
|
|
58
66
|
function removeCodeBlocks(text) {
|
|
59
67
|
return text.replace(CODE_BLOCK_PATTERN, "").replace(INLINE_CODE_PATTERN, "");
|
|
60
68
|
}
|
|
@@ -67,19 +75,25 @@ function compileCustomPatterns() {
|
|
|
67
75
|
}
|
|
68
76
|
}).filter((p) => p !== null);
|
|
69
77
|
}
|
|
70
|
-
function
|
|
78
|
+
function detectKeyword(text) {
|
|
71
79
|
if (!CONFIG.keywords.enabled) {
|
|
72
|
-
return {
|
|
80
|
+
return { type: null };
|
|
73
81
|
}
|
|
74
82
|
const textWithoutCode = removeCodeBlocks(text);
|
|
75
|
-
const
|
|
76
|
-
|
|
83
|
+
for (const pattern of SAVE_PATTERNS) {
|
|
84
|
+
const match = textWithoutCode.match(pattern);
|
|
85
|
+
if (match) {
|
|
86
|
+
return { type: "save", match: match[0] };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const researchPatterns = [...RESEARCH_PATTERNS, ...compileCustomPatterns()];
|
|
90
|
+
for (const pattern of researchPatterns) {
|
|
77
91
|
const match = textWithoutCode.match(pattern);
|
|
78
92
|
if (match) {
|
|
79
|
-
return {
|
|
93
|
+
return { type: "research", match: match[0] };
|
|
80
94
|
}
|
|
81
95
|
}
|
|
82
|
-
return {
|
|
96
|
+
return { type: null };
|
|
83
97
|
}
|
|
84
98
|
var NIA_NUDGE_MESSAGE = `[NIA KNOWLEDGE TRIGGER]
|
|
85
99
|
The user is asking for research, documentation, or codebase exploration. You have access to **Nia** tools via MCP.
|
|
@@ -99,6 +113,30 @@ The user is asking for research, documentation, or codebase exploration. You hav
|
|
|
99
113
|
3. Read specific files if needed for deeper context
|
|
100
114
|
|
|
101
115
|
Use these tools to provide accurate, up-to-date information instead of relying solely on training data.`;
|
|
116
|
+
var NIA_SAVE_NUDGE_MESSAGE = `[NIA CONTEXT SAVE TRIGGER]
|
|
117
|
+
The user wants to save this conversation to continue later or hand off to another agent.
|
|
118
|
+
|
|
119
|
+
**Use \`nia.context\` to save:**
|
|
120
|
+
\`\`\`
|
|
121
|
+
nia.context({
|
|
122
|
+
action: "save",
|
|
123
|
+
title: "Brief title describing this session",
|
|
124
|
+
summary: "What was accomplished and what's pending",
|
|
125
|
+
content: "Key decisions, code snippets, and important context",
|
|
126
|
+
tags: ["relevant", "tags"],
|
|
127
|
+
edited_files: [{ path: "file/path.ts", action: "modified" }]
|
|
128
|
+
})
|
|
129
|
+
\`\`\`
|
|
130
|
+
|
|
131
|
+
**What to include:**
|
|
132
|
+
- Summary of what was discussed/accomplished
|
|
133
|
+
- Key decisions made
|
|
134
|
+
- Code snippets or plans created
|
|
135
|
+
- Files that were edited
|
|
136
|
+
- Next steps or pending tasks
|
|
137
|
+
- Any Nia sources that were referenced
|
|
138
|
+
|
|
139
|
+
This context can be loaded in Cursor, Claude Code, Windsurf, or any agent with Nia access.`;
|
|
102
140
|
|
|
103
141
|
// src/services/logger.ts
|
|
104
142
|
var DEBUG = process.env.NIA_DEBUG === "true" || process.env.NIA_DEBUG === "1";
|
|
@@ -143,20 +181,21 @@ var NiaPlugin = async (ctx) => {
|
|
|
143
181
|
messagePreview: userMessage.slice(0, 100),
|
|
144
182
|
partsCount: output.parts.length
|
|
145
183
|
});
|
|
146
|
-
const {
|
|
147
|
-
if (
|
|
148
|
-
|
|
184
|
+
const { type, match } = detectKeyword(userMessage);
|
|
185
|
+
if (type) {
|
|
186
|
+
const nudgeText = type === "save" ? NIA_SAVE_NUDGE_MESSAGE : NIA_NUDGE_MESSAGE;
|
|
187
|
+
log(`chat.message: ${type} keyword detected`, { match });
|
|
149
188
|
const nudgePart = {
|
|
150
|
-
id: `nia-nudge-${Date.now()}`,
|
|
189
|
+
id: `nia-${type}-nudge-${Date.now()}`,
|
|
151
190
|
sessionID: input.sessionID,
|
|
152
191
|
messageID: output.message.id,
|
|
153
192
|
type: "text",
|
|
154
|
-
text:
|
|
193
|
+
text: nudgeText,
|
|
155
194
|
synthetic: true
|
|
156
195
|
};
|
|
157
196
|
output.parts.push(nudgePart);
|
|
158
197
|
const duration = Date.now() - start;
|
|
159
|
-
log(
|
|
198
|
+
log(`chat.message: ${type} nudge injected`, { duration, match });
|
|
160
199
|
}
|
|
161
200
|
} catch (error) {
|
|
162
201
|
log("chat.message: ERROR", { error: String(error) });
|
package/dist/keywords.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
export type KeywordType = "research" | "save" | null;
|
|
2
|
+
export declare function detectKeyword(text: string): {
|
|
3
|
+
type: KeywordType;
|
|
4
|
+
match?: string;
|
|
5
|
+
};
|
|
1
6
|
export declare function detectResearchKeyword(text: string): {
|
|
2
7
|
detected: boolean;
|
|
3
8
|
match?: string;
|
|
4
9
|
};
|
|
5
10
|
export declare const NIA_NUDGE_MESSAGE = "[NIA KNOWLEDGE TRIGGER]\nThe user is asking for research, documentation, or codebase exploration. You have access to **Nia** tools via MCP.\n\n**Available Nia tools:**\n- `nia.search` - Semantic search across indexed repos, docs, and papers\n- `nia.nia_research` - Web search (quick) or deep AI research (deep/oracle modes)\n- `nia.index` - Index new GitHub repos, documentation sites, or arXiv papers\n- `nia.nia_read` - Read specific files from indexed sources\n- `nia.nia_grep` - Regex search across indexed codebases\n- `nia.nia_explore` - Browse file trees of indexed repos/docs\n- `nia.manage_resource` - List/check status of indexed sources\n\n**Workflow:**\n1. Check what's indexed: `nia.manage_resource(action: \"list\")`\n2. Search or research based on the user's question\n3. Read specific files if needed for deeper context\n\nUse these tools to provide accurate, up-to-date information instead of relying solely on training data.";
|
|
11
|
+
export declare const NIA_SAVE_NUDGE_MESSAGE = "[NIA CONTEXT SAVE TRIGGER]\nThe user wants to save this conversation to continue later or hand off to another agent.\n\n**Use `nia.context` to save:**\n```\nnia.context({\n action: \"save\",\n title: \"Brief title describing this session\",\n summary: \"What was accomplished and what's pending\",\n content: \"Key decisions, code snippets, and important context\",\n tags: [\"relevant\", \"tags\"],\n edited_files: [{ path: \"file/path.ts\", action: \"modified\" }]\n})\n```\n\n**What to include:**\n- Summary of what was discussed/accomplished\n- Key decisions made\n- Code snippets or plans created\n- Files that were edited\n- Next steps or pending tasks\n- Any Nia sources that were referenced\n\nThis context can be loaded in Cursor, Claude Code, Windsurf, or any agent with Nia access.";
|