@maestrofrontier/frontier 1.4.0 → 1.4.2

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,91 @@
1
+ ---
2
+ name: frontier
3
+ description: Maestro Frontier local multi-CLI fusion engine — switch mode, or run a prompt through the panel
4
+ ---
5
+
6
+ Drive the **Maestro Frontier** engine — a zero-dependency local multi-CLI fusion
7
+ engine (a parallel panel of local CLIs → a judge model's analysis → a grounded
8
+ synthesis). It is the same engine the Claude Code plugin ships; here it runs
9
+ through the `maestro` CLI with `--scope codex`.
10
+
11
+ **This is a typing shortcut, not a prompt hook.** Codex has no automatic
12
+ prompt hook, so arming a mode does **not** auto-run the engine on later prompts —
13
+ it only persists the mode. To actually fuse a prompt, invoke `run` explicitly
14
+ (step 3).
15
+
16
+ Map the user's request to one engine CLI call and run it from the repo root.
17
+ Do not edit the engine's state file by hand.
18
+
19
+ ## 1. Switch mode
20
+
21
+ Persists to `~/.config/maestro/frontier-state.codex.json`; default `off`.
22
+ `--scope codex` keeps Codex's armed mode independent from Claude Code, Cline,
23
+ Cursor, and Gemini on the same machine:
24
+
25
+ ```bash
26
+ maestro frontier mode off --scope codex
27
+ maestro frontier mode single --model <model> --scope codex
28
+ maestro frontier mode fusion --preset <preset> --scope codex
29
+ maestro frontier mode fusion --preset custom --models <a,b,c> --scope codex
30
+ maestro frontier mode fusion --preset <preset> --judge <model> --synth <model> --scope codex
31
+ ```
32
+
33
+ Models: `opus` (Claude Opus 4.8, needs `claude`), `gpt-5.5` (needs `codex`),
34
+ `gemini` (needs `gemini`). Presets: `opus-duo`, `opus-gpt`, `gpt-duo`,
35
+ `frontier-trio`, `custom`. Judge + synth default to Opus; `--judge`/`--synth`
36
+ override for any preset (e.g. `--judge opus --synth gpt-5.5`). `gpt-duo` runs
37
+ judge + synth on GPT-5.5 — a Codex-only fusion that needs no `claude`.
38
+
39
+ ## 2. Show current mode/preset
40
+
41
+ ```bash
42
+ maestro frontier status --scope codex
43
+ ```
44
+
45
+ ## 3. Run a prompt through the current mode
46
+
47
+ This is the action that actually fuses, since nothing auto-runs here. Set a
48
+ mode first (step 1), then:
49
+
50
+ ```bash
51
+ maestro frontier run "<prompt>" --scope codex
52
+ ```
53
+
54
+ - `off`: prints a notice, spawns nothing.
55
+ - `single`: dispatches the one selected CLI, prints its answer.
56
+ - `fusion`: runs the panel in parallel → judge → synthesizer; prints the final
57
+ answer (a one-line run meta goes to stderr). Report stdout verbatim.
58
+
59
+ On error the engine prints `ERROR [<reason>]: <detail>` to stderr and exits
60
+ non-zero — relay the reason.
61
+
62
+ ## Indicator instruction
63
+
64
+ <!-- INDICATOR INSTRUCTION — apply every turn while this skill is active -->
65
+
66
+ Before composing your first reply each turn, run:
67
+
68
+ ```bash
69
+ maestro frontier status --scope codex
70
+ ```
71
+
72
+ Parse the returned JSON `mode` field:
73
+
74
+ - If `mode` is not `off`, make the **first line** of your reply exactly:
75
+ `Maestro Frontier ON (<label>)`
76
+ where `<label>` is formed as follows:
77
+ - single mode → `single · <model>` (e.g. `single · opus`)
78
+ - fusion mode → `fusion · <preset>` (e.g. `fusion · frontier-trio`);
79
+ for a custom preset use `fusion · custom (<model1>, <model2>, ...)`
80
+ - If `mode` is `off`, output no indicator line.
81
+
82
+ <!-- END INDICATOR INSTRUCTION -->
83
+
84
+ ## Notes
85
+
86
+ - Real `single`/`fusion` runs spawn local CLIs and cost tokens; use small prompts.
87
+ `off` is free.
88
+ - Each model's CLI must be on `PATH`, or point at a specific build with
89
+ `MAESTRO_CLAUDE_BIN` / `MAESTRO_CODEX_BIN` / `MAESTRO_GEMINI_BIN`.
90
+ - Requires `maestro` on `PATH` (installed during Maestro setup). If it is
91
+ missing, install Maestro first.
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: settings
3
+ description: View and change Maestro toggles (terse, frontier, context-bar) via the settings CLI
4
+ ---
5
+
6
+ View or change **Maestro settings** for this project. The settings CLI manages
7
+ the three primary toggles: `terse`, `frontier`, and `context-bar`.
8
+
9
+ When the user invokes this skill, run the settings CLI from the repo root.
10
+ Do not edit settings files by hand.
11
+
12
+ ## Discover available commands
13
+
14
+ ```bash
15
+ node settings/cli.cjs --help
16
+ ```
17
+
18
+ If `settings/cli.cjs` is not present, run `maestro --help` to locate the
19
+ correct entry point.
20
+
21
+ ## Common operations
22
+
23
+ List current settings:
24
+
25
+ ```bash
26
+ node settings/cli.cjs
27
+ ```
28
+
29
+ Set a toggle:
30
+
31
+ ```bash
32
+ node settings/cli.cjs terse <off|lite|full|ultra>
33
+ node settings/cli.cjs frontier <off|single|fusion>
34
+ node settings/cli.cjs context-bar <on|off>
35
+ ```
36
+
37
+ If a subcommand name or argument differs from the above, follow the usage
38
+ printed by `--help` — do not guess flags.
39
+
40
+ ## Notes
41
+
42
+ - Changes persist in Maestro's settings store and apply to subsequent agent
43
+ turns in this project.
44
+ - Requires `node` on `PATH` and Maestro installed in the project root. If
45
+ `settings/cli.cjs` is missing, re-run the installer:
46
+ `npx github:mbanderas/maestro install --target codex`
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: terse
3
+ description: Toggle Maestro terse output level (lite, full, ultra, off) via the settings CLI
4
+ ---
5
+
6
+ Toggle the **Maestro terse** output level for this environment. Terse mode
7
+ condenses agent replies; levels range from `off` (default verbosity) through
8
+ `lite`, `full`, and `ultra` (most compressed).
9
+
10
+ When the user invokes this skill, run the settings CLI to read or change the
11
+ terse level. Do not edit settings files by hand.
12
+
13
+ ## Check current terse level
14
+
15
+ ```bash
16
+ node settings/cli.cjs --help
17
+ ```
18
+
19
+ Consult the help output for the exact read subcommand, then run it. If
20
+ `settings/cli.cjs` is not present, run `maestro --help` to discover the
21
+ correct path.
22
+
23
+ ## Set terse level
24
+
25
+ ```bash
26
+ node settings/cli.cjs terse <level>
27
+ ```
28
+
29
+ Valid levels: `off` | `lite` | `full` | `ultra`
30
+
31
+ Examples:
32
+
33
+ ```bash
34
+ node settings/cli.cjs terse off
35
+ node settings/cli.cjs terse lite
36
+ node settings/cli.cjs terse full
37
+ node settings/cli.cjs terse ultra
38
+ ```
39
+
40
+ If the CLI rejects an argument or the subcommand name differs, run
41
+ `node settings/cli.cjs --help` first and follow the printed usage.
42
+
43
+ ## Notes
44
+
45
+ - The change persists in Maestro's settings store; it applies to subsequent
46
+ agent turns in this project.
47
+ - Requires `node` on `PATH` and Maestro installed in the project root. If
48
+ `settings/cli.cjs` is missing, re-run the Maestro installer:
49
+ `npx github:mbanderas/maestro install --target codex`
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: update
3
+ description: Update Maestro to the latest version by re-running the installer for Codex
4
+ ---
5
+
6
+ Update **Maestro** to the latest marketplace code. This re-runs the installer,
7
+ which pulls the current release and overwrites the local Maestro files in place.
8
+
9
+ When the user invokes this skill, run the installer from the repo root:
10
+
11
+ ```bash
12
+ npx github:mbanderas/maestro install --target codex
13
+ ```
14
+
15
+ The installer is idempotent — it is safe to re-run against an existing
16
+ installation. It will:
17
+
18
+ - Pull the latest Maestro source from the repository.
19
+ - Overwrite skills, hooks, and settings scaffolding with the new versions.
20
+ - Leave project-local configuration (state files, secrets) untouched.
21
+
22
+ ## Notes
23
+
24
+ - Requires `node` and `npx` on `PATH`.
25
+ - Run from the project root so the installer targets the correct directory.
26
+ - After the installer completes, restart the Codex session (or reload the
27
+ project) so updated skills and hooks take effect.
28
+ - If `npx` is unavailable, clone `https://github.com/mbanderas/maestro`
29
+ manually and follow the repository's install instructions.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maestrofrontier/frontier",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "Achieve Frontier AI performance in your CLI by fusing the model CLIs you already run. Maestro Frontier is an opt-in, zero-dependency local multi-CLI fusion engine for AI coding agents: fan a prompt across a panel of any 1 to 8 local model CLIs you pick, have a judge model and a synthesizer you choose read the answers into a structured analysis and write one grounded synthesis (default Opus 4.8, override either with --judge/--synth). On a 100-task benchmark every fusion panel outscored its individual member models. Three adapters ship today: Opus 4.8, GPT-5.5, Gemini 3.1 Pro, with Kimi, DeepSeek, GLM, and Qwen to follow. Off, single, and fusion modes switch via /maestro:frontier. Built on Maestro orchestration discipline: decision-gated routing, verified done-claims, surgical scope, and structural enforcement hooks.",
5
5
  "keywords": ["multi-cli-fusion", "fusion-engine", "frontier", "multi-agent", "orchestration", "claude-code", "gemini", "codex", "agents", "hooks", "doctrine"],
