@maestrofrontier/frontier 1.4.5 → 1.5.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.
Files changed (50) hide show
  1. package/.agents/plugins/marketplace.json +21 -21
  2. package/.codex-plugin/plugin.json +29 -29
  3. package/.cursorrules +197 -194
  4. package/AGENTS.md +3 -3
  5. package/README.md +368 -368
  6. package/bin/maestro.cjs +75 -75
  7. package/commands/compress.md +36 -36
  8. package/commands/frontier.md +124 -124
  9. package/commands/terse.md +23 -23
  10. package/docs/codex.md +167 -167
  11. package/docs/orchestration.md +168 -168
  12. package/frontier/cli.cjs +279 -252
  13. package/frontier/config.cjs +468 -468
  14. package/frontier/dispatch.cjs +267 -255
  15. package/frontier/judge.cjs +92 -92
  16. package/frontier/run.cjs +201 -180
  17. package/frontier/schema.cjs +112 -112
  18. package/frontier/semaphore.cjs +49 -49
  19. package/frontier/synthesize.cjs +79 -79
  20. package/hooks/frontier-autorun.cjs +127 -120
  21. package/hooks/hooks.json +103 -103
  22. package/hooks/maestro-doctrine-guard.cjs +81 -81
  23. package/hooks/maestro-gate-reminder.cjs +22 -7
  24. package/hooks/maestro-gate-telemetry.cjs +79 -77
  25. package/hooks/maestro-phase-scope.cjs +118 -118
  26. package/hooks/maestro-statusline-sync.cjs +152 -152
  27. package/hooks/maestro-subagent-guard.cjs +148 -148
  28. package/hooks/maestro-terse-mode.cjs +189 -189
  29. package/hooks/maestro-toolbudget-advisory.cjs +127 -127
  30. package/integrations/README.md +111 -111
  31. package/integrations/cline/skills/frontier/SKILL.md +75 -75
  32. package/integrations/codex/prompts/frontier.md +70 -70
  33. package/integrations/codex/prompts/update.md +39 -39
  34. package/integrations/codex/skills/maestro-frontier/SKILL.md +122 -122
  35. package/integrations/codex/skills/maestro-settings/SKILL.md +55 -55
  36. package/integrations/codex/skills/maestro-terse/SKILL.md +58 -58
  37. package/integrations/codex/skills/maestro-update/SKILL.md +31 -31
  38. package/integrations/cursor/commands/frontier.md +63 -63
  39. package/integrations/cursor/commands/update.md +34 -34
  40. package/integrations/gemini/commands/frontier.toml +76 -76
  41. package/integrations/windsurf/workflows/frontier.md +70 -70
  42. package/package.json +58 -58
  43. package/scripts/install.cjs +1014 -1014
  44. package/settings/cli.cjs +140 -140
  45. package/settings/config.cjs +309 -309
  46. package/skills/maestro-frontier/SKILL.md +122 -122
  47. package/skills/maestro-settings/SKILL.md +55 -55
  48. package/skills/maestro-terse/SKILL.md +58 -58
  49. package/skills/maestro-update/SKILL.md +31 -31
  50. package/skills/terse/SKILL.md +74 -74
