82 lines
4.2 KiB
Markdown
82 lines
4.2 KiB
Markdown
# Didactyl — Agent Instructions
|
|
|
|
## Build
|
|
|
|
**Always use `./build_static.sh` to build the binary.** Never run `make` directly — the default `all` target prints a warning and exits with an error. `make` is kept only for auxiliary targets (`make clean`, `make deps`, `make test_pool`).
|
|
|
|
```bash
|
|
./build_static.sh # Release build (static MUSL via Docker)
|
|
./build_static.sh --debug # Debug build
|
|
./build_static.sh --platform=linux/arm64 # Cross-compile
|
|
```
|
|
|
|
## Project Overview
|
|
|
|
Didactyl is a Nostr agent written in C (C99). It runs as a long-lived daemon that listens for DMs from its admin, processes them via an LLM, and can execute tools (post notes, query relays, manage skills, etc.).
|
|
|
|
## Key Files
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `src/main.c` | Entry point, CLI arg parsing, signer construction |
|
|
| `src/setup_wizard.c` | Interactive setup wizard (8 steps) |
|
|
| `src/config.c` / `src/config.h` | Config loading from genesis.jsonc |
|
|
| `src/nostr_handler.c` / `src/nostr_handler.h` | Nostr relay pool, event publish/subscribe |
|
|
| `src/llm.c` / `src/llm.h` | LLM HTTP client (OpenAI-compatible API) |
|
|
| `src/agent.c` | Agent loop: DM → LLM → tool dispatch → response |
|
|
| `src/tools/` | Tool implementations (one file per tool) |
|
|
| `src/tools/tools_schema.c` | Tool schemas (JSON Schema for each tool) |
|
|
| `src/tools/tools_dispatch.c` | Tool dispatch routing |
|
|
| `src/default_events.h` | Default relays, skill templates |
|
|
| `src/signer_health.c` / `src/signer_health.h` | Signer health monitoring |
|
|
| `nostr_core_lib/` | Vendored nostr_core_lib (v0.6.15) |
|
|
| `genesis.jsonc.example` | Example genesis config |
|
|
| `build_static.sh` | Static build script (Docker + Alpine + musl) |
|
|
| `Dockerfile.alpine-musl` | Dockerfile for static builds |
|
|
| `increment_and_push.sh` | Version bump + git tag + push |
|
|
|
|
## Coding Conventions
|
|
|
|
- **Language**: C99 (`-std=c99`)
|
|
- **Style**: 4-space indentation, K&R braces
|
|
- **Naming**: `snake_case` for functions and variables, `UPPER_CASE` for macros
|
|
- **Headers**: Include guard via `#ifndef` / `#define` / `#endif`
|
|
- **Error handling**: Return negative error codes, use `NOSTR_SUCCESS` (0) from `nostr_core_lib/nostr_core/nostr_common.h`
|
|
- **Logging**: Use `DEBUG_INFO()`, `DEBUG_WARN()`, `DEBUG_ERROR()` macros from `src/debug.h`
|
|
- **Memory**: Manual malloc/free; no GC
|
|
- **JSON**: Use cJSON library (`nostr_core_lib/cjson/`)
|
|
|
|
## Key Architecture Decisions
|
|
|
|
- **Signer abstraction**: All crypto operations go through `nostr_signer_t` (local or remote via n_signer). The signer is constructed in `main.c` and passed through the tool context.
|
|
- **Role paths**: Uses BIP-44 derivation paths (`role_path`) instead of the old `nostr_index` selectors. See `docs/SIGNER.md`.
|
|
- **Wizard**: The setup wizard (`src/setup_wizard.c`) is a plain-text interactive TUI (no ncurses). It uses `fprintf(stderr, ...)` for output and `fgets()` for input.
|
|
- **Config persistence**: Config is published as NIP-44 encrypted kind 30078 events on Nostr, keyed by `d_tag` (e.g. `"user-settings"`).
|
|
- **Skills**: Agent skills are kind 31124 events with `d_tag` identifiers. They contain markdown with template variables like `{{my_kind0_profile}}`, `{{my_npub}}`, `{{nostr_dm_history(...)}}`.
|
|
|
|
## Testing
|
|
|
|
```bash
|
|
cd tests && python3 run_tests.py # Run test suite
|
|
```
|
|
|
|
Tests are in `tests/suites/` using a Python harness. Each test spawns a didactyl instance with a test genesis config and checks behavior via the HTTP admin API.
|
|
|
|
## Versioning
|
|
|
|
Versions follow `vMAJOR.MINOR.PATCH` format. Use `./increment_and_push.sh "description"` to bump the patch version, commit, tag, and push.
|
|
|
|
## Nostr Core Library
|
|
|
|
The vendored `nostr_core_lib/` is a static library (`libnostr_core_x64.a`). To rebuild it:
|
|
|
|
```bash
|
|
cd nostr_core_lib && ./build.sh --nips=001,004,005,006,011,013,017,019,021,042,044,046,059,060,061
|
|
```
|
|
|
|
## Important Notes
|
|
|
|
- The wizard runs before `main()` sets up the CA bundle, so `fetch_models_for_llm_config()` in the wizard explicitly calls `nostr_http_set_ca_bundle()`.
|
|
- ppq.ai does not expose a balance/credits endpoint — the wizard skips balance checks for `"ppq"` provider.
|
|
- The `relay_remove_index()` function in `setup_wizard.c` is unused (relays use toggle-based enable/disable, not removal).
|