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
8 changes: 8 additions & 0 deletions BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ _Closed as not planned or rejected. Kept for the record; nothing here is pending

## Done

### Shipped — 2026-10-04 (v10.3.0)

- [x] **Grok Build client: setup, doctor, guard payloads, and quickstart** ([#470](https://github.com/CryptoJones/omind/issues/470)) — **v10.3.0** — _feature_ —
`omind setup --agent grok` registers the MCP server, hooks, skill, and rules file.
The guard reads Grok's camelCase events (including when Grok runs the Claude-compatible
hook), counts `omi__…` as a consult, and denies with stdout JSON so the reason is visible.
`omind quickstart --agent grok` prints the same wiring.

### Shipped — 2026-10-02 (v10.2.28)

- [x] **guard: `decide()` raises OSError (ENAMETOOLONG) on long cd chains; add an overall fail-closed hook deadline** ([#460](https://github.com/CryptoJones/omind/issues/460)) — **v10.2.28** — _bug_ —
Expand Down
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [10.3.0] - 2026-10-04

### Added
- **Grok Build (`grok`) support
([#470](https://github.com/CryptoJones/omind/issues/470)).** `omind setup --agent grok` registers the `omi`
MCP server in `~/.grok/config.toml`, installs lifecycle hooks in
`~/.grok/hooks/omind.json` (`PreToolUse`, `UserPromptSubmit`, `PostToolUse`,
`Stop`, `SessionStart`), installs the packaged skill under
`~/.grok/skills/omind/`, and writes a managed bootstrap rule at
`~/.grok/rules/omind.md`. Grok does not inject `SessionStart` stdout, so the
rules file is what primes a session. `omind doctor --agent grok` checks the
wiring. `omind quickstart --agent grok` prints the same MCP table, hook
file, rules block, and skill path.
- **Grok hook payloads.** `omind guard adapter` accepts Grok's camelCase events
(`toolName`, `toolInput`, `sessionId`, `hookEventName`), including when Grok
is running the Claude-compatible hook. Built-in tool names map onto the names
the guard already classifies, and `omi__…` counts as a memory consult. A deny
is `{"decision":"deny","reason":…}` on stdout so the guard text is what Grok
shows. The embedding model load no longer prints a Hugging Face progress bar
onto that channel.

### Fixed
- **Grok setup refuses to replace a hooks or rules file it cannot read.**
Invalid JSON or an unreadable `~/.grok/hooks/omind.json` raises
`ProvisionError` instead of being treated as missing. A rules file that
exists but cannot be decoded is left in place; a missing rules file is
still created.

## [10.2.28] - 2026-10-02

### Fixed
Expand Down
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,21 @@ under Claude Code blocks under Laguna too. Re-runs replace only the entries
omind owns (named `omind-omi-*`), so hooks you wrote and pool's own
`stop_hook_max_continuations` are preserved.

**Grok Build** (the `grok` CLI) gets memory, the guard, the skill, and a home
rule from one `omind setup --agent grok`. The `omi` MCP server is registered
under `[mcp_servers.omi]` in `~/.grok/config.toml`. Hooks go in
`~/.grok/hooks/omind.json` (other files in that directory are left alone):
`PreToolUse` hard-blocks via `omind guard adapter --harness grok`,
`UserPromptSubmit` runs the per-turn preflight, and `PostToolUse` / `Stop` /
`SessionStart` do accounting, the loop guard, and session bookkeeping. Grok
does not inject `SessionStart` stdout, so priming is a managed
`~/.grok/rules/omind.md` plus the packaged skill in `~/.grok/skills/omind/`.
Grok also runs Claude Code's hooks and sends them a camelCase payload
(`toolName`, `toolInput`, `sessionId`); the guard accepts that shape and
answers a deny as `{"decision":"deny","reason":…}`. `$GROK_HOME` overrides
`~/.grok`. `omind quickstart --agent grok` prints the same wiring to apply
by hand.

**goose** (Block's on-machine agent) gets OMI **memory + priming** from one
`omind setup --agent goose` — it has no blocking pre-tool or session-start shell
hooks, so the guard is not wired here. The `omi` MCP server is registered as a
Expand Down Expand Up @@ -696,7 +711,7 @@ as Claude Desktop but `Code/User`; a `servers` block with `type: stdio`); and Am
Q's `~/.aws/amazonq/mcp.json` (`mcpServers`). Restart the tool afterward to load the
server.

`omind doctor --agent hermes|openclaw|opencode|codex|gemini|poolside|goose|claude-desktop|kiro|vscode|q`
`omind doctor --agent hermes|openclaw|opencode|codex|gemini|poolside|goose|grok|claude-desktop|kiro|vscode|q`
diagnoses that agent's wiring, and `omind quickstart --agent <name>` prints the manual
steps (YAML/JSON snippets personalized to your paths) if you'd rather merge them in
yourself.
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "omind"
version = "10.2.28"
version = "10.3.0"
description = "Reproduce the OMI/Obsidian memory integration for AI agents, plus a local web app to view, edit, and add memory entries."
readme = "README.md"
requires-python = ">=3.10"
Expand Down
2 changes: 1 addition & 1 deletion src/omind/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
# Copyright 2026 Aaron K. Clark
"""omind — OMI/Obsidian memory tooling for AI agents."""

__version__ = "10.2.28"
__version__ = "10.3.0"
16 changes: 13 additions & 3 deletions src/omind/adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ def run_adapter(
return harness_mod.render_decision(
blocked, spec.block_format, sys.stdout, sys.stderr, event=""
)
# Grok runs Claude's hooks and sends a camelCase payload. Deny with Grok's
# JSON decision so the reason is the guard text, not the first stderr line.
fmt = spec.block_format
if harness == "grok" or harness_mod.payload_is_grok(event):
fmt = harness_mod.FMT_GROK
# Poolside's event shape differs from Claude's in three places (tool naming,
# ``cmd``, ``tool_output``); translate ONCE here so the guard, verifier, and
# accounting all see the Claude-shaped event they were written against.
Expand All @@ -184,7 +189,7 @@ def run_adapter(
verdict = guard.check_action(action, omi_dir=omi_dir)
stage = "adapter render"
return harness_mod.render_decision(
verdict, spec.block_format, sys.stdout, sys.stderr, event=hook_event
verdict, fmt, sys.stdout, sys.stderr, event=hook_event
)
except Exception as exc:
# ``check_action`` logs its own decision (and never raises), so a verdict
Expand All @@ -193,10 +198,10 @@ def run_adapter(
fallback = _fail_open_verdict(event, action, exc, verdict, stage=stage)
try:
return harness_mod.render_decision(
fallback, spec.block_format, sys.stdout, sys.stderr, event=hook_event
fallback, fmt, sys.stdout, sys.stderr, event=hook_event
)
except Exception:
return _render_last_resort(spec.block_format, fallback.allow, hook_event)
return _render_last_resort(fmt, fallback.allow, hook_event)


#: Fixed literal outputs for when :func:`omind.harness.render_decision` raises
Expand Down Expand Up @@ -273,6 +278,11 @@ def run_adapter(
0,
),
"agy": (json.dumps({"decision": "deny", "reason": _LAST_RESORT_REASON}) + "\n", "", 0),
"grok": (
json.dumps({"decision": "deny", "reason": f"OMI guard: {_LAST_RESORT_REASON}"}) + "\n",
"",
0,
),
}
_LAST_RESORT_ALLOW: dict[str, str] = {
"json_signal": json.dumps({"allow": True, "reason": "", "rule_id": ""}) + "\n",
Expand Down
Loading
Loading