@@ -1,309 +1,309 @@
1
- #!/usr/bin/env node
2
- // Maestro Settings — aggregating front-end over the three existing toggle
3
- // stores. Reads and writes terse (config.json terseLevel + live flag),
4
- // frontier (frontier-state.json, via frontier/config.cjs), and context-bar
5
- // (.context-bar-disabled next to the status-line script). It owns no state
6
- // of its own; the existing readers stay the source of truth. Zero deps, CJS.
7
- //
8
- // Hardened I/O (atomic temp+rename, O_NOFOLLOW, 0600, symlink refusal,
9
- // size-capped reads, whitelist validation) is ported from
10
- // frontier/config.cjs and hooks/maestro-terse-mode.cjs. See
11
- // docs/settings-design.md for the full design.
12
-
13
- 'use strict';
14
-
15
- const fs = require('fs');
16
- const os = require('os');
17
- const path = require('path');
18
- const frontier = require('../frontier/config.cjs');
19
-
20
- const TERSE_LEVELS = ['off', 'lite', 'full', 'ultra'];
21
- const FLAG_LEVELS = ['lite', 'full', 'ultra']; // 'off' = remove the flag
22
- const MAX_CONFIG_BYTES = 1 << 16; // 64 KB cap for config.json / settings.json
23
-
24
- // Human labels for model ids. Presentation only — the model SET is always
25
- // sourced from frontier DEFAULTS.adapters so there is one source of truth;
26
- // an id with no label here falls back to the id itself.
27
- const MODEL_LABELS = {
28
- opus: 'Opus 4.8',
29
- 'gpt-5.5': 'GPT-5.5 (Codex)',
30
- gemini: 'Gemini 3.1 Pro',
31
- };
32
-
33
- // ---------- directory resolvers ----------
34
-
35
- function claudeDir() {
36
- return process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');
37
- }
38
- function terseFlagPath() { return path.join(claudeDir(), '.maestro-terse'); }
39
- function configJsonPath() { return path.join(frontier.configDir(), 'config.json'); }
40
-
41
- // ---------- hardened low-level I/O ----------
42
-
43
- function safeWrite(targetPath, contents) {
44
- try {
45
- const dir = path.dirname(targetPath);
46
- fs.mkdirSync(dir, { recursive: true });
47
- try { if (fs.lstatSync(dir).isSymbolicLink()) return false; } catch { return false; }
48
- try {
49
- if (fs.lstatSync(targetPath).isSymbolicLink()) return false;
50
- } catch (e) {
51
- if (e.code !== 'ENOENT') return false;
52
- }
53
- const tempPath = path.join(dir, '.' + path.basename(targetPath) + '.' + process.pid + '.' + Date.now() + '.tmp');
54
- const O_NOFOLLOW = typeof fs.constants.O_NOFOLLOW === 'number' ? fs.constants.O_NOFOLLOW : 0;
55
- const flags = fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL | O_NOFOLLOW;
56
- let fd;
57
- try {
58
- if (O_NOFOLLOW === 0) { try { if (fs.lstatSync(tempPath).isSymbolicLink()) return false; } catch {} }
59
- fd = fs.openSync(tempPath, flags, 0o600);
60
- fs.writeSync(fd, contents);
61
- try { fs.fchmodSync(fd, 0o600); } catch {}
62
- } finally {
63
- if (fd !== undefined) fs.closeSync(fd);
64
- }
65
- fs.renameSync(tempPath, targetPath);
66
- return true;
67
- } catch {
68
- return false;
69
- }
70
- }
71
-
72
- function safeRead(targetPath, maxBytes) {
73
- try {
74
- let st;
75
- try { st = fs.lstatSync(targetPath); } catch { return null; }
76
- if (st.isSymbolicLink() || !st.isFile()) return null;
77
- if (st.size > maxBytes) return null;
78
- const O_NOFOLLOW = typeof fs.constants.O_NOFOLLOW === 'number' ? fs.constants.O_NOFOLLOW : 0;
79
- let fd, out;
80
- try {
81
- if (O_NOFOLLOW === 0) { try { if (fs.lstatSync(targetPath).isSymbolicLink()) return null; } catch {} }
82
- fd = fs.openSync(targetPath, fs.constants.O_RDONLY | O_NOFOLLOW);
83
- const buf = Buffer.alloc(maxBytes);
84
- const n = fs.readSync(fd, buf, 0, maxBytes, 0);
85
- out = buf.slice(0, n).toString('utf8');
86
- } finally {
87
- if (fd !== undefined) fs.closeSync(fd);
88
- }
89
- return out;
90
- } catch {
91
- return null;
92
- }
93
- }
94
-
95
- // ---------- terse ----------
96
-
97
- function readTerse() {
98
- const env = String(process.env.MAESTRO_TERSE_LEVEL || '').toLowerCase();
99
- const envValid = TERSE_LEVELS.includes(env);
100
- let configLevel = null;
101
- const raw = safeRead(configJsonPath(), MAX_CONFIG_BYTES);
102
- if (raw) {
103
- try {
104
- const c = JSON.parse(raw);
105
- const v = String((c && c.terseLevel) || '').toLowerCase();
106
- if (TERSE_LEVELS.includes(v)) configLevel = v;
107
- } catch {}
108
- }
109
- let level, source;
110
- if (envValid) { level = env; source = 'env'; }
111
- else if (configLevel) { level = configLevel; source = 'config'; }
112
- else { level = 'off'; source = 'default'; }
113
- return { level, source, envOverride: envValid ? env : null, configLevel: configLevel || 'off' };
114
- }
115
-
116
- function setTerse(level) {
117
- const lvl = String(level == null ? '' : level).toLowerCase();
118
- if (!TERSE_LEVELS.includes(lvl)) {
119
- return { ok: false, error: 'terse level must be off, lite, full, or ultra' };
120
- }
121
- let cfg = {};
122
- const raw = safeRead(configJsonPath(), MAX_CONFIG_BYTES);
123
- if (raw) {
124
- try {
125
- const parsed = JSON.parse(raw);
126
- if (parsed && typeof parsed === 'object') cfg = parsed;
127
- } catch {
128
- return { ok: false, error: 'config.json exists but is not valid JSON; refusing to overwrite it' };
129
- }
130
- }
131
- cfg.terseLevel = lvl;
132
- if (!safeWrite(configJsonPath(), JSON.stringify(cfg, null, 2))) {
133
- return { ok: false, error: 'failed to write config.json' };
134
- }
135
- if (FLAG_LEVELS.includes(lvl)) safeWrite(terseFlagPath(), lvl);
136
- else { try { fs.unlinkSync(terseFlagPath()); } catch {} }
137
- const env = String(process.env.MAESTRO_TERSE_LEVEL || '').toLowerCase();
138
- const warning = TERSE_LEVELS.includes(env)
139
- ? 'MAESTRO_TERSE_LEVEL=' + env + ' is set in the environment and overrides this until unset'
140
- : null;
141
- return { ok: true, warning };
142
- }
143
-
144
- // ---------- context-bar ----------
145
-
146
- function expandHome(p) {
147
- if (p === '~') return os.homedir();
148
- if (p && (p.startsWith('~/') || p.startsWith('~\\'))) return path.join(os.homedir(), p.slice(2));
149
- return p;
150
- }
151
-
152
- function resolveStatuslineDir() {
153
- const base = claudeDir();
154
- const fallback = { dir: path.join(base, 'statusline'), scriptOk: false, resolved: false };
155
- const raw = safeRead(path.join(base, 'settings.json'), MAX_CONFIG_BYTES);
156
- if (!raw) return fallback;
157
- let cmd = null;
158
- try {
159
- const s = JSON.parse(raw);
160
- const sl = s && s.statusLine;
161
- cmd = typeof sl === 'string' ? sl : (sl && typeof sl === 'object' ? sl.command : null);
162
- } catch {
163
- return fallback;
164
- }
165
- if (!cmd || typeof cmd !== 'string') return fallback;
166
- const tokens = cmd.split(/\s+/).map(t => t.replace(/^["']|["']$/g, '')).filter(Boolean);
167
- let tok = tokens.find(t => /context-bar(\.(ps1|sh|cmd|bat|js|cjs))?$/i.test(path.basename(t)));
168
- if (!tok) tok = tokens.find(t => /[\\/]/.test(t) || /\.(ps1|sh|cmd|bat|js|cjs)$/i.test(t));
169
- if (!tok) return fallback;
170
- const expanded = expandHome(tok);
171
- return {
172
- dir: path.dirname(expanded),
173
- scriptOk: /context-bar/i.test(path.basename(expanded)),
174
- resolved: true,
175
- };
176
- }
177
-
178
- function readContextBar() {
179
- const r = resolveStatuslineDir();
180
- const flag = path.join(r.dir, '.context-bar-disabled');
181
- let present = false;
182
- try { present = fs.statSync(flag).isFile(); } catch {}
183
- return { enabled: !present, dir: r.dir, flagPath: flag, scriptConfirmed: r.scriptOk, resolved: r.resolved };
184
- }
185
-
186
- function setContextBar(enabled) {
187
- const r = resolveStatuslineDir();
188
- const flag = path.join(r.dir, '.context-bar-disabled');
189
- if (enabled) {
190
- try { fs.unlinkSync(flag); } catch {}
191
- } else {
192
- if (!safeWrite(flag, '')) return { ok: false, error: 'failed to write context-bar flag at ' + flag };
193
- }
194
- const warning = r.scriptOk
195
- ? null
196
- : 'could not confirm the status line is the Maestro context bar; using ' + r.dir;
197
- return { ok: true, warning };
198
- }
199
-
200
- // ---------- frontier (delegated to frontier/config.cjs) ----------
201
-
202
- function readFrontier(scope) { return frontier.loadState(scope); }
203
-
204
- function saveFrontier(state, scope) {
205
- return frontier.saveState(state, scope)
206
- ? { ok: true, warning: null }
207
- : { ok: false, error: 'failed to write frontier state' };
208
- }
209
-
210
- function setFrontier(spec, opts) {
211
- opts = opts || {};
212
- const s = String(spec == null ? '' : spec).trim();
213
- const idx = s.indexOf(':');
214
- const head = (idx === -1 ? s : s.slice(0, idx)).toLowerCase();
215
- const tail = idx === -1 ? '' : s.slice(idx + 1).trim();
216
-
217
- if (head === 'off' || head === '') return saveFrontier({ mode: 'off' }, opts.scope);
218
-
219
- if (head === 'single') {
220
- const model = (tail || opts.model || '').trim();
221
- if (!frontier.validateModel(model)) return { ok: false, error: 'unknown model: ' + (model || '(none)') };
222
- return saveFrontier({ mode: 'single', model }, opts.scope);
223
- }
224
-
225
- if (head === 'fusion') {
226
- const preset = (tail || opts.preset || '').trim();
227
- if (!frontier.validatePreset(preset)) return { ok: false, error: 'unknown preset: ' + (preset || '(none)') };
228
- const state = { mode: 'fusion', preset };
229
- if (preset === 'custom') {
230
- const models = Array.isArray(opts.models)
231
- ? opts.models
232
- : String(opts.models || '').split(',').map(m => m.trim()).filter(Boolean);
233
- if (models.length === 0) return { ok: false, error: 'custom preset requires --models a,b,c' };
234
- if (models.length > 8) return { ok: false, error: 'custom preset exceeds the 8-model limit' };
235
- const unknown = models.filter(m => !frontier.validateModel(m));
236
- if (unknown.length) return { ok: false, error: 'unknown model(s): ' + unknown.join(', ') };
237
- state.models = models;
238
- }
239
- if (opts.judge != null) {
240
- if (!frontier.validateModel(opts.judge)) return { ok: false, error: 'unknown judge model: ' + opts.judge };
241
- state.judgeModel = opts.judge;
242
- }
243
- if (opts.synth != null) {
244
- if (!frontier.validateModel(opts.synth)) return { ok: false, error: 'unknown synth model: ' + opts.synth };
245
- state.synthModel = opts.synth;
246
- }
247
- return saveFrontier(state, opts.scope);
248
- }
249
-
250
- return { ok: false, error: 'frontier value must be off, single:<model>, or fusion:<preset>' };
251
- }
252
-
253
- // ---------- aggregate ----------
254
-
255
- function readAll(scope) {
256
- return { terse: readTerse(), frontier: readFrontier(scope), contextBar: readContextBar() };
257
- }
258
-
259
- // The available-values catalog: every toggle value a picker can offer. The
260
- // frontier model/preset SET is sourced from frontier DEFAULTS so there is no
261
- // second list to drift; `custom` is the one preset value frontier accepts
262
- // that is not a DEFAULTS preset (validatePreset special-cases it).
263
- function catalog() {
264
- const cfg = frontier.DEFAULTS;
265
- const models = Object.keys(cfg.adapters).map(id => ({ id, label: MODEL_LABELS[id] || id }));
266
- const presets = Object.keys(cfg.presets).map(id => ({ id, models: cfg.presets[id].slice() }));
267
- presets.push({ id: 'custom', models: null });
268
- return {
269
- terse: { key: 'terse', values: TERSE_LEVELS.slice() },
270
- frontier: {
271
- modes: ['off', 'single', 'fusion'],
272
- models,
273
- presets,
274
- stageModels: models.map(m => m.id),
275
- defaults: { judge: cfg.judgeModel, synth: cfg.synthModel },
276
- presetStages: cfg.presetStages || {},
277
- },
278
- contextBar: { key: 'context-bar', values: ['on', 'off'] },
279
- };
280
- }
281
-
282
- function setKey(key, value, opts) {
283
- const k = String(key == null ? '' : key).toLowerCase();
284
- if (k === 'terse') return setTerse(value);
285
- if (k === 'frontier') return setFrontier(value, opts);
286
- if (k === 'context-bar' || k === 'contextbar' || k === 'bar') {
287
- const v = String(value == null ? '' : value).toLowerCase();
288
- if (v !== 'on' && v !== 'off') return { ok: false, error: 'context-bar value must be on or off' };
289
- return setContextBar(v === 'on');
290
- }
291
- return { ok: false, error: 'unknown key: ' + key + ' (use terse, frontier, or context-bar)' };
292
- }
293
-
294
- module.exports = {
295
- TERSE_LEVELS,
296
- claudeDir,
297
- terseFlagPath,
298
- configJsonPath,
299
- resolveStatuslineDir,
300
- readTerse,
301
- setTerse,
302
- readFrontier,
303
- setFrontier,
304
- readContextBar,
305
- setContextBar,
306
- readAll,
307
- catalog,
308
- setKey,
309
- };
1
+ #!/usr/bin/env node
2
+ // Maestro Settings — aggregating front-end over the three existing toggle
3
+ // stores. Reads and writes terse (config.json terseLevel + live flag),
4
+ // frontier (frontier-state.json, via frontier/config.cjs), and context-bar
5
+ // (.context-bar-disabled next to the status-line script). It owns no state
6
+ // of its own; the existing readers stay the source of truth. Zero deps, CJS.
7
+ //
8
+ // Hardened I/O (atomic temp+rename, O_NOFOLLOW, 0600, symlink refusal,
9
+ // size-capped reads, whitelist validation) is ported from
10
+ // frontier/config.cjs and hooks/maestro-terse-mode.cjs. See
11
+ // docs/settings-design.md for the full design.
12
+
13
+ 'use strict';
14
+
15
+ const fs = require('fs');
16
+ const os = require('os');
17
+ const path = require('path');
18
+ const frontier = require('../frontier/config.cjs');
19
+
20
+ const TERSE_LEVELS = ['off', 'lite', 'full', 'ultra'];
21
+ const FLAG_LEVELS = ['lite', 'full', 'ultra']; // 'off' = remove the flag
22
+ const MAX_CONFIG_BYTES = 1 << 16; // 64 KB cap for config.json / settings.json
23
+
24
+ // Human labels for model ids. Presentation only — the model SET is always
25
+ // sourced from frontier DEFAULTS.adapters so there is one source of truth;
26
+ // an id with no label here falls back to the id itself.
27
+ const MODEL_LABELS = {
28
+ opus: 'Opus 4.8',
29
+ 'gpt-5.5': 'GPT-5.5 (Codex)',
30
+ gemini: 'Gemini 3.1 Pro',
31
+ };
32
+
33
+ // ---------- directory resolvers ----------
34
+
35
+ function claudeDir() {
36
+ return process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');
37
+ }
38
+ function terseFlagPath() { return path.join(claudeDir(), '.maestro-terse'); }
39
+ function configJsonPath() { return path.join(frontier.configDir(), 'config.json'); }
40
+
41
+ // ---------- hardened low-level I/O ----------
42
+
43
+ function safeWrite(targetPath, contents) {
44
+ try {
45
+ const dir = path.dirname(targetPath);
46
+ fs.mkdirSync(dir, { recursive: true });
47
+ try { if (fs.lstatSync(dir).isSymbolicLink()) return false; } catch { return false; }
48
+ try {
49
+ if (fs.lstatSync(targetPath).isSymbolicLink()) return false;
50
+ } catch (e) {
51
+ if (e.code !== 'ENOENT') return false;
52
+ }
53
+ const tempPath = path.join(dir, '.' + path.basename(targetPath) + '.' + process.pid + '.' + Date.now() + '.tmp');
54
+ const O_NOFOLLOW = typeof fs.constants.O_NOFOLLOW === 'number' ? fs.constants.O_NOFOLLOW : 0;
55
+ const flags = fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL | O_NOFOLLOW;
56
+ let fd;
57
+ try {
58
+ if (O_NOFOLLOW === 0) { try { if (fs.lstatSync(tempPath).isSymbolicLink()) return false; } catch {} }
59
+ fd = fs.openSync(tempPath, flags, 0o600);
60
+ fs.writeSync(fd, contents);
61
+ try { fs.fchmodSync(fd, 0o600); } catch {}
62
+ } finally {
63
+ if (fd !== undefined) fs.closeSync(fd);
64
+ }
65
+ fs.renameSync(tempPath, targetPath);
66
+ return true;
67
+ } catch {
68
+ return false;
69
+ }
70
+ }
71
+
72
+ function safeRead(targetPath, maxBytes) {
73
+ try {
74
+ let st;
75
+ try { st = fs.lstatSync(targetPath); } catch { return null; }
76
+ if (st.isSymbolicLink() || !st.isFile()) return null;
77
+ if (st.size > maxBytes) return null;
78
+ const O_NOFOLLOW = typeof fs.constants.O_NOFOLLOW === 'number' ? fs.constants.O_NOFOLLOW : 0;
79
+ let fd, out;
80
+ try {
81
+ if (O_NOFOLLOW === 0) { try { if (fs.lstatSync(targetPath).isSymbolicLink()) return null; } catch {} }
82
+ fd = fs.openSync(targetPath, fs.constants.O_RDONLY | O_NOFOLLOW);
83
+ const buf = Buffer.alloc(maxBytes);
84
+ const n = fs.readSync(fd, buf, 0, maxBytes, 0);
85
+ out = buf.slice(0, n).toString('utf8');
86
+ } finally {
87
+ if (fd !== undefined) fs.closeSync(fd);
88
+ }
89
+ return out;
90
+ } catch {
91
+ return null;
92
+ }
93
+ }
94
+
95
+ // ---------- terse ----------
96
+
97
+ function readTerse() {
98
+ const env = String(process.env.MAESTRO_TERSE_LEVEL || '').toLowerCase();
99
+ const envValid = TERSE_LEVELS.includes(env);
100
+ let configLevel = null;
101
+ const raw = safeRead(configJsonPath(), MAX_CONFIG_BYTES);
102
+ if (raw) {
103
+ try {
104
+ const c = JSON.parse(raw);
105
+ const v = String((c && c.terseLevel) || '').toLowerCase();
106
+ if (TERSE_LEVELS.includes(v)) configLevel = v;
107
+ } catch {}
108
+ }
109
+ let level, source;
110
+ if (envValid) { level = env; source = 'env'; }
111
+ else if (configLevel) { level = configLevel; source = 'config'; }
112
+ else { level = 'off'; source = 'default'; }
113
+ return { level, source, envOverride: envValid ? env : null, configLevel: configLevel || 'off' };
114
+ }
115
+
116
+ function setTerse(level) {
117
+ const lvl = String(level == null ? '' : level).toLowerCase();
118
+ if (!TERSE_LEVELS.includes(lvl)) {
119
+ return { ok: false, error: 'terse level must be off, lite, full, or ultra' };
120
+ }
121
+ let cfg = {};
122
+ const raw = safeRead(configJsonPath(), MAX_CONFIG_BYTES);
123
+ if (raw) {
124
+ try {
125
+ const parsed = JSON.parse(raw);
126
+ if (parsed && typeof parsed === 'object') cfg = parsed;
127
+ } catch {
128
+ return { ok: false, error: 'config.json exists but is not valid JSON; refusing to overwrite it' };
129
+ }
130
+ }
131
+ cfg.terseLevel = lvl;
132
+ if (!safeWrite(configJsonPath(), JSON.stringify(cfg, null, 2))) {
133
+ return { ok: false, error: 'failed to write config.json' };
134
+ }
135
+ if (FLAG_LEVELS.includes(lvl)) safeWrite(terseFlagPath(), lvl);
136
+ else { try { fs.unlinkSync(terseFlagPath()); } catch {} }
137
+ const env = String(process.env.MAESTRO_TERSE_LEVEL || '').toLowerCase();
138
+ const warning = TERSE_LEVELS.includes(env)
139
+ ? 'MAESTRO_TERSE_LEVEL=' + env + ' is set in the environment and overrides this until unset'
140
+ : null;
141
+ return { ok: true, warning };
142
+ }
143
+
144
+ // ---------- context-bar ----------
145
+
146
+ function expandHome(p) {
147
+ if (p === '~') return os.homedir();
148
+ if (p && (p.startsWith('~/') || p.startsWith('~\\'))) return path.join(os.homedir(), p.slice(2));
149
+ return p;
150
+ }
151
+
152
+ function resolveStatuslineDir() {
153
+ const base = claudeDir();
154
+ const fallback = { dir: path.join(base, 'statusline'), scriptOk: false, resolved: false };
155
+ const raw = safeRead(path.join(base, 'settings.json'), MAX_CONFIG_BYTES);
156
+ if (!raw) return fallback;
157
+ let cmd = null;
158
+ try {
159
+ const s = JSON.parse(raw);
160
+ const sl = s && s.statusLine;
161
+ cmd = typeof sl === 'string' ? sl : (sl && typeof sl === 'object' ? sl.command : null);
162
+ } catch {
163
+ return fallback;
164
+ }
165
+ if (!cmd || typeof cmd !== 'string') return fallback;
166
+ const tokens = cmd.split(/\s+/).map(t => t.replace(/^["']|["']$/g, '')).filter(Boolean);
167
+ let tok = tokens.find(t => /context-bar(\.(ps1|sh|cmd|bat|js|cjs))?$/i.test(path.basename(t)));
168
+ if (!tok) tok = tokens.find(t => /[\\/]/.test(t) || /\.(ps1|sh|cmd|bat|js|cjs)$/i.test(t));
169
+ if (!tok) return fallback;
170
+ const expanded = expandHome(tok);
171
+ return {
172
+ dir: path.dirname(expanded),
173
+ scriptOk: /context-bar/i.test(path.basename(expanded)),
174
+ resolved: true,
175
+ };
176
+ }
177
+
178
+ function readContextBar() {
179
+ const r = resolveStatuslineDir();
180
+ const flag = path.join(r.dir, '.context-bar-disabled');
181
+ let present = false;
182
+ try { present = fs.statSync(flag).isFile(); } catch {}
183
+ return { enabled: !present, dir: r.dir, flagPath: flag, scriptConfirmed: r.scriptOk, resolved: r.resolved };
184
+ }
185
+
186
+ function setContextBar(enabled) {
187
+ const r = resolveStatuslineDir();
188
+ const flag = path.join(r.dir, '.context-bar-disabled');
189
+ if (enabled) {
190
+ try { fs.unlinkSync(flag); } catch {}
191
+ } else {
192
+ if (!safeWrite(flag, '')) return { ok: false, error: 'failed to write context-bar flag at ' + flag };
193
+ }
194
+ const warning = r.scriptOk
195
+ ? null
196
+ : 'could not confirm the status line is the Maestro context bar; using ' + r.dir;
197
+ return { ok: true, warning };
198
+ }
199
+
200
+ // ---------- frontier (delegated to frontier/config.cjs) ----------
201
+
202
+ function readFrontier(scope) { return frontier.loadState(scope); }
203
+
204
+ function saveFrontier(state, scope) {
205
+ return frontier.saveState(state, scope)
206
+ ? { ok: true, warning: null }
207
+ : { ok: false, error: 'failed to write frontier state' };
208
+ }
209
+
210
+ function setFrontier(spec, opts) {
211
+ opts = opts || {};
212
+ const s = String(spec == null ? '' : spec).trim();
213
+ const idx = s.indexOf(':');
214
+ const head = (idx === -1 ? s : s.slice(0, idx)).toLowerCase();
215
+ const tail = idx === -1 ? '' : s.slice(idx + 1).trim();
216
+
217
+ if (head === 'off' || head === '') return saveFrontier({ mode: 'off' }, opts.scope);
218
+
219
+ if (head === 'single') {
220
+ const model = (tail || opts.model || '').trim();
221
+ if (!frontier.validateModel(model)) return { ok: false, error: 'unknown model: ' + (model || '(none)') };
222
+ return saveFrontier({ mode: 'single', model }, opts.scope);
223
+ }
224
+
225
+ if (head === 'fusion') {
226
+ const preset = (tail || opts.preset || '').trim();
227
+ if (!frontier.validatePreset(preset)) return { ok: false, error: 'unknown preset: ' + (preset || '(none)') };
228
+ const state = { mode: 'fusion', preset };
229
+ if (preset === 'custom') {
230
+ const models = Array.isArray(opts.models)
231
+ ? opts.models
232
+ : String(opts.models || '').split(',').map(m => m.trim()).filter(Boolean);
233
+ if (models.length === 0) return { ok: false, error: 'custom preset requires --models a,b,c' };
234
+ if (models.length > 8) return { ok: false, error: 'custom preset exceeds the 8-model limit' };
235
+ const unknown = models.filter(m => !frontier.validateModel(m));
236
+ if (unknown.length) return { ok: false, error: 'unknown model(s): ' + unknown.join(', ') };
237
+ state.models = models;
238
+ }
239
+ if (opts.judge != null) {
240
+ if (!frontier.validateModel(opts.judge)) return { ok: false, error: 'unknown judge model: ' + opts.judge };
241
+ state.judgeModel = opts.judge;
242
+ }
243
+ if (opts.synth != null) {
244
+ if (!frontier.validateModel(opts.synth)) return { ok: false, error: 'unknown synth model: ' + opts.synth };
245
+ state.synthModel = opts.synth;
246
+ }
247
+ return saveFrontier(state, opts.scope);
248
+ }
249
+
250
+ return { ok: false, error: 'frontier value must be off, single:<model>, or fusion:<preset>' };
251
+ }
252
+
253
+ // ---------- aggregate ----------
254
+
255
+ function readAll(scope) {
256
+ return { terse: readTerse(), frontier: readFrontier(scope), contextBar: readContextBar() };
257
+ }
258
+
259
+ // The available-values catalog: every toggle value a picker can offer. The
260
+ // frontier model/preset SET is sourced from frontier DEFAULTS so there is no
261
+ // second list to drift; `custom` is the one preset value frontier accepts
262
+ // that is not a DEFAULTS preset (validatePreset special-cases it).
263
+ function catalog() {
264
+ const cfg = frontier.DEFAULTS;
265
+ const models = Object.keys(cfg.adapters).map(id => ({ id, label: MODEL_LABELS[id] || id }));
266
+ const presets = Object.keys(cfg.presets).map(id => ({ id, models: cfg.presets[id].slice() }));
267
+ presets.push({ id: 'custom', models: null });
268
+ return {
269
+ terse: { key: 'terse', values: TERSE_LEVELS.slice() },
270
+ frontier: {
271
+ modes: ['off', 'single', 'fusion'],
272
+ models,
273
+ presets,
274
+ stageModels: models.map(m => m.id),
275
+ defaults: { judge: cfg.judgeModel, synth: cfg.synthModel },
276
+ presetStages: cfg.presetStages || {},
277
+ },
278
+ contextBar: { key: 'context-bar', values: ['on', 'off'] },
279
+ };
280
+ }
281
+
282
+ function setKey(key, value, opts) {
283
+ const k = String(key == null ? '' : key).toLowerCase();
284
+ if (k === 'terse') return setTerse(value);
285
+ if (k === 'frontier') return setFrontier(value, opts);
286
+ if (k === 'context-bar' || k === 'contextbar' || k === 'bar') {
287
+ const v = String(value == null ? '' : value).toLowerCase();
288
+ if (v !== 'on' && v !== 'off') return { ok: false, error: 'context-bar value must be on or off' };
289
+ return setContextBar(v === 'on');
290
+ }
291
+ return { ok: false, error: 'unknown key: ' + key + ' (use terse, frontier, or context-bar)' };
292
+ }
293
+
294
+ module.exports = {
295
+ TERSE_LEVELS,
296
+ claudeDir,
297
+ terseFlagPath,
298
+ configJsonPath,
299
+ resolveStatuslineDir,
300
+ readTerse,
301
+ setTerse,
302
+ readFrontier,
303
+ setFrontier,
304
+ readContextBar,
305
+ setContextBar,
306
+ readAll,
307
+ catalog,
308
+ setKey,
309
+ };