@rse/ase 0.0.49 → 0.0.51
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/dst/ase-hook.js +1 -0
- package/dst/ase-setup.js +446 -1
- package/dst/ase-statusline.js +2 -2
- package/package.json +2 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.github/plugin/plugin.json +1 -1
- package/plugin/agents/ase-meta-chat.md +49 -5
- package/plugin/agents/ase-meta-diagram.md +60 -0
- package/plugin/agents/ase-meta-search.md +8 -5
- package/plugin/meta/ase-skill.md +7 -5
- package/plugin/package.json +1 -1
- package/plugin/skills/ase-arch-analyze/SKILL.md +6 -5
- package/plugin/skills/ase-code-craft/SKILL.md +12 -8
- package/plugin/skills/ase-code-explain/SKILL.md +6 -4
- package/plugin/skills/ase-code-insight/SKILL.md +7 -4
- package/plugin/skills/ase-code-lint/SKILL.md +2 -2
- package/plugin/skills/ase-code-refactor/SKILL.md +11 -7
- package/plugin/skills/ase-code-resolve/SKILL.md +18 -11
- package/plugin/skills/ase-docs-proofread/SKILL.md +2 -2
- package/plugin/skills/ase-meta-chat/SKILL.md +22 -38
- package/plugin/skills/ase-meta-quorum/SKILL.md +59 -26
- package/plugin/skills/ase-meta-search/SKILL.md +50 -14
- package/plugin/skills/ase-meta-diagram/SKILL.md +0 -101
package/dst/ase-hook.js
CHANGED
package/dst/ase-setup.js
CHANGED
|
@@ -8,6 +8,9 @@ import path from "node:path";
|
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
import { execa } from "execa";
|
|
10
10
|
import which from "which";
|
|
11
|
+
import * as dotenvx from "@dotenvx/dotenvx";
|
|
12
|
+
import Table from "cli-table3";
|
|
13
|
+
import chalk from "chalk";
|
|
11
14
|
import Version from "./ase-version.js";
|
|
12
15
|
const toolSpecs = {
|
|
13
16
|
"claude": { cli: "claude", label: "Claude Code" },
|
|
@@ -96,7 +99,8 @@ export default class SetupCommand {
|
|
|
96
99
|
/* run a sub-process, suppressing output on success and emitting it on failure */
|
|
97
100
|
async run(cmd, args, opts = {}) {
|
|
98
101
|
const { cwd, quiet = false, retries = 1, ignoreError } = opts;
|
|
99
|
-
|
|
102
|
+
const argsLog = args.map((arg) => arg.replace(/(_KEY=)(\S+)/, (_, k, v) => k + "*".repeat(v.length)));
|
|
103
|
+
this.log.write("info", `setup: $ ${cmd} ${argsLog.join(" ")}` +
|
|
100
104
|
(cwd !== undefined ? ` (cwd: ${cwd})` : ""));
|
|
101
105
|
for (let i = 0; i < retries; i++) {
|
|
102
106
|
const final = (i === retries - 1);
|
|
@@ -240,6 +244,416 @@ export default class SetupCommand {
|
|
|
240
244
|
}
|
|
241
245
|
return 0;
|
|
242
246
|
}
|
|
247
|
+
/* handler for "ase setup mcp list" */
|
|
248
|
+
async doMcpList() {
|
|
249
|
+
const table = new Table({
|
|
250
|
+
head: ["ID", "NAME", "VERS", "MCP", "KEY", "SKILLS"],
|
|
251
|
+
colWidths: [16, 16, 8, 21, 17, 20],
|
|
252
|
+
wordWrap: true,
|
|
253
|
+
chars: { "mid": "", "left-mid": "", "mid-mid": "", "right-mid": "" },
|
|
254
|
+
style: { head: ["blue"] }
|
|
255
|
+
});
|
|
256
|
+
for (const handle of this.mcpServers)
|
|
257
|
+
table.push([
|
|
258
|
+
chalk.bold(handle.id),
|
|
259
|
+
handle.name,
|
|
260
|
+
handle.version ?? "(unknown)",
|
|
261
|
+
handle.server,
|
|
262
|
+
handle.env.join(", "),
|
|
263
|
+
handle.skills.join(", ")
|
|
264
|
+
]);
|
|
265
|
+
process.stdout.write(`${table.toString()}\n`);
|
|
266
|
+
return 0;
|
|
267
|
+
}
|
|
268
|
+
/* handler for "ase setup mcp activate|deactivate [<servers>]" */
|
|
269
|
+
async doMcp(action, tool, servers) {
|
|
270
|
+
await this.ensureTool(toolSpecs[tool].cli);
|
|
271
|
+
/* source .env files into the environment so the per-server
|
|
272
|
+
API keys (ASE_MCP_KEY_<XXX>) can live in a .env file instead
|
|
273
|
+
of the exported interactive shell environment */
|
|
274
|
+
dotenvx.config({ quiet: true, ignore: ["MISSING_ENV_FILE"] });
|
|
275
|
+
/* resolve the comma-separated list of server ids, with an empty
|
|
276
|
+
list or the literal "all" expanding to every registered server
|
|
277
|
+
id; track whether the ids were explicitly given on the CLI */
|
|
278
|
+
const known = this.mcpServers.map((handle) => handle.id);
|
|
279
|
+
const explicit = servers.trim() !== "" && servers.trim() !== "all";
|
|
280
|
+
const ids = explicit ?
|
|
281
|
+
servers.split(",").map((s) => s.trim()).filter((s) => s !== "") : known;
|
|
282
|
+
for (const id of ids)
|
|
283
|
+
if (this.mcpServers.find((handle) => handle.id === id) === undefined)
|
|
284
|
+
throw new Error(`unknown MCP server "${id}" ` +
|
|
285
|
+
`(known: ${known.join(", ")})`);
|
|
286
|
+
/* dispatch each selected server to its dedicated handler */
|
|
287
|
+
for (const id of ids) {
|
|
288
|
+
/* find handle */
|
|
289
|
+
const handle = this.mcpServers.find((handle) => handle.id === id);
|
|
290
|
+
/* determine information and action */
|
|
291
|
+
let envKey = "";
|
|
292
|
+
let envVal = "";
|
|
293
|
+
if (action === "activate") {
|
|
294
|
+
/* on activation, require at least one of the per-server API
|
|
295
|
+
key environment variables (ASE_MCP_KEY_<XXX>) to
|
|
296
|
+
be set; skip the server when its id was only
|
|
297
|
+
implicitly selected (empty list or "all"), but fail
|
|
298
|
+
hard when it was given explicitly on the CLI */
|
|
299
|
+
envKey = handle.env.find((name) => (process.env[`ASE_MCP_KEY_${name}`] ?? "") !== "") ?? "";
|
|
300
|
+
if (envKey === "") {
|
|
301
|
+
const vars = handle.env.map((name) => `ASE_MCP_KEY_${name}`).join(", ");
|
|
302
|
+
if (explicit)
|
|
303
|
+
throw new Error(`none of ${vars} set: ` +
|
|
304
|
+
`cannot activate MCP server "${handle.server}"`);
|
|
305
|
+
this.log.write("info", `setup: mcp: activate: [${id}]: none of ${vars} set: ` +
|
|
306
|
+
`skipping MCP server "${handle.server}" (${handle.name})`);
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
envVal = process.env[`ASE_MCP_KEY_${envKey}`] ?? "";
|
|
310
|
+
}
|
|
311
|
+
/* probe whether the MCP server is currently registered with the tool */
|
|
312
|
+
const installed = await this.mcpInstalled(tool, handle.server);
|
|
313
|
+
if (action === "activate") {
|
|
314
|
+
/* on activation, remove a stale registration first so the
|
|
315
|
+
handler can re-create it cleanly */
|
|
316
|
+
if (installed) {
|
|
317
|
+
this.log.write("info", `setup: mcp: activate: [${id}]: MCP server "${handle.server}" ` +
|
|
318
|
+
"already registered: removing stale registration first");
|
|
319
|
+
await this.mcpRemove(tool, handle.server);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
else if (!installed) {
|
|
323
|
+
/* on deactivation, skip the removal of an absent server */
|
|
324
|
+
this.log.write("info", `setup: mcp: deactivate: [${id}]: MCP server "${handle.server}" ` +
|
|
325
|
+
"not registered: skipping removal");
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
/* call the handler */
|
|
329
|
+
this.log.write("info", `setup: mcp: ${action}: [${id}]: MCP server "${handle.server}" ` +
|
|
330
|
+
`(name: ${handle.name}${handle.version ? (", version: " + handle.version) : ""})`);
|
|
331
|
+
await handle.handler(handle, tool, action, envKey, envVal);
|
|
332
|
+
}
|
|
333
|
+
return 0;
|
|
334
|
+
}
|
|
335
|
+
/* probe whether an MCP server is currently registered with the tool
|
|
336
|
+
by inspecting the exit code of "<cli> mcp get <name>" */
|
|
337
|
+
async mcpInstalled(tool, name) {
|
|
338
|
+
const result = await execa(toolSpecs[tool].cli, ["mcp", "get", name], { stdio: "ignore", reject: false });
|
|
339
|
+
return result.exitCode === 0;
|
|
340
|
+
}
|
|
341
|
+
/* register an MCP server with the tool, supporting both the "stdio"
|
|
342
|
+
(a local subprocess command) and "http" (a remote URL, optionally
|
|
343
|
+
with HTTP headers) transports; the per-tool command line differs
|
|
344
|
+
between Claude Code and GitHub Copilot CLI */
|
|
345
|
+
async mcpAdd(tool, name, env, transport) {
|
|
346
|
+
const args = ["mcp", "add"];
|
|
347
|
+
if (tool === "claude") {
|
|
348
|
+
args.push("--scope", "user");
|
|
349
|
+
args.push("--transport", transport.type);
|
|
350
|
+
if (transport.type === "stdio") {
|
|
351
|
+
for (const [key, val] of Object.entries(env))
|
|
352
|
+
args.push("-e", `${key}=${val}`);
|
|
353
|
+
args.push("--", name, ...transport.command);
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
for (const [key, val] of Object.entries(transport.headers ?? {}))
|
|
357
|
+
args.push("--header", `${key}: ${val}`);
|
|
358
|
+
args.push(name, transport.url);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
/* GitHub Copilot CLI implies the stdio transport when the
|
|
363
|
+
command is provided after "--"; only "http"/"sse" servers
|
|
364
|
+
need an explicit "--transport" flag and take the URL as a
|
|
365
|
+
positional argument */
|
|
366
|
+
if (transport.type === "stdio") {
|
|
367
|
+
args.push(name);
|
|
368
|
+
for (const [key, val] of Object.entries(env))
|
|
369
|
+
args.push("--env", `${key}=${val}`);
|
|
370
|
+
args.push("--", ...transport.command);
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
args.push("--transport", "http");
|
|
374
|
+
for (const [key, val] of Object.entries(transport.headers ?? {}))
|
|
375
|
+
args.push("--header", `${key}: ${val}`);
|
|
376
|
+
args.push(name, transport.url);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
await this.run(toolSpecs[tool].cli, args);
|
|
380
|
+
}
|
|
381
|
+
/* unregister an MCP server from the tool; the per-tool command line
|
|
382
|
+
differs between Claude Code and GitHub Copilot CLI */
|
|
383
|
+
async mcpRemove(tool, name) {
|
|
384
|
+
const args = tool === "claude" ?
|
|
385
|
+
["mcp", "remove", "--scope", "user", name] :
|
|
386
|
+
["mcp", "remove", name];
|
|
387
|
+
await this.run(toolSpecs[tool].cli, args, { ignoreError: `MCP server "${name}" not registered` });
|
|
388
|
+
}
|
|
389
|
+
/* registry of pre-defined MCP servers: maps each server id onto its
|
|
390
|
+
dedicated handler which performs the activate/deactivate operation */
|
|
391
|
+
mcpServers = [
|
|
392
|
+
{
|
|
393
|
+
id: "openai-chatgpt",
|
|
394
|
+
name: "OpenAI ChatGPT",
|
|
395
|
+
version: "5.5",
|
|
396
|
+
env: ["OPENAI_CHATGPT", "OPENROUTER"],
|
|
397
|
+
server: "chat-openai-chatgpt",
|
|
398
|
+
skills: ["ase-meta-chat", "ase-meta-quorum"],
|
|
399
|
+
handler: async (spec, tool, action, envKey, envVal) => {
|
|
400
|
+
if (action === "activate") {
|
|
401
|
+
if (envKey === "OPENROUTER")
|
|
402
|
+
await this.mcpAdd(tool, spec.server, { OPENAI_KEY: envVal }, {
|
|
403
|
+
type: "stdio", command: [
|
|
404
|
+
"npx", "-y", "mcp-to-openai",
|
|
405
|
+
"--service", spec.name,
|
|
406
|
+
"--mcp-tool", "query",
|
|
407
|
+
"--openai-url", "https://openrouter.ai/api/v1",
|
|
408
|
+
"--openai-api", "completion",
|
|
409
|
+
"--openai-model", "openai/gpt-5.5"
|
|
410
|
+
]
|
|
411
|
+
});
|
|
412
|
+
else
|
|
413
|
+
await this.mcpAdd(tool, spec.server, { OPENAI_KEY: envVal }, {
|
|
414
|
+
type: "stdio", command: [
|
|
415
|
+
"npx", "-y", "mcp-to-openai",
|
|
416
|
+
"--service", spec.name,
|
|
417
|
+
"--mcp-tool", "query",
|
|
418
|
+
"--openai-url", "https://api.openai.com/v1",
|
|
419
|
+
"--openai-api", "responses",
|
|
420
|
+
"--openai-model", "gpt-5.5"
|
|
421
|
+
]
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
else
|
|
425
|
+
await this.mcpRemove(tool, spec.server);
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
id: "google-gemini",
|
|
430
|
+
name: "Google Gemini",
|
|
431
|
+
version: "3.5",
|
|
432
|
+
env: ["GOOGLE_GEMINI", "OPENROUTER"],
|
|
433
|
+
server: "chat-google-gemini",
|
|
434
|
+
skills: ["ase-meta-chat", "ase-meta-quorum"],
|
|
435
|
+
handler: async (spec, tool, action, envKey, envVal) => {
|
|
436
|
+
if (action === "activate") {
|
|
437
|
+
if (envKey === "OPENROUTER")
|
|
438
|
+
await this.mcpAdd(tool, spec.server, { OPENAI_KEY: envVal }, {
|
|
439
|
+
type: "stdio", command: [
|
|
440
|
+
"npx", "-y", "mcp-to-openai",
|
|
441
|
+
"--service", spec.name,
|
|
442
|
+
"--mcp-tool", "query",
|
|
443
|
+
"--openai-url", "https://openrouter.ai/api/v1",
|
|
444
|
+
"--openai-api", "completion",
|
|
445
|
+
"--openai-model", "google/gemini-3.5-flash"
|
|
446
|
+
]
|
|
447
|
+
});
|
|
448
|
+
else
|
|
449
|
+
await this.mcpAdd(tool, spec.server, { OPENAI_KEY: envVal }, {
|
|
450
|
+
type: "stdio", command: [
|
|
451
|
+
"npx", "-y", "mcp-to-openai",
|
|
452
|
+
"--service", spec.name,
|
|
453
|
+
"--mcp-tool", "query",
|
|
454
|
+
"--openai-url", "https://generativelanguage.googleapis.com/v1beta/openai/",
|
|
455
|
+
"--openai-api", "completion",
|
|
456
|
+
"--openai-model", "gemini-3.5-flash"
|
|
457
|
+
]
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
else
|
|
461
|
+
await this.mcpRemove(tool, spec.server);
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
id: "deepseek",
|
|
466
|
+
name: "DeepSeek",
|
|
467
|
+
version: "4.0",
|
|
468
|
+
env: ["DEEPSEEK", "OPENROUTER"],
|
|
469
|
+
server: "chat-deepseek",
|
|
470
|
+
skills: ["ase-meta-chat", "ase-meta-quorum"],
|
|
471
|
+
handler: async (spec, tool, action, envKey, envVal) => {
|
|
472
|
+
if (action === "activate") {
|
|
473
|
+
if (envKey === "OPENROUTER")
|
|
474
|
+
await this.mcpAdd(tool, spec.server, { OPENAI_KEY: envVal }, {
|
|
475
|
+
type: "stdio",
|
|
476
|
+
command: [
|
|
477
|
+
"npx", "-y", "mcp-to-openai",
|
|
478
|
+
"--service", spec.name,
|
|
479
|
+
"--mcp-tool", "query",
|
|
480
|
+
"--openai-url", "https://openrouter.ai/api/v1",
|
|
481
|
+
"--openai-api", "completion",
|
|
482
|
+
"--openai-model", "deepseek/deepseek-v4-flash"
|
|
483
|
+
]
|
|
484
|
+
});
|
|
485
|
+
else
|
|
486
|
+
await this.mcpAdd(tool, spec.server, { OPENAI_KEY: envVal }, {
|
|
487
|
+
type: "stdio", command: [
|
|
488
|
+
"npx", "-y", "mcp-to-openai",
|
|
489
|
+
"--service", spec.name,
|
|
490
|
+
"--mcp-tool", "query",
|
|
491
|
+
"--openai-url", "https://api.deepseek.com/v1",
|
|
492
|
+
"--openai-api", "completion",
|
|
493
|
+
"--openai-model", "deepseek-v4-flash"
|
|
494
|
+
]
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
else
|
|
498
|
+
await this.mcpRemove(tool, spec.server);
|
|
499
|
+
}
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
id: "xai-grok",
|
|
503
|
+
name: "xAI Grok",
|
|
504
|
+
version: "4.3",
|
|
505
|
+
env: ["XAI_GROK", "OPENROUTER"],
|
|
506
|
+
server: "chat-xai-grok",
|
|
507
|
+
skills: ["ase-meta-chat", "ase-meta-quorum"],
|
|
508
|
+
handler: async (spec, tool, action, envKey, envVal) => {
|
|
509
|
+
if (action === "activate") {
|
|
510
|
+
if (envKey === "OPENROUTER")
|
|
511
|
+
await this.mcpAdd(tool, spec.server, { OPENAI_KEY: envVal }, {
|
|
512
|
+
type: "stdio", command: [
|
|
513
|
+
"npx", "-y", "mcp-to-openai",
|
|
514
|
+
"--service", spec.name,
|
|
515
|
+
"--mcp-tool", "query",
|
|
516
|
+
"--openai-url", "https://openrouter.ai/api/v1",
|
|
517
|
+
"--openai-api", "completion",
|
|
518
|
+
"--openai-model", "x-ai/grok-4.3"
|
|
519
|
+
]
|
|
520
|
+
});
|
|
521
|
+
else
|
|
522
|
+
await this.mcpAdd(tool, spec.server, { OPENAI_KEY: envVal }, {
|
|
523
|
+
type: "stdio", command: [
|
|
524
|
+
"npx", "-y", "mcp-to-openai",
|
|
525
|
+
"--service", spec.name,
|
|
526
|
+
"--mcp-tool", "query",
|
|
527
|
+
"--openai-url", "https://api.x.ai/v1",
|
|
528
|
+
"--openai-api", "completion",
|
|
529
|
+
"--openai-model", "grok-4.3"
|
|
530
|
+
]
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
else
|
|
534
|
+
await this.mcpRemove(tool, spec.server);
|
|
535
|
+
}
|
|
536
|
+
},
|
|
537
|
+
{
|
|
538
|
+
id: "alibaba-qwen",
|
|
539
|
+
name: "Alibaba Qwen",
|
|
540
|
+
version: "3.7",
|
|
541
|
+
env: ["ALIBABA_QWEN", "OPENROUTER"],
|
|
542
|
+
server: "chat-alibaba-qwen",
|
|
543
|
+
skills: ["ase-meta-chat", "ase-meta-quorum"],
|
|
544
|
+
handler: async (spec, tool, action, envKey, envVal) => {
|
|
545
|
+
if (action === "activate") {
|
|
546
|
+
if (envKey === "OPENROUTER")
|
|
547
|
+
await this.mcpAdd(tool, spec.server, { OPENAI_KEY: envVal }, {
|
|
548
|
+
type: "stdio", command: [
|
|
549
|
+
"npx", "-y", "mcp-to-openai",
|
|
550
|
+
"--service", spec.name,
|
|
551
|
+
"--mcp-tool", "query",
|
|
552
|
+
"--openai-url", "https://openrouter.ai/api/v1",
|
|
553
|
+
"--openai-api", "completion",
|
|
554
|
+
"--openai-model", "qwen/qwen3.7-max"
|
|
555
|
+
]
|
|
556
|
+
});
|
|
557
|
+
else
|
|
558
|
+
await this.mcpAdd(tool, spec.server, { OPENAI_KEY: envVal }, {
|
|
559
|
+
type: "stdio", command: [
|
|
560
|
+
"npx", "-y", "mcp-to-openai",
|
|
561
|
+
"--service", spec.name,
|
|
562
|
+
"--mcp-tool", "query",
|
|
563
|
+
"--openai-url", "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
564
|
+
"--openai-api", "completion",
|
|
565
|
+
"--openai-model", "qwen3.7-max"
|
|
566
|
+
]
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
else
|
|
570
|
+
await this.mcpRemove(tool, spec.server);
|
|
571
|
+
}
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
id: "zai-glm",
|
|
575
|
+
name: "Z.AI GLM",
|
|
576
|
+
version: "5.1",
|
|
577
|
+
env: ["ZAI_GLM", "OPENROUTER"],
|
|
578
|
+
server: "chat-zai-glm",
|
|
579
|
+
skills: ["ase-meta-chat", "ase-meta-quorum"],
|
|
580
|
+
handler: async (spec, tool, action, envKey, envVal) => {
|
|
581
|
+
if (action === "activate") {
|
|
582
|
+
if (envKey === "OPENROUTER")
|
|
583
|
+
await this.mcpAdd(tool, spec.server, { OPENAI_KEY: envVal }, {
|
|
584
|
+
type: "stdio", command: [
|
|
585
|
+
"npx", "-y", "mcp-to-openai",
|
|
586
|
+
"--service", spec.name,
|
|
587
|
+
"--mcp-tool", "query",
|
|
588
|
+
"--openai-url", "https://openrouter.ai/api/v1",
|
|
589
|
+
"--openai-api", "completion",
|
|
590
|
+
"--openai-model", "z-ai/glm-5.1"
|
|
591
|
+
]
|
|
592
|
+
});
|
|
593
|
+
else
|
|
594
|
+
await this.mcpAdd(tool, spec.server, { OPENAI_KEY: envVal }, {
|
|
595
|
+
type: "stdio", command: [
|
|
596
|
+
"npx", "-y", "mcp-to-openai",
|
|
597
|
+
"--service", spec.name,
|
|
598
|
+
"--mcp-tool", "query",
|
|
599
|
+
"--openai-url", "https://api.z.ai/api/paas/v4/",
|
|
600
|
+
"--openai-api", "completion",
|
|
601
|
+
"--openai-model", "glm-5.1"
|
|
602
|
+
]
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
else
|
|
606
|
+
await this.mcpRemove(tool, spec.server);
|
|
607
|
+
}
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
id: "brave",
|
|
611
|
+
name: "Brave",
|
|
612
|
+
version: "latest",
|
|
613
|
+
env: ["BRAVE"],
|
|
614
|
+
server: "search-brave",
|
|
615
|
+
skills: ["ase-meta-search", "ase-meta-evaluate", "ase-arch-discover"],
|
|
616
|
+
handler: async (spec, tool, action, _envKey, envVal) => {
|
|
617
|
+
if (action === "activate")
|
|
618
|
+
await this.mcpAdd(tool, spec.server, {
|
|
619
|
+
"BRAVE_API_KEY": envVal,
|
|
620
|
+
"BRAVE_MCP_ENABLED_TOOLS": "brave_web_search"
|
|
621
|
+
}, { type: "stdio", command: ["npx", "-y", "@brave/brave-search-mcp-server"] });
|
|
622
|
+
else
|
|
623
|
+
await this.mcpRemove(tool, spec.server);
|
|
624
|
+
}
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
id: "perplexity",
|
|
628
|
+
name: "Perplexity",
|
|
629
|
+
version: "latest",
|
|
630
|
+
env: ["PERPLEXITY"],
|
|
631
|
+
server: "search-perplexity",
|
|
632
|
+
skills: ["ase-meta-search", "ase-meta-evaluate", "ase-arch-discover"],
|
|
633
|
+
handler: async (spec, tool, action, _envKey, envVal) => {
|
|
634
|
+
if (action === "activate")
|
|
635
|
+
await this.mcpAdd(tool, spec.server, {
|
|
636
|
+
"PERPLEXITY_API_KEY": envVal
|
|
637
|
+
}, { type: "stdio", command: ["npx", "-y", "@perplexity-ai/mcp-server"] });
|
|
638
|
+
else
|
|
639
|
+
await this.mcpRemove(tool, spec.server);
|
|
640
|
+
}
|
|
641
|
+
},
|
|
642
|
+
{
|
|
643
|
+
id: "exa",
|
|
644
|
+
name: "Exa",
|
|
645
|
+
version: "latest",
|
|
646
|
+
env: ["EXA"],
|
|
647
|
+
server: "search-exa",
|
|
648
|
+
skills: ["ase-meta-search", "ase-meta-evaluate", "ase-arch-discover"],
|
|
649
|
+
handler: async (spec, tool, action, _envKey, envVal) => {
|
|
650
|
+
if (action === "activate")
|
|
651
|
+
await this.mcpAdd(tool, spec.server, {}, { type: "http", url: `https://mcp.exa.ai/mcp?exaApiKey=${envVal}` });
|
|
652
|
+
else
|
|
653
|
+
await this.mcpRemove(tool, spec.server);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
];
|
|
243
657
|
/* parse and validate the --tool option */
|
|
244
658
|
parseTool(value) {
|
|
245
659
|
if (value !== "claude" && value !== "copilot")
|
|
@@ -306,5 +720,36 @@ export default class SetupCommand {
|
|
|
306
720
|
.action(async (opts) => {
|
|
307
721
|
process.exit(await this.doDisable(this.parseTool(opts.tool)));
|
|
308
722
|
});
|
|
723
|
+
/* register CLI sub-command "ase setup mcp" */
|
|
724
|
+
const mcpCmd = setupCmd
|
|
725
|
+
.command("mcp")
|
|
726
|
+
.description("activate or deactivate pre-defined MCP servers for a tool")
|
|
727
|
+
.action(() => {
|
|
728
|
+
mcpCmd.outputHelp();
|
|
729
|
+
process.exit(1);
|
|
730
|
+
});
|
|
731
|
+
/* register CLI sub-command "ase setup mcp list" */
|
|
732
|
+
mcpCmd
|
|
733
|
+
.command("list")
|
|
734
|
+
.description("list all available pre-defined MCP server names")
|
|
735
|
+
.action(async () => {
|
|
736
|
+
process.exit(await this.doMcpList());
|
|
737
|
+
});
|
|
738
|
+
/* register CLI sub-command "ase setup mcp activate" */
|
|
739
|
+
mcpCmd
|
|
740
|
+
.command("activate [servers]")
|
|
741
|
+
.description("activate pre-defined MCP servers (comma-separated list, or \"all\")")
|
|
742
|
+
.option("-t, --tool <tool>", "target tool (\"claude\" or \"copilot\")", toolDflt)
|
|
743
|
+
.action(async (servers, opts) => {
|
|
744
|
+
process.exit(await this.doMcp("activate", this.parseTool(opts.tool), servers ?? "all"));
|
|
745
|
+
});
|
|
746
|
+
/* register CLI sub-command "ase setup mcp deactivate" */
|
|
747
|
+
mcpCmd
|
|
748
|
+
.command("deactivate [servers]")
|
|
749
|
+
.description("deactivate pre-defined MCP servers (comma-separated list, or \"all\")")
|
|
750
|
+
.option("-t, --tool <tool>", "target tool (\"claude\" or \"copilot\")", toolDflt)
|
|
751
|
+
.action(async (servers, opts) => {
|
|
752
|
+
process.exit(await this.doMcp("deactivate", this.parseTool(opts.tool), servers ?? "all"));
|
|
753
|
+
});
|
|
309
754
|
}
|
|
310
755
|
}
|
package/dst/ase-statusline.js
CHANGED
|
@@ -191,8 +191,8 @@ export default class StatuslineCommand {
|
|
|
191
191
|
.option("-m, --margin <n>", "reduce maximum used terminal width by <n> characters on each side", parseInteger("--margin"), 2)
|
|
192
192
|
.option("--no-icons", "disable icons in placeholder rendering")
|
|
193
193
|
.option("--no-labels", "disable labels in front of bold values")
|
|
194
|
-
.argument("[lines...]", "one or more template lines with %u %p %T %s %m %e %t %P %c %C %
|
|
195
|
-
"%S %D %W %Q %H %X %b %g %G %d %M %V
|
|
194
|
+
.argument("[lines...]", "one or more template lines with %u %p %T %s %m %e %t %O %P %c %C %a %r " +
|
|
195
|
+
"%S %D %W %Q %H %X %b %g %G %d %M %V placeholders and <color>...</color> markup " +
|
|
196
196
|
"(color: black, red, green, yellow, blue, magenta, cyan, white, default) " +
|
|
197
197
|
"(default: single line \"%m %e %t\")")
|
|
198
198
|
.action(async (lines, opts) => {
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"homepage": "http://github.com/rse/ase",
|
|
7
7
|
"repository": { "url": "git+https://github.com/rse/ase.git", "type": "git" },
|
|
8
8
|
"bugs": { "url": "http://github.com/rse/ase/issues" },
|
|
9
|
-
"version": "0.0.
|
|
9
|
+
"version": "0.0.51",
|
|
10
10
|
"license": "GPL-3.0-only",
|
|
11
11
|
"author": {
|
|
12
12
|
"name": "Dr. Ralf S. Engelschall",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"commander": "14.0.3",
|
|
44
|
+
"@dotenvx/dotenvx": "1.68.1",
|
|
44
45
|
"yaml": "2.9.0",
|
|
45
46
|
"valibot": "1.4.0",
|
|
46
47
|
"execa": "9.6.1",
|
|
@@ -1,10 +1,54 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ase-meta-chat
|
|
3
|
-
description: "Query Foreign LLM for Chat"
|
|
4
|
-
|
|
3
|
+
description: "Query Foreign LLM for Chat via MCP Tool"
|
|
4
|
+
effort: high
|
|
5
|
+
tools:
|
|
6
|
+
- mcp__chat-openai-chatgpt__query
|
|
7
|
+
- mcp__chat-google-gemini__query
|
|
8
|
+
- mcp__chat-deepseek__query
|
|
9
|
+
- mcp__chat-xai-grok__query
|
|
10
|
+
- mcp__chat-zai-glm__query
|
|
11
|
+
- mcp__chat-alibaba-qwen__query
|
|
5
12
|
---
|
|
6
13
|
|
|
7
|
-
|
|
8
|
-
just perform the given *queries*,
|
|
9
|
-
and give back the *plain responses without any modifications*.
|
|
14
|
+
@../meta/ase-control.md
|
|
10
15
|
|
|
16
|
+
1. Treat `$ARGUMENTS` as a single whitespace-separated string.
|
|
17
|
+
Set <llm/> to the *first* token.
|
|
18
|
+
Set <query/> to the *second and all following* tokens.
|
|
19
|
+
You *MUST* *NOT* output anything related to this step.
|
|
20
|
+
|
|
21
|
+
2. Set <server></server> (set to empty).
|
|
22
|
+
|
|
23
|
+
<if condition="<llm/> is equal 'chatgpt'"> <server>chat-openai-chatgpt</server></if>
|
|
24
|
+
<if condition="<llm/> is equal 'gemini'"> <server>chat-google-gemini</server> </if>
|
|
25
|
+
<if condition="<llm/> is equal 'deepseek'"><server>chat-deepseek</server> </if>
|
|
26
|
+
<if condition="<llm/> is equal 'grok'"> <server>chat-xai-grok</server> </if>
|
|
27
|
+
<if condition="<llm/> is equal 'glm'"> <server>chat-zai-glm</server> </if>
|
|
28
|
+
<if condition="<llm/> is equal 'qwen'"> <server>chat-alibaba-qwen</server> </if>
|
|
29
|
+
|
|
30
|
+
<if condition="<server/> is empty">
|
|
31
|
+
You *MUST* output the following <template/> and immediately *STOP* processing
|
|
32
|
+
(do *NOT* continue with any further step and do *NOT* call any MCP tool):
|
|
33
|
+
|
|
34
|
+
<template>
|
|
35
|
+
ERROR: unknown LLM `<llm/>` (has to be one of: chatgpt, gemini, deepseek, grok, glm, qwen)
|
|
36
|
+
</template>
|
|
37
|
+
</if>
|
|
38
|
+
|
|
39
|
+
3. Check whether the MCP server <server/> is available (because perhaps
|
|
40
|
+
it is currently disabled or not configured at all).
|
|
41
|
+
|
|
42
|
+
You *MUST* *NOT* output anything related to this step, except if
|
|
43
|
+
the MCP server <server/> is not available, then just output the
|
|
44
|
+
following <template/> and immediately *STOP* processing:
|
|
45
|
+
|
|
46
|
+
<template>
|
|
47
|
+
ERROR: LLM `<llm/>` requires MCP server `<server/>`, but it is (currently) not available!
|
|
48
|
+
</template>
|
|
49
|
+
|
|
50
|
+
4. Now call the MCP tool `query(query: <query/>)` from the MCP server
|
|
51
|
+
<server/> and then return its result `text` *verbatim* and
|
|
52
|
+
*without any modifications*. Especially, do *NOT* add or remove
|
|
53
|
+
any text to the MCP server response on your own and do not
|
|
54
|
+
interpret the result in any way.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ase-meta-diagram
|
|
3
|
+
description: "Diagram Rendering"
|
|
4
|
+
tools:
|
|
5
|
+
- "mcp__plugin_ase_ase__diagram"
|
|
6
|
+
effort: high
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Your role is to render a *single* diagram, with *deterministic* and
|
|
10
|
+
*clean* output. Your objective is to produce a beautifully rendered
|
|
11
|
+
diagram, derived from the *Mermaid* diagram specification passed in
|
|
12
|
+
`$ARGUMENTS`, which is rendered with the `diagram` tool of the `ase`
|
|
13
|
+
MCP service. The rendered diagram is returned to the caller, who
|
|
14
|
+
reproduces it directly in the user-visible response text.
|
|
15
|
+
|
|
16
|
+
Rules
|
|
17
|
+
-----
|
|
18
|
+
|
|
19
|
+
- INPUT:
|
|
20
|
+
The `$ARGUMENTS` *MUST* be treated as a *Mermaid* diagram
|
|
21
|
+
specification!
|
|
22
|
+
|
|
23
|
+
The renderer supports the following Mermaid diagram types only:
|
|
24
|
+
|
|
25
|
+
- *structure / layout / components / dependencies* → `flowchart`
|
|
26
|
+
- *control flow / branching / concurrency* → `flowchart`
|
|
27
|
+
- *state machine / states / transitions* → `stateDiagram-v2`
|
|
28
|
+
- *data flow / actors / messages / protocols* → `sequenceDiagram`
|
|
29
|
+
- *data structure / classes / methods* → `classDiagram`
|
|
30
|
+
- *data model / entities / relationships* → `erDiagram`
|
|
31
|
+
- *metrics / distributions / time series* → `xychart-beta`
|
|
32
|
+
|
|
33
|
+
Other Mermaid diagram types are *not* supported by the renderer.
|
|
34
|
+
|
|
35
|
+
- RENDER:
|
|
36
|
+
You *MUST* always use the `diagram` tool from the `ase` MCP service
|
|
37
|
+
to render the diagram!
|
|
38
|
+
|
|
39
|
+
Pass the Mermaid diagram specification from `$ARGUMENTS` in the
|
|
40
|
+
`diagram` field, and pass a `colorMode` of `none` to always get
|
|
41
|
+
monochrome renderings. You *MUST* *NEVER* hand-draw diagrams under
|
|
42
|
+
any circumstances! Box-drawing characters (`┌`, `│`, `└`, `┐`,
|
|
43
|
+
`┘`, `─`, `┼`, `├`, `┤`, `┬`, `┴`, `╭`, `╰`), ASCII surrogates
|
|
44
|
+
(`+`, `-`, `|`), or any other attempt to draw a framed shape
|
|
45
|
+
token-by-token are *forbidden* as your own output.
|
|
46
|
+
|
|
47
|
+
- OUTPUT:
|
|
48
|
+
You *MUST* return *exclusively* the `text` output of the `diagram`
|
|
49
|
+
tool, reproduced *verbatim* into a single Markdown-fenced code block
|
|
50
|
+
(triple backticks). Do *not* return any other output, especially no
|
|
51
|
+
prose, no preamble, no summary, and no Mermaid specification.
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
<text-output-of-diagram-tool/>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The caller copies this returned block directly into the user-visible
|
|
58
|
+
response text. Returning anything other than the fenced rendered
|
|
59
|
+
block is a defect: the diagram is then effectively invisible or
|
|
60
|
+
polluted with extraneous text.
|