6
6
  "license": "MIT",
@@ -27,7 +27,10 @@
27
27
  "scripts/install.cjs",
28
28
  "AGENTS.md",
29
29
  "CLAUDE.md",
30
+ "GEMINI.md",
31
+ ".cursorrules",
30
32
  "docs/orchestration.md",
33
+ "docs/codex.md",
31
34
  "commands/",
32
35
  "integrations/",
33
36
  "hooks/frontier-autorun.cjs",
@@ -49,6 +49,20 @@ const WRAPPER_MAP = {
49
49
  },
50
50
  };
51
51
 
52
+ // Codex skill templates installed alongside the deprecated codex wrapper.
53
+ // Codex loads skills from <project>/.agents/skills/<name>/SKILL.md (project)
54
+ // or ~/.agents/skills/<name>/SKILL.md (global). No-clobber, like wrappers.
55
+ const CODEX_SKILLS = ['frontier', 'terse', 'settings', 'update'];
56
+
57
+ // Runtime adapter per target. The adapter imports @AGENTS.md (Cursor has no
58
+ // imports, so .cursorrules embeds the kernel). codex/cline/windsurf read
59
+ // AGENTS.md directly and need no adapter.
60
+ const ADAPTER_MAP = {
61
+ claude: 'CLAUDE.md',
62
+ gemini: 'GEMINI.md',
63
+ cursor: '.cursorrules',
64
+ };
65
+
52
66
  // Marker dirs used for auto-detection (scanned inside project root)
