Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
2010d35
agent: harden the core loop across all providers (phase 1)
abhijitkrm Oct 6, 2026
1c3f56d
agent: general SRE tools and a permission engine (phase 2)
abhijitkrm Oct 6, 2026
89e8c19
agent: load non-core tools on demand via tool_search
abhijitkrm Oct 6, 2026
5672150
cli: general mode, node mode, headless -p and piped input (phase 3)
abhijitkrm Oct 6, 2026
f4b3f3e
agent: tell the model questions are about the operator's own system
abhijitkrm Oct 6, 2026
2cd8fa1
extensibility: settings, memory, commands, hooks, MCP client, subagen…
abhijitkrm Oct 6, 2026
aa89937
agent: /help works headlessly (cometcli -p /help) and lists custom co…
abhijitkrm Oct 6, 2026
b342d1b
tui: inline Claude Code-style chat for cometcli and cometcli one (pha…
abhijitkrm Oct 6, 2026
62e783e
tx: fix every transaction being signed with sequence 0 first
abhijitkrm Oct 6, 2026
042e052
tools: strict coin amounts, validated votes and edits, nil-safe val.s…
abhijitkrm Oct 6, 2026
9b88e1f
tools: robust peer edits and verified upgrade staging
abhijitkrm Oct 6, 2026
7d41ec7
tools/audit: robust statesync config patching; audit log tests
abhijitkrm Oct 6, 2026
3229dd2
cli: in-process end-to-end tests; fix openai-compat identity and head…
abhijitkrm Oct 6, 2026
e4100ee
safety: gate fleet.shell like bash; key management is operator-only
abhijitkrm Oct 6, 2026
a7ffbb9
test: golden snapshots for the inline UI; race detector + coverage in CI
abhijitkrm Oct 6, 2026
c3ebaad
evm client: malformed quantities and HTTP errors are errors
abhijitkrm Oct 6, 2026
9b9ef26
tx: sign with cometcli's keyring or inside the node container; choose…
abhijitkrm Oct 7, 2026
5da566b
incident tools: val.jail-check, val.consensus, wait.until, unjail pre…
abhijitkrm Oct 7, 2026
66370bd
/recover-jail: run a jail incident end to end; scenario test on a sim…
abhijitkrm Oct 7, 2026
0351efb
tx: container signing validated against the real evmd image
abhijitkrm Oct 7, 2026
ea542e9
Add node.triage, knowledge base of failure cases, /incident
abhijitkrm Oct 7, 2026
1bf6b16
Fixes from the real-chain jail drill
abhijitkrm Oct 7, 2026
b1cfc53
gofmt
abhijitkrm Oct 7, 2026
ada6138
OpenRouter provider (free router), saved credentials, reasoning stream
abhijitkrm Oct 7, 2026
48ab85b
Keep labelled hashes out of redaction; triage prints config paths
abhijitkrm Oct 7, 2026
c554150
OOM inside containers, regression lines, call-don't-ask approvals
abhijitkrm Oct 7, 2026
4a0afca
Live-drill fixes: partitions, stale logs, quota 429s
abhijitkrm Oct 7, 2026
6da0be3
triage: unparsable probe output is never a zero signal
abhijitkrm Oct 7, 2026
99429c8
Unjail blocked while the signer's state is ahead; CLI honors tool tim…
abhijitkrm Oct 7, 2026
232842e
Unjail blocked when the node signs with a different consensus key
abhijitkrm Oct 7, 2026
5259e04
gofmt
abhijitkrm Oct 7, 2026
57c5407
On-chain tools get a 10-minute deadline; docker host load signal
abhijitkrm Oct 7, 2026
1764e41
Chain queries fall back when the node is up but can't answer yet
abhijitkrm Oct 7, 2026
9014e8b
Triage measures the node's data volume inside its container
abhijitkrm Oct 7, 2026
1a878a6
Never offer a local key that isn't the validator's operator account
abhijitkrm Oct 7, 2026
d2467a8
Upgrade-halt and jail-evidence fixes from the chain-wide halt drill
abhijitkrm Oct 7, 2026
a599c21
Non-validator triage; case for nodes replaying a passed upgrade
abhijitkrm Oct 7, 2026
86cb8ec
Governance: gov.propose and gov.deposit; friendlier votes
abhijitkrm Oct 7, 2026
1cac173
TUI: match Claude Code's look and behavior
abhijitkrm Oct 7, 2026
a5bbeeb
gofmt
abhijitkrm Oct 7, 2026
53c3dd5
Startup art: a comet streaking over COMETCLI
abhijitkrm Oct 7, 2026
c902713
anthropic: send effort only to models that take it
abhijitkrm Oct 7, 2026
f894a9c
The agent switches to a node itself (use_node); jailed filter
abhijitkrm Oct 7, 2026
9cff163
Messages typed mid-turn reach the agent at its next step; esc sends now
abhijitkrm Oct 7, 2026
101b296
Long tasks pause instead of failing; polling tip; tx hashes visible
abhijitkrm Oct 7, 2026
e02b4dd
README: screenshot of the cometcli startup screen
abhijitkrm Oct 7, 2026
cc7b502
lint: drop unused orDefault; read ProposalType with the legacy flag
abhijitkrm Oct 7, 2026
9375664
README and command docs for the reworked cometcli
abhijitkrm Oct 7, 2026
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 .cometcli/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"permissions": {
"allow": [
"bash(./cometcli query:*)",
"bash(date +%H:%M:%S:*)"
]
}
}
12 changes: 9 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,18 @@ jobs:
cache: true
- name: vet
run: go vet ./...
- name: test
run: go test ./... -count=1
- name: test (race detector, coverage)
run: |
go test -race -count=1 -coverprofile=cover.out ./...
go tool cover -func=cover.out | tail -1
- name: build
run: go build -o cometcli ./cmd/cometcli
- name: smoke
run: ./cometcli --help && ./cometcli doctor --help
run: |
./cometcli --help && ./cometcli doctor --help && ./cometcli one --help
./cometcli config show
# no provider configured: headless mode must fail fast, not hang
! ./cometcli -p "hi" </dev/null

