@maestrofrontier/frontier 1.4.5 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/plugins/marketplace.json +21 -21
- package/.codex-plugin/plugin.json +29 -29
- package/.cursorrules +197 -194
- package/AGENTS.md +3 -3
- package/README.md +368 -368
- package/bin/maestro.cjs +75 -75
- package/commands/compress.md +36 -36
- package/commands/frontier.md +124 -124
- package/commands/terse.md +23 -23
- package/docs/codex.md +167 -167
- package/docs/orchestration.md +168 -168
- package/frontier/cli.cjs +279 -252
- package/frontier/config.cjs +468 -468
- package/frontier/dispatch.cjs +267 -255
- package/frontier/judge.cjs +92 -92
- package/frontier/progress.cjs +138 -0
- package/frontier/run.cjs +201 -180
- package/frontier/schema.cjs +112 -112
- package/frontier/semaphore.cjs +49 -49
- package/frontier/synthesize.cjs +79 -79
- package/hooks/frontier-autorun.cjs +135 -120
- package/hooks/hooks.json +103 -103
- package/hooks/maestro-doctrine-guard.cjs +81 -81
- package/hooks/maestro-gate-reminder.cjs +22 -7
- package/hooks/maestro-gate-telemetry.cjs +79 -77
- package/hooks/maestro-phase-scope.cjs +118 -118
- package/hooks/maestro-statusline-sync.cjs +152 -152
- package/hooks/maestro-subagent-guard.cjs +148 -148
- package/hooks/maestro-terse-mode.cjs +189 -189
- package/hooks/maestro-toolbudget-advisory.cjs +127 -127
- package/integrations/README.md +111 -111
- package/integrations/cline/skills/frontier/SKILL.md +75 -75
- package/integrations/codex/prompts/frontier.md +70 -70
- package/integrations/codex/prompts/update.md +39 -39
- package/integrations/codex/skills/maestro-frontier/SKILL.md +122 -122
- package/integrations/codex/skills/maestro-settings/SKILL.md +55 -55
- package/integrations/codex/skills/maestro-terse/SKILL.md +58 -58
- package/integrations/codex/skills/maestro-update/SKILL.md +31 -31
- package/integrations/cursor/commands/frontier.md +63 -63
- package/integrations/cursor/commands/update.md +34 -34
- package/integrations/gemini/commands/frontier.toml +76 -76
- package/integrations/windsurf/workflows/frontier.md +70 -70
- package/package.json +59 -58
- package/scripts/install.cjs +1014 -1014
- package/settings/cli.cjs +140 -140
- package/settings/config.cjs +309 -309
- package/skills/maestro-frontier/SKILL.md +122 -122
- package/skills/maestro-settings/SKILL.md +55 -55
- package/skills/maestro-terse/SKILL.md +58 -58
- package/skills/maestro-update/SKILL.md +31 -31
- package/skills/terse/SKILL.md +74 -74
package/frontier/cli.cjs
CHANGED
|
@@ -1,252 +1,279 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Maestro Frontier — CLI entrypoint. Subcommands: mode, status, run.
|
|
3
|
-
|
|
4
|
-
'use strict';
|
|
5
|
-
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const { DEFAULTS, loadState, saveState, resolveScope, validateMode, validatePreset, validateModel, adoptLegacyState } = require('./config.cjs');
|
|
8
|
-
const { runFrontier, canonicalModelId, canonicalPresetId } = require('./run.cjs');
|
|
9
|
-
|
|
10
|
-
// ---------- arg helpers ----------
|
|
11
|
-
|
|
12
|
-
function getFlag(argv, flag) {
|
|
13
|
-
const i = argv.indexOf(flag);
|
|
14
|
-
return i !== -1 && i + 1 < argv.length ? argv[i + 1] : null;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function hasFlag(argv, flag) {
|
|
18
|
-
return argv.indexOf(flag) !== -1;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Strip --scope <value> from an argv array so it never leaks into prompts.
|
|
23
|
-
* @param {string[]} argv
|
|
24
|
-
* @returns {string[]}
|
|
25
|
-
*/
|
|
26
|
-
function stripScopeFlag(argv) {
|
|
27
|
-
const out = [];
|
|
28
|
-
let i = 0;
|
|
29
|
-
while (i < argv.length) {
|
|
30
|
-
if (argv[i] === '--scope') {
|
|
31
|
-
i += 2; // skip flag and its value
|
|
32
|
-
} else {
|
|
33
|
-
out.push(argv[i]);
|
|
34
|
-
i++;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return out;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// ---------- usage ----------
|
|
41
|
-
|
|
42
|
-
function usage() {
|
|
43
|
-
process.stderr.write(
|
|
44
|
-
'Usage:\n' +
|
|
45
|
-
' frontier mode <off|single|fusion> [--model X] [--preset Y] [--models a,b,c] [--scope <name>]\n' +
|
|
46
|
-
' frontier status [--scope <name>]\n' +
|
|
47
|
-
' frontier run [<prompt>|-] [--scope <name>]\n' +
|
|
48
|
-
' frontier adopt [--force] [--scope <name>]\n'
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// ---------- subcommands ----------
|
|
53
|
-
|
|
54
|
-
function cmdMode(argv, scope) {
|
|
55
|
-
const newMode = argv[0];
|
|
56
|
-
if (!newMode || !validateMode(newMode)) {
|
|
57
|
-
process.stderr.write('ERROR: mode must be off, single, or fusion\n');
|
|
58
|
-
process.exit(2);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
let state;
|
|
62
|
-
|
|
63
|
-
if (newMode === 'off') {
|
|
64
|
-
state = { mode: 'off' };
|
|
65
|
-
} else if (newMode === 'single') {
|
|
66
|
-
const rawModel = getFlag(argv, '--model');
|
|
67
|
-
const model = rawModel && canonicalModelId(rawModel);
|
|
68
|
-
if (!model) {
|
|
69
|
-
process.stderr.write('ERROR: --model required for single mode\n');
|
|
70
|
-
process.exit(2);
|
|
71
|
-
}
|
|
72
|
-
if (!validateModel(model)) {
|
|
73
|
-
process.stderr.write('ERROR: unknown model: ' + model + '\n');
|
|
74
|
-
process.exit(2);
|
|
75
|
-
}
|
|
76
|
-
state = { mode: 'single', model };
|
|
77
|
-
} else {
|
|
78
|
-
// fusion
|
|
79
|
-
const rawPreset = getFlag(argv, '--preset');
|
|
80
|
-
const preset = rawPreset && canonicalPresetId(rawPreset);
|
|
81
|
-
if (!preset) {
|
|
82
|
-
process.stderr.write('ERROR: --preset required for fusion mode\n');
|
|
83
|
-
process.exit(2);
|
|
84
|
-
}
|
|
85
|
-
if (!validatePreset(preset)) {
|
|
86
|
-
process.stderr.write('ERROR: unknown preset: ' + preset + '\n');
|
|
87
|
-
process.exit(2);
|
|
88
|
-
}
|
|
89
|
-
if (preset === 'custom') {
|
|
90
|
-
const modelsRaw = getFlag(argv, '--models');
|
|
91
|
-
if (!modelsRaw) {
|
|
92
|
-
process.stderr.write('ERROR: --models required for custom preset\n');
|
|
93
|
-
process.exit(2);
|
|
94
|
-
}
|
|
95
|
-
const models = modelsRaw.split(',').map(m => canonicalModelId(m.trim())).filter(Boolean);
|
|
96
|
-
state = { mode: 'fusion', preset: 'custom', models };
|
|
97
|
-
} else {
|
|
98
|
-
state = { mode: 'fusion', preset };
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Optional judge/synth model overrides — apply to any fusion preset so
|
|
102
|
-
// users can mix freely (e.g. --judge opus --synth gpt-5.5). Unset =
|
|
103
|
-
// the preset's own stage model (presetStages) or the global default.
|
|
104
|
-
const rawJudge = getFlag(argv, '--judge');
|
|
105
|
-
const judge = rawJudge !== null ? canonicalModelId(rawJudge) : null;
|
|
106
|
-
if (judge !== null) {
|
|
107
|
-
if (!validateModel(judge)) {
|
|
108
|
-
process.stderr.write('ERROR: unknown judge model: ' + judge + '\n');
|
|
109
|
-
process.exit(2);
|
|
110
|
-
}
|
|
111
|
-
state.judgeModel = judge;
|
|
112
|
-
}
|
|
113
|
-
const rawSynth = getFlag(argv, '--synth');
|
|
114
|
-
const synth = rawSynth !== null ? canonicalModelId(rawSynth) : null;
|
|
115
|
-
if (synth !== null) {
|
|
116
|
-
if (!validateModel(synth)) {
|
|
117
|
-
process.stderr.write('ERROR: unknown synth model: ' + synth + '\n');
|
|
118
|
-
process.exit(2);
|
|
119
|
-
}
|
|
120
|
-
state.synthModel = synth;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
saveState(state, scope);
|
|
125
|
-
process.stdout.write('frontier mode set: ' + JSON.stringify(state) + '\n');
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function cmdStatus(scope) {
|
|
129
|
-
const state = loadState(scope);
|
|
130
|
-
process.stdout.write(JSON.stringify(state) + '\n');
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
async function cmdRun(argv, scope) {
|
|
134
|
-
// Strip --scope and its value before building the prompt so it never leaks.
|
|
135
|
-
const cleanArgv = stripScopeFlag(argv);
|
|
136
|
-
|
|
137
|
-
let prompt;
|
|
138
|
-
const rest = cleanArgv.join(' ').trim();
|
|
139
|
-
if (!rest || rest === '-') {
|
|
140
|
-
prompt = fs.readFileSync(0, 'utf8');
|
|
141
|
-
} else {
|
|
142
|
-
prompt = rest;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const state = loadState(scope);
|
|
146
|
-
|
|
147
|
-
if (state.mode === 'off') {
|
|
148
|
-
process.stdout.write('Frontier off — using normal Maestro (engine not invoked).\n');
|
|
149
|
-
process.exit(0);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Maestro Frontier — CLI entrypoint. Subcommands: mode, status, run.
|
|
3
|
+
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const { DEFAULTS, loadState, saveState, resolveScope, validateMode, validatePreset, validateModel, adoptLegacyState } = require('./config.cjs');
|
|
8
|
+
const { runFrontier, canonicalModelId, canonicalPresetId } = require('./run.cjs');
|
|
9
|
+
|
|
10
|
+
// ---------- arg helpers ----------
|
|
11
|
+
|
|
12
|
+
function getFlag(argv, flag) {
|
|
13
|
+
const i = argv.indexOf(flag);
|
|
14
|
+
return i !== -1 && i + 1 < argv.length ? argv[i + 1] : null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function hasFlag(argv, flag) {
|
|
18
|
+
return argv.indexOf(flag) !== -1;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Strip --scope <value> from an argv array so it never leaks into prompts.
|
|
23
|
+
* @param {string[]} argv
|
|
24
|
+
* @returns {string[]}
|
|
25
|
+
*/
|
|
26
|
+
function stripScopeFlag(argv) {
|
|
27
|
+
const out = [];
|
|
28
|
+
let i = 0;
|
|
29
|
+
while (i < argv.length) {
|
|
30
|
+
if (argv[i] === '--scope') {
|
|
31
|
+
i += 2; // skip flag and its value
|
|
32
|
+
} else {
|
|
33
|
+
out.push(argv[i]);
|
|
34
|
+
i++;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ---------- usage ----------
|
|
41
|
+
|
|
42
|
+
function usage() {
|
|
43
|
+
process.stderr.write(
|
|
44
|
+
'Usage:\n' +
|
|
45
|
+
' frontier mode <off|single|fusion> [--model X] [--preset Y] [--models a,b,c] [--scope <name>]\n' +
|
|
46
|
+
' frontier status [--scope <name>]\n' +
|
|
47
|
+
' frontier run [<prompt>|-] [--scope <name>]\n' +
|
|
48
|
+
' frontier adopt [--force] [--scope <name>]\n'
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ---------- subcommands ----------
|
|
53
|
+
|
|
54
|
+
function cmdMode(argv, scope) {
|
|
55
|
+
const newMode = argv[0];
|
|
56
|
+
if (!newMode || !validateMode(newMode)) {
|
|
57
|
+
process.stderr.write('ERROR: mode must be off, single, or fusion\n');
|
|
58
|
+
process.exit(2);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let state;
|
|
62
|
+
|
|
63
|
+
if (newMode === 'off') {
|
|
64
|
+
state = { mode: 'off' };
|
|
65
|
+
} else if (newMode === 'single') {
|
|
66
|
+
const rawModel = getFlag(argv, '--model');
|
|
67
|
+
const model = rawModel && canonicalModelId(rawModel);
|
|
68
|
+
if (!model) {
|
|
69
|
+
process.stderr.write('ERROR: --model required for single mode\n');
|
|
70
|
+
process.exit(2);
|
|
71
|
+
}
|
|
72
|
+
if (!validateModel(model)) {
|
|
73
|
+
process.stderr.write('ERROR: unknown model: ' + model + '\n');
|
|
74
|
+
process.exit(2);
|
|
75
|
+
}
|
|
76
|
+
state = { mode: 'single', model };
|
|
77
|
+
} else {
|
|
78
|
+
// fusion
|
|
79
|
+
const rawPreset = getFlag(argv, '--preset');
|
|
80
|
+
const preset = rawPreset && canonicalPresetId(rawPreset);
|
|
81
|
+
if (!preset) {
|
|
82
|
+
process.stderr.write('ERROR: --preset required for fusion mode\n');
|
|
83
|
+
process.exit(2);
|
|
84
|
+
}
|
|
85
|
+
if (!validatePreset(preset)) {
|
|
86
|
+
process.stderr.write('ERROR: unknown preset: ' + preset + '\n');
|
|
87
|
+
process.exit(2);
|
|
88
|
+
}
|
|
89
|
+
if (preset === 'custom') {
|
|
90
|
+
const modelsRaw = getFlag(argv, '--models');
|
|
91
|
+
if (!modelsRaw) {
|
|
92
|
+
process.stderr.write('ERROR: --models required for custom preset\n');
|
|
93
|
+
process.exit(2);
|
|
94
|
+
}
|
|
95
|
+
const models = modelsRaw.split(',').map(m => canonicalModelId(m.trim())).filter(Boolean);
|
|
96
|
+
state = { mode: 'fusion', preset: 'custom', models };
|
|
97
|
+
} else {
|
|
98
|
+
state = { mode: 'fusion', preset };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Optional judge/synth model overrides — apply to any fusion preset so
|
|
102
|
+
// users can mix freely (e.g. --judge opus --synth gpt-5.5). Unset =
|
|
103
|
+
// the preset's own stage model (presetStages) or the global default.
|
|
104
|
+
const rawJudge = getFlag(argv, '--judge');
|
|
105
|
+
const judge = rawJudge !== null ? canonicalModelId(rawJudge) : null;
|
|
106
|
+
if (judge !== null) {
|
|
107
|
+
if (!validateModel(judge)) {
|
|
108
|
+
process.stderr.write('ERROR: unknown judge model: ' + judge + '\n');
|
|
109
|
+
process.exit(2);
|
|
110
|
+
}
|
|
111
|
+
state.judgeModel = judge;
|
|
112
|
+
}
|
|
113
|
+
const rawSynth = getFlag(argv, '--synth');
|
|
114
|
+
const synth = rawSynth !== null ? canonicalModelId(rawSynth) : null;
|
|
115
|
+
if (synth !== null) {
|
|
116
|
+
if (!validateModel(synth)) {
|
|
117
|
+
process.stderr.write('ERROR: unknown synth model: ' + synth + '\n');
|
|
118
|
+
process.exit(2);
|
|
119
|
+
}
|
|
120
|
+
state.synthModel = synth;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
saveState(state, scope);
|
|
125
|
+
process.stdout.write('frontier mode set: ' + JSON.stringify(state) + '\n');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function cmdStatus(scope) {
|
|
129
|
+
const state = loadState(scope);
|
|
130
|
+
process.stdout.write(JSON.stringify(state) + '\n');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function cmdRun(argv, scope) {
|
|
134
|
+
// Strip --scope and its value before building the prompt so it never leaks.
|
|
135
|
+
const cleanArgv = stripScopeFlag(argv);
|
|
136
|
+
|
|
137
|
+
let prompt;
|
|
138
|
+
const rest = cleanArgv.join(' ').trim();
|
|
139
|
+
if (!rest || rest === '-') {
|
|
140
|
+
prompt = fs.readFileSync(0, 'utf8');
|
|
141
|
+
} else {
|
|
142
|
+
prompt = rest;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const state = loadState(scope);
|
|
146
|
+
|
|
147
|
+
if (state.mode === 'off') {
|
|
148
|
+
process.stdout.write('Frontier off — using normal Maestro (engine not invoked).\n');
|
|
149
|
+
process.exit(0);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function onProgress(ev) {
|
|
153
|
+
switch (ev.phase) {
|
|
154
|
+
case 'panel-start':
|
|
155
|
+
process.stderr.write('⚡ Activating Frontier Intelligence\n');
|
|
156
|
+
process.stderr.write('Fanning prompt to the panel — ' + ev.models.join(' \xb7 ') + '\n');
|
|
157
|
+
break;
|
|
158
|
+
case 'panel-progress':
|
|
159
|
+
process.stderr.write('Panel responding… ' + ev.done + '/' + ev.total + ' in\n');
|
|
160
|
+
break;
|
|
161
|
+
case 'judge-start':
|
|
162
|
+
process.stderr.write('Convening the judge (' + ev.model + ')\n');
|
|
163
|
+
break;
|
|
164
|
+
case 'synth-start':
|
|
165
|
+
process.stderr.write('Synthesizing the verdict\n');
|
|
166
|
+
break;
|
|
167
|
+
case 'degraded':
|
|
168
|
+
process.stderr.write('Frontier degraded — relaying best available (' + ev.failed + ' down)\n');
|
|
169
|
+
break;
|
|
170
|
+
case 'done':
|
|
171
|
+
process.stderr.write('Frontier verdict ready — ' + ev.models + ' models \xb7 ' + Math.round(ev.ms / 1000) + 's\n');
|
|
172
|
+
break;
|
|
173
|
+
case 'single-start':
|
|
174
|
+
process.stderr.write('⚡ Activating Frontier Intelligence (single \xb7 ' + ev.model + ')\n');
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const result = await runFrontier({ prompt, state, deps: { onProgress } });
|
|
180
|
+
|
|
181
|
+
if (result.status === 'error') {
|
|
182
|
+
process.stderr.write('ERROR [' + result.failure_reason + ']: ' + result.error + '\n');
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
process.stdout.write(result.final + '\n');
|
|
187
|
+
|
|
188
|
+
if (result.mode === 'fusion') {
|
|
189
|
+
const models = (result.responses || []).map(r => r.model);
|
|
190
|
+
const failed = (result.failed_models || []).length;
|
|
191
|
+
const hasAnal = !!result.analysis;
|
|
192
|
+
process.stderr.write(
|
|
193
|
+
'meta: preset=' + result.preset +
|
|
194
|
+
' models=' + models.join(',') +
|
|
195
|
+
' analysis=' + hasAnal +
|
|
196
|
+
' failed=' + failed + '\n'
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
process.exit(0);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Adopt the legacy global frontier-state.json into the current Claude Code
|
|
204
|
+
// workspace scope (cc-*). Source is read-only; never overwrites an existing
|
|
205
|
+
// workspace state file unless --force. This is the explicit escape hatch the
|
|
206
|
+
// per-workspace isolation change requires: a workspace never inherits the old
|
|
207
|
+
// global armed mode automatically, so a user who wants it copies it once.
|
|
208
|
+
function cmdAdopt(argv, scope) {
|
|
209
|
+
const res = adoptLegacyState(scope, { force: hasFlag(argv, '--force') });
|
|
210
|
+
|
|
211
|
+
if (res.ok) {
|
|
212
|
+
process.stdout.write(
|
|
213
|
+
'frontier adopted legacy state into ' + res.scope + ': ' +
|
|
214
|
+
JSON.stringify(loadState(res.scope)) + '\n');
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
let msg;
|
|
219
|
+
switch (res.reason) {
|
|
220
|
+
case 'not-cc-scope':
|
|
221
|
+
msg = 'adopt only targets a Claude Code per-workspace scope (cc-*); current ' +
|
|
222
|
+
'scope is "' + res.scope + '". Run it inside a Claude Code workspace ' +
|
|
223
|
+
'(under a git project root), or arm Codex/Cursor with `mode --scope`.';
|
|
224
|
+
break;
|
|
225
|
+
case 'missing-legacy':
|
|
226
|
+
msg = 'no legacy global state to adopt (frontier-state.json not found). ' +
|
|
227
|
+
'Nothing to do — arm this workspace with `mode` instead.';
|
|
228
|
+
break;
|
|
229
|
+
case 'invalid-legacy':
|
|
230
|
+
msg = 'legacy state file is unreadable, a symlink, or invalid; refusing to adopt.';
|
|
231
|
+
break;
|
|
232
|
+
case 'exists':
|
|
233
|
+
msg = 'this workspace already has frontier state (' +
|
|
234
|
+
JSON.stringify(loadState(res.scope)) + '); pass --force to overwrite.';
|
|
235
|
+
break;
|
|
236
|
+
case 'unsafe-target':
|
|
237
|
+
msg = 'refusing to write workspace state (symlink or unsafe path): ' + res.path;
|
|
238
|
+
break;
|
|
239
|
+
case 'write-failed':
|
|
240
|
+
msg = 'failed to write workspace state file: ' + res.path;
|
|
241
|
+
break;
|
|
242
|
+
default:
|
|
243
|
+
msg = 'adopt failed (' + res.reason + ').';
|
|
244
|
+
}
|
|
245
|
+
process.stderr.write('ERROR [' + res.reason + ']: ' + msg + '\n');
|
|
246
|
+
process.exit(2);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ---------- main ----------
|
|
250
|
+
|
|
251
|
+
async function main() {
|
|
252
|
+
const argv = process.argv.slice(2);
|
|
253
|
+
const cmd = argv[0];
|
|
254
|
+
|
|
255
|
+
// Resolve scope once from full argv; all subcommands receive it.
|
|
256
|
+
const scope = resolveScope(argv);
|
|
257
|
+
|
|
258
|
+
if (cmd === 'mode') {
|
|
259
|
+
cmdMode(argv.slice(1), scope);
|
|
260
|
+
} else if (cmd === 'status') {
|
|
261
|
+
cmdStatus(scope);
|
|
262
|
+
} else if (cmd === 'run') {
|
|
263
|
+
await cmdRun(argv.slice(1), scope);
|
|
264
|
+
} else if (cmd === 'adopt') {
|
|
265
|
+
cmdAdopt(argv.slice(1), scope);
|
|
266
|
+
} else {
|
|
267
|
+
usage();
|
|
268
|
+
process.exit(2);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (require.main === module) {
|
|
273
|
+
main().catch(err => {
|
|
274
|
+
process.stderr.write(String(err.stack || err) + '\n');
|
|
275
|
+
process.exit(1);
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
module.exports = { main, getFlag };
|