@rse/ase 0.9.13 → 0.9.14

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-kv.js CHANGED
@@ -45,10 +45,21 @@ export class KV {
45
45
  KV.validateKey(key);
46
46
  return KV.store.delete(key);
47
47
  }
48
- /* clear all keys; returns the number of keys removed */
49
- static clear() {
50
- const n = KV.store.size;
51
- KV.store.clear();
48
+ /* clear all keys, or just those starting with the given `prefix`;
49
+ returns the number of keys removed */
50
+ static clear(prefix) {
51
+ if (prefix === undefined || prefix === "") {
52
+ const n = KV.store.size;
53
+ KV.store.clear();
54
+ return n;
55
+ }
56
+ let n = 0;
57
+ for (const key of KV.store.keys()) {
58
+ if (key.startsWith(prefix)) {
59
+ KV.store.delete(key);
60
+ n++;
61
+ }
62
+ }
52
63
  return n;
53
64
  }
54
65
  /* snapshot the entire store into a fresh map (for transactional batch) */
@@ -112,12 +123,17 @@ export class KVMCP {
112
123
  /* key/value clear */
113
124
  mcp.registerTool("ase_kv_clear", {
114
125
  title: "ASE key/value clear",
115
- description: "Remove all keys from the in-memory key/value store. " +
126
+ description: "Remove keys from the in-memory key/value store. " +
127
+ "If `prefix` is given, only keys starting with `prefix` are removed; " +
128
+ "otherwise all keys are removed. " +
116
129
  "Returns a status `text` indicating how many keys were removed.",
117
- inputSchema: {}
118
- }, async () => {
130
+ inputSchema: {
131
+ prefix: z.string().optional()
132
+ .describe("if given, only remove keys starting with this prefix (otherwise remove all keys)")
133
+ }
134
+ }, async (args) => {
119
135
  try {
120
- const n = KV.clear();
136
+ const n = KV.clear(args.prefix);
121
137
  return { content: [{ type: "text", text: `kv_clear: OK: removed ${n} key(s)` }] };
122
138
  }
123
139
  catch (err) {
@@ -151,8 +167,9 @@ export class KVMCP {
151
167
  mcp.registerTool("ase_kv_batch", {
152
168
  title: "ASE key/value batch",
153
169
  description: "Execute an array of in-memory key/value `commands` in a single MCP call. " +
154
- "Each entry is an object `{ command: \"clear\"|\"set\"|\"get\"|\"delete\", key?, val? }` " +
170
+ "Each entry is an object `{ command: \"clear\"|\"set\"|\"get\"|\"delete\", key?, val?, prefix? }` " +
155
171
  "and is dispatched to the corresponding single-op tool. " +
172
+ "For `clear`, an optional `prefix` restricts removal to keys starting with it. " +
156
173
  "If `transactional` is true, the store is snapshotted up-front and rolled back on the " +
157
174
  "first per-command error (remaining commands are skipped); otherwise per-command errors " +
158
175
  "are recorded and execution continues. " +
@@ -168,7 +185,9 @@ export class KVMCP {
168
185
  key: z.string().optional()
169
186
  .describe("key identifier (required for `set`/`get`/`delete`)"),
170
187
  val: z.union([z.string(), z.number(), z.boolean(), z.null(), z.array(z.any()), z.record(z.string(), z.any())]).optional()
171
- .describe("value to store (required for `set`)")
188
+ .describe("value to store (required for `set`)"),
189
+ prefix: z.string().optional()
190
+ .describe("if given for `clear`, only remove keys starting with this prefix (otherwise remove all keys)")
172
191
  }))
173
192
  .describe("ordered list of KV commands to execute"),
174
193
  transactional: z.boolean().optional()
@@ -182,7 +201,7 @@ export class KVMCP {
182
201
  for (const c of args.commands) {
183
202
  try {
184
203
  if (c.command === "clear") {
185
- const n = KV.clear();
204
+ const n = KV.clear(c.prefix);
186
205
  results.push(`kv_clear: OK: removed ${n} key(s)`);
187
206
  cmdNames.push("clear");
188
207
  }
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.13",
9
+ "version": "0.9.14",
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.9.13",
3
+ "version": "0.9.14",
4
4
  "description": "Agentic Software Engineering (ASE)",
5
5
  "keywords": [ "agentic", "software", "engineering" ],
6
6
  "homepage": "https://ase.tools",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ase",
3
- "version": "0.9.13",
3
+ "version": "0.9.14",
4
4
  "description": "Agentic Software Engineering (ASE)",
5
5
  "keywords": [ "agentic", "software", "engineering" ],
6
6
  "homepage": "https://ase.tools",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ase",
3
- "version": "0.9.13",
3
+ "version": "0.9.14",
4
4
  "description": "Agentic Software Engineering (ASE)",
5
5
  "keywords": [ "agentic", "software", "engineering" ],
6
6
  "homepage": "https://ase.tools",
@@ -13,9 +13,9 @@ tools:
13
13
 
14
14
  @../meta/ase-control.md
15
15
 
16
- 1. Treat `$ARGUMENTS` as a single whitespace-separated string.
17
- Set <llm/> to the *first* token.
18
- Set <query/> to the *second and all following* tokens.
16
+ 1. Set <args>$ARGUMENTS</args>, the single whitespace-separated string.
17
+ Set <llm/> to the *first* token of <args/>.
18
+ Set <query/> to the *second and all following* tokens of <args/>.
19
19
  You *MUST* *NOT* output anything related to this step.
20
20
 
21
21
  2. Set <server></server> (set to empty).
@@ -96,7 +96,8 @@ set placeholders into the context as a side-effect.
96
96
  `<getopt-result/>.opts[<longN/>]`.
97
97
  Set <getopt-arguments/> to the value of `<getopt-result/>.args`.
98
98
  Set <getopt-info/> to `<getopt-result/>.info`, but remove
99
- information about the `help` option.
99
+ information about the `help` option and any *internal* option
100
+ whose long name starts with `int-`.
100
101
 
101
102
  7. **Display Results**:
102
103
  Just output the following <template/>:
@@ -250,8 +250,9 @@ Template Patterns
250
250
  - When `<ase-tpl-head title="<title/>"/>` should be expanded, use
251
251
  (where <raw-title/> is the visible un-styled text `⧉ ASE: <title/>`,
252
252
  <raw-title-len/> is the number of characters in <raw-title/>, and
253
- <bar/> is the `─` character repeated exactly (67 - <raw-title-len/>)
254
- times -- the very same bar-width rule as `<ase-tpl-foot/>` and
253
+ <bar/> is the `─` character repeated exactly max(0, 67 - <raw-title-len/>)
254
+ times -- clamped to zero so an over-long title never yields a negative
255
+ count -- the very same bar-width rule as `<ase-tpl-foot/>` and
255
256
  `<ase-tpl-boxed/>`, so equal visible text yields equal total width):
256
257
 
257
258
  <template>
@@ -271,8 +272,9 @@ Template Patterns
271
272
  - When `<ase-tpl-foot title="<title/>"/>` should be expanded, use
272
273
  (where <raw-title/> is the visible un-styled text `⧉ ASE: <title/>`,
273
274
  <raw-title-len/> is the number of characters in <raw-title/>, and
274
- <bar/> is the `─` character repeated exactly (67 - <raw-title-len/>)
275
- times -- the very same bar-width rule as `<ase-tpl-head/>` and
275
+ <bar/> is the `─` character repeated exactly max(0, 67 - <raw-title-len/>)
276
+ times -- clamped to zero so an over-long title never yields a negative
277
+ count -- the very same bar-width rule as `<ase-tpl-head/>` and
276
278
  `<ase-tpl-boxed/>`, so equal visible text yields equal total width):
277
279
 
278
280
  <template>
@@ -308,8 +310,9 @@ Template Patterns
308
310
  </else>
309
311
  - Set <raw-title-len/> to the number of characters in the visible
310
312
  un-styled text <raw-title/>.
311
- - Set <bar/> to the `─` character repeated exactly (67 - <raw-title-len/>)
312
- times -- the very same bar-width rule as `<ase-tpl-head/>` and
313
+ - Set <bar/> to the `─` character repeated exactly max(0, 67 - <raw-title-len/>)
314
+ times -- clamped to zero so an over-long title never yields a negative
315
+ count -- the very same bar-width rule as `<ase-tpl-head/>` and
313
316
  `<ase-tpl-foot/>`, so equal visible text yields equal total width.
314
317
  - Set <body/> to <content/> with all line-starts prefixed with `│ `.
315
318
 
@@ -348,7 +351,8 @@ Template Patterns
348
351
  <template>🟠</template>
349
352
 
350
353
  - When `<ase-tpl-pad width="<width/>" text="<text/>"/>` should be expanded, use
351
- (where <ws/> = ` ` x (<width/> - length("<text/>")), i.e., <ws/> is
352
- the ` ` character repeated (<width/> - length("<text/>")) times):
354
+ (where <ws/> = ` ` x max(0, <width/> - length("<text/>")), i.e., <ws/> is
355
+ the ` ` character repeated max(0, <width/> - length("<text/>")) times --
356
+ clamped to zero so an over-long text never yields a negative count):
353
357
 
354
358
  <template><text/><ws/></template>
@@ -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.13",
9
+ "version": "0.9.14",
10
10
  "license": "GPL-3.0-only",
11
11
  "author": {
12
12
  "name": "Dr. Ralf S. Engelschall",
@@ -399,9 +399,13 @@ interface quality, quality attributes, and architecture governance.
399
399
  DEPENDENCY-DIRECTION`).
400
400
 
401
401
  - The <focal-aspect/> is the aspect that participates in
402
- *all* tensions of the cluster. For a size-2 cluster, pick
403
- the aspect whose direction is most constrained by the
404
- detected style.
402
+ *all* tensions of the cluster. In a size-2 cluster both
403
+ aspects participate equally in the single tension, so this
404
+ rule cannot disambiguate; instead pick the focal aspect by
405
+ the first applicable tiebreaker: (1) the aspect whose
406
+ direction is more constrained by the detected style; else
407
+ (2) the aspect carrying the *higher* finding severity; else
408
+ (3) the aspect listed *first* in the tension matrix pair.
405
409
 
406
410
  - *Brevity and precision*: all free-form placeholders
407
411
  (<description/>, <focal-state/>, partner-implications)
@@ -438,9 +442,11 @@ interface quality, quality attributes, and architecture governance.
438
442
  - *Additionally*, persist all reported findings in a *single*
439
443
  `ase_kv_batch` call to the `ase` MCP server with `transactional`
440
444
  set to `true`. The `commands` parameter array of this call
441
- starts with one `{ command: "clear" }` entry, followed by one
442
- `{ command: "set", key: "ase-issue-P<n/>", val: "<title/>:
443
- <description/>" }` entry per reported PROBLEM and one
445
+ starts with one `{ command: "clear", prefix: "ase-issue-" }`
446
+ entry (which removes only the previously persisted `ase-issue-*`
447
+ keys, leaving any unrelated keys in the shared store intact),
448
+ followed by one `{ command: "set", key: "ase-issue-P<n/>", val:
449
+ "<title/>: <description/>" }` entry per reported PROBLEM and one
444
450
  `{ command: "set", key: "ase-issue-T<n/>", val: "<title/>:
445
451
  <description/>" }` entry per reported TRADEOFF.
446
452
  </step>
@@ -115,8 +115,16 @@ for the technology stack to *provide* the *needed functionality*
115
115
  <keyword-L/> (L=1-M), which allow you to search for suitable
116
116
  components.
117
117
 
118
- 3. In the to be discovered result set of components <component-K/>
119
- (K=1-N), remember the component name as <name-K/>, the
118
+ 3. Determine the *candidate pool size* <pool/> as twice the
119
+ <getopt-option-limit/> (i.e. <pool/> = 2 &times;
120
+ <getopt-option-limit/>). Each discovery source below may fetch up
121
+ to <pool/> candidates so that the later ranking and trimming
122
+ (which alone is governed by <getopt-option-limit/>) has a
123
+ meaningful set to choose from.
124
+
125
+ In the to be discovered candidate set of components <component-K/>
126
+ (K=1-C, where C is the merged and deduplicated candidate count),
127
+ remember the component name as <name-K/>, the
120
128
  official package name as <package-K/>, the latest version as
121
129
  <version-K/>, the stars as <stars-K/>, the created date as
122
130
  <created-K/>, the last updated date as <updated-K/>, the total
@@ -126,13 +134,13 @@ for the technology stack to *provide* the *needed functionality*
126
134
 
127
135
  1. Based on the essential keywords <keyword-L/> (L=1-M),
128
136
  use the `ase-meta-search` skill in a subagent to *generally*
129
- discover an initial set of a maximum of <getopt-option-limit/> *NPM packages*
137
+ discover an initial set of a maximum of <pool/> *NPM packages*
130
138
  <component-K/> and at least their real name <name-K/> and
131
139
  their unique package names <package-K/>.
132
140
 
133
- 2. Use the shell command `npm search --json --searchlimit <getopt-option-limit/>
141
+ 2. Use the shell command `npm search --json --searchlimit <pool/>
134
142
  "<keyword-1/>" [...] "<keyword-M/>"` to *specifically*
135
- discover an additional set of a maximum of <getopt-option-limit/> *NPM packages*
143
+ discover an additional set of a maximum of <pool/> *NPM packages*
136
144
  <component-K/> and at least their unique package names
137
145
  <package-K/>, based on the essential keywords <keyword-L/>
138
146
  (L=1-M). Merge the results into the already existing result
@@ -142,14 +150,14 @@ for the technology stack to *provide* the *needed functionality*
142
150
 
143
151
  1. Based on the essential keywords <keyword-L/> (L=1-M),
144
152
  use the `ase-meta-search` skill in a subagent to *generally*
145
- discover an initial set of a maximum of <getopt-option-limit/> *Maven packages*
153
+ discover an initial set of a maximum of <pool/> *Maven packages*
146
154
  <component-K/> and at least their real name <name-K/> and
147
155
  their unique Maven coordinates <package-K/> of the form
148
156
  `groupId:artifactId`.
149
157
 
150
- 2. Use the shell command `curl -s 'https://search.maven.org/solrsearch/select?q=<keyword-1/>+[...]+<keyword-M/>&rows=<getopt-option-limit/>&wt=json'`
158
+ 2. Use the shell command `curl -s 'https://search.maven.org/solrsearch/select?q=<keyword-1/>+[...]+<keyword-M/>&rows=<pool/>&wt=json'`
151
159
  to *specifically* discover an additional set of a maximum
152
- of <getopt-option-limit/> *Maven packages* <component-K/> and at least their
160
+ of <pool/> *Maven packages* <component-K/> and at least their
153
161
  unique Maven coordinates <package-K/> (i.e. `<g/>:<a/>` from
154
162
  each result document's `g` and `a` fields), based on the
155
163
  essential keywords <keyword-L/> (L=1-M). Merge the results
@@ -157,22 +165,25 @@ for the technology stack to *provide* the *needed functionality*
157
165
  entries by Maven coordinate.
158
166
 
159
167
  6. Call the `ase_component_info(stack: "<stack/>", components:
160
- [ "<package-1/>", ..., "<package-N/>" ])` tool of the `ase` MCP
161
- server *once* for the entire set of discovered packages.
168
+ [ "<package-1/>", ..., "<package-C/>" ])` tool of the `ase` MCP
169
+ server *once* for the entire candidate set of discovered packages.
162
170
  The tool dispatches internally on <stack/> and fetches all
163
171
  metadata in maximum parallel and returns an array of objects `{
164
172
  name, version, created, updated, repository, stars, downloads,
165
173
  rank }`. For each
166
- component <component-K/> (K=1-N) read from its corresponding
174
+ component <component-K/> (K=1-C) read from its corresponding
167
175
  entry: <version-K/> from `version`, <updated-K/> from `updated`,
168
176
  <created-K/> from `created`, <repository-K/> from `repository`,
169
177
  <stars-K/> from `stars` (numeric or `N.A.`), <downloads-K/>
170
178
  from `downloads` (numeric or `N.A.`) and <rank-K/> from `rank`
171
179
  (numeric).
172
180
 
173
- 7. Sort, in descending order, the discovered components
174
- <component-K/> (K=1-N) by their `rank` field and trim the result
175
- list to just a maximum of <getopt-option-limit/> total components.
181
+ 7. Sort, in descending order, the discovered candidate components
182
+ <component-K/> (K=1-C) by their `rank` field and trim the result
183
+ list to just a maximum of <getopt-option-limit/> total components,
184
+ which establishes the final retained set <component-K/> (K=1-N,
185
+ where N is at most <getopt-option-limit/>). All subsequent steps
186
+ operate solely on this retained set.
176
187
 
177
188
  8. For each component <component-K/> (K=1-N), research and then
178
189
  decide which *one* of *USP* (Unique Selling Point -- what makes
@@ -151,8 +151,11 @@ problems in *performance* and *efficiency*, or problems in *security*.
151
151
 
152
152
  Then renumber the surviving problems contiguously as `P<n/>` with
153
153
  <n/> = 1, 2, ... in the original ordering. If *all* problems are
154
- dropped, skip the per-problem report but still emit the final hint
155
- <template/> below.
154
+ dropped, skip the per-problem report but still purge any stale
155
+ persisted problems with a *single* `ase_kv_batch` call to the `ase`
156
+ MCP server with `transactional` set to `true` and a `commands`
157
+ parameter array holding exactly one `{ command: "clear" }` entry,
158
+ and still emit the final hint <template/> below.
156
159
 
157
160
  In this STEP 3, for *EVERY* surviving problem, immediately report
158
161
  it with the following output <template/>, based on concise bullet
@@ -234,9 +237,11 @@ problems in *performance* and *efficiency*, or problems in *security*.
234
237
  - *Additionally*, persist all reported problems in a *single*
235
238
  `ase_kv_batch` call to the `ase` MCP server with `transactional`
236
239
  set to `true`. The `commands` parameter array of this call
237
- starts with one `{ command: "clear" }` entry, followed by one
238
- `{ command: "set", key: "ase-issue-P<n/>", val: "<title/>:
239
- <description/>" }` entry per reported problem.
240
+ starts with one `{ command: "clear", prefix: "ase-issue-" }`
241
+ entry (which removes only the previously persisted `ase-issue-*`
242
+ keys, leaving any unrelated keys in the shared store intact),
243
+ followed by one `{ command: "set", key: "ase-issue-P<n/>", val:
244
+ "<title/>: <description/>" }` entry per reported problem.
240
245
 
241
246
  Finally, output the following <template/> to give a final hint:
242
247
 
@@ -107,6 +107,16 @@ related to a set of code quality aspects.
107
107
  "ase-code-lint", scope: "session:<ase-session-id/>")` tool
108
108
  from the `ase` MCP server. Do not output anything in this substep.
109
109
 
110
+ *Critical safety invariant*: the marker set here grants `Edit`
111
+ auto-approval and *MUST* be cleared again (substep 3 below)
112
+ *before* this skill yields control, *regardless* of how the
113
+ iteration in substep 2 ends - whether it completes normally,
114
+ is aborted early (e.g. an `Edit` failure, an unparseable value,
115
+ or any other unexpected condition), or is otherwise interrupted.
116
+ If you ever stop or bail out of substep 2 early, you *MUST*
117
+ still perform substep 3 first. Never leave this marker active
118
+ for a later, unrelated `Edit`.
119
+
110
120
  2. Iterate over all problems:
111
121
 
112
122
  <for items="<problems/>">
@@ -143,16 +153,46 @@ related to a set of code quality aspects.
143
153
  Set <new-text/> to the `new_text` field of <item/>.
144
154
  Set <context-after/> to the `context_after` field of <item/>.
145
155
 
146
- 2. Set <old-start/> to the line of the first hunk line, i.e.,
147
- the line of <context-before/> (one before <line/>).
148
- Set <new-start/> to the same as <old-start/> (the unchanged
149
- <context-before/> line shares the same start in both files).
150
- Set <old-count/> to the total number of old-side hunk lines, i.e.,
151
- the number of lines in <context-before/>, <old-text/>, and
152
- <context-after/> combined.
153
- Set <new-count/> to the total number of new-side hunk lines, i.e.,
154
- the number of lines in <context-before/>, <new-text/>, and
155
- <context-after/> combined.
156
+ 2. Determine the hunk *body* as an ordered list of lines,
157
+ each carrying a one-character prefix (` ` for context,
158
+ `-` for old-side, `+` for new-side). Build it by
159
+ concatenating, in order and *skipping any part that is
160
+ empty*:
161
+
162
+ - one ` `-prefixed line for *each* line of
163
+ <context-before/> (if non-empty),
164
+ - one `-`-prefixed line for *each* line of <old-text/>
165
+ (if non-empty; split <old-text/> on newlines),
166
+ - one `+`-prefixed line for *each* line of <new-text/>
167
+ (if non-empty; split <new-text/> on newlines),
168
+ - one ` `-prefixed line for *each* line of
169
+ <context-after/> (if non-empty).
170
+
171
+ Set <hunk-body/> to those prefixed lines joined by
172
+ newlines.
173
+
174
+ Set <old-count/> to the number of old-side hunk lines,
175
+ i.e., the combined line count of <context-before/>,
176
+ <old-text/>, and <context-after/> (each empty part
177
+ counts as `0`).
178
+ Set <new-count/> to the number of new-side hunk lines,
179
+ i.e., the combined line count of <context-before/>,
180
+ <new-text/>, and <context-after/> (each empty part
181
+ counts as `0`).
182
+
183
+ Set <old-start/> to the 1-based line number of the
184
+ *first* old-side hunk line: if <context-before/> is
185
+ non-empty, that is its line (one before <line/>);
186
+ otherwise it is <line/> itself (the first line of
187
+ <old-text/>). For a hunk that *only inserts* new lines
188
+ (empty <old-text/> *and* empty context), set it to the
189
+ line *before* which the insertion happens, clamped to a
190
+ minimum of `0`, so a top-of-file insertion yields
191
+ `@@ -0,0 ... @@`.
192
+ Set <new-start/> to the same value as <old-start/>, but
193
+ clamped to a minimum of `1` whenever <new-count/> is
194
+ greater than `0` (the corrected side then has a real
195
+ first line).
156
196
 
157
197
  3. If <context/> is not empty, set
158
198
  <context><context/>,</context> (append a comma).
@@ -160,14 +200,13 @@ related to a set of code quality aspects.
160
200
 
161
201
  <template>`<file/>`:<line/></template>
162
202
 
163
- 4. Append the following <template/> to <diff-file/>:
203
+ 4. Append the following <template/> to <diff-file/>,
204
+ emitting <hunk-body/> verbatim (one already-prefixed
205
+ line per line, with no extra blank or space-only lines):
164
206
 
165
207
  <template>
166
208
  @@ -<old-start/>,<old-count/> +<new-start/>,<new-count/> @@
167
- <context-before/>
168
- -<old-text/>
169
- +<new-text/>
170
- <context-after/>
209
+ <hunk-body/>
171
210
  </template>
172
211
 
173
212
  </for>
@@ -96,6 +96,16 @@ Analyze documents for spelling, punctuation, or grammar errors
96
96
  "ase-docs-proofread", scope: "session:<ase-session-id/>")` tool
97
97
  from the `ase` MCP server. Do not output anything in this substep.
98
98
 
99
+ *Critical safety invariant*: the marker set here grants `Edit`
100
+ auto-approval and *MUST* be cleared again (substep 3 below)
101
+ *before* this skill yields control, *regardless* of how the
102
+ iteration in substep 2 ends - whether it completes normally,
103
+ is aborted early (e.g. an `Edit` failure, an unparseable value,
104
+ or any other unexpected condition), or is otherwise interrupted.
105
+ If you ever stop or bail out of substep 2 early, you *MUST*
106
+ still perform substep 3 first. Never leave this marker active
107
+ for a later, unrelated `Edit`.
108
+
99
109
  2. Iterate over all problems:
100
110
 
101
111
  <for items="<problems/>">
@@ -119,19 +129,44 @@ Analyze documents for spelling, punctuation, or grammar errors
119
129
 
120
130
  3. <if condition="<getopt-option-auto/> is not 'true'">
121
131
 
122
- Set <old-start/> to the line of the first hunk line, i.e.,
123
- the line of <context-before/> (one before <line/>).
124
- Set <new-start/> to the same as <old-start/> (the unchanged
125
- <context-before/> line shares the same start in both files).
126
- Set <old-count/> to the total number of old-side hunk lines, i.e.,
127
- the number of lines in <context-before/>, <old-text/>, and
128
- <context-after/> combined.
129
- Set <new-count/> to the total number of new-side hunk lines, i.e.,
130
- the number of lines in <context-before/>, <new-text/>, and
131
- <context-after/> combined.
132
+ Determine the hunk *body* as an ordered list of lines, each
133
+ carrying a one-character prefix (` ` for context, `-` for
134
+ old-side, `+` for new-side). Build it by concatenating, in
135
+ order and *skipping any part that is empty*:
136
+
137
+ - one ` `-prefixed line for *each* line of <context-before/>
138
+ (if non-empty),
139
+ - one `-`-prefixed line for *each* line of <old-text/>
140
+ (if non-empty; split <old-text/> on newlines),
141
+ - one `+`-prefixed line for *each* line of <new-text/>
142
+ (if non-empty; split <new-text/> on newlines),
143
+ - one ` `-prefixed line for *each* line of <context-after/>
144
+ (if non-empty).
145
+
146
+ Set <hunk-body/> to those prefixed lines joined by newlines.
147
+
148
+ Set <old-count/> to the number of old-side hunk lines, i.e.,
149
+ the combined line count of <context-before/>, <old-text/>, and
150
+ <context-after/> (each empty part counts as `0`).
151
+ Set <new-count/> to the number of new-side hunk lines, i.e.,
152
+ the combined line count of <context-before/>, <new-text/>, and
153
+ <context-after/> (each empty part counts as `0`).
154
+
155
+ Set <old-start/> to the 1-based line number of the *first*
156
+ old-side hunk line: if <context-before/> is non-empty, that is
157
+ its line (one before <line/>); otherwise it is <line/> itself
158
+ (the first line of <old-text/>). For a hunk that *only inserts*
159
+ new lines (empty <old-text/> *and* empty context), set it to the
160
+ line *before* which the insertion happens, clamped to a minimum
161
+ of `0`, so a top-of-file insertion yields `@@ -0,0 ... @@`.
162
+ Set <new-start/> to the same value as <old-start/>, but clamped
163
+ to a minimum of `1` whenever <new-count/> is greater than `0`
164
+ (the corrected side then has a real first line).
132
165
 
133
166
  Render the proposed correction as a *unified diff* with *up to
134
- two* lines of context in a fenced block based on the following <template/>:
167
+ two* lines of context in a fenced block based on the following
168
+ <template/>, emitting <hunk-body/> verbatim (one already-prefixed
169
+ line per line, with no extra blank or space-only lines):
135
170
 
136
171
  <template>
137
172
 
@@ -141,10 +176,7 @@ Analyze documents for spelling, punctuation, or grammar errors
141
176
  --- <file/> (original)
142
177
  +++ <file/> (corrected)
143
178
  @@ -<old-start/>,<old-count/> +<new-start/>,<new-count/> @@
144
- <context-before/>
145
- -<old-text/>
146
- +<new-text/>
147
- <context-after/>
179
+ <hunk-body/>
148
180
  ```
149
181
 
150
182
  </template>
@@ -137,8 +137,11 @@ Honor the following tenets throughout the brainstorming:
137
137
  4. Dispatch on the dialog <result/>:
138
138
 
139
139
  - If <result/> is `CANCEL`:
140
- Skip the remaining sub-steps of this iteration cycle and
141
- continue directly with sub-step 2 below (cancellation handling).
140
+ Skip the remaining sub-steps of this iteration cycle, break
141
+ out of the iteration cycle entirely, and continue directly
142
+ with the outer item `2.` (cancellation handling) that
143
+ immediately follows this iteration cycle `1.` within this
144
+ STEP 2.
142
145
 
143
146
  - If <result/> starts with `ERROR:`:
144
147
  Ask the user interactively, without a special tool, the
@@ -201,12 +201,18 @@ multi-*criteria* decision matrix.
201
201
  - Determine rating distance percentage <percentage/> between
202
202
  <alternative-K/> and <alternative-X/> from their *raw,
203
203
  unrounded* ratings as follows. If <rating-K/> is exactly zero,
204
- skip the division entirely and treat <percentage/> as if it
205
- were equal to <distance/> (so a true zero tie with <distance/>
206
- = 0 falls into the *MULTIPLE BEST* branch below, while a non-zero
207
- gap with zero best is routed by the magnitude of <distance/>
208
- through the same thresholds as <percentage/> otherwise). Do not
209
- output anything.
204
+ skip the division entirely (it is undefined against a zero
205
+ anchor) and classify directly by <distance/> instead of routing a
206
+ raw rating magnitude through the ratio thresholds below: if
207
+ <distance/> is also exactly zero, both best and second best rate
208
+ zero, which is a genuine tie, so set <percentage/> = 0 to fall
209
+ into the *MULTIPLE BEST* branch below; otherwise <distance/> > 0
210
+ means the second best rates strictly negative (since <rating-K/>
211
+ is the maximum), so there is a clear positive gap to a worse
212
+ runner-up and <alternative-K/> is an unambiguous winner, hence set
213
+ <percentage/> = 1 (a sentinel exceeding all small-distance
214
+ thresholds) to fall through to the plain *BEST ALTERNATIVE* branch.
215
+ Do not output anything.
210
216
 
211
217
  - Otherwise (<rating-K/> is non-zero), calculate: <percentage/> =
212
218
  <distance/> / abs(<rating-K/>). Do not output anything.
@@ -280,12 +280,3 @@ explicitly requested by this procedure via outputs based on a <template/>!
280
280
  <template>
281
281
  ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ✪ plan: **<words/>** words, ▶ status: **plan condensed -- hand-off to pre-flight**
282
282
  </template>
283
-
284
- - If <result/> matches `OTHER: <text/>` or is any other
285
- unrecognized value:
286
- Treat it as `DONE` (the condensed plan is already saved):
287
- only output the following <template/> and then *STOP*.
288
-
289
- <template>
290
- ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ✪ plan: **<words/>** words, ▶ status: **plan condensed -- done**
291
- </template>
@@ -326,12 +326,3 @@ explicitly requested by this procedure via outputs based on a <template/>!
326
326
  <template>
327
327
  ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ✪ plan: **<words/>** words, ▶ status: **plan updated -- hand-off to implementation**
328
328
  </template>
329
-
330
- - If <result/> matches `OTHER: <text/>` or is any other
331
- unrecognized value:
332
- Treat it as `DONE` (the updated plan is already saved):
333
- only output the following <template/> and then *STOP*.
334
-
335
- <template>
336
- ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ✪ plan: **<words/>** words, ▶ status: **plan updated -- done**
337
- </template>
@@ -28,7 +28,8 @@ Configure Task Id
28
28
 
29
29
  1. Determine request:
30
30
  <request><getopt-arguments/></request>
31
- Inherit the always existing <ase-session-id/> from the current context.
31
+ Inherit the always existing <ase-session-id/> and the current
32
+ <ase-task-id/> from the current context.
32
33
 
33
34
  2. <if condition="<request/> is NOT empty AND <request/> does NOT match the regexp `^[a-zA-Z][a-zA-Z0-9_-]*$`">
34
35
  Only output the following <template/> and then immediately
@@ -44,6 +45,8 @@ Configure Task Id
44
45
  tool from the `ase` MCP server and set <text/> to its
45
46
  `text` output. Check the response as mandated above; only
46
47
  on a clean response set <ase-task-id><text/></ase-task-id>.
48
+ On an `ERROR:`/`WARNING:` response, keep the <ase-task-id/>
49
+ inherited from the current context as the fallback.
47
50
 
48
51
  - Output:
49
52
  <template>
@@ -223,12 +223,3 @@ explicitly requested by this procedure via outputs based on a <template/>!
223
223
  ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ✪ plan: **<words/>** words, ▶ status: **plan implemented -- hand-off to delete task**
224
224
  </template>
225
225
 
226
- - If <result/> matches `OTHER: <text/>` or is any other
227
- unrecognized value:
228
- Treat it as `DONE` (the plan is already implemented):
229
- only output the following <template/> and then *STOP*.
230
-
231
- <template>
232
- ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ✪ plan: **<words/>** words, ▶ status: **plan implemented -- done**
233
- </template>
234
-
@@ -251,12 +251,3 @@ explicitly requested by this procedure via outputs based on a <template/>!
251
251
  ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ✪ plan: **<words/>** words, ▶ status: **plan updated -- hand-off to implement**
252
252
  </template>
253
253
 
254
- - If <result/> matches `OTHER: <text/>` or is any other
255
- unrecognized value:
256
- Treat it as `DONE` (the plan is already preflighted):
257
- only output the following <template/> and then *STOP*.
258
-
259
- <template>
260
- ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ✪ plan: **<words/>** words, ▶ status: **plan updated -- done**
261
- </template>
262
-
@@ -129,13 +129,30 @@ explicitly requested by this procedure via outputs based on a <template/>!
129
129
  timestamp-created to extracted text)
130
130
  </if>
131
131
 
132
- 7. Create a new plan from scratch and store the result as
132
+ 7. <if condition="<instruction/> is empty or contains only whitespace">
133
+ The WHAT/WHY extraction yielded no usable text (e.g. a
134
+ `- **WHAT**:` line existed but captured empty text, so the
135
+ whole-content fallback above did not fire). Fall back to the
136
+ full previous plan content: set <instruction><content/></instruction>
137
+ (set instruction to content).
138
+ <if condition="<instruction/> is still empty or contains only whitespace">
139
+ There is nothing to reboot from. Only output the following
140
+ <template/> and then immediately *STOP* processing the entire
141
+ current skill:
142
+
143
+ <template>
144
+ ⧉ **ASE**: ☻ skill: **ase-task-reboot**, ▶ ERROR: empty instruction -- nothing to reboot from
145
+ </template>
146
+ </if>
147
+ </if>
148
+
149
+ 8. Create a new plan from scratch and store the result as
133
150
  <content/> by closely following the defined plan format
134
151
  <format/> and injecting into it all the information from
135
152
  the <instruction/> and all decisions you derived from the
136
153
  <instruction/>.
137
154
 
138
- 8. Call the `ase_timestamp(format: "yyyy-LL-dd HH:mm")` tool of the
155
+ 9. Call the `ase_timestamp(format: "yyyy-LL-dd HH:mm")` tool of the
139
156
  `ase` MCP server and use the `text` field of its response for
140
157
  <timestamp-modified/> information. If <timestamp-created/> is
141
158
  still unset (because the previous <content/> had no `Created:`
@@ -145,12 +162,12 @@ explicitly requested by this procedure via outputs based on a <template/>!
145
162
  <timestamp-modified/> information and calculate the number of
146
163
  words <words/> of <content/>.
147
164
 
148
- 9. Call the `ase_task_save(id: "<ase-task-id/>",
165
+ 10. Call the `ase_task_save(id: "<ase-task-id/>",
149
166
  text: "<content/>")` of the `ase` MCP server to save the updated
150
167
  task plan content. Do not output anything related to this MCP
151
168
  call.
152
169
 
153
- 10. Only output the following <template/> and continue processing:
170
+ 11. Only output the following <template/> and continue processing:
154
171
 
155
172
  <template>
156
173
  ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ✪ plan: **<words/>** words, ▶ status: **plan rebooted**
@@ -244,12 +261,3 @@ explicitly requested by this procedure via outputs based on a <template/>!
244
261
  ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ✪ plan: **<words/>** words, ▶ status: **plan updated -- hand-off to pre-flight**
245
262
  </template>
246
263
 
247
- - If <result/> matches `OTHER: <text/>` or is any other
248
- unrecognized value:
249
- Treat it as `DONE` (the updated plan is already saved):
250
- only output the following <template/> and then *STOP*.
251
-
252
- <template>
253
- ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ✪ plan: **<words/>** words, ▶ status: **plan updated -- done**
254
- </template>
255
-