@maestrofrontier/frontier 1.4.4 → 1.4.5
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/.agents/plugins/marketplace.json +21 -0
- package/.codex-plugin/plugin.json +29 -0
- package/AGENTS.md +214 -214
- package/CLAUDE.md +29 -29
- package/README.md +112 -22
- package/docs/codex.md +81 -12
- package/frontier/cli.cjs +10 -6
- package/frontier/config.cjs +41 -14
- package/frontier/run.cjs +33 -1
- package/hooks/frontier-autorun.cjs +2 -6
- package/hooks/hooks.json +1 -1
- package/integrations/README.md +51 -34
- package/integrations/codex/prompts/frontier.md +22 -18
- package/integrations/codex/prompts/update.md +3 -0
- package/integrations/codex/skills/maestro-frontier/SKILL.md +122 -0
- package/integrations/codex/skills/{settings → maestro-settings}/SKILL.md +15 -6
- package/integrations/codex/skills/{terse → maestro-terse}/SKILL.md +15 -6
- package/integrations/codex/skills/maestro-update/SKILL.md +31 -0
- package/package.json +4 -1
- package/scripts/install.cjs +424 -15
- package/settings/cli.cjs +1 -1
- package/skills/maestro-frontier/SKILL.md +122 -0
- package/skills/maestro-settings/SKILL.md +55 -0
- package/skills/maestro-terse/SKILL.md +58 -0
- package/skills/maestro-update/SKILL.md +31 -0
- package/skills/terse/SKILL.md +74 -0
- package/integrations/codex/skills/frontier/SKILL.md +0 -91
- package/integrations/codex/skills/update/SKILL.md +0 -29
package/scripts/install.cjs
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
const fs = require('fs');
|
|
12
12
|
const os = require('os');
|
|
13
13
|
const path = require('path');
|
|
14
|
+
const crypto = require('crypto');
|
|
14
15
|
|
|
15
16
|
// ---- constants ----
|
|
16
17
|
|
|
@@ -25,7 +26,7 @@ const WRAPPER_MAP = {
|
|
|
25
26
|
codex: {
|
|
26
27
|
src: 'integrations/codex/prompts/frontier.md',
|
|
27
28
|
proj: '.codex/prompts/frontier.md',
|
|
28
|
-
user: path.join(
|
|
29
|
+
user: () => path.join(homeDir(), '.codex', 'prompts', 'frontier.md'),
|
|
29
30
|
},
|
|
30
31
|
cursor: {
|
|
31
32
|
src: 'integrations/cursor/commands/frontier.md',
|
|
@@ -35,24 +36,252 @@ const WRAPPER_MAP = {
|
|
|
35
36
|
gemini: {
|
|
36
37
|
src: 'integrations/gemini/commands/frontier.toml',
|
|
37
38
|
proj: '.gemini/commands/frontier.toml',
|
|
38
|
-
user: path.join(
|
|
39
|
+
user: () => path.join(homeDir(), '.gemini', 'commands', 'frontier.toml'),
|
|
39
40
|
},
|
|
40
41
|
cline: {
|
|
41
42
|
src: 'integrations/cline/skills/frontier/SKILL.md',
|
|
42
43
|
proj: '.cline/skills/frontier/SKILL.md',
|
|
43
|
-
user: path.join(
|
|
44
|
+
user: () => path.join(homeDir(), '.cline', 'skills', 'frontier', 'SKILL.md'),
|
|
44
45
|
},
|
|
45
46
|
windsurf: {
|
|
46
47
|
src: 'integrations/windsurf/workflows/frontier.md',
|
|
47
48
|
proj: '.windsurf/workflows/frontier.md',
|
|
48
|
-
user: path.join(
|
|
49
|
+
user: () => path.join(homeDir(), '.codeium', 'windsurf', 'global_workflows', 'frontier.md'),
|
|
49
50
|
},
|
|
50
51
|
};
|
|
51
52
|
|
|
52
53
|
// Codex skill templates installed alongside the deprecated codex wrapper.
|
|
53
54
|
// Codex loads skills from <project>/.agents/skills/<name>/SKILL.md (project)
|
|
54
|
-
// or ~/.agents/skills/<name>/SKILL.md (global).
|
|
55
|
-
|
|
55
|
+
// or ~/.agents/skills/<name>/SKILL.md (global). Maestro-owned skills are
|
|
56
|
+
// refreshed when still managed; user-edited copies are preserved.
|
|
57
|
+
const CODEX_SKILLS = [
|
|
58
|
+
{ name: 'maestro-frontier', legacy: 'frontier' },
|
|
59
|
+
{ name: 'maestro-terse', legacy: 'terse' },
|
|
60
|
+
{ name: 'maestro-settings', legacy: 'settings' },
|
|
61
|
+
{ name: 'maestro-update', legacy: 'update' },
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
const LEGACY_CODEX_SKILL_TEMPLATES = {
|
|
65
|
+
frontier: `---
|
|
66
|
+
name: frontier
|
|
67
|
+
description: Maestro Frontier local multi-CLI fusion engine — switch mode, or run a prompt through the panel
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
Drive the **Maestro Frontier** engine — a zero-dependency local multi-CLI fusion
|
|
71
|
+
engine (a parallel panel of local CLIs → a judge model's analysis → a grounded
|
|
72
|
+
synthesis). It is the same engine the Claude Code plugin ships; here it runs
|
|
73
|
+
through the \`maestro\` CLI with \`--scope codex\`.
|
|
74
|
+
|
|
75
|
+
**This is a typing shortcut, not a prompt hook.** Codex has no automatic
|
|
76
|
+
prompt hook, so arming a mode does **not** auto-run the engine on later prompts —
|
|
77
|
+
it only persists the mode. To actually fuse a prompt, invoke \`run\` explicitly
|
|
78
|
+
(step 3).
|
|
79
|
+
|
|
80
|
+
Map the user's request to one engine CLI call and run it from the repo root.
|
|
81
|
+
Do not edit the engine's state file by hand.
|
|
82
|
+
|
|
83
|
+
## 1. Switch mode
|
|
84
|
+
|
|
85
|
+
Persists to \`~/.config/maestro/frontier-state.codex.json\`; default \`off\`.
|
|
86
|
+
\`--scope codex\` keeps Codex's armed mode independent from Claude Code, Cline,
|
|
87
|
+
Cursor, and Gemini on the same machine:
|
|
88
|
+
|
|
89
|
+
\`\`\`bash
|
|
90
|
+
maestro frontier mode off --scope codex
|
|
91
|
+
maestro frontier mode single --model <model> --scope codex
|
|
92
|
+
maestro frontier mode fusion --preset <preset> --scope codex
|
|
93
|
+
maestro frontier mode fusion --preset custom --models <a,b,c> --scope codex
|
|
94
|
+
maestro frontier mode fusion --preset <preset> --judge <model> --synth <model> --scope codex
|
|
95
|
+
\`\`\`
|
|
96
|
+
|
|
97
|
+
Models: \`opus\` (Claude Opus 4.8, needs \`claude\`), \`gpt-5.5\` (needs \`codex\`),
|
|
98
|
+
\`gemini\` (needs \`gemini\`). Presets: \`opus-duo\`, \`opus-gpt\`, \`gpt-duo\`,
|
|
99
|
+
\`frontier-trio\`, \`custom\`. Judge + synth default to Opus; \`--judge\`/\`--synth\`
|
|
100
|
+
override for any preset (e.g. \`--judge opus --synth gpt-5.5\`). \`gpt-duo\` runs
|
|
101
|
+
judge + synth on GPT-5.5 — a Codex-only fusion that needs no \`claude\`.
|
|
102
|
+
|
|
103
|
+
## 2. Show current mode/preset
|
|
104
|
+
|
|
105
|
+
\`\`\`bash
|
|
106
|
+
maestro frontier status --scope codex
|
|
107
|
+
\`\`\`
|
|
108
|
+
|
|
109
|
+
## 3. Run a prompt through the current mode
|
|
110
|
+
|
|
111
|
+
This is the action that actually fuses, since nothing auto-runs here. Set a
|
|
112
|
+
mode first (step 1), then:
|
|
113
|
+
|
|
114
|
+
\`\`\`bash
|
|
115
|
+
maestro frontier run "<prompt>" --scope codex
|
|
116
|
+
\`\`\`
|
|
117
|
+
|
|
118
|
+
- \`off\`: prints a notice, spawns nothing.
|
|
119
|
+
- \`single\`: dispatches the one selected CLI, prints its answer.
|
|
120
|
+
- \`fusion\`: runs the panel in parallel → judge → synthesizer; prints the final
|
|
121
|
+
answer (a one-line run meta goes to stderr). Report stdout verbatim.
|
|
122
|
+
|
|
123
|
+
On error the engine prints \`ERROR [<reason>]: <detail>\` to stderr and exits
|
|
124
|
+
non-zero — relay the reason.
|
|
125
|
+
|
|
126
|
+
## Indicator instruction
|
|
127
|
+
|
|
128
|
+
<!-- INDICATOR INSTRUCTION — apply every turn while this skill is active -->
|
|
129
|
+
|
|
130
|
+
Before composing your first reply each turn, run:
|
|
131
|
+
|
|
132
|
+
\`\`\`bash
|
|
133
|
+
maestro frontier status --scope codex
|
|
134
|
+
\`\`\`
|
|
135
|
+
|
|
136
|
+
Parse the returned JSON \`mode\` field:
|
|
137
|
+
|
|
138
|
+
- If \`mode\` is not \`off\`, make the **first line** of your reply exactly:
|
|
139
|
+
\`Maestro Frontier ON (<label>)\`
|
|
140
|
+
where \`<label>\` is formed as follows:
|
|
141
|
+
- single mode → \`single · <model>\` (e.g. \`single · opus\`)
|
|
142
|
+
- fusion mode → \`fusion · <preset>\` (e.g. \`fusion · frontier-trio\`);
|
|
143
|
+
for a custom preset use \`fusion · custom (<model1>, <model2>, ...)\`
|
|
144
|
+
- If \`mode\` is \`off\`, output no indicator line.
|
|
145
|
+
|
|
146
|
+
<!-- END INDICATOR INSTRUCTION -->
|
|
147
|
+
|
|
148
|
+
## Notes
|
|
149
|
+
|
|
150
|
+
- Real \`single\`/\`fusion\` runs spawn local CLIs and cost tokens; use small prompts.
|
|
151
|
+
\`off\` is free.
|
|
152
|
+
- Each model's CLI must be on \`PATH\`, or point at a specific build with
|
|
153
|
+
\`MAESTRO_CLAUDE_BIN\` / \`MAESTRO_CODEX_BIN\` / \`MAESTRO_GEMINI_BIN\`.
|
|
154
|
+
- Requires \`maestro\` on \`PATH\` (installed during Maestro setup). If it is
|
|
155
|
+
missing, install Maestro first.
|
|
156
|
+
`,
|
|
157
|
+
terse: `---
|
|
158
|
+
name: terse
|
|
159
|
+
description: Toggle Maestro terse output level (lite, full, ultra, off) via the settings CLI
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
Toggle the **Maestro terse** output level for this environment. Terse mode
|
|
163
|
+
condenses agent replies; levels range from \`off\` (default verbosity) through
|
|
164
|
+
\`lite\`, \`full\`, and \`ultra\` (most compressed).
|
|
165
|
+
|
|
166
|
+
When the user invokes this skill, run the settings CLI to read or change the
|
|
167
|
+
terse level. Do not edit settings files by hand.
|
|
168
|
+
|
|
169
|
+
## Check current terse level
|
|
170
|
+
|
|
171
|
+
\`\`\`bash
|
|
172
|
+
node settings/cli.cjs --help
|
|
173
|
+
\`\`\`
|
|
174
|
+
|
|
175
|
+
Consult the help output for the exact read subcommand, then run it. If
|
|
176
|
+
\`settings/cli.cjs\` is not present, run \`maestro --help\` to discover the
|
|
177
|
+
correct path.
|
|
178
|
+
|
|
179
|
+
## Set terse level
|
|
180
|
+
|
|
181
|
+
\`\`\`bash
|
|
182
|
+
node settings/cli.cjs terse <level>
|
|
183
|
+
\`\`\`
|
|
184
|
+
|
|
185
|
+
Valid levels: \`off\` | \`lite\` | \`full\` | \`ultra\`
|
|
186
|
+
|
|
187
|
+
Examples:
|
|
188
|
+
|
|
189
|
+
\`\`\`bash
|
|
190
|
+
node settings/cli.cjs terse off
|
|
191
|
+
node settings/cli.cjs terse lite
|
|
192
|
+
node settings/cli.cjs terse full
|
|
193
|
+
node settings/cli.cjs terse ultra
|
|
194
|
+
\`\`\`
|
|
195
|
+
|
|
196
|
+
If the CLI rejects an argument or the subcommand name differs, run
|
|
197
|
+
\`node settings/cli.cjs --help\` first and follow the printed usage.
|
|
198
|
+
|
|
199
|
+
## Notes
|
|
200
|
+
|
|
201
|
+
- The change persists in Maestro's settings store; it applies to subsequent
|
|
202
|
+
agent turns in this project.
|
|
203
|
+
- Requires \`node\` on \`PATH\` and Maestro installed in the project root. If
|
|
204
|
+
\`settings/cli.cjs\` is missing, re-run the Maestro installer:
|
|
205
|
+
\`npx github:mbanderas/maestro install --target codex\`
|
|
206
|
+
`,
|
|
207
|
+
settings: `---
|
|
208
|
+
name: settings
|
|
209
|
+
description: View and change Maestro toggles (terse, frontier, context-bar) via the settings CLI
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
View or change **Maestro settings** for this project. The settings CLI manages
|
|
213
|
+
the three primary toggles: \`terse\`, \`frontier\`, and \`context-bar\`.
|
|
214
|
+
|
|
215
|
+
When the user invokes this skill, run the settings CLI from the repo root.
|
|
216
|
+
Do not edit settings files by hand.
|
|
217
|
+
|
|
218
|
+
## Discover available commands
|
|
219
|
+
|
|
220
|
+
\`\`\`bash
|
|
221
|
+
node settings/cli.cjs --help
|
|
222
|
+
\`\`\`
|
|
223
|
+
|
|
224
|
+
If \`settings/cli.cjs\` is not present, run \`maestro --help\` to locate the
|
|
225
|
+
correct entry point.
|
|
226
|
+
|
|
227
|
+
## Common operations
|
|
228
|
+
|
|
229
|
+
List current settings:
|
|
230
|
+
|
|
231
|
+
\`\`\`bash
|
|
232
|
+
node settings/cli.cjs
|
|
233
|
+
\`\`\`
|
|
234
|
+
|
|
235
|
+
Set a toggle:
|
|
236
|
+
|
|
237
|
+
\`\`\`bash
|
|
238
|
+
node settings/cli.cjs terse <off|lite|full|ultra>
|
|
239
|
+
node settings/cli.cjs frontier <off|single|fusion>
|
|
240
|
+
node settings/cli.cjs context-bar <on|off>
|
|
241
|
+
\`\`\`
|
|
242
|
+
|
|
243
|
+
If a subcommand name or argument differs from the above, follow the usage
|
|
244
|
+
printed by \`--help\` — do not guess flags.
|
|
245
|
+
|
|
246
|
+
## Notes
|
|
247
|
+
|
|
248
|
+
- Changes persist in Maestro's settings store and apply to subsequent agent
|
|
249
|
+
turns in this project.
|
|
250
|
+
- Requires \`node\` on \`PATH\` and Maestro installed in the project root. If
|
|
251
|
+
\`settings/cli.cjs\` is missing, re-run the installer:
|
|
252
|
+
\`npx github:mbanderas/maestro install --target codex\`
|
|
253
|
+
`,
|
|
254
|
+
update: `---
|
|
255
|
+
name: update
|
|
256
|
+
description: Update Maestro to the latest version by re-running the installer for Codex
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
Update **Maestro** to the latest marketplace code. This re-runs the installer,
|
|
260
|
+
which pulls the current release and overwrites the local Maestro files in place.
|
|
261
|
+
|
|
262
|
+
When the user invokes this skill, run the installer from the repo root:
|
|
263
|
+
|
|
264
|
+
\`\`\`bash
|
|
265
|
+
npx github:mbanderas/maestro install --target codex
|
|
266
|
+
\`\`\`
|
|
267
|
+
|
|
268
|
+
The installer is idempotent — it is safe to re-run against an existing
|
|
269
|
+
installation. It will:
|
|
270
|
+
|
|
271
|
+
- Pull the latest Maestro source from the repository.
|
|
272
|
+
- Overwrite skills, hooks, and settings scaffolding with the new versions.
|
|
273
|
+
- Leave project-local configuration (state files, secrets) untouched.
|
|
274
|
+
|
|
275
|
+
## Notes
|
|
276
|
+
|
|
277
|
+
- Requires \`node\` and \`npx\` on \`PATH\`.
|
|
278
|
+
- Run from the project root so the installer targets the correct directory.
|
|
279
|
+
- After the installer completes, restart the Codex session (or reload the
|
|
280
|
+
project) so updated skills and hooks take effect.
|
|
281
|
+
- If \`npx\` is unavailable, clone \`https://github.com/mbanderas/maestro\`
|
|
282
|
+
manually and follow the repository's install instructions.
|
|
283
|
+
`,
|
|
284
|
+
};
|
|
56
285
|
|
|
57
286
|
// Runtime adapter per target. The adapter imports @AGENTS.md (Cursor has no
|
|
58
287
|
// imports, so .cursorrules embeds the kernel). codex/cline/windsurf read
|
|
@@ -132,6 +361,54 @@ function safeWrite(dest, content) {
|
|
|
132
361
|
}
|
|
133
362
|
}
|
|
134
363
|
|
|
364
|
+
function homeDir() {
|
|
365
|
+
return process.platform === 'win32'
|
|
366
|
+
? (process.env.USERPROFILE || os.homedir())
|
|
367
|
+
: (process.env.HOME || os.homedir());
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function sha256(content) {
|
|
371
|
+
return crypto.createHash('sha256').update(content, 'utf8').digest('hex');
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function codexSkillManagedContent(name, srcContent) {
|
|
375
|
+
const body = srcContent.trimEnd() + '\n';
|
|
376
|
+
return `${body}\n<!-- maestro-managed:codex-skill name=${name} sha256=${sha256(body)} -->\n`;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function splitCodexSkillMarker(content) {
|
|
380
|
+
const marker = /\n?<!-- maestro-managed:codex-skill name=([^\s]+) sha256=([a-f0-9]+|0000) -->\s*$/i.exec(content);
|
|
381
|
+
if (!marker) return null;
|
|
382
|
+
return {
|
|
383
|
+
name: marker[1],
|
|
384
|
+
hash: marker[2].toLowerCase(),
|
|
385
|
+
body: content.slice(0, marker.index).trimEnd() + '\n',
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function isManagedCodexSkillContent(content, expectedName, managedBodies) {
|
|
390
|
+
const marker = splitCodexSkillMarker(content);
|
|
391
|
+
if (marker && marker.name === expectedName) {
|
|
392
|
+
return marker.hash === '0000' || marker.hash === sha256(marker.body);
|
|
393
|
+
}
|
|
394
|
+
if (content.includes(`maestro-managed:codex-skill name=${expectedName} sha256=0000`)) {
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
return managedBodies.some((body) => content.trimEnd() === body.trimEnd());
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function legacyCodexSkillContent(legacyName, namespacedName) {
|
|
401
|
+
const body = `---\nname: ${legacyName}\ndescription: Legacy Maestro compatibility skill for ${namespacedName}\n---\n\nThis legacy Maestro skill has moved to \`${namespacedName}\`.\n\nUse the \`${namespacedName}\` skill for current Maestro behavior. This compatibility skill is kept only for existing Codex installs that still reference \`${legacyName}\`.\n`;
|
|
402
|
+
return codexSkillManagedContent(legacyName, body);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function legacyGenericCodexTemplate(srcContent, legacyName, namespacedName) {
|
|
406
|
+
return srcContent.replace(
|
|
407
|
+
new RegExp(`(^---\\r?\\nname: )${namespacedName}(\\r?\\n)`, 'm'),
|
|
408
|
+
`$1${legacyName}$2`
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
|
|
135
412
|
// ---- parse argv ----
|
|
136
413
|
|
|
137
414
|
/**
|
|
@@ -361,7 +638,7 @@ function copyDirRecursive(srcDir, destDir, dryRun, log) {
|
|
|
361
638
|
}
|
|
362
639
|
|
|
363
640
|
/**
|
|
364
|
-
* Install engine files (frontier/ dir + bin/maestro.cjs).
|
|
641
|
+
* Install engine files (frontier/ dir + settings/ dir + bin/maestro.cjs).
|
|
365
642
|
* @param {string} projectRoot
|
|
366
643
|
* @param {boolean} dryRun
|
|
367
644
|
* @param {(msg: string) => void} log
|
|
@@ -370,10 +647,13 @@ function copyDirRecursive(srcDir, destDir, dryRun, log) {
|
|
|
370
647
|
function installEngine(projectRoot, dryRun, log) {
|
|
371
648
|
const srcFrontier = path.join(PKG_ROOT, 'frontier');
|
|
372
649
|
const destFrontier = path.join(projectRoot, 'frontier');
|
|
650
|
+
const srcSettings = path.join(PKG_ROOT, 'settings');
|
|
651
|
+
const destSettings = path.join(projectRoot, 'settings');
|
|
373
652
|
const srcBin = path.join(PKG_ROOT, 'bin', 'maestro.cjs');
|
|
374
653
|
const destBin = path.join(projectRoot, 'bin', 'maestro.cjs');
|
|
375
654
|
|
|
376
655
|
let ok = copyDirRecursive(srcFrontier, destFrontier, dryRun, log);
|
|
656
|
+
if (!copyDirRecursive(srcSettings, destSettings, dryRun, log)) ok = false;
|
|
377
657
|
|
|
378
658
|
// bin/maestro.cjs
|
|
379
659
|
if (dryRun) {
|
|
@@ -500,7 +780,7 @@ function installWrapper(target, projectRoot, userGlobal, dryRun, log) {
|
|
|
500
780
|
log(`[wrapper] --user not supported for target ${target} — writing to project instead`);
|
|
501
781
|
dest = path.join(projectRoot, mapping.proj);
|
|
502
782
|
} else {
|
|
503
|
-
dest = mapping.user;
|
|
783
|
+
dest = mapping.user();
|
|
504
784
|
}
|
|
505
785
|
} else {
|
|
506
786
|
dest = path.join(projectRoot, mapping.proj);
|
|
@@ -510,7 +790,8 @@ function installWrapper(target, projectRoot, userGlobal, dryRun, log) {
|
|
|
510
790
|
}
|
|
511
791
|
|
|
512
792
|
/**
|
|
513
|
-
* Install the Codex skill templates
|
|
793
|
+
* Install the Codex skill templates alongside the codex wrapper. Maestro-owned
|
|
794
|
+
* skill files refresh in place; user-edited copies are preserved.
|
|
514
795
|
* Project mode -> <project>/.agents/skills/<name>/SKILL.md; --user/global mode
|
|
515
796
|
* -> ~/.agents/skills/<name>/SKILL.md (mirrors installWrapper's dest logic).
|
|
516
797
|
* @param {string} projectRoot
|
|
@@ -519,16 +800,139 @@ function installWrapper(target, projectRoot, userGlobal, dryRun, log) {
|
|
|
519
800
|
* @param {(msg: string) => void} log
|
|
520
801
|
* @returns {boolean}
|
|
521
802
|
*/
|
|
803
|
+
function installManagedCodexSkill(src, dest, name, legacyName, dryRun, log) {
|
|
804
|
+
let srcContent;
|
|
805
|
+
try {
|
|
806
|
+
srcContent = fs.readFileSync(src, 'utf8');
|
|
807
|
+
} catch (err) {
|
|
808
|
+
log(`ERROR: cannot read template ${src}: ${err.message}`);
|
|
809
|
+
return false;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
const managedContent = codexSkillManagedContent(name, srcContent);
|
|
813
|
+
const managedBodies = [srcContent, managedContent];
|
|
814
|
+
|
|
815
|
+
let destStat;
|
|
816
|
+
try { destStat = fs.lstatSync(dest); } catch { destStat = null; }
|
|
817
|
+
|
|
818
|
+
if (destStat) {
|
|
819
|
+
if (destStat.isSymbolicLink()) {
|
|
820
|
+
log(`ERROR: codex-skill dest is a symlink — refusing: ${dest}`);
|
|
821
|
+
return false;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
let existing;
|
|
825
|
+
try { existing = fs.readFileSync(dest, 'utf8'); } catch (err) {
|
|
826
|
+
log(`ERROR: cannot read existing Codex skill ${dest}: ${err.message}`);
|
|
827
|
+
return false;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
if (!isManagedCodexSkillContent(existing, name, managedBodies)) {
|
|
831
|
+
log(`[codex-skill] preserved user-edited Codex skill: ${dest}`);
|
|
832
|
+
log(`[codex-skill] next step: compare with integrations/codex/skills/${name}/SKILL.md and manually merge if desired`);
|
|
833
|
+
return true;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
if (existing === managedContent) {
|
|
837
|
+
log(`[codex-skill] up to date: ${dest}`);
|
|
838
|
+
return true;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
if (dryRun) {
|
|
842
|
+
log(`[dry-run] would refresh managed Codex skill ${dest}`);
|
|
843
|
+
return true;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
const res = safeWrite(dest, managedContent);
|
|
847
|
+
if (!res.ok) {
|
|
848
|
+
log(`ERROR: failed to refresh codex-skill ${dest}: ${res.reason}`);
|
|
849
|
+
return false;
|
|
850
|
+
}
|
|
851
|
+
log(`[codex-skill] refreshed managed Codex skill: ${dest}`);
|
|
852
|
+
return true;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
if (dryRun) {
|
|
856
|
+
log(`[dry-run] would create ${dest}`);
|
|
857
|
+
return true;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
if (!safeMkdirp(dest)) {
|
|
861
|
+
log(`ERROR: could not create parent dir for ${dest}`);
|
|
862
|
+
return false;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
const res = safeWrite(dest, managedContent);
|
|
866
|
+
if (!res.ok) {
|
|
867
|
+
log(`ERROR: failed to write codex-skill ${dest}: ${res.reason}`);
|
|
868
|
+
return false;
|
|
869
|
+
}
|
|
870
|
+
log(`[codex-skill] wrote ${dest}`);
|
|
871
|
+
return true;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
function migrateLegacyCodexSkill(dest, legacyName, namespacedName, knownTemplate, dryRun, log) {
|
|
875
|
+
let destStat;
|
|
876
|
+
try { destStat = fs.lstatSync(dest); } catch { return true; }
|
|
877
|
+
|
|
878
|
+
if (destStat.isSymbolicLink()) {
|
|
879
|
+
log(`ERROR: legacy codex-skill dest is a symlink — refusing: ${dest}`);
|
|
880
|
+
return false;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
let existing;
|
|
884
|
+
try { existing = fs.readFileSync(dest, 'utf8'); } catch (err) {
|
|
885
|
+
log(`ERROR: cannot read legacy Codex skill ${dest}: ${err.message}`);
|
|
886
|
+
return false;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
const shim = legacyCodexSkillContent(legacyName, namespacedName);
|
|
890
|
+
const managedBodies = [
|
|
891
|
+
knownTemplate,
|
|
892
|
+
legacyGenericCodexTemplate(knownTemplate, legacyName, namespacedName),
|
|
893
|
+
LEGACY_CODEX_SKILL_TEMPLATES[legacyName],
|
|
894
|
+
shim,
|
|
895
|
+
].filter(Boolean);
|
|
896
|
+
if (!isManagedCodexSkillContent(existing, legacyName, managedBodies)) {
|
|
897
|
+
log(`[codex-skill] preserved user-edited legacy Codex skill: ${dest}`);
|
|
898
|
+
log(`[codex-skill] next step: rename or merge it into .agents/skills/${namespacedName}/SKILL.md if you still need custom behavior`);
|
|
899
|
+
return true;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
if (existing === shim) {
|
|
903
|
+
log(`[codex-skill] legacy compatibility up to date: ${dest}`);
|
|
904
|
+
return true;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
if (dryRun) {
|
|
908
|
+
log(`[dry-run] would migrate legacy Codex skill ${dest}`);
|
|
909
|
+
return true;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
const res = safeWrite(dest, shim);
|
|
913
|
+
if (!res.ok) {
|
|
914
|
+
log(`ERROR: failed to migrate legacy codex-skill ${dest}: ${res.reason}`);
|
|
915
|
+
return false;
|
|
916
|
+
}
|
|
917
|
+
log(`[codex-skill] migrated legacy Codex skill to compatibility shim: ${dest}`);
|
|
918
|
+
return true;
|
|
919
|
+
}
|
|
920
|
+
|
|
522
921
|
function installCodexSkills(projectRoot, userGlobal, dryRun, log) {
|
|
523
922
|
const skillsRoot = userGlobal
|
|
524
|
-
? path.join(
|
|
923
|
+
? path.join(homeDir(), '.agents', 'skills')
|
|
525
924
|
: path.join(projectRoot, '.agents', 'skills');
|
|
526
925
|
|
|
527
926
|
let ok = true;
|
|
528
|
-
for (const
|
|
529
|
-
const src = path.join(PKG_ROOT, 'integrations', 'codex', 'skills', name, 'SKILL.md');
|
|
530
|
-
const dest = path.join(skillsRoot, name, 'SKILL.md');
|
|
531
|
-
if (!
|
|
927
|
+
for (const skill of CODEX_SKILLS) {
|
|
928
|
+
const src = path.join(PKG_ROOT, 'integrations', 'codex', 'skills', skill.name, 'SKILL.md');
|
|
929
|
+
const dest = path.join(skillsRoot, skill.name, 'SKILL.md');
|
|
930
|
+
if (!installManagedCodexSkill(src, dest, skill.name, skill.legacy, dryRun, log)) ok = false;
|
|
931
|
+
|
|
932
|
+
let legacyTemplate = '';
|
|
933
|
+
try { legacyTemplate = fs.readFileSync(src, 'utf8'); } catch {}
|
|
934
|
+
const legacyDest = path.join(skillsRoot, skill.legacy, 'SKILL.md');
|
|
935
|
+
if (!migrateLegacyCodexSkill(legacyDest, skill.legacy, skill.name, legacyTemplate, dryRun, log)) ok = false;
|
|
532
936
|
}
|
|
533
937
|
return ok;
|
|
534
938
|
}
|
|
@@ -602,4 +1006,9 @@ if (require.main === module) {
|
|
|
602
1006
|
process.exit(code);
|
|
603
1007
|
}
|
|
604
1008
|
|
|
605
|
-
module.exports = {
|
|
1009
|
+
module.exports = {
|
|
1010
|
+
run,
|
|
1011
|
+
_test: {
|
|
1012
|
+
LEGACY_CODEX_SKILL_TEMPLATES,
|
|
1013
|
+
},
|
|
1014
|
+
};
|
package/settings/cli.cjs
CHANGED
|
@@ -109,7 +109,7 @@ function usageText() {
|
|
|
109
109
|
' terse <off|lite|full|ultra>\n' +
|
|
110
110
|
' frontier <off | single:<model> | fusion:<preset>>\n' +
|
|
111
111
|
' context-bar <on|off>\n' +
|
|
112
|
-
' --scope targets a
|
|
112
|
+
' --scope targets a frontier state; use codex-project/codex-workspace for Codex repo scope, or an explicit name such as codex-global for shared state\n'
|
|
113
113
|
);
|
|
114
114
|
}
|
|
115
115
|
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: maestro-frontier
|
|
3
|
+
description: Maestro Frontier local multi-CLI fusion engine - arm, disarm, inspect, or debug-run the panel
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Drive the **Maestro Frontier** engine: a zero-dependency local multi-CLI fusion
|
|
7
|
+
engine where a parallel panel of local CLIs feeds a judge model's analysis and a
|
|
8
|
+
grounded synthesis.
|
|
9
|
+
|
|
10
|
+
When the Maestro Codex plugin hook is installed, enabled, and trusted, arming a
|
|
11
|
+
non-`off` mode makes normal later Codex prompts auto-run through Frontier until
|
|
12
|
+
you turn it off. Users should not need to type `maestro frontier run "<prompt>"`
|
|
13
|
+
for normal use.
|
|
14
|
+
|
|
15
|
+
Map the user's request to one engine CLI call and run it from the repo root.
|
|
16
|
+
Do not edit the engine's state file by hand.
|
|
17
|
+
|
|
18
|
+
## Command launcher
|
|
19
|
+
|
|
20
|
+
Use `maestro` when it is on `PATH`. If it is not, and this skill is loaded
|
|
21
|
+
from the Maestro Codex plugin, locate the plugin root by walking up from this
|
|
22
|
+
`SKILL.md` until `.codex-plugin/plugin.json` is present, then run:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
node "<maestro-plugin-root>/bin/maestro.cjs" frontier ...
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
In the examples below, `maestro frontier ...` means either the bare command or
|
|
29
|
+
that plugin-root `node .../bin/maestro.cjs frontier ...` form.
|
|
30
|
+
|
|
31
|
+
## 1. Switch mode
|
|
32
|
+
|
|
33
|
+
Project/workspace scope is the default recommendation. Use `--scope
|
|
34
|
+
codex-project` from the repository root; the CLI expands it to the same
|
|
35
|
+
`codex-<8hex>` workspace scope the Codex plugin hook resolves from
|
|
36
|
+
`PLUGIN_ROOT` / `PLUGIN_DATA`. Default mode is `off`.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
maestro frontier mode off --scope codex-project
|
|
40
|
+
maestro frontier mode single --model <model> --scope codex-project
|
|
41
|
+
maestro frontier mode fusion --preset chatgpt-duo --scope codex-project
|
|
42
|
+
maestro frontier mode fusion --preset frontier-trio --judge chatgpt --synth chatgpt --scope codex-project
|
|
43
|
+
maestro frontier mode fusion --preset custom --models <a,b,c> --scope codex-project
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Models: `opus` (Claude Opus 4.8, needs `claude`), `gpt-5.5` (needs `codex`),
|
|
47
|
+
`gemini` (needs `gemini`). Presets: `opus-duo`, `opus-gpt`, `gpt-duo`,
|
|
48
|
+
`frontier-trio`, `custom`. Friendly aliases are accepted: `chatgpt` maps to
|
|
49
|
+
`gpt-5.5`, and `chatgpt-duo` maps to `gpt-duo`.
|
|
50
|
+
|
|
51
|
+
Judge + synth default to Opus except for presets with explicit stage defaults.
|
|
52
|
+
Override them for mixed panels with `--judge <model>` and `--synth <model>`;
|
|
53
|
+
for example, `--judge chatgpt --synth chatgpt`.
|
|
54
|
+
|
|
55
|
+
## 2. Show current mode/preset
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
maestro frontier status --scope codex-project
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
If you intentionally want one shared Codex state across unrelated repos, choose
|
|
62
|
+
an explicit global name such as `--scope codex-global`. Do not use global scope
|
|
63
|
+
unless that cross-repo behavior is what you want.
|
|
64
|
+
|
|
65
|
+
## 3. Normal use after arming
|
|
66
|
+
|
|
67
|
+
After mode is non-`off`, type ordinary Codex prompts. The trusted Codex hook
|
|
68
|
+
auto-runs Frontier and injects the synthesized answer as context for the live
|
|
69
|
+
reply. Turn it off with:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
maestro frontier mode off --scope codex-project
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 4. Advanced/debug one-off run
|
|
76
|
+
|
|
77
|
+
Manual one-off execution remains available for debugging:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
maestro frontier run "<prompt>" --scope codex-project
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
- `off`: prints a notice, spawns nothing.
|
|
84
|
+
- `single`: dispatches the one selected CLI, prints its answer.
|
|
85
|
+
- `fusion`: runs the panel in parallel, then judge, then synthesizer; prints
|
|
86
|
+
the final answer (a one-line run meta goes to stderr). Report stdout verbatim.
|
|
87
|
+
|
|
88
|
+
On error the engine prints `ERROR [<reason>]: <detail>` to stderr and exits
|
|
89
|
+
non-zero; relay the reason.
|
|
90
|
+
|
|
91
|
+
## Indicator instruction
|
|
92
|
+
|
|
93
|
+
<!-- INDICATOR INSTRUCTION - apply every turn while this skill is active -->
|
|
94
|
+
|
|
95
|
+
Before composing your first reply each turn, run:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
maestro frontier status --scope codex-project
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Parse the returned JSON `mode` field:
|
|
102
|
+
|
|
103
|
+
- If `mode` is not `off`, make the **first line** of your reply exactly:
|
|
104
|
+
`Maestro Frontier ON (<label>)`
|
|
105
|
+
where `<label>` is formed as follows:
|
|
106
|
+
- single mode -> `single - <model>` (e.g. `single - opus`)
|
|
107
|
+
- fusion mode -> `fusion - <preset>` (e.g. `fusion - frontier-trio`);
|
|
108
|
+
for a custom preset use `fusion - custom (<model1>, <model2>, ...)`
|
|
109
|
+
- If `mode` is `off`, output no indicator line.
|
|
110
|
+
|
|
111
|
+
<!-- END INDICATOR INSTRUCTION -->
|
|
112
|
+
|
|
113
|
+
## Notes
|
|
114
|
+
|
|
115
|
+
- Real `single`/`fusion` runs spawn local CLIs and cost tokens; `off` is free.
|
|
116
|
+
- The autorun hook no-ops when `FUSION_DEPTH >= 1`, so child `codex`, `claude`,
|
|
117
|
+
and `gemini` panel processes do not recursively run Frontier.
|
|
118
|
+
- Each model's CLI must be on `PATH`, or point at a specific build with
|
|
119
|
+
`MAESTRO_CLAUDE_BIN` / `MAESTRO_CODEX_BIN` / `MAESTRO_GEMINI_BIN`.
|
|
120
|
+
- Requires `node` on `PATH`. The bare `maestro` command is optional when the
|
|
121
|
+
skill is loaded from the Maestro Codex plugin; use the plugin-root launcher
|
|
122
|
+
above.
|