Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions FEATURES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Sovereign AI Workstation — Full Product Breakdown

> Engine: **CODEC v2.3** — 367 features · 74 skills · 940+ tests · 58K+ lines of production code
> Engine: **CODEC v2.3** — 368 features · 75 skills · 940+ tests · 58K+ lines of production code

The product name is **Sovereign AI Workstation**. Throughout this document
and the codebase, **CODEC** refers to the underlying open-source engine /
Expand Down Expand Up @@ -182,7 +182,7 @@ and resume-after-restart guarantees throughout.

---

## 6. CODEC Skills — 78 features (74 skills + 4 infrastructure)
## 6. CODEC Skills — 79 features (75 skills + 4 infrastructure)

### Infrastructure

Expand All @@ -193,15 +193,15 @@ and resume-after-restart guarantees throughout.
| 3 | Skill Marketplace (install, search, list, update, remove, publish) |
| 4 | **`SKILL_OBSERVATION_TRIGGER` declarative trigger metadata** — skills opt into auto-fire via 5 trigger types (window_title_match, clipboard_pattern, file_change, time, compound) *(Phase 2 Step 6)* |

### 74 Built-in Skills
### 75 Built-in Skills

MCP tool name shown where it differs from the file name.

| Category | Skills |
|---|---|
| **Google Workspace** (8) | google_calendar, google_docs, google_drive, google_gmail, google_keep, google_sheets, google_slides, google_tasks |
| **Chrome Automation** (10) | chrome_automate, chrome_click_cdp, chrome_close, chrome_extract, chrome_fill, chrome_open, chrome_read, chrome_scroll, chrome_search, chrome_tabs |
| **System Control** (10) | app_switch, brightness, clipboard, file_ops, file_search, file_write, network_info, process_manager, `system` (system_info), terminal, `volume_brightness` (volume) |
| **System Control** (11) | app_switch, brightness, clipboard, file_ops, file_search, file_write, network_info, process_manager, `system` (system_info), terminal, `volume_brightness` (volume) |
| **Vision & Mouse** (2) | mouse_control (UI-TARS vision click), screenshot_text |
| **AI & Content** (6) | `AI_News_Digest` (ai_news_digest), create_skill, skill_forge, translate, web_search, memory_search |
| **Memory Layer** (5) | memory_search (FTS5), memory_history (temporal facts), memory_entities (CCF map), memory_save, auto_memorize, fact_extract |
Expand Down Expand Up @@ -494,17 +494,17 @@ notification dispatch.
| 3. CODEC Dashboard | 32 |
| 4. CODEC Vibe | 20 |
| 5. CODEC Agents | 20 |
| 6. CODEC Skills | 78 |
| 6. CODEC Skills | 79 |
| 7. CODEC Infrastructure | 36 |
| 8. CODEC Dictate | 15 |
| 9. CODEC Instant | 12 |
| 10. Phase 1 — Agent Substrate *(v2.3)* | 18 |
| 11. Phase 2 — Continuous Observation + Automation *(v2.3)* | 24 |
| 12. Phase 3 — Drop-a-Project Autonomous Agents *(v2.3)* | 32 |
| 13. Phase 3.5 — UX Polish + Proactive Overlay *(v2.3)* | 24 |
| **TOTAL** | **367** |
| **TOTAL** | **368** |

**367 features · 74 skills · 940+ tests · 58K+ lines of production code**
**368 features · 75 skills · 940+ tests · 58K+ lines of production code**

### What's new in v2.3 — Phase 1 + 2 + 3 + 3.5

Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
</p>

<p align="center">
<a href="FEATURES.md"><img src="https://img.shields.io/badge/features-367+-blue?style=flat-square" alt="367+ Features"/></a>
<img src="https://img.shields.io/badge/skills-74-orange?style=flat-square" alt="74 Skills"/>
<a href="FEATURES.md"><img src="https://img.shields.io/badge/features-368+-blue?style=flat-square" alt="368+ Features"/></a>
<img src="https://img.shields.io/badge/skills-75-orange?style=flat-square" alt="75 Skills"/>
<img src="https://img.shields.io/badge/tests-940+-green?style=flat-square" alt="940+ Tests"/>
<img src="https://img.shields.io/badge/lines-58K+-purple?style=flat-square" alt="58K+ Lines"/>
<img src="https://img.shields.io/badge/license-MIT-brightgreen?style=flat-square" alt="MIT License"/>
Expand Down Expand Up @@ -46,7 +46,7 @@ No cloud dependency. No data leaving the machine unless you choose. No subscript