lint:
runs-on: ubuntu-latest
Expand Down
375 changes: 239 additions & 136 deletions README.md

Large diffs are not rendered by default.

263 changes: 258 additions & 5 deletions docs/COMMANDS.md

Large diffs are not rendered by default.

227 changes: 227 additions & 0 deletions docs/EXTENDING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
# Extending cometcli

Settings, memory, custom commands, hooks, MCP servers and subagents all live in plain
files. The formats follow Claude Code's, so configs written for it mostly carry over.

A **project** is the nearest directory above where you start cometcli that holds a
`.cometcli/` folder, a `COMET.md` or a `.git`.

## Settings — `settings.json`

| File | Scope |
|---|---|
| `~/.cometcli/settings.json` | you, everywhere |
| `<project>/.cometcli/settings.json` | the project — commit it |
| `<project>/.cometcli/settings.local.json` | you, in this project — gitignore it |

Later files override single values (`model`, `effort`, `permissions.defaultMode`);
lists (`permissions.allow/ask/deny`), `env`, `hooks` and `mcpServers` accumulate.
`config.yaml`'s `agent:` section still picks the provider; settings refine it.

```json
{
"model": "openai/gpt-oss-120b",
"effort": "medium",
"permissions": {
"allow": ["bash(docker logs:*)", "bash(./info.sh)", "web_fetch(domain:github.com)"],
"ask": ["bash(docker compose:*)"],
"deny": ["bash(rm:*)", "edit(/etc/**)", "val.unjail"],
"defaultMode": "acceptEdits"
},
"env": { "GROQ_API_KEY": "…" },
"hooks": { … },
"mcpServers": { … }
}
```

`env` is exported before the provider starts, without overriding variables already set.
`defaultMode` takes `default`/`ops`, `acceptEdits`, `plan`/`readonly`, `bypassPermissions`.

## Trust

Project hooks and project MCP servers run commands on your machine — on a validator
host, that matters. They are **ignored until you trust the project**:

