im_wower
·
2026-03-21
index.ts
1export const TEMPLATE_TASK_TYPES = [
2 "feature_impl",
3 "bugfix",
4 "review_only",
5 "ops_change",
6 "infra_bootstrap"
7] as const;
8export type TaskTemplateType = (typeof TEMPLATE_TASK_TYPES)[number];
9
10export const TEMPLATE_STEP_KINDS = ["planner", "codex", "shell", "git", "review", "finalize"] as const;
11export type TemplateStepKind = (typeof TEMPLATE_STEP_KINDS)[number];
12
13export const TEMPLATE_PLANNING_STRATEGIES = [
14 "template_first",
15 "planner_assisted",
16 "manual"
17] as const;
18export type TemplatePlanningStrategy = (typeof TEMPLATE_PLANNING_STRATEGIES)[number];
19
20export const TEMPLATE_STEP_NAMES = [
21 "prepare_branch",
22 "inspect_context",
23 "implement",
24 "run_tests",
25 "review_fix",
26 "commit_push",
27 "finalize",
28 "reproduce",
29 "implement_fix",
30 "run_targeted_tests",
31 "prepare_context",
32 "analyze_diff",
33 "write_review",
34 "inspect_ops_context",
35 "edit_ops_files",
36 "validate_config",
37 "inspect_current_ops_state",
38 "edit_dns_or_nginx_docs",
39 "validate_nginx_config"
40] as const;
41export type TemplateStepName = (typeof TEMPLATE_STEP_NAMES)[number];
42
43export type TemplateValue =
44 | boolean
45 | number
46 | string
47 | null
48 | TemplateValue[]
49 | {
50 [key: string]: TemplateValue;
51 };
52
53export type TemplateInput = Record<string, TemplateValue>;
54
55export interface TemplateStep {
56 stepName: TemplateStepName;
57 stepKind: TemplateStepKind;
58 timeoutSec: number;
59 retryLimit: number;
60 input: TemplateInput;
61}
62
63export interface TaskTemplateDefinition {
64 taskType: TaskTemplateType;
65 summary: string;
66 defaultReasoning: string;
67 recommendedStrategies: readonly [
68 TemplatePlanningStrategy,
69 ...TemplatePlanningStrategy[]
70 ];
71 steps: readonly TemplateStep[];
72}
73
74export interface BuildTemplatePlanOptions {
75 strategy?: TemplatePlanningStrategy;
76 reasoning?: string;
77 riskFlags?: string[];
78}
79
80export interface TemplatePlan {
81 taskType: TaskTemplateType;
82 strategy: TemplatePlanningStrategy;
83 reasoning: string;
84 steps: TemplateStep[];
85 riskFlags: string[];
86}
87
88const STEP_TEMPLATES = {
89 feature_impl: {
90 taskType: "feature_impl",
91 summary: "Default feature-delivery flow with explicit context gathering, implementation, validation, review, and handoff.",
92 defaultReasoning:
93 "Use the feature implementation template so discovery, coding, validation, and review remain explicit recovery boundaries.",
94 recommendedStrategies: ["template_first", "planner_assisted"],
95 steps: [
96 createStep("prepare_branch", "git", 120, 0, {
97 action: "checkout_task_branch",
98 branch_name: "{task.branch_name}",
99 base_ref: "{task.base_ref}"
100 }),
101 createStep("inspect_context", "codex", 600, 1, {
102 goal: "Read the repository and summarize the files, modules, and tests relevant to the requested feature."
103 }),
104 createStep("implement", "codex", 1800, 1, {
105 goal: "Implement the requested feature within the approved write scope and update nearby tests or fixtures when needed."
106 }),
107 createStep("run_tests", "shell", 1200, 1, {
108 command: "pnpm test",
109 allow_override: true
110 }),
111 createStep("review_fix", "review", 900, 1, {
112 goal: "Review the diff for regressions and apply any follow-up fixes before final handoff."
113 }),
114 createStep("commit_push", "git", 300, 0, {
115 action: "commit_and_push_task_branch",
116 branch_name: "{task.branch_name}"
117 }),
118 createStep("finalize", "finalize", 120, 0, {
119 emit_task_summary: true
120 })
121 ]
122 },
123 bugfix: {
124 taskType: "bugfix",
125 summary: "Bugfix flow with reproduction before the fix and targeted validation after the fix.",
126 defaultReasoning:
127 "Use the standard bugfix template first so reproduction, implementation, and targeted validation stay deterministic.",
128 recommendedStrategies: ["template_first", "planner_assisted"],
129 steps: [
130 createStep("prepare_branch", "git", 120, 0, {
131 action: "checkout_task_branch",
132 branch_name: "{task.branch_name}",
133 base_ref: "{task.base_ref}"
134 }),
135 createStep("reproduce", "shell", 600, 1, {
136 command: "{task.repro_command}",
137 allow_override: true
138 }),
139 createStep("implement_fix", "codex", 1200, 1, {
140 goal: "Implement the requested bug fix without changing unrelated behavior."
141 }),
142 createStep("run_targeted_tests", "shell", 900, 1, {
143 command: "{task.targeted_test_command}",
144 allow_override: true
145 }),
146 createStep("commit_push", "git", 300, 0, {
147 action: "commit_and_push_task_branch",
148 branch_name: "{task.branch_name}"
149 }),
150 createStep("finalize", "finalize", 120, 0, {
151 emit_task_summary: true
152 })
153 ]
154 },
155 review_only: {
156 taskType: "review_only",
157 summary: "Diff review flow for findings-only work with no implementation step.",
158 defaultReasoning:
159 "Use the review-only template because the task needs review output, not repository modifications.",
160 recommendedStrategies: ["template_first"],
161 steps: [
162 createStep("prepare_context", "git", 120, 0, {
163 action: "prepare_review_context",
164 base_ref: "{task.base_ref}"
165 }),
166 createStep("analyze_diff", "review", 900, 1, {
167 goal: "Inspect the diff and enumerate findings, risks, and missing tests."
168 }),
169 createStep("write_review", "review", 600, 0, {
170 goal: "Write the final structured review with file references and severity."
171 }),
172 createStep("finalize", "finalize", 120, 0, {
173 emit_task_summary: true
174 })
175 ]
176 },
177 ops_change: {
178 taskType: "ops_change",
179 summary: "Ops change flow with manual-first strategy and explicit config validation.",
180 defaultReasoning:
181 "Prefer a manual-first ops template because environment-specific configuration changes often need operator confirmation.",
182 recommendedStrategies: ["manual", "template_first"],
183 steps: [
184 createStep("prepare_branch", "git", 120, 0, {
185 action: "checkout_task_branch",
186 branch_name: "{task.branch_name}",
187 base_ref: "{task.base_ref}"
188 }),
189 createStep("inspect_ops_context", "codex", 600, 1, {
190 goal: "Read the current ops configuration and summarize the change surface before editing."
191 }),
192 createStep("edit_ops_files", "codex", 1200, 1, {
193 goal: "Apply the requested ops change to the approved config or docs files."
194 }),
195 createStep("validate_config", "shell", 900, 1, {
196 command: "{task.validation_command}",
197 allow_override: true
198 }),
199 createStep("commit_push", "git", 300, 0, {
200 action: "commit_and_push_task_branch",
201 branch_name: "{task.branch_name}"
202 }),
203 createStep("finalize", "finalize", 120, 0, {
204 emit_task_summary: true
205 })
206 ]
207 },
208 infra_bootstrap: {
209 taskType: "infra_bootstrap",
210 summary: "Infrastructure bootstrap flow for DNS, nginx, and related docs or host setup tasks.",
211 defaultReasoning:
212 "Use the bootstrap template so current state inspection, config edits, and nginx validation stay separated into recoverable steps.",
213 recommendedStrategies: ["template_first", "planner_assisted"],
214 steps: [
215 createStep("prepare_branch", "git", 120, 0, {
216 action: "checkout_task_branch",
217 branch_name: "{task.branch_name}",
218 base_ref: "{task.base_ref}"
219 }),
220 createStep("inspect_current_ops_state", "codex", 600, 1, {
221 goal: "Inspect the current DNS, nginx, or host bootstrap state and summarize what must change."
222 }),
223 createStep("edit_dns_or_nginx_docs", "codex", 1200, 1, {
224 goal: "Apply the bootstrap-related DNS, nginx, or runbook edits required by the task."
225 }),
226 createStep("validate_nginx_config", "shell", 900, 1, {
227 command: "nginx -t",
228 allow_override: true
229 }),
230 createStep("commit_push", "git", 300, 0, {
231 action: "commit_and_push_task_branch",
232 branch_name: "{task.branch_name}"
233 }),
234 createStep("finalize", "finalize", 120, 0, {
235 emit_task_summary: true
236 })
237 ]
238 }
239} as const satisfies Record<TaskTemplateType, TaskTemplateDefinition>;
240
241export const TASK_TEMPLATE_ORDER = [...TEMPLATE_TASK_TYPES];
242export const TASK_TEMPLATES = STEP_TEMPLATES;
243
244export function isTaskTemplateType(value: string): value is TaskTemplateType {
245 return (TEMPLATE_TASK_TYPES as readonly string[]).includes(value);
246}
247
248export function getTaskTemplate(taskType: string): TaskTemplateDefinition | undefined {
249 if (!isTaskTemplateType(taskType)) {
250 return undefined;
251 }
252
253 return STEP_TEMPLATES[taskType];
254}
255
256export function listTaskTemplates(): TaskTemplateDefinition[] {
257 return TASK_TEMPLATE_ORDER.map((taskType) => STEP_TEMPLATES[taskType]);
258}
259
260export function buildTemplatePlan(
261 taskType: TaskTemplateType,
262 options: BuildTemplatePlanOptions = {}
263): TemplatePlan {
264 const template = STEP_TEMPLATES[taskType];
265
266 return {
267 taskType,
268 strategy: options.strategy ?? template.recommendedStrategies[0],
269 reasoning: options.reasoning ?? template.defaultReasoning,
270 steps: template.steps.map((step) => cloneTemplateStep(step)),
271 riskFlags: options.riskFlags ? [...options.riskFlags] : []
272 };
273}
274
275function createStep(
276 stepName: TemplateStepName,
277 stepKind: TemplateStepKind,
278 timeoutSec: number,
279 retryLimit: number,
280 input: TemplateInput
281): TemplateStep {
282 return {
283 stepName,
284 stepKind,
285 timeoutSec,
286 retryLimit,
287 input
288 };
289}
290
291function cloneTemplateStep(step: TemplateStep): TemplateStep {
292 return {
293 stepName: step.stepName,
294 stepKind: step.stepKind,
295 timeoutSec: step.timeoutSec,
296 retryLimit: step.retryLimit,
297 input: cloneTemplateInput(step.input)
298 };
299}
300
301function cloneTemplateInput(input: TemplateInput): TemplateInput {
302 return cloneTemplateValue(input) as TemplateInput;
303}
304
305function cloneTemplateValue(value: TemplateValue): TemplateValue {
306 if (Array.isArray(value)) {
307 return value.map((entry) => cloneTemplateValue(entry));
308 }
309
310 if (isPlainObject(value)) {
311 const next: TemplateInput = {};
312
313 for (const [key, entry] of Object.entries(value)) {
314 next[key] = cloneTemplateValue(entry);
315 }
316
317 return next;
318 }
319
320 return value;
321}
322
323function isPlainObject(value: TemplateValue): value is TemplateInput {
324 return typeof value === "object" && value !== null && !Array.isArray(value);
325}