@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
|
@@ -26,8 +26,8 @@ import { formatExtension, formatReport, hasProblems } from "../doctor/format.ts"
|
|
|
26
26
|
import type { DoctorRuntime } from "../doctor/runtime.ts";
|
|
27
27
|
import { updateOutcomeText } from "../update/format.ts";
|
|
28
28
|
import type { UpdateHandle } from "../update/register.ts";
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
29
|
+
import type { DiscoveryDeps } from "../picker/discovery.ts";
|
|
30
|
+
import type { PickerRuntime } from "../picker/runtime.ts";
|
|
31
31
|
import type { Discovery } from "../picker/types.ts";
|
|
32
32
|
import { usageRecords } from "../footer/collect/entries.ts";
|
|
33
33
|
import { loadByteflyConfig } from "../footer/price/config.ts";
|
|
@@ -35,10 +35,9 @@ import { createPricer } from "../footer/price/price.ts";
|
|
|
35
35
|
import { formatListing, formatSettingsListing, SKILL_NOUN } from "./format.ts";
|
|
36
36
|
import { pricesReport } from "./prices.ts";
|
|
37
37
|
import { buildPanelModel, buildSkillModel, extensionName } from "./join.ts";
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
38
|
+
import type { SettingsRuntime, SettingsRuntimeDeps } from "./settings-runtime.ts";
|
|
39
|
+
import type { SkillIndex } from "./skills.ts";
|
|
40
40
|
import { shippedThemeNames } from "./themes.ts";
|
|
41
|
-
import { PanelComponent } from "./tui.ts";
|
|
42
41
|
import type { PanelSection } from "./types.ts";
|
|
43
42
|
|
|
44
43
|
/** Host seams for the panel command, injected by tests and the composition root. */
|
|
@@ -174,12 +173,186 @@ function settingsThemeNames(ctx: ExtensionCommandContext): string[] {
|
|
|
174
173
|
}
|
|
175
174
|
}
|
|
176
175
|
|
|
176
|
+
/** The state one `/bytefly` invocation carries into the branch that answers it. */
|
|
177
|
+
interface PanelRun {
|
|
178
|
+
pi: ExtensionAPI;
|
|
179
|
+
deps: PanelCommandDeps;
|
|
180
|
+
ctx: ExtensionCommandContext;
|
|
181
|
+
cwd: string;
|
|
182
|
+
agentDir: string;
|
|
183
|
+
trusted: boolean;
|
|
184
|
+
themes: string[];
|
|
185
|
+
section: PanelSection;
|
|
186
|
+
name: string | undefined;
|
|
187
|
+
refresh: boolean;
|
|
188
|
+
mode: PanelMode;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** The mounted panel, so a notice before the screen exists still reaches the transcript. */
|
|
192
|
+
interface PanelHolder {
|
|
193
|
+
current: InstanceType<typeof import("./tui.ts").PanelComponent> | undefined;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** `/bytefly prices`: one question about the transcript, no discovery. */
|
|
197
|
+
function pricesAnswer(ctx: ExtensionCommandContext, cwd: string): void {
|
|
198
|
+
const config = loadByteflyConfig(cwd);
|
|
199
|
+
const active = ctx.model ? { provider: ctx.model.provider, model: ctx.model.id } : undefined;
|
|
200
|
+
const scanned = usageRecords(ctx.sessionManager.getEntries(), active);
|
|
201
|
+
ctx.ui.notify(pricesReport(createPricer(config)(scanned.records), config.currency).join("\n"), "info");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** `/bytefly update` and `/bytefly upgrade`: the check, and the install it offers. */
|
|
205
|
+
async function updateAnswer(
|
|
206
|
+
deps: PanelCommandDeps,
|
|
207
|
+
mode: "update" | "upgrade",
|
|
208
|
+
ctx: ExtensionCommandContext,
|
|
209
|
+
): Promise<void> {
|
|
210
|
+
if (mode === "update") {
|
|
211
|
+
const outcome = await deps.update.recheck();
|
|
212
|
+
const { text, severity } = updateOutcomeText(outcome, pkg.version);
|
|
213
|
+
ctx.ui.notify(text, severity);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const confirmed = await ctx.ui.confirm(
|
|
217
|
+
"Update packages?",
|
|
218
|
+
`Run Pi's package manager for every configured package, then reload to use it.`,
|
|
219
|
+
);
|
|
220
|
+
if (!confirmed) {
|
|
221
|
+
ctx.ui.notify("bytefly: update cancelled.", "info");
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const outcome = await deps.update.apply();
|
|
225
|
+
const { text, severity } = updateOutcomeText(outcome, pkg.version);
|
|
226
|
+
ctx.ui.notify(text, severity);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Settings outside `tui`: the grouped listing without resolving packages. */
|
|
230
|
+
function settingsText(
|
|
231
|
+
run: PanelRun,
|
|
232
|
+
makeSettings: NonNullable<PanelCommandDeps["settings"]>,
|
|
233
|
+
createSettingsStores: (cwd: string, agentDir: string, trusted: boolean) => Discovery["stores"],
|
|
234
|
+
surfaceNotice: (message: string, kind?: "warning" | "error") => void,
|
|
235
|
+
): void {
|
|
236
|
+
const stores = createSettingsStores(run.cwd, run.agentDir, run.trusted);
|
|
237
|
+
const settings = makeSettings({
|
|
238
|
+
stores,
|
|
239
|
+
cwd: run.cwd,
|
|
240
|
+
agentDir: run.agentDir,
|
|
241
|
+
trusted: run.trusted,
|
|
242
|
+
themes: run.themes,
|
|
243
|
+
onError: (message) => surfaceNotice(message, "warning"),
|
|
244
|
+
});
|
|
245
|
+
run.ctx.ui.notify(formatSettingsListing(settings.views("global")), "info");
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Skills outside `tui`: the grouped listing from Pi's metadata, with no probe. */
|
|
249
|
+
function skillsText(
|
|
250
|
+
run: PanelRun,
|
|
251
|
+
runtime: PickerRuntime,
|
|
252
|
+
discovery: Discovery,
|
|
253
|
+
loadSkills: (paths: readonly string[], cwd: string, agentDir: string) => SkillIndex,
|
|
254
|
+
resolveSkillPaths: (discovery: Discovery) => string[],
|
|
255
|
+
): void {
|
|
256
|
+
const index = loadSkills(resolveSkillPaths(discovery), run.cwd, run.agentDir);
|
|
257
|
+
const model = buildSkillModel({ items: runtime.items("skills"), index, agentDir: run.agentDir });
|
|
258
|
+
run.ctx.ui.notify(
|
|
259
|
+
formatListing(model, runtime.scope(), { noun: SKILL_NOUN, emptyLabel: "No skills found." }),
|
|
260
|
+
"info",
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Text modes: probe now, then print the report, a named extension, or the listing. */
|
|
265
|
+
async function reportText(run: PanelRun, runtime: PickerRuntime): Promise<void> {
|
|
266
|
+
let reports: ExtensionReport[];
|
|
267
|
+
try {
|
|
268
|
+
reports = await run.deps.doctor.probeAll({ refresh: run.refresh });
|
|
269
|
+
} catch (error) {
|
|
270
|
+
run.ctx.ui.notify(`Could not probe extensions: ${error instanceof Error ? error.message : String(error)}`, "error");
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
if (run.name !== undefined) {
|
|
274
|
+
notifyExtension(run.ctx, run.name, reports, runtime);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
if (run.mode === "doctor") {
|
|
278
|
+
run.ctx.ui.notify(formatReport(reports), hasProblems(reports) ? "warning" : "info");
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const model = buildPanelModel({ items: runtime.items("extensions"), reports, agentDir: run.agentDir });
|
|
282
|
+
run.ctx.ui.notify(formatListing(model, runtime.scope()), "info");
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** The TUI branch: mount the screen, then apply any pending write. */
|
|
286
|
+
async function mountPanel(
|
|
287
|
+
run: PanelRun,
|
|
288
|
+
state: {
|
|
289
|
+
runtime: PickerRuntime;
|
|
290
|
+
settings: SettingsRuntime;
|
|
291
|
+
discovery: Discovery;
|
|
292
|
+
loadSkills: (paths: readonly string[], cwd: string, agentDir: string) => SkillIndex;
|
|
293
|
+
resolveSkillPaths: (discovery: Discovery) => string[];
|
|
294
|
+
cached: ExtensionReport[] | undefined;
|
|
295
|
+
holder: PanelHolder;
|
|
296
|
+
},
|
|
297
|
+
): Promise<void> {
|
|
298
|
+
const { runtime, settings, discovery, loadSkills, resolveSkillPaths, cached, holder } = state;
|
|
299
|
+
const { ctx, section, name, refresh, agentDir, cwd } = run;
|
|
300
|
+
let reports: ExtensionReport[] = cached ?? [];
|
|
301
|
+
|
|
302
|
+
const { PanelComponent } = await import("./tui.ts");
|
|
303
|
+
// The dock screen owns the editor slot; hide the rails for its lifetime.
|
|
304
|
+
await withRailsSuppressed(run.pi, () =>
|
|
305
|
+
ctx.ui.custom<void>((tui, theme, _keybindings, done) => {
|
|
306
|
+
const unsubscribe = runtime.onChange(() => tui.requestRender());
|
|
307
|
+
// The settings runtime re-reads its snapshot after a write; without a
|
|
308
|
+
// subscriber that repaint depends on every write chain's own callback.
|
|
309
|
+
const unsubscribeSettings = settings.onChange(() => tui.requestRender());
|
|
310
|
+
const component = new PanelComponent({
|
|
311
|
+
runtime,
|
|
312
|
+
reports: () => reports,
|
|
313
|
+
settings,
|
|
314
|
+
filter: section === "extensions" ? name : undefined,
|
|
315
|
+
section,
|
|
316
|
+
theme,
|
|
317
|
+
agentDir,
|
|
318
|
+
cwd,
|
|
319
|
+
// A cached session probes only when the command asked for it.
|
|
320
|
+
needsProbe: cached === undefined || refresh,
|
|
321
|
+
initialRefresh: refresh,
|
|
322
|
+
viewportRows: () => tui.terminal?.rows,
|
|
323
|
+
requestRender: () => tui.requestRender(),
|
|
324
|
+
skills: () => loadSkills(resolveSkillPaths(discovery), cwd, agentDir),
|
|
325
|
+
refresh: async (force) => {
|
|
326
|
+
reports = await run.deps.doctor.probeAll({ refresh: force === true });
|
|
327
|
+
},
|
|
328
|
+
reload: async () => {
|
|
329
|
+
// The reload takes the session, so the screen asks first and says
|
|
330
|
+
// what will change; a refusal leaves the dock exactly as it was.
|
|
331
|
+
const reload = await ctx.ui.confirm("Reload to apply changes?", "Changes take effect after a reload.");
|
|
332
|
+
if (!reload) return;
|
|
333
|
+
await ctx.reload();
|
|
334
|
+
},
|
|
335
|
+
onClose: () => {
|
|
336
|
+
// A late error falls back to the transcript, never a disposed screen.
|
|
337
|
+
holder.current = undefined;
|
|
338
|
+
unsubscribe();
|
|
339
|
+
unsubscribeSettings();
|
|
340
|
+
done();
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
holder.current = component;
|
|
344
|
+
component.prime();
|
|
345
|
+
return component;
|
|
346
|
+
}),
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
if (!runtime.isDirty() && !settings.isDirty()) return;
|
|
350
|
+
const reload = await ctx.ui.confirm("Reload to apply changes?", "Changes take effect after a reload.");
|
|
351
|
+
if (reload) await ctx.reload();
|
|
352
|
+
}
|
|
353
|
+
|
|
177
354
|
/** Register the single `/bytefly` command. */
|
|
178
355
|
export function registerPanel(pi: ExtensionAPI, deps: PanelCommandDeps): void {
|
|
179
|
-
const discover = deps.discover ?? discoverResources;
|
|
180
|
-
const makeSettings = deps.settings ?? createSettingsRuntime;
|
|
181
|
-
const loadSkills = deps.skills ?? loadSkillIndex;
|
|
182
|
-
|
|
183
356
|
const handler = async (args: string, ctx: ExtensionCommandContext): Promise<void> => {
|
|
184
357
|
const { mode, section, name, refresh } = parseArgs(args);
|
|
185
358
|
// Only the Extensions section takes an extension argument; a stray word is a
|
|
@@ -193,13 +366,7 @@ export function registerPanel(pi: ExtensionAPI, deps: PanelCommandDeps): void {
|
|
|
193
366
|
// prices what the transcript already recorded and names what that left out. It runs
|
|
194
367
|
// in every mode, because the footer that would carry the same fact exists only in
|
|
195
368
|
// `tui`.
|
|
196
|
-
if (mode === "prices")
|
|
197
|
-
const config = loadByteflyConfig(cwd);
|
|
198
|
-
const active = ctx.model ? { provider: ctx.model.provider, model: ctx.model.id } : undefined;
|
|
199
|
-
const scanned = usageRecords(ctx.sessionManager.getEntries(), active);
|
|
200
|
-
ctx.ui.notify(pricesReport(createPricer(config)(scanned.records), config.currency).join("\n"), "info");
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
369
|
+
if (mode === "prices") return pricesAnswer(ctx, cwd);
|
|
203
370
|
|
|
204
371
|
const agentDir = getAgentDir();
|
|
205
372
|
const trusted = ctx.isProjectTrusted();
|
|
@@ -208,47 +375,28 @@ export function registerPanel(pi: ExtensionAPI, deps: PanelCommandDeps): void {
|
|
|
208
375
|
// the frame, not appended to the transcript behind it. Before the screen
|
|
209
376
|
// exists (the non-TUI settings path, a pre-mount failure) the transcript is
|
|
210
377
|
// the only surface.
|
|
211
|
-
|
|
378
|
+
const holder: PanelHolder = { current: undefined };
|
|
212
379
|
const surfaceNotice = (message: string, kind: "warning" | "error" = "warning"): void => {
|
|
213
|
-
if (
|
|
380
|
+
if (holder.current) holder.current.notify(message);
|
|
214
381
|
else ctx.ui.notify(message, kind);
|
|
215
382
|
};
|
|
216
383
|
const settingsNotice = (message: string): void => surfaceNotice(message, "warning");
|
|
384
|
+
const run: PanelRun = { pi, deps, ctx, cwd, agentDir, trusted, themes, section, name, refresh, mode };
|
|
217
385
|
|
|
218
|
-
// `/bytefly update` answers one question and resolves nothing
|
|
219
|
-
//
|
|
220
|
-
|
|
221
|
-
if (mode === "update") {
|
|
222
|
-
const outcome = await deps.update.recheck();
|
|
223
|
-
const { text, severity } = updateOutcomeText(outcome, pkg.version);
|
|
224
|
-
ctx.ui.notify(text, severity);
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
386
|
+
// `/bytefly update` answers one question and resolves nothing; `upgrade` is the
|
|
387
|
+
// same subject one verb further, behind a confirmation because it writes.
|
|
388
|
+
if (mode === "update" || mode === "upgrade") return updateAnswer(deps, mode, ctx);
|
|
227
389
|
|
|
228
|
-
//
|
|
229
|
-
//
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
"Update packages?",
|
|
234
|
-
`Run Pi's package manager for every configured package, then reload to use it.`,
|
|
235
|
-
);
|
|
236
|
-
if (!confirmed) {
|
|
237
|
-
ctx.ui.notify("bytefly: update cancelled.", "info");
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
const outcome = await deps.update.apply();
|
|
241
|
-
const { text, severity } = updateOutcomeText(outcome, pkg.version);
|
|
242
|
-
ctx.ui.notify(text, severity);
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
390
|
+
// Settings, discovery, and the picker are the command's, not registration's: the
|
|
391
|
+
// price, update, and upgrade answers above resolve none of them.
|
|
392
|
+
const makeSettings = deps.settings ?? (await import("./settings-runtime.ts")).createSettingsRuntime;
|
|
393
|
+
const { createSettingsStores, discoverResources } = await import("../picker/discovery.ts");
|
|
394
|
+
const discover = deps.discover ?? discoverResources;
|
|
245
395
|
|
|
246
396
|
// Settings outside `tui` prints its grouped listing without resolving
|
|
247
397
|
// packages or probing the doctor.
|
|
248
398
|
if (section === "settings" && ctx.mode !== "tui") {
|
|
249
|
-
|
|
250
|
-
const settings = makeSettings({ stores, cwd, agentDir, trusted, themes, onError: settingsNotice });
|
|
251
|
-
ctx.ui.notify(formatSettingsListing(settings.views("global")), "info");
|
|
399
|
+
settingsText(run, makeSettings, createSettingsStores, surfaceNotice);
|
|
252
400
|
return;
|
|
253
401
|
}
|
|
254
402
|
|
|
@@ -269,6 +417,7 @@ export function registerPanel(pi: ExtensionAPI, deps: PanelCommandDeps): void {
|
|
|
269
417
|
onError: settingsNotice,
|
|
270
418
|
});
|
|
271
419
|
|
|
420
|
+
const { createPickerRuntime } = await import("../picker/runtime.ts");
|
|
272
421
|
const runtime = createPickerRuntime({
|
|
273
422
|
discovery,
|
|
274
423
|
cwd,
|
|
@@ -283,15 +432,13 @@ export function registerPanel(pi: ExtensionAPI, deps: PanelCommandDeps): void {
|
|
|
283
432
|
return;
|
|
284
433
|
}
|
|
285
434
|
|
|
435
|
+
const { loadSkillIndex, resolvedSkillPaths } = await import("./skills.ts");
|
|
436
|
+
const loadSkills = deps.skills ?? loadSkillIndex;
|
|
437
|
+
|
|
286
438
|
// Skills outside `tui` print the same grouped listing, from the metadata Pi's
|
|
287
439
|
// loader reads; there is no report to probe for and no package to resolve.
|
|
288
440
|
if (section === "skills" && ctx.mode !== "tui") {
|
|
289
|
-
|
|
290
|
-
const model = buildSkillModel({ items: runtime.items("skills"), index, agentDir });
|
|
291
|
-
ctx.ui.notify(
|
|
292
|
-
formatListing(model, runtime.scope(), { noun: SKILL_NOUN, emptyLabel: "No skills found." }),
|
|
293
|
-
"info",
|
|
294
|
-
);
|
|
441
|
+
skillsText(run, runtime, discovery, loadSkills, resolvedSkillPaths);
|
|
295
442
|
return;
|
|
296
443
|
}
|
|
297
444
|
|
|
@@ -299,77 +446,20 @@ export function registerPanel(pi: ExtensionAPI, deps: PanelCommandDeps): void {
|
|
|
299
446
|
// the session cache and probes on its first Extensions view instead, which
|
|
300
447
|
// keeps `/bytefly` and `/bytefly settings` instant.
|
|
301
448
|
const cached = deps.doctor.peek();
|
|
302
|
-
let reports: ExtensionReport[] = cached ?? [];
|
|
303
|
-
|
|
304
449
|
if (ctx.mode !== "tui") {
|
|
305
|
-
|
|
306
|
-
reports = await deps.doctor.probeAll({ refresh });
|
|
307
|
-
} catch (error) {
|
|
308
|
-
ctx.ui.notify(`Could not probe extensions: ${error instanceof Error ? error.message : String(error)}`, "error");
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
|
-
if (name !== undefined) {
|
|
312
|
-
notifyExtension(ctx, name, reports, runtime);
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
if (mode === "doctor") {
|
|
316
|
-
ctx.ui.notify(formatReport(reports), hasProblems(reports) ? "warning" : "info");
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
const model = buildPanelModel({ items: runtime.items("extensions"), reports, agentDir });
|
|
320
|
-
ctx.ui.notify(formatListing(model, runtime.scope()), "info");
|
|
450
|
+
await reportText(run, runtime);
|
|
321
451
|
return;
|
|
322
452
|
}
|
|
323
453
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
reports: () => reports,
|
|
334
|
-
settings,
|
|
335
|
-
filter: section === "extensions" ? name : undefined,
|
|
336
|
-
section,
|
|
337
|
-
theme,
|
|
338
|
-
agentDir,
|
|
339
|
-
cwd,
|
|
340
|
-
// A cached session probes only when the command asked for it.
|
|
341
|
-
needsProbe: cached === undefined || refresh,
|
|
342
|
-
initialRefresh: refresh,
|
|
343
|
-
viewportRows: () => tui.terminal?.rows,
|
|
344
|
-
requestRender: () => tui.requestRender(),
|
|
345
|
-
skills: () => loadSkills(resolvedSkillPaths(discovery), cwd, agentDir),
|
|
346
|
-
refresh: async (force) => {
|
|
347
|
-
reports = await deps.doctor.probeAll({ refresh: force === true });
|
|
348
|
-
},
|
|
349
|
-
reload: async () => {
|
|
350
|
-
// The reload takes the session, so the screen asks first and says
|
|
351
|
-
// what will change; a refusal leaves the dock exactly as it was.
|
|
352
|
-
const reload = await ctx.ui.confirm("Reload to apply changes?", "Changes take effect after a reload.");
|
|
353
|
-
if (!reload) return;
|
|
354
|
-
await ctx.reload();
|
|
355
|
-
},
|
|
356
|
-
onClose: () => {
|
|
357
|
-
// A late error falls back to the transcript, never a disposed screen.
|
|
358
|
-
panel = undefined;
|
|
359
|
-
unsubscribe();
|
|
360
|
-
unsubscribeSettings();
|
|
361
|
-
done();
|
|
362
|
-
},
|
|
363
|
-
});
|
|
364
|
-
panel = component;
|
|
365
|
-
component.prime();
|
|
366
|
-
return component;
|
|
367
|
-
}),
|
|
368
|
-
);
|
|
369
|
-
|
|
370
|
-
if (!runtime.isDirty() && !settings.isDirty()) return;
|
|
371
|
-
const reload = await ctx.ui.confirm("Reload to apply changes?", "Changes take effect after a reload.");
|
|
372
|
-
if (reload) await ctx.reload();
|
|
454
|
+
await mountPanel(run, {
|
|
455
|
+
runtime,
|
|
456
|
+
settings,
|
|
457
|
+
discovery,
|
|
458
|
+
loadSkills,
|
|
459
|
+
resolveSkillPaths: resolvedSkillPaths,
|
|
460
|
+
cached,
|
|
461
|
+
holder,
|
|
462
|
+
});
|
|
373
463
|
};
|
|
374
464
|
|
|
375
465
|
pi.registerCommand("bytefly", {
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
* from a probe or a settings file is sanitized before it is printed.
|
|
10
10
|
*
|
|
11
11
|
* A row has two enablement axes: the picker's settings (`◼`/`◻`) and this
|
|
12
|
-
* session's
|
|
13
|
-
*
|
|
14
|
-
* contradict each other.
|
|
12
|
+
* session's environment — the disable list, and the opt-in list a default-off
|
|
13
|
+
* extension needs. `rowBadge` is the one place that decides how they read, so
|
|
14
|
+
* the screen, the text listing, and the counts cannot contradict each other.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { sanitizeLine } from "../../../lib/format.ts";
|
|
@@ -182,7 +182,7 @@ interface RowBadge {
|
|
|
182
182
|
/**
|
|
183
183
|
* The row's badge. Settings is the enablement axis the checkbox already shows,
|
|
184
184
|
* so an off row gets one verdict (`off`) instead of a health word that
|
|
185
|
-
* contradicts it.
|
|
185
|
+
* contradicts it. The environment switch is a different axis, so it is named
|
|
186
186
|
* rather than sharing the word: `env disabled`. A skill has no probed report, so
|
|
187
187
|
* its own file supplies the verdict: `not loaded` when Pi refused it, `hidden`
|
|
188
188
|
* when it is reachable only through `/skill:<name>`. The glyph marks what needs
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
* The catalogue is the shortlist a session changes, not the whole document: every
|
|
10
10
|
* row is one a workflow differs on, and each states its own rule so a value the
|
|
11
11
|
* screen accepts is a value the loader keeps. The catalogue order is the screen's
|
|
12
|
-
* order, and it is capped
|
|
13
|
-
*
|
|
12
|
+
* order, and it is capped — fifteen rows and their five headings want a 28-row
|
|
13
|
+
* terminal, and a shorter one scrolls the list rather than the frame
|
|
14
|
+
* (`settings.test.ts` holds the ceiling); a new row has to displace one. Everything else stays
|
|
14
15
|
* `bytefly.json` — a list (`doctor.skip`), a table (`price.rates`), a schedule
|
|
15
16
|
* (`price.peak`), and the tuning numbers and labels this screen deliberately does
|
|
16
17
|
* not carry — documented in `docs/configuration.md` and reachable from the JSON
|
|
@@ -25,6 +26,9 @@
|
|
|
25
26
|
*/
|
|
26
27
|
|
|
27
28
|
import { configSection, configValueAt } from "../../../lib/config.ts";
|
|
29
|
+
import { DEFAULT_BOARD_CONFIG, normalizeBoardConfig } from "../../../lib/board/config.ts";
|
|
30
|
+
import { MAX_LABEL } from "../../../lib/session-label.ts";
|
|
31
|
+
import { MAX_WIP } from "../../../lib/board/limits.ts";
|
|
28
32
|
import { DEFAULT_CONFIG as DEFAULT_DOCTOR_CONFIG, normalizeConfig as normalizeDoctorConfig } from "../doctor/config.ts";
|
|
29
33
|
import {
|
|
30
34
|
CURRENCY_DEFAULTS,
|
|
@@ -53,7 +57,7 @@ type SettingOrigin = "default" | "global" | "project" | "inherited";
|
|
|
53
57
|
* rows read as four short lists: what a session starts with, what it tells you
|
|
54
58
|
* about, what it keeps, and what it costs.
|
|
55
59
|
*/
|
|
56
|
-
export type SettingGroup = "Startup" | "Notifications" | "Session" | "Price";
|
|
60
|
+
export type SettingGroup = "Startup" | "Notifications" | "Session" | "Board" | "Price";
|
|
57
61
|
|
|
58
62
|
/** One curated value a row can take. */
|
|
59
63
|
interface SettingOption {
|
|
@@ -70,7 +74,7 @@ interface TextRule {
|
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
/** The `bytefly.json` sections a row still reaches. */
|
|
73
|
-
export type ConfigSectionName = "notify" | "doctor" | "price" | "rewind" | "job";
|
|
77
|
+
export type ConfigSectionName = "notify" | "doctor" | "price" | "rewind" | "job" | "board";
|
|
74
78
|
|
|
75
79
|
/** Where a `bytefly.json` row lives: a section, and the path to its leaf. */
|
|
76
80
|
interface SettingConfig {
|
|
@@ -153,12 +157,29 @@ function amountRule(min: number, max: number): TextRule {
|
|
|
153
157
|
};
|
|
154
158
|
}
|
|
155
159
|
|
|
160
|
+
/**
|
|
161
|
+
* A text rule for a whole number inside an inclusive range: the board's two WIP limits, at the
|
|
162
|
+
* `1..MAX_WIP` the loader clamps to, so the screen cannot save a number the board would shorten.
|
|
163
|
+
*/
|
|
164
|
+
function countRule(min: number, max: number): TextRule {
|
|
165
|
+
return {
|
|
166
|
+
maxLength: 2,
|
|
167
|
+
validate(raw) {
|
|
168
|
+
const text = raw.trim();
|
|
169
|
+
if (!/^\d+$/.test(text)) return `Use a whole number, ${min} to ${max}.`;
|
|
170
|
+
const value = Number(text);
|
|
171
|
+
if (value < min || value > max) return `Out of range: ${min} to ${max}.`;
|
|
172
|
+
return undefined;
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
156
177
|
/**
|
|
157
178
|
* The catalogue, in screen order.
|
|
158
179
|
*
|
|
159
|
-
* Global scope lists all
|
|
180
|
+
* Global scope lists all fifteen; Project lists the thirteen `bytefly.json` rows,
|
|
160
181
|
* because Pi's setters write the personal `settings.json` only. The list is
|
|
161
|
-
* capped
|
|
182
|
+
* capped, and the cap with its reason is stated where the screen's height is.
|
|
162
183
|
*/
|
|
163
184
|
export const SETTING_ROWS: readonly SettingDescriptor[] = [
|
|
164
185
|
{
|
|
@@ -281,6 +302,50 @@ export const SETTING_ROWS: readonly SettingDescriptor[] = [
|
|
|
281
302
|
options: ON_OFF,
|
|
282
303
|
config: { section: "doctor", path: ["proactive"] },
|
|
283
304
|
},
|
|
305
|
+
{
|
|
306
|
+
id: "board.wip.plan",
|
|
307
|
+
group: "Board",
|
|
308
|
+
label: "Plan limit",
|
|
309
|
+
description: "How many cards the board lets a repository plan at once, 1 to 20; `bytefly.json` `board.wip.plan`.",
|
|
310
|
+
source: "bytefly",
|
|
311
|
+
kind: "text",
|
|
312
|
+
fallback: String(DEFAULT_BOARD_CONFIG.wip.plan),
|
|
313
|
+
json: "number",
|
|
314
|
+
options: [],
|
|
315
|
+
text: countRule(1, MAX_WIP),
|
|
316
|
+
config: { section: "board", path: ["wip", "plan"] },
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
id: "board.wip.execute",
|
|
320
|
+
group: "Board",
|
|
321
|
+
label: "Execute limit",
|
|
322
|
+
description:
|
|
323
|
+
"How many cards the board lets a repository execute at once, 1 to 20; `bytefly.json` `board.wip.execute`.",
|
|
324
|
+
source: "bytefly",
|
|
325
|
+
kind: "text",
|
|
326
|
+
fallback: String(DEFAULT_BOARD_CONFIG.wip.execute),
|
|
327
|
+
json: "number",
|
|
328
|
+
options: [],
|
|
329
|
+
text: countRule(1, MAX_WIP),
|
|
330
|
+
config: { section: "board", path: ["wip", "execute"] },
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
id: "board.lead",
|
|
334
|
+
group: "Board",
|
|
335
|
+
label: "Lead",
|
|
336
|
+
description:
|
|
337
|
+
"The team name whose signature approves a contract and spot-checks the work; a card's holder never signs.",
|
|
338
|
+
source: "bytefly",
|
|
339
|
+
kind: "text",
|
|
340
|
+
fallback: DEFAULT_BOARD_CONFIG.lead,
|
|
341
|
+
options: [],
|
|
342
|
+
text: {
|
|
343
|
+
maxLength: MAX_LABEL,
|
|
344
|
+
validate: (raw) =>
|
|
345
|
+
raw.trim() === "" ? "A lead needs a name; `u` clears the key and the default comes back." : undefined,
|
|
346
|
+
},
|
|
347
|
+
config: { section: "board", path: ["lead"] },
|
|
348
|
+
},
|
|
284
349
|
{
|
|
285
350
|
id: "price.currency.code",
|
|
286
351
|
group: "Price",
|
|
@@ -432,6 +497,8 @@ const SECTION_READERS: Partial<Record<ConfigSectionName, SectionReader>> = {
|
|
|
432
497
|
configValueAt(normalizeDoctorConfig(projectRaw, normalizeDoctorConfig(globalRaw, DEFAULT_DOCTOR_CONFIG)), path),
|
|
433
498
|
price: (globalRaw, projectRaw, path) =>
|
|
434
499
|
configValueAt(normalizeByteflyConfig(projectRaw, normalizeByteflyConfig(globalRaw, DEFAULT_BYTEFLY_CONFIG)), path),
|
|
500
|
+
board: (globalRaw, projectRaw, path) =>
|
|
501
|
+
configValueAt(normalizeBoardConfig(projectRaw, normalizeBoardConfig(globalRaw, DEFAULT_BOARD_CONFIG)), path),
|
|
435
502
|
};
|
|
436
503
|
|
|
437
504
|
/**
|
|
@@ -16,7 +16,7 @@ import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
|
16
16
|
import { Key, matchesKey } from "@earendil-works/pi-tui";
|
|
17
17
|
import type { ScreenHint } from "../../../../lib/tui/chrome.ts";
|
|
18
18
|
import { clip, padTo } from "../../../../lib/tui/columns.ts";
|
|
19
|
-
import { isCloseKey } from "../../../../lib/tui/cursor.ts";
|
|
19
|
+
import { isCloseKey, isShiftTab } from "../../../../lib/tui/cursor.ts";
|
|
20
20
|
import { BODY_INDENT } from "../../../../lib/tui/indent.ts";
|
|
21
21
|
import { paint } from "../../../../lib/tui/tokens.ts";
|
|
22
22
|
import type { PanelSection } from "../types.ts";
|
|
@@ -58,9 +58,6 @@ export interface PanelKey {
|
|
|
58
58
|
const ALL: readonly PanelSection[] = ["extensions", "skills", "settings"];
|
|
59
59
|
const LISTS: readonly PanelSection[] = ["extensions", "skills"];
|
|
60
60
|
|
|
61
|
-
/** Shift+Tab arrives as this sequence on terminals without the Kitty protocol. */
|
|
62
|
-
const SHIFT_TAB = "\u001b[Z";
|
|
63
|
-
|
|
64
61
|
/** The screen's keys, in help order. */
|
|
65
62
|
export const PANEL_KEYS: readonly PanelKey[] = [
|
|
66
63
|
{
|
|
@@ -75,8 +72,7 @@ export const PANEL_KEYS: readonly PanelKey[] = [
|
|
|
75
72
|
label: "←",
|
|
76
73
|
sections: ALL,
|
|
77
74
|
help: "Previous section",
|
|
78
|
-
match: (data) =>
|
|
79
|
-
matchesKey(data, Key.left) || data === "h" || matchesKey(data, Key.shift("tab")) || data === SHIFT_TAB,
|
|
75
|
+
match: (data) => matchesKey(data, Key.left) || data === "h" || isShiftTab(data),
|
|
80
76
|
},
|
|
81
77
|
{
|
|
82
78
|
id: "nextSection",
|
|
@@ -21,7 +21,7 @@ import { writeConfigFile } from "../../../lib/config.ts";
|
|
|
21
21
|
import type { PackageUpdate } from "./types.ts";
|
|
22
22
|
import { parseVersion } from "./version.ts";
|
|
23
23
|
|
|
24
|
-
/** The file name under `<
|
|
24
|
+
/** The file name under `<agent-dir>/bytefly/`. */
|
|
25
25
|
export const UPDATE_CACHE_FILE = "update.json";
|
|
26
26
|
|
|
27
27
|
/** How long a cached snapshot is reused before the next session re-reads. */
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
import { applyUpdates, fetchAvailableUpdates, fetchLatestVersion } from "./check.ts";
|
|
31
31
|
import { isOwnPackage, updateNotice } from "./format.ts";
|
|
32
32
|
import { paintLatest, type UpdateOutcome, type UpdateSource } from "./state.ts";
|
|
33
|
-
import {
|
|
33
|
+
import type { UpdateSuppression } from "./suppress.ts";
|
|
34
34
|
import type { PackageUpdate, PackageUpdateCheck } from "./types.ts";
|
|
35
35
|
import { isNewerVersion } from "./version.ts";
|
|
36
36
|
|
|
@@ -112,7 +112,7 @@ export function registerUpdate(pi: ExtensionAPI, updates: UpdateSource, deps: Up
|
|
|
112
112
|
const offline = deps.isOffline ?? isOffline;
|
|
113
113
|
const cache = deps.cache ?? diskCache();
|
|
114
114
|
const now = deps.now ?? Date.now;
|
|
115
|
-
const suppress = deps.suppress ?? suppressPiPackageUpdates;
|
|
115
|
+
const suppress = deps.suppress ?? (async () => (await import("./suppress.ts")).suppressPiPackageUpdates());
|
|
116
116
|
// A session is announced at most once per load, even if `session_start` fires
|
|
117
117
|
// again after a resume or a session replacement.
|
|
118
118
|
let announced = false;
|
|
@@ -203,12 +203,12 @@ export function registerUpdate(pi: ExtensionAPI, updates: UpdateSource, deps: Up
|
|
|
203
203
|
}
|
|
204
204
|
};
|
|
205
205
|
|
|
206
|
-
pi.on("session_start", (_event, ctx) => {
|
|
206
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
207
207
|
// `/bytefly update` can answer in any mode; only the TUI installs the host
|
|
208
208
|
// shim and posts a notice.
|
|
209
209
|
context = ctx;
|
|
210
210
|
if (ctx.mode !== "tui") return;
|
|
211
|
-
suppression ??= suppress();
|
|
211
|
+
suppression ??= await suppress();
|
|
212
212
|
const cached = cache.read();
|
|
213
213
|
const key = keyFor(ctx);
|
|
214
214
|
if (offline()) {
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tuning for bytefly's live working line.
|
|
3
3
|
*
|
|
4
|
-
* While Pi's working
|
|
5
|
-
* spinner. Edit this file to change the
|
|
4
|
+
* While the composer's top border carries Pi's working indicator, the message is
|
|
5
|
+
* written into the border beside Pi's spinner. Edit this file to change the word
|
|
6
|
+
* or how often the count advances.
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
9
|
export const WORKING_CONFIG = {
|
|
9
|
-
/**
|
|
10
|
-
working: "
|
|
11
|
-
/** Between the word and the count. */
|
|
12
|
-
ellipsis: "…",
|
|
10
|
+
/** The word the line leads with, after Pi's spinner. */
|
|
11
|
+
working: "working",
|
|
13
12
|
/** How often the count advances, in milliseconds. */
|
|
14
13
|
intervalMs: 1000,
|
|
15
14
|
} as const satisfies {
|
|
16
15
|
working: string;
|
|
17
|
-
ellipsis: string;
|
|
18
16
|
intervalMs: number;
|
|
19
17
|
};
|