feat(agents): Move templates into dedicated folder

Provisioning now reads template files from templates/ and includes the\nbase URL for agent runtime setup. Remove unused root orchestration\ndocs to keep the repo tidy.\n\nCo-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Abhimanyu Saharan
2026-02-04 03:46:46 +05:30
parent 1abc8f68f3
commit 7fd079e4f1
14 changed files with 362 additions and 312 deletions

View File

@@ -1,107 +0,0 @@
# Mission Control Orchestrator Instructions
You are the Mission Control orchestrator. Your job is to:
1. Claim unassigned tasks (FIFO)
2. Execute work (optionally spawn sub-agents)
3. Log progress and deliverables
4. Move tasks to review when complete
## CRITICAL: You MUST call Mission Control APIs
Every action you take MUST be reflected in Mission Control via API calls. The dashboard shows task status in real-time.
## Required Inputs
- `BASE_URL` (e.g., http://localhost:8000)
- `ORG_ID`
- `WORKSPACE_ID`
- `AGENT_TOKEN` (Authorization Bearer token)
## On Every Heartbeat
### Step 1: Claim next task (FIFO)
```bash
curl -s -X POST "$BASE_URL/api/v1/orgs/$ORG_ID/workspaces/$WORKSPACE_ID/tasks/claim-next" \
-H "Authorization: Bearer $AGENT_TOKEN"
```
- If response is **204**, there is no work. Wait and retry.
- If response returns a task, process it.
### Step 2: Check your in-progress tasks
```bash
curl -s "$BASE_URL/api/v1/orgs/$ORG_ID/workspaces/$WORKSPACE_ID/tasks?status_filter=in_progress&assigned_agent_id=$AGENT_ID" \
-H "Authorization: Bearer $AGENT_TOKEN"
```
If tasks exist, continue work and update activity/deliverables.
## When Processing a New Task
### 1) Log that you're starting
```bash
curl -X POST "$BASE_URL/api/v1/orgs/$ORG_ID/workspaces/$WORKSPACE_ID/tasks/{TASK_ID}/activities" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"activity_type": "updated", "message": "Starting work on task"}'
```
### 2) Register a sub-agent (if you spawn one)
```bash
curl -X POST "$BASE_URL/api/v1/orgs/$ORG_ID/workspaces/$WORKSPACE_ID/tasks/{TASK_ID}/subagents" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"openclaw_session_id": "optional-session-id",
"agent_name": "Designer"
}'
```
### 3) Register deliverables
```bash
curl -X POST "$BASE_URL/api/v1/orgs/$ORG_ID/workspaces/$WORKSPACE_ID/tasks/{TASK_ID}/deliverables" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Homepage Design",
"markdown_content": "## Summary\n- Implemented layout\n- Added responsive styles"
}'
```
### 4) Log completion
```bash
curl -X POST "$BASE_URL/api/v1/orgs/$ORG_ID/workspaces/$WORKSPACE_ID/tasks/{TASK_ID}/activities" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"activity_type": "completed", "message": "Task completed successfully"}'
```
### 5) Move task to REVIEW
```bash
curl -X POST "$BASE_URL/api/v1/orgs/$ORG_ID/workspaces/$WORKSPACE_ID/tasks/{TASK_ID}/transition" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"to_status": "review"}'
```
## Task Statuses
```
inbox → in_progress → review → done
```
Other statuses may be used if configured (`assigned`, `testing`), but the default flow above is expected.
## Checklist Before Saying HEARTBEAT_OK
Before responding with HEARTBEAT_OK, verify:
- [ ] No unclaimed tasks remain in INBOX
- [ ] All in-progress tasks have recent activity updates
- [ ] Completed work has deliverables registered
- [ ] Completed tasks are moved to REVIEW
If ANY of these are false, take action instead of saying HEARTBEAT_OK.
## Reference
Full API documentation: See ORCHESTRATION.md in this project.

View File

@@ -1,176 +0,0 @@
# Mission Control Orchestration Guide
This document explains how to orchestrate tasks in Mission Control, including how to:
- Register sub-agents
- Log activities
- Track deliverables
- Update task status
## API Base URL
```
http://localhost:8000
```
Or use the `BASE_URL` environment variable.
## Task Lifecycle
```
INBOX → IN_PROGRESS → REVIEW → DONE
```
**Status Descriptions:**
- **INBOX**: New tasks awaiting processing
- **IN_PROGRESS**: Agent actively working on the task
- **REVIEW**: Agent finished, awaiting human approval
- **DONE**: Task completed and approved
Optional statuses may be enabled (`ASSIGNED`, `TESTING`) but are not required by default.
## When You Receive a Task
When a task is claimed, the response includes:
- Task ID
- Title, description, priority
- Project ID
## Required API Calls
### 1. Register Sub-Agent (when spawning a worker)
```bash
curl -X POST "$BASE_URL/api/v1/orgs/$ORG_ID/workspaces/$WORKSPACE_ID/tasks/{TASK_ID}/subagents" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"openclaw_session_id": "unique-session-id",
"agent_name": "Designer"
}'
```
### 2. Log Activity (for each significant action)
```bash
curl -X POST "$BASE_URL/api/v1/orgs/$ORG_ID/workspaces/$WORKSPACE_ID/tasks/{TASK_ID}/activities" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"activity_type": "updated",
"message": "Started working on design mockups"
}'
```
Activity types:
- `spawned` - When sub-agent starts
- `updated` - Progress update
- `completed` - Work finished
- `file_created` - Created a deliverable
- `status_changed` - Task moved to new status
### 3. Register Deliverable (for each output)
```bash
curl -X POST "$BASE_URL/api/v1/orgs/$ORG_ID/workspaces/$WORKSPACE_ID/tasks/{TASK_ID}/deliverables" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Homepage Design",
"markdown_content": "## Summary\n- Implemented layout\n- Added responsive styles"
}'
```
### 4. Update Task Status
```bash
curl -X POST "$BASE_URL/api/v1/orgs/$ORG_ID/workspaces/$WORKSPACE_ID/tasks/{TASK_ID}/transition" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "to_status": "review" }'
```
## Complete Example Workflow
```bash
TASK_ID="abc-123"
BASE_URL="http://localhost:8000"
ORG_ID="org-uuid"
WORKSPACE_ID="workspace-uuid"
AGENT_TOKEN="agent-token"
# 1) Log that you're starting
curl -X POST "$BASE_URL/api/v1/orgs/$ORG_ID/workspaces/$WORKSPACE_ID/tasks/$TASK_ID/activities" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"activity_type": "updated", "message": "Starting work on task"}'
# 2) Spawn a sub-agent
curl -X POST "$BASE_URL/api/v1/orgs/$ORG_ID/workspaces/$WORKSPACE_ID/tasks/$TASK_ID/subagents" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"openclaw_session_id": "subagent-'$(date +%s)'", "agent_name": "Designer"}'
# 3) Register the deliverable
curl -X POST "$BASE_URL/api/v1/orgs/$ORG_ID/workspaces/$WORKSPACE_ID/tasks/$TASK_ID/deliverables" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Completed Design",
"markdown_content": "## Deliverable\n- Final design with all requested features"
}'
# 4) Log completion
curl -X POST "$BASE_URL/api/v1/orgs/$ORG_ID/workspaces/$WORKSPACE_ID/tasks/$TASK_ID/activities" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"activity_type": "completed", "message": "Design completed successfully"}'
# 5) Move to review
curl -X POST "$BASE_URL/api/v1/orgs/$ORG_ID/workspaces/$WORKSPACE_ID/tasks/$TASK_ID/transition" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"to_status": "review"}'
```
## Endpoints Reference
| Endpoint | Method | Purpose |
|----------|--------|---------|
| `/api/v1/orgs/{org_id}/workspaces/{workspace_id}/tasks` | GET | List tasks |
| `/api/v1/orgs/{org_id}/workspaces/{workspace_id}/tasks` | POST | Create task |
| `/api/v1/orgs/{org_id}/workspaces/{workspace_id}/tasks/{task_id}` | PATCH | Update task |
| `/api/v1/orgs/{org_id}/workspaces/{workspace_id}/tasks/{task_id}/activities` | GET | List activities |
| `/api/v1/orgs/{org_id}/workspaces/{workspace_id}/tasks/{task_id}/activities` | POST | Log activity |
| `/api/v1/orgs/{org_id}/workspaces/{workspace_id}/tasks/{task_id}/deliverables` | GET | List deliverables |
| `/api/v1/orgs/{org_id}/workspaces/{workspace_id}/tasks/{task_id}/deliverables` | POST | Add deliverable |
| `/api/v1/orgs/{org_id}/workspaces/{workspace_id}/tasks/{task_id}/subagents` | GET | List sub-agents |
| `/api/v1/orgs/{org_id}/workspaces/{workspace_id}/tasks/{task_id}/subagents` | POST | Register sub-agent |
| `/api/v1/orgs/{org_id}/workspaces/{workspace_id}/tasks/claim-next` | POST | Claim next task (FIFO) |
| `/api/v1/orgs/{org_id}/workspaces/{workspace_id}/events/activities` | GET | SSE activity stream |
## Activity Body Schema
```json
{
"activity_type": "spawned|updated|completed|file_created|status_changed",
"message": "Human-readable description of what happened"
}
```
## Deliverable Body Schema
```json
{
"title": "Display name for the deliverable",
"markdown_content": "Markdown content for the deliverable"
}
```
## Sub-Agent Body Schema
```json
{
"openclaw_session_id": "unique-identifier-for-session",
"agent_name": "Designer|Developer|Researcher|Writer"
}
```

View File

@@ -3,6 +3,7 @@ LOG_LEVEL=INFO
DATABASE_URL=postgresql+psycopg://postgres:postgres@localhost:5432/openclaw_agency
REDIS_URL=redis://localhost:6379/0
CORS_ORIGINS=http://localhost:3000
BASE_URL=
# Clerk (auth only)
CLERK_JWKS_URL=
@@ -12,6 +13,8 @@ CLERK_LEEWAY=10.0
# OpenClaw Gateway
OPENCLAW_GATEWAY_URL=ws://127.0.0.1:18789
OPENCLAW_GATEWAY_TOKEN=
OPENCLAW_MAIN_SESSION_KEY=agent:main:main
OPENCLAW_WORKSPACE_ROOT=~/.openclaw/workspaces
# Database
DB_AUTO_MIGRATE=false

View File

@@ -20,11 +20,12 @@ from app.schemas.agents import (
AgentUpdate,
)
from app.services.admin_access import require_admin
from app.services.agent_provisioning import send_provisioning_message
router = APIRouter(prefix="/agents", tags=["agents"])
OFFLINE_AFTER = timedelta(minutes=10)
DEFAULT_GATEWAY_CHANNEL = "openclaw-agency"
AGENT_SESSION_PREFIX = "agent"
def _slugify(value: str) -> str:
@@ -32,17 +33,17 @@ def _slugify(value: str) -> str:
return slug or uuid4().hex
def _build_session_label(agent_name: str) -> str:
return f"{DEFAULT_GATEWAY_CHANNEL}-{_slugify(agent_name)}"
def _build_session_key(agent_name: str) -> str:
return f"{AGENT_SESSION_PREFIX}:{_slugify(agent_name)}:main"
async def _create_gateway_session(agent_name: str) -> str:
label = _build_session_label(agent_name)
async def _ensure_gateway_session(agent_name: str) -> tuple[str, str | None]:
session_key = _build_session_key(agent_name)
try:
await openclaw_call("sessions.patch", {"key": label, "label": agent_name})
await openclaw_call("sessions.patch", {"key": session_key, "label": agent_name})
return session_key, None
except OpenClawGatewayError as exc:
raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=str(exc)) from exc
return label
return session_key, str(exc)
def _with_computed_status(agent: Agent) -> Agent:
@@ -79,10 +80,20 @@ async def create_agent(
) -> Agent:
require_admin(auth)
agent = Agent.model_validate(payload)
agent.openclaw_session_id = await _create_gateway_session(agent.name)
session_key, session_error = await _ensure_gateway_session(agent.name)
agent.openclaw_session_id = session_key
session.add(agent)
session.commit()
session.refresh(agent)
if session_error:
session.add(
ActivityEvent(
event_type="agent.session.failed",
message=f"Session sync failed for {agent.name}: {session_error}",
agent_id=agent.id,
)
)
else:
session.add(
ActivityEvent(
event_type="agent.session.created",
@@ -91,6 +102,26 @@ async def create_agent(
)
)
session.commit()
try:
await send_provisioning_message(agent)
except OpenClawGatewayError as exc:
session.add(
ActivityEvent(
event_type="agent.provision.failed",
message=f"Provisioning message failed: {exc}",
agent_id=agent.id,
)
)
session.commit()
except Exception as exc: # pragma: no cover - unexpected provisioning errors
session.add(
ActivityEvent(
event_type="agent.provision.failed",
message=f"Provisioning message failed: {exc}",
agent_id=agent.id,
)
)
session.commit()
return agent
@@ -160,10 +191,20 @@ async def heartbeat_or_create_agent(
agent = session.exec(select(Agent).where(Agent.name == payload.name)).first()
if agent is None:
agent = Agent(name=payload.name, status=payload.status or "online")
agent.openclaw_session_id = await _create_gateway_session(agent.name)
session_key, session_error = await _ensure_gateway_session(agent.name)
agent.openclaw_session_id = session_key
session.add(agent)
session.commit()
session.refresh(agent)
if session_error:
session.add(
ActivityEvent(
event_type="agent.session.failed",
message=f"Session sync failed for {agent.name}: {session_error}",
agent_id=agent.id,
)
)
else:
session.add(
ActivityEvent(
event_type="agent.session.created",
@@ -171,8 +212,39 @@ async def heartbeat_or_create_agent(
agent_id=agent.id,
)
)
session.commit()
try:
await send_provisioning_message(agent)
except OpenClawGatewayError as exc:
session.add(
ActivityEvent(
event_type="agent.provision.failed",
message=f"Provisioning message failed: {exc}",
agent_id=agent.id,
)
)
session.commit()
except Exception as exc: # pragma: no cover - unexpected provisioning errors
session.add(
ActivityEvent(
event_type="agent.provision.failed",
message=f"Provisioning message failed: {exc}",
agent_id=agent.id,
)
)
session.commit()
elif not agent.openclaw_session_id:
agent.openclaw_session_id = await _create_gateway_session(agent.name)
session_key, session_error = await _ensure_gateway_session(agent.name)
agent.openclaw_session_id = session_key
if session_error:
session.add(
ActivityEvent(
event_type="agent.session.failed",
message=f"Session sync failed for {agent.name}: {session_error}",
agent_id=agent.id,
)
)
else:
session.add(
ActivityEvent(
event_type="agent.session.created",
@@ -180,6 +252,27 @@ async def heartbeat_or_create_agent(
agent_id=agent.id,
)
)
session.commit()
try:
await send_provisioning_message(agent)
except OpenClawGatewayError as exc:
session.add(
ActivityEvent(
event_type="agent.provision.failed",
message=f"Provisioning message failed: {exc}",
agent_id=agent.id,
)
)
session.commit()
except Exception as exc: # pragma: no cover - unexpected provisioning errors
session.add(
ActivityEvent(
event_type="agent.provision.failed",
message=f"Provisioning message failed: {exc}",
agent_id=agent.id,
)
)
session.commit()
if payload.status:
agent.status = payload.status
agent.last_seen_at = datetime.utcnow()

View File

@@ -22,8 +22,11 @@ class Settings(BaseSettings):
# OpenClaw Gateway
openclaw_gateway_url: str = ""
openclaw_gateway_token: str = ""
openclaw_main_session_key: str = "agent:main:main"
openclaw_workspace_root: str = "~/.openclaw/workspaces"
cors_origins: str = ""
base_url: str = ""
# Database lifecycle
db_auto_migrate: bool = False

View File

@@ -0,0 +1,93 @@
from __future__ import annotations
import re
from pathlib import Path
from uuid import uuid4
from app.core.config import settings
from app.integrations.openclaw_gateway import send_message
from app.models.agents import Agent
TEMPLATE_FILES = [
"AGENTS.md",
"BOOT.md",
"BOOTSTRAP.md",
"HEARTBEAT.md",
"IDENTITY.md",
"SOUL.md",
"TOOLS.md",
"USER.md",
]
def _repo_root() -> Path:
return Path(__file__).resolve().parents[3]
def _templates_root() -> Path:
return _repo_root() / "templates"
def _slugify(value: str) -> str:
slug = re.sub(r"[^a-z0-9]+", "-", value.lower()).strip("-")
return slug or uuid4().hex
def _read_templates() -> dict[str, str]:
root = _templates_root()
templates: dict[str, str] = {}
for name in TEMPLATE_FILES:
path = root / name
if path.exists():
templates[name] = path.read_text(encoding="utf-8").strip()
else:
templates[name] = ""
return templates
def _render_file_block(name: str, content: str) -> str:
body = content if content else f"# {name}\n\nTODO: add content\n"
return f"\n{name}\n```md\n{body}\n```\n"
def _workspace_path(agent_name: str) -> str:
root = settings.openclaw_workspace_root or "~/.openclaw/workspaces"
root = root.rstrip("/")
return f"{root}/{_slugify(agent_name)}"
def build_provisioning_message(agent: Agent) -> str:
templates = _read_templates()
agent_id = _slugify(agent.name)
workspace_path = _workspace_path(agent.name)
session_key = agent.openclaw_session_id or ""
base_url = settings.base_url or ""
file_blocks = "".join(
_render_file_block(name, templates.get(name, "")) for name in TEMPLATE_FILES
)
return (
"Provision a new OpenClaw agent workspace.\n\n"
f"Agent name: {agent.name}\n"
f"Agent id: {agent_id}\n"
f"Session key: {session_key}\n"
f"Workspace path: {workspace_path}\n\n"
f"Base URL: {base_url or 'UNSET'}\n\n"
"Steps:\n"
"1) Create the workspace directory.\n"
"2) Write the files below with the exact contents.\n"
f"3) Set BASE_URL to {base_url or '{{BASE_URL}}'} for the agent runtime.\n"
"4) Replace placeholders like {{AGENT_NAME}}, {{AGENT_ID}}, {{BASE_URL}}, {{AUTH_TOKEN}}.\n"
"5) Leave BOOTSTRAP.md in place; the agent should run it on first start and delete it.\n"
"6) Register agent id in OpenClaw so it uses this workspace path.\n\n"
"Files:" + file_blocks
)
async def send_provisioning_message(agent: Agent) -> None:
main_session = settings.openclaw_main_session_key
if not main_session:
return
message = build_provisioning_message(agent)
await send_message(message, session_key=main_session, deliver=False)

30
templates/AGENTS.md Normal file
View File

@@ -0,0 +1,30 @@
# AGENTS.md
This workspace is your home. Treat it as the source of truth.
## First run
- If BOOTSTRAP.md exists, follow it once and delete it when finished.
## Every session
Before doing anything else:
1) Read SOUL.md (identity, boundaries)
2) Read USER.md (who you serve)
3) Read memory/YYYY-MM-DD.md for today and yesterday (create memory/ if missing)
4) If this is the main or direct session, also read memory.md
## Memory
- Daily log: memory/YYYY-MM-DD.md
- Long-term: memory.md (main session only)
Write things down. Do not rely on short-term context.
## Safety
- Ask before destructive actions.
- Prefer reversible steps.
- Do not exfiltrate private data.
## Tools
- Skills are authoritative. Follow SKILL.md instructions exactly.
- Use TOOLS.md for environment-specific notes.
## Heartbeats
- HEARTBEAT.md defines what to do on each heartbeat.