| # | Product | What It Does |
|:-:|---|---|
| 1 | **CODEC Core** | Voice command layer + vision mouse control — 74 skills, screen clicks by voice |
| 1 | **CODEC Core** | Voice command layer + vision mouse control — 75 skills, screen clicks by voice |
| 2 | **CODEC Dictate** | Hold, speak, paste — hands-free F5 live typing at cursor, draft refinement, floating overlays |
| 3 | **CODEC Instant** | Right-click → 8 AI services system-wide — proofread, translate, reply, explain |
| 4 | **CODEC Chat** | 250K-context conversational AI + 12 autonomous agent crews |
Expand All @@ -60,7 +60,7 @@ No cloud dependency. No data leaving the machine unless you choose. No subscript

Always-on voice assistant. Say *"Hey CODEC"* or press F13 to activate. F18 for voice commands. F16 for text input.

74 skills fire instantly: Google Calendar, Gmail, Drive, Docs, Sheets, Tasks, Keep, Chrome automation, web search, Hue lights, timers, Spotify, clipboard, terminal commands, PM2 control, and more. Most skills bypass the LLM entirely — direct action, zero latency. Skills are matched by trigger specificity — longer, more specific triggers always win over generic ones.
75 skills fire instantly: Google Calendar, Gmail, Drive, Docs, Sheets, Tasks, Keep, Chrome automation, web search, Hue lights, timers, Spotify, clipboard, terminal commands, PM2 control, and more. Most skills bypass the LLM entirely — direct action, zero latency. Skills are matched by trigger specificity — longer, more specific triggers always win over generic ones.

**Vision Mouse Control — See & Click**

Expand Down Expand Up @@ -216,7 +216,7 @@ Three smart agents ship built-in: Daily Briefing, Restaurant Decider (location-a
</p>
<p align="center">
<img src="docs/screenshots/terminal.png" alt="Terminal" width="400"/><br/>
<em>74 skills loaded at startup</em>
<em>75 skills loaded at startup</em>
</p>
<p align="center">
<img src="docs/screenshots/cortex.png" alt="Cortex Neural Map" width="720"/><br/>
Expand Down Expand Up @@ -366,7 +366,7 @@ Claude Desktop/Code/Cursor gain — through this one MCP bridge — everything C

- **Your Mac, your apps** — native macOS control: mouse/keyboard via vision model, screenshot text extraction, app switching, clipboard, brightness/volume, Philips Hue, Spotify, Apple Notes, Reminders, Clock timers, music. No browser sandbox.
- **Your memory** — FTS5-searchable history of every CODEC conversation. Claude can recall what *you* said weeks ago, not just this chat.
- **Your skills, not Anthropic's** — 74 pluggable CODEC skills instantly callable as tools. Write one locally in Python, it shows up in Claude without a deploy.
- **Your skills, not Anthropic's** — 75 pluggable CODEC skills instantly callable as tools. Write one locally in Python, it shows up in Claude without a deploy.
- **Your LLM, your choice** — same skill catalog works whether the brain is local Qwen (offline, private) or cloud Claude. The toolkit outlives the model.
- **Your voice pipeline** — Whisper STT, Kokoro TTS, wake-word — all reachable from the chat loop if you want voice output of a Claude answer.

Expand Down Expand Up @@ -572,7 +572,7 @@ codec_marketplace.py — Skill marketplace CLI
codec_overlays.py — AppKit overlay notifications (fullscreen compatible)
ax_bridge/ — Swift AX accessibility bridge
swift-overlay/ — Native macOS status bar app (NSPanel, event JSONL poller)
skills/ — 74 built-in skills (incl. vision mouse control)
skills/ — 75 built-in skills (incl. vision mouse control)
tests/ — 940+ pytest tests across 53 files
request_mic.py — macOS microphone permission helper (AVFoundation)
install.sh — One-line installer
Expand Down Expand Up @@ -633,7 +633,7 @@ python3 setup_codec.py

## Contributing

All skill contributions welcome. 74 built-in skills, 940+ tests, marketplace growing.
All skill contributions welcome. 75 built-in skills, 940+ tests, marketplace growing.

```bash
git clone https://github.com/AVADSA25/codec.git
Expand Down
258 changes: 258 additions & 0 deletions skills/file_write.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
"""CODEC Skill: File Write — save content to a file anywhere on the Mac.

Purpose-built for remote callers (claude.ai over HTTP MCP). Writes only —
no read, no delete, no list. Every write is logged to ~/.codec/file_write.log
so you can audit what the remote Claude has been saving.

Usage patterns the skill understands (pass in the `task` string):

save this to ~/Documents/notes/plan.md
```
# Plan
- step 1
- step 2
```

write file ~/Desktop/scratch.txt content: hello world

path: ~/Projects/foo/bar.md
mode: append
content:
---
new entry
---

The skill accepts `mode: write` (default, overwrites) or `mode: append`.
"""
SKILL_NAME = "file_write"
SKILL_DESCRIPTION = (
"Save text content to a file anywhere on the Mac (creates parent dirs). "
"Input: a task string that includes the destination path and the content "
"to write. Example: \"save to ~/Documents/plan.md\\n```\\n# Plan\\n- ...\\n```\". "
"Supports mode: write (default, overwrites) or mode: append. "
"Blocks system paths (/System, /etc, /usr, /Library) and credential files "
"(.ssh, .env, id_rsa, keychain, etc.). Every write is audited."
)
SKILL_MCP_EXPOSE = True
SKILL_TRIGGERS = [
"save to file", "save this to", "write to file", "write file",
"create file", "save file", "store in file", "export to",
"dump to", "put in file", "append to file",
]

