Skip to main content

Agent-builder toolkit

The REPL's agent-builder surface — 15 tools, 10 slash commands, and the safety floors that keep it honest. Introduced in @declaragent/[email protected]; fleet + monitoring tools rounded it out through 0.6.0.

Builder tools load only when the host process opts in with DECLARAGENT_BUILDER=on. Production agents shipped as dependencies never see them.

For a walk-through of the flow, see:

Tool catalog

Every tool is permission-gated under the builder's scope root. The scope is resolved at session startup in this order: nearest fleet.yaml → nearest agent.yamlcwd.

Authoring

ToolPurpose
DeclaraAddSkill({ name, description, body, inputs?, outputs?, agentPath?, addToAgentYaml?, confirmOutsideScope? })Author a skill. Writes <agentPath>/skills/<name>.md + surgically appends the ref to agent.yaml (preserves comments). Refuses bodies that look like leaked secrets.
DeclaraAddSecret({ ref, provider, usedBy?, tenantScope?, agentPath? })Reserve a secret slot. Never accepts a value — derives a DECLARA_* env-var name, appends a commented block to .env.example, and returns an actionable hint. Validates the provider is declared in secrets.yaml for non-env providers.
DeclaraAddSource({ type, id, config, agentPath? }) (0.4.0)Author an event source on the agent's event-sources.yaml. Round-trips through core's loader + the in-process webhook/cron/file-watch adapters; rolls the file back on validation failure. External-broker types (kafka/nats/sqs/amqp/mqtt) pass through structurally and surface as external: true so the REPL can hint the user to install the matching adapter.
DeclaraAddChannel({ type, id, config }) (0.4.0)Register a user-facing channel (slack / telegram / discord / whatsapp) in the user-global ~/.declaragent/channels.json. Scope note: channels are shared across agents, not per-scaffold — the tool's hint makes this explicit. Inline structural validation; adapter-level checks stay in declaragent channels validate.
DeclaraAddMCP({ name, command, args?, env?, protocolVersion? }) (0.4.0)Register a stdio MCP server in the user-global ~/.declaragent/mcp-servers.json. Mirrors declaragent mcp add; refuses duplicates by name. Tools contributed by the server appear as mcp__<name>__<tool> after the next REPL restart.
DeclaraAddPlugin({ pluginPath, confirmOutsideScope? }) (0.4.0)Install a local plugin into the user-global ~/.declaragent/plugins.json. Reads + validates <pluginPath>/plugin.json via core's loader, records the manifest's declared permissions as consentedPermissions — the user's /yes on the surrounding proposal IS the consent.
DeclaraFleetAdd({ template, id?, force?, fleetRoot? })Scaffold a new agent into the current fleet from a named template. Wraps the same addAgentFromTemplate the CLI verb uses.
DeclaraAddPeer({ agent, transports, fleetRoot? })Append or merge a peer entry in rpc-peers.yaml. Preserves comments via yaml.parseDocument; validates the final file against core's peersConfigSchema.
DeclaraAuthPlaybook({ provider })Return a concise markdown playbook for a supported provider. Pure lookup. Supported: anthropic, openai, github, slack, vault.

Plan / apply

ToolPurpose
DeclaraProposeChange({ summary, steps: [{ kind, description, preview?, payload }], requiresExplicitYes? })Register a plan with the session's proposal registry and await the user's decision. Blocks until /yes, /no, /edit, or the 15-minute TTL expires. Returns { proposalId, confirmed, finalSteps, reason }.
DeclaraApplyChange({ proposalId })Walk a confirmed proposal's steps. Captures git HEAD before mutating; on any step failure halts + attempts a scoped git checkout rollback. Emits one tool_call audit record per step + a summary.

Read-only inspection

All four are readonly: true, parallelSafe: true — they auto-approve in default permission mode and can run concurrently.

ToolPurpose
DeclaraEventsTail({ last?, kind?, correlationId?, sinceMs? })Read the last N entries from the session event store. Payloads are truncated to 140 chars with a suffix to keep responses bounded.
DeclaraFleetStatus({ history?, historyLimit?, fleetRoot? })Return the full FleetStatusReport for the current fleet — agents, envs, capabilities, peers, optional deploy history.
DeclaraAuditVerify({ tenant? })Verify the audit-chain's hash continuity. Returns { ok, totalEntries, verifiedEntries, violations } — inspect ok before reporting.
DeclaraDlqShow({ sourceId?, limit? })List rejected events in the session store — the client-side analog of the daemon-side broker DLQ. For broker-level DLQ entries, run declaragent dlq show <sourceId> <entryId> outside the REPL.

Slash commands