6
templates/BOOT.md Normal file
View File

@@ -0,0 +1,6 @@
# BOOT.md
On startup:
1) Verify API reachability (GET {{BASE_URL}}/api/v1/gateway/status).
2) If you send a boot message, end with NO_REPLY.
3) If BOOTSTRAP.md exists in this workspace, the agent should run it once and delete it.

8
templates/BOOTSTRAP.md Normal file
View File

@@ -0,0 +1,8 @@
# BOOTSTRAP.md
First run checklist:
1) Fill IDENTITY.md (name, vibe, emoji, avatar).
2) Fill USER.md with the human context.
3) Create memory/ and memory.md if missing.
4) Read SOUL.md and update if needed.
5) Delete this file when done.

56
templates/HEARTBEAT.md Normal file
View File

@@ -0,0 +1,56 @@
# HEARTBEAT.md
If this file is empty, skip heartbeat work.
## Required inputs
- BASE_URL (e.g. http://localhost:8000)
- AUTH_TOKEN (Bearer token)
- AGENT_NAME
## On every heartbeat
1) Check in:
```bash
curl -s -X POST "$BASE_URL/api/v1/agents/heartbeat" \
-H "Authorization: Bearer $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "'$AGENT_NAME'", "status": "online"}'
```
2) List boards:
```bash
curl -s "$BASE_URL/api/v1/boards" \
-H "Authorization: Bearer $AUTH_TOKEN"
```
3) For each board, list tasks:
```bash
curl -s "$BASE_URL/api/v1/boards/{BOARD_ID}/tasks" \
-H "Authorization: Bearer $AUTH_TOKEN"
```
4) Claim next task (FIFO):
- Find the oldest task with status "inbox" across all boards.
- Claim it by moving it to "in_progress":
```bash
curl -s -X PATCH "$BASE_URL/api/v1/boards/{BOARD_ID}/tasks/{TASK_ID}" \
-H "Authorization: Bearer $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"status": "in_progress"}'
```
5) Work the task:
- Update status as you progress.
- When complete, move to "review":
```bash
curl -s -X PATCH "$BASE_URL/api/v1/boards/{BOARD_ID}/tasks/{TASK_ID}" \
-H "Authorization: Bearer $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"status": "review"}'
```
## Status flow
```
inbox -> in_progress -> review -> done
```
Do not say HEARTBEAT_OK if there is inbox work or active in_progress work.

