@gavin-hu/my-pi-agent 0.20.0 → 0.22.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 +46 -30
- package/README.zh-CN.md +40 -25
- package/assets/screens/board.png +0 -0
- package/assets/screens/chrome.png +0 -0
- package/assets/screens/file-browse.png +0 -0
- package/assets/screens/plan-review.png +0 -0
- package/assets/screens/plan.png +0 -0
- package/docs/extensions/README.md +22 -23
- package/docs/extensions/board.md +397 -168
- package/docs/extensions/bytefly.md +234 -197
- package/docs/extensions/check.md +15 -17
- package/docs/extensions/context.md +35 -15
- package/docs/extensions/file-browse.md +57 -63
- package/docs/extensions/file-open.md +9 -11
- package/docs/extensions/file-read.md +12 -14
- package/docs/extensions/goal.md +15 -17
- package/docs/extensions/job.md +54 -42
- package/docs/extensions/memory.md +17 -19
- package/docs/extensions/plan.md +197 -140
- package/docs/extensions/question.md +16 -18
- package/docs/extensions/rewind.md +17 -19
- package/docs/extensions/serve.md +145 -0
- package/docs/extensions/session-bus.md +38 -16
- package/docs/extensions/subagent.md +23 -25
- package/docs/extensions/team.md +83 -44
- package/docs/extensions/todo.md +18 -18
- package/docs/extensions/watch.md +31 -31
- package/docs/extensions/web-access.md +2 -4
- package/docs/extensions/wechat.md +34 -35
- package/docs/extensions/worktree.md +56 -50
- package/extensions/board/commands.ts +79 -25
- package/extensions/board/format.ts +43 -24
- package/extensions/board/index.ts +17 -4
- package/extensions/board/input.ts +4 -9
- package/extensions/board/join.ts +5 -20
- package/extensions/board/lazy-page.ts +40 -0
- package/extensions/board/page.ts +65 -93
- package/extensions/board/result.ts +30 -39
- package/extensions/board/rows.ts +62 -25
- package/extensions/board/schema.ts +49 -11
- package/extensions/board/snapshot.ts +8 -6
- package/extensions/board/tone.ts +22 -0
- package/extensions/board/tools.ts +154 -55
- package/extensions/board/transport/router.ts +3 -5
- package/extensions/board/transport/server.ts +59 -28
- package/extensions/board/types.ts +36 -42
- package/extensions/board/view/card.ts +229 -0
- package/extensions/board/view/client.ts +35 -16
- package/extensions/board/view/dialog.ts +169 -6
- package/extensions/board/view/empty.ts +47 -0
- package/extensions/board/view/keys.ts +13 -2
- package/extensions/board/view/live.ts +90 -28
- package/extensions/board/view/names.ts +13 -4
- package/extensions/board/view/page.ts +38 -13
- package/extensions/board/view/render.ts +219 -277
- package/extensions/board/view/styles.ts +130 -32
- package/extensions/board/view/team.ts +297 -76
- package/extensions/bytefly/block/fallback.ts +1 -1
- package/extensions/bytefly/doctor/runtime.ts +5 -2
- package/extensions/bytefly/footer/collect/label.ts +60 -0
- package/extensions/bytefly/footer/collect/snapshot.ts +8 -1
- package/extensions/bytefly/footer/config.ts +7 -8
- package/extensions/bytefly/footer/register.ts +33 -4
- package/extensions/bytefly/footer/render/footer.ts +18 -4
- package/extensions/bytefly/footer/render/key.ts +46 -0
- package/extensions/bytefly/footer/render/layout.ts +13 -3
- package/extensions/bytefly/footer/render/lines.ts +10 -7
- package/extensions/bytefly/footer/render/segments.ts +41 -38
- package/extensions/bytefly/footer/render/status.ts +0 -11
- package/extensions/bytefly/footer/types.ts +7 -0
- package/extensions/bytefly/header/component.ts +4 -20
- package/extensions/bytefly/header/config.ts +1 -1
- package/extensions/bytefly/index.ts +32 -6
- package/extensions/bytefly/mode/editor.ts +115 -0
- package/extensions/bytefly/panel/commands.ts +210 -120
- package/extensions/bytefly/panel/format.ts +4 -4
- package/extensions/bytefly/panel/settings.ts +73 -6
- package/extensions/bytefly/panel/tui/keys.ts +2 -6
- package/extensions/bytefly/update/cache.ts +1 -1
- package/extensions/bytefly/update/register.ts +4 -4
- package/extensions/bytefly/working/config.ts +5 -7
- package/extensions/bytefly/working/format.ts +10 -13
- package/extensions/bytefly/working/register.ts +22 -27
- package/extensions/check/commands.ts +3 -2
- package/extensions/check/render.ts +10 -1
- package/extensions/check/{tui.ts → view/check-screens.ts} +14 -19
- package/extensions/context/commands.ts +2 -2
- package/extensions/context/{tui.ts → view/context-report.ts} +51 -19
- package/extensions/file-browse/config/config.ts +4 -19
- package/extensions/file-browse/index.ts +89 -79
- package/extensions/file-browse/live/live.ts +1 -1
- package/extensions/file-browse/transport/router.ts +12 -4
- package/extensions/file-browse/transport/server.ts +61 -28
- package/extensions/file-browse/view/chrome.ts +4 -4
- package/extensions/file-browse/view/client.ts +3 -1
- package/extensions/file-browse/view/html.ts +73 -40
- package/extensions/file-browse/view/live-client.ts +2 -2
- package/extensions/file-browse/view/markdown.ts +13 -14
- package/extensions/file-browse/view/palette.ts +2 -2
- package/extensions/file-browse/view/raw-html.ts +9 -9
- package/extensions/file-browse/view/tree.ts +1 -1
- package/extensions/file-browse/view/urls.ts +20 -6
- package/extensions/file-read/render.ts +4 -4
- package/extensions/goal/plan-route.ts +2 -2
- package/extensions/goal/tui.ts +12 -2
- package/extensions/job/child.ts +527 -0
- package/extensions/job/clock.ts +142 -0
- package/extensions/job/commands.ts +4 -2
- package/extensions/job/config.ts +8 -1
- package/extensions/job/format.ts +22 -1
- package/extensions/job/log-writer.ts +13 -2
- package/extensions/job/registry.ts +63 -7
- package/extensions/job/render.ts +34 -0
- package/extensions/job/runtime.ts +296 -655
- package/extensions/job/session.ts +132 -19
- package/extensions/job/store.ts +7 -7
- package/extensions/job/tools.ts +21 -9
- package/extensions/job/types.ts +47 -0
- package/extensions/job/{tui.ts → view/jobs-screen.ts} +19 -99
- package/extensions/job/waiters.ts +44 -1
- package/extensions/memory/commands.ts +4 -2
- package/extensions/memory/{tui.ts → view/memory-picker.ts} +12 -12
- package/extensions/plan/commands.ts +12 -9
- package/extensions/plan/feedback.ts +62 -16
- package/extensions/plan/format.ts +8 -2
- package/extensions/plan/git-bash.ts +115 -16
- package/extensions/plan/index.ts +58 -51
- package/extensions/plan/plan-index.ts +4 -2
- package/extensions/plan/plan-text.ts +114 -0
- package/extensions/plan/plans.ts +5 -54
- package/extensions/plan/policy.ts +8 -10
- package/extensions/plan/review-lazy.ts +85 -0
- package/extensions/plan/review.ts +2 -2
- package/extensions/plan/runtime.ts +61 -41
- package/extensions/plan/sections.ts +1 -1
- package/extensions/plan/steps.ts +3 -3
- package/extensions/plan/todos.ts +1 -1
- package/extensions/plan/tools/cancel.ts +27 -62
- package/extensions/plan/tools/enter.ts +55 -81
- package/extensions/plan/tools/exit.ts +221 -238
- package/extensions/plan/tools/refusal.ts +18 -0
- package/extensions/plan/tools/steps.ts +75 -92
- package/extensions/plan/tools/write.ts +77 -120
- package/extensions/plan/tools.ts +188 -30
- package/extensions/plan/tui.ts +5 -5
- package/extensions/plan/types.ts +48 -5
- package/extensions/plan/web-blocks.ts +76 -5
- package/extensions/plan/web-chrome.ts +4 -0
- package/extensions/plan/web-client.ts +102 -2
- package/extensions/plan/web-collab.ts +4 -4
- package/extensions/plan/web-comments.ts +39 -8
- package/extensions/plan/web-composer.ts +14 -3
- package/extensions/plan/web-keys.ts +149 -25
- package/extensions/plan/web-lazy.ts +53 -0
- package/extensions/plan/web-markdown.ts +10 -68
- package/extensions/plan/web-outline.ts +22 -2
- package/extensions/plan/web-page.ts +56 -86
- package/extensions/plan/web-payload.ts +188 -0
- package/extensions/plan/web-plan.ts +23 -13
- package/extensions/plan/web-router.ts +18 -9
- package/extensions/plan/web-settle.ts +14 -7
- package/extensions/plan/web-share-session.ts +24 -55
- package/extensions/plan/web-share.ts +19 -37
- package/extensions/plan/web-write.ts +112 -0
- package/extensions/plan/web.ts +67 -131
- package/extensions/question/tools.ts +6 -2
- package/extensions/question/{tui.ts → view/question-screen.ts} +11 -11
- package/extensions/rewind/commands/commands.ts +6 -3
- package/extensions/rewind/state/runtime.ts +1 -1
- package/extensions/rewind/{view/status.ts → status.ts} +4 -4
- package/extensions/rewind/view/review-tui.ts +4 -4
- package/extensions/serve/config.ts +39 -0
- package/extensions/serve/index.ts +85 -0
- package/extensions/serve/registry.ts +260 -0
- package/extensions/session-bus/commands.ts +11 -6
- package/extensions/session-bus/join-request.ts +17 -9
- package/extensions/session-bus/mail-watch.ts +26 -17
- package/extensions/session-bus/protocol.ts +15 -3
- package/extensions/session-bus/resolve.ts +5 -7
- package/extensions/session-bus/result.ts +1 -27
- package/extensions/session-bus/runtime.ts +40 -12
- package/extensions/session-bus/store.ts +101 -19
- package/extensions/session-bus/tools.ts +5 -4
- package/extensions/session-bus/types.ts +19 -0
- package/extensions/session-bus/{tui.ts → view/session-list.ts} +16 -16
- package/extensions/subagent/agents.ts +1 -1
- package/extensions/subagent/commands.ts +4 -2
- package/extensions/subagent/format.ts +15 -15
- package/extensions/subagent/index.ts +1 -1
- package/extensions/subagent/render.ts +3 -3
- package/extensions/subagent/{background-tui.ts → view/agents-board.ts} +11 -11
- package/extensions/team/commands.ts +24 -5
- package/extensions/team/dispatch.ts +18 -4
- package/extensions/team/format.ts +17 -2
- package/extensions/team/index.ts +9 -8
- package/extensions/team/schema.ts +6 -5
- package/extensions/team/tools.ts +107 -45
- package/extensions/team/types.ts +3 -2
- package/extensions/todo/commands.ts +4 -2
- package/extensions/todo/format.ts +11 -0
- package/extensions/todo/render.ts +57 -0
- package/extensions/todo/runtime.ts +1 -1
- package/extensions/todo/tools.ts +1 -1
- package/extensions/todo/{tui.ts → view/todo-screen.ts} +18 -90
- package/extensions/todo/widget.ts +47 -0
- package/extensions/watch/commands.ts +4 -2
- package/extensions/watch/format.ts +36 -7
- package/extensions/watch/render.ts +57 -0
- package/extensions/watch/runtime.ts +27 -22
- package/extensions/watch/tools.ts +4 -4
- package/extensions/watch/{tui.ts → view/watch-screen.ts} +18 -80
- package/extensions/web-access/fetch/tool.ts +12 -4
- package/extensions/wechat/commands/commands.ts +18 -14
- package/extensions/wechat/index.ts +72 -37
- package/extensions/wechat/state/credentials.ts +1 -1
- package/extensions/wechat/state/state.ts +1 -1
- package/extensions/wechat/text/status.ts +112 -0
- package/extensions/wechat/types.ts +2 -2
- package/extensions/wechat/view/status-screen.ts +4 -103
- package/extensions/worktree/commands.ts +11 -9
- package/extensions/worktree/enter.ts +2 -2
- package/extensions/worktree/exit.ts +2 -2
- package/extensions/worktree/index.ts +5 -5
- package/extensions/worktree/screen.ts +3 -3
- package/extensions/worktree/status.ts +2 -2
- package/extensions/worktree/tools.ts +113 -116
- package/lib/README.md +28 -13
- package/lib/board/actions.ts +111 -0
- package/lib/board/artifact.ts +149 -0
- package/lib/board/card.ts +163 -52
- package/lib/board/config.ts +58 -0
- package/lib/board/contract.ts +113 -0
- package/lib/board/join.ts +38 -0
- package/lib/board/lanes.ts +182 -0
- package/lib/board/lead.ts +130 -0
- package/lib/board/limits.ts +78 -0
- package/lib/board/links.ts +31 -10
- package/lib/board/page-pointer.ts +30 -5
- package/lib/board/read.ts +83 -0
- package/lib/board/reason.ts +102 -0
- package/lib/board/round-text.ts +68 -6
- package/lib/board/round.ts +31 -15
- package/lib/board/rules.ts +151 -307
- package/lib/board/seat.ts +35 -0
- package/lib/board/session.ts +199 -51
- package/lib/board/skipped.ts +22 -0
- package/lib/board/steps.ts +487 -0
- package/lib/board/store.ts +119 -36
- package/lib/board/tone.ts +92 -0
- package/lib/board/view.ts +81 -63
- package/lib/bus-request.ts +83 -0
- package/{extensions/watch → lib}/child.ts +40 -21
- package/lib/env.ts +65 -7
- package/lib/format.ts +13 -0
- package/lib/http/index.ts +17 -11
- package/lib/http/mount.ts +142 -0
- package/lib/http/port.ts +30 -0
- package/lib/http/serve.ts +170 -46
- package/lib/http/server.ts +3 -3
- package/lib/lazy.ts +45 -0
- package/lib/plan-entry.ts +46 -21
- package/lib/plans/paths.ts +82 -0
- package/lib/policy.ts +9 -6
- package/lib/report.ts +3 -2
- package/lib/requirements.ts +10 -10
- package/lib/session-label.ts +129 -7
- package/lib/session-roster.ts +43 -1
- package/lib/session-tool.ts +39 -22
- package/lib/stray-fields.ts +7 -2
- package/lib/tool-names.ts +5 -10
- package/lib/tui/README.md +29 -11
- package/lib/tui/chrome.ts +16 -15
- package/lib/tui/cursor.ts +18 -3
- package/lib/tui/glyphs.ts +11 -11
- package/lib/tui/render-cache.ts +29 -0
- package/lib/tui/tokens.ts +33 -2
- package/lib/tui/tool.ts +33 -4
- package/lib/web/README.md +21 -0
- package/lib/web/client-runtime.ts +27 -1
- package/lib/web/components.ts +5 -1
- package/lib/web/index.ts +1 -0
- package/lib/web/markdown.ts +83 -0
- package/lib/web/markup.ts +20 -2
- package/lib/web/runtime.ts +9 -10
- package/package.json +8 -2
- package/skills/init/SKILL.md +5 -5
- package/skills/next/SKILL.md +3 -5
- package/extensions/board/config.ts +0 -36
- package/extensions/board/seat.ts +0 -28
- package/lib/tui/index.ts +0 -34
- package/skills/project-docs/SKILL.md +0 -133
- package/skills/project-docs/references/audit.md +0 -131
- package/skills/project-docs/references/changelog.md +0 -105
- package/skills/project-docs/references/discovery.md +0 -127
- package/skills/project-docs/references/readme.md +0 -80
- package/skills/project-docs/references/release.md +0 -84
- package/skills/project-docs/references/roadmap.md +0 -93
- package/skills/project-docs/scripts/state.ts +0 -316
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pure text for the live working line.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* A plain count while the run has started nothing (`working · 42s`), then how
|
|
5
|
+
* many top-level tools this run has started and the elapsed time
|
|
6
|
+
* (`working · 3 tools · 42s`). The line no longer names the tool: it is one word
|
|
7
|
+
* a reader can skim, and the transcript already draws each call as its own block.
|
|
8
|
+
* Whole seconds, because the tick advances once a second; negative and
|
|
9
|
+
* non-finite input reads as `0s`.
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
|
-
import {
|
|
12
|
+
import { formatToolCount } from "../../../lib/format.ts";
|
|
11
13
|
import { SEPARATORS } from "../../../lib/tui/glyphs.ts";
|
|
12
14
|
import { WORKING_CONFIG } from "./config.ts";
|
|
13
15
|
|
|
14
16
|
/** What the current run has done so far, for the live line. */
|
|
15
17
|
export interface WorkingRun {
|
|
16
|
-
/** The last top-level tool started, or `null` before the first one. */
|
|
17
|
-
readonly tool: string | null;
|
|
18
18
|
/** Top-level tool calls started in this run. */
|
|
19
19
|
readonly tools: number;
|
|
20
20
|
}
|
|
@@ -30,13 +30,10 @@ export function formatElapsed(ms: number): string {
|
|
|
30
30
|
return `${seconds}s`;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
/** The working
|
|
33
|
+
/** The working line's message: `working · 4s`, then `working · 3 tools · 4s`. */
|
|
34
34
|
export function workingCounter(ms: number, run?: WorkingRun): string {
|
|
35
35
|
const elapsed = formatElapsed(ms);
|
|
36
36
|
const tools = run?.tools ?? 0;
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
return [tool, formatToolCount(tools), elapsed].filter((part) => part !== "").join(SEPARATORS.item);
|
|
40
|
-
}
|
|
41
|
-
return `${WORKING_CONFIG.working}${WORKING_CONFIG.ellipsis} ${elapsed}`;
|
|
37
|
+
const count = tools > 0 ? formatToolCount(tools) : "";
|
|
38
|
+
return [WORKING_CONFIG.working, count, elapsed].filter((part) => part !== "").join(SEPARATORS.item);
|
|
42
39
|
}
|
|
@@ -1,31 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* bytefly's live working line.
|
|
3
3
|
*
|
|
4
|
-
* Pi's working
|
|
5
|
-
* short one. This surface advances the message once a second while the
|
|
6
|
-
* up, and hands it back to Pi when
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Pi's working indicator shows a static `Working`, so a long run reads the same as
|
|
5
|
+
* a short one. This surface advances the message once a second while the indicator
|
|
6
|
+
* is up, and hands it back to Pi when it comes down. Once a top-level tool starts,
|
|
7
|
+
* the message also counts this run's tools, turning the line on the composer's top
|
|
8
|
+
* border into `working · 3 tools · 12s`.
|
|
9
9
|
*
|
|
10
|
-
* The timer is tied to the
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* turns, and `start` is a no-op while it is counting.
|
|
10
|
+
* The timer is tied to the indicator's visibility. The host shows it in its own
|
|
11
|
+
* `turn_start` handler and clears it at `agent_end` (`interactive-mode.js`), so the
|
|
12
|
+
* tick starts at `turn_start` and stops at `agent_end` — the two moments it appears
|
|
13
|
+
* and disappears. Turns inside one run do not restart it: the host leaves the same
|
|
14
|
+
* indicator up across turns, and `start` is a no-op while it is counting.
|
|
16
15
|
*
|
|
17
16
|
* A nested tool call (one carrying `parentToolCallId`, such as a codemode
|
|
18
|
-
* script's inner call) is not counted
|
|
19
|
-
*
|
|
20
|
-
*
|
|
17
|
+
* script's inner call) is not counted, so the number matches the tool blocks the
|
|
18
|
+
* transcript shows. The tool events repaint the line immediately; the one-second
|
|
19
|
+
* tick is still the only timer.
|
|
21
20
|
*
|
|
22
|
-
* Compaction and auto-retry also replace the
|
|
21
|
+
* Compaction and auto-retry also replace the indicator, but both run outside this
|
|
23
22
|
* window (`auto_retry_start` follows `agent_end`; the before-settle compaction
|
|
24
23
|
* runs after it), and neither is an extension event, so there is nothing to
|
|
25
24
|
* hook and nothing to undo.
|
|
26
25
|
*
|
|
27
|
-
* The handle is global (`setWorkingMessage` drives the single
|
|
28
|
-
*
|
|
26
|
+
* The handle is global (`setWorkingMessage` drives the single indicator), so the
|
|
27
|
+
* tick is cancelled and the message reset on stop and, belt and braces, on
|
|
29
28
|
* `session_shutdown` — the factory itself only registers.
|
|
30
29
|
*/
|
|
31
30
|
|
|
@@ -50,14 +49,13 @@ export function registerWorkingTimer(pi: ExtensionAPI, deps: WorkingDeps = {}):
|
|
|
50
49
|
let startedAt = 0;
|
|
51
50
|
// A boolean, not `startedAt === 0`: an injected clock is allowed to start at 0.
|
|
52
51
|
let active = false;
|
|
53
|
-
//
|
|
54
|
-
let tool: string | null = null;
|
|
52
|
+
// How many top-level tools this run has started. Reset with the run.
|
|
55
53
|
let tools = 0;
|
|
56
54
|
|
|
57
|
-
/** The current
|
|
58
|
-
const message = (): string => workingCounter(now() - startedAt, {
|
|
55
|
+
/** The current line's text, from the run state and the elapsed time. */
|
|
56
|
+
const message = (): string => workingCounter(now() - startedAt, { tools });
|
|
59
57
|
|
|
60
|
-
/** Repaint an active
|
|
58
|
+
/** Repaint an active line now, so a tool start does not wait for the next tick. */
|
|
61
59
|
const refresh = (ctx: ExtensionContext): void => {
|
|
62
60
|
if (active) ctx.ui.setWorkingMessage(message());
|
|
63
61
|
};
|
|
@@ -68,7 +66,6 @@ export function registerWorkingTimer(pi: ExtensionAPI, deps: WorkingDeps = {}):
|
|
|
68
66
|
cancel = undefined;
|
|
69
67
|
if (!active) return;
|
|
70
68
|
active = false;
|
|
71
|
-
tool = null;
|
|
72
69
|
tools = 0;
|
|
73
70
|
ctx.ui.setWorkingMessage(undefined);
|
|
74
71
|
};
|
|
@@ -78,7 +75,6 @@ export function registerWorkingTimer(pi: ExtensionAPI, deps: WorkingDeps = {}):
|
|
|
78
75
|
if (active) return;
|
|
79
76
|
startedAt = now();
|
|
80
77
|
active = true;
|
|
81
|
-
tool = null;
|
|
82
78
|
tools = 0;
|
|
83
79
|
const tick = (): void => {
|
|
84
80
|
ctx.ui.setWorkingMessage(message());
|
|
@@ -88,17 +84,16 @@ export function registerWorkingTimer(pi: ExtensionAPI, deps: WorkingDeps = {}):
|
|
|
88
84
|
};
|
|
89
85
|
|
|
90
86
|
// The host shows the working indicator on its own `turn_start` handler, so
|
|
91
|
-
// this is the first frame the
|
|
87
|
+
// this is the first frame the indicator is up.
|
|
92
88
|
pi.on("turn_start", (_event, ctx) => {
|
|
93
89
|
if (ctx.mode !== "tui") return;
|
|
94
90
|
start(ctx);
|
|
95
91
|
});
|
|
96
92
|
|
|
97
93
|
// A nested tool is an implementation detail of its parent run, not a
|
|
98
|
-
// transcript block, so it
|
|
94
|
+
// transcript block, so it does not count toward the line.
|
|
99
95
|
pi.on("tool_execution_start", (event, ctx) => {
|
|
100
96
|
if (ctx.mode !== "tui" || !active || event.parentToolCallId) return;
|
|
101
|
-
tool = event.toolName;
|
|
102
97
|
tools += 1;
|
|
103
98
|
refresh(ctx);
|
|
104
99
|
});
|
|
@@ -12,10 +12,8 @@ import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-c
|
|
|
12
12
|
import { resolveEffectiveCwd } from "../../lib/env.ts";
|
|
13
13
|
import { planModeRefusal } from "../../lib/plan-entry.ts";
|
|
14
14
|
import { isPlanModeEnabled } from "../../lib/plan-state.ts";
|
|
15
|
-
import { withRailsSuppressed } from "../../lib/rails.ts";
|
|
16
15
|
import { formatCheckReport, formatCheckSummary } from "./format.ts";
|
|
17
16
|
import { CheckError, type CheckRuntime } from "./runtime.ts";
|
|
18
|
-
import { CheckResultComponent, CheckRunComponent } from "./tui.ts";
|
|
19
17
|
import type { CheckResult, CheckTarget } from "./types.ts";
|
|
20
18
|
|
|
21
19
|
/** The accepted targets and their completion descriptions. */
|
|
@@ -42,6 +40,8 @@ function runLabel(commands: readonly string[]): string {
|
|
|
42
40
|
|
|
43
41
|
/** The TUI loop: run, then list/detail, with rerun. */
|
|
44
42
|
async function runTui(runtime: CheckRuntime, ctx: ExtensionCommandContext, target: CheckTarget): Promise<void> {
|
|
43
|
+
// The screens are the TUI's: a text run above and registration load neither.
|
|
44
|
+
const { CheckResultComponent, CheckRunComponent } = await import("./view/check-screens.ts");
|
|
45
45
|
while (true) {
|
|
46
46
|
let active: ReturnType<CheckRuntime["start"]>;
|
|
47
47
|
try {
|
|
@@ -123,6 +123,7 @@ export function registerCheckCommand(pi: ExtensionAPI, runtime: CheckRuntime): v
|
|
|
123
123
|
return;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
const { withRailsSuppressed } = await import("../../lib/rails.ts");
|
|
126
127
|
await withRailsSuppressed(pi, () => runTui(runtime, ctx, target));
|
|
127
128
|
},
|
|
128
129
|
});
|
|
@@ -16,6 +16,7 @@ import type { Component } from "@earendil-works/pi-tui";
|
|
|
16
16
|
import { sanitizeLine } from "../../lib/format.ts";
|
|
17
17
|
import { clip } from "../../lib/tui/columns.ts";
|
|
18
18
|
import { expandHint, GLYPHS, SEPARATORS } from "../../lib/tui/glyphs.ts";
|
|
19
|
+
import { RenderCache } from "../../lib/tui/render-cache.ts";
|
|
19
20
|
import { listRow } from "../../lib/tui/rows.ts";
|
|
20
21
|
import { paint, type TuiRole } from "../../lib/tui/tokens.ts";
|
|
21
22
|
import { earlierHint } from "../../lib/tui/tool.ts";
|
|
@@ -134,6 +135,7 @@ export interface CheckResultInput {
|
|
|
134
135
|
export class CheckResultBlock implements Component {
|
|
135
136
|
private input: CheckResultInput;
|
|
136
137
|
private theme: Theme;
|
|
138
|
+
private readonly cache = new RenderCache();
|
|
137
139
|
|
|
138
140
|
constructor(input: CheckResultInput, theme: Theme) {
|
|
139
141
|
this.input = input;
|
|
@@ -144,12 +146,19 @@ export class CheckResultBlock implements Component {
|
|
|
144
146
|
setInput(input: CheckResultInput, theme: Theme): void {
|
|
145
147
|
this.input = input;
|
|
146
148
|
this.theme = theme;
|
|
149
|
+
this.cache.invalidate();
|
|
147
150
|
}
|
|
148
151
|
|
|
149
|
-
invalidate(): void {
|
|
152
|
+
invalidate(): void {
|
|
153
|
+
this.cache.invalidate();
|
|
154
|
+
}
|
|
150
155
|
|
|
151
156
|
render(width: number): string[] {
|
|
152
157
|
const w = Math.max(1, width);
|
|
158
|
+
return this.cache.lines(w, undefined, () => this.build(w));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private build(w: number): string[] {
|
|
153
162
|
const theme = this.theme;
|
|
154
163
|
const { details, expanded } = this.input;
|
|
155
164
|
const role = statusRole(details.status);
|
|
@@ -11,19 +11,19 @@
|
|
|
11
11
|
|
|
12
12
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
13
13
|
import { type Component, type TuiMouseEvent, type TuiMouseEventResult } from "@earendil-works/pi-tui";
|
|
14
|
-
import { formatDuration, sanitizeLine } from "
|
|
15
|
-
import { dockListFrame, screenHint, type ScreenHint, type ViewportRowsSource } from "
|
|
16
|
-
import { clip } from "
|
|
17
|
-
import { fitRows, formatRange, isCloseKey, wheelDelta } from "
|
|
18
|
-
import { DockListScreen, type DetailContents } from "
|
|
19
|
-
import { SEPARATORS } from "
|
|
20
|
-
import { LinePane } from "
|
|
21
|
-
import { emptyState, listRow } from "
|
|
22
|
-
import { titleRule } from "
|
|
23
|
-
import { paint } from "
|
|
24
|
-
import { formatFailureDetail, formatFailureLocation, formatResultSummary } from "
|
|
25
|
-
import { readSourceExcerpt } from "
|
|
26
|
-
import type { CheckResult, Failure } from "
|
|
14
|
+
import { formatDuration, sanitizeLine } from "../../../lib/format.ts";
|
|
15
|
+
import { dockListFrame, screenHint, type ScreenHint, type ViewportRowsSource } from "../../../lib/tui/chrome.ts";
|
|
16
|
+
import { clip } from "../../../lib/tui/columns.ts";
|
|
17
|
+
import { fitRows, formatRange, isCloseKey, wheelDelta } from "../../../lib/tui/cursor.ts";
|
|
18
|
+
import { DockListScreen, type DetailContents } from "../../../lib/tui/dock-screen.ts";
|
|
19
|
+
import { SEPARATORS } from "../../../lib/tui/glyphs.ts";
|
|
20
|
+
import { LinePane } from "../../../lib/tui/line-pane.ts";
|
|
21
|
+
import { emptyState, listRow } from "../../../lib/tui/rows.ts";
|
|
22
|
+
import { titleRule } from "../../../lib/tui/rules.ts";
|
|
23
|
+
import { paint } from "../../../lib/tui/tokens.ts";
|
|
24
|
+
import { formatFailureDetail, formatFailureLocation, formatResultSummary } from "../format.ts";
|
|
25
|
+
import { readSourceExcerpt } from "../source.ts";
|
|
26
|
+
import type { CheckResult, Failure } from "../types.ts";
|
|
27
27
|
|
|
28
28
|
const RUN_DEFAULT_ROWS = 20;
|
|
29
29
|
const RUN_CHROME_ROWS = 5;
|
|
@@ -166,12 +166,7 @@ export class CheckResultComponent extends DockListScreen<Failure> {
|
|
|
166
166
|
protected listHints(rows: readonly Failure[]): ScreenHint[] {
|
|
167
167
|
// An empty list has nothing to open, so it offers only the two ways out.
|
|
168
168
|
if (rows.length === 0) return ["Esc close", { text: "r rerun", color: "accent" }];
|
|
169
|
-
return [
|
|
170
|
-
"Esc close",
|
|
171
|
-
{ text: "Enter detail", color: "accent" },
|
|
172
|
-
{ text: "r rerun", color: "warning" },
|
|
173
|
-
"↑/↓ select",
|
|
174
|
-
];
|
|
169
|
+
return ["Esc close", { text: "Enter detail", color: "accent" }, { text: "r rerun", color: "warn" }, "↑/↓ select"];
|
|
175
170
|
}
|
|
176
171
|
|
|
177
172
|
protected detailHints(): ScreenHint[] {
|
|
@@ -10,12 +10,10 @@
|
|
|
10
10
|
|
|
11
11
|
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
12
12
|
import { formatTokens } from "../../lib/format.ts";
|
|
13
|
-
import { withRailsSuppressed } from "../../lib/rails.ts";
|
|
14
13
|
import { messageOf } from "../../lib/errors.ts";
|
|
15
14
|
import { collectInputs } from "./collect.ts";
|
|
16
15
|
import { formatReport } from "./format.ts";
|
|
17
16
|
import { buildReport } from "./report.ts";
|
|
18
|
-
import { ContextComponent } from "./tui.ts";
|
|
19
17
|
|
|
20
18
|
/** The command's argument surface, shared by the description and the usage warning. */
|
|
21
19
|
const CONTEXT_USAGE = "/context [compact [instructions]]";
|
|
@@ -37,6 +35,8 @@ async function openReport(pi: ExtensionAPI, ctx: ExtensionCommandContext): Promi
|
|
|
37
35
|
}
|
|
38
36
|
|
|
39
37
|
// The dock screen owns the editor slot; hide the rails for its lifetime.
|
|
38
|
+
const { withRailsSuppressed } = await import("../../lib/rails.ts");
|
|
39
|
+
const { ContextComponent } = await import("./view/context-report.ts");
|
|
40
40
|
await withRailsSuppressed(pi, () =>
|
|
41
41
|
ctx.ui.custom<void>((tui, theme, _keybindings, done) => {
|
|
42
42
|
return new ContextComponent({
|
|
@@ -14,17 +14,17 @@
|
|
|
14
14
|
|
|
15
15
|
import type { CompactionResult, Theme } from "@earendil-works/pi-coding-agent";
|
|
16
16
|
import { truncateToWidth, visibleWidth, type Component } from "@earendil-works/pi-tui";
|
|
17
|
-
import { messageOf } from "
|
|
18
|
-
import { formatTokens } from "
|
|
19
|
-
import { screenFooter, screenHint, type ViewportRowsSource } from "
|
|
20
|
-
import { ConfirmState } from "
|
|
21
|
-
import { clampScroll, fitRows, formatRange, isCloseKey, navigateScroll } from "
|
|
22
|
-
import { gaugeBar, gaugeRole } from "
|
|
23
|
-
import { BODY_INDENT } from "
|
|
24
|
-
import { titleRule } from "
|
|
25
|
-
import { paint, type TuiRole } from "
|
|
26
|
-
import { formatBudget, formatCompaction, formatDrift } from "
|
|
27
|
-
import type { ContextBucket, ContextReport } from "
|
|
17
|
+
import { messageOf } from "../../../lib/errors.ts";
|
|
18
|
+
import { formatTokens } from "../../../lib/format.ts";
|
|
19
|
+
import { screenFooter, screenHint, type ViewportRowsSource } from "../../../lib/tui/chrome.ts";
|
|
20
|
+
import { ConfirmState } from "../../../lib/tui/confirm.ts";
|
|
21
|
+
import { clampScroll, fitRows, formatRange, isCloseKey, navigateScroll } from "../../../lib/tui/cursor.ts";
|
|
22
|
+
import { gaugeBar, gaugeRole } from "../../../lib/tui/gauge.ts";
|
|
23
|
+
import { BODY_INDENT } from "../../../lib/tui/indent.ts";
|
|
24
|
+
import { titleRule } from "../../../lib/tui/rules.ts";
|
|
25
|
+
import { paint, type TuiRole } from "../../../lib/tui/tokens.ts";
|
|
26
|
+
import { formatBudget, formatCompaction, formatDrift } from "../format.ts";
|
|
27
|
+
import type { ContextBucket, ContextReport } from "../types.ts";
|
|
28
28
|
|
|
29
29
|
/** Fixed rows around the scrollable body: title, gauge, budget, blank, blank, footer, closing rule. */
|
|
30
30
|
const CHROME_ROWS = 7;
|
|
@@ -71,6 +71,34 @@ function shareCell(share: number): string {
|
|
|
71
71
|
return `${share.toFixed(1)}%`.padStart(6);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
/**
|
|
75
|
+
* The hue that identifies a context category, keyed by the bucket ids
|
|
76
|
+
* `report.ts` builds.
|
|
77
|
+
*
|
|
78
|
+
* Categories are what the screen tells apart at a glance; a bucket with no
|
|
79
|
+
* entry here — injected context and its children, summaries, shell output, a
|
|
80
|
+
* type this build has never seen — reads neutral, so the catch-all rows stay
|
|
81
|
+
* quiet and the coloured ones mean something. The same table answers for the
|
|
82
|
+
* largest-message list, so a tool result is one colour in both sections.
|
|
83
|
+
*/
|
|
84
|
+
const CATEGORY_HUES: Readonly<Record<string, TuiRole>> = {
|
|
85
|
+
system: "hue1",
|
|
86
|
+
tools: "hue2",
|
|
87
|
+
user: "hue3",
|
|
88
|
+
assistant: "hue4",
|
|
89
|
+
toolResults: "hue5",
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/** The hue for a bucket id, or `undefined` when the category is neutral. */
|
|
93
|
+
export function hueOf(id: string): TuiRole | undefined {
|
|
94
|
+
return CATEGORY_HUES[id];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** The hue for a message role word; `tool result` is the bucket's own category. */
|
|
98
|
+
export function hueForRole(role: string): TuiRole | undefined {
|
|
99
|
+
return CATEGORY_HUES[role === "tool result" ? "toolResults" : role];
|
|
100
|
+
}
|
|
101
|
+
|
|
74
102
|
/** Pad a label to its column with spaces, measured in terminal columns. */
|
|
75
103
|
function padCell(text: string, width: number): string {
|
|
76
104
|
const clipped = truncateToWidth(text, width, "…");
|
|
@@ -151,7 +179,7 @@ export class ContextComponent implements Component {
|
|
|
151
179
|
private compositionLine(width: number): string {
|
|
152
180
|
const label = paint(this.theme, "label", "Composition");
|
|
153
181
|
const total = paint(this.theme, "meta", `${formatTokens(this.report.estimate)} estimate`);
|
|
154
|
-
const full = ` ${label} · ${total}`;
|
|
182
|
+
const full = ` ${label}${paint(this.theme, "faint", " · ")}${total}`;
|
|
155
183
|
return visibleWidth(full) <= width ? full : truncateToWidth(` ${label}`, width);
|
|
156
184
|
}
|
|
157
185
|
|
|
@@ -161,12 +189,15 @@ export class ContextComponent implements Component {
|
|
|
161
189
|
const labelWidth = bucket.depth > 0 ? 20 : 22;
|
|
162
190
|
const label = padCell(bucket.label, labelWidth);
|
|
163
191
|
const tokens = formatTokens(bucket.tokens).padStart(6);
|
|
164
|
-
|
|
165
|
-
|
|
192
|
+
// The row's hue carries the category; the label, the count, and the printed
|
|
193
|
+
// share still say everything the colour repeats.
|
|
194
|
+
const hue = hueOf(bucket.id) ?? "meta";
|
|
195
|
+
const share = paint(this.theme, hue, shareCell(bucket.share));
|
|
196
|
+
const note = bucket.note ? paint(this.theme, "faint", ` ${bucket.note}`) : "";
|
|
166
197
|
const bar = gaugeBar(this.theme, {
|
|
167
198
|
percent: bucket.share,
|
|
168
199
|
blocks: BUCKET_BLOCKS,
|
|
169
|
-
role:
|
|
200
|
+
role: hue,
|
|
170
201
|
emptyRole: "faint",
|
|
171
202
|
});
|
|
172
203
|
const head = `${indent}${paint(this.theme, "label", label)}${paint(this.theme, "meta", tokens)}`;
|
|
@@ -204,7 +235,8 @@ export class ContextComponent implements Component {
|
|
|
204
235
|
for (const block of report.largest) {
|
|
205
236
|
const tokens = formatTokens(block.tokens).padStart(6);
|
|
206
237
|
const role = padCell(block.role, 12);
|
|
207
|
-
const
|
|
238
|
+
const hue = hueForRole(block.role) ?? "meta";
|
|
239
|
+
const candidate = ` ${paint(this.theme, "meta", tokens)} ${paint(this.theme, hue, role)}${paint(this.theme, "label", block.label)}`;
|
|
208
240
|
lines.push(truncateToWidth(candidate, width));
|
|
209
241
|
}
|
|
210
242
|
}
|
|
@@ -221,9 +253,9 @@ export class ContextComponent implements Component {
|
|
|
221
253
|
width,
|
|
222
254
|
[
|
|
223
255
|
{ text: "c compact", color: "accent" },
|
|
224
|
-
{ text: "r refresh", color: "
|
|
225
|
-
{ text: "↑↓ scroll", color: "
|
|
226
|
-
{ text: "Esc close", color: "
|
|
256
|
+
{ text: "r refresh", color: "faint" },
|
|
257
|
+
{ text: "↑↓ scroll", color: "faint" },
|
|
258
|
+
{ text: "Esc close", color: "faint" },
|
|
227
259
|
],
|
|
228
260
|
BODY_INDENT,
|
|
229
261
|
);
|
|
@@ -2,16 +2,14 @@
|
|
|
2
2
|
* Configuration for the browse extension.
|
|
3
3
|
*
|
|
4
4
|
* The `file-browse` section of the shared `bytefly.json` (global then project,
|
|
5
|
-
* project wins). Everything
|
|
6
|
-
* is
|
|
5
|
+
* project wins). Everything is validated and clamped so a typo cannot produce a
|
|
6
|
+
* nonsensical page. The port is not here: the session serves every page on one, and
|
|
7
|
+
* `serve.port` is where that is pinned.
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
|
-
import { randomInt } from "node:crypto";
|
|
10
10
|
import { asBoolean, clampInteger, loadConfigSection } from "../../../lib/config.ts";
|
|
11
11
|
|
|
12
12
|
export interface ServeConfig {
|
|
13
|
-
/** TCP port on 127.0.0.1; `0` picks one random port fixed for the session, a positive value pins it. */
|
|
14
|
-
port: number;
|
|
15
13
|
/** Open the default browser when the server starts. */
|
|
16
14
|
autoOpen: boolean;
|
|
17
15
|
/** Files larger than this are not rendered inline. */
|
|
@@ -43,7 +41,6 @@ export interface ServeConfig {
|
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
export const DEFAULT_CONFIG: ServeConfig = {
|
|
46
|
-
port: 0,
|
|
47
44
|
autoOpen: true,
|
|
48
45
|
maxFileBytes: 1024 * 1024,
|
|
49
46
|
maxThumbBytes: 5 * 1024 * 1024,
|
|
@@ -60,12 +57,6 @@ export const DEFAULT_CONFIG: ServeConfig = {
|
|
|
60
57
|
liveIntervalMs: 5000,
|
|
61
58
|
};
|
|
62
59
|
|
|
63
|
-
/** Base of the per-session port range. */
|
|
64
|
-
export const PORT_BASE = 4780;
|
|
65
|
-
|
|
66
|
-
/** Number of ports in the per-session range. */
|
|
67
|
-
export const PORT_RANGE = 1000;
|
|
68
|
-
|
|
69
60
|
/** Fastest live-status cadence a page may be given: short enough to watch, and a
|
|
70
61
|
* floor so a page cannot outrun the git reads behind each frame. */
|
|
71
62
|
export const LIVE_INTERVAL_MIN = 250;
|
|
@@ -76,7 +67,7 @@ export const LIVE_INTERVAL_MAX = 60_000;
|
|
|
76
67
|
/**
|
|
77
68
|
* How long the server keeps listening once the last page has gone. A reload or a
|
|
78
69
|
* navigation detaches the stream and re-attaches within a moment, so the grace has
|
|
79
|
-
* to outlast one; a closed window never re-attaches, and the
|
|
70
|
+
* to outlast one; a closed window never re-attaches, and the prefix is given back
|
|
80
71
|
* instead of listening for nobody until the session ends.
|
|
81
72
|
*/
|
|
82
73
|
export const IDLE_STOP_MS = 10_000;
|
|
@@ -90,11 +81,6 @@ export const IDLE_STOP_MS = 10_000;
|
|
|
90
81
|
*/
|
|
91
82
|
export const LIVE_MAX_CLIENTS = 8;
|
|
92
83
|
|
|
93
|
-
/** A random port in the per-session range `PORT_BASE`–`PORT_BASE + PORT_RANGE - 1`. */
|
|
94
|
-
export function randomPort(): number {
|
|
95
|
-
return PORT_BASE + randomInt(PORT_RANGE);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
84
|
/** Validate and clamp a raw config object over the defaults. */
|
|
99
85
|
export function normalizeConfig(
|
|
100
86
|
raw: Record<string, unknown> | undefined,
|
|
@@ -102,7 +88,6 @@ export function normalizeConfig(
|
|
|
102
88
|
): ServeConfig {
|
|
103
89
|
if (!raw) return base;
|
|
104
90
|
return {
|
|
105
|
-
port: clampInteger(raw.port, base.port, 0, 65535),
|
|
106
91
|
autoOpen: asBoolean(raw.autoOpen, base.autoOpen),
|
|
107
92
|
maxFileBytes: clampInteger(raw.maxFileBytes, base.maxFileBytes, 0, 1024 * 1024 * 1024),
|
|
108
93
|
maxThumbBytes: clampInteger(raw.maxThumbBytes, base.maxThumbBytes, 0, 1024 * 1024 * 1024),
|