@rse/ase 0.9.50 → 0.9.52

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 (75) hide show
  1. package/dst/ase-hook.js +9 -13
  2. package/dst/ase-statusline.js +30 -4
  3. package/dst/ase-task.js +7 -2
  4. package/package.json +7 -7
  5. package/plugin/.claude-plugin/plugin.json +1 -1
  6. package/plugin/.codex-plugin/plugin.json +1 -1
  7. package/plugin/.github/plugin/plugin.json +1 -1
  8. package/plugin/agents/ase-code-lint.md +21 -1
  9. package/plugin/agents/ase-meta-proximity.md +133 -0
  10. package/plugin/etc/stx.conf +2 -2
  11. package/plugin/meta/ase-common-dissect.md +110 -0
  12. package/plugin/meta/ase-common-task.md +5 -3
  13. package/plugin/meta/ase-control.md +53 -0
  14. package/plugin/meta/ase-format-task.md +6 -0
  15. package/plugin/meta/ase-skill.md +8 -8
  16. package/plugin/package.json +2 -2
  17. package/plugin/skills/ase-arch-analyze/SKILL.md +23 -12
  18. package/plugin/skills/ase-arch-analyze/help.md +16 -0
  19. package/plugin/skills/ase-arch-discover/SKILL.md +2 -2
  20. package/plugin/skills/ase-code-analyze/SKILL.md +33 -16
  21. package/plugin/skills/ase-code-analyze/help.md +19 -2
  22. package/plugin/skills/ase-code-craft/SKILL.md +2 -2
  23. package/plugin/skills/ase-code-dissect/SKILL.md +380 -0
  24. package/plugin/skills/ase-code-dissect/help.md +121 -0
  25. package/plugin/skills/ase-code-explain/SKILL.md +2 -2
  26. package/plugin/skills/ase-code-insight/SKILL.md +2 -2
  27. package/plugin/skills/ase-code-lint/SKILL.md +43 -12
  28. package/plugin/skills/ase-code-lint/help.md +43 -1
  29. package/plugin/skills/ase-code-refactor/SKILL.md +2 -2
  30. package/plugin/skills/ase-code-resolve/SKILL.md +2 -2
  31. package/plugin/skills/ase-docs-distill/SKILL.md +2 -2
  32. package/plugin/skills/ase-docs-proofread/SKILL.md +2 -2
  33. package/plugin/skills/ase-help-intent/SKILL.md +2 -2
  34. package/plugin/skills/ase-help-skill/SKILL.md +2 -2
  35. package/plugin/skills/ase-help-skill/catalog.md +4 -0
  36. package/plugin/skills/ase-meta-brainstorm/SKILL.md +2 -2
  37. package/plugin/skills/ase-meta-changelog/SKILL.md +2 -2
  38. package/plugin/skills/ase-meta-chat/SKILL.md +2 -2
  39. package/plugin/skills/ase-meta-commit/SKILL.md +2 -2
  40. package/plugin/skills/ase-meta-config/SKILL.md +2 -2
  41. package/plugin/skills/ase-meta-diaboli/SKILL.md +2 -2
  42. package/plugin/skills/ase-meta-diff/SKILL.md +2 -2
  43. package/plugin/skills/ase-meta-eli5/SKILL.md +2 -2
  44. package/plugin/skills/ase-meta-eli5/help.md +1 -1
  45. package/plugin/skills/ase-meta-evaluate/SKILL.md +2 -2
  46. package/plugin/skills/ase-meta-proximity/SKILL.md +51 -92
  47. package/plugin/skills/ase-meta-proximity/help.md +11 -10
  48. package/plugin/skills/ase-meta-quorum/SKILL.md +2 -2
  49. package/plugin/skills/ase-meta-quotes/SKILL.md +214 -0
  50. package/plugin/skills/ase-meta-quotes/help.md +109 -0
  51. package/plugin/skills/ase-meta-review/SKILL.md +2 -2
  52. package/plugin/skills/ase-meta-review/help.md +4 -3
  53. package/plugin/skills/ase-meta-search/SKILL.md +2 -2
  54. package/plugin/skills/ase-meta-steelman/SKILL.md +2 -2
  55. package/plugin/skills/ase-meta-why/SKILL.md +2 -2
  56. package/plugin/skills/ase-meta-workflow/SKILL.md +378 -0
  57. package/plugin/skills/ase-meta-workflow/help.md +117 -0
  58. package/plugin/skills/ase-meta-workflow/sample.md +70 -0
  59. package/plugin/skills/ase-meta-workflow/workflow.txt +97 -0
  60. package/plugin/skills/ase-sync-export/SKILL.md +2 -2
  61. package/plugin/skills/ase-sync-import/SKILL.md +2 -2
  62. package/plugin/skills/ase-sync-reconcile/SKILL.md +2 -2
  63. package/plugin/skills/ase-task-condense/SKILL.md +26 -6
  64. package/plugin/skills/ase-task-delete/SKILL.md +2 -2
  65. package/plugin/skills/ase-task-dissect/SKILL.md +299 -0
  66. package/plugin/skills/ase-task-dissect/help.md +114 -0
  67. package/plugin/skills/ase-task-edit/SKILL.md +6 -4
  68. package/plugin/skills/ase-task-grill/SKILL.md +6 -4
  69. package/plugin/skills/ase-task-id/SKILL.md +2 -2
  70. package/plugin/skills/ase-task-implement/SKILL.md +2 -2
  71. package/plugin/skills/ase-task-list/SKILL.md +2 -2
  72. package/plugin/skills/ase-task-preflight/SKILL.md +2 -2
  73. package/plugin/skills/ase-task-reboot/SKILL.md +12 -6
  74. package/plugin/skills/ase-task-rename/SKILL.md +2 -2
  75. package/plugin/skills/ase-task-view/SKILL.md +2 -2