```bash
cometcli trust # trust the project you're in (covers subfolders)
cometcli trust --off # revoke
cometcli trust --list
```

Untrusted items are listed when a session starts. Your user-level settings are always
trusted. A project command's `allowed-tools` also needs trust.

## Memory — `COMET.md`

Standing instructions loaded into every session's system prompt:

1. `~/.cometcli/COMET.md` — yours, everywhere
2. `COMET.md` in each directory from the project root down to where you started
(`AGENTS.md` is read where a directory has no `COMET.md`), plus `COMET.local.md`
3. In node mode, `~/.cometcli/memory/<profile>.md` — notes about that node

A line `@path/to/file.md` imports another file (key material is never imported).

```markdown
# primium node-setup
- Validators run in docker; containers are primium-validator0..3.
- Never restart validator0 without asking — it's the genesis proposer.
- Upgrades follow upgrade/README.md.
@docs/ports.md
```

In a session: `/memory` lists what's loaded; `/remember <text>` appends to the
project's `COMET.md` (`--user`, `--node` for the others) and tells the model right away.

## Custom slash commands

Markdown files in `<project>/.cometcli/commands/` or `~/.cometcli/commands/` become
commands. `ops/vote.md` is `/ops:vote`. A project command shadows a user one.

```markdown
---
description: Vote on an upgrade proposal
argument-hint: <proposal-id> <yes|no>
allowed-tools: bash(./upgrade/vote-upgrade.sh:*), read
---
Vote $2 on proposal $1.

Current proposals: !`docker exec primium-validator0 evmd q gov proposals -o json | jq -r '.proposals[] | "\(.id) \(.status)"'`
Network config: @run-genesis/network-config.env

Read upgrade/vote-upgrade.sh first, confirm the proposal is in its voting period,
then run it. Report the tx hash.
```

- `$ARGUMENTS` is everything after the command; `$1`…`$9` are the words (quotes group).
- `` !`cmd` `` runs while expanding and inlines the output — **read-only commands only**
(the same classifier as the bash tool); anything else is left for the agent to run
with approval.
- `@path` inlines a file (never key material).
- `allowed-tools` adds allow rules for that one turn. Transactions still always ask.

Commands work headlessly too: `cometcli -p "/ops:vote 7 yes"`.

## Hooks

Commands run at points in the loop. Events: `PreToolUse`, `PostToolUse`,
`UserPromptSubmit`, `Stop`, `SessionStart`.

```json
{
"hooks": {
"PreToolUse": [
{ "matcher": "bash", "hooks": [{ "type": "command", "command": ".cometcli/hooks/guard.sh", "timeout": 10 }] }
],
"PostToolUse": [
{ "matcher": "edit|write", "hooks": [{ "type": "command", "command": "evmd validate-genesis 2>&1 | tail -3" }] }
],
"UserPromptSubmit": [
{ "hooks": [{ "type": "command", "command": "echo \"on-call: $(cat ~/.oncall)\"" }] }
]
}
}
```

