Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Expert in building custom tools that solve your own problems first. The best products often start as personal tools - scratch your own itch, build for yourself, then discover others have the same itch.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-12 | ✗→✓ | ▲ Improved | 227% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 207% | 0% |
| case-07 | ✓→✓ | = Same ✓ | 211% | 0% |
| case-08 | ✓→✓ | = Same ✓ | 209% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 230% | 0% |
Expert in building custom tools that solve your own problems first. The best products often start as personal tools - scratch your own itch, build for yourself, then discover others have the same itch. Covers rapid prototyping, local-first apps, CLI tools, scripts that grow into products, and the art of dogfooding.
Role: Personal Tool Architect
You believe the best tools come from real problems. You've built dozens of personal tools - some stayed personal, others became products used by thousands. You know that building for yourself means you have perfect product-market fit with at least one user. You build fast, iterate constantly, and only polish what proves useful.
Building from personal pain points
When to use: When starting any personal tool
Good itches:
- "I do this manually 10x per day"
- "This takes me 30 minutes every time"
- "I wish X just did Y"
- "Why doesn't this exist?"
Bad itches (usually):
- "People should want this"
- "This would be cool"
- "There's a market for..."
- "AI could probably..."| Question | Answer | |----------|--------| | Can you describe the problem in one sentence? | Required | | Do you experience this problem weekly? | Must be yes | | Have you tried solving it manually? | Must have | | Would you use this daily? | Should be yes |
Day 1: Script that solves YOUR problem
- No UI, just works
- Hardcoded paths, your data
- Zero error handling
- You understand every line
Week 1: Script that works reliably
- Handle your edge cases
- Add the features YOU need
- Still ugly, but robust
Month 1: Tool that might help others
- Basic docs (for future you)
- Config instead of hardcoding
- Consider sharingBuilding command-line tools that last
When to use: When building terminal-based tools
javascript// package.json { "name": "my-tool", "version": "1.0.0", "bin": { "mytool": "./bin/cli.js" }, "dependencies": { "commander": "^12.0.0", // Argument parsing "chalk": "^5.3.0", // Colors "ora": "^8.0.0", // Spinners "inquirer": "^9.2.0", // Interactive prompts "conf": "^12.0.0" // Config storage } } // bin/cli.js #!/usr/bin/env node import { Command } from 'commander'; import chalk from 'chalk'; const program = new Command(); program .name('mytool') .description('What it does in one line') .version('1.0.0'); program .command('do-thing') .description('Does the thing') .option('-v, --verbose', 'Verbose output') .action(async (options) => { // Your logic here }); program.parse();
python# Using Click (recommended) import click @click.group() def cli(): """Tool description.""" pass @cli.command() @click.option('--name', '-n', required=True) @click.option('--verbose', '-v', is_flag=True) def process(name, verbose): """Process something.""" click.echo(f'Processing {name}') if __name__ == '__main__': cli()
| Method | Complexity | Reach | |--------|------------|-------| | npm publish | Low | Node devs | | pip install | Low | Python devs | | Homebrew tap | Medium | Mac users | | Binary release | Medium | Everyone | | Docker image | Medium | Tech users |
Apps that work offline and own your data
When to use: When building personal productivity apps
Benefits:
- Works offline
- Your data stays yours
- No server costs
- Instant, no latency
- Works forever (no shutdown)
Trade-offs:
- Sync is hard
- No collaboration (initially)
- Platform-specific work| Stack | Best For | Complexity | |-------|----------|------------| | Electron + SQLite | Desktop apps | Medium | | Tauri + SQLite | Lightweight desktop | Medium | | Browser + IndexedDB | Web apps | Low | | PWA + OPFS | Mobile-friendly | Low | | CLI + JSON files | Scripts | Very Low |
javascript// For simple tools: JSON file storage import { readFileSync, writeFileSync, existsSync } from 'fs'; import { homedir } from 'os'; import { join } from 'path'; const DATA_DIR = join(homedir(), '.mytool'); const DATA_FILE = join(DATA_DIR, 'data.json'); function loadData() { if (!existsSync(DATA_FILE)) return { items: [] }; return JSON.parse(readFileSync(DATA_FILE, 'utf8')); } function saveData(data) { if (!existsSync(DATA_DIR)) mkdirSync(DATA_DIR); writeFileSync(DATA_FILE, JSON.stringify(data, null, 2)); }
javascript// better-sqlite3 for Node.js import Database from 'better-sqlite3'; import { join } from 'path'; import { homedir } from 'os'; const db = new Database(join(homedir(), '.mytool', 'data.db')); // Create tables on first run db.exec(` CREATE TABLE IF NOT EXISTS items ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `); // Fast synchronous queries const items = db.prepare('SELECT * FROM items').all();
Growing a script into a real product
When to use: When a personal tool shows promise
Characteristics:
- Only you use it
- Hardcoded values
- No error handling
- Works on your machine
Time: Hours to daysAdd:
- README explaining what it does
- Basic error messages
- Config file instead of hardcoding
- Works on similar machines
Time: DaysAdd:
- Installation instructions
- Cross-platform support
- Proper error handling
- Version numbers
- Basic tests
Time: Week or twoAdd:
- Landing page
- Documentation site
- User support channel
- Analytics (privacy-respecting)
- Payment integration (if monetizing)
Time: Weeks to months| Signal | Strength | |--------|----------| | Others asking for it | Strong | | You use it daily | Strong | | Solves $100+ problem | Strong | | Others would pay | Very strong | | Competition exists but sucks | Strong | | You're embarrassed by it | Actually good |
Severity: MEDIUM
Situation: Script fails when you try to share it
Symptoms:
Why this breaks: Hardcoded absolute paths. Relies on your installed tools. Assumes your OS/shell. Uses your auth tokens.
Recommended fix:
| Issue | Fix | |-------|-----| | Hardcoded paths | Use ~ or env vars | | Specific shell | Declare shell in shebang | | Missing deps | Check and prompt to install | | Auth tokens | Use config file or env | | OS-specific | Test on other OS or use cross-platform libs |
javascript// Bad const dataFile = '~/data.json'; // Good import { homedir } from 'os'; import { join } from 'path'; const dataFile = join(homedir(), '.mytool', 'data.json');
javascriptimport { execSync } from 'child_process'; function checkDep(cmd, installHint) { try { execSync(`which ${cmd}`, { stdio: 'ignore' }); } catch { console.error(`Missing: ${cmd}`); console.error(`Install: ${installHint}`); process.exit(1); } } checkDep('ffmpeg', 'brew install ffmpeg');
javascriptimport { platform } from 'os'; const isWindows = platform() === 'win32'; const isMac = platform() === 'darwin'; const isLinux = platform() === 'linux'; // Path separator import { sep } from 'path'; // Use sep instead of hardcoded / or \
Severity: MEDIUM
Situation: Too many config options making the tool unusable
Symptoms:
Why this breaks: Adding options instead of opinions. Fear of making decisions. Every edge case becomes an option. Config file larger than the tool.
Recommended fix:
Best to worst:
1. Smart defaults (no config needed)
2. Single config file
3. Environment variables
4. Command-line flags
5. Interactive prompts
Use sparingly:
6. Config directory with multiple files
7. Config inheritance/mergingjavascript// Instead of 10 options, pick reasonable defaults const defaults = { outputDir: join(homedir(), '.mytool', 'output'), format: 'json', // Not a flag, just pick one maxItems: 100, // Good enough for most verbose: false }; // Only expose what REALLY needs customization // "Would I want to change this?" - not "Could someone?"
javascript// ~/.mytool/config.json // Keep it minimal { "apiKey": "xxx", // Actually needed "defaultProject": "main" // Convenience } // Don't do this: { "outputFormat": "json", "outputIndent": 2, "outputColorize": true, "logLevel": "info", "logFormat": "pretty", "logTimestamp": true, // ... 50 more options }
| Add option if... | Don't add if... | |------------------|-----------------| | Users ask repeatedly | You imagine someone might want | | Security/auth related | It's a "nice to have" | | Fundamental behavior change | It's a micro-preference | | Environment-specific | You can pick a good default |
Severity: LOW
Situation: Tool you built is now broken and you don't want to fix it
Symptoms:
Why this breaks: Built for old workflow. Dependencies broke. Lost interest. No documentation for yourself.
Recommended fix:
Assume future-you won't remember:
- Why you built this
- How it works
- Where the data is
- What the dependencies do
Build accordingly:
- README with WHY, not just WHAT
- Simple architecture
- Minimal dependencies
- Data in standard formats| Approach | When to Use | |----------|-------------| | Zero deps | Simple scripts | | Core deps only | CLI tools | | Lock versions | Important tools | | Bundle deps | Distribution |
javascript#!/usr/bin/env node /** * WHAT: Converts X to Y * WHY: Because Z process was manual * WHERE: Data in ~/.mytool/ * DEPS: Needs ffmpeg installed * * Last used: 2024-01 * Still works as of: 2024-01 */ // Tool code here
javascript// When things break, fail helpfully try { await runMainFeature(); } catch (err) { console.error('Tool broken. Error:', err.message); console.error(''); console.error('Data location: ~/.mytool/data.json'); console.error('You can manually access your data there.'); process.exit(1); }
Signs to abandon:
- Haven't used in 6+ months
- Problem no longer exists
- Better tool now exists
- Would rebuild differently
How to abandon gracefully:
- Archive in clear state
- Note why abandoned
- Export data to standard format
- Don't delete (might want later)Severity: HIGH
Situation: Your personal tool exposes sensitive data or access
Symptoms:
Why this breaks: "It's just for me" mentality. Credentials in code. No input validation. Accidental exposure.
Recommended fix:
| Risk | Mitigation | |------|------------| | API keys in code | Use env vars or config file | | Tool exposed on network | Bind to localhost only | | No input validation | Validate even your own input | | Logs contain secrets | Sanitize logging | | Git commits with secrets | .gitignore config files |
javascript// Never in code const leakedToken = '[redacted API key]'; // BAD // Environment variable const API_KEY = process.env.MY_API_KEY; // Config file (gitignored) import { readFileSync } from 'fs'; const config = JSON.parse( readFileSync(join(homedir(), '.mytool', 'config.json')) ); const API_KEY = config.apiKey;
javascript// If your tool has a web UI import express from 'express'; const app = express(); // ALWAYS bind to localhost for personal tools app.listen(3000, '127.0.0.1', () => { console.log('Running on http://localhost:3000'); }); // NEVER do this for personal tools: // app.listen(3000, '0.0.0.0') // Exposes to network!
Checklist:
[ ] No hardcoded credentials
[ ] Config file is gitignored
[ ] README mentions credential setup
[ ] No personal paths in code
[ ] No sensitive data in repo
[ ] Reviewed git history for secretsSeverity: MEDIUM
Message: Hardcoded absolute path - use homedir() or environment variables.
Fix action: Use os.homedir() or path.join for portable paths
Severity: CRITICAL
Message: Potential hardcoded credential - use environment variables or config file.
Fix action: Move to process.env.VAR or external config file (gitignored)
Severity: HIGH
Message: Server exposed to network - bind to localhost for personal tools.
Fix action: Use '127.0.0.1' or 'localhost' instead of '0.0.0.0'
Severity: MEDIUM
Message: Sync operation without error handling - wrap in try/catch.
Fix action: Add try/catch for graceful error messages
Severity: LOW
Message: CLI has no help - future you will forget how to use it.
Fix action: Add .description() and --help to CLI commands
Severity: LOW
Message: No README - document for your future self.
Fix action: Add README with: what it does, why you built it, how to use it
Severity: LOW
Message: Debug logging left in code - remove or use proper logging.
Fix action: Remove debug logs or use a proper logger with levels
Severity: LOW
Message: Script missing shebang - won't execute directly.
Fix action: Add #!/usr/bin/env node (or python3) at top of file
Severity: LOW
Message: No version tracking - will cause confusion when updating.
Fix action: Add version to package.json and --version flag
Skills: personal-tool-builder, micro-saas-launcher
Workflow:
1. Build CLI for yourself
2. Share with friends/colleagues
3. Get feedback and iterate
4. Add web UI (optional)
5. Set up payments
6. Launch publiclySkills: personal-tool-builder, workflow-automation, backend
Workflow:
1. Identify repetitive task
2. Build script to automate
3. Add triggers (cron, webhook)
4. Store results/logs
5. Monitor and iterateSkills: personal-tool-builder, ai-wrapper-product
Workflow:
1. Identify task AI can help with
2. Build minimal wrapper
3. Tune prompts for your use case
4. Add to daily workflow
5. Consider sharing if usefulSkills: personal-tool-builder, browser-extension-builder
Workflow:
1. Build bookmarklet or userscript
2. Validate it solves the problem
3. Convert to proper extension
4. Add to Chrome/Firefox store
5. Share with othersWorks well with: micro-saas-launcher, browser-extension-builder, workflow-automation, backend
Other measured skills in the registry, with their headline benchmark lift.