@rse/ase 0.9.66 → 0.9.68
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-setup.js
CHANGED
|
@@ -240,6 +240,87 @@ export default class SetupCommand {
|
|
|
240
240
|
}
|
|
241
241
|
return 0;
|
|
242
242
|
}
|
|
243
|
+
/* handler for "ase setup status" */
|
|
244
|
+
async doStatus(tool) {
|
|
245
|
+
const spec = toolSpecs[tool];
|
|
246
|
+
await this.ensureTool(spec.cli);
|
|
247
|
+
this.log.write("info", `setup: status: probing ASE registrations for ${spec.label}`);
|
|
248
|
+
const table = new Table({
|
|
249
|
+
head: ["KIND", "ID", "SCOPE", "STATUS"],
|
|
250
|
+
colWidths: [13, 34, 10, 16],
|
|
251
|
+
wordWrap: true,
|
|
252
|
+
chars: { "mid": "", "left-mid": "", "mid-mid": "", "right-mid": "" },
|
|
253
|
+
style: { head: ["blue"] }
|
|
254
|
+
});
|
|
255
|
+
/* report the ASE plugin registrations, with an absent plugin
|
|
256
|
+
still rendered as an explicit row instead of being omitted */
|
|
257
|
+
const plugins = await this.pluginStatus(tool);
|
|
258
|
+
if (plugins.length === 0)
|
|
259
|
+
table.push(["PLUGIN", chalk.bold("ase@ase"), "(n/a)", "not installed"]);
|
|
260
|
+
else
|
|
261
|
+
for (const plugin of plugins)
|
|
262
|
+
table.push(["PLUGIN", chalk.bold("ase@ase"), plugin.scope, plugin.status]);
|
|
263
|
+
/* report the registered MCP servers, probed concurrently and
|
|
264
|
+
silently skipping every server which is not registered at all */
|
|
265
|
+
const scopes = await Promise.all(this.mcpServers.map((handle) => this.mcpScope(tool, handle.server)));
|
|
266
|
+
for (let i = 0; i < this.mcpServers.length; i++) {
|
|
267
|
+
const scope = scopes[i];
|
|
268
|
+
if (scope === undefined)
|
|
269
|
+
continue;
|
|
270
|
+
table.push(["MCP", chalk.bold(this.mcpServers[i].server), scope, "registered"]);
|
|
271
|
+
}
|
|
272
|
+
/* report the ASE statusline registrations */
|
|
273
|
+
for (const statusline of await this.statuslineStatus(tool))
|
|
274
|
+
table.push(["STATUSLINE", chalk.bold(statusline.file), statusline.scope, statusline.status]);
|
|
275
|
+
process.stdout.write(`${table.toString()}\n`);
|
|
276
|
+
return 0;
|
|
277
|
+
}
|
|
278
|
+
/* probe the ASE plugin registrations by scraping the human-readable
|
|
279
|
+
output of "<cli> plugin list", whose format differs per tool */
|
|
280
|
+
async pluginStatus(tool) {
|
|
281
|
+
const result = await execa(toolSpecs[tool].cli, ["plugin", "list"], { stdio: "pipe", reject: false });
|
|
282
|
+
const lines = (result.stdout ?? "").split(/\r?\n/);
|
|
283
|
+
const entries = [];
|
|
284
|
+
if (tool === "claude") {
|
|
285
|
+
/* the Anthropic Claude Code CLI renders one indented block per
|
|
286
|
+
plugin, carrying an explicit "Scope:" and "Status:" field */
|
|
287
|
+
for (let i = 0; i < lines.length; i++) {
|
|
288
|
+
if (!/(^|\s)ase@ase\s*$/.test(lines[i]))
|
|
289
|
+
continue;
|
|
290
|
+
let scope = "(unknown)";
|
|
291
|
+
let status = "(unknown)";
|
|
292
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
293
|
+
const m = lines[j].match(/^\s+(\w+):\s*(.+?)\s*$/);
|
|
294
|
+
if (m === null)
|
|
295
|
+
break;
|
|
296
|
+
/* strip the leading "✔"/"✘" glyph of the status value */
|
|
297
|
+
const value = m[2].replace(/^[^\p{L}\p{N}]+/u, "");
|
|
298
|
+
if (m[1] === "Scope")
|
|
299
|
+
scope = value;
|
|
300
|
+
else if (m[1] === "Status")
|
|
301
|
+
status = value;
|
|
302
|
+
}
|
|
303
|
+
entries.push({ scope, status });
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
else if (tool === "copilot") {
|
|
307
|
+
/* the GitHub Copilot CLI renders a flat bullet list which
|
|
308
|
+
carries neither a scope nor an enabled/disabled state */
|
|
309
|
+
for (const line of lines)
|
|
310
|
+
if (/(^|\s)ase@ase(\s|\(|$)/.test(line))
|
|
311
|
+
entries.push({ scope: "(n/a)", status: "installed" });
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
/* the OpenAI Codex CLI renders a per-marketplace table whose
|
|
315
|
+
second column carries the combined installation status */
|
|
316
|
+
for (const line of lines) {
|
|
317
|
+
const m = line.match(/^ase@ase\s+(not installed|installed(?:,\s*\w+)?)/);
|
|
318
|
+
if (m !== null && m[1] !== "not installed")
|
|
319
|
+
entries.push({ scope: "(n/a)", status: m[1] });
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return entries;
|
|
323
|
+
}
|
|
243
324
|
/* handler for "ase setup mcp list" */
|
|
244
325
|
async doMcpList() {
|
|
245
326
|
const table = new Table({
|
|
@@ -335,6 +416,19 @@ export default class SetupCommand {
|
|
|
335
416
|
const result = await execa(toolSpecs[tool].cli, ["mcp", "get", name], { stdio: "ignore", reject: false });
|
|
336
417
|
return result.exitCode === 0;
|
|
337
418
|
}
|
|
419
|
+
/* probe the registration scope of an MCP server by scraping the
|
|
420
|
+
human-readable output of "<cli> mcp get <name>"; returns "undefined"
|
|
421
|
+
for a server which is not registered with the tool at all */
|
|
422
|
+
async mcpScope(tool, name) {
|
|
423
|
+
const result = await execa(toolSpecs[tool].cli, ["mcp", "get", name], { stdio: "pipe", reject: false });
|
|
424
|
+
if (result.exitCode !== 0)
|
|
425
|
+
return undefined;
|
|
426
|
+
/* the Anthropic Claude Code CLI reports "Scope: <scope> config (...)",
|
|
427
|
+
the GitHub Copilot CLI reports "Source: <scope>", and the
|
|
428
|
+
OpenAI Codex CLI reports no scope information at all */
|
|
429
|
+
const m = (result.stdout ?? "").match(/^\s*(?:Scope|Source):\s*(\S+)/m);
|
|
430
|
+
return m !== null ? m[1].toLowerCase() : "(n/a)";
|
|
431
|
+
}
|
|
338
432
|
/* register an MCP server with the tool, supporting both the "stdio"
|
|
339
433
|
(a local subprocess command) and "http" (a remote URL, optionally
|
|
340
434
|
with HTTP headers) transports; the per-tool command line differs
|
|
@@ -785,6 +879,36 @@ export default class SetupCommand {
|
|
|
785
879
|
this.log.write("info", `setup: statusline: deactivate: removing ASE "statusLine" from ${file}`);
|
|
786
880
|
return 0;
|
|
787
881
|
}
|
|
882
|
+
/* probe the ASE statusline registrations by inspecting the tool
|
|
883
|
+
settings files of all applicable installation scopes */
|
|
884
|
+
async statuslineStatus(tool) {
|
|
885
|
+
/* the OpenAI Codex CLI has no scriptable statusline mechanism at all */
|
|
886
|
+
if (tool === "codex")
|
|
887
|
+
return [];
|
|
888
|
+
const home = os.homedir();
|
|
889
|
+
const scopes = tool === "claude" ? ["user", "project", "local"] : ["user"];
|
|
890
|
+
const entries = [];
|
|
891
|
+
for (const scope of scopes) {
|
|
892
|
+
const file = this.statuslineSettingsFile(tool, scope);
|
|
893
|
+
let root;
|
|
894
|
+
try {
|
|
895
|
+
root = await this.statuslineReadAst(file);
|
|
896
|
+
}
|
|
897
|
+
catch {
|
|
898
|
+
/* an unparsable settings file carries no usable state */
|
|
899
|
+
continue;
|
|
900
|
+
}
|
|
901
|
+
const member = this.statuslineFindMember(root);
|
|
902
|
+
if (member === undefined)
|
|
903
|
+
continue;
|
|
904
|
+
entries.push({
|
|
905
|
+
file: file.startsWith(home + path.sep) ? `~${file.slice(home.length)}` : file,
|
|
906
|
+
scope: tool === "claude" ? scope : "(n/a)",
|
|
907
|
+
status: this.statuslineIsOwned(member) ? "activated" : "foreign"
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
return entries;
|
|
911
|
+
}
|
|
788
912
|
/* parse and validate the --tool option */
|
|
789
913
|
parseTool(value) {
|
|
790
914
|
if (value !== "claude" && value !== "copilot" && value !== "codex")
|
|
@@ -870,6 +994,14 @@ export default class SetupCommand {
|
|
|
870
994
|
.action(async (opts) => {
|
|
871
995
|
process.exit(await this.doToggle(this.parseTool(opts.tool), this.parseScope(opts.scope), "disable"));
|
|
872
996
|
});
|
|
997
|
+
/* register CLI sub-command "ase setup status" */
|
|
998
|
+
setupCmd
|
|
999
|
+
.command("status")
|
|
1000
|
+
.description("report the ASE plugin, MCP server, and statusline registrations for a tool")
|
|
1001
|
+
.option("-t, --tool <tool>", "target tool (\"claude\", \"copilot\", or \"codex\")", toolDflt)
|
|
1002
|
+
.action(async (opts) => {
|
|
1003
|
+
process.exit(await this.doStatus(this.parseTool(opts.tool)));
|
|
1004
|
+
});
|
|
873
1005
|
/* register CLI sub-command "ase setup mcp" */
|
|
874
1006
|
const mcpCmd = setupCmd
|
|
875
1007
|
.command("mcp")
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"homepage": "https://ase.tools",
|
|
7
7
|
"repository": { "url": "git+https://github.com/rse/ase.git", "type": "git" },
|
|
8
8
|
"bugs": { "url": "https://github.com/rse/ase/issues" },
|
|
9
|
-
"version": "0.9.
|
|
9
|
+
"version": "0.9.68",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"author": {
|
|
12
12
|
"name": "Dr. Ralf S. Engelschall",
|
package/plugin/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"homepage": "https://ase.tools",
|
|
7
7
|
"repository": { "url": "git+https://github.com/rse/ase.git", "type": "git" },
|
|
8
8
|
"bugs": { "url": "https://github.com/rse/ase/issues" },
|
|
9
|
-
"version": "0.9.
|
|
9
|
+
"version": "0.9.68",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"author": {
|
|
12
12
|
"name": "Dr. Ralf S. Engelschall",
|