import os
import re
import json
import time
from datetime import datetime

# ── Configurable limits ──
_MAX_WRITE_BYTES = 500_000 # 500 KB per call — plenty for notes, code, docs
_AUDIT_LOG = os.path.expanduser("~/.codec/file_write.log")

# ── Path safety ──
# These directory roots are ALWAYS blocked, regardless of who's calling.
_BLOCKED_ROOTS = [
"/System", "/Library", "/usr", "/bin", "/sbin", "/etc",
"/var", "/private", "/dev", "/Volumes",
]
# Any filename (case-insensitive substring) in this list is blocked.
_BLOCKED_FILENAME_PATTERNS = [
".ssh", ".gnupg", ".env", "credentials", "secrets", "secret",
".aws", ".gcloud", ".kube", "id_rsa", "id_ed25519", "id_dsa",
".netrc", ".npmrc", ".pypirc", "keychain", "password", "token",
"api_key", "apikey", "private_key",
]
# Block extensions that could be executable shells / trust-sensitive.
_BLOCKED_EXTS = [".pem", ".key", ".p12", ".pfx", ".keystore"]


def _is_safe_target(path: str):
"""Return (True, "") if safe to write; (False, reason) otherwise.

Resolves symlinks via realpath so a symlink into /etc can't slip through.
"""
if not path:
return False, "Empty path."
expanded = os.path.expanduser(path)
# If parent exists, realpath the parent and append basename — the file
# itself may not exist yet, so we can't realpath(path) directly.
parent = os.path.dirname(expanded) or "."
try:
real_parent = os.path.realpath(parent)
except Exception:
real_parent = parent
real_path = os.path.join(real_parent, os.path.basename(expanded))

for blocked in _BLOCKED_ROOTS:
if real_path == blocked or real_path.startswith(blocked + os.sep):
return False, f"Blocked system path: {blocked}"

base_lower = os.path.basename(real_path).lower()
for pat in _BLOCKED_FILENAME_PATTERNS:
if pat in base_lower:
return False, f"Blocked filename pattern: {pat!r}"

for ext in _BLOCKED_EXTS:
if base_lower.endswith(ext):
return False, f"Blocked extension: {ext}"

# Sanity: must be under $HOME or /tmp (broad but not everything).
home = os.path.realpath(os.path.expanduser("~"))
tmp = "/tmp"
if not (real_path.startswith(home + os.sep) or real_path.startswith(tmp + os.sep)):
return False, (
f"Target must live under $HOME or /tmp (got: {real_path}). "
"Adjust file_write._BLOCKED_ROOTS if you need wider scope."
)

return True, ""


# ── Parsing ──

_PATH_HINTS = [
r'(?:^|\s)(?:path|file|to|into|at|destination|dest)\s*[:=]\s*["\']?([^"\'\n]+?)["\']?(?:\s|$)',
r'save\s+(?:this\s+|that\s+|it\s+)?(?:to\s+|into\s+|at\s+)["\']?([^"\'\n]+?)["\']?(?:\s|$)',
r'write\s+(?:this\s+|to\s+|into\s+)?["\']?(~?[/\w][\w./\s_-]*?\.[\w]{1,8})["\']?',
r'(["\'])(~?/[^"\'\n]+)\1',
r'(~?/[\w./_-]+\.[\w]{1,8})',
]

