Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 169 additions & 0 deletions .cursor/rules/code-generation.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
---
description: Code Generation Development Rules for SDK
globs: src/apis/**/*.ts, src/server/*.ts, src/types.ts
alwaysApply: true
---

# Code Generation Development Rules

> **⚠️ IMPORTANT**: These rules work in conjunction with the CLI rules. When updating these SDK rules, also update `cli/.cursor/rules/code-generation.mdc` to keep them in sync.

## Core Principles

### Optional Field Handling
- ✅ Generated code should NEVER require optional chaining (`?.`)
- ✅ Always generate both `system` and `prompt` fields, even if empty
- ✅ Empty fields should return empty strings, not undefined
- ❌ Never generate partial objects that require optional chaining

### Never Modify Generated Content in Source Files
- ❌ NEVER edit files in `src/` that contain generated content
- ❌ NEVER hardcode generated content like `copyWriter` prompts in source files
- ✅ ALWAYS load generated content dynamically at runtime
- ✅ 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<void> {
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');
}
}
```

## 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

### Context Integration
```typescript
// ✅ Good: Load generated content in context creation
export async function createServerContext(req: ServerContextRequest): Promise<AgentContext> {
// ... other initialization

// Load generated content dynamically
await promptAPI.loadPrompts();

return {
// ... other context properties
prompts: () => promptAPI.prompts,
};
}
```

### Type Safety
- Generate TypeScript definitions for generated content
- Use proper type annotations for dynamic imports
- Maintain type safety throughout the loading process

## Common Patterns

### Generated Content API Class
```typescript
export default class GeneratedContentAPI {
public content: typeof defaultContent;

constructor() {
this.content = defaultContent;
}

public async loadContent(): Promise<void> {
// 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
```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
```

### ✅ Do This Instead
```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;
}
```

## 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

## 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. **Install in test project**: `npm install /path/to/sdk-js`
6. **Run bundle command**: `agentuity-cli bundle` to regenerate content
7. **Copy dist to node_modules**: `cp -r dist/* node_modules/@agentuity/sdk/dist/` (if needed)

**Note**: After the first setup, you only need to run step 7 (`cp -r dist/* node_modules/@agentuity/sdk/dist/`) for subsequent changes to avoid reinstalling the entire 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

Remember: The SDK's job is to load generated content dynamically, not contain hardcoded generated content.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @agentuity/sdk Changelog

## 0.0.148

### Patch Changes

- added experimental support for prompts

## 0.0.147

### Patch Changes
Expand Down
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,28 @@ bun test
npm test
```

## Making Changes

To make changes for this repo, do the following in your PR:

```bash
npm run changeset
```

And if you plan to release in the same PR, run:

```bash
npm run version
```

Also run (only if you ran `npm run version`):

```bash
npm install
```

Then commit all files in the PR. When you merge the PR it will release the SDK.

## License

See the [LICENSE](LICENSE.md) file for details.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@agentuity/sdk",
"version": "0.0.147",
"version": "0.0.148",
"description": "The Agentuity SDK for NodeJS and Bun",
"license": "Apache-2.0",
"public": true,
Expand Down Expand Up @@ -96,4 +96,4 @@
"mailparser": "^3.7.4",
"nodemailer": "^7.0.3"
}
}
}
36 changes: 36 additions & 0 deletions src/apis/patchportal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Singleton class for PatchPortal
*/
export default class PatchPortal {
private static instance: PatchPortal | null = null;
private state: Record<string, unknown> = {};

private constructor() {
// Private constructor to prevent direct instantiation
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/**
* Get the singleton instance of PatchPortal
*/
public static async getInstance(): Promise<PatchPortal> {
if (!PatchPortal.instance) {
PatchPortal.instance = new PatchPortal();
}
return PatchPortal.instance;
}

/**
* Example method - you can add your specific functionality here
*/
public async process(key: string, data: unknown): Promise<unknown> {
this.state[key] = data;
return data;
}

/**
* Example method for demonstrating the singleton
*/
public getInstanceId(): string {
return `PatchPortal-${Date.now()}`;
}
Comment on lines +30 to +35

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Misleading method name and behavior.

getInstanceId() returns a different value on each call (Date.now()), which contradicts the expectation of an instance identifier. An instance ID should be stable for the lifetime of the instance.

Consider either:

  1. Generating a stable ID once during construction:
private readonly instanceId: string;

private constructor() {
	this.instanceId = `PatchPortal-${Date.now()}`;
}

public getInstanceId(): string {
	return this.instanceId;
}
  1. Renaming to clarify the behavior:
public getCurrentTimestamp(): string {
	return `PatchPortal-${Date.now()}`;
}
  1. Removing this example method if it's not part of the actual API.
🤖 Prompt for AI Agents
In src/apis/patchportal.ts around lines 42 to 47, the getInstanceId() method
returns a new value each call (using Date.now()), which is misleading for an
instance identifier; change it to generate and store a stable instanceId once
during construction (add a private readonly instanceId set in the constructor,
and have getInstanceId() return that field). If the example method is not
needed, remove it instead—do not leave a method that implies stability but
returns a different value each call.

}
7 changes: 7 additions & 0 deletions src/apis/prompt/generated/_index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const prompts = {
};

// Export types for compatibility
export const PromptConfig = undefined; // Type-only export, value is not used
export const PromptName = undefined; // Type-only export, value is not used
export const PromptsCollection = undefined; // Type-only export, value is not used
5 changes: 5 additions & 0 deletions src/apis/prompt/generated/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// biome-ignore lint/suspicious/noEmptyInterface: <explanation>
export interface PromptsCollection {}
export declare const prompts: PromptsCollection;
export type PromptConfig = any;
export type PromptName = any;
11 changes: 11 additions & 0 deletions src/apis/prompt/generated/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export type PromptConfig = any;
export type PromptName = any;

// This will be replaced by the CLI with the actual generated types
export interface PromptsCollection {
[promptSlug: string]: {
slug: string;
system: { compile: (variables?: Record<string, any>) => string };
prompt: { compile: (variables?: Record<string, any>) => string };
};
}
Loading
Loading