@ai-setting/roy-agent-cli 1.7.16 → 1.7.18

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
@@ -1677,7 +1677,7 @@ __export(exports_repl_ink, {
1677
1677
  });
1678
1678
  import * as React2 from "react";
1679
1679
  import { Box as Box3, Text as Text4, Static, useApp, useInput as useInput2 } from "ink";
1680
- import { createLogger as createLogger4 } from "@ai-setting/roy-agent-core";
1680
+ import { createLogger as createLogger5 } from "@ai-setting/roy-agent-core";
1681
1681
  import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
1682
1682
  function nextMessageId() {
1683
1683
  _msgIdCounter += 1;
@@ -2032,7 +2032,7 @@ var init_repl_ink = __esm(() => {
2032
2032
  children: out
2033
2033
  }, undefined, false, undefined, this);
2034
2034
  });
2035
- inkLogger = createLogger4("repl-ink");
2035
+ inkLogger = createLogger5("repl-ink");
2036
2036
  MultilineInput = React2.memo(React2.forwardRef(function MultilineInput2(props, ref) {
2037
2037
  const { onSubmit, placeholder, popupActive, onChange, onEscape } = props;
2038
2038
  const [buffer, setBuffer] = React2.useState("");
@@ -9460,7 +9460,7 @@ class CliQuietModeService {
9460
9460
  // package.json
9461
9461
  var package_default = {
9462
9462
  name: "@ai-setting/roy-agent-cli",
9463
- version: "1.7.15",
9463
+ version: "1.7.18",
9464
9464
  type: "module",
9465
9465
  description: "CLI for roy-agent - Non-interactive command execution",
9466
9466
  main: "./dist/index.js",
@@ -10380,6 +10380,8 @@ __legacyDecorateClassTS([
10380
10380
  import { ContextError as ContextError2, ErrorCodes as ErrorCodes2, getTracerProvider as getTracerProvider2 } from "@ai-setting/roy-agent-core";
10381
10381
 
10382
10382
  // src/commands/shared/task-event-handler.ts
10383
+ import { createLogger as createLogger4 } from "@ai-setting/roy-agent-core";
10384
+ var logger4 = createLogger4("task-event-handler");
10383
10385
  var TASK_EVENTS = {
10384
10386
  CREATED: "task.created",
10385
10387
  UPDATED: "task.updated",
@@ -10406,14 +10408,14 @@ class TaskEventHandler {
10406
10408
  this.handleEvent(event);
10407
10409
  }
10408
10410
  });
10409
- console.log("[TaskEventHandler] Started");
10411
+ logger4.info("[TaskEventHandler] Started");
10410
10412
  return () => this.stop();
10411
10413
  }
10412
10414
  stop() {
10413
10415
  if (this.unsubscribe) {
10414
10416
  this.unsubscribe();
10415
10417
  this.unsubscribe = null;
10416
- console.log("[TaskEventHandler] Stopped");
10418
+ logger4.info("[TaskEventHandler] Stopped");
10417
10419
  }
10418
10420
  }
10419
10421
  handleEvent(event) {
@@ -10438,47 +10440,47 @@ class TaskEventHandler {
10438
10440
  this.logTaskDelegated(payload);
10439
10441
  break;
10440
10442
  default:
10441
- console.log(`[${event.type}]`, payload);
10443
+ logger4.info(`[${event.type}]`, payload);
10442
10444
  }
10443
10445
  }
10444
10446
  logTaskCreated(payload) {
10445
10447
  const task = payload.task;
10446
- console.log(`\uD83D\uDCDD Task created: #${task.id} - ${task.title}`);
10447
- console.log(` Status: ${task.status}, Priority: ${task.priority}, Type: ${task.type || "normal"}`);
10448
+ logger4.info(`\uD83D\uDCDD Task created: #${task.id} - ${task.title}`);
10449
+ logger4.info(` Status: ${task.status}, Priority: ${task.priority}, Type: ${task.type || "normal"}`);
10448
10450
  if (task.project_path) {
10449
- console.log(` Project: ${task.project_path}`);
10451
+ logger4.info(` Project: ${task.project_path}`);
10450
10452
  }
10451
10453
  if (task.context) {
10452
- console.log(` Context: ${task.context}`);
10454
+ logger4.info(` Context: ${task.context}`);
10453
10455
  }
10454
10456
  }
10455
10457
  logTaskUpdated(payload) {
10456
10458
  const task = payload.task;
10457
10459
  const changes = payload.changes;
10458
- console.log(`✏️ Task updated: #${task.id} - ${task.title}`);
10459
- console.log(` Status: ${task.status}, Priority: ${task.priority}, Type: ${task.type || "normal"}, Progress: ${task.progress}%`);
10460
+ logger4.info(`✏️ Task updated: #${task.id} - ${task.title}`);
10461
+ logger4.info(` Status: ${task.status}, Priority: ${task.priority}, Type: ${task.type || "normal"}, Progress: ${task.progress}%`);
10460
10462
  if (changes) {
10461
- console.log(` Changes: ${JSON.stringify(changes)}`);
10463
+ logger4.info(` Changes: ${JSON.stringify(changes)}`);
10462
10464
  }
10463
10465
  }
10464
10466
  logTaskCompleted(payload) {
10465
10467
  const task = payload.task;
10466
- console.log(`✅ Task completed: #${task.id} - ${task.title}`);
10467
- console.log(` Progress: ${task.progress}%, Updated: ${task.updatedAt}`);
10468
+ logger4.info(`✅ Task completed: #${task.id} - ${task.title}`);
10469
+ logger4.info(` Progress: ${task.progress}%, Updated: ${task.updatedAt}`);
10468
10470
  }
10469
10471
  logTaskDeleted(payload) {
10470
- console.log(`\uD83D\uDDD1️ Task deleted: #${payload.taskId}`);
10472
+ logger4.info(`\uD83D\uDDD1️ Task deleted: #${payload.taskId}`);
10471
10473
  }
10472
10474
  logOperationAdded(payload) {
10473
10475
  const op = payload.operation;
10474
- console.log(`\uD83D\uDCCB Operation added to task #${payload.taskId}:`);
10475
- console.log(` [${op.milestoneType}] ${op.milestoneTitle}`);
10476
- console.log(` Session: ${op.sessionId}`);
10476
+ logger4.info(`\uD83D\uDCCB Operation added to task #${payload.taskId}:`);
10477
+ logger4.info(` [${op.milestoneType}] ${op.milestoneTitle}`);
10478
+ logger4.info(` Session: ${op.sessionId}`);
10477
10479
  }
10478
10480
  logTaskDelegated(payload) {
10479
- console.log(`\uD83D\uDE80 Task delegated: #${payload.taskId}`);
10480
- console.log(` Background: ${payload.backgroundTaskId}, Type: ${payload.subagentType}`);
10481
- console.log(` ${payload.description}`);
10481
+ logger4.info(`\uD83D\uDE80 Task delegated: #${payload.taskId}`);
10482
+ logger4.info(` Background: ${payload.backgroundTaskId}, Type: ${payload.subagentType}`);
10483
+ logger4.info(` ${payload.description}`);
10482
10484
  }
10483
10485
  }
10484
10486
 
@@ -10556,9 +10558,9 @@ async function runActShutdownCleanup(deps) {
10556
10558
  envService,
10557
10559
  shouldDisposeEnvService,
10558
10560
  output,
10559
- logger: logger4
10561
+ logger: logger5
10560
10562
  } = deps;
10561
- const log = logger4 ?? {
10563
+ const log = logger5 ?? {
10562
10564
  debug: () => {},
10563
10565
  warn: (msg) => console.warn(msg)
10564
10566
  };
@@ -11702,7 +11704,7 @@ function hasActiveDescendants(env, parentSessionId) {
11702
11704
 
11703
11705
  // src/commands/interactive.ts
11704
11706
  init_stream_output_service();
11705
- import { createLogger as createLogger5, getTracerProvider as getTracerProvider3 } from "@ai-setting/roy-agent-core";
11707
+ import { createLogger as createLogger6, getTracerProvider as getTracerProvider3 } from "@ai-setting/roy-agent-core";
11706
11708
 
11707
11709
  // src/commands/input-handler.ts
11708
11710
  class InputHandler {
@@ -12027,7 +12029,7 @@ async function shutdownInteractiveSteps(deps) {
12027
12029
  envService,
12028
12030
  shouldDisposeEnvService,
12029
12031
  output,
12030
- logger: logger4,
12032
+ logger: logger5,
12031
12033
  console: consoleLike
12032
12034
  } = deps;
12033
12035
  const log = (msg) => consoleLike?.log?.(msg) ?? console.log(msg);
@@ -12040,7 +12042,7 @@ async function shutdownInteractiveSteps(deps) {
12040
12042
  await memoryPluginDispose();
12041
12043
  log("\uD83D\uDCBE 记忆已保存完毕");
12042
12044
  } catch (err) {
12043
- logger4.warn(`[Shutdown] MemoryPlugin dispose failed: ${err}`);
12045
+ logger5.warn(`[Shutdown] MemoryPlugin dispose failed: ${err}`);
12044
12046
  output.warn(`⚠️ 记忆保存失败: ${err}`);
12045
12047
  }
12046
12048
  }
@@ -12048,25 +12050,25 @@ async function shutdownInteractiveSteps(deps) {
12048
12050
  try {
12049
12051
  await lspManagerDispose();
12050
12052
  } catch (err) {
12051
- logger4.warn(`[Shutdown] LSP Manager dispose failed: ${err}`);
12053
+ logger5.warn(`[Shutdown] LSP Manager dispose failed: ${err}`);
12052
12054
  }
12053
12055
  }
12054
12056
  if (pluginAdapterDispose) {
12055
12057
  try {
12056
12058
  await pluginAdapterDispose();
12057
12059
  } catch (err) {
12058
- logger4.warn(`[Shutdown] PluginAdapter dispose failed: ${err}`);
12060
+ logger5.warn(`[Shutdown] PluginAdapter dispose failed: ${err}`);
12059
12061
  }
12060
12062
  }
12061
12063
  try {
12062
12064
  eventHandler.stop();
12063
12065
  } catch (err) {
12064
- logger4.warn(`[Shutdown] EventHandler.stop failed: ${err}`);
12066
+ logger5.warn(`[Shutdown] EventHandler.stop failed: ${err}`);
12065
12067
  }
12066
12068
  try {
12067
12069
  queryExecutor.dispose();
12068
12070
  } catch (err) {
12069
- logger4.warn(`[Shutdown] QueryExecutor.dispose failed: ${err}`);
12071
+ logger5.warn(`[Shutdown] QueryExecutor.dispose failed: ${err}`);
12070
12072
  }
12071
12073
  try {
12072
12074
  const eventSourceComponent = env.getComponent("event-source");
@@ -12075,7 +12077,7 @@ async function shutdownInteractiveSteps(deps) {
12075
12077
  if (sources && sources.length > 0) {
12076
12078
  for (const source of sources) {
12077
12079
  const status = eventSourceComponent.getStatus?.(source.id);
12078
- logger4.debug(`[Shutdown] 检查事件源: ${source.id}, 状态: ${status}`);
12080
+ logger5.debug(`[Shutdown] 检查事件源: ${source.id}, 状态: ${status}`);
12079
12081
  if (status !== "stopped" && status !== "created") {
12080
12082
  try {
12081
12083
  await eventSourceComponent.stopSource(source.id);
@@ -12088,23 +12090,69 @@ async function shutdownInteractiveSteps(deps) {
12088
12090
  }
12089
12091
  }
12090
12092
  } catch (err) {
12091
- logger4.warn(`[Shutdown] EventSource cleanup failed: ${err}`);
12093
+ logger5.warn(`[Shutdown] EventSource cleanup failed: ${err}`);
12092
12094
  }
12093
12095
  if (shouldDisposeEnvService) {
12094
12096
  try {
12095
12097
  await envService.dispose();
12096
12098
  } catch (err) {
12097
- logger4.warn(`[Shutdown] envService.dispose failed: ${err}`);
12099
+ logger5.warn(`[Shutdown] envService.dispose failed: ${err}`);
12098
12100
  }
12099
12101
  }
12100
12102
  try {
12101
12103
  await TempFileManager2.getInstance().cleanupAll();
12102
- logger4.debug("[Shutdown] TempFileManager cleaned up");
12104
+ logger5.debug("[Shutdown] TempFileManager cleaned up");
12103
12105
  } catch (err) {
12104
- logger4.warn(`[Shutdown] TempFileManager cleanup failed: ${err}`);
12106
+ logger5.warn(`[Shutdown] TempFileManager cleanup failed: ${err}`);
12105
12107
  }
12106
12108
  }
12107
12109
 
12110
+ // src/commands/lifecycle.ts
12111
+ var DEFAULT_LOGGER = {
12112
+ warn: (msg) => console.error(`[shutdown] ${msg}`)
12113
+ };
12114
+ function registerShutdownHandlers(cleanup, options = {}) {
12115
+ const logger5 = options.logger ?? DEFAULT_LOGGER;
12116
+ const exitCode = options.exitCode ?? 0;
12117
+ const shutdownMessage = options.shutdownMessage ?? "Cleaning up...";
12118
+ let shuttingDown = false;
12119
+ const shutdown = async (signal) => {
12120
+ if (shuttingDown) {
12121
+ return;
12122
+ }
12123
+ shuttingDown = true;
12124
+ try {
12125
+ logger5.debug?.(`[${signal}] ${shutdownMessage}`);
12126
+ } catch {}
12127
+ if (cleanup) {
12128
+ try {
12129
+ await cleanup();
12130
+ } catch (err) {
12131
+ const message = err instanceof Error ? err.message : String(err);
12132
+ try {
12133
+ logger5.warn(`Cleanup failed: ${message}`);
12134
+ } catch {
12135
+ try {
12136
+ process.stderr.write(`[shutdown] Cleanup failed: ${message}
12137
+ `);
12138
+ } catch {}
12139
+ }
12140
+ }
12141
+ }
12142
+ try {
12143
+ process.exit(exitCode);
12144
+ } catch {}
12145
+ };
12146
+ process.on("SIGINT", shutdown);
12147
+ process.on("SIGTERM", shutdown);
12148
+ return {
12149
+ dispose: () => {
12150
+ process.removeListener("SIGINT", shutdown);
12151
+ process.removeListener("SIGTERM", shutdown);
12152
+ }
12153
+ };
12154
+ }
12155
+
12108
12156
  // src/commands/interactive.ts
12109
12157
  import { AskUserError, cascadeStopSessionTree } from "@ai-setting/roy-agent-core";
12110
12158
 
@@ -12291,7 +12339,7 @@ var COLOR_BY_STATUS = {
12291
12339
  busy: COLORS.accent
12292
12340
  };
12293
12341
  // src/commands/interactive.ts
12294
- var logger4 = createLogger5("cli:interactive");
12342
+ var logger5 = createLogger6("cli:interactive");
12295
12343
  var USER_PROMPT = COLORS.accent("❯ ");
12296
12344
 
12297
12345
  class REPL {
@@ -12483,10 +12531,10 @@ ${COLORS.system("[正在中断...]")}`);
12483
12531
  const agentComponent = this.env.getComponent("agent");
12484
12532
  if (agentComponent?.abortAll) {
12485
12533
  agentComponent.abortAll();
12486
- logger4.debug("[/abort] AgentComponent.abortAll() called");
12534
+ logger5.debug("[/abort] AgentComponent.abortAll() called");
12487
12535
  } else if (agentComponent?.abort) {
12488
12536
  agentComponent.abort("default");
12489
- logger4.debug("[/abort] AgentComponent.abort('default') called (fallback)");
12537
+ logger5.debug("[/abort] AgentComponent.abort('default') called (fallback)");
12490
12538
  }
12491
12539
  }
12492
12540
  abortStream();
@@ -12581,7 +12629,7 @@ ${COLORS.system("[没有正在执行的请求]")}`);
12581
12629
  try {
12582
12630
  await this.options.onAfterExecute(this.lastUsage);
12583
12631
  } catch (hookError) {
12584
- logger4.warn("onAfterExecute hook failed (ink path)", { error: String(hookError) });
12632
+ logger5.warn("onAfterExecute hook failed (ink path)", { error: String(hookError) });
12585
12633
  }
12586
12634
  }
12587
12635
  } catch (err) {
@@ -12601,7 +12649,7 @@ ${COLORS.system("[没有正在执行的请求]")}`);
12601
12649
  await this.shutdown();
12602
12650
  resolve();
12603
12651
  }).catch(async (err) => {
12604
- logger4.error("ink REPL render failed", { error: String(err) });
12652
+ logger5.error("ink REPL render failed", { error: String(err) });
12605
12653
  await this.shutdown();
12606
12654
  resolve();
12607
12655
  });
@@ -12658,7 +12706,7 @@ ${COLORS.system("[没有正在执行的请求]")}`);
12658
12706
  }
12659
12707
  if (key.name === "escape") {
12660
12708
  this.handleEscKey().catch((err) => {
12661
- logger4.warn(`[Esc] handleEscKey failed: ${err}`);
12709
+ logger5.warn(`[Esc] handleEscKey failed: ${err}`);
12662
12710
  });
12663
12711
  }
12664
12712
  };
@@ -12853,7 +12901,7 @@ ${COLORS.system("[通知2] ❯ " + message.replace(/\n/g, `
12853
12901
  try {
12854
12902
  await this.options.onAfterExecute(this.lastUsage);
12855
12903
  } catch (hookError) {
12856
- logger4.warn("onAfterExecute hook failed", { error: String(hookError) });
12904
+ logger5.warn("onAfterExecute hook failed", { error: String(hookError) });
12857
12905
  }
12858
12906
  }
12859
12907
  } catch (error) {
@@ -13016,12 +13064,12 @@ ${COLORS.error("❌ 执行失败: " + error)}
13016
13064
  }
13017
13065
  async handleEscKey() {
13018
13066
  if (this.isShuttingDown) {
13019
- logger4.debug("[Esc] handler entered (isShuttingDown=true) → early return");
13067
+ logger5.debug("[Esc] handler entered (isShuttingDown=true) → early return");
13020
13068
  return;
13021
13069
  }
13022
13070
  const mainSessionIdEarly = this.options.sessionId;
13023
13071
  const hasActiveDescendant = mainSessionIdEarly ? hasActiveDescendants(this.env, mainSessionIdEarly) : false;
13024
- logger4.debug(`[Esc] handler entered: isExecuting=${this.isExecuting} hasActiveDescendant=${hasActiveDescendant} lastEscPressTime=${this.lastEscPressTime}`);
13072
+ logger5.debug(`[Esc] handler entered: isExecuting=${this.isExecuting} hasActiveDescendant=${hasActiveDescendant} lastEscPressTime=${this.lastEscPressTime}`);
13025
13073
  const now = Date.now();
13026
13074
  const isExecuting = this.isExecuting;
13027
13075
  const canCascade = isExecuting || hasActiveDescendant;
@@ -13030,17 +13078,17 @@ ${COLORS.error("❌ 执行失败: " + error)}
13030
13078
  this.lastEscPressTime = now;
13031
13079
  }
13032
13080
  if (!canCascade) {
13033
- logger4.debug("[Esc] idle no-op: isExecuting=false, no active descendant");
13081
+ logger5.debug("[Esc] idle no-op: isExecuting=false, no active descendant");
13034
13082
  return;
13035
13083
  }
13036
13084
  if (this.env) {
13037
13085
  const agentComponent = this.env.getComponent("agent");
13038
13086
  if (agentComponent?.abortAll) {
13039
13087
  agentComponent.abortAll();
13040
- logger4.debug("[Esc] AgentComponent.abortAll() called");
13088
+ logger5.debug("[Esc] AgentComponent.abortAll() called");
13041
13089
  } else if (agentComponent?.abort) {
13042
13090
  agentComponent.abort("default");
13043
- logger4.debug("[Esc] AgentComponent.abort('default') called (fallback)");
13091
+ logger5.debug("[Esc] AgentComponent.abort('default') called (fallback)");
13044
13092
  }
13045
13093
  }
13046
13094
  abortStream();
@@ -13048,7 +13096,7 @@ ${COLORS.error("❌ 执行失败: " + error)}
13048
13096
  try {
13049
13097
  this.inkApi.finishAssistantStream();
13050
13098
  } catch (err) {
13051
- logger4.warn("[Esc] finishAssistantStream failed", { error: String(err) });
13099
+ logger5.warn("[Esc] finishAssistantStream failed", { error: String(err) });
13052
13100
  }
13053
13101
  }
13054
13102
  const mainSessionId = this.options.sessionId;
@@ -13061,9 +13109,9 @@ ${COLORS.error("❌ 执行失败: " + error)}
13061
13109
  timer = setTimeout(() => reject(new Error("cascade timeout 5s")), 5000);
13062
13110
  })
13063
13111
  ]);
13064
- logger4.info(`[Esc] cascade stop force=${result.force} tasks=${result.stoppedTaskIds.length} workflows=${result.stoppedWorkflowSessionIds.length} errors=${result.errors.length}`);
13112
+ logger5.info(`[Esc] cascade stop force=${result.force} tasks=${result.stoppedTaskIds.length} workflows=${result.stoppedWorkflowSessionIds.length} errors=${result.errors.length}`);
13065
13113
  } catch (err) {
13066
- logger4.warn(`[Esc] cascade stop failed: ${err}`);
13114
+ logger5.warn(`[Esc] cascade stop failed: ${err}`);
13067
13115
  } finally {
13068
13116
  if (timer !== undefined)
13069
13117
  clearTimeout(timer);
@@ -13445,11 +13493,27 @@ function createInteractiveCommand(externalEnvService) {
13445
13493
  envService,
13446
13494
  shouldDisposeEnvService,
13447
13495
  output,
13448
- logger: logger4,
13496
+ logger: logger5,
13449
13497
  console
13450
13498
  });
13451
13499
  }
13452
13500
  }, env);
13501
+ registerShutdownHandlers(async () => {
13502
+ await shutdownInteractiveSteps({
13503
+ memoryPluginDispose,
13504
+ memoryPluginInstance,
13505
+ lspManagerDispose,
13506
+ pluginAdapterDispose,
13507
+ eventHandler,
13508
+ queryExecutor,
13509
+ env,
13510
+ envService,
13511
+ shouldDisposeEnvService,
13512
+ output,
13513
+ logger: logger5,
13514
+ console
13515
+ });
13516
+ });
13453
13517
  const eventHandler = new EventHandler({
13454
13518
  env,
13455
13519
  eventTypes: ["task.background.*", "event-source.event.*"],
@@ -13679,9 +13743,9 @@ var App = ({
13679
13743
  }, undefined, true, undefined, this),
13680
13744
  /* @__PURE__ */ jsxDEV6(Static2, {
13681
13745
  items: history,
13682
- children: (item) => /* @__PURE__ */ jsxDEV6(HistoryEntry, {
13746
+ children: (item, index) => /* @__PURE__ */ jsxDEV6(HistoryEntry, {
13683
13747
  item
13684
- }, `${item.role}:${item.content}`, false, undefined, this)
13748
+ }, item.id ?? `${item.role}:${index}`, false, undefined, this)
13685
13749
  }, undefined, false, undefined, this),
13686
13750
  status === "thinking" ? /* @__PURE__ */ jsxDEV6(Box4, {
13687
13751
  marginBottom: 1,
@@ -13962,6 +14026,7 @@ var RunScreen = ({
13962
14026
  const assistantTurnCountRef = useRef2(0);
13963
14027
  const isProcessingRef = useRef2(0);
13964
14028
  const executeQueryRef = useRef2(null);
14029
+ const historyIdRef = useRef2(0);
13965
14030
  const width = process.stdout.columns ?? 80;
13966
14031
  const doExit = useCallback4(() => {
13967
14032
  onExit?.();
@@ -13980,15 +14045,20 @@ var RunScreen = ({
13980
14045
  const rendered = markdownEnabled2 ? renderMarkdown(responseText, { width }) : responseText;
13981
14046
  const finalText = truncateForTerminal(rendered, maxLines);
13982
14047
  stream.finish();
13983
- setHistory((h) => [...h, { role: "assistant", content: finalText }]);
14048
+ const assistantId = `h${++historyIdRef.current}`;
14049
+ setHistory((h) => [
14050
+ ...h,
14051
+ { role: "assistant", content: finalText, id: assistantId }
14052
+ ]);
13984
14053
  assistantTurnCountRef.current += 1;
13985
14054
  } catch (err) {
13986
14055
  const msg = err instanceof Error ? err.message : String(err);
13987
14056
  stream.fail(err instanceof Error ? err : new Error(msg));
13988
14057
  setErrorMessage(msg);
14058
+ const errorId = `h${++historyIdRef.current}`;
13989
14059
  setHistory((h) => [
13990
14060
  ...h,
13991
- { role: "tool", content: `❌ Error: ${msg}` }
14061
+ { role: "tool", content: `❌ Error: ${msg}`, id: errorId }
13992
14062
  ]);
13993
14063
  } finally {
13994
14064
  isProcessingRef.current -= 1;
@@ -14001,9 +14071,10 @@ var RunScreen = ({
14001
14071
  if (!onRegisterHandlers)
14002
14072
  return;
14003
14073
  const handleNotification = async (message, _envEvent) => {
14074
+ const notifId = `h${++historyIdRef.current}`;
14004
14075
  setHistory((h) => [
14005
14076
  ...h,
14006
- { role: "tool", content: `\uD83D\uDCE2 ${message}` }
14077
+ { role: "tool", content: `\uD83D\uDCE2 ${message}`, id: notifId }
14007
14078
  ]);
14008
14079
  if (executeQueryRef.current) {
14009
14080
  await executeQueryRef.current(message);
@@ -14027,10 +14098,12 @@ var RunScreen = ({
14027
14098
  };
14028
14099
  const result = dispatchSlashCommand(trimmed, slashCtx);
14029
14100
  if (result.kind === "exit") {
14101
+ const userId2 = `h${++historyIdRef.current}`;
14102
+ const toolId = `h${++historyIdRef.current}`;
14030
14103
  setHistory((h) => [
14031
14104
  ...h,
14032
- { role: "user", content: trimmed },
14033
- { role: "tool", content: "\uD83D\uDC4B Goodbye!" }
14105
+ { role: "user", content: trimmed, id: userId2 },
14106
+ { role: "tool", content: "\uD83D\uDC4B Goodbye!", id: toolId }
14034
14107
  ]);
14035
14108
  doExit();
14036
14109
  return;
@@ -14044,35 +14117,46 @@ var RunScreen = ({
14044
14117
  setErrorMessage(null);
14045
14118
  return;
14046
14119
  }
14120
+ const userId2 = `h${++historyIdRef.current}`;
14121
+ const toolId = `h${++historyIdRef.current}`;
14047
14122
  setHistory((h) => [
14048
14123
  ...h,
14049
- { role: "user", content: trimmed },
14050
- { role: "tool", content: result.message }
14124
+ { role: "user", content: trimmed, id: userId2 },
14125
+ { role: "tool", content: result.message, id: toolId }
14051
14126
  ]);
14052
14127
  return;
14053
14128
  }
14054
14129
  if (result.kind === "unknown") {
14130
+ const userId2 = `h${++historyIdRef.current}`;
14131
+ const toolId = `h${++historyIdRef.current}`;
14055
14132
  setHistory((h) => [
14056
14133
  ...h,
14057
- { role: "user", content: trimmed },
14058
- { role: "tool", content: `⚠ ${result.message}` }
14134
+ { role: "user", content: trimmed, id: userId2 },
14135
+ { role: "tool", content: `⚠ ${result.message}`, id: toolId }
14059
14136
  ]);
14060
14137
  return;
14061
14138
  }
14062
14139
  if (result.kind === "set-max-lines") {
14063
14140
  setMaxLines(result.value);
14141
+ const userId2 = `h${++historyIdRef.current}`;
14142
+ const toolId = `h${++historyIdRef.current}`;
14064
14143
  setHistory((h) => [
14065
14144
  ...h,
14066
- { role: "user", content: trimmed },
14145
+ { role: "user", content: trimmed, id: userId2 },
14067
14146
  {
14068
14147
  role: "tool",
14069
- content: `max-lines set to ${result.value}`
14148
+ content: `max-lines set to ${result.value}`,
14149
+ id: toolId
14070
14150
  }
14071
14151
  ]);
14072
14152
  return;
14073
14153
  }
14074
14154
  setErrorMessage(null);
14075
- setHistory((h) => [...h, { role: "user", content: trimmed }]);
14155
+ const userId = `h${++historyIdRef.current}`;
14156
+ setHistory((h) => [
14157
+ ...h,
14158
+ { role: "user", content: trimmed, id: userId }
14159
+ ]);
14076
14160
  setUserHistory((uh) => [trimmed, ...uh]);
14077
14161
  userTurnCountRef.current += 1;
14078
14162
  await executeQuery(trimmed);
@@ -14125,6 +14209,89 @@ var RunTui_default = RunScreen;
14125
14209
 
14126
14210
  // src/commands/run.ts
14127
14211
  init_markdown();
14212
+
14213
+ // src/commands/run-shutdown.ts
14214
+ import { TempFileManager as TempFileManager3 } from "@ai-setting/roy-agent-core/utils/temp-file-manager";
14215
+ async function shutdownRunSteps(deps) {
14216
+ const {
14217
+ lspManagerDispose,
14218
+ pluginAdapterDispose,
14219
+ stopEventHandler,
14220
+ stopTaskEventHandler,
14221
+ queryExecutor,
14222
+ env,
14223
+ envService,
14224
+ shouldDisposeEnvService,
14225
+ output,
14226
+ logger: logger6
14227
+ } = deps;
14228
+ try {
14229
+ stopEventHandler();
14230
+ } catch (err) {
14231
+ logger6?.warn?.(`[Shutdown] EventHandler.stop failed: ${err}`);
14232
+ }
14233
+ try {
14234
+ stopTaskEventHandler();
14235
+ } catch (err) {
14236
+ logger6?.warn?.(`[Shutdown] TaskEventHandler.stop failed: ${err}`);
14237
+ }
14238
+ if (lspManagerDispose) {
14239
+ try {
14240
+ await lspManagerDispose();
14241
+ } catch (err) {
14242
+ logger6?.warn?.(`[Shutdown] LSP Manager dispose failed: ${err}`);
14243
+ }
14244
+ }
14245
+ if (pluginAdapterDispose) {
14246
+ try {
14247
+ await pluginAdapterDispose();
14248
+ } catch (err) {
14249
+ logger6?.warn?.(`[Shutdown] PluginAdapter dispose failed: ${err}`);
14250
+ }
14251
+ }
14252
+ try {
14253
+ queryExecutor.dispose();
14254
+ } catch (err) {
14255
+ logger6?.warn?.(`[Shutdown] QueryExecutor.dispose failed: ${err}`);
14256
+ }
14257
+ try {
14258
+ const eventSourceComponent = env.getComponent("event-source");
14259
+ if (eventSourceComponent) {
14260
+ const sources = eventSourceComponent.list?.();
14261
+ if (sources && sources.length > 0) {
14262
+ for (const source of sources) {
14263
+ const status = eventSourceComponent.getStatus?.(source.id);
14264
+ logger6?.debug?.(`[Shutdown] 检查事件源: ${source.id}, 状态: ${status}`);
14265
+ if (status !== "stopped" && status !== "created") {
14266
+ try {
14267
+ await eventSourceComponent.stopSource(source.id);
14268
+ output?.info?.(`✓ 已停止事件源: ${source.name} (${status})`);
14269
+ } catch (error) {
14270
+ output?.warn?.(`⚠️ 停止事件源 ${source.name} 失败: ${error}`);
14271
+ }
14272
+ }
14273
+ }
14274
+ }
14275
+ }
14276
+ } catch (err) {
14277
+ logger6?.warn?.(`[Shutdown] EventSource cleanup failed: ${err}`);
14278
+ }
14279
+ if (shouldDisposeEnvService) {
14280
+ try {
14281
+ await envService.dispose();
14282
+ } catch (err) {
14283
+ logger6?.warn?.(`[Shutdown] envService.dispose failed: ${err}`);
14284
+ }
14285
+ }
14286
+ try {
14287
+ await TempFileManager3.getInstance().cleanupAll();
14288
+ logger6?.debug?.("[Shutdown] TempFileManager cleaned up");
14289
+ } catch (err) {
14290
+ logger6?.warn?.(`[Shutdown] TempFileManager cleanup failed: ${err}`);
14291
+ }
14292
+ }
14293
+
14294
+ // src/commands/run.ts
14128
14295
  async function loadHandleQuery(options, hooks) {
14129
14296
  const quiet = CliQuietModeService.getInstance().isQuiet();
14130
14297
  const output = new OutputService2;
@@ -14234,7 +14401,8 @@ async function loadHandleQuery(options, hooks) {
14234
14401
  eventSourceSystemPrompt,
14235
14402
  sessionComponent,
14236
14403
  eventHandler,
14237
- taskEventHandler
14404
+ taskEventHandler,
14405
+ sessionManager
14238
14406
  };
14239
14407
  }
14240
14408
  function createStreamHandleQuery(queryExecutor, sessionId, options, agentContext, eventSourceSystemPrompt) {
@@ -14350,16 +14518,33 @@ function createRunCommand() {
14350
14518
  };
14351
14519
  const {
14352
14520
  handleQuery,
14521
+ envService,
14522
+ env,
14353
14523
  queryExecutor,
14354
14524
  sessionId,
14355
14525
  agentContext,
14356
14526
  eventSourceSystemPrompt,
14357
14527
  eventHandler,
14358
- taskEventHandler
14528
+ taskEventHandler,
14529
+ sessionManager: _sessionManager
14359
14530
  } = await loadHandleQuery(argv, {
14360
14531
  isIdle: () => runState.isIdle(),
14361
14532
  onNotification: (msg, envEvent) => runState.onNotification(msg, envEvent)
14362
14533
  });
14534
+ const shutdownHandle = registerShutdownHandlers(async () => {
14535
+ await shutdownRunSteps({
14536
+ lspManagerDispose: null,
14537
+ pluginAdapterDispose: null,
14538
+ stopEventHandler,
14539
+ stopTaskEventHandler,
14540
+ queryExecutor,
14541
+ env,
14542
+ envService,
14543
+ shouldDisposeEnvService: true,
14544
+ output: undefined,
14545
+ logger: undefined
14546
+ });
14547
+ });
14363
14548
  const streamHandleQuery = createStreamHandleQuery(queryExecutor, sessionId, argv, agentContext, eventSourceSystemPrompt);
14364
14549
  const stopEventHandler = eventHandler.start();
14365
14550
  const stopTaskEventHandler = taskEventHandler.start();
@@ -14379,9 +14564,19 @@ function createRunCommand() {
14379
14564
  try {
14380
14565
  await waitUntilExit();
14381
14566
  } finally {
14382
- stopEventHandler();
14383
- stopTaskEventHandler();
14384
- queryExecutor.dispose();
14567
+ shutdownHandle.dispose();
14568
+ await shutdownRunSteps({
14569
+ lspManagerDispose: null,
14570
+ pluginAdapterDispose: null,
14571
+ stopEventHandler,
14572
+ stopTaskEventHandler,
14573
+ queryExecutor,
14574
+ env,
14575
+ envService,
14576
+ shouldDisposeEnvService: true,
14577
+ output: undefined,
14578
+ logger: undefined
14579
+ });
14385
14580
  process.exit(0);
14386
14581
  }
14387
14582
  }
@@ -14394,7 +14589,7 @@ import React8 from "react";
14394
14589
  import { render as render2 } from "ink";
14395
14590
 
14396
14591
  // src/ChatTui.tsx
14397
- import { useEffect as useEffect6, useState as useState7 } from "react";
14592
+ import { useEffect as useEffect6, useRef as useRef3, useState as useState7 } from "react";
14398
14593
  import { Box as Box7, useApp as useApp3 } from "ink";
14399
14594
  init_markdown();
14400
14595
  import { jsxDEV as jsxDEV9 } from "react/jsx-dev-runtime";
@@ -14408,8 +14603,9 @@ var ChatScreen = ({
14408
14603
  markdownEnabled: markdownEnabled2
14409
14604
  }) => {
14410
14605
  const [history, setHistory] = useState7([
14411
- { role: "user", content: message }
14606
+ { role: "user", content: message, id: "h-init-1" }
14412
14607
  ]);
14608
+ const historyIdRef = useRef3(1);
14413
14609
  const stream = useStreamingAccumulator();
14414
14610
  const { exit } = useApp3();
14415
14611
  const width = process.stdout.columns ?? 80;
@@ -14429,16 +14625,21 @@ var ChatScreen = ({
14429
14625
  stream.finish();
14430
14626
  const rendered = markdownEnabled2 ? renderMarkdown(responseText, { width }) : responseText;
14431
14627
  const finalText = truncateForTerminal(rendered, maxLines);
14432
- setHistory((h) => [...h, { role: "assistant", content: finalText }]);
14628
+ const assistantId = `h${++historyIdRef.current}`;
14629
+ setHistory((h) => [
14630
+ ...h,
14631
+ { role: "assistant", content: finalText, id: assistantId }
14632
+ ]);
14433
14633
  setTimeout(() => exit(), 100);
14434
14634
  } catch (err) {
14435
14635
  if (cancelled)
14436
14636
  return;
14437
14637
  const msg = err instanceof Error ? err.message : String(err);
14438
14638
  stream.fail(err instanceof Error ? err : new Error(msg));
14639
+ const errorId = `h${++historyIdRef.current}`;
14439
14640
  setHistory((h) => [
14440
14641
  ...h,
14441
- { role: "tool", content: `❌ Error: ${msg}` }
14642
+ { role: "tool", content: `❌ Error: ${msg}`, id: errorId }
14442
14643
  ]);
14443
14644
  setTimeout(() => exit(), 100);
14444
14645
  }
@@ -25492,24 +25693,106 @@ var LspCommand = {
25492
25693
 
25493
25694
  // src/commands/install/index.ts
25494
25695
  import { spawnSync } from "node:child_process";
25495
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync, mkdirSync, rmSync } from "node:fs";
25496
- import { join as join5, resolve } from "node:path";
25696
+ import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync, mkdirSync, rmSync as rmSync2 } from "node:fs";
25697
+ import { join as join6, resolve } from "node:path";
25497
25698
  import { homedir as homedir4 } from "node:os";
25699
+
25700
+ // src/commands/install/cleanup-stale-plugin-copies.ts
25701
+ import { existsSync as existsSync4, readFileSync as readFileSync3, rmSync, cpSync } from "node:fs";
25702
+ import { join as join5 } from "node:path";
25703
+ import { homedir as nodeHomedir, tmpdir as nodeTmpdir } from "node:os";
25704
+ function cleanupStalePluginCopies(packageName, options) {
25705
+ const HOME = options.homedir ?? nodeHomedir();
25706
+ const paths = options.localNodeModulesPaths ?? [
25707
+ join5(HOME, "node_modules"),
25708
+ join5(HOME, ".roy-agent", "node_modules")
25709
+ ];
25710
+ const backupRoot = options.backupDir ?? nodeTmpdir();
25711
+ const result = {
25712
+ cleaned: [],
25713
+ skipped: [],
25714
+ backups: []
25715
+ };
25716
+ if (!options.globalVersion) {
25717
+ for (const p of paths) {
25718
+ const candidate = join5(p, packageName);
25719
+ result.skipped.push({ path: candidate, reason: "no-global-version" });
25720
+ }
25721
+ return result;
25722
+ }
25723
+ for (const nmPath of paths) {
25724
+ const localPkgDir = join5(nmPath, packageName);
25725
+ const localPkgJson = join5(localPkgDir, "package.json");
25726
+ if (!existsSync4(localPkgJson)) {
25727
+ result.skipped.push({ path: localPkgDir, reason: "no-local-copy" });
25728
+ continue;
25729
+ }
25730
+ let localVersion = null;
25731
+ try {
25732
+ const pkg = JSON.parse(readFileSync3(localPkgJson, "utf-8"));
25733
+ localVersion = typeof pkg.version === "string" ? pkg.version : null;
25734
+ } catch {
25735
+ localVersion = null;
25736
+ }
25737
+ const shouldClean = localVersion === null || compareVersions(localVersion, options.globalVersion) < 0;
25738
+ if (!shouldClean) {
25739
+ result.skipped.push({ path: localPkgDir, reason: "local-equal-or-newer" });
25740
+ continue;
25741
+ }
25742
+ let backupPath = null;
25743
+ try {
25744
+ const safeName = packageName.replace(/@/g, "").replace(/[^a-zA-Z0-9._/-]/g, "-");
25745
+ const safeLocalVersion = (localVersion ?? "unknown").replace(/[^a-zA-Z0-9._-]/g, "-");
25746
+ const timestamp = Date.now();
25747
+ backupPath = join5(backupRoot, `${safeName}-${safeLocalVersion}-backup-${timestamp}`);
25748
+ cpSync(localPkgDir, backupPath, { recursive: true });
25749
+ result.backups.push({ originalPath: localPkgDir, backupPath });
25750
+ } catch (err) {
25751
+ backupPath = null;
25752
+ console.warn(`⚠️ Failed to backup stale copy at '${localPkgDir}': ${err}`);
25753
+ }
25754
+ try {
25755
+ rmSync(localPkgDir, { recursive: true, force: true });
25756
+ result.cleaned.push(localPkgDir);
25757
+ } catch (err) {
25758
+ console.warn(`⚠️ Failed to remove stale copy at '${localPkgDir}': ${err}`);
25759
+ result.skipped.push({ path: localPkgDir, reason: "cleanup-failed" });
25760
+ if (backupPath) {
25761
+ console.warn(` ↪ Backup preserved at '${backupPath}' for manual recovery`);
25762
+ }
25763
+ }
25764
+ }
25765
+ return result;
25766
+ }
25767
+ function compareVersions(a, b) {
25768
+ const pa = a.split(".").map((s) => Number.parseInt(s, 10));
25769
+ const pb = b.split(".").map((s) => Number.parseInt(s, 10));
25770
+ for (let i = 0;i < 3; i++) {
25771
+ const na = Number.isFinite(pa[i]) ? pa[i] : 0;
25772
+ const nb = Number.isFinite(pb[i]) ? pb[i] : 0;
25773
+ const diff = na - nb;
25774
+ if (diff !== 0)
25775
+ return diff;
25776
+ }
25777
+ return 0;
25778
+ }
25779
+
25780
+ // src/commands/install/index.ts
25498
25781
  function getConfigDir(scope) {
25499
- return scope === "global" ? join5(homedir4(), ".roy-agent") : join5(process.cwd(), ".roy");
25782
+ return scope === "global" ? join6(homedir4(), ".roy-agent") : join6(process.cwd(), ".roy");
25500
25783
  }
25501
25784
  function detectDefaultScope() {
25502
- const projectDir = join5(process.cwd(), ".roy");
25503
- return existsSync4(projectDir) ? "project" : "global";
25785
+ const projectDir = join6(process.cwd(), ".roy");
25786
+ return existsSync5(projectDir) ? "project" : "global";
25504
25787
  }
25505
25788
  function getConfigPath(scope) {
25506
- return join5(getConfigDir(scope), "plugins.json");
25789
+ return join6(getConfigDir(scope), "plugins.json");
25507
25790
  }
25508
25791
  function loadPlugins(scope) {
25509
25792
  const configPath = getConfigPath(scope);
25510
25793
  try {
25511
- if (existsSync4(configPath)) {
25512
- const raw = readFileSync3(configPath, "utf-8");
25794
+ if (existsSync5(configPath)) {
25795
+ const raw = readFileSync4(configPath, "utf-8");
25513
25796
  const config = JSON.parse(raw);
25514
25797
  return config.plugins ?? [];
25515
25798
  }
@@ -25519,7 +25802,7 @@ function loadPlugins(scope) {
25519
25802
  function savePlugins(plugins, scope) {
25520
25803
  const configDir = getConfigDir(scope);
25521
25804
  const configPath = getConfigPath(scope);
25522
- if (!existsSync4(configDir)) {
25805
+ if (!existsSync5(configDir)) {
25523
25806
  mkdirSync(configDir, { recursive: true });
25524
25807
  }
25525
25808
  const config = { plugins };
@@ -25535,10 +25818,10 @@ function stripVersionSpec(packageName) {
25535
25818
  function readPackageInfo(packageName) {
25536
25819
  const bareName = packageName.startsWith("/") || packageName.startsWith(".") ? packageName : stripVersionSpec(packageName);
25537
25820
  if (packageName.startsWith("/") || packageName.startsWith(".")) {
25538
- const localPkgPath = join5(packageName, "package.json");
25539
- if (existsSync4(localPkgPath)) {
25821
+ const localPkgPath = join6(packageName, "package.json");
25822
+ if (existsSync5(localPkgPath)) {
25540
25823
  try {
25541
- const pkg = JSON.parse(readFileSync3(localPkgPath, "utf-8"));
25824
+ const pkg = JSON.parse(readFileSync4(localPkgPath, "utf-8"));
25542
25825
  return {
25543
25826
  version: pkg.version ?? "0.0.0",
25544
25827
  name: pkg.name ?? packageName,
@@ -25550,19 +25833,19 @@ function readPackageInfo(packageName) {
25550
25833
  }
25551
25834
  }
25552
25835
  const searchPaths = [
25553
- join5(process.cwd(), "node_modules", bareName, "package.json"),
25836
+ join6(process.cwd(), "node_modules", bareName, "package.json"),
25554
25837
  resolve(process.cwd(), "..", "node_modules", bareName, "package.json"),
25555
- join5(process.cwd(), ".roy", "plugins", bareName, "package.json"),
25838
+ join6(process.cwd(), ".roy", "plugins", bareName, "package.json"),
25556
25839
  ...(() => {
25557
25840
  const globalRoot = getNpmGlobalRoot();
25558
- return globalRoot ? [join5(globalRoot, bareName, "package.json")] : [];
25841
+ return globalRoot ? [join6(globalRoot, bareName, "package.json")] : [];
25559
25842
  })(),
25560
- join5(homedir4(), ".roy-agent", "node_modules", bareName, "package.json")
25843
+ join6(homedir4(), ".roy-agent", "node_modules", bareName, "package.json")
25561
25844
  ];
25562
25845
  for (const pkgPath of searchPaths) {
25563
25846
  try {
25564
- if (existsSync4(pkgPath)) {
25565
- const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
25847
+ if (existsSync5(pkgPath)) {
25848
+ const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
25566
25849
  return {
25567
25850
  version: pkg.version ?? "0.0.0",
25568
25851
  name: pkg.name ?? bareName,
@@ -25584,19 +25867,6 @@ function getNpmGlobalRoot() {
25584
25867
  }
25585
25868
  return null;
25586
25869
  }
25587
- function cleanupStaleCpCopy(packageName) {
25588
- const cpDir = join5(homedir4(), ".roy-agent", "node_modules", packageName);
25589
- if (!existsSync4(cpDir)) {
25590
- return false;
25591
- }
25592
- try {
25593
- rmSync(cpDir, { recursive: true, force: true });
25594
- return true;
25595
- } catch (err) {
25596
- console.warn(`⚠️ Failed to clean stale cp copy at '${cpDir}': ${err}`);
25597
- return false;
25598
- }
25599
- }
25600
25870
  async function verifyInstalledVersion(packageName, declaredVersion) {
25601
25871
  const actual = readPackageInfo(packageName);
25602
25872
  if (!actual || !actual.version) {
@@ -25608,15 +25878,15 @@ async function verifyInstalledVersion(packageName, declaredVersion) {
25608
25878
  return { ok: true, actualVersion: actual.version };
25609
25879
  }
25610
25880
  function getPackageManagerCommand() {
25611
- if (existsSync4(join5(process.cwd(), "pnpm-lock.yaml"))) {
25612
- if (existsSync4(join5(process.cwd(), "pnpm-workspace.yaml"))) {
25881
+ if (existsSync5(join6(process.cwd(), "pnpm-lock.yaml"))) {
25882
+ if (existsSync5(join6(process.cwd(), "pnpm-workspace.yaml"))) {
25613
25883
  return "pnpm -w";
25614
25884
  }
25615
25885
  return "pnpm";
25616
25886
  }
25617
- if (existsSync4(join5(process.cwd(), "yarn.lock")))
25887
+ if (existsSync5(join6(process.cwd(), "yarn.lock")))
25618
25888
  return "yarn";
25619
- if (existsSync4(join5(process.cwd(), "bun.lock")) || existsSync4(join5(process.cwd(), "bun.lockb")))
25889
+ if (existsSync5(join6(process.cwd(), "bun.lock")) || existsSync5(join6(process.cwd(), "bun.lockb")))
25620
25890
  return "bun";
25621
25891
  return "npm";
25622
25892
  }
@@ -25655,14 +25925,14 @@ var InstallPackageCommand = {
25655
25925
  }
25656
25926
  resolvedName = info2.name;
25657
25927
  const srcDir = resolve(packageName);
25658
- if (!existsSync4(srcDir)) {
25928
+ if (!existsSync5(srcDir)) {
25659
25929
  console.error(`
25660
25930
  ❌ Local path '${srcDir}' does not exist.
25661
25931
  `);
25662
25932
  process.exit(1);
25663
25933
  }
25664
- const pluginDir = join5(process.cwd(), ".roy", "plugins", resolvedName);
25665
- if (!existsSync4(pluginDir)) {
25934
+ const pluginDir = join6(process.cwd(), ".roy", "plugins", resolvedName);
25935
+ if (!existsSync5(pluginDir)) {
25666
25936
  mkdirSync(pluginDir, { recursive: true });
25667
25937
  }
25668
25938
  const result = spawnSync("cp", ["-r", `${srcDir}/.`, pluginDir], {
@@ -25697,12 +25967,12 @@ var InstallPackageCommand = {
25697
25967
  process.exit(1);
25698
25968
  }
25699
25969
  resolvedName = info2.name;
25700
- const pluginDir = join5(process.cwd(), ".roy", "plugins", resolvedName);
25701
- if (!existsSync4(pluginDir)) {
25970
+ const pluginDir = join6(process.cwd(), ".roy", "plugins", resolvedName);
25971
+ if (!existsSync5(pluginDir)) {
25702
25972
  mkdirSync(pluginDir, { recursive: true });
25703
25973
  }
25704
- const nmPath = join5(process.cwd(), "node_modules", packageName);
25705
- if (existsSync4(nmPath)) {
25974
+ const nmPath = join6(process.cwd(), "node_modules", packageName);
25975
+ if (existsSync5(nmPath)) {
25706
25976
  spawnSync("cp", ["-r", `${nmPath}/.`, pluginDir], { stdio: "inherit", shell: false });
25707
25977
  }
25708
25978
  }
@@ -25722,11 +25992,24 @@ var InstallPackageCommand = {
25722
25992
  }
25723
25993
  const tmpInfo = readPackageInfo(packageName);
25724
25994
  if (tmpInfo) {
25725
- const cleaned = cleanupStaleCpCopy(tmpInfo.name);
25726
- if (cleaned) {
25995
+ const cleanupResult = cleanupStalePluginCopies(tmpInfo.name, {
25996
+ globalVersion: tmpInfo.version,
25997
+ homedir: homedir4()
25998
+ });
25999
+ if (cleanupResult.cleaned.length > 0) {
25727
26000
  console.log(`
25728
- \uD83E\uDDF9 Cleaned stale cp copy: ~/.roy-agent/node_modules/${tmpInfo.name}
25729
- `);
26001
+ \uD83E\uDDF9 Cleaned stale plugin copies:`);
26002
+ for (const path9 of cleanupResult.cleaned) {
26003
+ console.log(` - ${path9}`);
26004
+ }
26005
+ if (cleanupResult.backups.length > 0) {
26006
+ console.log(`
26007
+ \uD83D\uDCE6 Backups (for manual recovery):`);
26008
+ for (const b of cleanupResult.backups) {
26009
+ console.log(` ${b.originalPath} → ${b.backupPath}`);
26010
+ }
26011
+ }
26012
+ console.log();
25730
26013
  }
25731
26014
  }
25732
26015
  }
@@ -25851,8 +26134,8 @@ var InstallRemoveCommand = {
25851
26134
  \uD83D\uDDD1️ Removing plugin: ${plugin.packageName}@${plugin.version} (${scope})
25852
26135
  `);
25853
26136
  if (scope === "project") {
25854
- const pluginDir = join5(process.cwd(), ".roy", "plugins", plugin.packageName);
25855
- if (existsSync4(pluginDir)) {
26137
+ const pluginDir = join6(process.cwd(), ".roy", "plugins", plugin.packageName);
26138
+ if (existsSync5(pluginDir)) {
25856
26139
  spawnSync("rm", ["-rf", pluginDir], { stdio: "inherit", shell: false });
25857
26140
  console.log(` Removed plugin directory: ${pluginDir}`);
25858
26141
  }
@@ -25867,8 +26150,8 @@ var InstallRemoveCommand = {
25867
26150
  `);
25868
26151
  process.exit(result.status ?? 1);
25869
26152
  }
25870
- const globalNm = join5(homedir4(), ".roy-agent", "node_modules", plugin.packageName);
25871
- if (existsSync4(globalNm)) {
26153
+ const globalNm = join6(homedir4(), ".roy-agent", "node_modules", plugin.packageName);
26154
+ if (existsSync5(globalNm)) {
25872
26155
  spawnSync("rm", ["-rf", globalNm], { stdio: "inherit", shell: false });
25873
26156
  }
25874
26157
  }