@gavin-hu/my-pi-agent 0.15.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -7
- package/README.zh-CN.md +8 -5
- package/extensions/bytefly/README.md +98 -116
- package/extensions/bytefly/notice/component.ts +6 -4
- package/extensions/bytefly/notice/config.ts +16 -2
- package/extensions/bytefly/panel/format.ts +3 -8
- package/extensions/bytefly/panel/join.ts +1 -6
- package/extensions/bytefly/panel/settings.ts +74 -377
- package/extensions/bytefly/panel/tui/entries.ts +16 -14
- package/extensions/bytefly/panel/tui/extensions-view.ts +3 -0
- package/extensions/bytefly/panel/tui/frame.ts +2 -2
- package/extensions/bytefly/panel/tui/settings-view.ts +8 -12
- package/extensions/bytefly/panel/tui.ts +4 -6
- package/extensions/bytefly/picker/resources.ts +7 -3
- package/extensions/bytefly/working/register.ts +2 -8
- package/extensions/check/tools.ts +2 -11
- package/extensions/context/README.md +162 -0
- package/extensions/context/collect.ts +53 -0
- package/extensions/context/commands.ts +100 -0
- package/extensions/context/format.ts +82 -0
- package/extensions/context/index.ts +16 -0
- package/extensions/context/report.ts +319 -0
- package/extensions/context/tui.ts +268 -0
- package/extensions/context/types.ts +122 -0
- package/extensions/file-browse/README.md +38 -18
- package/extensions/file-browse/config.ts +8 -0
- package/extensions/file-browse/html.ts +2 -2
- package/extensions/file-browse/index.ts +17 -2
- package/extensions/file-browse/live-client.ts +13 -13
- package/extensions/file-browse/live.ts +44 -10
- package/extensions/file-browse/markdown.ts +3 -10
- package/extensions/file-browse/raw-html.ts +3 -10
- package/extensions/file-browse/server.ts +13 -2
- package/extensions/file-open/README.md +1 -2
- package/extensions/{document → file-read}/README.md +72 -56
- package/extensions/{document → file-read}/config.ts +12 -9
- package/extensions/{document → file-read}/extract/doc.ts +2 -2
- package/extensions/{document → file-read}/extract/docx.ts +3 -3
- package/extensions/{document → file-read}/extract/file.ts +9 -9
- package/extensions/{document → file-read}/extract/odt.ts +3 -3
- package/extensions/{document → file-read}/extract/pdf.ts +3 -3
- package/extensions/{document → file-read}/extract/rtf.ts +3 -3
- package/extensions/{document → file-read}/extract/xlsx.ts +4 -4
- package/extensions/{document → file-read}/formats.ts +11 -11
- package/extensions/file-read/index.ts +17 -0
- package/extensions/{document → file-read}/paging.ts +15 -13
- package/extensions/{document → file-read}/render.ts +17 -13
- package/extensions/{document → file-read}/schema.ts +7 -7
- package/extensions/{document → file-read}/tool.ts +36 -28
- package/extensions/goal/tools.ts +10 -14
- package/extensions/job/commands.ts +32 -46
- package/extensions/job/runtime.ts +6 -8
- package/extensions/job/tools.ts +7 -26
- package/extensions/memory/commands.ts +30 -44
- package/extensions/memory/tools.ts +6 -14
- package/extensions/plan/tools/enter.ts +6 -7
- package/extensions/plan/tools/exit.ts +6 -7
- package/extensions/plan/tools/steps.ts +6 -7
- package/extensions/plan/tools/write.ts +8 -9
- package/extensions/plan/web.ts +21 -12
- package/extensions/question/tools.ts +5 -4
- package/extensions/subagent/background.ts +1 -7
- package/extensions/todo/tools.ts +10 -14
- package/extensions/watch/README.md +5 -5
- package/extensions/watch/commands.ts +33 -47
- package/extensions/watch/runtime.ts +5 -7
- package/extensions/watch/tools.ts +7 -18
- package/extensions/web-access/README.md +3 -3
- package/extensions/web-access/search/search.ts +9 -18
- package/extensions/wechat/timer.ts +9 -30
- package/extensions/wechat/tools.ts +4 -7
- package/lib/README.md +9 -6
- package/lib/escape.ts +15 -2
- package/lib/pdf.ts +2 -2
- package/lib/requirements.ts +2 -2
- package/lib/screen-session.ts +85 -0
- package/lib/timer.ts +61 -0
- package/lib/tui/README.md +3 -3
- package/lib/tui/columns.ts +33 -8
- package/lib/tui/rows.ts +15 -6
- package/lib/tui/rules.ts +4 -8
- package/lib/tui/tool.ts +55 -2
- package/package.json +8 -4
- package/skills/changelog/SKILL.md +136 -0
- package/extensions/document/index.ts +0 -17
- /package/extensions/{document → file-read}/deadline.ts +0 -0
- /package/extensions/{document → file-read}/extract/vendor.d.ts +0 -0
|
@@ -3,40 +3,38 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A row is one preference: an app setting from Pi's `settings.json`, or a leaf
|
|
5
5
|
* of a `bytefly.json` section. This module is pure — descriptors, a snapshot to
|
|
6
|
-
* views, cycling,
|
|
7
|
-
* one definition and the tests need no host.
|
|
6
|
+
* views, cycling, and validation — so the screen, the text listing, and the
|
|
7
|
+
* runtime share one definition and the tests need no host.
|
|
8
8
|
*
|
|
9
|
-
* The catalogue is
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
9
|
+
* The catalogue is the shortlist a session changes, not the whole document: every
|
|
10
|
+
* row is one a workflow differs on, and each states its own rule so a value the
|
|
11
|
+
* screen accepts is a value the loader keeps. The catalogue order is the screen's
|
|
12
|
+
* order, and it is capped so the section fits a 24-row terminal (`settings.test.ts`
|
|
13
|
+
* holds the ceiling); a new row has to displace one. Everything else stays
|
|
14
|
+
* `bytefly.json` — a list (`doctor.skip`), a table (`price.rates`), a schedule
|
|
15
|
+
* (`price.peak`), and the tuning numbers and labels this screen deliberately does
|
|
16
|
+
* not carry — documented in `docs/configuration.md` and reachable from the JSON
|
|
17
|
+
* pane of any row whose section is still on the list.
|
|
15
18
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* file states them, because importing their
|
|
19
|
-
* importing another's module; each such row
|
|
20
|
-
* `test/settings-coverage.test.ts` holds the
|
|
19
|
+
* Three sections have a normalizer here, so the screen shows what the notifier,
|
|
20
|
+
* the doctor, and the footer keep. The three owned by other extensions (`rewind`,
|
|
21
|
+
* `job`, `watch`) are read as the file states them, because importing their
|
|
22
|
+
* normalizer would be one extension importing another's module; each such row
|
|
23
|
+
* declares the loader's default, and `test/settings-coverage.test.ts` holds the
|
|
24
|
+
* pair together.
|
|
21
25
|
*/
|
|
22
26
|
|
|
23
27
|
import { configSection, configValueAt } from "../../../lib/config.ts";
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
DEFAULT_CONFIG as DEFAULT_DOCTOR_CONFIG,
|
|
27
|
-
normalizeConfig as normalizeDoctorConfig,
|
|
28
|
-
TIMEOUT_MS_RANGE,
|
|
29
|
-
} from "../doctor/config.ts";
|
|
28
|
+
import { DEFAULT_CONFIG as DEFAULT_DOCTOR_CONFIG, normalizeConfig as normalizeDoctorConfig } from "../doctor/config.ts";
|
|
30
29
|
import {
|
|
31
30
|
CURRENCY_DEFAULTS,
|
|
32
31
|
DEFAULT_BYTEFLY_CONFIG,
|
|
33
32
|
DEFAULT_CURRENCY,
|
|
34
33
|
MAX_BUDGET,
|
|
35
|
-
MAX_DECIMALS,
|
|
36
34
|
normalizeByteflyConfig,
|
|
37
35
|
} from "../footer/price/config.ts";
|
|
38
36
|
import type { CurrencyCode } from "../footer/price/types.ts";
|
|
39
|
-
import { DEFAULT_NOTIFY_CONFIG,
|
|
37
|
+
import { DEFAULT_NOTIFY_CONFIG, normalizeNotifyConfig } from "../notify/config.ts";
|
|
40
38
|
import type { Settings, WriteScope } from "../picker/types.ts";
|
|
41
39
|
|
|
42
40
|
/** Where a preference is stored. */
|
|
@@ -62,24 +60,8 @@ interface TextRule {
|
|
|
62
60
|
validate?: (raw: string) => string | undefined;
|
|
63
61
|
}
|
|
64
62
|
|
|
65
|
-
/**
|
|
66
|
-
|
|
67
|
-
key: string;
|
|
68
|
-
label: string;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/** The `bytefly.json` sections the Settings section can read and write. */
|
|
72
|
-
export type ConfigSectionName =
|
|
73
|
-
| "notify"
|
|
74
|
-
| "chrome"
|
|
75
|
-
| "doctor"
|
|
76
|
-
| "price"
|
|
77
|
-
| "worktree"
|
|
78
|
-
| "rewind"
|
|
79
|
-
| "job"
|
|
80
|
-
| "watch"
|
|
81
|
-
| "memory"
|
|
82
|
-
| "goal";
|
|
63
|
+
/** The `bytefly.json` sections a row still reaches. */
|
|
64
|
+
export type ConfigSectionName = "notify" | "doctor" | "price" | "rewind" | "job" | "watch";
|
|
83
65
|
|
|
84
66
|
/** Where a `bytefly.json` row lives: a section, and the path to its leaf. */
|
|
85
67
|
interface SettingConfig {
|
|
@@ -98,8 +80,6 @@ interface SettingLeaf {
|
|
|
98
80
|
export interface SettingDescriptor {
|
|
99
81
|
/** Stable identity, also the focus key. */
|
|
100
82
|
id: string;
|
|
101
|
-
/** Group key from {@link SETTING_GROUPS}. */
|
|
102
|
-
group: string;
|
|
103
83
|
label: string;
|
|
104
84
|
description: string;
|
|
105
85
|
source: SettingSource;
|
|
@@ -139,27 +119,6 @@ export interface SettingsSnapshot {
|
|
|
139
119
|
themes: readonly string[];
|
|
140
120
|
}
|
|
141
121
|
|
|
142
|
-
/** One rendered group: its heading and the rows under it. */
|
|
143
|
-
interface GroupedSettings {
|
|
144
|
-
key: string;
|
|
145
|
-
label: string;
|
|
146
|
-
views: SettingView[];
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/** Group order on screen. */
|
|
150
|
-
export const SETTING_GROUPS: readonly SettingGroup[] = [
|
|
151
|
-
{ key: "startup", label: "Startup" },
|
|
152
|
-
{ key: "notifications", label: "Notifications" },
|
|
153
|
-
{ key: "diagnostics", label: "Diagnostics" },
|
|
154
|
-
{ key: "pricing", label: "Pricing" },
|
|
155
|
-
{ key: "transcript", label: "Transcript" },
|
|
156
|
-
{ key: "terminal", label: "Terminal" },
|
|
157
|
-
{ key: "worktree", label: "Worktree" },
|
|
158
|
-
{ key: "snapshots", label: "Snapshots" },
|
|
159
|
-
{ key: "jobs", label: "Jobs and watchers" },
|
|
160
|
-
{ key: "memory", label: "Memory" },
|
|
161
|
-
];
|
|
162
|
-
|
|
163
122
|
const ON_OFF: readonly SettingOption[] = [
|
|
164
123
|
{ value: "false", label: "Off" },
|
|
165
124
|
{ value: "true", label: "On" },
|
|
@@ -175,32 +134,6 @@ const METHOD_OPTIONS: readonly SettingOption[] = [
|
|
|
175
134
|
{ value: "notify-send", label: "Linux" },
|
|
176
135
|
];
|
|
177
136
|
|
|
178
|
-
/** Cost precision 0 through the loader's ceiling, the range `price` clamps `decimals` to. */
|
|
179
|
-
const DECIMALS_OPTIONS: readonly SettingOption[] = Array.from({ length: MAX_DECIMALS + 1 }, (_, value) => ({
|
|
180
|
-
value: String(value),
|
|
181
|
-
label: String(value),
|
|
182
|
-
}));
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* A text rule for a whole number inside an inclusive range.
|
|
186
|
-
*
|
|
187
|
-
* The bounds are the loader's own (`doctor.timeoutMs` is clamped to 100-30000),
|
|
188
|
-
* so an accepted value is never silently clamped on the next read. `maxLength`
|
|
189
|
-
* only stops a runaway buffer; `validate` is the rule.
|
|
190
|
-
*/
|
|
191
|
-
function integerRule(min: number, max: number): TextRule {
|
|
192
|
-
return {
|
|
193
|
-
maxLength: String(max).length + 2,
|
|
194
|
-
validate(raw) {
|
|
195
|
-
const text = raw.trim();
|
|
196
|
-
if (!/^\d+$/.test(text)) return `Use a whole number, ${min} to ${max}.`;
|
|
197
|
-
const value = Number(text);
|
|
198
|
-
if (value < min || value > max) return `Out of range: ${min} to ${max}.`;
|
|
199
|
-
return undefined;
|
|
200
|
-
},
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
|
|
204
137
|
/**
|
|
205
138
|
* A text rule for a money amount inside an inclusive range: a whole number or a
|
|
206
139
|
* decimal with up to four places, matching the loader's clamp for `price.budget`.
|
|
@@ -219,18 +152,15 @@ function amountRule(min: number, max: number): TextRule {
|
|
|
219
152
|
};
|
|
220
153
|
}
|
|
221
154
|
|
|
222
|
-
/**
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
/** The v1 catalogue, in screen order. */
|
|
155
|
+
/**
|
|
156
|
+
* The catalogue, in screen order.
|
|
157
|
+
*
|
|
158
|
+
* Global scope lists all fourteen; Project lists the twelve `bytefly.json` rows,
|
|
159
|
+
* because Pi's setters write the personal `settings.json` only.
|
|
160
|
+
*/
|
|
230
161
|
export const SETTING_ROWS: readonly SettingDescriptor[] = [
|
|
231
162
|
{
|
|
232
163
|
id: "byteflyUi",
|
|
233
|
-
group: "startup",
|
|
234
164
|
label: "Bytefly startup TUI",
|
|
235
165
|
description: "Use bytefly's startup header in place of Pi's startup TUI.",
|
|
236
166
|
source: "app",
|
|
@@ -239,9 +169,19 @@ export const SETTING_ROWS: readonly SettingDescriptor[] = [
|
|
|
239
169
|
json: "boolean",
|
|
240
170
|
options: ON_OFF,
|
|
241
171
|
},
|
|
172
|
+
{
|
|
173
|
+
id: "theme",
|
|
174
|
+
label: "Theme",
|
|
175
|
+
description:
|
|
176
|
+
"Pi's color theme, chosen from the palettes this package ships; `system` follows the terminal's colors. Takes effect after reload.",
|
|
177
|
+
source: "app",
|
|
178
|
+
kind: "enum",
|
|
179
|
+
fallback: "system",
|
|
180
|
+
// The host supplies this package's theme names in the snapshot; the static list stays empty.
|
|
181
|
+
options: [],
|
|
182
|
+
},
|
|
242
183
|
{
|
|
243
184
|
id: "notify.enabled",
|
|
244
|
-
group: "notifications",
|
|
245
185
|
label: "Desktop notifications",
|
|
246
186
|
description: "Raise an OS notification when a run or job finishes, or the agent asks for input.",
|
|
247
187
|
source: "bytefly",
|
|
@@ -253,7 +193,6 @@ export const SETTING_ROWS: readonly SettingDescriptor[] = [
|
|
|
253
193
|
},
|
|
254
194
|
{
|
|
255
195
|
id: "notify.turnFinished",
|
|
256
|
-
group: "notifications",
|
|
257
196
|
label: "Turn finished",
|
|
258
197
|
description: "Announce it when a full agent run settles.",
|
|
259
198
|
source: "bytefly",
|
|
@@ -265,7 +204,6 @@ export const SETTING_ROWS: readonly SettingDescriptor[] = [
|
|
|
265
204
|
},
|
|
266
205
|
{
|
|
267
206
|
id: "notify.needsInput",
|
|
268
|
-
group: "notifications",
|
|
269
207
|
label: "Needs input",
|
|
270
208
|
description: "Announce it when the agent opens a prompt and waits for an answer.",
|
|
271
209
|
source: "bytefly",
|
|
@@ -277,7 +215,6 @@ export const SETTING_ROWS: readonly SettingDescriptor[] = [
|
|
|
277
215
|
},
|
|
278
216
|
{
|
|
279
217
|
id: "notify.jobFinished",
|
|
280
|
-
group: "notifications",
|
|
281
218
|
label: "Job finished",
|
|
282
219
|
description: "Announce it when a background job or watcher settles.",
|
|
283
220
|
source: "bytefly",
|
|
@@ -289,7 +226,6 @@ export const SETTING_ROWS: readonly SettingDescriptor[] = [
|
|
|
289
226
|
},
|
|
290
227
|
{
|
|
291
228
|
id: "notify.matchFound",
|
|
292
|
-
group: "notifications",
|
|
293
229
|
label: "Watcher matched",
|
|
294
230
|
description: "Announce it when a `watch` whose `wake` is on matches while the agent is idle.",
|
|
295
231
|
source: "bytefly",
|
|
@@ -301,7 +237,6 @@ export const SETTING_ROWS: readonly SettingDescriptor[] = [
|
|
|
301
237
|
},
|
|
302
238
|
{
|
|
303
239
|
id: "notify.method",
|
|
304
|
-
group: "notifications",
|
|
305
240
|
label: "Delivery method",
|
|
306
241
|
description: "How a notification is delivered; Auto picks from the terminal, then the platform.",
|
|
307
242
|
source: "bytefly",
|
|
@@ -311,23 +246,40 @@ export const SETTING_ROWS: readonly SettingDescriptor[] = [
|
|
|
311
246
|
config: { section: "notify", path: ["method"] },
|
|
312
247
|
},
|
|
313
248
|
{
|
|
314
|
-
id: "
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
description: "Shown as the title of every OS notification; up to 40 characters.",
|
|
249
|
+
id: "job.wakeOnFinish",
|
|
250
|
+
label: "Wake when a job finishes",
|
|
251
|
+
description: "Start one agent turn when any background job finishes.",
|
|
318
252
|
source: "bytefly",
|
|
319
|
-
kind: "
|
|
320
|
-
fallback: "
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
253
|
+
kind: "boolean",
|
|
254
|
+
fallback: "false",
|
|
255
|
+
json: "boolean",
|
|
256
|
+
options: ON_OFF,
|
|
257
|
+
config: { section: "job", path: ["wakeOnFinish"] },
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
id: "watch.wakeOnMatch",
|
|
261
|
+
label: "Wake on a match",
|
|
262
|
+
description: "Start one agent turn when a watcher matches while the agent is idle.",
|
|
263
|
+
source: "bytefly",
|
|
264
|
+
kind: "boolean",
|
|
265
|
+
fallback: "false",
|
|
266
|
+
json: "boolean",
|
|
267
|
+
options: ON_OFF,
|
|
268
|
+
config: { section: "watch", path: ["wakeOnMatch"] },
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
id: "rewind.autoSnapshots",
|
|
272
|
+
label: "Automatic snapshots",
|
|
273
|
+
description: "Take one rewind snapshot per prompt, before the first mutating tool call.",
|
|
274
|
+
source: "bytefly",
|
|
275
|
+
kind: "boolean",
|
|
276
|
+
fallback: "true",
|
|
277
|
+
json: "boolean",
|
|
278
|
+
options: ON_OFF,
|
|
279
|
+
config: { section: "rewind", path: ["autoSnapshots"] },
|
|
327
280
|
},
|
|
328
281
|
{
|
|
329
282
|
id: "doctor.proactive",
|
|
330
|
-
group: "diagnostics",
|
|
331
283
|
label: "Proactive health check",
|
|
332
284
|
description: "Probe enabled extensions at session start and warn about a missing requirement.",
|
|
333
285
|
source: "bytefly",
|
|
@@ -337,22 +289,8 @@ export const SETTING_ROWS: readonly SettingDescriptor[] = [
|
|
|
337
289
|
options: ON_OFF,
|
|
338
290
|
config: { section: "doctor", path: ["proactive"] },
|
|
339
291
|
},
|
|
340
|
-
{
|
|
341
|
-
id: "doctor.timeoutMs",
|
|
342
|
-
group: "diagnostics",
|
|
343
|
-
label: "Probe timeout",
|
|
344
|
-
description: `How long one doctor probe may take, in milliseconds; ${TIMEOUT_MS_RANGE.min} to ${TIMEOUT_MS_RANGE.max}.`,
|
|
345
|
-
source: "bytefly",
|
|
346
|
-
kind: "text",
|
|
347
|
-
fallback: String(DEFAULT_DOCTOR_CONFIG.timeoutMs),
|
|
348
|
-
json: "number",
|
|
349
|
-
options: [],
|
|
350
|
-
text: integerRule(TIMEOUT_MS_RANGE.min, TIMEOUT_MS_RANGE.max),
|
|
351
|
-
config: { section: "doctor", path: ["timeoutMs"] },
|
|
352
|
-
},
|
|
353
292
|
{
|
|
354
293
|
id: "price.currency.code",
|
|
355
|
-
group: "pricing",
|
|
356
294
|
label: "Currency",
|
|
357
295
|
description:
|
|
358
296
|
"CNY prices DeepSeek from its yuan rates; USD from its published dollars, falling back to Pi's recorded cost.",
|
|
@@ -365,7 +303,7 @@ export const SETTING_ROWS: readonly SettingDescriptor[] = [
|
|
|
365
303
|
],
|
|
366
304
|
config: { section: "price", path: ["currency", "code"] },
|
|
367
305
|
// Choosing a currency also sets its default precision, so the screen and the
|
|
368
|
-
// footer agree; the precision stays overridable afterwards.
|
|
306
|
+
// footer agree; the precision stays overridable in `bytefly.json` afterwards.
|
|
369
307
|
also: (raw) => {
|
|
370
308
|
const currency = CURRENCY_DEFAULTS[raw as CurrencyCode];
|
|
371
309
|
return currency === undefined
|
|
@@ -373,21 +311,8 @@ export const SETTING_ROWS: readonly SettingDescriptor[] = [
|
|
|
373
311
|
: [{ section: "price", path: ["currency", "decimals"], value: currency.decimals }];
|
|
374
312
|
},
|
|
375
313
|
},
|
|
376
|
-
{
|
|
377
|
-
id: "price.currency.decimals",
|
|
378
|
-
group: "pricing",
|
|
379
|
-
label: "Cost decimals",
|
|
380
|
-
description: "Digits shown after the decimal point in the footer's cost total.",
|
|
381
|
-
source: "bytefly",
|
|
382
|
-
kind: "enum",
|
|
383
|
-
fallback: "2",
|
|
384
|
-
json: "number",
|
|
385
|
-
options: DECIMALS_OPTIONS,
|
|
386
|
-
config: { section: "price", path: ["currency", "decimals"] },
|
|
387
|
-
},
|
|
388
314
|
{
|
|
389
315
|
id: "price.budget",
|
|
390
|
-
group: "pricing",
|
|
391
316
|
label: "Session budget",
|
|
392
317
|
description: "Warn once when the session's cost reaches this amount; 0 is off. It never stops a turn.",
|
|
393
318
|
source: "bytefly",
|
|
@@ -398,210 +323,6 @@ export const SETTING_ROWS: readonly SettingDescriptor[] = [
|
|
|
398
323
|
text: amountRule(0, MAX_BUDGET),
|
|
399
324
|
config: { section: "price", path: ["budget"] },
|
|
400
325
|
},
|
|
401
|
-
{
|
|
402
|
-
id: "chrome.rail",
|
|
403
|
-
group: "transcript",
|
|
404
|
-
label: "Rail glyph",
|
|
405
|
-
description: "The single-column glyph that leads a tool result or notice body, e.g. ╎ │ ┃ ▏.",
|
|
406
|
-
source: "bytefly",
|
|
407
|
-
kind: "text",
|
|
408
|
-
fallback: DEFAULT_CHROME_CONFIG.rail,
|
|
409
|
-
options: [],
|
|
410
|
-
text: RAIL_RULE,
|
|
411
|
-
config: { section: "chrome", path: ["rail"] },
|
|
412
|
-
},
|
|
413
|
-
{
|
|
414
|
-
id: "goal.achieved",
|
|
415
|
-
group: "transcript",
|
|
416
|
-
label: "Achieved goal",
|
|
417
|
-
description: "How a completed goal renders: the dimmed rail line, or nothing.",
|
|
418
|
-
source: "bytefly",
|
|
419
|
-
kind: "enum",
|
|
420
|
-
fallback: "hide",
|
|
421
|
-
options: [
|
|
422
|
-
{ value: "show", label: "Show" },
|
|
423
|
-
{ value: "hide", label: "Hide" },
|
|
424
|
-
],
|
|
425
|
-
config: { section: "goal", path: ["achieved"] },
|
|
426
|
-
},
|
|
427
|
-
{
|
|
428
|
-
id: "outputPad",
|
|
429
|
-
group: "transcript",
|
|
430
|
-
label: "Output padding",
|
|
431
|
-
description: "Pi's transcript padding; bytefly's header and blocks follow it.",
|
|
432
|
-
source: "app",
|
|
433
|
-
kind: "enum",
|
|
434
|
-
fallback: "1",
|
|
435
|
-
json: "number",
|
|
436
|
-
options: [
|
|
437
|
-
{ value: "0", label: "0" },
|
|
438
|
-
{ value: "1", label: "1" },
|
|
439
|
-
],
|
|
440
|
-
},
|
|
441
|
-
{
|
|
442
|
-
id: "theme",
|
|
443
|
-
group: "terminal",
|
|
444
|
-
label: "Theme",
|
|
445
|
-
description:
|
|
446
|
-
"Pi's color theme, chosen from the palettes this package ships; `system` follows the terminal's colors. Takes effect after reload.",
|
|
447
|
-
source: "app",
|
|
448
|
-
kind: "enum",
|
|
449
|
-
fallback: "system",
|
|
450
|
-
// The host supplies this package's theme names in the snapshot; the static list stays empty.
|
|
451
|
-
options: [],
|
|
452
|
-
},
|
|
453
|
-
{
|
|
454
|
-
id: "showHardwareCursor",
|
|
455
|
-
group: "terminal",
|
|
456
|
-
label: "Hardware cursor",
|
|
457
|
-
description: "Use the terminal cursor instead of Pi's drawn cursor.",
|
|
458
|
-
source: "app",
|
|
459
|
-
kind: "boolean",
|
|
460
|
-
fallback: "false",
|
|
461
|
-
json: "boolean",
|
|
462
|
-
options: ON_OFF,
|
|
463
|
-
},
|
|
464
|
-
{
|
|
465
|
-
id: "worktree.baseRef",
|
|
466
|
-
group: "worktree",
|
|
467
|
-
label: "Base ref",
|
|
468
|
-
description: "Where a new worktree branches from: the remote default branch, or the current local HEAD.",
|
|
469
|
-
source: "bytefly",
|
|
470
|
-
kind: "enum",
|
|
471
|
-
fallback: "fresh",
|
|
472
|
-
options: [
|
|
473
|
-
{ value: "fresh", label: "Fresh" },
|
|
474
|
-
{ value: "head", label: "Head" },
|
|
475
|
-
],
|
|
476
|
-
config: { section: "worktree", path: ["baseRef"] },
|
|
477
|
-
},
|
|
478
|
-
{
|
|
479
|
-
id: "worktree.branchPrefix",
|
|
480
|
-
group: "worktree",
|
|
481
|
-
label: "Branch prefix",
|
|
482
|
-
description: "Prepended to every branch a managed worktree creates.",
|
|
483
|
-
source: "bytefly",
|
|
484
|
-
kind: "text",
|
|
485
|
-
fallback: "worktree-",
|
|
486
|
-
options: [],
|
|
487
|
-
text: {
|
|
488
|
-
// The loader trims and rejects an empty prefix but sets no ceiling; this
|
|
489
|
-
// only stops a runaway buffer.
|
|
490
|
-
maxLength: 80,
|
|
491
|
-
validate: (raw) => (raw.trim() === "" ? "A prefix is required." : undefined),
|
|
492
|
-
},
|
|
493
|
-
config: { section: "worktree", path: ["branchPrefix"] },
|
|
494
|
-
},
|
|
495
|
-
{
|
|
496
|
-
id: "worktree.fetchRemote",
|
|
497
|
-
group: "worktree",
|
|
498
|
-
label: "Fetch remote",
|
|
499
|
-
description: "Fetch the remote before branching, so a new worktree starts from its latest commit.",
|
|
500
|
-
source: "bytefly",
|
|
501
|
-
kind: "boolean",
|
|
502
|
-
fallback: "true",
|
|
503
|
-
json: "boolean",
|
|
504
|
-
options: ON_OFF,
|
|
505
|
-
config: { section: "worktree", path: ["fetchRemote"] },
|
|
506
|
-
},
|
|
507
|
-
{
|
|
508
|
-
id: "worktree.pruneAfterDays",
|
|
509
|
-
group: "worktree",
|
|
510
|
-
label: "Prune after (days)",
|
|
511
|
-
description: "Age at which `/worktree prune` removes a clean worktree nobody is using.",
|
|
512
|
-
source: "bytefly",
|
|
513
|
-
kind: "text",
|
|
514
|
-
fallback: "7",
|
|
515
|
-
json: "number",
|
|
516
|
-
options: [],
|
|
517
|
-
text: integerRule(0, 3650),
|
|
518
|
-
config: { section: "worktree", path: ["pruneAfterDays"] },
|
|
519
|
-
},
|
|
520
|
-
{
|
|
521
|
-
id: "rewind.autoSnapshots",
|
|
522
|
-
group: "snapshots",
|
|
523
|
-
label: "Automatic snapshots",
|
|
524
|
-
description: "Take one rewind snapshot per prompt, before the first mutating tool call.",
|
|
525
|
-
source: "bytefly",
|
|
526
|
-
kind: "boolean",
|
|
527
|
-
fallback: "true",
|
|
528
|
-
json: "boolean",
|
|
529
|
-
options: ON_OFF,
|
|
530
|
-
config: { section: "rewind", path: ["autoSnapshots"] },
|
|
531
|
-
},
|
|
532
|
-
{
|
|
533
|
-
id: "rewind.max",
|
|
534
|
-
group: "snapshots",
|
|
535
|
-
label: "Snapshots kept",
|
|
536
|
-
description: "Snapshot refs kept per repository, including pre-restore safety snapshots; 0 keeps none.",
|
|
537
|
-
source: "bytefly",
|
|
538
|
-
kind: "text",
|
|
539
|
-
fallback: "20",
|
|
540
|
-
json: "number",
|
|
541
|
-
options: [],
|
|
542
|
-
text: integerRule(0, 1000),
|
|
543
|
-
config: { section: "rewind", path: ["max"] },
|
|
544
|
-
},
|
|
545
|
-
{
|
|
546
|
-
id: "rewind.safetySnapshot",
|
|
547
|
-
group: "snapshots",
|
|
548
|
-
label: "Safety snapshot",
|
|
549
|
-
description: "Snapshot the current state before a restore, so a rewind can itself be undone.",
|
|
550
|
-
source: "bytefly",
|
|
551
|
-
kind: "boolean",
|
|
552
|
-
fallback: "true",
|
|
553
|
-
json: "boolean",
|
|
554
|
-
options: ON_OFF,
|
|
555
|
-
config: { section: "rewind", path: ["safetySnapshot"] },
|
|
556
|
-
},
|
|
557
|
-
{
|
|
558
|
-
id: "job.wakeOnFinish",
|
|
559
|
-
group: "jobs",
|
|
560
|
-
label: "Wake when a job finishes",
|
|
561
|
-
description: "Start one agent turn when any background job finishes.",
|
|
562
|
-
source: "bytefly",
|
|
563
|
-
kind: "boolean",
|
|
564
|
-
fallback: "false",
|
|
565
|
-
json: "boolean",
|
|
566
|
-
options: ON_OFF,
|
|
567
|
-
config: { section: "job", path: ["wakeOnFinish"] },
|
|
568
|
-
},
|
|
569
|
-
{
|
|
570
|
-
id: "job.detachedByDefault",
|
|
571
|
-
group: "jobs",
|
|
572
|
-
label: "Keep jobs after exit",
|
|
573
|
-
description: "Leave jobs running when the session ends, unless the job opts out.",
|
|
574
|
-
source: "bytefly",
|
|
575
|
-
kind: "boolean",
|
|
576
|
-
fallback: "false",
|
|
577
|
-
json: "boolean",
|
|
578
|
-
options: ON_OFF,
|
|
579
|
-
config: { section: "job", path: ["detachedByDefault"] },
|
|
580
|
-
},
|
|
581
|
-
{
|
|
582
|
-
id: "watch.wakeOnMatch",
|
|
583
|
-
group: "jobs",
|
|
584
|
-
label: "Wake on a match",
|
|
585
|
-
description: "Start one agent turn when a watcher matches while the agent is idle.",
|
|
586
|
-
source: "bytefly",
|
|
587
|
-
kind: "boolean",
|
|
588
|
-
fallback: "false",
|
|
589
|
-
json: "boolean",
|
|
590
|
-
options: ON_OFF,
|
|
591
|
-
config: { section: "watch", path: ["wakeOnMatch"] },
|
|
592
|
-
},
|
|
593
|
-
{
|
|
594
|
-
id: "memory.inject",
|
|
595
|
-
group: "memory",
|
|
596
|
-
label: "Inject notes",
|
|
597
|
-
description: "Put stored memory notes in context before each run.",
|
|
598
|
-
source: "bytefly",
|
|
599
|
-
kind: "boolean",
|
|
600
|
-
fallback: "true",
|
|
601
|
-
json: "boolean",
|
|
602
|
-
options: ON_OFF,
|
|
603
|
-
config: { section: "memory", path: ["inject"] },
|
|
604
|
-
},
|
|
605
326
|
];
|
|
606
327
|
|
|
607
328
|
/** The one refusal for a Project write while the project is untrusted. */
|
|
@@ -615,10 +336,10 @@ export function rowsForScope(scope: WriteScope): readonly SettingDescriptor[] {
|
|
|
615
336
|
/**
|
|
616
337
|
* Why a text row cannot store `raw`, or `undefined` when it can.
|
|
617
338
|
*
|
|
618
|
-
* The limits are the loaders' own (
|
|
619
|
-
*
|
|
620
|
-
*
|
|
621
|
-
*
|
|
339
|
+
* The limits are the loaders' own (the budget is clamped to `MAX_BUDGET`) so the
|
|
340
|
+
* editor cannot save a value the next read shortens. A row that is not text is
|
|
341
|
+
* refused here rather than in the caller, so a programmatic `setValue` gets the
|
|
342
|
+
* same reason the screen would show.
|
|
622
343
|
*/
|
|
623
344
|
export function textError(row: SettingDescriptor, raw: string): string | undefined {
|
|
624
345
|
const rule = row.text;
|
|
@@ -650,16 +371,6 @@ function appValue(snapshot: SettingsSnapshot, row: SettingDescriptor): { value:
|
|
|
650
371
|
const quiet = snapshot.app.quietStartup;
|
|
651
372
|
return { value: quiet === true ? "true" : row.fallback, origin: quiet === undefined ? "default" : "global" };
|
|
652
373
|
}
|
|
653
|
-
case "outputPad":
|
|
654
|
-
return {
|
|
655
|
-
value: snapshot.app.outputPad === 0 ? "0" : row.fallback,
|
|
656
|
-
origin: snapshot.app.outputPad === undefined ? "default" : "global",
|
|
657
|
-
};
|
|
658
|
-
case "showHardwareCursor":
|
|
659
|
-
return {
|
|
660
|
-
value: snapshot.app.showHardwareCursor === true ? "true" : row.fallback,
|
|
661
|
-
origin: snapshot.app.showHardwareCursor === undefined ? "default" : "global",
|
|
662
|
-
};
|
|
663
374
|
case "theme": {
|
|
664
375
|
const theme = snapshot.app.theme;
|
|
665
376
|
const set = typeof theme === "string" && theme !== "";
|
|
@@ -692,15 +403,10 @@ type SectionReader = (
|
|
|
692
403
|
path: readonly string[],
|
|
693
404
|
) => unknown;
|
|
694
405
|
|
|
695
|
-
/**
|
|
696
|
-
* The four sections bytefly owns, each read through its own normalizer so the
|
|
697
|
-
* screen shows the value the loader keeps.
|
|
698
|
-
*/
|
|
406
|
+
/** The sections bytefly owns, each read through its own normalizer so the screen shows what the loader keeps. */
|
|
699
407
|
const SECTION_READERS: Partial<Record<ConfigSectionName, SectionReader>> = {
|
|
700
408
|
notify: (globalRaw, projectRaw, path) =>
|
|
701
409
|
configValueAt(normalizeNotifyConfig(projectRaw, normalizeNotifyConfig(globalRaw, DEFAULT_NOTIFY_CONFIG)), path),
|
|
702
|
-
chrome: (globalRaw, projectRaw, path) =>
|
|
703
|
-
configValueAt(normalizeChromeConfig(projectRaw, normalizeChromeConfig(globalRaw, DEFAULT_CHROME_CONFIG)), path),
|
|
704
410
|
doctor: (globalRaw, projectRaw, path) =>
|
|
705
411
|
configValueAt(normalizeDoctorConfig(projectRaw, normalizeDoctorConfig(globalRaw, DEFAULT_DOCTOR_CONFIG)), path),
|
|
706
412
|
price: (globalRaw, projectRaw, path) =>
|
|
@@ -772,12 +478,3 @@ export function storedValue(row: SettingDescriptor, raw: string): unknown {
|
|
|
772
478
|
return raw;
|
|
773
479
|
}
|
|
774
480
|
}
|
|
775
|
-
|
|
776
|
-
/** Group views in screen order, dropping empty groups. */
|
|
777
|
-
export function groupViews(views: readonly SettingView[]): GroupedSettings[] {
|
|
778
|
-
return SETTING_GROUPS.map((group) => ({
|
|
779
|
-
key: group.key,
|
|
780
|
-
label: group.label,
|
|
781
|
-
views: views.filter((view) => view.group === group.key),
|
|
782
|
-
})).filter((group) => group.views.length > 0);
|
|
783
|
-
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The panel's entry model:
|
|
3
|
-
*
|
|
2
|
+
* The panel's entry model: one flat list of selectable rows per section.
|
|
3
|
+
*
|
|
4
|
+
* Extensions interleaves group headings with rows — a source group says where a
|
|
5
|
+
* resource came from, which the row cannot — while Settings is flat, in catalogue
|
|
6
|
+
* order. Both are built from a model or a set of views, so the component only has
|
|
7
|
+
* to keep one cursor per section.
|
|
4
8
|
*
|
|
5
9
|
* Pure: the list is rebuilt from a model or a set of views, and navigation is
|
|
6
|
-
* index arithmetic
|
|
10
|
+
* index arithmetic.
|
|
7
11
|
*/
|
|
8
12
|
|
|
9
|
-
import {
|
|
13
|
+
import type { SettingView } from "../settings.ts";
|
|
10
14
|
import type { PanelModel, PanelRow } from "../types.ts";
|
|
11
15
|
|
|
12
16
|
/** A flat display entry: a group header or a selectable row. */
|
|
@@ -30,9 +34,9 @@ export function isSelectable(entry: PanelEntry | undefined): boolean {
|
|
|
30
34
|
/**
|
|
31
35
|
* One display window as ordinals over the selectable rows it contains.
|
|
32
36
|
*
|
|
33
|
-
* The range row counts rows, not entries:
|
|
34
|
-
*
|
|
35
|
-
*
|
|
37
|
+
* The range row counts rows, not entries: an extension list's group headings are
|
|
38
|
+
* not focusable, so `showing 3–5 of 12` is what the user can land on.
|
|
39
|
+
* `formatRange` prints `start + 1` through `end`, so the ordinals here are
|
|
36
40
|
* zero-based and `end` is exclusive.
|
|
37
41
|
*/
|
|
38
42
|
export function selectableRange(
|
|
@@ -71,12 +75,10 @@ export function entriesOf(model: PanelModel): PanelEntry[] {
|
|
|
71
75
|
return entries;
|
|
72
76
|
}
|
|
73
77
|
|
|
74
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* The Settings entries: one row per view, in catalogue order, with no group
|
|
80
|
+
* headings — the list is short enough to read whole.
|
|
81
|
+
*/
|
|
75
82
|
export function settingEntries(views: readonly SettingView[]): PanelEntry[] {
|
|
76
|
-
|
|
77
|
-
for (const group of groupViews(views)) {
|
|
78
|
-
entries.push({ kind: "group", key: group.key, label: group.label, count: group.views.length });
|
|
79
|
-
for (const view of group.views) entries.push({ kind: "setting", setting: view });
|
|
80
|
-
}
|
|
81
|
-
return entries;
|
|
83
|
+
return views.map((setting) => ({ kind: "setting", setting }));
|
|
82
84
|
}
|
|
@@ -44,6 +44,9 @@ export function extensionRow(theme: Theme, width: number, row: PanelRow, selecte
|
|
|
44
44
|
// The glyph carries the status colour; the word stays quiet unless it is a
|
|
45
45
|
// problem, so a clean row does not shout.
|
|
46
46
|
trailingRole: badge.role === "bad" || badge.role === "warn" ? badge.role : "faint",
|
|
47
|
+
// The verdict sits beside the name instead of at the far edge of a wide
|
|
48
|
+
// terminal, where a two-word verdict would be unreadable.
|
|
49
|
+
align: "inline",
|
|
47
50
|
});
|
|
48
51
|
}
|
|
49
52
|
|