@rse/ase 0.0.46 → 0.0.48

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-config.js CHANGED
@@ -13,6 +13,7 @@ import * as v from "valibot";
13
13
  import Table from "cli-table3";
14
14
  import writeFileAtomic from "write-file-atomic";
15
15
  import lockfile from "proper-lockfile";
16
+ import { z } from "zod";
16
17
  /* classification taxonomy */
17
18
  export const projectClassification = {
18
19
  boxing: ["white", "grey", "black"]
@@ -53,7 +54,8 @@ export const projectClassificationPresets = {
53
54
  (reads always cascade through the full chain; this restricts writes only);
54
55
  keys absent from this map default to all non-"default" scope kinds */
55
56
  export const configWritableScopes = {
56
- "agent.task": ["session"]
57
+ "agent.task": ["session"],
58
+ "agent.skill": ["session"]
57
59
  };
58
60
  /* default set of scope kinds writable for any unrestricted key */
59
61
  const configWritableScopesDefault = ["user", "project", "task", "session"];
@@ -132,7 +134,8 @@ export const configSchema = v.nullish(v.strictObject({
132
134
  })),
133
135
  agent: v.optional(v.strictObject({
134
136
  persona: v.optional(v.picklist(agentClassification.persona)),
135
- task: v.optional(v.pipe(v.string(), v.minLength(1)))
137
+ task: v.optional(v.pipe(v.string(), v.minLength(1))),
138
+ skill: v.optional(v.pipe(v.string(), v.minLength(1)))
136
139
  }))
137
140
  }));