package/dst/ase-hook.js CHANGED
@@ -145,12 +145,9 @@ export default class HookCommand {
145
145
  pluginRootVars = ["PLUGIN_ROOT", "CLAUDE_PLUGIN_ROOT"];
146
146
  else
147
147
  pluginRootVars = ["CLAUDE_PLUGIN_ROOT"];
148
- let pluginRoot = "";
149
- for (const pluginRootVar of pluginRootVars)
150
- if ((process.env[pluginRootVar] ?? "") !== "") {
151
- pluginRoot = process.env[pluginRootVar];
152
- break;
153
- }
148
+ const pluginRoot = pluginRootVars
149
+ .map((varName) => process.env[varName] ?? "")
150
+ .find((value) => value !== "") ?? "";
154
151
  if (pluginRoot === "")
155
152
  throw new Error(`${pluginRootVars.join("/")} environment variable is not set`);
156
153
  return pluginRoot;
@@ -254,7 +251,7 @@ export default class HookCommand {
254
251
  const guidance = setting("agent.guidance", "ASE_GUIDANCE_LEVEL", "normal");
255
252
  const boxing = setting("project.boxing", "ASE_PROJECT_BOXING", "white");
256
253
  /* determine headless mode */
257
- const headless = (process.env.ASE_HEADLESS ?? "false") === "true" ? "true" : "false";
254
+ const headless = process.env.ASE_HEADLESS === "true" ? "true" : "false";
258
255
  /* provide ASE information to Anthropic Claude Code CLI shell commands
259
256
  (Anthropic Claude Code CLI only -- GitHub Copilot CLI has no equivalent mechanism) */
260
257
  const envFile = tool === "claude" ? (process.env.CLAUDE_ENV_FILE ?? "") : "";
@@ -300,15 +297,14 @@ export default class HookCommand {
300
297
  "systemMessage" field for this -- GitHub Copilot CLI has no equivalent);
301
298
  the trailing help hint is emitted only if the guidance level asks for it */
302
299
  const banner = "\n" +
303
- `\n⧉ ASE: ⎈ version: ${versionCurrentPlugin}${versionHint !== "" ? " " + versionHint.replaceAll(/\*/g, "") : ""}` +
300
+ `\n⧉ ASE: ⎈ version: ${versionCurrentPlugin}${versionHint !== "" ? " " + versionHint.replace(/\*/g, "") : ""}` +
304
301
  `\n⧉ ASE: ※ user: ${userId}, ⚑ project: ${projectId}` +
305
302
  `\n⧉ ASE: ◉ task: ${taskId}, ⏻ session: ${sessionId}` +
306
303
  `\n⧉ ASE: ☯ persona: ${persona}, ▶ guidance: ${guidance}, ▢ boxing: ${boxing}` +
307
304
  (guidance === "normal" || guidance === "verbose" ?
308
305
  "\n" +
309
306
  "\n⧉ ASE: ▷ hint: use \"/ase-help-intent <intent-description>\" for skill command proposal" +
310
- "\n⧉ ASE: ▷ hint: use \"/ase-help-skill [<skill-name>]\" for skill catalog or skill manpage" : "") +
311
- "\n";
307
+ "\n⧉ ASE: ▷ hint: use \"/ase-help-skill [<skill-name>]\" for skill catalog or skill manpage" : "");
312
308
  /* inject markdown into session context.
313
309
  Anthropic Claude Code CLI and OpenAI Codex CLI expect the context nested in
314
310
  "hookSpecificOutput"; GitHub Copilot CLI expects a flat top-level
@@ -434,10 +430,10 @@ export default class HookCommand {
434
430
  tools, but the decision logic is shared by the "pre-tool-use" and
435
431
  "permission-request" handlers. */
436
432
  decideApproval(tool, spec, input) {
437
- const toolName = typeof input[spec.toolNameField] === "string" ?
438
- input[spec.toolNameField] : "";
439
- let toolInput = {};
433
+ const rawName = input[spec.toolNameField];
440
434
  const rawInput = input[spec.toolInputField];
435
+ const toolName = typeof rawName === "string" ? rawName : "";
436
+ let toolInput = {};
441
437
  if (spec.toolInputIsString && typeof rawInput === "string")
442
438
  toolInput = this.parseJSON(rawInput, toolInputSchema);
443
439
  else if (!spec.toolInputIsString && typeof rawInput === "object" && rawInput !== null) {
@@ -159,6 +159,23 @@ const probeMemory = () => {
159
159
  return { used: 0, total: 0 };
160
160
  }
161
161
  };
162
+ /* set of reasoning effort levels GitHub Copilot CLI offers in its effort picker */
163
+ const EFFORTS = new Set([
164
+ "low", "medium", "high", "xhigh", "max"
165
+ ]);
166
+ /* split a GitHub Copilot CLI "model.display_name" into its plain model name and its
167
+ reasoning effort: Copilot CLI renders the display name as the model label, followed
168
+ by the optional request multiplier, the optional reasoning effort and the optional
169
+ context tier, all separated by " · " (e.g. "gpt-5.4 (2x) · high · 1M context"), so
170
+ only a segment matching a known effort level is taken as the effort */
171
+ const splitCopilotModel = (display) => {
172
+ const parts = display.split(" · ");
173
+ const effort = parts.filter((part) => EFFORTS.has(part.toLowerCase()));
174
+ const name = parts.filter((part) => !EFFORTS.has(part.toLowerCase()));
175
+ if (effort.length === 0)
176
+ return { name: display, effort: "" };
177
+ return { name: name.join(" · "), effort: effort[0].toLowerCase() };
178
+ };
162
179
  /* memoize a zero-argument function, computing its value at most once on first use */
163
180
  const memoize = (fn) => {
164
181
  let cache = null;
@@ -286,6 +303,15 @@ export default class StatuslineCommand {
286
303
  }
287
304
  return { taskId, persona, guidance };
288
305
  });
306
+ const getModel = memoize(() => {
307
+ const display = data.model?.display_name ?? "";
308
+ /* under GitHub Copilot CLI the reasoning effort is only available as
309
+ a segment of the display name, so split it off again and let the
310
+ regular "effort" field of Anthropic Claude Code CLI still win */
311
+ const split = tool === "copilot" && display !== "" ?
312
+ splitCopilotModel(display) : { name: display, effort: "" };
313
+ return { name: split.name, effort: data.effort?.level ?? split.effort };
314
+ });
289
315
  const getGit = memoize(() => probeGit(data.workspace?.current_dir ?? ""));
290
316
  const getMem = memoize(() => probeMemory());
291
317
  /* identifier to renderer map: each callback fetches its own information
@@ -308,12 +334,12 @@ export default class StatuslineCommand {
308
334
  s: () => emit(`${prefix("⏻", "session")}${c.bold(getSession())}`),
309
335
  /* ==== MODEL ==== */
310
336
  m: () => {
311
- const model = data.model?.display_name ?? "";
312
- emit(`${prefix("⚙", "model")}${c.bold(model)}`);
337
+ const { name } = getModel();
338
+ emit(`${prefix("⚙", "model")}${c.bold(name)}`);
313
339
  },
314
340
  e: () => {
315
- const effort = data.effort?.level ?? "unknown";
316
- emit(`${prefix("⚒", "effort")}${c.bold(effort)}`);
341
+ const { effort } = getModel();
342
+ emit(`${prefix("⚒", "effort")}${c.bold(effort !== "" ? effort : "unknown")}`);
317
343
  },
318
344
  t: () => {
319
345
  const thinking = data.thinking?.enabled === true ? "yes" : "no";
package/dst/ase-task.js CHANGED
@@ -451,7 +451,8 @@ export class TaskMCP {
451
451
  mcp.registerTool("ase_task_save", {
452
452
  title: "ASE task save",
453
453
  description: "Persist a task as `text` under `id`. " +
454
- "Overwrites any existing task for the same `id`.",
454
+ "Overwrites any existing task for the same `id`. " +
455
+ "Returns the persisted task as `text`, prepared for improved rendering.",
455
456
  inputSchema: {
456
457
  id: z.string()
457
458
  .describe("task identifier (allowed characters: A-Z, a-z, 0-9, '_', '-')"),
@@ -461,8 +462,12 @@ export class TaskMCP {
461
462
  }, async (args) => {
462
463
  try {
463
464
  Task.save(this.log, args.id, args.text);
465
+ /* return the prepared content, so a caller reusing the
466
+ just-saved plan instead of re-loading it still receives
467
+ the rendering-prepared variant */
468
+ const text = Markdown.prepare(args.text);
464
469
  return {
465
- content: [{ type: "text", text: `OK: saved task "${args.id}"` }]
470
+ content: [{ type: "text", text }]
466
471
  };
467
472
  }
468
473
  catch (err) {
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "homepage": "https://ase.tools",
7
7
  "repository": { "url": "git+https://github.com/rse/ase.git", "type": "git" },
8
8
  "bugs": { "url": "https://github.com/rse/ase/issues" },
9
- "version": "0.9.50",
9
+ "version": "0.9.52",
10
10
  "license": "Apache-2.0",
11
11
  "author": {
12
12
  "name": "Dr. Ralf S. Engelschall",
@@ -23,14 +23,14 @@
23
23
  "eslint-plugin-promise": "7.3.0",
24
24
  "eslint-plugin-import": "2.32.0",
25
25
  "neostandard": "0.13.0",
26
- "globals": "17.7.0",
26
+ "globals": "17.8.0",
27
27
  "typescript": "6.0.3",
28
28
 
29
29
  "@rse/stx": "1.1.6",
30
30
  "nodemon": "3.1.14",
31
31
  "shx": "0.4.0",
32
32
 
33
- "@types/node": "26.1.1",
33
+ "@types/node": "26.1.2",
34
34
  "@types/luxon": "3.7.2",
35
35
  "@types/which": "3.0.4",
36
36
  "@types/update-notifier": "6.0.8",
@@ -42,18 +42,18 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "commander": "15.0.0",
45
- "@dotenvx/dotenvx": "2.18.0",
45
+ "@dotenvx/dotenvx": "2.19.1",
46
46
  "yaml": "2.9.0",
47
47
  "valibot": "1.4.2",
48
- "execa": "10.0.0",
48
+ "execa": "10.0.1",
49
49
  "mkdirp": "3.0.1",
50
50
  "@hapi/hapi": "21.4.10",
51
51
  "beautiful-mermaid": "1.1.3",
52
52
  "cli-table3": "0.6.5",
53
- "chalk": "5.6.2",
53
+ "chalk": "6.0.0",
54
54
  "pretty-ms": "9.3.0",
55
55
  "luxon": "3.7.2",
56
- "@modelcontextprotocol/sdk": "1.29.0",
56
+ "@modelcontextprotocol/sdk": "1.30.0",
57
57
  "json-asty": "1.3.4",
58
58
  "zod": "4.4.3",
59
59
  "which": "7.0.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ase",
3
- "version": "0.9.50",
3
+ "version": "0.9.52",
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.50",
3
+ "version": "0.9.52",
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.50",
3
+ "version": "0.9.52",
4
4
  "description": "Agentic Software Engineering (ASE)",
5
5
  "keywords": [ "agentic", "software", "engineering" ],
6
6
  "homepage": "https://ase.tools",
@@ -13,6 +13,10 @@ Workflow
13
13
  --------
14
14
 
15
15
  1. Set the requested context: <context>$ARGUMENTS</context>.
16
+ The *first* whitespace-separated token of <context/> is the
17
+ comma-separated *aspect set* <aspects/> (a non-empty subset of the
18
+ aspect ids `A01`...`A20`). The *remaining* tokens are the source
19
+ code files to check.
16
20
 
17
21
  2. Use the `Read` tool to read all source code files referenced by
18
22
  <context/>, plus all *related* source code files needed to really
@@ -25,7 +29,9 @@ Workflow
25
29
  4. Set <problems/> to empty.
26
30
  Then check the read source code for the following aspects (each
27
31
  aspect is uniquely identified by its `aspect` id `A01 - XXX`...`A20
28
- - XXX`):
32
+ - XXX`), but *strictly limited* to those aspects whose id is
33
+ contained in the aspect set <aspects/> -- all other aspects are
34
+ *not* checked and their problems are *never* reported:
29
35
 
30
36
  - **A01 - FORMATTING**:
31
37
  Check for inconsistently formatted code and badly vertically
@@ -95,6 +101,20 @@ Workflow
95
101
  site as close as possible. For R4, prefer *parameterization*
96
102
  (table-driven, strategy map) over inheritance.
97
103
 
104
+ **PAYOFF GATE**: *Before* reporting any redundancy, draft the
105
+ solution diff and count its lines. The removed lines *MUST* be at
106
+ least *twice* the added lines (ratio >= 2:1, i.e. a net reduction
107
+ of at least 50%), where the added lines include the *entire*
108
+ extracted construct (signature, body, closing lines, comments,
109
+ type annotations) plus all replacing call sites. A break-even
110
+ proposal (e.g. 8 removed, 8 added) or any proposal below the 2:1
111
+ ratio *MUST* be *silently dropped* and *MUST* *NOT* be reported --
112
+ it merely relocates code instead of reducing it. Do *not* game
113
+ the ratio by compressing the extracted construct into unnatural
114
+ formatting or by omitting comments the code base style requires.
115
+ Two duplicated occurrences of a short block rarely pass this gate;
116
+ three or more occurrences usually do.
117
+
98
118
  - **A07 - PATTERNS**:
99
119
  Check for broken design patterns, broken conventions, or broken
100
120
  best practices.
@@ -0,0 +1,133 @@
1
+ ---
2
+ name: ase-meta-proximity
3
+ description: "Determine the Conceptual Proximity of a Topic"
4
+ effort: high
5
+ tools:
6
+ - "Agent"
7
+ ---
8
+
9
+ @../meta/ase-control.md
10
+
11
+ <define name="gather-facts">
12
+ <if condition="<ground/> is equal `true`">
13
+ Use the `ase-meta-search` skill in a sub-agent to gather facts with
14
+ the following tool call and store the returned facts in the placeholder
15
+ named `<arg2/>`:
16
+
17
+ `Agent(
18
+ description: "Query Web Search Service",
19
+ subagent_type: "ase:ase-meta-search",
20
+ prompt: "Search the Internet/Web and gather facts about <arg1/>",
21
+ run_in_background: false
22
+ )`
23
+
24
+ <if condition="the placeholder named `<arg2/>` contains no usable facts">
25
+ Set the placeholder named `<arg2/>` to empty, so the determination below
26
+ silently falls back to model knowledge. You *MUST* *NOT* output any
27
+ warning, because the caller expects the labeled list of step 4 as the
28
+ *only* output.
29
+ </if>
30
+ </if>
31
+ <else>
32
+ Use the model's world knowledge and determine facts about <arg1/> and
33
+ store those facts in the placeholder named `<arg2/>`.
34
+ </else>
35
+ </define>
36
+
37
+ 1. Set <args>$ARGUMENTS</args>, the single whitespace-separated string.
38
+
39
+ <if condition="the *first* token of <args/> is equal `GROUND`">
40
+ Set <ground>true</ground> (grounding requested) and set <topic/>
41
+ to the *second and all following* tokens of <args/>.
42
+ </if>
43
+ <else>
44
+ Set <ground>false</ground> (no grounding requested) and set
45
+ <topic/> to *all* tokens of <args/>.
46
+ </else>
47
+
48
+ You *MUST* *NOT* output anything related to this step.
49
+
50
+ 2. *Determine Topic*:
51
+
52
+ Determine the canonical name of the central *topic* which is stored
53
+ in <topic/>.
54
+
55
+ <expand name="gather-facts"
56
+ arg1="the following topic: <topic/>"
57
+ arg2="facts-topic"></expand>
58
+
59
+ Ground the determination of the canonical name of the topic <topic/>
60
+ in the facts of <facts-topic/> and do not contradict them. Update
61
+ <topic/> accordingly.
62
+
63
+ You *MUST* *NOT* output anything related to this step.
64
+
65
+ 3. *Determine Proximity*:
66
+
67
+ Determine the *conceptual proximity* of the current <topic/> along
68
+ three *dimensions* in parallel:
69
+
70
+ - **PARENT**:
71
+
72
+ The single most relevant *parent* topic (the broader topic that
73
+ <topic/> is a specialization of), which will be stored in
74
+ <parent/>.
75
+
76
+ <expand name="gather-facts"
77
+ arg1="the PARENT topic (the broader topic that the given topic is a specialization of) of the following topic: <topic/>"
78
+ arg2="facts-parent"></expand>
79
+
80
+ Ground the determination of the canonical name of the parent
81
+ topic <parent/> in the facts of <facts-parent/> and do not
82
+ contradict them.
83
+
84
+ - **SIBLINGS**:
85
+
86
+ The *four* most relevant *sibling* topics (topics on the same
87
+ level that share the same parent), which will be stored in
88
+ <sibling-1/> to <sibling-4/>.
89
+
90
+ <expand name="gather-facts"
91
+ arg1="the SIBLING topics (topics on the same level that share the same parent) of the following topic: <topic/>"
92
+ arg2="facts-siblings"></expand>
93
+
94
+ Ground the determination of the canonical names of the most
95
+ relevant sibling topics <sibling-1/> to <sibling-4/> in the facts
96
+ of <facts-siblings/> and do not contradict them.
97
+
98
+ - **CHILDREN**:
99
+
100
+ The *four* most relevant *child* topics (narrower topics that
101
+ are specializations of <topic/>), stored in <child-1/> to
102
+ <child-4/>.
103
+
104
+ <expand name="gather-facts"
105
+ arg1="the CHILDREN topics (narrower topics that are specializations) of the following topic: <topic/>"
106
+ arg2="facts-children"></expand>
107
+
108
+ Ground the determination of the canonical names of the most
109
+ relevant child topics <child-1/> to <child-4/> in the facts of
110
+ <facts-children/> and do not contradict them.
111
+
112
+ You *MUST* determine *exactly* one parent, *exactly* four siblings,
113
+ and *exactly* four children. All nine proximity topics *MUST* be
114
+ *distinct* from each other and *MUST* *NOT* be <topic/> itself or a
115
+ mere synonym or spelling variant of it. You *MUST* *NOT* output
116
+ anything related to this step.
117
+
118
+ 4. Return *exclusively* the following <template/> (no prose, no
119
+ preamble, no summary, and no Markdown formatting):
120
+
121
+ <template>
122
+ TOPIC: <topic/>
123
+ PARENT: <parent/>
124
+ SIBLING: <sibling-1/>
125
+ SIBLING: <sibling-2/>
126
+ SIBLING: <sibling-3/>
127
+ SIBLING: <sibling-4/>
128
+ CHILD: <child-1/>
129
+ CHILD: <child-2/>
130
+ CHILD: <child-3/>
131
+ CHILD: <child-4/>
132
+ </template>
133
+
@@ -12,9 +12,9 @@ lint
12
12
  # [plugin] build project
13
13
  build : lint
14
14
  ( for name in $(cd skills; ls -1 | grep -v ase-help-intent | sort); do
15
- echo "<skill name=\"$name\">"
15
+ echo "<purpose name=\"$name\">"
16
16
  cat skills/$name/help.md | sed -e '/^## SEE ALSO/,$d'
17
- echo "</skill>"
17
+ echo "</purpose>"
18
18
  done
19
19
  ) >skills/ase-help-intent/data.md
20
20
 
@@ -0,0 +1,110 @@
1
+
2
+ Dissect Skill Common Steps
3
+ ==========================
4
+
5
+ <define name="dissect-derive">
6
+
7
+ *Dissect* <content/> into *cohesive parts* by strictly honoring the
8
+ following ruleset:
9
+
10
+ 1. *Separate domain-wise and logically*: every part *MUST* be a
11
+ *self-contained* unit of work with a *single*, coherent purpose (one
12
+ domain, one concern, one logical change), so it can be implemented,
13
+ reviewed, and committed entirely *on its own*. A *technical* split
14
+ -- one part per file, per directory, per file type, or per technical
15
+ layer -- is usually *not* the obvious and intended one: files are
16
+ merely *where* the change lands, while a part is defined by *what*
17
+ it achieves. Cut along the *semantics* first, and let a file-wise
18
+ boundary result only when it *coincides* with a domain boundary.
19
+
20
+ 2. *Bound the part count*: derive at least *2* and at most
21
+ *<getopt-option-max-parts/>* parts. Prefer *fewer* and *larger*
22
+ cohesive parts over *many* and *tiny* ones.
23
+
24
+ 3. *Assign totally and disjointly*: *every* input element of the epic
25
+ *MUST* be assigned to *exactly one* part -- no input element is
26
+ dropped, and no input element occurs in two parts. An input element
27
+ which itself spans *multiple* domains or concerns *MAY* be *split*
28
+ into two or more *sub-elements*, which are then assigned like
29
+ ordinary input elements, provided the sub-elements *together* cover
30
+ the original element *completely*, *never* overlap, and each one
31
+ stays *self-contained* in the input form the calling skill defines.
32
+ Split *only* when rule 1 forces it: an input element whose content
33
+ fits a *single* part stays *unsplit*.
34
+
35
+ 4. *Keep mutually dependent elements together*: input elements which
36
+ only make sense *together* -- they reference each other, one is the
37
+ precondition of the other, or splitting them would leave a part
38
+ broken -- *MUST* land in the *same* part.
39
+
40
+ 5. *Never invent*: parts are formed *exclusively* from the input
41
+ elements of the epic. Do *not* add scope, do *not* re-interpret the
42
+ input elements, and do *not* re-word them beyond what a part-local
43
+ summary requires.
44
+
45
+ 6. *Slug and identify every part*: per part derive a <feature-slug/>
46
+ from its scope, matching the regexp `^[a-z][a-z0-9-]{0,23}$` and
47
+ *unique* across all parts, and then set
48
+ <part-id><arg3/>-<feature-slug/></part-id>, where <arg3/> is the
49
+ *id prefix* the calling skill passed in.
50
+
51
+ 7. *Order for implementation*: order the parts so that a part *never*
52
+ depends on a later one, and number them consecutively as <part-no/>,
53
+ starting at `1`.
54
+
55
+ 8. *Honor the dissection hint*:
56
+ <if condition="<arg2/> is not empty">
57
+ Set <hint><arg2/></hint>. The user explicitly told *how* the epic
58
+ should be split -- e.g. which input elements belong together, along
59
+ which axis to cut, or how many parts to aim at -- so you *MUST*
60
+ follow this <hint/> as closely as possible and let it *override* the
61
+ default grouping of rule 1. It *MUST NOT* override the rules 2-7,
62
+ though: the part count stays bounded, the assignment stays total and
63
+ disjoint, mutually dependent elements stay together, nothing is
64
+ invented, every part stays uniquely slugged, and the order stays
65
+ dependency-free. If the <hint/> conflicts with one of these rules,
66
+ honor the rule and follow the <hint/> only as far as the rule
67
+ permits.
68
+ </if>
69
+ <else>
70
+ No dissection hint was given, so the grouping follows the rules 1-7
71
+ alone. Do not output anything.
72
+ </else>
73
+
74
+ Per part, additionally derive an *ultra brief* <scope/> (*what* the part
75
+ covers) and an *ultra brief* <rationale/> (*why* exactly these input
76
+ elements form *one* cohesive part). Store the resulting parts in
77
+ <parts/> and their number in <n/>.
78
+
79
+ <if condition="fewer than 2 cohesive parts exist">
80
+ The epic is *not* dissectable, because it carries a *single* cohesive
81
+ purpose (or too few input elements) and splitting it would only produce
82
+ artificial fragments. Only output the following <template/> and then
83
+ immediately *STOP* processing the entire current skill:
84
+
85
+ <template>
86
+ ⧉ **ASE**: ✪ skill: **<arg1/>**, ▶ status: **epic not dissectable**
87
+ </template>
88
+ </if>
89
+
90
+ </define>
91
+
92
+ <define name="dissect-report">
93
+
94
+ Report the derived <parts/> with the following <template/>, emitting
95
+ *one* table row per part in <parts/>, in their derived order. Keep
96
+ <scope/> and <rationale/> each to *one* ultra brief sentence, align all
97
+ column edges of the table, and do *not* output any further explanation:
98
+
99
+ <template>
100
+ <ase-tpl-head title="DISSECTION" subtitle="<arg1/>">
101
+
102
+ | Part | Id | Scope | Rationale |
103
+ | --------------- | ------------ | -------- | ------------ |
104
+ | **P<part-no/>** | `<part-id/>` | <scope/> | <rationale/> |
105
+ | [...] | [...] | [...] | [...] |
106
+
107
+ <ase-tpl-foot title="DISSECTION" subtitle="<arg1/>">
108
+ </template>
109
+
110
+ </define>
@@ -65,9 +65,11 @@ Task Skill Common Steps
65
65
  *and* a `ase_task_save(id: '<ase-task-id/>', ...)` tool call
66
66
  exists earlier in the current session
67
67
  ">
68
- Set <text/> to the `text` argument of the most recent
69
- `ase_task_save(id: '<ase-task-id/>', ...)` tool call,
70
- *without* calling `ase_task_load` again. Set <status>plan
68
+ Set <text/> to the `text` *output* field of the most recent
69
+ `ase_task_save(id: '<ase-task-id/>', ...)` tool call -- this is
70
+ the rendering-prepared plan content and *MUST NOT* be confused
71
+ with the `text` *argument* passed into that call -- *without*
72
+ calling `ase_task_load` again. Set <status>plan
71
73
  reused</status>. Do not output anything.
72
74
  </if>
73
75
  <else>
@@ -95,3 +95,56 @@ Control Flow Constructs
95
95
  is finished and no further repetitions are performed. This construct
96
96
  is expanded into nothing. Do not output anything.
97
97
 
98
+ - *IMPORTANT*: You *MUST* honor the following control flow construct:
99
+ <agent <attr/>="<value/>" [...]><agent-body/></agent>:
100
+
101
+ This specifies the *invocation* of a *sub-agent* through the
102
+ `Agent` tool. Every XML attribute is passed *verbatim* as the
103
+ identically named parameter of the `Agent` tool (e.g. `description`,
104
+ `subagent_type`, `run_in_background`, `isolation`, `model`), and
105
+ <agent-body/> is passed as its `prompt` parameter.
106
+
107
+ The *sole exception* is the *reserved* attribute `result="<var/>"`,
108
+ which is *not* passed on but instead *binds* the result returned
109
+ by the sub-agent to the placeholder `<<var/>/>`. This construct is
110
+ expanded to the result returned by the sub-agent if `result` is
111
+ *absent*, or into nothing if `result` is *present*. Do not output
112
+ anything else.
113
+
114
+ - *IMPORTANT*: You *MUST* honor the following control flow construct:
115
+ <agent-consolidation [group=<agent-group/>]/>
116
+
117
+ This specifies the merging of all the Git WorkTrees created by the
118
+ <agent/> calls with either are identified with the unique group
119
+ <agent-group/> or all <agent/> calls in the last <parallel/> section
120
+ of the context. After merging a single Git WorkTree, remove the Git
121
+ WorkTree.
122
+
123
+ - *IMPORTANT*: You *MUST* honor the following control flow construct:
124
+ <skill name="<id/>" [args="<args/>"] [result="<var/>"]/>:
125
+
126
+ This specifies the *invocation* of another *skill* through the
127
+ `Skill` tool, with its `skill` parameter set to <id/> and its `args`
128
+ parameter set to <args/> (or to the empty string if the `args`
129
+ attribute is absent).
130
+
131
+ The *reserved* attribute `result="<var/>"` *binds* the result
132
+ returned by the skill to the placeholder `<<var/>/>`, exactly
133
+ as for <agent/>. This construct is *always* *self-closing*: a
134
+ *body-bearing* `<skill>` element is *never* an invocation. This
135
+ construct is expanded to the result returned by the skill if
136
+ `result` is *absent*, or into nothing if `result` is *present*. Do
137
+ not output anything else.
138
+
139
+ - *IMPORTANT*: You *MUST* honor the following control flow construct:
140
+ <parallel><parallel-body/></parallel>:
141
+
142
+ This specifies a <parallel-body/> whose <agent/> and <skill/>
143
+ invocations are *all* dispatched *concurrently*: you *MUST* emit
144
+ them *in one single message* instead of one per turn, and this
145
+ construct is finished only once *all* of them have returned. If two
146
+ or more of these invocations bind the *same* `result` name, the
147
+ corresponding placeholder carries an *array* of their results, in
148
+ the order of their occurrence in <parallel-body/>. This construct is
149
+ expanded to its <parallel-body/>. Do not output anything else.
150
+
@@ -70,3 +70,9 @@ You *MUST* honor the following hints on this *task* format:
70
70
  after about 100 characters per line for better subsequent
71
71
  manual editing.
72
72
 
73
+ - You *MUST* *NEVER* break a line *inside* an inline code span
74
+ <template>`<code/>`</template>, as a code span split across two
75
+ lines renders badly. Instead, break the line *before* its opening
76
+ backtick or *after* its closing backtick, even if this means
77
+ breaking the line noticeably earlier than after 100 characters.
78
+
@@ -209,18 +209,18 @@ MCP Tool Calls
209
209
  Skill Identification
210
210
  --------------------
211
211
 
212
- - *IMPORTANT*: Set <skill></skill> (set to empty)
212
+ - *IMPORTANT*: Set <purpose></purpose> (set to empty)
213
213
  and <skill-name></skill-name> (set name to empty).
214
214
 
215
- In case <skill/> later becomes *not* empty by defining it as <skill
216
- name="<name/>"><body/></skill>, set <skill-name><name/></skill-name>
217
- (set skill name to name), and then (but only if ((`$1` is *NOT* equal
218
- to `-h` or `--help`) *AND* (<ase-guidance-level/> is equal to `normal`
219
- or `verbose`))) you *MUST* once output the following output
220
- <template/>:
215
+ In case <purpose/> later becomes *not* empty by defining it as
216
+ <purpose name="<name/>"><body/></purpose>, set
217
+ <skill-name><name/></skill-name> (set skill name to name), and then
218
+ (but only if ((`$1` is *NOT* equal to `-h` or `--help`) *AND*
219
+ (<ase-guidance-level/> is equal to `normal` or `verbose`))) you *MUST*
220
+ once output the following output <template/>:
221
221
 
222
222
  <template>
223
- ⧉ **ASE**: ✪ skill: **<skill-name/>**, ✦ purpose: **<skill/>**, ▶ status: **skill started**
223
+ ⧉ **ASE**: ✪ skill: **<skill-name/>**, ✦ purpose: **<purpose/>**, ▶ status: **skill started**
224
224
  </template>
225
225
 
226
226
  Later (but only if ((`$1` is *NOT* equal to `-h` or `--help`) *AND*
@@ -6,7 +6,7 @@
6
6
  "homepage": "https://ase.tools",
7
7
  "repository": { "url": "git+https://github.com/rse/ase.git", "type": "git" },
8
8
  "bugs": { "url": "https://github.com/rse/ase/issues" },
9
- "version": "0.9.50",
9
+ "version": "0.9.52",
10
10
  "license": "Apache-2.0",
11
11
  "author": {
12
12
  "name": "Dr. Ralf S. Engelschall",
@@ -16,7 +16,7 @@
16
16
  "devDependencies": {
17
17
  "@rse/stx": "1.1.6",
18
18
  "markdownlint": "0.41.1",
19
- "markdownlint-cli2": "0.23.1",
19
+ "markdownlint-cli2": "0.23.2",
20
20
  "eslint": "10.8.0",
21
21
  "@eslint/markdown": "8.0.3",
22
22
  "eslint-markdown": "0.12.1"