diff --git a/dot_pi/agent/skills/frontend-design/SKILL.md b/dot_agents/skills/frontend-design/SKILL.md similarity index 100% rename from dot_pi/agent/skills/frontend-design/SKILL.md rename to dot_agents/skills/frontend-design/SKILL.md diff --git a/dot_pi/agent/skills/github/SKILL.md b/dot_agents/skills/github/SKILL.md similarity index 100% rename from dot_pi/agent/skills/github/SKILL.md rename to dot_agents/skills/github/SKILL.md diff --git a/dot_pi/agent/skills/improve-skill/SKILL.md b/dot_agents/skills/improve-skill/SKILL.md similarity index 100% rename from dot_pi/agent/skills/improve-skill/SKILL.md rename to dot_agents/skills/improve-skill/SKILL.md diff --git a/dot_pi/agent/skills/improve-skill/scripts/executable_extract-session.js b/dot_agents/skills/improve-skill/scripts/executable_extract-session.js similarity index 100% rename from dot_pi/agent/skills/improve-skill/scripts/executable_extract-session.js rename to dot_agents/skills/improve-skill/scripts/executable_extract-session.js diff --git a/dot_pi/agent/skills/kagi-fastgpt/SKILL.md b/dot_agents/skills/kagi-fastgpt/SKILL.md similarity index 100% rename from dot_pi/agent/skills/kagi-fastgpt/SKILL.md rename to dot_agents/skills/kagi-fastgpt/SKILL.md diff --git a/dot_pi/agent/skills/kagi-fastgpt/dot_gitignore b/dot_agents/skills/kagi-fastgpt/dot_gitignore similarity index 100% rename from dot_pi/agent/skills/kagi-fastgpt/dot_gitignore rename to dot_agents/skills/kagi-fastgpt/dot_gitignore diff --git a/dot_pi/agent/skills/kagi-fastgpt/executable_kagi-fastgpt b/dot_agents/skills/kagi-fastgpt/executable_kagi-fastgpt similarity index 100% rename from dot_pi/agent/skills/kagi-fastgpt/executable_kagi-fastgpt rename to dot_agents/skills/kagi-fastgpt/executable_kagi-fastgpt diff --git a/dot_pi/agent/skills/kagi-fastgpt/go.mod b/dot_agents/skills/kagi-fastgpt/go.mod similarity index 100% rename from dot_pi/agent/skills/kagi-fastgpt/go.mod rename to dot_agents/skills/kagi-fastgpt/go.mod diff --git a/dot_pi/agent/skills/kagi-fastgpt/main.go b/dot_agents/skills/kagi-fastgpt/main.go similarity index 100% rename from dot_pi/agent/skills/kagi-fastgpt/main.go rename to dot_agents/skills/kagi-fastgpt/main.go diff --git a/dot_pi/agent/skills/kagi-search/SKILL.md b/dot_agents/skills/kagi-search/SKILL.md similarity index 100% rename from dot_pi/agent/skills/kagi-search/SKILL.md rename to dot_agents/skills/kagi-search/SKILL.md diff --git a/dot_pi/agent/skills/kagi-search/dot_gitignore b/dot_agents/skills/kagi-search/dot_gitignore similarity index 100% rename from dot_pi/agent/skills/kagi-search/dot_gitignore rename to dot_agents/skills/kagi-search/dot_gitignore diff --git a/dot_pi/agent/skills/kagi-search/executable_kagi-search b/dot_agents/skills/kagi-search/executable_kagi-search similarity index 100% rename from dot_pi/agent/skills/kagi-search/executable_kagi-search rename to dot_agents/skills/kagi-search/executable_kagi-search diff --git a/dot_pi/agent/skills/kagi-search/go.mod b/dot_agents/skills/kagi-search/go.mod similarity index 100% rename from dot_pi/agent/skills/kagi-search/go.mod rename to dot_agents/skills/kagi-search/go.mod diff --git a/dot_pi/agent/skills/kagi-search/go.sum b/dot_agents/skills/kagi-search/go.sum similarity index 100% rename from dot_pi/agent/skills/kagi-search/go.sum rename to dot_agents/skills/kagi-search/go.sum diff --git a/dot_pi/agent/skills/kagi-search/main.go b/dot_agents/skills/kagi-search/main.go similarity index 100% rename from dot_pi/agent/skills/kagi-search/main.go rename to dot_agents/skills/kagi-search/main.go diff --git a/dot_pi/agent/skills/session-analyzer/IDEAS.md b/dot_agents/skills/session-analyzer/IDEAS.md similarity index 100% rename from dot_pi/agent/skills/session-analyzer/IDEAS.md rename to dot_agents/skills/session-analyzer/IDEAS.md diff --git a/dot_pi/agent/skills/session-analyzer/SKILL.md b/dot_agents/skills/session-analyzer/SKILL.md similarity index 100% rename from dot_pi/agent/skills/session-analyzer/SKILL.md rename to dot_agents/skills/session-analyzer/SKILL.md diff --git a/dot_pi/agent/skills/session-analyzer/executable_analyze.js b/dot_agents/skills/session-analyzer/executable_analyze.js similarity index 100% rename from dot_pi/agent/skills/session-analyzer/executable_analyze.js rename to dot_agents/skills/session-analyzer/executable_analyze.js diff --git a/dot_pi/agent/skills/session-analyzer/package-lock.json b/dot_agents/skills/session-analyzer/package-lock.json similarity index 100% rename from dot_pi/agent/skills/session-analyzer/package-lock.json rename to dot_agents/skills/session-analyzer/package-lock.json diff --git a/dot_pi/agent/skills/session-analyzer/package.json b/dot_agents/skills/session-analyzer/package.json similarity index 100% rename from dot_pi/agent/skills/session-analyzer/package.json rename to dot_agents/skills/session-analyzer/package.json diff --git a/dot_pi/agent/skills/summarize/SKILL.md b/dot_agents/skills/summarize/SKILL.md similarity index 100% rename from dot_pi/agent/skills/summarize/SKILL.md rename to dot_agents/skills/summarize/SKILL.md diff --git a/dot_pi/agent/skills/summarize/to-markdown.mjs b/dot_agents/skills/summarize/to-markdown.mjs similarity index 100% rename from dot_pi/agent/skills/summarize/to-markdown.mjs rename to dot_agents/skills/summarize/to-markdown.mjs diff --git a/dot_pi/agent/skills/uv/SKILL.md b/dot_agents/skills/uv/SKILL.md similarity index 100% rename from dot_pi/agent/skills/uv/SKILL.md rename to dot_agents/skills/uv/SKILL.md diff --git a/dot_pi/agent/skills/uv/build.md b/dot_agents/skills/uv/build.md similarity index 100% rename from dot_pi/agent/skills/uv/build.md rename to dot_agents/skills/uv/build.md diff --git a/dot_pi/agent/skills/uv/scripts.md b/dot_agents/skills/uv/scripts.md similarity index 100% rename from dot_pi/agent/skills/uv/scripts.md rename to dot_agents/skills/uv/scripts.md diff --git a/dot_pi/agent/skills/web-browser/SKILL.md b/dot_agents/skills/web-browser/SKILL.md similarity index 100% rename from dot_pi/agent/skills/web-browser/SKILL.md rename to dot_agents/skills/web-browser/SKILL.md diff --git a/dot_pi/agent/skills/web-browser/scripts/cdp.js b/dot_agents/skills/web-browser/scripts/cdp.js similarity index 100% rename from dot_pi/agent/skills/web-browser/scripts/cdp.js rename to dot_agents/skills/web-browser/scripts/cdp.js diff --git a/dot_claude/skills/web_browser/scripts/executable_dismiss-cookies.js b/dot_agents/skills/web-browser/scripts/executable_dismiss-cookies.js similarity index 100% rename from dot_claude/skills/web_browser/scripts/executable_dismiss-cookies.js rename to dot_agents/skills/web-browser/scripts/executable_dismiss-cookies.js diff --git a/dot_claude/skills/web_browser/scripts/executable_eval.js b/dot_agents/skills/web-browser/scripts/executable_eval.js similarity index 100% rename from dot_claude/skills/web_browser/scripts/executable_eval.js rename to dot_agents/skills/web-browser/scripts/executable_eval.js diff --git a/dot_pi/agent/skills/web-browser/scripts/executable_logs-tail.js b/dot_agents/skills/web-browser/scripts/executable_logs-tail.js similarity index 100% rename from dot_pi/agent/skills/web-browser/scripts/executable_logs-tail.js rename to dot_agents/skills/web-browser/scripts/executable_logs-tail.js diff --git a/dot_claude/skills/web_browser/scripts/executable_nav.js b/dot_agents/skills/web-browser/scripts/executable_nav.js similarity index 100% rename from dot_claude/skills/web_browser/scripts/executable_nav.js rename to dot_agents/skills/web-browser/scripts/executable_nav.js diff --git a/dot_pi/agent/skills/web-browser/scripts/executable_net-summary.js b/dot_agents/skills/web-browser/scripts/executable_net-summary.js similarity index 100% rename from dot_pi/agent/skills/web-browser/scripts/executable_net-summary.js rename to dot_agents/skills/web-browser/scripts/executable_net-summary.js diff --git a/dot_claude/skills/web_browser/scripts/executable_pick.js b/dot_agents/skills/web-browser/scripts/executable_pick.js similarity index 100% rename from dot_claude/skills/web_browser/scripts/executable_pick.js rename to dot_agents/skills/web-browser/scripts/executable_pick.js diff --git a/dot_claude/skills/web_browser/scripts/executable_screenshot.js b/dot_agents/skills/web-browser/scripts/executable_screenshot.js similarity index 100% rename from dot_claude/skills/web_browser/scripts/executable_screenshot.js rename to dot_agents/skills/web-browser/scripts/executable_screenshot.js diff --git a/dot_pi/agent/skills/web-browser/scripts/executable_start.js b/dot_agents/skills/web-browser/scripts/executable_start.js similarity index 100% rename from dot_pi/agent/skills/web-browser/scripts/executable_start.js rename to dot_agents/skills/web-browser/scripts/executable_start.js diff --git a/dot_pi/agent/skills/web-browser/scripts/executable_watch.js b/dot_agents/skills/web-browser/scripts/executable_watch.js similarity index 100% rename from dot_pi/agent/skills/web-browser/scripts/executable_watch.js rename to dot_agents/skills/web-browser/scripts/executable_watch.js diff --git a/dot_pi/agent/skills/web-browser/scripts/package-lock.json b/dot_agents/skills/web-browser/scripts/package-lock.json similarity index 100% rename from dot_pi/agent/skills/web-browser/scripts/package-lock.json rename to dot_agents/skills/web-browser/scripts/package-lock.json diff --git a/dot_pi/agent/skills/web-browser/scripts/package.json b/dot_agents/skills/web-browser/scripts/package.json similarity index 100% rename from dot_pi/agent/skills/web-browser/scripts/package.json rename to dot_agents/skills/web-browser/scripts/package.json diff --git a/dot_pi/agent/skills/youtube-transcript/SKILL.md b/dot_agents/skills/youtube-transcript/SKILL.md similarity index 100% rename from dot_pi/agent/skills/youtube-transcript/SKILL.md rename to dot_agents/skills/youtube-transcript/SKILL.md diff --git a/dot_pi/agent/skills/youtube-transcript/executable_transcript.js b/dot_agents/skills/youtube-transcript/executable_transcript.js similarity index 100% rename from dot_pi/agent/skills/youtube-transcript/executable_transcript.js rename to dot_agents/skills/youtube-transcript/executable_transcript.js diff --git a/dot_pi/agent/skills/youtube-transcript/package-lock.json b/dot_agents/skills/youtube-transcript/package-lock.json similarity index 100% rename from dot_pi/agent/skills/youtube-transcript/package-lock.json rename to dot_agents/skills/youtube-transcript/package-lock.json diff --git a/dot_pi/agent/skills/youtube-transcript/package.json b/dot_agents/skills/youtube-transcript/package.json similarity index 100% rename from dot_pi/agent/skills/youtube-transcript/package.json rename to dot_agents/skills/youtube-transcript/package.json diff --git a/dot_claude/skills/claude_code_analyzer/SKILL.md b/dot_claude/skills/claude_code_analyzer/SKILL.md deleted file mode 100644 index 20d16d3b..00000000 --- a/dot_claude/skills/claude_code_analyzer/SKILL.md +++ /dev/null @@ -1,336 +0,0 @@ ---- -name: claude_code_analyzer -description: Analyzes Claude Code usage patterns and provides comprehensive recommendations. Runs usage analysis, discovers GitHub community resources, suggests CLAUDE.md improvements, and fetches latest docs on-demand. Use when user wants to optimize their Claude Code workflow, create configurations (agents/skills/commands), or set up project documentation. ---- - -# Claude Code History Analyzer - -Complete workflow optimization for Claude Code through usage analysis, community discovery, and intelligent configuration generation. - -## Core Capabilities - -This skill provides a complete Claude Code optimization workflow: - -**1. Usage Analysis** - Extracts patterns from Claude Code history - -- Tool usage frequency -- Auto-allowed tools vs actual usage -- Model distribution -- Project activity levels - -**2. GitHub Discovery** - Finds community resources automatically - -- Skills matching your tools -- Agents for your workflows -- Slash commands for common operations -- CLAUDE.md examples from similar projects - -**3. Project Analysis** - Detects tech stack and suggests documentation - -- Package manager and scripts -- Framework and testing setup -- Docker, CI/CD, TypeScript configuration -- Project-specific CLAUDE.md sections - -**4. On-Demand Documentation** - Fetches latest Claude Code docs - -- Agents/subagents structure and configuration -- Skills architecture and bundled resources -- Slash commands with MCP integration -- CLAUDE.md best practices from Anthropic teams -- Settings and environment variables - -## Complete Analysis Workflow - -When user asks to optimize their Claude Code setup, follow this workflow: - -### Step 1: Run Usage Analysis - -```bash -bash scripts/analyze.sh --current-project -``` - -This automatically: - -- Extracts tool usage from JSONL files -- Checks auto-allowed tools configuration -- Analyzes model distribution -- **Searches GitHub for community resources** (always enabled) - -### Step 2: Run Project Analysis - -```bash -bash scripts/analyze-claude-md.sh -``` - -This detects: - -- Package manager (npm, pnpm, yarn, cargo, go, python) -- Framework (Next.js, React, Django, FastAPI, etc.) -- Testing setup (Vitest, Jest, pytest, etc.) -- CI/CD, Docker, TypeScript, linting configuration - -### Step 3: Interpret Combined Results - -Combine insights from both analyses: - -**Usage patterns** show: - -- Tools used frequently but requiring approval → Add to auto-allows -- Auto-allowed tools never used → Remove from config -- Repetitive bash commands → Create slash commands -- Complex workflows → Create dedicated agents -- Domain-specific tasks → Build custom skills - -**GitHub discovery** provides: - -- Similar configurations from community -- Proven patterns for your tool usage -- Example agents/skills/commands to adapt - -**Project analysis** reveals: - -- Required CLAUDE.md sections -- Framework-specific conventions to document -- Testing and build commands to include - -### Step 4: Fetch Docs and Create Configurations - -Based on recommendations, fetch latest docs and create: - -**For frequently used tools** → Update auto-allows: - -```bash -# Fetch settings docs -web_fetch: https://docs.claude.com/en/docs/claude-code/settings -# Update ~/.claude/settings.json -``` - -**For repetitive commands** → Create slash command: - -```bash -# Fetch slash commands docs -web_fetch: https://docs.claude.com/en/docs/claude-code/slash-commands -# Create .claude/commands/[command-name].md -``` - -**For complex workflows** → Create agent: - -```bash -# Fetch agents docs -web_fetch: https://docs.claude.com/en/docs/claude-code/sub-agents -# Create .claude/agents/[agent-name].md -``` - -**For reusable domain knowledge** → Build skill: - -```bash -# Fetch skills docs -web_fetch: https://docs.claude.com/en/docs/agents-and-tools/agent-skills/overview -# Create .claude/skills/[skill-name]/SKILL.md -``` - -**For project context** → Write CLAUDE.md: - -```bash -# Fetch CLAUDE.md best practices -web_fetch: https://www.anthropic.com/engineering/claude-code-best-practices -# Create .claude/CLAUDE.md with detected info -``` - -## Example: Complete Optimization Session - -**User**: "Help me optimize my Claude Code setup" - -**Claude executes**: - -1. **Analyze usage patterns** - - ```bash - bash scripts/analyze.sh --current-project - ``` - - Finds: Bash tool used 150 times, Read 80 times, Write 45 times - Auto-allows: None configured - GitHub: Discovers community skills for TypeScript testing - -2. **Analyze project structure** - - ```bash - bash scripts/analyze-claude-md.sh - ``` - - Detects: Next.js project with Vitest, npm scripts: dev, build, test, lint - Missing: CLAUDE.md doesn't exist - -3. **Make recommendations** - "I analyzed your Claude Code usage. Here's what I recommend: - - **Auto-Allows**: You use Bash (150×), Read (80×), and Write (45×) constantly. Let me add them to auto-allows. - - **Slash Command**: You run tests frequently. I'll create /test command. - - **CLAUDE.md**: Your Next.js project needs documentation. I'll create one with your npm scripts and testing setup. - - **Community Resource**: I found a TypeScript testing skill on GitHub that matches your workflow." - -4. **Fetch docs and create configs** - - Fetch settings docs → Update `~/.claude/settings.json`: - - ```json - { - "autoAllowedTools": ["Bash", "Read", "Write"] - } - ``` - - Fetch slash commands docs → Create `.claude/commands/test.md`: - - ```markdown - --- - name: /test - description: Run tests for current file or project - allowed-tools: [Bash] - --- - - Run tests: !npm test - ``` - - Fetch CLAUDE.md best practices → Create `.claude/CLAUDE.md`: - - ```markdown - # Project Context - - ## Commands - - - Dev: `npm run dev` (port 3000) - - Build: `npm run build` - - Test: `npm test` - - Lint: `npm run lint` - - ## Tech Stack - - - Next.js 14 - - TypeScript - - Vitest for testing - - ## Testing - - Run tests before commits: `npm test` - ``` - -5. **Share GitHub findings** - "I also found this community skill for TypeScript testing that you might find useful: [GitHub link]" - -## When to Use Each Tool - -### Use analyze.sh when: - -- User asks to "analyze my workflow" -- Optimizing Claude Code setup -- Finding unused auto-allows -- Discovering community resources -- Understanding usage patterns - -### Use analyze-claude-md.sh when: - -- Creating CLAUDE.md -- Setting up new project -- User asks "what should I document?" -- Need project-specific recommendations - -### Fetch docs when: - -- Creating any configuration file -- User asks "how do I create an agent/skill/command?" -- Explaining configuration options -- Need current best practices - -### Use GitHub discovery for: - -- Finding proven patterns -- Learning from community -- Getting configuration examples -- Discovering new approaches - -## Critical Documentation URLs - -Always fetch latest docs before creating configurations: - -| Type | URL | -| -------------- | ---------------------------------------------------------------------- | -| Agents | https://docs.claude.com/en/docs/claude-code/sub-agents | -| Skills | https://docs.claude.com/en/docs/agents-and-tools/agent-skills/overview | -| Slash Commands | https://docs.claude.com/en/docs/claude-code/slash-commands | -| Settings | https://docs.claude.com/en/docs/claude-code/settings | -| CLAUDE.md | https://www.anthropic.com/engineering/claude-code-best-practices | - -## Key Configuration Facts (from latest docs) - -**Agents** (.md files with YAML frontmatter): - -- Required: name, description -- Optional: tools (comma-separated), model (sonnet/opus/haiku/inherit) -- Location: `.claude/agents/` (project) or `~/.claude/agents/` (user) -- NOT .yaml files! - -**Skills** (directory with SKILL.md): - -- Structure: `skill-name/SKILL.md` -- Bundled resources: scripts/, references/, assets/ -- Progressive loading: metadata → instructions → resources -- Location: `.claude/skills/` - -**Slash Commands** (.md files): - -- Required: name (with / prefix) -- Arguments: $ARGUMENTS, $1, $2 -- Optional: allowed-tools, model, argument-hint -- Location: `.claude/commands/` - -**CLAUDE.md** (project documentation): - -- Hierarchical: user-level → parent → project → nested -- Include: commands, style guidelines, testing, issues -- Keep concise and actionable -- Location: `.claude/CLAUDE.md` - -## Output Formats - -### Usage Analysis JSON - -```json -{ - "tool_usage": [{"tool": "Bash", "count": 122}], - "auto_allowed_tools": [{"tool": "Read", "usage_count": 49}], - "model_usage": [{"model": "claude-sonnet-4-5-20250929", "count": 634}], - "github_discovery": {"searches": [...]} -} -``` - -### Project Analysis JSON - -```json -{ - "detected_package_manager": { "type": "npm", "scripts": ["dev", "test"] }, - "testing": { "framework": "vitest" }, - "framework": { "type": "nextjs" }, - "claude_md_suggestions": ["Document npm scripts", "Document testing"] -} -``` - -## Requirements - -- `jq` (install: `brew install jq` or `apt install jq`) -- Claude Code projects at `~/.claude/projects` -- Optional: `gh` CLI for direct GitHub search - -## Why This Approach Works - -**Comprehensive**: Combines usage analysis + community discovery + project detection -**Current**: Fetches latest docs on-demand, never stale -**Actionable**: Provides specific, implementable recommendations -**Automated**: GitHub discovery runs automatically, no flags needed -**Integrated**: All tools work together for complete workflow optimization - -When helping users optimize Claude Code, always run both analyses, interpret results together, fetch latest docs, and create configurations with current best practices. diff --git a/dot_claude/skills/claude_code_analyzer/references/best-practices.md b/dot_claude/skills/claude_code_analyzer/references/best-practices.md deleted file mode 100644 index 029012c8..00000000 --- a/dot_claude/skills/claude_code_analyzer/references/best-practices.md +++ /dev/null @@ -1,290 +0,0 @@ -# Claude Code Configuration Best Practices - -This reference contains best practices for creating agents, skills, slash commands, and project context based on Anthropic's documentation. - -## Agents - -**File Format**: `.md` (Markdown) -**Location**: `.claude/agents/` -**Naming**: `agent-name.md` (kebab-case) - -### Agent Structure - -```markdown ---- -name: Agent Name -description: Brief description of what this agent does ---- - -# Agent Name - -## Purpose -Clear statement of the agent's role and when to use it. - -## Capabilities -- Specific task 1 -- Specific task 2 -- Specific task 3 - -## Instructions -Detailed instructions for how the agent should behave, including: -- Context it should consider -- Tools it should use -- Output format expectations -- Edge cases to handle - -## Examples -Concrete examples of how to invoke and use the agent. -``` - -### Agent Best Practices - -1. **Use Markdown format** - Agents are `.md` files, not YAML -2. **Clear naming** - Name reflects the agent's purpose -3. **Specific scope** - Focused on a particular task or domain -4. **Explicit instructions** - Clear about what the agent should do -5. **Tool preferences** - Specify which tools the agent should favor -6. **Context awareness** - Include relevant project/domain knowledge - -### Example Agent - -```markdown ---- -name: TypeScript Test Generator -description: Generates comprehensive test files for TypeScript modules using Vitest ---- - -# TypeScript Test Generator - -## Purpose -Automatically generate test files for TypeScript modules with proper imports, setup, and test cases. - -## Capabilities -- Analyze TypeScript source files -- Generate Vitest test suites -- Include edge cases and error scenarios -- Follow project testing conventions - -## Instructions -1. Read the target TypeScript file -2. Identify exported functions, classes, and types -3. Create a corresponding `.test.ts` file -4. Generate test cases covering: - - Happy path scenarios - - Edge cases - - Error conditions -5. Use proper Vitest syntax and assertions -6. Match project's existing test patterns - -## Examples -"Generate tests for src/utils/parser.ts" -"Create comprehensive test coverage for the User class" -``` - -## Skills - -**File Format**: Directory with `SKILL.md` -**Location**: `.claude/skills/skill-name/` -**Structure**: -``` -.claude/skills/ - my-skill/ - SKILL.md # Required: Skill documentation - scripts/ # Optional: Executable scripts - references/ # Optional: Reference docs - assets/ # Optional: Templates/files -``` - -### SKILL.md Structure - -```markdown ---- -name: skill-name -description: What the skill does and when to use it ---- - -# Skill Name - -## Overview -Brief explanation of what this skill enables. - -## Usage -How to use the skill, including examples. - -## Requirements -Any dependencies or prerequisites. - -## Examples -Concrete usage examples. -``` - -### Skill Best Practices - -1. **Frontmatter required** - Always include `name` and `description` -2. **Modular resources** - Separate scripts, docs, and assets -3. **Self-contained** - Include everything needed to use the skill -4. **Clear documentation** - Explain when and how to use it -5. **Executable scripts** - Make scripts executable (`chmod +x`) - -## Slash Commands - -**File Format**: `.md` (Markdown) -**Location**: `.claude/commands/` -**Naming**: `command-name.md` (kebab-case) - -### Command Structure - -```markdown ---- -name: /command-name -description: Brief description of what this command does ---- - -# Command Name - -## Purpose -What this command accomplishes. - -## Usage -/command-name [arguments] - -## Behavior -Detailed explanation of what happens when the command runs. - -## Examples -- /command-name example1 -- /command-name example2 -``` - -### Slash Command Best Practices - -1. **Markdown format** - Commands are `.md` files -2. **Name prefix** - Include `/` in the name frontmatter -3. **Clear trigger** - Name should match what user types -4. **Concise action** - Commands should do one thing well -5. **Fast execution** - Optimize for quick operations -6. **Argument handling** - Document expected arguments - -### Example Slash Command - -```markdown ---- -name: /test -description: Run tests for the current file or project ---- - -# Test Command - -## Purpose -Quickly run tests for the current file or entire test suite. - -## Usage -- /test - Run all tests -- /test current - Run tests for current file only -- /test watch - Run tests in watch mode - -## Behavior -1. Detect the test framework (Vitest, Jest, etc.) -2. Run appropriate test command -3. Display results inline -4. Highlight failures - -## Examples -- /test - Runs `npm test` -- /test current - Runs tests for the active file -``` - -## Project Context (CLAUDE.md) - -**File Format**: `.md` (Markdown) -**Location**: `.claude/CLAUDE.md` -**Purpose**: Provide project-specific context - -### CLAUDE.md Structure - -```markdown -# Project Context - -## Overview -Brief description of the project. - -## Architecture -Key architectural decisions and patterns. - -## Development Workflow -How to build, test, and deploy. - -## Conventions -- Code style guidelines -- Naming conventions -- Testing patterns -- Documentation standards - -## Common Tasks -Frequent operations and how to perform them. - -## Important Files -Key files and their purposes. - -## Notes -Any other relevant context for working on this project. -``` - -### Project Context Best Practices - -1. **Project-specific** - Unique to this codebase -2. **Actionable** - Include practical information -3. **Up-to-date** - Keep current as project evolves -4. **Conventions** - Document patterns and standards -5. **Common tasks** - Reduce repetitive explanations -6. **Architecture notes** - Explain key design decisions - -## File Format Summary - -| Type | Format | Extension | Location | -|------|--------|-----------|----------| -| Agent | Markdown | `.md` | `.claude/agents/` | -| Skill | Directory + Markdown | `SKILL.md` | `.claude/skills/skill-name/` | -| Slash Command | Markdown | `.md` | `.claude/commands/` | -| Project Context | Markdown | `.md` | `.claude/CLAUDE.md` | - -## Common Mistakes to Avoid - -1. **Using YAML for agents** - Agents are Markdown files, not YAML -2. **Missing frontmatter** - Always include name and description -3. **Wrong file extensions** - Use `.md`, not `.yaml` or `.yml` -4. **Vague descriptions** - Be specific about purpose and usage -5. **No examples** - Include concrete usage examples -6. **Missing documentation** - Every feature needs clear docs - -## Creating Effective Configurations - -### For Agents -- Focus on a specific domain or task -- Provide clear, actionable instructions -- Include examples of good output -- Specify preferred tools and approaches - -### For Skills -- Create reusable, modular capabilities -- Include all necessary resources -- Document requirements and dependencies -- Provide clear usage examples - -### For Slash Commands -- Make them fast and focused -- Use clear, memorable names -- Handle common arguments -- Provide inline feedback - -### For Project Context -- Document the "why" not just the "what" -- Include team conventions -- List common gotchas -- Keep it current and relevant - -## References - -- Anthropic Claude Code Documentation: https://docs.claude.com/en/docs/claude-code -- Example configurations: Search GitHub for `.claude/` directories -- Community resources: Browse public dotfiles repositories diff --git a/dot_claude/skills/claude_code_analyzer/scripts/executable_analyze-claude-md.sh b/dot_claude/skills/claude_code_analyzer/scripts/executable_analyze-claude-md.sh deleted file mode 100644 index 7eb68a68..00000000 --- a/dot_claude/skills/claude_code_analyzer/scripts/executable_analyze-claude-md.sh +++ /dev/null @@ -1,239 +0,0 @@ -#!/bin/bash - -# Analyze project and suggest CLAUDE.md improvements -# Usage: bash scripts/analyze-claude-md.sh [project-path] - -set -e - -PROJECT_PATH="${1:-.}" - -if [ ! -d "$PROJECT_PATH" ]; then - echo "Error: Project path not found: $PROJECT_PATH" - exit 1 -fi - -cd "$PROJECT_PATH" - -echo "{" -echo ' "project_path": "'"$(pwd)"'",' -echo ' "timestamp": "'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'",' - -# Check if CLAUDE.md exists -if [ -f ".claude/CLAUDE.md" ]; then - echo ' "claude_md_exists": true,' - echo ' "claude_md_location": ".claude/CLAUDE.md",' - echo ' "claude_md_size": '"$(wc -l < .claude/CLAUDE.md)"',' -else - echo ' "claude_md_exists": false,' -fi - -# Detect package manager and commands -echo ' "detected_package_manager": {' - -if [ -f "package.json" ]; then - echo ' "type": "npm",' - echo ' "has_package_json": true,' - - # Extract scripts - if command -v jq &> /dev/null && [ -f "package.json" ]; then - SCRIPTS=$(jq -r '.scripts | keys[]' package.json 2>/dev/null | head -20) - if [ -n "$SCRIPTS" ]; then - echo ' "scripts": [' - echo "$SCRIPTS" | while IFS= read -r script; do - echo " \"$script\"," - done | sed '$ s/,$//' - echo ' ],' - fi - fi - - if [ -f "pnpm-lock.yaml" ]; then - echo ' "lockfile": "pnpm"' - elif [ -f "yarn.lock" ]; then - echo ' "lockfile": "yarn"' - elif [ -f "package-lock.json" ]; then - echo ' "lockfile": "npm"' - else - echo ' "lockfile": "none"' - fi -elif [ -f "Cargo.toml" ]; then - echo ' "type": "cargo",' - echo ' "has_cargo_toml": true' -elif [ -f "requirements.txt" ] || [ -f "pyproject.toml" ]; then - echo ' "type": "python",' - if [ -f "pyproject.toml" ]; then - echo ' "has_pyproject": true' - else - echo ' "has_requirements": true' - fi -elif [ -f "go.mod" ]; then - echo ' "type": "go",' - echo ' "has_go_mod": true' -else - echo ' "type": "unknown"' -fi - -echo ' },' - -# Detect testing framework -echo ' "testing": {' - -if [ -f "package.json" ]; then - if grep -q '"vitest"' package.json 2>/dev/null; then - echo ' "framework": "vitest"' - elif grep -q '"jest"' package.json 2>/dev/null; then - echo ' "framework": "jest"' - elif grep -q '"mocha"' package.json 2>/dev/null; then - echo ' "framework": "mocha"' - else - echo ' "framework": "unknown"' - fi -elif [ -f "pytest.ini" ] || grep -r "pytest" . 2>/dev/null | head -1 &> /dev/null; then - echo ' "framework": "pytest"' -elif [ -f "Cargo.toml" ]; then - echo ' "framework": "cargo test"' -elif [ -f "go.mod" ]; then - echo ' "framework": "go test"' -else - echo ' "framework": "unknown"' -fi - -echo ' },' - -# Detect framework -echo ' "framework": {' - -if [ -f "package.json" ]; then - if grep -q '"next"' package.json 2>/dev/null; then - echo ' "type": "nextjs"' - elif grep -q '"react"' package.json 2>/dev/null; then - echo ' "type": "react"' - elif grep -q '"vue"' package.json 2>/dev/null; then - echo ' "type": "vue"' - elif grep -q '"@angular/core"' package.json 2>/dev/null; then - echo ' "type": "angular"' - elif grep -q '"express"' package.json 2>/dev/null; then - echo ' "type": "express"' - elif grep -q '"fastify"' package.json 2>/dev/null; then - echo ' "type": "fastify"' - else - echo ' "type": "unknown"' - fi -elif [ -f "Cargo.toml" ]; then - if grep -q "actix-web" Cargo.toml 2>/dev/null; then - echo ' "type": "actix-web"' - elif grep -q "rocket" Cargo.toml 2>/dev/null; then - echo ' "type": "rocket"' - else - echo ' "type": "rust"' - fi -elif [ -f "requirements.txt" ] || [ -f "pyproject.toml" ]; then - if grep -r "django" . 2>/dev/null | head -1 &> /dev/null; then - echo ' "type": "django"' - elif grep -r "flask" . 2>/dev/null | head -1 &> /dev/null; then - echo ' "type": "flask"' - elif grep -r "fastapi" . 2>/dev/null | head -1 &> /dev/null; then - echo ' "type": "fastapi"' - else - echo ' "type": "python"' - fi -else - echo ' "type": "unknown"' -fi - -echo ' },' - -# Check for common files -echo ' "project_files": {' -echo ' "has_readme": '"$([ -f "README.md" ] && echo "true" || echo "false")"',' -echo ' "has_dockerfile": '"$([ -f "Dockerfile" ] && echo "true" || echo "false")"',' -echo ' "has_ci": '"$([ -d ".github/workflows" ] || [ -f ".gitlab-ci.yml" ] || [ -f ".circleci/config.yml" ] && echo "true" || echo "false")"',' -echo ' "has_gitignore": '"$([ -f ".gitignore" ] && echo "true" || echo "false")"',' -echo ' "has_editorconfig": '"$([ -f ".editorconfig" ] && echo "true" || echo "false")"'' -echo ' },' - -# Directory structure -echo ' "directory_structure": {' - -DIRS_TO_CHECK=("src" "lib" "app" "pages" "components" "api" "tests" "test" "__tests__" "scripts" "docs") -FOUND_DIRS="" - -for dir in "${DIRS_TO_CHECK[@]}"; do - if [ -d "$dir" ]; then - FOUND_DIRS="$FOUND_DIRS\"$dir\", " - fi -done - -if [ -n "$FOUND_DIRS" ]; then - echo ' "key_directories": ['"${FOUND_DIRS%, }"']' -else - echo ' "key_directories": []' -fi - -echo ' },' - -# Suggest CLAUDE.md improvements -echo ' "claude_md_suggestions": [' - -SUGGESTIONS=() - -# Check for package.json scripts -if [ -f "package.json" ] && command -v jq &> /dev/null; then - SCRIPTS=$(jq -r '.scripts | keys[]' package.json 2>/dev/null) - if [ -n "$SCRIPTS" ]; then - SUGGESTIONS+=(' "Document npm scripts (dev, build, test, lint) in CLAUDE.md"') - fi -fi - -# Check for testing -if [ -f "package.json" ]; then - if grep -q '"vitest"' package.json 2>/dev/null || grep -q '"jest"' package.json 2>/dev/null; then - SUGGESTIONS+=(' "Document testing approach and test commands"') - fi -fi - -# Check for TypeScript -if [ -f "tsconfig.json" ]; then - SUGGESTIONS+=(' "Document TypeScript configuration and type checking commands"') -fi - -# Check for linting -if [ -f ".eslintrc" ] || [ -f ".eslintrc.js" ] || [ -f ".eslintrc.json" ]; then - SUGGESTIONS+=(' "Document linting rules and lint commands"') -fi - -# Check for Docker -if [ -f "Dockerfile" ]; then - SUGGESTIONS+=(' "Document Docker build and run commands"') -fi - -# Check for environment variables -if [ -f ".env.example" ] || [ -f ".env.local.example" ]; then - SUGGESTIONS+=(' "Document required environment variables and setup"') -fi - -# Check for monorepo -if [ -f "pnpm-workspace.yaml" ] || [ -f "lerna.json" ]; then - SUGGESTIONS+=(' "Document monorepo structure and workspace commands"') -fi - -# Check for CI/CD -if [ -d ".github/workflows" ]; then - SUGGESTIONS+=(' "Document CI/CD workflow and deployment process"') -fi - -# Check for database -if grep -r "prisma" . 2>/dev/null | head -1 &> /dev/null || [ -f "prisma/schema.prisma" ]; then - SUGGESTIONS+=(' "Document database schema and migration commands"') -fi - -# Print suggestions -for i in "${!SUGGESTIONS[@]}"; do - if [ $i -eq $((${#SUGGESTIONS[@]} - 1)) ]; then - echo "${SUGGESTIONS[$i]}" - else - echo "${SUGGESTIONS[$i]}," - fi -done - -echo ' ]' -echo "}" diff --git a/dot_claude/skills/claude_code_analyzer/scripts/executable_analyze.sh b/dot_claude/skills/claude_code_analyzer/scripts/executable_analyze.sh deleted file mode 100644 index 7c55812c..00000000 --- a/dot_claude/skills/claude_code_analyzer/scripts/executable_analyze.sh +++ /dev/null @@ -1,274 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Claude Code History Analyzer - Fixed Version -# Key fixes: -# 1. Simplified jq syntax (remove != null checks) -# 2. Better error handling for empty results -# 3. Validate temp files before JSON generation -# 4. More robust file processing - -# Parse arguments -CURRENT_PROJECT_ONLY=false -SINGLE_FILE="" -SETTINGS_FILE="${HOME}/.claude/settings.json" -DISCOVER_GITHUB=true - -while [[ $# -gt 0 ]]; do - case $1 in - --current-project) - CURRENT_PROJECT_ONLY=true - shift - ;; - --file) - SINGLE_FILE="$2" - shift 2 - ;; - --discover-github) - DISCOVER_GITHUB=true - shift - ;; - *) - shift - ;; - esac -done - -CLAUDE_PROJECTS_DIR="${HOME}/.claude/projects" - -# Colors for status output (to stderr) -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' - -# Check if jq is installed -if ! command -v jq &>/dev/null; then - echo -e "${RED}Error: jq is required but not installed${NC}" >&2 - echo "Install with: brew install jq (macOS) or apt install jq (Linux)" >&2 - exit 1 -fi - -# Create temporary files for aggregation -TEMP_DIR=$(mktemp -d) -TOOLS_FILE="$TEMP_DIR/tools.txt" -PROJECTS_FILE="$TEMP_DIR/projects.txt" -MODELS_FILE="$TEMP_DIR/models.txt" -AUTO_ALLOWED_FILE="$TEMP_DIR/auto_allowed.txt" - -# Cleanup on exit -trap "rm -rf '$TEMP_DIR'" EXIT - -# Read existing auto-allowed tools from settings -if [ -f "$SETTINGS_FILE" ]; then - jq -r '.autoAllowedTools[]? // empty' "$SETTINGS_FILE" 2>/dev/null | sort >"$AUTO_ALLOWED_FILE" || touch "$AUTO_ALLOWED_FILE" -else - touch "$AUTO_ALLOWED_FILE" -fi - -echo -e "${BLUE}Claude Code History Analyzer${NC}" >&2 -echo -e "${BLUE}=============================${NC}\n" >&2 - -# Determine which files to analyze -if [ -n "$SINGLE_FILE" ]; then - if [ ! -f "$SINGLE_FILE" ]; then - echo -e "${RED}Error: File not found: $SINGLE_FILE${NC}" >&2 - exit 1 - fi - echo -e "Analyzing single file: ${GREEN}$(basename "$SINGLE_FILE")${NC}\n" >&2 - FILES_TO_ANALYZE=("$SINGLE_FILE") - SCOPE="single_file" - SCOPE_DETAIL="$(basename "$SINGLE_FILE")" - -elif [ "$CURRENT_PROJECT_ONLY" = true ]; then - CURRENT_DIR=$(pwd) - PROJECT_PATH=$(echo "$CURRENT_DIR" | sed 's/\//-/g') - PROJECT_DIR="${CLAUDE_PROJECTS_DIR}/${PROJECT_PATH}" - - if [ ! -d "$PROJECT_DIR" ]; then - echo -e "${RED}Error: No Claude Code history found for current project${NC}" >&2 - echo -e "Looking for: $PROJECT_DIR" >&2 - exit 1 - fi - - echo -e "Analyzing current project: ${GREEN}${CURRENT_DIR}${NC}\n" >&2 - mapfile -t FILES_TO_ANALYZE < <(find "$PROJECT_DIR" -name "*.jsonl" 2>/dev/null) - SCOPE="current_project" - SCOPE_DETAIL="$CURRENT_DIR" - -else - if [ ! -d "$CLAUDE_PROJECTS_DIR" ]; then - echo -e "${RED}Error: Claude Code projects directory not found at $CLAUDE_PROJECTS_DIR${NC}" >&2 - exit 1 - fi - - mapfile -t FILES_TO_ANALYZE < <(find "$CLAUDE_PROJECTS_DIR" -name "*.jsonl" 2>/dev/null) - SCOPE="all_projects" - SCOPE_DETAIL="" -fi - -TOTAL_FILES=${#FILES_TO_ANALYZE[@]} -echo -e "Found ${GREEN}${TOTAL_FILES}${NC} conversation file(s)\n" >&2 - -if [ "$TOTAL_FILES" -eq 0 ]; then - echo -e "${RED}No conversation files found${NC}" >&2 - exit 1 -fi - -# Extract all tool usage - FIXED: Simplified jq syntax -echo -e "${YELLOW}Extracting tool usage...${NC}" >&2 -for file in "${FILES_TO_ANALYZE[@]}"; do - # Process one file at a time to avoid memory issues - jq -r 'select(.message.content) | .message.content[] | select(.type == "tool_use") | .name' "$file" 2>/dev/null || true -done | sort | uniq -c | sort -rn >"$TOOLS_FILE" - -# Check if we got any results -if [ ! -s "$TOOLS_FILE" ]; then - echo -e "${YELLOW}Warning: No tool usage found in conversation files${NC}" >&2 - touch "$TOOLS_FILE" # Create empty file to continue -fi - -# Extract project paths (only if analyzing all projects) -if [ "$CURRENT_PROJECT_ONLY" = false ] && [ -z "$SINGLE_FILE" ]; then - echo -e "${YELLOW}Extracting project distribution...${NC}" >&2 - find "$CLAUDE_PROJECTS_DIR" -type d -mindepth 1 -maxdepth 1 2>/dev/null | - while read -r dir; do - count=$(find "$dir" -name "*.jsonl" 2>/dev/null | wc -l | tr -d ' ') - basename=$(basename "$dir") - echo "$count $basename" - done | sort -rn >"$PROJECTS_FILE" -fi - -# Extract model usage - FIXED: Simplified jq syntax -echo -e "${YELLOW}Extracting model usage...${NC}" >&2 -for file in "${FILES_TO_ANALYZE[@]}"; do - jq -r 'select(.message.model) | .message.model' "$file" 2>/dev/null || true -done | sort | uniq -c | sort -rn >"$MODELS_FILE" - -# Check if we got any results -if [ ! -s "$MODELS_FILE" ]; then - echo -e "${YELLOW}Warning: No model usage found in conversation files${NC}" >&2 - touch "$MODELS_FILE" -fi - -echo -e "${GREEN}✓ Data extraction complete!${NC}\n" >&2 -echo -e "${BLUE}Outputting structured data for analysis...${NC}\n" >&2 - -# Output structured data as JSON for Claude to interpret -cat </dev/null || date +"%Y-%m-%dT%H:%M:%SZ")", - "scope": "$SCOPE", - "scope_detail": "$SCOPE_DETAIL", - "total_conversations": $TOTAL_FILES - }, - "tool_usage": [ -EOF - -# Tool usage array - FIXED: Handle empty file -if [ -s "$TOOLS_FILE" ]; then - first=true - while read -r count tool; do - if [ "$first" = true ]; then - first=false - else - echo "," - fi - # Escape tool name for JSON - tool_escaped=$(echo "$tool" | sed 's/"/\\"/g') - printf ' {"tool": "%s", "count": %d}' "$tool_escaped" "$count" - done <"$TOOLS_FILE" - echo "" -fi - -cat </dev/null | awk '{print $1}' || echo "0") - printf ' {"tool": "%s", "usage_count": %d}' "$tool_escaped" "$usage" - done <"$AUTO_ALLOWED_FILE" - echo "" -fi - -cat <&2 - - # Run GitHub discovery and capture output - if [ -f "$SCRIPT_DIR/github-discovery.sh" ]; then - # FIXED: Only add JSON continuation if GitHub discovery succeeds - if output=$(bash "$SCRIPT_DIR/github-discovery.sh" all 2>/dev/null); then - echo "," - echo '"github_discovery": ' - echo "$output" - else - echo -e "${YELLOW}Warning: GitHub discovery failed or returned no results${NC}" >&2 - fi - else - echo -e "${YELLOW}Warning: github-discovery.sh not found${NC}" >&2 - fi -fi diff --git a/dot_claude/skills/claude_code_analyzer/scripts/executable_fetch-features.sh b/dot_claude/skills/claude_code_analyzer/scripts/executable_fetch-features.sh deleted file mode 100644 index 78cff087..00000000 --- a/dot_claude/skills/claude_code_analyzer/scripts/executable_fetch-features.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Claude Code Feature Analyzer -# Fetches latest docs and compares with user's actual usage patterns - -CLAUDE_DOCS_URL="https://docs.claude.com/en/docs/claude-code" -TEMP_DIR=$(mktemp -d) -DOCS_FILE="$TEMP_DIR/claude-code-docs.html" -FEATURES_FILE="$TEMP_DIR/features.txt" - -# Colors -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' - -echo -e "${BLUE}Fetching latest Claude Code documentation...${NC}\n" - -# Try to fetch docs -if command -v curl &> /dev/null; then - if curl -s -L "$CLAUDE_DOCS_URL" -o "$DOCS_FILE" 2>/dev/null; then - echo -e "${GREEN}✓ Documentation fetched${NC}\n" - else - echo -e "${YELLOW}⚠ Could not fetch docs, using fallback feature list${NC}\n" - DOCS_FILE="" - fi -else - echo -e "${YELLOW}⚠ curl not found, using fallback feature list${NC}\n" - DOCS_FILE="" -fi - -# Known Claude Code features (fallback + common features) -cat > "$FEATURES_FILE" <<'EOF' -Agents: Custom AI assistants for specific tasks -Slash Commands: Quick shortcuts for common operations -Skills: Modular capabilities you can add -Tool Auto-Allow: Automatically approve specific tools -Project Context: Automatic codebase understanding -Git Integration: Native git operations -Terminal Integration: Execute commands directly -File Watching: Automatic file change detection -Multi-file Editing: Edit multiple files simultaneously -Code Generation: Create new files and boilerplate -Refactoring: Automated code improvements -Testing: Generate and run tests -Documentation: Auto-generate docs from code -Search: Advanced codebase search -Context Memory: Remember past interactions -Web Search: Look up current information -API Integration: Connect to external services -Custom Tools: Build your own tools -Workflows: Chain multiple operations -Templates: Reusable code patterns -EOF - -echo -e "${BLUE}Available Claude Code Features${NC}" -echo -e "${BLUE}==============================${NC}\n" - -while IFS=: read -r feature description; do - echo "📌 $feature" - echo " $description" - echo "" -done < "$FEATURES_FILE" - -# Cleanup -rm -rf "$TEMP_DIR" - -echo -e "${GREEN}Feature list complete${NC}" diff --git a/dot_claude/skills/claude_code_analyzer/scripts/executable_github-discovery.sh b/dot_claude/skills/claude_code_analyzer/scripts/executable_github-discovery.sh deleted file mode 100644 index 8e952ff9..00000000 --- a/dot_claude/skills/claude_code_analyzer/scripts/executable_github-discovery.sh +++ /dev/null @@ -1,178 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# GitHub Claude Code Discovery -# Searches GitHub for skills, agents, and slash commands based on usage patterns - -# Parse arguments -SEARCH_TYPE="${1:-all}" # all, agents, skills, commands -QUERY="${2:-}" - -# Colors -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -RED='\033[0;31m' -NC='\033[0m' - -# Check if gh CLI is installed -HAS_GH=false -if command -v gh &>/dev/null; then - HAS_GH=true -fi - -echo -e "${BLUE}GitHub Claude Code Discovery${NC}" >&2 -echo -e "${BLUE}============================${NC}\n" >&2 - -# Function to search GitHub using gh CLI -search_with_gh() { - local path="$1" - local query="$2" - local limit="${3:-10}" - - if [ -z "$query" ]; then - gh search code "path:$path" --limit "$limit" --json path,repository,url 2>/dev/null - else - gh search code "$query path:$path" --limit "$limit" --json path,repository,url 2>/dev/null - fi -} - -# Function to search GitHub using web URLs (fallback) -get_search_url() { - local path="$1" - local query="$2" - - if [ -z "$query" ]; then - echo "https://github.com/search?type=code&q=path:${path}" - else - # URL encode the query - query_encoded=$(echo "$query" | sed 's/ /+/g') - echo "https://github.com/search?type=code&q=${query_encoded}+path:${path}" - fi -} - -# Output JSON structure -cat <&2 - - cat <&2 - - cat <&2 - - cat <&2 - - cat <&2 - -if [ "$HAS_GH" = false ]; then - echo -e "${YELLOW}Note: Install 'gh' CLI for direct search results${NC}" >&2 - echo -e "Install: ${BLUE}brew install gh${NC} (macOS) or see https://cli.github.com" >&2 -fi diff --git a/dot_claude/skills/github/SKILL.md b/dot_claude/skills/github/SKILL.md deleted file mode 100644 index 03b2a003..00000000 --- a/dot_claude/skills/github/SKILL.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -name: github -description: "Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries." ---- - -# GitHub Skill - -Use the `gh` CLI to interact with GitHub. Always specify `--repo owner/repo` when not in a git directory, or use URLs directly. - -## Pull Requests - -Check CI status on a PR: -```bash -gh pr checks 55 --repo owner/repo -``` - -List recent workflow runs: -```bash -gh run list --repo owner/repo --limit 10 -``` - -View a run and see which steps failed: -```bash -gh run view --repo owner/repo -``` - -View logs for failed steps only: -```bash -gh run view --repo owner/repo --log-failed -``` - -## API for Advanced Queries - -The `gh api` command is useful for accessing data not available through other subcommands. - -Get PR with specific fields: -```bash -gh api repos/owner/repo/pulls/55 --jq '.title, .state, .user.login' -``` - -## JSON Output - -Most commands support `--json` for structured output. You can use `--jq` to filter: - -```bash -gh issue list --repo owner/repo --json number,title --jq '.[] | "\(.number): \(.title)"' -``` diff --git a/dot_claude/skills/improve_skill/SKILL.md b/dot_claude/skills/improve_skill/SKILL.md deleted file mode 100644 index 0203c749..00000000 --- a/dot_claude/skills/improve_skill/SKILL.md +++ /dev/null @@ -1,155 +0,0 @@ ---- -name: improve_skill -description: "Analyze coding agent session transcripts to improve existing skills or create new ones. Use when asked to improve a skill based on a session, or extract a new skill from session history." ---- - -# Improve Skill - -This skill helps analyze coding agent sessions to improve or create skills. It works with Claude Code, Pi, and Codex session files. - -## Quick Start - -Extract the current session and generate an improvement prompt: - -```bash -# Auto-detect agent and extract current session -./scripts/extract-session.js -``` - -## Session Extraction - -The `extract-session.js` script finds and parses session files from any of the three agents: - -```bash -# Auto-detect (uses most recent session for current working directory) -./scripts/extract-session.js - -# Specify agent type -./scripts/extract-session.js --agent claude -./scripts/extract-session.js --agent pi -./scripts/extract-session.js --agent codex - -# Specify a different working directory -./scripts/extract-session.js --cwd /path/to/project - -# Use a specific session file -./scripts/extract-session.js /path/to/session.jsonl -``` - -**Session file locations:** -- **Claude Code**: `~/.claude/projects//*.jsonl` -- **Pi**: `~/.pi/agent/sessions//*.jsonl` -- **Codex**: `~/.codex/sessions/YYYY/MM/DD/*.jsonl` - -## Workflow: Improve an Existing Skill - -When asked to improve a skill based on a session: - -1. **Extract the session transcript:** - ```bash - ./scripts/extract-session.js > /tmp/session-transcript.txt - ``` - -2. **Find the existing skill** in one of these locations: - - `~/.codex/skills//SKILL.md` - - `~/.claude/skills//SKILL.md` - - `~/.pi/agent/skills//SKILL.md` - -3. **Generate an improvement prompt** for a new session: - -``` -═══════════════════════════════════════════════════════════════════════════════ -COPY THE FOLLOWING PROMPT INTO A NEW AGENT SESSION: -═══════════════════════════════════════════════════════════════════════════════ - -I need to improve the "" skill based on a session where I used it. - -First, read the current skill at: - -Then analyze this session transcript to understand: -- Where I struggled to use the skill correctly -- What information was missing from the skill -- What examples would have helped -- What I had to figure out on my own - - - - - -Based on this analysis, improve the skill by: -1. Adding missing instructions or clarifications -2. Adding examples for common use cases discovered -3. Fixing any incorrect guidance -4. Making the skill more concise where possible - -Write the improved skill back to the same location. - -═══════════════════════════════════════════════════════════════════════════════ -``` - -## Workflow: Create a New Skill - -When asked to create a new skill from a session: - -1. **Extract the session transcript:** - ```bash - ./scripts/extract-session.js > /tmp/session-transcript.txt - ``` - -2. **Generate a creation prompt** for a new session: - -``` -═══════════════════════════════════════════════════════════════════════════════ -COPY THE FOLLOWING PROMPT INTO A NEW AGENT SESSION: -═══════════════════════════════════════════════════════════════════════════════ - -Analyze this session transcript to extract a reusable skill called "": - - - - - -Create a new skill that captures: -1. The core capability or workflow demonstrated -2. Key commands, APIs, or patterns used -3. Common pitfalls and how to avoid them -4. Example usage for typical scenarios - -Write the skill to: ~/.codex/skills//SKILL.md - -Use this format: ---- -name: -description: "" ---- - -# Skill - - - -##
- - - -═══════════════════════════════════════════════════════════════════════════════ -``` - -## Why a Separate Session? - -The improvement prompt is meant to be copied into a **fresh agent session** because: - -1. **Token efficiency** - The current session already has a lot of context; starting fresh means only the transcript and skill are loaded -2. **Clean analysis** - The new session can focus purely on improvement without being influenced by the current task -3. **Reproducibility** - The prompt is self-contained and can be shared or reused - -## Tips for Good Skill Improvements - -When analyzing a transcript, look for: - -- **Confusion patterns** - Where did the agent retry or change approach? -- **Missing examples** - What specific commands or code patterns were discovered? -- **Workarounds** - What did the agent have to figure out that wasn't documented? -- **Errors** - What failed and how was it resolved? -- **Successful patterns** - What worked well and should be highlighted? - -Keep skills concise - focus on the most important information and examples. diff --git a/dot_claude/skills/improve_skill/scripts/executable_extract-session.js b/dot_claude/skills/improve_skill/scripts/executable_extract-session.js deleted file mode 100644 index f1eb43ac..00000000 --- a/dot_claude/skills/improve_skill/scripts/executable_extract-session.js +++ /dev/null @@ -1,349 +0,0 @@ -#!/usr/bin/env node - -/** - * Extract session transcript from Claude Code, Pi, or Codex session files. - * - * Usage: - * ./extract-session.js [session-path] - * ./extract-session.js --agent claude|pi|codex [--cwd /path/to/dir] - * - * If no arguments, auto-detects based on current working directory. - */ - -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -// Parse arguments -const args = process.argv.slice(2); -let sessionPath = null; -let agent = null; -let cwd = process.cwd(); - -for (let i = 0; i < args.length; i++) { - if (args[i] === '--agent' && args[i + 1]) { - agent = args[++i]; - } else if (args[i] === '--cwd' && args[i + 1]) { - cwd = args[++i]; - } else if (!args[i].startsWith('-')) { - sessionPath = args[i]; - } -} - -/** - * Encode CWD for session path lookup - */ -function encodeCwd(cwd, style) { - if (style === 'pi') { - // Pi uses: ---- - // e.g., /Users/mitsuhiko/Development/myproject -> --Users-mitsuhiko-Development-myproject-- - const safePath = `--${cwd.replace(/^[/\\]/, '').replace(/[/\\:]/g, '-')}--`; - return safePath; - } - // Claude Code: just replace / with - - return cwd.replace(/\//g, '-'); -} - -/** - * Find the most recent session file in a directory - */ -function findMostRecentSession(dir) { - if (!fs.existsSync(dir)) return null; - - const files = fs.readdirSync(dir) - .filter(f => f.endsWith('.jsonl')) - .map(f => ({ - name: f, - path: path.join(dir, f), - mtime: fs.statSync(path.join(dir, f)).mtime - })) - .sort((a, b) => b.mtime - a.mtime); - - return files.length > 0 ? files[0].path : null; -} - -/** - * Find Codex session matching CWD - */ -function findCodexSession(targetCwd) { - const baseDir = path.join(os.homedir(), '.codex', 'sessions'); - if (!fs.existsSync(baseDir)) return null; - - // Find all session files, sorted by mtime - const allSessions = []; - - function walkDir(dir) { - if (!fs.existsSync(dir)) return; - for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { - const fullPath = path.join(dir, entry.name); - if (entry.isDirectory()) { - walkDir(fullPath); - } else if (entry.name.endsWith('.jsonl')) { - allSessions.push({ - path: fullPath, - mtime: fs.statSync(fullPath).mtime - }); - } - } - } - - walkDir(baseDir); - allSessions.sort((a, b) => b.mtime - a.mtime); - - // Find most recent matching CWD - for (const session of allSessions.slice(0, 50)) { // Check last 50 - try { - const firstLine = fs.readFileSync(session.path, 'utf8').split('\n')[0]; - const data = JSON.parse(firstLine); - if (data.payload?.cwd === targetCwd) { - return session.path; - } - } catch (e) { - // Skip invalid files - } - } - - return null; -} - -/** - * Auto-detect session based on CWD - */ -function autoDetectSession(cwd) { - // Try Claude Code first - const claudePath = path.join(os.homedir(), '.claude', 'projects', encodeCwd(cwd, 'claude')); - let session = findMostRecentSession(claudePath); - if (session) return { agent: 'claude', path: session }; - - // Try Pi - const piPath = path.join(os.homedir(), '.pi', 'agent', 'sessions', encodeCwd(cwd, 'pi')); - session = findMostRecentSession(piPath); - if (session) return { agent: 'pi', path: session }; - - // Try Codex - session = findCodexSession(cwd); - if (session) return { agent: 'codex', path: session }; - - return null; -} - -/** - * Parse Claude Code session format - */ -function parseClaudeSession(content) { - const messages = []; - const lines = content.trim().split('\n'); - - for (const line of lines) { - try { - const entry = JSON.parse(line); - if (entry.message?.role && entry.message?.content) { - const msg = entry.message; - messages.push({ - role: msg.role, - content: extractContent(msg.content), - timestamp: entry.timestamp - }); - } - } catch (e) { - // Skip invalid lines - } - } - - return messages; -} - -/** - * Parse Pi session format - */ -function parsePiSession(content) { - const messages = []; - const lines = content.trim().split('\n'); - - for (const line of lines) { - try { - const entry = JSON.parse(line); - if (entry.type === 'message' && entry.message?.role) { - messages.push({ - role: entry.message.role, - content: extractContent(entry.message.content), - timestamp: entry.timestamp - }); - } - } catch (e) { - // Skip invalid lines - } - } - - return messages; -} - -/** - * Parse Codex session format - */ -function parseCodexSession(content) { - const messages = []; - const lines = content.trim().split('\n'); - - for (const line of lines) { - try { - const entry = JSON.parse(line); - if (entry.type === 'response_item' && entry.payload?.role) { - const payload = entry.payload; - messages.push({ - role: payload.role, - content: extractContent(payload.content), - timestamp: entry.timestamp - }); - } - } catch (e) { - // Skip invalid lines - } - } - - return messages; -} - -/** - * Extract text content from various content formats - */ -function extractContent(content) { - if (typeof content === 'string') return content; - if (!Array.isArray(content)) return JSON.stringify(content); - - const parts = []; - for (const item of content) { - if (typeof item === 'string') { - parts.push(item); - } else if (item.type === 'text') { - parts.push(item.text); - } else if (item.type === 'input_text') { - parts.push(item.text); - } else if (item.type === 'tool_use') { - parts.push(`[Tool: ${item.name}]\n${JSON.stringify(item.input, null, 2)}`); - } else if (item.type === 'tool_result') { - const result = typeof item.content === 'string' - ? item.content - : JSON.stringify(item.content); - // Truncate long tool results - const truncated = result.length > 500 - ? result.slice(0, 500) + '\n[... truncated ...]' - : result; - parts.push(`[Tool Result]\n${truncated}`); - } else { - parts.push(`[${item.type}]`); - } - } - - return parts.join('\n'); -} - -/** - * Format messages as readable transcript - */ -function formatTranscript(messages, maxMessages = 100) { - const recent = messages.slice(-maxMessages); - const lines = []; - - for (const msg of recent) { - const role = msg.role.toUpperCase(); - lines.push(`\n### ${role}:\n`); - lines.push(msg.content); - } - - if (messages.length > maxMessages) { - lines.unshift(`\n[... ${messages.length - maxMessages} earlier messages omitted ...]\n`); - } - - return lines.join('\n'); -} - -// Main -async function main() { - let result; - - if (sessionPath) { - // Explicit path provided - if (!fs.existsSync(sessionPath)) { - console.error(`Session file not found: ${sessionPath}`); - process.exit(1); - } - // Guess agent from path - if (sessionPath.includes('.claude')) { - result = { agent: 'claude', path: sessionPath }; - } else if (sessionPath.includes('.pi')) { - result = { agent: 'pi', path: sessionPath }; - } else if (sessionPath.includes('.codex')) { - result = { agent: 'codex', path: sessionPath }; - } else { - // Default to Claude format - result = { agent: 'claude', path: sessionPath }; - } - } else if (agent) { - // Agent specified, find session for that agent - if (agent === 'claude') { - const dir = path.join(os.homedir(), '.claude', 'projects', encodeCwd(cwd, 'claude')); - const session = findMostRecentSession(dir); - if (!session) { - console.error(`No Claude Code session found for: ${cwd}`); - process.exit(1); - } - result = { agent: 'claude', path: session }; - } else if (agent === 'pi') { - const dir = path.join(os.homedir(), '.pi', 'agent', 'sessions', encodeCwd(cwd, 'pi')); - const session = findMostRecentSession(dir); - if (!session) { - console.error(`No Pi session found for: ${cwd}`); - process.exit(1); - } - result = { agent: 'pi', path: session }; - } else if (agent === 'codex') { - const session = findCodexSession(cwd); - if (!session) { - console.error(`No Codex session found for: ${cwd}`); - process.exit(1); - } - result = { agent: 'codex', path: session }; - } else { - console.error(`Unknown agent: ${agent}`); - process.exit(1); - } - } else { - // Auto-detect - result = autoDetectSession(cwd); - if (!result) { - console.error(`No session found for: ${cwd}`); - console.error('Try specifying --agent claude|pi|codex or provide a session path directly.'); - process.exit(1); - } - } - - // Read and parse session - const content = fs.readFileSync(result.path, 'utf8'); - - let messages; - switch (result.agent) { - case 'claude': - messages = parseClaudeSession(content); - break; - case 'pi': - messages = parsePiSession(content); - break; - case 'codex': - messages = parseCodexSession(content); - break; - } - - // Output metadata and transcript - console.log(`# Session Transcript`); - console.log(`Agent: ${result.agent}`); - console.log(`File: ${result.path}`); - console.log(`Messages: ${messages.length}`); - console.log(''); - console.log(formatTranscript(messages)); -} - -main().catch(e => { - console.error(e.message); - process.exit(1); -}); diff --git a/dot_claude/skills/web_browser/SKILL.md b/dot_claude/skills/web_browser/SKILL.md deleted file mode 100644 index 163dc59c..00000000 --- a/dot_claude/skills/web_browser/SKILL.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -name: web_browser -description: "Allows to interact with web pages by performing actions such as clicking buttons, filling out forms, and navigating links. It works by remote controlling Google Chrome or Chromium browsers using the Chrome DevTools Protocol (CDP). When Claude needs to browse the web, it can use this skill to do so." -license: Stolen from Mario ---- - -# Web Browser Skill - -Minimal CDP tools for collaborative site exploration. - -## Start Chrome - -\`\`\`bash -./scripts/start.js # Fresh profile -./scripts/start.js --profile # Copy your profile (cookies, logins) -\`\`\` - -Start Chrome on `:9222` with remote debugging. - -## Navigate - -\`\`\`bash -./scripts/nav.js https://example.com -./scripts/nav.js https://example.com --new -\`\`\` - -Navigate current tab or open new tab. - -## Evaluate JavaScript - -\`\`\`bash -./scripts/eval.js 'document.title' -./scripts/eval.js 'document.querySelectorAll("a").length' -./scripts/eval.js 'JSON.stringify(Array.from(document.querySelectorAll("a")).map(a => ({ text: a.textContent.trim(), href: a.href })).filter(link => !link.href.startsWith("https://")))' -\`\`\` - -Execute JavaScript in active tab (async context). Be careful with string escaping, best to use single quotes. - -## Screenshot - -\`\`\`bash -./scripts/screenshot.js -\`\`\` - -Screenshot current viewport, returns temp file path - -## Pick Elements - -\`\`\`bash -./scripts/pick.js "Click the submit button" -\`\`\` - -Interactive element picker. Click to select, Cmd/Ctrl+Click for multi-select, Enter to finish. - -## Dismiss Cookie Dialogs - -\`\`\`bash -./scripts/dismiss-cookies.js # Accept cookies -./scripts/dismiss-cookies.js --reject # Reject cookies (where possible) -\`\`\` - -Automatically dismisses EU cookie consent dialogs. Supports: -- **OneTrust** (booking.com, ikea.com, many others) -- **Google** consent dialogs -- **Cookiebot** -- **Didomi** -- **Quantcast Choice** -- **Usercentrics** (shadow DOM) -- **Sourcepoint** (BBC, etc. - works with iframes) -- **Amazon** -- **TrustArc** -- **Klaro** -- Generic cookie banners with common button text patterns - -Run after navigating to a page (with a short delay for dialogs to load): -\`\`\`bash -./scripts/nav.js https://example.com && sleep 2 && ./scripts/dismiss-cookies.js -\`\`\` diff --git a/dot_claude/skills/web_browser/scripts/cdp.js b/dot_claude/skills/web_browser/scripts/cdp.js deleted file mode 100644 index b72474eb..00000000 --- a/dot_claude/skills/web_browser/scripts/cdp.js +++ /dev/null @@ -1,175 +0,0 @@ -/** - * Minimal CDP client - no puppeteer, no hangs - */ - -import WebSocket from "ws"; - -export async function connect(timeout = 5000) { - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), timeout); - - try { - const resp = await fetch("http://localhost:9222/json/version", { - signal: controller.signal, - }); - const { webSocketDebuggerUrl } = await resp.json(); - clearTimeout(timeoutId); - - return new Promise((resolve, reject) => { - const ws = new WebSocket(webSocketDebuggerUrl); - const connectTimeout = setTimeout(() => { - ws.close(); - reject(new Error("WebSocket connect timeout")); - }, timeout); - - ws.on("open", () => { - clearTimeout(connectTimeout); - resolve(new CDP(ws)); - }); - ws.on("error", (e) => { - clearTimeout(connectTimeout); - reject(e); - }); - }); - } catch (e) { - clearTimeout(timeoutId); - if (e.name === "AbortError") { - throw new Error("Connection timeout - is Chrome running with --remote-debugging-port=9222?"); - } - throw e; - } -} - -class CDP { - constructor(ws) { - this.ws = ws; - this.id = 0; - this.callbacks = new Map(); - this.sessions = new Map(); - - ws.on("message", (data) => { - const msg = JSON.parse(data.toString()); - if (msg.id && this.callbacks.has(msg.id)) { - const { resolve, reject } = this.callbacks.get(msg.id); - this.callbacks.delete(msg.id); - if (msg.error) { - reject(new Error(msg.error.message)); - } else { - resolve(msg.result); - } - } - }); - } - - send(method, params = {}, sessionId = null, timeout = 10000) { - return new Promise((resolve, reject) => { - const msgId = ++this.id; - const msg = { id: msgId, method, params }; - if (sessionId) msg.sessionId = sessionId; - - const timeoutId = setTimeout(() => { - this.callbacks.delete(msgId); - reject(new Error(`CDP timeout: ${method}`)); - }, timeout); - - this.callbacks.set(msgId, { - resolve: (result) => { - clearTimeout(timeoutId); - resolve(result); - }, - reject: (err) => { - clearTimeout(timeoutId); - reject(err); - }, - }); - - this.ws.send(JSON.stringify(msg)); - }); - } - - async getPages() { - const { targetInfos } = await this.send("Target.getTargets"); - return targetInfos.filter((t) => t.type === "page"); - } - - async attachToPage(targetId) { - const { sessionId } = await this.send("Target.attachToTarget", { - targetId, - flatten: true, - }); - return sessionId; - } - - async evaluate(sessionId, expression, timeout = 30000) { - const result = await this.send( - "Runtime.evaluate", - { - expression, - returnByValue: true, - awaitPromise: true, - }, - sessionId, - timeout - ); - - if (result.exceptionDetails) { - throw new Error( - result.exceptionDetails.exception?.description || - result.exceptionDetails.text - ); - } - return result.result?.value; - } - - async screenshot(sessionId, timeout = 10000) { - const { data } = await this.send( - "Page.captureScreenshot", - { format: "png" }, - sessionId, - timeout - ); - return Buffer.from(data, "base64"); - } - - async navigate(sessionId, url, timeout = 30000) { - await this.send("Page.navigate", { url }, sessionId, timeout); - } - - async getFrameTree(sessionId) { - const { frameTree } = await this.send("Page.getFrameTree", {}, sessionId); - return frameTree; - } - - async evaluateInFrame(sessionId, frameId, expression, timeout = 30000) { - // Create isolated world for the frame - const { executionContextId } = await this.send( - "Page.createIsolatedWorld", - { frameId, worldName: "cdp-eval" }, - sessionId - ); - - const result = await this.send( - "Runtime.evaluate", - { - expression, - contextId: executionContextId, - returnByValue: true, - awaitPromise: true, - }, - sessionId, - timeout - ); - - if (result.exceptionDetails) { - throw new Error( - result.exceptionDetails.exception?.description || - result.exceptionDetails.text - ); - } - return result.result?.value; - } - - close() { - this.ws.close(); - } -} diff --git a/dot_claude/skills/web_browser/scripts/executable_start.js b/dot_claude/skills/web_browser/scripts/executable_start.js deleted file mode 100644 index 42f79587..00000000 --- a/dot_claude/skills/web_browser/scripts/executable_start.js +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env node - -import { spawn, execSync } from "node:child_process"; -import puppeteer from "puppeteer-core"; - -const useProfile = process.argv[2] === "--profile"; - -if (process.argv[2] && process.argv[2] !== "--profile") { - console.log("Usage: start.ts [--profile]"); - console.log("\nOptions:"); - console.log( - " --profile Copy your default Chrome profile (cookies, logins)", - ); - console.log("\nExamples:"); - console.log(" start.ts # Start with fresh profile"); - console.log(" start.ts --profile # Start with your Chrome profile"); - process.exit(1); -} - -// Kill existing Chrome -try { - execSync("killall 'Google Chrome'", { stdio: "ignore" }); -} catch {} - -// Wait a bit for processes to fully die -await new Promise((r) => setTimeout(r, 1000)); - -// Setup profile directory -execSync("mkdir -p ~/.cache/scraping", { stdio: "ignore" }); - -if (useProfile) { - // Sync profile with rsync (much faster on subsequent runs) - execSync( - `rsync -a --delete "${process.env["HOME"]}/Library/Application Support/Google/Chrome/" ~/.cache/scraping/`, - { stdio: "pipe" }, - ); -} - -// Start Chrome in background (detached so Node can exit) -spawn( - "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", - [ - "--remote-debugging-port=9222", - `--user-data-dir=${process.env["HOME"]}/.cache/scraping`, - "--profile-directory=Default", - "--disable-search-engine-choice-screen", - "--no-first-run", - "--disable-features=ProfilePicker", - ], - { detached: true, stdio: "ignore" }, -).unref(); - -// Wait for Chrome to be ready by attempting to connect -let connected = false; -for (let i = 0; i < 30; i++) { - try { - const browser = await puppeteer.connect({ - browserURL: "http://localhost:9222", - defaultViewport: null, - }); - await browser.disconnect(); - connected = true; - break; - } catch { - await new Promise((r) => setTimeout(r, 500)); - } -} - -if (!connected) { - console.error("✗ Failed to connect to Chrome"); - process.exit(1); -} - -console.log( - `✓ Chrome started on :9222${useProfile ? " with your profile" : ""}`, -); diff --git a/dot_claude/skills/web_browser/scripts/package-lock.json b/dot_claude/skills/web_browser/scripts/package-lock.json deleted file mode 100644 index 336f26d6..00000000 --- a/dot_claude/skills/web_browser/scripts/package-lock.json +++ /dev/null @@ -1,924 +0,0 @@ -{ - "name": "scripts", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "puppeteer-core": "^24.34.0", - "ws": "^8.18.0" - } - }, - "node_modules/@puppeteer/browsers": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.11.0.tgz", - "integrity": "sha512-n6oQX6mYkG8TRPuPXmbPidkUbsSRalhmaaVAQxvH1IkQy63cwsH+kOjB3e4cpCDHg0aSvsiX9bQ4s2VB6mGWUQ==", - "license": "Apache-2.0", - "dependencies": { - "debug": "^4.4.3", - "extract-zip": "^2.0.1", - "progress": "^2.0.3", - "proxy-agent": "^6.5.0", - "semver": "^7.7.3", - "tar-fs": "^3.1.1", - "yargs": "^17.7.2" - }, - "bin": { - "browsers": "lib/cjs/main-cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "25.0.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz", - "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==", - "license": "MIT", - "optional": true, - "dependencies": { - "undici-types": "~7.16.0" - } - }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ast-types": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", - "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/b4a": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", - "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", - "license": "Apache-2.0", - "peerDependencies": { - "react-native-b4a": "*" - }, - "peerDependenciesMeta": { - "react-native-b4a": { - "optional": true - } - } - }, - "node_modules/bare-events": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", - "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", - "license": "Apache-2.0", - "peerDependencies": { - "bare-abort-controller": "*" - }, - "peerDependenciesMeta": { - "bare-abort-controller": { - "optional": true - } - } - }, - "node_modules/bare-fs": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.2.tgz", - "integrity": "sha512-veTnRzkb6aPHOvSKIOy60KzURfBdUflr5VReI+NSaPL6xf+XLdONQgZgpYvUuZLVQ8dCqxpBAudaOM1+KpAUxw==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-events": "^2.5.4", - "bare-path": "^3.0.0", - "bare-stream": "^2.6.4", - "bare-url": "^2.2.2", - "fast-fifo": "^1.3.2" - }, - "engines": { - "bare": ">=1.16.0" - }, - "peerDependencies": { - "bare-buffer": "*" - }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - } - } - }, - "node_modules/bare-os": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz", - "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==", - "license": "Apache-2.0", - "optional": true, - "engines": { - "bare": ">=1.14.0" - } - }, - "node_modules/bare-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", - "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-os": "^3.0.1" - } - }, - "node_modules/bare-stream": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz", - "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "streamx": "^2.21.0" - }, - "peerDependencies": { - "bare-buffer": "*", - "bare-events": "*" - }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - }, - "bare-events": { - "optional": true - } - } - }, - "node_modules/bare-url": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz", - "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-path": "^3.0.0" - } - }, - "node_modules/basic-ftp": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.1.0.tgz", - "integrity": "sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/chromium-bidi": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-12.0.1.tgz", - "integrity": "sha512-fGg+6jr0xjQhzpy5N4ErZxQ4wF7KLEvhGZXD6EgvZKDhu7iOhZXnZhcDxPJDcwTcrD48NPzOCo84RP2lv3Z+Cg==", - "license": "Apache-2.0", - "dependencies": { - "mitt": "^3.0.1", - "zod": "^3.24.1" - }, - "peerDependencies": { - "devtools-protocol": "*" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/data-uri-to-buffer": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", - "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/degenerator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", - "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", - "license": "MIT", - "dependencies": { - "ast-types": "^0.13.4", - "escodegen": "^2.1.0", - "esprima": "^4.0.1" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/devtools-protocol": { - "version": "0.0.1534754", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1534754.tgz", - "integrity": "sha512-26T91cV5dbOYnXdJi5qQHoTtUoNEqwkHcAyu/IKtjIAxiEqPMrDiRkDOPWVsGfNZGmlQVHQbZRSjD8sxagWVsQ==", - "license": "BSD-3-Clause" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "license": "BSD-2-Clause", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/events-universal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", - "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", - "license": "Apache-2.0", - "dependencies": { - "bare-events": "^2.7.0" - } - }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "license": "BSD-2-Clause", - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "license": "MIT" - }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "license": "MIT", - "dependencies": { - "pend": "~1.2.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-uri": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", - "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", - "license": "MIT", - "dependencies": { - "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/ip-address": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", - "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "license": "MIT" - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/pac-proxy-agent": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", - "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", - "license": "MIT", - "dependencies": { - "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "get-uri": "^6.0.1", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.6", - "pac-resolver": "^7.0.1", - "socks-proxy-agent": "^8.0.5" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-resolver": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", - "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", - "license": "MIT", - "dependencies": { - "degenerator": "^5.0.0", - "netmask": "^2.0.2" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "license": "MIT" - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/proxy-agent": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", - "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.6", - "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.1.0", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.5" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/puppeteer-core": { - "version": "24.34.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.34.0.tgz", - "integrity": "sha512-24evawO+mUGW4mvS2a2ivwLdX3gk8zRLZr9HP+7+VT2vBQnm0oh9jJEZmUE3ePJhRkYlZ93i7OMpdcoi2qNCLg==", - "license": "Apache-2.0", - "dependencies": { - "@puppeteer/browsers": "2.11.0", - "chromium-bidi": "12.0.1", - "debug": "^4.4.3", - "devtools-protocol": "0.0.1534754", - "typed-query-selector": "^2.12.0", - "webdriver-bidi-protocol": "0.3.10", - "ws": "^8.18.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", - "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", - "license": "MIT", - "dependencies": { - "ip-address": "^10.0.1", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", - "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "socks": "^2.8.3" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/streamx": { - "version": "2.23.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", - "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", - "license": "MIT", - "dependencies": { - "events-universal": "^1.0.0", - "fast-fifo": "^1.3.2", - "text-decoder": "^1.1.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tar-fs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz", - "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==", - "license": "MIT", - "dependencies": { - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - }, - "optionalDependencies": { - "bare-fs": "^4.0.1", - "bare-path": "^3.0.0" - } - }, - "node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "license": "MIT", - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, - "node_modules/text-decoder": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", - "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", - "license": "Apache-2.0", - "dependencies": { - "b4a": "^1.6.4" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/typed-query-selector": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", - "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "license": "MIT", - "optional": true - }, - "node_modules/webdriver-bidi-protocol": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.3.10.tgz", - "integrity": "sha512-5LAE43jAVLOhB/QqX4bwSiv0Hg1HBfMmOuwBSXHdvg4GMGu9Y0lIq7p4R/yySu6w74WmaR4GM4H9t2IwLW7hgw==", - "license": "Apache-2.0" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/ws": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "license": "MIT", - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, - "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - } -} diff --git a/dot_claude/skills/web_browser/scripts/package.json b/dot_claude/skills/web_browser/scripts/package.json deleted file mode 100644 index 6e5e1979..00000000 --- a/dot_claude/skills/web_browser/scripts/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "module", - "dependencies": { - "puppeteer-core": "^24.34.0", - "ws": "^8.18.0" - } -} diff --git a/dot_claude/symlink_skills b/dot_claude/symlink_skills new file mode 100644 index 00000000..9e640718 --- /dev/null +++ b/dot_claude/symlink_skills @@ -0,0 +1 @@ +/Users/joelazar/.agents/skills diff --git a/dot_pi/agent/skills/web-browser/scripts/executable_dismiss-cookies.js b/dot_pi/agent/skills/web-browser/scripts/executable_dismiss-cookies.js deleted file mode 100644 index 09e24509..00000000 --- a/dot_pi/agent/skills/web-browser/scripts/executable_dismiss-cookies.js +++ /dev/null @@ -1,373 +0,0 @@ -#!/usr/bin/env node - -/** - * Cookie Consent Dismissal Helper - * - * Automatically accepts cookie consent dialogs on EU websites. - * Supports common CMPs: OneTrust, Cookiebot, Didomi, Quantcast, Google, BBC, Amazon, etc. - * - * Usage: - * ./dismiss-cookies.js # Accept cookies - * ./dismiss-cookies.js --reject # Reject cookies (where possible) - */ - -import { connect } from "./cdp.js"; - -const DEBUG = process.env.DEBUG === "1"; -const log = DEBUG ? (...args) => console.error("[debug]", ...args) : () => {}; - -const reject = process.argv.includes("--reject"); -const mode = reject ? "reject" : "accept"; - -const COOKIE_DISMISS_SCRIPT = `(acceptCookies) => { - const clicked = []; - - const isVisible = (el) => { - if (!el) return false; - const style = getComputedStyle(el); - return style.display !== 'none' && - style.visibility !== 'hidden' && - style.opacity !== '0' && - (el.offsetParent !== null || style.position === 'fixed' || style.position === 'sticky'); - }; - - const tryClick = (selector, description) => { - const el = typeof selector === 'string' ? document.querySelector(selector) : selector; - if (isVisible(el)) { - el.click(); - clicked.push(description || selector); - return true; - } - return false; - }; - - const findButtonByText = (patterns, container = document) => { - const buttons = Array.from(container.querySelectorAll('button, [role="button"], a.button, input[type="submit"], input[type="button"]')); - // Sort patterns by length descending to match more specific patterns first - const sortedPatterns = [...patterns].sort((a, b) => b.length - a.length); - - // Check patterns in order of specificity (longest first) - for (const pattern of sortedPatterns) { - for (const btn of buttons) { - const text = (btn.textContent || btn.value || '').trim().toLowerCase(); - if (text.length > 100) continue; // Skip buttons with very long text - if (!isVisible(btn)) continue; // Skip hidden buttons - if (typeof pattern === 'string' ? text.includes(pattern) : pattern.test(text)) { - return btn; - } - } - } - return null; - }; - - const acceptPatterns = [ - 'accept all', 'accept cookies', 'allow all', 'allow cookies', - 'i agree', 'i accept', 'yes, i agree', 'agree and continue', - 'alle akzeptieren', 'akzeptieren', 'alle zulassen', 'zustimmen', - 'annehmen', 'einverstanden', - 'accepter tout', 'tout accepter', "j'accepte", 'accepter et continuer', 'accepter', - 'accetta tutti', 'accetta', 'accetto', - 'aceptar todo', 'aceptar', 'acepto', - 'aceitar tudo', 'aceitar', - 'continue', 'agree', - ]; - - const rejectPatterns = [ - 'reject all', 'decline all', 'deny all', 'refuse all', - 'i do not agree', 'i disagree', 'no thanks', - 'alle ablehnen', 'ablehnen', 'nicht zustimmen', - 'refuser tout', 'tout refuser', 'refuser', - 'rifiuta tutti', 'rifiuta', - 'rechazar todo', 'rechazar', - 'rejeitar tudo', 'rejeitar', - 'only necessary', 'necessary only', 'nur notwendige', - 'essential only', 'nur essentielle', - ]; - - const patterns = acceptCookies ? acceptPatterns : rejectPatterns; - - // OneTrust - if (document.querySelector('#onetrust-banner-sdk')) { - const selector = acceptCookies ? '#onetrust-accept-btn-handler' : '#onetrust-reject-all-handler'; - if (tryClick(selector, 'OneTrust')) return clicked; - } - - // Google - if (document.querySelector('[data-consent-dialog]') || document.querySelector('form[action*="consent.google"]') || document.querySelector('#CXQnmb')) { - const selector = acceptCookies ? '#L2AGLb' : '#W0wltc'; - if (tryClick(selector, 'Google Consent')) return clicked; - } - - // YouTube (Google-owned, custom consent element) - if (document.querySelector('ytd-consent-bump-v2-lightbox')) { - const btn = Array.from(document.querySelectorAll('ytd-consent-bump-v2-lightbox button')) - .find(b => acceptCookies - ? b.textContent.includes('Accept all') || b.ariaLabel?.includes('Accept') - : b.textContent.includes('Reject all') || b.ariaLabel?.includes('Reject')); - if (btn) { - btn.click(); - clicked.push('YouTube'); - return clicked; - } - } - - // Cookiebot - if (document.querySelector('#CybotCookiebotDialog')) { - const selector = acceptCookies - ? '#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll, #CybotCookiebotDialogBodyButtonAccept' - : '#CybotCookiebotDialogBodyButtonDecline, #CybotCookiebotDialogBodyLevelButtonLevelOptinDeclineAll'; - if (tryClick(selector, 'Cookiebot')) return clicked; - } - - // Didomi - if (document.querySelector('#didomi-host') || window.Didomi) { - const selector = acceptCookies ? '#didomi-notice-agree-button' : '#didomi-notice-disagree-button, [data-testid="disagree-button"]'; - if (tryClick(selector, 'Didomi')) return clicked; - } - - // Quantcast - if (document.querySelector('.qc-cmp2-container')) { - const selector = acceptCookies - ? '.qc-cmp2-summary-buttons button[mode="primary"], .qc-cmp2-button[data-testid="accept-all"]' - : '.qc-cmp2-summary-buttons button[mode="secondary"], .qc-cmp2-button[data-testid="reject-all"]'; - if (tryClick(selector, 'Quantcast')) return clicked; - } - - // Usercentrics (shadow DOM) - const ucRoot = document.querySelector('#usercentrics-root'); - if (ucRoot && ucRoot.shadowRoot) { - const shadow = ucRoot.shadowRoot; - const btn = acceptCookies - ? shadow.querySelector('[data-testid="uc-accept-all-button"]') - : shadow.querySelector('[data-testid="uc-deny-all-button"]'); - if (btn) { btn.click(); clicked.push('Usercentrics'); return clicked; } - } - - // TrustArc - if (document.querySelector('#truste-consent-track') || document.querySelector('.trustarc-banner')) { - const selector = acceptCookies ? '#truste-consent-button, .trustarc-agree-btn' : '.trustarc-decline-btn'; - if (tryClick(selector, 'TrustArc')) return clicked; - } - - // Klaro - if (document.querySelector('.klaro')) { - const selector = acceptCookies ? '.klaro .cm-btn-accept-all, .klaro .cm-btn-success' : '.klaro .cm-btn-decline'; - if (tryClick(selector, 'Klaro')) return clicked; - } - - // BBC - if (document.querySelector('#bbccookies, .bbccookies-banner')) { - if (acceptCookies && tryClick('#bbccookies-continue-button', 'BBC')) return clicked; - } - - // Amazon - if (document.querySelector('#sp-cc') || document.querySelector('#sp-cc-accept')) { - const selector = acceptCookies ? '#sp-cc-accept' : '#sp-cc-rejectall-link, #sp-cc-decline'; - if (tryClick(selector, 'Amazon')) return clicked; - } - - // CookieYes - if (document.querySelector('#cookie-law-info-bar') || document.querySelector('.cky-consent-container')) { - const selector = acceptCookies ? '#cookie_action_close_header, .cky-btn-accept' : '.cky-btn-reject'; - if (tryClick(selector, 'CookieYes')) return clicked; - } - - // Generic containers - const consentContainers = [ - '[class*="cookie-banner"]', '[class*="cookie-consent"]', '[class*="cookie-notice"]', - '[class*="cookieBanner"]', '[class*="cookieConsent"]', '[class*="cookieNotice"]', - '[id*="cookie-banner"]', '[id*="cookie-consent"]', '[id*="cookie-notice"]', - '[class*="consent-banner"]', '[class*="consent-modal"]', '[class*="consent-dialog"]', - '[class*="gdpr"]', '[id*="gdpr"]', '[class*="privacy-banner"]', '[class*="privacy-notice"]', - '[role="dialog"][aria-label*="cookie" i]', '[role="dialog"][aria-label*="consent" i]', - ]; - - for (const containerSel of consentContainers) { - const containers = document.querySelectorAll(containerSel); - for (const container of containers) { - if (!isVisible(container)) continue; - // Skip html/body elements that might match - if (container.tagName === 'HTML' || container.tagName === 'BODY') continue; - const btn = findButtonByText(patterns, container); - if (btn) { btn.click(); clicked.push('Generic (' + containerSel + ')'); return clicked; } - } - } - - // Last resort: find button near cookie-related text content - // Look for visible containers that mention "cookie" and have accept/reject buttons - // Include custom elements (Reddit uses rpl-modal-card, etc.) - const allContainers = document.querySelectorAll('div, section, aside, [class*="modal"], [class*="dialog"], [role="dialog"]'); - for (const container of allContainers) { - if (!isVisible(container)) continue; - const text = container.textContent?.toLowerCase() || ''; - // Must mention cookies and be reasonably sized (not the whole page) - if (text.includes('cookie') && text.length > 100 && text.length < 3000) { - const btn = findButtonByText(patterns, container); - if (btn && isVisible(btn)) { - btn.click(); - clicked.push('Generic (text-based)'); - return clicked; - } - } - } - - // Final fallback: look for any visible button with exact accept/reject text - // that appears alongside cookie-related content on the page - if (document.body.textContent?.toLowerCase().includes('cookie')) { - const exactPatterns = acceptCookies - ? ['accept all', 'accept cookies', 'allow all', 'i agree', 'alle akzeptieren'] - : ['reject all', 'decline all', 'reject optional', 'alle ablehnen']; - const singleWordPatterns = acceptCookies ? ['accept', 'agree'] : ['reject', 'decline']; - const buttons = document.querySelectorAll('button, [role="button"]'); - for (const btn of buttons) { - if (!isVisible(btn)) continue; - const text = (btn.textContent || '').trim().toLowerCase(); - if (exactPatterns.some(p => text.includes(p))) { - btn.click(); - clicked.push('Generic (exact match)'); - return clicked; - } - } - // Try single-word matches as last resort - for (const btn of buttons) { - if (!isVisible(btn)) continue; - const text = (btn.textContent || '').trim().toLowerCase(); - if (singleWordPatterns.some(p => text === p)) { - btn.click(); - clicked.push('Generic (single word)'); - return clicked; - } - } - } - - return clicked; -}`; - -const IFRAME_DISMISS_SCRIPT = `(acceptCookies) => { - const clicked = []; - - const isVisible = (el) => { - if (!el) return false; - const style = getComputedStyle(el); - return style.display !== 'none' && - style.visibility !== 'hidden' && - style.opacity !== '0' && - (el.offsetParent !== null || style.position === 'fixed' || style.position === 'sticky'); - }; - - const rejectIndicators = ['do not', "don't", 'nicht', 'no ', 'refuse', 'reject', 'decline', 'deny', 'disagree', 'ablehnen', 'refuser', 'rifiuta', 'rechazar', 'manage', 'settings', 'options', 'customize']; - const acceptIndicators = ['accept', 'agree', 'allow', 'yes', 'ok', 'got it', 'continue', 'akzeptieren', 'zustimmen', 'accepter', 'accetta', 'aceptar']; - - const isRejectButton = (text) => rejectIndicators.some(p => text.includes(p)); - const isAcceptButton = (text) => acceptIndicators.some(p => text.includes(p)) && !isRejectButton(text); - - const buttons = document.querySelectorAll('button, [role="button"]'); - for (const btn of buttons) { - const text = (btn.textContent || '').trim().toLowerCase(); - if (!isVisible(btn)) continue; - const shouldClick = acceptCookies ? isAcceptButton(text) : isRejectButton(text); - if (shouldClick) { btn.click(); clicked.push('iframe: ' + text.slice(0, 30)); return clicked; } - } - - const spBtn = acceptCookies - ? document.querySelector('[title="Accept All"], [title="Accept"], [aria-label*="Accept"]') - : document.querySelector('[title="Reject All"], [title="Reject"], [aria-label*="Reject"]'); - if (spBtn) { spBtn.click(); clicked.push('Sourcepoint iframe'); return clicked; } - - return clicked; -}`; - -// Recursively collect all frames -function collectFrames(frameTree, frames = []) { - frames.push({ id: frameTree.frame.id, url: frameTree.frame.url }); - if (frameTree.childFrames) { - for (const child of frameTree.childFrames) { - collectFrames(child, frames); - } - } - return frames; -} - -// Global timeout -const globalTimeout = setTimeout(() => { - console.error("✗ Global timeout exceeded (30s)"); - process.exit(1); -}, 30000); - -try { - log("connecting..."); - const cdp = await connect(5000); - - log("getting pages..."); - const pages = await cdp.getPages(); - const page = pages.at(-1); - - if (!page) { - console.error("✗ No active tab found"); - process.exit(1); - } - - log("attaching to page..."); - const sessionId = await cdp.attachToPage(page.targetId); - - // Wait a bit for consent dialogs to appear - await new Promise((r) => setTimeout(r, 500)); - - log("trying main page..."); - let result = await cdp.evaluate(sessionId, `(${COOKIE_DISMISS_SCRIPT})(${!reject})`); - - // If nothing found, try iframes - if (result.length === 0) { - log("trying iframes..."); - try { - const frameTree = await cdp.getFrameTree(sessionId); - const frames = collectFrames(frameTree); - - for (const frame of frames) { - if (frame.url === "about:blank" || frame.url.startsWith("javascript:")) continue; - if ( - frame.url.includes("sp_message") || - frame.url.includes("consent") || - frame.url.includes("privacy") || - frame.url.includes("cmp") || - frame.url.includes("sourcepoint") || - frame.url.includes("cookie") || - frame.url.includes("privacy-mgmt") - ) { - log("trying frame:", frame.url.slice(0, 60)); - try { - const frameResult = await cdp.evaluateInFrame( - sessionId, - frame.id, - `(${IFRAME_DISMISS_SCRIPT})(${!reject})` - ); - if (frameResult.length > 0) { - result = frameResult; - break; - } - } catch (e) { - log("frame error:", e.message); - } - } - } - } catch (e) { - log("getFrameTree error:", e.message); - } - } - - if (result.length > 0) { - console.log(`✓ Dismissed cookie dialog (${mode}): ${result.join(", ")}`); - } else { - console.log(`○ No cookie dialog found to ${mode}`); - } - - log("closing..."); - cdp.close(); - log("done"); -} catch (e) { - console.error("✗", e.message); - process.exit(1); -} finally { - clearTimeout(globalTimeout); - setTimeout(() => process.exit(0), 100); -} diff --git a/dot_pi/agent/skills/web-browser/scripts/executable_eval.js b/dot_pi/agent/skills/web-browser/scripts/executable_eval.js deleted file mode 100644 index 98e6f0d0..00000000 --- a/dot_pi/agent/skills/web-browser/scripts/executable_eval.js +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env node - -import { connect } from "./cdp.js"; - -const DEBUG = process.env.DEBUG === "1"; -const log = DEBUG ? (...args) => console.error("[debug]", ...args) : () => {}; - -const code = process.argv.slice(2).join(" "); -if (!code) { - console.log("Usage: eval.js 'code'"); - console.log("\nExamples:"); - console.log(' eval.js "document.title"'); - console.log(" eval.js \"document.querySelectorAll('a').length\""); - process.exit(1); -} - -// Global timeout -const globalTimeout = setTimeout(() => { - console.error("✗ Global timeout exceeded (45s)"); - process.exit(1); -}, 45000); - -try { - log("connecting..."); - const cdp = await connect(5000); - - log("getting pages..."); - const pages = await cdp.getPages(); - const page = pages.at(-1); - - if (!page) { - console.error("✗ No active tab found"); - process.exit(1); - } - - log("attaching to page..."); - const sessionId = await cdp.attachToPage(page.targetId); - - log("evaluating..."); - const expression = `(async () => { return (${code}); })()`; - const result = await cdp.evaluate(sessionId, expression); - - log("formatting result..."); - if (Array.isArray(result)) { - for (let i = 0; i < result.length; i++) { - if (i > 0) console.log(""); - for (const [key, value] of Object.entries(result[i])) { - console.log(`${key}: ${value}`); - } - } - } else if (typeof result === "object" && result !== null) { - for (const [key, value] of Object.entries(result)) { - console.log(`${key}: ${value}`); - } - } else { - console.log(result); - } - - log("closing..."); - cdp.close(); - log("done"); -} catch (e) { - console.error("✗", e.message); - process.exit(1); -} finally { - clearTimeout(globalTimeout); - setTimeout(() => process.exit(0), 100); -} diff --git a/dot_pi/agent/skills/web-browser/scripts/executable_nav.js b/dot_pi/agent/skills/web-browser/scripts/executable_nav.js deleted file mode 100644 index ef100c6b..00000000 --- a/dot_pi/agent/skills/web-browser/scripts/executable_nav.js +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env node - -import { connect } from "./cdp.js"; - -const DEBUG = process.env.DEBUG === "1"; -const log = DEBUG ? (...args) => console.error("[debug]", ...args) : () => {}; - -const url = process.argv[2]; -const newTab = process.argv[3] === "--new"; - -if (!url) { - console.log("Usage: nav.js [--new]"); - console.log("\nExamples:"); - console.log(" nav.js https://example.com # Navigate current tab"); - console.log(" nav.js https://example.com --new # Open in new tab"); - process.exit(1); -} - -// Global timeout -const globalTimeout = setTimeout(() => { - console.error("✗ Global timeout exceeded (45s)"); - process.exit(1); -}, 45000); - -try { - log("connecting..."); - const cdp = await connect(5000); - - log("getting pages..."); - let targetId; - - if (newTab) { - log("creating new tab..."); - const { targetId: newTargetId } = await cdp.send("Target.createTarget", { - url: "about:blank", - }); - targetId = newTargetId; - } else { - const pages = await cdp.getPages(); - const page = pages.at(-1); - if (!page) { - console.error("✗ No active tab found"); - process.exit(1); - } - targetId = page.targetId; - } - - log("attaching to page..."); - const sessionId = await cdp.attachToPage(targetId); - - log("navigating..."); - await cdp.navigate(sessionId, url); - - console.log(newTab ? "✓ Opened:" : "✓ Navigated to:", url); - - log("closing..."); - cdp.close(); - log("done"); -} catch (e) { - console.error("✗", e.message); - process.exit(1); -} finally { - clearTimeout(globalTimeout); - setTimeout(() => process.exit(0), 100); -} diff --git a/dot_pi/agent/skills/web-browser/scripts/executable_pick.js b/dot_pi/agent/skills/web-browser/scripts/executable_pick.js deleted file mode 100644 index a592e43b..00000000 --- a/dot_pi/agent/skills/web-browser/scripts/executable_pick.js +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/env node - -import { connect } from "./cdp.js"; - -const DEBUG = process.env.DEBUG === "1"; -const log = DEBUG ? (...args) => console.error("[debug]", ...args) : () => {}; - -const message = process.argv.slice(2).join(" "); -if (!message) { - console.log("Usage: pick.js 'message'"); - console.log("\nExample:"); - console.log(' pick.js "Click the submit button"'); - process.exit(1); -} - -// Global timeout - 5 minutes for interactive picking -const globalTimeout = setTimeout(() => { - console.error("✗ Global timeout exceeded (5m)"); - process.exit(1); -}, 300000); - -const PICK_SCRIPT = `(message) => { - if (!message) throw new Error("pick() requires a message parameter"); - return new Promise((resolve) => { - const selections = []; - const selectedElements = new Set(); - - const overlay = document.createElement("div"); - overlay.style.cssText = "position:fixed;top:0;left:0;width:100%;height:100%;z-index:2147483647;pointer-events:none"; - - const highlight = document.createElement("div"); - highlight.style.cssText = "position:absolute;border:2px solid #3b82f6;background:rgba(59,130,246,0.1);transition:all 0.1s"; - overlay.appendChild(highlight); - - const banner = document.createElement("div"); - banner.style.cssText = "position:fixed;bottom:20px;left:50%;transform:translateX(-50%);background:#1f2937;color:white;padding:12px 24px;border-radius:8px;font:14px sans-serif;box-shadow:0 4px 12px rgba(0,0,0,0.3);pointer-events:auto;z-index:2147483647"; - - const updateBanner = () => { - banner.textContent = message + " (" + selections.length + " selected, Cmd/Ctrl+click to add, Enter to finish, ESC to cancel)"; - }; - updateBanner(); - - document.body.append(banner, overlay); - - const cleanup = () => { - document.removeEventListener("mousemove", onMove, true); - document.removeEventListener("click", onClick, true); - document.removeEventListener("keydown", onKey, true); - overlay.remove(); - banner.remove(); - selectedElements.forEach((el) => { el.style.outline = ""; }); - }; - - const onMove = (e) => { - const el = document.elementFromPoint(e.clientX, e.clientY); - if (!el || overlay.contains(el) || banner.contains(el)) return; - const r = el.getBoundingClientRect(); - highlight.style.cssText = "position:absolute;border:2px solid #3b82f6;background:rgba(59,130,246,0.1);top:" + r.top + "px;left:" + r.left + "px;width:" + r.width + "px;height:" + r.height + "px"; - }; - - const buildElementInfo = (el) => { - const parents = []; - let current = el.parentElement; - while (current && current !== document.body) { - const parentInfo = current.tagName.toLowerCase(); - const id = current.id ? "#" + current.id : ""; - const cls = current.className ? "." + current.className.trim().split(/\\s+/).join(".") : ""; - parents.push(parentInfo + id + cls); - current = current.parentElement; - } - return { - tag: el.tagName.toLowerCase(), - id: el.id || null, - class: el.className || null, - text: (el.textContent || "").trim().slice(0, 200) || null, - html: el.outerHTML.slice(0, 500), - parents: parents.join(" > "), - }; - }; - - const onClick = (e) => { - if (banner.contains(e.target)) return; - e.preventDefault(); - e.stopPropagation(); - const el = document.elementFromPoint(e.clientX, e.clientY); - if (!el || overlay.contains(el) || banner.contains(el)) return; - - if (e.metaKey || e.ctrlKey) { - if (!selectedElements.has(el)) { - selectedElements.add(el); - el.style.outline = "3px solid #10b981"; - selections.push(buildElementInfo(el)); - updateBanner(); - } - } else { - cleanup(); - const info = buildElementInfo(el); - resolve(selections.length > 0 ? selections : info); - } - }; - - const onKey = (e) => { - if (e.key === "Escape") { - e.preventDefault(); - cleanup(); - resolve(null); - } else if (e.key === "Enter" && selections.length > 0) { - e.preventDefault(); - cleanup(); - resolve(selections); - } - }; - - document.addEventListener("mousemove", onMove, true); - document.addEventListener("click", onClick, true); - document.addEventListener("keydown", onKey, true); - }); -}`; - -try { - log("connecting..."); - const cdp = await connect(5000); - - log("getting pages..."); - const pages = await cdp.getPages(); - const page = pages.at(-1); - - if (!page) { - console.error("✗ No active tab found"); - process.exit(1); - } - - log("attaching to page..."); - const sessionId = await cdp.attachToPage(page.targetId); - - log("waiting for user pick..."); - const expression = `(${PICK_SCRIPT})(${JSON.stringify(message)})`; - const result = await cdp.evaluate(sessionId, expression, 300000); - - log("formatting result..."); - if (Array.isArray(result)) { - for (let i = 0; i < result.length; i++) { - if (i > 0) console.log(""); - for (const [key, value] of Object.entries(result[i])) { - console.log(`${key}: ${value}`); - } - } - } else if (typeof result === "object" && result !== null) { - for (const [key, value] of Object.entries(result)) { - console.log(`${key}: ${value}`); - } - } else { - console.log(result); - } - - log("closing..."); - cdp.close(); - log("done"); -} catch (e) { - console.error("✗", e.message); - process.exit(1); -} finally { - clearTimeout(globalTimeout); - setTimeout(() => process.exit(0), 100); -} diff --git a/dot_pi/agent/skills/web-browser/scripts/executable_screenshot.js b/dot_pi/agent/skills/web-browser/scripts/executable_screenshot.js deleted file mode 100644 index ccffc529..00000000 --- a/dot_pi/agent/skills/web-browser/scripts/executable_screenshot.js +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env node - -import { tmpdir } from "node:os"; -import { join } from "node:path"; -import { writeFileSync } from "node:fs"; -import { connect } from "./cdp.js"; - -const DEBUG = process.env.DEBUG === "1"; -const log = DEBUG ? (...args) => console.error("[debug]", ...args) : () => {}; - -// Global timeout -const globalTimeout = setTimeout(() => { - console.error("✗ Global timeout exceeded (15s)"); - process.exit(1); -}, 15000); - -try { - log("connecting..."); - const cdp = await connect(5000); - - log("getting pages..."); - const pages = await cdp.getPages(); - const page = pages.at(-1); - - if (!page) { - console.error("✗ No active tab found"); - process.exit(1); - } - - log("attaching to page..."); - const sessionId = await cdp.attachToPage(page.targetId); - - log("taking screenshot..."); - const data = await cdp.screenshot(sessionId); - - const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); - const filename = `screenshot-${timestamp}.png`; - const filepath = join(tmpdir(), filename); - - writeFileSync(filepath, data); - console.log(filepath); - - log("closing..."); - cdp.close(); - log("done"); -} catch (e) { - console.error("✗", e.message); - process.exit(1); -} finally { - clearTimeout(globalTimeout); - setTimeout(() => process.exit(0), 100); -}