alchemy 0.1.2 → 0.1.3

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.
Files changed (54) hide show
  1. package/README.md +8 -39
  2. package/package.json +12 -8
  3. package/src/$.ts +36 -0
  4. package/src/agent/ai.ts +215 -0
  5. package/src/{components/agent → agent}/anthropic.ts +6 -2
  6. package/src/agent/dependencies.ts +62 -0
  7. package/src/agent/file-context.ts +8 -0
  8. package/src/agent/index.ts +9 -0
  9. package/src/agent/model.ts +39 -0
  10. package/src/agent/module.ts +418 -0
  11. package/src/{components/agent → agent}/openai.ts +6 -0
  12. package/src/agent/program.md +38 -0
  13. package/src/agent/program.ts +37 -0
  14. package/src/agent/prompts.ts +20 -0
  15. package/src/alchemize.ts +45 -14
  16. package/src/apply.ts +25 -10
  17. package/src/{components/aws → aws}/bucket.ts +2 -2
  18. package/src/{components/aws → aws}/function.ts +2 -2
  19. package/src/{components/aws → aws}/policy.ts +2 -2
  20. package/src/{components/aws → aws}/queue.ts +1 -1
  21. package/src/{components/aws → aws}/role.ts +2 -2
  22. package/src/{components/aws → aws}/table.ts +2 -2
  23. package/src/destroy.ts +36 -12
  24. package/src/{components/esbuild.ts → esbuild.ts} +1 -1
  25. package/src/{components/fs.ts → fs.ts} +20 -18
  26. package/src/global.ts +7 -14
  27. package/src/index.ts +11 -2
  28. package/src/input.ts +1 -1
  29. package/src/markdown/design.ts +125 -0
  30. package/src/markdown/extract.ts +9 -0
  31. package/src/markdown/index.ts +3 -0
  32. package/src/{components/agent → markdown}/requirements.ts +53 -29
  33. package/src/output.ts +6 -0
  34. package/src/resource.ts +208 -75
  35. package/src/scope.ts +56 -0
  36. package/src/slug.ts +3 -0
  37. package/src/state.ts +66 -38
  38. package/src/typescript/check-omission.ts +47 -0
  39. package/src/typescript/debug-type-errors.ts +42 -0
  40. package/src/typescript/extract.ts +9 -0
  41. package/src/{components/agent → typescript}/index.ts +3 -1
  42. package/src/typescript/install-packages.ts +50 -0
  43. package/src/{components/agent → typescript}/package.ts +44 -27
  44. package/src/typescript/repair-omissions.ts +31 -0
  45. package/src/typescript/repair.ts +45 -0
  46. package/src/{components/agent → typescript}/tsconfig.ts +37 -17
  47. package/src/typescript/typescript.ts +217 -0
  48. package/src/typescript/validate.ts +73 -0
  49. package/test/scope.test.ts +67 -0
  50. package/src/components/agent/agent.ts +0 -45
  51. package/src/components/agent/model.ts +0 -22
  52. package/src/components/agent/typescript.ts +0 -177
  53. /package/src/{components/agent → agent}/scrape.ts +0 -0
  54. /package/src/{components/aws → aws}/index.ts +0 -0
