alchemy 0.1.1 → 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.
- package/README.md +324 -0
- package/package.json +15 -8
- package/src/$.ts +36 -0
- package/src/agent/ai.ts +215 -0
- package/src/{components/agent → agent}/anthropic.ts +6 -2
- package/src/agent/dependencies.ts +62 -0
- package/src/agent/file-context.ts +8 -0
- package/src/agent/index.ts +9 -0
- package/src/agent/model.ts +39 -0
- package/src/agent/module.ts +418 -0
- package/src/{components/agent → agent}/openai.ts +6 -0
- package/src/agent/program.md +38 -0
- package/src/agent/program.ts +37 -0
- package/src/agent/prompts.ts +20 -0
- package/src/alchemize.ts +45 -14
- package/src/apply.ts +25 -10
- package/src/{components/aws → aws}/bucket.ts +2 -2
- package/src/{components/aws → aws}/function.ts +2 -2
- package/src/{components/aws → aws}/policy.ts +2 -2
- package/src/{components/aws → aws}/queue.ts +1 -1
- package/src/{components/aws → aws}/role.ts +2 -2
- package/src/{components/aws → aws}/table.ts +2 -2
- package/src/destroy.ts +36 -12
- package/src/{components/esbuild.ts → esbuild.ts} +1 -1
- package/src/{components/fs.ts → fs.ts} +20 -18
- package/src/global.ts +7 -14
- package/src/index.ts +11 -2
- package/src/input.ts +1 -1
- package/src/markdown/design.ts +125 -0
- package/src/markdown/extract.ts +9 -0
- package/src/markdown/index.ts +3 -0
- package/src/{components/agent → markdown}/requirements.ts +53 -29
- package/src/output.ts +6 -0
- package/src/resource.ts +208 -75
- package/src/scope.ts +56 -0
- package/src/slug.ts +3 -0
- package/src/state.ts +66 -38
- package/src/typescript/check-omission.ts +47 -0
- package/src/typescript/debug-type-errors.ts +42 -0
- package/src/typescript/extract.ts +9 -0
- package/src/{components/agent → typescript}/index.ts +3 -1
- package/src/typescript/install-packages.ts +50 -0
- package/src/{components/agent → typescript}/package.ts +44 -27
- package/src/typescript/repair-omissions.ts +31 -0
- package/src/typescript/repair.ts +45 -0
- package/src/{components/agent → typescript}/tsconfig.ts +37 -17
- package/src/typescript/typescript.ts +217 -0
- package/src/typescript/validate.ts +73 -0
- package/test/scope.test.ts +67 -0
- package/src/components/agent/agent.ts +0 -45
- package/src/components/agent/model.ts +0 -22
- package/src/components/agent/typescript.ts +0 -177
- /package/src/{components/agent → agent}/scrape.ts +0 -0
- /package/src/{components/aws → aws}/index.ts +0 -0
package/src/state.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { ignore } from "./error";
|
|
3
4
|
|
|
4
5
|
export interface State {
|
|
5
6
|
status:
|
|
@@ -18,41 +19,64 @@ export interface State {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export interface StateStore {
|
|
22
|
+
/** Initialize the state container if one is required */
|
|
21
23
|
init?(): Promise<void>;
|
|
24
|
+
/** Delete the state container if one exists */
|
|
25
|
+
deinit?(): Promise<void>;
|
|
22
26
|
/** List all resources in the given stage. */
|
|
23
|
-
list(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
list(): Promise<string[]>;
|
|
28
|
+
/** Return the number of items let in this store */
|
|
29
|
+
count(): Promise<number>;
|
|
30
|
+
get(key: string): Promise<State | undefined>;
|
|
31
|
+
getBatch(ids: string[]): Promise<Record<string, State>>;
|
|
32
|
+
all(): Promise<Record<string, State>>;
|
|
33
|
+
set(key: string, value: State): Promise<void>;
|
|
34
|
+
delete(key: string): Promise<void>;
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
const stateFile = path.join(process.cwd(), ".alchemy");
|
|
32
38
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
export type StateStoreType = new (dir: string) => StateStore;
|
|
40
|
+
|
|
41
|
+
export class FileSystemStateStore implements StateStore {
|
|
42
|
+
public readonly path: string;
|
|
43
|
+
constructor(dir: string) {
|
|
44
|
+
this.path = path.join(stateFile, dir);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async init(): Promise<void> {
|
|
38
48
|
await fs.promises.mkdir(stateFile, { recursive: true });
|
|
39
|
-
|
|
40
|
-
|
|
49
|
+
await fs.promises.mkdir(this.path, { recursive: true });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async deinit(): Promise<void> {
|
|
53
|
+
await ignore("ENOENT", () => fs.promises.rmdir(this.path));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async count(): Promise<number> {
|
|
57
|
+
return Object.keys(await this.list()).length;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async list(): Promise<string[]> {
|
|
41
61
|
try {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
);
|
|
62
|
+
const files = await fs.promises.readdir(this.path, {
|
|
63
|
+
withFileTypes: true,
|
|
64
|
+
});
|
|
65
|
+
return files
|
|
66
|
+
.filter((dirent) => dirent.isFile() && dirent.name.endsWith(".json"))
|
|
67
|
+
.map((dirent) => dirent.name.replace(/\.json$/, ""));
|
|
45
68
|
} catch (error: any) {
|
|
46
69
|
if (error.code === "ENOENT") {
|
|
47
70
|
return [];
|
|
48
71
|
}
|
|
49
72
|
throw error;
|
|
50
73
|
}
|
|
51
|
-
}
|
|
52
|
-
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async get(key: string): Promise<State | undefined> {
|
|
53
77
|
try {
|
|
54
78
|
const content = await fs.promises.readFile(
|
|
55
|
-
await getPath(
|
|
79
|
+
await this.getPath(key),
|
|
56
80
|
"utf8",
|
|
57
81
|
);
|
|
58
82
|
return JSON.parse(content) as State;
|
|
@@ -62,25 +86,29 @@ export const defaultStore: StateStore = {
|
|
|
62
86
|
}
|
|
63
87
|
throw error;
|
|
64
88
|
}
|
|
65
|
-
}
|
|
66
|
-
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async set(key: string, value: State): Promise<void> {
|
|
67
92
|
return fs.promises.writeFile(
|
|
68
|
-
await getPath(
|
|
93
|
+
await this.getPath(key),
|
|
69
94
|
JSON.stringify(value, null, 2),
|
|
70
95
|
);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async delete(key: string): Promise<void> {
|
|
99
|
+
return fs.promises.unlink(await this.getPath(key));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async all(): Promise<Record<string, State>> {
|
|
103
|
+
return this.getBatch(await this.list());
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async getBatch(ids: string[]): Promise<Record<string, State>> {
|
|
79
107
|
return Object.fromEntries(
|
|
80
108
|
(
|
|
81
109
|
await Promise.all(
|
|
82
110
|
Array.from(ids).flatMap(async (id) => {
|
|
83
|
-
const s = await this.get(
|
|
111
|
+
const s = await this.get(id);
|
|
84
112
|
if (s === undefined) {
|
|
85
113
|
return [] as const;
|
|
86
114
|
}
|
|
@@ -89,12 +117,12 @@ export const defaultStore: StateStore = {
|
|
|
89
117
|
)
|
|
90
118
|
).flat(),
|
|
91
119
|
);
|
|
92
|
-
}
|
|
93
|
-
};
|
|
120
|
+
}
|
|
94
121
|
|
|
95
|
-
async
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
122
|
+
private async getPath(key: string): Promise<string> {
|
|
123
|
+
const file = path.join(this.path, `${key}.json`);
|
|
124
|
+
const dir = path.dirname(file);
|
|
125
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
126
|
+
return file;
|
|
127
|
+
}
|
|
100
128
|
}
|
|
@@ -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
|
+
}
|
|
@@ -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,
|
|
2
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
3
3
|
import { dirname } from "path";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import { resolveModel } from "
|
|
8
|
-
import
|
|
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
|
|
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
|
|
69
|
-
|
|
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
|
|
80
|
+
export class PackageJson extends Resource(
|
|
72
81
|
"code::Package",
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
|
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
|
|
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:
|
|
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: ${
|
|
112
|
+
Package name: ${props.name}
|
|
104
113
|
|
|
105
114
|
Requirements:
|
|
106
|
-
${
|
|
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(
|
|
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(
|
|
135
|
+
await writeFile(props.path, content);
|
|
123
136
|
|
|
124
|
-
return
|
|
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,
|
|
2
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
3
3
|
import { dirname } from "path";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { FileContext, dependenciesAsMessages, resolveModel } from "../agent";
|
|
6
|
+
import { rm } from "../fs";
|
|
7
|
+
import { type Context, Resource } from "../resource";
|
|
7
8
|
|
|
8
|
-
export type
|
|
9
|
+
export type TypeScriptConfigInput = z.infer<typeof TypeScriptConfigInput>;
|
|
9
10
|
|
|
10
|
-
export const
|
|
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
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
|
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
|
|
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,
|
|
162
|
+
await writeFile(props.path, content);
|
|
147
163
|
|
|
148
|
-
return
|
|
164
|
+
return {
|
|
165
|
+
path: props.path,
|
|
166
|
+
content,
|
|
167
|
+
tsconfig: result.object,
|
|
168
|
+
};
|
|
149
169
|
},
|
|
150
170
|
) {}
|