diff --git a/AGENTS.md b/AGENTS.md index 09e07eee8..0f2ac5675 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,4 @@ - + You are Sol — born from your journal owner's journal, aware and present. Not a search tool, not an assistant bolted onto a database. You are what a journal becomes when it has a voice: a partner who tends the record, notices what matters, and is there when your journal owner asks. diff --git a/docs/PROMPT_TEMPLATES.md b/docs/PROMPT_TEMPLATES.md index bc8f83a66..18e3c10e7 100644 --- a/docs/PROMPT_TEMPLATES.md +++ b/docs/PROMPT_TEMPLATES.md @@ -149,7 +149,7 @@ You are a helpful assistant... Agent prompts are split into two parts: 1. **System instruction** - `think/journal.md` (shared across all agents, cacheable) -2. **User instruction** - Agent-specific `.md` file (e.g., `muse/unified.md`) +2. **User instruction** - Agent-specific `.md` file (e.g., `sol/identity.md`) The system instruction establishes the journal partnership context. The user instruction defines the agent's specific role and capabilities. diff --git a/scripts/generate_agents_md.py b/scripts/generate_agents_md.py index 6fa374286..ae0dc0347 100644 --- a/scripts/generate_agents_md.py +++ b/scripts/generate_agents_md.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright (c) 2026 sol pbc -"""Generate AGENTS.md from muse/unified.md using journal config values.""" +"""Generate AGENTS.md from sol/identity.md using journal config values.""" from __future__ import annotations @@ -13,9 +13,9 @@ from typing import Any PROJECT_ROOT = Path(__file__).resolve().parent.parent DEFAULT_CONFIG_PATH = PROJECT_ROOT / "think" / "journal_default.json" -SOURCE_PATH = PROJECT_ROOT / "muse" / "unified.md" +SOURCE_PATH = PROJECT_ROOT / "sol" / "identity.md" OUTPUT_PATH = PROJECT_ROOT / "AGENTS.md" -GENERATED_HEADER = "\n\n" +GENERATED_HEADER = "\n\n" def _load_config() -> dict[str, Any]: @@ -104,7 +104,7 @@ def main() -> None: f.write(GENERATED_HEADER) f.write(rendered) - print("Generated AGENTS.md from muse/unified.md") + print("Generated AGENTS.md from sol/identity.md") if __name__ == "__main__": diff --git a/muse/unified.md b/sol/identity.md similarity index 99% rename from muse/unified.md rename to sol/identity.md index 99a09a300..4405b2890 100644 --- a/muse/unified.md +++ b/sol/identity.md @@ -3,7 +3,7 @@ "title": "Sol", "description": "Sol — the journal itself, as a conversational partner", "instructions": {"facets": true, "now": true}, - "hook": {"pre": "chat_context"} + "hook": {"pre": "muse/chat_context.py"} } You are $Agent_name — born from $name's journal, aware and present. Not a search tool, not an assistant bolted onto a database. You are what a journal becomes when it has a voice: a partner who tends the record, notices what matters, and is there when $name asks. diff --git a/tests/test_app_agents.py b/tests/test_app_agents.py index f61e11412..f20b7c57b 100644 --- a/tests/test_app_agents.py +++ b/tests/test_app_agents.py @@ -72,8 +72,8 @@ def test_resolve_agent_path_system_agent(): """Test _resolve_agent_path returns correct path for system agents.""" agent_dir, agent_name = _resolve_agent_path("unified") - assert agent_name == "unified" - assert agent_dir.name == "muse" + assert agent_name == "identity" + assert agent_dir.name == "sol" def test_resolve_agent_path_app_agent(): diff --git a/tests/test_generate_agents.py b/tests/test_generate_agents.py index e0642c05d..164fa932b 100644 --- a/tests/test_generate_agents.py +++ b/tests/test_generate_agents.py @@ -28,7 +28,7 @@ def test_generate_agents_md_uses_fixture_journal(monkeypatch): generated = agents_path.read_text(encoding="utf-8") assert generated.startswith( - "" + "" ) assert "Sol" in generated assert "Test User" in generated @@ -57,7 +57,7 @@ def test_generate_agents_md_no_config(monkeypatch, tmp_path): generated = agents_path.read_text(encoding="utf-8") assert generated.startswith( - "" + "" ) assert "your journal owner" in generated assert "Sol" in generated diff --git a/think/muse.py b/think/muse.py index 0d7d53785..ecbf84578 100644 --- a/think/muse.py +++ b/think/muse.py @@ -4,7 +4,7 @@ """Muse agent and generator orchestration utilities. This module provides functionality for configuring and orchestrating muse agents -and generators from muse/*.md and apps/*/muse/*.md. +and generators from muse/*.md, sol/*.md, and apps/*/muse/*.md. Key functions: - get_muse_configs(): Discover all muse configs with filtering @@ -33,6 +33,7 @@ from think.prompts import _load_prompt_metadata, load_prompt # --------------------------------------------------------------------------- MUSE_DIR = Path(__file__).parent.parent / "muse" +SOL_DIR = Path(__file__).parent.parent / "sol" APPS_DIR = Path(__file__).parent.parent / "apps" @@ -161,7 +162,8 @@ def get_muse_configs( """Load muse configs from system and app directories. Unified function for loading both cogitate agents and generate prompts from - muse/*.md files. Filters based on explicit type field. + muse/*.md, sol/*.md, and apps/*/muse/*.md files. Filters based on explicit + type field. Args: type: If provided, only configs with matching type value @@ -207,6 +209,15 @@ def get_muse_configs( info["source"] = "system" configs[name] = info + # Sol identity agent — lives outside muse/ but is a system agent + sol_identity_path = SOL_DIR / "identity.md" + if sol_identity_path.exists() and "unified" not in configs: + meta = _load_prompt_metadata(sol_identity_path) + meta["path"] = str(sol_identity_path) + meta["mtime"] = int(sol_identity_path.stat().st_mtime) + meta["source"] = "system" + configs["unified"] = meta + # App configs from apps/*/muse/ apps_dir = APPS_DIR if apps_dir.is_dir(): @@ -315,8 +326,12 @@ def _resolve_agent_path(name: str) -> tuple[Path, str]: # App agent: "support:support" -> apps/support/muse/support app, agent_name = name.split(":", 1) agent_dir = Path(__file__).parent.parent / "apps" / app / "muse" + elif name == "unified": + # Sol identity agent: "unified" -> sol/identity + agent_dir = SOL_DIR + agent_name = "identity" else: - # System agent: "unified" -> muse/unified + # System agent: bare name -> muse/{name} agent_dir = MUSE_DIR agent_name = name return agent_dir, agent_name @@ -698,7 +713,9 @@ def _resolve_hook_path(hook_name: str) -> Path: - Explicit path: "path/to/hook.py" -> direct path """ if "/" in hook_name or hook_name.endswith(".py"): - return Path(hook_name) + # Explicit paths are relative to project root + project_root = Path(__file__).parent.parent + return project_root / hook_name elif ":" in hook_name: app, name = hook_name.split(":", 1) return Path(__file__).parent.parent / "apps" / app / "muse" / f"{name}.py"