diff --git a/TASKS.md b/TASKS.md index 8c0a69b..d489727 100644 --- a/TASKS.md +++ b/TASKS.md @@ -7,7 +7,7 @@ ## In Progress *Currently working on* -- [ ] Sandbox buildout - setting up environment +- [ ] Sandbox buildout - continue expanding capabilities ## Waiting *Blocked or waiting on something* @@ -18,6 +18,9 @@ - [x] 2026-01-30: Initial setup - folder structure, templates, scripts - [x] 2026-01-30: System packages installed - ffmpeg, imagemagick, neovim, tmux, pip, pnpm, build-essential +- [x] 2026-01-30: Shell aliases, tmux config, bashrc integration +- [x] 2026-01-30: Tools: search, inbox-processor, daily-digest, web-clipper +- [x] 2026-01-30: Created workspace-tools skill --- diff --git a/skills/workspace-tools/SKILL.md b/skills/workspace-tools/SKILL.md new file mode 100644 index 0000000..d2c82cd --- /dev/null +++ b/skills/workspace-tools/SKILL.md @@ -0,0 +1,67 @@ +--- +name: workspace-tools +description: Tools for managing the OpenClaw workspace. Use for searching files, checking inbox, generating daily digests, creating projects, and workspace organization tasks. +--- + +# Workspace Tools + +Tools for managing this workspace. + +## Available Tools + +### Search (`tools/search.py`) +Search workspace files for keywords. +```bash +python3 $WORKSPACE/tools/search.py "query" [max_results] +``` + +### Inbox (`tools/inbox-processor.py`) +Show inbox contents and pending items. +```bash +python3 $WORKSPACE/tools/inbox-processor.py +``` + +### Daily Digest (`tools/daily-digest.py`) +Generate activity summary. +```bash +python3 $WORKSPACE/tools/daily-digest.py +``` + +### New Project (`scripts/new-project.sh`) +Create a new project from template. +```bash +$WORKSPACE/scripts/new-project.sh project-name +``` + +## Shell Aliases + +If sourced from bashrc, these aliases are available: +- `ws` - cd to workspace +- `proj` - cd to projects +- `tasks` - show TASKS.md +- `today` - show today's notes +- `note "text"` - add timestamped note to today's log +- `search "query"` - search workspace +- `digest` - run daily digest + +## Workspace Structure + +``` +workspace/ +├── projects/ - Active work +├── docs/ - Reference materials +├── inbox/ - Items to process +├── archive/ - Completed work +├── memory/ - Daily notes (YYYY-MM-DD.md) +├── templates/ - Project/note templates +├── scripts/ - Shell scripts +├── tools/ - Python utilities +└── skills/ - Custom skills +``` + +## Conventions + +- File names: lowercase, hyphens, no spaces +- Dates: YYYY-MM-DD format +- Projects: one folder per project in projects/ +- Archive completed work, don't delete diff --git a/tools/README.md b/tools/README.md index 05ce729..3697bed 100644 --- a/tools/README.md +++ b/tools/README.md @@ -22,8 +22,15 @@ Generate daily activity summary. python3 tools/daily-digest.py ``` +### web-clipper.py +Save URLs with titles, notes, and tags. +```bash +python3 tools/web-clipper.py add "url" "title" "notes" +python3 tools/web-clipper.py list [limit] +python3 tools/web-clipper.py search "query" +``` + ## Planned -- [ ] Web clipper (save URLs with notes) - [ ] File indexer with tags - [ ] Reminder system diff --git a/tools/web-clipper.py b/tools/web-clipper.py new file mode 100755 index 0000000..e0e9686 --- /dev/null +++ b/tools/web-clipper.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +""" +Web clipper - save URLs with notes to a clips file. +""" + +import sys +import json +from datetime import datetime +from pathlib import Path +from urllib.parse import urlparse + +WORKSPACE = Path("/home/wdjones/.openclaw/workspace") +CLIPS_FILE = WORKSPACE / "docs" / "clips.json" + +def load_clips() -> list: + """Load existing clips.""" + if CLIPS_FILE.exists(): + with open(CLIPS_FILE) as f: + return json.load(f) + return [] + +def save_clips(clips: list): + """Save clips to file.""" + CLIPS_FILE.parent.mkdir(parents=True, exist_ok=True) + with open(CLIPS_FILE, 'w') as f: + json.dump(clips, f, indent=2) + +def add_clip(url: str, title: str = None, notes: str = None, tags: list = None): + """Add a new clip.""" + clips = load_clips() + + clip = { + 'url': url, + 'title': title or urlparse(url).netloc, + 'notes': notes, + 'tags': tags or [], + 'saved': datetime.now().isoformat(), + } + + clips.insert(0, clip) + save_clips(clips) + print(f"Saved: {clip['title']}") + return clip + +def list_clips(limit: int = 10, tag: str = None): + """List saved clips.""" + clips = load_clips() + + if tag: + clips = [c for c in clips if tag in c.get('tags', [])] + + if not clips: + print("No clips found") + return + + print(f"📎 Saved Clips ({len(clips)} total)\n") + + for clip in clips[:limit]: + saved = clip['saved'][:10] + tags = ' '.join(f"#{t}" for t in clip.get('tags', [])) + print(f"[{saved}] {clip['title']}") + print(f" {clip['url']}") + if clip.get('notes'): + print(f" → {clip['notes']}") + if tags: + print(f" {tags}") + print() + +def search_clips(query: str): + """Search clips by title, URL, or notes.""" + clips = load_clips() + query_lower = query.lower() + + matches = [] + for clip in clips: + searchable = f"{clip.get('title', '')} {clip.get('url', '')} {clip.get('notes', '')}".lower() + if query_lower in searchable: + matches.append(clip) + + if not matches: + print(f"No clips matching: {query}") + return + + print(f"Found {len(matches)} clips:\n") + for clip in matches: + print(f" {clip['title']}: {clip['url']}") + +def main(): + if len(sys.argv) < 2: + print("Usage:") + print(" web-clipper.py add [title] [notes]") + print(" web-clipper.py list [limit]") + print(" web-clipper.py search ") + sys.exit(1) + + cmd = sys.argv[1] + + if cmd == "add" and len(sys.argv) >= 3: + url = sys.argv[2] + title = sys.argv[3] if len(sys.argv) > 3 else None + notes = sys.argv[4] if len(sys.argv) > 4 else None + add_clip(url, title, notes) + elif cmd == "list": + limit = int(sys.argv[2]) if len(sys.argv) > 2 else 10 + list_clips(limit) + elif cmd == "search" and len(sys.argv) >= 3: + search_clips(sys.argv[2]) + else: + print("Unknown command") + +if __name__ == "__main__": + main()