@rse/ase 0.9.32 → 0.9.34
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 +18 -18
- package/dst/ase-kv.js +1 -1
- package/dst/ase-setup.js +77 -47
- package/dst/ase-statusline.js +4 -4
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/.github/plugin/plugin.json +1 -1
- package/plugin/meta/ase-constitution.md +2 -2
- package/plugin/meta/ase-skill.md +1 -1
- package/plugin/package.json +1 -1
package/dst/ase-hook.js
CHANGED
|
@@ -25,8 +25,8 @@ const addonMcpServers = [
|
|
|
25
25
|
"search-perplexity"
|
|
26
26
|
];
|
|
27
27
|
/* build a per-tool regular expression matching the tool names exposed
|
|
28
|
-
by the addon MCP servers: Claude Code prefixes them as
|
|
29
|
-
"mcp__<server>__<tool>", whereas Copilot CLI prefixes them as
|
|
28
|
+
by the addon MCP servers: Anthropic Claude Code CLI prefixes them as
|
|
29
|
+
"mcp__<server>__<tool>", whereas GitHub Copilot CLI prefixes them as
|
|
30
30
|
"<server>-<tool>" */
|
|
31
31
|
const addonMcpToolNamePattern = (prefix, suffix) => {
|
|
32
32
|
const alternatives = addonMcpServers
|
|
@@ -202,8 +202,8 @@ export default class HookCommand {
|
|
|
202
202
|
if (process.env.ASE_SETUP_DEV !== undefined)
|
|
203
203
|
versionHints.push("**NOTICE:** *development* setup");
|
|
204
204
|
const versionHint = versionHints.length > 0 ? "(" + versionHints.join(", ") + ")" : "";
|
|
205
|
-
/* read session information (Claude Code uses snake_case fields,
|
|
206
|
-
Copilot CLI uses camelCase fields) */
|
|
205
|
+
/* read session information (Anthropic Claude Code CLI uses snake_case fields,
|
|
206
|
+
GitHub Copilot CLI uses camelCase fields) */
|
|
207
207
|
const stdin = await this.readStdin();
|
|
208
208
|
const input = this.parseJSON(stdin, v.object({
|
|
209
209
|
session_id: v.optional(v.string()),
|
|
@@ -252,8 +252,8 @@ export default class HookCommand {
|
|
|
252
252
|
persona = val;
|
|
253
253
|
/* determine headless mode */
|
|
254
254
|
const headless = (process.env.ASE_HEADLESS ?? "false") === "true" ? "true" : "false";
|
|
255
|
-
/* provide ASE information to Claude Code shell commands
|
|
256
|
-
(Claude Code only -- Copilot CLI has no equivalent mechanism) */
|
|
255
|
+
/* provide ASE information to Anthropic Claude Code CLI shell commands
|
|
256
|
+
(Anthropic Claude Code CLI only -- GitHub Copilot CLI has no equivalent mechanism) */
|
|
257
257
|
const envFile = tool === "claude" ? (process.env.CLAUDE_ENV_FILE ?? "") : "";
|
|
258
258
|
if (envFile !== "") {
|
|
259
259
|
const script = `export ASE_VERSION=${quote([versionCurrentPlugin])}\n` +
|
|
@@ -284,15 +284,15 @@ export default class HookCommand {
|
|
|
284
284
|
/* build the deterministic ASE banner (rendered directly by the
|
|
285
285
|
agent harness, independent of any model decision, so it is
|
|
286
286
|
guaranteed to appear once in every non-headless session;
|
|
287
|
-
Claude Code and OpenAI Codex CLI surface a top-level
|
|
288
|
-
"systemMessage" field for this -- Copilot CLI has no equivalent) */
|
|
287
|
+
Anthropic Claude Code CLI and OpenAI Codex CLI surface a top-level
|
|
288
|
+
"systemMessage" field for this -- GitHub Copilot CLI has no equivalent) */
|
|
289
289
|
const banner = `\n⧉ ASE: ⎈ version: ${versionCurrentPlugin}${versionHint !== "" ? " " + versionHint.replaceAll(/\*/g, "") : ""}` +
|
|
290
290
|
`\n⧉ ASE: ※ user: ${userId}, ⚑ project: ${projectId}` +
|
|
291
291
|
`\n⧉ ASE: ◉ task: ${taskId}, ⏻ session: ${sessionId}` +
|
|
292
292
|
`\n⧉ ASE: ☯ persona: ${persona}`;
|
|
293
293
|
/* inject markdown into session context.
|
|
294
|
-
Claude Code and OpenAI Codex CLI expect the context nested in
|
|
295
|
-
"hookSpecificOutput"; Copilot CLI expects a flat top-level
|
|
294
|
+
Anthropic Claude Code CLI and OpenAI Codex CLI expect the context nested in
|
|
295
|
+
"hookSpecificOutput"; GitHub Copilot CLI expects a flat top-level
|
|
296
296
|
"additionalContext" field. */
|
|
297
297
|
const payload = tool !== "copilot" ? {
|
|
298
298
|
"hookSpecificOutput": {
|
|
@@ -313,8 +313,8 @@ export default class HookCommand {
|
|
|
313
313
|
/* publish the agent activity marker to tmux as a per-pane user
|
|
314
314
|
option, so tmux can render the live state via
|
|
315
315
|
#{@ase_agent_status} (refreshed on tmux's own interval,
|
|
316
|
-
independent of Claude Code's statusline repaint cadence).
|
|
317
|
-
Notice: the Claude Code statusline is not usable for this case
|
|
316
|
+
independent of Anthropic Claude Code CLI's statusline repaint cadence).
|
|
317
|
+
Notice: the Anthropic Claude Code CLI statusline is not usable for this case
|
|
318
318
|
at all, as it is not repainted during agent processing! */
|
|
319
319
|
writeAgentStatus(status) {
|
|
320
320
|
const icon = status === "busy" ? "▶" : "⏸";
|
|
@@ -354,8 +354,8 @@ export default class HookCommand {
|
|
|
354
354
|
}
|
|
355
355
|
return 0;
|
|
356
356
|
}
|
|
357
|
-
/* pick the session id from a parsed payload (Claude Code uses
|
|
358
|
-
snake_case fields, Copilot CLI uses camelCase fields) */
|
|
357
|
+
/* pick the session id from a parsed payload (Anthropic Claude Code CLI uses
|
|
358
|
+
snake_case fields, GitHub Copilot CLI uses camelCase fields) */
|
|
359
359
|
pickSessionId(input) {
|
|
360
360
|
return input.session_id ?? input.sessionId ?? "";
|
|
361
361
|
}
|
|
@@ -444,7 +444,7 @@ export default class HookCommand {
|
|
|
444
444
|
return { approve: false, reason: "" };
|
|
445
445
|
}
|
|
446
446
|
/* handler for "ase hook pre-tool-use" (all tools).
|
|
447
|
-
For Claude Code and Copilot CLI this is where ASE tool
|
|
447
|
+
For Anthropic Claude Code CLI and GitHub Copilot CLI this is where ASE tool
|
|
448
448
|
invocations are auto-approved (via "permissionDecision: allow").
|
|
449
449
|
OpenAI Codex CLI rejects that mechanism in "PreToolUse", so for
|
|
450
450
|
Codex this handler stays silent and approval is granted in the
|
|
@@ -465,8 +465,8 @@ export default class HookCommand {
|
|
|
465
465
|
/* determine whether to auto-approve the tool invocation */
|
|
466
466
|
const { approve, reason } = this.decideApproval(tool, spec, input);
|
|
467
467
|
/* emit permission decision (or stay silent to defer to default flow).
|
|
468
|
-
Claude Code expects the decision nested in "hookSpecificOutput";
|
|
469
|
-
Copilot CLI expects flat top-level fields. */
|
|
468
|
+
Anthropic Claude Code CLI expects the decision nested in "hookSpecificOutput";
|
|
469
|
+
GitHub Copilot CLI expects flat top-level fields. */
|
|
470
470
|
if (approve) {
|
|
471
471
|
const payload = spec.preToolUseWrapped ? {
|
|
472
472
|
"hookSpecificOutput": {
|
|
@@ -524,7 +524,7 @@ export default class HookCommand {
|
|
|
524
524
|
/* register CLI top-level command "ase hook" */
|
|
525
525
|
const hookCmd = program
|
|
526
526
|
.command("hook")
|
|
527
|
-
.description("Claude Code and Copilot CLI hook entry points")
|
|
527
|
+
.description("Anthropic Claude Code CLI and GitHub Copilot CLI hook entry points")
|
|
528
528
|
.action(() => {
|
|
529
529
|
hookCmd.outputHelp();
|
|
530
530
|
process.exit(1);
|
package/dst/ase-kv.js
CHANGED
|
@@ -7,7 +7,7 @@ import { z } from "zod";
|
|
|
7
7
|
/* reusable functionality: in-memory key/value store living inside the
|
|
8
8
|
"ase service" process; per-project (one service per project) and
|
|
9
9
|
not persisted; intended for sharing information between skills
|
|
10
|
-
across multiple Claude Code instances connected to the same service */
|
|
10
|
+
across multiple Anthropic Claude Code CLI instances connected to the same service */
|
|
11
11
|
export class KV {
|
|
12
12
|
/* the actual in-memory store */
|
|
13
13
|
static store = new Map();
|
package/dst/ase-setup.js
CHANGED
|
@@ -13,8 +13,8 @@ import Table from "cli-table3";
|
|
|
13
13
|
import chalk from "chalk";
|
|
14
14
|
import Version from "./ase-version.js";
|
|
15
15
|
const toolSpecs = {
|
|
16
|
-
"claude": { cli: "claude", label: "Claude Code", pInstall: "install", pRemove: "uninstall", pUpdate: "update" },
|
|
17
|
-
"copilot": { cli: "copilot", label: "Copilot CLI", pInstall: "install", pRemove: "uninstall", pUpdate: "update" },
|
|
16
|
+
"claude": { cli: "claude", label: "Anthropic Claude Code CLI", pInstall: "install", pRemove: "uninstall", pUpdate: "update" },
|
|
17
|
+
"copilot": { cli: "copilot", label: "GitHub Copilot CLI", pInstall: "install", pRemove: "uninstall", pUpdate: "update" },
|
|
18
18
|
"codex": { cli: "codex", label: "OpenAI Codex CLI", pInstall: "add", pRemove: "remove", pUpdate: "upgrade" }
|
|
19
19
|
};
|
|
20
20
|
/* CLI command "ase setup" */
|
|
@@ -153,7 +153,8 @@ export default class SetupCommand {
|
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
/* handler for "ase setup install" (both tools) */
|
|
156
|
-
async doInstall(tool, dev) {
|
|
156
|
+
async doInstall(tool, dev, scope) {
|
|
157
|
+
this.requireClaudeScope(tool, scope);
|
|
157
158
|
const spec = toolSpecs[tool];
|
|
158
159
|
await this.ensureTool("npm");
|
|
159
160
|
await this.ensureTool(spec.cli);
|
|
@@ -162,15 +163,18 @@ export default class SetupCommand {
|
|
|
162
163
|
`installing ASE ${spec.label} plugin (origin: ${dev ? "local" : "remote/bundled"})`);
|
|
163
164
|
const pkgdir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
164
165
|
const source = dev ? path.resolve(pkgdir, "..") : pkgdir;
|
|
165
|
-
|
|
166
|
-
await this.run(spec.cli, ["plugin",
|
|
166
|
+
const scopeArgs = tool === "claude" ? ["--scope", scope] : [];
|
|
167
|
+
await this.run(spec.cli, ["plugin", "marketplace", "add", source, ...scopeArgs]);
|
|
168
|
+
await this.run(spec.cli, ["plugin", spec.pInstall, "ase@ase", ...scopeArgs], { retries: 3 });
|
|
167
169
|
return 0;
|
|
168
170
|
}
|
|
169
171
|
/* handler for "ase setup update" (both tools) */
|
|
170
|
-
async doUpdate(tool, force, dev) {
|
|
172
|
+
async doUpdate(tool, force, dev, scope) {
|
|
173
|
+
this.requireClaudeScope(tool, scope);
|
|
171
174
|
const spec = toolSpecs[tool];
|
|
172
175
|
await this.ensureTool("npm");
|
|
173
176
|
await this.ensureTool(spec.cli);
|
|
177
|
+
const scopeArgs = tool === "claude" ? ["--scope", scope] : [];
|
|
174
178
|
/* best-effort stop of background service */
|
|
175
179
|
this.log.write("info", `setup: update${dev ? "[dev]" : ""}: ` +
|
|
176
180
|
"stopping potentially running ASE service");
|
|
@@ -186,8 +190,8 @@ export default class SetupCommand {
|
|
|
186
190
|
but there is no version change in the plugin manifest,
|
|
187
191
|
so just re-install the plugin to let the tool update its copy */
|
|
188
192
|
this.log.write("info", `setup: update[dev]: re-install ASE ${spec.label} plugin (origin: local)`);
|
|
189
|
-
await this.run(spec.cli, ["plugin", spec.pRemove, "ase@ase"], { ignoreError: `ASE ${spec.label} plugin not installed` });
|
|
190
|
-
await this.run(spec.cli, ["plugin", spec.pInstall, "ase@ase"], { retries: 3 });
|
|
193
|
+
await this.run(spec.cli, ["plugin", spec.pRemove, "ase@ase", ...scopeArgs], { ignoreError: `ASE ${spec.label} plugin not installed` });
|
|
194
|
+
await this.run(spec.cli, ["plugin", spec.pInstall, "ase@ase", ...scopeArgs], { retries: 3 });
|
|
191
195
|
}
|
|
192
196
|
else {
|
|
193
197
|
/* perform NPM version check */
|
|
@@ -204,49 +208,55 @@ export default class SetupCommand {
|
|
|
204
208
|
await this.run(updateCmd.cmd, updateCmd.args);
|
|
205
209
|
/* update ASE plugin (refresh the marketplace snapshot, then
|
|
206
210
|
update the plugin itself; the OpenAI Codex CLI has no
|
|
207
|
-
"plugin update" subcommand, so re-install it instead
|
|
211
|
+
"plugin update" subcommand, so re-install it instead;
|
|
212
|
+
"plugin marketplace update" has no "--scope" option at all,
|
|
213
|
+
so it never receives one) */
|
|
208
214
|
this.log.write("info", `setup: update: updating ASE ${spec.label} plugin`);
|
|
209
215
|
await this.run(spec.cli, ["plugin", "marketplace", spec.pUpdate, "ase"]);
|
|
210
216
|
if (tool !== "codex")
|
|
211
|
-
await this.run(spec.cli, ["plugin", "update", "ase@ase"]);
|
|
217
|
+
await this.run(spec.cli, ["plugin", "update", "ase@ase", ...scopeArgs]);
|
|
212
218
|
else {
|
|
213
|
-
await this.run(spec.cli, ["plugin", spec.pRemove, "ase@ase"], { ignoreError: `ASE ${spec.label} plugin not installed` });
|
|
214
|
-
await this.run(spec.cli, ["plugin", spec.pInstall, "ase@ase"], { retries: 3 });
|
|
219
|
+
await this.run(spec.cli, ["plugin", spec.pRemove, "ase@ase", ...scopeArgs], { ignoreError: `ASE ${spec.label} plugin not installed` });
|
|
220
|
+
await this.run(spec.cli, ["plugin", spec.pInstall, "ase@ase", ...scopeArgs], { retries: 3 });
|
|
215
221
|
}
|
|
216
222
|
}
|
|
217
223
|
return 0;
|
|
218
224
|
}
|
|
219
225
|
/* handler for "ase setup enable" (both tools) */
|
|
220
|
-
async doEnable(tool) {
|
|
226
|
+
async doEnable(tool, scope) {
|
|
227
|
+
this.requireClaudeScope(tool, scope);
|
|
221
228
|
const spec = toolSpecs[tool];
|
|
222
229
|
await this.ensureTool(spec.cli);
|
|
223
230
|
this.log.write("info", `setup: enable: enabling ASE ${spec.label} plugin`);
|
|
224
231
|
/* the GitHub Copilot CLI and OpenAI Codex CLI have no "plugin
|
|
225
232
|
enable" subcommand, so (re-)install the plugin instead */
|
|
226
233
|
const args = tool === "claude" ?
|
|
227
|
-
["plugin", "enable", "ase@ase"] :
|
|
234
|
+
["plugin", "enable", "ase@ase", "--scope", scope] :
|
|
228
235
|
["plugin", spec.pInstall, "ase@ase"];
|
|
229
236
|
await this.run(spec.cli, args, { retries: tool === "claude" ? 1 : 3 });
|
|
230
237
|
return 0;
|
|
231
238
|
}
|
|
232
239
|
/* handler for "ase setup disable" (both tools) */
|
|
233
|
-
async doDisable(tool) {
|
|
240
|
+
async doDisable(tool, scope) {
|
|
241
|
+
this.requireClaudeScope(tool, scope);
|
|
234
242
|
const spec = toolSpecs[tool];
|
|
235
243
|
await this.ensureTool(spec.cli);
|
|
236
244
|
this.log.write("info", `setup: disable: disabling ASE ${spec.label} plugin`);
|
|
237
245
|
/* the GitHub Copilot CLI and OpenAI Codex CLI have no "plugin
|
|
238
246
|
disable" subcommand, so uninstall the plugin instead */
|
|
239
247
|
const args = tool === "claude" ?
|
|
240
|
-
["plugin", "disable", "ase@ase"] :
|
|
248
|
+
["plugin", "disable", "ase@ase", "--scope", scope] :
|
|
241
249
|
["plugin", spec.pRemove, "ase@ase"];
|
|
242
250
|
await this.run(spec.cli, args, { retries: tool === "claude" ? 1 : 3 });
|
|
243
251
|
return 0;
|
|
244
252
|
}
|
|
245
253
|
/* handler for "ase setup uninstall" (both tools) */
|
|
246
|
-
async doUninstall(tool, dev) {
|
|
254
|
+
async doUninstall(tool, dev, scope) {
|
|
255
|
+
this.requireClaudeScope(tool, scope);
|
|
247
256
|
const spec = toolSpecs[tool];
|
|
248
257
|
await this.ensureTool("npm");
|
|
249
258
|
await this.ensureTool(spec.cli);
|
|
259
|
+
const scopeArgs = tool === "claude" ? ["--scope", scope] : [];
|
|
250
260
|
/* best-effort stop of background service */
|
|
251
261
|
this.log.write("info", `setup: uninstall${dev ? "[dev]" : ""}: ` +
|
|
252
262
|
"stopping potentially running ASE service");
|
|
@@ -254,8 +264,8 @@ export default class SetupCommand {
|
|
|
254
264
|
/* uninstall ASE plugin */
|
|
255
265
|
this.log.write("info", `setup: uninstall${dev ? "[dev]" : ""}: ` +
|
|
256
266
|
`uninstalling ASE ${spec.label} plugin (origin: ${dev ? "local" : "remote/bundled"})`);
|
|
257
|
-
await this.run(spec.cli, ["plugin", spec.pRemove, "ase@ase"], { ignoreError: `ASE ${spec.label} plugin not installed` });
|
|
258
|
-
await this.run(spec.cli, ["plugin", "marketplace", "remove", "ase"], { ignoreError: `ASE ${spec.label} plugin marketplace not registered` });
|
|
267
|
+
await this.run(spec.cli, ["plugin", spec.pRemove, "ase@ase", ...scopeArgs], { ignoreError: `ASE ${spec.label} plugin not installed` });
|
|
268
|
+
await this.run(spec.cli, ["plugin", "marketplace", "remove", "ase", ...scopeArgs], { ignoreError: `ASE ${spec.label} plugin marketplace not registered` });
|
|
259
269
|
/* uninstall ASE CLI tool (non-development only) */
|
|
260
270
|
if (!dev) {
|
|
261
271
|
this.log.write("info", "setup: uninstall: uninstalling ASE CLI tool (origin: remote)");
|
|
@@ -286,7 +296,8 @@ export default class SetupCommand {
|
|
|
286
296
|
return 0;
|
|
287
297
|
}
|
|
288
298
|
/* handler for "ase setup mcp activate|deactivate [<servers>]" */
|
|
289
|
-
async doMcp(action, tool, servers) {
|
|
299
|
+
async doMcp(action, tool, servers, scope) {
|
|
300
|
+
this.requireClaudeScope(tool, scope);
|
|
290
301
|
await this.ensureTool(toolSpecs[tool].cli);
|
|
291
302
|
/* source .env files into the environment so the per-server
|
|
292
303
|
API keys (ASE_MCP_KEY_<XXX>) can live in a .env file instead
|
|
@@ -336,7 +347,7 @@ export default class SetupCommand {
|
|
|
336
347
|
if (installed) {
|
|
337
348
|
this.log.write("info", `setup: mcp: activate: [${id}]: MCP server "${handle.server}" ` +
|
|
338
349
|
"already registered: removing stale registration first");
|
|
339
|
-
await this.mcpRemove(tool, handle.server);
|
|
350
|
+
await this.mcpRemove(tool, handle.server, scope);
|
|
340
351
|
}
|
|
341
352
|
}
|
|
342
353
|
else if (!installed) {
|
|
@@ -348,7 +359,7 @@ export default class SetupCommand {
|
|
|
348
359
|
/* call the handler */
|
|
349
360
|
this.log.write("info", `setup: mcp: ${action}: [${id}]: MCP server "${handle.server}" ` +
|
|
350
361
|
`(name: ${handle.name}${handle.version ? (", version: " + handle.version) : ""})`);
|
|
351
|
-
await handle.handler(handle, tool, action, envKey, envVal);
|
|
362
|
+
await handle.handler(handle, tool, scope, action, envKey, envVal);
|
|
352
363
|
}
|
|
353
364
|
return 0;
|
|
354
365
|
}
|
|
@@ -361,11 +372,11 @@ export default class SetupCommand {
|
|
|
361
372
|
/* register an MCP server with the tool, supporting both the "stdio"
|
|
362
373
|
(a local subprocess command) and "http" (a remote URL, optionally
|
|
363
374
|
with HTTP headers) transports; the per-tool command line differs
|
|
364
|
-
between Claude Code, GitHub Copilot CLI, and OpenAI Codex CLI */
|
|
365
|
-
async mcpAdd(tool, name, env, transport) {
|
|
375
|
+
between Anthropic Claude Code CLI, GitHub Copilot CLI, and OpenAI Codex CLI */
|
|
376
|
+
async mcpAdd(tool, name, env, transport, scope) {
|
|
366
377
|
const args = ["mcp", "add"];
|
|
367
378
|
if (tool === "claude") {
|
|
368
|
-
args.push("--scope",
|
|
379
|
+
args.push("--scope", scope);
|
|
369
380
|
args.push("--transport", transport.type);
|
|
370
381
|
if (transport.type === "stdio") {
|
|
371
382
|
for (const [key, val] of Object.entries(env))
|
|
@@ -416,10 +427,10 @@ export default class SetupCommand {
|
|
|
416
427
|
await this.run(toolSpecs[tool].cli, args);
|
|
417
428
|
}
|
|
418
429
|
/* unregister an MCP server from the tool; the per-tool command line
|
|
419
|
-
differs between Claude Code, GitHub Copilot CLI, and OpenAI Codex CLI */
|
|
420
|
-
async mcpRemove(tool, name) {
|
|
430
|
+
differs between Anthropic Claude Code CLI, GitHub Copilot CLI, and OpenAI Codex CLI */
|
|
431
|
+
async mcpRemove(tool, name, scope) {
|
|
421
432
|
const args = tool === "claude" ?
|
|
422
|
-
["mcp", "remove", "--scope",
|
|
433
|
+
["mcp", "remove", "--scope", scope, name] :
|
|
423
434
|
["mcp", "remove", name];
|
|
424
435
|
await this.run(toolSpecs[tool].cli, args, { ignoreError: `MCP server "${name}" not registered` });
|
|
425
436
|
}
|
|
@@ -427,7 +438,7 @@ export default class SetupCommand {
|
|
|
427
438
|
OPENROUTER url/api/model triples, factoring out the shared
|
|
428
439
|
mcp-to-openai stdio scaffold common to all chat-model servers */
|
|
429
440
|
chatMcpHandler(direct, router) {
|
|
430
|
-
return async (spec, tool, action, envKey, envVal) => {
|
|
441
|
+
return async (spec, tool, scope, action, envKey, envVal) => {
|
|
431
442
|
if (action === "activate")
|
|
432
443
|
await this.mcpAdd(tool, spec.server, { OPENAI_KEY: envVal }, {
|
|
433
444
|
type: "stdio", command: [
|
|
@@ -444,9 +455,9 @@ export default class SetupCommand {
|
|
|
444
455
|
"--openai-model", direct.model
|
|
445
456
|
])
|
|
446
457
|
]
|
|
447
|
-
});
|
|
458
|
+
}, scope);
|
|
448
459
|
else
|
|
449
|
-
await this.mcpRemove(tool, spec.server);
|
|
460
|
+
await this.mcpRemove(tool, spec.server, scope);
|
|
450
461
|
};
|
|
451
462
|
}
|
|
452
463
|
/* registry of pre-defined MCP servers: maps each server id onto its
|
|
@@ -513,14 +524,14 @@ export default class SetupCommand {
|
|
|
513
524
|
env: ["BRAVE"],
|
|
514
525
|
server: "search-brave",
|
|
515
526
|
skills: ["ase-meta-search", "ase-meta-evaluate", "ase-arch-discover"],
|
|
516
|
-
handler: async (spec, tool, action, _envKey, envVal) => {
|
|
527
|
+
handler: async (spec, tool, scope, action, _envKey, envVal) => {
|
|
517
528
|
if (action === "activate")
|
|
518
529
|
await this.mcpAdd(tool, spec.server, {
|
|
519
530
|
"BRAVE_API_KEY": envVal,
|
|
520
531
|
"BRAVE_MCP_ENABLED_TOOLS": "brave_web_search"
|
|
521
|
-
}, { type: "stdio", command: ["npx", "-y", "@brave/brave-search-mcp-server"] });
|
|
532
|
+
}, { type: "stdio", command: ["npx", "-y", "@brave/brave-search-mcp-server"] }, scope);
|
|
522
533
|
else
|
|
523
|
-
await this.mcpRemove(tool, spec.server);
|
|
534
|
+
await this.mcpRemove(tool, spec.server, scope);
|
|
524
535
|
}
|
|
525
536
|
},
|
|
526
537
|
{
|
|
@@ -530,13 +541,13 @@ export default class SetupCommand {
|
|
|
530
541
|
env: ["PERPLEXITY"],
|
|
531
542
|
server: "search-perplexity",
|
|
532
543
|
skills: ["ase-meta-search", "ase-meta-evaluate", "ase-arch-discover"],
|
|
533
|
-
handler: async (spec, tool, action, _envKey, envVal) => {
|
|
544
|
+
handler: async (spec, tool, scope, action, _envKey, envVal) => {
|
|
534
545
|
if (action === "activate")
|
|
535
546
|
await this.mcpAdd(tool, spec.server, {
|
|
536
547
|
"PERPLEXITY_API_KEY": envVal
|
|
537
|
-
}, { type: "stdio", command: ["npx", "-y", "@perplexity-ai/mcp-server"] });
|
|
548
|
+
}, { type: "stdio", command: ["npx", "-y", "@perplexity-ai/mcp-server"] }, scope);
|
|
538
549
|
else
|
|
539
|
-
await this.mcpRemove(tool, spec.server);
|
|
550
|
+
await this.mcpRemove(tool, spec.server, scope);
|
|
540
551
|
}
|
|
541
552
|
},
|
|
542
553
|
{
|
|
@@ -546,11 +557,11 @@ export default class SetupCommand {
|
|
|
546
557
|
env: ["EXA"],
|
|
547
558
|
server: "search-exa",
|
|
548
559
|
skills: ["ase-meta-search", "ase-meta-evaluate", "ase-arch-discover"],
|
|
549
|
-
handler: async (spec, tool, action, _envKey, envVal) => {
|
|
560
|
+
handler: async (spec, tool, scope, action, _envKey, envVal) => {
|
|
550
561
|
if (action === "activate")
|
|
551
|
-
await this.mcpAdd(tool, spec.server, {}, { type: "http", url: `https://mcp.exa.ai/mcp?exaApiKey=${envVal}` });
|
|
562
|
+
await this.mcpAdd(tool, spec.server, {}, { type: "http", url: `https://mcp.exa.ai/mcp?exaApiKey=${envVal}` }, scope);
|
|
552
563
|
else
|
|
553
|
-
await this.mcpRemove(tool, spec.server);
|
|
564
|
+
await this.mcpRemove(tool, spec.server, scope);
|
|
554
565
|
}
|
|
555
566
|
}
|
|
556
567
|
];
|
|
@@ -560,6 +571,18 @@ export default class SetupCommand {
|
|
|
560
571
|
throw new Error(`invalid --tool value: "${value}" (expected "claude", "copilot", or "codex")`);
|
|
561
572
|
return value;
|
|
562
573
|
}
|
|
574
|
+
/* parse and validate the --scope option */
|
|
575
|
+
parseScope(value) {
|
|
576
|
+
if (value !== "user" && value !== "project" && value !== "local")
|
|
577
|
+
throw new Error(`invalid --scope value: "${value}" (expected "user", "project", or "local")`);
|
|
578
|
+
return value;
|
|
579
|
+
}
|
|
580
|
+
/* reject a non-default --scope for tools which have no scope concept
|
|
581
|
+
at all (only Anthropic Claude Code CLI supports plugin/MCP scopes) */
|
|
582
|
+
requireClaudeScope(tool, scope) {
|
|
583
|
+
if (tool !== "claude" && scope !== "user")
|
|
584
|
+
throw new Error("--scope is only supported for --tool claude");
|
|
585
|
+
}
|
|
563
586
|
/* register commands */
|
|
564
587
|
register(program) {
|
|
565
588
|
/* default for --dev derived from ASE_SETUP_DEV environment variable */
|
|
@@ -581,44 +604,49 @@ export default class SetupCommand {
|
|
|
581
604
|
.command("install")
|
|
582
605
|
.description("install the ASE plugin for a tool")
|
|
583
606
|
.option("-t, --tool <tool>", "target tool (\"claude\", \"copilot\", or \"codex\")", toolDflt)
|
|
607
|
+
.option("-s, --scope <scope>", "target scope (\"user\", \"project\", or \"local\")", "user")
|
|
584
608
|
.option("-d, --dev", "use local working copy instead of remote/bundled repository", devDflt)
|
|
585
609
|
.action(async (opts) => {
|
|
586
|
-
process.exit(await this.doInstall(this.parseTool(opts.tool), opts.dev));
|
|
610
|
+
process.exit(await this.doInstall(this.parseTool(opts.tool), opts.dev, this.parseScope(opts.scope)));
|
|
587
611
|
});
|
|
588
612
|
/* register CLI sub-command "ase setup update" */
|
|
589
613
|
setupCmd
|
|
590
614
|
.command("update")
|
|
591
615
|
.description("update the ASE tool and the ASE plugin for a tool")
|
|
592
616
|
.option("-t, --tool <tool>", "target tool (\"claude\", \"copilot\", or \"codex\")", toolDflt)
|
|
617
|
+
.option("-s, --scope <scope>", "target scope (\"user\", \"project\", or \"local\")", "user")
|
|
593
618
|
.option("-f, --force", "always perform the update, even if already at latest version", false)
|
|
594
619
|
.option("-d, --dev", "use local working copy instead of remote/bundled repository", devDflt)
|
|
595
620
|
.action(async (opts) => {
|
|
596
|
-
process.exit(await this.doUpdate(this.parseTool(opts.tool), opts.force, opts.dev));
|
|
621
|
+
process.exit(await this.doUpdate(this.parseTool(opts.tool), opts.force, opts.dev, this.parseScope(opts.scope)));
|
|
597
622
|
});
|
|
598
623
|
/* register CLI sub-command "ase setup uninstall" */
|
|
599
624
|
setupCmd
|
|
600
625
|
.command("uninstall")
|
|
601
626
|
.description("uninstall the ASE plugin for a tool and the ASE tool")
|
|
602
627
|
.option("-t, --tool <tool>", "target tool (\"claude\", \"copilot\", or \"codex\")", toolDflt)
|
|
628
|
+
.option("-s, --scope <scope>", "target scope (\"user\", \"project\", or \"local\")", "user")
|
|
603
629
|
.option("-d, --dev", "use local working copy instead of remote/bundled repository", devDflt)
|
|
604
630
|
.action(async (opts) => {
|
|
605
|
-
process.exit(await this.doUninstall(this.parseTool(opts.tool), opts.dev));
|
|
631
|
+
process.exit(await this.doUninstall(this.parseTool(opts.tool), opts.dev, this.parseScope(opts.scope)));
|
|
606
632
|
});
|
|
607
633
|
/* register CLI sub-command "ase setup enable" */
|
|
608
634
|
setupCmd
|
|
609
635
|
.command("enable")
|
|
610
636
|
.description("enable the ASE plugin for a tool")
|
|
611
637
|
.option("-t, --tool <tool>", "target tool (\"claude\", \"copilot\", or \"codex\")", toolDflt)
|
|
638
|
+
.option("-s, --scope <scope>", "target scope (\"user\", \"project\", or \"local\")", "user")
|
|
612
639
|
.action(async (opts) => {
|
|
613
|
-
process.exit(await this.doEnable(this.parseTool(opts.tool)));
|
|
640
|
+
process.exit(await this.doEnable(this.parseTool(opts.tool), this.parseScope(opts.scope)));
|
|
614
641
|
});
|
|
615
642
|
/* register CLI sub-command "ase setup disable" */
|
|
616
643
|
setupCmd
|
|
617
644
|
.command("disable")
|
|
618
645
|
.description("disable the ASE plugin for a tool")
|
|
619
646
|
.option("-t, --tool <tool>", "target tool (\"claude\", \"copilot\", or \"codex\")", toolDflt)
|
|
647
|
+
.option("-s, --scope <scope>", "target scope (\"user\", \"project\", or \"local\")", "user")
|
|
620
648
|
.action(async (opts) => {
|
|
621
|
-
process.exit(await this.doDisable(this.parseTool(opts.tool)));
|
|
649
|
+
process.exit(await this.doDisable(this.parseTool(opts.tool), this.parseScope(opts.scope)));
|
|
622
650
|
});
|
|
623
651
|
/* register CLI sub-command "ase setup mcp" */
|
|
624
652
|
const mcpCmd = setupCmd
|
|
@@ -640,16 +668,18 @@ export default class SetupCommand {
|
|
|
640
668
|
.command("activate [servers]")
|
|
641
669
|
.description("activate pre-defined MCP servers (comma-separated list, or \"all\")")
|
|
642
670
|
.option("-t, --tool <tool>", "target tool (\"claude\", \"copilot\", or \"codex\")", toolDflt)
|
|
671
|
+
.option("-s, --scope <scope>", "target scope (\"user\", \"project\", or \"local\")", "user")
|
|
643
672
|
.action(async (servers, opts) => {
|
|
644
|
-
process.exit(await this.doMcp("activate", this.parseTool(opts.tool), servers ?? "all"));
|
|
673
|
+
process.exit(await this.doMcp("activate", this.parseTool(opts.tool), servers ?? "all", this.parseScope(opts.scope)));
|
|
645
674
|
});
|
|
646
675
|
/* register CLI sub-command "ase setup mcp deactivate" */
|
|
647
676
|
mcpCmd
|
|
648
677
|
.command("deactivate [servers]")
|
|
649
678
|
.description("deactivate pre-defined MCP servers (comma-separated list, or \"all\")")
|
|
650
679
|
.option("-t, --tool <tool>", "target tool (\"claude\", \"copilot\", or \"codex\")", toolDflt)
|
|
680
|
+
.option("-s, --scope <scope>", "target scope (\"user\", \"project\", or \"local\")", "user")
|
|
651
681
|
.action(async (servers, opts) => {
|
|
652
|
-
process.exit(await this.doMcp("deactivate", this.parseTool(opts.tool), servers ?? "all"));
|
|
682
|
+
process.exit(await this.doMcp("deactivate", this.parseTool(opts.tool), servers ?? "all", this.parseScope(opts.scope)));
|
|
653
683
|
});
|
|
654
684
|
}
|
|
655
685
|
}
|
package/dst/ase-statusline.js
CHANGED
|
@@ -12,7 +12,7 @@ import { execaSync } from "execa";
|
|
|
12
12
|
import { Chalk } from "chalk";
|
|
13
13
|
import { Config, configSchema, parseScope } from "./ase-config.js";
|
|
14
14
|
import pkg from "../package.json" with { type: "json" };
|
|
15
|
-
/* forced-color chalk instance: stdout is a pipe under Claude Code,
|
|
15
|
+
/* forced-color chalk instance: stdout is a pipe under Anthropic Claude Code CLI,
|
|
16
16
|
so chalk auto-detection would yield level 0; force level 1 to keep
|
|
17
17
|
emitting ANSI sequences as the original implementation did */
|
|
18
18
|
const c = new Chalk({ level: 1 });
|
|
@@ -34,7 +34,7 @@ const readStdin = async () => {
|
|
|
34
34
|
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
35
35
|
return Buffer.concat(chunks).toString("utf8");
|
|
36
36
|
};
|
|
37
|
-
/* detect terminal column width via /dev/tty (stdout is a pipe under Claude Code) */
|
|
37
|
+
/* detect terminal column width via /dev/tty (stdout is a pipe under Anthropic Claude Code CLI) */
|
|
38
38
|
const detectTermWidth = () => {
|
|
39
39
|
let width = 0;
|
|
40
40
|
let tty = null;
|
|
@@ -195,7 +195,7 @@ export default class StatuslineCommand {
|
|
|
195
195
|
const toolDflt = envTool !== "" ? this.parseTool(envTool) : "claude";
|
|
196
196
|
program
|
|
197
197
|
.command("statusline")
|
|
198
|
-
.description("Render Claude Code or GitHub Copilot CLI statusline from stdin JSON")
|
|
198
|
+
.description("Render Anthropic Claude Code CLI or GitHub Copilot CLI statusline from stdin JSON")
|
|
199
199
|
.option("-t, --tool <tool>", "target tool (\"claude\" or \"copilot\"; \"codex\" is unsupported)", toolDflt)
|
|
200
200
|
.option("-w, --width <n>", "force terminal width to <n> characters (0 = auto-detect via /dev/tty)", parseInteger("--width"), 0)
|
|
201
201
|
.option("-m, --margin <n>", "reduce maximum used terminal width by <n> characters on each side", parseInteger("--margin"), 2)
|
|
@@ -225,7 +225,7 @@ export default class StatuslineCommand {
|
|
|
225
225
|
process.exit(1);
|
|
226
226
|
}
|
|
227
227
|
/* normalize Copilot CLI's top-level "cwd" into the
|
|
228
|
-
"workspace.current_dir" structure shared with Claude Code */
|
|
228
|
+
"workspace.current_dir" structure shared with Anthropic Claude Code CLI */
|
|
229
229
|
if (tool === "copilot"
|
|
230
230
|
&& (data.workspace?.current_dir === undefined || data.workspace.current_dir === "")
|
|
231
231
|
&& typeof data.cwd === "string" && data.cwd !== "") {
|
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.9.
|
|
9
|
+
"version": "0.9.34",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"author": {
|
|
12
12
|
"name": "Dr. Ralf S. Engelschall",
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
ASE Constitution
|
|
3
3
|
================
|
|
4
4
|
|
|
5
|
-
You are
|
|
6
|
-
You have the **Agentic Software Engineering (ASE)**
|
|
5
|
+
You are an expert-level AI coding assistant.
|
|
6
|
+
You have the **Agentic Software Engineering (ASE)** companion toolkit enabled,
|
|
7
7
|
which boosts you to an expert-level Software Engineering AI agent.
|
|
8
8
|
|
|
9
9
|
If ((<ase-agent-tool/> is equal `codex`) *AND* (<ase-headless/> is empty
|
package/plugin/meta/ase-skill.md
CHANGED
|
@@ -28,7 +28,7 @@ Skill Output
|
|
|
28
28
|
- *IMPORTANT*: For *Final-Message-Only Display* ("focus mode"):
|
|
29
29
|
some agent harnesses show the user only the *final* text message of
|
|
30
30
|
a turn and *hide* all text emitted *between* tool calls. If your
|
|
31
|
-
harness instructions indicate such a mode (e.g. *Claude Code* with
|
|
31
|
+
harness instructions indicate such a mode (e.g. *Anthropic Claude Code CLI* with
|
|
32
32
|
"focus mode" enabled), you *MUST* repeat *all* <template/> outputs
|
|
33
33
|
emitted since the last shown final text message -- *verbatim*, in
|
|
34
34
|
their *original order*, and each only *once* -- at the *top* of the
|
package/plugin/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.9.
|
|
9
|
+
"version": "0.9.34",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"author": {
|
|
12
12
|
"name": "Dr. Ralf S. Engelschall",
|