netheriteai-code 0.2.5 → 0.3.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/package.json +1 -1
- package/src/cli.js +73 -0
- package/src/ollama.js +55 -84
- package/src/tools.js +15 -144
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -338,6 +338,79 @@ async function runChatSession({ workspaceRoot, model, useTui, session }) {
|
|
|
338
338
|
}
|
|
339
339
|
}
|
|
340
340
|
|
|
341
|
+
export async function main() {
|
|
342
|
+
await handleAutoUpdate();
|
|
343
|
+
const { positional, flags } = parseArgs(process.argv.slice(2));
|
|
344
|
+
const command = positional[0] || "tui";
|
|
345
|
+
const requestedWorkspaceRoot = resolveWorkspaceRoot(flags.dir || flags.d || process.cwd());
|
|
346
|
+
|
|
347
|
+
if (!fs.existsSync(requestedWorkspaceRoot)) {
|
|
348
|
+
throw new Error(`Workspace does not exist: ${requestedWorkspaceRoot}`);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (command === "help" || flags.help || flags.h) {
|
|
352
|
+
printHelp();
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (command === "tools") {
|
|
357
|
+
printToolList();
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
let session = null;
|
|
362
|
+
let workspaceRoot = requestedWorkspaceRoot;
|
|
363
|
+
|
|
364
|
+
if (command === "session") {
|
|
365
|
+
const sessionId = positional[1];
|
|
366
|
+
if (!sessionId) {
|
|
367
|
+
throw new Error("Usage: netheritecode session <id>");
|
|
368
|
+
}
|
|
369
|
+
session = loadSessionById(sessionId);
|
|
370
|
+
if (!session) {
|
|
371
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
372
|
+
}
|
|
373
|
+
workspaceRoot = session.workspaceRoot;
|
|
374
|
+
} else {
|
|
375
|
+
session = createSession(workspaceRoot);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const model = await resolveModel(flags.model || flags.m);
|
|
379
|
+
setSelectedModel(model);
|
|
380
|
+
|
|
381
|
+
if (command === "chat") {
|
|
382
|
+
await runChatSession({ workspaceRoot, model, useTui: false, session });
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (command === "tui" || command === "session") {
|
|
387
|
+
await runChatSession({ workspaceRoot, model, useTui: true, session });
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (!command.startsWith("-")) {
|
|
392
|
+
const prompt = positional.join(" ");
|
|
393
|
+
const result = await runAgentTurn({
|
|
394
|
+
workspaceRoot,
|
|
395
|
+
model,
|
|
396
|
+
history: session.messages,
|
|
397
|
+
userPrompt: prompt,
|
|
398
|
+
});
|
|
399
|
+
session.messages = result.messages;
|
|
400
|
+
saveSession(session);
|
|
401
|
+
console.log(result.reply);
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
printHelp();
|
|
406
|
+
}
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
await submit(prompt);
|
|
410
|
+
rl.prompt();
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
341
414
|
export async function main() {
|
|
342
415
|
const { positional, flags } = parseArgs(process.argv.slice(2));
|
|
343
416
|
const command = positional[0] || "tui";
|
package/src/ollama.js
CHANGED
|
@@ -7,16 +7,6 @@ const PREFERRED_DEFAULT_MODELS = [
|
|
|
7
7
|
"glm-5:cloud",
|
|
8
8
|
];
|
|
9
9
|
|
|
10
|
-
function getThinkMode(model) {
|
|
11
|
-
const override = process.env.NETHERITEAI_THINK_MODE;
|
|
12
|
-
if (override === "true") return true;
|
|
13
|
-
if (override === "false") return false;
|
|
14
|
-
if (override === "medium" || override === "low" || override === "high") return override;
|
|
15
|
-
const name = String(model || "").toLowerCase();
|
|
16
|
-
if (name.includes("gpt-oss")) return "medium";
|
|
17
|
-
return true;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
10
|
async function request(pathname, body, signal) {
|
|
21
11
|
try {
|
|
22
12
|
const response = await fetch(`${BASE_URL}${pathname}`, {
|
|
@@ -31,7 +21,8 @@ async function request(pathname, body, signal) {
|
|
|
31
21
|
}
|
|
32
22
|
|
|
33
23
|
return response;
|
|
34
|
-
} catch {
|
|
24
|
+
} catch (err) {
|
|
25
|
+
if (err.name === "AbortError") throw err;
|
|
35
26
|
throw new Error("Server down");
|
|
36
27
|
}
|
|
37
28
|
}
|
|
@@ -64,26 +55,16 @@ export async function pickDefaultModel() {
|
|
|
64
55
|
const preferredMatch = models.find((model) =>
|
|
65
56
|
PREFERRED_DEFAULT_MODELS.some((preferred) => preferred.toLowerCase() === model.name.toLowerCase()),
|
|
66
57
|
);
|
|
67
|
-
|
|
68
|
-
return preferredMatch.name;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const familyMatch = models.find((model) => model.name.toLowerCase().startsWith("netheriteai"));
|
|
72
|
-
return familyMatch?.name || models[0].name;
|
|
58
|
+
return preferredMatch ? preferredMatch.name : models[0].name;
|
|
73
59
|
} catch {
|
|
74
60
|
throw new Error("Server down");
|
|
75
61
|
}
|
|
76
62
|
}
|
|
77
63
|
|
|
78
64
|
export async function chat({ model, messages, tools, signal }) {
|
|
79
|
-
const body = {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
stream: false,
|
|
83
|
-
};
|
|
84
|
-
if (tools && tools.length) {
|
|
85
|
-
body.tools = tools;
|
|
86
|
-
}
|
|
65
|
+
const body = { model, messages, stream: false };
|
|
66
|
+
if (tools && tools.length) body.tools = tools;
|
|
67
|
+
|
|
87
68
|
const response = await request("/api/chat", body, signal);
|
|
88
69
|
const json = await response.json();
|
|
89
70
|
return {
|
|
@@ -98,13 +79,8 @@ function createTaggedStreamParser({ onContent, onReasoning }) {
|
|
|
98
79
|
const openTag = "<think>";
|
|
99
80
|
const closeTag = "</think>";
|
|
100
81
|
|
|
101
|
-
function emitVisible(text) {
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function emitReasoning(text) {
|
|
106
|
-
if (text) onReasoning?.(text);
|
|
107
|
-
}
|
|
82
|
+
function emitVisible(text) { if (text) onContent?.(text); }
|
|
83
|
+
function emitReasoning(text) { if (text) onReasoning?.(text); }
|
|
108
84
|
|
|
109
85
|
function flush(final = false) {
|
|
110
86
|
while (buffer.length) {
|
|
@@ -149,34 +125,23 @@ function createTaggedStreamParser({ onContent, onReasoning }) {
|
|
|
149
125
|
}
|
|
150
126
|
|
|
151
127
|
return {
|
|
152
|
-
push(text) {
|
|
153
|
-
|
|
154
|
-
flush(false);
|
|
155
|
-
},
|
|
156
|
-
finish() {
|
|
157
|
-
flush(true);
|
|
158
|
-
},
|
|
128
|
+
push(text) { buffer += text; flush(false); },
|
|
129
|
+
finish() { flush(true); },
|
|
159
130
|
};
|
|
160
131
|
}
|
|
161
132
|
|
|
162
133
|
export async function chatStream({ model, messages, tools, onChunk, onReasoningChunk, signal }) {
|
|
163
|
-
const body = {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
stream: true,
|
|
167
|
-
};
|
|
168
|
-
if (tools && tools.length) {
|
|
169
|
-
body.tools = tools;
|
|
170
|
-
}
|
|
134
|
+
const body = { model, messages, stream: true };
|
|
135
|
+
if (tools && tools.length) body.tools = tools;
|
|
136
|
+
|
|
171
137
|
const response = await request("/api/chat", body, signal);
|
|
172
138
|
|
|
173
|
-
|
|
174
|
-
if (!reader) {
|
|
139
|
+
if (!response.body) {
|
|
175
140
|
throw new Error("Server down");
|
|
176
141
|
}
|
|
177
142
|
|
|
178
143
|
const decoder = new TextDecoder();
|
|
179
|
-
let
|
|
144
|
+
let lineBuffer = "";
|
|
180
145
|
let lastMessage = { role: "assistant", content: "", reasoning: "", tool_calls: [] };
|
|
181
146
|
const parser = createTaggedStreamParser({
|
|
182
147
|
onContent(text) {
|
|
@@ -189,17 +154,41 @@ export async function chatStream({ model, messages, tools, onChunk, onReasoningC
|
|
|
189
154
|
},
|
|
190
155
|
});
|
|
191
156
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
157
|
+
try {
|
|
158
|
+
// Use async iterator for better Node.js compatibility
|
|
159
|
+
for await (const chunk of response.body) {
|
|
160
|
+
lineBuffer += decoder.decode(chunk, { stream: true });
|
|
161
|
+
|
|
162
|
+
let lines = lineBuffer.split("\n");
|
|
163
|
+
lineBuffer = lines.pop() || "";
|
|
164
|
+
|
|
165
|
+
for (const line of lines) {
|
|
166
|
+
if (!line.trim()) continue;
|
|
167
|
+
try {
|
|
168
|
+
const json = JSON.parse(line);
|
|
169
|
+
const message = json.message || {};
|
|
170
|
+
if (message.thinking) {
|
|
171
|
+
lastMessage.reasoning += message.thinking;
|
|
172
|
+
onReasoningChunk?.(message.thinking);
|
|
173
|
+
}
|
|
174
|
+
if (message.content) {
|
|
175
|
+
parser.push(message.content);
|
|
176
|
+
}
|
|
177
|
+
if (message.tool_calls?.length) {
|
|
178
|
+
lastMessage.tool_calls = message.tool_calls;
|
|
179
|
+
}
|
|
180
|
+
if (message.role) {
|
|
181
|
+
lastMessage.role = message.role;
|
|
182
|
+
}
|
|
183
|
+
} catch {
|
|
184
|
+
// Ignore parse errors from partial lines or server noise
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (lineBuffer.trim()) {
|
|
190
|
+
try {
|
|
191
|
+
const json = JSON.parse(lineBuffer);
|
|
203
192
|
const message = json.message || {};
|
|
204
193
|
if (message.thinking) {
|
|
205
194
|
lastMessage.reasoning += message.thinking;
|
|
@@ -211,33 +200,15 @@ export async function chatStream({ model, messages, tools, onChunk, onReasoningC
|
|
|
211
200
|
if (message.tool_calls?.length) {
|
|
212
201
|
lastMessage.tool_calls = message.tool_calls;
|
|
213
202
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
203
|
+
} catch {
|
|
204
|
+
// Final line noise
|
|
217
205
|
}
|
|
218
|
-
newlineIndex = buffer.indexOf("\n");
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
if (buffer.trim()) {
|
|
223
|
-
const json = JSON.parse(buffer.trim());
|
|
224
|
-
const message = json.message || {};
|
|
225
|
-
if (message.thinking) {
|
|
226
|
-
lastMessage.reasoning += message.thinking;
|
|
227
|
-
onReasoningChunk?.(message.thinking);
|
|
228
|
-
}
|
|
229
|
-
if (message.content) {
|
|
230
|
-
parser.push(message.content);
|
|
231
|
-
}
|
|
232
|
-
if (message.tool_calls?.length) {
|
|
233
|
-
lastMessage.tool_calls = message.tool_calls;
|
|
234
|
-
}
|
|
235
|
-
if (message.role) {
|
|
236
|
-
lastMessage.role = message.role;
|
|
237
206
|
}
|
|
207
|
+
} catch (err) {
|
|
208
|
+
if (err.name === "AbortError") throw err;
|
|
209
|
+
throw new Error("Server down");
|
|
238
210
|
}
|
|
239
211
|
|
|
240
212
|
parser.finish();
|
|
241
|
-
|
|
242
213
|
return lastMessage;
|
|
243
214
|
}
|
package/src/tools.js
CHANGED
|
@@ -38,12 +38,12 @@ export function getToolDefinitions() {
|
|
|
38
38
|
type: "function",
|
|
39
39
|
function: {
|
|
40
40
|
name: "list_files",
|
|
41
|
-
description: "List files
|
|
41
|
+
description: "List files in the workspace.",
|
|
42
42
|
parameters: {
|
|
43
43
|
type: "object",
|
|
44
44
|
properties: {
|
|
45
|
-
path: { type: "string"
|
|
46
|
-
recursive: { type: "boolean"
|
|
45
|
+
path: { type: "string" },
|
|
46
|
+
recursive: { type: "boolean" },
|
|
47
47
|
},
|
|
48
48
|
},
|
|
49
49
|
},
|
|
@@ -52,56 +52,24 @@ export function getToolDefinitions() {
|
|
|
52
52
|
type: "function",
|
|
53
53
|
function: {
|
|
54
54
|
name: "read_file",
|
|
55
|
-
description: "Read a
|
|
55
|
+
description: "Read a file.",
|
|
56
56
|
parameters: {
|
|
57
57
|
type: "object",
|
|
58
|
-
properties: {
|
|
59
|
-
path: { type: "string", description: "Relative file path." },
|
|
60
|
-
},
|
|
58
|
+
properties: { path: { type: "string" } },
|
|
61
59
|
required: ["path"],
|
|
62
60
|
},
|
|
63
61
|
},
|
|
64
62
|
},
|
|
65
|
-
{
|
|
66
|
-
type: "function",
|
|
67
|
-
function: {
|
|
68
|
-
name: "create_file",
|
|
69
|
-
description: "Create a new text file inside the workspace. Fails if the file already exists.",
|
|
70
|
-
parameters: {
|
|
71
|
-
type: "object",
|
|
72
|
-
properties: {
|
|
73
|
-
path: { type: "string", description: "Relative file path." },
|
|
74
|
-
content: { type: "string", description: "Initial file content." },
|
|
75
|
-
},
|
|
76
|
-
required: ["path", "content"],
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
},
|
|
80
63
|
{
|
|
81
64
|
type: "function",
|
|
82
65
|
function: {
|
|
83
66
|
name: "write_file",
|
|
84
|
-
description: "Create or overwrite a
|
|
85
|
-
parameters: {
|
|
86
|
-
type: "object",
|
|
87
|
-
properties: {
|
|
88
|
-
path: { type: "string", description: "Relative file path." },
|
|
89
|
-
content: { type: "string", description: "New file content." },
|
|
90
|
-
},
|
|
91
|
-
required: ["path", "content"],
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
type: "function",
|
|
97
|
-
function: {
|
|
98
|
-
name: "append_file",
|
|
99
|
-
description: "Append text to the end of a file inside the workspace, creating it if needed.",
|
|
67
|
+
description: "Create or overwrite a file.",
|
|
100
68
|
parameters: {
|
|
101
69
|
type: "object",
|
|
102
70
|
properties: {
|
|
103
|
-
path: { type: "string"
|
|
104
|
-
content: { type: "string"
|
|
71
|
+
path: { type: "string" },
|
|
72
|
+
content: { type: "string" },
|
|
105
73
|
},
|
|
106
74
|
required: ["path", "content"],
|
|
107
75
|
},
|
|
@@ -111,126 +79,29 @@ export function getToolDefinitions() {
|
|
|
111
79
|
type: "function",
|
|
112
80
|
function: {
|
|
113
81
|
name: "edit_file",
|
|
114
|
-
description: "Edit a
|
|
82
|
+
description: "Edit a file by replacing text.",
|
|
115
83
|
parameters: {
|
|
116
84
|
type: "object",
|
|
117
85
|
properties: {
|
|
118
|
-
path: { type: "string"
|
|
119
|
-
oldText: { type: "string"
|
|
120
|
-
newText: { type: "string"
|
|
121
|
-
replaceAll: { type: "boolean", description: "Replace all occurrences instead of just the first." },
|
|
86
|
+
path: { type: "string" },
|
|
87
|
+
oldText: { type: "string" },
|
|
88
|
+
newText: { type: "string" },
|
|
122
89
|
},
|
|
123
90
|
required: ["path", "oldText", "newText"],
|
|
124
91
|
},
|
|
125
92
|
},
|
|
126
93
|
},
|
|
127
|
-
{
|
|
128
|
-
type: "function",
|
|
129
|
-
function: {
|
|
130
|
-
name: "make_dir",
|
|
131
|
-
description: "Create a directory inside the workspace.",
|
|
132
|
-
parameters: {
|
|
133
|
-
type: "object",
|
|
134
|
-
properties: {
|
|
135
|
-
path: { type: "string", description: "Relative directory path." },
|
|
136
|
-
},
|
|
137
|
-
required: ["path"],
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
type: "function",
|
|
143
|
-
function: {
|
|
144
|
-
name: "remove_path",
|
|
145
|
-
description: "Remove a file or directory inside the workspace.",
|
|
146
|
-
parameters: {
|
|
147
|
-
type: "object",
|
|
148
|
-
properties: {
|
|
149
|
-
path: { type: "string", description: "Relative path to delete." },
|
|
150
|
-
recursive: { type: "boolean", description: "Allow recursive directory deletion." },
|
|
151
|
-
},
|
|
152
|
-
required: ["path"],
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
},
|
|
156
94
|
{
|
|
157
95
|
type: "function",
|
|
158
96
|
function: {
|
|
159
97
|
name: "run_command",
|
|
160
|
-
description: "Run a shell command
|
|
161
|
-
parameters: {
|
|
162
|
-
type: "object",
|
|
163
|
-
properties: {
|
|
164
|
-
command: { type: "string", description: "Executable name, for example `npm` or `git`." },
|
|
165
|
-
args: { type: "array", items: { type: "string" }, description: "Argument array." },
|
|
166
|
-
commandLine: {
|
|
167
|
-
type: "string",
|
|
168
|
-
description: "Optional raw shell command line. Use it for pipes, redirects, globs, and shell syntax.",
|
|
169
|
-
},
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
{
|
|
175
|
-
type: "function",
|
|
176
|
-
function: {
|
|
177
|
-
name: "batch_command",
|
|
178
|
-
description: "Run multiple shell commands sequentially in the workspace.",
|
|
179
|
-
parameters: {
|
|
180
|
-
type: "object",
|
|
181
|
-
properties: {
|
|
182
|
-
commands: {
|
|
183
|
-
type: "array",
|
|
184
|
-
items: {
|
|
185
|
-
type: "object",
|
|
186
|
-
properties: {
|
|
187
|
-
command: { type: "string" },
|
|
188
|
-
args: { type: "array", items: { type: "string" } },
|
|
189
|
-
commandLine: { type: "string" },
|
|
190
|
-
},
|
|
191
|
-
},
|
|
192
|
-
},
|
|
193
|
-
},
|
|
194
|
-
required: ["commands"],
|
|
195
|
-
},
|
|
196
|
-
},
|
|
197
|
-
},
|
|
198
|
-
{
|
|
199
|
-
type: "function",
|
|
200
|
-
function: {
|
|
201
|
-
name: "todo_create",
|
|
202
|
-
description: "Create a todo item for the current workspace.",
|
|
203
|
-
parameters: {
|
|
204
|
-
type: "object",
|
|
205
|
-
properties: {
|
|
206
|
-
text: { type: "string" },
|
|
207
|
-
},
|
|
208
|
-
required: ["text"],
|
|
209
|
-
},
|
|
210
|
-
},
|
|
211
|
-
},
|
|
212
|
-
{
|
|
213
|
-
type: "function",
|
|
214
|
-
function: {
|
|
215
|
-
name: "todo_list",
|
|
216
|
-
description: "List todo items for the current workspace.",
|
|
217
|
-
parameters: {
|
|
218
|
-
type: "object",
|
|
219
|
-
properties: {},
|
|
220
|
-
},
|
|
221
|
-
},
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
type: "function",
|
|
225
|
-
function: {
|
|
226
|
-
name: "todo_complete",
|
|
227
|
-
description: "Mark a todo item complete by numeric id.",
|
|
98
|
+
description: "Run a shell command.",
|
|
228
99
|
parameters: {
|
|
229
100
|
type: "object",
|
|
230
101
|
properties: {
|
|
231
|
-
|
|
102
|
+
commandLine: { type: "string" },
|
|
232
103
|
},
|
|
233
|
-
required: ["
|
|
104
|
+
required: ["commandLine"],
|
|
234
105
|
},
|
|
235
106
|
},
|
|
236
107
|
},
|