jinzd-ai-cli 0.1.67 → 0.1.69
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/index.js
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
SUBAGENT_MAX_ROUNDS_LIMIT,
|
|
31
31
|
VERSION,
|
|
32
32
|
runTestsTool
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-3HISC6FK.js";
|
|
34
34
|
|
|
35
35
|
// src/index.ts
|
|
36
36
|
import { program } from "commander";
|
|
@@ -2607,6 +2607,7 @@ var Renderer = class {
|
|
|
2607
2607
|
console.log(feat("/diff command: show aggregated diff of all file modifications in current session (merge multi-edit per file)"));
|
|
2608
2608
|
console.log(feat("/fork conversation branch: fork from current position or checkpoint into new session, explore alternatives"));
|
|
2609
2609
|
console.log(feat("Streaming Tool Use: real-time text streaming + instant tool name display in agentic loop (OpenAI/Claude)"));
|
|
2610
|
+
console.log(feat("User interjection: type a message + Enter during agentic loop to redirect AI mid-execution"));
|
|
2610
2611
|
console.log();
|
|
2611
2612
|
}
|
|
2612
2613
|
printPrompt(provider, _model) {
|
|
@@ -4597,7 +4598,7 @@ ${hint}` : "")
|
|
|
4597
4598
|
description: "Run project tests and show structured report",
|
|
4598
4599
|
usage: "/test [command|filter]",
|
|
4599
4600
|
async execute(args, _ctx) {
|
|
4600
|
-
const { executeTests } = await import("./run-tests-
|
|
4601
|
+
const { executeTests } = await import("./run-tests-4MKYSWRR.js");
|
|
4601
4602
|
const argStr = args.join(" ").trim();
|
|
4602
4603
|
let testArgs = {};
|
|
4603
4604
|
if (argStr) {
|
|
@@ -9014,6 +9015,12 @@ var Repl = class {
|
|
|
9014
9015
|
streamAbortController = null;
|
|
9015
9016
|
/** ESC 键监听器引用(用于 removeListener 时取消注册) */
|
|
9016
9017
|
_escHandler = null;
|
|
9018
|
+
/** User interjection: completed line queued for injection into agentic loop */
|
|
9019
|
+
_userInterjection = null;
|
|
9020
|
+
/** User interjection: character accumulator (typed but not yet Enter'd) */
|
|
9021
|
+
_interjectionBuf = "";
|
|
9022
|
+
/** User interjection: stdin data handler reference */
|
|
9023
|
+
_interjectionHandler = null;
|
|
9017
9024
|
/** 运行时动态添加的额外上下文目录(/add-dir 命令) */
|
|
9018
9025
|
extraContextDirs = [];
|
|
9019
9026
|
/** 启动时允许的工具名集合(--allowed-tools 限制) */
|
|
@@ -10115,6 +10122,50 @@ Session '${this.resumeSessionId}' not found.
|
|
|
10115
10122
|
process.stdin.pause();
|
|
10116
10123
|
this.streamAbortController = null;
|
|
10117
10124
|
}
|
|
10125
|
+
/**
|
|
10126
|
+
* Set up a stdin listener that lets the user type a message during the agentic loop.
|
|
10127
|
+
* Typed characters are echoed and buffered; pressing Enter queues the line for injection.
|
|
10128
|
+
* Coexists with setupStreamInterrupt() — they handle disjoint byte ranges.
|
|
10129
|
+
*/
|
|
10130
|
+
setupInterjectionListener() {
|
|
10131
|
+
this._userInterjection = null;
|
|
10132
|
+
this._interjectionBuf = "";
|
|
10133
|
+
const handler = (data) => {
|
|
10134
|
+
if (this.toolExecutor.confirming || askUserContext.prompting) return;
|
|
10135
|
+
for (let i = 0; i < data.length; i++) {
|
|
10136
|
+
const byte = data[i];
|
|
10137
|
+
if (byte === 13 || byte === 10) {
|
|
10138
|
+
if (this._interjectionBuf.length > 0) {
|
|
10139
|
+
this._userInterjection = this._interjectionBuf;
|
|
10140
|
+
this._interjectionBuf = "";
|
|
10141
|
+
process.stdout.write("\n");
|
|
10142
|
+
}
|
|
10143
|
+
} else if (byte === 127 || byte === 8) {
|
|
10144
|
+
if (this._interjectionBuf.length > 0) {
|
|
10145
|
+
this._interjectionBuf = this._interjectionBuf.slice(0, -1);
|
|
10146
|
+
process.stdout.write("\b \b");
|
|
10147
|
+
}
|
|
10148
|
+
} else if (byte >= 32 && byte <= 126) {
|
|
10149
|
+
const ch = String.fromCharCode(byte);
|
|
10150
|
+
this._interjectionBuf += ch;
|
|
10151
|
+
process.stdout.write(ch);
|
|
10152
|
+
}
|
|
10153
|
+
}
|
|
10154
|
+
};
|
|
10155
|
+
this._interjectionHandler = handler;
|
|
10156
|
+
process.stdin.on("data", handler);
|
|
10157
|
+
process.stdin.resume();
|
|
10158
|
+
}
|
|
10159
|
+
/**
|
|
10160
|
+
* Remove the interjection listener, clean up state.
|
|
10161
|
+
*/
|
|
10162
|
+
teardownInterjectionListener() {
|
|
10163
|
+
if (this._interjectionHandler) {
|
|
10164
|
+
process.stdin.removeListener("data", this._interjectionHandler);
|
|
10165
|
+
this._interjectionHandler = null;
|
|
10166
|
+
}
|
|
10167
|
+
this._interjectionBuf = "";
|
|
10168
|
+
}
|
|
10118
10169
|
/**
|
|
10119
10170
|
* 注册 Ctrl+V 剪贴板图片粘贴快捷键。
|
|
10120
10171
|
*
|
|
@@ -10351,9 +10402,18 @@ Session '${this.resumeSessionId}' not found.
|
|
|
10351
10402
|
const roundUsage = { inputTokens: 0, outputTokens: 0 };
|
|
10352
10403
|
const supportsStreamingTools = useStreaming && typeof provider.chatWithToolsStream === "function";
|
|
10353
10404
|
let consecutiveFreeRounds = 0;
|
|
10405
|
+
this.setupInterjectionListener();
|
|
10406
|
+
process.stdout.write(theme.dim(" (Type a message + Enter to redirect AI at any time)\n"));
|
|
10354
10407
|
try {
|
|
10355
10408
|
for (let round = 0; round < MAX_TOOL_ROUNDS; round++) {
|
|
10356
10409
|
this.toolExecutor.setRoundInfo(round + 1, MAX_TOOL_ROUNDS);
|
|
10410
|
+
if (this._userInterjection) {
|
|
10411
|
+
const msg = this._userInterjection;
|
|
10412
|
+
this._userInterjection = null;
|
|
10413
|
+
process.stdout.write(theme.warning(`\u26A1 Interjection: "${msg}"
|
|
10414
|
+
`));
|
|
10415
|
+
extraMessages.push({ role: "user", content: msg });
|
|
10416
|
+
}
|
|
10357
10417
|
let result;
|
|
10358
10418
|
let alreadyRendered = false;
|
|
10359
10419
|
const chatRequest = {
|
|
@@ -10524,7 +10584,14 @@ Session '${this.resumeSessionId}' not found.
|
|
|
10524
10584
|
spawnAgentContext.systemPrompt = systemPrompt;
|
|
10525
10585
|
spawnAgentContext.modelParams = modelParams;
|
|
10526
10586
|
spawnAgentContext.configManager = this.config;
|
|
10587
|
+
if (this._interjectionHandler) {
|
|
10588
|
+
process.stdin.removeListener("data", this._interjectionHandler);
|
|
10589
|
+
}
|
|
10527
10590
|
const toolResults = await this.toolExecutor.executeAll(result.toolCalls);
|
|
10591
|
+
if (this._interjectionHandler) {
|
|
10592
|
+
process.stdin.on("data", this._interjectionHandler);
|
|
10593
|
+
process.stdin.resume();
|
|
10594
|
+
}
|
|
10528
10595
|
const reasoningContent = "reasoningContent" in result ? result.reasoningContent : void 0;
|
|
10529
10596
|
const newMsgs = provider.buildToolResultMessages(result.toolCalls, toolResults, reasoningContent);
|
|
10530
10597
|
extraMessages.push(...newMsgs);
|
|
@@ -10537,6 +10604,13 @@ Session '${this.resumeSessionId}' not found.
|
|
|
10537
10604
|
} else {
|
|
10538
10605
|
consecutiveFreeRounds = 0;
|
|
10539
10606
|
}
|
|
10607
|
+
if (this._userInterjection) {
|
|
10608
|
+
const msg = this._userInterjection;
|
|
10609
|
+
this._userInterjection = null;
|
|
10610
|
+
process.stdout.write(theme.warning(`\u26A1 Interjection: "${msg}"
|
|
10611
|
+
`));
|
|
10612
|
+
extraMessages.push({ role: "user", content: msg });
|
|
10613
|
+
}
|
|
10540
10614
|
const nextRound = round + 2;
|
|
10541
10615
|
spinner.start(
|
|
10542
10616
|
nextRound <= MAX_TOOL_ROUNDS ? `Thinking... (round ${nextRound}/${MAX_TOOL_ROUNDS})` : "Thinking..."
|
|
@@ -10602,6 +10676,7 @@ Tip: You can continue the conversation by asking the AI to proceed.`
|
|
|
10602
10676
|
}
|
|
10603
10677
|
}
|
|
10604
10678
|
} finally {
|
|
10679
|
+
this.teardownInterjectionListener();
|
|
10605
10680
|
spinner.stop();
|
|
10606
10681
|
await this.checkContextPressure();
|
|
10607
10682
|
}
|