@@ -0,0 +1,47 @@
1
+ import { z } from "zod";
2
+ import { generateObject } from "../agent/ai";
3
+ import { resolveModel } from "../agent/model";
4
+
5
+ const CodeOmissionCheck = z.object({
6
+ hasOmittedCode: z.boolean(),
7
+ explanation: z.string(),
8
+ });
9
+
10
+ const haiku = await resolveModel("claude-3-5-haiku-latest");
11
+
12
+ export async function checkForCodeOmission(code: string): Promise<boolean> {
13
+ const result = await generateObject({
14
+ model: haiku,
15
+ schema: CodeOmissionCheck,
16
+ temperature: 0.1,
17
+ messages: [
18
+ {
19
+ role: "system",
20
+ content:
21
+ "You are checking for signs of code omission in TypeScript code. Look for comments that indicate abbreviated or omitted content.",
22
+ },
23
+ {
24
+ role: "user",
25
+ content: `Check this code for any signs of content being lazily omitted:
26
+
27
+ \`\`\`typescript
28
+ ${code}
29
+ \`\`\`
30
+
31
+ Look for patterns like:
32
+ - "// rest of the ..."
33
+ - "// remaining code ..."
34
+ - "// ... existing code ..."
35
+ - "// other methods ..."
36
+ - "// etc ..."
37
+ - "// Implementation would go here"
38
+ - "// For now just ..."
39
+ - Any similar comments that indicate code was abbreviated
40
+
41
+ Only answer true if you find such comments indicating omitted content. Otherwise answer false.`,
42
+ },
43
+ ],
44
+ });
45
+
46
+ return result.object.hasOmittedCode;
47
+ }
@@ -0,0 +1,42 @@
1
+ import type { CoreMessage } from "ai";
2
+ import { generateText } from "../agent/ai";
3
+ import { resolveModel } from "../agent/model";
4
+
5
+ export async function debugTypeErrors(
6
+ messages: CoreMessage[],
7
+ typeErrors: string,
8
+ ): Promise<string> {
9
+ const o3Model = await resolveModel("o3-mini");
10
+
11
+ // Get diagnostic analysis from o3-mini
12
+ const consultationMessages: CoreMessage[] = [
13
+ ...messages,
14
+ {
15
+ role: "user",
16
+ content: `Analyze these TypeScript errors and the code to identify the root causes:
17
+
18
+ TypeScript errors:
19
+ ${typeErrors}
20
+
21
+ Please help by:
22
+ 1. Diagnosing the root cause of any type errors
23
+ 2. Identifying specific sections of code that need to change
24
+ 3. Providing step-by-step guidance on how to fix them
25
+
26
+ Focus only on type-related issues and necessary fixes.`,
27
+ },
28
+ ];
29
+
30
+ const { text: analysis } = await generateText({
31
+ model: o3Model,
32
+ temperature: 0.1,
33
+ providerOptions: {
34
+ openai: {
35
+ reasoningEffort: "high",
36
+ },
37
+ },
38
+ messages: consultationMessages,
39
+ });
40
+
41
+ return analysis;
42
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Extracts TypeScript code from a markdown code block
3
+ * @param text Response text that may contain markdown
4
+ * @returns The TypeScript code if found, undefined otherwise
5
+ */
6
+ export function extractTypeScriptCode(text: string): string | undefined {
7
+ const matches = text.match(/```(?:ts|typescript)\n([\s\S]*?)```/);
8
+ return matches?.[1]?.trim();
9
+ }
@@ -1,4 +1,6 @@
1
+ export * from "./extract";
1
2
  export * from "./package";
2
- export * from "./requirements";
3
+ export * from "./repair";
3
4
  export * from "./tsconfig";
4
5
  export * from "./typescript";
6
+ export * from "./validate";
@@ -0,0 +1,50 @@
1
+ import { tool } from "ai";
2
+ import { exec } from "child_process";
3
+ import { promisify } from "util";
4
+ import { z } from "zod";
5
+
6
+ const execAsync = promisify(exec);
7
+
8
+ export const installPackages = tool({
9
+ description: "Installs one or more packages using bun add",
10
+ parameters: z.object({
11
+ packages: z.array(z.string()).describe("Array of package names to install"),
12
+ isDev: z
13
+ .boolean()
14
+ .optional()
15
+ .describe("Whether to install as dev dependencies"),
16
+ cwd: z
17
+ .string()
18
+ .optional()
19
+ .describe("Working directory where to run the install command"),
20
+ }),
21
+ execute: async ({ packages, isDev = false, cwd = process.cwd() }) => {
22
+ if (packages.length === 0) {
23
+ throw new Error("No packages specified for installation");
24
+ }
25
+
26
+ const command = `bun add ${isDev ? "-D " : ""}${packages.join(" ")}`;
27
+
28
+ try {
29
+ console.log(`Running command: ${command}`);
30
+ const { stdout, stderr } = await execAsync(command, { cwd });
31
+
32
+ if (stderr) {
33
+ console.error("Installation stderr:", stderr);
34
+ }
35
+
36
+ return {
37
+ success: true,
38
+ command,
39
+ output: stdout,
40
+ packages,
41
+ type: isDev ? "dev" : "regular",
42
+ };
43
+ } catch (error: any) {
44
+ console.error("Installation error:", error);
45
+ throw new Error(
46
+ `Failed to install packages: ${error?.message || String(error)}`,
47
+ );
48
+ }
49
+ },
50
+ });
@@ -1,13 +1,12 @@
1
1
  import { generateObject } from "ai";
2
- import { mkdir, unlink, writeFile } from "fs/promises";
2
+ import { mkdir, writeFile } from "fs/promises";
3
3
  import { dirname } from "path";
4
4
  import { z } from "zod";
5
- import { Agent } from "./agent";
6
- import type { AnthropicModelId } from "./anthropic";
7
- import { resolveModel } from "./model";
8
- import type { OpenAIChatModelId } from "./openai";
9
-
10
- export type ModelId = OpenAIChatModelId | AnthropicModelId;
5
+ import { dependenciesAsMessages } from "../agent/dependencies";
6
+ import { FileContext } from "../agent/file-context";
7
+ import { resolveModel } from "../agent/model";
8
+ import { rm } from "../fs";
9
+ import { type Context, Resource } from "../resource";
11
10
 
12
11
  export const PackageJsonSchema = z.object({
13
12
  name: z.string(),
@@ -35,13 +34,18 @@ export const PackageJsonSchema = z.object({
35
34
  files: z.array(z.string()).optional(),
36
35
  });
37
36
 
38
- export const PackageInput = z.object({
37
+ export const PackageJsonInput = z.object({
39
38
  /**
40
39
  * List of requirements for the package
41
40
  * Can include dependencies, dev dependencies, scripts, etc.
42
41
  */
43
42
  requirements: z.array(z.string()),
44
43
 
44
+ /**
45
+ * List of dependencies for the package
46
+ */
47
+ dependencies: z.array(FileContext).optional(),
48
+
45
49
  /**
46
50
  * Name of the package
47
51
  */
@@ -65,62 +69,75 @@ export const PackageInput = z.object({
65
69
  path: z.string(),
66
70
  });
67
71
 
68
- export type PackageInput = z.infer<typeof PackageInput>;
69
- export type PackageOutput = z.infer<typeof PackageJsonSchema>;
72
+ export type PackageJsonInput = z.infer<typeof PackageJsonInput>;
73
+
74
+ export interface PackageJsonOutput {
75
+ path: string;
76
+ content: string;
77
+ packageJson: z.infer<typeof PackageJsonSchema>;
78
+ }
70
79
 
71
- export class PackageJson extends Agent(
80
+ export class PackageJson extends Resource(
72
81
  "code::Package",
73
- {
74
- description:
75
- "This Agent is responsible for generating package.json configuration based on requirements.",
76
- input: PackageInput,
77
- output: PackageJsonSchema,
78
- },
79
- async (ctx, input) => {
82
+ async (
83
+ ctx: Context<PackageJsonOutput>,
84
+ props: PackageJsonInput,
85
+ ): Promise<PackageJsonOutput | void> => {
80
86
  if (ctx.event === "delete") {
81
- await unlink(input.path);
87
+ await rm(props.path);
82
88
  return;
83
89
  }
84
90
 
91
+ console.log(ctx.event === "create" ? "Creating" : "Updating", props.path);
92
+
85
93
  // Get the appropriate model
86
- const { model } = await resolveModel(input.model ?? "gpt-4o");
94
+ const model = await resolveModel(props.model ?? "gpt-4o");
87
95
 
88
96
  // Generate the package configuration using generateObject for type safety
89
97
  const result = await generateObject({
90
98
  model,
91
99
  schema: PackageJsonSchema,
92
- temperature: input.temperature ?? 0.3,
100
+ temperature: props.temperature ?? 0.3,
93
101
  messages: [
94
102
  {
95
103
  role: "system",
96
104
  content:
97
105
  "You are an expert at creating Node.js/TypeScript package configurations. You will generate a package.json configuration based on requirements.",
98
106
  },
107
+ ...dependenciesAsMessages(props.dependencies ?? []),
99
108
  {
100
109
  role: "user",
101
110
  content: `Please generate a package.json configuration based on these requirements:
102
111
 
103
- Package name: ${input.name}
112
+ Package name: ${props.name}
104
113
 
105
114
  Requirements:
106
- ${input.requirements.map((req) => `- ${req}`).join("\n")}
115
+ ${props.requirements.map((req) => `- ${req}`).join("\n")}
107
116
 
108
117
  Rules:
109
118
  1. All dependencies must be peer dependencies
110
119
  2. Use latest stable versions for all dependencies
111
120
  3. Include standard TypeScript configuration in devDependencies
112
121
  4. Use "bun" as the package manager
113
- 5. Must use ESM modules (type: "module")`,
122
+ 5. Must use ESM modules (type: "module")
123
+
124
+ Output only the package.json code (do not emit any other files).`,
114
125
  },
115
126
  ],
116
127
  });
117
128
 
118
129
  // Ensure the directory exists
119
- await mkdir(dirname(input.path), { recursive: true });
130
+ await mkdir(dirname(props.path), { recursive: true });
131
+
132
+ const content = JSON.stringify(result.object, null, 2);
120
133
 
121
134
  // Write the package.json file
122
- await writeFile(input.path, JSON.stringify(result.object, null, 2));
135
+ await writeFile(props.path, content);
123
136
 
124
- return result.object;
137
+ return {
138
+ path: props.path,
139
+ content,
140
+ packageJson: result.object,
141
+ };
125
142
  },
126
143
  ) {}
@@ -0,0 +1,31 @@
1
+ import type { CoreMessage, LanguageModelV1 } from "ai";
2
+ import { generateText } from "../agent/ai";
3
+ import { extractTypeScriptCode } from "./extract";
4
+
5
+ export async function repairCodeOmissions(
6
+ model: LanguageModelV1,
7
+ messages: CoreMessage[] = [],
8
+ ): Promise<string> {
9
+ console.log("[TypeScript] Repairing code omissions...");
10
+
11
+ const repairMessages: CoreMessage[] = [
12
+ ...messages,
13
+ {
14
+ role: "user" as const,
15
+ content: `Do not omit implementation or leave placeholders. Complete the implementation fully.`,
16
+ },
17
+ ];
18
+
19
+ const { text } = await generateText({
20
+ model,
21
+ temperature: 0.1,
22
+ messages: repairMessages,
23
+ });
24
+
25
+ const repairedCode = extractTypeScriptCode(text);
26
+ if (!repairedCode) {
27
+ throw new Error("Failed to repair code omissions");
28
+ }
29
+
30
+ return repairedCode;
31
+ }
@@ -0,0 +1,45 @@
1
+ import type { CoreMessage } from "ai";
2
+ import { generateText } from "../agent";
3
+ import { extractTypeScriptCode } from "./extract";
4
+
5
+ export async function repairTypeScriptCode(
6
+ model: any,
7
+ typeErrors: string,
8
+ messages: CoreMessage[],
9
+ ): Promise<string> {
10
+ // If we have type check options, validate and potentially repair type errors
11
+ console.log("[TypeScript] Fixing type errors...");
12
+
13
+ messages.push({
14
+ role: "user" as const,
15
+ content: `Fix the type errors in this code:
16
+
17
+ Type errors found:
18
+ ${typeErrors}
19
+
20
+ Provide a complete implementation that fixes these issues while preserving all functionality.`,
21
+ });
22
+
23
+ // Add the repair attempt to the message history
24
+ const repairMessages: CoreMessage[] = [
25
+ {
26
+ role: "system" as const,
27
+ content:
28
+ "You are an expert TypeScript developer fixing type errors. Provide a complete implementation that resolves the identified issues.",
29
+ },
30
+ ...messages,
31
+ ];
32
+
33
+ const { text: repairAttempt } = await generateText({
34
+ model,
35
+ temperature: 0.1,
36
+ messages: repairMessages,
37
+ });
38
+
39
+ const repairedCode = extractTypeScriptCode(repairAttempt);
40
+ if (!repairedCode) {
41
+ throw new Error("Failed to repair type errors");
42
+ }
43
+
44
+ return repairedCode;
45
+ }
@@ -1,13 +1,14 @@
1
1
  import { generateObject } from "ai";
2
- import { mkdir, unlink, writeFile } from "fs/promises";
2
+ import { mkdir, writeFile } from "fs/promises";
3
3
  import { dirname } from "path";
4
4
  import { z } from "zod";
5
- import { Agent } from "./agent";
6
- import { resolveModel } from "./model";
5
+ import { FileContext, dependenciesAsMessages, resolveModel } from "../agent";
6
+ import { rm } from "../fs";
7
+ import { type Context, Resource } from "../resource";
7
8
 
8
- export type TsConfigProps = z.infer<typeof TsConfigProps>;
9
+ export type TypeScriptConfigInput = z.infer<typeof TypeScriptConfigInput>;
9
10
 
10
- export const TsConfigProps = z.object({
11
+ export const TypeScriptConfigInput = z.object({
11
12
  /**
12
13
  * The ID of the model to use for generating tsconfig configuration
13
14
  * @default "gpt-4o"
@@ -20,6 +21,11 @@ export const TsConfigProps = z.object({
20
21
  */
21
22
  requirements: z.array(z.string()),
22
23
 
24
+ /**
25
+ * List of dependencies for the TypeScript configuration
26
+ */
27
+ dependencies: z.array(FileContext).optional(),
28
+
23
29
  /**
24
30
  * Temperature setting for model generation
25
31
  * @default 0.3
@@ -93,22 +99,29 @@ export const TsConfigSchema = z.object({
93
99
  .optional(),
94
100
  });
95
101
 
96
- export class TypeScriptConfig extends Agent(
102
+ export interface TypeScriptConfigOutput
103
+ extends z.infer<typeof TypeScriptConfigOutput> {}
104
+ export const TypeScriptConfigOutput = z.object({
105
+ path: z.string(),
106
+ content: z.string(),
107
+ tsconfig: TsConfigSchema,
108
+ });
109
+
110
+ export class TypeScriptConfig extends Resource(
97
111
  "code::tsconfig",
98
- {
99
- description:
100
- "This Agent is responsible for generating tsconfig.json configuration based on requirements.",
101
- input: TsConfigProps,
102
- output: TsConfigSchema,
103
- },
104
- async (ctx, props) => {
112
+ async (
113
+ ctx: Context<TypeScriptConfigOutput>,
114
+ props: TypeScriptConfigInput,
115
+ ) => {
105
116
  if (ctx.event === "delete") {
106
- await unlink(props.path);
117
+ await rm(props.path);
107
118
  return;
108
119
  }
109
120
 
121
+ console.log(ctx.event === "create" ? "Creating" : "Updating", props.path);
122
+
110
123
  // Get the appropriate model
111
- const { model } = await resolveModel(props.modelId ?? "gpt-4o");
124
+ const model = await resolveModel(props.modelId ?? "gpt-4o");
112
125
 
113
126
  // Generate the tsconfig configuration using generateObject for type safety
114
127
  const result = await generateObject({
@@ -121,6 +134,7 @@ export class TypeScriptConfig extends Agent(
121
134
  content:
122
135
  "You are an expert at creating TypeScript configurations. You will generate a tsconfig.json configuration based on requirements.",
123
136
  },
137
+ ...dependenciesAsMessages(props.dependencies),
124
138
  {
125
139
  role: "user",
126
140
  content: `Please generate a tsconfig.json configuration based on these requirements:
@@ -142,9 +156,15 @@ Rules:
142
156
  // Ensure the directory exists
143
157
  await mkdir(dirname(props.path), { recursive: true });
144
158
 
159
+ const content = JSON.stringify(result.object, null, 2);
160
+
145
161
  // Write the tsconfig.json file
146
- await writeFile(props.path, JSON.stringify(result.object, null, 2));
162
+ await writeFile(props.path, content);
147
163
 
148
- return result.object;
164
+ return {
165
+ path: props.path,
166
+ content,
167
+ tsconfig: result.object,
168
+ };
149
169
  },
150
170
  ) {}
@@ -0,0 +1,217 @@
1
+ import type { CoreMessage } from "ai";
2
+ import { mkdir, writeFile } from "fs/promises";
3
+ import { dirname } from "path";
4
+ import { z } from "zod";
5
+ import { ModelId, generateText, resolveModel } from "../agent";
6
+ import { dependenciesAsMessages } from "../agent/dependencies";
7
+ import { FileContext } from "../agent/file-context";
8
+ import { rm } from "../fs";
9
+ import { type Context, Resource } from "../resource";
10
+ import { checkForCodeOmission } from "./check-omission";
11
+ import { debugTypeErrors } from "./debug-type-errors";
12
+ import { extractTypeScriptCode } from "./extract";
13
+ import { repairTypeScriptCode } from "./repair";
14
+ import { repairCodeOmissions } from "./repair-omissions";
15
+ import { validateTypeScript } from "./validate";
16
+
17
+ export type TypeScriptFileInput = z.infer<typeof TypeScriptFileInput>;
18
+
19
+ const TypeScriptFileInput = z.object({
20
+ /**
21
+ * The ID of the model to use for generating TypeScript code
22
+ * @default "gpt-4o"
23
+ */
24
+ modelId: ModelId.optional(),
25
+
26
+ /**
27
+ * The name of the file to write the code to
28
+ */
29
+ path: z.string(),
30
+
31
+ /**
32
+ * The requirements for the TypeScript file
33
+ */
34
+ requirements: z.string(),
35
+
36
+ /**
37
+ * List of other code files that it depends on
38
+ */
39
+ dependencies: z.array(FileContext).optional(),
40
+
41
+ /**
42
+ * Temperature setting for model generation (higher = more creative, lower = more focused)
43
+ * @default 0.7
44
+ */
45
+ temperature: z.number().optional(),
46
+
47
+ /**
48
+ * Whether to perform TypeScript type checking on the generated code
49
+ * @default false
50
+ */
51
+ typeCheck: z.boolean().optional(),
52
+
53
+ /**
54
+ * Path to the tsconfig.json file to use for validation
55
+ * If not provided, will look for tsconfig.json in the project root
56
+ * Only used if typeCheck is true
57
+ */
58
+ tsconfigPath: z.string().optional(),
59
+
60
+ /**
61
+ * Project root directory. Used to resolve tsconfig.json and module imports
62
+ * If not provided, will use the directory of the target file
63
+ * Only used if typeCheck is true
64
+ */
65
+ projectRoot: z.string().optional(),
66
+ });
67
+
68
+ export type TypeScriptFileOutput = z.infer<typeof TypeScriptFileOutput>;
69
+ export const TypeScriptFileOutput = FileContext;
70
+
71
+ export class TypeScriptFile extends Resource(
72
+ "code::TypeScriptFile",
73
+ async (ctx: Context<TypeScriptFileOutput>, props: TypeScriptFileInput) => {
74
+ if (ctx.event === "delete") {
75
+ await rm(props.path);
76
+ return;
77
+ }
78
+
79
+ console.log("Implementing", props.path);
80
+
81
+ // Get the appropriate model based on the ID
82
+ const model = await resolveModel(props.modelId ?? "gpt-4o");
83
+
84
+ const messages: CoreMessage[] = [
85
+ {
86
+ role: "system" as const,
87
+ content:
88
+ "You are an expert TypeScript developer that generates clean, well-documented code following best practices. " +
89
+ "Your response must follow this exact format:\n" +
90
+ "1. A single sentence describing what the code will do\n" +
91
+ "2. A single TypeScript code block surrounded by ```ts and ``` tags containing the COMPLETE implementation\n" +
92
+ "IMPORTANT: You must ALWAYS provide the ENTIRE file contents, including ALL imports, types, and implementations.\n" +
93
+ "NEVER output partial code or omit any functionality from the original file when making changes.\n" +
94
+ "Do not include any other explanations or multiple code blocks.",
95
+ },
96
+ ...dependenciesAsMessages(props.dependencies),
97
+ {
98
+ role: "user" as const,
99
+ content: `Please generate TypeScript code based on the following specifications:
100
+
101
+ Requirements:
102
+ ${props.requirements}
103
+
104
+ The code should:
105
+ 1. Be well-documented with JSDoc comments
106
+ 2. Follow TypeScript best practices
107
+ 3. Include proper type definitions
108
+ 4. Be ready to use with the provided context files
109
+ 5. Include ALL necessary imports and dependencies
110
+ 6. Contain the COMPLETE implementation with no omissions`,
111
+ },
112
+ ];
113
+
114
+ const maxAttempts = 3;
115
+ let code: string | undefined;
116
+ let typeErrors: string | undefined;
117
+
118
+ // Try generating and validating code up to maxAttempts times
119
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
120
+ try {
121
+ // After 2 failed attempts, consult o3-mini for analysis
122
+ if (attempt === maxAttempts) {
123
+ if (!ctx.quiet) {
124
+ console.log("[TypeScript] Consulting expert for code analysis...");
125
+ }
126
+ const diagnosis = await debugTypeErrors(messages, typeErrors!);
127
+
128
+ messages.push({
129
+ role: "user",
130
+ content: diagnosis,
131
+ });
132
+ }
133
+
134
+ // Generate the TypeScript code
135
+ if (!ctx.quiet) {
136
+ console.log(`[TypeScript] Generating code attempt ${attempt}...`);
137
+ }
138
+ const { text } = await generateText({
139
+ model,
140
+ temperature: Math.max(
141
+ 0.1,
142
+ (props.temperature ?? 0.7) * (1 - attempt * 0.2),
143
+ ),
144
+ messages,
145
+ });
146
+
147
+ code = extractTypeScriptCode(text);
148
+ if (!code) {
149
+ console.log("Format mismatch");
150
+ messages.push(
151
+ {
152
+ role: "assistant" as const,
153
+ content: text,
154
+ },
155
+ {
156
+ role: "user" as const,
157
+ content:
158
+ "Your response did not match the required format. Please provide your response with exactly one sentence summary" +
159
+ " followed by a single TypeScript code block surrounded by ```ts and ``` tags containing the COMPLETE implementation. No other text or explanations.",
160
+ },
161
+ );
162
+ continue;
163
+ }
164
+
165
+ // First check for omissions
166
+ if (!ctx.quiet) {
167
+ console.log("[TypeScript] Checking for code omissions...");
168
+ }
169
+ const hasOmissions = await checkForCodeOmission(code);
170
+ if (hasOmissions) {
171
+ code = await repairCodeOmissions(model, messages);
172
+ }
173
+
174
+ // Ensure the directory exists
175
+ await mkdir(dirname(props.path), { recursive: true });
176
+
177
+ // Write the TypeScript file
178
+ await writeFile(props.path, code);
179
+
180
+ if (props.typeCheck) {
181
+ typeErrors = await validateTypeScript(props.path, {
182
+ tsconfigPath: props.tsconfigPath,
183
+ projectRoot: props.projectRoot,
184
+ });
185
+ if (typeErrors) {
186
+ if (attempt === maxAttempts) {
187
+ throw new Error(
188
+ `Failed to generate type-safe code after maximum attempts. Errors:\n${typeErrors}`,
189
+ );
190
+ } else {
191
+ code = await repairTypeScriptCode(model, typeErrors, messages);
192
+ continue;
193
+ }
194
+ }
195
+ }
196
+
197
+ // If we get here, the code is valid
198
+ return {
199
+ path: props.path,
200
+ content: code,
201
+ };
202
+ } catch (error) {
203
+ // On the last attempt, rethrow the error
204
+ if (attempt === maxAttempts) {
205
+ throw error;
206
+ }
207
+ // Otherwise continue to the next attempt
208
+ if (!ctx.quiet) {
209
+ console.log(`Attempt ${attempt} failed:`, error);
210
+ }
211
+ }
212
+ }
213
+
214
+ // This should never be reached due to the throws above
215
+ throw new Error("Unexpected code generation failure");
216
+ },
217
+ ) {}