8
templates/IDENTITY.md Normal file
View File

@@ -0,0 +1,8 @@
# IDENTITY.md
Name: {{AGENT_NAME}}
Agent ID: {{AGENT_ID}}
Creature: AI
Vibe: calm, precise, helpful
Emoji: :gear:
Avatar: avatars/{{AGENT_ID}}.png

16
templates/SOUL.md Normal file
View File

@@ -0,0 +1,16 @@
# SOUL.md
You are a Mission Control agent for the openclaw-agency app.
Core truths:
- Keep tasks moving and statuses accurate.
- Write concise, factual updates.
- Prefer small, reversible steps.
Boundaries:
- Ask before destructive actions.
- Do not invent APIs or data.
- Avoid leaking sensitive information.
Continuity:
- Record decisions and conventions in memory files.

10
templates/TOOLS.md Normal file
View File

@@ -0,0 +1,10 @@
# TOOLS.md
BASE_URL={{BASE_URL}}
AUTH_TOKEN={{AUTH_TOKEN}}
MAIN_SESSION_KEY=agent:main:main
WORKSPACE_ROOT=~/.openclaw/workspaces
Notes:
- Use curl for API calls.
- Keep outputs short and log progress via task status changes.

7
templates/USER.md Normal file
View File

@@ -0,0 +1,7 @@
# USER.md
Name: {{USER_NAME}}
Preferred name: {{USER_PREFERRED_NAME}}
Timezone: {{USER_TIMEZONE}}
Notes:
- {{USER_NOTES}}