Skill Reload
Pick up newly installed, edited, or removed skills without restarting the session
Skill reload refreshes a running agent’s skill index so newly installed, edited, or removed skills are available on the next turn — no restart needed.
from praisonaiagents import Agent
agent = Agent(
name="Support Agent",
instructions="Use the latest skills to help the user.",
skills=["*"],
)
# Later, after `praisonai skills install <url>` or editing a SKILL.md on disk:
diff = agent.skill_manager.reload()
# {"added": ["refund-policy"], "changed": ["greeting"], "removed": []}Quick Start
- Reload in a live session
python from praisonaiagents import SkillManager mgr = SkillManager() mgr.discover(["./skills"], include_defaults=False) # A skill was installed or edited on disk after discover()… diff = mgr.reload() # {"added": ["refund-policy"], "changed": ["greeting"], "removed": []} print(f"+{len(diff['added'])} / ~{len(diff['changed'])} / -{len(diff['removed'])}") - Scan from another shell
bash # Convenience scan: list the skills discoverable on disk right now praisonai skills reload # Scan a specific directory praisonai skills reload --dirs ./skills• greeting • refund-policy Skills available: 2
How It Works
reload() re-scans the same directories the manager last discovered, diffs the fresh scan against the in-memory registry, and swaps the index atomically.
Changes take effect on the next turn via to_prompt(), never mid-turn — so a reload is prompt-cache-safe.
| Diff key | Meaning | Effect on the index |
|---|---|---|
added | A new SKILL.md appeared | Loaded fresh (metadata only) |
changed | An existing skill’s SKILL.md changed on disk | Reloaded fresh; stale instructions dropped |
removed | The skill directory disappeared | Dropped and deactivated |
Choosing where to reload from
Pick the live primitive for a running session; pick the CLI to check disk from another shell.
What Reload Guarantees
| Guarantee | Behaviour |
|---|---|
| Atomic swap | The index is replaced in one step; changes appear on the next to_prompt() |
| Sorted diff | Returns {"added": [...], "changed": [...], "removed": [...]} as sorted name lists |
| Telemetry survives | Unchanged skills keep their existing LoadedSkill, preserving activation and usage counts |
| Change detection | Uses the SKILL.md mtime; a baseline is captured at load time |
| Removed skills deactivated | Cached instructions are cleared and the skill is dropped |
add_skill()-safe | Skills added out-of-band are preserved and never reported as removed |
| Telemetry writes ignored | use-count / last-used / patch-count rewrites don’t misfire as content changes |
No prior discover() needed | reload() works even when discover() was never called |
Behaviour under edge cases
Edit between discover() and the first reload() is caught
The SKILL.md mtime baseline is recorded at load time, so an edit made after discover() but before the first reload() still shows up as changed.
from praisonaiagents import SkillManager
mgr = SkillManager()
mgr.discover(["./skills"], include_defaults=False)
# Edit ./skills/greeting/SKILL.md on disk here…
diff = mgr.reload()
# {"added": [], "changed": ["greeting"], "removed": []}Skills added via add_skill() are preserved
A skill registered out-of-band with add_skill() was never part of a discovery scan, so reload() leaves it untouched and never lists it under removed.
mgr.add_skill("./extra/beta") # outside the discovered dirs
diff = mgr.reload()
assert "beta" not in diff["removed"] # preservedTelemetry-only rewrites are ignored
Recording a use rewrites SKILL.md frontmatter, but the manager refreshes its own mtime baseline afterwards — so the next reload() does not report the skill as changed, and the same activated object is kept.
mgr.get_instructions("used") # bumps use-count on disk
diff = mgr.reload()
assert diff["changed"] == []Removed skills are deactivated; unchanged keep activation
A deleted skill directory is dropped and its cached instructions cleared. Unchanged skills keep the same LoadedSkill instance, so activation and telemetry carry across the reload.
reload() works with no prior discover()
Calling reload() before any explicit discover() still returns a valid diff with the three keys — it simply scans the default sources.
mgr = SkillManager()
diff = mgr.reload()
# {"added": [...], "changed": [], "removed": []}Common Patterns
Refresh after a skill install from a bot or gateway
from praisonaiagents import SkillManager
mgr = SkillManager()
mgr.discover(["./skills"], include_defaults=False)
# A gateway handler just ran `praisonai skills install <url>`…
diff = mgr.reload()
if diff["added"]:
print("New skills live:", ", ".join(diff["added"]))Watch-and-reload while authoring a skill
import time
from praisonaiagents import SkillManager
mgr = SkillManager()
mgr.discover(["./skills"], include_defaults=False)
while True:
diff = mgr.reload()
if any(diff.values()):
print(f"+{diff['added']} ~{diff['changed']} -{diff['removed']}")
time.sleep(2)Reload and confirm a specific skill is live
diff = mgr.reload()
if "refund-policy" in mgr:
print("refund-policy is now available")Best Practices
Call reload() at turn boundaries
Reload is safe by design — the index swap never takes effect mid-turn — but calling it between turns keeps the mental model simple: the next to_prompt() reflects the change.
Report the diff to the user
Surface {added, changed, removed} after a reload so people see exactly what moved: +1 added / ~1 changed / -0 removed.
Reload in-process, not from a subprocess
praisonai skills reload scans disk in its own process and cannot refresh a parent session. To pick up new skills in a running agent, call SkillManager.reload() in that same process.
Keep skill names stable across edits
Change detection keys on the skill name and its SKILL.md mtime. Renaming a skill reads as a removed + added pair and drops its telemetry — prefer editing content over renaming so usage counts survive.