# i-self Features
A deeper tour of every subcommand than the [README](README.md): what it
actually does, when to use it, worked examples, and known limitations.
Where the README is a reference card, this is the manual.
> **Versioning note.** This document targets **0.4.x**. Earlier 1.0.0
> releases advertised features that were stubs; the [CHANGELOG](CHANGELOG.md)
> entry for 0.4.0 lists the breaking changes that made everything below
> actually work. Anything still flagged "stub" or "best-effort" is called
> out explicitly here.
## Contents
- [Code intelligence](#code-intelligence)
- [Repo scanning & profile (`setup`)](#repo-scanning--profile-setup)
- [Semantic search & RAG (`index`, `query`, `ask`)](#semantic-search--rag-index-query-ask)
- [Code review (`review`)](#code-review-review)
- [Skills & learning (`skills`, `learn`)](#skills--learning-skills-learn)
- [Cross-agent session sharing](#cross-agent-session-sharing)
- [Activity monitoring (`monitor`, `track`)](#activity-monitoring-monitor-track)
- [Automation rules (`automate`)](#automation-rules-automate)
- [Vulnerability scanner (`vuln`)](#vulnerability-scanner-vuln)
- [Cloud sync (`sync`)](#cloud-sync-sync)
- [Web dashboard / REST API (`dashboard`, `api`)](#web-dashboard--rest-api-dashboard-api)
- [Snippets (`snippet`)](#snippets-snippet)
- [Messaging (`message`)](#messaging-message)
- [Team aggregation (`team`)](#team-aggregation-team)
- [Built-in analyzers (`plugin`)](#built-in-analyzers-plugin)
- [Things that look like features but aren't](#things-that-look-like-features-but-arent)
---
## Code intelligence
### Repo scanning & profile (`setup`)
Walks every repo it can find — across GitHub, GitLab, Bitbucket, and
local directories — and builds a "developer profile" stored at
`~/.i-self/profile.json`. The profile records languages, framework usage,
commit cadence, and patterns the analyzer pulled out of your code.
```bash
i-self setup # interactive wizard
i-self refresh # rescan, picks up new repos and recent commits
i-self status # show what's been profiled
```
Tokens for each VCS go in `~/.i-self/config.toml`:
```toml
github_token = "ghp_..."
gitlab_token = "..."
bitbucket_token = "..."
```
**Limitations.** Forks and archived repos are skipped by default —
override with `include_forks = true` / `include_archived = true` in
config. Initial scan of a large GitHub account can take several minutes;
subsequent `refresh` runs are incremental.
### Semantic search & RAG (`index`, `query`, `ask`)
`index` chunks source files and embeds them; `query`/`search` finds
similar chunks; `ask` runs RAG (retrieval-augmented generation) by
fetching relevant chunks and feeding them to the configured LLM.
```bash
i-self index ./my-project
i-self query "where do we handle retry logic?"
i-self search "auth middleware" --top-k 20 --language rust
i-self ask "Why does the websocket reconnect?" --use-rag
```
**Embedding backend selection.** When `OPENAI_API_KEY` is set, embeddings
go through `text-embedding-3-small` at 384 dimensions (real semantic
embeddings). Without a key, a hash-bucket fallback is used and a loud
warning is logged at startup — search will degrade to keyword matching.
**Vector-space safety.** Embeddings are tagged with the producing model
in `~/.i-self/embeddings/manifest.json`. If you toggle `OPENAI_API_KEY`
between runs, the index refuses to mix vectors from different backends —
loads return empty and the warning tells you to re-run `i-self index`.
**Limitations.** No incremental reindexing yet — `i-self index` always
re-embeds the whole tree. Expensive on big monorepos. Chunking is
boundary-aware (function / class headers) but not language-server-aware,
so very long functions get split mid-body.
### Code review (`review`)
Runs the configured LLM over a diff or a code blob, with your developer
profile injected as context so the review reflects your conventions.
```bash
i-self review code --code "$(cat src/foo.rs)" --language rust
i-self review pr --base main --head HEAD # against current git diff
```
Built-in analyzer plugins (security patterns, performance patterns, doc
coverage) run alongside the LLM and surface deterministic findings.
### Skills & learning (`skills`, `learn`)
`skills` extracts a proficiency profile from your code and (optionally)
gap-analyzes it against a job profile.
```bash
i-self skills profile # what you do well
i-self skills gap "Senior Backend Engineer (Python, AWS, Kafka)"
i-self learn paths # recommended courses / books / projects
```
**Limitations.** Skill levels are heuristic (lines-of-code × recency × variety),
not benchmarked. Treat the gap analysis as a conversation starter, not a
verdict.
---
## Cross-agent session sharing
The headline feature added in 0.4.x. Discover AI-agent transcripts on
disk, render them to a portable format, and **import them into a different
agent** so a conversation started in one tool can continue in another.
### Provider matrix
| **Claude Code** | ✅ | ✅ | ✅ | `~/.claude/projects/<encoded-cwd>/<uuid>.jsonl` |
| **Aider** | ✅ | ✅ | ✅ | `<project>/.aider.chat.history.md` |
| **Goose** (Block) | ✅ | ✅ | ✅ | `~/.config/goose/sessions/<id>.jsonl` |
| **OpenAI Codex CLI** | ✅ | ✅ | ✅ | `~/.codex/sessions/YYYY/MM/DD/rollout-<uuid>.jsonl` |
| **Continue.dev** | ✅ | ✅ | ✅ | `~/.continue/sessions/<id>.json` (or legacy `sessions.json`) |
| **OpenCode** | ✅ | ✅ | ✗ | `~/.local/share/opencode/storage/session/{info,message}/` |
| **Generic OpenAI JSON** | ✅* | ✅ | ✅ | any dir set via `ISELF_GENERIC_DIR` |
| **Clipboard** (paste-into) | n/a | n/a | ✅ | stdout (pipe to `pbcopy`/`xclip`/`Set-Clipboard`) |
| **Copilot Chat / Cline / Cursor** | ✗ | ✗ | clipboard only | not file-addressable |
*The generic provider only enumerates when `ISELF_GENERIC_DIR` is set,
because we don't want to scan random JSON files in your home dir.
Per-target data directory overrides: `ISELF_GOOSE_DIR`, `ISELF_CODEX_DIR`,
`ISELF_CONTINUE_DIR`, `ISELF_OPENCODE_DIR`, `ISELF_AIDER_SEARCH_ROOTS`
(colon-separated), `ISELF_GENERIC_DIR`.
### Discovering, exporting, uploading
```bash
# Newest-first list across every supported provider
i-self share ls
i-self share ls --provider claude-code --since 7d # filters: h/d/w
# Export to portable JSON (the canonical interchange format)
i-self share export <id> --format json --output session.json
# Render for human reading
i-self share export <id> --format markdown
i-self share export <id> --format html # self-contained, no external assets
# Strip secrets that look like API keys, tokens, passwords
i-self share export <id> --format markdown --redact
# Upload to S3 + presigned URL (reuses your i-self sync config)
i-self share upload <id> --format html --expires-in 86400
```
**Secret redaction** ([src/share/redact.rs](src/share/redact.rs)) catches
high-recall patterns: OpenAI / Anthropic / GitHub / GitLab / Slack / Stripe /
Google API keys, AWS access-key + secret pairs, generic JWTs, `Bearer ...`
headers, `password=...` assignments. Each match becomes
`[REDACTED:<kind>]`. **Not a substitute for human review** — your session
can still leak business logic, customer names, schema information.
### Importing — the cross-agent magic
```bash
# Continue a Claude Code session in Aider
i-self share export <claude-id> --format json --output s.json
i-self share import s.json --target aider --project /path/to/repo
# Or hand off in either direction
i-self share import s.json --target claude-code
i-self share import s.json --target goose
i-self share import s.json --target codex
i-self share import s.json --target continue
i-self share import s.json --target generic-openai
# For tools without addressable on-disk storage (Copilot, Cline, Cursor)
i-self share import s.json --target clipboard | Set-Clipboard # PowerShell
```
`<input>` accepts a file path, an `https://...` URL (e.g. a presigned
`share upload` link), or `-` for stdin. JSON only — Markdown is
intentionally rejected as ambiguous to round-trip.
Imported sessions get a leading provenance message:
> [i-self import] Continued from \<provider\> session \<id\>. \<N\> prior messages follow.
so the recipient agent (and you, scrolling back) can tell at a glance
where the transcript came from.
### Fidelity caveat
Each agent has its own tool catalog (Claude Code's `Bash`, Aider's
`/run`, Goose's plugins, etc.). When crossing agent boundaries, tool
calls flatten to text descriptions. The recipient gets a faithful,
readable transcript they can continue from — **not** a wire-compatible
session that re-executes the source agent's tool calls verbatim.
OpenCode import is intentionally absent: its session-write paths
reference internal Bun runtime IDs, and a synthetic file may not appear
in `opencode --list`. Use `--target clipboard` for OpenCode handoffs.
---
## Activity monitoring (`monitor`, `track`)
Polls keyboard / mouse / active-window state every 50ms (via
`device_query`) and snapshots the screen on a configurable cadence.
Counters drive automation triggers.
```bash
i-self monitor start # spawns the background poller
i-self monitor stop
i-self monitor suggestions # surface what the analyzer flagged
i-self track summary --days 7 # weekly activity summary
```
**macOS:** the first run prompts for Accessibility permission so the
system can report keyboard/mouse state for non-foreground apps. Without
it, counters stay at zero — and so do automation triggers that depend
on them.
**Limitations.** Screenshots are saved unredacted to
`~/.i-self/screenshots/`. If your screen routinely shows secrets, set
`screenshot_interval = 0` to disable, or wipe the directory regularly.
---
## Automation rules (`automate`)
Trigger-based rules persisted in `~/.i-self/automation.toml`. Each rule
has a trigger (event + threshold) and a list of actions.
```bash
i-self automate init # write default rules
i-self automate ls
i-self automate add --name "Meeting alert" --trigger meeting-silence \
--duration 60 --action notify --message "Quiet for a min"
i-self automate test --event idle --value 600 # actually fires matched actions
```
### Triggers
| `idle` | User idle ≥ N seconds |
| `meeting-silence` | No meeting activity for N seconds |
| `no-activity` | No keyboard/mouse for N seconds |
| `activity-spike` | Keystrokes-per-window above threshold |
| `incoming-call` | Detect incoming call (system events; platform-dependent) |
### Actions
| `notify` | ✅ | Desktop notification via `osascript` / `notify-send` / PowerShell |
| `telegram` | ✅ | POST to Telegram Bot API |
| `whatsapp` | ✅ | Cloud WhatsApp Business API |
| `log` | ✅ | Tracing log line |
| `run-command` | ✅ | Runs the command via `sh -c`; stdout/stderr captured to logs |
| `exit-meeting` | ⚠️ stub | Logs only |
| `start-recording` | ⚠️ stub | Logs only |
| `stop-recording` | ⚠️ stub | Logs only |
| `send-message` | ⚠️ stub | Logs only — distinct from `telegram`/`whatsapp` |
Required env for messaging actions:
- Telegram: `TELEGRAM_BOT_TOKEN`, `TELEGRAM_CHAT_ID`
- WhatsApp: `WHATSAPP_API_KEY`, `WHATSAPP_PHONE`, `WHATSAPP_TO_PHONE`
---
## Vulnerability scanner (`vuln`)
Backed by [OSV](https://osv.dev). Reads lockfiles, calls
`https://api.osv.dev/v1/query` per dependency, surfaces results.
```bash
i-self vuln scan --path ./my-project
i-self vuln check lodash 4.17.20 --ecosystem npm
```
**Supported lockfiles:** `Cargo.lock`, `package-lock.json` (lockfile
versions 1, 2, 3). Other names are detected but only those two are
parsed today. Pip / Go / Maven / NuGet are flagged for future work.
**Severity** is taken from `database_specific.severity` when present
(GHSA records always carry this), otherwise computed from a CVSS v3
vector via the implementation in
[src/vuln/mod.rs](src/vuln/mod.rs).
Bucketing follows the official ratings: Low (<4.0), Medium (4.0–6.9),
High (7.0–8.9), Critical (≥9.0).
**Exit code 2 on partial scans.** If any per-package OSV lookup fails
(timeout, 5xx), the scan is incomplete and "0 vulnerabilities" is not
safe to trust. Failures are listed on stderr; CI scripts can grep the
exit code to gate. The summary line shows ⚠️ INCOMPLETE rather than ✅
in this case.
---
## Cloud sync (`sync`)
S3-compatible backup of `~/.i-self/` — single backend covers every
provider speaking the S3 API.
```bash
export ISELF_SYNC_BUCKET=my-iself
export ISELF_SYNC_ENDPOINT=http://localhost:9000 # MinIO; AWS leaves this unset
export AWS_ACCESS_KEY_ID=minio
export AWS_SECRET_ACCESS_KEY=minio-secret
i-self sync push # upload ~/.i-self/ to bucket
i-self sync pull # download bucket back to ~/.i-self/
i-self sync status # show effective config
```
| AWS S3 | (leave unset) |
| MinIO | `http://localhost:9000` (or your URL) |
| Cloudflare R2 | `https://<account-id>.r2.cloudflarestorage.com` |
| DigitalOcean Spaces | `https://<region>.digitaloceanspaces.com` |
| Backblaze B2 | `https://s3.<region>.backblazeb2.com` |
Credentials follow the standard AWS chain (env → `~/.aws/credentials` →
IAM). Other env-var overrides: `ISELF_SYNC_REGION`, `ISELF_SYNC_PREFIX`,
`ISELF_SYNC_VHOST_STYLE=1` (force virtual-hosted-style; only AWS).
`[cloud]` in `config.toml` is the persistent equivalent — fields
`bucket`, `region`, `endpoint`, `prefix`, `access_key`, `secret_key`.
**Env wins** over config so a single shell can override without rewriting
the file.
---
## Web dashboard / REST API (`dashboard`, `api`)
`i-self dashboard` and `i-self api` start the same axum server. The
"dashboard" subcommand binds 8080 and serves the HTML UI; `api` is an
alias bound to 3000. Routes:
- `GET /healthz` — always public
- `GET /` — HTML dashboard
- `GET /api/profile`, `GET /api/stats`
- `POST /api/search`, `GET /api/search/stats`
- `POST /api/ai/{ask,explain,generate}` — bearer-auth + per-IP rate-limited
- `GET /api/teams`, `GET /api/teams/:name`, `POST /api/teams/:name/aggregate`
**Default bind is `127.0.0.1`.** Auth is loopback-trust by default — any
local process can call the API.
**Network exposure requires a token.** Set `ISELF_BIND` to a non-loopback
address and the server demands `ISELF_API_TOKEN`. If neither
`ISELF_API_TOKEN` env nor `~/.i-self/api_token` file is present, a 256-bit
hex token is auto-generated and written to `~/.i-self/api_token` (mode
0600 on Unix). The token is also printed once at startup.
```bash
export ISELF_BIND=0.0.0.0
export ISELF_API_TOKEN=$(openssl rand -hex 32) # or skip → auto-generated
i-self dashboard --port 8080
# Clients send: Authorization: Bearer $ISELF_API_TOKEN
```
**Browser bootstrap.** Open the dashboard with `?token=<token>` in the
URL once. The dashboard JS strips the token from the URL via
`history.replaceState`, stores it in localStorage, and adds it to every
API call as `Authorization: Bearer <token>`. On 401, the JS prompts the
user for a fresh token and reloads.
**Rate limiting.** `/api/ai/*` is per-IP rate-limited (default: 10 req/sec,
burst 12). Tunable via `ISELF_AI_RATE_PER_SECOND` / `ISELF_AI_RATE_BURST`.
This is in addition to the bearer token — a leaked token still can't
translate to unbounded LLM spend.
**Request logging.** Every request is logged via
`tower_http::trace::TraceLayer`. The `Authorization` header is *not* in
default span fields; the `?token=...` query string *is* (tradeoff for
browser bootstrap), and the dashboard JS strips it on first load.
---
## Snippets (`snippet`)
Local code snippet manager. Stores snippets in `~/.i-self/snippets.json`.
```bash
i-self snippet add "Tokio retry" "$(cat retry.rs)" rust --tags "async,tokio"
i-self snippet ls --language rust
i-self snippet ls --tag tokio --favorites
i-self snippet search "http"
i-self snippet show <id>
```
---
## Messaging (`message`)
Two distinct paths. **Outgoing** messages (i-self → your phone) go
through the `automation` action handlers. **Incoming** messages (your
phone → i-self) go through the `message listen` command.
### Outgoing — automation actions
The `telegram` / `whatsapp` automation actions read these env vars
each time they fire:
- Telegram: `TELEGRAM_BOT_TOKEN`, `TELEGRAM_CHAT_ID`
- WhatsApp: `WHATSAPP_API_KEY`, `WHATSAPP_PHONE` (your sender),
`WHATSAPP_TO_PHONE` (recipient)
```bash
i-self automate add --name "Idle alert" --trigger idle --duration 300 \
--action telegram --message "you've been idle"
i-self automate test --event idle --value 600 # actually sends
```
### Incoming — `i-self message listen`
A real Telegram bot listener: long-polls the bot inbox, dispatches
slash-commands, replies in the same chat. Run it in the foreground
(Ctrl+C to stop) or under launchctl/systemd if you want it persistent.
```bash
export TELEGRAM_BOT_TOKEN=<from BotFather>
export TELEGRAM_ALLOWED_CHAT_IDS=<your chat id> # comma-separated
i-self message listen
```
**Allowlist is mandatory.** The listener refuses to start without
`TELEGRAM_ALLOWED_CHAT_IDS` because `/screenshot` would otherwise leak
your desktop to anyone who finds the bot. To bootstrap: send any text
to your bot, then check `https://api.telegram.org/bot<TOKEN>/getUpdates`
to find your chat id.
#### Commands
| `/help` (or `/start`) | Show available commands |
| `/status` | Profile loaded? Embedder backend? Visible session count |
| `/sessions [N]` | List N most recent agent sessions across all providers (default 10, capped at 50) |
| `/share <id>` | Render a session as Markdown and reply with it |
| `/activity` | Current active window + timestamp |
| `/screenshot` | Capture and reply with a photo |
#### Persistence
Last-seen `update_id` is stored at `~/.i-self/messaging_state.json` so
restarts don't re-process old messages. Long-poll timeout is 25s; on
Telegram-side errors the listener backs off 5s and retries.
### What about WhatsApp listen?
Not implemented. WhatsApp Cloud API delivers incoming messages via
**webhooks**, not polling — that requires a public HTTPS endpoint.
Practical path: run `i-self dashboard` on a real domain and add a
WhatsApp webhook handler in a future change.
### `message send` and `message config`
`i-self message send` and `i-self message config` are scaffold commands
that predate the listener. Both work but are limited:
- `send` reads env vars and tries to dispatch; it depends on
`allowed_chat_ids` being set in the in-memory config, which the CLI
doesn't currently populate. **Treat as a stub** — use the automation
`telegram`/`whatsapp` actions for one-shot sends instead.
- `config` prints which env vars to set.
---
## Team aggregation (`team`)
Combine multiple developer profiles to surface team-wide skill coverage,
knowledge silos, and bus-factor risk.
```bash
i-self team add alice path/to/alices/profile.json
i-self team aggregate engineering
i-self team show engineering
```
**Limitations.** This is a JSON-aggregation feature — it doesn't fetch
profiles from a central server, so each developer has to share their
`profile.json` (or use `i-self sync` to mirror it through a shared
bucket).
---
## Built-in analyzers (`plugin`)
```bash
i-self plugin list
```
Three compiled-in analyzers:
- **security_patterns** — high-recall regex hits for `eval(`, `exec(`,
hardcoded passwords / secrets / api_keys.
- **performance_patterns** — Python-specific anti-patterns
(unnecessary `.keys()`, string concat in loops, `== None`).
- **documentation** — flags long functions with no comments / docstrings.
**There is no dynamic plugin loader.** The 1.0.0 README claimed one;
that code walked `.so` files and `println!`-ed their names without ever
`dlopen`-ing them. The 0.4.x release deleted the pretense and renamed
the surface area honestly. To add an analyzer, implement
`AnalyzerPlugin` in [src/plugins/mod.rs](src/plugins/mod.rs) and
recompile.
---
## Things that look like features but aren't
Honest list, current as of 0.4.1:
- **Automation `exit-meeting`, `start-recording`, `stop-recording`,
`send-message`** — defined in the rule schema, advertised in the UI,
but the action handlers only `info!` log. `notify`, `telegram`,
`whatsapp`, `log`, and `run-command` are real.
- **Cursor / Copilot Chat / Cline session enumeration.** These tools
store sessions in editor globalState / IndexedDB. Not file-addressable.
Use `share import --target clipboard` to get a paste-friendly Markdown
blob instead.
- **OpenCode session import.** Provider works (read), importer is
intentionally absent (write). See the [share matrix](#provider-matrix).
- **Pip / Go / Maven / NuGet vuln scanning.** Lockfiles are detected but
only Cargo.lock and package-lock.json are parsed. The OSV call path is
ecosystem-agnostic; what's missing is the lockfile parser.
- **Incremental embedding reindex.** `i-self index` always re-embeds
every file; for big monorepos this is slow.
- **Any Web 1.0 tool with no programmatic export** (ChatGPT.com session
pages, Claude.ai conversations, Gemini chat). Use the manual data-export
flow, then point `ISELF_GENERIC_DIR` at the unzipped JSON.
---
For the architecture overview and per-module breakdown, see the
"Architecture" section of the [README](README.md#architecture). For the
list of breaking changes between releases, [CHANGELOG.md](CHANGELOG.md).