zapmyco 0.4.0 → 0.6.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 +147 -28
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.d.mts +341 -319
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{loader-BK1Z72gI.mjs → loader-CyGlMdl7.mjs} +49 -11
- package/dist/{loader-BK1Z72gI.mjs.map → loader-CyGlMdl7.mjs.map} +1 -1
- package/package.json +5 -5
package/dist/cli/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { D as
|
|
2
|
+
import { D as eventBus, F as __require, M as APP_NAME, N as SESSION_DIR_NAME, O as buildSkillSnapshot, P as __VERSION__, a as SubAgentManager, c as logger, g as ZapmycoErrorCode, k as loadSkills, m as WebError, s as configureLogger, t as loadConfig, v as createLlmBasedAgent } from "../loader-CyGlMdl7.mjs";
|
|
3
3
|
import { createHash, randomBytes } from "node:crypto";
|
|
4
4
|
import { mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
@@ -11,7 +11,7 @@ import { EventEmitter } from "node:events";
|
|
|
11
11
|
import chalk, { Chalk } from "chalk";
|
|
12
12
|
import { Command } from "commander";
|
|
13
13
|
import { getModel } from "@mariozechner/pi-ai";
|
|
14
|
-
import { Container, Editor, Key, ProcessTerminal, TUI, matchesKey, truncateToWidth, wrapTextWithAnsi } from "@mariozechner/pi-tui";
|
|
14
|
+
import { CombinedAutocompleteProvider, Container, Editor, Key, ProcessTerminal, TUI, matchesKey, truncateToWidth, wrapTextWithAnsi } from "@mariozechner/pi-tui";
|
|
15
15
|
import { spawn } from "node:child_process";
|
|
16
16
|
import TurndownService from "turndown";
|
|
17
17
|
import { lookup } from "node:dns/promises";
|
|
@@ -19,7 +19,7 @@ import { Client } from "@modelcontextprotocol/sdk/client";
|
|
|
19
19
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
20
20
|
|
|
21
21
|
//#region src/cli/repl/command-registry.ts
|
|
22
|
-
const log$
|
|
22
|
+
const log$6 = logger.child("repl:command-registry");
|
|
23
23
|
/**
|
|
24
24
|
* 命令注册表
|
|
25
25
|
*/
|
|
@@ -34,7 +34,7 @@ var CommandRegistry = class {
|
|
|
34
34
|
*/
|
|
35
35
|
register(cmd) {
|
|
36
36
|
const canonicalName = cmd.name.toLowerCase();
|
|
37
|
-
if (this.commands.has(canonicalName)) log$
|
|
37
|
+
if (this.commands.has(canonicalName)) log$6.warn(`命令 "${canonicalName}" 已存在,将被覆盖`);
|
|
38
38
|
this.commands.set(canonicalName, cmd);
|
|
39
39
|
for (const alias of cmd.aliases) {
|
|
40
40
|
const lowerAlias = alias.toLowerCase();
|
|
@@ -62,7 +62,7 @@ var CommandRegistry = class {
|
|
|
62
62
|
*/
|
|
63
63
|
async dispatch(parsed) {
|
|
64
64
|
if (parsed.kind !== "command") {
|
|
65
|
-
log$
|
|
65
|
+
log$6.warn("dispatch 收到了非 command 类型的输入");
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
68
68
|
const cmd = this.getCommand(parsed.name);
|
|
@@ -74,7 +74,7 @@ var CommandRegistry = class {
|
|
|
74
74
|
await cmd.handler(parsed.args, this.session);
|
|
75
75
|
} catch (error) {
|
|
76
76
|
const message = error instanceof Error ? error.message : String(error);
|
|
77
|
-
log$
|
|
77
|
+
log$6.error(`命令 /${cmd.name} 执行出错`, {}, error);
|
|
78
78
|
console.log(`\n 命令执行出错: ${message}\n`);
|
|
79
79
|
}
|
|
80
80
|
}
|
|
@@ -359,6 +359,8 @@ var ZapmycoEditor = class extends Editor {
|
|
|
359
359
|
onCtrlD;
|
|
360
360
|
/** 是否正在执行(用于显示 loading) */
|
|
361
361
|
#executing = false;
|
|
362
|
+
/** 是否显示 spinner(执行期间禁用输入但不一定显示 spinner) */
|
|
363
|
+
#showSpinner = true;
|
|
362
364
|
/** loading 动画帧索引 */
|
|
363
365
|
#loadingFrame = 0;
|
|
364
366
|
/** loading 动画定时器 */
|
|
@@ -380,11 +382,14 @@ var ZapmycoEditor = class extends Editor {
|
|
|
380
382
|
}
|
|
381
383
|
/**
|
|
382
384
|
* 设置执行状态(控制 loading spinner 显示)
|
|
385
|
+
* @param executing 是否正在执行
|
|
386
|
+
* @param showSpinner 是否显示 spinner(默认 true)。设为 false 时仅禁用输入,不显示动画
|
|
383
387
|
*/
|
|
384
|
-
setExecuting(executing) {
|
|
385
|
-
if (this.#executing === executing) return;
|
|
388
|
+
setExecuting(executing, showSpinner = true) {
|
|
389
|
+
if (this.#executing === executing && this.#showSpinner === showSpinner) return;
|
|
386
390
|
this.#executing = executing;
|
|
387
|
-
|
|
391
|
+
this.#showSpinner = showSpinner;
|
|
392
|
+
if (executing && showSpinner) {
|
|
388
393
|
this.#loadingFrame = 0;
|
|
389
394
|
this.#loadingTimer = setInterval(() => {
|
|
390
395
|
this.#loadingFrame = (this.#loadingFrame + 1) % LOADING_FRAMES.length;
|
|
@@ -417,7 +422,7 @@ var ZapmycoEditor = class extends Editor {
|
|
|
417
422
|
for (let i = 0; i < contentLines.length; i++) {
|
|
418
423
|
const prefix = i === 0 ? PROMPT_PREFIX : " ".repeat(promptWidth);
|
|
419
424
|
let line;
|
|
420
|
-
if (i === 0 && this.#executing) line = `${prefix}${LOADING_FRAMES[this.#loadingFrame]} ${contentLines[i]}`;
|
|
425
|
+
if (i === 0 && this.#executing && this.#showSpinner) line = `${prefix}${LOADING_FRAMES[this.#loadingFrame]} ${contentLines[i]}`;
|
|
421
426
|
else line = prefix + contentLines[i];
|
|
422
427
|
contentLines[i] = truncateToWidth(line, width);
|
|
423
428
|
}
|
|
@@ -718,7 +723,7 @@ const CRON_CONSTANTS = {
|
|
|
718
723
|
*
|
|
719
724
|
* @module cli/repl/cron/cron-scheduler
|
|
720
725
|
*/
|
|
721
|
-
const log$
|
|
726
|
+
const log$5 = logger.child("cron:scheduler");
|
|
722
727
|
var CronScheduler = class extends EventEmitter {
|
|
723
728
|
store;
|
|
724
729
|
jobs = [];
|
|
@@ -739,7 +744,7 @@ var CronScheduler = class extends EventEmitter {
|
|
|
739
744
|
if (this.running) return;
|
|
740
745
|
const loadedJobs = await this.store.load();
|
|
741
746
|
this.jobs = loadedJobs;
|
|
742
|
-
log$
|
|
747
|
+
log$5.info(`调度器启动,加载 ${loadedJobs.length} 个 durable 任务`);
|
|
743
748
|
await this.handleMissedJobs();
|
|
744
749
|
this.checkAutoExpiry();
|
|
745
750
|
this.running = true;
|
|
@@ -755,7 +760,7 @@ var CronScheduler = class extends EventEmitter {
|
|
|
755
760
|
clearInterval(this.timer);
|
|
756
761
|
this.timer = null;
|
|
757
762
|
}
|
|
758
|
-
log$
|
|
763
|
+
log$5.info("调度器已停止");
|
|
759
764
|
}
|
|
760
765
|
/** 添加任务 */
|
|
761
766
|
async addJob(job) {
|
|
@@ -767,7 +772,7 @@ var CronScheduler = class extends EventEmitter {
|
|
|
767
772
|
this.jobs.push(job);
|
|
768
773
|
await this.store.persist(this.jobs);
|
|
769
774
|
} else this.sessionJobs.push(job);
|
|
770
|
-
log$
|
|
775
|
+
log$5.info("任务已添加", {
|
|
771
776
|
id: job.id,
|
|
772
777
|
cron: job.cron,
|
|
773
778
|
durable: job.durable
|
|
@@ -897,7 +902,7 @@ var CronScheduler = class extends EventEmitter {
|
|
|
897
902
|
}, delay);
|
|
898
903
|
}
|
|
899
904
|
if (toDelete.length > 0) {
|
|
900
|
-
log$
|
|
905
|
+
log$5.info(`跳过 ${toDelete.length} 个错过的一次性任务(超出补发上限)`);
|
|
901
906
|
this.emit("missed-overflow", {
|
|
902
907
|
count: toDelete.length,
|
|
903
908
|
jobIds: toDelete.map((m) => m.id)
|
|
@@ -915,7 +920,7 @@ var CronScheduler = class extends EventEmitter {
|
|
|
915
920
|
job.lastFiredAt = now;
|
|
916
921
|
job.fireCount++;
|
|
917
922
|
this.removeJob(job.id);
|
|
918
|
-
log$
|
|
923
|
+
log$5.info("任务已过期并触发最后一次", { id: job.id });
|
|
919
924
|
}
|
|
920
925
|
}
|
|
921
926
|
}
|
|
@@ -976,7 +981,7 @@ function applyOneShotJitter(jobId, rawNext) {
|
|
|
976
981
|
*
|
|
977
982
|
* @module cli/repl/cron/cron-store
|
|
978
983
|
*/
|
|
979
|
-
const log$
|
|
984
|
+
const log$4 = logger.child("cron:store");
|
|
980
985
|
const STORE_FILE = join(join(homedir(), ".zapmyco", "cron"), "scheduled_tasks.json");
|
|
981
986
|
var CronStore = class {
|
|
982
987
|
filePath;
|
|
@@ -1000,13 +1005,13 @@ var CronStore = class {
|
|
|
1000
1005
|
const raw = await readFile(this.filePath, "utf-8");
|
|
1001
1006
|
const data = JSON.parse(raw);
|
|
1002
1007
|
if (!Array.isArray(data)) {
|
|
1003
|
-
log$
|
|
1008
|
+
log$4.warn("存储文件格式无效(非数组),将使用空列表");
|
|
1004
1009
|
return [];
|
|
1005
1010
|
}
|
|
1006
1011
|
return this.validateJobs(data);
|
|
1007
1012
|
} catch (err) {
|
|
1008
1013
|
if (err.code === "ENOENT") return [];
|
|
1009
|
-
log$
|
|
1014
|
+
log$4.warn("加载定时任务文件失败,将使用空列表", { error: err instanceof Error ? err.message : String(err) });
|
|
1010
1015
|
return [];
|
|
1011
1016
|
}
|
|
1012
1017
|
}
|
|
@@ -1053,7 +1058,7 @@ var CronStore = class {
|
|
|
1053
1058
|
if (typeof obj.maxFires === "number") job.maxFires = obj.maxFires;
|
|
1054
1059
|
valid.push(job);
|
|
1055
1060
|
}
|
|
1056
|
-
if (valid.length < raw.length) log$
|
|
1061
|
+
if (valid.length < raw.length) log$4.warn(`跳过 ${raw.length - valid.length} 个无效任务条目`);
|
|
1057
1062
|
return valid;
|
|
1058
1063
|
}
|
|
1059
1064
|
};
|
|
@@ -1065,8 +1070,19 @@ function getCronStore() {
|
|
|
1065
1070
|
|
|
1066
1071
|
//#endregion
|
|
1067
1072
|
//#region src/cli/repl/history-store.ts
|
|
1073
|
+
/**
|
|
1074
|
+
* 会话历史存储
|
|
1075
|
+
*
|
|
1076
|
+
* 基于内存的环形缓冲区,记录 REPL 会话中的用户输入和执行结果。
|
|
1077
|
+
* 支持文件持久化到 ~/.zapmyco/history.json,跨会话恢复。
|
|
1078
|
+
*/
|
|
1079
|
+
const log$3 = logger.child("history:store");
|
|
1068
1080
|
/** 默认最大历史条数 */
|
|
1069
1081
|
const DEFAULT_MAX_SIZE = 100;
|
|
1082
|
+
/** 历史文件存储路径 */
|
|
1083
|
+
function getHistoryFilePath() {
|
|
1084
|
+
return join(homedir(), SESSION_DIR_NAME, "history.json");
|
|
1085
|
+
}
|
|
1070
1086
|
/**
|
|
1071
1087
|
* 历史存储类
|
|
1072
1088
|
*/
|
|
@@ -1074,8 +1090,11 @@ var HistoryStore = class {
|
|
|
1074
1090
|
entries = [];
|
|
1075
1091
|
nextId = 1;
|
|
1076
1092
|
maxSize;
|
|
1093
|
+
filePath;
|
|
1077
1094
|
constructor(maxSize = DEFAULT_MAX_SIZE) {
|
|
1078
1095
|
this.maxSize = maxSize;
|
|
1096
|
+
this.filePath = getHistoryFilePath();
|
|
1097
|
+
this.load();
|
|
1079
1098
|
}
|
|
1080
1099
|
/** 添加条目 */
|
|
1081
1100
|
push(entry) {
|
|
@@ -1085,6 +1104,7 @@ var HistoryStore = class {
|
|
|
1085
1104
|
};
|
|
1086
1105
|
this.entries.push(newEntry);
|
|
1087
1106
|
if (this.entries.length > this.maxSize) this.entries.shift();
|
|
1107
|
+
this.save();
|
|
1088
1108
|
return newEntry;
|
|
1089
1109
|
}
|
|
1090
1110
|
/** 获取所有条目 */
|
|
@@ -1096,16 +1116,52 @@ var HistoryStore = class {
|
|
|
1096
1116
|
const count = Math.min(n, this.entries.length);
|
|
1097
1117
|
return this.entries.slice(-count);
|
|
1098
1118
|
}
|
|
1099
|
-
/**
|
|
1119
|
+
/** 清空所有条目(同时清除持久化文件) */
|
|
1100
1120
|
clear() {
|
|
1101
1121
|
this.entries = [];
|
|
1122
|
+
this.save();
|
|
1102
1123
|
}
|
|
1103
1124
|
/** 搜索条目(按输入内容模糊匹配) */
|
|
1104
1125
|
search(query) {
|
|
1105
1126
|
const lowerQuery = query.toLowerCase();
|
|
1106
1127
|
return this.entries.filter((entry) => entry.input.toLowerCase().includes(lowerQuery));
|
|
1107
1128
|
}
|
|
1129
|
+
/** 从文件加载历史记录 */
|
|
1130
|
+
load() {
|
|
1131
|
+
try {
|
|
1132
|
+
ensureDir(dirname(this.filePath));
|
|
1133
|
+
const raw = readFileSync(this.filePath, "utf-8");
|
|
1134
|
+
const data = JSON.parse(raw);
|
|
1135
|
+
if (Array.isArray(data.entries)) {
|
|
1136
|
+
this.entries = data.entries.slice(-this.maxSize);
|
|
1137
|
+
this.nextId = typeof data.nextId === "number" ? data.nextId : 1;
|
|
1138
|
+
log$3.debug("历史记录已加载", {
|
|
1139
|
+
count: this.entries.length,
|
|
1140
|
+
nextId: this.nextId
|
|
1141
|
+
});
|
|
1142
|
+
}
|
|
1143
|
+
} catch {
|
|
1144
|
+
log$3.debug("无历史文件或加载失败,使用空历史");
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
/** 持久化历史记录到文件 */
|
|
1148
|
+
save() {
|
|
1149
|
+
try {
|
|
1150
|
+
ensureDir(dirname(this.filePath));
|
|
1151
|
+
const data = JSON.stringify({
|
|
1152
|
+
entries: this.entries,
|
|
1153
|
+
nextId: this.nextId
|
|
1154
|
+
}, null, 2);
|
|
1155
|
+
writeFileSync(this.filePath, data, "utf-8");
|
|
1156
|
+
} catch (err) {
|
|
1157
|
+
log$3.warn("历史记录保存失败", { error: err instanceof Error ? err.message : String(err) });
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1108
1160
|
};
|
|
1161
|
+
/** 确保目录存在 */
|
|
1162
|
+
function ensureDir(dir) {
|
|
1163
|
+
mkdirSync(dir, { recursive: true });
|
|
1164
|
+
}
|
|
1109
1165
|
|
|
1110
1166
|
//#endregion
|
|
1111
1167
|
//#region src/cli/repl/input-parser.ts
|
|
@@ -6599,6 +6655,12 @@ var OutputArea = class extends Container {
|
|
|
6599
6655
|
else this.lines[this.lines.length - 1] += text;
|
|
6600
6656
|
this.invalidate();
|
|
6601
6657
|
}
|
|
6658
|
+
/** 替换最后一行的完整内容(用于 spinner 动画和首 chunk 替换) */
|
|
6659
|
+
replaceLastLine(text) {
|
|
6660
|
+
if (this.lines.length > 0) this.lines[this.lines.length - 1] = text;
|
|
6661
|
+
else this.lines.push(text);
|
|
6662
|
+
this.invalidate();
|
|
6663
|
+
}
|
|
6602
6664
|
/** 清空所有内容 */
|
|
6603
6665
|
clear() {
|
|
6604
6666
|
this.lines = [];
|
|
@@ -6666,6 +6728,7 @@ var ReplSession = class {
|
|
|
6666
6728
|
this.registry = new CommandRegistry(this);
|
|
6667
6729
|
this.renderer = new Renderer(this.options);
|
|
6668
6730
|
this.history = new HistoryStore(this.options.maxHistorySize);
|
|
6731
|
+
for (const entry of this.history.getAll()) this.editor.addToHistory(entry.input);
|
|
6669
6732
|
this.agent = this.createReplAgent();
|
|
6670
6733
|
this.taskStore = new TaskStore();
|
|
6671
6734
|
this.taskStore.load();
|
|
@@ -6749,10 +6812,13 @@ var ReplSession = class {
|
|
|
6749
6812
|
const startTime = Date.now();
|
|
6750
6813
|
let historyEntry;
|
|
6751
6814
|
const taskId = `task-${Date.now()}`;
|
|
6815
|
+
const ZAPMYCO_PREFIX = "ZapMyco: ";
|
|
6816
|
+
let spinnerActive = true;
|
|
6817
|
+
let spinnerInterval;
|
|
6752
6818
|
try {
|
|
6753
6819
|
this._state = "executing";
|
|
6754
6820
|
this.updateStatsState();
|
|
6755
|
-
this.editor.setExecuting(true);
|
|
6821
|
+
this.editor.setExecuting(true, false);
|
|
6756
6822
|
this.currentTaskAbort = new AbortController();
|
|
6757
6823
|
historyEntry = this.history.push({
|
|
6758
6824
|
timestamp: Date.now(),
|
|
@@ -6762,11 +6828,24 @@ var ReplSession = class {
|
|
|
6762
6828
|
goalId: `goal-${startTime}`,
|
|
6763
6829
|
rawInput
|
|
6764
6830
|
});
|
|
6765
|
-
|
|
6766
|
-
|
|
6831
|
+
this.outputArea.append([`Me: ${rawInput}`, ZAPMYCO_PREFIX + LOADING_FRAMES[0]]);
|
|
6832
|
+
let spinnerFrame = 0;
|
|
6833
|
+
spinnerActive = true;
|
|
6834
|
+
spinnerInterval = setInterval(() => {
|
|
6835
|
+
if (!spinnerActive) return;
|
|
6836
|
+
spinnerFrame = (spinnerFrame + 1) % LOADING_FRAMES.length;
|
|
6837
|
+
this.outputArea.replaceLastLine(ZAPMYCO_PREFIX + LOADING_FRAMES[spinnerFrame]);
|
|
6838
|
+
this.tui.requestRender();
|
|
6839
|
+
}, 100);
|
|
6840
|
+
let firstOutputReceived = false;
|
|
6767
6841
|
const outputHandler = (event) => {
|
|
6768
6842
|
if (event.taskId === taskId) {
|
|
6769
|
-
|
|
6843
|
+
if (!firstOutputReceived) {
|
|
6844
|
+
firstOutputReceived = true;
|
|
6845
|
+
spinnerActive = false;
|
|
6846
|
+
clearInterval(spinnerInterval);
|
|
6847
|
+
this.outputArea.replaceLastLine(ZAPMYCO_PREFIX + event.text);
|
|
6848
|
+
} else this.outputArea.appendText(event.text);
|
|
6770
6849
|
this.tui.requestRender();
|
|
6771
6850
|
}
|
|
6772
6851
|
};
|
|
@@ -6806,6 +6885,11 @@ var ReplSession = class {
|
|
|
6806
6885
|
duration: Date.now() - startTime
|
|
6807
6886
|
});
|
|
6808
6887
|
const outputText = typeof taskResult.output === "string" ? taskResult.output : taskResult.output != null ? JSON.stringify(taskResult.output) : null;
|
|
6888
|
+
if (spinnerActive) {
|
|
6889
|
+
spinnerActive = false;
|
|
6890
|
+
clearInterval(spinnerInterval);
|
|
6891
|
+
if (outputText) this.outputArea.replaceLastLine(ZAPMYCO_PREFIX + outputText);
|
|
6892
|
+
}
|
|
6809
6893
|
if (taskResult.status !== "success") {
|
|
6810
6894
|
const errorMsg = taskResult.error?.message ?? "Agent 执行失败(无详细错误信息)";
|
|
6811
6895
|
this.outputArea.appendText(`[错误] ${errorMsg}`);
|
|
@@ -6854,14 +6938,17 @@ var ReplSession = class {
|
|
|
6854
6938
|
} catch (error) {
|
|
6855
6939
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
6856
6940
|
log.error("目标执行失败", { input: rawInput }, err);
|
|
6941
|
+
spinnerActive = false;
|
|
6942
|
+
clearInterval(spinnerInterval);
|
|
6857
6943
|
this.stats.totalRequests++;
|
|
6858
6944
|
this.stats.failureCount++;
|
|
6859
6945
|
eventBus.emit("goal:failed", {
|
|
6860
6946
|
goalId: `goal-${startTime}`,
|
|
6861
6947
|
error: err
|
|
6862
6948
|
});
|
|
6863
|
-
|
|
6864
|
-
this.
|
|
6949
|
+
this.outputArea.replaceLastLine(`ZapMyco: [错误] ${err.message}`);
|
|
6950
|
+
const errorLines = this.renderer.renderError(err).slice(1);
|
|
6951
|
+
if (errorLines.length > 0) this.outputArea.append(errorLines);
|
|
6865
6952
|
const duration = Date.now() - startTime;
|
|
6866
6953
|
return {
|
|
6867
6954
|
goalId: `goal-${startTime}`,
|
|
@@ -6878,6 +6965,8 @@ var ReplSession = class {
|
|
|
6878
6965
|
}
|
|
6879
6966
|
};
|
|
6880
6967
|
} finally {
|
|
6968
|
+
spinnerActive = false;
|
|
6969
|
+
if (spinnerInterval) clearInterval(spinnerInterval);
|
|
6881
6970
|
this._state = "idle";
|
|
6882
6971
|
this.updateStatsState();
|
|
6883
6972
|
this.editor.setExecuting(false);
|
|
@@ -6906,9 +6995,11 @@ var ReplSession = class {
|
|
|
6906
6995
|
case "incomplete": break;
|
|
6907
6996
|
case "command":
|
|
6908
6997
|
await this.registry.dispatch(parsed);
|
|
6998
|
+
this.editor.addToHistory(line);
|
|
6909
6999
|
break;
|
|
6910
7000
|
case "goal":
|
|
6911
7001
|
await this.executeGoal(parsed.rawInput);
|
|
7002
|
+
this.editor.addToHistory(line);
|
|
6912
7003
|
break;
|
|
6913
7004
|
}
|
|
6914
7005
|
}
|
|
@@ -6921,6 +7012,26 @@ var ReplSession = class {
|
|
|
6921
7012
|
this.registry.register(createConfigCommand());
|
|
6922
7013
|
this.registry.register(createAgentsCommand());
|
|
6923
7014
|
this.registry.register(createStatusCommand());
|
|
7015
|
+
this.buildAutocompleteProvider();
|
|
7016
|
+
}
|
|
7017
|
+
/** 构建并设置 autocomplete provider,将命令注册表中的命令接入 pi-tui 补全系统 */
|
|
7018
|
+
buildAutocompleteProvider() {
|
|
7019
|
+
const slashCommands = [];
|
|
7020
|
+
for (const cmd of this.registry.listCommands()) {
|
|
7021
|
+
const base = {
|
|
7022
|
+
name: cmd.name,
|
|
7023
|
+
description: cmd.description
|
|
7024
|
+
};
|
|
7025
|
+
if (cmd.usage !== `/${cmd.name}`) base.argumentHint = cmd.usage;
|
|
7026
|
+
slashCommands.push(base);
|
|
7027
|
+
for (const alias of cmd.aliases) slashCommands.push({
|
|
7028
|
+
name: alias,
|
|
7029
|
+
description: `${cmd.description}(别名: /${cmd.name})`
|
|
7030
|
+
});
|
|
7031
|
+
}
|
|
7032
|
+
const provider = new CombinedAutocompleteProvider(slashCommands, process.cwd(), null);
|
|
7033
|
+
this.editor.setAutocompleteProvider(provider);
|
|
7034
|
+
this.editor.setAutocompleteMaxVisible(12);
|
|
6924
7035
|
}
|
|
6925
7036
|
/**
|
|
6926
7037
|
* 创建 REPL 专用的 Agent 实例
|
|
@@ -7010,6 +7121,7 @@ var ReplSession = class {
|
|
|
7010
7121
|
const snapshot = buildSkillSnapshot(entries, skillConfig.maxSkillsInPrompt);
|
|
7011
7122
|
this.agent.skillPrompt = snapshot.prompt;
|
|
7012
7123
|
this._registerSkillCommands(entries);
|
|
7124
|
+
this.buildAutocompleteProvider();
|
|
7013
7125
|
log.info("Skill 系统初始化完成", {
|
|
7014
7126
|
count: snapshot.count,
|
|
7015
7127
|
names: snapshot.names
|
|
@@ -7118,7 +7230,14 @@ var ReplSession = class {
|
|
|
7118
7230
|
* 加载配置 → 创建会话 → 进入输入循环
|
|
7119
7231
|
*/
|
|
7120
7232
|
async function startRepl() {
|
|
7121
|
-
|
|
7233
|
+
configureLogger({
|
|
7234
|
+
logFilePath: join(homedir(), ".zapmyco", "logs", "zapmyco.log"),
|
|
7235
|
+
quiet: true
|
|
7236
|
+
});
|
|
7237
|
+
const config = await loadConfig();
|
|
7238
|
+
if (config.logging?.level) configureLogger({ level: config.logging.level });
|
|
7239
|
+
if (config.logging?.file) configureLogger({ logFilePath: config.logging.file });
|
|
7240
|
+
await new ReplSession(config).start();
|
|
7122
7241
|
}
|
|
7123
7242
|
|
|
7124
7243
|
//#endregion
|