@rse/ase 0.9.11 → 0.9.13
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-compat.js +68 -0
- package/dst/ase-diagram.js +5 -2
- package/dst/ase-getopt.js +2 -1
- package/dst/ase-hook.js +158 -67
- package/dst/ase-mcp.js +2 -0
- package/dst/ase-persona.js +4 -1
- package/dst/ase-service.js +2 -0
- package/dst/ase-setup.js +52 -23
- package/dst/ase-skills.js +13 -3
- package/dst/ase-statusline.js +9 -5
- package/dst/ase.js +3 -1
- package/package.json +2 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/mcp.json +8 -0
- package/plugin/.codex-plugin/plugin.json +17 -0
- package/plugin/.github/plugin/plugin.json +1 -1
- package/plugin/hooks/hooks-codex.json +67 -0
- package/plugin/meta/ase-dialog.md +21 -1
- package/plugin/meta/ase-format-arch.md +26 -9
- package/plugin/meta/ase-format-meta.md +7 -3
- package/plugin/meta/ase-format-spec.md +11 -6
- package/plugin/meta/ase-getopt.md +6 -2
- package/plugin/meta/ase-skill.md +43 -19
- package/plugin/package.json +1 -1
- package/plugin/skills/ase-arch-analyze/SKILL.md +17 -10
- package/plugin/skills/ase-code-craft/SKILL.md +17 -5
- package/plugin/skills/ase-code-explain/SKILL.md +0 -1
- package/plugin/skills/ase-code-insight/SKILL.md +1 -1
- package/plugin/skills/ase-code-lint/SKILL.md +13 -14
- package/plugin/skills/ase-code-refactor/SKILL.md +17 -5
- package/plugin/skills/ase-code-resolve/SKILL.md +18 -5
- package/plugin/skills/ase-docs-proofread/SKILL.md +11 -12
- package/plugin/skills/ase-meta-compat/SKILL.md +278 -0
- package/plugin/skills/ase-meta-compat/help.md +67 -0
- package/plugin/skills/ase-meta-diff/SKILL.md +3 -3
- package/plugin/skills/ase-meta-evaluate/SKILL.md +20 -18
- package/plugin/skills/ase-meta-quorum/SKILL.md +3 -3
- package/plugin/skills/ase-task-condense/SKILL.md +6 -0
- package/plugin/skills/ase-task-edit/SKILL.md +6 -2
- package/plugin/skills/ase-task-grill/SKILL.md +10 -7
- package/plugin/skills/ase-task-id/SKILL.md +3 -3
- package/plugin/skills/ase-task-implement/SKILL.md +6 -2
- package/plugin/skills/ase-task-preflight/SKILL.md +5 -1
- package/plugin/skills/ase-task-rename/SKILL.md +9 -0
package/dst/ase-setup.js
CHANGED
|
@@ -13,8 +13,9 @@ 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" },
|
|
17
|
-
"copilot": { cli: "copilot", label: "Copilot CLI" }
|
|
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" },
|
|
18
|
+
"codex": { cli: "codex", label: "OpenAI Codex CLI", pInstall: "add", pRemove: "remove", pUpdate: "upgrade" }
|
|
18
19
|
};
|
|
19
20
|
/* CLI command "ase setup" */
|
|
20
21
|
export default class SetupCommand {
|
|
@@ -162,7 +163,7 @@ export default class SetupCommand {
|
|
|
162
163
|
const pkgdir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
163
164
|
const source = dev ? path.resolve(pkgdir, "..") : pkgdir;
|
|
164
165
|
await this.run(spec.cli, ["plugin", "marketplace", "add", source]);
|
|
165
|
-
await this.run(spec.cli, ["plugin",
|
|
166
|
+
await this.run(spec.cli, ["plugin", spec.pInstall, "ase@ase"], { retries: 3 });
|
|
166
167
|
return 0;
|
|
167
168
|
}
|
|
168
169
|
/* handler for "ase setup update" (both tools) */
|
|
@@ -185,8 +186,8 @@ export default class SetupCommand {
|
|
|
185
186
|
but there is no version change in the plugin manifest,
|
|
186
187
|
so just re-install the plugin to let the tool update its copy */
|
|
187
188
|
this.log.write("info", `setup: update[dev]: re-install ASE ${spec.label} plugin (origin: local)`);
|
|
188
|
-
await this.run(spec.cli, ["plugin",
|
|
189
|
-
await this.run(spec.cli, ["plugin",
|
|
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 });
|
|
190
191
|
}
|
|
191
192
|
else {
|
|
192
193
|
/* perform NPM version check */
|
|
@@ -201,10 +202,17 @@ export default class SetupCommand {
|
|
|
201
202
|
this.log.write("info", `setup: update: updating ASE CLI tool: ${current} -> ${latest}`);
|
|
202
203
|
const updateCmd = await this.npmCmd(["update", "-g", "@rse/ase"], true);
|
|
203
204
|
await this.run(updateCmd.cmd, updateCmd.args);
|
|
204
|
-
/* update ASE plugin
|
|
205
|
+
/* update ASE plugin (refresh the marketplace snapshot, then
|
|
206
|
+
update the plugin itself; the OpenAI Codex CLI has no
|
|
207
|
+
"plugin update" subcommand, so re-install it instead) */
|
|
205
208
|
this.log.write("info", `setup: update: updating ASE ${spec.label} plugin`);
|
|
206
|
-
await this.run(spec.cli, ["plugin", "marketplace",
|
|
207
|
-
|
|
209
|
+
await this.run(spec.cli, ["plugin", "marketplace", spec.pUpdate, "ase"]);
|
|
210
|
+
if (tool !== "codex")
|
|
211
|
+
await this.run(spec.cli, ["plugin", "update", "ase@ase"]);
|
|
212
|
+
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 });
|
|
215
|
+
}
|
|
208
216
|
}
|
|
209
217
|
return 0;
|
|
210
218
|
}
|
|
@@ -213,9 +221,11 @@ export default class SetupCommand {
|
|
|
213
221
|
const spec = toolSpecs[tool];
|
|
214
222
|
await this.ensureTool(spec.cli);
|
|
215
223
|
this.log.write("info", `setup: enable: enabling ASE ${spec.label} plugin`);
|
|
224
|
+
/* the GitHub Copilot CLI and OpenAI Codex CLI have no "plugin
|
|
225
|
+
enable" subcommand, so (re-)install the plugin instead */
|
|
216
226
|
const args = tool === "claude" ?
|
|
217
227
|
["plugin", "enable", "ase@ase"] :
|
|
218
|
-
["plugin",
|
|
228
|
+
["plugin", spec.pInstall, "ase@ase"];
|
|
219
229
|
await this.run(spec.cli, args, { retries: tool === "claude" ? 1 : 3 });
|
|
220
230
|
return 0;
|
|
221
231
|
}
|
|
@@ -224,9 +234,11 @@ export default class SetupCommand {
|
|
|
224
234
|
const spec = toolSpecs[tool];
|
|
225
235
|
await this.ensureTool(spec.cli);
|
|
226
236
|
this.log.write("info", `setup: disable: disabling ASE ${spec.label} plugin`);
|
|
237
|
+
/* the GitHub Copilot CLI and OpenAI Codex CLI have no "plugin
|
|
238
|
+
disable" subcommand, so uninstall the plugin instead */
|
|
227
239
|
const args = tool === "claude" ?
|
|
228
240
|
["plugin", "disable", "ase@ase"] :
|
|
229
|
-
["plugin",
|
|
241
|
+
["plugin", spec.pRemove, "ase@ase"];
|
|
230
242
|
await this.run(spec.cli, args, { retries: tool === "claude" ? 1 : 3 });
|
|
231
243
|
return 0;
|
|
232
244
|
}
|
|
@@ -242,7 +254,7 @@ export default class SetupCommand {
|
|
|
242
254
|
/* uninstall ASE plugin */
|
|
243
255
|
this.log.write("info", `setup: uninstall${dev ? "[dev]" : ""}: ` +
|
|
244
256
|
`uninstalling ASE ${spec.label} plugin (origin: ${dev ? "local" : "remote/bundled"})`);
|
|
245
|
-
await this.run(spec.cli, ["plugin",
|
|
257
|
+
await this.run(spec.cli, ["plugin", spec.pRemove, "ase@ase"], { ignoreError: `ASE ${spec.label} plugin not installed` });
|
|
246
258
|
await this.run(spec.cli, ["plugin", "marketplace", "remove", "ase"], { ignoreError: `ASE ${spec.label} plugin marketplace not registered` });
|
|
247
259
|
/* uninstall ASE CLI tool (non-development only) */
|
|
248
260
|
if (!dev) {
|
|
@@ -349,7 +361,7 @@ export default class SetupCommand {
|
|
|
349
361
|
/* register an MCP server with the tool, supporting both the "stdio"
|
|
350
362
|
(a local subprocess command) and "http" (a remote URL, optionally
|
|
351
363
|
with HTTP headers) transports; the per-tool command line differs
|
|
352
|
-
between Claude Code
|
|
364
|
+
between Claude Code, GitHub Copilot CLI, and OpenAI Codex CLI */
|
|
353
365
|
async mcpAdd(tool, name, env, transport) {
|
|
354
366
|
const args = ["mcp", "add"];
|
|
355
367
|
if (tool === "claude") {
|
|
@@ -366,7 +378,7 @@ export default class SetupCommand {
|
|
|
366
378
|
args.push(name, transport.url);
|
|
367
379
|
}
|
|
368
380
|
}
|
|
369
|
-
else {
|
|
381
|
+
else if (tool === "copilot") {
|
|
370
382
|
/* GitHub Copilot CLI implies the stdio transport when the
|
|
371
383
|
command is provided after "--"; only "http"/"sse" servers
|
|
372
384
|
need an explicit "--transport" flag and take the URL as a
|
|
@@ -384,10 +396,27 @@ export default class SetupCommand {
|
|
|
384
396
|
args.push(name, transport.url);
|
|
385
397
|
}
|
|
386
398
|
}
|
|
399
|
+
else {
|
|
400
|
+
/* OpenAI Codex CLI takes the server name as the first
|
|
401
|
+
positional argument and implies the stdio transport when the
|
|
402
|
+
command is provided after "--"; "http" servers take the URL
|
|
403
|
+
via the "--url" option (with optional "--header" flags) */
|
|
404
|
+
if (transport.type === "stdio") {
|
|
405
|
+
args.push(name);
|
|
406
|
+
for (const [key, val] of Object.entries(env))
|
|
407
|
+
args.push("--env", `${key}=${val}`);
|
|
408
|
+
args.push("--", ...transport.command);
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
for (const [key, val] of Object.entries(transport.headers ?? {}))
|
|
412
|
+
args.push("--header", `${key}: ${val}`);
|
|
413
|
+
args.push(name, "--url", transport.url);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
387
416
|
await this.run(toolSpecs[tool].cli, args);
|
|
388
417
|
}
|
|
389
418
|
/* unregister an MCP server from the tool; the per-tool command line
|
|
390
|
-
differs between Claude Code
|
|
419
|
+
differs between Claude Code, GitHub Copilot CLI, and OpenAI Codex CLI */
|
|
391
420
|
async mcpRemove(tool, name) {
|
|
392
421
|
const args = tool === "claude" ?
|
|
393
422
|
["mcp", "remove", "--scope", "user", name] :
|
|
@@ -527,8 +556,8 @@ export default class SetupCommand {
|
|
|
527
556
|
];
|
|
528
557
|
/* parse and validate the --tool option */
|
|
529
558
|
parseTool(value) {
|
|
530
|
-
if (value !== "claude" && value !== "copilot")
|
|
531
|
-
throw new Error(`invalid --tool value: "${value}" (expected "claude" or "
|
|
559
|
+
if (value !== "claude" && value !== "copilot" && value !== "codex")
|
|
560
|
+
throw new Error(`invalid --tool value: "${value}" (expected "claude", "copilot", or "codex")`);
|
|
532
561
|
return value;
|
|
533
562
|
}
|
|
534
563
|
/* register commands */
|
|
@@ -551,7 +580,7 @@ export default class SetupCommand {
|
|
|
551
580
|
setupCmd
|
|
552
581
|
.command("install")
|
|
553
582
|
.description("install the ASE plugin for a tool")
|
|
554
|
-
.option("-t, --tool <tool>", "target tool (\"claude\" or \"
|
|
583
|
+
.option("-t, --tool <tool>", "target tool (\"claude\", \"copilot\", or \"codex\")", toolDflt)
|
|
555
584
|
.option("-d, --dev", "use local working copy instead of remote/bundled repository", devDflt)
|
|
556
585
|
.action(async (opts) => {
|
|
557
586
|
process.exit(await this.doInstall(this.parseTool(opts.tool), opts.dev));
|
|
@@ -560,7 +589,7 @@ export default class SetupCommand {
|
|
|
560
589
|
setupCmd
|
|
561
590
|
.command("update")
|
|
562
591
|
.description("update the ASE tool and the ASE plugin for a tool")
|
|
563
|
-
.option("-t, --tool <tool>", "target tool (\"claude\" or \"
|
|
592
|
+
.option("-t, --tool <tool>", "target tool (\"claude\", \"copilot\", or \"codex\")", toolDflt)
|
|
564
593
|
.option("-f, --force", "always perform the update, even if already at latest version", false)
|
|
565
594
|
.option("-d, --dev", "use local working copy instead of remote/bundled repository", devDflt)
|
|
566
595
|
.action(async (opts) => {
|
|
@@ -570,7 +599,7 @@ export default class SetupCommand {
|
|
|
570
599
|
setupCmd
|
|
571
600
|
.command("uninstall")
|
|
572
601
|
.description("uninstall the ASE plugin for a tool and the ASE tool")
|
|
573
|
-
.option("-t, --tool <tool>", "target tool (\"claude\" or \"
|
|
602
|
+
.option("-t, --tool <tool>", "target tool (\"claude\", \"copilot\", or \"codex\")", toolDflt)
|
|
574
603
|
.option("-d, --dev", "use local working copy instead of remote/bundled repository", devDflt)
|
|
575
604
|
.action(async (opts) => {
|
|
576
605
|
process.exit(await this.doUninstall(this.parseTool(opts.tool), opts.dev));
|
|
@@ -579,7 +608,7 @@ export default class SetupCommand {
|
|
|
579
608
|
setupCmd
|
|
580
609
|
.command("enable")
|
|
581
610
|
.description("enable the ASE plugin for a tool")
|
|
582
|
-
.option("-t, --tool <tool>", "target tool (\"claude\" or \"
|
|
611
|
+
.option("-t, --tool <tool>", "target tool (\"claude\", \"copilot\", or \"codex\")", toolDflt)
|
|
583
612
|
.action(async (opts) => {
|
|
584
613
|
process.exit(await this.doEnable(this.parseTool(opts.tool)));
|
|
585
614
|
});
|
|
@@ -587,7 +616,7 @@ export default class SetupCommand {
|
|
|
587
616
|
setupCmd
|
|
588
617
|
.command("disable")
|
|
589
618
|
.description("disable the ASE plugin for a tool")
|
|
590
|
-
.option("-t, --tool <tool>", "target tool (\"claude\" or \"
|
|
619
|
+
.option("-t, --tool <tool>", "target tool (\"claude\", \"copilot\", or \"codex\")", toolDflt)
|
|
591
620
|
.action(async (opts) => {
|
|
592
621
|
process.exit(await this.doDisable(this.parseTool(opts.tool)));
|
|
593
622
|
});
|
|
@@ -610,7 +639,7 @@ export default class SetupCommand {
|
|
|
610
639
|
mcpCmd
|
|
611
640
|
.command("activate [servers]")
|
|
612
641
|
.description("activate pre-defined MCP servers (comma-separated list, or \"all\")")
|
|
613
|
-
.option("-t, --tool <tool>", "target tool (\"claude\" or \"
|
|
642
|
+
.option("-t, --tool <tool>", "target tool (\"claude\", \"copilot\", or \"codex\")", toolDflt)
|
|
614
643
|
.action(async (servers, opts) => {
|
|
615
644
|
process.exit(await this.doMcp("activate", this.parseTool(opts.tool), servers ?? "all"));
|
|
616
645
|
});
|
|
@@ -618,7 +647,7 @@ export default class SetupCommand {
|
|
|
618
647
|
mcpCmd
|
|
619
648
|
.command("deactivate [servers]")
|
|
620
649
|
.description("deactivate pre-defined MCP servers (comma-separated list, or \"all\")")
|
|
621
|
-
.option("-t, --tool <tool>", "target tool (\"claude\" or \"
|
|
650
|
+
.option("-t, --tool <tool>", "target tool (\"claude\", \"copilot\", or \"codex\")", toolDflt)
|
|
622
651
|
.action(async (servers, opts) => {
|
|
623
652
|
process.exit(await this.doMcp("deactivate", this.parseTool(opts.tool), servers ?? "all"));
|
|
624
653
|
});
|
package/dst/ase-skills.js
CHANGED
|
@@ -263,10 +263,20 @@ export class Skills {
|
|
|
263
263
|
const halfLife = 365 / 2;
|
|
264
264
|
/* lifespan requires both timestamps; recentness requires the
|
|
265
265
|
updated timestamp -- any unavailable date-derived factor is
|
|
266
|
-
|
|
267
|
-
remaining metrics, instead of collapsing the product to zero
|
|
266
|
+
given a neutral default so the entry can still be ranked by the
|
|
267
|
+
remaining metrics, instead of collapsing the product to zero.
|
|
268
|
+
For `lifespan` (an unbounded duration) the multiplicative-neutral
|
|
269
|
+
`1` is also the low floor, so a missing value cannot unfairly
|
|
270
|
+
inflate the rank. For `recentness`, however, the value is an
|
|
271
|
+
exp-decay factor bounded in `(0,1]` where `1` is the *ceiling*
|
|
272
|
+
(a just-now update), not a floor -- defaulting a missing update
|
|
273
|
+
date to `1` would hand packages with absent metadata the highest
|
|
274
|
+
possible recentness and let them outrank genuinely fresh ones.
|
|
275
|
+
We therefore default a missing `recentness` to the decay value at
|
|
276
|
+
one half-life (`0.5`), a conservative midpoint that keeps the
|
|
277
|
+
entry rankable without rewarding the missing date. */
|
|
268
278
|
const lifespan = (!Number.isNaN(cMs) && !Number.isNaN(uMs)) ? Math.max(0, uMs - cMs) : 1;
|
|
269
|
-
const recentness = !Number.isNaN(uMs) ? Math.exp(-Math.max(0, (now - uMs) / msPerDay) / halfLife) :
|
|
279
|
+
const recentness = !Number.isNaN(uMs) ? Math.exp(-Math.max(0, (now - uMs) / msPerDay) / halfLife) : 0.5;
|
|
270
280
|
return d * s * lifespan * recentness;
|
|
271
281
|
}
|
|
272
282
|
/* compute the per-alternative product-sum (rating) row from a
|
package/dst/ase-statusline.js
CHANGED
|
@@ -67,7 +67,7 @@ const formatTokens = (n) => {
|
|
|
67
67
|
if (n >= 1_000_000)
|
|
68
68
|
return `${(n / 1_000_000).toFixed(1)}M`;
|
|
69
69
|
if (n >= 1_000)
|
|
70
|
-
return `${
|
|
70
|
+
return `${(n / 1_000).toFixed(1)}k`;
|
|
71
71
|
return `${n}`;
|
|
72
72
|
};
|
|
73
73
|
/* format a millisecond duration as a compact human-readable string (e.g. 6d 12hr 7m, 4hr 27m, 12m 30s) */
|
|
@@ -184,8 +184,8 @@ export default class StatuslineCommand {
|
|
|
184
184
|
}
|
|
185
185
|
/* parse and validate the --tool option */
|
|
186
186
|
parseTool(value) {
|
|
187
|
-
if (value !== "claude" && value !== "copilot")
|
|
188
|
-
throw new InvalidArgumentError(`invalid --tool value: "${value}" (expected "claude" or "
|
|
187
|
+
if (value !== "claude" && value !== "copilot" && value !== "codex")
|
|
188
|
+
throw new InvalidArgumentError(`invalid --tool value: "${value}" (expected "claude", "copilot", or "codex")`);
|
|
189
189
|
return value;
|
|
190
190
|
}
|
|
191
191
|
/* register commands */
|
|
@@ -196,7 +196,7 @@ export default class StatuslineCommand {
|
|
|
196
196
|
program
|
|
197
197
|
.command("statusline")
|
|
198
198
|
.description("Render Claude Code or GitHub Copilot CLI statusline from stdin JSON")
|
|
199
|
-
.option("-t, --tool <tool>", "target tool (\"claude\" or \"copilot\")", toolDflt)
|
|
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)
|
|
202
202
|
.option("--no-icons", "disable icons in placeholder rendering")
|
|
@@ -208,6 +208,10 @@ export default class StatuslineCommand {
|
|
|
208
208
|
.action(async (lines, opts) => {
|
|
209
209
|
/* validate target tool */
|
|
210
210
|
const tool = this.parseTool(opts.tool);
|
|
211
|
+
/* OpenAI Codex CLI has no scriptable custom statusline
|
|
212
|
+
mechanism, so reject the request explicitly */
|
|
213
|
+
if (tool === "codex")
|
|
214
|
+
throw new Error("OpenAI Codex CLI does not support a custom statusline");
|
|
211
215
|
/* read all of stdin */
|
|
212
216
|
const input = await readStdin();
|
|
213
217
|
/* parse JSON data */
|
|
@@ -358,7 +362,7 @@ export default class StatuslineCommand {
|
|
|
358
362
|
emit(`${prefix("◔", "context")}${bar} ${pct}%`);
|
|
359
363
|
},
|
|
360
364
|
C: () => {
|
|
361
|
-
const pct =
|
|
365
|
+
const pct = data.context_window?.used_percentage ?? 0;
|
|
362
366
|
const tokensCur = (data.context_window?.total_input_tokens ?? 0) +
|
|
363
367
|
(data.context_window?.total_output_tokens ?? 0);
|
|
364
368
|
const tokensLim = pct > 0 && tokensCur > 0 ? Math.round(tokensCur * 100 / pct) : 0;
|
package/dst/ase.js
CHANGED
|
@@ -15,9 +15,10 @@ import SetupCommand from "./ase-setup.js";
|
|
|
15
15
|
import StatuslineCommand from "./ase-statusline.js";
|
|
16
16
|
import TaskCommand from "./ase-task.js";
|
|
17
17
|
import ArtifactCommand from "./ase-artifact.js";
|
|
18
|
+
import CompatCommand from "./ase-compat.js";
|
|
18
19
|
import pkg from "../package.json" with { type: "json" };
|
|
19
20
|
/* globally initialize logger */
|
|
20
|
-
const log = new Log("ase", "
|
|
21
|
+
const log = new Log("ase", "info", "-");
|
|
21
22
|
/* main entry point (wrapped in a regular async function to avoid
|
|
22
23
|
top-level await, which would be reported as "unsettled" by Node in
|
|
23
24
|
the long-running daemon process spawned by "ase service start") */
|
|
@@ -51,6 +52,7 @@ const main = async () => {
|
|
|
51
52
|
new StatuslineCommand(log).register(program);
|
|
52
53
|
new TaskCommand(log).register(program);
|
|
53
54
|
new ArtifactCommand(log).register(program);
|
|
55
|
+
new CompatCommand().register(program);
|
|
54
56
|
new DiagramCommand(log).register(program);
|
|
55
57
|
/* parse program arguments */
|
|
56
58
|
await program.parseAsync(process.argv);
|
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.13",
|
|
10
10
|
"license": "GPL-3.0-only",
|
|
11
11
|
"author": {
|
|
12
12
|
"name": "Dr. Ralf S. Engelschall",
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"which": "7.0.0",
|
|
59
59
|
"update-notifier": "7.3.1",
|
|
60
60
|
"shell-quote": "1.8.4",
|
|
61
|
+
"get-stdin": "10.0.0",
|
|
61
62
|
"proper-lockfile": "4.1.2",
|
|
62
63
|
"write-file-atomic": "8.0.0",
|
|
63
64
|
"pacote": "21.5.1",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ase",
|
|
3
|
+
"version": "0.9.13",
|
|
4
|
+
"description": "Agentic Software Engineering (ASE)",
|
|
5
|
+
"keywords": [ "agentic", "software", "engineering" ],
|
|
6
|
+
"homepage": "https://ase.tools",
|
|
7
|
+
"repository": "https://github.com/rse/ase",
|
|
8
|
+
"license": "GPL-3.0",
|
|
9
|
+
"author": {
|
|
10
|
+
"name": "Dr. Ralf S. Engelschall",
|
|
11
|
+
"email": "rse@engelschall.com",
|
|
12
|
+
"url": "https://engelschall.com"
|
|
13
|
+
},
|
|
14
|
+
"skills": "./skills/",
|
|
15
|
+
"hooks": "./hooks/hooks-codex.json",
|
|
16
|
+
"mcpServers": "./.codex-plugin/mcp.json"
|
|
17
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"hooks": [
|
|
6
|
+
{
|
|
7
|
+
"type": "command",
|
|
8
|
+
"command": "ase hook session-start --tool codex",
|
|
9
|
+
"statusMessage": "Loading ASE..."
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"SessionEnd": [
|
|
15
|
+
{
|
|
16
|
+
"hooks": [
|
|
17
|
+
{
|
|
18
|
+
"type": "command",
|
|
19
|
+
"command": "ase hook session-end --tool codex",
|
|
20
|
+
"statusMessage": "Cleaning up ASE..."
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"PreToolUse": [
|
|
26
|
+
{
|
|
27
|
+
"hooks": [
|
|
28
|
+
{
|
|
29
|
+
"type": "command",
|
|
30
|
+
"command": "ase hook pre-tool-use --tool codex"
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"PermissionRequest": [
|
|
36
|
+
{
|
|
37
|
+
"hooks": [
|
|
38
|
+
{
|
|
39
|
+
"type": "command",
|
|
40
|
+
"command": "ase hook permission-request --tool codex",
|
|
41
|
+
"statusMessage": "Approve ASE..."
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"UserPromptSubmit": [
|
|
47
|
+
{
|
|
48
|
+
"hooks": [
|
|
49
|
+
{
|
|
50
|
+
"type": "command",
|
|
51
|
+
"command": "ase hook user-prompt-submit --tool codex"
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"Stop": [
|
|
57
|
+
{
|
|
58
|
+
"hooks": [
|
|
59
|
+
{
|
|
60
|
+
"type": "command",
|
|
61
|
+
"command": "ase hook stop --tool codex"
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -9,6 +9,9 @@ User Dialog
|
|
|
9
9
|
<if condition="<ase-agent-tool/> is 'copilot'">
|
|
10
10
|
<user-dialog-tool>ask_user</user-dialog-tool>
|
|
11
11
|
</if>
|
|
12
|
+
<if condition="<ase-agent-tool/> is 'codex'">
|
|
13
|
+
<user-dialog-tool>none</user-dialog-tool>
|
|
14
|
+
</if>
|
|
12
15
|
|
|
13
16
|
<define name="user-dialog">
|
|
14
17
|
|
|
@@ -138,6 +141,23 @@ Let the *user interactively choose* an answer.
|
|
|
138
141
|
|
|
139
142
|
</if>
|
|
140
143
|
|
|
144
|
+
- <if condition="<ase-agent-tool/> is 'codex'">
|
|
145
|
+
|
|
146
|
+
OpenAI Codex CLI has *no* interactive user-dialog tool, so you
|
|
147
|
+
*MUST* *NOT* call any tool here. Instead, render the question and
|
|
148
|
+
answers as a custom Markdown dialog and let the user reply with a
|
|
149
|
+
free-text choice -- exactly as defined by the following expansion,
|
|
150
|
+
passing <spec/> as its question specification, and set <result/> to
|
|
151
|
+
the result of that custom dialog:
|
|
152
|
+
|
|
153
|
+
<expand name="custom-dialog" arg1="--other">
|
|
154
|
+
<spec/>
|
|
155
|
+
</expand>
|
|
156
|
+
|
|
157
|
+
Do not output anything in this step!
|
|
158
|
+
|
|
159
|
+
</if>
|
|
160
|
+
|
|
141
161
|
</define>
|
|
142
162
|
|
|
143
163
|
<define name="custom-dialog">
|
|
@@ -165,7 +185,7 @@ following procedure:
|
|
|
165
185
|
|
|
166
186
|
Do not output anything in this step!
|
|
167
187
|
|
|
168
|
-
2.
|
|
188
|
+
2. Render the custom dialog, collect the user input, and dispatch on the result:
|
|
169
189
|
|
|
170
190
|
1. You *MUST* not output anything in this step.
|
|
171
191
|
|
|
@@ -243,7 +243,7 @@ at runtime.
|
|
|
243
243
|
for the component's existence and boundary.
|
|
244
244
|
|
|
245
245
|
- In case a component has no dependencies at all, the
|
|
246
|
-
entire `-
|
|
246
|
+
entire `- Depends On:` bullet point is omitted.
|
|
247
247
|
|
|
248
248
|
- In case the rationale is not present, the
|
|
249
249
|
entire `, **BECAUSE** [...]` clause is omitted.
|
|
@@ -666,8 +666,16 @@ How the Non-Functional Requirements (NR) are addressed.
|
|
|
666
666
|
perspective addresses, one of `Performance`, `Scalability`,
|
|
667
667
|
`Reliability`, `Availability`, `Security`, `Privacy`,
|
|
668
668
|
`Usability`, `Accessibility`, `Maintainability`, `Portability`,
|
|
669
|
-
`Compatibility`, or `Compliance
|
|
670
|
-
Requirements
|
|
669
|
+
`Compatibility`, or `Compliance`. This enumeration is a
|
|
670
|
+
solution-oriented refinement of the Non-Functional Requirements
|
|
671
|
+
(NR) categories and intentionally differs from them: the NR
|
|
672
|
+
categories follow the ISO/IEC 25010:2023 top-level characteristics
|
|
673
|
+
(and include `Safety` and `Flexibility`), whereas the perspective
|
|
674
|
+
qualities decompose some of these into the finer-grained,
|
|
675
|
+
architecturally actionable attributes `Scalability`,
|
|
676
|
+
`Availability`, `Privacy`, `Accessibility`, and `Portability`. A
|
|
677
|
+
perspective's quality therefore need not match the category of the
|
|
678
|
+
`SPEC-NR-<spec-nr-requirement-id/>` it addresses.
|
|
671
679
|
|
|
672
680
|
- <arch-qp-perspective-requirement/> is a
|
|
673
681
|
`SPEC-NR-<spec-nr-requirement-id/>` reference to the corresponding
|
|
@@ -722,10 +730,11 @@ forces at play, the chosen response, and the reasoning that justifies it.
|
|
|
722
730
|
|
|
723
731
|
## DECISION: <arch-dr-decision-name/> <a id="ARCH-DR-<arch-dr-decision-id/>"></a>
|
|
724
732
|
|
|
725
|
-
- Status:
|
|
726
|
-
- Affects:
|
|
727
|
-
-
|
|
728
|
-
-
|
|
733
|
+
- Status: <arch-dr-decision-status/>
|
|
734
|
+
- Affects: <arch-dr-decision-element/>, [...]
|
|
735
|
+
- Superseded-By: <arch-dr-decision-superseded-by/>
|
|
736
|
+
- Created: <arch-dr-decision-created/>
|
|
737
|
+
- Modified: <arch-dr-decision-modified/>
|
|
729
738
|
|
|
730
739
|
- WHEN (Context):
|
|
731
740
|
<arch-dr-decision-context/>
|
|
@@ -752,12 +761,17 @@ forces at play, the chosen response, and the reasoning that justifies it.
|
|
|
752
761
|
<arch-dr-decision-decision/>, not longer than 80 characters.
|
|
753
762
|
|
|
754
763
|
- <arch-dr-decision-status/> is one of `proposed`, `accepted`,
|
|
755
|
-
`deprecated`, or `superseded
|
|
764
|
+
`deprecated`, or `superseded`.
|
|
756
765
|
|
|
757
766
|
- <arch-dr-decision-element/> is an `ARCH-FV-<arch-fv-component-id/>`
|
|
758
767
|
or `ARCH-DP-<arch-dp-node-id/>` reference to the functional
|
|
759
768
|
element or deployment node the decision affects.
|
|
760
769
|
|
|
770
|
+
- <arch-dr-decision-superseded-by/> is an
|
|
771
|
+
`ARCH-DR-<arch-dr-decision-id/>` reference to the decision that
|
|
772
|
+
supersedes this decision. It is present if and only if the
|
|
773
|
+
<arch-dr-decision-status/> is `superseded`.
|
|
774
|
+
|
|
761
775
|
- <arch-dr-decision-created/> is the timestamp when this decision was
|
|
762
776
|
created, and <arch-dr-decision-modified/> is the timestamp when
|
|
763
777
|
this decision was last modified, both in the same ISO-style format
|
|
@@ -832,7 +846,10 @@ forces at play, the chosen response, and the reasoning that justifies it.
|
|
|
832
846
|
job.
|
|
833
847
|
|
|
834
848
|
- In case the element reference is not present, the
|
|
835
|
-
entire `-
|
|
849
|
+
entire `- Affects:` bullet point is omitted.
|
|
850
|
+
|
|
851
|
+
- In case the <arch-dr-decision-status/> is not `superseded`, the
|
|
852
|
+
entire `- Superseded-By:` bullet point is omitted.
|
|
836
853
|
|
|
837
854
|
- In case the `NOTES (Background)` content is not present, the
|
|
838
855
|
entire `NOTES (Background)` chunk is omitted.
|
|
@@ -29,9 +29,13 @@ Artifact Meta Information
|
|
|
29
29
|
- **Artifact**:
|
|
30
30
|
|
|
31
31
|
At level 2, each **Artifact Set** is composed of many **Artifact**s.
|
|
32
|
-
Each **Artifact** has
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
Each **Artifact** has an identifier <artifact-id/>, which is an
|
|
33
|
+
upper-case, two-letter identifier (e.g. `CJ` for `Customer Journey`)
|
|
34
|
+
derived from the **Artifact** name. The <artifact-id/> is unique
|
|
35
|
+
only *within* its **Artifact Set**; the globally-unique handle of an
|
|
36
|
+
**Artifact** is the qualified form <artifact-set-id/>-<artifact-id/>
|
|
37
|
+
(e.g. `SPEC-DP` and `ARCH-DP` are distinct artifacts). All
|
|
38
|
+
references to an **Artifact** *MUST* use this qualified form.
|
|
35
39
|
|
|
36
40
|
Each **Artifact** also has a sequence number <artifact-no/>, which
|
|
37
41
|
is the zero-padded, two-digit position of the **Artifact** (starting
|
|
@@ -871,8 +871,8 @@ for main, alternative, and exceptional paths.
|
|
|
871
871
|
|
|
872
872
|
- <spec-uc-usecase-description/>: a concise paragraph (1-3
|
|
873
873
|
sentences) of prose describing the use case at a glance, without
|
|
874
|
-
prescribing the step-by-step flow (which belongs to the
|
|
875
|
-
|
|
874
|
+
prescribing the step-by-step flow (which belongs to the
|
|
875
|
+
**SCENARIO** blocks of this use case).
|
|
876
876
|
|
|
877
877
|
- In case the use case realizes no specific functional requirement,
|
|
878
878
|
the entire `- Requirements:` bullet point is omitted.
|
|
@@ -884,7 +884,7 @@ for main, alternative, and exceptional paths.
|
|
|
884
884
|
|
|
885
885
|
<format>
|
|
886
886
|
|
|
887
|
-
### SCENARIO: <spec-uc-usecase-scenario-name/> (<spec-uc-usecase-scenario-type/>)
|
|
887
|
+
### SCENARIO: <spec-uc-usecase-scenario-name/> (<spec-uc-usecase-scenario-type/>) <a id="SPEC-UC-<spec-uc-usecase-id/>-<spec-uc-usecase-scenario-id/>"></a>
|
|
888
888
|
|
|
889
889
|
1. <spec-uc-usecase-scenario-step/>
|
|
890
890
|
2. <spec-uc-usecase-scenario-step/>
|
|
@@ -894,6 +894,11 @@ for main, alternative, and exceptional paths.
|
|
|
894
894
|
|
|
895
895
|
- <spec-uc-usecase-scenario/> details:
|
|
896
896
|
|
|
897
|
+
- <spec-uc-usecase-scenario-id/>: per-use-case unique "slug" of
|
|
898
|
+
always 1-3 lower-cased words (concatenated with "-" characters
|
|
899
|
+
and in total not longer than 30 characters), derived from
|
|
900
|
+
<spec-uc-usecase-scenario-name/>.
|
|
901
|
+
|
|
897
902
|
- <spec-uc-usecase-scenario-name/>: a short (3-8 word) summary of the
|
|
898
903
|
scenario.
|
|
899
904
|
|
|
@@ -1213,9 +1218,9 @@ turn.
|
|
|
1213
1218
|
visualizes (optional).
|
|
1214
1219
|
|
|
1215
1220
|
- <spec-ds-storyboard-scenario/> is a
|
|
1216
|
-
|
|
1217
|
-
the corresponding **Aspect** of the Use
|
|
1218
|
-
storyboard visualizes (optional).
|
|
1221
|
+
`SPEC-UC-<spec-uc-usecase-id/>-<spec-uc-usecase-scenario-id/>`
|
|
1222
|
+
reference to the corresponding scenario **Aspect** of the Use
|
|
1223
|
+
Case **Artifact** the storyboard visualizes (optional).
|
|
1219
1224
|
|
|
1220
1225
|
- <spec-ds-frame-name/>: a short (2-5 word) label for the screen,
|
|
1221
1226
|
turn, or state depicted by the storyboard frame. Frames are
|
|
@@ -27,8 +27,8 @@ set placeholders into the context as a side-effect.
|
|
|
27
27
|
`--<long/>[|-<short/>][=<default/>|=(<c1/>|<c2/>|...)[...]]`, set
|
|
28
28
|
<getopt-option-<long/>/> to <default/> (for `=<default/>`
|
|
29
29
|
form), or to the *single token* <c1/> (the first choice, both
|
|
30
|
-
for the choice form `=(<c1/>|<c2
|
|
31
|
-
`=(<c1/>|<c2
|
|
30
|
+
for the choice form `=(<c1/>|<c2/>|...)` and for the list form
|
|
31
|
+
`=(<c1/>|<c2/>|...)...` -- an *unsupplied* list-option always
|
|
32
32
|
defaults to the single token <c1/>, *not* to a whole list),
|
|
33
33
|
or to `false` (for value-less options). Then set
|
|
34
34
|
<getopt-arguments><getopt-args/></getopt-arguments>.
|
|
@@ -63,6 +63,10 @@ set placeholders into the context as a side-effect.
|
|
|
63
63
|
validate each remaining token themselves as they consume it).
|
|
64
64
|
|
|
65
65
|
4. **Short-Circuit for Error**:
|
|
66
|
+
This step is the *sole* and *authoritative* handler for an
|
|
67
|
+
`ERROR:`-prefixed `ase_getopt` response; the generic MCP-Tool-Calls
|
|
68
|
+
error rule (see `ase-skill.md`) explicitly does *NOT* apply here, so
|
|
69
|
+
only the <template/> below is emitted (never both).
|
|
66
70
|
If <text/> starts with `ERROR:`:
|
|
67
71
|
Remove all `ERROR:` or `error:` prefixes from <text/>.
|
|
68
72
|
Then only output the following <template/> and
|