_MODE_RE = re.compile(r'(?:^|\s)mode\s*[:=]\s*(write|append|overwrite)\b', re.I)


def _extract_path(task: str):
"""Best-effort path extraction from a natural-language instruction."""
for pat in _PATH_HINTS:
m = re.search(pat, task, re.IGNORECASE | re.MULTILINE)
if m:
# Last group is always the captured path
groups = [g for g in m.groups() if g]
if groups:
candidate = groups[-1].strip().rstrip(".,;:")
if candidate and ("/" in candidate or candidate.startswith("~")):
return os.path.expanduser(candidate)
return None


def _extract_content(task: str):
"""Pull the content out. Preference order:
1) Triple-backtick fenced block (optionally with language tag)
2) After an explicit 'content:' / 'body:' / 'data:' / 'text:' marker
3) After a markdown '---' separator
"""
# 1) ```[lang]\n ... \n```
fence = re.search(r'```[\w+-]*\s*\n?(.*?)\n?```', task, re.DOTALL)
if fence:
return fence.group(1).rstrip("\n")

# 2) explicit marker — take everything after it (trailing newline trimmed)
for kw in ("content:", "body:", "data:", "text:"):
idx = task.lower().find(kw)
if idx >= 0:
after = task[idx + len(kw):].lstrip("\n").rstrip()
# strip a leading space
if after.startswith(" "):
after = after[1:]
if after:
return after

# 3) --- separator (take everything AFTER the last ---)
if "\n---\n" in task:
return task.rsplit("\n---\n", 1)[-1].rstrip()

return None


def _extract_mode(task: str) -> str:
m = _MODE_RE.search(task)
if not m:
return "write"
val = m.group(1).lower()
if val in ("write", "overwrite"):
return "write"
if val == "append":
return "append"
return "write"


# ── Audit log ──

def _audit_write(path: str, size: int, mode: str, transport: str):
"""Append one JSON line per successful write."""
try:
os.makedirs(os.path.dirname(_AUDIT_LOG), exist_ok=True)
entry = {
"ts": datetime.utcnow().isoformat() + "Z",
"path": path,
"size": size,
"mode": mode,
"transport": transport,
}
with open(_AUDIT_LOG, "a", encoding="utf-8") as f:
f.write(json.dumps(entry) + "\n")
except Exception:
# Never fail a write because of audit-log problems.
pass


# ── Entry point ──

def run(task: str, context: str = "") -> str:
if not isinstance(task, str) or not task.strip():
return "file_write: empty task. Example: \"save to ~/notes.txt content: hello\""

path = _extract_path(task)
if not path:
return (
"file_write: couldn't find a destination path in the task. "
"Try: 'save to ~/Documents/foo.md\\n```\\n<your text>\\n```' "
"or 'path: ~/Desktop/x.txt\\ncontent: hi'"
)

content = _extract_content(task)
if content is None:
return (
f"file_write: resolved path '{path}' but found no content. "
"Put the content in a triple-backtick block, or after 'content:'."
)

size = len(content.encode("utf-8"))
if size > _MAX_WRITE_BYTES:
return (
f"file_write: content too large ({size:,} bytes > "
f"{_MAX_WRITE_BYTES:,} cap). Split into smaller chunks or raise "
"_MAX_WRITE_BYTES in skills/file_write.py."
)

safe, reason = _is_safe_target(path)
if not safe:
return f"file_write: refused — {reason}"

mode_label = _extract_mode(task)
fmode = "a" if mode_label == "append" else "w"

# Ensure parent dir exists.
parent = os.path.dirname(path)
if parent and not os.path.exists(parent):
try:
os.makedirs(parent, exist_ok=True)
except Exception as e:
return f"file_write: cannot create directory {parent}: {e}"

try:
with open(path, fmode, encoding="utf-8") as f:
f.write(content)
except PermissionError as e:
return f"file_write: permission denied for {path}: {e}"
except OSError as e:
return f"file_write: OS error writing {path}: {e}"
except Exception as e:
return f"file_write: unexpected error writing {path}: {type(e).__name__}: {e}"

transport = os.environ.get("CODEC_MCP_TRANSPORT", "stdio")
_audit_write(path, size, mode_label, transport)

verb = "Appended to" if mode_label == "append" else "Saved"
return f"{verb} {path} ({size:,} bytes)."
Loading