Skip to content

Commit b0f9541

Browse files
ZD Studiosclaude
andcommitted
feat: WSL-IP URL, 10 built-in skills, editable system prompt from the hub
WSL access fix (root cause of 'site can't be reached'): 127.0.0.1 doesn't forward from the Windows browser into WSL. aios start/url now detect WSL and print the WSL-IP URL (http://<ip>:8787/) which always works with the 0.0.0.0 bind. - skills/: 10 built-in skills (skill-maker, mcp-maker, web-search, web-browse, image-gen, code-review, summarize, research, data-analyst, task-scheduler). mount_aios_skills copies them into each agent's skill dir on setup/update (skills.mount config flag). - Editable system prompt for Brain/Team: stored in .aios/system_prompt.txt, used by route(); hub GET/POST /api/system_prompt; new Settings card in the dashboard. Verified on Windows: 10 skills mount; system_prompt GET/POST round-trips and /api/config still works; syntax clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 1fffc34 commit b0f9541

15 files changed

Lines changed: 162 additions & 2 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ Plus two integrations that glue it together:
172172

173173
**Auto-updates:** `aios start` now auto-runs `git pull` + reinstalls changed deps when the repo has updates (`updates.auto_update: true`). Turn it off in `aios.config.yaml`.
174174

175+
**Skills & system prompt:** 10 skills ship built-in (skill-maker, mcp-maker, web-search, web-browse, image-gen, code-review, summarize, research, data-analyst, task-scheduler) and mount into every agent. Edit the Brain/Team **system prompt** live in the hub → **Settings**.
176+
177+
**On WSL?** `127.0.0.1:8787` often won't reach WSL from your Windows browser (localhost-forwarding is flaky). `aios start`/`aios url` now print your **WSL IP** URL — use that (e.g. `http://172.31.x.x:8787/`). The hub binds `0.0.0.0` so the WSL IP always works.
178+
175179
## ⚙️ Configuration
176180

177181
- **`.env`** — secrets only (`AIOS_LLM_PROVIDER`, `AIOS_LLM_API_KEY`, `AIOS_DEFAULT_MODEL`, optional channel tokens). Git-ignored.

aios.config.example.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ lifeos:
4747
openui:
4848
mount_context: true # mount OpenUI (generative UI) context into the agents
4949

50+
skills:
51+
mount: true # mount the bundled skills/ (skill-maker, mcp-maker, …) into agents
52+
5053
updates:
5154
check_on_start: true # on `aios start`, check the repo for updates and notify
5255
auto_update: true # on `aios start`, auto git-pull + reinstall if the repo changed

aios.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,10 @@ def cmd_setup(args):
753753
if cfg_get(cfg, "openui.mount_context", True):
754754
mount_openui(cfg)
755755

756+
# 6c) mount the bundled AI OS skills (skill-maker, mcp-maker, …) into the agents
757+
if cfg_get(cfg, "skills.mount", True):
758+
mount_aios_skills(cfg)
759+
756760
# 7) wire openclaw-os plugin (best-effort; needs openclaw runnable)
757761
if cfg_get(cfg, "services.openclaw_os.enabled", True) and not args.skip_wire:
758762
wire_openclaw_os(quiet=True)
@@ -1088,6 +1092,29 @@ def mount_lifeos(cfg: dict):
10881092
say(f"{C.GRY}Source: {src}{C.R}")
10891093

10901094

1095+
def mount_aios_skills(cfg: dict):
1096+
"""Copy the bundled AI OS skills (skills/) into each agent's skill directory."""
1097+
head("Mounting AI OS skills")
1098+
src = ROOT / "skills"
1099+
if not src.exists():
1100+
warn("skills/ not found; skipping")
1101+
return
1102+
names = [p.name for p in src.iterdir() if p.is_dir()]
1103+
targets = [Path.home() / ".openclaw" / "skills", Path.home() / ".hermes" / "skills",
1104+
STATE / "skills"]
1105+
for t in targets:
1106+
for name in names:
1107+
try:
1108+
dst = t / name
1109+
if dst.exists():
1110+
shutil.rmtree(dst)
1111+
dst.parent.mkdir(parents=True, exist_ok=True)
1112+
shutil.copytree(src / name, dst)
1113+
except Exception as e:
1114+
warn(f"could not mount {name}{t}: {e}")
1115+
ok(f"mounted {len(names)} skills: {', '.join(names)}")
1116+
1117+
10911118
def mount_openui(cfg: dict):
10921119
"""Mount OpenUI (generative-UI) context into every agent's skill dir so any
10931120
agent can respond with OpenUI Lang that the openclaw-os dashboard renders."""
@@ -1399,6 +1426,8 @@ def cmd_update(args):
13991426
mount_lifeos(cfg)
14001427
if cfg_get(cfg, "openui.mount_context", True):
14011428
mount_openui(cfg)
1429+
if cfg_get(cfg, "skills.mount", True):
1430+
mount_aios_skills(cfg)
14021431
# Restart any services that are currently running so the new code takes effect.
14031432
specs = service_specs(cfg)
14041433
running = [s for s in specs if (read_pid(s) and pid_alive(read_pid(s)["pid"]))]
@@ -1656,10 +1685,30 @@ def _openclaw_os_url(cfg) -> str | None:
16561685
return cfg_get(cfg, "health.openclaw_os", f"http://127.0.0.1:{port}/plugins/openclawos/")
16571686

16581687

1688+
def _is_wsl() -> bool:
1689+
try:
1690+
return "microsoft" in Path("/proc/version").read_text(errors="ignore").lower()
1691+
except Exception:
1692+
return bool(os.environ.get("WSL_DISTRO_NAME"))
1693+
1694+
1695+
def _wsl_ip() -> str | None:
1696+
try:
1697+
out = subprocess.run(["hostname", "-I"], capture_output=True, text=True, timeout=5).stdout.split()
1698+
return out[0] if out else None
1699+
except Exception:
1700+
return None
1701+
1702+
16591703
def _print_urls(cfg):
16601704
hbp = int(cfg_get(cfg, "services.hub.port", 8787))
16611705
say(f"{C.B}★ Control Room — talk to everything{C.R}")
16621706
say(f" {C.CYN}http://127.0.0.1:{hbp}/{C.R} (the AIOS Hub dashboard)")
1707+
if _is_wsl():
1708+
ip = _wsl_ip()
1709+
if ip:
1710+
say(f" {C.B}{C.YEL}WSL → open THIS in your Windows browser: http://{ip}:{hbp}/{C.R}")
1711+
say(f" {C.GRY}(127.0.0.1 may not forward from Windows to WSL — the IP above always works){C.R}")
16631712
say(f"\n{C.B}Individual surfaces{C.R}")
16641713
osurl = _openclaw_os_url(cfg)
16651714
if osurl:

aios_hub.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,14 @@ def run_aios(*a, background=False):
9393
OPENCLAW_EMBED = os.environ.get("AIOS_OPENCLAWPROXY_URL", "http://127.0.0.1:8791/")
9494
HERMES_EMBED = os.environ.get("AIOS_HERMESPROXY_URL", "http://127.0.0.1:8792/")
9595
SCHEDULES_FILE = ROOT / ".aios" / "schedules.json"
96+
SYSTEM_PROMPT_FILE = ROOT / ".aios" / "system_prompt.txt"
97+
98+
99+
def read_system_prompt() -> str:
100+
try:
101+
return SYSTEM_PROMPT_FILE.read_text(encoding="utf-8").strip()
102+
except Exception:
103+
return ""
96104

97105

98106
# --------------------------------------------------------------------------- #
@@ -185,7 +193,8 @@ def route(target: str, message: str, history: list | None = None) -> str:
185193
history = history or []
186194
target = (target or "brain").lower()
187195
if target in ("brain", "aios", "hub"):
188-
sys = ("You are the AIOS Brain — the orchestrator of The AI OS, which unifies six agents: "
196+
sys = read_system_prompt() or (
197+
"You are the AIOS Brain — the orchestrator of The AI OS, which unifies six agents: "
189198
"opencode (coding), hermes (autonomous), openclaw (channels), CrewAI (multi-agent crews), "
190199
"claude-code (Claude Code API), and LifeOS (shared skills). Be concise and helpful. Suggest "
191200
"which agent is best for a task. When a visual answer helps (charts, tables, forms, dashboards), "
@@ -361,6 +370,8 @@ def do_GET(self):
361370
self._send(200, {**PEERS, "openclaw-embed": OPENCLAW_EMBED, "hermes-embed": HERMES_EMBED})
362371
elif self.path == "/api/schedules":
363372
self._send(200, load_schedules())
373+
elif self.path == "/api/system_prompt":
374+
self._send(200, {"prompt": read_system_prompt()})
364375
elif self.path == "/api/config":
365376
env = read_env_file()
366377
self._send(200, {
@@ -436,6 +447,13 @@ def do_POST(self):
436447
self._send(200, {"ok": True})
437448
except Exception as e:
438449
self._send(200, {"ok": False, "error": str(e)})
450+
elif self.path == "/api/system_prompt":
451+
try:
452+
SYSTEM_PROMPT_FILE.parent.mkdir(parents=True, exist_ok=True)
453+
SYSTEM_PROMPT_FILE.write_text(payload.get("prompt", ""), encoding="utf-8")
454+
self._send(200, {"ok": True})
455+
except Exception as e:
456+
self._send(200, {"ok": False, "error": str(e)})
439457
elif self.path == "/api/action":
440458
action = payload.get("action")
441459
svc = payload.get("service")

docs/dashboard.html

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,13 @@ <h3>🔑 Model provider</h3>
253253
<div class="row"><button class="btn primary" onclick="saveModel()">Save & apply</button><span id="modelMsg" class="kv"></span></div>
254254
</div>
255255

256+
<div class="card">
257+
<h3>🧠 System prompt (Brain &amp; Team)</h3>
258+
<p>Set the personality/instructions for the Brain and Team. Leave blank to use the default.</p>
259+
<div class="field" style="margin-top:12px"><textarea id="sSysPrompt" rows="6" placeholder="You are my personal AI OS assistant. Be concise, proactive, and…"></textarea></div>
260+
<div class="row"><button class="btn primary" onclick="saveSysPrompt()">Save system prompt</button><span id="sysMsg" class="kv"></span></div>
261+
</div>
262+
256263
<div class="card">
257264
<h3>📣 Channels <span class="badge down" style="font-weight:600">optional</span></h3>
258265
<p>Tokens for openclaw messaging channels. Leave blank to skip.</p>
@@ -301,7 +308,7 @@ <h3>🧩 Advanced — aios.config.yaml</h3>
301308
if(v==='crewai')agentPanel('crewai','crewaiBody','Role-based multi-agent crews. Chat with it from the Chat tab (target “CrewAI”). It can call the other agents via the hub.',peers.crewai);
302309
if(v==='claudecode')agentPanel('claudecode','claudecodeBody','Claude Code exposed as an OpenAI-compatible API. Chat with it (target “claude-code”), or point any tool at <span class="mono">'+(peers.claudecode||'http://127.0.0.1:8000')+'/v1</span>. Needs the <span class="mono">claude</span> CLI installed + authenticated.',peers.claudecode);
303310
if(v==='automations')loadSchedules();
304-
if(v==='settings')loadSettings();
311+
if(v==='settings'){loadSettings();loadSysPrompt();}
305312
}
306313
function frame(fid,linkId,url){if(!url)return;const f=document.getElementById(fid);document.getElementById(linkId).href=url;if(!f.dataset.loaded){f.src=url;f.dataset.loaded='1';}}
307314
function agentPanel(key,bodyId,desc,url,external){
@@ -383,6 +390,12 @@ <h3>${ICON[key]} ${key} <span class="badge ${up?'up':'down'}">${up?'running':'st
383390
async function saveConfig(){
384391
await fetch('/api/config_text',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({text:document.getElementById('sConfig').value})});toast('Config saved — restart to apply');
385392
}
393+
async function loadSysPrompt(){try{const r=await(await fetch('/api/system_prompt')).json();document.getElementById('sSysPrompt').value=r.prompt||'';}catch(e){}}
394+
async function saveSysPrompt(){
395+
document.getElementById('sysMsg').innerHTML='<span class="spin"></span>';
396+
await fetch('/api/system_prompt',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({prompt:document.getElementById('sSysPrompt').value})});
397+
document.getElementById('sysMsg').textContent='saved';toast('System prompt saved');
398+
}
386399
async function act(action,service){
387400
toast((service||'all')+': '+action+'…');
388401
await fetch('/api/action',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action,service})});

skills/code-review/SKILL.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
name: code-review
3+
description: Review a diff or file for bugs, security issues, and simplifications.
4+
---
5+
# Code Review
6+
Review the given code/diff. Report, most-severe-first: correctness bugs (with a concrete failing
7+
input), security issues, then reuse/simplification/efficiency cleanups. Be specific and cite lines.
8+
For deep work, delegate to `opencode` via the hub.

skills/data-analyst/SKILL.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
name: data-analyst
3+
description: Analyze CSV/tabular data and produce charts or tables.
4+
---
5+
# Data Analyst
6+
Load the data, describe its shape, compute the requested metrics, and present results as a table or
7+
chart. When a visual helps, emit OpenUI Lang so the openclaw-os dashboard renders it live.

skills/image-gen/SKILL.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
name: image-gen
3+
description: Generate images from text prompts via the configured media provider.
4+
---
5+
# Image Generation
6+
Turn the user's request into a clear, detailed image prompt (subject, style, composition, lighting),
7+
call the configured image/media generation tool, and return the result. Offer 1-2 prompt variations.

skills/mcp-maker/SKILL.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
name: mcp-maker
3+
description: Scaffold and register Model Context Protocol (MCP) servers.
4+
---
5+
# MCP Maker
6+
Help the user build an MCP server: pick Python (FastMCP) or Node (@modelcontextprotocol/sdk),
7+
scaffold tools/resources with typed schemas, and show how to register it with openclaw
8+
(`openclaw mcp add`) or in the client config. Keep tools small, well-described, and idempotent.

skills/research/SKILL.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
name: research
3+
description: Multi-step research with sources, best handled by a crew.
4+
---
5+
# Research
6+
Break the question into sub-questions, gather evidence (web-search/web-browse), cross-check across
7+
sources, and synthesize a cited answer with a confidence note. For breadth, delegate to `crewai`.

0 commit comments

Comments
 (0)