From 93c05d078473dac201c5865c36523c20beda0f6b Mon Sep 17 00:00:00 2001 From: Bobby Christopher Date: Fri, 3 Oct 2025 08:31:32 -0400 Subject: [PATCH 01/13] added new sig route 2 --- internal/bundler/prompts/code_generator.go | 334 +++++++++++++++++---- 1 file changed, 271 insertions(+), 63 deletions(-) diff --git a/internal/bundler/prompts/code_generator.go b/internal/bundler/prompts/code_generator.go index f54a630c..bf74b272 100644 --- a/internal/bundler/prompts/code_generator.go +++ b/internal/bundler/prompts/code_generator.go @@ -22,9 +22,11 @@ func NewCodeGenerator(prompts []Prompt) *CodeGenerator { // GenerateJavaScript generates the main JavaScript file func (cg *CodeGenerator) GenerateJavaScript() string { var objects []string + var signatures []string for _, prompt := range cg.prompts { objects = append(objects, cg.generatePromptObject(prompt)) + signatures = append(signatures, cg.generateSignatureFunction(prompt)) } return fmt.Sprintf(`// Generated prompts - do not edit manually @@ -38,28 +40,67 @@ import { interpolateTemplate } from '@agentuity/sdk'; */ export const prompts = { %s -};`, strings.Join(objects, "\n\n"), cg.generatePromptExports()) +}; + +/** + * Generated signature functions for each prompt + * These provide type-safe function signatures based on prompt structure + */ +export const signatures = { +%s +};`, strings.Join(objects, "\n\n"), cg.generatePromptExports(), strings.Join(signatures, ",\n")) } // GenerateTypeScriptTypes generates the TypeScript definitions file func (cg *CodeGenerator) GenerateTypeScriptTypes() string { var promptTypes []string + var signatureTypes []string for _, prompt := range cg.prompts { promptType := cg.generatePromptType(prompt) promptTypes = append(promptTypes, promptType) + signatureType := cg.generateSignatureType(prompt) + signatureTypes = append(signatureTypes, signatureType) } return fmt.Sprintf(`// Generated prompt types - do not edit manually -import { interpolateTemplate } from '@agentuity/sdk'; +import { interpolateTemplate, Prompt } from '@agentuity/sdk'; %s -export type PromptsCollection = { +export interface GeneratedPromptsCollection { +%s +} + +export type PromptsCollection = GeneratedPromptsCollection; + +export type SignaturesCollection = { %s }; -export const prompts: PromptsCollection = {} as any;`, strings.Join(promptTypes, "\n\n"), cg.generatePromptTypeExports()) +export const prompts: PromptsCollection = {} as any; +export const signatures: SignaturesCollection = {} as any;`, strings.Join(promptTypes, "\n\n"), cg.generatePromptTypeExports(), cg.generateSignatureTypeExports()) +} + +// GenerateStubsFile generates the stubs file with actual generated types +func (cg *CodeGenerator) GenerateStubsFile() string { + var promptTypes []string + for _, prompt := range cg.prompts { + promptTypes = append(promptTypes, cg.generatePromptType(prompt)) + } + promptCollection := cg.generatePromptTypeExports() + + return fmt.Sprintf(`// Generated prompt types - do not edit manually +import { interpolateTemplate, Prompt } from '@agentuity/sdk'; + +%s + +export interface GeneratedPromptsCollection { +%s +} + +export type PromptsCollection = GeneratedPromptsCollection; +`, strings.Join(promptTypes, "\n\n"), promptCollection) } // GenerateTypeScriptInterfaces generates the TypeScript interfaces file @@ -77,35 +118,132 @@ func (cg *CodeGenerator) GenerateTypeScriptInterfaces() string { // generatePromptObject generates a single prompt object with system and prompt properties func (cg *CodeGenerator) generatePromptObject(prompt Prompt) string { - // Get variables from system template - systemVariables := cg.getSystemVariables(prompt) - var systemParams []string - if len(systemVariables) > 0 { - systemParams = append(systemParams, "variables") + // Determine if prompt has system, prompt, and variables + hasSystem := prompt.System != "" + hasPrompt := prompt.Prompt != "" + hasSystemVars := len(cg.getSystemVariables(prompt)) > 0 + hasPromptVars := len(cg.getPromptVariables(prompt)) > 0 + hasVariables := hasSystemVars || hasPromptVars + + // Generate the prompt object with conditional fields + var fields []string + fields = append(fields, fmt.Sprintf("slug: %q", prompt.Slug)) + + if hasSystem { + fields = append(fields, cg.generateSystemField(prompt)) } - systemParamStr := strings.Join(systemParams, ", ") - // Get variables from prompt template - promptVariables := cg.getPromptVariables(prompt) - var promptParams []string - if len(promptVariables) > 0 { - promptParams = append(promptParams, "variables") + if hasPrompt { + fields = append(fields, cg.generatePromptField(prompt)) + } + + if hasVariables { + fields = append(fields, cg.generateVariablesField(prompt)) } - promptParamStr := strings.Join(promptParams, ", ") return fmt.Sprintf(`const %s = { - slug: %q, - system: { - compile: (%s) => { - return %s - } - }, - prompt: { - compile: (%s) => { - return %s - } - } -};`, strcase.ToLowerCamel(prompt.Slug), prompt.Slug, systemParamStr, cg.generateTemplateValue(prompt.System), promptParamStr, cg.generateTemplateValue(prompt.Prompt)) + %s +};`, strcase.ToLowerCamel(prompt.Slug), strings.Join(fields, ",\n ")) +} + +// generateSystemField generates the system field for a prompt +func (cg *CodeGenerator) generateSystemField(prompt Prompt) string { + systemVars := cg.getSystemVariables(prompt) + if len(systemVars) > 0 { + return fmt.Sprintf(`system: (params) => { + return interpolateTemplate(%q, params.system) + }`, prompt.System) + } + return fmt.Sprintf(`system: (params) => { + return %q + }`, prompt.System) +} + +// generatePromptField generates the prompt field for a prompt +func (cg *CodeGenerator) generatePromptField(prompt Prompt) string { + promptVars := cg.getPromptVariables(prompt) + if len(promptVars) > 0 { + return fmt.Sprintf(`prompt: (params) => { + return interpolateTemplate(%q, params.prompt) + }`, prompt.Prompt) + } + return fmt.Sprintf(`prompt: (params) => { + return %q + }`, prompt.Prompt) +} + +// generateVariablesField generates the variables field for a prompt +func (cg *CodeGenerator) generateVariablesField(prompt Prompt) string { + var allVars []Variable + systemVars := cg.getSystemVariableObjects(prompt) + promptVars := cg.getPromptVariableObjects(prompt) + + // Combine system and prompt variables + allVars = append(allVars, systemVars...) + allVars = append(allVars, promptVars...) + + if len(allVars) == 0 { + return "" + } + + var varDefs []string + for _, variable := range allVars { + varDefs = append(varDefs, fmt.Sprintf("%s: %q", variable.Name, "string")) + } + + return fmt.Sprintf("variables: { %s }", strings.Join(varDefs, ", ")) +} + +// generateSignatureFunction generates a signature function for a prompt +func (cg *CodeGenerator) generateSignatureFunction(prompt Prompt) string { + hasSystem := prompt.System != "" + hasPrompt := prompt.Prompt != "" + + // Generate the function signature based on what the prompt has + var params []string + if hasSystem { + params = append(params, "system: Record") + } + if hasPrompt { + params = append(params, "prompt: Record") + } + + paramStr := strings.Join(params, ", ") + if paramStr != "" { + paramStr = fmt.Sprintf("params: { %s }", paramStr) + } else { + paramStr = "" + } + + // Generate the function body + var bodyParts []string + if hasSystem { + bodyParts = append(bodyParts, fmt.Sprintf("const systemResult = prompts['%s'].system(params)", prompt.Slug)) + } + if hasPrompt { + bodyParts = append(bodyParts, fmt.Sprintf("const promptResult = prompts['%s'].prompt(params)", prompt.Slug)) + } + + // Combine results + if len(bodyParts) == 1 { + bodyParts = append(bodyParts, "return systemResult || promptResult") + } else if len(bodyParts) == 2 { + bodyParts = append(bodyParts, "return `${systemResult}\\n${promptResult}`") + } else { + bodyParts = append(bodyParts, "return ''") + } + + body := strings.Join(bodyParts, ";\n ") + + if paramStr == "" { + return fmt.Sprintf(`%s: () => { + %s +}`, prompt.Slug, body) + } + + return fmt.Sprintf(`%s: (%s) => { + %s +}`, prompt.Slug, paramStr, body) } // generateTemplateValue generates the value for a template (either compile function or direct interpolateTemplate call) @@ -117,48 +255,89 @@ func (cg *CodeGenerator) generateTemplateValue(template string) string { return fmt.Sprintf("interpolateTemplate(%q, variables)", template) } -// generatePromptType generates a TypeScript type for a prompt object +// generatePromptType generates a TypeScript type for a prompt object using generics func (cg *CodeGenerator) generatePromptType(prompt Prompt) string { - // Get variables from system template - systemVariables := cg.getSystemVariableObjects(prompt) - var systemParams []string - if len(systemVariables) > 0 { - systemParams = append(systemParams, fmt.Sprintf("variables?: { %s }", cg.generateVariableTypesFromObjects(systemVariables))) + hasSystem := prompt.System != "" + hasPrompt := prompt.Prompt != "" + hasSystemVars := len(cg.getSystemVariables(prompt)) > 0 + hasPromptVars := len(cg.getPromptVariables(prompt)) > 0 + hasVariables := hasSystemVars || hasPromptVars + + // Generate the generic type parameters + var genericParams []string + if hasSystem { + genericParams = append(genericParams, "true") + } else { + genericParams = append(genericParams, "false") } - systemParamStr := strings.Join(systemParams, ", ") - - // Get variables from prompt template - promptVariables := cg.getPromptVariableObjects(prompt) - var promptParams []string - if len(promptVariables) > 0 { - promptParams = append(promptParams, fmt.Sprintf("variables?: { %s }", cg.generateVariableTypesFromObjects(promptVariables))) + if hasPrompt { + genericParams = append(genericParams, "true") + } else { + genericParams = append(genericParams, "false") + } + if hasVariables { + genericParams = append(genericParams, "true") + } else { + genericParams = append(genericParams, "false") } - promptParamStr := strings.Join(promptParams, ", ") - // Generate separate system and prompt types with docstrings - systemTypeName := fmt.Sprintf("%sSystem", strcase.ToCamel(prompt.Slug)) - promptTypeName := fmt.Sprintf("%sPrompt", strcase.ToCamel(prompt.Slug)) + genericStr := strings.Join(genericParams, ", ") mainTypeName := strcase.ToCamel(prompt.Slug) - systemTypeWithDocstring := cg.generateTypeWithDocstring(prompt.System, systemTypeName, systemParamStr, mainTypeName) - promptTypeWithDocstring := cg.generateTypeWithDocstring(prompt.Prompt, promptTypeName, promptParamStr, mainTypeName) + // Generate the type definition + var fields []string + fields = append(fields, "slug: string") - return fmt.Sprintf(`%s + if hasSystem { + fields = append(fields, "system: (params: { system: Record }) => string") + } -%s + if hasPrompt { + fields = append(fields, "prompt: (params: { prompt: Record }) => string") + } -export type %s = { - slug: string; - /** -%s - */ - system: %s; - /** -%s - */ - prompt: %s; -};`, - systemTypeWithDocstring, promptTypeWithDocstring, mainTypeName, cg.generateTemplateDocstring(prompt.System), systemTypeName, cg.generateTemplateDocstring(prompt.Prompt), promptTypeName) + if hasVariables { + // Generate variable types + var allVars []Variable + allVars = append(allVars, cg.getSystemVariableObjects(prompt)...) + allVars = append(allVars, cg.getPromptVariableObjects(prompt)...) + + var varDefs []string + for _, variable := range allVars { + varDefs = append(varDefs, fmt.Sprintf("%s: string", variable.Name)) + } + fields = append(fields, fmt.Sprintf("variables: { %s }", strings.Join(varDefs, ", "))) + } + + return fmt.Sprintf(`export type %s = Prompt<%s>;`, mainTypeName, genericStr) +} + +// generateSignatureType generates a TypeScript type for a signature function +func (cg *CodeGenerator) generateSignatureType(prompt Prompt) string { + hasSystem := prompt.System != "" + hasPrompt := prompt.Prompt != "" + + // Generate the function signature based on what the prompt has + var params []string + if hasSystem { + params = append(params, "system: Record") + } + if hasPrompt { + params = append(params, "prompt: Record") + } + + paramStr := strings.Join(params, ", ") + if paramStr != "" { + paramStr = fmt.Sprintf("params: { %s }", paramStr) + } else { + paramStr = "" + } + + if paramStr == "" { + return fmt.Sprintf(`%s: () => string`, prompt.Slug) + } + + return fmt.Sprintf(`%s: (%s) => string`, prompt.Slug, paramStr) } // generatePromptInterface generates a TypeScript interface for a prompt @@ -270,7 +449,7 @@ func (cg *CodeGenerator) generatePromptExports() string { // Generate JSDoc comment for each prompt property jsdocComment := cg.generatePromptPropertyJSDoc(prompt) exports = append(exports, jsdocComment) - exports = append(exports, fmt.Sprintf(" %s,", strcase.ToLowerCamel(prompt.Slug))) + exports = append(exports, fmt.Sprintf(" '%s',", prompt.Slug)) } return strings.Join(exports, "\n") } @@ -282,11 +461,40 @@ func (cg *CodeGenerator) generatePromptTypeExports() string { // Generate JSDoc comment for each prompt property jsdocComment := cg.generatePromptPropertyJSDoc(prompt) exports = append(exports, jsdocComment) - exports = append(exports, fmt.Sprintf(" %s: %s;", strcase.ToLowerCamel(prompt.Slug), strcase.ToCamel(prompt.Slug))) + exports = append(exports, fmt.Sprintf(" '%s': %s;", prompt.Slug, strcase.ToCamel(prompt.Slug))) } return strings.Join(exports, "\n") } +// generateSignatureTypeExports generates the exports object for signature types +func (cg *CodeGenerator) generateSignatureTypeExports() string { + var exports []string + for _, prompt := range cg.prompts { + exports = append(exports, fmt.Sprintf(" '%s': (%s) => string", prompt.Slug, cg.generateSignatureFunctionParams(prompt))) + } + return strings.Join(exports, "\n") +} + +// generateSignatureFunctionParams generates the parameter string for signature functions +func (cg *CodeGenerator) generateSignatureFunctionParams(prompt Prompt) string { + hasSystem := prompt.System != "" + hasPrompt := prompt.Prompt != "" + + var params []string + if hasSystem { + params = append(params, "system: Record") + } + if hasPrompt { + params = append(params, "prompt: Record") + } + + if len(params) == 0 { + return "" + } + + return fmt.Sprintf("params: { %s }", strings.Join(params, ", ")) +} + // generatePromptPropertyJSDoc generates JSDoc comments for prompt properties in PromptsCollection func (cg *CodeGenerator) generatePromptPropertyJSDoc(prompt Prompt) string { var docLines []string From 19afb9620096236f12d2669ee8bb117084e56fa3 Mon Sep 17 00:00:00 2001 From: Bobby Christopher Date: Fri, 3 Oct 2025 09:00:27 -0400 Subject: [PATCH 02/13] ts seems good onto js --- .cursor/rules/code-generation.mdc | 253 ++++++++++++++++++++- CODING_AGENT_PROMPT.md | 187 +++++++++++++++ TASK_COMPLETION_PLAN.md | 145 ++++++++++++ internal/bundler/prompts/code_generator.go | 108 ++++++++- 4 files changed, 682 insertions(+), 11 deletions(-) create mode 100644 CODING_AGENT_PROMPT.md create mode 100644 TASK_COMPLETION_PLAN.md diff --git a/.cursor/rules/code-generation.mdc b/.cursor/rules/code-generation.mdc index 8e3f886d..d9c42411 100644 --- a/.cursor/rules/code-generation.mdc +++ b/.cursor/rules/code-generation.mdc @@ -1,12 +1,12 @@ --- -description: Code Generation Development Rules for CLI -globs: internal/bundler/*.go, cmd/*.go +description: Code Generation Development Rules for CLI and SDK +globs: internal/bundler/*.go, cmd/*.go, src/apis/**/*.ts, src/server/*.ts, src/types.ts alwaysApply: true --- # Code Generation Development Rules -> **⚠️ IMPORTANT**: These rules work in conjunction with the SDK rules. When updating these CLI rules, also update `sdk-js/.cursor/rules/code-generation.mdc` to keep them in sync. +> **⚠️ IMPORTANT**: These rules apply to both CLI and SDK development. Keep both codebases in sync when making changes. ## Core Principles @@ -27,6 +27,43 @@ alwaysApply: true - ✅ Empty fields should return empty strings, not undefined - ❌ Never generate partial objects that require optional chaining +## Architecture Overview + +### File Structure +``` +sdk-js/src/apis/prompt/ +├── generic_types.ts # Simple utility types for CLI to use +├── generated/ +│ ├── index.d.ts # Shell TypeScript definitions (replaced by CLI) +│ ├── index.js # Shell JavaScript (replaced by CLI) +│ └── _index.js # Actual generated JavaScript (created by CLI) +├── index.ts # Main API with dynamic loading +└── signature.ts # Signature function factory +``` + +### Key Learnings from Implementation + +#### 1. Simplified Architecture +- **Use shell files**: `index.d.ts` and `index.js` are placeholders that get completely replaced +- **Less complex generics**: Avoid overly complex TypeScript generics that cause compilation issues +- **Rely on code generation**: Let the CLI do the heavy lifting instead of complex type manipulation + +#### 2. Slug-Based Naming +- **Use slugs directly**: Generate code using the original slug names (e.g., `'simple-helper'`) +- **Quote property names**: Use `'slug-name'` syntax in TypeScript interfaces +- **Bracket notation**: Use `prompts['slug-name']` in JavaScript for property access +- **CamelCase variables**: Use `strcase.ToLowerCamel(slug)` for JavaScript variable names + +#### 3. Type Safety Without Complexity +```typescript +// ✅ Simple, working approach +export type PromptsCollection = Record; +export type PromptSignature = (params: any) => string; + +// ❌ Avoid overly complex generics that cause compilation issues +export type ComplexGeneric> = ... +``` + ## CLI-Specific Rules ### Generation Target Locations @@ -38,10 +75,20 @@ sdkPath := filepath.Join(root, "node_modules", "@agentuity", "sdk", "dist", "gen sdkPath := filepath.Join(root, "src", "generated") ``` -### Path Resolution Priority -1. Try `dist/` directory first (production) -2. Fallback to `src/` directory (development) -3. Always check if SDK exists before generating +### Slug Handling in Code Generation +```go +// ✅ Correct: Use slugs directly with proper quoting +const %s = { + slug: "%s", + // ... fields +};`, strcase.ToLowerCamel(prompt.Slug), prompt.Slug + +// In TypeScript interfaces +exports = append(exports, fmt.Sprintf(" '%s': %s;", prompt.Slug, strcase.ToCamel(prompt.Slug))) + +// In JavaScript property access +bodyParts = append(bodyParts, fmt.Sprintf("const result = prompts['%s'].system(params)", prompt.Slug)) +``` ### File Generation Pattern ```go @@ -71,6 +118,104 @@ func FindSDKGeneratedDir(ctx BundleContext, projectDir string) (string, error) { } ``` +## SDK-Specific Rules + +### Path Resolution +- Use absolute paths only (relative paths don't work in bundled environments) +- Check `dist/` directory first, then `src/` directory +- Always provide fallbacks for missing generated content + +### Dynamic Loading Pattern +Generated content doesn't exist at SDK build time, so use dynamic loading patterns: + +```typescript +// ✅ Good: Dynamic loading with fallbacks +public async loadGeneratedContent(): Promise { + try { + const path = require('path'); + const fs = require('fs'); + + const possiblePaths = [ + path.join(process.cwd(), 'node_modules', '@agentuity', 'sdk', 'dist', 'generated', 'content.js'), + path.join(process.cwd(), 'node_modules', '@agentuity', 'sdk', 'src', 'generated', 'content.js') + ]; + + for (const possiblePath of possiblePaths) { + if (fs.existsSync(possiblePath)) { + const generatedModule = require(possiblePath); + this.content = generatedModule.content || defaultContent; + break; + } + } + } catch (error) { + this.content = defaultContent; + console.warn('No generated content found'); + } +} +``` + +### Context Integration +```typescript +// ✅ Good: Load generated content in context creation +export async function createServerContext(req: ServerContextRequest): Promise { + // ... other initialization + + // Load generated content dynamically + await promptAPI.loadPrompts(); + + return { + // ... other context properties + prompts: { + getPrompt: (slug: string) => promptAPI.prompts[slug], + compile: (slug: string, params: any) => { + // Use signature functions or fallback to manual compilation + const signature = promptAPI.signatures[slug]; + if (signature) { + return signature(params); + } + // Fallback logic... + } + }, + }; +} +``` + +### Type Safety +- Generate TypeScript definitions for generated content +- Use proper type annotations for dynamic imports +- Maintain type safety throughout the loading process +- Keep types simple to avoid compilation issues + +## Common Patterns + +### Generated Content API Class +```typescript +export default class GeneratedContentAPI { + public content: typeof defaultContent; + + constructor() { + this.content = defaultContent; + } + + public async loadContent(): Promise { + // Dynamic loading logic here + } +} +``` + +### Error Handling +```typescript +try { + // Try to load generated content + const generatedModule = require(possiblePath); + this.content = generatedModule.content || defaultContent; +} catch (error) { + // Fallback to default content + this.content = defaultContent; + console.warn('No generated content found'); +} +``` + ## Common Pitfalls to Avoid ### ❌ Don't Do This @@ -83,6 +228,25 @@ sdkPath := filepath.Join(root, "src", "generated") // Not checking if SDK exists os.WriteFile(path, content, 0644) // Without checking if path exists + +// Using unquoted slugs in TypeScript +optional-with-defaults: OptionalWithDefaults; // ❌ Invalid syntax +``` + +```typescript +// Hardcoding generated content in source files +export const prompts = { + copyWriter: { /* hardcoded content */ } +}; + +// Using relative imports that don't work in bundles +const generatedModule = require('./generated/_index.js'); + +// Not providing fallbacks +const generatedModule = require(possiblePath); // Will crash if file doesn't exist + +// Overly complex generics that cause compilation issues +export type ComplexGeneric> = ... ``` ### ✅ Do This Instead @@ -97,6 +261,79 @@ sdkPath := filepath.Join(root, "node_modules", "@agentuity", "sdk", "dist", "gen if err := os.MkdirAll(sdkPath, 0755); err != nil { return fmt.Errorf("failed to create directory: %w", err) } + +// Use quoted slugs in TypeScript +'optional-with-defaults': OptionalWithDefaults; // ✅ Valid syntax +``` + +```typescript +// Dynamic loading with absolute paths +const possiblePaths = [ + path.join(process.cwd(), 'node_modules', '@agentuity', 'sdk', 'dist', 'generated', '_index.js') +]; + +// With proper error handling +try { + const generatedModule = require(possiblePath); + this.content = generatedModule.content || defaultContent; +} catch (error) { + this.content = defaultContent; +} + +// Simple, working types +export type PromptsCollection = Record; +export type PromptSignature = (params: any) => string; ``` -Remember: The CLI's job is to generate content into the installed SDK, not modify source files. \ No newline at end of file +## Build Considerations + +- Generated content is loaded at runtime, not build time +- Use `require()` for CommonJS compatibility in bundled environments +- Avoid `import()` statements for generated content +- Ensure fallbacks work when generated content is missing +- Keep TypeScript types simple to avoid compilation issues + +## Development Workflow + +### When Making Changes to Generated Content Structure: +1. **Update CLI code generation** in `cli/internal/bundler/prompts/code_generator.go` +2. **Update SDK to handle new structure** in `src/apis/prompt/index.ts` +3. **Bump SDK version** in `package.json` +4. **Build SDK**: `npm run build` +5. **Run bundle command**: `go run . bundle` to regenerate content +6. **Copy dist to node_modules**: Use `./build-and-copy.sh ` or `cp -r dist/* node_modules/@agentuity/sdk/dist/` (if needed) + +**Note**: After the first setup, you only need to run step 6 for subsequent changes to avoid reinstalling the entire package. + +### Quick SDK Update Workflow: +For faster iteration when testing SDK changes: +```bash +# In sdk-js directory +npm run build +./build-and-copy.sh /path/to/test-project/node_modules/@agentuity/sdk/dist +``` + +This copies the built SDK directly to your test project without reinstalling the package. + +### Required Exports +- ✅ Always export `interpolateTemplate` from main SDK index +- ✅ Generated content must import from `@agentuity/sdk` (not relative paths) +- ✅ Ensure all dependencies are properly exported + +## Key Implementation Notes + +### What We Learned +1. **Shell-based approach works better** than complex type manipulation +2. **Slug-based naming** is more intuitive than camelCase conversion +3. **Simple types** are more maintainable than complex generics +4. **Complete file replacement** is cleaner than partial updates +5. **Proper quoting** is essential for TypeScript interfaces with hyphens + +### Best Practices +- Use slugs directly in generated code with proper quoting +- Keep generic types simple and focused +- Rely on code generation for complex type structures +- Provide clear fallbacks for missing generated content +- Test the full pipeline regularly + +Remember: The CLI's job is to generate content into the installed SDK, and the SDK's job is to load generated content dynamically, not contain hardcoded generated content. \ No newline at end of file diff --git a/CODING_AGENT_PROMPT.md b/CODING_AGENT_PROMPT.md new file mode 100644 index 00000000..46c4de8b --- /dev/null +++ b/CODING_AGENT_PROMPT.md @@ -0,0 +1,187 @@ +# Coding Agent Prompt: Type-Safe Prompt Compilation + +## Context + +You are working on a code generation system where a CLI (Go) generates TypeScript/JavaScript code that gets consumed by an SDK (TypeScript). The system generates prompt templates with variable interpolation, and we need to make the `compile()` function type-safe. + +## Current Problem + +The `ctx.prompts.compile()` function currently accepts `any` parameters, but it should provide proper TypeScript intellisense based on the actual prompt template requirements. + +## What's Already Working + +1. **CLI generates code correctly** - `internal/bundler/prompts/code_generator.go` +2. **SDK loads generated content** - `src/apis/prompt/index.ts` +3. **Basic compilation works** - `ctx.prompts.compile('slug', params)` +4. **Slug-based naming** - Uses `'simple-helper'` instead of camelCase + +## What Needs to be Fixed + +The `compile()` function should have conditional TypeScript signatures based on prompt template analysis: + +### Example Requirements + +Given these prompt templates from `test-prompt-compile/src/prompts.yaml`: + +```yaml +# Required variables only +- slug: required-variables-only + system: | + You are a {!role} assistant. + Your specialization is {!specialization}. + prompt: | + Complete the {!task} with {!quality} quality. + +# Optional variables with defaults +- slug: optional-with-defaults + system: | + You are a {role:helpful assistant} specializing in {domain:general topics}. + Your experience level is {experience:intermediate} + prompt: | + Help the user with: {task:their question} + Use a {tone:friendly} approach. + +# No variables +- slug: simple-helper + system: | + You are a helpful assistant that provides clear and concise answers. + prompt: | + Please help the user with their question. +``` + +### Expected TypeScript Behavior + +```typescript +// For 'required-variables-only' - should require system and prompt with specific fields +ctx.prompts.compile('required-variables-only', { + system: { role: string, specialization: string }, + prompt: { task: string, quality: string } +}); + +// For 'optional-with-defaults' - should make system and prompt optional with optional fields +ctx.prompts.compile('optional-with-defaults', { + system?: { role?: string, domain?: string, experience?: string }, + prompt?: { task?: string, tone?: string } +}); + +// For 'simple-helper' - should require no parameters +ctx.prompts.compile('simple-helper'); +``` + +## Implementation Approach + +### 1. Template Analysis (CLI) + +Extend `internal/bundler/prompts/code_generator.go` to: + +1. **Parse variable syntax**: + - `{!variable}` = required variable + - `{variable:default}` = optional variable with default + - `{variable}` = optional variable without default + +2. **Analyze each prompt**: + - Extract system variables and their requirements + - Extract prompt variables and their requirements + - Determine if system/prompt parameters should be included + +3. **Generate TypeScript interfaces**: + - Create parameter interfaces for each prompt + - Generate conditional compile signature types + +### 2. Type Generation (CLI) + +Generate TypeScript code that creates: + +```typescript +// Parameter interfaces for each prompt +export interface RequiredVariablesOnlyParams { + system: { + role: string; + specialization: string; + }; + prompt: { + task: string; + quality: string; + }; +} + +export interface OptionalWithDefaultsParams { + system?: { + role?: string; + domain?: string; + experience?: string; + }; + prompt?: { + task?: string; + tone?: string; + }; +} + +// Conditional compile signature +export type CompileParams = + T extends 'required-variables-only' ? RequiredVariablesOnlyParams : + T extends 'optional-with-defaults' ? OptionalWithDefaultsParams : + T extends 'simple-helper' ? never : + any; + +// Updated compile function signature +compile( + slug: T, + params: CompileParams +): string; +``` + +### 3. SDK Integration (SDK) + +Update `src/types.ts` and `src/server/server.ts` to: + +1. **Use generated types** for compile function signature +2. **Implement runtime validation** for required parameters +3. **Maintain backward compatibility** + +## Key Files to Focus On + +### CLI Files: +- `internal/bundler/prompts/code_generator.go` - Main logic for template analysis and type generation +- `internal/bundler/prompts/prompts.go` - Orchestration + +### SDK Files: +- `src/types.ts` - Update AgentContext compile signature +- `src/server/server.ts` - Implement type-safe compile function + +### Test Files: +- `test-prompt-compile/src/agents/my-agent/index.ts` - Current test cases + +## Reference Implementation + +Look at how `getPrompt()` currently works for inspiration on conditional types. The `compile()` function should follow a similar pattern but with more complex parameter analysis. + +## Success Criteria + +1. **TypeScript intellisense** shows correct parameters for each prompt slug +2. **Required parameters** are enforced at compile time +3. **Optional parameters** are properly marked as optional +4. **No parameters** prompts work with empty object +5. **Runtime validation** throws clear errors for missing required parameters +6. **Backward compatibility** maintained for existing code + +## Testing + +Use the existing test project at `test-prompt-compile/` to verify your changes work correctly. The test cases in `src/agents/my-agent/index.ts` should provide good examples of expected behavior. + +## Important Notes + +- **Don't break existing functionality** - maintain backward compatibility +- **Use the existing patterns** - follow the shell-based approach already implemented +- **Test incrementally** - verify each change works before moving to the next +- **Keep it simple** - avoid overly complex generics that cause compilation issues + +## Getting Started + +1. Start by analyzing the template parsing logic in `code_generator.go` +2. Add functions to extract variable requirements from system/prompt templates +3. Generate the appropriate TypeScript interfaces +4. Update the SDK to use the generated types +5. Test with the existing test project + +The goal is to make `ctx.prompts.compile()` as type-safe and user-friendly as possible while maintaining the existing architecture and patterns. diff --git a/TASK_COMPLETION_PLAN.md b/TASK_COMPLETION_PLAN.md new file mode 100644 index 00000000..69939b8d --- /dev/null +++ b/TASK_COMPLETION_PLAN.md @@ -0,0 +1,145 @@ +# Task Completion Plan: Type-Safe Prompt Compilation + +## Current State Analysis + +### What's Working ✅ +- CLI generates JavaScript and TypeScript files correctly +- SDK loads generated content dynamically +- Basic prompt compilation works with `ctx.prompts.compile()` +- Slug-based naming is implemented (`'simple-helper'` instead of camelCase) + +### What Needs Fixing ❌ +- **Type Safety Issue**: The `compile()` function doesn't provide proper TypeScript feedback +- **Parameter Structure**: Should conditionally include `system` and `prompt` parameters based on template content +- **Variable Requirements**: Should distinguish between required, optional, and no variables + +## Required Changes + +### 1. Type-Safe Compile Function Signature + +The `compile()` function should have conditional parameters based on prompt template analysis: + +```typescript +// Current (incorrect): +compile(slug: string, params: any): string + +// Should be (conditional based on template): +compile( + slug: T, + params: CompileParams +): string + +// Where CompileParams is conditionally typed: +type CompileParams = + T extends 'required-variables-only' + ? { system: { role: string; specialization: string }; prompt: { task: string; quality: string } } + : T extends 'optional-with-defaults' + ? { system?: { role?: string; domain?: string; experience?: string }; prompt?: { task?: string; tone?: string } } + : T extends 'simple-helper' + ? never // No parameters needed + : any; // Fallback +``` + +### 2. Template Analysis Requirements + +Need to analyze each prompt template to determine: +- **System variables**: Required (`{!var}`), Optional (`{var:default}`), or None +- **Prompt variables**: Required (`{!var}`), Optional (`{var:default}`), or None +- **Parameter structure**: Include `system`/`prompt` only if they have variables + +### 3. Generated Type Structure + +The CLI should generate TypeScript types that reflect the actual template requirements: + +```typescript +// For 'required-variables-only': +export interface RequiredVariablesOnlyParams { + system: { + role: string; + specialization: string; + }; + prompt: { + task: string; + quality: string; + }; +} + +// For 'optional-with-defaults': +export interface OptionalWithDefaultsParams { + system?: { + role?: string; + domain?: string; + experience?: string; + }; + prompt?: { + task?: string; + tone?: string; + }; +} + +// For 'simple-helper': +// No parameters interface needed (no parameters at all) +``` + +## Implementation Plan + +### Phase 1: Template Analysis (CLI) +1. **Extend `code_generator.go`** to analyze prompt templates +2. **Parse variable syntax**: + - `{!variable}` = required + - `{variable:default}` = optional with default + - `{variable}` = optional without default +3. **Generate parameter interfaces** for each prompt +4. **Create conditional compile signature** types + +### Phase 2: Type Generation (CLI) +1. **Generate TypeScript interfaces** for each prompt's parameters +2. **Create union types** for compile function parameters +3. **Update generated `index.d.ts`** with proper type definitions +4. **Ensure backward compatibility** with existing code + +### Phase 3: SDK Integration (SDK) +1. **Update `AgentContext`** to use generated types +2. **Implement type-safe compile function** in server context +3. **Add proper error handling** for missing required parameters +4. **Test with existing agent code** + +### Phase 4: Testing & Validation +1. **Test all prompt scenarios** from `prompts.yaml` +2. **Verify TypeScript intellisense** works correctly +3. **Ensure runtime behavior** matches type expectations +4. **Update documentation** with new patterns + +## Files to Modify + +### CLI Files: +- `internal/bundler/prompts/code_generator.go` - Template analysis & type generation +- `internal/bundler/prompts/prompts.go` - Orchestration updates + +### SDK Files: +- `src/types.ts` - Update AgentContext compile signature +- `src/server/server.ts` - Implement type-safe compile function +- `src/apis/prompt/index.ts` - Export generated types + +### Test Files: +- `test-prompt-compile/src/agents/my-agent/index.ts` - Update test cases + +## Success Criteria + +1. **Type Safety**: `ctx.prompts.compile()` provides accurate TypeScript intellisense +2. **Conditional Parameters**: Only required parameters are included in function signature +3. **Runtime Validation**: Missing required parameters throw clear errors +4. **Backward Compatibility**: Existing code continues to work +5. **Performance**: No significant impact on compilation or runtime performance + +## Reference Implementation + +Look at the current working `getPrompt()` function for inspiration on how conditional types should work. The `compile()` function should follow a similar pattern but with more complex parameter analysis. + +## Next Steps + +1. Start with Phase 1 (Template Analysis) +2. Test each phase incrementally +3. Maintain backward compatibility throughout +4. Update documentation as you go +5. Test with the existing `test-prompt-compile` project diff --git a/internal/bundler/prompts/code_generator.go b/internal/bundler/prompts/code_generator.go index bf74b272..96d7ac56 100644 --- a/internal/bundler/prompts/code_generator.go +++ b/internal/bundler/prompts/code_generator.go @@ -57,7 +57,7 @@ func (cg *CodeGenerator) GenerateTypeScriptTypes() string { var signatureTypes []string for _, prompt := range cg.prompts { - promptType := cg.generatePromptType(prompt) + promptType := cg.generateDetailedPromptType(prompt) promptTypes = append(promptTypes, promptType) signatureType := cg.generateSignatureType(prompt) signatureTypes = append(signatureTypes, signatureType) @@ -70,6 +70,23 @@ import { interpolateTemplate, Prompt } from '@agentuity/sdk'; export interface GeneratedPromptsCollection { %s + compile: ( + name: T, + ...args: T extends 'simple-helper' + ? [] // No parameters needed + : T extends keyof { [K in keyof GeneratedPromptsCollection as GeneratedPromptsCollection[K] extends { system: () => string; prompt: () => string } ? K : never]: any } + ? [] // No parameters needed + : [GeneratedPromptsCollection[T] extends { system: (variables: infer S) => string; prompt: (variables: infer P) => string } + ? { system: S; prompt: P } + : GeneratedPromptsCollection[T] extends { system: (variables: infer S) => string } + ? { system: S } + : GeneratedPromptsCollection[T] extends { prompt: (variables: infer P) => string } + ? { prompt: P } + : never] + ) => string; + getPrompt: ( + name: T + ) => GeneratedPromptsCollection[T]; } export type PromptsCollection = GeneratedPromptsCollection; @@ -312,6 +329,91 @@ func (cg *CodeGenerator) generatePromptType(prompt Prompt) string { return fmt.Sprintf(`export type %s = Prompt<%s>;`, mainTypeName, genericStr) } +// generateDetailedPromptType generates detailed TypeScript types with specific parameter interfaces +func (cg *CodeGenerator) generateDetailedPromptType(prompt Prompt) string { + hasSystem := prompt.System != "" + hasPrompt := prompt.Prompt != "" + hasSystemVars := len(cg.getSystemVariables(prompt)) > 0 + hasPromptVars := len(cg.getPromptVariables(prompt)) > 0 + + mainTypeName := strcase.ToCamel(prompt.Slug) + + // Generate parameter interfaces + var systemParams string + var promptParams string + + if hasSystemVars { + systemVars := cg.getSystemVariableObjects(prompt) + systemParams = cg.generateParameterInterface(systemVars, true) + } + + if hasPromptVars { + promptVars := cg.getPromptVariableObjects(prompt) + promptParams = cg.generateParameterInterface(promptVars, true) + } + + // Generate the main prompt type + var fields []string + fields = append(fields, "slug: string") + + if hasSystem { + if hasSystemVars { + fields = append(fields, fmt.Sprintf("system: (variables: %s) => string", systemParams)) + } else { + fields = append(fields, "system: () => string") + } + } + + if hasPrompt { + if hasPromptVars { + fields = append(fields, fmt.Sprintf("prompt: (variables: %s) => string", promptParams)) + } else { + fields = append(fields, "prompt: () => string") + } + } + + fieldsStr := strings.Join(fields, ";\n ") + + // Generate parameter interface for compile function + var compileParams string + if hasSystemVars || hasPromptVars { + var paramFields []string + if hasSystemVars { + paramFields = append(paramFields, fmt.Sprintf("system: %s", systemParams)) + } + if hasPromptVars { + paramFields = append(paramFields, fmt.Sprintf("prompt: %s", promptParams)) + } + compileParams = fmt.Sprintf("{\n %s\n }", strings.Join(paramFields, ";\n ")) + } else { + compileParams = "never" + } + + return fmt.Sprintf(`export type %s = { + %s +}; + +export type %sParams = %s;`, mainTypeName, fieldsStr, mainTypeName, compileParams) +} + +// generateParameterInterface generates a TypeScript interface for variables +func (cg *CodeGenerator) generateParameterInterface(variables []Variable, isOptional bool) string { + if len(variables) == 0 { + return "{}" + } + + var fields []string + for _, variable := range variables { + if variable.IsRequired { + fields = append(fields, fmt.Sprintf("%s: string", variable.Name)) + } else { + fields = append(fields, fmt.Sprintf("%s?: string", variable.Name)) + } + } + + return fmt.Sprintf("{\n %s\n }", strings.Join(fields, ";\n ")) +} + // generateSignatureType generates a TypeScript type for a signature function func (cg *CodeGenerator) generateSignatureType(prompt Prompt) string { hasSystem := prompt.System != "" @@ -604,7 +706,7 @@ func (cg *CodeGenerator) getPromptVariables(prompt Prompt) []string { return promptTemplate.VariableNames() } -// getSystemVariableObjects gets variable objects from the system template only +// getSystemVariableObjects gets Variable objects from the system template func (cg *CodeGenerator) getSystemVariableObjects(prompt Prompt) []Variable { // Parse system template if not already parsed systemTemplate := prompt.SystemTemplate @@ -615,7 +717,7 @@ func (cg *CodeGenerator) getSystemVariableObjects(prompt Prompt) []Variable { return systemTemplate.Variables } -// getPromptVariableObjects gets variable objects from the prompt template only +// getPromptVariableObjects gets Variable objects from the prompt template func (cg *CodeGenerator) getPromptVariableObjects(prompt Prompt) []Variable { // Parse prompt template if not already parsed promptTemplate := prompt.PromptTemplate From c4f128d800a75266d9ced65858907c0b419fd3f9 Mon Sep 17 00:00:00 2001 From: Bobby Christopher Date: Fri, 3 Oct 2025 10:07:28 -0400 Subject: [PATCH 03/13] js seems good now --- internal/bundler/prompts/code_generator.go | 94 +++++++++++++++------- 1 file changed, 66 insertions(+), 28 deletions(-) diff --git a/internal/bundler/prompts/code_generator.go b/internal/bundler/prompts/code_generator.go index 96d7ac56..a6f120dc 100644 --- a/internal/bundler/prompts/code_generator.go +++ b/internal/bundler/prompts/code_generator.go @@ -30,7 +30,7 @@ func (cg *CodeGenerator) GenerateJavaScript() string { } return fmt.Sprintf(`// Generated prompts - do not edit manually -import { interpolateTemplate } from '@agentuity/sdk'; +import { interpolateTemplate } from '../../../index.js'; %s @@ -39,7 +39,14 @@ import { interpolateTemplate } from '@agentuity/sdk'; * Each prompt includes original system and prompt templates for reference */ export const prompts = { -%s +%s, + compile: (name, ...args) => { + const signature = signatures[name]; + if (signature) { + return signature(...args); + } + throw new Error('No signature function found for prompt: ' + name); + } }; /** @@ -48,6 +55,17 @@ export const prompts = { */ export const signatures = { %s +}; + +/** + * Compile function that provides type-safe compilation of prompts + */ +export const compile = (name, ...args) => { + const signature = signatures[name]; + if (signature) { + return signature(...args); + } + throw new Error('No signature function found for prompt: ' + name); };`, strings.Join(objects, "\n\n"), cg.generatePromptExports(), strings.Join(signatures, ",\n")) } @@ -165,27 +183,39 @@ func (cg *CodeGenerator) generatePromptObject(prompt Prompt) string { // generateSystemField generates the system field for a prompt func (cg *CodeGenerator) generateSystemField(prompt Prompt) string { - systemVars := cg.getSystemVariables(prompt) + systemVars := cg.getSystemVariableObjects(prompt) if len(systemVars) > 0 { - return fmt.Sprintf(`system: (params) => { - return interpolateTemplate(%q, params.system) - }`, prompt.System) + // Generate parameter destructuring for variables + var paramNames []string + for _, variable := range systemVars { + paramNames = append(paramNames, variable.Name) + } + paramStr := strings.Join(paramNames, ", ") + return fmt.Sprintf(`system: ({ %s }) => { + return interpolateTemplate(%q, { %s }) + }`, paramStr, prompt.System, paramStr) } - return fmt.Sprintf(`system: (params) => { - return %q + return fmt.Sprintf(`system: () => { + return interpolateTemplate(%q, {}) }`, prompt.System) } // generatePromptField generates the prompt field for a prompt func (cg *CodeGenerator) generatePromptField(prompt Prompt) string { - promptVars := cg.getPromptVariables(prompt) + promptVars := cg.getPromptVariableObjects(prompt) if len(promptVars) > 0 { - return fmt.Sprintf(`prompt: (params) => { - return interpolateTemplate(%q, params.prompt) - }`, prompt.Prompt) + // Generate parameter destructuring for variables + var paramNames []string + for _, variable := range promptVars { + paramNames = append(paramNames, variable.Name) + } + paramStr := strings.Join(paramNames, ", ") + return fmt.Sprintf(`prompt: ({ %s }) => { + return interpolateTemplate(%q, { %s }) + }`, paramStr, prompt.Prompt, paramStr) } - return fmt.Sprintf(`prompt: (params) => { - return %q + return fmt.Sprintf(`prompt: () => { + return interpolateTemplate(%q, {}) }`, prompt.Prompt) } @@ -215,30 +245,38 @@ func (cg *CodeGenerator) generateVariablesField(prompt Prompt) string { func (cg *CodeGenerator) generateSignatureFunction(prompt Prompt) string { hasSystem := prompt.System != "" hasPrompt := prompt.Prompt != "" + hasSystemVars := len(cg.getSystemVariableObjects(prompt)) > 0 + hasPromptVars := len(cg.getPromptVariableObjects(prompt)) > 0 // Generate the function signature based on what the prompt has var params []string - if hasSystem { - params = append(params, "system: Record") + if hasSystem && hasSystemVars { + params = append(params, "system") } - if hasPrompt { - params = append(params, "prompt: Record") + if hasPrompt && hasPromptVars { + params = append(params, "prompt") } paramStr := strings.Join(params, ", ") if paramStr != "" { - paramStr = fmt.Sprintf("params: { %s }", paramStr) - } else { - paramStr = "" + paramStr = fmt.Sprintf("{ %s }", paramStr) } // Generate the function body var bodyParts []string if hasSystem { - bodyParts = append(bodyParts, fmt.Sprintf("const systemResult = prompts['%s'].system(params)", prompt.Slug)) + if hasSystemVars { + bodyParts = append(bodyParts, fmt.Sprintf("const systemResult = prompts['%s'].system(system)", prompt.Slug)) + } else { + bodyParts = append(bodyParts, fmt.Sprintf("const systemResult = prompts['%s'].system()", prompt.Slug)) + } } if hasPrompt { - bodyParts = append(bodyParts, fmt.Sprintf("const promptResult = prompts['%s'].prompt(params)", prompt.Slug)) + if hasPromptVars { + bodyParts = append(bodyParts, fmt.Sprintf("const promptResult = prompts['%s'].prompt(prompt)", prompt.Slug)) + } else { + bodyParts = append(bodyParts, fmt.Sprintf("const promptResult = prompts['%s'].prompt()", prompt.Slug)) + } } // Combine results @@ -253,12 +291,12 @@ func (cg *CodeGenerator) generateSignatureFunction(prompt Prompt) string { body := strings.Join(bodyParts, ";\n ") if paramStr == "" { - return fmt.Sprintf(`%s: () => { + return fmt.Sprintf(`'%s': () => { %s }`, prompt.Slug, body) } - return fmt.Sprintf(`%s: (%s) => { + return fmt.Sprintf(`'%s': (%s) => { %s }`, prompt.Slug, paramStr, body) } @@ -550,10 +588,10 @@ func (cg *CodeGenerator) generatePromptExports() string { for _, prompt := range cg.prompts { // Generate JSDoc comment for each prompt property jsdocComment := cg.generatePromptPropertyJSDoc(prompt) - exports = append(exports, jsdocComment) - exports = append(exports, fmt.Sprintf(" '%s',", prompt.Slug)) + // Combine JSDoc and property on the same line + exports = append(exports, fmt.Sprintf("%s\n '%s': %s", jsdocComment, prompt.Slug, strcase.ToLowerCamel(prompt.Slug))) } - return strings.Join(exports, "\n") + return strings.Join(exports, ",\n") } // generatePromptTypeExports generates the exports object for TypeScript types From d0a3953a8ab4e7eaaa4f4edb667cb93ece0a4296 Mon Sep 17 00:00:00 2001 From: Bobby Christopher Date: Fri, 3 Oct 2025 10:28:03 -0400 Subject: [PATCH 04/13] everything seems to be working --- internal/bundler/prompts/code_generator.go | 173 ++++++++++++++++++--- 1 file changed, 148 insertions(+), 25 deletions(-) diff --git a/internal/bundler/prompts/code_generator.go b/internal/bundler/prompts/code_generator.go index a6f120dc..5173911d 100644 --- a/internal/bundler/prompts/code_generator.go +++ b/internal/bundler/prompts/code_generator.go @@ -69,6 +69,66 @@ export const compile = (name, ...args) => { };`, strings.Join(objects, "\n\n"), cg.generatePromptExports(), strings.Join(signatures, ",\n")) } +// generateCompileFunctionSignature generates the compile function signature dynamically +func (cg *CodeGenerator) generateCompileFunctionSignature() string { + var cases []string + + for _, prompt := range cg.prompts { + hasSystem := prompt.System != "" + hasPrompt := prompt.Prompt != "" + hasSystemVars := len(cg.getSystemVariableObjects(prompt)) > 0 + hasPromptVars := len(cg.getPromptVariableObjects(prompt)) > 0 + + // Check if all variables are optional + systemVars := cg.getSystemVariableObjects(prompt) + promptVars := cg.getPromptVariableObjects(prompt) + allSystemOptional := cg.areAllVariablesOptional(systemVars) + allPromptOptional := cg.areAllVariablesOptional(promptVars) + + var caseStr string + if !hasSystemVars && !hasPromptVars { + // No variables at all + caseStr = fmt.Sprintf("T extends '%s' ? []", prompt.Slug) + } else if allSystemOptional && allPromptOptional { + // All variables are optional - make params optional + var params []string + if hasSystem && hasSystemVars { + systemType := cg.generateParameterInterface(systemVars, allSystemOptional) + params = append(params, fmt.Sprintf("system?: %s", systemType)) + } + if hasPrompt && hasPromptVars { + promptType := cg.generateParameterInterface(promptVars, allPromptOptional) + params = append(params, fmt.Sprintf("prompt?: %s", promptType)) + } + + paramStr := strings.Join(params, ", ") + caseStr = fmt.Sprintf("T extends '%s' ? [] | [{ %s }]", prompt.Slug, paramStr) + } else { + // Has required variables + var params []string + if hasSystem && hasSystemVars { + systemType := cg.generateParameterInterface(systemVars, allSystemOptional) + params = append(params, fmt.Sprintf("system: %s", systemType)) + } + if hasPrompt && hasPromptVars { + promptType := cg.generateParameterInterface(promptVars, allPromptOptional) + params = append(params, fmt.Sprintf("prompt: %s", promptType)) + } + + paramStr := strings.Join(params, ", ") + caseStr = fmt.Sprintf("T extends '%s' ? [{ %s }]", prompt.Slug, paramStr) + } + + cases = append(cases, caseStr) + } + + if len(cases) == 0 { + return "[]" + } + + return strings.Join(cases, "\n : ") + "\n : []" +} + // GenerateTypeScriptTypes generates the TypeScript definitions file func (cg *CodeGenerator) GenerateTypeScriptTypes() string { var promptTypes []string @@ -90,17 +150,7 @@ export interface GeneratedPromptsCollection { %s compile: ( name: T, - ...args: T extends 'simple-helper' - ? [] // No parameters needed - : T extends keyof { [K in keyof GeneratedPromptsCollection as GeneratedPromptsCollection[K] extends { system: () => string; prompt: () => string } ? K : never]: any } - ? [] // No parameters needed - : [GeneratedPromptsCollection[T] extends { system: (variables: infer S) => string; prompt: (variables: infer P) => string } - ? { system: S; prompt: P } - : GeneratedPromptsCollection[T] extends { system: (variables: infer S) => string } - ? { system: S } - : GeneratedPromptsCollection[T] extends { prompt: (variables: infer P) => string } - ? { prompt: P } - : never] + ...args: %s ) => string; getPrompt: ( name: T @@ -114,7 +164,7 @@ export type SignaturesCollection = { }; export const prompts: PromptsCollection = {} as any; -export const signatures: SignaturesCollection = {} as any;`, strings.Join(promptTypes, "\n\n"), cg.generatePromptTypeExports(), cg.generateSignatureTypeExports()) +export const signatures: SignaturesCollection = {} as any;`, strings.Join(promptTypes, "\n\n"), cg.generatePromptTypeExports(), cg.generateCompileFunctionSignature(), cg.generateSignatureTypeExports()) } // GenerateStubsFile generates the stubs file with actual generated types @@ -185,15 +235,26 @@ func (cg *CodeGenerator) generatePromptObject(prompt Prompt) string { func (cg *CodeGenerator) generateSystemField(prompt Prompt) string { systemVars := cg.getSystemVariableObjects(prompt) if len(systemVars) > 0 { + allOptional := cg.areAllVariablesOptional(systemVars) + // Generate parameter destructuring for variables var paramNames []string for _, variable := range systemVars { paramNames = append(paramNames, variable.Name) } paramStr := strings.Join(paramNames, ", ") - return fmt.Sprintf(`system: ({ %s }) => { + + if allOptional { + // Make parameters optional + return fmt.Sprintf(`system: ({ %s } = {}) => { return interpolateTemplate(%q, { %s }) }`, paramStr, prompt.System, paramStr) + } else { + // Parameters are required + return fmt.Sprintf(`system: ({ %s }) => { + return interpolateTemplate(%q, { %s }) + }`, paramStr, prompt.System, paramStr) + } } return fmt.Sprintf(`system: () => { return interpolateTemplate(%q, {}) @@ -204,15 +265,26 @@ func (cg *CodeGenerator) generateSystemField(prompt Prompt) string { func (cg *CodeGenerator) generatePromptField(prompt Prompt) string { promptVars := cg.getPromptVariableObjects(prompt) if len(promptVars) > 0 { + allOptional := cg.areAllVariablesOptional(promptVars) + // Generate parameter destructuring for variables var paramNames []string for _, variable := range promptVars { paramNames = append(paramNames, variable.Name) } paramStr := strings.Join(paramNames, ", ") - return fmt.Sprintf(`prompt: ({ %s }) => { + + if allOptional { + // Make parameters optional + return fmt.Sprintf(`prompt: ({ %s } = {}) => { + return interpolateTemplate(%q, { %s }) + }`, paramStr, prompt.Prompt, paramStr) + } else { + // Parameters are required + return fmt.Sprintf(`prompt: ({ %s }) => { return interpolateTemplate(%q, { %s }) }`, paramStr, prompt.Prompt, paramStr) + } } return fmt.Sprintf(`prompt: () => { return interpolateTemplate(%q, {}) @@ -248,6 +320,13 @@ func (cg *CodeGenerator) generateSignatureFunction(prompt Prompt) string { hasSystemVars := len(cg.getSystemVariableObjects(prompt)) > 0 hasPromptVars := len(cg.getPromptVariableObjects(prompt)) > 0 + // Check if all variables are optional + systemVars := cg.getSystemVariableObjects(prompt) + promptVars := cg.getPromptVariableObjects(prompt) + allSystemOptional := cg.areAllVariablesOptional(systemVars) + allPromptOptional := cg.areAllVariablesOptional(promptVars) + allOptional := allSystemOptional && allPromptOptional + // Generate the function signature based on what the prompt has var params []string if hasSystem && hasSystemVars { @@ -259,21 +338,33 @@ func (cg *CodeGenerator) generateSignatureFunction(prompt Prompt) string { paramStr := strings.Join(params, ", ") if paramStr != "" { - paramStr = fmt.Sprintf("{ %s }", paramStr) + if allOptional { + paramStr = fmt.Sprintf("{ %s } = {}", paramStr) + } else { + paramStr = fmt.Sprintf("{ %s }", paramStr) + } } // Generate the function body var bodyParts []string if hasSystem { if hasSystemVars { - bodyParts = append(bodyParts, fmt.Sprintf("const systemResult = prompts['%s'].system(system)", prompt.Slug)) + if allSystemOptional { + bodyParts = append(bodyParts, fmt.Sprintf("const systemResult = prompts['%s'].system(system)", prompt.Slug)) + } else { + bodyParts = append(bodyParts, fmt.Sprintf("const systemResult = prompts['%s'].system(system)", prompt.Slug)) + } } else { bodyParts = append(bodyParts, fmt.Sprintf("const systemResult = prompts['%s'].system()", prompt.Slug)) } } if hasPrompt { if hasPromptVars { - bodyParts = append(bodyParts, fmt.Sprintf("const promptResult = prompts['%s'].prompt(prompt)", prompt.Slug)) + if allPromptOptional { + bodyParts = append(bodyParts, fmt.Sprintf("const promptResult = prompts['%s'].prompt(prompt)", prompt.Slug)) + } else { + bodyParts = append(bodyParts, fmt.Sprintf("const promptResult = prompts['%s'].prompt(prompt)", prompt.Slug)) + } } else { bodyParts = append(bodyParts, fmt.Sprintf("const promptResult = prompts['%s'].prompt()", prompt.Slug)) } @@ -382,12 +473,14 @@ func (cg *CodeGenerator) generateDetailedPromptType(prompt Prompt) string { if hasSystemVars { systemVars := cg.getSystemVariableObjects(prompt) - systemParams = cg.generateParameterInterface(systemVars, true) + allSystemOptional := cg.areAllVariablesOptional(systemVars) + systemParams = cg.generateParameterInterface(systemVars, allSystemOptional) } if hasPromptVars { promptVars := cg.getPromptVariableObjects(prompt) - promptParams = cg.generateParameterInterface(promptVars, true) + allPromptOptional := cg.areAllVariablesOptional(promptVars) + promptParams = cg.generateParameterInterface(promptVars, allPromptOptional) } // Generate the main prompt type @@ -396,7 +489,13 @@ func (cg *CodeGenerator) generateDetailedPromptType(prompt Prompt) string { if hasSystem { if hasSystemVars { - fields = append(fields, fmt.Sprintf("system: (variables: %s) => string", systemParams)) + systemVars := cg.getSystemVariableObjects(prompt) + allSystemOptional := cg.areAllVariablesOptional(systemVars) + if allSystemOptional { + fields = append(fields, fmt.Sprintf("system: (variables?: %s) => string", systemParams)) + } else { + fields = append(fields, fmt.Sprintf("system: (variables: %s) => string", systemParams)) + } } else { fields = append(fields, "system: () => string") } @@ -404,7 +503,13 @@ func (cg *CodeGenerator) generateDetailedPromptType(prompt Prompt) string { if hasPrompt { if hasPromptVars { - fields = append(fields, fmt.Sprintf("prompt: (variables: %s) => string", promptParams)) + promptVars := cg.getPromptVariableObjects(prompt) + allPromptOptional := cg.areAllVariablesOptional(promptVars) + if allPromptOptional { + fields = append(fields, fmt.Sprintf("prompt: (variables?: %s) => string", promptParams)) + } else { + fields = append(fields, fmt.Sprintf("prompt: (variables: %s) => string", promptParams)) + } } else { fields = append(fields, "prompt: () => string") } @@ -434,6 +539,16 @@ func (cg *CodeGenerator) generateDetailedPromptType(prompt Prompt) string { export type %sParams = %s;`, mainTypeName, fieldsStr, mainTypeName, compileParams) } +// areAllVariablesOptional checks if all variables in a list are optional +func (cg *CodeGenerator) areAllVariablesOptional(variables []Variable) bool { + for _, variable := range variables { + if variable.IsRequired { + return false + } + } + return true +} + // generateParameterInterface generates a TypeScript interface for variables func (cg *CodeGenerator) generateParameterInterface(variables []Variable, isOptional bool) string { if len(variables) == 0 { @@ -619,13 +734,21 @@ func (cg *CodeGenerator) generateSignatureTypeExports() string { func (cg *CodeGenerator) generateSignatureFunctionParams(prompt Prompt) string { hasSystem := prompt.System != "" hasPrompt := prompt.Prompt != "" + hasSystemVars := len(cg.getSystemVariableObjects(prompt)) > 0 + hasPromptVars := len(cg.getPromptVariableObjects(prompt)) > 0 var params []string - if hasSystem { - params = append(params, "system: Record") + if hasSystem && hasSystemVars { + systemVars := cg.getSystemVariableObjects(prompt) + allOptional := cg.areAllVariablesOptional(systemVars) + systemType := cg.generateParameterInterface(systemVars, allOptional) + params = append(params, fmt.Sprintf("system: %s", systemType)) } - if hasPrompt { - params = append(params, "prompt: Record") + if hasPrompt && hasPromptVars { + promptVars := cg.getPromptVariableObjects(prompt) + allOptional := cg.areAllVariablesOptional(promptVars) + promptType := cg.generateParameterInterface(promptVars, allOptional) + params = append(params, fmt.Sprintf("prompt: %s", promptType)) } if len(params) == 0 { From 6d84c5a1e141ab2cf1e2e004ea4936a435959a3b Mon Sep 17 00:00:00 2001 From: Bobby Christopher Date: Fri, 3 Oct 2025 11:45:20 -0400 Subject: [PATCH 05/13] stuff is working --- internal/bundler/prompts/code_generator.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/bundler/prompts/code_generator.go b/internal/bundler/prompts/code_generator.go index 5173911d..caaeddc3 100644 --- a/internal/bundler/prompts/code_generator.go +++ b/internal/bundler/prompts/code_generator.go @@ -560,7 +560,12 @@ func (cg *CodeGenerator) generateParameterInterface(variables []Variable, isOpti if variable.IsRequired { fields = append(fields, fmt.Sprintf("%s: string", variable.Name)) } else { - fields = append(fields, fmt.Sprintf("%s?: string", variable.Name)) + // Include default value as union type if it exists + if variable.HasDefault { + fields = append(fields, fmt.Sprintf("%s?: string | %q", variable.Name, variable.DefaultValue)) + } else { + fields = append(fields, fmt.Sprintf("%s?: string", variable.Name)) + } } } From a46616493b1d4044f69139cb0964390d2c445dc7 Mon Sep 17 00:00:00 2001 From: Bobby Christopher Date: Fri, 3 Oct 2025 12:11:22 -0400 Subject: [PATCH 06/13] stuff is working --- internal/bundler/prompts/code_generator.go | 45 +--------------------- internal/bundler/prompts/prompts.go | 2 + 2 files changed, 4 insertions(+), 43 deletions(-) diff --git a/internal/bundler/prompts/code_generator.go b/internal/bundler/prompts/code_generator.go index caaeddc3..d1a36feb 100644 --- a/internal/bundler/prompts/code_generator.go +++ b/internal/bundler/prompts/code_generator.go @@ -22,11 +22,9 @@ func NewCodeGenerator(prompts []Prompt) *CodeGenerator { // GenerateJavaScript generates the main JavaScript file func (cg *CodeGenerator) GenerateJavaScript() string { var objects []string - var signatures []string for _, prompt := range cg.prompts { objects = append(objects, cg.generatePromptObject(prompt)) - signatures = append(signatures, cg.generateSignatureFunction(prompt)) } return fmt.Sprintf(`// Generated prompts - do not edit manually @@ -39,34 +37,10 @@ import { interpolateTemplate } from '../../../index.js'; * Each prompt includes original system and prompt templates for reference */ export const prompts = { -%s, - compile: (name, ...args) => { - const signature = signatures[name]; - if (signature) { - return signature(...args); - } - throw new Error('No signature function found for prompt: ' + name); - } -}; - -/** - * Generated signature functions for each prompt - * These provide type-safe function signatures based on prompt structure - */ -export const signatures = { %s }; -/** - * Compile function that provides type-safe compilation of prompts - */ -export const compile = (name, ...args) => { - const signature = signatures[name]; - if (signature) { - return signature(...args); - } - throw new Error('No signature function found for prompt: ' + name); -};`, strings.Join(objects, "\n\n"), cg.generatePromptExports(), strings.Join(signatures, ",\n")) +`, strings.Join(objects, "\n\n"), cg.generatePromptExports()) } // generateCompileFunctionSignature generates the compile function signature dynamically @@ -132,13 +106,10 @@ func (cg *CodeGenerator) generateCompileFunctionSignature() string { // GenerateTypeScriptTypes generates the TypeScript definitions file func (cg *CodeGenerator) GenerateTypeScriptTypes() string { var promptTypes []string - var signatureTypes []string for _, prompt := range cg.prompts { promptType := cg.generateDetailedPromptType(prompt) promptTypes = append(promptTypes, promptType) - signatureType := cg.generateSignatureType(prompt) - signatureTypes = append(signatureTypes, signatureType) } return fmt.Sprintf(`// Generated prompt types - do not edit manually @@ -148,23 +119,11 @@ import { interpolateTemplate, Prompt } from '@agentuity/sdk'; export interface GeneratedPromptsCollection { %s - compile: ( - name: T, - ...args: %s - ) => string; - getPrompt: ( - name: T - ) => GeneratedPromptsCollection[T]; } export type PromptsCollection = GeneratedPromptsCollection; -export type SignaturesCollection = { -%s -}; - -export const prompts: PromptsCollection = {} as any; -export const signatures: SignaturesCollection = {} as any;`, strings.Join(promptTypes, "\n\n"), cg.generatePromptTypeExports(), cg.generateCompileFunctionSignature(), cg.generateSignatureTypeExports()) +export const prompts: PromptsCollection = {} as any;`, strings.Join(promptTypes, "\n\n"), cg.generatePromptTypeExports()) } // GenerateStubsFile generates the stubs file with actual generated types diff --git a/internal/bundler/prompts/prompts.go b/internal/bundler/prompts/prompts.go index 25dd26dc..4f5acab2 100644 --- a/internal/bundler/prompts/prompts.go +++ b/internal/bundler/prompts/prompts.go @@ -16,6 +16,8 @@ type VariableInfo struct { // FindPromptsYAML finds prompts.yaml in the given directory func FindPromptsYAML(dir string) string { possiblePaths := []string{ + filepath.Join(dir, "src", "prompts", "prompts.yaml"), + filepath.Join(dir, "src", "prompts", "prompts.yml"), filepath.Join(dir, "src", "prompts.yaml"), filepath.Join(dir, "src", "prompts.yml"), filepath.Join(dir, "prompts.yaml"), From 8133604b7944a25b63eec3d6bc1616e50dd33281 Mon Sep 17 00:00:00 2001 From: Bobby Christopher Date: Fri, 3 Oct 2025 13:09:25 -0400 Subject: [PATCH 07/13] removed compile --- internal/bundler/prompts/prompts.go | 71 ++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 17 deletions(-) diff --git a/internal/bundler/prompts/prompts.go b/internal/bundler/prompts/prompts.go index 4f5acab2..35c32bfb 100644 --- a/internal/bundler/prompts/prompts.go +++ b/internal/bundler/prompts/prompts.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "github.com/agentuity/go-common/logger" ) @@ -13,7 +14,7 @@ type VariableInfo struct { Names []string } -// FindPromptsYAML finds prompts.yaml in the given directory +// FindPromptsYAML finds prompts.yaml in the given directory (legacy support) func FindPromptsYAML(dir string) string { possiblePaths := []string{ filepath.Join(dir, "src", "prompts", "prompts.yaml"), @@ -33,6 +34,36 @@ func FindPromptsYAML(dir string) string { return "" } +// FindAllPromptFiles finds all YAML files in the prompts directory +func FindAllPromptFiles(dir string) []string { + var promptFiles []string + + // Check for prompts directory in various locations + possibleDirs := []string{ + filepath.Join(dir, "src", "prompts"), + filepath.Join(dir, "prompts"), + } + + for _, promptDir := range possibleDirs { + if _, err := os.Stat(promptDir); err == nil { + // Found prompts directory, scan for YAML files + entries, err := os.ReadDir(promptDir) + if err != nil { + continue + } + + for _, entry := range entries { + if !entry.IsDir() && (strings.HasSuffix(entry.Name(), ".yaml") || strings.HasSuffix(entry.Name(), ".yml")) { + promptFiles = append(promptFiles, filepath.Join(promptDir, entry.Name())) + } + } + break // Use the first prompts directory found + } + } + + return promptFiles +} + // FindSDKGeneratedDir finds the SDK's generated directory in node_modules func FindSDKGeneratedDir(logger logger.Logger, projectDir string) (string, error) { // Try project dir first @@ -68,28 +99,34 @@ func FindSDKGeneratedDir(logger logger.Logger, projectDir string) (string, error // ProcessPrompts finds, parses, and generates prompt files into the SDK func ProcessPrompts(logger logger.Logger, projectDir string) error { - // Find prompts.yaml - promptsPath := FindPromptsYAML(projectDir) - if promptsPath == "" { - // No prompts.yaml found - this is OK, not all projects will have prompts - logger.Debug("No prompts.yaml found in project, skipping prompt generation") + // Find all prompt files + promptFiles := FindAllPromptFiles(projectDir) + if len(promptFiles) == 0 { + // No prompt files found - this is OK, not all projects will have prompts + logger.Debug("No prompt files found in project, skipping prompt generation") return nil } - logger.Debug("Found prompts.yaml at: %s", promptsPath) + logger.Debug("Found %d prompt files: %v", len(promptFiles), promptFiles) - // Read and parse prompts.yaml - data, err := os.ReadFile(promptsPath) - if err != nil { - return fmt.Errorf("failed to read prompts.yaml: %w", err) - } + // Parse all prompt files and combine prompts + var allPrompts []Prompt + for _, promptFile := range promptFiles { + data, err := os.ReadFile(promptFile) + if err != nil { + return fmt.Errorf("failed to read %s: %w", promptFile, err) + } - promptsList, err := ParsePromptsYAML(data) - if err != nil { - return fmt.Errorf("failed to parse prompts: %w", err) + promptsList, err := ParsePromptsYAML(data) + if err != nil { + return fmt.Errorf("failed to parse %s: %w", promptFile, err) + } + + allPrompts = append(allPrompts, promptsList...) + logger.Debug("Parsed %d prompts from %s", len(promptsList), promptFile) } - logger.Debug("Parsed %d prompts from YAML", len(promptsList)) + logger.Debug("Total prompts parsed: %d", len(allPrompts)) // Find SDK generated directory sdkGeneratedDir, err := FindSDKGeneratedDir(logger, projectDir) @@ -100,7 +137,7 @@ func ProcessPrompts(logger logger.Logger, projectDir string) error { logger.Debug("Found SDK generated directory: %s", sdkGeneratedDir) // Generate code using the code generator - codeGen := NewCodeGenerator(promptsList) + codeGen := NewCodeGenerator(allPrompts) // Generate index.js file (overwrite SDK's placeholder, following POC pattern) jsContent := codeGen.GenerateJavaScript() From 2e35b401d5becd1c81d044b9cee526c9dd0b7138 Mon Sep 17 00:00:00 2001 From: Bobby Christopher Date: Fri, 3 Oct 2025 14:56:24 -0400 Subject: [PATCH 08/13] added route 2 --- .cursor/rules/code-generation.mdc | 12 + .cursor/rules/prompt-docstrings.mdc | 73 ++++-- internal/bundler/prompts/code_generator.go | 263 ++++++++++++++++++--- 3 files changed, 303 insertions(+), 45 deletions(-) diff --git a/.cursor/rules/code-generation.mdc b/.cursor/rules/code-generation.mdc index d9c42411..0737d786 100644 --- a/.cursor/rules/code-generation.mdc +++ b/.cursor/rules/code-generation.mdc @@ -21,6 +21,18 @@ alwaysApply: true 2. **Update SDK to handle generated content dynamically** (e.g., `src/apis/prompt/index.ts`) 3. **Build and test the full pipeline**: CLI generation → SDK loading → Agent usage +### Multi-File Support +- **Directory Scanning**: CLI scans `src/prompts/` and `prompts/` directories for all YAML files +- **File Processing**: Processes all `.yaml` and `.yml` files found in the prompts directory +- **Combined Output**: Merges prompts from multiple files into a single generated output +- **Legacy Support**: Still supports single `prompts.yaml` file in various locations + +### JSDoc Documentation +- **Function Comments**: Generated `system` and `prompt` functions include JSDoc comments with actual prompt content +- **IDE Support**: JSDoc comments provide better IDE hover tooltips and documentation +- **Content Preservation**: Original prompt templates are preserved exactly as written in YAML +- **Line Wrapping**: Long lines are automatically wrapped at 80 characters for readability + ### Optional Field Handling - ✅ Generated code should NEVER require optional chaining (`?.`) - ✅ Always generate both `system` and `prompt` fields, even if empty diff --git a/.cursor/rules/prompt-docstrings.mdc b/.cursor/rules/prompt-docstrings.mdc index df6191cd..0a5fd123 100644 --- a/.cursor/rules/prompt-docstrings.mdc +++ b/.cursor/rules/prompt-docstrings.mdc @@ -1,33 +1,74 @@ -# Prompt JSDoc Docstrings +# Prompt JSDoc Docstrings and Multi-File Support -## Goal -Add JSDoc-style docstrings to generated prompt objects that provide better IDE support and make the generated code self-documenting. +https://stackoverflow.com/questions/19230971/how-do-i-jsdoc-a-nested-objects-methods -## Requirements +## Goal +Add JSDoc-style docstrings to generated prompt functions and support scanning multiple YAML files in the prompts directory for better IDE support and organization. + +## Features Implemented + +### 1. Multi-File Support +The CLI now scans the entire `prompts` directory and processes all YAML files: +- **Directory Locations**: Checks `src/prompts/` and `prompts/` directories +- **File Types**: Processes all `.yaml` and `.yml` files +- **Combined Output**: Merges all prompts from multiple files into a single generated output +- **Legacy Support**: Still supports single `prompts.yaml` file in various locations + +### 2. JSDoc Comments on Functions and Types +Generate JSDoc comments on both JavaScript functions and TypeScript type definitions: + +**JavaScript Functions:** +```javascript +system: /** + * System prompt: + * You are a {role:senior} code reviewer specializing in {language:JavaScript}. + * Your experience level is {experience:expert} + * + */ +({ role, language, experience } = {}) => { + return interpolateTemplate("...", { role, language, experience }) +}, +``` -### 1. JSDoc Format for PromptsCollection -Generate JSDoc comments on the `PromptsCollection` type properties with the following structure: +**TypeScript Type Definitions:** ```typescript -export type PromptsCollection = { +export type Assistant = { + slug: string; /** - * [Prompt Name] - [Prompt Description] - * - * @prompt - * [Original prompt template with variables] + * System prompt: + * You are a {role:helpful assistant} specializing in {domain:programming}. + * Your experience level is {experience:intermediate} + * + */ + system: (variables?: { + role?: string | "helpful assistant"; + domain?: string | "programming"; + experience?: string | "intermediate" + }) => string; + /** + * User prompt: + * Help the user with: {task:their question} + * Use a {!tone} approach. */ - promptName: PromptName; + prompt: (variables: { + task?: string | "their question"; + tone: string + }) => string }; ``` -### 2. Content Inclusion -- **Name and Description**: Include both from YAML in format "Name - Description" -- **@prompt**: Include only the original prompt template (not system template) +### 3. Content Inclusion +- **System Prompts**: Include the actual system prompt content in JSDoc +- **User Prompts**: Include the actual user prompt content in JSDoc - **Template Preservation**: Show original templates exactly as written in YAML +- **Variable Syntax**: Preserve `{variable:default}`, `{!variable}`, `{{variable}}` syntax +- **Line Wrapping**: Automatically wrap long lines at 80 characters for readability -### 3. Template Preservation +### 4. Template Preservation - Show original templates exactly as written in YAML - Preserve variable syntax: `{variable:default}`, `{!variable}`, `{{variable}}` - Maintain line breaks and formatting +- Escape JSDoc special characters (`*/` becomes `*\/`) - Escape JSDoc comment characters (`*/` → `* /`) ### 4. IDE Integration diff --git a/internal/bundler/prompts/code_generator.go b/internal/bundler/prompts/code_generator.go index d1a36feb..6a69c5ee 100644 --- a/internal/bundler/prompts/code_generator.go +++ b/internal/bundler/prompts/code_generator.go @@ -193,6 +193,10 @@ func (cg *CodeGenerator) generatePromptObject(prompt Prompt) string { // generateSystemField generates the system field for a prompt func (cg *CodeGenerator) generateSystemField(prompt Prompt) string { systemVars := cg.getSystemVariableObjects(prompt) + + // Generate JSDoc comment with the system prompt + jsdoc := cg.generateSystemJSDoc(prompt) + if len(systemVars) > 0 { allOptional := cg.areAllVariablesOptional(systemVars) @@ -205,24 +209,28 @@ func (cg *CodeGenerator) generateSystemField(prompt Prompt) string { if allOptional { // Make parameters optional - return fmt.Sprintf(`system: ({ %s } = {}) => { + return fmt.Sprintf(`system: %s({ %s } = {}) => { return interpolateTemplate(%q, { %s }) - }`, paramStr, prompt.System, paramStr) + }`, jsdoc, paramStr, prompt.System, paramStr) } else { // Parameters are required - return fmt.Sprintf(`system: ({ %s }) => { + return fmt.Sprintf(`system: %s({ %s }) => { return interpolateTemplate(%q, { %s }) - }`, paramStr, prompt.System, paramStr) + }`, jsdoc, paramStr, prompt.System, paramStr) } } - return fmt.Sprintf(`system: () => { + return fmt.Sprintf(`system: %s() => { return interpolateTemplate(%q, {}) - }`, prompt.System) + }`, jsdoc, prompt.System) } // generatePromptField generates the prompt field for a prompt func (cg *CodeGenerator) generatePromptField(prompt Prompt) string { promptVars := cg.getPromptVariableObjects(prompt) + + // Generate JSDoc comment with the prompt content + jsdoc := cg.generatePromptJSDoc(prompt) + if len(promptVars) > 0 { allOptional := cg.areAllVariablesOptional(promptVars) @@ -235,19 +243,19 @@ func (cg *CodeGenerator) generatePromptField(prompt Prompt) string { if allOptional { // Make parameters optional - return fmt.Sprintf(`prompt: ({ %s } = {}) => { + return fmt.Sprintf(`prompt: %s({ %s } = {}) => { return interpolateTemplate(%q, { %s }) - }`, paramStr, prompt.Prompt, paramStr) + }`, jsdoc, paramStr, prompt.Prompt, paramStr) } else { // Parameters are required - return fmt.Sprintf(`prompt: ({ %s }) => { + return fmt.Sprintf(`prompt: %s({ %s }) => { return interpolateTemplate(%q, { %s }) - }`, paramStr, prompt.Prompt, paramStr) + }`, jsdoc, paramStr, prompt.Prompt, paramStr) } } - return fmt.Sprintf(`prompt: () => { + return fmt.Sprintf(`prompt: %s() => { return interpolateTemplate(%q, {}) - }`, prompt.Prompt) + }`, jsdoc, prompt.Prompt) } // generateVariablesField generates the variables field for a prompt @@ -442,35 +450,37 @@ func (cg *CodeGenerator) generateDetailedPromptType(prompt Prompt) string { promptParams = cg.generateParameterInterface(promptVars, allPromptOptional) } - // Generate the main prompt type + // Generate the main prompt type with JSDoc comments var fields []string fields = append(fields, "slug: string") if hasSystem { + systemJSDoc := cg.generateSystemJSDocForType(prompt) if hasSystemVars { systemVars := cg.getSystemVariableObjects(prompt) allSystemOptional := cg.areAllVariablesOptional(systemVars) if allSystemOptional { - fields = append(fields, fmt.Sprintf("system: (variables?: %s) => string", systemParams)) + fields = append(fields, fmt.Sprintf("%s system: (variables?: %s) => string", systemJSDoc, systemParams)) } else { - fields = append(fields, fmt.Sprintf("system: (variables: %s) => string", systemParams)) + fields = append(fields, fmt.Sprintf("%s system: (variables: %s) => string", systemJSDoc, systemParams)) } } else { - fields = append(fields, "system: () => string") + fields = append(fields, fmt.Sprintf("%s system: () => string", systemJSDoc)) } } if hasPrompt { + promptJSDoc := cg.generatePromptJSDocForType(prompt) if hasPromptVars { promptVars := cg.getPromptVariableObjects(prompt) allPromptOptional := cg.areAllVariablesOptional(promptVars) if allPromptOptional { - fields = append(fields, fmt.Sprintf("prompt: (variables?: %s) => string", promptParams)) + fields = append(fields, fmt.Sprintf("%s prompt: (variables?: %s) => string", promptJSDoc, promptParams)) } else { - fields = append(fields, fmt.Sprintf("prompt: (variables: %s) => string", promptParams)) + fields = append(fields, fmt.Sprintf("%s prompt: (variables: %s) => string", promptJSDoc, promptParams)) } } else { - fields = append(fields, "prompt: () => string") + fields = append(fields, fmt.Sprintf("%s prompt: () => string", promptJSDoc)) } } @@ -491,11 +501,14 @@ func (cg *CodeGenerator) generateDetailedPromptType(prompt Prompt) string { compileParams = "never" } - return fmt.Sprintf(`export type %s = { + // Generate JSDoc typedef for the prompt type + typedefJSDoc := cg.generateTypedefJSDoc(prompt) + + return fmt.Sprintf(`%sexport type %s = { %s }; -export type %sParams = %s;`, mainTypeName, fieldsStr, mainTypeName, compileParams) +export type %sParams = %s;`, typedefJSDoc, mainTypeName, fieldsStr, mainTypeName, compileParams) } // areAllVariablesOptional checks if all variables in a list are optional @@ -729,22 +742,33 @@ func (cg *CodeGenerator) generatePromptPropertyJSDoc(prompt Prompt) string { // Create JSDoc comment with name, description, and templates docLines = append(docLines, " /**") - // Add name and description with separate tags - if prompt.Name != "" { - docLines = append(docLines, fmt.Sprintf(" * @name %s", prompt.Name)) + // Add name and description in the main comment + if prompt.Name != "" && prompt.Description != "" { + docLines = append(docLines, fmt.Sprintf(" * %s - %s", prompt.Name, prompt.Description)) + } else if prompt.Name != "" { + docLines = append(docLines, fmt.Sprintf(" * %s", prompt.Name)) + } else if prompt.Description != "" { + docLines = append(docLines, fmt.Sprintf(" * %s", prompt.Description)) } else { // Fallback to slug-based name - docLines = append(docLines, fmt.Sprintf(" * @name %s", strcase.ToCamel(prompt.Slug))) + docLines = append(docLines, fmt.Sprintf(" * %s", strcase.ToCamel(prompt.Slug))) } - if prompt.Description != "" { - docLines = append(docLines, fmt.Sprintf(" * @description %s", prompt.Description)) + // Add function signatures in the description + docLines = append(docLines, " *") + docLines = append(docLines, " * Functions:") + + if prompt.System != "" { + docLines = append(docLines, " * - system(): Returns the system prompt") + } + if prompt.Prompt != "" { + docLines = append(docLines, " * - prompt(): Returns the user prompt") } // Add original templates if prompt.System != "" { docLines = append(docLines, " *") - docLines = append(docLines, " * @system") + docLines = append(docLines, " * System prompt:") // Escape the template for JSDoc and add proper line breaks escapedSystem := strings.ReplaceAll(prompt.System, "*/", "* /") // Split by newlines and add proper JSDoc formatting @@ -756,7 +780,7 @@ func (cg *CodeGenerator) generatePromptPropertyJSDoc(prompt Prompt) string { if prompt.Prompt != "" { docLines = append(docLines, " *") - docLines = append(docLines, " * @prompt") + docLines = append(docLines, " * User prompt:") // Escape the template for JSDoc and add proper line breaks escapedPrompt := strings.ReplaceAll(prompt.Prompt, "*/", "* /") // Split by newlines and add proper JSDoc formatting @@ -961,3 +985,184 @@ func (cg *CodeGenerator) wrapLine(line string, width int) []string { return wrapped } + +// generateSystemJSDoc generates JSDoc comment for the system function +func (cg *CodeGenerator) generateSystemJSDoc(prompt Prompt) string { + if prompt.System == "" { + return "" + } + + // Escape the system prompt for JSDoc + escapedSystem := strings.ReplaceAll(prompt.System, "*/", "*\\/") + + // Clean up the system prompt for single line display but keep variable placeholders + cleanSystem := strings.ReplaceAll(escapedSystem, "\n", " ") + cleanSystem = strings.TrimSpace(cleanSystem) + + // Get system variables for parameter documentation + systemVars := cg.getSystemVariableObjects(prompt) + allOptional := cg.areAllVariablesOptional(systemVars) + + // Build JSDoc + var jsdoc strings.Builder + jsdoc.WriteString("/**\n") + jsdoc.WriteString(fmt.Sprintf(" * System prompt: %s\n", cleanSystem)) + + // Add parameter documentation + if len(systemVars) > 0 { + jsdoc.WriteString(" * @param {Object} variables - System prompt variables\n") + for _, variable := range systemVars { + if allOptional { + jsdoc.WriteString(fmt.Sprintf(" * @param {string} [variables.%s] - System prompt variable\n", variable.Name)) + } else { + jsdoc.WriteString(fmt.Sprintf(" * @param {string} variables.%s - System prompt variable\n", variable.Name)) + } + } + } + + jsdoc.WriteString(" * @returns {string} The compiled system prompt\n") + jsdoc.WriteString(" */\n ") + + return jsdoc.String() +} + +// generatePromptJSDoc generates JSDoc comment for the prompt function +func (cg *CodeGenerator) generatePromptJSDoc(prompt Prompt) string { + if prompt.Prompt == "" { + return "" + } + + // Escape the prompt for JSDoc + escapedPrompt := strings.ReplaceAll(prompt.Prompt, "*/", "*\\/") + + // Clean up the prompt for single line display + cleanPrompt := strings.ReplaceAll(escapedPrompt, "\n", " ") + cleanPrompt = strings.TrimSpace(cleanPrompt) + + // Get prompt variables for parameter documentation + promptVars := cg.getPromptVariableObjects(prompt) + allOptional := cg.areAllVariablesOptional(promptVars) + + // Build JSDoc + var jsdoc strings.Builder + jsdoc.WriteString("/**\n") + jsdoc.WriteString(fmt.Sprintf(" * User prompt: %s\n", cleanPrompt)) + + // Add parameter documentation + if len(promptVars) > 0 { + jsdoc.WriteString(" * @param {Object} variables - User prompt variables\n") + for _, variable := range promptVars { + if allOptional { + jsdoc.WriteString(fmt.Sprintf(" * @param {string} [variables.%s] - User prompt variable\n", variable.Name)) + } else { + jsdoc.WriteString(fmt.Sprintf(" * @param {string} variables.%s - User prompt variable\n", variable.Name)) + } + } + } + + jsdoc.WriteString(" * @returns {string} The compiled user prompt\n") + jsdoc.WriteString(" */\n ") + + return jsdoc.String() +} + +// generateSystemJSDocForType generates JSDoc comment for the system function in TypeScript types +func (cg *CodeGenerator) generateSystemJSDocForType(prompt Prompt) string { + if prompt.System == "" { + return "" + } + + // Escape the system prompt for JSDoc + escapedSystem := strings.ReplaceAll(prompt.System, "*/", "*\\/") + + // Clean up the system prompt for single line display but keep variable placeholders + cleanSystem := strings.ReplaceAll(escapedSystem, "\n", " ") + cleanSystem = strings.TrimSpace(cleanSystem) + + // Convert slug to the destructured variable name pattern + variableName := strcase.ToLowerCamel(prompt.Slug) + "System" + + // Build JSDoc with actual prompt content and variable name + var jsdoc strings.Builder + jsdoc.WriteString("/**\n") + jsdoc.WriteString(fmt.Sprintf(" * %s - System prompt: %s\n", variableName, cleanSystem)) + jsdoc.WriteString(" * @param variables - System prompt variables\n") + jsdoc.WriteString(" * @returns The compiled system prompt string\n") + jsdoc.WriteString(" */\n") + + return jsdoc.String() +} + +// generatePromptJSDocForType generates JSDoc comment for the prompt function in TypeScript types +func (cg *CodeGenerator) generatePromptJSDocForType(prompt Prompt) string { + if prompt.Prompt == "" { + return "" + } + + // Escape the prompt for JSDoc + escapedPrompt := strings.ReplaceAll(prompt.Prompt, "*/", "*\\/") + + // Clean up the prompt for single line display but keep variable placeholders + cleanPrompt := strings.ReplaceAll(escapedPrompt, "\n", " ") + cleanPrompt = strings.TrimSpace(cleanPrompt) + + // Convert slug to the destructured variable name pattern + variableName := strcase.ToLowerCamel(prompt.Slug) + "Prompt" + + // Build JSDoc with actual prompt content and variable name + var jsdoc strings.Builder + jsdoc.WriteString("/**\n") + jsdoc.WriteString(fmt.Sprintf(" * %s - User prompt: %s\n", variableName, cleanPrompt)) + jsdoc.WriteString(" * @param variables - User prompt variables\n") + jsdoc.WriteString(" * @returns The compiled user prompt string\n") + jsdoc.WriteString(" */\n") + + return jsdoc.String() +} + +// generateTypedefJSDoc generates JSDoc typedef for the prompt type +func (cg *CodeGenerator) generateTypedefJSDoc(prompt Prompt) string { + var jsdoc strings.Builder + jsdoc.WriteString("/**\n") + jsdoc.WriteString(" * ") + jsdoc.WriteString(prompt.Name) + jsdoc.WriteString(" - ") + jsdoc.WriteString(prompt.Description) + jsdoc.WriteString("\n * @typedef {Object} ") + jsdoc.WriteString(strcase.ToCamel(prompt.Slug)) + jsdoc.WriteString("\n * @property {string} slug - The prompt slug\n") + + if prompt.System != "" { + jsdoc.WriteString(" * @property {Function} system - System prompt function\n") + // Add system prompt content + escapedSystem := strings.ReplaceAll(prompt.System, "*/", "*\\/") + lines := strings.Split(escapedSystem, "\n") + for _, line := range lines { + wrapped := cg.wrapLine(line, 80) + for _, wrappedLine := range wrapped { + jsdoc.WriteString(" * System: ") + jsdoc.WriteString(wrappedLine) + jsdoc.WriteString("\n") + } + } + } + + if prompt.Prompt != "" { + jsdoc.WriteString(" * @property {Function} prompt - User prompt function\n") + // Add user prompt content + escapedPrompt := strings.ReplaceAll(prompt.Prompt, "*/", "*\\/") + lines := strings.Split(escapedPrompt, "\n") + for _, line := range lines { + wrapped := cg.wrapLine(line, 80) + for _, wrappedLine := range wrapped { + jsdoc.WriteString(" * Prompt: ") + jsdoc.WriteString(wrappedLine) + jsdoc.WriteString("\n") + } + } + } + + jsdoc.WriteString(" */\n") + + return jsdoc.String() +} From bf4615f591dbb9eaf26c0b1ab764c4c9944fa775 Mon Sep 17 00:00:00 2001 From: Bobby Christopher Date: Fri, 3 Oct 2025 15:04:21 -0400 Subject: [PATCH 09/13] remove unused file --- internal/bundler/prompts/code_generator.go | 359 ------------------ .../bundler/prompts/code_generator_test.go | 22 -- 2 files changed, 381 deletions(-) diff --git a/internal/bundler/prompts/code_generator.go b/internal/bundler/prompts/code_generator.go index 6a69c5ee..abccba86 100644 --- a/internal/bundler/prompts/code_generator.go +++ b/internal/bundler/prompts/code_generator.go @@ -43,66 +43,6 @@ export const prompts = { `, strings.Join(objects, "\n\n"), cg.generatePromptExports()) } -// generateCompileFunctionSignature generates the compile function signature dynamically -func (cg *CodeGenerator) generateCompileFunctionSignature() string { - var cases []string - - for _, prompt := range cg.prompts { - hasSystem := prompt.System != "" - hasPrompt := prompt.Prompt != "" - hasSystemVars := len(cg.getSystemVariableObjects(prompt)) > 0 - hasPromptVars := len(cg.getPromptVariableObjects(prompt)) > 0 - - // Check if all variables are optional - systemVars := cg.getSystemVariableObjects(prompt) - promptVars := cg.getPromptVariableObjects(prompt) - allSystemOptional := cg.areAllVariablesOptional(systemVars) - allPromptOptional := cg.areAllVariablesOptional(promptVars) - - var caseStr string - if !hasSystemVars && !hasPromptVars { - // No variables at all - caseStr = fmt.Sprintf("T extends '%s' ? []", prompt.Slug) - } else if allSystemOptional && allPromptOptional { - // All variables are optional - make params optional - var params []string - if hasSystem && hasSystemVars { - systemType := cg.generateParameterInterface(systemVars, allSystemOptional) - params = append(params, fmt.Sprintf("system?: %s", systemType)) - } - if hasPrompt && hasPromptVars { - promptType := cg.generateParameterInterface(promptVars, allPromptOptional) - params = append(params, fmt.Sprintf("prompt?: %s", promptType)) - } - - paramStr := strings.Join(params, ", ") - caseStr = fmt.Sprintf("T extends '%s' ? [] | [{ %s }]", prompt.Slug, paramStr) - } else { - // Has required variables - var params []string - if hasSystem && hasSystemVars { - systemType := cg.generateParameterInterface(systemVars, allSystemOptional) - params = append(params, fmt.Sprintf("system: %s", systemType)) - } - if hasPrompt && hasPromptVars { - promptType := cg.generateParameterInterface(promptVars, allPromptOptional) - params = append(params, fmt.Sprintf("prompt: %s", promptType)) - } - - paramStr := strings.Join(params, ", ") - caseStr = fmt.Sprintf("T extends '%s' ? [{ %s }]", prompt.Slug, paramStr) - } - - cases = append(cases, caseStr) - } - - if len(cases) == 0 { - return "[]" - } - - return strings.Join(cases, "\n : ") + "\n : []" -} - // GenerateTypeScriptTypes generates the TypeScript definitions file func (cg *CodeGenerator) GenerateTypeScriptTypes() string { var promptTypes []string @@ -126,40 +66,6 @@ export type PromptsCollection = GeneratedPromptsCollection; export const prompts: PromptsCollection = {} as any;`, strings.Join(promptTypes, "\n\n"), cg.generatePromptTypeExports()) } -// GenerateStubsFile generates the stubs file with actual generated types -func (cg *CodeGenerator) GenerateStubsFile() string { - var promptTypes []string - for _, prompt := range cg.prompts { - promptTypes = append(promptTypes, cg.generatePromptType(prompt)) - } - promptCollection := cg.generatePromptTypeExports() - - return fmt.Sprintf(`// Generated prompt types - do not edit manually -import { interpolateTemplate, Prompt } from '@agentuity/sdk'; - -%s - -export interface GeneratedPromptsCollection { -%s -} - -export type PromptsCollection = GeneratedPromptsCollection; -`, strings.Join(promptTypes, "\n\n"), promptCollection) -} - -// GenerateTypeScriptInterfaces generates the TypeScript interfaces file -func (cg *CodeGenerator) GenerateTypeScriptInterfaces() string { - var interfaces []string - - for _, prompt := range cg.prompts { - interfaceDef := cg.generatePromptInterface(prompt) - interfaces = append(interfaces, interfaceDef) - } - - return fmt.Sprintf(`// Generated prompt interfaces - do not edit manually -%s`, strings.Join(interfaces, "\n\n")) -} - // generatePromptObject generates a single prompt object with system and prompt properties func (cg *CodeGenerator) generatePromptObject(prompt Prompt) string { // Determine if prompt has system, prompt, and variables @@ -280,151 +186,6 @@ func (cg *CodeGenerator) generateVariablesField(prompt Prompt) string { return fmt.Sprintf("variables: { %s }", strings.Join(varDefs, ", ")) } -// generateSignatureFunction generates a signature function for a prompt -func (cg *CodeGenerator) generateSignatureFunction(prompt Prompt) string { - hasSystem := prompt.System != "" - hasPrompt := prompt.Prompt != "" - hasSystemVars := len(cg.getSystemVariableObjects(prompt)) > 0 - hasPromptVars := len(cg.getPromptVariableObjects(prompt)) > 0 - - // Check if all variables are optional - systemVars := cg.getSystemVariableObjects(prompt) - promptVars := cg.getPromptVariableObjects(prompt) - allSystemOptional := cg.areAllVariablesOptional(systemVars) - allPromptOptional := cg.areAllVariablesOptional(promptVars) - allOptional := allSystemOptional && allPromptOptional - - // Generate the function signature based on what the prompt has - var params []string - if hasSystem && hasSystemVars { - params = append(params, "system") - } - if hasPrompt && hasPromptVars { - params = append(params, "prompt") - } - - paramStr := strings.Join(params, ", ") - if paramStr != "" { - if allOptional { - paramStr = fmt.Sprintf("{ %s } = {}", paramStr) - } else { - paramStr = fmt.Sprintf("{ %s }", paramStr) - } - } - - // Generate the function body - var bodyParts []string - if hasSystem { - if hasSystemVars { - if allSystemOptional { - bodyParts = append(bodyParts, fmt.Sprintf("const systemResult = prompts['%s'].system(system)", prompt.Slug)) - } else { - bodyParts = append(bodyParts, fmt.Sprintf("const systemResult = prompts['%s'].system(system)", prompt.Slug)) - } - } else { - bodyParts = append(bodyParts, fmt.Sprintf("const systemResult = prompts['%s'].system()", prompt.Slug)) - } - } - if hasPrompt { - if hasPromptVars { - if allPromptOptional { - bodyParts = append(bodyParts, fmt.Sprintf("const promptResult = prompts['%s'].prompt(prompt)", prompt.Slug)) - } else { - bodyParts = append(bodyParts, fmt.Sprintf("const promptResult = prompts['%s'].prompt(prompt)", prompt.Slug)) - } - } else { - bodyParts = append(bodyParts, fmt.Sprintf("const promptResult = prompts['%s'].prompt()", prompt.Slug)) - } - } - - // Combine results - if len(bodyParts) == 1 { - bodyParts = append(bodyParts, "return systemResult || promptResult") - } else if len(bodyParts) == 2 { - bodyParts = append(bodyParts, "return `${systemResult}\\n${promptResult}`") - } else { - bodyParts = append(bodyParts, "return ''") - } - - body := strings.Join(bodyParts, ";\n ") - - if paramStr == "" { - return fmt.Sprintf(`'%s': () => { - %s -}`, prompt.Slug, body) - } - - return fmt.Sprintf(`'%s': (%s) => { - %s -}`, prompt.Slug, paramStr, body) -} - -// generateTemplateValue generates the value for a template (either compile function or direct interpolateTemplate call) -func (cg *CodeGenerator) generateTemplateValue(template string) string { - if template == "" { - return `""` - } - - return fmt.Sprintf("interpolateTemplate(%q, variables)", template) -} - -// generatePromptType generates a TypeScript type for a prompt object using generics -func (cg *CodeGenerator) generatePromptType(prompt Prompt) string { - hasSystem := prompt.System != "" - hasPrompt := prompt.Prompt != "" - hasSystemVars := len(cg.getSystemVariables(prompt)) > 0 - hasPromptVars := len(cg.getPromptVariables(prompt)) > 0 - hasVariables := hasSystemVars || hasPromptVars - - // Generate the generic type parameters - var genericParams []string - if hasSystem { - genericParams = append(genericParams, "true") - } else { - genericParams = append(genericParams, "false") - } - if hasPrompt { - genericParams = append(genericParams, "true") - } else { - genericParams = append(genericParams, "false") - } - if hasVariables { - genericParams = append(genericParams, "true") - } else { - genericParams = append(genericParams, "false") - } - - genericStr := strings.Join(genericParams, ", ") - mainTypeName := strcase.ToCamel(prompt.Slug) - - // Generate the type definition - var fields []string - fields = append(fields, "slug: string") - - if hasSystem { - fields = append(fields, "system: (params: { system: Record }) => string") - } - - if hasPrompt { - fields = append(fields, "prompt: (params: { prompt: Record }) => string") - } - - if hasVariables { - // Generate variable types - var allVars []Variable - allVars = append(allVars, cg.getSystemVariableObjects(prompt)...) - allVars = append(allVars, cg.getPromptVariableObjects(prompt)...) - - var varDefs []string - for _, variable := range allVars { - varDefs = append(varDefs, fmt.Sprintf("%s: string", variable.Name)) - } - fields = append(fields, fmt.Sprintf("variables: { %s }", strings.Join(varDefs, ", "))) - } - - return fmt.Sprintf(`export type %s = Prompt<%s>;`, mainTypeName, genericStr) -} - // generateDetailedPromptType generates detailed TypeScript types with specific parameter interfaces func (cg *CodeGenerator) generateDetailedPromptType(prompt Prompt) string { hasSystem := prompt.System != "" @@ -544,70 +305,6 @@ func (cg *CodeGenerator) generateParameterInterface(variables []Variable, isOpti return fmt.Sprintf("{\n %s\n }", strings.Join(fields, ";\n ")) } -// generateSignatureType generates a TypeScript type for a signature function -func (cg *CodeGenerator) generateSignatureType(prompt Prompt) string { - hasSystem := prompt.System != "" - hasPrompt := prompt.Prompt != "" - - // Generate the function signature based on what the prompt has - var params []string - if hasSystem { - params = append(params, "system: Record") - } - if hasPrompt { - params = append(params, "prompt: Record") - } - - paramStr := strings.Join(params, ", ") - if paramStr != "" { - paramStr = fmt.Sprintf("params: { %s }", paramStr) - } else { - paramStr = "" - } - - if paramStr == "" { - return fmt.Sprintf(`%s: () => string`, prompt.Slug) - } - - return fmt.Sprintf(`%s: (%s) => string`, prompt.Slug, paramStr) -} - -// generatePromptInterface generates a TypeScript interface for a prompt -func (cg *CodeGenerator) generatePromptInterface(prompt Prompt) string { - // Get variables from system template - systemVariables := cg.getSystemVariableObjects(prompt) - var systemParams []string - if len(systemVariables) > 0 { - systemParams = append(systemParams, fmt.Sprintf("variables?: { %s }", cg.generateVariableTypesFromObjects(systemVariables))) - } - systemParamStr := strings.Join(systemParams, ", ") - systemCompileType := fmt.Sprintf("(%s) => string", systemParamStr) - - // Get variables from prompt template - promptVariables := cg.getPromptVariableObjects(prompt) - var promptParams []string - if len(promptVariables) > 0 { - promptParams = append(promptParams, fmt.Sprintf("variables?: { %s }", cg.generateVariableTypesFromObjects(promptVariables))) - } - promptParamStr := strings.Join(promptParams, ", ") - promptCompileType := fmt.Sprintf("(%s) => string", promptParamStr) - - return fmt.Sprintf(`export interface %s { - slug: string; - system: { compile: %s }; - prompt: { compile: %s }; -}`, strcase.ToCamel(prompt.Slug), systemCompileType, promptCompileType) -} - -// generateVariableTypes generates TypeScript types for variables -func (cg *CodeGenerator) generateVariableTypes(variables []string) string { - var types []string - for _, variable := range variables { - types = append(types, fmt.Sprintf("%s: string", variable)) - } - return strings.Join(types, "; ") -} - // generateVariableTypesFromObjects generates TypeScript types for variables with default values func (cg *CodeGenerator) generateVariableTypesFromObjects(variables []Variable) string { var types []string @@ -698,43 +395,6 @@ func (cg *CodeGenerator) generatePromptTypeExports() string { return strings.Join(exports, "\n") } -// generateSignatureTypeExports generates the exports object for signature types -func (cg *CodeGenerator) generateSignatureTypeExports() string { - var exports []string - for _, prompt := range cg.prompts { - exports = append(exports, fmt.Sprintf(" '%s': (%s) => string", prompt.Slug, cg.generateSignatureFunctionParams(prompt))) - } - return strings.Join(exports, "\n") -} - -// generateSignatureFunctionParams generates the parameter string for signature functions -func (cg *CodeGenerator) generateSignatureFunctionParams(prompt Prompt) string { - hasSystem := prompt.System != "" - hasPrompt := prompt.Prompt != "" - hasSystemVars := len(cg.getSystemVariableObjects(prompt)) > 0 - hasPromptVars := len(cg.getPromptVariableObjects(prompt)) > 0 - - var params []string - if hasSystem && hasSystemVars { - systemVars := cg.getSystemVariableObjects(prompt) - allOptional := cg.areAllVariablesOptional(systemVars) - systemType := cg.generateParameterInterface(systemVars, allOptional) - params = append(params, fmt.Sprintf("system: %s", systemType)) - } - if hasPrompt && hasPromptVars { - promptVars := cg.getPromptVariableObjects(prompt) - allOptional := cg.areAllVariablesOptional(promptVars) - promptType := cg.generateParameterInterface(promptVars, allOptional) - params = append(params, fmt.Sprintf("prompt: %s", promptType)) - } - - if len(params) == 0 { - return "" - } - - return fmt.Sprintf("params: { %s }", strings.Join(params, ", ")) -} - // generatePromptPropertyJSDoc generates JSDoc comments for prompt properties in PromptsCollection func (cg *CodeGenerator) generatePromptPropertyJSDoc(prompt Prompt) string { var docLines []string @@ -877,25 +537,6 @@ func (cg *CodeGenerator) getPromptVariableObjects(prompt Prompt) []Variable { return promptTemplate.Variables } -// generateTypeWithDocstring generates a separate type with docstring -func (cg *CodeGenerator) generateTypeWithDocstring(template, typeName, paramStr, mainTypeName string) string { - if template == "" { - return fmt.Sprintf(`export type %s = { compile: (%s) => string };`, - typeName, paramStr) - } - - // Generate JSDoc comment for the type with @memberof - docstring := cg.generateTemplateDocstring(template) - - return fmt.Sprintf(`/** -%s - * @memberof %s - * @type {object} - */ -export type %s = { compile: (%s) => string };`, - docstring, mainTypeName, typeName, paramStr) -} - // generateTemplateDocstring generates the docstring content for any template func (cg *CodeGenerator) generateTemplateDocstring(template string) string { if template == "" { diff --git a/internal/bundler/prompts/code_generator_test.go b/internal/bundler/prompts/code_generator_test.go index 301f9f9a..21ee819d 100644 --- a/internal/bundler/prompts/code_generator_test.go +++ b/internal/bundler/prompts/code_generator_test.go @@ -73,24 +73,6 @@ func TestCodeGenerator(t *testing.T) { assert.Contains(t, types, "domain: string") assert.Contains(t, types, "task?: string | \"their question\"") }) - - t.Run("GenerateTypeScriptInterfaces", func(t *testing.T) { - interfaces := codeGen.GenerateTypeScriptInterfaces() - - // Check that it contains both interfaces - assert.Contains(t, interfaces, "export interface TestPrompt1 {") - assert.Contains(t, interfaces, "export interface TestPrompt2 {") - - // Check that it contains variable types with proper optional/default syntax - assert.Contains(t, interfaces, "variables?: {") - assert.Contains(t, interfaces, "role?: string | \"assistant\"") - assert.Contains(t, interfaces, "domain: string") - assert.Contains(t, interfaces, "task?: string | \"their question\"") - - // Check that it contains system and prompt compile functions - assert.Contains(t, interfaces, "system: { compile:") - assert.Contains(t, interfaces, "prompt: { compile:") - }) } func TestCodeGenerator_EmptyPrompts(t *testing.T) { @@ -113,10 +95,6 @@ func TestCodeGenerator_EmptyPrompts(t *testing.T) { assert.Contains(t, types, "export const prompts: PromptsCollection = {} as any;") }) - t.Run("GenerateTypeScriptInterfaces", func(t *testing.T) { - interfaces := codeGen.GenerateTypeScriptInterfaces() - assert.Equal(t, "// Generated prompt interfaces - do not edit manually\n", interfaces) - }) } func TestCodeGenerator_SingleFieldPrompts(t *testing.T) { From a289f27aa5698c50868b078784e7d0d21a20c27d Mon Sep 17 00:00:00 2001 From: Bobby Christopher Date: Fri, 3 Oct 2025 16:35:50 -0400 Subject: [PATCH 10/13] remove markdown --- CODING_AGENT_PROMPT.md | 187 ---------------------------------------- TASK_COMPLETION_PLAN.md | 145 ------------------------------- 2 files changed, 332 deletions(-) delete mode 100644 CODING_AGENT_PROMPT.md delete mode 100644 TASK_COMPLETION_PLAN.md diff --git a/CODING_AGENT_PROMPT.md b/CODING_AGENT_PROMPT.md deleted file mode 100644 index 46c4de8b..00000000 --- a/CODING_AGENT_PROMPT.md +++ /dev/null @@ -1,187 +0,0 @@ -# Coding Agent Prompt: Type-Safe Prompt Compilation - -## Context - -You are working on a code generation system where a CLI (Go) generates TypeScript/JavaScript code that gets consumed by an SDK (TypeScript). The system generates prompt templates with variable interpolation, and we need to make the `compile()` function type-safe. - -## Current Problem - -The `ctx.prompts.compile()` function currently accepts `any` parameters, but it should provide proper TypeScript intellisense based on the actual prompt template requirements. - -## What's Already Working - -1. **CLI generates code correctly** - `internal/bundler/prompts/code_generator.go` -2. **SDK loads generated content** - `src/apis/prompt/index.ts` -3. **Basic compilation works** - `ctx.prompts.compile('slug', params)` -4. **Slug-based naming** - Uses `'simple-helper'` instead of camelCase - -## What Needs to be Fixed - -The `compile()` function should have conditional TypeScript signatures based on prompt template analysis: - -### Example Requirements - -Given these prompt templates from `test-prompt-compile/src/prompts.yaml`: - -```yaml -# Required variables only -- slug: required-variables-only - system: | - You are a {!role} assistant. - Your specialization is {!specialization}. - prompt: | - Complete the {!task} with {!quality} quality. - -# Optional variables with defaults -- slug: optional-with-defaults - system: | - You are a {role:helpful assistant} specializing in {domain:general topics}. - Your experience level is {experience:intermediate} - prompt: | - Help the user with: {task:their question} - Use a {tone:friendly} approach. - -# No variables -- slug: simple-helper - system: | - You are a helpful assistant that provides clear and concise answers. - prompt: | - Please help the user with their question. -``` - -### Expected TypeScript Behavior - -```typescript -// For 'required-variables-only' - should require system and prompt with specific fields -ctx.prompts.compile('required-variables-only', { - system: { role: string, specialization: string }, - prompt: { task: string, quality: string } -}); - -// For 'optional-with-defaults' - should make system and prompt optional with optional fields -ctx.prompts.compile('optional-with-defaults', { - system?: { role?: string, domain?: string, experience?: string }, - prompt?: { task?: string, tone?: string } -}); - -// For 'simple-helper' - should require no parameters -ctx.prompts.compile('simple-helper'); -``` - -## Implementation Approach - -### 1. Template Analysis (CLI) - -Extend `internal/bundler/prompts/code_generator.go` to: - -1. **Parse variable syntax**: - - `{!variable}` = required variable - - `{variable:default}` = optional variable with default - - `{variable}` = optional variable without default - -2. **Analyze each prompt**: - - Extract system variables and their requirements - - Extract prompt variables and their requirements - - Determine if system/prompt parameters should be included - -3. **Generate TypeScript interfaces**: - - Create parameter interfaces for each prompt - - Generate conditional compile signature types - -### 2. Type Generation (CLI) - -Generate TypeScript code that creates: - -```typescript -// Parameter interfaces for each prompt -export interface RequiredVariablesOnlyParams { - system: { - role: string; - specialization: string; - }; - prompt: { - task: string; - quality: string; - }; -} - -export interface OptionalWithDefaultsParams { - system?: { - role?: string; - domain?: string; - experience?: string; - }; - prompt?: { - task?: string; - tone?: string; - }; -} - -// Conditional compile signature -export type CompileParams = - T extends 'required-variables-only' ? RequiredVariablesOnlyParams : - T extends 'optional-with-defaults' ? OptionalWithDefaultsParams : - T extends 'simple-helper' ? never : - any; - -// Updated compile function signature -compile( - slug: T, - params: CompileParams -): string; -``` - -### 3. SDK Integration (SDK) - -Update `src/types.ts` and `src/server/server.ts` to: - -1. **Use generated types** for compile function signature -2. **Implement runtime validation** for required parameters -3. **Maintain backward compatibility** - -## Key Files to Focus On - -### CLI Files: -- `internal/bundler/prompts/code_generator.go` - Main logic for template analysis and type generation -- `internal/bundler/prompts/prompts.go` - Orchestration - -### SDK Files: -- `src/types.ts` - Update AgentContext compile signature -- `src/server/server.ts` - Implement type-safe compile function - -### Test Files: -- `test-prompt-compile/src/agents/my-agent/index.ts` - Current test cases - -## Reference Implementation - -Look at how `getPrompt()` currently works for inspiration on conditional types. The `compile()` function should follow a similar pattern but with more complex parameter analysis. - -## Success Criteria - -1. **TypeScript intellisense** shows correct parameters for each prompt slug -2. **Required parameters** are enforced at compile time -3. **Optional parameters** are properly marked as optional -4. **No parameters** prompts work with empty object -5. **Runtime validation** throws clear errors for missing required parameters -6. **Backward compatibility** maintained for existing code - -## Testing - -Use the existing test project at `test-prompt-compile/` to verify your changes work correctly. The test cases in `src/agents/my-agent/index.ts` should provide good examples of expected behavior. - -## Important Notes - -- **Don't break existing functionality** - maintain backward compatibility -- **Use the existing patterns** - follow the shell-based approach already implemented -- **Test incrementally** - verify each change works before moving to the next -- **Keep it simple** - avoid overly complex generics that cause compilation issues - -## Getting Started - -1. Start by analyzing the template parsing logic in `code_generator.go` -2. Add functions to extract variable requirements from system/prompt templates -3. Generate the appropriate TypeScript interfaces -4. Update the SDK to use the generated types -5. Test with the existing test project - -The goal is to make `ctx.prompts.compile()` as type-safe and user-friendly as possible while maintaining the existing architecture and patterns. diff --git a/TASK_COMPLETION_PLAN.md b/TASK_COMPLETION_PLAN.md deleted file mode 100644 index 69939b8d..00000000 --- a/TASK_COMPLETION_PLAN.md +++ /dev/null @@ -1,145 +0,0 @@ -# Task Completion Plan: Type-Safe Prompt Compilation - -## Current State Analysis - -### What's Working ✅ -- CLI generates JavaScript and TypeScript files correctly -- SDK loads generated content dynamically -- Basic prompt compilation works with `ctx.prompts.compile()` -- Slug-based naming is implemented (`'simple-helper'` instead of camelCase) - -### What Needs Fixing ❌ -- **Type Safety Issue**: The `compile()` function doesn't provide proper TypeScript feedback -- **Parameter Structure**: Should conditionally include `system` and `prompt` parameters based on template content -- **Variable Requirements**: Should distinguish between required, optional, and no variables - -## Required Changes - -### 1. Type-Safe Compile Function Signature - -The `compile()` function should have conditional parameters based on prompt template analysis: - -```typescript -// Current (incorrect): -compile(slug: string, params: any): string - -// Should be (conditional based on template): -compile( - slug: T, - params: CompileParams -): string - -// Where CompileParams is conditionally typed: -type CompileParams = - T extends 'required-variables-only' - ? { system: { role: string; specialization: string }; prompt: { task: string; quality: string } } - : T extends 'optional-with-defaults' - ? { system?: { role?: string; domain?: string; experience?: string }; prompt?: { task?: string; tone?: string } } - : T extends 'simple-helper' - ? never // No parameters needed - : any; // Fallback -``` - -### 2. Template Analysis Requirements - -Need to analyze each prompt template to determine: -- **System variables**: Required (`{!var}`), Optional (`{var:default}`), or None -- **Prompt variables**: Required (`{!var}`), Optional (`{var:default}`), or None -- **Parameter structure**: Include `system`/`prompt` only if they have variables - -### 3. Generated Type Structure - -The CLI should generate TypeScript types that reflect the actual template requirements: - -```typescript -// For 'required-variables-only': -export interface RequiredVariablesOnlyParams { - system: { - role: string; - specialization: string; - }; - prompt: { - task: string; - quality: string; - }; -} - -// For 'optional-with-defaults': -export interface OptionalWithDefaultsParams { - system?: { - role?: string; - domain?: string; - experience?: string; - }; - prompt?: { - task?: string; - tone?: string; - }; -} - -// For 'simple-helper': -// No parameters interface needed (no parameters at all) -``` - -## Implementation Plan - -### Phase 1: Template Analysis (CLI) -1. **Extend `code_generator.go`** to analyze prompt templates -2. **Parse variable syntax**: - - `{!variable}` = required - - `{variable:default}` = optional with default - - `{variable}` = optional without default -3. **Generate parameter interfaces** for each prompt -4. **Create conditional compile signature** types - -### Phase 2: Type Generation (CLI) -1. **Generate TypeScript interfaces** for each prompt's parameters -2. **Create union types** for compile function parameters -3. **Update generated `index.d.ts`** with proper type definitions -4. **Ensure backward compatibility** with existing code - -### Phase 3: SDK Integration (SDK) -1. **Update `AgentContext`** to use generated types -2. **Implement type-safe compile function** in server context -3. **Add proper error handling** for missing required parameters -4. **Test with existing agent code** - -### Phase 4: Testing & Validation -1. **Test all prompt scenarios** from `prompts.yaml` -2. **Verify TypeScript intellisense** works correctly -3. **Ensure runtime behavior** matches type expectations -4. **Update documentation** with new patterns - -## Files to Modify - -### CLI Files: -- `internal/bundler/prompts/code_generator.go` - Template analysis & type generation -- `internal/bundler/prompts/prompts.go` - Orchestration updates - -### SDK Files: -- `src/types.ts` - Update AgentContext compile signature -- `src/server/server.ts` - Implement type-safe compile function -- `src/apis/prompt/index.ts` - Export generated types - -### Test Files: -- `test-prompt-compile/src/agents/my-agent/index.ts` - Update test cases - -## Success Criteria - -1. **Type Safety**: `ctx.prompts.compile()` provides accurate TypeScript intellisense -2. **Conditional Parameters**: Only required parameters are included in function signature -3. **Runtime Validation**: Missing required parameters throw clear errors -4. **Backward Compatibility**: Existing code continues to work -5. **Performance**: No significant impact on compilation or runtime performance - -## Reference Implementation - -Look at the current working `getPrompt()` function for inspiration on how conditional types should work. The `compile()` function should follow a similar pattern but with more complex parameter analysis. - -## Next Steps - -1. Start with Phase 1 (Template Analysis) -2. Test each phase incrementally -3. Maintain backward compatibility throughout -4. Update documentation as you go -5. Test with the existing `test-prompt-compile` project From 82972829065e05e80e1ed270044b644ab2b5d293 Mon Sep 17 00:00:00 2001 From: Bobby Christopher Date: Fri, 3 Oct 2025 16:41:10 -0400 Subject: [PATCH 11/13] made small change --- internal/bundler/prompts/prompts.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/internal/bundler/prompts/prompts.go b/internal/bundler/prompts/prompts.go index 35c32bfb..b0c2e706 100644 --- a/internal/bundler/prompts/prompts.go +++ b/internal/bundler/prompts/prompts.go @@ -37,6 +37,7 @@ func FindPromptsYAML(dir string) string { // FindAllPromptFiles finds all YAML files in the prompts directory func FindAllPromptFiles(dir string) []string { var promptFiles []string + seenFiles := make(map[string]bool) // Check for prompts directory in various locations possibleDirs := []string{ @@ -44,6 +45,7 @@ func FindAllPromptFiles(dir string) []string { filepath.Join(dir, "prompts"), } + // Scan all possible directories for _, promptDir := range possibleDirs { if _, err := os.Stat(promptDir); err == nil { // Found prompts directory, scan for YAML files @@ -54,10 +56,13 @@ func FindAllPromptFiles(dir string) []string { for _, entry := range entries { if !entry.IsDir() && (strings.HasSuffix(entry.Name(), ".yaml") || strings.HasSuffix(entry.Name(), ".yml")) { - promptFiles = append(promptFiles, filepath.Join(promptDir, entry.Name())) + filePath := filepath.Join(promptDir, entry.Name()) + if !seenFiles[filePath] { + promptFiles = append(promptFiles, filePath) + seenFiles[filePath] = true + } } } - break // Use the first prompts directory found } } From 9b9088e066423dbf4de15b4276f1d1a1c8bdcc83 Mon Sep 17 00:00:00 2001 From: Bobby Christopher Date: Fri, 3 Oct 2025 16:45:25 -0400 Subject: [PATCH 12/13] remove unused functions --- internal/bundler/prompts/prompts.go | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/internal/bundler/prompts/prompts.go b/internal/bundler/prompts/prompts.go index b0c2e706..469281f2 100644 --- a/internal/bundler/prompts/prompts.go +++ b/internal/bundler/prompts/prompts.go @@ -9,31 +9,6 @@ import ( "github.com/agentuity/go-common/logger" ) -// VariableInfo holds information about extracted variables -type VariableInfo struct { - Names []string -} - -// FindPromptsYAML finds prompts.yaml in the given directory (legacy support) -func FindPromptsYAML(dir string) string { - possiblePaths := []string{ - filepath.Join(dir, "src", "prompts", "prompts.yaml"), - filepath.Join(dir, "src", "prompts", "prompts.yml"), - filepath.Join(dir, "src", "prompts.yaml"), - filepath.Join(dir, "src", "prompts.yml"), - filepath.Join(dir, "prompts.yaml"), - filepath.Join(dir, "prompts.yml"), - } - - for _, path := range possiblePaths { - if _, err := os.Stat(path); err == nil { - return path - } - } - - return "" -} - // FindAllPromptFiles finds all YAML files in the prompts directory func FindAllPromptFiles(dir string) []string { var promptFiles []string From 251a0b1597f677fd538fb1d6fa4b1430ade20c1f Mon Sep 17 00:00:00 2001 From: Bobby Christopher Date: Fri, 3 Oct 2025 16:50:28 -0400 Subject: [PATCH 13/13] fix tests --- .../bundler/prompts/code_generator_test.go | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/internal/bundler/prompts/code_generator_test.go b/internal/bundler/prompts/code_generator_test.go index 21ee819d..cf569cfa 100644 --- a/internal/bundler/prompts/code_generator_test.go +++ b/internal/bundler/prompts/code_generator_test.go @@ -29,7 +29,7 @@ func TestCodeGenerator(t *testing.T) { js := codeGen.GenerateJavaScript() // Check that it contains the import - assert.Contains(t, js, "import { interpolateTemplate } from '@agentuity/sdk';") + assert.Contains(t, js, "import { interpolateTemplate } from '../../../index.js';") // Check that it contains the prompts object assert.Contains(t, js, "export const prompts = {") @@ -41,10 +41,9 @@ func TestCodeGenerator(t *testing.T) { // Check that it contains variables parameter (no TypeScript types) assert.Contains(t, js, "variables") - // Check that it contains compile functions - assert.Contains(t, js, "system: {") - assert.Contains(t, js, "prompt: {") - assert.Contains(t, js, "compile: (variables) => {") + // Check that it contains function signatures + assert.Contains(t, js, "system: /**") + assert.Contains(t, js, "prompt: /**") assert.Contains(t, js, "interpolateTemplate(") // Ensure no TypeScript syntax in JavaScript @@ -58,7 +57,7 @@ func TestCodeGenerator(t *testing.T) { types := codeGen.GenerateTypeScriptTypes() // Check that it contains the import - assert.Contains(t, types, "import { interpolateTemplate } from '@agentuity/sdk';") + assert.Contains(t, types, "import { interpolateTemplate, Prompt } from '@agentuity/sdk';") // Check that it contains the prompts object assert.Contains(t, types, "export const prompts: PromptsCollection = {} as any;") @@ -68,7 +67,6 @@ func TestCodeGenerator(t *testing.T) { assert.Contains(t, types, "TestPrompt2") // Check that it contains variable types with proper optional/default syntax - assert.Contains(t, types, "variables?: {") assert.Contains(t, types, "role?: string | \"assistant\"") assert.Contains(t, types, "domain: string") assert.Contains(t, types, "task?: string | \"their question\"") @@ -116,10 +114,10 @@ func TestCodeGenerator_SingleFieldPrompts(t *testing.T) { t.Run("GenerateJavaScript", func(t *testing.T) { js := codeGen.GenerateJavaScript() - // Check that it contains the correct compile functions - assert.Contains(t, js, "system: {") - assert.Contains(t, js, "prompt: {") - assert.Contains(t, js, "compile: (variables) => {") + // Check that it contains the correct function signatures + assert.Contains(t, js, "system: /**") + assert.Contains(t, js, "prompt: /**") + assert.Contains(t, js, "interpolateTemplate(") assert.Contains(t, js, "slug:") // Ensure no TypeScript syntax in JavaScript @@ -154,14 +152,14 @@ func TestCodeGenerator_ComplexPrompts(t *testing.T) { js := codeGen.GenerateJavaScript() // Check that it handles multiline templates correctly - assert.Contains(t, js, "interpolateTemplate(\"You are a {role:helpful assistant} specializing in {!domain}.\\nYour experience level is {experience:intermediate}.\", variables)") - assert.Contains(t, js, "interpolateTemplate(\"Help the user with: {task:their question}\\nUse a {approach:detailed} approach.\\nPriority: {priority:normal}\", variables)") + assert.Contains(t, js, "interpolateTemplate(\"You are a {role:helpful assistant} specializing in {!domain}.\\nYour experience level is {experience:intermediate}.\", { role, domain, experience })") + assert.Contains(t, js, "interpolateTemplate(\"Help the user with: {task:their question}\\nUse a {approach:detailed} approach.\\nPriority: {priority:normal}\", { task, approach, priority })") // Check that it contains the correct object structure assert.Contains(t, js, "const complexPrompt = {") - assert.Contains(t, js, "system: {") - assert.Contains(t, js, "prompt: {") - assert.Contains(t, js, "compile: (variables) => {") + assert.Contains(t, js, "system: /**") + assert.Contains(t, js, "prompt: /**") + assert.Contains(t, js, "interpolateTemplate(") assert.Contains(t, js, "slug:") // Ensure no TypeScript syntax in JavaScript @@ -175,8 +173,8 @@ func TestCodeGenerator_ComplexPrompts(t *testing.T) { types := codeGen.GenerateTypeScriptTypes() // Check that it has the correct object structure for complex prompts - assert.Contains(t, types, "system: ComplexPromptSystem;") - assert.Contains(t, types, "prompt: ComplexPromptPrompt;") + assert.Contains(t, types, "system: (variables:") + assert.Contains(t, types, "prompt: (variables?:") assert.Contains(t, types, "slug: string;") // Check that it includes all variables with proper optional/default syntax @@ -205,7 +203,6 @@ func TestCodeGenerator_VariableTypes(t *testing.T) { types := codeGen.GenerateTypeScriptTypes() // Check that it includes all variable types with proper optional/default syntax - assert.Contains(t, types, "variables?: {") assert.Contains(t, types, "legacy?: string") assert.Contains(t, types, "new?: string | \"default\"") assert.Contains(t, types, "required: string")