@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.66",
9
+ "version": "0.9.68",
10
10
  "license": "Apache-2.0",
11
11
  "author": {
12
12
  "name": "Dr. Ralf S. Engelschall",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ase",
3
- "version": "0.9.66",
3
+ "version": "0.9.68",
4
4
  "description": "Agentic Software Engineering (ASE)",
5
5
  "keywords": [ "agentic", "software", "engineering" ],
6
6
  "homepage": "https://ase.tools",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ase",
3
- "version": "0.9.66",
3
+ "version": "0.9.68",
4
4
  "description": "Agentic Software Engineering (ASE)",
5
5
  "keywords": [ "agentic", "software", "engineering" ],
6
6
  "homepage": "https://ase.tools",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ase",
3
- "version": "0.9.66",
3
+ "version": "0.9.68",
4
4
  "description": "Agentic Software Engineering (ASE)",
5
5
  "keywords": [ "agentic", "software", "engineering" ],
6
6
  "homepage": "https://ase.tools",
@@ -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.66",
9
+ "version": "0.9.68",
10
10
  "license": "Apache-2.0",
11
11
  "author": {
12
12
  "name": "Dr. Ralf S. Engelschall",