diff --git a/.claude/skills/development-workflow/SKILL.md b/.claude/skills/development-workflow/SKILL.md index 30c4eaf7..39c9e485 100644 --- a/.claude/skills/development-workflow/SKILL.md +++ b/.claude/skills/development-workflow/SKILL.md @@ -1,6 +1,8 @@ --- name: development-workflow -description: Build, development, and code quality commands for StackOne SDK +description: Build commands, testing, linting, git workflow, commit conventions, and file naming standards. (project) +globs: "" +alwaysApply: true --- # Development Workflow @@ -107,7 +109,29 @@ feat(parser): add support for custom parameter transformers 4. Verify with `git status` before committing ### TypeScript Issues -Use the TypeScript exhaustiveness pattern (`satisfies never`) when branching on unions. See `openapi-architecture` skill for examples. +Use the TypeScript exhaustiveness pattern (`satisfies never`) when branching on unions. See `typescript-patterns` skill for examples. + +## Pull Request Guidelines + +### PR Title Format +Use the same format as commit messages: `type(scope): description` + +Types: `feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `ci`, `perf` + +Examples: +- `feat(tools): add support for custom OpenAPI specs` +- `fix(parser): handle empty response bodies` +- `refactor(skills): unify cursor rules and claude skills` + +### PR Body +Include: +- **Summary**: 1-3 bullet points describing changes +- **Test plan**: How to verify the changes work +- Reference related issues with `Closes #123` or `Fixes #123` + +## File Naming Conventions + +- Use `.yaml` extension instead of `.yml` for all YAML files (e.g., `lefthook.yaml`, GitHub Actions workflows) ## Working with Tools diff --git a/.claude/skills/file-operations/SKILL.md b/.claude/skills/file-operations/SKILL.md index 98864e3f..7fa09e8c 100644 --- a/.claude/skills/file-operations/SKILL.md +++ b/.claude/skills/file-operations/SKILL.md @@ -1,6 +1,8 @@ --- name: file-operations -description: HTTP request standards for StackOne SDK +description: Use when making HTTP requests. Covers native fetch API patterns and error handling. (project) +globs: "*.ts" +alwaysApply: false --- # HTTP Request Standards diff --git a/.claude/skills/orama-integration/SKILL.md b/.claude/skills/orama-integration/SKILL.md index 52f2d33e..519170ea 100644 --- a/.claude/skills/orama-integration/SKILL.md +++ b/.claude/skills/orama-integration/SKILL.md @@ -1,6 +1,8 @@ --- name: orama-integration -description: Orama API integration reference for StackOne +description: Use when integrating with Orama. Links to official docs for search, indexing, answer engine. (project) +globs: "" +alwaysApply: false --- # Orama Integration diff --git a/.claude/skills/typescript-patterns/SKILL.md b/.claude/skills/typescript-patterns/SKILL.md index 40465f4d..8d512129 100644 --- a/.claude/skills/typescript-patterns/SKILL.md +++ b/.claude/skills/typescript-patterns/SKILL.md @@ -1,11 +1,13 @@ --- name: typescript-patterns -description: TypeScript patterns and best practices for StackOne SDK +description: Use when writing or reviewing TypeScript code. Covers type safety, exhaustiveness checks, avoiding any/non-null assertions, clean code practices. (project) +globs: "*.ts" +alwaysApply: false --- # TypeScript Patterns and Best Practices -This skill provides guidance on TypeScript patterns and best practices for writing clean, type-safe code in the StackOne SDK. +Guidelines for writing clean, type-safe TypeScript code in this repository. ## Exhaustiveness Checking with `satisfies never` @@ -207,12 +209,20 @@ function removeProperty(obj: Record): void { } ``` -## Recommendations +## Remove Unused Code -1. Use `satisfies never` for all union type switches -2. Prefer `unknown` over `any` and use type guards -3. Use optional chaining (`?.`) and nullish coalescing (`??`) +After refactoring, always remove unused code: +- Delete unused variables, parameters, functions, classes, imports +- Don't comment out old code - delete it (git history preserves it) +- Remove unreachable code paths + +## Quick Reference + +1. Use `satisfies never` for union type switches +2. Prefer `unknown` over `any` with type guards +3. Use `?.` and `??` instead of non-null assertions 4. Always specify return types -5. Use destructuring for immutable property removal -6. Write functions as simple exports, not class static methods -7. Create new variables instead of reassigning parameters +5. Use destructuring for property removal +6. Use simple exports instead of static-only classes +7. Don't reassign parameters - create new variables +8. Remove unused code after refactoring diff --git a/.claude/skills/typescript-testing/SKILL.md b/.claude/skills/typescript-testing/SKILL.md index 6ffb5ab1..05e4ae74 100644 --- a/.claude/skills/typescript-testing/SKILL.md +++ b/.claude/skills/typescript-testing/SKILL.md @@ -1,6 +1,8 @@ --- name: typescript-testing -description: Vitest test runner and MSW-based testing patterns for StackOne SDK +description: Use when writing or running tests. Covers Vitest commands, MSW HTTP mocking, fs-fixture for file system tests. (project) +globs: "*.spec.ts" +alwaysApply: false --- # TypeScript Testing with Vitest and MSW diff --git a/.cursor/rules/clean-code.mdc b/.cursor/rules/clean-code.mdc deleted file mode 100644 index 456741ce..00000000 --- a/.cursor/rules/clean-code.mdc +++ /dev/null @@ -1,119 +0,0 @@ ---- -description: Standards for maintaining clean, readable, and maintainable code in the repository. -globs: src/**/*.ts -alwaysApply: false ---- -# Clean Code Standards - -Standards for maintaining clean, readable, and maintainable code in the repository. - - -name: remove_unused_code -description: Always remove unused code after refactoring to maintain a clean codebase - -filters: - - type: path - pattern: "^src/.*\\.ts$" - -actions: - - type: suggest - message: | - After refactoring, always remove unused code to maintain a clean and maintainable codebase: - - 1. Remove unused: - - Variables, parameters, and function arguments - - Functions, methods, and classes - - Imports and exports - - Properties in interfaces and types - - Commented-out code blocks - - 2. Benefits of removing unused code: - - Reduces cognitive load when reading code - - Prevents confusion about which code paths are actually used - - Improves maintainability by reducing the surface area of the codebase - - Makes the codebase easier to understand for new contributors - - Prevents "dead" code from being accidentally reactivated - - 3. Tools to help identify unused code: - - TypeScript compiler with `noUnusedLocals` and `noUnusedParameters` flags - - ESLint with rules like `no-unused-vars` and `no-dead-code` - - IDE features that gray out unused code - - Code coverage tools to identify untested (potentially unused) code paths - - 4. When refactoring: - - Don't comment out old code - delete it (git history preserves it) - - Remove any code paths that are no longer reachable - - Delete any functions that are no longer called - - Remove any parameters that are no longer used - - Clean up imports that are no longer needed - -examples: - - input: | - import { parseJson, formatJson, validateJson } from './utils'; - - function processData(data: string, options: { validate: boolean }): object { - // Old implementation - // const parsed = JSON.parse(data); - // return parsed; - - const parsed = parseJson(data); - - // We might need validation later - // if (options.validate) { - // validateJson(parsed); - // } - - return parsed; - } - output: | - import { parseJson } from './utils'; - - function processData(data: string): object { - const parsed = parseJson(data); - return parsed; - } - - - input: | - class DataProcessor { - private cache: Map = new Map(); - private logger: Logger; - private config: Config; - - constructor(logger: Logger, config: Config) { - this.logger = logger; - this.config = config; - } - - process(data: string): object { - const result = JSON.parse(data); - return result; - } - - // Old method, no longer used after refactoring - // cacheResult(key: string, data: any): void { - // this.cache.set(key, data); - // this.logger.debug(`Cached data for key: ${key}`); - // } - } - output: | - class DataProcessor { - private logger: Logger; - - constructor(logger: Logger) { - this.logger = logger; - } - - process(data: string): object { - const result = JSON.parse(data); - return result; - } - } - -metadata: - priority: high - version: 1.0 - tags: - - clean-code - - refactoring - - best-practices - \ No newline at end of file diff --git a/.cursor/rules/cursor-rules-location.mdc b/.cursor/rules/cursor-rules-location.mdc deleted file mode 100644 index 4d1bd9f7..00000000 --- a/.cursor/rules/cursor-rules-location.mdc +++ /dev/null @@ -1,75 +0,0 @@ ---- -description: Standards for placing Cursor rule files in the correct -globs: *.mdc ---- -# Cursor Rules Location - -Rules for placing and organizing Cursor rule files in the repository. - - -name: cursor_rules_location -description: Standards for placing Cursor rule files in the correct directory -filters: - # Match any .mdc files - - type: file_extension - pattern: "\\.mdc$" - # Match files that look like Cursor rules - - type: content - pattern: "(?s).*?" - # Match file creation events - - type: event - pattern: "file_create" - -actions: - - type: reject - conditions: - - pattern: "^(?!\\.\\/\\.cursor\\/rules\\/.*\\.mdc$)" - message: "Cursor rule files (.mdc) must be placed in the .cursor/rules directory" - - - type: suggest - message: | - When creating Cursor rules: - - 1. Always place rule files in PROJECT_ROOT/.cursor/rules/: - ``` - .cursor/rules/ - ├── your-rule-name.mdc - ├── another-rule.mdc - └── ... - ``` - - 2. Follow the naming convention: - - Use kebab-case for filenames - - Always use .mdc extension - - Make names descriptive of the rule's purpose - - 3. Directory structure: - ``` - PROJECT_ROOT/ - ├── .cursor/ - │ └── rules/ - │ ├── your-rule-name.mdc - │ └── ... - └── ... - ``` - - 4. Never place rule files: - - In the project root - - In subdirectories outside .cursor/rules - - In any other location - -examples: - - input: | - # Bad: Rule file in wrong location - rules/my-rule.mdc - my-rule.mdc - .rules/my-rule.mdc - - # Good: Rule file in correct location - .cursor/rules/my-rule.mdc - output: "Correctly placed Cursor rule file" - -metadata: - priority: high - version: 1.0 - \ No newline at end of file diff --git a/.cursor/rules/development-workflow.mdc b/.cursor/rules/development-workflow.mdc new file mode 120000 index 00000000..ae7c011d --- /dev/null +++ b/.cursor/rules/development-workflow.mdc @@ -0,0 +1 @@ +../../.claude/skills/development-workflow/SKILL.md \ No newline at end of file diff --git a/.cursor/rules/examples-standards.mdc b/.cursor/rules/examples-standards.mdc deleted file mode 100644 index 20ede611..00000000 --- a/.cursor/rules/examples-standards.mdc +++ /dev/null @@ -1,112 +0,0 @@ ---- -description: Standards for creating and maintaining examples in the StackOne repository -globs: examples/* -alwaysApply: false ---- -# Examples Standards - -Standards for creating and maintaining examples in the StackOne repository. - - -name: examples_standards -description: Standards for creating and maintaining examples for all functionality - -filters: - - type: path - pattern: "^examples/.*" - -actions: - - type: suggest - message: | - When working with examples: - - 1. Location Requirements: - ``` - examples/ - ├── basic_usage/ - │ ├── basic_tool_usage.ts # Basic usage examples - │ └── error_handling.ts # Error handling examples - ├── integrations/ # Integration examples - │ ├── openai_integration.ts - │ └── other_integration.ts - └── README.md # Examples documentation - ``` - - 2. Example Requirements: - - Every public function/class needs at least one example - - Examples should be runnable TypeScript scripts - - Use Node's assert module for validation instead of console.logs - - Include proper error handling with try/catch blocks - - Include TypeScript return types for all functions - - Follow the same code style as the main codebase - - Exit with non-zero code on error using process.exit(1) - - 3. Documentation: - - Each example file should start with a docstring explaining its purpose - - Use assertions to validate expected behavior - - Document any prerequisites (environment variables, etc) - - 4. Testing: - - Examples should be tested as part of CI - - Examples should work with the latest package version - - Use assertions to verify expected behavior - -examples: - - input: | - /** - * Example showing basic usage of StackOneToolSet. - */ - - import assert from 'node:assert'; - import { StackOneToolSet } from '../src'; - - const exampleFunction = async (): Promise => { - // Initialize the toolset - const toolset = new StackOneToolSet(); - - // Get tools and verify - const tools = toolset.getTools('hris_*'); - assert(tools.length > 0, 'Expected to find HRIS tools'); - - // Use a specific tool - const employeeTool = tools.getTool('hris_list_employees'); - assert(employeeTool !== undefined, 'Expected to find tool'); - - // Execute and verify result - const result = await employeeTool.execute(); - assert(Array.isArray(result), 'Expected result to be an array'); - }; - - // Run the example - exampleFunction().catch((error) => { - console.error('Error:', error); - process.exit(1); - }); - output: "Correctly structured example" - - - input: | - // Bad example - missing assertions, error handling, types - import { StackOneToolSet } from '../src'; - - const badExample = async () => { - const toolset = new StackOneToolSet(); - const tools = toolset.getTools("hris_*"); - const tool = tools.getTool("hris_list_employees"); - - if (tool) { - const result = await tool.execute(); - console.log(result); - } - }; - - badExample(); - output: "Incorrectly structured example" - -metadata: - priority: high - version: 1.0 - tags: - - examples - - documentation - - testing - \ No newline at end of file diff --git a/.cursor/rules/file-operations.mdc b/.cursor/rules/file-operations.mdc new file mode 120000 index 00000000..c5030a9b --- /dev/null +++ b/.cursor/rules/file-operations.mdc @@ -0,0 +1 @@ +../../.claude/skills/file-operations/SKILL.md \ No newline at end of file diff --git a/.cursor/rules/json-schema-handling.mdc b/.cursor/rules/json-schema-handling.mdc deleted file mode 100644 index 6dbf5772..00000000 --- a/.cursor/rules/json-schema-handling.mdc +++ /dev/null @@ -1,110 +0,0 @@ ---- -description: Standards for working with JSON Schema types in the repository. -globs: *.ts -alwaysApply: false ---- -# JSON Schema Handling - -Standards for working with JSON Schema types in the repository. - - -name: json_schema_handling -description: Standards for properly handling JSON Schema types to avoid type errors - -filters: - - type: path - pattern: "^src/.*\\.ts$" - -actions: - - type: suggest - message: | - When working with JSON Schema types, follow these guidelines to avoid type errors: - - 1. Understanding JSONSchema7Definition type: - - `JSONSchema7Definition` is a union type that can be either a `JSONSchema7` object or a boolean - - It does NOT accept `undefined` as a valid value - - Setting a property to `undefined` will cause a type error - - 2. Handling the conflict between TypeScript types and linter rules: - - The TypeScript type system doesn't allow setting a `JSONSchema7Definition` property to `undefined` - - However, linters like Biome may prefer using `undefined` assignment over the `delete` operator for performance - - To satisfy both, use one of these approaches: - - 3. Recommended approaches for removing JSON Schema properties: - - Use type assertion when setting to undefined: `obj.properties['prop'] = undefined as unknown as JSONSchema7Definition` - - Create a new object without the property using destructuring: `const { propToRemove, ...rest } = obj.properties` - - Use a helper function that handles the type casting internally - - For complex objects, consider using a deep clone and filter approach - - 4. When modifying schema objects: - - Create a new object with spread syntax rather than modifying in place when possible - - Use proper type guards to ensure type safety - - Consider using a deep clone for complex nested schemas to avoid reference issues - - Always check if a property exists before attempting to modify it - -examples: - - input: | - // Problematic: Type error with undefined, linter warning with delete - function removeSchemaProperty(schema: { properties: Record }): void { - // TypeScript error: Type 'undefined' is not assignable to type 'JSONSchema7Definition' - schema.properties['propertyToRemove'] = undefined; - - // Linter warning: Avoid the delete operator which can impact performance - delete schema.properties['propertyToRemove']; - } - output: | - // Solution 1: Type assertion to satisfy TypeScript - function removeSchemaProperty(schema: { properties: Record }): void { - // Use type assertion to satisfy TypeScript while using undefined (preferred by linters) - schema.properties['propertyToRemove'] = undefined as unknown as JSONSchema7Definition; - } - - // Solution 2: Create a new object without the property - function removeSchemaProperty(schema: { properties: Record }): { properties: Record } { - const { propertyToRemove, ...rest } = schema.properties; - return { ...schema, properties: rest }; - } - - // Solution 3: Helper function approach - function removeJsonSchemaProperty(obj: Record, key: string): void { - // This function encapsulates the type assertion - obj[key] = undefined as unknown as T; - } - - function removeSchemaProperty(schema: { properties: Record }): void { - removeJsonSchemaProperty(schema.properties, 'propertyToRemove'); - } - - - input: | - // Incorrect: Not checking if properties exist - function processSchema(schema: JSONSchema7): void { - const requiredProps = schema.required; - requiredProps.forEach(prop => { - schema.properties[prop].description = 'This property is required'; - }); - } - output: | - // Correct: Checking if properties exist - function processSchema(schema: JSONSchema7): void { - if (!schema.required || !schema.properties) { - return; - } - - schema.required.forEach(prop => { - if (schema.properties && prop in schema.properties) { - const propSchema = schema.properties[prop]; - if (typeof propSchema === 'object' && propSchema !== null) { - propSchema.description = 'This property is required'; - } - } - }); - } - -metadata: - priority: high - version: 1.0 - tags: - - json-schema - - typescript - - type-safety - \ No newline at end of file diff --git a/.cursor/rules/native-fetch.mdc b/.cursor/rules/native-fetch.mdc deleted file mode 100644 index 719b25a4..00000000 --- a/.cursor/rules/native-fetch.mdc +++ /dev/null @@ -1,128 +0,0 @@ ---- -description: how to use fetch -globs: *.ts -alwaysApply: false ---- - # Native Fetch Standards - -Standards for using the native fetch API in the StackOne repository. - - -name: native_fetch_standards -description: Standards for using the native fetch API instead of external fetch implementations - -filters: - - type: path - pattern: "^src/.*\\.ts$" - -actions: - - type: suggest - message: | - When making HTTP requests: - - 1. Use the native fetch API: - - Node.js now includes a built-in fetch API, so external packages like node-fetch are no longer needed - - Do not import fetch from any external packages (e.g., `import fetch from 'node-fetch'`) - - Simply use the globally available `fetch` function - - 2. Error handling: - - Always check `response.ok` before processing the response - - Use try/catch blocks to handle network errors - - Provide meaningful error messages that include the URL and status code - - 3. Response processing: - - Use the appropriate method based on the expected response type: - - `response.json()` for JSON responses - - `response.text()` for text responses - - `response.arrayBuffer()` for binary data - - 4. Request configuration: - - Set appropriate headers (Content-Type, Authorization, etc.) - - Use the correct HTTP method (GET, POST, PUT, DELETE, etc.) - - For JSON requests, use `JSON.stringify()` for the body and set Content-Type to application/json - -examples: - - input: | - import fetch from 'node-fetch'; - - async function fetchData() { - const response = await fetch('https://api.example.com/data'); - return response.json(); - } - output: | - // No import needed for fetch - - async function fetchData() { - const response = await fetch('https://api.example.com/data'); - if (!response.ok) { - throw new Error(`API error: ${response.status} for https://api.example.com/data`); - } - return response.json(); - } - - - input: | - import { default as fetch } from 'node-fetch'; - - async function postData(data) { - const response = await fetch('https://api.example.com/data', { - method: 'POST', - body: JSON.stringify(data), - headers: { 'Content-Type': 'application/json' } - }); - const result = await response.json(); - return result; - } - output: | - async function postData(data) { - try { - const response = await fetch('https://api.example.com/data', { - method: 'POST', - body: JSON.stringify(data), - headers: { 'Content-Type': 'application/json' } - }); - - if (!response.ok) { - throw new Error(`API error: ${response.status} for https://api.example.com/data`); - } - - return await response.json(); - } catch (error) { - throw new Error(`Failed to post data: ${error.message}`); - } - } - - - input: | - import * as nodeFetch from 'node-fetch'; - - async function fetchWithAuth(token) { - const response = await nodeFetch.default('https://api.example.com/protected', { - headers: { 'Authorization': `Bearer ${token}` } - }); - return await response.json(); - } - output: | - async function fetchWithAuth(token) { - try { - const response = await fetch('https://api.example.com/protected', { - headers: { 'Authorization': `Bearer ${token}` } - }); - - if (!response.ok) { - throw new Error(`Auth API error: ${response.status} for https://api.example.com/protected`); - } - - return await response.json(); - } catch (error) { - throw new Error(`Authentication request failed: ${error.message}`); - } - } - -metadata: - priority: high - version: 1.0 - tags: - - fetch - - http - - api - - standards - \ No newline at end of file diff --git a/.cursor/rules/orama-integration.mdc b/.cursor/rules/orama-integration.mdc new file mode 120000 index 00000000..a69e7c34 --- /dev/null +++ b/.cursor/rules/orama-integration.mdc @@ -0,0 +1 @@ +../../.claude/skills/orama-integration/SKILL.md \ No newline at end of file diff --git a/.cursor/rules/pnpm-standards.mdc b/.cursor/rules/pnpm-standards.mdc deleted file mode 100644 index 62592e51..00000000 --- a/.cursor/rules/pnpm-standards.mdc +++ /dev/null @@ -1,48 +0,0 @@ ---- -description: Standards for using pnpm in this repository -globs: -alwaysApply: false ---- - -# pnpm Standards - -## Overview - -This repository uses [pnpm](https://pnpm.io) as the package manager. pnpm is a fast, disk space efficient package manager. - -## Package Management - -- Use `pnpm install` instead of `npm install` to add dependencies -- Use `pnpm-lock.yaml` for lockfiles (automatically generated) -- When adding new dependencies, use: `pnpm add package@x.y.z` - -## TypeScript Configuration - -- TypeScript is configured via `tsconfig.json` -- Use `pnpm typecheck` to run type checking with tsgo - -## Running Scripts - -- Use `pnpm