@moikapy/lich 0.6.0 → 0.7.1
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/CHANGELOG.md +75 -1
- package/README.md +59 -7
- package/dist/{chunk-JC2G3XH2.js → chunk-CVX7LZWC.js} +2 -2
- package/dist/chunk-PZNYVGD4.js +58 -0
- package/dist/chunk-PZNYVGD4.js.map +1 -0
- package/dist/{chunk-7HLVKVIG.js → chunk-WNFBIX4E.js} +1344 -162
- package/dist/chunk-WNFBIX4E.js.map +1 -0
- package/dist/cli.js +246 -18
- package/dist/cli.js.map +1 -1
- package/dist/{gateway-RJFZJEUZ.js → gateway-44QTIJTJ.js} +108 -49
- package/dist/gateway-44QTIJTJ.js.map +1 -0
- package/dist/index.d.ts +288 -154
- package/dist/index.js +5 -3
- package/dist/{tui-LOUJVZ6A.js → tui-MEJGEILU.js} +25 -10
- package/dist/tui-MEJGEILU.js.map +1 -0
- package/docs/.vitepress/config.mts +4 -0
- package/docs/architecture/overview.md +30 -18
- package/docs/architecture/plugins.md +1 -1
- package/docs/architecture/tools.md +36 -4
- package/docs/getting-started.md +7 -6
- package/docs/index.md +5 -4
- package/docs/user-guide/cli.md +21 -8
- package/docs/user-guide/games.md +1 -1
- package/docs/user-guide/gateway.md +70 -16
- package/docs/user-guide/godot.md +3 -1
- package/docs/user-guide/library.md +4 -2
- package/docs/user-guide/plugins.md +2 -2
- package/docs/user-guide/redot.md +93 -0
- package/docs/user-guide/tui.md +2 -2
- package/examples/game_bridge/README.md +2 -0
- package/optional-mcps/godot/manifest.json +6 -0
- package/optional-mcps/redot/manifest.json +18 -0
- package/package.json +2 -1
- package/dist/chunk-6M6OAQGN.js +0 -17
- package/dist/chunk-6M6OAQGN.js.map +0 -1
- package/dist/chunk-7HLVKVIG.js.map +0 -1
- package/dist/gateway-RJFZJEUZ.js.map +0 -1
- package/dist/tui-LOUJVZ6A.js.map +0 -1
- /package/dist/{chunk-JC2G3XH2.js.map → chunk-CVX7LZWC.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -2,20 +2,22 @@
|
|
|
2
2
|
import {
|
|
3
3
|
load_theme,
|
|
4
4
|
notice_flavor
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-CVX7LZWC.js";
|
|
6
6
|
import {
|
|
7
|
-
LICH_VERSION
|
|
8
|
-
|
|
7
|
+
LICH_VERSION,
|
|
8
|
+
catalog_client_entry
|
|
9
|
+
} from "./chunk-PZNYVGD4.js";
|
|
9
10
|
import {
|
|
10
11
|
DEFAULT_GATEWAY_TOKEN_ENVS,
|
|
11
12
|
create_agent_with_plugins,
|
|
12
13
|
is_env_var_name,
|
|
13
14
|
parse_agent_config,
|
|
15
|
+
refuse_mcp_entry,
|
|
14
16
|
safe_json_parse
|
|
15
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-WNFBIX4E.js";
|
|
16
18
|
|
|
17
19
|
// src/cli.ts
|
|
18
|
-
import { existsSync as
|
|
20
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2, realpathSync } from "fs";
|
|
19
21
|
import { createInterface } from "readline";
|
|
20
22
|
import path4 from "path";
|
|
21
23
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
@@ -89,22 +91,26 @@ function starter_config_object() {
|
|
|
89
91
|
}
|
|
90
92
|
return parsed;
|
|
91
93
|
}
|
|
92
|
-
function write_lich_config(work_dir, config) {
|
|
94
|
+
function write_lich_config(work_dir, config, update = false) {
|
|
93
95
|
ensure_lich_config_dir(work_dir);
|
|
94
96
|
const file = project_config_path(work_dir);
|
|
95
|
-
if (existsSync(file) === true) {
|
|
97
|
+
if (existsSync(file) === true && update !== true) {
|
|
96
98
|
return already_exists(file);
|
|
97
99
|
}
|
|
98
100
|
try {
|
|
99
101
|
writeFileSync(file, `${JSON.stringify(config, null, 2)}
|
|
100
|
-
`, {
|
|
102
|
+
`, {
|
|
103
|
+
encoding: "utf8",
|
|
104
|
+
flag: update === true ? "w" : "wx"
|
|
105
|
+
});
|
|
101
106
|
} catch (error) {
|
|
102
|
-
if (is_eexist(error) === true) {
|
|
107
|
+
if (update !== true && is_eexist(error) === true) {
|
|
103
108
|
return already_exists(file);
|
|
104
109
|
}
|
|
105
110
|
throw error;
|
|
106
111
|
}
|
|
107
|
-
|
|
112
|
+
const verb = update === true ? "updated" : "wrote";
|
|
113
|
+
return { path: file, written: true, message: `${verb} ${file}` };
|
|
108
114
|
}
|
|
109
115
|
function already_exists(file) {
|
|
110
116
|
return {
|
|
@@ -117,9 +123,211 @@ function is_eexist(error) {
|
|
|
117
123
|
return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
|
|
118
124
|
}
|
|
119
125
|
|
|
126
|
+
// src/cli_mcp_add.ts
|
|
127
|
+
var SERVER_NAME = /^[a-z][a-z0-9_]*$/;
|
|
128
|
+
function require_server_name(name, action) {
|
|
129
|
+
if (name === void 0 || SERVER_NAME.test(name) === false) {
|
|
130
|
+
throw new Error(`mcp ${action} requires a snake_case name`);
|
|
131
|
+
}
|
|
132
|
+
return name;
|
|
133
|
+
}
|
|
134
|
+
function entry_from_flags(name, flags) {
|
|
135
|
+
if (flags.command !== void 0 && flags.url !== void 0) {
|
|
136
|
+
throw new Error("mcp add accepts --command or --url, not both");
|
|
137
|
+
}
|
|
138
|
+
if (flags.url !== void 0) {
|
|
139
|
+
if (flags.args.length > 0) {
|
|
140
|
+
throw new Error("mcp add --url does not take --arg");
|
|
141
|
+
}
|
|
142
|
+
return { enabled: false, url: flags.url };
|
|
143
|
+
}
|
|
144
|
+
if (flags.command === void 0) {
|
|
145
|
+
throw new Error("mcp add needs a catalog entry, --command, or --url");
|
|
146
|
+
}
|
|
147
|
+
return { enabled: false, command: flags.command, args: [...flags.args] };
|
|
148
|
+
}
|
|
149
|
+
function entry_from_catalog(name, flags) {
|
|
150
|
+
const substitutes = {};
|
|
151
|
+
if (flags.project_path !== void 0) {
|
|
152
|
+
substitutes["project_path"] = flags.project_path;
|
|
153
|
+
}
|
|
154
|
+
const built = catalog_client_entry(name, substitutes);
|
|
155
|
+
if (typeof built === "string") {
|
|
156
|
+
throw new Error(built);
|
|
157
|
+
}
|
|
158
|
+
return built;
|
|
159
|
+
}
|
|
160
|
+
function build_mcp_entry(name, flags) {
|
|
161
|
+
const entry = flags.command !== void 0 || flags.url !== void 0 ? entry_from_flags(name, flags) : entry_from_catalog(name, flags);
|
|
162
|
+
const refused = refuse_mcp_entry(name, {
|
|
163
|
+
command: typeof entry["command"] === "string" ? entry["command"] : void 0,
|
|
164
|
+
args: Array.isArray(entry["args"]) ? entry["args"].filter((arg) => typeof arg === "string") : [],
|
|
165
|
+
url: typeof entry["url"] === "string" ? entry["url"] : void 0
|
|
166
|
+
});
|
|
167
|
+
if (refused !== void 0) {
|
|
168
|
+
throw new Error(refused);
|
|
169
|
+
}
|
|
170
|
+
return entry;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/cli_mcp_store.ts
|
|
174
|
+
import { existsSync as existsSync2 } from "fs";
|
|
175
|
+
function read_work_config(work_dir) {
|
|
176
|
+
const file = project_config_path(work_dir);
|
|
177
|
+
if (existsSync2(file) === false) {
|
|
178
|
+
return {};
|
|
179
|
+
}
|
|
180
|
+
return load_config(file) ?? {};
|
|
181
|
+
}
|
|
182
|
+
function mcp_servers_of(config) {
|
|
183
|
+
const raw = config["mcp_servers"];
|
|
184
|
+
if (raw === void 0) {
|
|
185
|
+
return {};
|
|
186
|
+
}
|
|
187
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw) === true) {
|
|
188
|
+
throw new Error("mcp_servers must be an object");
|
|
189
|
+
}
|
|
190
|
+
const servers = {};
|
|
191
|
+
for (const [name, entry] of Object.entries(raw)) {
|
|
192
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry) === true) {
|
|
193
|
+
throw new Error(`mcp server '${name}' is not an object`);
|
|
194
|
+
}
|
|
195
|
+
servers[name] = { ...entry };
|
|
196
|
+
}
|
|
197
|
+
return servers;
|
|
198
|
+
}
|
|
199
|
+
function save_mcp_servers(work_dir, current, servers) {
|
|
200
|
+
const next = { ...current };
|
|
201
|
+
if (Object.keys(servers).length === 0) {
|
|
202
|
+
delete next["mcp_servers"];
|
|
203
|
+
} else {
|
|
204
|
+
next["mcp_servers"] = servers;
|
|
205
|
+
}
|
|
206
|
+
const result = write_lich_config(work_dir, next, true);
|
|
207
|
+
if (result.written !== true) {
|
|
208
|
+
throw new Error("mcp config was not written");
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function list_mcp_lines(servers) {
|
|
212
|
+
const lines = [];
|
|
213
|
+
for (const [name, entry] of Object.entries(servers)) {
|
|
214
|
+
const transport = typeof entry["url"] === "string" ? "http" : "stdio";
|
|
215
|
+
const enabled = entry["enabled"] === true ? "true" : "false";
|
|
216
|
+
lines.push(`${name} ${transport} ${enabled}`);
|
|
217
|
+
}
|
|
218
|
+
return lines;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// src/cli_mcp_edit.ts
|
|
222
|
+
function require_entry(servers, name) {
|
|
223
|
+
const entry = servers[name];
|
|
224
|
+
if (entry === void 0) {
|
|
225
|
+
throw new Error(`mcp server '${name}' not found`);
|
|
226
|
+
}
|
|
227
|
+
return entry;
|
|
228
|
+
}
|
|
229
|
+
function set_mcp_enabled(work_dir, name, enabled) {
|
|
230
|
+
const server = require_server_name(name, enabled === true ? "enable" : "disable");
|
|
231
|
+
const current = read_work_config(work_dir);
|
|
232
|
+
const servers = mcp_servers_of(current);
|
|
233
|
+
const entry = require_entry(servers, server);
|
|
234
|
+
servers[server] = { ...entry, enabled };
|
|
235
|
+
save_mcp_servers(work_dir, current, servers);
|
|
236
|
+
return enabled === true ? `enabled ${server}` : `disabled ${server}`;
|
|
237
|
+
}
|
|
238
|
+
function remove_mcp_server(work_dir, name) {
|
|
239
|
+
const server = require_server_name(name, "remove");
|
|
240
|
+
const current = read_work_config(work_dir);
|
|
241
|
+
const servers = mcp_servers_of(current);
|
|
242
|
+
require_entry(servers, server);
|
|
243
|
+
delete servers[server];
|
|
244
|
+
save_mcp_servers(work_dir, current, servers);
|
|
245
|
+
return `removed ${server}`;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// src/cli_mcp.ts
|
|
249
|
+
function write_line(line) {
|
|
250
|
+
process.stdout.write(`${line}
|
|
251
|
+
`);
|
|
252
|
+
}
|
|
253
|
+
function add_mcp_server(work_dir, name, flags) {
|
|
254
|
+
const server = require_server_name(name, "add");
|
|
255
|
+
const current = read_work_config(work_dir);
|
|
256
|
+
const servers = mcp_servers_of(current);
|
|
257
|
+
if (servers[server] !== void 0) {
|
|
258
|
+
throw new Error(`mcp server '${server}' already exists`);
|
|
259
|
+
}
|
|
260
|
+
servers[server] = build_mcp_entry(server, flags);
|
|
261
|
+
save_mcp_servers(work_dir, current, servers);
|
|
262
|
+
return `added ${server} disabled`;
|
|
263
|
+
}
|
|
264
|
+
function run_mcp(work_dir, positionals, flags) {
|
|
265
|
+
const action = positionals[1];
|
|
266
|
+
if (positionals.length > 3) {
|
|
267
|
+
throw new Error("mcp takes one name");
|
|
268
|
+
}
|
|
269
|
+
if (action === "list") {
|
|
270
|
+
if (positionals[2] !== void 0) {
|
|
271
|
+
throw new Error("mcp list takes no name");
|
|
272
|
+
}
|
|
273
|
+
const lines = list_mcp_lines(mcp_servers_of(read_work_config(work_dir)));
|
|
274
|
+
if (lines.length > 0) {
|
|
275
|
+
process.stdout.write(`${lines.join("\n")}
|
|
276
|
+
`);
|
|
277
|
+
}
|
|
278
|
+
return 0;
|
|
279
|
+
}
|
|
280
|
+
if (action === "add") {
|
|
281
|
+
write_line(add_mcp_server(work_dir, positionals[2], flags));
|
|
282
|
+
return 0;
|
|
283
|
+
}
|
|
284
|
+
if (action === "enable" || action === "disable") {
|
|
285
|
+
write_line(set_mcp_enabled(work_dir, positionals[2], action === "enable"));
|
|
286
|
+
return 0;
|
|
287
|
+
}
|
|
288
|
+
if (action === "remove") {
|
|
289
|
+
write_line(remove_mcp_server(work_dir, positionals[2]));
|
|
290
|
+
return 0;
|
|
291
|
+
}
|
|
292
|
+
throw new Error("mcp requires list, add, enable, disable, or remove");
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// src/cli_mcp_flags.ts
|
|
296
|
+
function empty_mcp_flags() {
|
|
297
|
+
return { args: [] };
|
|
298
|
+
}
|
|
299
|
+
function take_mcp_flag(argv, index, flags) {
|
|
300
|
+
const arg = argv[index];
|
|
301
|
+
if (arg === "--arg") {
|
|
302
|
+
const value2 = argv[index + 1];
|
|
303
|
+
if (value2 === void 0) {
|
|
304
|
+
throw new Error("--arg requires a value");
|
|
305
|
+
}
|
|
306
|
+
flags.args.push(value2);
|
|
307
|
+
return index + 1;
|
|
308
|
+
}
|
|
309
|
+
if (arg !== "--command" && arg !== "--url" && arg !== "--project-path") {
|
|
310
|
+
return void 0;
|
|
311
|
+
}
|
|
312
|
+
const value = argv[index + 1];
|
|
313
|
+
if (value === void 0 || value.startsWith("--") === true) {
|
|
314
|
+
throw new Error(`${arg} requires a value`);
|
|
315
|
+
}
|
|
316
|
+
if (arg === "--command") {
|
|
317
|
+
flags.command = value;
|
|
318
|
+
}
|
|
319
|
+
if (arg === "--url") {
|
|
320
|
+
flags.url = value;
|
|
321
|
+
}
|
|
322
|
+
if (arg === "--project-path") {
|
|
323
|
+
flags.project_path = value;
|
|
324
|
+
}
|
|
325
|
+
return index + 1;
|
|
326
|
+
}
|
|
327
|
+
|
|
120
328
|
// src/cli_update.ts
|
|
121
329
|
import { spawn } from "child_process";
|
|
122
|
-
import { existsSync as
|
|
330
|
+
import { existsSync as existsSync3 } from "fs";
|
|
123
331
|
import path2 from "path";
|
|
124
332
|
var PACKAGE_NAME = "@moikapy/lich";
|
|
125
333
|
var VIEW_ARGS = ["view", PACKAGE_NAME, "version"];
|
|
@@ -351,7 +559,7 @@ function bun_command_runner(command, args) {
|
|
|
351
559
|
async function run_update(module_path) {
|
|
352
560
|
return run_lich_update({
|
|
353
561
|
installed_version: LICH_VERSION,
|
|
354
|
-
install_kind: detect_install_kind(module_path, process.env, (dir) =>
|
|
562
|
+
install_kind: detect_install_kind(module_path, process.env, (dir) => existsSync3(path2.join(dir, ".git"))),
|
|
355
563
|
run_command: default_command_runner,
|
|
356
564
|
write_stdout: (text) => {
|
|
357
565
|
process.stdout.write(text);
|
|
@@ -607,6 +815,9 @@ function usage_text() {
|
|
|
607
815
|
" lich gateway <plat..> messaging gateway (webhook|telegram|discord|twitch)",
|
|
608
816
|
" lich config print a starter config template (save as .lich/config.json)",
|
|
609
817
|
" lich update install a newer @moikapy/lich from npm, if one exists",
|
|
818
|
+
" lich mcp list list mcp servers in .lich/config.json",
|
|
819
|
+
" lich mcp add <name> add a catalog or --command/--url server (disabled)",
|
|
820
|
+
" lich mcp enable <name> / disable <name> / remove <name>",
|
|
610
821
|
" lich --help show this help",
|
|
611
822
|
" lich --version print version",
|
|
612
823
|
"",
|
|
@@ -621,7 +832,11 @@ function usage_text() {
|
|
|
621
832
|
" --system-prompt <s> system prompt override",
|
|
622
833
|
" --session-dir <path> session transcript directory",
|
|
623
834
|
" --log-level <level> debug | info | warn | error",
|
|
624
|
-
" --theme <name> display theme (default lich; files in ~/.lich/themes)"
|
|
835
|
+
" --theme <name> display theme (default lich; files in ~/.lich/themes)",
|
|
836
|
+
" --command <bin> mcp add: local stdio binary",
|
|
837
|
+
" --arg <value> mcp add: repeatable stdio arg (may start with --)",
|
|
838
|
+
" --url <url> mcp add: loopback http url",
|
|
839
|
+
" --project-path <path> mcp add: catalog ${project_path} substitute"
|
|
625
840
|
].join("\n");
|
|
626
841
|
}
|
|
627
842
|
function error_message(error) {
|
|
@@ -640,7 +855,8 @@ function error_for_mode(mode, base_message) {
|
|
|
640
855
|
return base_message;
|
|
641
856
|
}
|
|
642
857
|
function parse_args(argv) {
|
|
643
|
-
const options = { overrides: {}, positionals: [] };
|
|
858
|
+
const options = { overrides: {}, positionals: [], mcp_flags: empty_mcp_flags() };
|
|
859
|
+
const mcp = argv.includes("mcp");
|
|
644
860
|
for (let index = 0; index < argv.length; index += 1) {
|
|
645
861
|
const arg = argv[index];
|
|
646
862
|
if (arg === void 0) {
|
|
@@ -663,6 +879,13 @@ function parse_args(argv) {
|
|
|
663
879
|
index += 1;
|
|
664
880
|
continue;
|
|
665
881
|
}
|
|
882
|
+
if (mcp === true) {
|
|
883
|
+
const consumed = take_mcp_flag(argv, index, options.mcp_flags);
|
|
884
|
+
if (consumed !== void 0) {
|
|
885
|
+
index = consumed;
|
|
886
|
+
continue;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
666
889
|
const override_key = FLAG_KEYS[arg];
|
|
667
890
|
if (override_key === void 0) {
|
|
668
891
|
if (arg.startsWith("--") === true) {
|
|
@@ -760,7 +983,7 @@ function build_config(options) {
|
|
|
760
983
|
apply_overrides(base, options.overrides);
|
|
761
984
|
const providers = base["providers"];
|
|
762
985
|
if (Array.isArray(providers) === false || providers.length === 0) {
|
|
763
|
-
|
|
986
|
+
base["providers"] = [env_provider(options.overrides)];
|
|
764
987
|
}
|
|
765
988
|
return parse_agent_config(base);
|
|
766
989
|
}
|
|
@@ -798,6 +1021,7 @@ async function run_one_shot(config, input) {
|
|
|
798
1021
|
result = await agent.run({ input });
|
|
799
1022
|
} finally {
|
|
800
1023
|
stop_progress();
|
|
1024
|
+
agent.close();
|
|
801
1025
|
}
|
|
802
1026
|
const final = result.outcome.final;
|
|
803
1027
|
if (final !== void 0 && final.content.length > 0) {
|
|
@@ -868,6 +1092,7 @@ async function run_chat(config) {
|
|
|
868
1092
|
}
|
|
869
1093
|
} finally {
|
|
870
1094
|
rl.close();
|
|
1095
|
+
agent.close();
|
|
871
1096
|
}
|
|
872
1097
|
}
|
|
873
1098
|
function model_hint(options) {
|
|
@@ -955,7 +1180,7 @@ function run_init(options) {
|
|
|
955
1180
|
return 0;
|
|
956
1181
|
}
|
|
957
1182
|
async function run_tui_entry(config) {
|
|
958
|
-
const { run_tui } = await import("./tui-
|
|
1183
|
+
const { run_tui } = await import("./tui-MEJGEILU.js");
|
|
959
1184
|
return run_tui(config);
|
|
960
1185
|
}
|
|
961
1186
|
function gateway_platforms(config, cli_platforms) {
|
|
@@ -966,7 +1191,7 @@ function gateway_platforms(config, cli_platforms) {
|
|
|
966
1191
|
return configured.length === 0 ? ["webhook"] : configured;
|
|
967
1192
|
}
|
|
968
1193
|
async function run_gateway_entry(config, platforms) {
|
|
969
|
-
const { run_gateway } = await import("./gateway-
|
|
1194
|
+
const { run_gateway } = await import("./gateway-44QTIJTJ.js");
|
|
970
1195
|
return run_gateway(config, gateway_platforms(config, platforms));
|
|
971
1196
|
}
|
|
972
1197
|
function is_cli_entry(input) {
|
|
@@ -994,7 +1219,7 @@ function node_entry_matches(module_url, entry) {
|
|
|
994
1219
|
return same_real_file(fileURLToPath(module_url), resolved);
|
|
995
1220
|
}
|
|
996
1221
|
function same_real_file(left, right) {
|
|
997
|
-
if (
|
|
1222
|
+
if (existsSync4(left) === false || existsSync4(right) === false) {
|
|
998
1223
|
return false;
|
|
999
1224
|
}
|
|
1000
1225
|
return realpathSync(left) === realpathSync(right);
|
|
@@ -1023,6 +1248,9 @@ async function run_cli(argv) {
|
|
|
1023
1248
|
`);
|
|
1024
1249
|
return 0;
|
|
1025
1250
|
}
|
|
1251
|
+
if (first === "mcp") {
|
|
1252
|
+
return run_mcp(work_dir_of(options), options.positionals, options.mcp_flags);
|
|
1253
|
+
}
|
|
1026
1254
|
if (first === "update") {
|
|
1027
1255
|
if (options.positionals.length > 1) {
|
|
1028
1256
|
throw new Error("update takes no arguments");
|