AppWeaver Agent Instructions

This repo uses a CLI-based tool system for AI agents.

How to call tools

Each plugin exposes tools via bash:

```bash bun src/cli.ts '' bun src/cli.ts # print full JSON schema for plugin bun src/cli.ts # list all plugins and tools ```

Draft system

All mutating operations (create, update, delete) return a draft for user review. The user accepts/revises/declines via bot DM commands shown in the tool output. Never retry a mutating tool if it returned a Draft ID.

Skills

Task-specific workflows live under .claude/skills/. Load a skill when its description matches the task; do not read unrelated skills or recursively load documentation inventories. Core and local working instructions live in AGENTS.md; feature context lives in the nearest README and relevant local docs.

Each skill is a folder: .claude/skills/<skill_name>/SKILL.md (OpenCode-compatible YAML frontmatter with name matching <skill_name>).

Managed skills and the disabled-skill check

The skill-status skill is always available and lists every managed AppWeaver skill with its current enabled or disabled state for this workspace.

When the user asks about a skill that is not in your available skills, do not assume it does not exist. Check the skill-status skill — if the skill is listed there as disabled, warn the user that it exists but is currently disabled, and ask them to enable it with <prefix>skills set <name> enable or the skills manager, which can be opened from the footer joggler icon. Only the user can enable or disable skills — the AI must not change skill state on its own.

User intent comes first

Before changing files, running implementation commands, linting, or applying the post-edit workflow, infer the user's intent from the whole prompt and the current conversation.

Keep implementation plans current

Local documentation and stable instructions

Web command UI

Rich command output uses WebNodeRoot and optional per-render stylesheets (Shadow DOM). See docs/WEB_RENDERER.md (section “Scoped styles”).

Widget design defaults

Web UI style conventions

No plugin code under src/ or web/

src/ (bot core, shared WebNode schema, Nostr, etc.) and web/ (the web app) must stay plugin-agnostic. Do not add imports from plugins/, command-plugin–specific types, WebElementTag / WebProps values, renderer branches, or comments that exist only to support one plugin. Plugin behavior belongs under plugins/ (renderers, adapters, definitions). The Web UI wire format is still JSON (WebNodeRoot / WebNode); the web app only implements generic tags and WebAction handling once. If a feature needs a new building block, add a reusable primitive in src/web/ui-schema.ts and the client renderer, not a one-off for a given plugin. See docs/WEB_RENDERER.md (e.g. “Scoped styles”) for the renderer model.


Tool permissions and workspace boundary

Active agent permissions

Long commands

Fallback safe commands

If the active runtime does not provide more specific command permissions, these commands are considered safe to run without asking.

Package scripts (trusted by project config)

Before running a package script, the agent should:

  1. Read package.json.
  2. Verify the script name exists.
  3. Run only that script command (no extra shell chaining like &&, ;, or pipes unless user explicitly approves).

Common read-only commands

Read-only plugin CLI calls

Destructive and sensitive commands

Agent workspace boundary


Commits

Release preparation

Before preparing a release commit:

  1. Inspect both unstaged and staged changes with git --no-pager diff and git --no-pager diff --staged.
  2. Summarize the scope, files, and behavior changed in plain language.
  3. Use one concise conventional commit subject that includes the appropriate version bump flag below.

Release hooks update the version, vX.Y.Z tag, and CHANGELOG.md. To regenerate the changelog from tags only, run bun run release:changelog.

Before making a commit, include a version bump flag in the commit message:

Example: git commit -m "chore: remove unused file --patch"

See CONTRIBUTING.md for setup (hooks, semver) and more.


TypeScript: function parameters (3+ arguments)

When writing a function that takes more than 2 parameters, use a single object parameter and define a named type for its properties.

Pattern

type MyFunctionProps = {
  foo: string;
  bar: number;
  baz: boolean | null;
};

function myFunction({ foo, bar, baz }: MyFunctionProps): ReturnType {
  // ...
}

Requirements

This keeps call sites explicit and forces every caller to acknowledge every argument.

Example (from codebase)

type ParseModelProps = {
  dmBotRoot: string;
  mode: AgentMode;
  modelOverride: string | null | undefined;
  providerName: ProviderName | null;
};

function parseModel({ dmBotRoot, mode, modelOverride, providerName }: ParseModelProps): string {
  // ...
}

Post-agent lint behavior (agent mode)

When the bot runs in agent mode after an implementation/change request:

Agent expectations

Testing preference


AppWeaver Codebase Context

When editing or extending AppWeaver, use this as the map. AppWeaver is an open-source app hub for running AI-powered tools from a project or workspace folder the user controls. Installable apps like todos, bookmarks, scheduled jobs, file tools, browser actions, publishing, and more can be used through chat, the web UI, or AI prompts.

File map

File Purpose
index.ts Main entry: env, SQLite, Nostr pool, subscription (kind 1059), command handling, agent backend dispatch, DM sending. Version computed at startup with git rev-parse HEAD from the project root.
scripts/run-with-restart.ts bun run watch: runs the Bun bot + optional Vite (WATCH_WEB_UI=0 to disable). Restarts the bot only when restart.requested is created/touched — not on every code save. Use that file so agents and humans pick a deliberate restart point.
package.json Scripts: start (bot), watch (bot + watcher above), web:dev / web:build, lint. Deps: nostr-tools, @types/bun.
.env.example Template for BOT_KEY, BOT_MASTER_PUBKEY, BOT_RELAYS, BOT_OPENCODE_SERVE_URL, DEBUG.
opencode.json OpenCode project config: defines ask, plan, build agents with per-agent models and permissions.

State and persistence

Agent backends

OpenCode is the only active agent backend. createBackend() uses the OpenCode SDK and the managed runtime controller. Legacy backend names in saved state normalize to OpenCode; there is no backend-switching command. Workspace model selection uses the ai-model-source capability: core exposes the normal OpenCode catalog, and installed apps may register their own source. Use <prefix>ai source to list sources and <prefix>ai source core to select the core catalog. PPQ is an optional model-source app; Routstr has no model-source plugin yet.

The canonical normal OpenCode config lives at <workspace>/.appweaver/opencode.json; AppWeaver materializes the active runtime config at <workspace>/opencode.json. See docs/MODEL_SOURCES_AND_RUNTIME.md for the managed config and model-source design, and plugins/ppq/docs/PPQ_PLUGIN_DESIGN_AND_IMPLEMENTATION_PLAN.md for PPQ-specific implementation history.

ANSI colors (local terminal only)

Colors are applied for local terminal output and stripped (stripAnsi()) before sending over Nostr.

Element Color
<ask> prefix cyan
<plan> prefix yellow
<agent> prefix green
Backend name magenta
[bot] / [sent] dim / blue
Errors red
Token/cost footer gray

Where to change what

After editing AppWeaver code

Codebase vs agent workspace

Environment (runtime)