@rse/ase 0.9.59 → 0.9.60

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 (34) hide show
  1. package/dst/ase-hook.js +45 -5
  2. package/dst/ase-markdown.js +1 -1
  3. package/dst/ase-meta.js +1 -1
  4. package/dst/ase-setup.js +1 -1
  5. package/dst/ase-statusline.js +1 -1
  6. package/package.json +12 -13
  7. package/plugin/.claude-plugin/plugin.json +1 -1
  8. package/plugin/.codex-plugin/plugin.json +1 -1
  9. package/plugin/.github/plugin/plugin.json +1 -1
  10. package/plugin/meta/ase-common-code.md +1 -1
  11. package/plugin/meta/ase-common-task.md +8 -8
  12. package/plugin/meta/ase-dialog.md +4 -2
  13. package/plugin/package.json +4 -4
  14. package/plugin/skills/ase-code-craft/SKILL.md +43 -9
  15. package/plugin/skills/ase-code-craft/help.md +13 -0
  16. package/plugin/skills/ase-code-edit/SKILL.md +472 -0
  17. package/plugin/skills/ase-code-edit/help.md +127 -0
  18. package/plugin/skills/ase-code-refactor/SKILL.md +7 -6
  19. package/plugin/skills/ase-code-resolve/SKILL.md +7 -6
  20. package/plugin/skills/ase-help-skill/catalog.md +1 -0
  21. package/plugin/skills/ase-sync-import/SKILL.md +2 -1
  22. package/plugin/skills/ase-sync-reconcile/SKILL.md +3 -1
  23. package/plugin/skills/ase-task-condense/SKILL.md +13 -13
  24. package/plugin/skills/ase-task-dissect/SKILL.md +6 -5
  25. package/plugin/skills/ase-task-edit/SKILL.md +42 -20
  26. package/plugin/skills/ase-task-edit/help.md +7 -0
  27. package/plugin/skills/ase-task-grill/SKILL.md +4 -4
  28. package/plugin/skills/ase-task-implement/SKILL.md +21 -10
  29. package/plugin/skills/ase-task-implement/help.md +4 -2
  30. package/plugin/skills/ase-task-preflight/SKILL.md +18 -4
  31. package/plugin/skills/ase-task-preflight/help.md +5 -1
  32. package/plugin/skills/ase-task-reboot/SKILL.md +1 -1
  33. package/plugin/skills/ase-task-view/SKILL.md +1 -2
  34. package/plugin/package-lock.json +0 -2890
package/dst/ase-hook.js CHANGED
@@ -75,6 +75,10 @@ const toolInputSchema = v.object({
75
75
  skill: v.optional(v.string()),
76
76
  file_path: v.optional(v.string())
77
77
  });
78
+ /* maximum tolerated age of an idle session directory: the session-end hook
79
+ removes it regularly, but a crashed or SIGKILLed agent leaves it behind
80
+ forever, so orphans are garbage-collected once they exceed this age */
81
+ const SESSION_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
78
82
  /* CLI command "ase hook" */
