@ttfw/envoi 1.0.7 → 1.0.8
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/README.md +9 -5
- package/dist/commands/install.d.ts +2 -0
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +2 -0
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/onboard.d.ts +7 -0
- package/dist/commands/onboard.d.ts.map +1 -1
- package/dist/commands/onboard.js +168 -41
- package/dist/commands/onboard.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -9
- package/dist/index.js.map +1 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +5 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/runner/orchestrator.d.ts.map +1 -1
- package/dist/runner/orchestrator.js +104 -57
- package/dist/runner/orchestrator.js.map +1 -1
- package/dist/types/config.d.ts +2 -0
- package/dist/types/config.d.ts.map +1 -1
- package/envoi.config.json +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -83,11 +83,14 @@ envoi start
|
|
|
83
83
|
`envoi start` handles onboarding end-to-end:
|
|
84
84
|
1. asks `Tour` vs `Setup`,
|
|
85
85
|
2. captures PRD/context (`--prd-file`, stdin, discovered file, editor, or inline paste),
|
|
86
|
-
3. configures mode (`
|
|
87
|
-
4.
|
|
88
|
-
5.
|
|
89
|
-
6.
|
|
90
|
-
7.
|
|
86
|
+
3. configures mode (`milestone|autonomous|task`) and builder (`cursor|claude_code`),
|
|
87
|
+
4. chooses planner provider (`claude_code|chatgpt`) and assigns planner model automatically,
|
|
88
|
+
5. optionally configures builder model when builder is `claude_code` (`sonnet|haiku`),
|
|
89
|
+
6. configures reviewer (`codex|none`, default recommended: `codex`),
|
|
90
|
+
7. auto-initializes git (`git init`) when no repository exists,
|
|
91
|
+
8. runs role connectivity checks,
|
|
92
|
+
9. writes `ROADMAP.json` from the planner snapshot,
|
|
93
|
+
10. asks whether to start execution now.
|
|
91
94
|
|
|
92
95
|
Non-TTY (agent-run) onboarding:
|
|
93
96
|
- Envoi emits exactly one next question per run (`envoi.onboarding.next_question.v1`) and returns exit code `0` by default (to avoid noisy error wrappers in agent CLIs).
|
|
@@ -98,6 +101,7 @@ Non-TTY (agent-run) onboarding:
|
|
|
98
101
|
- `--verbose-onboarding` (human + pretty JSON)
|
|
99
102
|
- `--onboarding-output compact|json|verbose`
|
|
100
103
|
- Use `--strict-exit` to return exit code `20` when onboarding still needs input.
|
|
104
|
+
- Use `--reconfigure` to force a fresh questionnaire after onboarding is already marked complete.
|
|
101
105
|
- Resume with either:
|
|
102
106
|
- `envoi start --answers-json '<json>'`
|
|
103
107
|
- `envoi start --answers-file <path>`
|
|
@@ -5,6 +5,7 @@ interface InstallOptions {
|
|
|
5
5
|
prdFile?: string;
|
|
6
6
|
mode?: string;
|
|
7
7
|
builder?: string;
|
|
8
|
+
plannerProvider?: string;
|
|
8
9
|
reviewer?: string;
|
|
9
10
|
orchestratorModel?: string;
|
|
10
11
|
builderModel?: string;
|
|
@@ -15,6 +16,7 @@ interface InstallOptions {
|
|
|
15
16
|
verboseOnboarding?: boolean;
|
|
16
17
|
onboardingOutput?: string;
|
|
17
18
|
strictExit?: boolean;
|
|
19
|
+
reconfigure?: boolean;
|
|
18
20
|
autoRun?: boolean;
|
|
19
21
|
globalInstall?: boolean;
|
|
20
22
|
skipGlobalInstall?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAMA,UAAU,cAAc;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAqCD,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAMA,UAAU,cAAc;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAqCD,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAqE3E"}
|
package/dist/commands/install.js
CHANGED
|
@@ -49,6 +49,7 @@ export async function installCommand(options) {
|
|
|
49
49
|
prdFile: options.prdFile,
|
|
50
50
|
mode: options.mode,
|
|
51
51
|
builder: options.builder,
|
|
52
|
+
plannerProvider: options.plannerProvider,
|
|
52
53
|
reviewer: options.reviewer,
|
|
53
54
|
orchestratorModel: options.orchestratorModel,
|
|
54
55
|
builderModel: options.builderModel,
|
|
@@ -59,6 +60,7 @@ export async function installCommand(options) {
|
|
|
59
60
|
verboseOnboarding: options.verboseOnboarding,
|
|
60
61
|
onboardingOutput: options.onboardingOutput,
|
|
61
62
|
strictExit: options.strictExit,
|
|
63
|
+
reconfigure: options.reconfigure,
|
|
62
64
|
showTourPrompt: true,
|
|
63
65
|
autoRun: options.autoRun,
|
|
64
66
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
type LoopMode = 'task' | 'milestone' | 'autonomous';
|
|
13
13
|
type BuilderChoice = 'claude_code' | 'cursor';
|
|
14
|
+
type PlannerProvider = 'claude_code' | 'chatgpt';
|
|
14
15
|
type ReviewerChoice = 'none' | 'codex';
|
|
15
16
|
declare const ONBOARDING_REVIEW_PAYLOAD_TYPE = "envoi.onboarding.review.v1";
|
|
16
17
|
export type OnboardingOutputMode = 'compact' | 'json' | 'verbose';
|
|
@@ -39,6 +40,7 @@ interface OnboardingReviewPayload {
|
|
|
39
40
|
summary: {
|
|
40
41
|
mode?: string;
|
|
41
42
|
builder?: string;
|
|
43
|
+
planner_provider?: string;
|
|
42
44
|
orchestrator_model?: string;
|
|
43
45
|
builder_model?: string;
|
|
44
46
|
reviewer?: string;
|
|
@@ -61,6 +63,7 @@ interface OnboardingAnswerPayload {
|
|
|
61
63
|
export interface OnboardingQuestionInputs {
|
|
62
64
|
mode?: LoopMode;
|
|
63
65
|
builder?: BuilderChoice;
|
|
66
|
+
plannerProvider?: PlannerProvider;
|
|
64
67
|
orchestratorModel?: string;
|
|
65
68
|
builderModel?: string;
|
|
66
69
|
reviewer?: ReviewerChoice;
|
|
@@ -69,6 +72,7 @@ export interface OnboardingQuestionInputs {
|
|
|
69
72
|
defaults: {
|
|
70
73
|
mode: LoopMode;
|
|
71
74
|
builder: BuilderChoice;
|
|
75
|
+
plannerProvider: PlannerProvider;
|
|
72
76
|
orchestratorModel: string;
|
|
73
77
|
builderModel: string;
|
|
74
78
|
reviewer: ReviewerChoice;
|
|
@@ -78,6 +82,7 @@ export interface OnboardingQuestionInputs {
|
|
|
78
82
|
interface InlineAnswersState {
|
|
79
83
|
mode?: LoopMode;
|
|
80
84
|
builder?: BuilderChoice;
|
|
85
|
+
plannerProvider?: PlannerProvider;
|
|
81
86
|
orchestratorModel?: string;
|
|
82
87
|
builderModel?: string;
|
|
83
88
|
reviewer?: ReviewerChoice;
|
|
@@ -107,9 +112,11 @@ export declare function extractRoadmapQuestions(prompt: string): string[];
|
|
|
107
112
|
export declare function onboardCommand(options: {
|
|
108
113
|
configPath?: string;
|
|
109
114
|
forceInit?: boolean;
|
|
115
|
+
reconfigure?: boolean;
|
|
110
116
|
prdFile?: string;
|
|
111
117
|
mode?: string;
|
|
112
118
|
builder?: string;
|
|
119
|
+
plannerProvider?: string;
|
|
113
120
|
reviewer?: string;
|
|
114
121
|
orchestratorModel?: string;
|
|
115
122
|
builderModel?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onboard.d.ts","sourceRoot":"","sources":["../../src/commands/onboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAqBH,KAAK,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,YAAY,CAAC;AACpD,KAAK,aAAa,GAAG,aAAa,GAAG,QAAQ,CAAC;AAC9C,KAAK,cAAc,GAAG,MAAM,GAAG,OAAO,CAAC;AA2BvC,QAAA,MAAM,8BAA8B,+BAA+B,CAAC;AAOpE,MAAM,MAAM,oBAAoB,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAElE,MAAM,WAAW,0BAA0B;IACzC,UAAU,EAAE,oBAAoB,CAAC;IACjC,UAAU,EAAE,OAAO,CAAC;CACrB;
|
|
1
|
+
{"version":3,"file":"onboard.d.ts","sourceRoot":"","sources":["../../src/commands/onboard.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/onboard.js
CHANGED
|
@@ -34,16 +34,21 @@ const NON_INTERACTIVE_STDIN_PROBE_TIMEOUT_MS = 200;
|
|
|
34
34
|
const NON_INTERACTIVE_ANSWER_TIMEOUT_MS = 20_000;
|
|
35
35
|
const NON_INTERACTIVE_MAX_INVALID_RETRIES = 2;
|
|
36
36
|
const MODE_CHOICES = [
|
|
37
|
-
{ value: 'task', label: 'task', desc: 'One bounded cycle each time (lowest drift).' },
|
|
38
37
|
{ value: 'milestone', label: 'milestone', desc: 'Runs until milestone boundary, then pauses.' },
|
|
39
38
|
{ value: 'autonomous', label: 'autonomous', desc: 'Runs continuously until blocked or limited.' },
|
|
39
|
+
{ value: 'task', label: 'task', desc: 'One bounded cycle each time (lowest drift).' },
|
|
40
40
|
];
|
|
41
41
|
const BUILDER_CHOICES = [
|
|
42
42
|
{ value: 'cursor', label: 'cursor agent', desc: 'Fast iterative execution, low token pressure.' },
|
|
43
43
|
{ value: 'claude_code', label: 'claude code', desc: 'Single CLI stack with simpler setup.' },
|
|
44
44
|
];
|
|
45
|
-
const
|
|
46
|
-
|
|
45
|
+
const PLANNER_PROVIDER_CHOICES = [
|
|
46
|
+
{ value: 'claude_code', label: 'claude code', desc: 'Claude Code orchestrator runtime.' },
|
|
47
|
+
{ value: 'chatgpt', label: 'chatgpt', desc: 'Codex/OpenAI orchestrator runtime.' },
|
|
48
|
+
];
|
|
49
|
+
const CLAUDE_PLANNER_MODEL = 'opus';
|
|
50
|
+
const CHATGPT_PLANNER_MODEL = 'gpt-5.3';
|
|
51
|
+
const BUILDER_MODEL_CHOICES = ['sonnet', 'haiku'];
|
|
47
52
|
const REVIEWER_MODEL_CHOICES = ['gpt-5', 'o3', 'gpt-5-mini'];
|
|
48
53
|
function normalizeLoopMode(value) {
|
|
49
54
|
if (!value || value.trim() === '')
|
|
@@ -62,6 +67,17 @@ function normalizeBuilderChoice(value) {
|
|
|
62
67
|
return normalized;
|
|
63
68
|
throw new Error(`Invalid builder: ${value}. Must be 'cursor' or 'claude_code'.`);
|
|
64
69
|
}
|
|
70
|
+
function normalizePlannerProvider(value) {
|
|
71
|
+
if (!value || value.trim() === '')
|
|
72
|
+
return undefined;
|
|
73
|
+
const normalized = value.trim().toLowerCase();
|
|
74
|
+
if (normalized === 'claude_code' || normalized === 'chatgpt')
|
|
75
|
+
return normalized;
|
|
76
|
+
throw new Error(`Invalid planner provider: ${value}. Must be 'claude_code' or 'chatgpt'.`);
|
|
77
|
+
}
|
|
78
|
+
function resolvePlannerModel(provider) {
|
|
79
|
+
return provider === 'chatgpt' ? CHATGPT_PLANNER_MODEL : CLAUDE_PLANNER_MODEL;
|
|
80
|
+
}
|
|
65
81
|
function asNonEmptyString(value) {
|
|
66
82
|
return typeof value === 'string' && value.trim() !== '' ? value.trim() : undefined;
|
|
67
83
|
}
|
|
@@ -191,17 +207,18 @@ export function buildNonInteractiveQuestions(input) {
|
|
|
191
207
|
choices: BUILDER_CHOICES.map((choice) => ({ value: choice.value, label: choice.label, desc: choice.desc })),
|
|
192
208
|
});
|
|
193
209
|
}
|
|
194
|
-
if (!
|
|
210
|
+
if (!input.plannerProvider) {
|
|
195
211
|
questions.push({
|
|
196
|
-
id: '
|
|
197
|
-
label: 'Planner
|
|
212
|
+
id: 'planner_provider',
|
|
213
|
+
label: 'Planner provider',
|
|
198
214
|
type: 'select',
|
|
199
215
|
required: true,
|
|
200
|
-
default: input.defaults.
|
|
201
|
-
choices:
|
|
216
|
+
default: input.defaults.plannerProvider,
|
|
217
|
+
choices: PLANNER_PROVIDER_CHOICES.map((choice) => ({ value: choice.value, label: choice.label, desc: choice.desc })),
|
|
202
218
|
});
|
|
203
219
|
}
|
|
204
|
-
|
|
220
|
+
const effectiveBuilder = input.builder ?? input.defaults.builder;
|
|
221
|
+
if (effectiveBuilder === 'claude_code' && !asNonEmptyString(input.builderModel)) {
|
|
205
222
|
questions.push({
|
|
206
223
|
id: 'builder_model',
|
|
207
224
|
label: 'Builder model',
|
|
@@ -219,13 +236,12 @@ export function buildNonInteractiveQuestions(input) {
|
|
|
219
236
|
required: true,
|
|
220
237
|
default: input.defaults.reviewer,
|
|
221
238
|
choices: [
|
|
222
|
-
{ value: 'none', label: 'none', desc: 'Less friction, fastest onboarding.' },
|
|
223
239
|
{ value: 'codex', label: 'codex', desc: 'Adds second-pass checks for risky changes.' },
|
|
240
|
+
{ value: 'none', label: 'none', desc: 'Skip reviewer for less friction.' },
|
|
224
241
|
],
|
|
225
242
|
});
|
|
226
243
|
}
|
|
227
|
-
|
|
228
|
-
if (effectiveReviewer === 'codex' && !asNonEmptyString(input.reviewerModel)) {
|
|
244
|
+
if (input.reviewer === 'codex' && !asNonEmptyString(input.reviewerModel)) {
|
|
229
245
|
questions.push({
|
|
230
246
|
id: 'reviewer_model',
|
|
231
247
|
label: 'Reviewer model',
|
|
@@ -255,7 +271,7 @@ function buildQuestionPayload(sessionId, questions, workspaceDir, exitCode) {
|
|
|
255
271
|
next_action: {
|
|
256
272
|
command: `${CLI_NAME} start --answers-file ${workspaceDir}/onboarding.answers.json`,
|
|
257
273
|
examples: [
|
|
258
|
-
`${CLI_NAME} start --answers-json '{"mode":"milestone","builder":"cursor","
|
|
274
|
+
`${CLI_NAME} start --answers-json '{"mode":"milestone","builder":"cursor","planner_provider":"claude_code","reviewer":"codex","prd_text":"..."}'`,
|
|
259
275
|
`npx -y ${PACKAGE_NAME}@latest start --answers-file ${workspaceDir}/onboarding.answers.json`,
|
|
260
276
|
],
|
|
261
277
|
accepted_inputs: ['--answers-json <json-object>', '--answers-file <path-to-json>'],
|
|
@@ -413,6 +429,11 @@ function clearInlineField(state, field) {
|
|
|
413
429
|
return;
|
|
414
430
|
case 'builder':
|
|
415
431
|
state.builder = undefined;
|
|
432
|
+
state.builderModel = undefined;
|
|
433
|
+
return;
|
|
434
|
+
case 'planner_provider':
|
|
435
|
+
state.plannerProvider = undefined;
|
|
436
|
+
state.orchestratorModel = undefined;
|
|
416
437
|
return;
|
|
417
438
|
case 'orchestrator_model':
|
|
418
439
|
state.orchestratorModel = undefined;
|
|
@@ -441,6 +462,15 @@ function applyQuestionValue(state, questionId, value) {
|
|
|
441
462
|
return;
|
|
442
463
|
case 'builder':
|
|
443
464
|
state.builder = normalizeBuilderChoice(value);
|
|
465
|
+
if (state.builder !== 'claude_code') {
|
|
466
|
+
state.builderModel = undefined;
|
|
467
|
+
}
|
|
468
|
+
return;
|
|
469
|
+
case 'planner_provider':
|
|
470
|
+
state.plannerProvider = normalizePlannerProvider(value);
|
|
471
|
+
if (state.plannerProvider) {
|
|
472
|
+
state.orchestratorModel = resolvePlannerModel(state.plannerProvider);
|
|
473
|
+
}
|
|
444
474
|
return;
|
|
445
475
|
case 'orchestrator_model':
|
|
446
476
|
state.orchestratorModel = value;
|
|
@@ -472,6 +502,7 @@ export function buildReviewPayload(sessionId, state) {
|
|
|
472
502
|
summary: {
|
|
473
503
|
mode: state.mode,
|
|
474
504
|
builder: state.builder,
|
|
505
|
+
planner_provider: state.plannerProvider,
|
|
475
506
|
orchestrator_model: state.orchestratorModel,
|
|
476
507
|
builder_model: state.builderModel,
|
|
477
508
|
reviewer: state.reviewer,
|
|
@@ -533,7 +564,7 @@ async function promptInlineReview(sessionId, state, reader, timeoutMs) {
|
|
|
533
564
|
// eslint-disable-next-line no-constant-condition
|
|
534
565
|
while (true) {
|
|
535
566
|
const payload = buildReviewPayload(sessionId, state);
|
|
536
|
-
console.log('Onboarding review: confirm choices or edit one field (mode, builder,
|
|
567
|
+
console.log('Onboarding review: confirm choices or edit one field (mode, builder, planner_provider, builder_model, reviewer, reviewer_model, prd_text).');
|
|
537
568
|
console.log(JSON.stringify(payload, null, 2));
|
|
538
569
|
const response = await reader.nextLine(timeoutMs);
|
|
539
570
|
if (response.status !== 'line')
|
|
@@ -553,6 +584,7 @@ async function promptInlineReview(sessionId, state, reader, timeoutMs) {
|
|
|
553
584
|
const field = typeof parsed.field === 'string' ? parsed.field.trim() : '';
|
|
554
585
|
if (field === 'mode' ||
|
|
555
586
|
field === 'builder' ||
|
|
587
|
+
field === 'planner_provider' ||
|
|
556
588
|
field === 'orchestrator_model' ||
|
|
557
589
|
field === 'builder_model' ||
|
|
558
590
|
field === 'reviewer' ||
|
|
@@ -585,6 +617,7 @@ async function runInlineNonInteractiveWizard(params) {
|
|
|
585
617
|
const questions = buildNonInteractiveQuestions({
|
|
586
618
|
mode: state.mode,
|
|
587
619
|
builder: state.builder,
|
|
620
|
+
plannerProvider: state.plannerProvider,
|
|
588
621
|
orchestratorModel: state.orchestratorModel,
|
|
589
622
|
builderModel: state.builderModel,
|
|
590
623
|
reviewer: state.reviewer,
|
|
@@ -598,6 +631,7 @@ async function runInlineNonInteractiveWizard(params) {
|
|
|
598
631
|
const unresolved = buildNonInteractiveQuestions({
|
|
599
632
|
mode: state.mode,
|
|
600
633
|
builder: state.builder,
|
|
634
|
+
plannerProvider: state.plannerProvider,
|
|
601
635
|
orchestratorModel: state.orchestratorModel,
|
|
602
636
|
builderModel: state.builderModel,
|
|
603
637
|
reviewer: state.reviewer,
|
|
@@ -619,6 +653,7 @@ async function runInlineNonInteractiveWizard(params) {
|
|
|
619
653
|
const unresolved = buildNonInteractiveQuestions({
|
|
620
654
|
mode: state.mode,
|
|
621
655
|
builder: state.builder,
|
|
656
|
+
plannerProvider: state.plannerProvider,
|
|
622
657
|
orchestratorModel: state.orchestratorModel,
|
|
623
658
|
builderModel: state.builderModel,
|
|
624
659
|
reviewer: state.reviewer,
|
|
@@ -651,6 +686,9 @@ async function runInlineNonInteractiveWizard(params) {
|
|
|
651
686
|
if (reviewResult.field === 'reviewer') {
|
|
652
687
|
delete mergedAnswerBag.reviewer_model;
|
|
653
688
|
}
|
|
689
|
+
if (reviewResult.field === 'planner_provider') {
|
|
690
|
+
delete mergedAnswerBag.orchestrator_model;
|
|
691
|
+
}
|
|
654
692
|
}
|
|
655
693
|
}
|
|
656
694
|
finally {
|
|
@@ -710,9 +748,18 @@ function buildReviewQuestion(state) {
|
|
|
710
748
|
return undefined;
|
|
711
749
|
}
|
|
712
750
|
})();
|
|
751
|
+
const safePlannerProvider = (() => {
|
|
752
|
+
try {
|
|
753
|
+
return normalizePlannerProvider(state.plannerProvider);
|
|
754
|
+
}
|
|
755
|
+
catch {
|
|
756
|
+
return undefined;
|
|
757
|
+
}
|
|
758
|
+
})();
|
|
713
759
|
const payload = buildReviewPayload('inline-review', {
|
|
714
760
|
mode: safeMode,
|
|
715
761
|
builder: safeBuilder,
|
|
762
|
+
plannerProvider: safePlannerProvider,
|
|
716
763
|
orchestratorModel: state.orchestratorModel,
|
|
717
764
|
builderModel: state.builderModel,
|
|
718
765
|
reviewer: safeReviewer,
|
|
@@ -723,6 +770,7 @@ function buildReviewQuestion(state) {
|
|
|
723
770
|
const reviewHint = [
|
|
724
771
|
`mode=${summary.mode ?? 'n/a'}`,
|
|
725
772
|
`builder=${summary.builder ?? 'n/a'}`,
|
|
773
|
+
`planner_provider=${summary.planner_provider ?? 'n/a'}`,
|
|
726
774
|
`orchestrator_model=${summary.orchestrator_model ?? 'n/a'}`,
|
|
727
775
|
`builder_model=${summary.builder_model ?? 'n/a'}`,
|
|
728
776
|
`reviewer=${summary.reviewer ?? 'n/a'}`,
|
|
@@ -734,14 +782,20 @@ function buildReviewQuestion(state) {
|
|
|
734
782
|
label: 'Review onboarding choices',
|
|
735
783
|
type: 'text',
|
|
736
784
|
required: true,
|
|
737
|
-
help: `${reviewHint}\nReply with 'confirm', 'confirm_and_start', or 'edit:<field>' (field: mode|builder|
|
|
785
|
+
help: `${reviewHint}\nReply with 'confirm', 'confirm_and_start', or 'edit:<field>' (field: mode|builder|planner_provider|builder_model|reviewer|reviewer_model|prd_text).`,
|
|
738
786
|
};
|
|
739
787
|
}
|
|
740
788
|
function clearAnswerByField(answers, field) {
|
|
741
789
|
if (field === 'mode')
|
|
742
790
|
delete answers.mode;
|
|
743
|
-
if (field === 'builder')
|
|
791
|
+
if (field === 'builder') {
|
|
744
792
|
delete answers.builder;
|
|
793
|
+
delete answers.builder_model;
|
|
794
|
+
}
|
|
795
|
+
if (field === 'planner_provider') {
|
|
796
|
+
delete answers.planner_provider;
|
|
797
|
+
delete answers.orchestrator_model;
|
|
798
|
+
}
|
|
745
799
|
if (field === 'orchestrator_model')
|
|
746
800
|
delete answers.orchestrator_model;
|
|
747
801
|
if (field === 'builder_model')
|
|
@@ -818,6 +872,7 @@ function parseReviewAction(value) {
|
|
|
818
872
|
const field = normalized.slice('edit:'.length).trim();
|
|
819
873
|
if (field === 'mode' ||
|
|
820
874
|
field === 'builder' ||
|
|
875
|
+
field === 'planner_provider' ||
|
|
821
876
|
field === 'orchestrator_model' ||
|
|
822
877
|
field === 'builder_model' ||
|
|
823
878
|
field === 'reviewer' ||
|
|
@@ -902,6 +957,16 @@ async function ensureRepoRootCwd() {
|
|
|
902
957
|
process.chdir(top);
|
|
903
958
|
}
|
|
904
959
|
}
|
|
960
|
+
async function ensureGitRepoInitialized() {
|
|
961
|
+
if (isGitRepo())
|
|
962
|
+
return;
|
|
963
|
+
const result = spawnSync('git', ['init'], { stdio: 'pipe' });
|
|
964
|
+
if (result.status !== 0) {
|
|
965
|
+
const error = result.stderr?.toString('utf-8').trim() || 'git init failed';
|
|
966
|
+
throw new Error(`Unable to initialize git repository: ${error}`);
|
|
967
|
+
}
|
|
968
|
+
console.log('Initialized git repository for Envoi onboarding.');
|
|
969
|
+
}
|
|
905
970
|
async function resolveConfigPath(configPath) {
|
|
906
971
|
if (configPath)
|
|
907
972
|
return resolve(configPath);
|
|
@@ -1320,12 +1385,14 @@ function printRoadmapPreview(roadmap) {
|
|
|
1320
1385
|
}
|
|
1321
1386
|
async function runConnectivityChecks(config) {
|
|
1322
1387
|
console.log('\nRole connectivity checks:');
|
|
1323
|
-
const
|
|
1388
|
+
const plannerProvider = (config.models.orchestrator_provider === 'chatgpt' ? 'chatgpt' : 'claude_code');
|
|
1389
|
+
const plannerCommand = plannerProvider === 'chatgpt' ? 'codex' : config.claude_code_cli.command;
|
|
1390
|
+
const plannerCli = checkCommandVersion(plannerCommand);
|
|
1324
1391
|
if (plannerCli.available) {
|
|
1325
|
-
console.log(`- planner: CLEARED (${
|
|
1392
|
+
console.log(`- planner: CLEARED (${plannerCommand}${plannerCli.version ? ` ${plannerCli.version}` : ''})`);
|
|
1326
1393
|
}
|
|
1327
1394
|
else {
|
|
1328
|
-
console.log(`- planner: BLOCKED (${
|
|
1395
|
+
console.log(`- planner: BLOCKED (${plannerCommand} not available)`);
|
|
1329
1396
|
}
|
|
1330
1397
|
if (config.builder.default_mode === 'cursor') {
|
|
1331
1398
|
const cursor = await checkCursorAgent(config);
|
|
@@ -1398,17 +1465,27 @@ async function maybeStartRun(config, mode, autoRun) {
|
|
|
1398
1465
|
}
|
|
1399
1466
|
export async function onboardCommand(options) {
|
|
1400
1467
|
await ensureRepoRootCwd();
|
|
1468
|
+
await ensureGitRepoInitialized();
|
|
1401
1469
|
await ensureInitialized(Boolean(options.forceInit));
|
|
1402
1470
|
const configPath = await resolveConfigPath(options.configPath);
|
|
1403
1471
|
const config = await loadConfig(configPath);
|
|
1404
1472
|
const raw = await atomicReadJson(configPath);
|
|
1405
1473
|
const nonInteractive = !input.isTTY;
|
|
1474
|
+
const inferredProviderFromModel = (model) => {
|
|
1475
|
+
if (!model)
|
|
1476
|
+
return undefined;
|
|
1477
|
+
return model.toLowerCase().startsWith('gpt-') ? 'chatgpt' : 'claude_code';
|
|
1478
|
+
};
|
|
1479
|
+
const existingProvider = normalizePlannerProvider(raw.models.orchestrator_provider)
|
|
1480
|
+
?? inferredProviderFromModel(raw.models.orchestrator_model)
|
|
1481
|
+
?? 'claude_code';
|
|
1406
1482
|
const defaults = {
|
|
1407
|
-
mode:
|
|
1408
|
-
builder:
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1483
|
+
mode: 'milestone',
|
|
1484
|
+
builder: 'cursor',
|
|
1485
|
+
plannerProvider: existingProvider,
|
|
1486
|
+
orchestratorModel: resolvePlannerModel(existingProvider),
|
|
1487
|
+
builderModel: 'sonnet',
|
|
1488
|
+
reviewer: 'codex',
|
|
1412
1489
|
reviewerModel: raw.reviewer?.model || 'gpt-5',
|
|
1413
1490
|
};
|
|
1414
1491
|
const incomingAnswers = await loadOnboardingAnswers({
|
|
@@ -1423,21 +1500,57 @@ export async function onboardCommand(options) {
|
|
|
1423
1500
|
});
|
|
1424
1501
|
const sessionPath = onboardingSessionPath(config.workspace_dir);
|
|
1425
1502
|
const existingSession = nonInteractive ? await loadOnboardingSession(sessionPath) : null;
|
|
1503
|
+
const incomingSessionId = asNonEmptyString(incomingAnswers.__session_id);
|
|
1504
|
+
if (nonInteractive &&
|
|
1505
|
+
existingSession?.status === 'waiting_input' &&
|
|
1506
|
+
incomingSessionId &&
|
|
1507
|
+
incomingSessionId !== existingSession.session_id) {
|
|
1508
|
+
throw new Error(`Onboarding session mismatch: expected ${existingSession.session_id}, got ${incomingSessionId}. Resume with the expected session_id or restart with --reconfigure.`);
|
|
1509
|
+
}
|
|
1426
1510
|
const persistedAnswers = nonInteractive && existingSession?.status === 'waiting_input'
|
|
1427
1511
|
? existingSession.answers
|
|
1428
1512
|
: {};
|
|
1429
1513
|
const answerBag = { ...persistedAnswers, ...incomingAnswers };
|
|
1514
|
+
const hasExplicitOnboardingInput = Boolean(options.answersJson ||
|
|
1515
|
+
options.answersFile ||
|
|
1516
|
+
options.prdFile ||
|
|
1517
|
+
options.mode ||
|
|
1518
|
+
options.builder ||
|
|
1519
|
+
options.plannerProvider ||
|
|
1520
|
+
options.reviewer ||
|
|
1521
|
+
options.orchestratorModel ||
|
|
1522
|
+
options.builderModel ||
|
|
1523
|
+
options.reviewerModel);
|
|
1524
|
+
if (nonInteractive &&
|
|
1525
|
+
!options.reconfigure &&
|
|
1526
|
+
existingSession?.status === 'completed' &&
|
|
1527
|
+
!hasExplicitOnboardingInput) {
|
|
1528
|
+
console.log(`${PRODUCT_NAME} onboarding already complete.`);
|
|
1529
|
+
const mode = raw.runner.default_loop_mode ?? defaults.mode;
|
|
1530
|
+
const autoRunRequested = options.autoRun ?? false;
|
|
1531
|
+
await maybeStartRun(raw, mode, autoRunRequested);
|
|
1532
|
+
return { needsInput: false };
|
|
1533
|
+
}
|
|
1430
1534
|
const sessionId = asNonEmptyString(answerBag.__session_id) ??
|
|
1431
1535
|
existingSession?.session_id ??
|
|
1432
1536
|
randomUUID();
|
|
1433
1537
|
let modeSeed = normalizeLoopMode(options.mode ?? asNonEmptyString(answerBag.mode));
|
|
1434
1538
|
let builderSeed = normalizeBuilderChoice(options.builder ?? asNonEmptyString(answerBag.builder));
|
|
1539
|
+
let plannerProviderSeed = normalizePlannerProvider(options.plannerProvider ??
|
|
1540
|
+
asNonEmptyString(answerBag.planner_provider) ??
|
|
1541
|
+
inferredProviderFromModel(asNonEmptyString(answerBag.orchestrator_model)));
|
|
1435
1542
|
let reviewerSeed = parseOptionalReviewerChoice(options.reviewer ?? asNonEmptyString(answerBag.reviewer));
|
|
1436
1543
|
let orchestratorModelSeed = asNonEmptyString(options.orchestratorModel) ?? asNonEmptyString(answerBag.orchestrator_model);
|
|
1437
1544
|
let builderModelSeed = asNonEmptyString(options.builderModel) ?? asNonEmptyString(answerBag.builder_model);
|
|
1438
1545
|
let reviewerModelSeed = asNonEmptyString(options.reviewerModel) ?? asNonEmptyString(answerBag.reviewer_model);
|
|
1439
1546
|
let prdTextSeed = asNonEmptyString(answerBag.prd_text);
|
|
1440
1547
|
let nonInteractiveStartNow = false;
|
|
1548
|
+
if (!orchestratorModelSeed && plannerProviderSeed) {
|
|
1549
|
+
orchestratorModelSeed = resolvePlannerModel(plannerProviderSeed);
|
|
1550
|
+
}
|
|
1551
|
+
if (!plannerProviderSeed && orchestratorModelSeed) {
|
|
1552
|
+
plannerProviderSeed = inferredProviderFromModel(orchestratorModelSeed);
|
|
1553
|
+
}
|
|
1441
1554
|
const intent = await promptStartIntentIfNeeded(Boolean(options.showTourPrompt));
|
|
1442
1555
|
if (intent === 'tour') {
|
|
1443
1556
|
printTour();
|
|
@@ -1453,6 +1566,7 @@ export async function onboardCommand(options) {
|
|
|
1453
1566
|
const missingNonPrdQuestions = buildNonInteractiveQuestions({
|
|
1454
1567
|
mode: modeSeed,
|
|
1455
1568
|
builder: builderSeed,
|
|
1569
|
+
plannerProvider: plannerProviderSeed,
|
|
1456
1570
|
orchestratorModel: orchestratorModelSeed,
|
|
1457
1571
|
builderModel: builderModelSeed,
|
|
1458
1572
|
reviewer: reviewerSeed,
|
|
@@ -1470,6 +1584,7 @@ export async function onboardCommand(options) {
|
|
|
1470
1584
|
let pendingQuestions = buildNonInteractiveQuestions({
|
|
1471
1585
|
mode: modeSeed,
|
|
1472
1586
|
builder: builderSeed,
|
|
1587
|
+
plannerProvider: plannerProviderSeed,
|
|
1473
1588
|
orchestratorModel: orchestratorModelSeed,
|
|
1474
1589
|
builderModel: builderModelSeed,
|
|
1475
1590
|
reviewer: reviewerSeed,
|
|
@@ -1494,6 +1609,7 @@ export async function onboardCommand(options) {
|
|
|
1494
1609
|
const reviewQuestion = buildReviewQuestion({
|
|
1495
1610
|
mode: modeSeed,
|
|
1496
1611
|
builder: builderSeed,
|
|
1612
|
+
plannerProvider: plannerProviderSeed,
|
|
1497
1613
|
orchestratorModel: orchestratorModelSeed,
|
|
1498
1614
|
builderModel: builderModelSeed,
|
|
1499
1615
|
reviewer: reviewerSeed,
|
|
@@ -1516,11 +1632,20 @@ export async function onboardCommand(options) {
|
|
|
1516
1632
|
delete answerBag.__review_action;
|
|
1517
1633
|
modeSeed = normalizeLoopMode(options.mode ?? asNonEmptyString(answerBag.mode));
|
|
1518
1634
|
builderSeed = normalizeBuilderChoice(options.builder ?? asNonEmptyString(answerBag.builder));
|
|
1635
|
+
plannerProviderSeed = normalizePlannerProvider(options.plannerProvider ??
|
|
1636
|
+
asNonEmptyString(answerBag.planner_provider) ??
|
|
1637
|
+
inferredProviderFromModel(asNonEmptyString(answerBag.orchestrator_model)));
|
|
1519
1638
|
reviewerSeed = parseOptionalReviewerChoice(options.reviewer ?? asNonEmptyString(answerBag.reviewer));
|
|
1520
1639
|
orchestratorModelSeed = asNonEmptyString(options.orchestratorModel) ?? asNonEmptyString(answerBag.orchestrator_model);
|
|
1521
1640
|
builderModelSeed = asNonEmptyString(options.builderModel) ?? asNonEmptyString(answerBag.builder_model);
|
|
1522
1641
|
reviewerModelSeed = asNonEmptyString(options.reviewerModel) ?? asNonEmptyString(answerBag.reviewer_model);
|
|
1523
1642
|
prdTextSeed = asNonEmptyString(answerBag.prd_text);
|
|
1643
|
+
if (!orchestratorModelSeed && plannerProviderSeed) {
|
|
1644
|
+
orchestratorModelSeed = resolvePlannerModel(plannerProviderSeed);
|
|
1645
|
+
}
|
|
1646
|
+
if (!plannerProviderSeed && orchestratorModelSeed) {
|
|
1647
|
+
plannerProviderSeed = inferredProviderFromModel(orchestratorModelSeed);
|
|
1648
|
+
}
|
|
1524
1649
|
prdCapture = {
|
|
1525
1650
|
text: prdTextSeed ?? '',
|
|
1526
1651
|
source: isNonEmptyContent(prdTextSeed ?? '') ? 'paste' : 'skip',
|
|
@@ -1528,6 +1653,7 @@ export async function onboardCommand(options) {
|
|
|
1528
1653
|
pendingQuestions = buildNonInteractiveQuestions({
|
|
1529
1654
|
mode: modeSeed,
|
|
1530
1655
|
builder: builderSeed,
|
|
1656
|
+
plannerProvider: plannerProviderSeed,
|
|
1531
1657
|
orchestratorModel: orchestratorModelSeed,
|
|
1532
1658
|
builderModel: builderModelSeed,
|
|
1533
1659
|
reviewer: reviewerSeed,
|
|
@@ -1550,6 +1676,7 @@ export async function onboardCommand(options) {
|
|
|
1550
1676
|
const reviewQuestion = buildReviewQuestion({
|
|
1551
1677
|
mode: modeSeed,
|
|
1552
1678
|
builder: builderSeed,
|
|
1679
|
+
plannerProvider: plannerProviderSeed,
|
|
1553
1680
|
orchestratorModel: orchestratorModelSeed,
|
|
1554
1681
|
builderModel: builderModelSeed,
|
|
1555
1682
|
reviewer: reviewerSeed,
|
|
@@ -1586,29 +1713,28 @@ export async function onboardCommand(options) {
|
|
|
1586
1713
|
(input.isTTY
|
|
1587
1714
|
? (await promptChoice('Which builder should implement code changes?', BUILDER_CHOICES, defaults.builder))
|
|
1588
1715
|
: defaults.builder);
|
|
1589
|
-
// 4)
|
|
1590
|
-
const
|
|
1716
|
+
// 4) Planner provider and model defaults
|
|
1717
|
+
const plannerProvider = plannerProviderSeed ??
|
|
1591
1718
|
(input.isTTY
|
|
1592
|
-
? await promptChoice('Planner
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
const builderModel = builderModelSeed ??
|
|
1599
|
-
(builderChoice === 'claude_code'
|
|
1600
|
-
? (input.isTTY
|
|
1719
|
+
? (await promptChoice('Planner provider?', PLANNER_PROVIDER_CHOICES, defaults.plannerProvider))
|
|
1720
|
+
: defaults.plannerProvider);
|
|
1721
|
+
const orchestratorModel = resolvePlannerModel(plannerProvider);
|
|
1722
|
+
const builderModel = builderChoice === 'claude_code'
|
|
1723
|
+
? (builderModelSeed ??
|
|
1724
|
+
(input.isTTY
|
|
1601
1725
|
? await promptChoice('Builder model?', [
|
|
1602
1726
|
{ value: 'sonnet', label: 'sonnet (recommended)', desc: 'Strong coding throughput.' },
|
|
1603
|
-
{ value: 'opus', label: 'opus', desc: 'Higher quality, higher cost.' },
|
|
1604
1727
|
{ value: 'haiku', label: 'haiku', desc: 'Lower cost for simpler edits.' },
|
|
1605
1728
|
], defaults.builderModel)
|
|
1606
|
-
: defaults.builderModel)
|
|
1607
|
-
|
|
1729
|
+
: defaults.builderModel))
|
|
1730
|
+
: defaults.builderModel;
|
|
1608
1731
|
raw.models = {
|
|
1609
1732
|
...raw.models,
|
|
1733
|
+
orchestrator_provider: plannerProvider,
|
|
1610
1734
|
orchestrator_model: orchestratorModel,
|
|
1611
|
-
orchestrator_fallback_model:
|
|
1735
|
+
orchestrator_fallback_model: plannerProvider === 'chatgpt'
|
|
1736
|
+
? (raw.models.orchestrator_fallback_model || CHATGPT_PLANNER_MODEL)
|
|
1737
|
+
: (raw.models.orchestrator_fallback_model || 'sonnet'),
|
|
1612
1738
|
builder_model: builderModel,
|
|
1613
1739
|
builder_fallback_model: raw.models.builder_fallback_model || 'haiku',
|
|
1614
1740
|
};
|
|
@@ -1635,8 +1761,8 @@ export async function onboardCommand(options) {
|
|
|
1635
1761
|
? reviewerSeed
|
|
1636
1762
|
: (input.isTTY
|
|
1637
1763
|
? normalizeReviewerChoice(await promptChoice('Optional reviewer?', [
|
|
1638
|
-
{ value: '
|
|
1639
|
-
{ value: '
|
|
1764
|
+
{ value: 'codex', label: 'Use Codex reviewer (recommended)', desc: 'Adds second-pass checks for risky changes.' },
|
|
1765
|
+
{ value: 'none', label: 'No reviewer', desc: 'Less friction, fastest onboarding.' },
|
|
1640
1766
|
], defaults.reviewer))
|
|
1641
1767
|
: defaults.reviewer);
|
|
1642
1768
|
if (reviewerChoice === 'codex') {
|
|
@@ -1684,6 +1810,7 @@ export async function onboardCommand(options) {
|
|
|
1684
1810
|
console.log(`- PRD source: ${prdCapture.source}`);
|
|
1685
1811
|
console.log(`- Default mode: ${mode}`);
|
|
1686
1812
|
console.log(`- Builder: ${builderChoice}`);
|
|
1813
|
+
console.log(`- Planner: ${plannerProvider} (${orchestratorModel})`);
|
|
1687
1814
|
console.log(`- Reviewer: ${raw.reviewer?.enabled ? 'enabled' : 'disabled'}`);
|
|
1688
1815
|
await runConnectivityChecks(raw);
|
|
1689
1816
|
const roadmapPath = join(dirname(configPath), 'ROADMAP.json');
|