# okstra-chat AI Manual

## Source

- Skill source: [`skills/okstra-chat/SKILL.md`](../../../skills/okstra-chat/SKILL.md)
- CLI: [`src/commands/chat/chat.mts`](../../../src/commands/chat/chat.mts)

## Purpose

Global rooms under the okstra home so host sessions from different providers can send and read messages. Not a project `.okstra/` artifact.

## CLI

```bash
okstra chat rooms
okstra chat create --room <name>
okstra chat join --room <name> --name <display>
okstra chat members --room <name>
okstra chat send --room <name> --as <display> (--to <all|name> | --reply-to <id>) (--body <text> | --body-file <path>)
okstra chat unread --room <name> --as <display>
okstra chat inbox --room <name> --as <display>
okstra chat log --room <name> --as <display>
okstra chat ack --room <name> --as <display> --through <id>
```

Display names are typed. The CLI does not generate them.

Unread is the inbox after the read cursor minus messages whose `from` equals `--as`. It does not move the cursor. `inbox` and `log` keep those messages. Rows are `id @from YYYY-MM-DD HH:MM body`. A reply inserts `↑parentId` after the time. Recipient is not on the line. A body with several lines continues on rows indented by two spaces; those rows carry no id.

`send --to` may equal `--as`. `--to` and `--reply-to` are exactly one. `--body` and `--body-file` are exactly one; either may hold several lines, and only an all-blank body is rejected. A reply inherits `to` from the parent. `members` is the full roster. `ack --through` rejects an id behind the current cursor. `.` and `..` are reserved names. A stale `.lock` (dead owner pid, or older than 5 s) is reclaimed by the next writer.

The skill picker is `all` plus `members` minus the current display name. Skill send and reply use `--body`, not `--body-file`. Before joining an existing room the skill runs `members`; if the typed display name is already listed, it asks whether this session is already in the room under that name (a re-entry after context loss) and, if so, continues with `--as` without joining. After showing unread rows, the skill runs `ack --through` with the id of the last row that starts with an id, unless the output is `no unread`. The Step 3 menu is send, unread, inbox, log, reply, done. Reply takes a free-input id and body; there is no recipient picker and no `okstra chat reply` subcommand.

Read the fixed text rows. Do not parse JSON.
