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.
@@ -8,7 +8,7 @@ import { platform } from "os";
8
8
  import chalk from "chalk";
9
9
 
10
10
  // src/core/constants.ts
11
- var VERSION = "0.1.67";
11
+ var VERSION = "0.1.69";
12
12
  var APP_NAME = "ai-cli";
13
13
  var CONFIG_DIR_NAME = ".aicli";
14
14
  var CONFIG_FILE_NAME = "config.json";
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-YAOWQ5J2.js";
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-73CPEF6O.js");
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
  }
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-YAOWQ5J2.js";
5
+ } from "./chunk-3HISC6FK.js";
6
6
  export {
7
7
  executeTests,
8
8
  runTestsTool
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.1.67",
3
+ "version": "0.1.69",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",