mirror of
https://github.com/samiyev/puaros.git
synced 2025-12-27 23:06:54 +05:00
feat(ipuaro): add enum value definitions to initial context
Extract enum declarations with member names and values from TypeScript AST and display them in the initial LLM context. This allows the LLM to know valid enum values without making tool calls. Features: - Numeric values (Active=1) - String values (Admin="admin") - Implicit values (Up, Down) - Negative numbers (Cold=-10) - const enum modifier - export enum modifier - Long enum truncation (>100 chars) Adds EnumInfo and EnumMemberInfo interfaces, extractEnum() method in ASTParser, formatEnumSignature() in prompts.ts, and 17 new unit tests.
This commit is contained in:
@@ -562,4 +562,138 @@ third: value3`
|
||||
expect(ast.exports[2].line).toBe(3)
|
||||
})
|
||||
})
|
||||
|
||||
describe("enums (0.24.3)", () => {
|
||||
it("should extract enum with numeric values", () => {
|
||||
const code = `enum Status {
|
||||
Active = 1,
|
||||
Inactive = 0,
|
||||
Pending = 2
|
||||
}`
|
||||
const ast = parser.parse(code, "ts")
|
||||
|
||||
expect(ast.enums).toHaveLength(1)
|
||||
expect(ast.enums[0]).toMatchObject({
|
||||
name: "Status",
|
||||
isExported: false,
|
||||
isConst: false,
|
||||
})
|
||||
expect(ast.enums[0].members).toHaveLength(3)
|
||||
expect(ast.enums[0].members[0]).toMatchObject({ name: "Active", value: 1 })
|
||||
expect(ast.enums[0].members[1]).toMatchObject({ name: "Inactive", value: 0 })
|
||||
expect(ast.enums[0].members[2]).toMatchObject({ name: "Pending", value: 2 })
|
||||
})
|
||||
|
||||
it("should extract enum with string values", () => {
|
||||
const code = `enum Role {
|
||||
Admin = "admin",
|
||||
User = "user",
|
||||
Guest = "guest"
|
||||
}`
|
||||
const ast = parser.parse(code, "ts")
|
||||
|
||||
expect(ast.enums).toHaveLength(1)
|
||||
expect(ast.enums[0].members).toHaveLength(3)
|
||||
expect(ast.enums[0].members[0]).toMatchObject({ name: "Admin", value: "admin" })
|
||||
expect(ast.enums[0].members[1]).toMatchObject({ name: "User", value: "user" })
|
||||
expect(ast.enums[0].members[2]).toMatchObject({ name: "Guest", value: "guest" })
|
||||
})
|
||||
|
||||
it("should extract enum without explicit values", () => {
|
||||
const code = `enum Direction {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right
|
||||
}`
|
||||
const ast = parser.parse(code, "ts")
|
||||
|
||||
expect(ast.enums).toHaveLength(1)
|
||||
expect(ast.enums[0].members).toHaveLength(4)
|
||||
expect(ast.enums[0].members[0]).toMatchObject({ name: "Up", value: undefined })
|
||||
expect(ast.enums[0].members[1]).toMatchObject({ name: "Down", value: undefined })
|
||||
})
|
||||
|
||||
it("should extract exported enum", () => {
|
||||
const code = `export enum Color {
|
||||
Red = "#FF0000",
|
||||
Green = "#00FF00",
|
||||
Blue = "#0000FF"
|
||||
}`
|
||||
const ast = parser.parse(code, "ts")
|
||||
|
||||
expect(ast.enums).toHaveLength(1)
|
||||
expect(ast.enums[0].isExported).toBe(true)
|
||||
expect(ast.exports).toHaveLength(1)
|
||||
expect(ast.exports[0].kind).toBe("type")
|
||||
})
|
||||
|
||||
it("should extract const enum", () => {
|
||||
const code = `const enum HttpStatus {
|
||||
OK = 200,
|
||||
NotFound = 404,
|
||||
InternalError = 500
|
||||
}`
|
||||
const ast = parser.parse(code, "ts")
|
||||
|
||||
expect(ast.enums).toHaveLength(1)
|
||||
expect(ast.enums[0].isConst).toBe(true)
|
||||
expect(ast.enums[0].members[0]).toMatchObject({ name: "OK", value: 200 })
|
||||
})
|
||||
|
||||
it("should extract exported const enum", () => {
|
||||
const code = `export const enum LogLevel {
|
||||
Debug = 0,
|
||||
Info = 1,
|
||||
Warn = 2,
|
||||
Error = 3
|
||||
}`
|
||||
const ast = parser.parse(code, "ts")
|
||||
|
||||
expect(ast.enums).toHaveLength(1)
|
||||
expect(ast.enums[0].isExported).toBe(true)
|
||||
expect(ast.enums[0].isConst).toBe(true)
|
||||
})
|
||||
|
||||
it("should extract line range for enum", () => {
|
||||
const code = `enum Test {
|
||||
A = 1,
|
||||
B = 2
|
||||
}`
|
||||
const ast = parser.parse(code, "ts")
|
||||
|
||||
expect(ast.enums[0].lineStart).toBe(1)
|
||||
expect(ast.enums[0].lineEnd).toBe(4)
|
||||
})
|
||||
|
||||
it("should handle enum with negative values", () => {
|
||||
const code = `enum Temperature {
|
||||
Cold = -10,
|
||||
Freezing = -20,
|
||||
Hot = 40
|
||||
}`
|
||||
const ast = parser.parse(code, "ts")
|
||||
|
||||
expect(ast.enums).toHaveLength(1)
|
||||
expect(ast.enums[0].members[0]).toMatchObject({ name: "Cold", value: -10 })
|
||||
expect(ast.enums[0].members[1]).toMatchObject({ name: "Freezing", value: -20 })
|
||||
expect(ast.enums[0].members[2]).toMatchObject({ name: "Hot", value: 40 })
|
||||
})
|
||||
|
||||
it("should handle empty enum", () => {
|
||||
const code = `enum Empty {}`
|
||||
const ast = parser.parse(code, "ts")
|
||||
|
||||
expect(ast.enums).toHaveLength(1)
|
||||
expect(ast.enums[0].name).toBe("Empty")
|
||||
expect(ast.enums[0].members).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("should not extract enum from JavaScript", () => {
|
||||
const code = `enum Status { Active = 1 }`
|
||||
const ast = parser.parse(code, "js")
|
||||
|
||||
expect(ast.enums).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1191,9 +1191,7 @@ describe("prompts", () => {
|
||||
|
||||
const context = buildInitialContext(structure, asts)
|
||||
|
||||
expect(context).toContain(
|
||||
"interface AdminUser extends User { role: string }",
|
||||
)
|
||||
expect(context).toContain("interface AdminUser extends User { role: string }")
|
||||
})
|
||||
|
||||
it("should format interface with readonly fields", () => {
|
||||
@@ -1494,4 +1492,285 @@ describe("prompts", () => {
|
||||
expect(context).toContain("- type Handler = (event: Event) => void")
|
||||
})
|
||||
})
|
||||
|
||||
describe("enum definitions (0.24.3)", () => {
|
||||
it("should format enum with numeric values", () => {
|
||||
const structure: ProjectStructure = {
|
||||
name: "test",
|
||||
rootPath: "/test",
|
||||
files: ["enums.ts"],
|
||||
directories: [],
|
||||
}
|
||||
const asts = new Map<string, FileAST>([
|
||||
[
|
||||
"enums.ts",
|
||||
{
|
||||
imports: [],
|
||||
exports: [],
|
||||
functions: [],
|
||||
classes: [],
|
||||
interfaces: [],
|
||||
typeAliases: [],
|
||||
enums: [
|
||||
{
|
||||
name: "Status",
|
||||
lineStart: 1,
|
||||
lineEnd: 5,
|
||||
members: [
|
||||
{ name: "Active", value: 1 },
|
||||
{ name: "Inactive", value: 0 },
|
||||
{ name: "Pending", value: 2 },
|
||||
],
|
||||
isExported: true,
|
||||
isConst: false,
|
||||
},
|
||||
],
|
||||
parseError: false,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
const context = buildInitialContext(structure, asts)
|
||||
|
||||
expect(context).toContain("- enum Status { Active=1, Inactive=0, Pending=2 }")
|
||||
})
|
||||
|
||||
it("should format enum with string values", () => {
|
||||
const structure: ProjectStructure = {
|
||||
name: "test",
|
||||
rootPath: "/test",
|
||||
files: ["enums.ts"],
|
||||
directories: [],
|
||||
}
|
||||
const asts = new Map<string, FileAST>([
|
||||
[
|
||||
"enums.ts",
|
||||
{
|
||||
imports: [],
|
||||
exports: [],
|
||||
functions: [],
|
||||
classes: [],
|
||||
interfaces: [],
|
||||
typeAliases: [],
|
||||
enums: [
|
||||
{
|
||||
name: "Role",
|
||||
lineStart: 1,
|
||||
lineEnd: 5,
|
||||
members: [
|
||||
{ name: "Admin", value: "admin" },
|
||||
{ name: "User", value: "user" },
|
||||
],
|
||||
isExported: true,
|
||||
isConst: false,
|
||||
},
|
||||
],
|
||||
parseError: false,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
const context = buildInitialContext(structure, asts)
|
||||
|
||||
expect(context).toContain('- enum Role { Admin="admin", User="user" }')
|
||||
})
|
||||
|
||||
it("should format const enum", () => {
|
||||
const structure: ProjectStructure = {
|
||||
name: "test",
|
||||
rootPath: "/test",
|
||||
files: ["enums.ts"],
|
||||
directories: [],
|
||||
}
|
||||
const asts = new Map<string, FileAST>([
|
||||
[
|
||||
"enums.ts",
|
||||
{
|
||||
imports: [],
|
||||
exports: [],
|
||||
functions: [],
|
||||
classes: [],
|
||||
interfaces: [],
|
||||
typeAliases: [],
|
||||
enums: [
|
||||
{
|
||||
name: "HttpStatus",
|
||||
lineStart: 1,
|
||||
lineEnd: 5,
|
||||
members: [
|
||||
{ name: "OK", value: 200 },
|
||||
{ name: "NotFound", value: 404 },
|
||||
],
|
||||
isExported: true,
|
||||
isConst: true,
|
||||
},
|
||||
],
|
||||
parseError: false,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
const context = buildInitialContext(structure, asts)
|
||||
|
||||
expect(context).toContain("- const enum HttpStatus { OK=200, NotFound=404 }")
|
||||
})
|
||||
|
||||
it("should format enum without explicit values", () => {
|
||||
const structure: ProjectStructure = {
|
||||
name: "test",
|
||||
rootPath: "/test",
|
||||
files: ["enums.ts"],
|
||||
directories: [],
|
||||
}
|
||||
const asts = new Map<string, FileAST>([
|
||||
[
|
||||
"enums.ts",
|
||||
{
|
||||
imports: [],
|
||||
exports: [],
|
||||
functions: [],
|
||||
classes: [],
|
||||
interfaces: [],
|
||||
typeAliases: [],
|
||||
enums: [
|
||||
{
|
||||
name: "Direction",
|
||||
lineStart: 1,
|
||||
lineEnd: 5,
|
||||
members: [
|
||||
{ name: "Up", value: undefined },
|
||||
{ name: "Down", value: undefined },
|
||||
],
|
||||
isExported: true,
|
||||
isConst: false,
|
||||
},
|
||||
],
|
||||
parseError: false,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
const context = buildInitialContext(structure, asts)
|
||||
|
||||
expect(context).toContain("- enum Direction { Up, Down }")
|
||||
})
|
||||
|
||||
it("should format empty enum", () => {
|
||||
const structure: ProjectStructure = {
|
||||
name: "test",
|
||||
rootPath: "/test",
|
||||
files: ["enums.ts"],
|
||||
directories: [],
|
||||
}
|
||||
const asts = new Map<string, FileAST>([
|
||||
[
|
||||
"enums.ts",
|
||||
{
|
||||
imports: [],
|
||||
exports: [],
|
||||
functions: [],
|
||||
classes: [],
|
||||
interfaces: [],
|
||||
typeAliases: [],
|
||||
enums: [
|
||||
{
|
||||
name: "Empty",
|
||||
lineStart: 1,
|
||||
lineEnd: 1,
|
||||
members: [],
|
||||
isExported: true,
|
||||
isConst: false,
|
||||
},
|
||||
],
|
||||
parseError: false,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
const context = buildInitialContext(structure, asts)
|
||||
|
||||
expect(context).toContain("- enum Empty")
|
||||
})
|
||||
|
||||
it("should include enum in compact format", () => {
|
||||
const structure: ProjectStructure = {
|
||||
name: "test",
|
||||
rootPath: "/test",
|
||||
files: ["enums.ts"],
|
||||
directories: [],
|
||||
}
|
||||
const asts = new Map<string, FileAST>([
|
||||
[
|
||||
"enums.ts",
|
||||
{
|
||||
imports: [],
|
||||
exports: [],
|
||||
functions: [],
|
||||
classes: [],
|
||||
interfaces: [],
|
||||
typeAliases: [],
|
||||
enums: [
|
||||
{
|
||||
name: "Status",
|
||||
lineStart: 1,
|
||||
lineEnd: 5,
|
||||
members: [{ name: "Active", value: 1 }],
|
||||
isExported: true,
|
||||
isConst: false,
|
||||
},
|
||||
],
|
||||
parseError: false,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
const context = buildInitialContext(structure, asts, undefined, {
|
||||
includeSignatures: false,
|
||||
})
|
||||
|
||||
expect(context).toContain("enum: Status")
|
||||
})
|
||||
|
||||
it("should truncate long enum definitions", () => {
|
||||
const structure: ProjectStructure = {
|
||||
name: "test",
|
||||
rootPath: "/test",
|
||||
files: ["enums.ts"],
|
||||
directories: [],
|
||||
}
|
||||
const asts = new Map<string, FileAST>([
|
||||
[
|
||||
"enums.ts",
|
||||
{
|
||||
imports: [],
|
||||
exports: [],
|
||||
functions: [],
|
||||
classes: [],
|
||||
interfaces: [],
|
||||
typeAliases: [],
|
||||
enums: [
|
||||
{
|
||||
name: "VeryLongEnumName",
|
||||
lineStart: 1,
|
||||
lineEnd: 20,
|
||||
members: [
|
||||
{ name: "FirstVeryLongMemberName", value: "first_value" },
|
||||
{ name: "SecondVeryLongMemberName", value: "second_value" },
|
||||
{ name: "ThirdVeryLongMemberName", value: "third_value" },
|
||||
],
|
||||
isExported: true,
|
||||
isConst: false,
|
||||
},
|
||||
],
|
||||
parseError: false,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
const context = buildInitialContext(structure, asts)
|
||||
|
||||
expect(context).toContain("...")
|
||||
expect(context).toContain("- enum VeryLongEnumName")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user