@rse/ase 0.9.48 → 0.9.49
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 +45 -1
- package/dst/ase-guidance.js +89 -0
- package/dst/ase-hook.js +17 -6
- package/dst/ase-service.js +0 -2
- package/dst/ase-setup.js +7 -5
- package/dst/ase-statusline.js +28 -4
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/.github/plugin/plugin.json +1 -1
- package/plugin/meta/ase-common-task.md +9 -0
- package/plugin/meta/ase-constitution.md +17 -6
- package/plugin/meta/ase-getopt.md +11 -1
- package/plugin/meta/ase-skill.md +92 -4
- package/plugin/package.json +1 -1
- package/plugin/skills/ase-arch-analyze/SKILL.md +8 -6
- package/plugin/skills/ase-arch-discover/SKILL.md +12 -0
- package/plugin/skills/ase-code-analyze/SKILL.md +6 -4
- package/plugin/skills/ase-code-lint/SKILL.md +16 -0
- package/plugin/skills/ase-docs-proofread/SKILL.md +10 -0
- package/plugin/skills/ase-help-skill/SKILL.md +10 -2
- package/plugin/skills/ase-help-skill/catalog.md +1 -1
- package/plugin/skills/ase-meta-brainstorm/SKILL.md +8 -0
- package/plugin/skills/ase-meta-commit/SKILL.md +13 -0
- package/plugin/skills/ase-meta-config/SKILL.md +165 -0
- package/plugin/skills/ase-meta-config/help.md +136 -0
- package/plugin/skills/ase-meta-diaboli/help.md +1 -1
- package/plugin/skills/ase-meta-diff/SKILL.md +22 -0
- package/plugin/skills/ase-meta-review/SKILL.md +13 -1
- package/plugin/skills/ase-sync-export/SKILL.md +14 -0
- package/plugin/skills/ase-sync-import/SKILL.md +10 -0
- package/plugin/skills/ase-task-list/SKILL.md +21 -0
- package/plugin/skills/ase-task-view/SKILL.md +21 -0
- package/plugin/skills/ase-meta-persona/SKILL.md +0 -84
- package/plugin/skills/ase-meta-persona/help.md +0 -60
package/dst/ase-config.js
CHANGED
|
@@ -22,7 +22,8 @@ export const projectClassification = {
|
|
|
22
22
|
};
|
|
23
23
|
/* agent classification taxonomy */
|
|
24
24
|
export const agentClassification = {
|
|
25
|
-
persona: ["writer", "engineer", "journalist", "telegrapher", "caveman"]
|
|
25
|
+
persona: ["writer", "engineer", "journalist", "telegrapher", "caveman"],
|
|
26
|
+
guidance: ["none", "minimal", "normal", "verbose"]
|
|
26
27
|
};
|
|
27
28
|
/* classification presets */
|
|
28
29
|
export const projectClassificationPresets = {
|
|
@@ -41,6 +42,7 @@ export const projectClassificationPresets = {
|
|
|
41
42
|
default: {
|
|
42
43
|
"agent.task": "default",
|
|
43
44
|
"agent.persona": "engineer",
|
|
45
|
+
"agent.guidance": "normal",
|
|
44
46
|
"project.id": "example",
|
|
45
47
|
"project.name": "Example Project",
|
|
46
48
|
"project.boxing": "white",
|
|
@@ -190,6 +192,7 @@ export const configSchema = v.nullish(v.strictObject({
|
|
|
190
192
|
})),
|
|
191
193
|
agent: v.optional(v.strictObject({
|
|
192
194
|
persona: v.optional(v.picklist(agentClassification.persona)),
|
|
195
|
+
guidance: v.optional(v.picklist(agentClassification.guidance)),
|
|
193
196
|
task: v.optional(v.pipe(v.string(), v.minLength(1))),
|
|
194
197
|
skill: v.optional(v.pipe(v.string(), v.minLength(1)))
|
|
195
198
|
}))
|
|
@@ -768,5 +771,46 @@ export class ConfigMCP {
|
|
|
768
771
|
return mcpToolError(err);
|
|
769
772
|
}
|
|
770
773
|
});
|
|
774
|
+
/* config list */
|
|
775
|
+
mcp.registerTool("ase_config_list", {
|
|
776
|
+
title: "ASE config list",
|
|
777
|
+
description: "List all effective configuration entries of the layered configuration, " +
|
|
778
|
+
"cascading through the default/user/project/task/session chain up to and " +
|
|
779
|
+
"including the requested `scope`. Returns an `entries` array (in lexicographic " +
|
|
780
|
+
"`key` order) where each item has the dotted `key`, its effective `value`, and " +
|
|
781
|
+
"the `scope` label (\"default\", \"user\", \"project\", \"task:<id>\", or " +
|
|
782
|
+
"\"session:<id>\") that supplied it. For overlapping keys only the value of the " +
|
|
783
|
+
"strongest scope is reported.",
|
|
784
|
+
inputSchema: {
|
|
785
|
+
scope: z.string()
|
|
786
|
+
.describe("scope chain (e.g. \"session:<id>\", \"task:<id>\", \"project\", \"user\")")
|
|
787
|
+
},
|
|
788
|
+
outputSchema: {
|
|
789
|
+
entries: z.array(z.object({
|
|
790
|
+
key: z.string().describe("dotted configuration key"),
|
|
791
|
+
value: z.string().describe("effective configuration value"),
|
|
792
|
+
scope: z.string().describe("scope label which supplied the value")
|
|
793
|
+
})).describe("all effective configuration entries in lexicographic key order")
|
|
794
|
+
}
|
|
795
|
+
}, async (args) => {
|
|
796
|
+
try {
|
|
797
|
+
const scope = parseScope(args.scope);
|
|
798
|
+
const cfg = new Config("config", configSchema, this.log, scope);
|
|
799
|
+
cfg.read();
|
|
800
|
+
const entries = cfg.entries().map((e) => ({
|
|
801
|
+
key: e.key,
|
|
802
|
+
value: String(isScalar(e.value) ? e.value.value : e.value),
|
|
803
|
+
scope: Config.scopeLabel(e.scope)
|
|
804
|
+
}));
|
|
805
|
+
const result = { entries };
|
|
806
|
+
return {
|
|
807
|
+
structuredContent: result,
|
|
808
|
+
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
catch (err) {
|
|
812
|
+
return mcpToolError(err);
|
|
813
|
+
}
|
|
814
|
+
});
|
|
771
815
|
}
|
|
772
816
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/*
|
|
2
|
+
** Agentic Software Engineering (ASE)
|
|
3
|
+
** Copyright (c) 2025-2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
|
+
** Licensed under Apache 2.0 <https://spdx.org/licenses/Apache-2.0>
|
|
5
|
+
*/
|
|
6
|
+
import { isScalar } from "yaml";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { Config, configSchema, parseScope, agentClassification } from "./ase-config.js";
|
|
9
|
+
/* reusable functionality: ASE agent guidance level get/set */
|
|
10
|
+
export class Guidance {
|
|
11
|
+
/* allowed guidance level values */
|
|
12
|
+
static levels = agentClassification.guidance;
|
|
13
|
+
/* get the effective guidance level for an optional session;
|
|
14
|
+
returns the default "normal" if nothing is configured */
|
|
15
|
+
static get(log, session) {
|
|
16
|
+
const scope = parseScope(session !== undefined ? `session:${session}` : undefined);
|
|
17
|
+
const cfg = new Config("config", configSchema, log, scope);
|
|
18
|
+
cfg.read();
|
|
19
|
+
const val = cfg.get("agent.guidance");
|
|
20
|
+
if (val === undefined)
|
|
21
|
+
return "normal";
|
|
22
|
+
const level = String(isScalar(val) ? val.value : val);
|
|
23
|
+
return Guidance.levels.find((l) => l === level) ?? "normal";
|
|
24
|
+
}
|
|
25
|
+
/* set the guidance level on the strongest scope of an optional session */
|
|
26
|
+
static set(log, level, session) {
|
|
27
|
+
const scope = parseScope(session !== undefined ? `session:${session}` : undefined);
|
|
28
|
+
const cfg = new Config("config", configSchema, log, scope);
|
|
29
|
+
cfg.lock(() => {
|
|
30
|
+
cfg.read();
|
|
31
|
+
cfg.set("agent.guidance", level);
|
|
32
|
+
cfg.write();
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/* MCP registration entry point for guidance tools */
|
|
37
|
+
export default class GuidanceMCP {
|
|
38
|
+
log;
|
|
39
|
+
constructor(log) {
|
|
40
|
+
this.log = log;
|
|
41
|
+
}
|
|
42
|
+
register(mcp) {
|
|
43
|
+
mcp.registerTool("ase_guidance", {
|
|
44
|
+
title: "ASE guidance level get/set",
|
|
45
|
+
description: "Get or set the active ASE agent guidance `level`. " +
|
|
46
|
+
"If `level` is provided, it sets the guidance level, " +
|
|
47
|
+
"otherwise it returns the current guidance `level`. " +
|
|
48
|
+
"If `session` is provided, the operation is scoped to that session, " +
|
|
49
|
+
"otherwise it operates on the strongest/closest scope (user/project cascade). " +
|
|
50
|
+
"The guidance level controls the amount of unsolicited help hints -- " +
|
|
51
|
+
"pointers to the available ASE skills, their options, and the recommended " +
|
|
52
|
+
"next operations -- the agent emits. " +
|
|
53
|
+
"Allowed levels: \"none\" (no help hints at all), " +
|
|
54
|
+
"\"minimal\" (the single most essential help hint only), " +
|
|
55
|
+
"\"normal\" (the essential help hints only), " +
|
|
56
|
+
"\"verbose\" (all available help hints).",
|
|
57
|
+
inputSchema: {
|
|
58
|
+
level: z.enum(Guidance.levels).optional()
|
|
59
|
+
.describe("guidance level to set; if omitted, the current guidance level is returned"),
|
|
60
|
+
session: z.string().regex(/^[A-Za-z0-9._-]+$/).optional()
|
|
61
|
+
.describe("session identifier (allowed characters: A-Z, a-z, 0-9, '.', '_', '-'); " +
|
|
62
|
+
"if omitted, the operation is not scoped to a specific session")
|
|
63
|
+
}
|
|
64
|
+
}, async (args) => {
|
|
65
|
+
try {
|
|
66
|
+
if (args.level !== undefined) {
|
|
67
|
+
Guidance.set(this.log, args.level, args.session);
|
|
68
|
+
const where = args.session !== undefined ?
|
|
69
|
+
` for session "${args.session}"` : "";
|
|
70
|
+
const msg = `OK: set agent.guidance to "${args.level}"${where}`;
|
|
71
|
+
return {
|
|
72
|
+
content: [{ type: "text", text: msg }]
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const text = Guidance.get(this.log, args.session);
|
|
76
|
+
return {
|
|
77
|
+
content: [{ type: "text", text }]
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
82
|
+
return {
|
|
83
|
+
isError: true,
|
|
84
|
+
content: [{ type: "text", text: `ERROR: ${message}` }]
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
package/dst/ase-hook.js
CHANGED
|
@@ -249,8 +249,9 @@ export default class HookCommand {
|
|
|
249
249
|
const val = cfg.get(key);
|
|
250
250
|
return typeof val === "string" ? val : (process.env[envVar] ?? dflt);
|
|
251
251
|
};
|
|
252
|
-
/* determine agent persona style and project boxing transparency */
|
|
252
|
+
/* determine agent persona style, agent guidance level, and project boxing transparency */
|
|
253
253
|
const persona = setting("agent.persona", "ASE_PERSONA_STYLE", "engineer");
|
|
254
|
+
const guidance = setting("agent.guidance", "ASE_GUIDANCE_LEVEL", "normal");
|
|
254
255
|
const boxing = setting("project.boxing", "ASE_PROJECT_BOXING", "white");
|
|
255
256
|
/* determine headless mode */
|
|
256
257
|
const headless = (process.env.ASE_HEADLESS ?? "false") === "true" ? "true" : "false";
|
|
@@ -281,6 +282,7 @@ export default class HookCommand {
|
|
|
281
282
|
`<ase-version-hint>${versionHint}</ase-version-hint>\n` +
|
|
282
283
|
`<ase-plugin-root>${pluginRoot}</ase-plugin-root>\n` +
|
|
283
284
|
`<ase-persona-style>${persona}</ase-persona-style>\n` +
|
|
285
|
+
`<ase-guidance-level>${guidance}</ase-guidance-level>\n` +
|
|
284
286
|
`<ase-user-id>${userId}</ase-user-id>\n` +
|
|
285
287
|
`<ase-project-id>${projectId}</ase-project-id>\n` +
|
|
286
288
|
`<ase-project-boxing>${boxing}</ase-project-boxing>\n` +
|
|
@@ -295,11 +297,18 @@ export default class HookCommand {
|
|
|
295
297
|
agent harness, independent of any model decision, so it is
|
|
296
298
|
guaranteed to appear once in every non-headless session;
|
|
297
299
|
Anthropic Claude Code CLI and OpenAI Codex CLI surface a top-level
|
|
298
|
-
"systemMessage" field for this -- GitHub Copilot CLI has no equivalent)
|
|
299
|
-
|
|
300
|
+
"systemMessage" field for this -- GitHub Copilot CLI has no equivalent);
|
|
301
|
+
the trailing help hint is emitted only if the guidance level asks for it */
|
|
302
|
+
const banner = "\n" +
|
|
303
|
+
`\n⧉ ASE: ⎈ version: ${versionCurrentPlugin}${versionHint !== "" ? " " + versionHint.replaceAll(/\*/g, "") : ""}` +
|
|
300
304
|
`\n⧉ ASE: ※ user: ${userId}, ⚑ project: ${projectId}` +
|
|
301
305
|
`\n⧉ ASE: ◉ task: ${taskId}, ⏻ session: ${sessionId}` +
|
|
302
|
-
`\n⧉ ASE: ☯ persona: ${persona}, ▢ boxing: ${boxing}
|
|
306
|
+
`\n⧉ ASE: ☯ persona: ${persona}, ▶ guidance: ${guidance}, ▢ boxing: ${boxing}` +
|
|
307
|
+
(guidance === "normal" || guidance === "verbose" ?
|
|
308
|
+
"\n" +
|
|
309
|
+
"\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";
|
|
303
312
|
/* inject markdown into session context.
|
|
304
313
|
Anthropic Claude Code CLI and OpenAI Codex CLI expect the context nested in
|
|
305
314
|
"hookSpecificOutput"; GitHub Copilot CLI expects a flat top-level
|
|
@@ -314,8 +323,10 @@ export default class HookCommand {
|
|
|
314
323
|
};
|
|
315
324
|
/* attach the deterministic banner as a top-level "systemMessage"
|
|
316
325
|
(only for the harnesses that support it and only when not
|
|
317
|
-
running headless --
|
|
318
|
-
|
|
326
|
+
running headless -- complementing the constitution box condition,
|
|
327
|
+
which covers exactly the remaining harness GitHub Copilot CLI
|
|
328
|
+
by letting the model emit the banner itself) */
|
|
329
|
+
if ((tool === "claude" || tool === "codex") && headless !== "true" && guidance !== "none")
|
|
319
330
|
payload.systemMessage = banner;
|
|
320
331
|
await writeStdout(JSON.stringify(payload));
|
|
321
332
|
return 0;
|
package/dst/ase-service.js
CHANGED
|
@@ -22,7 +22,6 @@ import { TaskMCP } from "./ase-task.js";
|
|
|
22
22
|
import { MarkdownMCP } from "./ase-markdown.js";
|
|
23
23
|
import { ArtifactMCP } from "./ase-artifact.js";
|
|
24
24
|
import { KVMCP } from "./ase-kv.js";
|
|
25
|
-
import PersonaMCP from "./ase-persona.js";
|
|
26
25
|
import { TimestampMCP } from "./ase-timestamp.js";
|
|
27
26
|
import { GetoptMCP } from "./ase-getopt.js";
|
|
28
27
|
import { SkillsMCP } from "./ase-skills.js";
|
|
@@ -243,7 +242,6 @@ export default class ServiceCommand {
|
|
|
243
242
|
new MarkdownMCP().register(mcp);
|
|
244
243
|
new ArtifactMCP(this.log).register(mcp);
|
|
245
244
|
new KVMCP().register(mcp);
|
|
246
|
-
new PersonaMCP(this.log).register(mcp);
|
|
247
245
|
new TimestampMCP().register(mcp);
|
|
248
246
|
new GetoptMCP().register(mcp);
|
|
249
247
|
new SkillsMCP().register(mcp);
|
package/dst/ase-setup.js
CHANGED
|
@@ -575,7 +575,7 @@ export default class SetupCommand {
|
|
|
575
575
|
statuslineFormatDflt = [
|
|
576
576
|
"<blue>%u</blue> <red>%p</red> <black>%T</black> %s",
|
|
577
577
|
"%m %e %t",
|
|
578
|
-
"%P %c"
|
|
578
|
+
"%P %h %c"
|
|
579
579
|
];
|
|
580
580
|
/* resolve the tool settings file for a given installation scope */
|
|
581
581
|
statuslineSettingsFile(tool, scope) {
|
|
@@ -607,8 +607,9 @@ export default class SetupCommand {
|
|
|
607
607
|
}
|
|
608
608
|
/* build the "ase statusline ..." command string from the activate
|
|
609
609
|
options and the effective format lines */
|
|
610
|
-
statuslineCommand(opts, format) {
|
|
611
|
-
const parts = ["ase", "statusline", "-w", String(opts.width), "-m", String(opts.margin)
|
|
610
|
+
statuslineCommand(tool, opts, format) {
|
|
611
|
+
const parts = ["ase", "statusline", "--tool", tool, "-w", String(opts.width), "-m", String(opts.margin),
|
|
612
|
+
"-p", String(opts.padding)];
|
|
612
613
|
if (!opts.icons)
|
|
613
614
|
parts.push("--no-icons");
|
|
614
615
|
if (!opts.labels)
|
|
@@ -699,7 +700,7 @@ export default class SetupCommand {
|
|
|
699
700
|
this.requireStatuslineTool(tool);
|
|
700
701
|
this.requireStatuslineScope(tool, scope);
|
|
701
702
|
const file = this.statuslineSettingsFile(tool, scope);
|
|
702
|
-
const command = this.statuslineCommand(opts, format);
|
|
703
|
+
const command = this.statuslineCommand(tool, opts, format);
|
|
703
704
|
const root = await this.statuslineReadAst(file);
|
|
704
705
|
const existing = this.statuslineFindMember(root);
|
|
705
706
|
if (existing !== undefined) {
|
|
@@ -923,10 +924,11 @@ export default class SetupCommand {
|
|
|
923
924
|
.option("-s, --scope <scope>", "target scope (\"user\", \"project\", or \"local\")", "user")
|
|
924
925
|
.option("-w, --width <n>", "force terminal width to <n> characters (0 = auto-detect via /dev/tty)", parseNonNeg("width"), 0)
|
|
925
926
|
.option("-m, --margin <n>", "reduce maximum used terminal width by <n> characters on each side", parseNonNeg("margin"), 2)
|
|
927
|
+
.option("-p, --padding <n>", "pad each rendered line with <n> spaces on each side", parseNonNeg("padding"), 0)
|
|
926
928
|
.option("--no-icons", "disable icons in placeholder rendering")
|
|
927
929
|
.option("--no-labels", "disable labels in front of bold values")
|
|
928
930
|
.action(async (format, opts) => {
|
|
929
|
-
process.exit(await this.doStatuslineActivate(this.parseTool(opts.tool), this.parseScope(opts.scope), { width: opts.width, margin: opts.margin, icons: opts.icons, labels: opts.labels }, format ?? []));
|
|
931
|
+
process.exit(await this.doStatuslineActivate(this.parseTool(opts.tool), this.parseScope(opts.scope), { width: opts.width, margin: opts.margin, padding: opts.padding, icons: opts.icons, labels: opts.labels }, format ?? []));
|
|
930
932
|
});
|
|
931
933
|
/* register CLI sub-command "ase setup statusline deactivate" */
|
|
932
934
|
statuslineCmd
|
package/dst/ase-statusline.js
CHANGED
|
@@ -189,9 +189,10 @@ export default class StatuslineCommand {
|
|
|
189
189
|
.option("-t, --tool <tool>", "target tool (\"claude\" or \"copilot\"; \"codex\" is unsupported)", toolDflt)
|
|
190
190
|
.option("-w, --width <n>", "force terminal width to <n> characters (0 = auto-detect via /dev/tty)", parseInteger("--width"), 0)
|
|
191
191
|
.option("-m, --margin <n>", "reduce maximum used terminal width by <n> characters on each side", parseInteger("--margin"), 2)
|
|
192
|
+
.option("-p, --padding <n>", "pad each rendered line with <n> spaces on each side", parseInteger("--padding"), 0)
|
|
192
193
|
.option("--no-icons", "disable icons in placeholder rendering")
|
|
193
194
|
.option("--no-labels", "disable labels in front of bold values")
|
|
194
|
-
.argument("[lines...]", "one or more template lines with %u %p %T %s %m %e %t %O %P %c %C %a %r " +
|
|
195
|
+
.argument("[lines...]", "one or more template lines with %u %p %T %s %m %e %t %O %P %h %c %C %a %r " +
|
|
195
196
|
"%S %D %W %Q %H %X %b %g %G %d %M %V placeholders and <color>...</color> markup " +
|
|
196
197
|
"(color: black, red, green, yellow, blue, magenta, cyan, white, default) " +
|
|
197
198
|
"(default: single line \"%m %e %t\")")
|
|
@@ -220,9 +221,11 @@ export default class StatuslineCommand {
|
|
|
220
221
|
&& typeof data.cwd === "string" && data.cwd !== "") {
|
|
221
222
|
data.workspace = { ...(data.workspace ?? {}), current_dir: data.cwd };
|
|
222
223
|
}
|
|
223
|
-
/* determine effective terminal width and budget
|
|
224
|
+
/* determine effective terminal width and budget
|
|
225
|
+
(the padding spaces occupy terminal columns, too,
|
|
226
|
+
so they have to be subtracted from the budget) */
|
|
224
227
|
const width = opts.width > 0 ? opts.width : detectTermWidth();
|
|
225
|
-
const budget = width > 0 ? width - 2 * opts.margin : 0;
|
|
228
|
+
const budget = width > 0 ? width - 2 * (opts.margin + opts.padding) : 0;
|
|
226
229
|
/* shared output state and append helper with auto-wrap;
|
|
227
230
|
the helper itself strips ANSI CSI escape sequences to
|
|
228
231
|
measure the raw visible width of the chunk */
|
|
@@ -264,20 +267,24 @@ export default class StatuslineCommand {
|
|
|
264
267
|
const getCfg = memoize(() => {
|
|
265
268
|
let taskId = process.env.ASE_TASK_ID ?? "";
|
|
266
269
|
let persona = process.env.ASE_PERSONA_STYLE ?? "";
|
|
270
|
+
let guidance = process.env.ASE_GUIDANCE_LEVEL ?? "";
|
|
267
271
|
try {
|
|
268
272
|
const cfg = new Config("config", configSchema, this.log, parseScope(`session:${getSession()}`));
|
|
269
273
|
cfg.read("lenient");
|
|
270
274
|
const t = String(cfg.get("agent.task") ?? "").trim();
|
|
271
275
|
const p = String(cfg.get("agent.persona") ?? "").trim();
|
|
276
|
+
const g = String(cfg.get("agent.guidance") ?? "").trim();
|
|
272
277
|
if (t !== "")
|
|
273
278
|
taskId = t;
|
|
274
279
|
if (p !== "")
|
|
275
280
|
persona = p;
|
|
281
|
+
if (g !== "")
|
|
282
|
+
guidance = g;
|
|
276
283
|
}
|
|
277
284
|
catch (_e) {
|
|
278
285
|
/* cascade unavailable; keep env-var fallbacks */
|
|
279
286
|
}
|
|
280
|
-
return { taskId, persona };
|
|
287
|
+
return { taskId, persona, guidance };
|
|
281
288
|
});
|
|
282
289
|
const getGit = memoize(() => probeGit(data.workspace?.current_dir ?? ""));
|
|
283
290
|
const getMem = memoize(() => probeMemory());
|
|
@@ -323,6 +330,11 @@ export default class StatuslineCommand {
|
|
|
323
330
|
if (persona !== "")
|
|
324
331
|
emit(`${prefix("☯", "persona")}${c.bold(persona)}`);
|
|
325
332
|
},
|
|
333
|
+
h: () => {
|
|
334
|
+
const { guidance } = getCfg();
|
|
335
|
+
if (guidance !== "")
|
|
336
|
+
emit(`${prefix("▶", "guidance")}${c.bold(guidance)}`);
|
|
337
|
+
},
|
|
326
338
|
/* ==== CONTEXT ==== */
|
|
327
339
|
c: () => {
|
|
328
340
|
const pct = Math.min(100, Math.max(0, Math.floor(data.context_window?.used_percentage ?? 0)));
|
|
@@ -462,6 +474,18 @@ export default class StatuslineCommand {
|
|
|
462
474
|
out += "\n";
|
|
463
475
|
col = 0;
|
|
464
476
|
}
|
|
477
|
+
/* optionally pad all non-empty lines with spaces on both sides
|
|
478
|
+
(the trailing empty chunk after the final line break and any
|
|
479
|
+
intentionally empty line are left untouched, so that no
|
|
480
|
+
whitespace-only line is produced) */
|
|
481
|
+
if (opts.padding > 0) {
|
|
482
|
+
const pad = " ".repeat(opts.padding);
|
|
483
|
+
out = out.split("\n").map((line) => line !== "" ? `${pad}${line}${pad}` : line).join("\n");
|
|
484
|
+
/* for Copilot, prefix with a zero-width non-breaking space
|
|
485
|
+
to prevent the first line to be unindented */
|
|
486
|
+
if (tool === "copilot")
|
|
487
|
+
out = "\u200B" + out;
|
|
488
|
+
}
|
|
465
489
|
/* send output */
|
|
466
490
|
await writeStdout(out);
|
|
467
491
|
/* optionally publish task id to the calling tmux pane as a per-pane user
|
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.
|
|
9
|
+
"version": "0.9.49",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"author": {
|
|
12
12
|
"name": "Dr. Ralf S. Engelschall",
|
|
@@ -45,6 +45,15 @@ Task Skill Common Steps
|
|
|
45
45
|
<template>
|
|
46
46
|
⧉ **ASE**: ☻ skill: **<arg1/>**, ▶ ERROR: expected single `[<id>]` argument
|
|
47
47
|
</template>
|
|
48
|
+
|
|
49
|
+
Directly *after* this error <template/>, and *before* stopping,
|
|
50
|
+
give the corrective hint by expanding the following (which,
|
|
51
|
+
depending on the configured <ase-guidance-level/>, may expand into
|
|
52
|
+
nothing and hence emit no output at all):
|
|
53
|
+
|
|
54
|
+
<ase-tpl-hint level="verbose">
|
|
55
|
+
Run `/<arg1/> --help` for the accepted arguments of this skill, or use `/ase-help-intent` to have a fitting command proposed for a free-text intent.
|
|
56
|
+
</ase-tpl-hint>
|
|
48
57
|
</elseif>
|
|
49
58
|
|
|
50
59
|
</define>
|
|
@@ -6,17 +6,28 @@ You are an expert-level AI coding assistant.
|
|
|
6
6
|
You have the **Agentic Software Engineering (ASE)** companion toolkit enabled,
|
|
7
7
|
which boosts you to an expert-level Software Engineering AI agent.
|
|
8
8
|
|
|
9
|
-
If ((<ase-agent-tool/> is equal to `
|
|
10
|
-
or not set)
|
|
11
|
-
|
|
12
|
-
*
|
|
13
|
-
any skill flow:
|
|
9
|
+
If ((<ase-agent-tool/> is equal to `copilot`) *AND* (<ase-headless/> is empty
|
|
10
|
+
or not set) *AND* (<ase-guidance-level/> is not equal to `none`)) you *MUST* output
|
|
11
|
+
the following <template/> *exactly once* as the very *first* thing in
|
|
12
|
+
your *first* response of this session -- *before* any other text,
|
|
13
|
+
*before* any tool call, and *before* entering any skill flow:
|
|
14
14
|
|
|
15
15
|
<template>
|
|
16
16
|
⧉ **ASE**: ⎈ version: **<ase-version/>** <ase-version-hint/>
|
|
17
17
|
⧉ **ASE**: ※ user: **<ase-user-id/>**, ⚑ project: **<ase-project-id/>**
|
|
18
18
|
⧉ **ASE**: ◉ task: **<ase-task-id/>**, ⏻ session: **<ase-session-id/>**
|
|
19
|
-
⧉ **ASE**: ☯ persona: **<ase-persona-style/>**, ▢ boxing: **<ase-project-boxing/>**
|
|
19
|
+
⧉ **ASE**: ☯ persona: **<ase-persona-style/>**, ▶ guidance: **<ase-guidance-level/>**, ▢ boxing: **<ase-project-boxing/>**
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
If ((<ase-agent-tool/> is equal to `copilot`) *AND* (<ase-headless/> is
|
|
23
|
+
empty or not set) *AND* (<ase-guidance-level/> is equal to `normal` or
|
|
24
|
+
`verbose`)) you *MUST* output the following <template/> *exactly once*
|
|
25
|
+
as the very *second* thing in your *first* response of this session,
|
|
26
|
+
too:
|
|
27
|
+
|
|
28
|
+
<template>
|
|
29
|
+
⧉ **ASE**: ▷ hint: use "/ase-help-intent *intent-description*" for skill command proposal
|
|
30
|
+
⧉ **ASE**: ▷ hint: use "/ase-help-skill [*skill-name*]" for skill catalog or skill manpage
|
|
20
31
|
</template>
|
|
21
32
|
|
|
22
33
|
Prohibitions
|
|
@@ -76,6 +76,15 @@ set placeholders into the context as a side-effect.
|
|
|
76
76
|
⧉ **ASE**: ✪ skill: **<getopt-skill/>**, ▶ ERROR: option parsing failed: **<text/>**
|
|
77
77
|
</template>
|
|
78
78
|
|
|
79
|
+
Directly *after* this error <template/>, and *before* stopping, give
|
|
80
|
+
the corrective hint by expanding the following (which, depending on
|
|
81
|
+
the configured <ase-guidance-level/>, may expand into nothing and
|
|
82
|
+
hence emit no output at all):
|
|
83
|
+
|
|
84
|
+
<ase-tpl-hint level="verbose">
|
|
85
|
+
Run `/<getopt-skill/> --help` for the accepted options and arguments of this skill.
|
|
86
|
+
</ase-tpl-hint>
|
|
87
|
+
|
|
79
88
|
5. **Parsing JSON Result**:
|
|
80
89
|
The tool returned a single `text` content payload containing JSON.
|
|
81
90
|
Parse this JSON in <text/> now into <getopt-result/> by recognizing
|
|
@@ -100,7 +109,8 @@ set placeholders into the context as a side-effect.
|
|
|
100
109
|
whose long name starts with `int-`.
|
|
101
110
|
|
|
102
111
|
7. **Display Results**:
|
|
103
|
-
|
|
112
|
+
If <ase-guidance-level/> is equal to `normal` or `verbose`,
|
|
113
|
+
output the following <template/>:
|
|
104
114
|
|
|
105
115
|
<template>
|
|
106
116
|
⧉ **ASE**: ✪ skill: **<getopt-skill/>**, ▶ options: <getopt-info/>
|
package/plugin/meta/ase-skill.md
CHANGED
|
@@ -214,15 +214,17 @@ Skill Identification
|
|
|
214
214
|
|
|
215
215
|
In case <skill/> later becomes *not* empty by defining it as <skill
|
|
216
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`)
|
|
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
|
|
219
220
|
<template/>:
|
|
220
221
|
|
|
221
222
|
<template>
|
|
222
223
|
⧉ **ASE**: ✪ skill: **<skill-name/>**, ✦ purpose: **<skill/>**, ▶ status: **skill started**
|
|
223
224
|
</template>
|
|
224
225
|
|
|
225
|
-
Later (but only if `$1` is *NOT* equal to `-h` or `--help`)
|
|
226
|
+
Later (but only if ((`$1` is *NOT* equal to `-h` or `--help`) *AND*
|
|
227
|
+
(<ase-guidance-level/> is equal to `normal` or `verbose`))), once
|
|
226
228
|
this skill finally will stop processing, you *MUST* once output the
|
|
227
229
|
following output <template/>:
|
|
228
230
|
|
|
@@ -231,7 +233,8 @@ Skill Identification
|
|
|
231
233
|
</template>
|
|
232
234
|
|
|
233
235
|
- *IMPORTANT*: Set <objective></objective> (set to empty).
|
|
234
|
-
Then, in case <objective/> later becomes *not* empty
|
|
236
|
+
Then, in case <objective/> later becomes *not* empty
|
|
237
|
+
(but only if <ase-guidance-level/> is equal to `normal` or `verbose`),
|
|
235
238
|
you *MUST* once output the following output <template/>:
|
|
236
239
|
|
|
237
240
|
<template>
|
|
@@ -292,6 +295,65 @@ Artifact Boxing Transparency
|
|
|
292
295
|
deterministically skip or suppress and *win* over the implicit decisions
|
|
293
296
|
above. Where no such branches exist, the decisions above apply.
|
|
294
297
|
|
|
298
|
+
Guidance Hint Level
|
|
299
|
+
-------------------
|
|
300
|
+
|
|
301
|
+
- *IMPORTANT*: The *guidance* of the agent (indicated by
|
|
302
|
+
<ase-guidance-level/> configuration) classifies how many
|
|
303
|
+
*unsolicited hints* -- pointers to the available *ASE* skills, to
|
|
304
|
+
their options, and to the recommended next operations, which the
|
|
305
|
+
user did *not* explicitly ask for -- you *MUST* emit in addition to
|
|
306
|
+
the requested result.
|
|
307
|
+
|
|
308
|
+
- *IMPORTANT*: Guidance modulates only the *hints*, per the four
|
|
309
|
+
levels below. It *MUST* *NOT* alter the *communication style*
|
|
310
|
+
(governed exclusively by the persona), it *MUST* *NOT* alter the
|
|
311
|
+
*work depth* or the *output visibility* (governed exclusively by the
|
|
312
|
+
boxing), and it *MUST* *NOT* suppress, reduce, or expand any
|
|
313
|
+
*requested* output. In particular, guidance *MUST* *NOT* apply to:
|
|
314
|
+
|
|
315
|
+
- the *manual page* emitted for `-h`/`--help`, which is
|
|
316
|
+
explicitly requested,
|
|
317
|
+
- the *skill catalog*, *manual page*, and *command proposal*
|
|
318
|
+
results of the `ase-help-skill` and `ase-help-intent` skills,
|
|
319
|
+
which are explicitly requested,
|
|
320
|
+
- the *option affordance* lines of a user dialog (e.g. `Please
|
|
321
|
+
choose *one* option by typing ...`), which are functional parts
|
|
322
|
+
of the dialog and not hints,
|
|
323
|
+
- the *artifact state* lines of a skill (e.g. `plan loaded`, `plan
|
|
324
|
+
created`, `task given`) and every `ERROR:` line, which carry
|
|
325
|
+
state rather than pointers.
|
|
326
|
+
|
|
327
|
+
The *skill identification* chrome (the `skill started`, `skill
|
|
328
|
+
finished`, and `objective` lines) is the sole exception: it is
|
|
329
|
+
*decoration* rather than result, so it is gated on `normal` and
|
|
330
|
+
`verbose` directly in the `Skill Identification` section above.
|
|
331
|
+
|
|
332
|
+
- *IMPORTANT*: Every hint carries a *level* -- `minimal`, `normal`,
|
|
333
|
+
or `verbose` -- and is emitted only if the configured
|
|
334
|
+
<ase-guidance-level/> *includes* that level:
|
|
335
|
+
|
|
336
|
+
- If <ase-guidance-level/> is `none`:
|
|
337
|
+
- Hints emitted: *none*
|
|
338
|
+
- If <ase-guidance-level/> is `minimal`:
|
|
339
|
+
- Hints emitted: `minimal` -- the *essential* pointers only,
|
|
340
|
+
i.e. those without which the just-produced result cannot be
|
|
341
|
+
consumed at all (such as the follow-up command that turns
|
|
342
|
+
persisted findings into actual changes)
|
|
343
|
+
- If <ase-guidance-level/> is `normal`:
|
|
344
|
+
- Hints emitted: `minimal` *and* `normal` -- additionally the
|
|
345
|
+
*canonical next step* of the finished skill
|
|
346
|
+
- If <ase-guidance-level/> is `verbose`:
|
|
347
|
+
- Hints emitted: `minimal`, `normal`, *and* `verbose` --
|
|
348
|
+
additionally the *unused options* of the current skill, the
|
|
349
|
+
*related* skills, and the *configuration* skills
|
|
350
|
+
- If <ase-guidance-level/> is empty or not set:
|
|
351
|
+
- Treat it as `normal`
|
|
352
|
+
|
|
353
|
+
- *IMPORTANT*: A hint *MUST* be emitted *exclusively* through the
|
|
354
|
+
`<ase-tpl-hint/>` template pattern below, and *never* as free-text
|
|
355
|
+
prose, so that its suppression is uniform across all skills.
|
|
356
|
+
|
|
295
357
|
Template Patterns
|
|
296
358
|
-----------------
|
|
297
359
|
|
|
@@ -347,6 +409,32 @@ Template Patterns
|
|
|
347
409
|
|
|
348
410
|
</template>
|
|
349
411
|
|
|
412
|
+
- When `<ase-tpl-hint level="<level/>"><content/></ase-tpl-hint>`
|
|
413
|
+
should be expanded, first determine whether the hint passes the
|
|
414
|
+
configured guidance level (see `Guidance Hint Level` above):
|
|
415
|
+
|
|
416
|
+
- Set <emit>false</emit>.
|
|
417
|
+
- If <ase-guidance-level/> is `minimal` and <level/> is `minimal`,
|
|
418
|
+
set <emit>true</emit>.
|
|
419
|
+
- If <ase-guidance-level/> is `normal` and <level/> is `minimal`
|
|
420
|
+
or `normal`, set <emit>true</emit>.
|
|
421
|
+
- If <ase-guidance-level/> is `verbose` and <level/> is `minimal`,
|
|
422
|
+
`normal`, or `verbose`, set <emit>true</emit>.
|
|
423
|
+
- If <ase-guidance-level/> is empty or not set, treat it as
|
|
424
|
+
`normal` and apply the corresponding decision above.
|
|
425
|
+
|
|
426
|
+
Then dispatch on <emit/>:
|
|
427
|
+
|
|
428
|
+
<if condition="<emit/> is `true`">
|
|
429
|
+
<template>
|
|
430
|
+
⧉ **ASE**: ▷ hint [<level/>]: **<content/>**
|
|
431
|
+
</template>
|
|
432
|
+
</if>
|
|
433
|
+
<else>
|
|
434
|
+
This construct is expanded into *nothing*.
|
|
435
|
+
Do not output anything.
|
|
436
|
+
</else>
|
|
437
|
+
|
|
350
438
|
- When `<ase-tpl-boxline><line/></ase-tpl-boxline>` should be expanded, use:
|
|
351
439
|
|
|
352
440
|
<if condition="<line/> is not empty">
|
package/plugin/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.
|
|
9
|
+
"version": "0.9.49",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"author": {
|
|
12
12
|
"name": "Dr. Ralf S. Engelschall",
|
|
@@ -465,14 +465,16 @@ interface quality, quality attributes, and architecture governance.
|
|
|
465
465
|
"<title/>: <description/>" }` entry per reported PROBLEM and one
|
|
466
466
|
`{ command: "set", key: "ase-issue-T<n/>", val: "<title/>:
|
|
467
467
|
<description/>" }` entry per reported TRADEOFF.
|
|
468
|
-
</step>
|
|
469
468
|
|
|
470
|
-
|
|
471
|
-
|
|
469
|
+
Finally, give a final hint by expanding the following (which,
|
|
470
|
+
depending on the configured <ase-guidance-level/>, may expand into
|
|
471
|
+
nothing and hence emit no output at all):
|
|
472
|
+
|
|
473
|
+
<ase-tpl-hint level="minimal">
|
|
474
|
+
For deeper analysis, suggestions on solution approaches and then final source code changes, use `/ase-code-resolve P{n}` or `/ase-code-resolve T{n}` in the same or even a different session.
|
|
475
|
+
</ase-tpl-hint>
|
|
472
476
|
|
|
473
|
-
<template>
|
|
474
|
-
⧉ **ASE**: ↪ hint: **For deeper analysis, suggestions on solution approaches and then final source code changes, use `/ase-code-resolve P{n}` or `/ase-code-resolve T{n}` in the same or even a different session.**
|
|
475
|
-
</template>
|
|
476
477
|
</step>
|
|
478
|
+
|
|
477
479
|
</flow>
|
|
478
480
|
|