@rse/ase 0.9.55 → 0.9.57
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 +72 -31
- package/dst/ase-getopt.js +3 -1
- package/dst/ase-hook.js +5 -3
- package/dst/ase-markdown.js +7 -4
- package/dst/ase-service.js +1 -1
- package/dst/ase-setup.js +4 -2
- package/dst/ase-statusline.js +6 -8
- package/dst/ase-task.js +37 -11
- package/package.json +5 -5
- 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/agents/ase-code-analyze.md +1 -1
- package/plugin/agents/ase-code-lint.md +4 -4
- package/plugin/agents/ase-meta-chat.md +1 -1
- package/plugin/agents/ase-meta-proximity.md +1 -1
- package/plugin/meta/ase-common-code.md +168 -1
- package/plugin/meta/ase-common-task.md +31 -13
- package/plugin/meta/ase-constitution.md +3 -3
- package/plugin/meta/ase-control.md +7 -7
- package/plugin/meta/ase-dialog.md +5 -4
- package/plugin/meta/ase-format-arch.md +6 -21
- package/plugin/meta/ase-format-meta.md +15 -2
- package/plugin/meta/ase-format-spec.md +9 -24
- package/plugin/meta/ase-format-task.md +2 -2
- package/plugin/meta/ase-getopt.md +1 -1
- package/plugin/meta/ase-persona.md +3 -3
- package/plugin/meta/ase-skill.md +14 -24
- package/plugin/meta/ase-tenets.md +5 -5
- package/plugin/package.json +2 -2
- package/plugin/skills/ase-arch-analyze/SKILL.md +2 -2
- package/plugin/skills/ase-arch-analyze/help.md +1 -1
- package/plugin/skills/ase-arch-discover/SKILL.md +9 -7
- package/plugin/skills/ase-code-analyze/SKILL.md +1 -1
- package/plugin/skills/ase-code-analyze/help.md +1 -1
- package/plugin/skills/ase-code-craft/SKILL.md +2 -153
- package/plugin/skills/ase-code-craft/help.md +1 -1
- package/plugin/skills/ase-code-explain/SKILL.md +3 -3
- package/plugin/skills/ase-code-insight/SKILL.md +9 -7
- package/plugin/skills/ase-code-lint/SKILL.md +7 -5
- package/plugin/skills/ase-code-refactor/SKILL.md +4 -155
- package/plugin/skills/ase-code-resolve/SKILL.md +48 -173
- package/plugin/skills/ase-code-resolve/help.md +15 -4
- package/plugin/skills/ase-docs-distill/SKILL.md +2 -2
- package/plugin/skills/ase-docs-distill/help.md +1 -1
- package/plugin/skills/ase-docs-proofread/SKILL.md +7 -3
- package/plugin/skills/ase-help-intent/SKILL.md +33 -9
- package/plugin/skills/ase-help-skill/SKILL.md +1 -1
- package/plugin/skills/ase-meta-brainstorm/SKILL.md +11 -2
- package/plugin/skills/ase-meta-changelog/SKILL.md +13 -8
- package/plugin/skills/ase-meta-chat/SKILL.md +9 -0
- package/plugin/skills/ase-meta-commit/SKILL.md +11 -1
- package/plugin/skills/ase-meta-compat/SKILL.md +2 -2
- package/plugin/skills/ase-meta-config/help.md +3 -2
- package/plugin/skills/ase-meta-diaboli/SKILL.md +12 -3
- package/plugin/skills/ase-meta-diff/SKILL.md +11 -13
- package/plugin/skills/ase-meta-eli5/SKILL.md +3 -3
- package/plugin/skills/ase-meta-eli5/help.md +1 -1
- package/plugin/skills/ase-meta-evaluate/SKILL.md +3 -3
- package/plugin/skills/ase-meta-quorum/SKILL.md +17 -17
- package/plugin/skills/ase-meta-quotes/SKILL.md +1 -1
- package/plugin/skills/ase-meta-review/SKILL.md +7 -5
- package/plugin/skills/ase-meta-review/help.md +2 -2
- package/plugin/skills/ase-meta-search/SKILL.md +18 -0
- package/plugin/skills/ase-meta-steelman/SKILL.md +11 -2
- package/plugin/skills/ase-meta-why/SKILL.md +15 -6
- package/plugin/skills/ase-meta-why/help.md +3 -3
- package/plugin/skills/ase-sync-export/SKILL.md +2 -1
- package/plugin/skills/ase-sync-export/help.md +1 -1
- package/plugin/skills/ase-sync-reconcile/SKILL.md +16 -2
- package/plugin/skills/ase-task-condense/SKILL.md +13 -67
- package/plugin/skills/ase-task-delete/SKILL.md +1 -1
- package/plugin/skills/ase-task-dissect/SKILL.md +5 -18
- package/plugin/skills/ase-task-edit/SKILL.md +49 -22
- package/plugin/skills/ase-task-grill/SKILL.md +31 -129
- package/plugin/skills/ase-task-id/SKILL.md +12 -2
- package/plugin/skills/ase-task-implement/SKILL.md +11 -7
- package/plugin/skills/ase-task-implement/help.md +1 -1
- package/plugin/skills/ase-task-list/SKILL.md +9 -0
- package/plugin/skills/ase-task-preflight/SKILL.md +17 -33
- package/plugin/skills/ase-task-reboot/SKILL.md +24 -100
- package/plugin/skills/ase-task-reboot/help.md +1 -1
- package/plugin/skills/ase-task-view/SKILL.md +4 -2
- package/dst/ase-guidance.js +0 -89
- package/dst/ase-persona.js +0 -87
- package/dst/ase-stdout.js +0 -18
package/dst/ase-config.js
CHANGED
|
@@ -124,27 +124,33 @@ const gitToplevel = () => {
|
|
|
124
124
|
gitToplevelCache.set(cwd, top);
|
|
125
125
|
return top === "" ? null : top;
|
|
126
126
|
};
|
|
127
|
-
/*
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
127
|
+
/* determine the project root directory, i.e. either the top-level
|
|
128
|
+
directory of the Git working tree or the nearest directory at or
|
|
129
|
+
above cwd which carries a ".ase" directory */
|
|
130
|
+
const projectRoot = () => {
|
|
131
|
+
const top = gitToplevel();
|
|
132
|
+
if (top !== null)
|
|
133
|
+
return top;
|
|
132
134
|
let dir = fs.realpathSync(process.cwd());
|
|
133
135
|
for (;;) {
|
|
134
136
|
if (fs.existsSync(path.join(dir, ".ase")))
|
|
135
|
-
return
|
|
137
|
+
return dir;
|
|
136
138
|
const parent = path.dirname(dir);
|
|
137
139
|
if (parent === dir)
|
|
138
|
-
return
|
|
140
|
+
return null;
|
|
139
141
|
dir = parent;
|
|
140
142
|
}
|
|
141
143
|
};
|
|
144
|
+
/* detect whether a project context exists, i.e. either we are inside
|
|
145
|
+
a Git working tree or a ".ase" directory is present at or above cwd */
|
|
146
|
+
const hasProjectContext = () => projectRoot() !== null;
|
|
142
147
|
/* parse a raw "--scope" option value into a canonical Scope chain;
|
|
143
148
|
accepts a comma-separated list of terms in any order. The "user"
|
|
144
149
|
term is always implicitly added at the bottom of the chain; the
|
|
145
150
|
"project" term is implicitly added only when a project context
|
|
146
|
-
exists (Git repository or ".ase" directory at or above cwd)
|
|
147
|
-
|
|
151
|
+
exists (Git repository or ".ase" directory at or above cwd) and it
|
|
152
|
+
stays weaker than the strongest explicitly requested term, and an
|
|
153
|
+
explicit "project" term requires that same context */
|
|
148
154
|
export const parseScope = (value) => {
|
|
149
155
|
const projectActive = hasProjectContext();
|
|
150
156
|
const input = (value === undefined || value === "") ?
|
|
@@ -162,7 +168,11 @@ export const parseScope = (value) => {
|
|
|
162
168
|
if (seen.has("project") && !projectActive)
|
|
163
169
|
throw new Error("invalid --scope: \"project\" requires a project context " +
|
|
164
170
|
"(a Git repository or a \".ase\" directory at or above the current directory)");
|
|
165
|
-
|
|
171
|
+
/* the strongest term of the chain is the write target, so an implicitly
|
|
172
|
+
added "project" term must never outrank the strongest explicitly
|
|
173
|
+
requested term, as this would silently retarget the caller's request */
|
|
174
|
+
const rankMax = Math.max(...terms.map((t) => scopeRank(t.kind)));
|
|
175
|
+
if (!seen.has("project") && projectActive && rankMax > scopeRank("project"))
|
|
166
176
|
terms.unshift({ kind: "project" });
|
|
167
177
|
if (!seen.has("user"))
|
|
168
178
|
terms.unshift({ kind: "user" });
|
|
@@ -210,6 +220,7 @@ export class Config {
|
|
|
210
220
|
log;
|
|
211
221
|
docs;
|
|
212
222
|
target;
|
|
223
|
+
pruned;
|
|
213
224
|
/* creation */
|
|
214
225
|
constructor(name, schema, log, scope = [{ kind: "user" }, { kind: "project" }]) {
|
|
215
226
|
if (scope.length === 0)
|
|
@@ -222,6 +233,7 @@ export class Config {
|
|
|
222
233
|
this.filename = this.resolveFilename(name, tgt);
|
|
223
234
|
this.docs = [{ scope: tgt, filename: this.filename, doc: new Document() }];
|
|
224
235
|
this.target = 0;
|
|
236
|
+
this.pruned = [];
|
|
225
237
|
}
|
|
226
238
|
/* render a scope term as a short textual label */
|
|
227
239
|
static scopeLabel(term) {
|
|
@@ -252,16 +264,13 @@ export class Config {
|
|
|
252
264
|
return path.join(this.userConfigDir(), `${name}.yaml`);
|
|
253
265
|
else if (term.kind === "project") {
|
|
254
266
|
const rel = path.join(".ase", `${name}.yaml`);
|
|
255
|
-
const
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
this.findUpward(cwd, top, rel) :
|
|
259
|
-
(fs.existsSync(path.join(cwd, rel)) ? path.join(cwd, rel) : null);
|
|
260
|
-
return found ?? path.join(top ?? cwd, rel);
|
|
267
|
+
const root = projectRoot() ?? process.cwd();
|
|
268
|
+
const found = this.findUpward(process.cwd(), root, rel);
|
|
269
|
+
return found ?? path.join(root, rel);
|
|
261
270
|
}
|
|
262
271
|
else if (term.kind === "task") {
|
|
263
|
-
const
|
|
264
|
-
return path.join(
|
|
272
|
+
const root = projectRoot() ?? process.cwd();
|
|
273
|
+
return path.join(root, ".ase", "task", term.id, `${name}.yaml`);
|
|
265
274
|
}
|
|
266
275
|
else
|
|
267
276
|
return path.join(os.homedir(), ".ase", "session", term.id, `${name}.yaml`);
|
|
@@ -290,6 +299,7 @@ export class Config {
|
|
|
290
299
|
read(mode = "lenient") {
|
|
291
300
|
const chain = this.scope;
|
|
292
301
|
const docs = [];
|
|
302
|
+
this.pruned = [];
|
|
293
303
|
for (let i = 0; i < chain.length; i++) {
|
|
294
304
|
const sc = chain[i];
|
|
295
305
|
if (sc.kind === "default") {
|
|
@@ -321,6 +331,8 @@ export class Config {
|
|
|
321
331
|
if (perDocMode === "strict")
|
|
322
332
|
throw new Error(msg);
|
|
323
333
|
this.log.write("warning", msg);
|
|
334
|
+
if (isTarget)
|
|
335
|
+
this.pruned.push(`unparsable YAML (${doc.errors[0].message.split("\n")[0]})`);
|
|
324
336
|
doc = new Document();
|
|
325
337
|
}
|
|
326
338
|
docs.push({ scope: sc, filename, doc });
|
|
@@ -330,7 +342,9 @@ export class Config {
|
|
|
330
342
|
for (let i = 0; i < docs.length; i++) {
|
|
331
343
|
const isTarget = (i === this.target);
|
|
332
344
|
const perDocMode = isTarget ? mode : "lenient";
|
|
333
|
-
this.validateDoc(docs[i].doc, docs[i].filename, perDocMode);
|
|
345
|
+
const removed = this.validateDoc(docs[i].doc, docs[i].filename, perDocMode);
|
|
346
|
+
if (isTarget)
|
|
347
|
+
this.pruned.push(...removed);
|
|
334
348
|
}
|
|
335
349
|
}
|
|
336
350
|
/* acquire a cross-process advisory lock on the target scope's file,
|
|
@@ -355,18 +369,28 @@ export class Config {
|
|
|
355
369
|
const td = this.docs[this.target];
|
|
356
370
|
if (td.scope.kind === "default")
|
|
357
371
|
throw new Error("internal error: \"default\" scope is not writable");
|
|
372
|
+
/* a lenient read physically removes the invalid content from the in-memory
|
|
373
|
+
target document, so writing that document back would silently erase the
|
|
374
|
+
very same content from the file on disk */
|
|
375
|
+
if (this.pruned.length > 0)
|
|
376
|
+
throw new Error(`refusing to overwrite ${td.filename}: it carries invalid content ` +
|
|
377
|
+
`(${this.pruned.join(", ")}) which was skipped on reading and hence would be ` +
|
|
378
|
+
"lost on writing -- repair the file first");
|
|
358
379
|
this.validateDoc(td.doc, td.filename, "strict");
|
|
359
380
|
fs.mkdirSync(path.dirname(td.filename), { recursive: true });
|
|
360
381
|
writeFileAtomic.sync(td.filename, td.doc.toString({ indent: 4 }), { encoding: "utf8" });
|
|
361
382
|
}
|
|
362
|
-
/* validate a single YAML document against the optional schema
|
|
383
|
+
/* validate a single YAML document against the optional schema; in "strict"
|
|
384
|
+
mode all invalid entries are reported as a thrown error, in "lenient" mode
|
|
385
|
+
they are removed from the document and their dotted paths are returned */
|
|
363
386
|
validateDoc(doc, filename, mode = "strict") {
|
|
364
387
|
if (this.schema === null)
|
|
365
|
-
return;
|
|
388
|
+
return [];
|
|
389
|
+
const removed = [];
|
|
366
390
|
for (;;) {
|
|
367
391
|
const result = v.safeParse(this.schema, doc.toJS());
|
|
368
392
|
if (result.success)
|
|
369
|
-
return;
|
|
393
|
+
return removed;
|
|
370
394
|
if (mode === "strict") {
|
|
371
395
|
const issues = result.issues.map((i) => {
|
|
372
396
|
const dotPath = (i.path ?? []).map((p) => String(p.key)).join(".");
|
|
@@ -374,19 +398,20 @@ export class Config {
|
|
|
374
398
|
}).join("; ");
|
|
375
399
|
throw new Error(`invalid configuration in ${filename}: ${issues}`);
|
|
376
400
|
}
|
|
377
|
-
|
|
401
|
+
const before = removed.length;
|
|
378
402
|
for (const i of result.issues) {
|
|
379
403
|
const segs = (i.path ?? []).map((p) => String(p.key));
|
|
380
404
|
const dotPath = segs.join(".");
|
|
381
405
|
this.log.write("warning", `invalid entry in ${filename}: ${dotPath ? `${dotPath}: ` : ""}${i.message}`);
|
|
382
|
-
if (segs.length > 0)
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
406
|
+
if (segs.length > 0 && doc.deleteIn(segs))
|
|
407
|
+
removed.push(dotPath);
|
|
408
|
+
/* issues at the document root and issues whose stringified path does not
|
|
409
|
+
address a deletable node (e.g. a non-string YAML key like "404:", which
|
|
410
|
+
"toJS" stringifies) cannot be removed; processing continues with the
|
|
411
|
+
remaining issues */
|
|
387
412
|
}
|
|
388
|
-
if (
|
|
389
|
-
return;
|
|
413
|
+
if (removed.length === before)
|
|
414
|
+
return removed;
|
|
390
415
|
}
|
|
391
416
|
}
|
|
392
417
|
/* enumerate all full dotted leaf paths from the attached valibot schema */
|
|
@@ -444,6 +469,21 @@ export class Config {
|
|
|
444
469
|
}
|
|
445
470
|
return undefined;
|
|
446
471
|
}
|
|
472
|
+
/* retrieve the explicitly configured value at a dotted key, i.e. the same
|
|
473
|
+
cascade as "get", but skipping the built-in "default" scope layer, so
|
|
474
|
+
callers can distinguish a deliberately configured value from a merely
|
|
475
|
+
preset one */
|
|
476
|
+
getExplicit(key) {
|
|
477
|
+
const segs = this.resolveKey(key).split(".");
|
|
478
|
+
for (let i = this.docs.length - 1; i >= 0; i--) {
|
|
479
|
+
if (this.docs[i].scope.kind === "default")
|
|
480
|
+
continue;
|
|
481
|
+
const node = this.docs[i].doc.getIn(segs);
|
|
482
|
+
if (node !== undefined)
|
|
483
|
+
return node;
|
|
484
|
+
}
|
|
485
|
+
return undefined;
|
|
486
|
+
}
|
|
447
487
|
/* enumerate the effective leaf entries across the full scope chain;
|
|
448
488
|
each returned entry identifies the originating scope */
|
|
449
489
|
entries() {
|
|
@@ -551,7 +591,8 @@ export default class ConfigCommand {
|
|
|
551
591
|
.option("--scope <scope>", "configuration scope chain: comma-separated list of \"user\", \"project\", " +
|
|
552
592
|
"\"task:<id>\", and/or \"session:<id>\" terms (e.g. \"task:N,session:M\"); " +
|
|
553
593
|
"\"user\" is always implicitly included and \"project\" is implicitly " +
|
|
554
|
-
"included whenever a project context (Git repo or upward \".ase\" directory) exists"
|
|
594
|
+
"included whenever a project context (Git repo or upward \".ase\" directory) exists, " +
|
|
595
|
+
"but never above the strongest explicitly requested term")
|
|
555
596
|
.description("manage ASE configuration")
|
|
556
597
|
.action(() => {
|
|
557
598
|
configCmd.outputHelp();
|
package/dst/ase-getopt.js
CHANGED
|
@@ -7,7 +7,7 @@ import { z } from "zod";
|
|
|
7
7
|
import { Command, Option } from "commander";
|
|
8
8
|
import { parse as shParse, quote as shQuote } from "shell-quote";
|
|
9
9
|
/* tokenize a raw input string into [start,end) token ranges, preserving
|
|
10
|
-
the quoting so the original text can later be sliced verbatim */
|
|
10
|
+
the quoting and escaping so the original text can later be sliced verbatim */
|
|
11
11
|
const tokenizeRanges = (input) => {
|
|
12
12
|
const ranges = [];
|
|
13
13
|
let i = 0;
|
|
@@ -30,6 +30,8 @@ const tokenizeRanges = (input) => {
|
|
|
30
30
|
if (i < input.length)
|
|
31
31
|
i++;
|
|
32
32
|
}
|
|
33
|
+
else if (ch === "\\" && i + 1 < input.length)
|
|
34
|
+
i += 2;
|
|
33
35
|
else
|
|
34
36
|
i++;
|
|
35
37
|
}
|
package/dst/ase-hook.js
CHANGED
|
@@ -240,10 +240,12 @@ export default class HookCommand {
|
|
|
240
240
|
const projectId = path.basename(projectDir);
|
|
241
241
|
/* determine user id */
|
|
242
242
|
const userId = process.env.USER ?? process.env.LOGNAME ?? "unknown";
|
|
243
|
-
/* helper function: determine a setting from the
|
|
244
|
-
falling back to an environment variable and a default
|
|
243
|
+
/* helper function: determine a setting from the explicitly configured
|
|
244
|
+
scopes, falling back to an environment variable and a default; the
|
|
245
|
+
built-in "default" scope layer is deliberately skipped, as its preset
|
|
246
|
+
value is always present and would hence shadow the environment variable */
|
|
245
247
|
const setting = (key, envVar, dflt) => {
|
|
246
|
-
const val = cfg.
|
|
248
|
+
const val = cfg.getExplicit(key);
|
|
247
249
|
return typeof val === "string" ? val : (process.env[envVar] ?? dflt);
|
|
248
250
|
};
|
|
249
251
|
/* determine agent persona style, agent guidance level, and project boxing transparency */
|
package/dst/ase-markdown.js
CHANGED
|
@@ -19,9 +19,12 @@ export class Markdown {
|
|
|
19
19
|
/* segment the input line-wise into alternating non-fenced and
|
|
20
20
|
fenced regions: a fenced code block opens with a line whose first
|
|
21
21
|
non-whitespace content is a run of 3+ backticks or tildes and
|
|
22
|
-
closes with a matching-or-longer run of the same marker;
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
closes with a matching-or-longer run of the same marker; for a
|
|
23
|
+
backtick fence the info string trailing the run must not carry any
|
|
24
|
+
further backtick, so an inline code span delimited by a run of 3+
|
|
25
|
+
backticks never opens a fence; fenced regions are emitted verbatim
|
|
26
|
+
while only non-fenced regions are handed to the rewriting passes
|
|
27
|
+
below */
|
|
25
28
|
const lines = text.split("\n");
|
|
26
29
|
let result = "";
|
|
27
30
|
let buf = "";
|
|
@@ -37,7 +40,7 @@ export class Markdown {
|
|
|
37
40
|
for (let li = 0; li < lines.length; li++) {
|
|
38
41
|
const line = lines[li];
|
|
39
42
|
const nl = li < lines.length - 1 ? "\n" : "";
|
|
40
|
-
const m = line.match(/^\s*(`{3,}|~{3,})/);
|
|
43
|
+
const m = line.match(/^\s*(`{3,}(?![^`]*`)|~{3,})/);
|
|
41
44
|
if (!inFence && m) {
|
|
42
45
|
/* a fence-opening line: flush the pending non-fenced buffer,
|
|
43
46
|
enter fenced mode, and emit the opener verbatim */
|
package/dst/ase-service.js
CHANGED
|
@@ -439,7 +439,7 @@ export default class ServiceCommand {
|
|
|
439
439
|
return await new Promise(() => { });
|
|
440
440
|
}
|
|
441
441
|
if (port !== null) {
|
|
442
|
-
const match = await probe(port, ctx.projectId);
|
|
442
|
+
const match = await probe(port, ctx.projectId).catch(() => null);
|
|
443
443
|
if (match === true) {
|
|
444
444
|
this.log.write("info", `service: already running on port ${port}`);
|
|
445
445
|
return 0;
|
package/dst/ase-setup.js
CHANGED
|
@@ -808,9 +808,11 @@ export default class SetupCommand {
|
|
|
808
808
|
/* default for --dev derived from ASE_SETUP_DEV environment variable */
|
|
809
809
|
const envDev = process.env.ASE_SETUP_DEV ?? "";
|
|
810
810
|
const devDflt = envDev !== "" && envDev !== "0" && envDev.toLowerCase() !== "false";
|
|
811
|
-
/* default for --tool derived from ASE_TOOL environment variable
|
|
811
|
+
/* default for --tool derived from ASE_TOOL environment variable
|
|
812
|
+
(validated lazily by "parseTool" in each action, so an invalid
|
|
813
|
+
value cannot break unrelated "ase" commands at startup) */
|
|
812
814
|
const envTool = process.env.ASE_TOOL ?? "";
|
|
813
|
-
const toolDflt = envTool !== "" ?
|
|
815
|
+
const toolDflt = envTool !== "" ? envTool : "claude";
|
|
814
816
|
/* register CLI top-level command "ase setup" */
|
|
815
817
|
const setupCmd = program
|
|
816
818
|
.command("setup")
|
package/dst/ase-statusline.js
CHANGED
|
@@ -388,25 +388,23 @@ export default class StatuslineCommand {
|
|
|
388
388
|
/* ==== RATE LIMITS ==== */
|
|
389
389
|
S: () => {
|
|
390
390
|
const pct5h = data.rate_limits?.five_hour?.used_percentage;
|
|
391
|
-
|
|
392
|
-
|
|
391
|
+
const s = pct5h !== undefined ? `${pct5h.toFixed(1)}%` : "-";
|
|
392
|
+
emit(`${prefix("⏲", "5h-usage")}${c.bold(s)}`);
|
|
393
393
|
},
|
|
394
394
|
D: () => {
|
|
395
395
|
const until5h = data.rate_limits?.five_hour?.resets_at;
|
|
396
396
|
const s = formatTimeUntil(until5h);
|
|
397
|
-
|
|
398
|
-
emit(`${prefix("⏱", "session-resets")}${c.bold(s)}`);
|
|
397
|
+
emit(`${prefix("⏱", "5h-resets")}${c.bold(s !== "" ? s : "-")}`);
|
|
399
398
|
},
|
|
400
399
|
W: () => {
|
|
401
400
|
const pctWk = data.rate_limits?.seven_day?.used_percentage;
|
|
402
|
-
|
|
403
|
-
|
|
401
|
+
const s = pctWk !== undefined ? `${pctWk.toFixed(1)}%` : "-";
|
|
402
|
+
emit(`${prefix("⏲", "7d-usage")}${c.bold(s)}`);
|
|
404
403
|
},
|
|
405
404
|
Q: () => {
|
|
406
405
|
const untilWk = data.rate_limits?.seven_day?.resets_at;
|
|
407
406
|
const s = formatTimeUntil(untilWk);
|
|
408
|
-
|
|
409
|
-
emit(`${prefix("⏱", "weekly-resets")}${c.bold(s)}`);
|
|
407
|
+
emit(`${prefix("⏱", "7d-resets")}${c.bold(s !== "" ? s : "-")}`);
|
|
410
408
|
},
|
|
411
409
|
/* ==== COSTS ==== */
|
|
412
410
|
H: () => {
|
package/dst/ase-task.js
CHANGED
|
@@ -334,6 +334,7 @@ export class Task {
|
|
|
334
334
|
/* set the active task id for a given session */
|
|
335
335
|
static setId(log, session, id) {
|
|
336
336
|
Task.validateSession(session);
|
|
337
|
+
Task.validateId(id);
|
|
337
338
|
const scope = parseScope(`session:${session}`);
|
|
338
339
|
const cfg = new Config("config", configSchema, log, scope);
|
|
339
340
|
cfg.lock(() => {
|
|
@@ -529,15 +530,33 @@ export class TaskMCP {
|
|
|
529
530
|
title: "ASE task load",
|
|
530
531
|
description: "Load a previously persisted task by `id`. " +
|
|
531
532
|
"Returns the task as `text`, normalized into the current Markdown frontmatter shape; " +
|
|
532
|
-
"returns an empty string if no task exists for the `id`."
|
|
533
|
+
"returns an empty string if no task exists for the `id`. " +
|
|
534
|
+
"The `variant` argument selects the returned form of the plan: " +
|
|
535
|
+
"`source` (the default) is the *authoring* form and the *only* form which may be " +
|
|
536
|
+
"edited and passed back into `ase_task_save`; " +
|
|
537
|
+
"`render` is the *rendering-prepared* form, which is for *display only* and " +
|
|
538
|
+
"MUST NOT be persisted; " +
|
|
539
|
+
"`both` returns the source form enclosed in `<task-plan-source>` delimiter lines, " +
|
|
540
|
+
"followed by the render form enclosed in `<task-plan-render>` delimiter lines.",
|
|
533
541
|
inputSchema: {
|
|
534
542
|
id: z.string()
|
|
535
|
-
.describe("task identifier (allowed characters: A-Z, a-z, 0-9, '_', '-')")
|
|
543
|
+
.describe("task identifier (allowed characters: A-Z, a-z, 0-9, '_', '-')"),
|
|
544
|
+
variant: z.enum(["source", "render", "both"]).optional()
|
|
545
|
+
.describe("returned form of the plan: `source` (authoring form, the default), " +
|
|
546
|
+
"`render` (rendering-prepared form, display only), or `both` " +
|
|
547
|
+
"(both forms, each enclosed in its delimiter lines)")
|
|
536
548
|
}
|
|
537
549
|
}, async (args) => {
|
|
538
550
|
try {
|
|
539
|
-
const
|
|
540
|
-
const
|
|
551
|
+
const source = Task.load(this.log, args.id);
|
|
552
|
+
const variant = args.variant ?? "source";
|
|
553
|
+
let text = source;
|
|
554
|
+
if (source !== "" && variant === "render")
|
|
555
|
+
text = Markdown.prepare(source);
|
|
556
|
+
else if (source !== "" && variant === "both")
|
|
557
|
+
text =
|
|
558
|
+
`<task-plan-source>\n${source}\n</task-plan-source>\n\n` +
|
|
559
|
+
`<task-plan-render>\n${Markdown.prepare(source)}\n</task-plan-render>\n`;
|
|
541
560
|
return {
|
|
542
561
|
content: [{ type: "text", text }]
|
|
543
562
|
};
|
|
@@ -550,21 +569,28 @@ export class TaskMCP {
|
|
|
550
569
|
mcp.registerTool("ase_task_save", {
|
|
551
570
|
title: "ASE task save",
|
|
552
571
|
description: "Persist a task as `text` under `id`. " +
|
|
553
|
-
"
|
|
554
|
-
"
|
|
572
|
+
"The `text` MUST be the *authoring* form of the plan (as returned by the " +
|
|
573
|
+
"`source` variant of `ase_task_load`) and hence MUST NOT carry any rendering " +
|
|
574
|
+
"artifacts. Overwrites any existing task for the same `id`. " +
|
|
575
|
+
"Returns a status `text` by default, or, if `render` is `true`, the " +
|
|
576
|
+
"*rendering-prepared* form of the just-saved plan, for display purposes only.",
|
|
555
577
|
inputSchema: {
|
|
556
578
|
id: z.string()
|
|
557
579
|
.describe("task identifier (allowed characters: A-Z, a-z, 0-9, '_', '-')"),
|
|
558
580
|
text: z.string()
|
|
559
|
-
.describe("text content of the task")
|
|
581
|
+
.describe("text content of the task, in its authoring form"),
|
|
582
|
+
render: z.boolean().optional()
|
|
583
|
+
.describe("if true, return the rendering-prepared form of the just-saved " +
|
|
584
|
+
"plan instead of a status message (default: false)")
|
|
560
585
|
}
|
|
561
586
|
}, async (args) => {
|
|
562
587
|
try {
|
|
563
588
|
Task.save(this.log, args.id, args.text);
|
|
564
|
-
/* return the prepared content, so a caller
|
|
565
|
-
just-saved plan
|
|
566
|
-
|
|
567
|
-
|
|
589
|
+
/* return the rendering-prepared content on demand, so a caller
|
|
590
|
+
displaying the just-saved plan does not have to re-load it */
|
|
591
|
+
const text = (args.render ?? false) ?
|
|
592
|
+
Markdown.prepare(args.text) :
|
|
593
|
+
`OK: saved task "${args.id}"`;
|
|
568
594
|
return {
|
|
569
595
|
content: [{ type: "text", text }]
|
|
570
596
|
};
|
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.57",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"author": {
|
|
12
12
|
"name": "Dr. Ralf S. Engelschall",
|
|
@@ -18,12 +18,12 @@
|
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"eslint": "9.39.4",
|
|
20
20
|
"@eslint/js": "9.39.4",
|
|
21
|
-
"@typescript-eslint/parser": "8.
|
|
22
|
-
"@typescript-eslint/eslint-plugin": "8.
|
|
21
|
+
"@typescript-eslint/parser": "8.66.0",
|
|
22
|
+
"@typescript-eslint/eslint-plugin": "8.66.0",
|
|
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.
|
|
26
|
+
"globals": "17.9.0",
|
|
27
27
|
"typescript": "6.0.3",
|
|
28
28
|
|
|
29
29
|
"@rse/stx": "1.1.6",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"commander": "15.0.0",
|
|
45
|
-
"@dotenvx/dotenvx": "2.19.
|
|
45
|
+
"@dotenvx/dotenvx": "2.19.2",
|
|
46
46
|
"yaml": "2.9.0",
|
|
47
47
|
"valibot": "1.4.2",
|
|
48
48
|
"execa": "10.0.1",
|
|
@@ -63,7 +63,7 @@ Workflow
|
|
|
63
63
|
- incorrect short-circuit evaluation or operator precedence
|
|
64
64
|
- logical vs. bitwise operator confusion
|
|
65
65
|
- negation mistakes in compound predicates
|
|
66
|
-
- wrong comparison operator (`==` instead of `===`, `<` vs. `<=`, etc)
|
|
66
|
+
- wrong comparison operator (`==` instead of `===`, `<` vs. `<=`, etc.)
|
|
67
67
|
- inverted condition or swapped if/else branches
|
|
68
68
|
- dead or duplicated conditional branches
|
|
69
69
|
- fall-through where a break or return was intended
|
|
@@ -65,10 +65,10 @@ Workflow
|
|
|
65
65
|
identifiers, string literals and comments.
|
|
66
66
|
|
|
67
67
|
Especially, for comments ensure English language only and
|
|
68
|
-
prefer short very brief one-line descriptions.
|
|
68
|
+
prefer short, very brief one-line descriptions.
|
|
69
69
|
|
|
70
70
|
- **A05 - COMPLEXITY**:
|
|
71
|
-
Check for extremely long functions
|
|
71
|
+
Check for extremely long functions and deeply nested code
|
|
72
72
|
constructs.
|
|
73
73
|
|
|
74
74
|
Especially, for functions prefer fewer than 100 lines, and for
|
|
@@ -185,8 +185,8 @@ Workflow
|
|
|
185
185
|
- **A15 - PERFORMANCE**:
|
|
186
186
|
Check for bad performance and inefficiency issues.
|
|
187
187
|
|
|
188
|
-
Especially, check for code constructs with a high (i.e., not
|
|
189
|
-
constant/O(1), or linear/O(n)
|
|
188
|
+
Especially, check for code constructs with a high complexity (i.e., not
|
|
189
|
+
constant/O(1), or linear/O(n)) in their execution time
|
|
190
190
|
and/or memory consumption.
|
|
191
191
|
|
|
192
192
|
- **A16 - SECURITY**:
|
|
@@ -56,5 +56,5 @@ tools:
|
|
|
56
56
|
4. Now call the MCP tool `query(prompt: <query/>)` from the MCP server
|
|
57
57
|
<server/> and then return its result `text` *verbatim* and *without
|
|
58
58
|
any modifications* as the last message. Especially, do *NOT* add or
|
|
59
|
-
remove any text to the MCP server response on your own and do not
|
|
59
|
+
remove any text to or from the MCP server response on your own and do not
|
|
60
60
|
interpret the result in any way.
|
|
@@ -102,7 +102,7 @@ store those facts in the placeholder named `<arg2/>`.
|
|
|
102
102
|
<child-4/>.
|
|
103
103
|
|
|
104
104
|
<expand name="gather-facts"
|
|
105
|
-
arg1="the
|
|
105
|
+
arg1="the CHILD topics (narrower topics that are specializations) of the following topic: <topic/>"
|
|
106
106
|
arg2="facts-children"></expand>
|
|
107
107
|
|
|
108
108
|
Ground the determination of the canonical names of the most
|