CommandBehavior
/plan <description>Ask the builder to draft a proposal without executing. No args: still aliases /mode plan for backwards compatibility.
/yes [<phrase>]Confirm the active proposal. Deploy / audit-erase / scope-breach proposals require the exact phrase shown in the prompt (e.g. /yes deploy).
/noReject the active proposal.
/edit <n> <replacement>Revise step <n>'s description. Payloads are not user-editable via slash — ask the model to re-propose if the structured args need reshaping.
/diff [<path>]Show git diff scoped to the builder's scope root (or the given path).
/scopePrint the current builder scope root.
/fleet graph [mermaid|dot|json]Render the fleet's peer graph inline. Default: mermaid.
/undoRevert the last DeclaraApplyChange via scoped git checkout. Requires git + the apply captured a HEAD. Single-step in v0.2; stacked undo lands in v0.3.
/history [<limit>]Render recent builder actions from the audit chain (default: 50 entries, newest first).

Safety model

Four floors operate automatically — each is scoped to a specific failure mode the plan's §5 calls out.

Secret-leak redaction (pre-turn)

Every user message flows through redactSecrets() before the model, the transcript, or the SQLite session store see it. Seven pattern detectors cover the common cases:

  • Anthropic / OpenAI-project / OpenAI-user API keys (sk-ant-…, sk-proj-…, sk-live-…)
  • GitHub PATs + OAuth tokens (ghp_…, gho_…)
  • npm tokens (npm_…)
  • Slack tokens (xoxb-…, xapp-…, etc.)
  • AWS access key ids (AKIA…)
  • JWTs (three dot-separated segments with ey prefix)

Matches are replaced with <redacted:label> in the redacted message. The original value is discarded — not stored anywhere in the session. A system line tells the user a redaction happened; the model is instructed never to echo the marker back.

Scope confinement

Every file-writing tool asserts its target path lives at or below the session's scope root via path.startsWith(scopeRoot + sep) (the classic /foo vs /foo-bar prefix bug is guarded explicitly).

Reaching outside scope requires confirmOutsideScope: true, which the proposal flow routes through a requiresExplicitYes confirmation. Silent cross-repo writes are not possible.

Deploy deny floor

Bash:declaragent deploy* and Bash:declaragent fleet deploy* are bottom-of-stack deny rules in the permission gate. A rogue model calling Bash with declaragent deploy trips the gate rather than the live site.

To actually deploy:

  1. Let the model propose via DeclaraProposeChange({ summary: "deploy …", requiresExplicitYes: true }).
  2. The user types /yes deploy (exact phrase derived from the summary).
  3. The user switches to /mode bypass + runs declaragent deploy … themselves, or exits the REPL.

The builder never drops the deny rule on its own.

Plan-then-execute

Every multi-file change flows through DeclaraProposeChange → explicit user /yesDeclaraApplyChange. The proposal is a plan, not an action — the file system is untouched until confirmation. Direct DeclaraAddSkill / DeclaraAddSecret calls stay available for trivially-scoped single-step work.

Audit chain

Every DeclaraApplyChange emits:

  • One tool_call record per step with tool: 'Declara:<stepKind>'.
  • One summary tool_call record with tool: 'DeclaraApplyChange'.

All records share a correlation id so /history threads them. The hash-chain stays verifiable through DeclaraAuditVerify({ tenant }) or declaragent audit verify outside the REPL.

REPL input UX (0.4.1)

Four conversational-flow helpers make long prompts + proposals feel natural:

  • Bracketed paste. Paste a multi-line prompt into the REPL and every line lands in the input atomically. The REPL enables CSI ?2004h on startup and owns a parallel stdin listener that watches for the paste markers; embedded \n mid-paste no longer fires submit. Works out of the box in every modern terminal (iTerm2, Terminal.app, Alacritty, Ghostty, VS Code's integrated terminal, tmux). Terminals without bracketed-paste support degrade gracefully.
  • /prompt <path>. Read a file and submit its contents verbatim as your next user message. Useful for prompts that outgrow a single paste buffer, or for reusing a saved brief.
  • @<path> file refs. Mention a file inline (please summarise @README.md) — the REPL inlines the file contents in a fenced block before sending to the model. Supports absolute, relative (resolved from cwd), and ~/ paths. Dedupes repeated tokens; truncates attachments at 256KB; leaves emails ([email protected]) alone.
  • Bare-letter proposal shortcuts. When a proposal is pending, a bare y / yes / n / no submission routes as /yes / /no. Explicit-yes proposals (deploys, audit erasures) still require the full phrase — the shortcut bows out safely. The typed /edit <n> <replacement> path stays unchanged for revising steps.

Environment variables

VariablePurpose
DECLARAGENT_BUILDERon enables the builder toolkit. Default: off.

The scope root is resolved from process.cwd() at session start; no env var overrides it. Use /scope to confirm.

Deferred to v0.3+

  • Multi-step /undo — stack the last N applies.
  • Snapshot fallback for non-git projects — phase 6 mandates git; a homegrown snapshot layer for non-git workflows is tracked.
  • /monitor pane — bottom-of-screen Ink live-tail for events. The four read-only tools cover the "check state" need; the pane adds ambient awareness, which needs daemon-side push first.
  • builder.* audit kinds — v0.2 reuses kind: 'tool_call' with a Declara:-prefixed tool field. A dedicated builder.apply / builder.addSkill union lands when the core schema next bumps.
  • Multi-model builders — the propose / plan step could run on a cheaper model; execution stays on the session model.