@rse/ase 1.0.5 → 1.0.6
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-hook.js +14 -0
- package/dst/ase-setup.js +151 -49
- package/package.json +3 -3
- 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-format-specbook.yaml +23 -20
- package/plugin/meta/ase-skill.md +7 -0
- package/plugin/output-styles/ase-terse.md +58 -0
package/dst/ase-hook.js
CHANGED
|
@@ -208,9 +208,11 @@ export default class HookCommand {
|
|
|
208
208
|
/* determine path to external files */
|
|
209
209
|
const filePkg = path.join(pluginRoot, ".claude-plugin", "plugin.json");
|
|
210
210
|
const fileMd = path.join(pluginRoot, "meta", "ase-constitution.md");
|
|
211
|
+
const fileStyle = path.join(pluginRoot, "output-styles", "ase-terse.md");
|
|
211
212
|
/* read external files */
|
|
212
213
|
let pkg;
|
|
213
214
|
let md;
|
|
215
|
+
let style;
|
|
214
216
|
try {
|
|
215
217
|
pkg = fs.readFileSync(filePkg, "utf8");
|
|
216
218
|
}
|
|
@@ -223,6 +225,12 @@ export default class HookCommand {
|
|
|
223
225
|
catch (err) {
|
|
224
226
|
throw new Error(`failed to read constitution file: ${fileMd}`, { cause: err });
|
|
225
227
|
}
|
|
228
|
+
try {
|
|
229
|
+
style = fs.readFileSync(fileStyle, "utf8");
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
throw new Error(`failed to read output style file: ${fileStyle}`, { cause: err });
|
|
233
|
+
}
|
|
226
234
|
/* determine own version */
|
|
227
235
|
const pkgObj = this.parseJSON(pkg, v.object({ version: v.optional(v.string()) }));
|
|
228
236
|
const versionCurrentPlugin = pkgObj.version ?? "";
|
|
@@ -340,6 +348,12 @@ export default class HookCommand {
|
|
|
340
348
|
"\n" + md;
|
|
341
349
|
/* expand all @<file> references manually */
|
|
342
350
|
md = this.expandReferences(md, path.dirname(fileMd));
|
|
351
|
+
/* append the output style to the constitution markdown (GitHub
|
|
352
|
+
Copilot CLI and OpenAI Codex CLI only -- Anthropic Claude Code CLI
|
|
353
|
+
applies the plugin output style natively), stripping the
|
|
354
|
+
YAML frontmatter which only Anthropic Claude Code CLI understands */
|
|
355
|
+
if (tool !== "claude")
|
|
356
|
+
md += "\n" + style.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n/, "");
|
|
343
357
|
/* build the deterministic ASE banner (rendered directly by the
|
|
344
358
|
agent harness, independent of any model decision, so it is
|
|
345
359
|
guaranteed to appear once in every non-headless session;
|
package/dst/ase-setup.js
CHANGED
|
@@ -143,6 +143,9 @@ export default class SetupCommand {
|
|
|
143
143
|
const scopeArgs = tool === "claude" && scope !== "user" ? ["--scope", scope] : [];
|
|
144
144
|
await this.run(spec.cli, ["plugin", "marketplace", "add", source, ...scopeArgs]);
|
|
145
145
|
await this.run(spec.cli, ["plugin", spec.pInstall, "ase@ase", ...scopeArgs], { retries: 3 });
|
|
146
|
+
/* select the plugin-shipped ASE output style (Anthropic Claude Code CLI only) */
|
|
147
|
+
if (tool === "claude")
|
|
148
|
+
await this.outputStyleActivate(`install${dev ? "[dev]" : ""}`, scope);
|
|
146
149
|
return 0;
|
|
147
150
|
}
|
|
148
151
|
/* handler for "ase setup update" (both tools) */
|
|
@@ -197,6 +200,9 @@ export default class SetupCommand {
|
|
|
197
200
|
await this.run(spec.cli, ["plugin", spec.pInstall, "ase@ase", ...scopeArgs], { retries: 3 });
|
|
198
201
|
}
|
|
199
202
|
}
|
|
203
|
+
/* select the plugin-shipped ASE output style (Anthropic Claude Code CLI only) */
|
|
204
|
+
if (tool === "claude")
|
|
205
|
+
await this.outputStyleActivate(`update${dev ? "[dev]" : ""}`, scope);
|
|
200
206
|
return 0;
|
|
201
207
|
}
|
|
202
208
|
/* handler for "ase setup enable|disable" (both tools) */
|
|
@@ -227,6 +233,9 @@ export default class SetupCommand {
|
|
|
227
233
|
this.log.write("info", `setup: uninstall${dev ? "[dev]" : ""}: ` +
|
|
228
234
|
"stopping potentially running ASE service");
|
|
229
235
|
await this.run("ase", ["service", "stop"], { quiet: true, ignoreError: "ASE service not running" });
|
|
236
|
+
/* deselect the plugin-shipped ASE output style (Anthropic Claude Code CLI only) */
|
|
237
|
+
if (tool === "claude")
|
|
238
|
+
await this.outputStyleDeactivate(`uninstall${dev ? "[dev]" : ""}`, scope);
|
|
230
239
|
/* uninstall ASE plugin */
|
|
231
240
|
this.log.write("info", `setup: uninstall${dev ? "[dev]" : ""}: ` +
|
|
232
241
|
`uninstalling ASE ${spec.label} plugin (origin: ${dev ? "local" : "remote/bundled"})`);
|
|
@@ -263,6 +272,9 @@ export default class SetupCommand {
|
|
|
263
272
|
continue;
|
|
264
273
|
rows.push(["MCP", chalk.bold(this.mcpServers[i].server), scope, "registered"]);
|
|
265
274
|
}
|
|
275
|
+
/* report the ASE output style selections */
|
|
276
|
+
for (const outputStyle of await this.outputStyleStatus(tool))
|
|
277
|
+
rows.push(["OUTPUTSTYLE", chalk.bold(outputStyle.file), outputStyle.scope, outputStyle.status]);
|
|
266
278
|
/* report the ASE statusline registrations */
|
|
267
279
|
for (const statusline of await this.statuslineStatus(tool))
|
|
268
280
|
rows.push(["STATUSLINE", chalk.bold(statusline.file), statusline.scope, statusline.status]);
|
|
@@ -660,7 +672,7 @@ export default class SetupCommand {
|
|
|
660
672
|
"%P %h %B %c"
|
|
661
673
|
];
|
|
662
674
|
/* resolve the tool settings file for a given installation scope */
|
|
663
|
-
|
|
675
|
+
settingsFile(tool, scope) {
|
|
664
676
|
if (tool === "copilot") {
|
|
665
677
|
const home = process.env.COPILOT_HOME ?? "";
|
|
666
678
|
const base = home !== "" ? home : path.join(os.homedir(), ".copilot");
|
|
@@ -716,11 +728,11 @@ export default class SetupCommand {
|
|
|
716
728
|
}
|
|
717
729
|
return false;
|
|
718
730
|
}
|
|
719
|
-
/* locate
|
|
720
|
-
|
|
731
|
+
/* locate a top-level member node by key within the root object */
|
|
732
|
+
settingsFindMember(root, name) {
|
|
721
733
|
for (const m of root.query("/ member")) {
|
|
722
734
|
const key = m.query("/ string [ pos() == 1 ]")[0];
|
|
723
|
-
if (key !== undefined && key.get("value") ===
|
|
735
|
+
if (key !== undefined && key.get("value") === name)
|
|
724
736
|
return m;
|
|
725
737
|
}
|
|
726
738
|
return undefined;
|
|
@@ -749,7 +761,7 @@ export default class SetupCommand {
|
|
|
749
761
|
return root.create("member").add(key, obj);
|
|
750
762
|
}
|
|
751
763
|
/* normalize the previous-last member so a following ",\n " comma reads cleanly */
|
|
752
|
-
|
|
764
|
+
settingsMakeNonLast(member) {
|
|
753
765
|
const val = member.query("/ * [ pos() == 2 ]")[0];
|
|
754
766
|
if (val !== undefined) {
|
|
755
767
|
const ep = val.get("epilog");
|
|
@@ -758,8 +770,42 @@ export default class SetupCommand {
|
|
|
758
770
|
}
|
|
759
771
|
member.set({ epilog: ",\n " });
|
|
760
772
|
}
|
|
773
|
+
/* append a member to the root object, repairing the whitespace at
|
|
774
|
+
the seam: a scalar value carries no pre-"}" newline of its own
|
|
775
|
+
(a container value carries it in its own epilog already), so the
|
|
776
|
+
root epilog has to supply it */
|
|
777
|
+
settingsAddMember(root, member, scalar) {
|
|
778
|
+
const members = root.query("/ member");
|
|
779
|
+
if (members.length === 0)
|
|
780
|
+
root.set({ prolog: "{\n " });
|
|
781
|
+
else
|
|
782
|
+
this.settingsMakeNonLast(members[members.length - 1]);
|
|
783
|
+
root.set({ epilog: scalar ? "\n}\n" : "}\n" });
|
|
784
|
+
root.add(member);
|
|
785
|
+
}
|
|
786
|
+
/* remove a member from the root object, repairing the whitespace at the seam */
|
|
787
|
+
settingsRemoveMember(root, target) {
|
|
788
|
+
const members = root.query("/ member");
|
|
789
|
+
const wasLast = members[members.length - 1] === target;
|
|
790
|
+
root.del(target);
|
|
791
|
+
if (wasLast) {
|
|
792
|
+
if (members.length > 1) {
|
|
793
|
+
/* promote the new last member: strip its comma epilog and
|
|
794
|
+
restore the pre-"}" newline into the root epilog when the
|
|
795
|
+
new-last value is a scalar (a container value carries the
|
|
796
|
+
newline in its own epilog already) */
|
|
797
|
+
const newLast = members[members.length - 2];
|
|
798
|
+
newLast.set({ epilog: undefined });
|
|
799
|
+
const val = newLast.query("/ * [ pos() == 2 ]")[0];
|
|
800
|
+
const ep = val !== undefined ? val.get("epilog") : undefined;
|
|
801
|
+
root.set({ epilog: (typeof ep === "string" && ep.endsWith("\n")) ? "}\n" : "\n}\n" });
|
|
802
|
+
}
|
|
803
|
+
else
|
|
804
|
+
root.set({ epilog: "}\n" });
|
|
805
|
+
}
|
|
806
|
+
}
|
|
761
807
|
/* read a settings.json file into a JSON-ASTy AST */
|
|
762
|
-
async
|
|
808
|
+
async settingsReadAst(file) {
|
|
763
809
|
let text = "";
|
|
764
810
|
try {
|
|
765
811
|
text = await fs.readFile(file, "utf8");
|
|
@@ -772,7 +818,7 @@ export default class SetupCommand {
|
|
|
772
818
|
return JsonAsty.parse(text);
|
|
773
819
|
}
|
|
774
820
|
/* write a JSON-ASTy AST back to a settings.json file */
|
|
775
|
-
async
|
|
821
|
+
async settingsWriteAst(file, root) {
|
|
776
822
|
await mkdirp(path.dirname(file));
|
|
777
823
|
const text = JsonAsty.unparse(root);
|
|
778
824
|
await writeFileAtomic(file, text, { encoding: "utf8" });
|
|
@@ -781,10 +827,10 @@ export default class SetupCommand {
|
|
|
781
827
|
async doStatuslineActivate(tool, scope, opts, format) {
|
|
782
828
|
this.requireStatuslineTool(tool);
|
|
783
829
|
this.requireStatuslineScope(tool, scope);
|
|
784
|
-
const file = this.
|
|
830
|
+
const file = this.settingsFile(tool, scope);
|
|
785
831
|
const command = this.statuslineCommand(tool, opts, format);
|
|
786
|
-
const root = await this.
|
|
787
|
-
const existing = this.
|
|
832
|
+
const root = await this.settingsReadAst(file);
|
|
833
|
+
const existing = this.settingsFindMember(root, "statusLine");
|
|
788
834
|
if (existing !== undefined) {
|
|
789
835
|
/* preserve a foreign, hand-crafted statusLine: skip and warn */
|
|
790
836
|
if (!this.statuslineIsOwned(existing)) {
|
|
@@ -801,27 +847,17 @@ export default class SetupCommand {
|
|
|
801
847
|
}
|
|
802
848
|
else {
|
|
803
849
|
/* insert a fresh statusLine member */
|
|
804
|
-
|
|
805
|
-
const member = this.statuslineBuildMember(root, command);
|
|
806
|
-
if (members.length === 0) {
|
|
807
|
-
root.set({ prolog: "{\n ", epilog: "}\n" });
|
|
808
|
-
root.add(member);
|
|
809
|
-
}
|
|
810
|
-
else {
|
|
811
|
-
root.set({ epilog: "}\n" });
|
|
812
|
-
this.statuslineMakeNonLast(members[members.length - 1]);
|
|
813
|
-
root.add(member);
|
|
814
|
-
}
|
|
850
|
+
this.settingsAddMember(root, this.statuslineBuildMember(root, command), false);
|
|
815
851
|
this.log.write("info", `setup: statusline: activate: adding ASE "statusLine" to ${file}`);
|
|
816
852
|
}
|
|
817
|
-
await this.
|
|
853
|
+
await this.settingsWriteAst(file, root);
|
|
818
854
|
return 0;
|
|
819
855
|
}
|
|
820
856
|
/* handler for "ase setup statusline deactivate" */
|
|
821
857
|
async doStatuslineDeactivate(tool, scope) {
|
|
822
858
|
this.requireStatuslineTool(tool);
|
|
823
859
|
this.requireStatuslineScope(tool, scope);
|
|
824
|
-
const file = this.
|
|
860
|
+
const file = this.settingsFile(tool, scope);
|
|
825
861
|
/* a missing settings file means nothing to remove */
|
|
826
862
|
try {
|
|
827
863
|
await fs.access(file);
|
|
@@ -831,8 +867,8 @@ export default class SetupCommand {
|
|
|
831
867
|
return 0;
|
|
832
868
|
}
|
|
833
869
|
/* read file */
|
|
834
|
-
const root = await this.
|
|
835
|
-
const target = this.
|
|
870
|
+
const root = await this.settingsReadAst(file);
|
|
871
|
+
const target = this.settingsFindMember(root, "statusLine");
|
|
836
872
|
if (target === undefined) {
|
|
837
873
|
this.log.write("info", `setup: statusline: deactivate: no "statusLine" in ${file} (skipped)`);
|
|
838
874
|
return 0;
|
|
@@ -844,26 +880,8 @@ export default class SetupCommand {
|
|
|
844
880
|
return 0;
|
|
845
881
|
}
|
|
846
882
|
/* remove the member and repair the whitespace at the seam */
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
root.del(target);
|
|
850
|
-
if (wasLast) {
|
|
851
|
-
if (members.length > 1) {
|
|
852
|
-
/* promote the new last member: strip its comma epilog and
|
|
853
|
-
restore the pre-"}" newline into the root epilog when the
|
|
854
|
-
new-last value is a scalar (a container value carries the
|
|
855
|
-
newline in its own epilog already) */
|
|
856
|
-
const newLast = members[members.length - 2];
|
|
857
|
-
newLast.set({ epilog: undefined });
|
|
858
|
-
const val = newLast.query("/ * [ pos() == 2 ]")[0];
|
|
859
|
-
const ep = val !== undefined ? val.get("epilog") : undefined;
|
|
860
|
-
if (!(typeof ep === "string" && ep.endsWith("\n")))
|
|
861
|
-
root.set({ epilog: "\n}\n" });
|
|
862
|
-
}
|
|
863
|
-
else
|
|
864
|
-
root.set({ epilog: "}\n" });
|
|
865
|
-
}
|
|
866
|
-
await this.statuslineWriteAst(file, root);
|
|
883
|
+
this.settingsRemoveMember(root, target);
|
|
884
|
+
await this.settingsWriteAst(file, root);
|
|
867
885
|
this.log.write("info", `setup: statusline: deactivate: removing ASE "statusLine" from ${file}`);
|
|
868
886
|
return 0;
|
|
869
887
|
}
|
|
@@ -877,16 +895,16 @@ export default class SetupCommand {
|
|
|
877
895
|
const scopes = tool === "claude" ? ["user", "project", "local"] : ["user"];
|
|
878
896
|
const entries = [];
|
|
879
897
|
for (const scope of scopes) {
|
|
880
|
-
const file = this.
|
|
898
|
+
const file = this.settingsFile(tool, scope);
|
|
881
899
|
let root;
|
|
882
900
|
try {
|
|
883
|
-
root = await this.
|
|
901
|
+
root = await this.settingsReadAst(file);
|
|
884
902
|
}
|
|
885
903
|
catch {
|
|
886
904
|
/* an unparsable settings file carries no usable state */
|
|
887
905
|
continue;
|
|
888
906
|
}
|
|
889
|
-
const member = this.
|
|
907
|
+
const member = this.settingsFindMember(root, "statusLine");
|
|
890
908
|
if (member === undefined)
|
|
891
909
|
continue;
|
|
892
910
|
entries.push({
|
|
@@ -897,6 +915,90 @@ export default class SetupCommand {
|
|
|
897
915
|
}
|
|
898
916
|
return entries;
|
|
899
917
|
}
|
|
918
|
+
/* determine whether an existing "outputStyle" value is owned by us */
|
|
919
|
+
outputStyleIsOwned(member) {
|
|
920
|
+
const val = member.query("/ string [ pos() == 2 ]")[0];
|
|
921
|
+
return val !== undefined && val.get("value") === "ase:ase-terse";
|
|
922
|
+
}
|
|
923
|
+
/* build the "outputStyle" member subtree natively inside the target AST */
|
|
924
|
+
outputStyleBuildMember(root) {
|
|
925
|
+
const key = root.create("string").set({ body: JSON.stringify("outputStyle"), value: "outputStyle", epilog: ": " });
|
|
926
|
+
const val = root.create("string").set({ body: JSON.stringify("ase:ase-terse"), value: "ase:ase-terse" });
|
|
927
|
+
return root.create("member").add(key, val);
|
|
928
|
+
}
|
|
929
|
+
/* activate the plugin-shipped ASE output style by selecting it in
|
|
930
|
+
the settings file of the scope (Anthropic Claude Code CLI only:
|
|
931
|
+
the other tools have no output style concept, so the session-start
|
|
932
|
+
hook injects the style into their session context instead) */
|
|
933
|
+
async outputStyleActivate(action, scope) {
|
|
934
|
+
const file = this.settingsFile("claude", scope);
|
|
935
|
+
const root = await this.settingsReadAst(file);
|
|
936
|
+
const existing = this.settingsFindMember(root, "outputStyle");
|
|
937
|
+
if (existing !== undefined) {
|
|
938
|
+
/* preserve a foreign, hand-selected outputStyle: skip and warn */
|
|
939
|
+
if (!this.outputStyleIsOwned(existing))
|
|
940
|
+
this.log.write("warning", `setup: ${action}: a non-ASE "outputStyle" ` +
|
|
941
|
+
`is already selected in ${file}: preserving it (skipped)`);
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
this.settingsAddMember(root, this.outputStyleBuildMember(root), true);
|
|
945
|
+
await this.settingsWriteAst(file, root);
|
|
946
|
+
this.log.write("info", `setup: ${action}: selecting ASE "outputStyle" in ${file}`);
|
|
947
|
+
}
|
|
948
|
+
/* deactivate the plugin-shipped ASE output style by removing its
|
|
949
|
+
selection from the settings file of the scope (Anthropic Claude Code CLI only) */
|
|
950
|
+
async outputStyleDeactivate(action, scope) {
|
|
951
|
+
const file = this.settingsFile("claude", scope);
|
|
952
|
+
/* a missing settings file means nothing to remove */
|
|
953
|
+
try {
|
|
954
|
+
await fs.access(file);
|
|
955
|
+
}
|
|
956
|
+
catch {
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
const root = await this.settingsReadAst(file);
|
|
960
|
+
const target = this.settingsFindMember(root, "outputStyle");
|
|
961
|
+
if (target === undefined)
|
|
962
|
+
return;
|
|
963
|
+
/* preserve a foreign, hand-selected outputStyle: skip and warn */
|
|
964
|
+
if (!this.outputStyleIsOwned(target)) {
|
|
965
|
+
this.log.write("warning", `setup: ${action}: a non-ASE "outputStyle" ` +
|
|
966
|
+
`is selected in ${file}: preserving it (skipped)`);
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
this.settingsRemoveMember(root, target);
|
|
970
|
+
await this.settingsWriteAst(file, root);
|
|
971
|
+
this.log.write("info", `setup: ${action}: deselecting ASE "outputStyle" in ${file}`);
|
|
972
|
+
}
|
|
973
|
+
/* probe the output style selections by inspecting the tool
|
|
974
|
+
settings files of all installation scopes (Anthropic Claude Code CLI only) */
|
|
975
|
+
async outputStyleStatus(tool) {
|
|
976
|
+
if (tool !== "claude")
|
|
977
|
+
return [];
|
|
978
|
+
const home = os.homedir();
|
|
979
|
+
const scopes = ["user", "project", "local"];
|
|
980
|
+
const entries = [];
|
|
981
|
+
for (const scope of scopes) {
|
|
982
|
+
const file = this.settingsFile(tool, scope);
|
|
983
|
+
let root;
|
|
984
|
+
try {
|
|
985
|
+
root = await this.settingsReadAst(file);
|
|
986
|
+
}
|
|
987
|
+
catch {
|
|
988
|
+
/* an unparsable settings file carries no usable state */
|
|
989
|
+
continue;
|
|
990
|
+
}
|
|
991
|
+
const member = this.settingsFindMember(root, "outputStyle");
|
|
992
|
+
if (member === undefined)
|
|
993
|
+
continue;
|
|
994
|
+
entries.push({
|
|
995
|
+
file: file.startsWith(home + path.sep) ? `~${file.slice(home.length)}` : file,
|
|
996
|
+
scope,
|
|
997
|
+
status: this.outputStyleIsOwned(member) ? "selected" : "foreign"
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
return entries;
|
|
1001
|
+
}
|
|
900
1002
|
/* parse and validate the --tool option */
|
|
901
1003
|
parseTool(value) {
|
|
902
1004
|
if (value !== "claude" && value !== "copilot" && value !== "codex")
|
|
@@ -985,7 +1087,7 @@ export default class SetupCommand {
|
|
|
985
1087
|
/* register CLI sub-command "ase setup status" */
|
|
986
1088
|
setupCmd
|
|
987
1089
|
.command("status")
|
|
988
|
-
.description("report the ASE plugin, MCP server, and statusline registrations for a tool")
|
|
1090
|
+
.description("report the ASE plugin, MCP server, output style, and statusline registrations for a tool")
|
|
989
1091
|
.option("-t, --tool <tool>", "target tool (\"claude\", \"copilot\", or \"codex\")", toolDflt)
|
|
990
1092
|
.action(async (opts) => {
|
|
991
1093
|
process.exit(await this.doStatus(this.parseTool(opts.tool)));
|
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": "1.0.
|
|
9
|
+
"version": "1.0.6",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"author": {
|
|
12
12
|
"name": "Dr. Ralf S. Engelschall",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"luxon": "3.7.2",
|
|
56
56
|
"@modelcontextprotocol/sdk": "1.30.0",
|
|
57
57
|
"json-asty": "1.3.4",
|
|
58
|
-
"zod": "4.6.
|
|
58
|
+
"zod": "4.6.5",
|
|
59
59
|
"which": "7.0.0",
|
|
60
60
|
"update-notifier": "7.3.1",
|
|
61
61
|
"shell-quote": "1.10.0",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"ofetch": "1.5.1",
|
|
67
67
|
"picomatch": "4.0.7",
|
|
68
68
|
"lru-cache": "11.5.2",
|
|
69
|
-
"@rse/specbook": "1.2.
|
|
69
|
+
"@rse/specbook": "1.2.13",
|
|
70
70
|
"source-code-error": "1.2.0",
|
|
71
71
|
"pure-uuid": "2.0.0"
|
|
72
72
|
},
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
optional: true
|
|
46
46
|
- name: THEME-STYLE
|
|
47
47
|
desc: The theme style.
|
|
48
|
-
value: enum(
|
|
48
|
+
value: enum(Dark,Light)
|
|
49
49
|
optional: true
|
|
50
50
|
- name: THEME-TONE
|
|
51
51
|
desc: The theme color tone.
|
|
@@ -190,7 +190,7 @@
|
|
|
190
190
|
diagram:
|
|
191
191
|
type: hub
|
|
192
192
|
center: self
|
|
193
|
-
nodes: "[[TERM:*]], [[PREMISE:*]], [[RULE:*]], [[DM.ENTITY:*]], [[ROLE:*]], [[PERSONA:*]]"
|
|
193
|
+
nodes: "[[TERM:*]], [[PREMISE:*]], [[RULE:*]], [[DM.ENTITY:*]], [[ROLE:*]], [[PERSONA:*]], [[WORKFLOW:*]], [[CONVENTION:*]], [[CV.ENTITY:*]]"
|
|
194
194
|
labeled: true
|
|
195
195
|
qualified: true
|
|
196
196
|
config:
|
|
@@ -257,7 +257,7 @@
|
|
|
257
257
|
diagram:
|
|
258
258
|
type: hub
|
|
259
259
|
center: self
|
|
260
|
-
nodes: "[[ROLE:*]], [[TERM:*]], [[PERSONA:*]], [[ACTIVITY:*]], [[USE-CASE:*]], [[CV.ENTITY:*]]"
|
|
260
|
+
nodes: "[[ROLE:*]], [[TERM:*]], [[PERSONA:*]], [[WORKFLOW:*]], [[ACTIVITY:*]], [[USE-CASE:*]], [[TRANSITION:*]], [[CV.ENTITY:*]]"
|
|
261
261
|
labeled: true
|
|
262
262
|
qualified: true
|
|
263
263
|
config:
|
|
@@ -292,8 +292,8 @@
|
|
|
292
292
|
optional: true
|
|
293
293
|
- name: GENDER
|
|
294
294
|
desc: >
|
|
295
|
-
The gender of the persona: `
|
|
296
|
-
value: enum(
|
|
295
|
+
The gender of the persona: `Male`, `Female`, or `Diverse`.
|
|
296
|
+
value: enum(Male,Female,Diverse)
|
|
297
297
|
optional: true
|
|
298
298
|
- name: AGE
|
|
299
299
|
desc: >
|
|
@@ -312,8 +312,8 @@
|
|
|
312
312
|
optional: true
|
|
313
313
|
- name: PROFICIENCY
|
|
314
314
|
desc: >
|
|
315
|
-
The technical and domain skill level of the persona: `
|
|
316
|
-
value: enum(
|
|
315
|
+
The technical and domain skill level of the persona: `Novice`, `Intermediate`, or `Expert`.
|
|
316
|
+
value: enum(Novice,Intermediate,Expert)
|
|
317
317
|
optional: true
|
|
318
318
|
- name: GOAL
|
|
319
319
|
desc: >
|
|
@@ -714,7 +714,7 @@
|
|
|
714
714
|
diagram:
|
|
715
715
|
type: hub
|
|
716
716
|
center: self
|
|
717
|
-
nodes: "[[USE-CASE:*]], [[ROLE:*]], [[PERSONA:*]], [[STEP:*]], [[ACTIVITY:*]], [[FR.REQUIREMENT:*]], [[RULE:*]], [[TRANSITION:*]], [[PERMISSION:*]], [[PRINCIPLE:*]], [[CV.ENTITY:*]]"
|
|
717
|
+
nodes: "[[USE-CASE:*]], [[ROLE:*]], [[PERSONA:*]], [[STEP:*]], [[ACTIVITY:*]], [[FR.REQUIREMENT:*]], [[RULE:*]], [[DM.ENTITY:*]], [[TRANSITION:*]], [[PERMISSION:*]], [[PRINCIPLE:*]], [[STORYBOARD:*]], [[CV.ENTITY:*]], [[TEST-CASE:*]]"
|
|
718
718
|
labeled: true
|
|
719
719
|
qualified: true
|
|
720
720
|
config:
|
|
@@ -830,7 +830,7 @@
|
|
|
830
830
|
diagram:
|
|
831
831
|
type: hub
|
|
832
832
|
center: self
|
|
833
|
-
nodes: "[[DM.ENTITY:*]], [[FR.REQUIREMENT:*]], [[USE-CASE:*]]"
|
|
833
|
+
nodes: "[[DM.ENTITY:*]], [[FR.REQUIREMENT:*]], [[USE-CASE:*]], [[LIFECYCLE:*]], [[FV.COMPONENT:*]], [[CONCERN:*]], [[TACTIC:*]], [[DECISION:*]], [[TEST-CASE:*]]"
|
|
834
834
|
edges: "[[RELATION:*]]"
|
|
835
835
|
labeled: true
|
|
836
836
|
qualified: true
|
|
@@ -894,14 +894,16 @@
|
|
|
894
894
|
desc: >
|
|
895
895
|
A named directed relation of the entity, carrying a camel-cased, 1-3 word long, per-entity unique identifier and targeting another entity, and described by a 1-sentence description ("what") plus a 1-sentence rationale ("why"). A relation carries no attributes of its own: a relation needing attributes (an "association class") is reified as an entity of its own, related from both sides.
|
|
896
896
|
optional: true
|
|
897
|
+
format:
|
|
898
|
+
maxTableColumns: 5
|
|
897
899
|
props:
|
|
898
900
|
- name: TARGET
|
|
899
901
|
desc: Reference to the DATA-MODEL entity the directed relation targets.
|
|
900
902
|
value: "[[DM.ENTITY:*]]"
|
|
901
903
|
- name: KIND
|
|
902
904
|
desc: >
|
|
903
|
-
The kind of the directed relation: `
|
|
904
|
-
value: enum(
|
|
905
|
+
The kind of the directed relation: `Association` for a plain reference to an independent target entity, `Aggregation` for a (optionally even shared) part-of relation whose target outlives the entity, or `Composition` for an exclusive part-of relation whose target is owned by and deleted with the entity; defaults to `Association`.
|
|
906
|
+
value: enum(Association,Aggregation,Composition)
|
|
905
907
|
optional: true
|
|
906
908
|
- name: ARITY
|
|
907
909
|
desc: >
|
|
@@ -1109,7 +1111,7 @@
|
|
|
1109
1111
|
diagram:
|
|
1110
1112
|
type: hub
|
|
1111
1113
|
center: self
|
|
1112
|
-
nodes: "[[INTERFACE:*]], [[TYPE:*]], [[ENDPOINT:*]]"
|
|
1114
|
+
nodes: "[[INTERFACE:*]], [[TYPE:*]], [[ENDPOINT:*]], [[FV.COMPONENT:*]]"
|
|
1113
1115
|
labeled: true
|
|
1114
1116
|
qualified: true
|
|
1115
1117
|
config:
|
|
@@ -1175,7 +1177,7 @@
|
|
|
1175
1177
|
diagram:
|
|
1176
1178
|
type: hub
|
|
1177
1179
|
center: self
|
|
1178
|
-
nodes: "[[TYPE:*]], [[INTERFACE:*]]"
|
|
1180
|
+
nodes: "[[TYPE:*]], [[INTERFACE:*]], [[ENDPOINT:*]]"
|
|
1179
1181
|
deep: true
|
|
1180
1182
|
labeled: true
|
|
1181
1183
|
qualified: true
|
|
@@ -1442,7 +1444,7 @@
|
|
|
1442
1444
|
diagram:
|
|
1443
1445
|
type: hub
|
|
1444
1446
|
center: self
|
|
1445
|
-
nodes: "[[PRINCIPLE:*]], [[PERSONA:*]], [[STEP:*]], [[USE-CASE:*]], [[PATTERN:*]]"
|
|
1447
|
+
nodes: "[[PRINCIPLE:*]], [[PERSONA:*]], [[STEP:*]], [[USE-CASE:*]], [[CONVENTION:*]], [[PATTERN:*]], [[ELEMENT:*]], [[DECISION:*]]"
|
|
1446
1448
|
labeled: true
|
|
1447
1449
|
qualified: true
|
|
1448
1450
|
config:
|
|
@@ -1554,7 +1556,7 @@
|
|
|
1554
1556
|
diagram:
|
|
1555
1557
|
type: hub
|
|
1556
1558
|
center: self
|
|
1557
|
-
nodes: "[[PATTERN:*]], [[PRINCIPLE:*]], [[STORYBOARD:*]]"
|
|
1559
|
+
nodes: "[[PATTERN:*]], [[PRINCIPLE:*]], [[STORYBOARD:*]], [[DECISION:*]]"
|
|
1558
1560
|
deep: true
|
|
1559
1561
|
labeled: true
|
|
1560
1562
|
qualified: true
|
|
@@ -1620,7 +1622,7 @@
|
|
|
1620
1622
|
diagram:
|
|
1621
1623
|
type: hub
|
|
1622
1624
|
center: self
|
|
1623
|
-
nodes: "[[STORYBOARD:*]], [[PERSONA:*]], [[SCENARIO:*]], [[PATTERN:*]]"
|
|
1625
|
+
nodes: "[[STORYBOARD:*]], [[PERSONA:*]], [[SCENARIO:*]], [[PATTERN:*]], [[MOCKUP:*]]"
|
|
1624
1626
|
deep: true
|
|
1625
1627
|
labeled: true
|
|
1626
1628
|
qualified: true
|
|
@@ -1867,7 +1869,7 @@
|
|
|
1867
1869
|
diagram:
|
|
1868
1870
|
type: hub
|
|
1869
1871
|
center: self
|
|
1870
|
-
nodes: "[[FV.COMPONENT:*]], [[FR.REQUIREMENT:*]], [[INTERFACE:*]], [[DM.ENTITY:*]], [[TACTIC:*]], [[DECISION:*]]"
|
|
1872
|
+
nodes: "[[FV.COMPONENT:*]], [[FR.REQUIREMENT:*]], [[INTERFACE:*]], [[DM.ENTITY:*]], [[UNIT:*]], [[NODE:*]], [[CONCERN:*]], [[TACTIC:*]], [[DECISION:*]]"
|
|
1871
1873
|
labeled: true
|
|
1872
1874
|
qualified: true
|
|
1873
1875
|
properties:
|
|
@@ -1960,6 +1962,7 @@
|
|
|
1960
1962
|
diagram:
|
|
1961
1963
|
type: hub
|
|
1962
1964
|
center: self
|
|
1965
|
+
nodes: "[[TS.TIER:*]], [[TS.TIER:*.COMPONENT:*]], [[DV.TIER:*]]"
|
|
1963
1966
|
hierarchy: true
|
|
1964
1967
|
properties:
|
|
1965
1968
|
- PRODUCT
|
|
@@ -1975,7 +1978,7 @@
|
|
|
1975
1978
|
diagram:
|
|
1976
1979
|
type: hub
|
|
1977
1980
|
center: self
|
|
1978
|
-
nodes: "[[TS.TIER:*.COMPONENT:*]], [[FV.COMPONENT:*]], [[TACTIC:*]], [[DECISION:*]]"
|
|
1981
|
+
nodes: "[[TS.TIER:*.COMPONENT:*]], [[FV.COMPONENT:*]], [[CONCERN:*]], [[TACTIC:*]], [[DECISION:*]]"
|
|
1979
1982
|
labeled: true
|
|
1980
1983
|
qualified: true
|
|
1981
1984
|
properties:
|
|
@@ -2434,7 +2437,7 @@
|
|
|
2434
2437
|
diagram:
|
|
2435
2438
|
type: hub
|
|
2436
2439
|
center: self
|
|
2437
|
-
nodes: "[[DV.TIER:*]], [[AREA:*]], [[NODE:*]], [[NETWORK:*]], [[FV.COMPONENT:*]], [[UNIT:*]], [[TACTIC:*]], [[DECISION:*]]"
|
|
2440
|
+
nodes: "[[DV.TIER:*]], [[AREA:*]], [[NODE:*]], [[NETWORK:*]], [[FV.COMPONENT:*]], [[UNIT:*]], [[CONCERN:*]], [[TACTIC:*]], [[DECISION:*]]"
|
|
2438
2441
|
labeled: true
|
|
2439
2442
|
qualified: true
|
|
2440
2443
|
properties:
|
|
@@ -2642,7 +2645,7 @@
|
|
|
2642
2645
|
diagram:
|
|
2643
2646
|
type: hub
|
|
2644
2647
|
center: self
|
|
2645
|
-
nodes: "[[TACTIC:*]], [[NR.REQUIREMENT:*]], [[FV.COMPONENT:*]], [[UNIT:*]], [[ASPECT:*]], [[NODE:*]], [[DM.ENTITY:*]], [[CONCERN:*]], [[DECISION:*]], [[TS.TIER:*.COMPONENT:*]]"
|
|
2648
|
+
nodes: "[[TACTIC:*]], [[NR.REQUIREMENT:*]], [[FV.COMPONENT:*]], [[UNIT:*]], [[ASPECT:*]], [[NODE:*]], [[DM.ENTITY:*]], [[CONCERN:*]], [[DECISION:*]], [[TS.TIER:*.COMPONENT:*]], [[TEST-CASE:*]]"
|
|
2646
2649
|
labeled: true
|
|
2647
2650
|
qualified: true
|
|
2648
2651
|
properties:
|
package/plugin/meta/ase-skill.md
CHANGED
|
@@ -476,6 +476,13 @@ Template Patterns
|
|
|
476
476
|
un-styled text <raw-title/>.
|
|
477
477
|
- Set <bar/> to the `─` character repeated exactly max(0, 67 - <raw-title-len/>)
|
|
478
478
|
times -- clamped to zero so an over-long title never yields a negative count.
|
|
479
|
+
- Pre-wrap every line of <content/> at *96 visible columns* (the box
|
|
480
|
+
width minus the `│ ` prefix, Markdown markup not counted), breaking
|
|
481
|
+
only at word boundaries and never inside an identifier, path, URL,
|
|
482
|
+
or quoted code: the agent tool wraps longer lines *without* the `│ `
|
|
483
|
+
prefix and visibly breaks the box. Indent each continuation line to
|
|
484
|
+
the text column of its first line (e.g. behind a key glyph, bullet,
|
|
485
|
+
or padded label), so gutters stay aligned.
|
|
479
486
|
- Set <body/> to <content/> with all non-empty lines prefixed with
|
|
480
487
|
`│ ` and all empty lines prefixed with just `│` (so no trailing
|
|
481
488
|
whitespace is produced). Do *not* prefix any line twice -- the
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "ase-terse"
|
|
3
|
+
description: Respond tersely, leading with results and skipping preamble and narration.
|
|
4
|
+
keep-coding-instructions: true
|
|
5
|
+
force-for-plugin: false
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## ASE Output Style
|
|
9
|
+
|
|
10
|
+
In your communication to the user you should:
|
|
11
|
+
|
|
12
|
+
- **Assume no shared context**:
|
|
13
|
+
The user has not read anything you read: no files, no tool output, no
|
|
14
|
+
search results. Only your messages. Assume the user has no prior context on
|
|
15
|
+
internal files or draft iterations.
|
|
16
|
+
|
|
17
|
+
- **Respond tersely**:
|
|
18
|
+
The user chose brevity over narration, so keep your responses short and
|
|
19
|
+
direct while doing the work just as thoroughly.
|
|
20
|
+
|
|
21
|
+
- **Lead with the result**:
|
|
22
|
+
Your **FIRST** sentence **MUST** state the direct answer, verdict, or status
|
|
23
|
+
immediately and with zero preamble (like "Let me...", "Now I'll...", etc.).
|
|
24
|
+
|
|
25
|
+
- **Cut narration, keep substance**:
|
|
26
|
+
Don't restate the request, the plan, or each step you took. Report outcomes,
|
|
27
|
+
decisions, and anything the user must act on.
|
|
28
|
+
|
|
29
|
+
- **Select formatting per output block**:
|
|
30
|
+
Map decisions/tradeoffs to a *table*, temporal steps to a *numbered
|
|
31
|
+
list*, concepts to *bulleted list* anchors, caveats/warnings/notices to a
|
|
32
|
+
*callout/quote* (`> **Notice:**`), previews of code edits to a *unified
|
|
33
|
+
diff*, and execution syntax to a *code block*.
|
|
34
|
+
|
|
35
|
+
- **Execute tools silently**:
|
|
36
|
+
Perform intermediate tool operations silently without narrating your intent
|
|
37
|
+
or planned tool calls in your messages.
|
|
38
|
+
|
|
39
|
+
- **State things plainly**:
|
|
40
|
+
Skip hedging boilerplate. Mention a caveat only when it changes what the
|
|
41
|
+
user should do next.
|
|
42
|
+
|
|
43
|
+
- **Give full detail on request**:
|
|
44
|
+
When the user asks for an explanation or detail, answer completely.
|
|
45
|
+
Conciseness never means withholding requested information.
|
|
46
|
+
|
|
47
|
+
- **Never trade correctness for brevity**:
|
|
48
|
+
Error reports, failing test output, security warnings, and confirmations for
|
|
49
|
+
destructive actions keep their full content.
|
|
50
|
+
|
|
51
|
+
- **End without closing recap**:
|
|
52
|
+
Avoid telling the user what you already said in the same turn.
|
|
53
|
+
|
|
54
|
+
Where these rules conflict with the default communication and formatting
|
|
55
|
+
guidance of the agent harness, these rules here win. But these rules never
|
|
56
|
+
override the ASE persona communication style, which stays in effect at all
|
|
57
|
+
times.
|
|
58
|
+
|