mirror of
https://github.com/samiyev/puaros.git
synced 2025-12-27 23:06:54 +05:00
feat(ipuaro): add function signatures to initial context
- Add full function signatures with parameter types and return types - Arrow functions now extract returnType in ASTParser - New formatFunctionSignature() helper in prompts.ts - Add includeSignatures config option (default: true) - Support compact format when includeSignatures: false - 15 new tests, coverage 91.14% branches
This commit is contained in:
@@ -108,13 +108,23 @@ describe("prompts", () => {
|
||||
expect(context).toContain("tests/")
|
||||
})
|
||||
|
||||
it("should include file overview with AST summaries", () => {
|
||||
it("should include file overview with AST summaries (signatures format)", () => {
|
||||
const context = buildInitialContext(structure, asts)
|
||||
|
||||
expect(context).toContain("## Files")
|
||||
expect(context).toContain("src/index.ts")
|
||||
expect(context).toContain("### src/index.ts")
|
||||
expect(context).toContain("- main()")
|
||||
expect(context).toContain("### src/utils.ts")
|
||||
expect(context).toContain("- class Helper")
|
||||
})
|
||||
|
||||
it("should use compact format when includeSignatures is false", () => {
|
||||
const context = buildInitialContext(structure, asts, undefined, {
|
||||
includeSignatures: false,
|
||||
})
|
||||
|
||||
expect(context).toContain("## Files")
|
||||
expect(context).toContain("fn: main")
|
||||
expect(context).toContain("src/utils.ts")
|
||||
expect(context).toContain("class: Helper")
|
||||
})
|
||||
|
||||
@@ -506,7 +516,16 @@ describe("prompts", () => {
|
||||
exports: [],
|
||||
functions: [],
|
||||
classes: [],
|
||||
interfaces: [{ name: "IFoo", lineStart: 1, lineEnd: 5, isExported: true }],
|
||||
interfaces: [
|
||||
{
|
||||
name: "IFoo",
|
||||
lineStart: 1,
|
||||
lineEnd: 5,
|
||||
properties: [],
|
||||
extends: [],
|
||||
isExported: true,
|
||||
},
|
||||
],
|
||||
typeAliases: [],
|
||||
parseError: false,
|
||||
},
|
||||
@@ -515,6 +534,44 @@ describe("prompts", () => {
|
||||
|
||||
const context = buildInitialContext(structure, asts)
|
||||
|
||||
expect(context).toContain("- interface IFoo")
|
||||
})
|
||||
|
||||
it("should handle file with only interfaces (compact format)", () => {
|
||||
const structure: ProjectStructure = {
|
||||
name: "test",
|
||||
rootPath: "/test",
|
||||
files: ["types.ts"],
|
||||
directories: [],
|
||||
}
|
||||
const asts = new Map<string, FileAST>([
|
||||
[
|
||||
"types.ts",
|
||||
{
|
||||
imports: [],
|
||||
exports: [],
|
||||
functions: [],
|
||||
classes: [],
|
||||
interfaces: [
|
||||
{
|
||||
name: "IFoo",
|
||||
lineStart: 1,
|
||||
lineEnd: 5,
|
||||
properties: [],
|
||||
extends: [],
|
||||
isExported: true,
|
||||
},
|
||||
],
|
||||
typeAliases: [],
|
||||
parseError: false,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
const context = buildInitialContext(structure, asts, undefined, {
|
||||
includeSignatures: false,
|
||||
})
|
||||
|
||||
expect(context).toContain("interface: IFoo")
|
||||
})
|
||||
|
||||
@@ -534,9 +591,7 @@ describe("prompts", () => {
|
||||
functions: [],
|
||||
classes: [],
|
||||
interfaces: [],
|
||||
typeAliases: [
|
||||
{ name: "MyType", lineStart: 1, lineEnd: 1, isExported: true },
|
||||
],
|
||||
typeAliases: [{ name: "MyType", line: 1, isExported: true }],
|
||||
parseError: false,
|
||||
},
|
||||
],
|
||||
@@ -544,6 +599,35 @@ describe("prompts", () => {
|
||||
|
||||
const context = buildInitialContext(structure, asts)
|
||||
|
||||
expect(context).toContain("- type MyType")
|
||||
})
|
||||
|
||||
it("should handle file with only type aliases (compact format)", () => {
|
||||
const structure: ProjectStructure = {
|
||||
name: "test",
|
||||
rootPath: "/test",
|
||||
files: ["types.ts"],
|
||||
directories: [],
|
||||
}
|
||||
const asts = new Map<string, FileAST>([
|
||||
[
|
||||
"types.ts",
|
||||
{
|
||||
imports: [],
|
||||
exports: [],
|
||||
functions: [],
|
||||
classes: [],
|
||||
interfaces: [],
|
||||
typeAliases: [{ name: "MyType", line: 1, isExported: true }],
|
||||
parseError: false,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
const context = buildInitialContext(structure, asts, undefined, {
|
||||
includeSignatures: false,
|
||||
})
|
||||
|
||||
expect(context).toContain("type: MyType")
|
||||
})
|
||||
|
||||
@@ -686,6 +770,22 @@ describe("prompts", () => {
|
||||
expect(context).toContain("exists.ts")
|
||||
expect(context).not.toContain("missing.ts")
|
||||
})
|
||||
|
||||
it("should skip undefined AST entries", () => {
|
||||
const structure: ProjectStructure = {
|
||||
name: "test",
|
||||
rootPath: "/test",
|
||||
files: ["file.ts"],
|
||||
directories: [],
|
||||
}
|
||||
const asts = new Map<string, FileAST>()
|
||||
asts.set("file.ts", undefined as unknown as FileAST)
|
||||
|
||||
const context = buildInitialContext(structure, asts)
|
||||
|
||||
expect(context).toContain("## Files")
|
||||
expect(context).not.toContain("file.ts")
|
||||
})
|
||||
})
|
||||
|
||||
describe("truncateContext", () => {
|
||||
@@ -714,4 +814,276 @@ describe("prompts", () => {
|
||||
expect(result).toContain("truncated")
|
||||
})
|
||||
})
|
||||
|
||||
describe("function signatures with types", () => {
|
||||
it("should format function with typed parameters", () => {
|
||||
const structure: ProjectStructure = {
|
||||
name: "test",
|
||||
rootPath: "/test",
|
||||
files: ["user.ts"],
|
||||
directories: [],
|
||||
}
|
||||
const asts = new Map<string, FileAST>([
|
||||
[
|
||||
"user.ts",
|
||||
{
|
||||
imports: [],
|
||||
exports: [],
|
||||
functions: [
|
||||
{
|
||||
name: "getUser",
|
||||
lineStart: 1,
|
||||
lineEnd: 5,
|
||||
params: [
|
||||
{
|
||||
name: "id",
|
||||
type: "string",
|
||||
optional: false,
|
||||
hasDefault: false,
|
||||
},
|
||||
],
|
||||
isAsync: false,
|
||||
isExported: true,
|
||||
},
|
||||
],
|
||||
classes: [],
|
||||
interfaces: [],
|
||||
typeAliases: [],
|
||||
parseError: false,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
const context = buildInitialContext(structure, asts)
|
||||
|
||||
expect(context).toContain("- getUser(id: string)")
|
||||
})
|
||||
|
||||
it("should format async function with return type", () => {
|
||||
const structure: ProjectStructure = {
|
||||
name: "test",
|
||||
rootPath: "/test",
|
||||
files: ["user.ts"],
|
||||
directories: [],
|
||||
}
|
||||
const asts = new Map<string, FileAST>([
|
||||
[
|
||||
"user.ts",
|
||||
{
|
||||
imports: [],
|
||||
exports: [],
|
||||
functions: [
|
||||
{
|
||||
name: "getUser",
|
||||
lineStart: 1,
|
||||
lineEnd: 5,
|
||||
params: [
|
||||
{
|
||||
name: "id",
|
||||
type: "string",
|
||||
optional: false,
|
||||
hasDefault: false,
|
||||
},
|
||||
],
|
||||
isAsync: true,
|
||||
isExported: true,
|
||||
returnType: "Promise<User>",
|
||||
},
|
||||
],
|
||||
classes: [],
|
||||
interfaces: [],
|
||||
typeAliases: [],
|
||||
parseError: false,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
const context = buildInitialContext(structure, asts)
|
||||
|
||||
expect(context).toContain("- async getUser(id: string): Promise<User>")
|
||||
})
|
||||
|
||||
it("should format function with optional parameters", () => {
|
||||
const structure: ProjectStructure = {
|
||||
name: "test",
|
||||
rootPath: "/test",
|
||||
files: ["utils.ts"],
|
||||
directories: [],
|
||||
}
|
||||
const asts = new Map<string, FileAST>([
|
||||
[
|
||||
"utils.ts",
|
||||
{
|
||||
imports: [],
|
||||
exports: [],
|
||||
functions: [
|
||||
{
|
||||
name: "format",
|
||||
lineStart: 1,
|
||||
lineEnd: 5,
|
||||
params: [
|
||||
{
|
||||
name: "value",
|
||||
type: "string",
|
||||
optional: false,
|
||||
hasDefault: false,
|
||||
},
|
||||
{
|
||||
name: "options",
|
||||
type: "FormatOptions",
|
||||
optional: true,
|
||||
hasDefault: false,
|
||||
},
|
||||
],
|
||||
isAsync: false,
|
||||
isExported: true,
|
||||
returnType: "string",
|
||||
},
|
||||
],
|
||||
classes: [],
|
||||
interfaces: [],
|
||||
typeAliases: [],
|
||||
parseError: false,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
const context = buildInitialContext(structure, asts)
|
||||
|
||||
expect(context).toContain("- format(value: string, options?: FormatOptions): string")
|
||||
})
|
||||
|
||||
it("should format function with multiple typed parameters", () => {
|
||||
const structure: ProjectStructure = {
|
||||
name: "test",
|
||||
rootPath: "/test",
|
||||
files: ["api.ts"],
|
||||
directories: [],
|
||||
}
|
||||
const asts = new Map<string, FileAST>([
|
||||
[
|
||||
"api.ts",
|
||||
{
|
||||
imports: [],
|
||||
exports: [],
|
||||
functions: [
|
||||
{
|
||||
name: "createUser",
|
||||
lineStart: 1,
|
||||
lineEnd: 10,
|
||||
params: [
|
||||
{
|
||||
name: "name",
|
||||
type: "string",
|
||||
optional: false,
|
||||
hasDefault: false,
|
||||
},
|
||||
{
|
||||
name: "email",
|
||||
type: "string",
|
||||
optional: false,
|
||||
hasDefault: false,
|
||||
},
|
||||
{
|
||||
name: "age",
|
||||
type: "number",
|
||||
optional: true,
|
||||
hasDefault: false,
|
||||
},
|
||||
],
|
||||
isAsync: true,
|
||||
isExported: true,
|
||||
returnType: "Promise<User>",
|
||||
},
|
||||
],
|
||||
classes: [],
|
||||
interfaces: [],
|
||||
typeAliases: [],
|
||||
parseError: false,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
const context = buildInitialContext(structure, asts)
|
||||
|
||||
expect(context).toContain(
|
||||
"- async createUser(name: string, email: string, age?: number): Promise<User>",
|
||||
)
|
||||
})
|
||||
|
||||
it("should format function without types (JavaScript style)", () => {
|
||||
const structure: ProjectStructure = {
|
||||
name: "test",
|
||||
rootPath: "/test",
|
||||
files: ["legacy.js"],
|
||||
directories: [],
|
||||
}
|
||||
const asts = new Map<string, FileAST>([
|
||||
[
|
||||
"legacy.js",
|
||||
{
|
||||
imports: [],
|
||||
exports: [],
|
||||
functions: [
|
||||
{
|
||||
name: "doSomething",
|
||||
lineStart: 1,
|
||||
lineEnd: 5,
|
||||
params: [
|
||||
{ name: "x", optional: false, hasDefault: false },
|
||||
{ name: "y", optional: false, hasDefault: false },
|
||||
],
|
||||
isAsync: false,
|
||||
isExported: true,
|
||||
},
|
||||
],
|
||||
classes: [],
|
||||
interfaces: [],
|
||||
typeAliases: [],
|
||||
parseError: false,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
const context = buildInitialContext(structure, asts)
|
||||
|
||||
expect(context).toContain("- doSomething(x, y)")
|
||||
})
|
||||
|
||||
it("should format interface with extends", () => {
|
||||
const structure: ProjectStructure = {
|
||||
name: "test",
|
||||
rootPath: "/test",
|
||||
files: ["types.ts"],
|
||||
directories: [],
|
||||
}
|
||||
const asts = new Map<string, FileAST>([
|
||||
[
|
||||
"types.ts",
|
||||
{
|
||||
imports: [],
|
||||
exports: [],
|
||||
functions: [],
|
||||
classes: [],
|
||||
interfaces: [
|
||||
{
|
||||
name: "AdminUser",
|
||||
lineStart: 1,
|
||||
lineEnd: 5,
|
||||
properties: [],
|
||||
extends: ["User", "Admin"],
|
||||
isExported: true,
|
||||
},
|
||||
],
|
||||
typeAliases: [],
|
||||
parseError: false,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
const context = buildInitialContext(structure, asts)
|
||||
|
||||
expect(context).toContain("- interface AdminUser extends User, Admin")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -15,6 +15,7 @@ describe("ContextConfigSchema", () => {
|
||||
maxContextUsage: 0.8,
|
||||
autoCompressAt: 0.8,
|
||||
compressionMethod: "llm-summary",
|
||||
includeSignatures: true,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -26,6 +27,7 @@ describe("ContextConfigSchema", () => {
|
||||
maxContextUsage: 0.8,
|
||||
autoCompressAt: 0.8,
|
||||
compressionMethod: "llm-summary",
|
||||
includeSignatures: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -162,6 +164,7 @@ describe("ContextConfigSchema", () => {
|
||||
maxContextUsage: 0.8,
|
||||
autoCompressAt: 0.8,
|
||||
compressionMethod: "llm-summary",
|
||||
includeSignatures: true,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -175,6 +178,7 @@ describe("ContextConfigSchema", () => {
|
||||
maxContextUsage: 0.8,
|
||||
autoCompressAt: 0.9,
|
||||
compressionMethod: "llm-summary",
|
||||
includeSignatures: true,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -189,6 +193,7 @@ describe("ContextConfigSchema", () => {
|
||||
maxContextUsage: 0.7,
|
||||
autoCompressAt: 0.8,
|
||||
compressionMethod: "truncate",
|
||||
includeSignatures: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -200,6 +205,7 @@ describe("ContextConfigSchema", () => {
|
||||
maxContextUsage: 0.9,
|
||||
autoCompressAt: 0.85,
|
||||
compressionMethod: "truncate" as const,
|
||||
includeSignatures: false,
|
||||
}
|
||||
|
||||
const result = ContextConfigSchema.parse(config)
|
||||
@@ -212,10 +218,36 @@ describe("ContextConfigSchema", () => {
|
||||
maxContextUsage: 0.8,
|
||||
autoCompressAt: 0.8,
|
||||
compressionMethod: "llm-summary" as const,
|
||||
includeSignatures: true,
|
||||
}
|
||||
|
||||
const result = ContextConfigSchema.parse(config)
|
||||
expect(result).toEqual(config)
|
||||
})
|
||||
})
|
||||
|
||||
describe("includeSignatures", () => {
|
||||
it("should accept true", () => {
|
||||
const result = ContextConfigSchema.parse({ includeSignatures: true })
|
||||
expect(result.includeSignatures).toBe(true)
|
||||
})
|
||||
|
||||
it("should accept false", () => {
|
||||
const result = ContextConfigSchema.parse({ includeSignatures: false })
|
||||
expect(result.includeSignatures).toBe(false)
|
||||
})
|
||||
|
||||
it("should default to true", () => {
|
||||
const result = ContextConfigSchema.parse({})
|
||||
expect(result.includeSignatures).toBe(true)
|
||||
})
|
||||
|
||||
it("should reject non-boolean", () => {
|
||||
expect(() => ContextConfigSchema.parse({ includeSignatures: "true" })).toThrow()
|
||||
})
|
||||
|
||||
it("should reject number", () => {
|
||||
expect(() => ContextConfigSchema.parse({ includeSignatures: 1 })).toThrow()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user