Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion coworker/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from .environment import environment_context
from .memory import MemoryStore, Scope, format_memories, memory_tools
from .permissions import Mode, PermissionEngine
from .project import load_agents_md
from .project import load_agents_md, load_wiki_index
from .roots import RootDir, normalize_roots, render_context
from .providers import ProviderClient, ProviderRouter
from .overrides import RiskOverrideStore
Expand Down Expand Up @@ -280,6 +280,9 @@ def build_engine(
conventions = load_agents_md(ws)
if conventions:
instructions = f"{instructions}\n\n{conventions}"
wiki = load_wiki_index(ws)
if wiki:
instructions = f"{instructions}\n\n{wiki}"

if memory_store is not None:
registry.register_all(
Expand Down
33 changes: 32 additions & 1 deletion coworker/project.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Project context — AGENTS.md ingestion (root + global) into the system prompt."""
"""Project context — AGENTS.md + knowledge-wiki ingestion into the system prompt."""

from __future__ import annotations

Expand Down Expand Up @@ -38,3 +38,34 @@ def load_agents_md(
for label, text in parts
]
return "Project conventions:\n" + "\n\n".join(blocks)


# Knowledge-wiki index injection. Convention: a workspace that maintains a
# long-lived knowledge base keeps it as markdown pages under `wiki/` with an
# `INDEX.md` map-of-content. Injecting ONLY the index (progressive disclosure,
# same trick as the skills catalog) gives every session standing knowledge of
# what is known — pages load on demand via the normal file tools — at a context
# cost that stays flat as the wiki grows.
_WIKI_INDEX_CAP = 6_000 # chars; an index past this is a wiki smell, not a need


def load_wiki_index(workspace: str | Path) -> str:
"""System-prompt block from `<workspace>/wiki/INDEX.md`, if present."""
index = Path(workspace).expanduser().resolve() / "wiki" / "INDEX.md"
if not index.is_file():
return ""
try:
text = index.read_text(encoding="utf-8").strip()
except OSError:
return ""
if not text:
return ""
if len(text) > _WIKI_INDEX_CAP:
text = text[:_WIKI_INDEX_CAP] + "\n… (index truncated — trim it; an index is a map, not a page)"
return (
"This workspace maintains a knowledge wiki (markdown pages under `wiki/`, "
"linked with [[wikilinks]]). Its index follows. Open pages on demand with "
"the file tools; when you learn something durable that changes a page's "
"claims, revise that page in place rather than appending duplicates.\n"
f"<wiki INDEX.md>\n{text}\n</wiki INDEX.md>"
)
32 changes: 32 additions & 0 deletions tests/test_skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,35 @@ def test_build_engine_code_has_agents_md_and_skills(tmp_path):
assert engine.agent_name == "code"
finally:
engine.executor.close()


def test_build_engine_injects_wiki_index(tmp_path):
(tmp_path / "wiki").mkdir()
(tmp_path / "wiki" / "INDEX.md").write_text(
"# Home Wiki\n- [[adrian/heart-rate]] — baselines\n"
)
engine = build_engine(agent=code_agent(), workspace=tmp_path, provider=_Stub())
try:
sys_msg = engine.messages[0]["content"]
assert "knowledge wiki" in sys_msg
assert "[[adrian/heart-rate]]" in sys_msg
assert "revise that page in place" in sys_msg
finally:
engine.executor.close()


def test_no_wiki_no_injection(tmp_path):
engine = build_engine(agent=code_agent(), workspace=tmp_path, provider=_Stub())
try:
assert "knowledge wiki" not in engine.messages[0]["content"]
finally:
engine.executor.close()


def test_wiki_index_truncated_when_huge(tmp_path):
(tmp_path / "wiki").mkdir()
(tmp_path / "wiki" / "INDEX.md").write_text("x" * 10_000)
from coworker.project import load_wiki_index

block = load_wiki_index(tmp_path)
assert "index truncated" in block and len(block) < 7_000