@gavin-hu/my-pi-agent 0.13.1 → 0.14.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 +14 -11
- package/README.zh-CN.md +12 -12
- package/extensions/bytefly/README.md +140 -58
- package/extensions/bytefly/doctor/config.ts +4 -1
- package/extensions/bytefly/footer/price/config.ts +3 -2
- package/extensions/bytefly/header/component.ts +30 -8
- package/extensions/bytefly/header/config.ts +22 -0
- package/extensions/bytefly/header/register.ts +2 -1
- package/extensions/bytefly/index.ts +3 -3
- package/extensions/bytefly/notify/config.ts +1 -1
- package/extensions/bytefly/panel/commands.ts +1 -1
- package/extensions/bytefly/panel/format.ts +12 -19
- package/extensions/bytefly/panel/settings-runtime.ts +62 -18
- package/extensions/bytefly/panel/settings.ts +239 -43
- package/extensions/bytefly/panel/tui/settings-view.ts +49 -3
- package/extensions/bytefly/panel/tui.ts +87 -19
- package/extensions/bytefly/separator/component.ts +7 -6
- package/extensions/bytefly/separator/config.ts +15 -0
- package/extensions/bytefly/separator/format.ts +60 -6
- package/extensions/bytefly/separator/register.ts +68 -9
- package/extensions/bytefly/separator/types.ts +8 -0
- package/extensions/bytefly/working/config.ts +4 -5
- package/extensions/bytefly/working/format.ts +24 -7
- package/extensions/bytefly/working/register.ts +34 -3
- package/extensions/memory/README.md +2 -0
- package/extensions/plan/README.md +12 -0
- package/extensions/subagent/README.md +60 -5
- package/extensions/subagent/config.ts +58 -0
- package/extensions/subagent/index.ts +27 -1
- package/extensions/subagent/orchestrate.ts +16 -3
- package/extensions/subagent/render.ts +12 -0
- package/extensions/subagent/run.ts +100 -37
- package/extensions/subagent/transcripts.ts +202 -0
- package/extensions/subagent/types.ts +12 -0
- package/extensions/watch/README.md +2 -0
- package/extensions/wechat/README.md +2 -0
- package/lib/README.md +1 -1
- package/lib/config.ts +33 -9
- package/lib/format.ts +6 -0
- package/package.json +2 -1
- package/themes/bytefly-dark.json +2 -3
- package/themes/bytefly-light.json +2 -3
|
@@ -18,9 +18,9 @@ import { BODY_INDENT } from "../../../lib/tui/indent.ts";
|
|
|
18
18
|
import { RULE_GLYPH } from "../../../lib/tui/rules.ts";
|
|
19
19
|
import { paint } from "../../../lib/tui/tokens.ts";
|
|
20
20
|
import { updateMarker } from "../update/format.ts";
|
|
21
|
-
import { HEADER_CONFIG, type HelpEntry } from "./config.ts";
|
|
21
|
+
import { HEADER_CONFIG, type HeaderHeight, type HelpEntry } from "./config.ts";
|
|
22
22
|
|
|
23
|
-
/** The header's shape
|
|
23
|
+
/** The header's shape: everything but the live terminal height is resolved once per apply. */
|
|
24
24
|
export interface HeaderView {
|
|
25
25
|
/** Print Pi's version on the agent line. */
|
|
26
26
|
showVersion: boolean;
|
|
@@ -28,6 +28,8 @@ export interface HeaderView {
|
|
|
28
28
|
halfBlocks: boolean;
|
|
29
29
|
/** Columns of left margin: Pi's output pad, so the nameplate sits on the transcript margin. */
|
|
30
30
|
pad: number;
|
|
31
|
+
/** Terminal rows, when the host reports them; short terminals shed help rows. */
|
|
32
|
+
rows?: number;
|
|
31
33
|
/** A newer published version of this package, when one is known. */
|
|
32
34
|
latest?: string;
|
|
33
35
|
}
|
|
@@ -35,6 +37,15 @@ export interface HeaderView {
|
|
|
35
37
|
/** The mark block is four columns on both rows, so the identity column aligns. */
|
|
36
38
|
const MARK_WIDTH = 4;
|
|
37
39
|
|
|
40
|
+
/**
|
|
41
|
+
* The tallest shape whose threshold the terminal meets. An unknown or impossible
|
|
42
|
+
* height keeps the full header, so a host without terminal rows still reads well.
|
|
43
|
+
*/
|
|
44
|
+
function heightShape(rows: number | undefined): HeaderHeight {
|
|
45
|
+
if (rows === undefined || !Number.isFinite(rows) || rows <= 0) return HEADER_CONFIG.heights[0];
|
|
46
|
+
return HEADER_CONFIG.heights.find((shape) => rows >= shape.minRows) ?? HEADER_CONFIG.heights[0];
|
|
47
|
+
}
|
|
48
|
+
|
|
38
49
|
/** Pad a themed mark line to the block width with plain spaces. */
|
|
39
50
|
function markBlock(line: string): string {
|
|
40
51
|
return line + " ".repeat(Math.max(0, MARK_WIDTH - visibleWidth(line)));
|
|
@@ -81,7 +92,7 @@ export class ByteflyHeader implements Component {
|
|
|
81
92
|
const w = Math.max(1, width);
|
|
82
93
|
const theme = this.getTheme();
|
|
83
94
|
const view = this.view();
|
|
84
|
-
const viewKey = `${view.showVersion}:${view.halfBlocks}:${view.pad}:${view.latest ?? ""}`;
|
|
95
|
+
const viewKey = `${view.showVersion}:${view.halfBlocks}:${view.pad}:${view.latest ?? ""}:${view.rows ?? ""}`;
|
|
85
96
|
const keys = this.keySignature();
|
|
86
97
|
if (
|
|
87
98
|
this.cachedWidth === w &&
|
|
@@ -111,6 +122,7 @@ export class ByteflyHeader implements Component {
|
|
|
111
122
|
private build(w: number, theme: Theme, view: HeaderView): string[] {
|
|
112
123
|
const { nameRole, chromeRole, detailRole, updateRole, chromeTagline, chromeName, chromeVersion, help } =
|
|
113
124
|
HEADER_CONFIG;
|
|
125
|
+
const shape = heightShape(view.rows);
|
|
114
126
|
const padWidth = Math.max(0, Math.min(Math.floor(view.pad), Math.max(0, w - 1)));
|
|
115
127
|
const pad = " ".repeat(padWidth);
|
|
116
128
|
const [markTop, markBottom] = markLines(theme, view.halfBlocks);
|
|
@@ -145,13 +157,17 @@ export class ByteflyHeader implements Component {
|
|
|
145
157
|
|
|
146
158
|
// The header owns its leading blank: Pi mounts the built-in header inside
|
|
147
159
|
// `Spacer(1)`s, but the `quietStartup: true` branch that hands this header the
|
|
148
|
-
// screen adds none, and `setHeader` swaps only the header child.
|
|
160
|
+
// screen adds none, and `setHeader` swaps only the header child. A short
|
|
161
|
+
// terminal gives that row back, then sheds whole help groups.
|
|
149
162
|
return [
|
|
150
|
-
"",
|
|
163
|
+
...(shape.blank ? [""] : []),
|
|
151
164
|
this.identityLine(pad, markTop, agentCell, w),
|
|
152
165
|
this.identityLine(pad, markBottom, chromeCell, w),
|
|
153
166
|
this.rule(theme, w, pad),
|
|
154
|
-
...
|
|
167
|
+
...shape.helpRows.flatMap((index) => {
|
|
168
|
+
const group = help[index];
|
|
169
|
+
return group ? [this.helpLine(theme, w, group, padWidth)] : [];
|
|
170
|
+
}),
|
|
155
171
|
];
|
|
156
172
|
}
|
|
157
173
|
|
|
@@ -193,6 +209,12 @@ export class ByteflyHeader implements Component {
|
|
|
193
209
|
}
|
|
194
210
|
|
|
195
211
|
/** Header factory passed to `ctx.ui.setHeader`. */
|
|
196
|
-
export function createHeader(
|
|
197
|
-
|
|
212
|
+
export function createHeader(
|
|
213
|
+
getTheme: () => Theme,
|
|
214
|
+
view: (rows: number | undefined) => HeaderView,
|
|
215
|
+
): (tui: TUI, theme: Theme) => Component {
|
|
216
|
+
// Read the height on every render, so a terminal resize reflows the header
|
|
217
|
+
// instead of keeping the shape it was installed with. Tests may pass a bare
|
|
218
|
+
// object as the host, so the terminal is optional at the boundary.
|
|
219
|
+
return (tui) => new ByteflyHeader(getTheme, () => view(tui.terminal?.rows), VERSION);
|
|
198
220
|
}
|
|
@@ -19,6 +19,16 @@ export type HelpEntry =
|
|
|
19
19
|
| { readonly binding: AppKeybinding; readonly description: string }
|
|
20
20
|
| { readonly raw: string; readonly description: string };
|
|
21
21
|
|
|
22
|
+
/** How much of the help block a terminal height can carry. */
|
|
23
|
+
export interface HeaderHeight {
|
|
24
|
+
/** Smallest terminal height, in rows, this shape applies to. Taller tiers come first. */
|
|
25
|
+
readonly minRows: number;
|
|
26
|
+
/** Help groups to print, by index in `help`; a group is dropped whole. */
|
|
27
|
+
readonly helpRows: readonly number[];
|
|
28
|
+
/** Open with a blank row above the nameplate. */
|
|
29
|
+
readonly blank: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
22
32
|
/** The header's shape and content. */
|
|
23
33
|
export interface HeaderConfig {
|
|
24
34
|
/** The agent's name, shown before its version. */
|
|
@@ -49,6 +59,12 @@ export interface HeaderConfig {
|
|
|
49
59
|
readonly chromeVersion: string;
|
|
50
60
|
/** Grouped help rows, printed one line each under the rule. */
|
|
51
61
|
readonly help: readonly (readonly HelpEntry[])[];
|
|
62
|
+
/**
|
|
63
|
+
* The height ladder, tallest first: the first tier the terminal meets decides
|
|
64
|
+
* which help groups print and whether the leading blank row survives. The
|
|
65
|
+
* floor is `minRows: 0`, so a short terminal keeps the nameplate and rule.
|
|
66
|
+
*/
|
|
67
|
+
readonly heights: readonly HeaderHeight[];
|
|
52
68
|
}
|
|
53
69
|
|
|
54
70
|
export const HEADER_CONFIG: HeaderConfig = {
|
|
@@ -89,4 +105,10 @@ export const HEADER_CONFIG: HeaderConfig = {
|
|
|
89
105
|
{ binding: "app.clipboard.pasteImage", description: "paste files" },
|
|
90
106
|
],
|
|
91
107
|
],
|
|
108
|
+
heights: [
|
|
109
|
+
{ minRows: 28, helpRows: [0, 1, 2], blank: true },
|
|
110
|
+
{ minRows: 22, helpRows: [0, 2], blank: true },
|
|
111
|
+
{ minRows: 16, helpRows: [2], blank: true },
|
|
112
|
+
{ minRows: 0, helpRows: [], blank: false },
|
|
113
|
+
],
|
|
92
114
|
};
|
|
@@ -55,11 +55,12 @@ export function registerHeader(pi: ExtensionAPI, updates: UpdateSource = createU
|
|
|
55
55
|
// value the tool frame and the notice inset by. An older host without
|
|
56
56
|
// `getSettings` keeps the default margin.
|
|
57
57
|
const pad = pi.getSettings?.().outputPad ?? DEFAULT_HOST_PAD;
|
|
58
|
-
const view = (): HeaderView => ({
|
|
58
|
+
const view = (rows: number | undefined): HeaderView => ({
|
|
59
59
|
showVersion: HEADER_CONFIG.showVersion,
|
|
60
60
|
halfBlocks,
|
|
61
61
|
pad,
|
|
62
62
|
latest: updates.latest(),
|
|
63
|
+
rows,
|
|
63
64
|
});
|
|
64
65
|
ctx.ui.setHeader(createHeader(() => ctx.ui.theme, view));
|
|
65
66
|
};
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
* (the two-line, width-adaptive status bar with its price calculation),
|
|
5
5
|
* `message/` (the user-message nameplate), `notice/` (the shared transcript
|
|
6
6
|
* notice renderer), `block/` (the unified tool-block frame), `separator/` (the
|
|
7
|
-
* transcript turn rule), `working/` (the live
|
|
8
|
-
*
|
|
7
|
+
* transcript turn rule), `working/` (the live run line on Pi's working row),
|
|
8
|
+
* `panel/` (the unified `/bytefly` screen and its
|
|
9
9
|
* `doctor` report), `doctor/` (the `run_doctor` tool and its session-scoped
|
|
10
10
|
* probe runtime), `notify/` (OS notifications for unattended runs), and
|
|
11
11
|
* `update/` (the published-version check behind the header's update marker).
|
|
12
12
|
*
|
|
13
|
-
* The header, footer, turn rule, and working
|
|
13
|
+
* The header, footer, turn rule, and working line are installed only in
|
|
14
14
|
* interactive (`tui`) sessions, and are always on; the user-message marker,
|
|
15
15
|
* notice, and tool blocks register at load and take effect wherever a
|
|
16
16
|
* transcript renders. `pi config` turns bytefly's chrome off. The panel and doctor work
|
|
@@ -18,7 +18,7 @@ import { cleanString, loadConfigSection } from "../../../lib/config.ts";
|
|
|
18
18
|
import { NOTIFY_METHODS, type NotifyConfig, type NotifyMethod } from "./types.ts";
|
|
19
19
|
|
|
20
20
|
/** A notification title longer than this is clamped; it is a label, not a message. */
|
|
21
|
-
const MAX_TITLE_LENGTH = 40;
|
|
21
|
+
export const MAX_TITLE_LENGTH = 40;
|
|
22
22
|
|
|
23
23
|
export const DEFAULT_NOTIFY_CONFIG: NotifyConfig = {
|
|
24
24
|
enabled: false,
|
|
@@ -172,7 +172,7 @@ export function registerPanel(pi: ExtensionAPI, deps: PanelCommandDeps): void {
|
|
|
172
172
|
if (section === "settings" && ctx.mode !== "tui") {
|
|
173
173
|
const stores = createSettingsStores(cwd, agentDir, trusted);
|
|
174
174
|
const settings = makeSettings({ stores, cwd, agentDir, trusted, onError: settingsNotice });
|
|
175
|
-
ctx.ui.notify(formatSettingsListing(settings.views("global")
|
|
175
|
+
ctx.ui.notify(formatSettingsListing(settings.views("global")), "info");
|
|
176
176
|
return;
|
|
177
177
|
}
|
|
178
178
|
|
|
@@ -31,7 +31,7 @@ export interface SummarySegment {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/** Row and problem counts over a model. */
|
|
34
|
-
|
|
34
|
+
interface PanelCounts {
|
|
35
35
|
extensions: number;
|
|
36
36
|
/** Rows switched off in settings (the picker's checkbox), not the env flag. */
|
|
37
37
|
off: number;
|
|
@@ -113,28 +113,21 @@ export function sectionSegments(active: PanelSection): SummarySegment[] {
|
|
|
113
113
|
export function settingsSummarySegments(scope: PanelScope, count: number): SummarySegment[] {
|
|
114
114
|
return [
|
|
115
115
|
{ text: scope === "global" ? "Global" : "Project", color: "accent" },
|
|
116
|
-
{ text: `${count}
|
|
116
|
+
{ text: `${count} settings`, color: "meta" },
|
|
117
117
|
];
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
/** The Settings headline for non-TUI modes, e.g. `bytefly: Settings · Global · 5 settings`. */
|
|
121
|
-
export function formatSettingsHeadline(scope: PanelScope, count: number): string {
|
|
122
|
-
return `bytefly: Settings · ${settingsSummarySegments(scope, count)
|
|
123
|
-
.map((segment) => segment.text)
|
|
124
|
-
.join(" · ")}`;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
120
|
/**
|
|
128
|
-
* The
|
|
129
|
-
*
|
|
121
|
+
* The Settings headline for non-TUI modes, e.g. `bytefly: Settings · Global · 16 settings`.
|
|
122
|
+
* The listing only ever reads the personal document, so the scope is not a parameter.
|
|
130
123
|
*/
|
|
131
|
-
export function
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
124
|
+
export function formatSettingsHeadline(count: number): string {
|
|
125
|
+
return `bytefly: Settings · Global · ${count} settings`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** The grouped Settings listing for non-TUI modes. */
|
|
129
|
+
export function formatSettingsListing(views: readonly SettingView[]): string {
|
|
130
|
+
const lines = [formatSettingsHeadline(views.length)];
|
|
138
131
|
for (const group of groupViews(views)) {
|
|
139
132
|
lines.push("", `${group.label} (${group.views.length})`);
|
|
140
133
|
for (const view of group.views) {
|
|
@@ -152,7 +145,7 @@ export function formatHeadline(model: PanelModel, scope: PanelScope, options: Pa
|
|
|
152
145
|
}
|
|
153
146
|
|
|
154
147
|
/** A row's glyph and verdict, drawn from its enablement and health axes. */
|
|
155
|
-
|
|
148
|
+
interface RowBadge {
|
|
156
149
|
/** The status glyph, or `undefined` when the row has nothing to say. */
|
|
157
150
|
glyph?: string;
|
|
158
151
|
/** The role the glyph is painted in. */
|
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The stateful Settings section: one snapshot of the two config documents and
|
|
3
|
-
* the personal settings, the dirty flag, and the
|
|
3
|
+
* the personal settings, the dirty flag, and the writes it offers.
|
|
4
4
|
*
|
|
5
5
|
* App rows are written through Pi's `SettingsStore` (personal file only), and
|
|
6
|
-
* `bytefly.json` rows through the per-key
|
|
7
|
-
* sibling key or another extension's section. A
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* `bytefly.json` rows through the per-key writers, so an edit cannot disturb a
|
|
7
|
+
* sibling key or another extension's section. A reset deletes one leaf through the
|
|
8
|
+
* same path, and the JSON pane reads the section the row belongs to. A failed
|
|
9
|
+
* write leaves the snapshot untouched and reports through `onError`; the screen
|
|
10
|
+
* never shows a value the file did not accept.
|
|
10
11
|
*/
|
|
11
12
|
|
|
12
13
|
import { join } from "node:path";
|
|
13
14
|
import { CONFIG_DIR_NAME, type QuietStartup } from "@earendil-works/pi-coding-agent";
|
|
14
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
configSection,
|
|
17
|
+
configValueAt,
|
|
18
|
+
readJson,
|
|
19
|
+
SHARED_CONFIG_FILE,
|
|
20
|
+
unsetConfigValueAt,
|
|
21
|
+
writeConfigValueAt,
|
|
22
|
+
} from "../../../lib/config.ts";
|
|
15
23
|
import type { SettingsStore, WriteScope } from "../picker/types.ts";
|
|
16
24
|
import {
|
|
17
|
-
|
|
25
|
+
type ConfigSectionName,
|
|
18
26
|
nextValue,
|
|
19
27
|
type SettingDescriptor,
|
|
20
28
|
type SettingsSnapshot,
|
|
@@ -23,7 +31,6 @@ import {
|
|
|
23
31
|
settingViews,
|
|
24
32
|
storedValue,
|
|
25
33
|
textError,
|
|
26
|
-
validValue,
|
|
27
34
|
} from "./settings.ts";
|
|
28
35
|
|
|
29
36
|
/** Host seams for the Settings runtime. */
|
|
@@ -40,11 +47,14 @@ export interface SettingsRuntimeDeps {
|
|
|
40
47
|
/** What the panel needs from the runtime. */
|
|
41
48
|
export interface SettingsSurface {
|
|
42
49
|
views(scope: WriteScope): SettingView[];
|
|
43
|
-
count(scope: WriteScope): number;
|
|
44
50
|
/** Cycle an enum or toggle a boolean. */
|
|
45
51
|
change(scope: WriteScope, id: string): Promise<void>;
|
|
46
52
|
/** Store a value the user typed; `false` when the row refused it. */
|
|
47
53
|
setValue(scope: WriteScope, id: string, raw: string): Promise<boolean>;
|
|
54
|
+
/** Delete the focused row's leaf so its default applies; `false` when nothing changed. */
|
|
55
|
+
reset(scope: WriteScope, id: string): Promise<boolean>;
|
|
56
|
+
/** The raw `bytefly.json` section for `scope`, for the JSON pane. */
|
|
57
|
+
sectionDocument(scope: WriteScope, section: ConfigSectionName): unknown;
|
|
48
58
|
isDirty(): boolean;
|
|
49
59
|
}
|
|
50
60
|
|
|
@@ -62,8 +72,8 @@ export function configPath(scope: WriteScope, cwd: string, agentDir: string): st
|
|
|
62
72
|
function readSnapshot(deps: SettingsRuntimeDeps): SettingsSnapshot {
|
|
63
73
|
return {
|
|
64
74
|
app: deps.stores.global.globalSettings(),
|
|
65
|
-
global: readJson(
|
|
66
|
-
project: deps.trusted ? (readJson(
|
|
75
|
+
global: readJson(configPath("global", deps.cwd, deps.agentDir)) ?? {},
|
|
76
|
+
project: deps.trusted ? (readJson(configPath("project", deps.cwd, deps.agentDir)) ?? {}) : {},
|
|
67
77
|
};
|
|
68
78
|
}
|
|
69
79
|
|
|
@@ -133,10 +143,15 @@ export function createSettingsRuntime(deps: SettingsRuntimeDeps): SettingsRuntim
|
|
|
133
143
|
|
|
134
144
|
return {
|
|
135
145
|
views: (scope) => settingViews(snapshot, scope),
|
|
136
|
-
count: (scope) => countSettings(scope),
|
|
137
146
|
change: async (scope, id) => {
|
|
138
147
|
const row = SETTING_ROWS.find((entry) => entry.id === id);
|
|
139
148
|
if (!row) return;
|
|
149
|
+
// Only a boolean or an enum cycles; a text row is stored from its editor,
|
|
150
|
+
// so refusing here keeps `nextValue`'s cycle contract honest.
|
|
151
|
+
if (row.kind === "text") {
|
|
152
|
+
deps.onError?.(`${row.label} is edited as text, not cycled.`);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
140
155
|
const refused = refusal(row, scope, deps.trusted);
|
|
141
156
|
if (refused !== undefined) {
|
|
142
157
|
deps.onError?.(refused);
|
|
@@ -145,10 +160,6 @@ export function createSettingsRuntime(deps: SettingsRuntimeDeps): SettingsRuntim
|
|
|
145
160
|
const current = settingViews(snapshot, scope).find((entry) => entry.id === id);
|
|
146
161
|
if (!current) return;
|
|
147
162
|
const next = nextValue(row, current.value);
|
|
148
|
-
if (!validValue(row, next.value)) {
|
|
149
|
-
deps.onError?.(`Cannot save ${row.label}: unknown value.`);
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
163
|
try {
|
|
153
164
|
await write(scope, row, next.value);
|
|
154
165
|
} catch (error) {
|
|
@@ -159,8 +170,8 @@ export function createSettingsRuntime(deps: SettingsRuntimeDeps): SettingsRuntim
|
|
|
159
170
|
},
|
|
160
171
|
async setValue(scope, id, raw) {
|
|
161
172
|
const row = SETTING_ROWS.find((entry) => entry.id === id);
|
|
162
|
-
if (!row
|
|
163
|
-
deps.onError?.(`${
|
|
173
|
+
if (!row) {
|
|
174
|
+
deps.onError?.(`${id} is not a setting.`);
|
|
164
175
|
return false;
|
|
165
176
|
}
|
|
166
177
|
const refused = refusal(row, scope, deps.trusted);
|
|
@@ -182,6 +193,39 @@ export function createSettingsRuntime(deps: SettingsRuntimeDeps): SettingsRuntim
|
|
|
182
193
|
committed();
|
|
183
194
|
return true;
|
|
184
195
|
},
|
|
196
|
+
async reset(scope, id) {
|
|
197
|
+
const row = SETTING_ROWS.find((entry) => entry.id === id);
|
|
198
|
+
if (!row) {
|
|
199
|
+
deps.onError?.(`${id} is not a setting.`);
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
if (row.source === "app") {
|
|
203
|
+
deps.onError?.(`${row.label} is a Pi setting; Pi has no unset, so set the value instead.`);
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
const refused = refusal(row, scope, deps.trusted);
|
|
207
|
+
if (refused !== undefined) {
|
|
208
|
+
deps.onError?.(refused);
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
const config = row.config;
|
|
212
|
+
if (!config) return false;
|
|
213
|
+
const document = scope === "global" ? snapshot.global : snapshot.project;
|
|
214
|
+
if (configValueAt(configSection(document, config.section), config.path) === undefined) {
|
|
215
|
+
deps.onError?.(`${row.label} is already at its default.`);
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
unsetConfigValueAt(configPath(scope, deps.cwd, deps.agentDir), config.section, config.path);
|
|
220
|
+
} catch (error) {
|
|
221
|
+
deps.onError?.(`Could not reset ${row.label}: ${error instanceof Error ? error.message : String(error)}`);
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
committed();
|
|
225
|
+
return true;
|
|
226
|
+
},
|
|
227
|
+
sectionDocument: (scope, section) =>
|
|
228
|
+
configSection(scope === "global" ? snapshot.global : snapshot.project, section),
|
|
185
229
|
isDirty: () => dirty,
|
|
186
230
|
onChange: (listener) => {
|
|
187
231
|
listeners.add(listener);
|