@zhongqian97-code/ecode 0.5.3 → 0.5.5

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.
Files changed (2) hide show
  1. package/dist/index.js +53 -28
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -148,33 +148,26 @@ function createOpenAIProvider(profile) {
148
148
  stream(messages, tools, signal) {
149
149
  return {
150
150
  [Symbol.asyncIterator]: async function* () {
151
- let inThinkBlock = false;
152
- function stripThinkTags(raw) {
153
- let text = "";
154
- let thinking = "";
155
- let i = 0;
156
- while (i < raw.length) {
157
- if (!inThinkBlock) {
158
- const start = raw.indexOf("<think>", i);
159
- if (start === -1) {
160
- text += raw.slice(i);
161
- break;
162
- }
163
- text += raw.slice(i, start);
164
- inThinkBlock = true;
165
- i = start + 7;
151
+ let thinkPhase = "pre";
152
+ let scanningForClose = false;
153
+ let closeSearchBuffer = "";
154
+ function processContent(raw) {
155
+ if (!raw) return { text: "", thinking: "" };
156
+ if (thinkPhase === "post") return { text: raw, thinking: "" };
157
+ if (thinkPhase === "pre") {
158
+ if (raw.startsWith("<think>")) {
159
+ thinkPhase = "in";
160
+ raw = raw.slice(7);
166
161
  } else {
167
- const end = raw.indexOf("</think>", i);
168
- if (end === -1) {
169
- thinking += raw.slice(i);
170
- break;
171
- }
172
- thinking += raw.slice(i, end);
173
- inThinkBlock = false;
174
- i = end + 8;
162
+ thinkPhase = "post";
163
+ return { text: raw, thinking: "" };
175
164
  }
176
165
  }
177
- return { text, thinking };
166
+ const endIdx = raw.indexOf("</think>");
167
+ if (endIdx === -1) return { text: "", thinking: raw };
168
+ const thinking = raw.slice(0, endIdx);
169
+ thinkPhase = "post";
170
+ return { text: raw.slice(endIdx + 8), thinking };
178
171
  }
179
172
  const requestParams = {
180
173
  model: profile.model,
@@ -200,6 +193,7 @@ function createOpenAIProvider(profile) {
200
193
  reasoningAccumulator += delta.reasoning_content;
201
194
  }
202
195
  if (delta.reasoning_details && delta.reasoning_details.length > 0) {
196
+ scanningForClose = true;
203
197
  for (const rd of delta.reasoning_details) {
204
198
  const id = rd.id ?? "";
205
199
  const text = rd.text ?? "";
@@ -235,8 +229,29 @@ function createOpenAIProvider(profile) {
235
229
  }
236
230
  }
237
231
  const isLast = choice.finish_reason != null;
238
- const raw = delta.content ?? "";
239
- const { text: filteredText, thinking: thinkContent } = stripThinkTags(raw);
232
+ let rawToProcess;
233
+ if (delta.reasoning_details && delta.reasoning_details.length > 0) {
234
+ rawToProcess = "";
235
+ } else if (scanningForClose) {
236
+ closeSearchBuffer += delta.content ?? "";
237
+ const closeIdx = closeSearchBuffer.indexOf("</think>");
238
+ if (closeIdx !== -1) {
239
+ rawToProcess = closeSearchBuffer.slice(closeIdx + 8);
240
+ scanningForClose = false;
241
+ closeSearchBuffer = "";
242
+ thinkPhase = "post";
243
+ } else if (isLast) {
244
+ rawToProcess = closeSearchBuffer;
245
+ scanningForClose = false;
246
+ closeSearchBuffer = "";
247
+ thinkPhase = "post";
248
+ } else {
249
+ rawToProcess = "";
250
+ }
251
+ } else {
252
+ rawToProcess = delta.content ?? "";
253
+ }
254
+ const { text: filteredText, thinking: thinkContent } = processContent(rawToProcess);
240
255
  if (thinkContent) {
241
256
  reasoningAccumulator += thinkContent;
242
257
  }
@@ -3524,6 +3539,7 @@ var SkillRegistry = class {
3524
3539
  };
3525
3540
 
3526
3541
  // src/pipe.ts
3542
+ var PIPE_SYSTEM_PROMPT = "You are a helpful assistant running in headless pipe mode. You have access to read-only file tools (read, glob, grep). Answer concisely and accurately.";
3527
3543
  var PIPE_TOOLS = [READ_TOOL, GLOB_TOOL, GREP_TOOL];
3528
3544
  function emit(out, event) {
3529
3545
  out.write(JSON.stringify(event) + "\n");
@@ -3544,10 +3560,15 @@ async function executeToolCall(name, args) {
3544
3560
  return `Unknown tool: ${name}`;
3545
3561
  }
3546
3562
  async function runPipe(prompt, llm, out = process.stdout) {
3547
- const messages = [{ role: "user", content: prompt }];
3563
+ const messages = [
3564
+ { role: "system", content: PIPE_SYSTEM_PROMPT },
3565
+ { role: "user", content: prompt }
3566
+ ];
3548
3567
  while (true) {
3549
3568
  let assistantText = "";
3550
3569
  let lastUsage;
3570
+ let lastReasoning;
3571
+ let lastReasoningDetails;
3551
3572
  const toolCalls = [];
3552
3573
  for await (const chunk of llm.stream(messages, PIPE_TOOLS)) {
3553
3574
  if (chunk.text) {
@@ -3560,6 +3581,8 @@ async function runPipe(prompt, llm, out = process.stdout) {
3560
3581
  if (chunk.done) {
3561
3582
  if (chunk.toolCalls) toolCalls.push(...chunk.toolCalls);
3562
3583
  if (chunk.usage) lastUsage = chunk.usage;
3584
+ if (chunk.reasoning) lastReasoning = chunk.reasoning;
3585
+ if (chunk.reasoningDetails) lastReasoningDetails = chunk.reasoningDetails;
3563
3586
  }
3564
3587
  }
3565
3588
  if (toolCalls.length > 0) {
@@ -3570,7 +3593,9 @@ async function runPipe(prompt, llm, out = process.stdout) {
3570
3593
  id: tc.id,
3571
3594
  type: "function",
3572
3595
  function: { name: tc.name, arguments: tc.arguments }
3573
- }))
3596
+ })),
3597
+ ...lastReasoning ? { reasoning_content: lastReasoning } : {},
3598
+ ...lastReasoningDetails ? { reasoning_details: lastReasoningDetails } : {}
3574
3599
  });
3575
3600
  for (const tc of toolCalls) {
3576
3601
  emit(out, { type: "tool_call", id: tc.id, name: tc.name, arguments: tc.arguments });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhongqian97-code/ecode",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "A minimal Claude Code clone with REPL interface and bash tool calling",
5
5
  "type": "module",
6
6
  "author": "zhongqian97-code",