-
Notifications
You must be signed in to change notification settings - Fork 228
Expand file tree
/
Copy pathpreflight.ts
More file actions
150 lines (129 loc) · 5.63 KB
/
preflight.ts
File metadata and controls
150 lines (129 loc) · 5.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/**
* Workflow Pre-flight Checks
*
* Consolidates all checks that must pass before a workflow can start.
* Single source of truth for workflow startup validation.
*/
import * as path from 'node:path';
import type { WorkflowTemplate } from './templates/types.js';
import { loadTemplateWithPath } from './templates/loader.js';
import { getTemplatePathFromTracking, getSelectedTrack, hasSelectedConditions, getProjectName } from '../shared/workflows/index.js';
import { validateSpecification } from '../runtime/services/index.js';
import { ensureWorkspaceStructure } from '../runtime/services/workspace/index.js';
import type { AgentDefinition } from '../shared/agents/config/types.js';
import { registerImportedAgents, clearImportedAgents } from './utils/config.js';
import { getAllInstalledImports } from '../shared/imports/index.js';
export { ValidationError } from '../runtime/services/index.js';
/**
* Ensure imported agents are registered before loading templates
* This must be called before any loadTemplateWithPath() call to ensure
* resolveStep() can find agents from imported packages
*/
function ensureImportedAgentsRegistered(): void {
clearImportedAgents();
const importedPackages = getAllInstalledImports();
for (const imp of importedPackages) {
registerImportedAgents(imp.resolvedPaths.config);
}
}
/**
* Onboarding requirements - what the user needs to configure before workflow can start
*/
export interface OnboardingNeeds {
needsProjectName: boolean;
needsTrackSelection: boolean;
needsConditionsSelection: boolean;
needsControllerSelection: boolean;
/** @deprecated Controller is now pre-specified via controller() function */
controllerAgents: AgentDefinition[];
/** The loaded template for reference */
template: WorkflowTemplate;
}
/**
* Check what onboarding steps are needed before workflow can start
* Does NOT throw - returns the requirements for the UI to handle
*/
export async function checkOnboardingRequired(options: { cwd?: string } = {}): Promise<OnboardingNeeds> {
const cwd = options.cwd ? path.resolve(options.cwd) : process.cwd();
const cmRoot = path.join(cwd, '.codemachine');
// Ensure workspace structure exists
await ensureWorkspaceStructure({ cwd });
// Ensure imported agents are registered before loading template
// This allows resolveStep() to find agents from imported packages
ensureImportedAgentsRegistered();
// Load template
const templatePath = await getTemplatePathFromTracking(cmRoot);
const { template } = await loadTemplateWithPath(cwd, templatePath);
// Check existing selections
const selectedTrack = await getSelectedTrack(cmRoot);
const conditionsSelected = await hasSelectedConditions(cmRoot);
const existingProjectName = await getProjectName(cmRoot);
// Determine what's needed
const hasTracks = !!(template.tracks && Object.keys(template.tracks.options).length > 0);
const hasConditionGroups = !!(template.conditionGroups && template.conditionGroups.length > 0);
const needsTrackSelection = hasTracks && !selectedTrack;
const needsConditionsSelection = hasConditionGroups && !conditionsSelected;
// TODO: Re-enable project name check - temporarily disabled due to persistence bug
const needsProjectName = false; // !existingProjectName;
// Controller is now pre-specified via controller() function - no selection needed
const needsControllerSelection = false;
const controllerAgents: AgentDefinition[] = [];
return {
needsProjectName,
needsTrackSelection,
needsConditionsSelection,
needsControllerSelection,
controllerAgents,
template,
};
}
/**
* Check if specification file is required and valid
* Throws ValidationError if template requires specification but it's missing/empty
*
* Path can be overridden via:
* - CLI: --spec <path>
* - Env: CODEMACHINE_SPEC_PATH
*/
export async function checkSpecificationRequired(options: { cwd?: string } = {}): Promise<void> {
const cwd = options.cwd ? path.resolve(options.cwd) : process.cwd();
const cmRoot = path.join(cwd, '.codemachine');
const specificationPath = process.env.CODEMACHINE_SPEC_PATH
|| path.resolve(cwd, '.codemachine', 'inputs', 'specifications.md');
// Ensure workspace structure exists
await ensureWorkspaceStructure({ cwd });
// Ensure imported agents are registered before loading template
// This allows resolveStep() to find agents from imported packages
ensureImportedAgentsRegistered();
// Load template to check specification requirement
const templatePath = await getTemplatePathFromTracking(cmRoot);
const { template } = await loadTemplateWithPath(cwd, templatePath);
// Validate specification only if template requires it
if (template.specification === true) {
await validateSpecification(specificationPath);
}
}
/**
* Main pre-flight check - verifies workflow can start
* Throws ValidationError if workflow cannot start due to missing specification
* Returns onboarding needs if user configuration is required
*/
export async function checkWorkflowCanStart(options: { cwd?: string } = {}): Promise<OnboardingNeeds> {
const cwd = options.cwd ? path.resolve(options.cwd) : process.cwd();
// First check specification requirement (throws if invalid)
await checkSpecificationRequired({ cwd });
// Then check onboarding requirements (returns needs, doesn't throw)
return checkOnboardingRequired({ cwd });
}
/**
* Quick check if any onboarding is needed
* Useful for UI to decide whether to show onboarding flow
*/
export function needsOnboarding(needs: OnboardingNeeds): boolean {
return (
needs.needsProjectName ||
needs.needsTrackSelection ||
needs.needsConditionsSelection ||
needs.needsControllerSelection
);
}