53
67
  const AUTO_MARKERS = [
54
68
  { dir: '.cursor', target: 'cursor' },
@@ -177,48 +191,53 @@ function detectTarget(projectRoot) {
177
191
  // ---- install actions ----
178
192
 
179
193
  /**
180
- * Install doctrine (AGENTS.md) into project root. Append-only, idempotent.
181
- * @param {string} projectRoot
182
- * @param {boolean} dryRun
194
+ * Read a file from the package root. Returns string, or null (logs) on error.
195
+ * @param {string} rel
183
196
  * @param {(msg: string) => void} log
184
- * @returns {boolean} true = success (or no-op), false = error
197
+ * @returns {string|null}
185
198
  */
186
- function installDoctrine(projectRoot, dryRun, log) {
187
- const dest = path.join(projectRoot, 'AGENTS.md');
188
- const srcPath = path.join(PKG_ROOT, 'AGENTS.md');
189
-
190
- let srcContent;
199
+ function readPkgFile(rel, log) {
191
200
  try {
192
- srcContent = fs.readFileSync(srcPath, 'utf8');
201
+ return fs.readFileSync(path.join(PKG_ROOT, rel), 'utf8');
193
202
  } catch (err) {
194
- log(`ERROR: cannot read package AGENTS.md: ${err.message}`);
195
- return false;
203
+ log(`ERROR: cannot read package ${rel}: ${err.message}`);
204
+ return null;
196
205
  }
206
+ }
197
207
 
208
+ /**
209
+ * Install a doctrine/adapter markdown file. Append-only, idempotent, never
210
+ * clobbers user content above the maestro block; refuses symlinks.
211
+ * @param {string} dest absolute destination path
212
+ * @param {string} srcContent content to install
213
+ * @param {string} label short name for logs (e.g. "AGENTS.md")
214
+ * @param {boolean} dryRun
215
+ * @param {(msg: string) => void} log
216
+ * @returns {boolean} true = success (or no-op), false = error
217
+ */
218
+ function appendOnlyDoctrine(dest, srcContent, label, dryRun, log) {
198
219
  const block = `\n${SENTINEL}\n${srcContent}\n${SENTINEL_END}\n`;
199
220
 
200
- // Check if dest exists
201
221
  let existsStat;
202
222
  try { existsStat = fs.lstatSync(dest); } catch { existsStat = null; }
203
223
 
204
224
  if (existsStat) {
205
225
  if (existsStat.isSymbolicLink()) {
206
- log(`ERROR: AGENTS.md is a symlink — refusing to write through it: ${dest}`);
226
+ log(`ERROR: ${label} is a symlink — refusing to write through it: ${dest}`);
207
227
  return false;
208
228
  }
209
229
 
210
230
  let existing;
211
231
  try { existing = fs.readFileSync(dest, 'utf8'); } catch (err) {
212
- log(`ERROR: cannot read existing AGENTS.md: ${err.message}`);
232
+ log(`ERROR: cannot read existing ${label}: ${err.message}`);
213
233
  return false;
214
234
  }
215
235
 
216
236
  if (existing.includes(SENTINEL)) {
217
- log(`[doctrine] AGENTS.md already contains sentinel — skipping`);
237
+ log(`[doctrine] ${label} already contains sentinel — skipping`);
218
238
  return true;
219
239
  }
220
240
 
221
- // Append block
222
241
  if (dryRun) {
223
242
  log(`[dry-run] would append maestro doctrine to existing ${dest}`);
224
243
  return true;
@@ -226,14 +245,14 @@ function installDoctrine(projectRoot, dryRun, log) {
226
245
 
227
246
  const res = safeWrite(dest, existing + block);
228
247
  if (!res.ok) {
229
- log(`ERROR: failed to append to AGENTS.md: ${res.reason}`);
248
+ log(`ERROR: failed to append to ${label}: ${res.reason}`);
230
249
  return false;
231
250
  }
232
- log(`[doctrine] appended maestro block to existing AGENTS.md`);
251
+ log(`[doctrine] appended maestro block to existing ${label}`);
233
252
  return true;
234
253
  }
235
254
 
236
- // Absent — write fresh. Wrap in sentinel block so subsequent runs detect it.
255
+ // Absent — write fresh, wrapped in the sentinel so re-runs detect it.
237
256
  if (dryRun) {
238
257
  log(`[dry-run] would create ${dest}`);
239
258
  return true;
@@ -247,13 +266,43 @@ function installDoctrine(projectRoot, dryRun, log) {
247
266
  const freshContent = SENTINEL + '\n' + srcContent + '\n' + SENTINEL_END + '\n';
248
267
  const res = safeWrite(dest, freshContent);
249
268
  if (!res.ok) {
250
- log(`ERROR: failed to write AGENTS.md: ${res.reason}`);
269
+ log(`ERROR: failed to write ${label}: ${res.reason}`);
251
270
  return false;
252
271
  }
253
- log(`[doctrine] wrote AGENTS.md`);
272
+ log(`[doctrine] wrote ${label}`);
254
273
  return true;
255
274
  }
256
275
 
276
+ /**
277
+ * Install the portable doctrine core (AGENTS.md) into the project root.
278
+ * @param {string} projectRoot
279
+ * @param {boolean} dryRun
280
+ * @param {(msg: string) => void} log
281
+ * @returns {boolean}
282
+ */
283
+ function installDoctrine(projectRoot, dryRun, log) {
284
+ const src = readPkgFile('AGENTS.md', log);
285
+ if (src === null) return false;
286
+ return appendOnlyDoctrine(path.join(projectRoot, 'AGENTS.md'), src, 'AGENTS.md', dryRun, log);
287
+ }
288
+
289
+ /**
290
+ * Install the runtime adapter for a target (CLAUDE.md / GEMINI.md /
291
+ * .cursorrules). codex/cline/windsurf read AGENTS.md directly -> no-op.
292
+ * @param {string} target
293
+ * @param {string} projectRoot
294
+ * @param {boolean} dryRun
295
+ * @param {(msg: string) => void} log
296
+ * @returns {boolean}
297
+ */
298
+ function installAdapter(target, projectRoot, dryRun, log) {
299
+ const rel = ADAPTER_MAP[target];
300
+ if (!rel) return true; // no adapter for this target
301
+ const src = readPkgFile(rel, log);
302
+ if (src === null) return false;
303
+ return appendOnlyDoctrine(path.join(projectRoot, rel), src, rel, dryRun, log);
304
+ }
305
+
257
306
  /**
258
307
  * Recursively copy srcDir -> destDir, skipping *.test.cjs files.
259
308
  * @param {string} srcDir
@@ -345,9 +394,81 @@ function installEngine(projectRoot, dryRun, log) {
345
394
  }
346
395
  }
347
396
 
397
+ // docs/orchestration.md — the on-demand S2-S6 multi-agent protocol the
398
+ // kernel references. Maestro-owned reference file; copy (refuse symlinks).
399
+ const srcDocs = path.join(PKG_ROOT, 'docs', 'orchestration.md');
400
+ const destDocs = path.join(projectRoot, 'docs', 'orchestration.md');
401
+ if (dryRun) {
402
+ log(`[dry-run] would write ${destDocs}`);
403
+ } else if (isSymlink(destDocs)) {
404
+ log(`ERROR: docs/orchestration.md is a symlink — refusing: ${destDocs}`);
405
+ ok = false;
406
+ } else {
407
+ try {
408
+ fs.mkdirSync(path.dirname(destDocs), { recursive: true });
409
+ fs.writeFileSync(destDocs, fs.readFileSync(srcDocs));
410
+ log(`[doctrine] copied ${destDocs}`);
411
+ } catch (err) {
412
+ log(`ERROR: failed to copy docs/orchestration.md: ${err.message}`);
413
+ ok = false;
414
+ }
415
+ }
416
+
348
417
  return ok;
349
418
  }
350
419
 
420
+ /**
421
+ * Copy a single package template file to dest, no-clobber. Skips when dest
422
+ * already exists, refuses symlinks, honors dry-run. Reuses safeMkdirp +
423
+ * safeWrite. Shared by wrapper and Codex-skill installs.
424
+ * @param {string} src absolute source path (under PKG_ROOT)
425
+ * @param {string} dest absolute destination path
426
+ * @param {string} label short tag for logs (e.g. "wrapper", "codex-skill")
427
+ * @param {boolean} dryRun
428
+ * @param {(msg: string) => void} log
429
+ * @returns {boolean} true = success (wrote, skipped, or planned), false = error
430
+ */
431
+ function installNoClobberFile(src, dest, label, dryRun, log) {
432
+ // Check if dest exists already (no-clobber)
433
+ let destStat;
434
+ try { destStat = fs.lstatSync(dest); } catch { destStat = null; }
435
+
436
+ if (destStat) {
437
+ if (destStat.isSymbolicLink()) {
438
+ log(`ERROR: ${label} dest is a symlink — refusing: ${dest}`);
439
+ return false;
440
+ }
441
+ log(`[${label}] skipped (exists, not clobbered): ${dest}`);
442
+ return true;
443
+ }
444
+
445
+ let srcContent;
446
+ try {
447
+ srcContent = fs.readFileSync(src, 'utf8');
448
+ } catch (err) {
449
+ log(`ERROR: cannot read template ${src}: ${err.message}`);
450
+ return false;
451
+ }
452
+
453
+ if (dryRun) {
454
+ log(`[dry-run] would create ${dest}`);
455
+ return true;
456
+ }
457
+
458
+ if (!safeMkdirp(dest)) {
459
+ log(`ERROR: could not create parent dir for ${dest}`);
460
+ return false;
461
+ }
462
+
463
+ const res = safeWrite(dest, srcContent);
464
+ if (!res.ok) {
465
+ log(`ERROR: failed to write ${label} ${dest}: ${res.reason}`);
466
+ return false;
467
+ }
468
+ log(`[${label}] wrote ${dest}`);
469
+ return true;
470
+ }
471
+
351
472
  /**
352
473
  * Install wrapper file (no-clobber).
353
474
  * @param {string} target
@@ -385,44 +506,31 @@ function installWrapper(target, projectRoot, userGlobal, dryRun, log) {
385
506
  dest = path.join(projectRoot, mapping.proj);
386
507
  }
387
508
 
388
- // Check if dest exists already (no-clobber)
389
- let destStat;
390
- try { destStat = fs.lstatSync(dest); } catch { destStat = null; }
391
-
392
- if (destStat) {
393
- if (destStat.isSymbolicLink()) {
394
- log(`ERROR: wrapper dest is a symlink — refusing: ${dest}`);
395
- return false;
396
- }
397
- log(`[wrapper] skipped (exists, not clobbered): ${dest}`);
398
- return true;
399
- }
400
-
401
- let srcContent;
402
- try {
403
- srcContent = fs.readFileSync(src, 'utf8');
404
- } catch (err) {
405
- log(`ERROR: cannot read template ${src}: ${err.message}`);
406
- return false;
407
- }
408
-
409
- if (dryRun) {
410
- log(`[dry-run] would create ${dest}`);
411
- return true;
412
- }
509
+ return installNoClobberFile(src, dest, 'wrapper', dryRun, log);
510
+ }
413
511
 
414
- if (!safeMkdirp(dest)) {
415
- log(`ERROR: could not create parent dir for ${dest}`);
416
- return false;
417
- }
512
+ /**
513
+ * Install the Codex skill templates (no-clobber) alongside the codex wrapper.
514
+ * Project mode -> <project>/.agents/skills/<name>/SKILL.md; --user/global mode
515
+ * -> ~/.agents/skills/<name>/SKILL.md (mirrors installWrapper's dest logic).
516
+ * @param {string} projectRoot
517
+ * @param {boolean} userGlobal
518
+ * @param {boolean} dryRun
519
+ * @param {(msg: string) => void} log
520
+ * @returns {boolean}
521
+ */
522
+ function installCodexSkills(projectRoot, userGlobal, dryRun, log) {
523
+ const skillsRoot = userGlobal
524
+ ? path.join(os.homedir(), '.agents', 'skills')
525
+ : path.join(projectRoot, '.agents', 'skills');
418
526
 
419
- const res = safeWrite(dest, srcContent);
420
- if (!res.ok) {
421
- log(`ERROR: failed to write wrapper ${dest}: ${res.reason}`);
422
- return false;
527
+ let ok = true;
528
+ for (const name of CODEX_SKILLS) {
529
+ const src = path.join(PKG_ROOT, 'integrations', 'codex', 'skills', name, 'SKILL.md');
530
+ const dest = path.join(skillsRoot, name, 'SKILL.md');
531
+ if (!installNoClobberFile(src, dest, 'codex-skill', dryRun, log)) ok = false;
423
532
  }
424
- log(`[wrapper] wrote ${dest}`);
425
- return true;
533
+ return ok;
426
534
  }
427
535
 
428
536
  // ---- main entry ----
@@ -461,17 +569,24 @@ function run(argv) {
461
569
 
462
570
  let anyError = false;
463
571
 
464
- // 1. Doctrine
572
+ // 1. Doctrine — portable AGENTS.md kernel + this target's runtime adapter.
465
573
  if (!installDoctrine(project, dryRun, log)) anyError = true;
574
+ if (!installAdapter(target, project, dryRun, log)) anyError = true;
466
575
 
467
- // 2. Engine
576
+ // 2. Engine — frontier/ + bin/maestro.cjs + docs/orchestration.md.
468
577
  if (!installEngine(project, dryRun, log)) anyError = true;
469
578
 
470
- // 3. Wrapper (skip if no specific target detected)
579
+ // 3. Wrapper — this target's /frontier command (skip if no target detected).
471
580
  if (target !== 'none') {
472
581
  if (!installWrapper(target, project, userGlobal, dryRun, log)) anyError = true;
473
582
  }
474
583
 
584
+ // 3b. Codex skills — the .agents/skills/<name>/SKILL.md set ships alongside
585
+ // the deprecated codex prompt wrapper.
586
+ if (target === 'codex') {
587
+ if (!installCodexSkills(project, userGlobal, dryRun, log)) anyError = true;
588
+ }
589
+
475
590
  if (anyError) {
476
591
  log('install completed with errors (see above)');
477
592
  return 1;