@rse/ase 2.0.5 → 2.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-config-schema.js +1 -1
- package/dst/ase-setup-settings.js +111 -1
- package/dst/ase-setup.js +19 -7
- package/dst/ase-spec.js +184 -49
- package/dst/ase-task-board-tui-model.js +8 -1
- package/dst/ase-task-board-tui.js +26 -2
- package/dst/ase-task-board-web-client.js +10 -10
- package/dst/man/ase-artifact.md +1 -1
- package/dst/man/ase-config.md +5 -3
- package/dst/man/ase-mcp.md +1 -1
- package/dst/man/ase-setup-install.md +10 -1
- package/dst/man/ase-setup-status.md +6 -0
- package/dst/man/ase-setup-uninstall.md +3 -1
- package/dst/man/ase-setup-update.md +3 -0
- package/dst/man/ase-setup.md +4 -4
- package/dst/man/ase-spec-export.md +14 -3
- package/dst/man/ase-spec-init.md +10 -1
- package/dst/man/ase-spec-lint.md +2 -1
- package/dst/man/ase-spec-preview.md +10 -1
- package/dst/man/ase-spec-query.md +96 -0
- package/dst/man/ase-spec.md +32 -11
- package/dst/man/ase.md +3 -3
- package/package.json +18 -15
- package/plugin/.claude-plugin/icon.png +0 -0
- 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-spec.md +29 -5
- package/plugin/meta/ase-format-specbook.md +427 -47
- package/plugin/meta/ase-format-specbook.yaml +180 -78
- package/plugin/mods/ase-mods-common.ts +36 -6
- package/plugin/mods/ase-mods-evaluate.ts +227 -0
- package/plugin/mods/ase-mods-grill.ts +1 -30
- package/plugin/mods/ase-mods-help-browse.ts +18 -13
- package/plugin/mods/ase-mods-help-find.ts +17 -9
- package/plugin/mods/ase-mods.ts +23 -17
- package/plugin/skills/ase-spec-activate/SKILL.md +4 -1
- package/plugin/mods/claude-code.d.ts +0 -13440
|
@@ -13,16 +13,12 @@
|
|
|
13
13
|
"n" or "nX" into the prompt. */
|
|
14
14
|
|
|
15
15
|
import type { Elements, Frozen, MatchedEvent, RenderElement } from "claude-code"
|
|
16
|
-
import { type Engine, type Char, PICKED, style, width, wrap, split, paint } from "./ase-mods-common.ts"
|
|
16
|
+
import { type Engine, type Char, PICKED, ROW, RULE, cells, style, width, wrap, split, paint } from "./ase-mods-common.ts"
|
|
17
17
|
|
|
18
18
|
/* the marker line above a grilling table (single-round and multi-round,
|
|
19
19
|
optionally labeled with the perspective and/or grounding) */
|
|
20
20
|
const MARKER = /⧉ \*{0,2}ASE\*{0,2}: GRILLING(?: ROUND \d+\/\d+)?(?: \([^)]*\))?:/
|
|
21
21
|
|
|
22
|
-
/* a row and the delimiter row of a Markdown table */
|
|
23
|
-
const ROW = /^ *\|.*\| *$/
|
|
24
|
-
const RULE = /^ *\|(?: *:?-+:? *\|)+ *$/
|
|
25
|
-
|
|
26
22
|
/* the separator between two answers, the letter of
|
|
27
23
|
an answer, and the number of a question */
|
|
28
24
|
const ANSWER = /,\s+(?=\*{0,2}[A-Z]\*{0,2}\s+▶)/
|
|
@@ -48,31 +44,6 @@ type Table = {
|
|
|
48
44
|
rows: { number: number, question: string, answers: { letter: string, text: string }[] }[]
|
|
49
45
|
}
|
|
50
46
|
|
|
51
|
-
/* split a Markdown table row into its cells */
|
|
52
|
-
const cells = (line: string): string[] => {
|
|
53
|
-
const result: string[] = []
|
|
54
|
-
const body = line.trim().replace(/^\|/, "").replace(/\|$/, "")
|
|
55
|
-
let cell = ""
|
|
56
|
-
let code = false
|
|
57
|
-
for (let i = 0; i < body.length; i++) {
|
|
58
|
-
const ch = body[i]!
|
|
59
|
-
if (ch === "`")
|
|
60
|
-
code = !code
|
|
61
|
-
if (!code && ch === "\\" && body[i + 1] === "|") {
|
|
62
|
-
cell += "|"
|
|
63
|
-
i++
|
|
64
|
-
}
|
|
65
|
-
else if (!code && ch === "|") {
|
|
66
|
-
result.push(cell.trim())
|
|
67
|
-
cell = ""
|
|
68
|
-
}
|
|
69
|
-
else
|
|
70
|
-
cell += ch
|
|
71
|
-
}
|
|
72
|
-
result.push(cell.trim())
|
|
73
|
-
return result
|
|
74
|
-
}
|
|
75
|
-
|
|
76
47
|
/* parse the grilling table out of the text of a reply */
|
|
77
48
|
const parse = (text: string): Table | undefined => {
|
|
78
49
|
const lines = text.split("\n")
|
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
where typed text edits the filter, LEFT/RIGHT (or the wheel over the
|
|
13
13
|
skills) selects a skill, UP/DOWN (or the wheel over the manual page)
|
|
14
14
|
scroll its manual page (with the matches of the filter marked), RETURN
|
|
15
|
-
takes over its command into the prompt,
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
takes over its command into the prompt, SPACE takes it over into the
|
|
16
|
+
synopsis form, "!!" switches over to the intent help for the filter,
|
|
17
|
+
and "//" leaves the filter in the prompt (which mirrors the filter while
|
|
18
|
+
the browser is shown). */
|
|
18
19
|
|
|
19
20
|
import type { Elements, Frozen, PromptBox, PromptEditInput, RenderElement } from "claude-code"
|
|
20
21
|
import { type Engine, type Char, type BandRender, type BandScroll, type View, PICKED,
|
|
@@ -228,24 +229,26 @@ export const modHelpBrowse = () => {
|
|
|
228
229
|
const hold = (current: Browse): PromptBox => held(current.filter.trimEnd())
|
|
229
230
|
|
|
230
231
|
/* take over the command of the selected manual page into the prompt (closing
|
|
231
|
-
the browser), if any page matches,
|
|
232
|
-
|
|
232
|
+
the browser), if any page matches, optionally suffixed with a trigger (to
|
|
233
|
+
switch over to its band), and determine the prompt to show */
|
|
234
|
+
const take = (current: Browse, trigger = ""): PromptBox => {
|
|
233
235
|
const doc = matching(current)[current.select]?.doc
|
|
234
236
|
if (doc === undefined)
|
|
235
237
|
return hold(current)
|
|
236
238
|
reset()
|
|
237
|
-
const text = `/${doc.skill} `
|
|
239
|
+
const text = `/${doc.skill} ${trigger}`
|
|
238
240
|
return { text, cursor: text.length }
|
|
239
241
|
}
|
|
240
242
|
|
|
241
243
|
/* while the manpage browser is shown, every edit is a key of the browser:
|
|
242
244
|
LEFT/RIGHT select a manual page, UP/DOWN scroll it by a line, RETURN takes
|
|
243
|
-
over the command of the selected page,
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
245
|
+
over the command of the selected page, SPACE takes it over suffixed with
|
|
246
|
+
the trigger of the synopsis form, "//" (two "/" in a row) closes the
|
|
247
|
+
browser and leaves the filter in the prompt, "!!" (two "!" in a row, for
|
|
248
|
+
a non-empty filter) closes the browser with the filter suffixed with the
|
|
249
|
+
trigger of the intent help (both to switch over to the other band), a
|
|
250
|
+
deletion (like BACKSPACE) deletes the last character of the filter, and
|
|
251
|
+
any other typed text is appended to the filter */
|
|
249
252
|
const edit = (e: Frozen<PromptEditInput>): PromptBox => {
|
|
250
253
|
const current = browse!
|
|
251
254
|
const key = decode(e)
|
|
@@ -272,6 +275,8 @@ export const modHelpBrowse = () => {
|
|
|
272
275
|
current.filter += pending
|
|
273
276
|
if (key.name === "return")
|
|
274
277
|
return take(current)
|
|
278
|
+
if (key.text === " ")
|
|
279
|
+
return take(current, "!!")
|
|
275
280
|
if (key.name === "up" || key.name === "down")
|
|
276
281
|
current.scroll = Math.max(0, current.scroll + (key.name === "up" ? -1 : 1))
|
|
277
282
|
else if (key.step !== 0) {
|
|
@@ -393,7 +398,7 @@ export const modHelpBrowse = () => {
|
|
|
393
398
|
status: `[ ${list.length}/${docs?.length ?? 0} manpages ]`,
|
|
394
399
|
hints: `←/→${wheeled}: select · ` +
|
|
395
400
|
`${geometry.overflow ? "wheel or CTRL+x + TAB + ↑/↓/PgUp/PgDn" : "↑/↓"}: scroll · ` +
|
|
396
|
-
"//: cancel+close · ⏎: apply+close" +
|
|
401
|
+
"//: cancel+close · ⏎: apply+close · SPACE: compose" +
|
|
397
402
|
(current.filter.trim() !== "" ? " · !!: Find Skill Command" : "") + " · (type for filtering)",
|
|
398
403
|
rows: (i) => i === 0 ? field : entry(current.top + i - 2),
|
|
399
404
|
rule: (i) => i === 1,
|
|
@@ -10,9 +10,10 @@
|
|
|
10
10
|
the skill "ase-help-intent", but over the session context), and showing
|
|
11
11
|
the proposed skills in a master/detail view in the band above the
|
|
12
12
|
prompt, where LEFT/RIGHT (or the wheel over the skills) selects a skill,
|
|
13
|
-
UP/DOWN and "b"/"f"
|
|
14
|
-
|
|
15
|
-
any other key restores the prompt as typed
|
|
13
|
+
UP/DOWN and "b"/"f" (or the wheel over the details) scroll its details,
|
|
14
|
+
RETURN picks its command into the prompt, SPACE picks it into the synopsis
|
|
15
|
+
form, and "!!", BACKSPACE, or any other key restores the prompt as typed
|
|
16
|
+
without the trigger. */
|
|
16
17
|
|
|
17
18
|
import type { Elements, Frozen, PromptBox, PromptEditInput, RenderElement } from "claude-code"
|
|
18
19
|
import { type Engine, type Char, type BandRender, type BandScroll, type View, PICKED, CODE,
|
|
@@ -198,11 +199,13 @@ export const modHelpFind = () => {
|
|
|
198
199
|
|
|
199
200
|
/* while the intent band is shown, every edit is a key of the band (with the
|
|
200
201
|
prompt held): LEFT/RIGHT select a proposal, UP/DOWN scroll its details by
|
|
201
|
-
a line and "b"/"f"
|
|
202
|
+
a line and "b"/"f" by a page (as the engine consumes PAGEUP/PAGEDOWN),
|
|
202
203
|
RETURN (arriving as a typed newline in the held prompt) operates the band
|
|
203
|
-
as at the prompt,
|
|
204
|
-
|
|
205
|
-
|
|
204
|
+
as at the prompt, SPACE closes the band with the command of the selected
|
|
205
|
+
proposal suffixed with the trigger of the synopsis form, "//" (two "/"
|
|
206
|
+
in a row) closes the band with the prompt as typed suffixed with the
|
|
207
|
+
trigger of the manpage browser (both to switch over to the other band),
|
|
208
|
+
and "!!" (two "!" in a row), a deletion (like BACKSPACE), or any
|
|
206
209
|
other key closes the band and restores the prompt as typed without the
|
|
207
210
|
trigger (onto whose end the edit of a typed text is still to be applied) */
|
|
208
211
|
const edit = (e: Frozen<PromptEditInput>): PromptBox | string => {
|
|
@@ -222,8 +225,13 @@ export const modHelpFind = () => {
|
|
|
222
225
|
const text = submit()
|
|
223
226
|
return intent !== undefined ? held(draft) : { text, cursor: text.length }
|
|
224
227
|
}
|
|
228
|
+
if (state === "done" && key.text === " ") {
|
|
229
|
+
reset()
|
|
230
|
+
const text = `${proposals[current.select]!.command} !!`
|
|
231
|
+
return { text, cursor: text.length }
|
|
232
|
+
}
|
|
225
233
|
const roll = key.name === "up" ? -1 : key.name === "down" ? 1 :
|
|
226
|
-
key.text === "b" ? -current.page : key.text === "f"
|
|
234
|
+
key.text === "b" ? -current.page : key.text === "f" ? current.page : 0
|
|
227
235
|
const step = roll !== 0 ? 0 : key.step
|
|
228
236
|
if (state === "done" && (roll !== 0 || step !== 0)) {
|
|
229
237
|
if (step !== 0) {
|
|
@@ -406,7 +414,7 @@ export const modHelpFind = () => {
|
|
|
406
414
|
(the intent itself stays visible in the prompt) */
|
|
407
415
|
return panel(ui, geometry, current, {
|
|
408
416
|
title: TITLE,
|
|
409
|
-
hints: current.state === "done" ? "⌫/!!: cancel+close · ⏎: apply+close · ←/→: select · b/f
|
|
417
|
+
hints: current.state === "done" ? "⌫/!!: cancel+close · ⏎: apply+close · SPACE: compose · ←/→: select · b/f: scroll" +
|
|
410
418
|
(geometry.overflow ? " · wheel: select/scroll" : "") + " · //: Browse Skill Manpages" :
|
|
411
419
|
"⌫/!!/any key: cancel+close · //: Browse Skill Manpages",
|
|
412
420
|
rows: (i) => {
|
package/plugin/mods/ase-mods.ts
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
/* Claude Code function hooks module ("mod", EARLY ACCESS):
|
|
8
8
|
Hooking infrastructure, registering the function hooks and dispatching
|
|
9
|
-
them into the grill table ("ase-mods-grill.ts"), the
|
|
10
|
-
("ase-mods-
|
|
9
|
+
them into the grill table ("ase-mods-grill.ts"), the decision matrix
|
|
10
|
+
("ase-mods-evaluate.ts"), the intent help ("ase-mods-help-find.ts"),
|
|
11
|
+
the synopsis form ("ase-mods-help-compose.ts"),
|
|
11
12
|
the manual band ("ase-mods-help-show.ts"), the manpage browser
|
|
12
13
|
("ase-mods-help-browse.ts"), and the hint band ("ase-mods-help-hint.ts").
|
|
13
14
|
As the engine loads only a module spelling "on", "$", and "next" literally,
|
|
@@ -17,6 +18,7 @@
|
|
|
17
18
|
import type { Register, PromptBox, Timer } from "claude-code"
|
|
18
19
|
import { type Band, TRIGGER } from "./ase-mods-common.ts"
|
|
19
20
|
import { modGrill } from "./ase-mods-grill.ts"
|
|
21
|
+
import { modEvaluate } from "./ase-mods-evaluate.ts"
|
|
20
22
|
import { modHelpFind } from "./ase-mods-help-find.ts"
|
|
21
23
|
import { modHelpCompose } from "./ase-mods-help-compose.ts"
|
|
22
24
|
import { modHelpShow } from "./ase-mods-help-show.ts"
|
|
@@ -29,14 +31,15 @@ export const register: Register = (on, options) => {
|
|
|
29
31
|
if (options.enabled === false)
|
|
30
32
|
return
|
|
31
33
|
|
|
32
|
-
/* the grill table, the
|
|
33
|
-
the manpage browser, and the hint band */
|
|
34
|
-
const grill
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const
|
|
34
|
+
/* the grill table, the decision matrix, the intent help, the synopsis form,
|
|
35
|
+
the manual band, the manpage browser, and the hint band */
|
|
36
|
+
const grill = modGrill()
|
|
37
|
+
const evaluate = modEvaluate()
|
|
38
|
+
const intent = modHelpFind()
|
|
39
|
+
const skill = modHelpCompose()
|
|
40
|
+
const manual = modHelpShow()
|
|
41
|
+
const browse = modHelpBrowse()
|
|
42
|
+
const hint = modHelpHint()
|
|
40
43
|
|
|
41
44
|
/* the bands above the prompt (the synopsis form, the intent band, and the
|
|
42
45
|
manpage browser, opened in this order), the shown one (as at most one is
|
|
@@ -198,16 +201,19 @@ export const register: Register = (on, options) => {
|
|
|
198
201
|
next.called || shown() === undefined ? next(e) :
|
|
199
202
|
{ drop: "intent band or synopsis form failed" })
|
|
200
203
|
|
|
201
|
-
/* redraw the replies which contain a grilling table
|
|
202
|
-
the chain beneath runs, although its drawing is
|
|
203
|
-
at least silences a warning in the debug output
|
|
204
|
+
/* redraw the replies which contain a grilling table or a decision matrix
|
|
205
|
+
(terminal only), where the chain beneath runs, although its drawing is
|
|
206
|
+
replaced by us (but this at least silences a warning in the debug output
|
|
207
|
+
of Claude Code) */
|
|
204
208
|
on("ui.render", { component: "AssistantMessage", surface: "terminal" }, async ($, e, next) => {
|
|
205
|
-
const drawn
|
|
206
|
-
|
|
207
|
-
|
|
209
|
+
const drawn = await next(e)
|
|
210
|
+
const ui = $.ui.resolve(e)
|
|
211
|
+
const invalidate = () => $.ui.invalidate("ui.render")
|
|
212
|
+
return grill.render(e, ui, {
|
|
213
|
+
invalidate,
|
|
208
214
|
prompt: () => $.prompt.read(),
|
|
209
215
|
fill: (text) => $.prompt.fill({ text })
|
|
210
|
-
}) ?? drawn
|
|
216
|
+
}) ?? evaluate.render(e, ui, { invalidate }) ?? drawn
|
|
211
217
|
})
|
|
212
218
|
|
|
213
219
|
/* draw the intent band resp. the synopsis form, else the manual page,
|
|
@@ -118,7 +118,10 @@ an ad-hoc change, and it *never* touches the artifact kinds `CODE`,
|
|
|
118
118
|
|
|
119
119
|
Serve <query/> *ad-hoc* under the **Activated Behavior** below:
|
|
120
120
|
read the `SPEC` artifacts of <artifact-files/> which are related to
|
|
121
|
-
<query
|
|
121
|
+
<query/> (or, for a question spanning entire object kinds, property
|
|
122
|
+
values, or reference relations, query them structurally via the
|
|
123
|
+
`ase_specbook_query(selector: "<selector/>")` tool of the `ase` MCP
|
|
124
|
+
server), resolve their `[[xxx]]` references across artifacts, and
|
|
122
125
|
answer the query grounded in the specification content, citing the
|
|
123
126
|
artifact file and object id of every statement you rely on. If
|
|
124
127
|
<query/> asks for a change, apply it as an *ad-hoc modification*
|