agent-lyceum 0.15.1 → 0.16.0
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/README.md +1 -1
- package/README.zh-TW.md +1 -1
- package/dist/format.d.ts +2 -1
- package/dist/format.js +12 -11
- package/dist/run-session.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ claude --plugin-dir /path/to/agent-lyceum/plugins/status-monitor
|
|
|
65
65
|
|
|
66
66
|
**Use it.**
|
|
67
67
|
- **Toasts:** a message pops up when a run finishes, is interrupted, or an agent's wake fails (prefixed `[project]` when several are watched). Nothing to do.
|
|
68
|
-
- **`/status-monitor`:** type it to open (or, if already open, close) the pane. It is off at session start unless `autoOpen` is set. In the **fullscreen layout** (`"tui": "fullscreen"` in `~/.claude/settings.json`, restart the session to apply; `/tui fullscreen` does the same) from 110 terminal columns it docks on the right at half the terminal width, and tabs and **關閉** (Close) can be clicked. Otherwise it shows above the prompt; the layout is fixed when the session starts, so a session started on the main screen stays that way. Keyboard (works in both): `x` closes, `v` switches graph/text, `1`-`9` or Tab/Enter switch task, Esc returns to the prompt, running the command again also closes. It draws one block per agent, with the links between the lead and each member taken from `can_message` (`▼` the lead may write to the member, `▲` the member to the lead, `↕` both; yellow marks the latest hand-off). Each block shows what that agent is doing now, the mail it is handling and the mail queued for it as short readable sections by type (task: goal and acceptance criteria; reply: changes, verification, open items, risks; done: result and what is not done), and its wake count and output tokens. Above the graph: the run id and state, task, progress bar and checklist; below it: blocked integrations, notes and the result summary. `v` switches to the plain text list (per-runtime tokens, per-agent statistics, the last 8 wakes). Every block lists the agents it may message in a "→" line, with the latest hand-off marked "●" (this also covers member-to-member targets, which get no glyph). When the graph area is narrower than 70 columns (compared with the pane's own width, as the host reports it), the blocks stack in one column and the link glyphs are left out. Each running task is a tab, newest first; finished and interrupted runs are not listed. It refreshes with the polling interval while open.
|
|
68
|
+
- **`/status-monitor`:** type it to open (or, if already open, close) the pane. It is off at session start unless `autoOpen` is set. In the **fullscreen layout** (`"tui": "fullscreen"` in `~/.claude/settings.json`, restart the session to apply; `/tui fullscreen` does the same) from 110 terminal columns it docks on the right at half the terminal width, and tabs and **關閉** (Close) can be clicked. Otherwise it shows above the prompt; the layout is fixed when the session starts, so a session started on the main screen stays that way. Keyboard (works in both): `x` closes, `v` switches graph/text, `1`-`9` or Tab/Enter switch task, Esc returns to the prompt, running the command again also closes. It draws one block per agent, with the links between the lead and each member taken from `can_message` (`▼` the lead may write to the member, `▲` the member to the lead, `↕` both; yellow marks the latest hand-off). Each block shows what that agent is doing now, the mail it is handling and the mail queued for it as short readable sections by type (task: goal and acceptance criteria; reply: changes, verification, open items, risks; done: result and what is not done), and its wake count and output tokens. An agent that is working right now is drawn larger: its block gets a row of its own at the full width, and its text wraps instead of being cut, with the message it is handling shown in full (every line of every section); idle and queued blocks stay compact. Above the graph: the run id and state, task, progress bar and checklist; below it: blocked integrations, notes and the result summary. `v` switches to the plain text list (per-runtime tokens, per-agent statistics, the last 8 wakes). Every block lists the agents it may message in a "→" line, with the latest hand-off marked "●" (this also covers member-to-member targets, which get no glyph). When the graph area is narrower than 70 columns (compared with the pane's own width, as the host reports it), the blocks stack in one column and the link glyphs are left out. Each running task is a tab, newest first; finished and interrupted runs are not listed. It refreshes with the polling interval while open.
|
|
69
69
|
|
|
70
70
|
**Configure it.** In `~/.claude/settings.json` under `pluginConfigs."status-monitor@inline"` (`options`):
|
|
71
71
|
|
package/README.zh-TW.md
CHANGED
|
@@ -65,7 +65,7 @@ claude --plugin-dir /path/to/agent-lyceum/plugins/status-monitor
|
|
|
65
65
|
|
|
66
66
|
**操作。**
|
|
67
67
|
- **通知:** 執行完成、被中斷或某個 agent 喚醒失敗時會跳出訊息(監看多個專案時前面加 `[專案名]`),不需要任何操作。
|
|
68
|
-
- **`/status-monitor`:** 輸入即開啟面板(已開著則關閉)。session 啟動時預設不開,除非設定 `autoOpen`。在 **fullscreen 版面**(`~/.claude/settings.json` 設 `"tui": "fullscreen"` 後重開 session;`/tui fullscreen` 效果相同)且終端寬度 110 欄以上時,面板會靠右 dock 在終端寬度的一半,分頁與 **關閉** 都可以用滑鼠點。否則顯示在輸入框上方;版面在 session 啟動時就固定,從主畫面啟動的 session 不會變。鍵盤操作兩種版面都能用:`x` 關閉,`v` 切換圖形/文字,`1`-`9` 或 Tab/Enter 切換任務,Esc 回到輸入框,再執行一次指令也會關閉。面板為每個 agent 畫一個區塊,並依 `can_message` 在 lead 與各成員之間畫出連結(`▼` lead 可寄給成員、`▲` 成員可寄給 lead、`↕` 雙向;黃色表示最近一次寄信的那條邊)。每個區塊顯示該 agent 現在在做什麼、正在處理與排隊中的信(依信種整理成可讀的重點:task 看目標與驗收,reply 看變更、驗證、待辦與風險,done 看結果與未完成),以及喚醒次數與輸出 token
|
|
68
|
+
- **`/status-monitor`:** 輸入即開啟面板(已開著則關閉)。session 啟動時預設不開,除非設定 `autoOpen`。在 **fullscreen 版面**(`~/.claude/settings.json` 設 `"tui": "fullscreen"` 後重開 session;`/tui fullscreen` 效果相同)且終端寬度 110 欄以上時,面板會靠右 dock 在終端寬度的一半,分頁與 **關閉** 都可以用滑鼠點。否則顯示在輸入框上方;版面在 session 啟動時就固定,從主畫面啟動的 session 不會變。鍵盤操作兩種版面都能用:`x` 關閉,`v` 切換圖形/文字,`1`-`9` 或 Tab/Enter 切換任務,Esc 回到輸入框,再執行一次指令也會關閉。面板為每個 agent 畫一個區塊,並依 `can_message` 在 lead 與各成員之間畫出連結(`▼` lead 可寄給成員、`▲` 成員可寄給 lead、`↕` 雙向;黃色表示最近一次寄信的那條邊)。每個區塊顯示該 agent 現在在做什麼、正在處理與排隊中的信(依信種整理成可讀的重點:task 看目標與驗收,reply 看變更、驗證、待辦與風險,done 看結果與未完成),以及喚醒次數與輸出 token。正在運作的 agent 會被放大:它的區塊獨佔一整列、使用全寬,文字自動換行而不截斷,正在處理的那封信完整顯示(每個段落的每一行);閒置與排隊中的區塊維持精簡。圖形上方是執行 id 與狀態、任務、進度條與清單;下方是受阻的整合、備註與結果摘要。按 `v` 切換成純文字清單(各 runtime 的 token、各 agent 統計、最近 8 次喚醒)。每個區塊以「→」一行列出它可以寄信的對象,最近一次寄信的對象標 「●」(成員之間的對象也在此,不畫符號)。當圖形區域寬度少於 70 欄(以面板本身的寬度比較,由 host 回報),區塊改成單欄堆疊,不畫連結符號。每個執行中的任務是一個分頁,最新的排最前面;已結束或已中斷的不列出。面板開著時會依輪詢間隔自動更新。
|
|
69
69
|
|
|
70
70
|
**設定。** 在 `~/.claude/settings.json` 的 `pluginConfigs."status-monitor@inline"`(`options`):
|
|
71
71
|
|
package/dist/format.d.ts
CHANGED
|
@@ -39,6 +39,7 @@ export interface DigestSection {
|
|
|
39
39
|
* What a message is about, as short readable sections for the status panel: by type, the lines under the
|
|
40
40
|
* sections that matter (task: Goal, Acceptance criteria; reply: Changes, Verification, Open items, Risks;
|
|
41
41
|
* done: Result, Not done; ask: each question; failure: the first paragraph). With none of those, the one-line brief.
|
|
42
|
+
* `full` keeps every line of every section uncut (for the one message an agent is working on right now).
|
|
42
43
|
* Never throws: a body it cannot read gives [].
|
|
43
44
|
*/
|
|
44
|
-
export declare function digestOf(type: string, body: string): DigestSection[];
|
|
45
|
+
export declare function digestOf(type: string, body: string, full?: boolean): DigestSection[];
|
package/dist/format.js
CHANGED
|
@@ -119,21 +119,21 @@ const DIGEST_SECTIONS = {
|
|
|
119
119
|
{ name: "Not done", label: "未完成" },
|
|
120
120
|
],
|
|
121
121
|
};
|
|
122
|
-
function digestLine(l) {
|
|
122
|
+
function digestLine(l, maxChars = DIGEST_MAX_CHARS) {
|
|
123
123
|
const t = l
|
|
124
124
|
.trim()
|
|
125
125
|
.replace(/^[-*]\s+(\[[ xX]\]\s+)?/, "")
|
|
126
126
|
.replace(/\*\*|`/g, "")
|
|
127
127
|
.trim();
|
|
128
|
-
return t.length >
|
|
128
|
+
return t.length > maxChars ? `${t.slice(0, maxChars - 1)}…` : t;
|
|
129
129
|
}
|
|
130
|
-
/** Readable lines of a block of text: markdown marks gone, empty / heading / warning / `None` lines dropped, at most 3 lines. */
|
|
131
|
-
function digestLines(text) {
|
|
130
|
+
/** Readable lines of a block of text: markdown marks gone, empty / heading / warning / `None` lines dropped, at most 3 lines (`full`: all of them, uncut). */
|
|
131
|
+
function digestLines(text, full = false) {
|
|
132
132
|
const lines = text
|
|
133
133
|
.split(/\r?\n/)
|
|
134
|
-
.map(digestLine)
|
|
134
|
+
.map((l) => digestLine(l, full ? Infinity : DIGEST_MAX_CHARS))
|
|
135
135
|
.filter((l) => l !== "" && !/^#+\s/.test(l) && !l.startsWith("[agent-lyceum]") && !/^none\.?$/i.test(l));
|
|
136
|
-
if (lines.length <= DIGEST_MAX_LINES)
|
|
136
|
+
if (full || lines.length <= DIGEST_MAX_LINES)
|
|
137
137
|
return lines;
|
|
138
138
|
const last = lines[DIGEST_MAX_LINES - 1] ?? "";
|
|
139
139
|
const cut = last.endsWith("…") ? last : `${last.slice(0, last.length >= DIGEST_MAX_CHARS ? DIGEST_MAX_CHARS - 1 : last.length)}…`;
|
|
@@ -143,14 +143,15 @@ function digestLines(text) {
|
|
|
143
143
|
* What a message is about, as short readable sections for the status panel: by type, the lines under the
|
|
144
144
|
* sections that matter (task: Goal, Acceptance criteria; reply: Changes, Verification, Open items, Risks;
|
|
145
145
|
* done: Result, Not done; ask: each question; failure: the first paragraph). With none of those, the one-line brief.
|
|
146
|
+
* `full` keeps every line of every section uncut (for the one message an agent is working on right now).
|
|
146
147
|
* Never throws: a body it cannot read gives [].
|
|
147
148
|
*/
|
|
148
|
-
export function digestOf(type, body) {
|
|
149
|
+
export function digestOf(type, body, full = false) {
|
|
149
150
|
try {
|
|
150
151
|
const out = [];
|
|
151
152
|
if (type === "ask") {
|
|
152
153
|
for (const m of body.matchAll(/<question\b[^>]*?\bid="([^"]*)"[^>]*>[\s\S]*?<text>([\s\S]*?)<\/text>/g)) {
|
|
153
|
-
const lines = digestLines(m[2] ?? "");
|
|
154
|
+
const lines = digestLines(m[2] ?? "", full);
|
|
154
155
|
if (lines.length)
|
|
155
156
|
out.push({ label: m[1] ?? "問題", lines });
|
|
156
157
|
}
|
|
@@ -158,7 +159,7 @@ export function digestOf(type, body) {
|
|
|
158
159
|
else if (type === "failure") {
|
|
159
160
|
const first = body
|
|
160
161
|
.split(/\n[ \t]*\n/)
|
|
161
|
-
.map(digestLines)
|
|
162
|
+
.map((t) => digestLines(t, full))
|
|
162
163
|
.find((l) => l.length);
|
|
163
164
|
if (first)
|
|
164
165
|
out.push({ label: "訊息", lines: first });
|
|
@@ -166,7 +167,7 @@ export function digestOf(type, body) {
|
|
|
166
167
|
else {
|
|
167
168
|
for (const s of DIGEST_SECTIONS[type] ?? []) {
|
|
168
169
|
const text = sectionText(body, s.name);
|
|
169
|
-
const lines = text === undefined ? [] : digestLines(text);
|
|
170
|
+
const lines = text === undefined ? [] : digestLines(text, full);
|
|
170
171
|
if (lines.length)
|
|
171
172
|
out.push({ label: s.label, lines });
|
|
172
173
|
}
|
|
@@ -174,7 +175,7 @@ export function digestOf(type, body) {
|
|
|
174
175
|
if (out.length)
|
|
175
176
|
return out;
|
|
176
177
|
const brief = briefOf(body);
|
|
177
|
-
return brief ? [{ label: "摘要", lines: [digestLine(brief)] }] : [];
|
|
178
|
+
return brief ? [{ label: "摘要", lines: [digestLine(brief, full ? Infinity : DIGEST_MAX_CHARS)] }] : [];
|
|
178
179
|
}
|
|
179
180
|
catch {
|
|
180
181
|
return [];
|
package/dist/run-session.js
CHANGED
|
@@ -312,7 +312,9 @@ export class RunSession {
|
|
|
312
312
|
say(`[round ${state.rounds}/${state.max_rounds}] waking ${agent.name} (${agent.runtime})${attempt ? ` — retry ${attempt}` : ""}`);
|
|
313
313
|
const sessionId = agent.resume ? state.sessions[agent.name] : undefined;
|
|
314
314
|
const handling = unread.map((m) => ({ from: m.meta.from, type: m.meta.type, subject: m.meta.subject, brief: briefOf(m.body), digest: digestOf(m.meta.type, m.body) }));
|
|
315
|
-
|
|
315
|
+
// What the panel shows for the message being worked on is uncut; the wake record below keeps the short digest.
|
|
316
|
+
const activeHandling = unread.map((m, i) => ({ ...handling[i], digest: digestOf(m.meta.type, m.body, true) }));
|
|
317
|
+
state.active[agent.name] = { round, since: new Date(started).toISOString(), handling: activeHandling };
|
|
316
318
|
this.saveState();
|
|
317
319
|
result = await invoke({ ...base, signal: opts.signal, logDir: attemptLogDir(runDir, att.id), userPrompt: attempt ? retryPrompt(base.userPrompt, result?.error) : base.userPrompt, sessionId }).catch((e) => ({
|
|
318
320
|
ok: false,
|