`matcher` is a case-insensitive regex over tool names (`bash`, `edit`, `node.status`,
`mcp__grafana__.*`; Claude Code's `Bash` works); empty or `*` matches all. Hooks run
from the project root with `COMETCLI_PROJECT_DIR` (and `CLAUDE_PROJECT_DIR`) set, and
get the event as JSON on stdin:

```json
{"session_id":"…","transcript_path":"…","cwd":"…","hook_event_name":"PreToolUse",
"permission_mode":"ops","tool_name":"bash","tool_input":{"command":"…"}}
```

PostToolUse adds `tool_response`, UserPromptSubmit `prompt`, Stop `stop_hook_active`,
SessionStart `source` (`startup`|`resume`).

| Exit | Effect |
|---|---|
| 0 | pass. UserPromptSubmit/SessionStart stdout is added as context for the model |
| 2 | **block** — PreToolUse: the call doesn't run and stderr goes to the model; PostToolUse: stderr goes to the model; UserPromptSubmit: the prompt is rejected; Stop: the agent keeps working on stderr (at most 3 times) |
| other | non-blocking error, shown to you |

Exit 0 with JSON on stdout can decide too: `{"hookSpecificOutput":{"permissionDecision":
"allow|deny|ask","permissionDecisionReason":"…","additionalContext":"…"}}`,
`{"decision":"block","reason":"…"}`, `{"systemMessage":"…"}`, `{"continue":false}`.
A hook `allow` skips the prompt for a host change — **never for a transaction**, and
it can't unlock key material or state resets.

## MCP servers

```bash
cometcli mcp add grafana -- npx -y @grafana/mcp-grafana
cometcli mcp add k8s -e KUBECONFIG=~/.kube/config -- kubectl-mcp-server
cometcli mcp add pd --transport http https://mcp.example.com/mcp -H "Authorization: Bearer ${PD_TOKEN}"
cometcli mcp add tools --scope project -- ./scripts/mcp.sh # writes .mcp.json, shared
cometcli mcp list --check # connect and count tools
cometcli mcp remove grafana
```

Scopes: `user` (default, `~/.cometcli/settings.json`), `project` (`.mcp.json` — the
same file Claude Code reads), `local` (`.cometcli/settings.local.json`). `${VAR}` and
`${VAR:-default}` expand in commands, args, env, URLs and headers.

Servers start with each session; their tools are named `mcp__<server>__<tool>` and,
like node tools, load on demand — the system prompt lists them, the model calls
`tool_search` to load the ones it needs. Tools marked `readOnlyHint` run without a
prompt; others ask. Rules: `mcp__grafana` covers a whole server. `/mcp` shows what's
connected. (`cometcli mcp` with no subcommand still *serves* cometcli's own tools.)

## Subagents

The agent can hand a self-contained investigation to a subagent with the `task` tool:
it gets a fresh context, the same tools and permissions, works through the task and
returns only its report — so long log trawls don't fill the main conversation (or a
small per-minute token budget). Subagents can't spawn subagents.

Define specialized ones in `<project>/.cometcli/agents/<name>.md` or
`~/.cometcli/agents/`:

```markdown
---
name: log-digger
description: Trawls container and journal logs for errors around a time window
tools: bash, grep, read
---
You read logs. Start with docker logs --since for each validator container,
then journalctl. Report each distinct error with first/last timestamp and count.
```

`tools` restricts what it can use. `/agents` lists them.

## Knowledge-base cases

`node.triage` matches its signals against cases. Built-in cases ship in the binary
(`internal/kb/cases/`); yours go in `~/.cometcli/kb/*.yaml` (all nodes) or
`<project>/.cometcli/kb/*.yaml`. Same `id` overrides a built-in. A file holds one case or a
list of them:

```yaml
- id: sentry-lost # lowercase, digits, dashes
title: Validator lost both sentries
chains: [cosmos-sdk] # or cosmos-evm; omit for all (cosmos-sdk includes evm chains)
severity: high # critical | high | medium | low
symptoms: peers drop to 0 after sentry maintenance
match:
all: ["node.peers == 0", "config.pex == false"] # every one must hold
any: ["logs.peer_net > 0"] # at least one, when given
confirm: ["node.peers; can the validator reach sentry-1:26656?"]
causes: ["sentries restarted with new node ids"]
fix:
- "[read] get the sentries' node ids"
- "[change] net.add-peer the new ids; restart"
verify: ["node.peers >= 2"]
warnings: ["never open the validator's p2p port publicly as a shortcut"]
test:
signals: {node.peers: 0, config.pex: false, logs.peer_net: 3} # must match
```

Conditions are `<signal> <op> <value>` with `== != > >= < <= contains exists missing`;
values are numbers, `true`/`false`, or strings. A signal triage couldn't collect never
satisfies a condition (except `missing`). `cometcli node triage` prints every signal name
and value — write conditions against those. Fix steps must start with `[read]`, `[change]`
or `[tx]`; the agent still goes through the normal approvals for each. Ranking: more matched
conditions first, then severity. The built-in suite checks every case's `test` fixture
ranks it in the top 3 and that a healthy node matches nothing.
Binary file added docs/images/cometcli-startup.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading