zapmyco 0.13.0 → 0.14.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/dist/cli/index.mjs +835 -38
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.d.mts +3 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{loader-BVhTlDtc.mjs → loader-BD5Odmr5.mjs} +4 -3
- package/dist/{loader-BVhTlDtc.mjs.map → loader-BD5Odmr5.mjs.map} +1 -1
- package/package.json +3 -2
package/dist/cli/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { A as configureLogger, B as buildSkillSnapshot, D as eventBus, G as SESSION_DIR_NAME, K as VERSION, L as WebError, O as DEFAULT_COMPACTION_CONFIG, V as loadSkills, W as APP_NAME, d as SubAgentManager, f as SecurityBlockedError, g as runWithToolGuardContext, h as getToolGuardContext, i as AgentLlmFacade, j as logger, m as createToolInfoResolver, n as loadConfig, p as ToolGuard, q as __require, t as HOME_CONFIG_PATH, v as createLlmBasedAgent, z as ZapmycoErrorCode } from "../loader-
|
|
2
|
+
import { A as configureLogger, B as buildSkillSnapshot, D as eventBus, G as SESSION_DIR_NAME, K as VERSION, L as WebError, O as DEFAULT_COMPACTION_CONFIG, V as loadSkills, W as APP_NAME, d as SubAgentManager, f as SecurityBlockedError, g as runWithToolGuardContext, h as getToolGuardContext, i as AgentLlmFacade, j as logger, m as createToolInfoResolver, n as loadConfig, p as ToolGuard, q as __require, t as HOME_CONFIG_PATH, v as createLlmBasedAgent, z as ZapmycoErrorCode } from "../loader-BD5Odmr5.mjs";
|
|
3
3
|
import { createHash, randomBytes } from "node:crypto";
|
|
4
4
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
@@ -18,6 +18,7 @@ import i18next from "i18next";
|
|
|
18
18
|
import TurndownService from "turndown";
|
|
19
19
|
import { lookup } from "node:dns/promises";
|
|
20
20
|
import { promisify } from "node:util";
|
|
21
|
+
import { marked } from "marked";
|
|
21
22
|
import { Client } from "@modelcontextprotocol/sdk/client";
|
|
22
23
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
23
24
|
|
|
@@ -447,6 +448,14 @@ const noColor = {
|
|
|
447
448
|
|
|
448
449
|
//#endregion
|
|
449
450
|
//#region src/core/agent-team/agent-instance-manager.ts
|
|
451
|
+
/**
|
|
452
|
+
* Agent 实例管理器
|
|
453
|
+
*
|
|
454
|
+
* 管理所有 AgentInstance 的生命周期:创建、注册、状态转换、取消、清理。
|
|
455
|
+
* 全局实例注册表,支持按 team/type/depth/status 查询。
|
|
456
|
+
*
|
|
457
|
+
* @module core/agent-team
|
|
458
|
+
*/
|
|
450
459
|
const log$19 = logger.child("agent-instance-manager");
|
|
451
460
|
/**
|
|
452
461
|
* Agent 实例状态转换表
|
|
@@ -479,8 +488,9 @@ const TERMINAL_STATES = [
|
|
|
479
488
|
*
|
|
480
489
|
* 单例模式,全局唯一。
|
|
481
490
|
* 维护所有活跃和已完成的 Agent 实例的运行状态。
|
|
491
|
+
* 继承 EventEmitter 以支持 UI 组件实时监听状态变更。
|
|
482
492
|
*/
|
|
483
|
-
var AgentInstanceManager = class {
|
|
493
|
+
var AgentInstanceManager = class extends EventEmitter {
|
|
484
494
|
/** 实例注册表(按 instanceId 索引) */
|
|
485
495
|
instances = /* @__PURE__ */ new Map();
|
|
486
496
|
/**
|
|
@@ -517,6 +527,11 @@ var AgentInstanceManager = class {
|
|
|
517
527
|
depth,
|
|
518
528
|
parentInstanceId
|
|
519
529
|
});
|
|
530
|
+
this.emit("instance:registered", {
|
|
531
|
+
instanceId: instance.instanceId,
|
|
532
|
+
typeId: instance.typeId,
|
|
533
|
+
depth
|
|
534
|
+
});
|
|
520
535
|
return instance;
|
|
521
536
|
}
|
|
522
537
|
/**
|
|
@@ -542,16 +557,47 @@ var AgentInstanceManager = class {
|
|
|
542
557
|
});
|
|
543
558
|
return false;
|
|
544
559
|
}
|
|
560
|
+
const oldStatus = instance.status;
|
|
545
561
|
instance.status = newStatus;
|
|
546
562
|
log$19.debug("Agent 实例状态转换", {
|
|
547
563
|
instanceId,
|
|
548
564
|
typeId: instance.typeId,
|
|
549
|
-
from:
|
|
565
|
+
from: oldStatus,
|
|
566
|
+
to: newStatus
|
|
567
|
+
});
|
|
568
|
+
this.emit("instance:transitioned", {
|
|
569
|
+
instanceId,
|
|
570
|
+
typeId: instance.typeId,
|
|
571
|
+
from: oldStatus,
|
|
550
572
|
to: newStatus
|
|
551
573
|
});
|
|
552
574
|
return true;
|
|
553
575
|
}
|
|
554
576
|
/**
|
|
577
|
+
* 更新实例当前活动信息(供 UI 状态栏实时展示)
|
|
578
|
+
*
|
|
579
|
+
* @param instanceId - 实例 ID
|
|
580
|
+
* @param activity - 活动信息(工具名称、调用次数、参数等)
|
|
581
|
+
*/
|
|
582
|
+
setActivity(instanceId, activity) {
|
|
583
|
+
const instance = this.instances.get(instanceId);
|
|
584
|
+
if (!instance) return;
|
|
585
|
+
instance.currentActivity = activity;
|
|
586
|
+
this.emit("instance:activity", {
|
|
587
|
+
instanceId,
|
|
588
|
+
typeId: instance.typeId,
|
|
589
|
+
activity
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* 获取实例当前活动信息
|
|
594
|
+
*
|
|
595
|
+
* @param instanceId - 实例 ID
|
|
596
|
+
*/
|
|
597
|
+
getActivity(instanceId) {
|
|
598
|
+
return this.instances.get(instanceId)?.currentActivity;
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
555
601
|
* 获取指定实例
|
|
556
602
|
*
|
|
557
603
|
* @param instanceId - 实例 ID
|
|
@@ -3021,6 +3067,146 @@ function createStatusCommand() {
|
|
|
3021
3067
|
};
|
|
3022
3068
|
}
|
|
3023
3069
|
|
|
3070
|
+
//#endregion
|
|
3071
|
+
//#region src/cli/repl/components/agent-status-bar.ts
|
|
3072
|
+
/**
|
|
3073
|
+
* Agent 状态栏组件
|
|
3074
|
+
*
|
|
3075
|
+
* 实时显示正在运行的子 Agent 状态,类似 Claude Code 的 "Running N agents…" 效果。
|
|
3076
|
+
* 固定在 OutputArea 和 Editor 之间,无活跃 Agent 时自动隐藏。
|
|
3077
|
+
*
|
|
3078
|
+
* @module cli/repl/components
|
|
3079
|
+
*/
|
|
3080
|
+
/** 状态图标映射 */
|
|
3081
|
+
const STATUS_ICONS$1 = {
|
|
3082
|
+
idle: "○",
|
|
3083
|
+
running: "◉",
|
|
3084
|
+
paused: "◐",
|
|
3085
|
+
completed: "●",
|
|
3086
|
+
failed: "✕",
|
|
3087
|
+
cancelled: "◌"
|
|
3088
|
+
};
|
|
3089
|
+
/** Loading 动画帧 */
|
|
3090
|
+
const LOADING_FRAMES$2 = [
|
|
3091
|
+
"⠋",
|
|
3092
|
+
"⠙",
|
|
3093
|
+
"⠹",
|
|
3094
|
+
"⠸",
|
|
3095
|
+
"⠼",
|
|
3096
|
+
"⠴",
|
|
3097
|
+
"⠦",
|
|
3098
|
+
"⠧",
|
|
3099
|
+
"⠇",
|
|
3100
|
+
"⠏"
|
|
3101
|
+
];
|
|
3102
|
+
const LOADING_INTERVAL_MS$1 = 200;
|
|
3103
|
+
/** 树形连接线 */
|
|
3104
|
+
const TREE_BRANCH = "├── ";
|
|
3105
|
+
const TREE_LAST = "└── ";
|
|
3106
|
+
const TREE_PIPE = "│ ";
|
|
3107
|
+
const TREE_SPACE = " ";
|
|
3108
|
+
const TREE_CONT = "⎿ ";
|
|
3109
|
+
/**
|
|
3110
|
+
* Agent 状态栏组件
|
|
3111
|
+
*
|
|
3112
|
+
* 从 AgentInstanceManager 读取活跃实例状态,渲染为紧凑状态栏。
|
|
3113
|
+
*/
|
|
3114
|
+
var AgentStatusBar = class extends Container {
|
|
3115
|
+
/** 是否展开显示详情 */
|
|
3116
|
+
#expanded = false;
|
|
3117
|
+
/** loading 动画帧索引 */
|
|
3118
|
+
#loadingFrame = 0;
|
|
3119
|
+
/** loading 动画定时器 */
|
|
3120
|
+
#loadingTimer;
|
|
3121
|
+
/** 上次活跃实例快照(用于检测变化) */
|
|
3122
|
+
#lastActiveCount = 0;
|
|
3123
|
+
/**
|
|
3124
|
+
* 切换展开/折叠状态
|
|
3125
|
+
*/
|
|
3126
|
+
toggle() {
|
|
3127
|
+
this.#expanded = !this.#expanded;
|
|
3128
|
+
this.invalidate();
|
|
3129
|
+
}
|
|
3130
|
+
/** 获取当前展开状态 */
|
|
3131
|
+
get isExpanded() {
|
|
3132
|
+
return this.#expanded;
|
|
3133
|
+
}
|
|
3134
|
+
invalidate() {
|
|
3135
|
+
super.invalidate();
|
|
3136
|
+
}
|
|
3137
|
+
render(width) {
|
|
3138
|
+
const activeInstances = getAgentInstanceManager().listActive();
|
|
3139
|
+
if (activeInstances.length === 0) {
|
|
3140
|
+
this.#stopLoading();
|
|
3141
|
+
this.#lastActiveCount = 0;
|
|
3142
|
+
return [];
|
|
3143
|
+
}
|
|
3144
|
+
if (this.#lastActiveCount === 0) this.#startLoading();
|
|
3145
|
+
this.#lastActiveCount = activeInstances.length;
|
|
3146
|
+
const totalToolUses = activeInstances.reduce((sum, inst) => {
|
|
3147
|
+
return sum + (inst.currentActivity?.toolUses ?? 0);
|
|
3148
|
+
}, 0);
|
|
3149
|
+
const totalDuration = this.#formatDuration(Math.max(...activeInstances.map((i) => Date.now() - i.createdAt)));
|
|
3150
|
+
const frame = LOADING_FRAMES$2[this.#loadingFrame % LOADING_FRAMES$2.length] ?? "";
|
|
3151
|
+
if (!this.#expanded) return [this.#renderCollapsed(frame, activeInstances.length, totalToolUses, totalDuration).slice(0, width)];
|
|
3152
|
+
return this.#renderExpanded(frame, activeInstances, width);
|
|
3153
|
+
}
|
|
3154
|
+
/** 渲染折叠模式单行 */
|
|
3155
|
+
#renderCollapsed(frame, count, toolUses, duration) {
|
|
3156
|
+
return ` ${chalk.cyan(`${frame} Running ${count} agent${count > 1 ? "s" : ""}...`)} ${chalk.gray(`\u00B7 ${toolUses} tool use${toolUses !== 1 ? "s" : ""} \u00B7 ${duration}`)} ${chalk.dim("(ctrl+o to expand)")}`;
|
|
3157
|
+
}
|
|
3158
|
+
/** 渲染展开模式多行 */
|
|
3159
|
+
#renderExpanded(frame, instances, width) {
|
|
3160
|
+
const lines = [];
|
|
3161
|
+
const count = instances.length;
|
|
3162
|
+
const header = chalk.cyan(` ${frame} Running ${count} agent${count > 1 ? "s" : ""}...`);
|
|
3163
|
+
const hint = chalk.dim("(ctrl+o to collapse)");
|
|
3164
|
+
lines.push(`${header} ${hint}`.slice(0, width));
|
|
3165
|
+
for (let i = 0; i < instances.length; i++) {
|
|
3166
|
+
const inst = instances[i];
|
|
3167
|
+
const isLast = i === instances.length - 1;
|
|
3168
|
+
const connector = isLast ? TREE_LAST : TREE_BRANCH;
|
|
3169
|
+
const childPrefix = isLast ? TREE_SPACE : TREE_PIPE;
|
|
3170
|
+
const icon = STATUS_ICONS$1[inst.status] ?? "?";
|
|
3171
|
+
const statusColor = inst.status === "running" ? chalk.yellow : chalk.gray;
|
|
3172
|
+
const typeLabel = chalk.bold(inst.typeId);
|
|
3173
|
+
const taskPreview = inst.task.description.slice(0, 40);
|
|
3174
|
+
const act = inst.currentActivity;
|
|
3175
|
+
const toolUsesStr = act ? chalk.gray(`\u00B7 ${act.toolUses} tool uses`) : "";
|
|
3176
|
+
const durationStr = chalk.gray(`\u00B7 ${this.#formatDuration(Date.now() - inst.createdAt)}`);
|
|
3177
|
+
const line1 = ` ${chalk.dim(connector)}${statusColor(icon)}${chalk.reset} ${typeLabel} ${chalk.dim(taskPreview)} ${toolUsesStr} ${durationStr}`;
|
|
3178
|
+
lines.push(line1.slice(0, width));
|
|
3179
|
+
if (act) {
|
|
3180
|
+
const toolDisplay = act.args ? `${act.toolName}: ${act.args.slice(0, 60)}` : act.toolName;
|
|
3181
|
+
const line2 = ` ${chalk.dim(childPrefix + TREE_CONT)}${chalk.cyan(toolDisplay)}`;
|
|
3182
|
+
lines.push(line2.slice(0, width));
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
return lines;
|
|
3186
|
+
}
|
|
3187
|
+
/** 启动 loading 动画 */
|
|
3188
|
+
#startLoading() {
|
|
3189
|
+
if (this.#loadingTimer) return;
|
|
3190
|
+
this.#loadingTimer = setInterval(() => {
|
|
3191
|
+
this.#loadingFrame = (this.#loadingFrame + 1) % LOADING_FRAMES$2.length;
|
|
3192
|
+
this.invalidate();
|
|
3193
|
+
}, LOADING_INTERVAL_MS$1);
|
|
3194
|
+
}
|
|
3195
|
+
/** 停止 loading 动画 */
|
|
3196
|
+
#stopLoading() {
|
|
3197
|
+
if (this.#loadingTimer) {
|
|
3198
|
+
clearInterval(this.#loadingTimer);
|
|
3199
|
+
this.#loadingTimer = void 0;
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
/** 格式化持续时间为可读字符串 */
|
|
3203
|
+
#formatDuration(ms) {
|
|
3204
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
3205
|
+
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
|
|
3206
|
+
return `${Math.floor(ms / 6e4)}m${Math.floor(ms % 6e4 / 1e3)}s`;
|
|
3207
|
+
}
|
|
3208
|
+
};
|
|
3209
|
+
|
|
3024
3210
|
//#endregion
|
|
3025
3211
|
//#region src/cli/repl/components/custom-editor.ts
|
|
3026
3212
|
/**
|
|
@@ -3040,7 +3226,7 @@ function createStatusCommand() {
|
|
|
3040
3226
|
/** 输入提示符 */
|
|
3041
3227
|
const PROMPT_PREFIX = "❯ ";
|
|
3042
3228
|
/** loading 动画帧 */
|
|
3043
|
-
const LOADING_FRAMES = [
|
|
3229
|
+
const LOADING_FRAMES$1 = [
|
|
3044
3230
|
"⠋",
|
|
3045
3231
|
"⠙",
|
|
3046
3232
|
"⠹",
|
|
@@ -3068,8 +3254,12 @@ var ZapmycoEditor = class extends Editor {
|
|
|
3068
3254
|
onCtrlD;
|
|
3069
3255
|
/** Ctrl+O 回调(打开外部编辑器) */
|
|
3070
3256
|
onOpenEditor;
|
|
3071
|
-
/** Ctrl+T
|
|
3257
|
+
/** Ctrl+T 回调(展开/折叠 TaskStatusBar) */
|
|
3258
|
+
onToggleTasks;
|
|
3259
|
+
/** Ctrl+Y 回调(展开/折叠 thinking 内容) */
|
|
3072
3260
|
onToggleThinking;
|
|
3261
|
+
/** Ctrl+Shift+O 回调(展开/折叠 Agent 状态栏) */
|
|
3262
|
+
onToggleAgentBar;
|
|
3073
3263
|
/** 是否正在执行(用于显示 loading) */
|
|
3074
3264
|
#executing = false;
|
|
3075
3265
|
/** 是否显示 spinner(执行期间禁用输入但不一定显示 spinner) */
|
|
@@ -3120,14 +3310,24 @@ var ZapmycoEditor = class extends Editor {
|
|
|
3120
3310
|
if (this.getText().length === 0 && this.onCtrlD) this.onCtrlD();
|
|
3121
3311
|
return;
|
|
3122
3312
|
}
|
|
3123
|
-
if (matchesKey(data, Key.ctrl("o"))
|
|
3124
|
-
this.onOpenEditor();
|
|
3313
|
+
if (matchesKey(data, Key.ctrl("o"))) {
|
|
3314
|
+
if (this.onOpenEditor) this.onOpenEditor();
|
|
3125
3315
|
return;
|
|
3126
3316
|
}
|
|
3127
|
-
if (matchesKey(data, Key.
|
|
3128
|
-
this.
|
|
3317
|
+
if (matchesKey(data, Key.ctrlShift("o")) && this.onToggleAgentBar) {
|
|
3318
|
+
this.onToggleAgentBar();
|
|
3129
3319
|
return;
|
|
3130
3320
|
}
|
|
3321
|
+
if (matchesKey(data, Key.ctrl("t"))) {
|
|
3322
|
+
if (this.onToggleTasks) {
|
|
3323
|
+
this.onToggleTasks();
|
|
3324
|
+
return;
|
|
3325
|
+
}
|
|
3326
|
+
if (this.onToggleThinking) {
|
|
3327
|
+
this.onToggleThinking();
|
|
3328
|
+
return;
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3131
3331
|
if (matchesKey(data, Key.ctrl("y")) && this.onToggleThinking) {
|
|
3132
3332
|
this.onToggleThinking();
|
|
3133
3333
|
return;
|
|
@@ -3178,7 +3378,7 @@ var ZapmycoEditor = class extends Editor {
|
|
|
3178
3378
|
if (executing && showSpinner) {
|
|
3179
3379
|
this.#loadingFrame = 0;
|
|
3180
3380
|
this.#loadingTimer = setInterval(() => {
|
|
3181
|
-
this.#loadingFrame = (this.#loadingFrame + 1) % LOADING_FRAMES.length;
|
|
3381
|
+
this.#loadingFrame = (this.#loadingFrame + 1) % LOADING_FRAMES$1.length;
|
|
3182
3382
|
this.tui?.requestRender();
|
|
3183
3383
|
}, 100);
|
|
3184
3384
|
} else if (this.#loadingTimer) {
|
|
@@ -3211,7 +3411,7 @@ var ZapmycoEditor = class extends Editor {
|
|
|
3211
3411
|
for (let i = 0; i < contentLines.length; i++) {
|
|
3212
3412
|
const prefix = i === 0 ? PROMPT_PREFIX : " ".repeat(promptWidth);
|
|
3213
3413
|
let line;
|
|
3214
|
-
if (i === 0 && this.#executing && this.#showSpinner) line = `${prefix}${LOADING_FRAMES[this.#loadingFrame]} ${contentLines[i]}`;
|
|
3414
|
+
if (i === 0 && this.#executing && this.#showSpinner) line = `${prefix}${LOADING_FRAMES$1[this.#loadingFrame]} ${contentLines[i]}`;
|
|
3215
3415
|
else line = prefix + contentLines[i];
|
|
3216
3416
|
contentLines[i] = truncateToWidth(line, width);
|
|
3217
3417
|
}
|
|
@@ -3245,6 +3445,243 @@ var ZapmycoEditor = class extends Editor {
|
|
|
3245
3445
|
}
|
|
3246
3446
|
};
|
|
3247
3447
|
|
|
3448
|
+
//#endregion
|
|
3449
|
+
//#region src/cli/repl/components/task-status-bar.ts
|
|
3450
|
+
/**
|
|
3451
|
+
* TaskStatusBar — 任务状态栏组件
|
|
3452
|
+
*
|
|
3453
|
+
* 显示 TaskManage 创建的任务列表,支持折叠/展开两种模式。
|
|
3454
|
+
* 类似 Claude Code 的 TaskListV2,使用 pi-tui Container 实现。
|
|
3455
|
+
*
|
|
3456
|
+
* 折叠模式(默认,单行):
|
|
3457
|
+
* 📋 3 tasks · ◼ 1 in_progress · ◻ 1 pending · ✔ 1 completed Ctrl+T expand
|
|
3458
|
+
*
|
|
3459
|
+
* 展开模式(Ctrl+T 切换):
|
|
3460
|
+
* ⠋ #1 Search files
|
|
3461
|
+
* ◻ #2 Core logic
|
|
3462
|
+
* ◻ #3 Tests ▸ blocked by #1
|
|
3463
|
+
* ✔ #4 Analysis
|
|
3464
|
+
*
|
|
3465
|
+
* 1 in_progress · 1 pending · 1 completed
|
|
3466
|
+
* (Ctrl+T collapse)
|
|
3467
|
+
*
|
|
3468
|
+
* 自动隐藏:无任务时 render() 返回 []。
|
|
3469
|
+
* 进行中任务:显示 loading spinner 动画 + cyan 高亮。
|
|
3470
|
+
* 已完成任务:绿色 + 删除线。
|
|
3471
|
+
*
|
|
3472
|
+
* @module cli/repl/components
|
|
3473
|
+
*/
|
|
3474
|
+
/** Loading 动画帧(Braille 模式 spinner) */
|
|
3475
|
+
const LOADING_FRAMES = [
|
|
3476
|
+
"⠋",
|
|
3477
|
+
"⠙",
|
|
3478
|
+
"⠹",
|
|
3479
|
+
"⠸",
|
|
3480
|
+
"⠼",
|
|
3481
|
+
"⠴",
|
|
3482
|
+
"⠦",
|
|
3483
|
+
"⠧",
|
|
3484
|
+
"⠇",
|
|
3485
|
+
"⠏"
|
|
3486
|
+
];
|
|
3487
|
+
const LOADING_INTERVAL_MS = 200;
|
|
3488
|
+
/** 静态状态图标映射 */
|
|
3489
|
+
const STATUS_ICONS = {
|
|
3490
|
+
pending: "◻",
|
|
3491
|
+
in_progress: "◼",
|
|
3492
|
+
completed: "✔",
|
|
3493
|
+
cancelled: "✕"
|
|
3494
|
+
};
|
|
3495
|
+
/**
|
|
3496
|
+
* 任务状态栏组件
|
|
3497
|
+
*
|
|
3498
|
+
* 从 TaskStore 读取任务列表,渲染为紧凑状态栏。
|
|
3499
|
+
* 固定在 OutputArea 和 AgentStatusBar 之间。
|
|
3500
|
+
*/
|
|
3501
|
+
var TaskStatusBar = class extends Container {
|
|
3502
|
+
/**
|
|
3503
|
+
* 用户手动折叠标记
|
|
3504
|
+
*
|
|
3505
|
+
* - false(默认):根据任务状态自动决定展开/折叠
|
|
3506
|
+
* - true:用户通过 Ctrl+T 手动折叠,覆盖自动行为
|
|
3507
|
+
*/
|
|
3508
|
+
#forceCollapsed = false;
|
|
3509
|
+
/** TaskStore 引用(只读,不写) */
|
|
3510
|
+
#store;
|
|
3511
|
+
/** loading 动画帧索引 */
|
|
3512
|
+
#loadingFrame = 0;
|
|
3513
|
+
/** loading 动画定时器 */
|
|
3514
|
+
#loadingTimer;
|
|
3515
|
+
/** 上次是否有 in_progress 任务(用于启停动画) */
|
|
3516
|
+
#hadInProgress = false;
|
|
3517
|
+
constructor(store) {
|
|
3518
|
+
super();
|
|
3519
|
+
this.#store = store;
|
|
3520
|
+
}
|
|
3521
|
+
/**
|
|
3522
|
+
* 切换展开/折叠状态
|
|
3523
|
+
*
|
|
3524
|
+
* 用户手动 Ctrl+T 时调用,设置 forceCollapsed 标记。
|
|
3525
|
+
* 当新任务出现时,forceCollapsed 会被自动重置。
|
|
3526
|
+
*/
|
|
3527
|
+
toggle() {
|
|
3528
|
+
this.#forceCollapsed = !this.#forceCollapsed;
|
|
3529
|
+
this.invalidate();
|
|
3530
|
+
}
|
|
3531
|
+
/** 当前是否处于展开状态 */
|
|
3532
|
+
get isExpanded() {
|
|
3533
|
+
const summary = this.#store.summary();
|
|
3534
|
+
return this.#shouldExpand(summary);
|
|
3535
|
+
}
|
|
3536
|
+
/**
|
|
3537
|
+
* 当 TaskStore 变化时由外部调用
|
|
3538
|
+
*
|
|
3539
|
+
* 如果出现新的活跃任务,自动展开;全部完成后自动折叠。
|
|
3540
|
+
*/
|
|
3541
|
+
onTasksChanged() {
|
|
3542
|
+
const summary = this.#store.summary();
|
|
3543
|
+
if (summary.pending > 0 || summary.in_progress > 0) this.#forceCollapsed = false;
|
|
3544
|
+
this.invalidate();
|
|
3545
|
+
}
|
|
3546
|
+
invalidate() {
|
|
3547
|
+
super.invalidate();
|
|
3548
|
+
}
|
|
3549
|
+
/** 判断当前是否应展开 */
|
|
3550
|
+
#shouldExpand(summary) {
|
|
3551
|
+
if (summary.total === 0) return false;
|
|
3552
|
+
if (this.#forceCollapsed) return false;
|
|
3553
|
+
return summary.pending > 0 || summary.in_progress > 0;
|
|
3554
|
+
}
|
|
3555
|
+
/**
|
|
3556
|
+
* 管理 loading 动画的启停
|
|
3557
|
+
*
|
|
3558
|
+
* 当有 in_progress 任务时启动 spinner,全部完成后停止。
|
|
3559
|
+
*/
|
|
3560
|
+
#updateLoading(summary) {
|
|
3561
|
+
const hasInProgress = summary.in_progress > 0;
|
|
3562
|
+
if (hasInProgress && !this.#hadInProgress) this.#startLoading();
|
|
3563
|
+
else if (!hasInProgress && this.#hadInProgress) this.#stopLoading();
|
|
3564
|
+
}
|
|
3565
|
+
/** 启动 loading 动画 */
|
|
3566
|
+
#startLoading() {
|
|
3567
|
+
if (this.#loadingTimer) return;
|
|
3568
|
+
this.#loadingTimer = setInterval(() => {
|
|
3569
|
+
this.#loadingFrame = (this.#loadingFrame + 1) % LOADING_FRAMES.length;
|
|
3570
|
+
this.invalidate();
|
|
3571
|
+
}, LOADING_INTERVAL_MS);
|
|
3572
|
+
}
|
|
3573
|
+
/** 停止 loading 动画 */
|
|
3574
|
+
#stopLoading() {
|
|
3575
|
+
if (this.#loadingTimer) {
|
|
3576
|
+
clearInterval(this.#loadingTimer);
|
|
3577
|
+
this.#loadingTimer = void 0;
|
|
3578
|
+
this.#loadingFrame = 0;
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3581
|
+
/** 获取当前 in_progress 的图标(可能为动画帧) */
|
|
3582
|
+
#getInProgressIcon() {
|
|
3583
|
+
return LOADING_FRAMES[this.#loadingFrame % LOADING_FRAMES.length];
|
|
3584
|
+
}
|
|
3585
|
+
render(width) {
|
|
3586
|
+
const tasks = this.#store.read();
|
|
3587
|
+
const summary = this.#store.summary();
|
|
3588
|
+
this.#updateLoading(summary);
|
|
3589
|
+
this.#hadInProgress = summary.in_progress > 0;
|
|
3590
|
+
if (tasks.length === 0) {
|
|
3591
|
+
this.#stopLoading();
|
|
3592
|
+
return [];
|
|
3593
|
+
}
|
|
3594
|
+
if (this.#shouldExpand(summary)) return this.#renderExpanded(tasks, summary, width);
|
|
3595
|
+
return [this.#renderCollapsed(summary).slice(0, width)];
|
|
3596
|
+
}
|
|
3597
|
+
/** 渲染折叠模式单行 */
|
|
3598
|
+
#renderCollapsed(summary) {
|
|
3599
|
+
const parts = [];
|
|
3600
|
+
const taskCount = summary.total === 1 ? "1 task" : `${summary.total} tasks`;
|
|
3601
|
+
parts.push(chalk.cyan(`\uD83D\uDCCB ${taskCount}`));
|
|
3602
|
+
if (summary.in_progress > 0) {
|
|
3603
|
+
const icon = this.#getInProgressIcon();
|
|
3604
|
+
parts.push(chalk.cyan(`${icon} ${summary.in_progress} in_progress`));
|
|
3605
|
+
}
|
|
3606
|
+
if (summary.pending > 0) parts.push(`${STATUS_ICONS.pending} ${summary.pending} pending`);
|
|
3607
|
+
if (summary.completed > 0) parts.push(chalk.green(`${STATUS_ICONS.completed} ${summary.completed} completed`));
|
|
3608
|
+
if (summary.cancelled > 0) parts.push(chalk.red.dim(`${STATUS_ICONS.cancelled} ${summary.cancelled} cancelled`));
|
|
3609
|
+
const hint = chalk.dim("Ctrl+T expand");
|
|
3610
|
+
return ` ${parts.join(" · ")} ${hint}`;
|
|
3611
|
+
}
|
|
3612
|
+
/** 渲染展开模式多行 */
|
|
3613
|
+
#renderExpanded(tasks, summary, width) {
|
|
3614
|
+
const lines = [];
|
|
3615
|
+
const sorted = this.#sortTasks(tasks);
|
|
3616
|
+
for (const task of sorted) lines.push(this.#renderTaskLine(task, width));
|
|
3617
|
+
lines.push("");
|
|
3618
|
+
const summaryParts = [];
|
|
3619
|
+
if (summary.in_progress > 0) summaryParts.push(chalk.cyan(`${summary.in_progress} in_progress`));
|
|
3620
|
+
if (summary.pending > 0) summaryParts.push(`${summary.pending} pending`);
|
|
3621
|
+
if (summary.completed > 0) summaryParts.push(chalk.green(`${summary.completed} completed`));
|
|
3622
|
+
if (summary.cancelled > 0) summaryParts.push(chalk.red.dim(`${summary.cancelled} cancelled`));
|
|
3623
|
+
lines.push(` ${summaryParts.join(" · ")}`);
|
|
3624
|
+
lines.push(chalk.dim(" (Ctrl+T collapse)"));
|
|
3625
|
+
return lines;
|
|
3626
|
+
}
|
|
3627
|
+
/** 渲染单个任务行 */
|
|
3628
|
+
#renderTaskLine(task, width) {
|
|
3629
|
+
const prefix = ` ${task.status === "in_progress" ? this.#getInProgressIcon() : STATUS_ICONS[task.status] ?? "?"} #${task.id} `;
|
|
3630
|
+
const blockedBy = this.#getBlockedHint(task);
|
|
3631
|
+
const blockedText = blockedBy ? ` ${blockedBy}` : "";
|
|
3632
|
+
const availableWidth = Math.max(10, width - [...prefix].length - [...blockedText].length);
|
|
3633
|
+
const subject = task.subject.length > availableWidth ? `${task.subject.slice(0, availableWidth - 1)}\u2026` : task.subject;
|
|
3634
|
+
let subjectStyled;
|
|
3635
|
+
if (task.status === "completed") subjectStyled = chalk.green.dim.strikethrough(subject);
|
|
3636
|
+
else if (task.status === "in_progress") subjectStyled = chalk.cyan.bold(subject);
|
|
3637
|
+
else if (task.status === "cancelled") subjectStyled = chalk.red.dim(subject);
|
|
3638
|
+
else subjectStyled = subject;
|
|
3639
|
+
const rightStyled = blockedBy ? chalk.dim(blockedBy) : "";
|
|
3640
|
+
return `${prefix}${subjectStyled}${blockedBy ? ` ${rightStyled}` : ""}`.slice(0, width);
|
|
3641
|
+
}
|
|
3642
|
+
/** 获取阻塞提示文本 */
|
|
3643
|
+
#getBlockedHint(task) {
|
|
3644
|
+
if (!task.dependencies || task.dependencies.length === 0) return "";
|
|
3645
|
+
if (task.status !== "pending") return "";
|
|
3646
|
+
const allTasks = this.#store.read();
|
|
3647
|
+
const openBlockers = [];
|
|
3648
|
+
for (const depId of task.dependencies) {
|
|
3649
|
+
const depTask = allTasks.find((t) => t.id === depId);
|
|
3650
|
+
if (depTask && depTask.status !== "completed") openBlockers.push(`#${depId}`);
|
|
3651
|
+
else if (!depTask) openBlockers.push(`#${depId}?`);
|
|
3652
|
+
}
|
|
3653
|
+
if (openBlockers.length === 0) return "";
|
|
3654
|
+
return `\u25B8 blocked by ${openBlockers.join(", ")}`;
|
|
3655
|
+
}
|
|
3656
|
+
/** 排序任务 */
|
|
3657
|
+
#sortTasks(tasks) {
|
|
3658
|
+
const allTasks = this.#store.read();
|
|
3659
|
+
return [...tasks].sort((a, b) => {
|
|
3660
|
+
return this.#taskPriority(a, allTasks) - this.#taskPriority(b, allTasks);
|
|
3661
|
+
});
|
|
3662
|
+
}
|
|
3663
|
+
/** 计算任务优先级(值越小越靠前) */
|
|
3664
|
+
#taskPriority(task, allTasks) {
|
|
3665
|
+
switch (task.status) {
|
|
3666
|
+
case "in_progress": return 0;
|
|
3667
|
+
case "pending":
|
|
3668
|
+
if (this.#isBlocked(task, allTasks)) return 2;
|
|
3669
|
+
return 1;
|
|
3670
|
+
case "completed": return 10 - Date.now() + task.updatedAt;
|
|
3671
|
+
case "cancelled": return 20;
|
|
3672
|
+
default: return 100;
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
/** 判断任务是否被阻塞 */
|
|
3676
|
+
#isBlocked(task, allTasks) {
|
|
3677
|
+
if (!task.dependencies || task.dependencies.length === 0) return false;
|
|
3678
|
+
return task.dependencies.some((depId) => {
|
|
3679
|
+
const depTask = allTasks.find((t) => t.id === depId);
|
|
3680
|
+
return !depTask || depTask.status !== "completed";
|
|
3681
|
+
});
|
|
3682
|
+
}
|
|
3683
|
+
};
|
|
3684
|
+
|
|
3248
3685
|
//#endregion
|
|
3249
3686
|
//#region src/cli/repl/cron/cron-parser.ts
|
|
3250
3687
|
/** 通配符 * — 匹配所有值 */
|
|
@@ -9218,6 +9655,12 @@ function setSkillEntries(entries) {
|
|
|
9218
9655
|
skillEntries = entries;
|
|
9219
9656
|
}
|
|
9220
9657
|
/**
|
|
9658
|
+
* 获取当前已加载的 Skill 列表
|
|
9659
|
+
*/
|
|
9660
|
+
function getSkillEntries() {
|
|
9661
|
+
return skillEntries;
|
|
9662
|
+
}
|
|
9663
|
+
/**
|
|
9221
9664
|
* 替换 Skill 内容中的模板变量
|
|
9222
9665
|
*
|
|
9223
9666
|
* 支持的变量:
|
|
@@ -9382,6 +9825,33 @@ function getSkillCommandSpecs(entries) {
|
|
|
9382
9825
|
}));
|
|
9383
9826
|
}
|
|
9384
9827
|
/**
|
|
9828
|
+
* 格式化 Skill 指令内容(供 Skill tool 和斜杠命令 handler 共用)
|
|
9829
|
+
*
|
|
9830
|
+
* 使用内存中已解析的 skill.body,无需重新读取 SKILL.md 文件。
|
|
9831
|
+
*
|
|
9832
|
+
* @param skill - Skill 定义(含 frontmatter、body、baseDir)
|
|
9833
|
+
* @param args - 用户传递的参数(可选)
|
|
9834
|
+
* @returns 格式化后的完整指令文本
|
|
9835
|
+
*/
|
|
9836
|
+
function formatSkillContent(skill, args) {
|
|
9837
|
+
const substituted = substituteVariables(skill.body, args, skill.baseDir, skill.name);
|
|
9838
|
+
const hint = skill.frontmatter["argument-hint"] ? ` [${skill.frontmatter["argument-hint"]}]` : "";
|
|
9839
|
+
const instructionParts = [
|
|
9840
|
+
`# Skill: ${skill.name}${hint}`,
|
|
9841
|
+
"",
|
|
9842
|
+
skill.description ? `> ${skill.description}` : "",
|
|
9843
|
+
"",
|
|
9844
|
+
"---",
|
|
9845
|
+
"",
|
|
9846
|
+
substituted,
|
|
9847
|
+
"",
|
|
9848
|
+
"---",
|
|
9849
|
+
`Base directory: ${skill.baseDir}`
|
|
9850
|
+
];
|
|
9851
|
+
if (!substituted.includes(args ?? "") && args) instructionParts.push("", `ARGUMENTS: ${args}`);
|
|
9852
|
+
return instructionParts.filter(Boolean).join("\n");
|
|
9853
|
+
}
|
|
9854
|
+
/**
|
|
9385
9855
|
* 规范化技能命令名称
|
|
9386
9856
|
*
|
|
9387
9857
|
* 转换为小写,非字母数字字符替换为连字符。
|
|
@@ -11514,6 +11984,7 @@ var AgentOrchestrator = class {
|
|
|
11514
11984
|
};
|
|
11515
11985
|
const depth = 1;
|
|
11516
11986
|
const instanceId = `flat-${spec.id}-${Date.now()}`;
|
|
11987
|
+
let stopRelay;
|
|
11517
11988
|
try {
|
|
11518
11989
|
const agent = createAgentFromType(definition, {
|
|
11519
11990
|
instanceId,
|
|
@@ -11533,6 +12004,7 @@ var AgentOrchestrator = class {
|
|
|
11533
12004
|
timeoutMs: this.flatConfig.taskTimeoutMs,
|
|
11534
12005
|
inheritContext: false
|
|
11535
12006
|
}, null, depth);
|
|
12007
|
+
stopRelay = this.#relayAgentProgress(agent, instanceId);
|
|
11536
12008
|
agent.systemPromptOverride = this.buildFlatSystemPrompt(spec, context);
|
|
11537
12009
|
const result = await Promise.race([agent.execute({
|
|
11538
12010
|
taskId: `flat-${spec.id}`,
|
|
@@ -11547,6 +12019,7 @@ var AgentOrchestrator = class {
|
|
|
11547
12019
|
const outputText = this.extractOutputText(result);
|
|
11548
12020
|
const isSuccess = typeof result === "object" && result !== null && "status" in result && result.status === "success";
|
|
11549
12021
|
instanceManager.transition(instanceId, isSuccess ? "completed" : "failed");
|
|
12022
|
+
stopRelay();
|
|
11550
12023
|
return {
|
|
11551
12024
|
specId: spec.id,
|
|
11552
12025
|
status: isSuccess ? "success" : "failure",
|
|
@@ -11561,6 +12034,9 @@ var AgentOrchestrator = class {
|
|
|
11561
12034
|
error: message,
|
|
11562
12035
|
duration
|
|
11563
12036
|
});
|
|
12037
|
+
try {
|
|
12038
|
+
stopRelay?.();
|
|
12039
|
+
} catch {}
|
|
11564
12040
|
try {
|
|
11565
12041
|
instanceManager.transition(instanceId, "failed");
|
|
11566
12042
|
} catch {}
|
|
@@ -11642,6 +12118,7 @@ var AgentOrchestrator = class {
|
|
|
11642
12118
|
const instanceId = `agent-${typeId}-${Date.now()}`;
|
|
11643
12119
|
const timeoutMs = options?.timeoutMs ?? this.flatConfig.taskTimeoutMs;
|
|
11644
12120
|
let worktreeInfo;
|
|
12121
|
+
let stopRelay;
|
|
11645
12122
|
try {
|
|
11646
12123
|
const agent = createAgentFromType(definition, {
|
|
11647
12124
|
instanceId,
|
|
@@ -11661,6 +12138,7 @@ var AgentOrchestrator = class {
|
|
|
11661
12138
|
timeoutMs,
|
|
11662
12139
|
inheritContext: options?.inheritContext ?? false
|
|
11663
12140
|
}, parentInstanceId || null, depth);
|
|
12141
|
+
stopRelay = this.#relayAgentProgress(agent, instanceId);
|
|
11664
12142
|
const promptCtx = {
|
|
11665
12143
|
taskDescription,
|
|
11666
12144
|
workdir: process.cwd()
|
|
@@ -11762,6 +12240,9 @@ var AgentOrchestrator = class {
|
|
|
11762
12240
|
}
|
|
11763
12241
|
};
|
|
11764
12242
|
} finally {
|
|
12243
|
+
try {
|
|
12244
|
+
stopRelay?.();
|
|
12245
|
+
} catch {}
|
|
11765
12246
|
try {
|
|
11766
12247
|
const instance = instanceManager.get(instanceId);
|
|
11767
12248
|
if (instance) instance.agent.systemPromptOverride = null;
|
|
@@ -11814,6 +12295,41 @@ var AgentOrchestrator = class {
|
|
|
11814
12295
|
return aggregateResults(teamId, workerResults);
|
|
11815
12296
|
}
|
|
11816
12297
|
/**
|
|
12298
|
+
* 监听子 Agent 进度事件并中继到 InstanceManager
|
|
12299
|
+
*
|
|
12300
|
+
* 在子 Agent 执行工具调用时,更新其 AgentInstance 的 currentActivity,
|
|
12301
|
+
* 驱动 UI 状态栏实时展示子 Agent 当前操作。
|
|
12302
|
+
*
|
|
12303
|
+
* @param agent - 子 Agent 实例
|
|
12304
|
+
* @param instanceId - 对应的 InstanceManager 实例 ID
|
|
12305
|
+
* @returns 清理函数,调用后移除事件监听
|
|
12306
|
+
*/
|
|
12307
|
+
#relayAgentProgress(agent, instanceId) {
|
|
12308
|
+
const progressHandler = (event) => {
|
|
12309
|
+
const instanceManager = getAgentInstanceManager();
|
|
12310
|
+
const instance = instanceManager.get(instanceId);
|
|
12311
|
+
if (!instance) return;
|
|
12312
|
+
if (event.percent === 0) {
|
|
12313
|
+
const toolName = event.message;
|
|
12314
|
+
const prevUses = instance.currentActivity?.toolUses ?? 0;
|
|
12315
|
+
const parenIdx = toolName.indexOf("(");
|
|
12316
|
+
const namePart = parenIdx > 0 ? toolName.slice(0, parenIdx) : toolName;
|
|
12317
|
+
const argsPart = parenIdx > 0 ? toolName.slice(parenIdx + 1, -1) : void 0;
|
|
12318
|
+
const activity = {
|
|
12319
|
+
toolName: namePart,
|
|
12320
|
+
toolUses: prevUses + 1,
|
|
12321
|
+
...argsPart !== void 0 ? { args: argsPart } : {},
|
|
12322
|
+
startedAt: Date.now()
|
|
12323
|
+
};
|
|
12324
|
+
instanceManager.setActivity(instanceId, activity);
|
|
12325
|
+
} else if (event.percent === 100 || event.percent === -1) {}
|
|
12326
|
+
};
|
|
12327
|
+
agent.on(agent.EVENT_PROGRESS, progressHandler);
|
|
12328
|
+
return () => {
|
|
12329
|
+
agent.off(agent.EVENT_PROGRESS, progressHandler);
|
|
12330
|
+
};
|
|
12331
|
+
}
|
|
12332
|
+
/**
|
|
11817
12333
|
* 为扁平子 Agent 构建系统提示词
|
|
11818
12334
|
*/
|
|
11819
12335
|
buildFlatSystemPrompt(spec, context) {
|
|
@@ -11923,10 +12439,10 @@ const MODE_STRATEGIES = {
|
|
|
11923
12439
|
defaultAction: "deny",
|
|
11924
12440
|
maxAutoAllow: "low"
|
|
11925
12441
|
},
|
|
11926
|
-
/** normal:
|
|
12442
|
+
/** normal: 工具默认放行,仅内容级安全检查(shell 危险命令、敏感文件路径)触发审批 */
|
|
11927
12443
|
normal: {
|
|
11928
|
-
defaultAction: "
|
|
11929
|
-
maxAutoAllow: "
|
|
12444
|
+
defaultAction: "allow",
|
|
12445
|
+
maxAutoAllow: "high"
|
|
11930
12446
|
},
|
|
11931
12447
|
/** permissive: low/medium 直接 allow,high ask,critical deny */
|
|
11932
12448
|
permissive: {
|
|
@@ -12597,6 +13113,207 @@ function createLspTool(lspManager) {
|
|
|
12597
13113
|
};
|
|
12598
13114
|
}
|
|
12599
13115
|
|
|
13116
|
+
//#endregion
|
|
13117
|
+
//#region src/cli/repl/utils/markdown-formatter.ts
|
|
13118
|
+
/**
|
|
13119
|
+
* Markdown → ANSI 格式化器
|
|
13120
|
+
*
|
|
13121
|
+
* 将 LLM 返回的 Markdown 文本解析并应用 ANSI 样式(颜色、加粗、斜体等),
|
|
13122
|
+
* 使终端输出更易读。参考 Claude Code 的 formatToken 实现。
|
|
13123
|
+
*
|
|
13124
|
+
* 使用方式:
|
|
13125
|
+
* import { formatMarkdown } from '@/cli/repl/utils/markdown-formatter';
|
|
13126
|
+
* console.log(formatMarkdown('**hello** world'));
|
|
13127
|
+
*/
|
|
13128
|
+
let markedConfigured = false;
|
|
13129
|
+
function configureMarked() {
|
|
13130
|
+
if (markedConfigured) return;
|
|
13131
|
+
markedConfigured = true;
|
|
13132
|
+
marked.use({ tokenizer: { del() {} } });
|
|
13133
|
+
}
|
|
13134
|
+
const EOL = "\n";
|
|
13135
|
+
const LIST_ITEM_MARKER = "-";
|
|
13136
|
+
const BLOCKQUOTE_BAR = "│";
|
|
13137
|
+
/**
|
|
13138
|
+
* 格式化 Markdown 文本为 ANSI 样式字符串
|
|
13139
|
+
*
|
|
13140
|
+
* @param text - 原始 Markdown 文本
|
|
13141
|
+
* @param colorEnabled - 是否启用颜色(false 时仅保留结构格式化)
|
|
13142
|
+
* @returns 格式化后的字符串
|
|
13143
|
+
*/
|
|
13144
|
+
function formatMarkdown(text, colorEnabled = true) {
|
|
13145
|
+
if (!text) return "";
|
|
13146
|
+
configureMarked();
|
|
13147
|
+
const tokens = marked.lexer(text);
|
|
13148
|
+
const parts = [];
|
|
13149
|
+
for (const token of tokens) parts.push(formatToken(token, colorEnabled, 0, null, null));
|
|
13150
|
+
return parts.join("").trimEnd();
|
|
13151
|
+
}
|
|
13152
|
+
function c(colorEnabled) {
|
|
13153
|
+
if (colorEnabled) return chalk;
|
|
13154
|
+
const noColor = Object.create(chalk);
|
|
13155
|
+
noColor.level = 0;
|
|
13156
|
+
return noColor;
|
|
13157
|
+
}
|
|
13158
|
+
function formatToken(token, colorEnabled, listDepth, orderedListNumber, parent) {
|
|
13159
|
+
const ch = c(colorEnabled);
|
|
13160
|
+
switch (token.type) {
|
|
13161
|
+
case "blockquote": {
|
|
13162
|
+
const inner = token.tokens ? token.tokens.map((t) => formatToken(t, colorEnabled, 0, null, null)).join("") : "";
|
|
13163
|
+
const bar = ch.dim(BLOCKQUOTE_BAR);
|
|
13164
|
+
return inner.split(EOL).map((line) => line.trim() ? `${bar} ${ch.italic(line)}` : line).join(EOL);
|
|
13165
|
+
}
|
|
13166
|
+
case "code": {
|
|
13167
|
+
const lang = token.lang ? `${ch.dim(` ${token.lang}`)}` : "";
|
|
13168
|
+
const header = token.lang ? `${ch.dim("```")}${lang}${EOL}` : "";
|
|
13169
|
+
const footer = token.lang ? `${EOL}${ch.dim("```")}` : "";
|
|
13170
|
+
return `${header}${ch.dim(token.text)}${footer}${EOL}`;
|
|
13171
|
+
}
|
|
13172
|
+
case "codespan": return ch.cyan(token.text);
|
|
13173
|
+
case "em": {
|
|
13174
|
+
const inner = token.tokens ? token.tokens.map((t) => formatToken(t, colorEnabled, 0, null, token)).join("") : "";
|
|
13175
|
+
return ch.italic(inner);
|
|
13176
|
+
}
|
|
13177
|
+
case "strong": {
|
|
13178
|
+
const inner = token.tokens ? token.tokens.map((t) => formatToken(t, colorEnabled, 0, null, token)).join("") : "";
|
|
13179
|
+
return ch.bold(inner);
|
|
13180
|
+
}
|
|
13181
|
+
case "heading": {
|
|
13182
|
+
const inner = token.tokens ? token.tokens.map((t) => formatToken(t, colorEnabled, 0, null, null)).join("") : "";
|
|
13183
|
+
if (token.depth === 1) return `${ch.bold.italic.underline(inner)}${EOL}${EOL}`;
|
|
13184
|
+
return `${ch.bold(inner)}${EOL}${EOL}`;
|
|
13185
|
+
}
|
|
13186
|
+
case "hr": return ch.dim("─".repeat(50)) + EOL;
|
|
13187
|
+
case "image": return token.href;
|
|
13188
|
+
case "link": {
|
|
13189
|
+
const linkText = token.tokens ? token.tokens.map((t) => formatToken(t, colorEnabled, 0, null, token)).join("") : "";
|
|
13190
|
+
if (linkText && linkText !== token.href) return `${linkText} ${ch.dim(`(${token.href})`)}`;
|
|
13191
|
+
return ch.dim(token.href);
|
|
13192
|
+
}
|
|
13193
|
+
case "list": return token.items.map((item, index) => formatToken(item, colorEnabled, listDepth, token.ordered ? (token.start ?? 1) + index : null, token)).join("");
|
|
13194
|
+
case "list_item": {
|
|
13195
|
+
const marker = orderedListNumber !== null ? `${orderedListNumber}.` : LIST_ITEM_MARKER;
|
|
13196
|
+
return `${" ".repeat(listDepth)}${marker} ${(token.tokens ? token.tokens.map((t) => formatToken(t, colorEnabled, listDepth + 1, orderedListNumber, token)).join("") : "").trimStart()}`;
|
|
13197
|
+
}
|
|
13198
|
+
case "paragraph": return (token.tokens ? token.tokens.map((t) => formatToken(t, colorEnabled, 0, null, null)).join("") : "") + EOL;
|
|
13199
|
+
case "space": return EOL;
|
|
13200
|
+
case "br": return EOL;
|
|
13201
|
+
case "text":
|
|
13202
|
+
if (parent?.type === "list_item") {
|
|
13203
|
+
const marker = orderedListNumber !== null ? `${orderedListNumber}.` : LIST_ITEM_MARKER;
|
|
13204
|
+
return `${" ".repeat(listDepth)}${marker} ${token.tokens ? token.tokens.map((t) => formatToken(t, colorEnabled, listDepth, orderedListNumber, token)).join("") : token.text}${EOL}`;
|
|
13205
|
+
}
|
|
13206
|
+
return token.text;
|
|
13207
|
+
case "table": return formatTable(token, colorEnabled);
|
|
13208
|
+
case "escape": return token.text;
|
|
13209
|
+
case "def":
|
|
13210
|
+
case "html":
|
|
13211
|
+
case "del": return "";
|
|
13212
|
+
default: return "";
|
|
13213
|
+
}
|
|
13214
|
+
}
|
|
13215
|
+
/**
|
|
13216
|
+
* 格式化表格为对齐的 ASCII 表格
|
|
13217
|
+
*/
|
|
13218
|
+
function formatTable(token, colorEnabled) {
|
|
13219
|
+
const ch = c(colorEnabled);
|
|
13220
|
+
function getCellText(cellTokens) {
|
|
13221
|
+
if (!cellTokens) return "";
|
|
13222
|
+
return cellTokens.map((t) => formatToken(t, colorEnabled, 0, null, null)).join("");
|
|
13223
|
+
}
|
|
13224
|
+
const columnCount = token.header.length;
|
|
13225
|
+
const colWidths = [];
|
|
13226
|
+
for (let i = 0; i < columnCount; i++) {
|
|
13227
|
+
const headerCell = token.header[i];
|
|
13228
|
+
let maxW = ansiCellWidth(headerCell ? getCellText(headerCell.tokens) : "");
|
|
13229
|
+
for (const row of token.rows) {
|
|
13230
|
+
const cell = row[i];
|
|
13231
|
+
if (cell?.tokens) {
|
|
13232
|
+
const cellText = getCellText(cell.tokens);
|
|
13233
|
+
maxW = Math.max(maxW, ansiCellWidth(cellText));
|
|
13234
|
+
}
|
|
13235
|
+
}
|
|
13236
|
+
colWidths.push(Math.max(maxW, 3));
|
|
13237
|
+
}
|
|
13238
|
+
let output = "";
|
|
13239
|
+
output += "┌";
|
|
13240
|
+
for (let i = 0; i < columnCount; i++) {
|
|
13241
|
+
const cw = colWidths[i] ?? 3;
|
|
13242
|
+
output += "─".repeat(cw + 2) + "┬";
|
|
13243
|
+
}
|
|
13244
|
+
output = output.slice(0, -1) + "┐\n";
|
|
13245
|
+
output += "│ ";
|
|
13246
|
+
for (let i = 0; i < columnCount; i++) {
|
|
13247
|
+
const cell = token.header[i];
|
|
13248
|
+
const content = cell?.tokens ? cell.tokens.map((t) => formatToken(t, colorEnabled, 0, null, null)).join("") : "";
|
|
13249
|
+
const displayLen = ansiCellWidth(content);
|
|
13250
|
+
const pad = (colWidths[i] ?? 3) - displayLen;
|
|
13251
|
+
const align = token.align?.[i];
|
|
13252
|
+
output += align === "right" ? " ".repeat(pad) + ch.bold(content) : align === "center" ? " ".repeat(Math.floor(pad / 2)) + ch.bold(content) + " ".repeat(Math.ceil(pad / 2)) : ch.bold(content) + " ".repeat(pad);
|
|
13253
|
+
output += " │ ";
|
|
13254
|
+
}
|
|
13255
|
+
output = output.trimEnd() + EOL;
|
|
13256
|
+
output += "├";
|
|
13257
|
+
for (let i = 0; i < columnCount; i++) {
|
|
13258
|
+
const align = token.align?.[i];
|
|
13259
|
+
const left = align === "center" || align === "right" ? ":" : "─";
|
|
13260
|
+
const right = align === "center" || align === "left" ? ":" : "─";
|
|
13261
|
+
const cw = colWidths[i] ?? 3;
|
|
13262
|
+
output += `${left}${"─".repeat(cw)}${right}┼`;
|
|
13263
|
+
}
|
|
13264
|
+
output = `${output.slice(0, -1)}┤${EOL}`;
|
|
13265
|
+
for (const row of token.rows) {
|
|
13266
|
+
output += "│ ";
|
|
13267
|
+
for (let i = 0; i < columnCount; i++) {
|
|
13268
|
+
const cell = row[i];
|
|
13269
|
+
const content = cell?.tokens ? cell.tokens.map((t) => formatToken(t, colorEnabled, 0, null, null)).join("") : "";
|
|
13270
|
+
const displayLen = ansiCellWidth(content);
|
|
13271
|
+
const pad = (colWidths[i] ?? 3) - displayLen;
|
|
13272
|
+
const align = token.align?.[i];
|
|
13273
|
+
output += align === "right" ? " ".repeat(pad) + content : align === "center" ? " ".repeat(Math.floor(pad / 2)) + content + " ".repeat(Math.ceil(pad / 2)) : content + " ".repeat(pad);
|
|
13274
|
+
output += " │ ";
|
|
13275
|
+
}
|
|
13276
|
+
output = output.trimEnd() + EOL;
|
|
13277
|
+
}
|
|
13278
|
+
output += "└";
|
|
13279
|
+
for (let i = 0; i < columnCount; i++) {
|
|
13280
|
+
const cw = colWidths[i] ?? 3;
|
|
13281
|
+
output += "─".repeat(cw + 2) + "┴";
|
|
13282
|
+
}
|
|
13283
|
+
output = output.slice(0, -1) + "┘\n";
|
|
13284
|
+
return output + EOL;
|
|
13285
|
+
}
|
|
13286
|
+
/**
|
|
13287
|
+
* 计算字符串在终端中的可见宽度(考虑 CJK 和 emoji 占 2 列宽)
|
|
13288
|
+
*
|
|
13289
|
+
* ANSI 转义序列不计入宽度,中文字符/emoji 占 2 列,ASCII 占 1 列。
|
|
13290
|
+
*/
|
|
13291
|
+
function ansiCellWidth(str) {
|
|
13292
|
+
const ESC = String.fromCharCode(27);
|
|
13293
|
+
const plain = str.replace(new RegExp(`${ESC}\\[[0-9;]*m`, "g"), "");
|
|
13294
|
+
let width = 0;
|
|
13295
|
+
for (const ch of plain) {
|
|
13296
|
+
const code = ch.codePointAt(0) ?? 0;
|
|
13297
|
+
if (isZeroWidthChar(code)) continue;
|
|
13298
|
+
if (isWideChar(code)) width += 2;
|
|
13299
|
+
else width += 1;
|
|
13300
|
+
}
|
|
13301
|
+
return width;
|
|
13302
|
+
}
|
|
13303
|
+
/**
|
|
13304
|
+
* 判断 Unicode 码点是否为零宽字符(不可见 / 组合用字符)
|
|
13305
|
+
*
|
|
13306
|
+
* 这些字符在终端中不占据任何列宽,无论是 CJK 字体还是等宽字体下。
|
|
13307
|
+
* 如果在宽度计算中按 1 或 2 计算,会导致表格列对齐错误。
|
|
13308
|
+
*/
|
|
13309
|
+
function isZeroWidthChar(code) {
|
|
13310
|
+
return code === 8203 || code === 8204 || code === 8205 || code === 8206 || code === 8207 || code === 8288 || code >= 8289 && code <= 8292 || code === 65038 || code === 65039 || code >= 768 && code <= 879 || code >= 7616 && code <= 7679 || code >= 8400 && code <= 8447 || code >= 65056 && code <= 65071;
|
|
13311
|
+
}
|
|
13312
|
+
/** 判断 Unicode 码点是否为终端宽字符(CJK / 全角 / emoji) */
|
|
13313
|
+
function isWideChar(code) {
|
|
13314
|
+
return code >= 4352 && code <= 4447 || code >= 11904 && code <= 12287 || code >= 12288 && code <= 13311 || code >= 13312 && code <= 19903 || code >= 19968 && code <= 40959 || code >= 40960 && code <= 42191 || code >= 44032 && code <= 55215 || code >= 63744 && code <= 64255 || code >= 65040 && code <= 65049 || code >= 65072 && code <= 65135 || code >= 65281 && code <= 65376 || code >= 65504 && code <= 65510 || code >= 126976 && code <= 131071 || code >= 131072 && code <= 196607 || code >= 196608 && code <= 262143 || code === 169 || code === 174 || code >= 8192 && code <= 8303 || code >= 8448 && code <= 10175 || code >= 11008 && code <= 11263 || code >= 10496 && code <= 10623 || code === 12336 || code === 12349 || code === 12951 || code === 12952 || code === 8986 || code === 8987 || code === 9193 || code === 9196 || code === 9200 || code === 9203 || code === 9725 || code === 9726 || code === 9748 || code === 9749 || code === 9800 || code === 9811 || code === 9855 || code === 9875 || code === 9889 || code === 9898 || code === 9899 || code === 9917 || code === 9918 || code === 9924 || code === 9925 || code === 9934 || code === 9940 || code === 9962 || code === 9970 || code === 9971 || code === 9973 || code === 9978 || code === 9981 || code === 9986 || code === 9989 || code === 9992 || code === 9993 || code === 9994 || code === 9995 || code === 9996 || code === 9997 || code === 9999 || code === 10002 || code === 10004 || code === 10006 || code === 10013 || code === 10017 || code === 10024 || code === 10035 || code === 10036 || code === 10052 || code === 10055 || code === 10060 || code === 10062 || code === 10067 || code === 10068 || code === 10069 || code === 10071 || code === 10083 || code === 10084 || code === 10133 || code === 10134 || code === 10135 || code === 10145 || code === 10160 || code === 10175 || code === 10548 || code === 10549 || code === 11013 || code === 11014 || code === 11015 || code === 11035 || code === 11036 || code === 11088 || code === 11093 || code === 11203 || code === 11204;
|
|
13315
|
+
}
|
|
13316
|
+
|
|
12600
13317
|
//#endregion
|
|
12601
13318
|
//#region src/config/types.ts
|
|
12602
13319
|
/**
|
|
@@ -13603,6 +14320,7 @@ function ensureTaskDir() {
|
|
|
13603
14320
|
}
|
|
13604
14321
|
var TaskStore = class {
|
|
13605
14322
|
tasks = /* @__PURE__ */ new Map();
|
|
14323
|
+
changeCallbacks = [];
|
|
13606
14324
|
/** 读取完整任务列表 */
|
|
13607
14325
|
read() {
|
|
13608
14326
|
return Array.from(this.tasks.values());
|
|
@@ -13646,6 +14364,24 @@ var TaskStore = class {
|
|
|
13646
14364
|
return this.tasks.size > 0;
|
|
13647
14365
|
}
|
|
13648
14366
|
/**
|
|
14367
|
+
* 注册任务变更监听
|
|
14368
|
+
*
|
|
14369
|
+
* @param callback - 任务列表变更时回调
|
|
14370
|
+
* @returns unsubscribe 函数,组件销毁时调用以清理
|
|
14371
|
+
*/
|
|
14372
|
+
onChange(callback) {
|
|
14373
|
+
this.changeCallbacks.push(callback);
|
|
14374
|
+
return () => {
|
|
14375
|
+
this.changeCallbacks = this.changeCallbacks.filter((cb) => cb !== callback);
|
|
14376
|
+
};
|
|
14377
|
+
}
|
|
14378
|
+
/** 通知所有监听者(write/update/clear 操作后调用) */
|
|
14379
|
+
notifyChange() {
|
|
14380
|
+
for (const cb of this.changeCallbacks) try {
|
|
14381
|
+
cb();
|
|
14382
|
+
} catch {}
|
|
14383
|
+
}
|
|
14384
|
+
/**
|
|
13649
14385
|
* 全量替换任务列表
|
|
13650
14386
|
*
|
|
13651
14387
|
* @param items - 新任务列表
|
|
@@ -13675,11 +14411,13 @@ var TaskStore = class {
|
|
|
13675
14411
|
updatedAt: now
|
|
13676
14412
|
};
|
|
13677
14413
|
if (item.description !== void 0) taskItem.description = item.description;
|
|
14414
|
+
if (item.dependencies !== void 0) taskItem.dependencies = item.dependencies;
|
|
13678
14415
|
newTasks.set(item.id, taskItem);
|
|
13679
14416
|
}
|
|
13680
14417
|
if (inProgressCount > 1) return `不允许同时有 ${inProgressCount} 个进行中的任务,请先将当前任务完成或取消后再开始新任务`;
|
|
13681
14418
|
this.tasks = newTasks;
|
|
13682
14419
|
this.persist();
|
|
14420
|
+
this.notifyChange();
|
|
13683
14421
|
return null;
|
|
13684
14422
|
}
|
|
13685
14423
|
/**
|
|
@@ -13705,12 +14443,14 @@ var TaskStore = class {
|
|
|
13705
14443
|
};
|
|
13706
14444
|
this.tasks.set(id, updated);
|
|
13707
14445
|
this.persist();
|
|
14446
|
+
this.notifyChange();
|
|
13708
14447
|
return null;
|
|
13709
14448
|
}
|
|
13710
14449
|
/** 清空所有任务 */
|
|
13711
14450
|
clear() {
|
|
13712
14451
|
this.tasks.clear();
|
|
13713
14452
|
this.persist();
|
|
14453
|
+
this.notifyChange();
|
|
13714
14454
|
}
|
|
13715
14455
|
/** 持久化到文件系统 */
|
|
13716
14456
|
persist() {
|
|
@@ -13761,7 +14501,7 @@ var TaskStore = class {
|
|
|
13761
14501
|
const lines = ["## 当前任务列表"];
|
|
13762
14502
|
for (const task of active) {
|
|
13763
14503
|
const marker = task.status === "in_progress" ? "▶" : "○";
|
|
13764
|
-
lines.push(` ${marker}
|
|
14504
|
+
lines.push(` ${marker} #${task.id} ${task.subject}`);
|
|
13765
14505
|
}
|
|
13766
14506
|
return lines.join("\n");
|
|
13767
14507
|
}
|
|
@@ -14863,6 +15603,8 @@ var ReplSession = class {
|
|
|
14863
15603
|
tui;
|
|
14864
15604
|
editor;
|
|
14865
15605
|
outputArea;
|
|
15606
|
+
taskStatusBar;
|
|
15607
|
+
agentStatusBar;
|
|
14866
15608
|
options;
|
|
14867
15609
|
_state = "idle";
|
|
14868
15610
|
parser;
|
|
@@ -14927,10 +15669,19 @@ var ReplSession = class {
|
|
|
14927
15669
|
"tui.select.cancel": ["escape"],
|
|
14928
15670
|
"tui.select.confirm": ["enter"]
|
|
14929
15671
|
});
|
|
15672
|
+
this.taskStore = new TaskStore();
|
|
14930
15673
|
this.outputArea = new OutputArea();
|
|
15674
|
+
this.agentStatusBar = new AgentStatusBar();
|
|
15675
|
+
this.taskStatusBar = new TaskStatusBar(this.taskStore);
|
|
15676
|
+
this.taskStore.onChange(() => {
|
|
15677
|
+
this.taskStatusBar.onTasksChanged();
|
|
15678
|
+
this.tui.requestRender();
|
|
15679
|
+
});
|
|
14931
15680
|
this.editor = new ZapmycoEditor(this.tui, theme.editorTheme);
|
|
14932
15681
|
const root = new Container();
|
|
14933
15682
|
root.addChild(this.outputArea);
|
|
15683
|
+
root.addChild(this.taskStatusBar);
|
|
15684
|
+
root.addChild(this.agentStatusBar);
|
|
14934
15685
|
root.addChild(this.editor);
|
|
14935
15686
|
this.tui.addChild(root);
|
|
14936
15687
|
this.tui.setFocus(this.editor);
|
|
@@ -14940,8 +15691,6 @@ var ReplSession = class {
|
|
|
14940
15691
|
this.history = new HistoryStore(this.options.maxHistorySize);
|
|
14941
15692
|
for (const entry of this.history.getAll()) this.editor.addToHistory(entry.input);
|
|
14942
15693
|
this.agent = this.createReplAgent();
|
|
14943
|
-
this.taskStore = new TaskStore();
|
|
14944
|
-
this.taskStore.load();
|
|
14945
15694
|
this.cronScheduler = new CronScheduler(getCronStore(), { isIdle: () => this._state === "idle" });
|
|
14946
15695
|
this.cronScheduler.start();
|
|
14947
15696
|
this.initWorktreeManager();
|
|
@@ -15128,12 +15877,13 @@ var ReplSession = class {
|
|
|
15128
15877
|
/**
|
|
15129
15878
|
* 执行用户目标 — 通过 Agent 执行并流式输出回复
|
|
15130
15879
|
*/
|
|
15131
|
-
async executeGoal(rawInput) {
|
|
15880
|
+
async executeGoal(rawInput, displayLabel) {
|
|
15132
15881
|
const startTime = Date.now();
|
|
15133
15882
|
let historyEntry;
|
|
15134
15883
|
const taskId = `task-${Date.now()}`;
|
|
15135
15884
|
const colorEnabled = this.options.color;
|
|
15136
15885
|
const userStyle = (s) => colorEnabled ? chalk.bold.cyan(s) : s;
|
|
15886
|
+
const markdownFormattingEnabled = this.config.cli.markdownFormatting !== false;
|
|
15137
15887
|
const responseStyle = (s) => s;
|
|
15138
15888
|
const toolStyle = (s) => colorEnabled ? chalk.yellow(s) : s;
|
|
15139
15889
|
const execStyle = (s) => colorEnabled ? chalk.cyan(s) : s;
|
|
@@ -15156,19 +15906,20 @@ var ReplSession = class {
|
|
|
15156
15906
|
goalId: `goal-${startTime}`,
|
|
15157
15907
|
rawInput
|
|
15158
15908
|
});
|
|
15159
|
-
this.outputArea.append([userStyle(rawInput), LOADING_FRAMES[0] ?? ""]);
|
|
15909
|
+
this.outputArea.append([userStyle(displayLabel ?? rawInput), LOADING_FRAMES$1[0] ?? ""]);
|
|
15160
15910
|
let spinnerFrame = 0;
|
|
15161
15911
|
spinnerActive = true;
|
|
15162
15912
|
spinnerInterval = setInterval(() => {
|
|
15163
15913
|
if (!spinnerActive) return;
|
|
15164
|
-
spinnerFrame = (spinnerFrame + 1) % LOADING_FRAMES.length;
|
|
15165
|
-
this.outputArea.replaceLastLine(LOADING_FRAMES[spinnerFrame] ?? "");
|
|
15914
|
+
spinnerFrame = (spinnerFrame + 1) % LOADING_FRAMES$1.length;
|
|
15915
|
+
this.outputArea.replaceLastLine(LOADING_FRAMES$1[spinnerFrame] ?? "");
|
|
15166
15916
|
this.tui.requestRender();
|
|
15167
15917
|
}, 100);
|
|
15168
15918
|
let spinnerStopped = false;
|
|
15169
15919
|
let outputAccumulator = "";
|
|
15170
15920
|
let thinkingAccumulator = "";
|
|
15171
15921
|
let streamMode = "response";
|
|
15922
|
+
let responseLineIndex = null;
|
|
15172
15923
|
const thinkingDisplayMode = this.config.cli.thinkingDisplay ?? "collapse";
|
|
15173
15924
|
let thinkingHeaderLineIndex = null;
|
|
15174
15925
|
let thinkingContentLineCount = 0;
|
|
@@ -15215,20 +15966,20 @@ var ReplSession = class {
|
|
|
15215
15966
|
streamMode = "response";
|
|
15216
15967
|
thinkingAccumulator = "";
|
|
15217
15968
|
outputAccumulator = event.text;
|
|
15218
|
-
this.outputArea.replaceLastLine(responseStyle(outputAccumulator));
|
|
15969
|
+
responseLineIndex = this.outputArea.replaceLastLine(responseStyle(outputAccumulator));
|
|
15219
15970
|
} else if (streamMode !== "response") {
|
|
15220
15971
|
streamMode = "response";
|
|
15221
15972
|
if (thinkingElapsedInterval) {
|
|
15222
15973
|
clearInterval(thinkingElapsedInterval);
|
|
15223
15974
|
thinkingElapsedInterval = void 0;
|
|
15224
15975
|
}
|
|
15225
|
-
if (thinkingHeaderLineIndex !== null && thinkingDisplayMode === "collapse") {
|
|
15226
|
-
const elapsed = ((Date.now() - thinkingStartTimeMs) / 1e3).toFixed(1);
|
|
15227
|
-
this.outputArea.updateLine(thinkingHeaderLineIndex, dimStyle(` \u2234 Thinking (${elapsed}s)`));
|
|
15228
|
-
}
|
|
15229
15976
|
thinkingAccumulator = "";
|
|
15230
15977
|
outputAccumulator = event.text;
|
|
15231
|
-
|
|
15978
|
+
if (thinkingHeaderLineIndex !== null) {
|
|
15979
|
+
this.outputArea.spliceLines(thinkingHeaderLineIndex, 1, [responseStyle(outputAccumulator)]);
|
|
15980
|
+
responseLineIndex = thinkingHeaderLineIndex;
|
|
15981
|
+
thinkingHeaderLineIndex = null;
|
|
15982
|
+
} else responseLineIndex = this.outputArea.append([responseStyle(outputAccumulator)]);
|
|
15232
15983
|
} else {
|
|
15233
15984
|
outputAccumulator += event.text;
|
|
15234
15985
|
this.outputArea.replaceLastLine(responseStyle(outputAccumulator));
|
|
@@ -15245,8 +15996,8 @@ var ReplSession = class {
|
|
|
15245
15996
|
spinnerStopped = true;
|
|
15246
15997
|
spinnerActive = false;
|
|
15247
15998
|
clearInterval(spinnerInterval);
|
|
15248
|
-
thinkingHeaderLineIndex = this.outputArea.replaceLastLine(dimStyle(` ${LOADING_FRAMES[0] ?? ""} Thinking...`));
|
|
15249
|
-
} else thinkingHeaderLineIndex = this.outputArea.append([dimStyle(` ${LOADING_FRAMES[0] ?? ""} Thinking...`)]);
|
|
15999
|
+
thinkingHeaderLineIndex = this.outputArea.replaceLastLine(dimStyle(` ${LOADING_FRAMES$1[0] ?? ""} Thinking...`));
|
|
16000
|
+
} else thinkingHeaderLineIndex = this.outputArea.append([dimStyle(` ${LOADING_FRAMES$1[0] ?? ""} Thinking...`)]);
|
|
15250
16001
|
thinkingAccumulator = event.text;
|
|
15251
16002
|
thinkingContentLineCount = 0;
|
|
15252
16003
|
if (thinkingDisplayMode === "expand") {
|
|
@@ -15258,7 +16009,7 @@ var ReplSession = class {
|
|
|
15258
16009
|
if (thinkingHeaderLineIndex === null) return;
|
|
15259
16010
|
if (thinkingContentLineCount > 0) return;
|
|
15260
16011
|
const elapsed = ((Date.now() - thinkingStartTimeMs) / 1e3).toFixed(1);
|
|
15261
|
-
const frame = LOADING_FRAMES[thinkingFrame % LOADING_FRAMES.length];
|
|
16012
|
+
const frame = LOADING_FRAMES$1[thinkingFrame % LOADING_FRAMES$1.length];
|
|
15262
16013
|
thinkingFrame++;
|
|
15263
16014
|
this.outputArea.updateLine(thinkingHeaderLineIndex, dimStyle(` ${frame} Thinking... (${elapsed}s)`));
|
|
15264
16015
|
this.tui.requestRender();
|
|
@@ -15286,16 +16037,17 @@ var ReplSession = class {
|
|
|
15286
16037
|
if (thinkingElapsedInterval && streamMode === "thinking") {
|
|
15287
16038
|
clearInterval(thinkingElapsedInterval);
|
|
15288
16039
|
thinkingElapsedInterval = void 0;
|
|
15289
|
-
if (thinkingHeaderLineIndex !== null
|
|
15290
|
-
|
|
15291
|
-
|
|
16040
|
+
if (thinkingHeaderLineIndex !== null) {
|
|
16041
|
+
this.outputArea.spliceLines(thinkingHeaderLineIndex, 1, []);
|
|
16042
|
+
thinkingHeaderLineIndex = null;
|
|
15292
16043
|
this.tui.requestRender();
|
|
15293
16044
|
}
|
|
15294
16045
|
}
|
|
15295
16046
|
if (event.message.startsWith("Exec(")) {
|
|
15296
16047
|
execMessage = event.message;
|
|
15297
16048
|
execLineIndex = this.outputArea.append([execStyle(` ⏺ ${event.message}`)]);
|
|
15298
|
-
} else
|
|
16049
|
+
} else if (event.message.startsWith("TaskManage(")) log.debug("TaskManage 工具调用已记录,跳过 TUI 展示");
|
|
16050
|
+
else this.outputArea.append([toolStyle(` → ${event.message}`)]);
|
|
15299
16051
|
this.tui.requestRender();
|
|
15300
16052
|
} else if (event.percent === 100 && execLineIndex !== void 0 && execMessage) {
|
|
15301
16053
|
if (event.message.startsWith("工具 Exec")) {
|
|
@@ -15360,8 +16112,8 @@ var ReplSession = class {
|
|
|
15360
16112
|
spinnerActive = true;
|
|
15361
16113
|
spinnerInterval = setInterval(() => {
|
|
15362
16114
|
if (!spinnerActive) return;
|
|
15363
|
-
spinnerFrame = (spinnerFrame + 1) % LOADING_FRAMES.length;
|
|
15364
|
-
this.outputArea.replaceLastLine(LOADING_FRAMES[spinnerFrame] ?? "");
|
|
16115
|
+
spinnerFrame = (spinnerFrame + 1) % LOADING_FRAMES$1.length;
|
|
16116
|
+
this.outputArea.replaceLastLine(LOADING_FRAMES$1[spinnerFrame] ?? "");
|
|
15365
16117
|
this.tui.requestRender();
|
|
15366
16118
|
}, 100);
|
|
15367
16119
|
}
|
|
@@ -15419,6 +16171,24 @@ var ReplSession = class {
|
|
|
15419
16171
|
status: taskResult.status
|
|
15420
16172
|
});
|
|
15421
16173
|
}
|
|
16174
|
+
if (markdownFormattingEnabled && taskResult.status === "success") {
|
|
16175
|
+
if (outputAccumulator && responseLineIndex !== null) {
|
|
16176
|
+
const formatted = formatMarkdown(outputAccumulator, colorEnabled);
|
|
16177
|
+
if (formatted) {
|
|
16178
|
+
const formattedLines = formatted.split("\n");
|
|
16179
|
+
this.outputArea.spliceLines(responseLineIndex, 1, formattedLines);
|
|
16180
|
+
this.tui.requestRender();
|
|
16181
|
+
}
|
|
16182
|
+
} else if (spinnerStopped === false && outputText) {
|
|
16183
|
+
const formatted = formatMarkdown(outputText, colorEnabled);
|
|
16184
|
+
if (formatted) {
|
|
16185
|
+
const lines = formatted.split("\n");
|
|
16186
|
+
this.outputArea.replaceLastLine(lines[0] ?? "");
|
|
16187
|
+
if (lines.length > 1) this.outputArea.append(lines.slice(1));
|
|
16188
|
+
this.tui.requestRender();
|
|
16189
|
+
}
|
|
16190
|
+
}
|
|
16191
|
+
}
|
|
15422
16192
|
this.outputArea.append([""]);
|
|
15423
16193
|
const duration = Date.now() - startTime;
|
|
15424
16194
|
const result = {
|
|
@@ -15843,8 +16613,14 @@ var ReplSession = class {
|
|
|
15843
16613
|
usage: spec.name,
|
|
15844
16614
|
handler: async (args) => {
|
|
15845
16615
|
const argsStr = args.join(" ");
|
|
15846
|
-
const
|
|
15847
|
-
|
|
16616
|
+
const entry = getSkillEntries().find((e) => sanitizeSkillCommandName(e.skill.name) === spec.name);
|
|
16617
|
+
if (entry) {
|
|
16618
|
+
const skillContent = formatSkillContent(entry.skill, argsStr);
|
|
16619
|
+
await this.executeGoal(skillContent, `/${spec.name}`);
|
|
16620
|
+
} else {
|
|
16621
|
+
const goalInput = argsStr ? `请使用 /${spec.name} 技能,参数: ${argsStr}` : `请使用 /${spec.name} 技能`;
|
|
16622
|
+
await this.executeGoal(goalInput);
|
|
16623
|
+
}
|
|
15848
16624
|
}
|
|
15849
16625
|
});
|
|
15850
16626
|
}
|
|
@@ -15884,7 +16660,15 @@ var ReplSession = class {
|
|
|
15884
16660
|
this.editor.onCtrlD = () => {
|
|
15885
16661
|
this.shutdown("收到 EOF (Ctrl+D)");
|
|
15886
16662
|
};
|
|
16663
|
+
this.editor.onToggleAgentBar = () => {
|
|
16664
|
+
this.agentStatusBar.toggle();
|
|
16665
|
+
this.tui.requestRender();
|
|
16666
|
+
};
|
|
15887
16667
|
this.editor.onOpenEditor = () => this.openInEditor();
|
|
16668
|
+
this.editor.onToggleTasks = () => {
|
|
16669
|
+
this.taskStatusBar.toggle();
|
|
16670
|
+
this.tui.requestRender();
|
|
16671
|
+
};
|
|
15888
16672
|
}
|
|
15889
16673
|
/** 设置信号处理 */
|
|
15890
16674
|
setupSignalHandlers() {
|
|
@@ -15895,6 +16679,19 @@ var ReplSession = class {
|
|
|
15895
16679
|
eventBus.on("system:shutdown", ({ reason }) => {
|
|
15896
16680
|
log.debug(`收到系统关闭信号: ${reason ?? "未知"}`);
|
|
15897
16681
|
});
|
|
16682
|
+
const instanceManager = getAgentInstanceManager();
|
|
16683
|
+
const refreshStatusBar = () => {
|
|
16684
|
+
this.agentStatusBar.invalidate();
|
|
16685
|
+
this.tui.requestRender();
|
|
16686
|
+
};
|
|
16687
|
+
instanceManager.on("instance:registered", refreshStatusBar);
|
|
16688
|
+
instanceManager.on("instance:transitioned", refreshStatusBar);
|
|
16689
|
+
instanceManager.on("instance:activity", refreshStatusBar);
|
|
16690
|
+
eventBus.on("system:shutdown", () => {
|
|
16691
|
+
instanceManager.off("instance:registered", refreshStatusBar);
|
|
16692
|
+
instanceManager.off("instance:transitioned", refreshStatusBar);
|
|
16693
|
+
instanceManager.off("instance:activity", refreshStatusBar);
|
|
16694
|
+
});
|
|
15898
16695
|
}
|
|
15899
16696
|
/** 取消当前正在执行的任务 */
|
|
15900
16697
|
cancelCurrentTask() {
|