@aiwayds/dsh-tui-pi 0.26.0 → 0.28.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.
@@ -0,0 +1,402 @@
1
+ /**
2
+ * User model profiles — named snapshots of the whole model configuration
3
+ * (default model + think level, plus per-subagent model/thinking) that can
4
+ * be saved, reviewed and applied in one step, so switching between contexts
5
+ * (work ↔ personal) is a single command instead of a /model + per-agent
6
+ * /agents tour.
7
+ *
8
+ * Storage: `$DSH_HOME/model-profiles.json` — a small JSON document the TUI
9
+ * owns outright (same model as tui-command-usage.json, src/usage.ts). Reads
10
+ * never throw: a missing, corrupt or wrong-version file degrades to the
11
+ * seeded default document (work / personal / other, empty until configured).
12
+ * Writes are atomic (tmp sibling + rename) and whole-document
13
+ * last-write-wins — the accepted loss model for a human-paced UI feature
14
+ * (same as settings.yaml).
15
+ *
16
+ * Snapshot semantics — the part to keep straight:
17
+ * - `profile.agents[name]` PRESENT → applying writes that agent's
18
+ * frontmatter model/thinking to exactly the recorded values (absent keys
19
+ * CLEAR the line — explicit inherit).
20
+ * - `profile.agents[name]` ABSENT → applying leaves the agent untouched
21
+ * (an agent file created after the last save — the profile never knew it).
22
+ * - "Save current" records EVERY discovered agent, inherit ones as empty
23
+ * entries, so a round-trip switch restores inherit where inherit was.
24
+ */
25
+ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
26
+ import { dirname, join, resolve } from 'node:path';
27
+ import { dshHome } from "./append-system.js";
28
+ /** On-disk schema version; bump only for breaking changes. */
29
+ export const MODEL_PROFILES_VERSION = 1;
30
+ /** Profiles seeded into a fresh document (and re-seeded when the stored one is empty). */
31
+ export const DEFAULT_PROFILE_NAMES = ['work', 'personal', 'other'];
32
+ /** `$DSH_HOME/model-profiles.json` (or an explicit home override). */
33
+ export function modelProfilesPath(home = dshHome()) {
34
+ return join(home, 'model-profiles.json');
35
+ }
36
+ /** Narrow an unknown JSON value into a route; anything else is dropped. */
37
+ function narrowRoute(value) {
38
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
39
+ return undefined;
40
+ const { provider, model, reasoningEffort } = value;
41
+ if (typeof provider !== 'string' || provider === '')
42
+ return undefined;
43
+ if (typeof model !== 'string' || model === '')
44
+ return undefined;
45
+ const route = { provider, model };
46
+ if (typeof reasoningEffort === 'string' && reasoningEffort !== '')
47
+ route.reasoningEffort = reasoningEffort;
48
+ return route;
49
+ }
50
+ /** Narrow one agents-map value; `undefined` drops the entry, `{}` keeps it. */
51
+ function narrowEntry(value) {
52
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
53
+ return undefined;
54
+ const { model, thinking } = value;
55
+ const entry = {};
56
+ if (typeof model === 'string' && model !== '')
57
+ entry.model = model;
58
+ if (typeof thinking === 'string' && thinking !== '')
59
+ entry.thinking = thinking;
60
+ return entry;
61
+ }
62
+ /** Narrow one profile; a missing/invalid name drops the whole profile. */
63
+ function narrowProfile(value) {
64
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
65
+ return undefined;
66
+ const { name, defaultModel, agents } = value;
67
+ if (typeof name !== 'string' || name.trim() === '')
68
+ return undefined;
69
+ const profile = { name: name.trim(), agents: {} };
70
+ const route = narrowRoute(defaultModel);
71
+ if (route !== undefined)
72
+ profile.defaultModel = route;
73
+ if (agents !== null && typeof agents === 'object' && !Array.isArray(agents)) {
74
+ for (const [agentName, entry] of Object.entries(agents)) {
75
+ const key = agentName.trim();
76
+ if (key === '')
77
+ continue;
78
+ const narrowed = narrowEntry(entry);
79
+ if (narrowed !== undefined)
80
+ profile.agents[key] = narrowed;
81
+ }
82
+ }
83
+ return profile;
84
+ }
85
+ /** A fresh document with the three seeded, unconfigured default profiles. */
86
+ export function seedModelProfilesDoc() {
87
+ return { profiles: DEFAULT_PROFILE_NAMES.map(name => ({ name, agents: {} })) };
88
+ }
89
+ /**
90
+ * Validate an unknown parsed document. A wrong version, a non-object or an
91
+ * empty/invalid profiles array degrades to the seeded defaults — the store
92
+ * is self-healing, never fatal. Duplicate names (case-insensitive) keep the
93
+ * first occurrence; a `current` pointing at a dropped profile is unset.
94
+ */
95
+ export function normalizeModelProfiles(raw) {
96
+ const doc = seedModelProfilesDoc();
97
+ if (raw === null || typeof raw !== 'object' || Array.isArray(raw))
98
+ return doc;
99
+ const { version, current, profiles } = raw;
100
+ if (version !== MODEL_PROFILES_VERSION)
101
+ return doc;
102
+ const narrowed = [];
103
+ const seen = new Set();
104
+ if (Array.isArray(profiles)) {
105
+ for (const value of profiles) {
106
+ const profile = narrowProfile(value);
107
+ if (profile === undefined)
108
+ continue;
109
+ const key = profile.name.toLowerCase();
110
+ if (seen.has(key))
111
+ continue;
112
+ seen.add(key);
113
+ narrowed.push(profile);
114
+ }
115
+ }
116
+ if (narrowed.length === 0)
117
+ return doc;
118
+ doc.profiles = narrowed;
119
+ if (typeof current === 'string' && narrowed.some(profile => profile.name === current)) {
120
+ doc.current = current;
121
+ }
122
+ return doc;
123
+ }
124
+ /** Read + validate the store; any failure degrades to the seeded document. */
125
+ export function loadModelProfiles(path) {
126
+ let text;
127
+ try {
128
+ text = readFileSync(path, 'utf8');
129
+ }
130
+ catch {
131
+ return seedModelProfilesDoc();
132
+ }
133
+ try {
134
+ return normalizeModelProfiles(JSON.parse(text));
135
+ }
136
+ catch {
137
+ return seedModelProfilesDoc();
138
+ }
139
+ }
140
+ /**
141
+ * Atomically persist the document (tmp sibling + rename). Creates the
142
+ * directory when missing. Resolves an error message on failure, or
143
+ * `undefined` on success — callers flash it, never throw.
144
+ */
145
+ export function saveModelProfiles(path, doc) {
146
+ const body = JSON.stringify({
147
+ version: MODEL_PROFILES_VERSION,
148
+ current: doc.current,
149
+ profiles: doc.profiles,
150
+ });
151
+ const dir = dirname(path);
152
+ const base = path.split('/').pop() ?? 'model-profiles.json';
153
+ const tmp = join(dir, `.${base}.tmp-${process.pid}`);
154
+ try {
155
+ mkdirSync(dir, { recursive: true });
156
+ writeFileSync(tmp, body);
157
+ renameSync(tmp, path);
158
+ return undefined;
159
+ }
160
+ catch (error) {
161
+ try {
162
+ rmSync(tmp, { force: true });
163
+ }
164
+ catch { /* best-effort cleanup of the failed tmp sibling */ }
165
+ return error instanceof Error ? error.message : String(error);
166
+ }
167
+ }
168
+ /** Find a profile by (case-insensitive) name. */
169
+ export function findProfile(doc, name) {
170
+ const needle = name.trim().toLowerCase();
171
+ return doc.profiles.find(profile => profile.name.toLowerCase() === needle);
172
+ }
173
+ /** Trim one candidate profile name; empty (or whitespace-only) is invalid. */
174
+ export function normalizeProfileName(name) {
175
+ const trimmed = name.trim();
176
+ return trimmed === '' ? undefined : trimmed;
177
+ }
178
+ /** Create a new empty profile; the name must be valid and not taken. */
179
+ export function createProfile(doc, rawName) {
180
+ const name = normalizeProfileName(rawName);
181
+ if (name === undefined)
182
+ return { error: 'profile name must not be empty' };
183
+ if (findProfile(doc, name) !== undefined)
184
+ return { error: `a profile named "${name}" already exists` };
185
+ const profile = { name, agents: {} };
186
+ doc.profiles.push(profile);
187
+ return { profile };
188
+ }
189
+ /** Rename a profile in place, keeping `current` consistent. Error or undefined. */
190
+ export function renameProfile(doc, from, to) {
191
+ const profile = findProfile(doc, from);
192
+ if (profile === undefined)
193
+ return `no profile named "${from}"`;
194
+ const name = normalizeProfileName(to);
195
+ if (name === undefined)
196
+ return 'profile name must not be empty';
197
+ if (name.toLowerCase() !== profile.name.toLowerCase() && findProfile(doc, name) !== undefined) {
198
+ return `a profile named "${name}" already exists`;
199
+ }
200
+ if (doc.current === profile.name)
201
+ doc.current = name;
202
+ profile.name = name;
203
+ return undefined;
204
+ }
205
+ /** Delete a profile; the last remaining one is refused (the store never goes empty). */
206
+ export function deleteProfile(doc, name) {
207
+ const index = doc.profiles.findIndex(profile => profile.name.toLowerCase() === name.trim().toLowerCase());
208
+ if (index < 0)
209
+ return `no profile named "${name}"`;
210
+ if (doc.profiles.length <= 1)
211
+ return 'the last profile cannot be deleted';
212
+ const [removed] = doc.profiles.splice(index, 1);
213
+ if (doc.current === removed.name)
214
+ doc.current = undefined;
215
+ return undefined;
216
+ }
217
+ /**
218
+ * Record every discovered agent's model/thinking into a fresh agents map —
219
+ * the "save current" capture. Inherit agents become EMPTY entries so the
220
+ * snapshot is complete: applying it later restores inherit where inherit
221
+ * was (see the module header's snapshot semantics).
222
+ */
223
+ export function captureAgentsSnapshot(agents) {
224
+ const snapshot = {};
225
+ for (const agent of agents) {
226
+ snapshot[agent.meta.name] = {
227
+ ...(agent.meta.model !== undefined ? { model: agent.meta.model } : {}),
228
+ ...(agent.meta.thinking !== undefined ? { thinking: agent.meta.thinking } : {}),
229
+ };
230
+ }
231
+ return snapshot;
232
+ }
233
+ /**
234
+ * The frontmatter writes that apply `profile` onto the discovered `agents`:
235
+ * every agent LISTED in the profile gets exactly the recorded overrides
236
+ * (absent keys clear the line); agents absent from the profile are skipped
237
+ * — the profile never knew them, applying must not guess. The caller
238
+ * executes each plan through `updateAgentFrontmatter`.
239
+ */
240
+ export function planAgentApply(profile, agents) {
241
+ const planned = [];
242
+ for (const agent of agents) {
243
+ const entry = profile.agents[agent.meta.name];
244
+ if (entry === undefined)
245
+ continue;
246
+ planned.push({
247
+ agent,
248
+ updates: {
249
+ model: entry.model ?? null,
250
+ thinking: entry.thinking ?? null,
251
+ },
252
+ });
253
+ }
254
+ return planned;
255
+ }
256
+ /** `provider/model · think high` label for a route; `fallback` when unset. */
257
+ export function formatProfileRoute(route, fallback = '(not set)') {
258
+ if (route === undefined)
259
+ return fallback;
260
+ const base = `${route.provider}/${route.model}`;
261
+ return route.reasoningEffort === undefined ? base : `${base} · think ${route.reasoningEffort}`;
262
+ }
263
+ /**
264
+ * The full review dump of one profile (the `v` viewer): header, default
265
+ * model, then every discovered agent's recorded state — plus stale entries
266
+ * whose agent file no longer exists, marked so the user can prune them.
267
+ */
268
+ export function profileReviewLines(profile, agents, isCurrent = false) {
269
+ const lines = [
270
+ `Profile: ${profile.name}${isCurrent ? ' · current' : ''}`,
271
+ `Default model: ${formatProfileRoute(profile.defaultModel)}`,
272
+ ];
273
+ const discovered = new Set(agents.map(agent => agent.meta.name));
274
+ const rows = agents.map(agent => ({ name: agent.meta.name, entry: profile.agents[agent.meta.name], stale: false }));
275
+ for (const [name, entry] of Object.entries(profile.agents)) {
276
+ if (!discovered.has(name))
277
+ rows.push({ name, entry, stale: true });
278
+ }
279
+ if (rows.length === 0) {
280
+ lines.push('Agents: none recorded — pick models under /profile-cfg, or press s to save the current configuration');
281
+ return lines;
282
+ }
283
+ lines.push(`Agents (${String(rows.length)}):`);
284
+ for (const { name, entry, stale } of rows) {
285
+ const model = entry?.model ?? (entry === undefined ? '(not saved — apply leaves untouched)' : '(inherit)');
286
+ const think = entry?.thinking !== undefined ? `think ${entry.thinking}` : 'inherit';
287
+ lines.push(` ${name}${stale ? ' (file missing)' : ''} · ${model} · ${think}`);
288
+ }
289
+ return lines;
290
+ }
291
+ // ------------------------------------------------------- directory pin (.dsh-profile) --
292
+ //
293
+ // A workspace can pin itself to a profile with a `.dsh-profile` dot file: the
294
+ // nearest one found walking UP from the process cwd wins, so a subdirectory
295
+ // (or a linked worktree) may drop its own file to override a parent's. New
296
+ // sessions then assemble their initial model selection from that profile
297
+ // instead of the global agent-default-model — per-tree isolation without
298
+ // touching settings.yaml. The file is plain text: blank lines and `#`
299
+ // comments are skipped; the first remaining line, trimmed, is the profile
300
+ // name.
301
+ /** The workspace pin file name (`.nvmrc` convention). */
302
+ export const PROFILE_PIN_FILE = '.dsh-profile';
303
+ /**
304
+ * Parse the text of one `.dsh-profile` file. Returns `{ name }` — the first
305
+ * usable line, trimmed — or an error when blank/comment-only.
306
+ */
307
+ export function parseProfilePinText(text) {
308
+ for (const rawLine of text.split(/\r?\n/)) {
309
+ const line = rawLine.trim();
310
+ if (line === '' || line.startsWith('#'))
311
+ continue;
312
+ const normalized = normalizeProfileName(line);
313
+ if (normalized === undefined)
314
+ continue;
315
+ return { name: normalized };
316
+ }
317
+ return { error: 'no profile name in file' };
318
+ }
319
+ /**
320
+ * Walk up from `startDir` to the filesystem root looking for the nearest
321
+ * `.dsh-profile`. No memoization: the walk costs one stat per ancestor level,
322
+ * runs only at session assembly, and staying uncached means a hand-added or
323
+ * removed pin takes effect on the very next session in this process.
324
+ * Returns `undefined` when nothing is found.
325
+ */
326
+ export function readNearestProfilePin(startDir) {
327
+ let current = resolve(startDir);
328
+ // Bounded by the directory depth; `/`'s parent is itself.
329
+ while (true) {
330
+ const candidate = join(current, PROFILE_PIN_FILE);
331
+ try {
332
+ if (existsSync(candidate)) {
333
+ const parsed = parseProfilePinText(readFileSync(candidate, 'utf8'));
334
+ if (parsed.name !== undefined)
335
+ return { name: parsed.name, path: candidate };
336
+ }
337
+ }
338
+ catch {
339
+ // An unreadable file is treated as absent — never block session create.
340
+ }
341
+ const parent = dirname(current);
342
+ if (parent === current)
343
+ break;
344
+ current = parent;
345
+ }
346
+ return undefined;
347
+ }
348
+ /**
349
+ * Resolve the pinned profile for `startDir`: find the nearest pin file and
350
+ * look its name up in `doc`. Resolves `undefined` when unbound, the file is
351
+ * broken, or the named profile no longer exists — binding stays best-effort,
352
+ * the caller falls back to the global default.
353
+ */
354
+ export function resolvePinnedProfile(doc, startDir) {
355
+ const pin = readNearestProfilePin(startDir);
356
+ return pin === undefined ? undefined : findProfile(doc, pin.name);
357
+ }
358
+ /**
359
+ * Write `<dir>/.dsh-profile` naming `profileName`. Drops the miss memo so a
360
+ * same-process session create sees it. Resolves an error message on failure,
361
+ * else `undefined`.
362
+ */
363
+ export function writeProfilePin(dir, profileName) {
364
+ const path = join(dir, PROFILE_PIN_FILE);
365
+ try {
366
+ writeFileSync(path, `${profileName}\n`);
367
+ return undefined;
368
+ }
369
+ catch (error) {
370
+ return error instanceof Error ? error.message : String(error);
371
+ }
372
+ }
373
+ /**
374
+ * Remove `<dir>/.dsh-profile` — but only when we understand it fully: its
375
+ * sole content must be one entry line naming exactly `expectName`. A
376
+ * hand-decorated file (comments, extra entries) or a different name is
377
+ * refused so `p` can never silently destroy manual edits. Resolves an error
378
+ * message on refusal or fs failure, else `undefined`.
379
+ */
380
+ export function removeProfilePin(dir, expectName) {
381
+ const path = join(dir, PROFILE_PIN_FILE);
382
+ let text;
383
+ try {
384
+ text = readFileSync(path, 'utf8');
385
+ }
386
+ catch (error) {
387
+ return error instanceof Error ? error.message : String(error);
388
+ }
389
+ const lines = text.split(/\r?\n/).map(line => line.trim()).filter(line => line !== '' && !line.startsWith('#'));
390
+ const firstName = lines.length === 1 ? normalizeProfileName(lines[0]) : undefined;
391
+ if (firstName === undefined || firstName.toLowerCase() !== expectName.trim().toLowerCase()) {
392
+ return `refusing to remove ${PROFILE_PIN_FILE} — edited by hand (remove it yourself)`;
393
+ }
394
+ try {
395
+ rmSync(path);
396
+ return undefined;
397
+ }
398
+ catch (error) {
399
+ return error instanceof Error ? error.message : String(error);
400
+ }
401
+ }
402
+ //# sourceMappingURL=model-profiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-profiles.js","sourceRoot":"","sources":["../src/model-profiles.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * `/profile-switch` + `/profile-cfg` — user model profiles: named snapshots
3
+ * of the whole model configuration (default model + think level, every
4
+ * subagent's model/thinking) that switch in one step between contexts like
5
+ * work and personal.
6
+ *
7
+ * Layers, all on the select-panel framework (src/panels.ts):
8
+ *
9
+ * 1. `/profile-switch` switcher (TablePanel) — one row per profile (name |
10
+ * default model | think | agents), Enter applies it to the live
11
+ * selection AND the agent markdown files, Esc backs out.
12
+ * 2. `/profile-cfg` manager (TablePanel) — the roster: n new, d delete
13
+ * (double-press confirm), Enter opens the profile's fields.
14
+ * 3. profile fields (FieldPanel) — model / think / agents rows plus the
15
+ * s save-current, r rename, v review shortcuts.
16
+ * 4. agents sub-table (TablePanel) — every discovered agent's recorded
17
+ * model/thinking; Enter picks through the SAME favorites/hidden model
18
+ * table /model uses (pickModel with the shared PanelHost), i sets
19
+ * explicit inherit, t edits think alone.
20
+ *
21
+ * Every edit persists to `$DSH_HOME/model-profiles.json` immediately
22
+ * (src/model-profiles.ts). Applying a profile writes the agent frontmatter
23
+ * files (updateAgentFrontmatter) and the default model through the same
24
+ * chain as /model: live bridge selection + persistDefaultModel.
25
+ */
26
+ import type { Context } from '@deepseek-ai/cordis';
27
+ import type { ModelSelection } from '@deepseek-ai/dsh-agent';
28
+ import type { TUI } from '@earendil-works/pi-tui';
29
+ import { type TuiTheme } from './theme/index.ts';
30
+ /** Live-session handles the profile flows need (bridged in from index.ts). */
31
+ export interface ProfileDeps {
32
+ getSelection(): ModelSelection | undefined;
33
+ setSelection(selection: ModelSelection): void;
34
+ }
35
+ /**
36
+ * Open the `/profile-switch` switcher. Resolves the apply summary when a
37
+ * profile was switched, or `undefined` when cancelled / nothing applied.
38
+ *
39
+ * `p` binds the CWD to the cursor profile (writes `.dsh-profile`, see
40
+ * src/model-profiles.ts) so every NEW session in this tree auto-loads it;
41
+ * pressing `p` on the already-bound profile unpins it again — unless the pin
42
+ * lives in an ancestor or was hand-decorated, which is reported in the
43
+ * status line instead. Enter keeps its global meaning: apply now AND persist
44
+ * the global default.
45
+ */
46
+ export declare function openProfileSwitcher(ctx: Context, tui: TUI, theme: TuiTheme, deps: ProfileDeps, restoreFocus: () => void): Promise<string | undefined>;
47
+ /**
48
+ * Open the `/profile-cfg` manager. Resolves a summary when anything changed,
49
+ * or `undefined` when the manager closed untouched. `preselect` jumps
50
+ * straight into one profile's fields window.
51
+ */
52
+ export declare function openProfileManager(ctx: Context, tui: TUI, theme: TuiTheme, deps: ProfileDeps, restoreFocus: () => void, preselect?: string): Promise<string | undefined>;