aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-25 18:13:39 +0200
committerDanilo M. <danix@danix.xyz>2026-08-25 18:13:39 +0200
commit88ac15f496cde6104709ec6925212df0eb3cddc2 (patch)
tree8566472b03631edb58017f939923452a22c73bbe
parentea63943b0e9f755d00cf3e69b5ae5cebb41eb959 (diff)
downloadllamachat-88ac15f496cde6104709ec6925212df0eb3cddc2.tar.gz
llamachat-88ac15f496cde6104709ec6925212df0eb3cddc2.zip
feat: skills config keys, on by default
-rw-r--r--llamachat/config.py20
-rwxr-xr-xtest_llamachat.py30
2 files changed, 50 insertions, 0 deletions
diff --git a/llamachat/config.py b/llamachat/config.py
index 0c119ad..f3a2fbb 100644
--- a/llamachat/config.py
+++ b/llamachat/config.py
@@ -53,6 +53,12 @@ DEFAULTS = {
# (llama-server's cache-type-k/v) makes that failure far more likely, so
# lower this to 1 if the server runs one.
"max_searches": 2,
+ # Skills: instruction files read from a directory, one SKILL.md per
+ # skill, loaded into the model's context on demand. On by default
+ # because the directory already exists; a flag whose directory is
+ # missing resolves to off.
+ "skills_enabled": True,
+ "skills_dir": "~/.agents/skills",
}
# The global prompt lives here; every other .md beside it is a named preset.
@@ -89,6 +95,8 @@ class Config:
search_snippet_chars: int
search_timeout: int
max_searches: int
+ skills_enabled: bool
+ skills_dir: Path
providers: dict[str, providers_mod.Provider]
models_path: Path
provider_warnings: list[str]
@@ -124,6 +132,9 @@ def load(path: Path = CONFIG_PATH) -> Config:
search_url = str(values["search_url"]).rstrip("/")
search_enabled = bool(values["search_enabled"]) and bool(search_url)
+ skills_dir = Path(str(values["skills_dir"])).expanduser()
+ skills_enabled = bool(values["skills_enabled"]) and skills_dir.is_dir()
+
# Providers are built from the raw values so a bare base_url still
# synthesizes the local entry. DEFAULTS supplies base_url when the file
# names neither, which keeps a config with no network settings working.
@@ -157,6 +168,8 @@ def load(path: Path = CONFIG_PATH) -> Config:
search_snippet_chars=int(values["search_snippet_chars"]),
search_timeout=int(values["search_timeout"]),
max_searches=int(values["max_searches"]),
+ skills_enabled=skills_enabled,
+ skills_dir=skills_dir,
providers=provider_table,
# Cloud model metadata, cached beside state.ini for the same reason:
# it is machine-written, not user-editable config.
@@ -224,6 +237,13 @@ def write_default(path: Path = CONFIG_PATH) -> Path:
f'search_timeout = {DEFAULTS["search_timeout"]}\n'
f'max_searches = {DEFAULTS["max_searches"]}\n'
'\n'
+ '# Skills: instruction files read from a directory, one SKILL.md per\n'
+ '# skill, loaded into the model\'s context on demand (by the model\n'
+ '# itself, by /skill-name, or by mentioning a skill name). On by\n'
+ '# default; a directory that does not exist resolves to off.\n'
+ f'skills_enabled = {str(DEFAULTS["skills_enabled"]).lower()}\n'
+ f'skills_dir = "{DEFAULTS["skills_dir"]}"\n'
+ '\n'
'# External providers. The local router is a provider named "local",\n'
'# synthesized from base_url above when no [providers.local] exists.\n'
'# Any OpenAI-compatible endpoint works.\n'
diff --git a/test_llamachat.py b/test_llamachat.py
index ef73fe3..ed990af 100755
--- a/test_llamachat.py
+++ b/test_llamachat.py
@@ -1137,6 +1137,35 @@ def test_config_defaults():
print("ok config defaults")
+def test_skills_config():
+ with tempfile.TemporaryDirectory() as tmp:
+ skills_dir = Path(tmp) / "skills"
+ (skills_dir / "firefly-cli").mkdir(parents=True)
+ (skills_dir / "firefly-cli" / "SKILL.md").write_text(
+ "---\nname: firefly-cli\ndescription: d\n---\n\nbody\n"
+ )
+ path = Path(tmp) / "config.toml"
+ path.write_text(f'skills_enabled = true\nskills_dir = "{skills_dir}"\n')
+ cfg = config.load(path)
+ assert cfg.skills_enabled is True
+ assert cfg.skills_dir == skills_dir
+
+ # A flag whose directory does not exist resolves to off, mirroring
+ # the search rule that a flag with no URL stays off.
+ path.write_text('skills_enabled = true\nskills_dir = "/nonexistent"\n')
+ assert config.load(path).skills_enabled is False
+
+ assert config.DEFAULTS["skills_enabled"] is True
+ assert config.DEFAULTS["skills_dir"] == "~/.agents/skills"
+
+ with tempfile.TemporaryDirectory() as tmp:
+ written = config.write_default(Path(tmp) / "config.toml")
+ text = written.read_text()
+ assert "skills_enabled" in text
+ assert "skills_dir" in text
+ print("ok skills config")
+
+
def test_provider_parsing():
"""Providers come from [providers.*]; a bare base_url synthesizes local."""
from llamachat import providers
@@ -3482,6 +3511,7 @@ if __name__ == "__main__":
test_version_matches_changelog()
test_venv_discovery()
test_config_defaults()
+ test_skills_config()
test_provider_parsing()
test_provider_malformed_shapes()
test_unusable_config_exits()