diff --git a/.claude/skills/agentmail b/.claude/skills/agentmail deleted file mode 120000 index 63f635b..0000000 --- a/.claude/skills/agentmail +++ /dev/null @@ -1 +0,0 @@ -../../.agents/skills/agentmail \ No newline at end of file diff --git a/.claude/skills/tpmjs-tool-creator/SKILL.md b/.claude/skills/tpmjs-tool-creator/SKILL.md deleted file mode 100644 index 38d0c7b..0000000 --- a/.claude/skills/tpmjs-tool-creator/SKILL.md +++ /dev/null @@ -1,296 +0,0 @@ ---- -name: tpmjs-tool-creator -description: Guide for creating official TPMJS tools using the blocks CLI. Use when a user wants to create a new tool for the TPMJS registry, add a tool to packages/tools/official/, implement an AI SDK v6 tool, define a block in blocks.yml, validate a tool with `pnpm blocks run`, or publish a tool to npm with the tpmjs keyword. ---- - -# TPMJS Tool Creator - -Create production-ready tools for the TPMJS registry using the blocks CLI. Tools are npm packages following the AI SDK v6 pattern, validated by blocks, and automatically synced to tpmjs.com. - -## Workflow - -1. Define the tool block in `packages/tools/official/blocks.yml` -2. Create the tool package directory -3. Implement the tool using AI SDK v6 `tool()` + `jsonSchema()` -4. Validate with `pnpm blocks run ` -5. Build and publish to npm - -## Step 1: Define in blocks.yml - -Add to the `blocks:` section of `packages/tools/official/blocks.yml`: - -```yaml -blocks: - category.toolName: - type: utility - description: "LLM-friendly description of what the tool does" - path: "tool-directory-name" - domain_rules: - - id: rule_name - description: "What this implementation must do" - inputs: - - name: inputName - type: string - description: "Description for LLMs" - - name: optionalInput - type: number - optional: true - description: "Optional parameter" - outputs: - - name: result - type: ResultType - description: "What the tool returns" - measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] -``` - -**Category prefix** (before the dot): `research`, `web`, `data`, `documentation`, `engineering`, `security`, `statistics`, `ops`, `agent`, `sandbox`, `utilities`, `html`, `compliance`, `finance`, `legal`, `hr`, `marketing`, `cx`, `edu`, `sales`. - -For domain entities and quality measures, see [references/domain.md](references/domain.md). - -## Step 2: Create Package Directory - -Create `packages/tools/official//`: - -``` -/ -├── package.json -├── tsconfig.json -├── tsup.config.ts -├── README.md -└── src/ - └── index.ts -``` - -**package.json:** -```json -{ - "name": "@tpmjs/official-", - "version": "0.1.0", - "description": "Short description", - "type": "module", - "keywords": ["tpmjs", "", "ai"], - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - }, - "files": ["dist"], - "scripts": { - "build": "tsup", - "dev": "tsup --watch", - "type-check": "tsc --noEmit", - "clean": "rm -rf dist .turbo" - }, - "devDependencies": { - "@tpmjs/tsconfig": "workspace:*", - "tsup": "^8.5.1", - "typescript": "^5.9.3" - }, - "dependencies": { - "ai": "6.0.49" - }, - "publishConfig": { "access": "public" }, - "repository": { - "type": "git", - "url": "https://github.com/tpmjs/tpmjs.git", - "directory": "packages/tools/official/" - }, - "homepage": "https://tpmjs.com", - "license": "MIT", - "tpmjs": { - "category": "", - "frameworks": ["vercel-ai"], - "tools": [ - { - "name": "toolName", - "description": "Clear description (20+ chars)." - } - ] - } -} -``` - -**tsconfig.json:** -```json -{ - "extends": "@tpmjs/tsconfig/react-library.json", - "compilerOptions": { "outDir": "dist", "rootDir": "src" }, - "include": ["src/**/*.ts"], - "exclude": ["node_modules", "dist"] -} -``` - -**tsup.config.ts:** -```typescript -import { defineConfig } from 'tsup'; - -export default defineConfig({ - entry: ['src/index.ts'], - format: ['esm'], - dts: true, - clean: true, - sourcemap: true, - target: 'es2022', -}); -``` - -## Step 3: Implement the Tool - -Every tool follows this AI SDK v6 pattern in `src/index.ts`: - -```typescript -import { jsonSchema, tool } from 'ai'; - -interface MyToolInput { - param1: string; - param2?: number; -} - -export interface MyToolResult { - data: string; - metadata: { processedAt: string }; -} - -export const myTool = tool({ - description: 'Clear LLM-friendly description of what this tool does.', - parameters: jsonSchema({ - type: 'object', - properties: { - param1: { - type: 'string', - description: 'What param1 is for', - }, - param2: { - type: 'number', - description: 'Optional: what param2 is for', - }, - }, - required: ['param1'], - additionalProperties: false, - }), - execute: async (input): Promise => { - if (!input.param1) { - throw new Error('param1 is required and must be non-empty'); - } - - try { - const result = await processData(input.param1); - return { - data: result, - metadata: { processedAt: new Date().toISOString() }, - }; - } catch (error) { - throw new Error( - `Failed to process: ${error instanceof Error ? error.message : String(error)}` - ); - } - }, -}); - -export default myTool; -``` - -**Hard rules:** -- No stubs, TODOs, or placeholders — every tool must be fully working -- Single-shot: one call in, one structured result out -- Validate inputs before processing -- Try-catch with descriptive errors including context -- `additionalProperties: false` on jsonSchema -- Description on every schema property -- Export as both named and default export -- Output interface must be exported - -### Multi-Tool Packages - -For packages with multiple tools, add root-level files: - -**block.ts:** -```typescript -import { toolA, toolB } from './src/index.js'; -export const block = { name: 'package-name', tools: { toolA, toolB } }; -export default block; -``` - -**index.ts (root):** -```typescript -export * from './src/index.js'; -export { default } from './src/index.js'; -``` - -Each tool gets its own entry in blocks.yml (same `path`) and in `tpmjs.tools` array. - -## Step 4: Validate - -The blocks CLI domain validator requires an OpenAI API key. Source it from `.env.local` before running: - -```bash -cd packages/tools/official - -# Load the OpenAI API key for domain validation -source ../../../.env.local -export OPENAI_API_KEY - -pnpm blocks run # Validate (schema → shape → domain) -pnpm blocks run --force # Force full validation (skip cache) -pnpm blocks run --json # JSON output for debugging -pnpm blocks run --all # Validate all tools -``` - -**Common errors:** -- `Tool "X" not found in exports` → Export name must match blocks.yml -- `Required file not found` → Check package root has all required files -- `invalid tpmjs field` → Category must be valid, tools array required - -## Step 5: Build and Publish - -```bash -pnpm --filter=@tpmjs/official- build -cd packages/tools/official/ && npm publish --access public -``` - -The tool syncs to tpmjs.com automatically via the changes feed (every 2 min) and keyword search (every 15 min). To trigger immediately: - -```bash -source apps/web/.env.local -curl -X POST https://tpmjs.com/api/sync/keyword \ - -H "Authorization: Bearer $CRON_SECRET" -``` - -## README Template - -Every tool needs a README: - -```markdown -# @tpmjs/official- - -Short description. - -## Installation - -npm install @tpmjs/official- - -## Usage - -\`\`\`typescript -import { myTool } from '@tpmjs/official-'; - -const result = await myTool.execute({ param1: 'example' }); -\`\`\` - -## Parameters - -| Name | Type | Required | Description | -|--------|--------|----------|--------------------| -| param1 | string | Yes | What param1 is for | - -## Output - -| Field | Type | Description | -|-------|--------|----------------------| -| data | string | The processed result | - -## License - -MIT -``` diff --git a/.claude/skills/tpmjs-tool-creator/references/domain.md b/.claude/skills/tpmjs-tool-creator/references/domain.md deleted file mode 100644 index db62b0f..0000000 --- a/.claude/skills/tpmjs-tool-creator/references/domain.md +++ /dev/null @@ -1,58 +0,0 @@ -# Domain Reference - -## Entities - -Reusable output types defined in blocks.yml. Reference these in your tool's output `type` field. - -| Entity | Fields | -|--------|--------| -| url | href, domain, protocol, path, query, fragment | -| webpage | url, title, html, text, metadata | -| text_content | raw, sentences, paragraphs, wordCount | -| claim | statement, confidence, needsCitation, category | -| timeline | events, dateRange, gaps, eventCount | -| evidence | source, type, strength, relevance | -| summary | text, keyPoints, length, compressionRatio | -| sentiment | score, label, confidence, aspects | -| entity | name, type, mentions, context | -| relationship | source, target, type, strength | -| pattern | name, frequency, examples, significance | -| anomaly | description, severity, context, recommendation | -| metric | name, value, unit, trend | -| comparison | items, criteria, rankings, analysis | -| recommendation | action, priority, rationale, impact | -| risk | description, likelihood, impact, mitigation | -| code_snippet | language, code, explanation, complexity | -| api_endpoint | method, path, parameters, response | -| data_schema | fields, types, constraints, relationships | -| workflow_step | action, input, output, conditions | - -## Quality Measures - -Reference these in your output's `measures` array. - -| Measure | Severity | What it checks | -|---------|----------|---------------| -| working_implementation | error | No TODOs, stubs, or placeholders. Returns actual computed values. | -| valid_output_structure | error | Returns object matching declared interface. All required fields present. Arrays never undefined. | -| proper_error_handling | error | Throws descriptive Error with context. Validates inputs. Catches external API errors. | -| ai_sdk_compliance | error | Uses `tool()` + `jsonSchema()` from 'ai'. Clear description. Every property has description. | -| npm_publishable | error | Valid package.json with tpmjs field. Named + default exports. Proper types. Semver version. | -| readme_documentation | error | README exists. Describes tool. Usage example. Documents inputs/outputs. | -| deterministic_output | warning | Same input produces same output (where applicable). | -| minimal_dependencies | warning | Uses stable, well-maintained packages. Avoids unnecessary deps. | - -## Domain Rules - -Common domain rule categories for the `domain_rules` field in blocks.yml: - -- **Core implementation**: working code, proper types, error handling -- **Web & fetch**: URL validation, content extraction, timeout handling -- **Document generation**: format compliance, template rendering -- **Data transformation**: schema validation, type coercion, encoding -- **Engineering/code analysis**: AST parsing, complexity metrics -- **Security & compliance**: input sanitization, safe execution -- **Statistical rigor**: numerical accuracy, proper rounding -- **Workflow/recipe**: step sequencing, state management - -Define custom rules specific to your tool's requirements. Each rule needs an `id` and `description`. diff --git a/.github/workflows/build-omega-mac.yml b/.github/workflows/build-omega-mac.yml deleted file mode 100644 index f987110..0000000 --- a/.github/workflows/build-omega-mac.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Build Omega Mac - -on: - workflow_dispatch: - -jobs: - build: - runs-on: macos-15 - steps: - - uses: actions/checkout@v4 - - - name: Select Xcode - run: sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer - - - name: Resolve dependencies - working-directory: apps/omega-mac - run: swift package resolve - - - name: Build release - working-directory: apps/omega-mac - run: swift build -c release - - - name: Package .app bundle - working-directory: apps/omega-mac - run: | - mkdir -p OmegaMac.app/Contents/MacOS - mkdir -p OmegaMac.app/Contents/Resources - cp .build/release/OmegaMac OmegaMac.app/Contents/MacOS/OmegaMac - - cat > OmegaMac.app/Contents/Info.plist << 'PLIST' - - - - - CFBundleExecutable - OmegaMac - CFBundleIdentifier - com.tpmjs.omega-mac - CFBundleName - Omega - CFBundleDisplayName - Omega - CFBundleVersion - 1 - CFBundleShortVersionString - 1.0.0 - CFBundlePackageType - APPL - LSMinimumSystemVersion - 14.0 - NSHighResolutionCapable - - com.apple.security.app-sandbox - - com.apple.security.network.client - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - - - - PLIST - - codesign --force --sign - OmegaMac.app - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: OmegaMac - path: apps/omega-mac/OmegaMac.app diff --git a/.github/workflows/sync-enrich.yml b/.github/workflows/sync-enrich.yml deleted file mode 100644 index bef72fc..0000000 --- a/.github/workflows/sync-enrich.yml +++ /dev/null @@ -1,114 +0,0 @@ -name: Sync Tool Enrichment - -on: - schedule: - # Run every 2 minutes - - cron: '*/2 * * * *' - workflow_dispatch: - -jobs: - sync-enrich: - runs-on: ubuntu-latest - steps: - - name: Trigger enrichment sync - id: sync - run: | - # Call the sync API and capture response - response=$(curl -X POST "${{ secrets.VERCEL_PRODUCTION_URL }}/api/sync/enrich" \ - -H "Authorization: Bearer ${{ secrets.CRON_SECRET }}" \ - -H "Content-Type: application/json" \ - -f -s -S) - - echo "Response: $response" - - # Extract data using jq - enriched=$(echo "$response" | jq -r '.data.enriched') - discovered=$(echo "$response" | jq -r '.data.discovered') - skipped=$(echo "$response" | jq -r '.data.skipped') - errors=$(echo "$response" | jq -r '.data.errors') - durationMs=$(echo "$response" | jq -r '.data.durationMs') - - # Extract and display error messages - errorMessages=$(echo "$response" | jq -r '.data.errorMessages[]?' 2>/dev/null || echo "") - - if [ -n "$errorMessages" ]; then - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "⚠️ ENRICHMENT ERRORS ($errors total):" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "$response" | jq -r '.data.errorMessages[]?' | while IFS= read -r error; do - echo " • $error" - done - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - fi - - # Set outputs for Discord notification - echo "enriched=$enriched" >> $GITHUB_OUTPUT - echo "discovered=$discovered" >> $GITHUB_OUTPUT - echo "skipped=$skipped" >> $GITHUB_OUTPUT - echo "errors=$errors" >> $GITHUB_OUTPUT - echo "durationMs=$durationMs" >> $GITHUB_OUTPUT - - # Store error messages for Discord (first 3, truncated) - if [ "$errors" -gt 0 ]; then - errorSummary=$(echo "$response" | jq -r '.data.errorMessages[0:3]? | join("\n• ")' 2>/dev/null || echo "") - if [ -n "$errorSummary" ]; then - echo "• $errorSummary" > /tmp/error_summary.txt - fi - fi - - # Determine status emoji - if [ "$errors" -gt 0 ]; then - echo "status_emoji=⚠️" >> $GITHUB_OUTPUT - echo "status_color=16776960" >> $GITHUB_OUTPUT # Yellow - else - echo "status_emoji=✅" >> $GITHUB_OUTPUT - echo "status_color=5763719" >> $GITHUB_OUTPUT # Green - fi - - - name: Send Discord notification - if: always() - run: | - # Format duration - duration_sec=$(echo "scale=2; ${{ steps.sync.outputs.durationMs }} / 1000" | bc) - - # Build Discord payload using jq for proper JSON escaping - error_text="" - - if [ -f /tmp/error_summary.txt ] && [ ${{ steps.sync.outputs.errors }} -gt 0 ]; then - error_text=$(cat /tmp/error_summary.txt | head -c 800) - fi - - # Build fields array dynamically - base_fields='[ - { "name": "🔧 Enriched", "value": "${{ steps.sync.outputs.enriched }}", "inline": true }, - { "name": "🔍 Discovered", "value": "${{ steps.sync.outputs.discovered }}", "inline": true }, - { "name": "⏭️ Skipped", "value": "${{ steps.sync.outputs.skipped }}", "inline": true }, - { "name": "❌ Errors", "value": "${{ steps.sync.outputs.errors }}", "inline": true }, - { "name": "⏱️ Duration", "value": "'"${duration_sec}s"'", "inline": true }, - { "name": "🔗 Run", "value": "[View Logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})", "inline": true } - ]' - - # Create payload with dynamic fields - payload=$(jq -n \ - --arg title "${{ steps.sync.outputs.status_emoji }} Tool Enrichment Sync" \ - --argjson color ${{ steps.sync.outputs.status_color }} \ - --argjson baseFields "$base_fields" \ - --arg error_text "$error_text" \ - --arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%S.000Z)" \ - ' - { - embeds: [{ - title: $title, - color: $color, - fields: ( - $baseFields + - (if $error_text != "" then [{ name: "🔍 Error Details", value: ("```\n" + $error_text + "\n```"), inline: false }] else [] end) - ), - timestamp: $timestamp - }] - }') - - # Send to Discord - curl -X POST "${{ secrets.DISCORD_WEBHOOK }}" \ - -H "Content-Type: application/json" \ - -d "$payload" diff --git a/.gitignore b/.gitignore index c826f12..2d7c8f1 100644 --- a/.gitignore +++ b/.gitignore @@ -63,22 +63,6 @@ secrets.json # ide .idea -.agent/ -.agents/ -.continue/ -.cursor/ -.windsurf/ - -# temporary analysis docs -COMPREHENSIVE_ANALYSIS.md -PLAN.md -REGISTRY_TOOLS_ANALYSIS.md -USER_ACCOUNT_ANALYSIS_*.md -*.skill - -# symlinked skill dirs (source is .agents/ which is gitignored) -/skills/ -.claude/skills/skill-creator # storybook storybook-static @@ -88,4 +72,3 @@ storybook-static !.changeset/README.md .vercel packages/tool-ideas/data/tools-export.json -.env*.local diff --git a/CLAUDE.md b/CLAUDE.md index 5af27cd..6995eac 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,144 +1,1170 @@ -## Project Overview -Turborepo monorepo. pnpm workspaces. Next.js 16 App Router (`apps/web`). PostgreSQL via Prisma (`packages/db`). Deployed on Vercel. Database on Neon (via Railway for some services). +## Monorepo Setup -## Architecture Rules +This project uses a Turborepo monorepo architecture with the following structure: -1. **Use `@tpmjs/ui` components** — never raw HTML `