138
141
  /* encapsulate read/write access to a stack of "<name>.yaml" configuration files,
@@ -626,3 +629,100 @@ export default class ConfigCommand {
626
629
  });
627
630
  }
628
631
  }
632
+ /* MCP registration entry point for layered YAML configuration access */
633
+ export class ConfigMCP {
634
+ log;
635
+ constructor(log) {
636
+ this.log = log;
637
+ }
638
+ /* register the MCP tools */
639
+ register(mcp) {
640
+ /* config get */
641
+ mcp.registerTool("config_get", {
642
+ title: "ASE config get",
643
+ description: "Read the effective value of a dotted configuration `key` from the layered " +
644
+ "configuration, cascading through default/user/project/task/session chain up to and " +
645
+ "including the requested `scope`. Returns the value as JSON-encoded `text`; " +
646
+ "returns an empty string if no value is set.",
647
+ inputSchema: {
648
+ key: z.string()
649
+ .describe("dotted configuration key (e.g. \"agent.skill\")"),
650
+ scope: z.string()
651
+ .describe("scope chain (e.g. \"session:<id>\", \"task:<id>\", \"project\", \"user\")")
652
+ }
653
+ }, async (args) => {
654
+ try {
655
+ const scope = parseScope(args.scope);
656
+ const cfg = new Config("config", configSchema, this.log, scope);
657
+ let text = "";
658
+ cfg.lock(() => {
659
+ cfg.read();
660
+ const val = cfg.get(args.key);
661
+ text = val === undefined ? "" : JSON.stringify(val);
662
+ });
663
+ return { content: [{ type: "text", text }] };
664
+ }
665
+ catch (err) {
666
+ const message = err instanceof Error ? err.message : String(err);
667
+ return { isError: true, content: [{ type: "text", text: `config_get: ERROR: ${message}` }] };
668
+ }
669
+ });
670
+ /* config set */
671
+ mcp.registerTool("config_set", {
672
+ title: "ASE config set",
673
+ description: "Write `val` to a dotted configuration `key` at the target `scope` " +
674
+ "(the strongest scope term in the chain). The value is validated against " +
675
+ "the configuration schema before being persisted.",
676
+ inputSchema: {
677
+ key: z.string()
678
+ .describe("dotted configuration key (e.g. \"agent.skill\")"),
679
+ val: z.union([z.string(), z.number(), z.boolean(), z.null(), z.array(z.any()), z.record(z.string(), z.any())])
680
+ .describe("value to store under `key`"),
681
+ scope: z.string()
682
+ .describe("scope chain (e.g. \"session:<id>\", \"task:<id>\", \"project\", \"user\")")
683
+ }
684
+ }, async (args) => {
685
+ try {
686
+ const scope = parseScope(args.scope);
687
+ const cfg = new Config("config", configSchema, this.log, scope);
688
+ cfg.lock(() => {
689
+ cfg.read();
690
+ cfg.set(args.key, args.val);
691
+ cfg.write();
692
+ });
693
+ return { content: [{ type: "text", text: `config_set: OK: stored "${args.key}" on scope "${args.scope}"` }] };
694
+ }
695
+ catch (err) {
696
+ const message = err instanceof Error ? err.message : String(err);
697
+ return { isError: true, content: [{ type: "text", text: `config_set: ERROR: ${message}` }] };
698
+ }
699
+ });
700
+ /* config delete */
701
+ mcp.registerTool("config_delete", {
702
+ title: "ASE config delete",
703
+ description: "Delete the value at a dotted configuration `key` from the target `scope` " +
704
+ "(the strongest scope term in the chain). No-op if the key is not present.",
705
+ inputSchema: {
706
+ key: z.string()
707
+ .describe("dotted configuration key (e.g. \"agent.skill\")"),
708
+ scope: z.string()
709
+ .describe("scope chain (e.g. \"session:<id>\", \"task:<id>\", \"project\", \"user\")")
710
+ }
711
+ }, async (args) => {
712
+ try {
713
+ const scope = parseScope(args.scope);
714
+ const cfg = new Config("config", configSchema, this.log, scope);
715
+ cfg.lock(() => {
716
+ cfg.read();
717
+ cfg.delete(args.key);
718
+ cfg.write();
719
+ });
720
+ return { content: [{ type: "text", text: `config_delete: OK: removed "${args.key}" on scope "${args.scope}"` }] };
721
+ }
722
+ catch (err) {
723
+ const message = err instanceof Error ? err.message : String(err);
724
+ return { isError: true, content: [{ type: "text", text: `config_delete: ERROR: ${message}` }] };
725
+ }
726
+ });
727
+ }
728
+ }
package/dst/ase-getopt.js CHANGED
@@ -34,7 +34,11 @@ export class GetoptMCP {
34
34
  /* normalize args */
35
35
  const argsRaw = typeof args.args === "string" ? args.args : null;
36
36
  const argsVec = typeof args.args === "string" ?
37
- shParse(args.args).filter((e) => typeof e === "string") :
37
+ shParse(args.args)
38
+ .map((e) => typeof e === "string" ? e :
39
+ (e !== null && typeof e === "object" && "op" in e && e.op === "glob" ?
40
+ e.pattern : null))
41
+ .filter((e) => e !== null) :
38
42
  args.args;
39
43
  /* build a fresh commander program */
40
44
  const cmd = new Command(args.name)
package/dst/ase-hook.js CHANGED
@@ -196,6 +196,26 @@ export default class HookCommand {
196
196
  /* handler for "ase hook stop" (both tools) */
197
197
  doStop(_tool) {
198
198
  this.writeAgentStatus("ready");
199
+ /* safety net: clear any lingering "agent.skill" marker so a
200
+ crashed or aborted skill loop does not leave information active */
201
+ const stdin = fs.readFileSync(0, "utf8");
202
+ const input = stdin.trim() !== "" ? JSON.parse(stdin) : {};
203
+ const sessionId = input.session_id ?? input.sessionId ?? "";
204
+ if (/^[A-Za-z0-9._-]+$/.test(sessionId)) {
205
+ try {
206
+ const cfg = new Config("config", configSchema, this.log, parseScope(`session:${sessionId}`));
207
+ cfg.lock(() => {
208
+ cfg.read();
209
+ if (typeof cfg.get("agent.skill") === "string") {
210
+ cfg.delete("agent.skill");
211
+ cfg.write();
212
+ }
213
+ });
214
+ }
215
+ catch (_e) {
216
+ /* best-effort: ignore failures */
217
+ }
218
+ }
199
219
  return 0;
200
220
  }
201
221
  /* handler for "ase hook session-end" (both tools) */
@@ -218,6 +238,25 @@ export default class HookCommand {
218
238
  }
219
239
  return 0;
220
240
  }
241
+ /* read the session-scoped "agent.skill" config value */
242
+ readActiveSkill(sessionId) {
243
+ if (!/^[A-Za-z0-9._-]+$/.test(sessionId))
244
+ return "";
245
+ try {
246
+ const cfg = new Config("config", configSchema, this.log, parseScope(`session:${sessionId}`));
247
+ let val = "";
248
+ cfg.lock(() => {
249
+ cfg.read();
250
+ const v = cfg.get("agent.skill");
251
+ if (typeof v === "string")
252
+ val = v;
253
+ });
254
+ return val;
255
+ }
256
+ catch (_e) {
257
+ return "";
258
+ }
259
+ }
221
260
  /* handler for "ase hook pre-tool-use" (both tools) */
222
261
  doPreToolUse(tool) {
223
262
  const spec = toolSpecs[tool];
@@ -254,6 +293,14 @@ export default class HookCommand {
254
293
  approve = true;
255
294
  reason = "ASE MCP tool invocation auto-approved";
256
295
  }
296
+ else if (toolName === "Edit") {
297
+ const sessionId = input.session_id ?? input.sessionId ?? "";
298
+ const activeSkill = this.readActiveSkill(sessionId);
299
+ if (activeSkill === "ase-docs-proofread") {
300
+ approve = true;
301
+ reason = `${activeSkill}: user already consented via AskUserQuestion`;
302
+ }
303
+ }
257
304
  /* emit permission decision (or stay silent to defer to default flow).
258
305
  Claude Code expects the decision nested in "hookSpecificOutput";
259
306
  Copilot CLI expects flat top-level fields. */
@@ -15,7 +15,7 @@ import prettyMs from "pretty-ms";
15
15
  import * as v from "valibot";
16
16
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
17
17
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
18
- import { Config, configSchema } from "./ase-config.js";
18
+ import { Config, configSchema, ConfigMCP } from "./ase-config.js";
19
19
  import { DiagramMCP } from "./ase-diagram.js";
20
20
  import { TaskMCP } from "./ase-task.js";
21
21
  import { KVMCP } from "./ase-kv.js";
@@ -241,6 +241,7 @@ export default class ServiceCommand {
241
241
  new TimestampMCP().register(mcp);
242
242
  new GetoptMCP().register(mcp);
243
243
  new SkillsMCP().register(mcp);
244
+ new ConfigMCP(this.log).register(mcp);
244
245
  return mcp;
245
246
  };
246
247
  /* listen to HTTP/REST endpoints */
package/dst/ase-skills.js CHANGED
@@ -75,50 +75,184 @@ export class Skills {
75
75
  return "N.A.";
76
76
  }
77
77
  }
78
+ /* fetch Maven Central metadata for a single coordinate `groupId:artifactId`:
79
+ the Solr search endpoint provides the latest version + a `timestamp` (ms
80
+ epoch) of that release; a second `core=gav` query yields the earliest
81
+ known release, which we treat as the "created" date. The latest POM is
82
+ then downloaded directly from the repo to extract the SCM/project URL. */
83
+ static async fetchMavenInfo(coord) {
84
+ const [groupId, artifactId] = coord.split(":", 2);
85
+ if (groupId === undefined || artifactId === undefined || groupId === "" || artifactId === "")
86
+ return { version: "", created: "", updated: "", repository: "" };
87
+ try {
88
+ const latest = await Skills.httpLimit(() => ofetch("https://search.maven.org/solrsearch/select" +
89
+ `?q=g:%22${encodeURIComponent(groupId)}%22+AND+a:%22${encodeURIComponent(artifactId)}%22` +
90
+ "&rows=1&wt=json", { timeout: Skills.HTTP_TIMEOUT_MS, ignoreResponseError: true }));
91
+ const doc = latest?.response?.docs?.[0];
92
+ const version = doc?.latestVersion ?? doc?.v ?? "";
93
+ const updated = typeof doc?.timestamp === "number" ? new Date(doc.timestamp).toISOString() : "";
94
+ /* Maven Central caps `rows` at 20 and ignores client `sort`,
95
+ so paginate via `start=` to walk all versions, accumulate the
96
+ minimum timestamp client-side */
97
+ const baseUrl = "https://search.maven.org/solrsearch/select" +
98
+ `?q=g:%22${encodeURIComponent(groupId)}%22+AND+a:%22${encodeURIComponent(artifactId)}%22` +
99
+ "&core=gav&rows=20&wt=json";
100
+ let firstTs;
101
+ let start = 0;
102
+ for (let page = 0; page < 50; page++) {
103
+ const chunk = await Skills.httpLimit(() => ofetch(`${baseUrl}&start=${start}`, { timeout: Skills.HTTP_TIMEOUT_MS, ignoreResponseError: true }));
104
+ const docs = chunk?.response?.docs ?? [];
105
+ for (const d of docs) {
106
+ if (typeof d.timestamp === "number" && (firstTs === undefined || d.timestamp < firstTs))
107
+ firstTs = d.timestamp;
108
+ }
109
+ start += docs.length;
110
+ const total = chunk?.response?.numFound ?? 0;
111
+ if (docs.length === 0 || start >= total)
112
+ break;
113
+ }
114
+ const created = typeof firstTs === "number" ? new Date(firstTs).toISOString() : updated;
115
+ let repository = "";
116
+ if (version !== "") {
117
+ try {
118
+ const pom = await Skills.httpLimit(() => ofetch(`https://repo1.maven.org/maven2/${groupId.replace(/\./g, "/")}` +
119
+ `/${artifactId}/${version}/${artifactId}-${version}.pom`, { timeout: Skills.HTTP_TIMEOUT_MS, ignoreResponseError: true, responseType: "text" }));
120
+ if (typeof pom === "string") {
121
+ const scm = /<scm>[\s\S]*?<url>\s*([^<\s]+)\s*<\/url>[\s\S]*?<\/scm>/i.exec(pom);
122
+ if (scm !== null)
123
+ repository = scm[1];
124
+ else {
125
+ const url = /<project\b[\s\S]*?<url>\s*([^<\s]+)\s*<\/url>/i.exec(pom);
126
+ if (url !== null)
127
+ repository = url[1];
128
+ }
129
+ }
130
+ }
131
+ catch {
132
+ repository = "";
133
+ }
134
+ }
135
+ return { version, created, updated, repository };
136
+ }
137
+ catch {
138
+ return { version: "", created: "", updated: "", repository: "" };
139
+ }
140
+ }
141
+ /* fetch the "Used By" count from `mvnrepository.com` as a downloads proxy
142
+ for a Maven coordinate (Maven Central exposes no per-artifact download
143
+ counts). The label `Used By (NNK)` on the artifact landing page denotes
144
+ the number of other published artifacts depending on it. Note: the site
145
+ is fronted by *Cloudflare* and frequently serves a JS-challenge to
146
+ non-browser clients regardless of `User-Agent` (the block is driven by
147
+ TLS/HTTP2 fingerprinting, not headers). A realistic browser UA is
148
+ best-effort and degrades gracefully to `"N.A."` when blocked. */
149
+ static async fetchMavenDownloads(coord) {
150
+ const [groupId, artifactId] = coord.split(":", 2);
151
+ if (groupId === undefined || artifactId === undefined || groupId === "" || artifactId === "")
152
+ return "N.A.";
153
+ try {
154
+ const html = await Skills.httpLimit(() => ofetch(`https://mvnrepository.com/artifact/${encodeURIComponent(groupId)}/${encodeURIComponent(artifactId)}`, {
155
+ timeout: Skills.HTTP_TIMEOUT_MS,
156
+ ignoreResponseError: true,
157
+ responseType: "text",
158
+ headers: {
159
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " +
160
+ "AppleWebKit/537.36 (KHTML, like Gecko) " +
161
+ "Chrome/131.0.0.0 Safari/537.36"
162
+ }
163
+ }));
164
+ if (typeof html !== "string")
165
+ return "N.A.";
166
+ const m = /Used\s+By\s*\(\s*([\d.,]+)\s*([KMB]?)\s*\)/i.exec(html);
167
+ if (m === null)
168
+ return "N.A.";
169
+ const base = parseFloat(m[1].replace(/,/g, ""));
170
+ const mult = m[2] === "K" ? 1_000 : m[2] === "M" ? 1_000_000 : m[2] === "B" ? 1_000_000_000 : 1;
171
+ const n = Math.round(base * mult);
172
+ return Number.isFinite(n) ? n : "N.A.";
173
+ }
174
+ catch {
175
+ return "N.A.";
176
+ }
177
+ }
78
178
  /* gather metadata for all given components with maximum parallelism,
79
179
  dispatching on the technology stack:
80
180
  - "JavaScript"/"TypeScript": NPM registry (pacote) + GitHub stars + npm-downloads
81
- - "Java"/"Kotlin"/"Unknown": not yet supported -- return empty result */
181
+ - "Java"/"Kotlin": Maven Central + GitHub stars + mvnrepository.com "Used By"
182
+ - "Unknown": not supported -- return empty result */
82
183
  static async info(stack, components) {
83
- /* FIXME: currently just limited technology stack support */
84
- if (stack !== "JavaScript" && stack !== "TypeScript")
184
+ if (stack === "JavaScript" || stack === "TypeScript") {
185
+ /* per package: kick off packument and downloads in parallel,
186
+ then stars as soon as the packument resolves; across packages
187
+ everything runs concurrently via Promise.all */
188
+ const results = await Promise.all(components.map(async (name) => {
189
+ const packumentPromise = Skills.fetchPackument(name);
190
+ const downloadsPromise = Skills.fetchDownloads(name);
191
+ const starsPromise = packumentPromise.then((p) => Skills.fetchStars(p.repository));
192
+ const [p, downloads, stars] = await Promise.all([
193
+ packumentPromise, downloadsPromise, starsPromise
194
+ ]);
195
+ const created = p.time.created ?? "";
196
+ const updated = p.version !== "" ? (p.time[p.version] ?? "") : "";
197
+ const rank = Skills.computeRank(downloads, stars, created, updated);
198
+ return {
199
+ name,
200
+ version: p.version,
201
+ created,
202
+ updated,
203
+ repository: p.repository,
204
+ stars,
205
+ downloads,
206
+ rank
207
+ };
208
+ }));
209
+ /* sort by rank in descending order (best first) */
210
+ results.sort((a, b) => b.rank - a.rank);
211
+ return results;
212
+ }
213
+ else if (stack === "Java" || stack === "Kotlin") {
214
+ /* per coordinate: kick off Maven Central info and mvnrepository
215
+ downloads in parallel, then stars as soon as the POM-derived
216
+ repository is known; across coordinates everything runs
217
+ concurrently via Promise.all */
218
+ const results = await Promise.all(components.map(async (name) => {
219
+ const infoPromise = Skills.fetchMavenInfo(name);
220
+ const downloadsPromise = Skills.fetchMavenDownloads(name);
221
+ const starsPromise = infoPromise.then((i) => Skills.fetchStars(i.repository));
222
+ const [i, downloads, stars] = await Promise.all([
223
+ infoPromise, downloadsPromise, starsPromise
224
+ ]);
225
+ const rank = Skills.computeRank(downloads, stars, i.created, i.updated);
226
+ return {
227
+ name,
228
+ version: i.version,
229
+ created: i.created,
230
+ updated: i.updated,
231
+ repository: i.repository,
232
+ stars,
233
+ downloads,
234
+ rank
235
+ };
236
+ }));
237
+ /* sort by rank in descending order (best first) */
238
+ results.sort((a, b) => b.rank - a.rank);
239
+ return results;
240
+ }
241
+ else
85
242
  return [];
86
- /* per package: kick off packument and downloads in parallel,
87
- then stars as soon as the packument resolves; across packages
88
- everything runs concurrently via Promise.all */
89
- const results = await Promise.all(components.map(async (name) => {
90
- const packumentPromise = Skills.fetchPackument(name);
91
- const downloadsPromise = Skills.fetchDownloads(name);
92
- const starsPromise = packumentPromise.then((p) => Skills.fetchStars(p.repository));
93
- const [p, downloads, stars] = await Promise.all([
94
- packumentPromise, downloadsPromise, starsPromise
95
- ]);
96
- const created = p.time.created ?? "";
97
- const updated = p.version !== "" ? (p.time[p.version] ?? "") : "";
98
- const rank = Skills.computeRank(downloads, stars, created, updated);
99
- return {
100
- name,
101
- version: p.version,
102
- created,
103
- updated,
104
- repository: p.repository,
105
- stars,
106
- downloads,
107
- rank
108
- };
109
- }));
110
- /* sort by rank in descending order (best first) */
111
- results.sort((a, b) => b.rank - a.rank);
112
- return results;
113
243
  }
114
244
  /* compute composite rank score from weighted metrics:
115
245
  downloads x
116
246
  stars x
117
247
  ([lifespan =] (updated - created)) x
118
- ([recentness =] exp(-(now - updated) / halfLife)) */
248
+ ([recentness =] exp(-(now - updated) / halfLife))
249
+ `"N.A."` factors are treated as neutral `1` so that stacks for which
250
+ a particular metric is structurally unavailable (e.g. Maven Central
251
+ exposes no per-artifact download counts) can still be ranked by the
252
+ remaining metrics, instead of collapsing the entire product to zero. */
119
253
  static computeRank(downloads, stars, created, updated) {
120
- const d = typeof downloads === "number" ? downloads : 0;
121
- const s = typeof stars === "number" ? stars : 0;
254
+ const d = typeof downloads === "number" ? downloads : 1;
255
+ const s = typeof stars === "number" ? stars : 1;
122
256
  const cMs = created !== "" ? Date.parse(created) : NaN;
123
257
  const uMs = updated !== "" ? Date.parse(updated) : NaN;
124
258
  if (Number.isNaN(cMs) || Number.isNaN(uMs))
@@ -160,20 +294,26 @@ export class SkillsMCP {
160
294
  register(mcp) {
161
295
  mcp.registerTool("component_info", {
162
296
  title: "ASE component info",
163
- description: "Gather metadata for a list of NPM packages with maximum parallelism. " +
164
- "For each package, fetches the full registry packument via `pacote` " +
165
- "(in-process, no `npm view` subprocess), the GitHub `stargazers_count` " +
297
+ description: "Gather metadata for a list of packages with maximum parallelism, " +
298
+ "dispatching on the technology `stack`. For `JavaScript`/`TypeScript`, " +
299
+ "components are NPM package names: fetches the full registry packument via " +
300
+ "`pacote` (in-process, no `npm view` subprocess), the GitHub `stargazers_count` " +
166
301
  "from `api.github.com` (if the repository points to GitHub), and the " +
167
- "last-month downloads from `api.npmjs.org`. Returns a JSON `text` array " +
168
- "of `{ name, version, created, updated, repository, stars, downloads, rank }` " +
169
- "objects, sorted in descending order by `rank`. Failures of " +
170
- "individual side calls are isolated and reported as `\"N.A.\"` or empty " +
171
- "string so every entry has the full shape.",
302
+ "last-month downloads from `api.npmjs.org`. For `Java`/`Kotlin`, components " +
303
+ "are Maven coordinates of the form `groupId:artifactId`: fetches the latest " +
304
+ "version + earliest/latest release timestamps from the Maven Central Solr " +
305
+ "search endpoint, the SCM/project URL from the latest POM at `repo1.maven.org`, " +
306
+ "GitHub stars (if applicable), and the \"Used By\" count from `mvnrepository.com` " +
307
+ "as the `downloads` proxy. Returns a JSON `text` array of " +
308
+ "`{ name, version, created, updated, repository, stars, downloads, rank }` " +
309
+ "objects, sorted in descending order by `rank`. Failures of individual side " +
310
+ "calls are isolated and reported as `\"N.A.\"` or empty string so every entry " +
311
+ "has the full shape.",
172
312
  inputSchema: {
173
313
  stack: z.string()
174
314
  .describe("Technology stack: \"JavaScript\", \"TypeScript\", \"Java\", \"Kotlin\", or \"Unknown\""),
175
315
  components: z.array(z.string())
176
- .describe("List of package names to gather metadata for")
316
+ .describe("List of package names (NPM) or Maven coordinates `groupId:artifactId` (Java/Kotlin)")
177
317
  }
178
318
  }, async (args) => {
179
319
  try {
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.0.46",
9
+ "version": "0.0.48",
10
10
  "license": "GPL-3.0-only",
11
11
  "author": {
12
12
  "name": "Dr. Ralf S. Engelschall",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ase",
3
- "version": "0.0.46",
3
+ "version": "0.0.48",
4
4
  "description": "Agentic Software Engineering (ASE)",
5
5
  "keywords": [ "agentic", "software", "engineering" ],
6
6
  "homepage": "https://ase.tools",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ase",
3
- "version": "0.0.46",
3
+ "version": "0.0.48",
4
4
  "description": "Agentic Software Engineering (ASE)",
5
5
  "keywords": [ "agentic", "software", "engineering" ],
6
6
  "homepage": "https://ase.tools",
@@ -18,14 +18,14 @@ you *MUST* once and immediately output the following <template/> now:
18
18
  Prohibitions
19
19
  ------------
20
20
 
21
- - Do *not* factor out (move) code into own functions without good reason, like necessary reusability.
22
- - Do *not* use braces around single statement blocks in "if" and "while" constructs unless the language requires them.
21
+ - Do *not* factor out (move) code blocks into their own functions without good reason, such as necessary reusability.
22
+ - Do *not* use braces around single-statement blocks in "if" and "while" constructs unless the language requires them.
23
23
  - Do *not* insist on early "return" in "if" blocks if an "else" block exists.
24
- - Do *not* remove any whitespaces in the code formatting -- keep whitespaces aligned with code base.
24
+ - Do *not* remove any whitespace in the code formatting -- keep whitespace aligned with code base.
25
25
  - Do *not* show just partial code changes -- always show complete changes.
26
26
  - Do *not* produce any line which consists of just one or more space characters before the newline -- use just the newline.
27
27
  - Do *not* use semicolons where they are syntactically optional, except inside `for` clauses.
28
- - Do *not* split continuous chunks of code less than 100 lines into individual functions.
28
+ - Do *not* split continuous chunks of code fewer than 100 lines into individual functions.
29
29
  - Do *not* refactor deeply nested code constructs into individual functions.
30
30
  - Do *not* answer with the "You're absolutely right", "You are
31
31
  absolutely right", "You're absolutely correct", or "You are absolutely
@@ -41,16 +41,16 @@ Commandments
41
41
  - Be very *pedantic* on code style.
42
42
  - Before proposing any code changes, explain *WHAT* the proposed changes do and *WHY* it is necessary.
43
43
  - Propose *entire, complete, and necessary code change sets* for each solution.
44
- - Place a *blank line before a comment line*, but not when it is the first line of a block or an end of line comment.
44
+ - Place a *blank line before a comment line*, but not when it is the first line of a block or an end-of-line comment.
45
45
  - Keep code and comment *formatting exactly as in the existing code*.
46
46
  - Use *regular comments* `/* [...] */` instead of end-of-line comments `// [...]`.
47
47
  - Use *two leading/trailing spaces within comments* as in `/* [...] */`.
48
- - Always use *parenthesis around arrow function parameters*, even for a single parameter.
48
+ - Always use *parentheses around arrow function parameters*, even for a single parameter.
49
49
  - Make a line break before the keywords "else", "catch", and "finally".
50
50
  - Try to *vertically align similar operators* on consecutive, similar lines.
51
51
  - Place spaces after opening and before closing angle brackets and braces.
52
52
  - Use *double-quotes* (`"[...]"`) instead of single-quotes (`'[...]'`) for all strings.
53
- - Use K&R coding style with *opening braces* at end of lines and *closing braces* at the beginning of lines.
53
+ - Use K&R coding style with *opening braces* at the end of lines and *closing braces* at the beginning of lines.
54
54
  - When a language has a *more strongly-typed variant*, prefer that
55
55
  variant (e.g., TypeScript over JavaScript, Python with type hints
56
56
  over untyped Python).
@@ -14,7 +14,7 @@ Control Flow Constructs
14
14
  Do not output anything.
15
15
 
16
16
  - *IMPORTANT*: You *MUST* honor the following control flow construct:
17
- <expand name="<define-name/>" [arg1="<expand-arg1/>" [arg2="<expand-arg2/>]" [...]]]><expand-content/></expand>:
17
+ <expand name="<define-name/>" [arg1="<expand-arg1/>" [arg2="<expand-arg2/>" [...]]]><expand-content/></expand>:
18
18
 
19
19
  This specifies the *expansion* of previous <define/>.
20
20
  This construct is expanded into the <define-body/> of <define/>
@@ -79,7 +79,7 @@ Let the *user interactively choose* an answer.
79
79
  tool result `"<question-description/>"="<answer/>"`.
80
80
  Set <result><answer/></result>.
81
81
  If <result/> is then NOT one
82
- the "label" values from <config/>, set
82
+ of the "label" values from <config/>, set
83
83
  <result>OTHER: <result/></result>
84
84
  (prefix result with "OTHER").
85
85
 
@@ -109,7 +109,7 @@ Let the *user interactively choose* an answer.
109
109
  2. Call the `ask_user` tool of the agent harness with:
110
110
 
111
111
  `ask_user({
112
- question: "<question-label>: <question-description/>",
112
+ question: "<question-label/>: <question-description/>",
113
113
  allow_freeform: true,
114
114
  choices: [
115
115
  <config/>
@@ -80,7 +80,7 @@ rules even if later skill rules say different:
80
80
  </if>
81
81
 
82
82
  - <if condition="<ase-persona-style/> is 'caveman'">
83
- - You *MUST* use the terse, rough and stuttering communication style of a *caveman*.
83
+ - You *MUST* use the terse, rough, and stuttering communication style of a *caveman*.
84
84
  - Apply ruleset "level0": <expand name="level0"/>
85
85
  - Apply ruleset "level1": <expand name="level1"/>
86
86
  - Apply ruleset "level2": <expand name="level2"/>
@@ -61,10 +61,10 @@ You *MUST* honor the following hints on this *task plan* format:
61
61
  50 characters.
62
62
 
63
63
  - The sections `※ CHANGES` and `※ VERIFICATION` all are just a short
64
- list of 1-5 bullet points. Each bullet points is formatted as
64
+ list of 1-5 bullet points. Each bullet point is formatted as
65
65
  `- **<aspect/>**: <specification/>` where <aspect/> indicates
66
66
  the aspect of the section and <specification/> is 1-3 sentences
67
- giving a *ultra precise* but also *ultra brief* and *ultra concise*
67
+ giving an *ultra precise* but also *ultra brief* and *ultra concise*
68
68
  description of the aspect.
69
69
 
70
70
  - In all sections, break all lines with a newline character
@@ -15,7 +15,7 @@ Skill Output
15
15
  - *IMPORTANT*: *All* output is *exclusively* requested through
16
16
  <template/> sections. You *MUST* *NOT* output anything *EXCEPT* it
17
17
  is explicitly included in such a <template/> section. Especially,
18
- you *MUST* *NOT* output any explanations on your own, except
18
+ you *MUST* *NOT* output any explanations on your own, unless
19
19
  explicitly requested.
20
20
 
21
21
  - *IMPORTANT*: You *MUST* output all <template/> sections *EXACTLY* as provided
@@ -49,7 +49,7 @@ Skill Output
49
49
  padding, draw the top edge to that width, then keep every
50
50
  inner line (including annotations like `!`, `?`, `*`)
51
51
  within it. Count columns and verify before emitting; a
52
- one-space drift is a defect -— re-render.
52
+ one-space drift is a defect re-render.
53
53
 
54
54
  - *IMPORTANT*: For *Findings* (problems, tradeoffs, warnings
55
55
  emitted by analysis skills):
@@ -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.0.46",
9
+ "version": "0.0.48",
10
10
  "license": "GPL-3.0-only",
11
11
  "author": {
12
12
  "name": "Dr. Ralf S. Engelschall",
@@ -170,7 +170,7 @@ interface quality, quality attributes, and architecture governance.
170
170
  races and shared mutable state is covered by SA17.)
171
171
  - **SA09 COHESION**: *strong cohesion* within each component —
172
172
  internal parts (functions, fields, methods) are *tightly
173
- related*, *co-change*, and share data or behaviour; scattered
173
+ related*, *co-change*, and share data or behavior; scattered
174
174
  helpers that merely coexist by accident are flagged.
175
175
  - **SA10 EXTENSIBILITY**: components are *open for extension*
176
176
  (plugins, SPIs, hooks) but *closed for modification*.
@@ -266,7 +266,7 @@ interface quality, quality attributes, and architecture governance.
266
266
 
267
267
  - *Package-graph construction (SA19–SA21)*: parse each file's
268
268
  import (or equivalent language construct) statement, keep
269
- only first-party packages within the analysed scope, and
269
+ only first-party packages within the analyzed scope, and
270
270
  persist two intermediate structures: the per-package
271
271
  incoming/outgoing class-edge map (drives SA20, SA21) and
272
272
  the per-package noun-token frequency table extracted from
@@ -9,6 +9,7 @@ disable-model-invocation: false
9
9
  effort: medium
10
10
  allowed-tools:
11
11
  - "Bash(npm search --json *)"
12
+ - "Bash(curl -s https://search.maven.org/*)"
12
13
  - "Skill"
13
14
  - "Agent"
14
15
  ---
@@ -92,7 +93,7 @@ for the technology stack to *provide* the *needed functionality*
92
93
  official package name as <package-K/>, the latest version as
93
94
  <version-K/>, the stars as <stars-K/>, the created date as
94
95
  <created-K/>, the last updated date as <updated-K/>, the total
95
- number of downloads in the last month is <downloads-K/>.
96
+ number of downloads in the last month as <downloads-K/>.
96
97
 
97
98
  - If <stack/> is "JavaScript" or "TypeScript":
98
99
 
@@ -114,9 +115,19 @@ for the technology stack to *provide* the *needed functionality*
114
115
 
115
116
  - Based on the essential keywords <keyword-L/> (L=1-M),
116
117
  use the `ase-meta-search` skill in a subagent to *generally*
117
- discover an initial set of a maximum of 12 *Java packages*
118
+ discover an initial set of a maximum of 12 *Maven packages*
118
119
  <component-K/> and at least their real name <name-K/> and
119
- their unique package names <package-K/>.
120
+ their unique Maven coordinates <package-K/> of the form
121
+ `groupId:artifactId`.
122
+
123
+ - Use the shell command `curl -s 'https://search.maven.org/solrsearch/select?q=<keyword-1/>+<keyword-M/>&rows=12&wt=json'`
124
+ to *specifically* discover an additional set of a maximum
125
+ of 12 *Maven packages* <component-K/> and at least their
126
+ unique Maven coordinates <package-K/> (i.e. `<g/>:<a/>` from
127
+ each result document's `g` and `a` fields), based on the
128
+ essential keywords <keyword-L/> (L=1-M). Merge the results
129
+ into the already existing result set, but deduplicate
130
+ entries by Maven coordinate.
120
131
 
121
132
  - Call the `component_info(stack: <stack/>, components:
122
133
  [ <package-1/>, ..., <package-N/> ])` tool of the `ase` MCP
@@ -37,7 +37,7 @@ related to a set of code quality aspects.
37
37
  </template>
38
38
 
39
39
  Then decide whether you detected *potential problems* which
40
- *requires* a *code change* and *think* about this decision to be
40
+ *require* a *code change* and *think* about this decision to be
41
41
  sure it is *not* a false positive. Then choose one of the following
42
42
  cases:
43
43
 
@@ -183,8 +183,8 @@ related to a set of code quality aspects.
183
183
  </expand>
184
184
  </step>
185
185
 
186
- 9. <step id="A08 - COMPLICATENESS">
187
- <expand name="linter" arg1="A08 - COMPLICATENESS">
186
+ 9. <step id="A08 - COMPLICATEDNESS">
187
+ <expand name="linter" arg1="A08 - COMPLICATEDNESS">
188
188
  Check for complicated or cumbersome code constructs.
189
189
 
190
190
  Especially, check for unnecessarily difficult code constructs
@@ -206,7 +206,7 @@ permitted way to persist artifacts is via `task_save(...)`.
206
206
  - **Origin Proximity**:
207
207
  Problems for *obvious, particular, or expected* errors
208
208
  *should* be handled *near the origin*. Problems for
209
- *theoretical, fictive, or unexpected* errors, *should* be
209
+ *theoretical, fictive, or unexpected* errors *should* be
210
210
  handled more generally and in parent scopes.
211
211
 
212
212
  4. **Find Problem Resolution Approaches**:
@@ -3,14 +3,16 @@ name: ase-docs-proofread
3
3
  argument-hint: "<docs-reference>"
4
4
  description: >
5
5
  Analyze the documents for spelling, punctuation, or grammar errors.
6
- Use when the user wants to "spellcheck", "proofread", or "lint" a text or document.
6
+ Use when the user wants to "proofread" or "spellcheck" a document.
7
7
  user-invocable: true
8
8
  disable-model-invocation: false
9
- effort: high
9
+ effort: medium
10
10
  ---
11
11
 
12
12
  @${CLAUDE_SKILL_DIR}/../../meta/ase-control.md
13
13
  @${CLAUDE_SKILL_DIR}/../../meta/ase-skill.md
14
+ @${CLAUDE_SKILL_DIR}/../../meta/ase-dialog.md
15
+ @${CLAUDE_SKILL_DIR}/../../meta/ase-getopt.md
14
16
 
15
17
  Proofread Documentation
16
18
  =======================
@@ -19,6 +21,12 @@ Proofread Documentation
19
21
  Analyze the documents for spelling, punctuation, or grammar errors.
20
22
  </skill>
21
23
 
24
+ <expand name="getopt"
25
+ arg1="ase-docs-proofread"
26
+ arg2="--auto|-a">
27
+ $ARGUMENTS
28
+ </expand>
29
+
22
30
  <role>
23
31
  Your role is an experienced, *expert-level proofreader*, specialized in
24
32
  checking and correcting the *spelling*, *punctuation* and *grammar* of
@@ -26,93 +34,259 @@ documents.
26
34
  </role>
27
35
 
28
36
  <objective>
29
- *Analyze* the documents of $ARGUMENTS for problems in their *spelling*,
30
- *punctuation* or *grammar* and immediately correct all found problems.
37
+ *Analyze* the documents of `<getopt-arguments/>` for problems in their
38
+ *spelling*, *punctuation*, or *grammar* and propose corrections.
31
39
  </objective>
32
40
 
33
41
  <flow>
34
42
 
35
- 1. <step id="STEP 1: Investigation & Checking">
36
- Investigate on the document base by using the `Read` tool to read all
37
- referenced document files. For all contained texts in those files,
38
- check the following problem types *ONLY*:
43
+ 1. <step id="STEP 1: Investigation">
44
+
45
+ First, use the following <template/> to give a hint on this step:
46
+
47
+ <template>
48
+ &#x26AA; **PROOFREADING INVESTIGATION**
49
+ </template>
50
+
51
+ Then dispatch the investigation to a *sub-agent* via the `Agent`
52
+ tool so that *no* investigation details leak into the user-visible
53
+ transcript. The sub-agent performs the silent reading and checking;
54
+ only its final structured return value is consumed here.
55
+
56
+ Invoke the `Agent` tool *exactly once* with:
57
+
58
+ - `subagent_type`: `general-purpose`
59
+ - `description`: `Proofread Investigation`
60
+ - `prompt`: a *self-contained* briefing, instructing the sub-agent to:
61
+
62
+ 1. Use the `Read` tool to read all document files referenced
63
+ by <getopt-arguments/>.
64
+
65
+ 2. Check the contained texts *only* for the following problem
66
+ types:
67
+
68
+ - **Spelling**
69
+ - **Punctuation**
70
+ - **Grammar**
71
+
72
+ Do *NOT* flag stylistic preferences, Markdown formatting
73
+ choices, code/identifiers, XML/template tags, technical
74
+ terms, intentional capitalization, list/heading style, or
75
+ anything inside fenced code blocks or backtick spans. Be
76
+ conservative — only report clear, objective errors.
39
77
 
40
- - **Spelling**
41
- - **Punctuation**
42
- - **Grammar**
78
+ For each found problem:
43
79
 
44
- You *MUST* collect *all* found problems into an internal list only,
45
- and then set <problems/> to the value of this list.
80
+ - Set <type/> to the string `SPELLING`, `PUNCTUATION`, or
81
+ `GRAMMAR`, indicating the problem type.
46
82
 
47
- You *MUST* *NOT* propose document changes in this step 1.
48
- You *MUST* *NOT* output anything in this step 1.
83
+ - Set <file/> to the *relative* filename path of the document.
84
+ Set <line/> to the numeric 1-based line number in the
85
+ document.
86
+
87
+ - Set <old-text/> to the lines of the old text which
88
+ should be changed. Set <new-text/> to the lines of the
89
+ new text which will be changed.
90
+
91
+ - Set <description/> to an ultra-brief and concise
92
+ Markdown-formatted description of the problem with
93
+ a hint of what is wrong and why it is wrong. In
94
+ this description, mark up all referenced verbatim
95
+ words <words/> from <old-text/> or <new-text/> as
96
+ quoted strings containing monospaced text with
97
+ Markdown based on the following <template/>:
98
+ <template>"`<words/>`"</template>.
99
+
100
+ - Set <context-before/> to exactly *up to two* lines of
101
+ *unchanged* text context which occurs in the document
102
+ directly *before* <old-text/>, i.e., the lines (<line/> -
103
+ 2) and (<line/> - 1). Reduce to just one line (<line/> -
104
+ 1) if <old-text/> is the second line of the document. Set
105
+ <context-before/> to empty if <old-text/> is the first line in
106
+ the document.
107
+
108
+ - Set <context-after/> to exactly *up to two* lines of
109
+ *unchanged* text content which occurs in the document
110
+ directly *after* <old-text/> the lines (<line/> + <n/> + 1)
111
+ and (<line/> + <n/> + 2), where <n/> is the number of lines
112
+ in <old-text/>. Reduce to just one line (<line/> + <n/> + 1)
113
+ if <old-text/> is the second-last line of the document. Set
114
+ <context-after/> to empty if <old-text/> is the last line in
115
+ the document.
116
+
117
+ 3. Return *exclusively* a single fenced JSON block (no prose,
118
+ no preamble, no summary) of the following shape:
119
+
120
+ ```json
121
+ [
122
+ {
123
+ "type": <type/>,
124
+ "file": <file/>,
125
+ "line": <line/>,
126
+ "description": <description/>,
127
+ "context_before": <context-before/>,
128
+ "old_text": <old-text/>,
129
+ "new_text": <new-text/>,
130
+ "context_after": <context-after/>
131
+ },
132
+ [...]
133
+ ]
134
+ ```
135
+
136
+ 4. You *MUST* *NOT* propose, apply, or render any document
137
+ changes itself.
138
+
139
+ Parse the JSON array from the sub-agent's return value and set
140
+ <problems/> to that list.
141
+
142
+ You *MUST* *NOT* output anything at all in this step 1 beyond the
143
+ single `Agent` tool invocation.
49
144
  </step>
50
145
 
51
146
  2. <step id="STEP 2: Summary">
147
+
52
148
  Use the following <template/> to give a summary of the detected
53
149
  problems in <problems/>:
54
150
 
55
151
  <template>
152
+ &#x26AA; **PROOFREADING SUMMARY**:
153
+
56
154
  | *Proofread Type* | *Proofread Result* |
57
155
  | ---------------- | ----------------------- |
58
156
  | **SPELLING**: | **<n/>** problems found |
59
157
  | **PUNCTUATION**: | **<m/>** problems found |
60
158
  | **GRAMMAR**: | **<k/>** problems found |
159
+
61
160
  </template>
62
161
 
63
162
  Hints:
64
163
 
65
- - <n/> is the number of spelling problems in <problems/>
66
- - <m/> is the number of punctuation problems in <problems/>
67
- - <k/> is the number of grammar problems in <problems/>
164
+ - <n/> is the number of problems with `type` equal to `SPELLING` in <problems/>
165
+ - <m/> is the number of problems with `type` equal to `PUNCTUATION` in <problems/>
166
+ - <k/> is the number of problems with `type` equal to `GRAMMAR` in <problems/>
167
+
68
168
  </step>
69
169
 
70
- 3. <step id="STEP 3: Details & Correction">
170
+ 3. <step id="STEP 3: Correction">
171
+
172
+ 1. You *MUST* activate the auto-approve gate for the `Edit` tool
173
+ by setting the session-scoped `agent.skill` configuration value
174
+ to this skill's name via the MCP tool call `config_set(key:
175
+ "agent.skill", val: "ase-docs-proofread", scope:
176
+ "session:<ase-session-id/>")` of the `ase` service.
177
+
178
+ 2. Iterate over all problems:
179
+
180
+ <for items="<problems/>">
181
+
182
+ 1. Set <type/> to the `type` field of <item/>.
183
+ Set <file/> to the `file` field of <item/>.
184
+ Set <line/> to the `line` field of <item/>.
185
+ Set <description/> to the `description` field of <item/>.
186
+ Set <context-before/> to the `context_before` field of <item/>.
187
+ Set <old-text/> to the `old_text` field of <item/>.
188
+ Set <new-text/> to the `new_text` field of <item/>.
189
+ Set <context-after/> to the `context_after` field of <item/>.
190
+
191
+ 2. Report the problem with the following <template/>:
192
+
193
+ <template>
194
+ &#x1F7E0; **<type/> PROBLEM**: `<file/>`:<line/>:
195
+
196
+ <description/>
197
+ </template>
198
+
199
+ 3. <if condition="<getopt-option-auto/> is not 'true'">
200
+
201
+ Render the proposed correction as a *unified diff* with *one*
202
+ line of context in a fenced block based on the following <template/>:
203
+
204
+ <template>
205
+
206
+ &#x1F535; **<type/> CORRECTION**:
207
+
208
+ ```diff
209
+ --- <file/> (original)
210
+ +++ <file/> (corrected)
211
+ @@ -<line/>,<n/> +<line/>,<m/> @@
212
+ <context-before/>
213
+ -<old-text/>
214
+ +<new-text/>
215
+ <context-after/>
216
+ ```
217
+
218
+ </template>
219
+
220
+ Hints:
221
+ - The <n/> is the number of lines in <old-text/>.
222
+ - The <m/> is the number of lines in <new-text/>.
223
+
224
+ </if>
225
+
226
+ 4. <if condition="<getopt-option-auto/> is not 'true'">
227
+
228
+ Ask the user how to proceed via an interactive user dialog:
229
+
230
+ <expand name="user-dialog">
231
+ CORRECTION: How would you like to proceed with this proposed correction?
232
+ ACCEPT: Apply the proposed correction.
233
+ REFINE: Discard this proposed correction and generate a new one.
234
+ REJECT: Skip this proposed correction.
235
+ </expand>
236
+
237
+ </if>
238
+
239
+ <if condition="<getopt-option-auto/> is 'true'">
240
+
241
+ Set <result>ACCEPT</result>.
242
+
243
+ </if>
244
+
245
+ 5. Check <result/> and dispatch accordingly:
71
246
 
72
- <for items="<problems/>">
247
+ - <if condition="<result/> is 'ACCEPT'">
73
248
 
74
- For the current <item/> from the collected list <problems/>, do the
75
- following:
249
+ Invoke the `Edit` tool to apply the change exactly as shown
250
+ in the diff. The operation will be auto-approved because of
251
+ the active proofread marker, so *no* interactive permission
252
+ prompt will appear. Then continue with the next <item/>.
76
253
 
77
- 1. Report the problem with the following <template/>:
254
+ </if>
78
255
 
79
- <template>
80
- &#x1F7E0; **<type/>**: [`<file/>`:<line/>]: <description/>
81
- </template>
256
+ - <if condition="
257
+ <result/> starts with 'REFINE' or
258
+ <result/> starts with 'OTHER'
259
+ ">
82
260
 
83
- Hints:
261
+ Generate a *new* proposal for the *same* <item/>
262
+ (incorporating the user's free-text hint from <result/> if
263
+ provided via the "OTHER" prefix) and loop back to substep 2
264
+ of this iteration. There is *no* cap on refinement rounds —
265
+ keep refining until the user picks `ACCEPT` or `REJECT`.
84
266
 
85
- - The <type/> is `SPELLING`, `PUNCTUATION` or `GRAMMAR`
86
- of <item/>.
267
+ </if>
87
268
 
88
- - The <file/> is the name of the document artifact of <item/>.
89
- The <line/> is the line number of the document artifact of <item/>.
269
+ - <if condition="
270
+ <result/> is 'REJECT' or
271
+ <result/> is 'CANCEL' or
272
+ <result/> starts with 'ERROR'
273
+ ">
90
274
 
91
- - The <description/> is an ultra brief and concise description
92
- of the problem <item/> with a hint what is wrong and why
93
- it is wrong. In <description/>, markup all verbatim words
94
- <words/> related to the checked text (or the proposed
95
- corrected text) as quoted strings containing monospaced text
96
- with the Markdown <template>"`<words/>`"</template>.
275
+ Skip this <item/> without any `Edit` call and continue
276
+ with the next <item/>.
97
277
 
98
- 2. Propose a direct change to the corresponding document via the
99
- interactive `Edit` tool.
278
+ </if>
100
279
 
101
- **IMPORTANT**: Regardless of whether the change was *accepted*
102
- or *rejected* by the user (i.e., even if the `Edit` tool returns
103
- an error like "The user doesn't want to proceed with this tool
104
- use" or "User rejected"), you *MUST* immediately *CONTINUE* with
105
- the *next* <item/> in the iteration.
280
+ </for>
106
281
 
107
- Do *NOT* stop. Do *NOT* ask the user for confirmation. Do *NOT*
108
- summarize. A rejection is *NOT* a signal to abort; it is only
109
- a signal that *this one* change is skipped. Only after *all*
110
- problems have been processed may you stop.
282
+ 3. After the iteration has finished, you *MUST* clear the auto-approve
283
+ gate via the call MCP tool call `config_delete(key:
284
+ "agent.skill", scope: "session:<ase-session-id/>")` of the `ase`
285
+ service.
111
286
 
112
- </for>
287
+ 4. You *MUST* *NOT* output any further additional explanations or
288
+ summaries at the end of this skill processing.
113
289
 
114
- You *MUST* *NOT* output any further additional explanations or
115
- summaries at the end of this skill processing.
116
290
  </step>
117
291
 
118
292
  </flow>
@@ -66,7 +66,7 @@ are also grouped and sorted according to the above <prefix/>es.
66
66
  </step>
67
67
 
68
68
  3. <step id="STEP 3: Complete ChangeLog entries">
69
- Without immediately modifying `CHANGELOG.md` file,
69
+ Without immediately modifying the `CHANGELOG.md` file,
70
70
  *complete* the entries in the first (most recent) section only,
71
71
  by adding the corresponding (most recent) Git commits only.
72
72
  For each Git commit, reduce the Git commit messages to a single
@@ -74,17 +74,17 @@ are also grouped and sorted according to the above <prefix/>es.
74
74
  </step>
75
75
 
76
76
  4. <step id="STEP 4: Consolidate ChangeLog entries">
77
- Without immediately modifying `CHANGELOG.md` file,
77
+ Without immediately modifying the `CHANGELOG.md` file,
78
78
  *consolidate* the entries in the first (most recent) section only,
79
79
  by summarizing and merging closely related entries.
80
80
  Perform the entry consolidation per prefix group only.
81
- If a changelog <summary/> is too short or is too little comprehensible
81
+ If a changelog <summary/> is too short or is not comprehensible enough
82
82
  because of too little context, add some context, especially references
83
83
  to the class/module/package, etc.
84
84
  </step>
85
85
 
86
86
  5. <step id="STEP 5: Sort ChangeLog entries">
87
- Without immediately modifying `CHANGELOG.md` file,
87
+ Without immediately modifying the `CHANGELOG.md` file,
88
88
  *sort* the entries in the first (most recent) section only.
89
89
  Instead of the chronological commit order, group the entries
90
90
  by the prefixes.
@@ -5,9 +5,9 @@ description: >
5
5
  *Always use* when you have to *visualize*
6
6
  structure/layout/components/dependencies as Flowchart,
7
7
  control-flow/branching/concurrency as Flowchart,
8
- state-machine/states/transitions as an UML State Diagram,
9
- data-flow/actors/messages/protocols as an UML Sequence Diagram,
10
- data-structure/classes/methods as an UML Class Diagram,
8
+ state-machine/states/transitions as a UML State Diagram,
9
+ data-flow/actors/messages/protocols as a UML Sequence Diagram,
10
+ data-structure/classes/methods as a UML Class Diagram,
11
11
  data-model/entities/relationships as an ER Diagram, or
12
12
  metrics/distributions/time-series as XY-Charts.
13
13
  user-invocable: false
@@ -71,7 +71,7 @@ Rules
71
71
  In other words, after the `diagram` tool call completes, the
72
72
  skill *MUST* copy the tool's `text` result *verbatim* into a
73
73
  Markdown-fenced code block (triple backticks), directly placed
74
- in the response text immediately after the MCP tool call -— the
74
+ in the response text immediately after the MCP tool call the
75
75
  user reads the Markdown fenced block in the response, not the
76
76
  (truncated) tool call display. Emitting only the tool call without
77
77
  the reproduction of the `text` output is a defect: the diagram is
@@ -95,7 +95,7 @@ Rules
95
95
  For *comparison diagrams* (e.g., *current vs. proposed*, *before
96
96
  vs. after*), render each side as a *separate* Mermaid diagram
97
97
  specification via the `diagram` tool from the `ase` MCP service, and
98
- then stack the two rendered blocks *vertically* -— each preceded by
98
+ then stack the two rendered blocks *vertically* each preceded by
99
99
  a bold label (`**BEFORE:**` / `**AFTER:**`, or similar). Do *not*
100
100
  attempt side-by-side layout.
101
101
 
@@ -121,7 +121,7 @@ multi-*criteria* decision matrix.
121
121
  important, over normal, to less important). Do not output anything.
122
122
 
123
123
  - Ensure the final number of criteria is always within the range of
124
- minimum 8 and maximum 12: if less than 8 criteria were requested,
124
+ minimum 8 and maximum 12: if fewer than 8 criteria were requested,
125
125
  use the set of alternatives to decide on additional criteria
126
126
  which potentially allow best to triage the alternatives, take the
127
127
  <reason/> into account, and use the `ase-meta-search` skill (drawing from