@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.
Files changed (44) hide show
  1. package/dst/ase-compat.js +68 -0
  2. package/dst/ase-diagram.js +5 -2
  3. package/dst/ase-getopt.js +2 -1
  4. package/dst/ase-hook.js +158 -67
  5. package/dst/ase-mcp.js +2 -0
  6. package/dst/ase-persona.js +4 -1
  7. package/dst/ase-service.js +2 -0
  8. package/dst/ase-setup.js +52 -23
  9. package/dst/ase-skills.js +13 -3
  10. package/dst/ase-statusline.js +9 -5
  11. package/dst/ase.js +3 -1
  12. package/package.json +2 -1
  13. package/plugin/.claude-plugin/plugin.json +1 -1
  14. package/plugin/.codex-plugin/mcp.json +8 -0
  15. package/plugin/.codex-plugin/plugin.json +17 -0
  16. package/plugin/.github/plugin/plugin.json +1 -1
  17. package/plugin/hooks/hooks-codex.json +67 -0
  18. package/plugin/meta/ase-dialog.md +21 -1
  19. package/plugin/meta/ase-format-arch.md +26 -9
  20. package/plugin/meta/ase-format-meta.md +7 -3
  21. package/plugin/meta/ase-format-spec.md +11 -6
  22. package/plugin/meta/ase-getopt.md +6 -2
  23. package/plugin/meta/ase-skill.md +43 -19
  24. package/plugin/package.json +1 -1
  25. package/plugin/skills/ase-arch-analyze/SKILL.md +17 -10
  26. package/plugin/skills/ase-code-craft/SKILL.md +17 -5
  27. package/plugin/skills/ase-code-explain/SKILL.md +0 -1
  28. package/plugin/skills/ase-code-insight/SKILL.md +1 -1
  29. package/plugin/skills/ase-code-lint/SKILL.md +13 -14
  30. package/plugin/skills/ase-code-refactor/SKILL.md +17 -5
  31. package/plugin/skills/ase-code-resolve/SKILL.md +18 -5
  32. package/plugin/skills/ase-docs-proofread/SKILL.md +11 -12
  33. package/plugin/skills/ase-meta-compat/SKILL.md +278 -0
  34. package/plugin/skills/ase-meta-compat/help.md +67 -0
  35. package/plugin/skills/ase-meta-diff/SKILL.md +3 -3
  36. package/plugin/skills/ase-meta-evaluate/SKILL.md +20 -18
  37. package/plugin/skills/ase-meta-quorum/SKILL.md +3 -3
  38. package/plugin/skills/ase-task-condense/SKILL.md +6 -0
  39. package/plugin/skills/ase-task-edit/SKILL.md +6 -2
  40. package/plugin/skills/ase-task-grill/SKILL.md +10 -7
  41. package/plugin/skills/ase-task-id/SKILL.md +3 -3
  42. package/plugin/skills/ase-task-implement/SKILL.md +6 -2
  43. package/plugin/skills/ase-task-preflight/SKILL.md +5 -1
  44. 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", "install", "ase@ase"], { retries: 3 });
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", "uninstall", "ase@ase"], { ignoreError: `ASE ${spec.label} plugin not installed` });
189
- await this.run(spec.cli, ["plugin", "install", "ase@ase"], { retries: 3 });
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", "update", "ase"]);
207
- await this.run(spec.cli, ["plugin", "update", "ase@ase"]);
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", "install", "ase@ase"];
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", "uninstall", "ase@ase"];
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", "uninstall", "ase@ase"], { ignoreError: `ASE ${spec.label} plugin not installed` });
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 and GitHub Copilot CLI */
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 and GitHub Copilot CLI */
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 "copilot")`);
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 \"copilot\")", toolDflt)
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 \"copilot\")", toolDflt)
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 \"copilot\")", toolDflt)
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 \"copilot\")", toolDflt)
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 \"copilot\")", toolDflt)
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 \"copilot\")", toolDflt)
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 \"copilot\")", toolDflt)
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
- treated as neutral `1` so the entry can still be ranked by the
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) : 1;
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
@@ -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 `${Math.round(n / 1_000)}k`;
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 "copilot")`);
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 = Math.floor(data.context_window?.used_percentage ?? 0);
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", "warning", "-");
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.11",
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",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ase",
3
- "version": "0.9.11",
3
+ "version": "0.9.13",
4
4
  "description": "Agentic Software Engineering (ASE)",
5
5
  "keywords": [ "agentic", "software", "engineering" ],
6
6
  "homepage": "https://ase.tools",
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "ase": {
4
+ "command": "ase",
5
+ "args": [ "mcp" ]
6
+ }
7
+ }
8
+ }
@@ -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
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ase",
3
- "version": "0.9.11",
3
+ "version": "0.9.13",
4
4
  "description": "Agentic Software Engineering (ASE)",
5
5
  "keywords": [ "agentic", "software", "engineering" ],
6
6
  "homepage": "https://ase.tools",
@@ -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. Dispatch according to the agent tool:
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 `- Depends On:` bullet point is omitted.
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` (aligned with the Non-Functional
670
- Requirements categories).
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: <arch-dr-decision-status/>
726
- - Affects: <arch-dr-decision-element/>, [...]
727
- - Created: <arch-dr-decision-created/>
728
- - Modified: <arch-dr-decision-modified/>
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 by ARCH-DR-<arch-dr-decision-id/>`.
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 `- Affects:` bullet point is omitted.
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 a unique identifier <artifact-id/>, which is
33
- an upper-case, two-letter identifier (e.g. `CJ` for `Customer
34
- Journey`) derived from the **Artifact** name.
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 **Use
875
- Case Scenarios** **Artifact**).
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
- Use Case scenario type `<spec-uc-usecase-scenario-type/>` of
1217
- the corresponding **Aspect** of the Use Case **Artifact** the
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>/|...)` and for the list form
31
- `=(<c1/>|<c2>/|...)...` -- an *unsupplied* list-option always
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