79
83
  export default class HookCommand {
80
84
  log;
@@ -85,6 +89,40 @@ export default class HookCommand {
85
89
  isValidSessionId(id) {
86
90
  return /^[A-Za-z0-9._-]+$/.test(id);
87
91
  }
92
+ /* resolve the base directory holding all per-session state */
93
+ sessionBaseDir() {
94
+ return path.join(os.homedir(), ".ase", "session");
95
+ }
96
+ /* garbage-collect orphaned session directories left behind by agents
97
+ which died before their session-end hook could run; a live session
98
+ keeps its directory's mtime current, as every tool call acquires a
99
+ lock file inside it, so plain age is a reliable liveness signal */
100
+ pruneStaleSessions(currentSessionId) {
101
+ const base = this.sessionBaseDir();
102
+ let entries;
103
+ try {
104
+ entries = fs.readdirSync(base, { withFileTypes: true });
105
+ }
106
+ catch (_e) {
107
+ /* best-effort: no base directory yet, or unreadable */
108
+ return;
109
+ }
110
+ const deadline = Date.now() - SESSION_MAX_AGE_MS;
111
+ for (const entry of entries) {
112
+ if (!entry.isDirectory() || entry.name === currentSessionId)
113
+ continue;
114
+ const dir = path.join(base, entry.name);
115
+ try {
116
+ if (fs.statSync(dir).mtimeMs >= deadline)
117
+ continue;
118
+ fs.rmSync(dir, { recursive: true, force: true });
119
+ this.log.write("debug", `hook: pruned stale session directory: ${dir}`);
120
+ }
121
+ catch (_e) {
122
+ /* best-effort: ignore vanished or undeletable directories */
123
+ }
124
+ }
125
+ }
88
126
  /* drain and discard the stdin event payload */
89
127
  async drainStdin() {
90
128
  await readStdin().catch(() => "");
@@ -176,14 +214,14 @@ export default class HookCommand {
176
214
  try {
177
215
  pkg = fs.readFileSync(filePkg, "utf8");
178
216
  }
179
- catch (_e) {
180
- throw new Error(`failed to read plugin manifest: ${filePkg}`);
217
+ catch (err) {
218
+ throw new Error(`failed to read plugin manifest: ${filePkg}`, { cause: err });
181
219
  }
182
220
  try {
183
221
  md = fs.readFileSync(fileMd, "utf8");
184
222
  }
185
- catch (_e) {
186
- throw new Error(`failed to read constitution file: ${fileMd}`);
223
+ catch (err) {
224
+ throw new Error(`failed to read constitution file: ${fileMd}`, { cause: err });
187
225
  }
188
226
  /* determine own version */
189
227
  const pkgObj = this.parseJSON(pkg, v.object({ version: v.optional(v.string()) }));
@@ -210,6 +248,8 @@ export default class HookCommand {
210
248
  }));
211
249
  /* determine session id */
212
250
  const sessionId = this.pickSessionId(input);
251
+ /* garbage-collect orphaned session directories of previous agent runs */
252
+ this.pruneStaleSessions(sessionId);
213
253
  /* establish config context (session-scoped only if a valid sessionId is present) */
214
254
  const hasSession = this.isValidSessionId(sessionId);
215
255
  const cfg = new Config("config", configSchema, this.log, hasSession ? parseScope(`session:${sessionId}`) : parseScope(undefined));
@@ -363,7 +403,7 @@ export default class HookCommand {
363
403
  const sessionId = await this.readSessionIdFromStdin();
364
404
  /* remove the session directory ~/.ase/session/<id> (only for a valid sessionId) */
365
405
  if (this.isValidSessionId(sessionId)) {
366
- const dir = path.join(os.homedir(), ".ase", "session", sessionId);
406
+ const dir = path.join(this.sessionBaseDir(), sessionId);
367
407
  try {
368
408
  fs.rmSync(dir, { recursive: true, force: true });
369
409
  }
@@ -215,7 +215,7 @@ export class Markdown {
215
215
  }
216
216
  if (fence > 0 && (ch === "\r" || ch === "\n")) {
217
217
  /* consume optional CR followed by mandatory LF */
218
- let nl = "";
218
+ let nl;
219
219
  if (ch === "\r" && i + 1 < text.length && text[i + 1] === "\n") {
220
220
  nl = "\r\n";
221
221
  i += 2;
package/dst/ase-meta.js CHANGED
@@ -41,7 +41,7 @@ export class Meta {
41
41
  }
42
42
  catch (err) {
43
43
  const message = err instanceof Error ? err.message : String(err);
44
- throw new Error(`meta: failed to read file: ${abs} (${message})`);
44
+ throw new Error(`meta: failed to read file: ${abs} (${message})`, { cause: err });
45
45
  }
46
46
  }
47
47
  }
package/dst/ase-setup.js CHANGED
@@ -47,7 +47,7 @@ export default class SetupCommand {
47
47
  return false;
48
48
  /* determine the npm global prefix and probe writability of the
49
49
  directories that "npm -g" actually mutates */
50
- let prefix = "";
50
+ let prefix;
51
51
  try {
52
52
  const result = await execa("npm", ["prefix", "-g"], { stdio: "pipe" });
53
53
  prefix = result.stdout.trim();
@@ -229,7 +229,7 @@ export default class StatuslineCommand {
229
229
  }
230
230
  catch (err) {
231
231
  const message = err instanceof Error ? err.message : String(err);
232
- throw new Error(`statusline: invalid JSON on stdin: ${message}`);
232
+ throw new Error(`statusline: invalid JSON on stdin: ${message}`, { cause: err });
233
233
  }
234
234
  /* normalize Copilot CLI's top-level "cwd" into the
235
235
  "workspace.current_dir" structure shared with Anthropic Claude Code CLI */
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.59",
9
+ "version": "0.9.60",
10
10
  "license": "Apache-2.0",
11
11
  "author": {
12
12
  "name": "Dr. Ralf S. Engelschall",
@@ -16,22 +16,22 @@
16
16
  "type": "module",
17
17
  "bin": { "ase": "bin/ase" },
18
18
  "devDependencies": {
19
- "eslint": "9.39.4",
20
- "@eslint/js": "9.39.4",
21
- "@typescript-eslint/parser": "8.66.0",
22
- "@typescript-eslint/eslint-plugin": "8.66.0",
19
+ "eslint": "10.9.0",
20
+ "@eslint/js": "10.0.1",
21
+ "@typescript-eslint/parser": "8.67.0",
22
+ "@typescript-eslint/eslint-plugin": "8.67.0",
23
+ "typescript-eslint": "8.67.0",
23
24
  "eslint-plugin-promise": "7.3.0",
24
- "eslint-plugin-import": "2.32.0",
25
- "neostandard": "0.13.0",
26
- "globals": "17.9.0",
25
+ "neostandard": "0.14.0-next.1",
26
+ "globals": "17.11.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.2",
34
- "@types/luxon": "3.7.3",
33
+ "@types/node": "26.2.0",
34
+ "@types/luxon": "3.7.5",
35
35
  "@types/which": "3.0.4",
36
36
  "@types/update-notifier": "6.0.8",
37
37
  "@types/shell-quote": "1.7.5",
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "commander": "15.0.0",
45
- "@dotenvx/dotenvx": "2.19.2",
45
+ "@dotenvx/dotenvx": "2.21.0",
46
46
  "yaml": "2.9.0",
47
47
  "valibot": "1.4.2",
48
48
  "execa": "10.0.1",
@@ -72,9 +72,8 @@
72
72
  },
73
73
  "engines": {
74
74
  "npm": ">=10.0.0",
75
- "node": ">=22.0.0"
75
+ "node": ">=22.13.0"
76
76
  },
77
- "upd": [ "!eslint", "!@eslint/js" ],
78
77
  "files": [
79
78
  "dst/**/*",
80
79
  "bin/**/*",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ase",
3
- "version": "0.9.59",
3
+ "version": "0.9.60",
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.59",
3
+ "version": "0.9.60",
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.59",
3
+ "version": "0.9.60",
4
4
  "description": "Agentic Software Engineering (ASE)",
5
5
  "keywords": [ "agentic", "software", "engineering" ],
6
6
  "homepage": "https://ase.tools",
@@ -138,7 +138,7 @@ Set <args>--int-reuse-task</args>.
138
138
  <template/> and then *STOP*. Do *not* implement the plan.
139
139
 
140
140
  <template>
141
- ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ✪ plan: **<words/>** words, ▶ status: **plan finalized -- done**
141
+ ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ▶ status: **plan finalized -- done**
142
142
  </template>
143
143
  </if>
144
144
 
@@ -83,16 +83,14 @@ Task Skill Common Steps
83
83
 
84
84
  - If <text/> starts with `ERROR:` or `WARNING:`:
85
85
  Set <task-content></task-content> (set task content to empty).
86
- Set <words/> to "0".
87
86
 
88
87
  - If <text/> does NOT start with `ERROR:` and NOT with `WARNING:`:
89
88
  Set <task-content><text/></task-content> (set task content to text).
90
- Calculate the number of words <words/> of <task-content/>.
91
89
 
92
90
  Only output the following <template/>:
93
91
 
94
92
  <template>
95
- ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ✪ plan: **<words/>** words, ▶ status: **<status/>**
93
+ ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ▶ status: **<status/>**
96
94
  </template>
97
95
 
98
96
  </define>
@@ -109,16 +107,18 @@ had no `Created:` frontmatter key), set
109
107
  (fall back to the modified timestamp). Re-insert the current
110
108
  <ase-task-id/>, the original <timestamp-created/>, and the
111
109
  refreshed <timestamp-modified/> into the frontmatter keys `Id:`,
112
- `Created:`, and `Modified:` of <task-content/> and calculate
113
- the number of words <words/> of <task-content/>.
110
+ `Created:`, and `Modified:` of <task-content/>.
114
111
 
115
112
  Call the `ase_task_save(id: "<ase-task-id/>", text:
116
113
  "<task-content/>")` tool of the `ase` MCP server to save the task
117
- plan content in its *authoring form*. Do not output anything
114
+ plan content in its *authoring form*. This `ase_task_save` MCP
115
+ tool call is the *only* permitted way to persist the task plan --
116
+ you *MUST* *NEVER* write the plan file via `Write`/`Edit` or by
117
+ executing a shell command. Do not output anything
118
118
  related to this MCP call except the following <template/>:
119
119
 
120
120
  <template>
121
- ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ✪ plan: **<words/>** words, ▶ status: **<arg1/>**
121
+ ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ▶ status: **<arg1/>**
122
122
  </template>
123
123
 
124
124
  </define>
@@ -169,7 +169,7 @@ Only output the following <template/> and then call the tool
169
169
  stop processing the current skill once the `Skill` tool was used.
170
170
 
171
171
  <template>
172
- ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ✪ plan: **<words/>** words, ▶ status: **<arg2/>**
172
+ ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ▶ status: **<arg2/>**
173
173
  </template>
174
174
 
175
175
  </define>
@@ -102,8 +102,10 @@ following procedure:
102
102
  </ase-tpl-boxed>
103
103
  </text>
104
104
 
105
- If <n/> is less than 2:
106
- Set <result>ERROR: custom-dialog requires 2-9 answer lines, got <n/></result>
105
+ If <n/> is less than 2 -- or less than 1 when <opts/> contains
106
+ `--other` and does *not* contain `--no-other`, as the free-text
107
+ path then complements a single answer option:
108
+ Set <result>ERROR: custom-dialog requires 2-9 (with free-text: 1-9) answer lines, got <n/></result>
107
109
  and *SKIP* the following step 2.2 and continue with step 2.3 dispatch.
108
110
 
109
111
  2. Output the following <template/>, end the current turn, wait for the
@@ -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.59",
9
+ "version": "0.9.60",
10
10
  "license": "Apache-2.0",
11
11
  "author": {
12
12
  "name": "Dr. Ralf S. Engelschall",
@@ -17,13 +17,13 @@
17
17
  "@rse/stx": "1.1.6",
18
18
  "markdownlint": "0.41.1",
19
19
  "markdownlint-cli2": "0.23.2",
20
- "eslint": "10.8.0",
20
+ "eslint": "10.9.0",
21
21
  "@eslint/markdown": "8.0.3",
22
- "eslint-markdown": "0.13.0"
22
+ "eslint-markdown": "0.14.0"
23
23
  },
24
24
  "engines": {
25
25
  "npm": ">=10.0.0",
26
- "node": ">=22.0.0"
26
+ "node": ">=22.13.0"
27
27
  },
28
28
  "scripts": {
29
29
  "start": "stx -v4 -l warning -c etc/stx.conf"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: ase-code-craft
3
- argument-hint: "[--help|-h] [--auto|-a] [--dry|-d] [--direct|-D] [--quick|-Q] [--next|-n <option>[,...]] [<task-id>:] <feature>"
3
+ argument-hint: "[--help|-h] [--auto|-a] [--dry|-d] [--direct|-D] [--interactive|-i] [--quick|-Q] [--next|-n <option>[,...]] [<task-id>:] <feature>"
4
4
  description: >
5
5
  Craft Source Code:
6
6
  Use when user wants to "create", "add", or "craft" a new feature from scratch.
@@ -24,7 +24,7 @@ Craft Source Code
24
24
 
25
25
  <expand name="getopt"
26
26
  arg1="ase-code-craft"
27
- arg2="--auto|-a --dry|-d --direct|-D --quick|-Q --next|-n=(none|DONE|EDIT|GRILL|PREFLIGHT|IMPLEMENT)...">
27
+ arg2="--auto|-a --dry|-d --direct|-D --interactive|-i --quick|-Q --next|-n=(none|DONE|EDIT|GRILL|PREFLIGHT|IMPLEMENT)...">
28
28
  $ARGUMENTS
29
29
  </expand>
30
30
 
@@ -34,6 +34,11 @@ to `true`, <getopt-option-dry/> to `true`, and <getopt-option-next/> to
34
34
  `IMPLEMENT,DELETE`. Do not output anything.
35
35
  </if>
36
36
 
37
+ <if condition="<getopt-option-interactive/> is equal `true`">
38
+ The `--interactive`/`-i` flag *implies* the `--direct`/`-D` mode: set
39
+ <getopt-option-direct/> to `true`. Do not output anything.
40
+ </if>
41
+
37
42
  <objective>
38
43
  From scratch *craft* the following feature:
39
44
  <feature><getopt-arguments/></feature>
@@ -48,8 +53,9 @@ Procedure
48
53
 
49
54
  <if condition="<getopt-option-direct/> is not equal to 'true'">
50
55
  You *MUST* *NOT* call `Edit`, `Write`, `NotebookEdit`, or any
51
- filesystem-modifying tool during this entire skill. The *only*
52
- permitted way to persist artifacts is via `ase_task_save(...)`.
56
+ filesystem-modifying tool, nor execute any filesystem-modifying
57
+ shell command, during this entire skill. The *only* permitted way
58
+ to persist artifacts is via the `ase_task_save(...)` MCP tool.
53
59
  </if>
54
60
  <else>
55
61
  The `--direct`/`-D` mode applies the crafting *in place*, so STEP 4
@@ -162,7 +168,35 @@ crafting actually demands, and you *MUST* *NOT* call
162
168
  ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ▶ status: **changes directly applied**
163
169
  </template>
164
170
 
165
- 3. Then *IMMEDIATELY* *STOP* all further skill processing. You
171
+ 3. <if condition="<getopt-option-interactive/> is equal `true`">
172
+ Enter the *interactive crafting loop*:
173
+
174
+ <while condition="`true`">
175
+
176
+ 1. In the following, you *MUST* *NOT* use your built-in
177
+ <user-dialog-tool/> tool! Instead, you *MUST* just show a
178
+ custom dialog according to the expanded `custom-dialog`
179
+ definition. You *MUST* closely follow this definition.
180
+
181
+ Ask the user for the next change with the following
182
+ custom dialog:
183
+
184
+ <expand name="custom-dialog" arg1="--other">
185
+ Next Change: What is the next change to craft?
186
+ DONE: Finish the interactive crafting loop
187
+ </expand>
188
+
189
+ 2. If <result/> is `DONE` or `CANCEL`, <break/>.
190
+
191
+ 3. Otherwise <result/> has the format `OTHER: <text/>`: set
192
+ <feature/> to <text/>, then directly craft it and report
193
+ it exactly as specified by the items 1 and 2 above
194
+ (including the CHANGELOG.md entry).
195
+
196
+ </while>
197
+ </if>
198
+
199
+ 4. Then *IMMEDIATELY* *STOP* all further skill processing. You
166
200
  *MUST* *NOT* output anything else in this STEP 4 or after it --
167
201
  *independent* of <ase-project-boxing/>, whose exposure rules
168
202
  are explicitly *overridden* here. Especially, do not output a
@@ -207,16 +241,16 @@ crafting actually demands, and you *MUST* *NOT* call
207
241
  `ase` MCP server and use the `text` field of its response for
208
242
  <timestamp-created/> and <timestamp-modified/> information. Then
209
243
  insert the current <ase-task-id/>, <timestamp-created/>,
210
- <timestamp-modified/>, and <task-kind/> information and calculate
211
- the number of words <words/> of <task-content/>.
244
+ <timestamp-modified/>, and <task-kind/> information.
212
245
 
213
246
  3. You then *MUST* *save* the resulting plan content with the
214
- `ase_task_save(id: "<ase-task-id/>", text: "<task-content/>")`.
247
+ `ase_task_save(id: "<ase-task-id/>", text: "<task-content/>")`
248
+ MCP tool call only -- *NEVER* by executing a shell command.
215
249
 
216
250
  4. Output a hint with the following <template/>:
217
251
 
218
252
  <template>
219
- ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ✪ plan: **<words/>** words, ▶ status: **plan created**
253
+ ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ▶ status: **plan created**
220
254
  </template>
221
255
 
222
256
  5. Directly pass-through control to the next skill:
@@ -10,6 +10,7 @@
10
10
  [`--auto`|`-a`]
11
11
  [`--dry`|`-d`]
12
12
  [`--direct`|`-D`]
13
+ [`--interactive`|`-i`]
13
14
  [`--quick`|`-Q`]
14
15
  [`--next`|`-n` *option*[,...]]
15
16
  [*task-id*:] *feature*
@@ -51,6 +52,12 @@ entirely and applies the change set to the affected artifacts itself.
51
52
  `--next` have no effect, as neither approaches are proposed nor a
52
53
  plan is composed.
53
54
 
55
+ `--interactive`|`-i`:
56
+ Craft *interactively*: implies `--direct` and, once the initial
57
+ *feature* has been applied, repeatedly asks for the *next change*
58
+ with a free-text dialog and immediately applies it in place, too,
59
+ until the user stops the loop by answering `DONE`.
60
+
54
61
  `--quick`|`-Q`:
55
62
  Shorthand alias for `-a -d -n IMPLEMENT,DELETE`: automatically pick
56
63
  the recommended feature approach, compose the plan *without* the
@@ -96,6 +103,12 @@ Craft a feature under a named task and directly hand off to implementation:
96
103
  ❯ /ase-code-craft --next IMPLEMENT auth: add JWT authentication middleware
97
104
  ```
98
105
 
106
+ Craft interactively, applying one change after the other in place:
107
+
108
+ ```text
109
+ ❯ /ase-code-craft -i add a --verbose option to the CLI
110
+ ```
111
+
99
112
  ## SEE ALSO
100
113
 
101
114
  [`ase-code-refactor`](../ase-code-refactor/help.md), [`ase-code-resolve`](../ase-code-resolve/help.md), [`ase-task-edit`](../ase-task-edit/help.md),