How the Skills Service Uses DefaultResourceLoader for Settings Paths and Project Skills
The Pi Web skills service instantiates DefaultResourceLoader with the current working directory and agent directory, then reloads it with project-trust options to discover skills from settings.json, npm packages, and the .agents/skills project folder.
The Pi Web backend provides a Skills API at /api/skills that exposes available skills and their installation metadata. This API relies on the DefaultResourceLoader class from the @earendil-works/pi-coding-agent package to unify skill discovery across global configuration, package installations, and project-local definitions. Understanding how this loader works is essential for debugging skill visibility issues or extending the Pi Web platform.
How DefaultResourceLoader Discovers Skill Sources
The DefaultResourceLoader aggregates skills from three distinct locations. Each source follows a predictable resolution path that mirrors the runtime's own startup behavior.
| Source | Discovery Mechanism |
|---|---|
settings.json / settings.yaml |
Loader reads model and skill configuration from the agent's global directory |
| Package-installed skills | Scans node_modules for packages with a pi-agent-skills entry point |
| Project-local skills | Resolves .agents/skills folder within the project tree as a trusted root |
This three-tier approach ensures that skills can be defined globally, distributed via npm, or maintained per-project without conflicts.
The Skills API Route Implementation
The entry point for skill discovery is app/api/skills/route.ts. This Next.js API route validates the request before delegating to the service layer.
// app/api/skills/route.ts – GET handler
export async function GET(req: Request) {
const { searchParams } = new URL(req.url);
const cwd = searchParams.get("cwd");
if (!cwd) return NextResponse.json({ error: "cwd required" }, { status: 400 });
// Verify the cwd is allowed for the current session
const allowedRoots = await getAllowedFileRoots();
if (!isExistingFilePathAllowed(cwd, allowedRoots)) {
return NextResponse.json({ error: "Access denied" }, { status: 403 });
}
// Delegate to the service that uses DefaultResourceLoader
return NextResponse.json(await loadSkillsWithInstallInfo(cwd));
}
The route enforces two security constraints: the cwd parameter must be present, and the path must appear in the session's allowed roots. Only then does it invoke loadSkillsWithInstallInfo from lib/skills-service.ts.
Skills Service: Initializing and Reloading the Loader
The loadSkillsWithInstallInfo function in lib/skills-service.ts orchestrates the DefaultResourceLoader lifecycle. It constructs the loader, applies trust configuration, and extracts annotated results.
// lib/skills-service.ts
export async function loadSkillsWithInstallInfo(cwd: string): Promise<SkillsResponse> {
const agentDir = getAgentDir(); // ~/.agents
const loader = new DefaultResourceLoader({ cwd, agentDir });
// Apply project-trust configuration (e.g., "restricted" vs. "trusted")
await loader.reload(projectTrustReloadOptions(cwd, agentDir));
// Pull the discovered skills and any diagnostics
const { skills, diagnostics } = loader.getSkills();
return {
skills: annotateSkillsWithInstallInfo(skills as SkillInfo[], { cwd, agentDir }),
diagnostics,
projectResourcesLoaded: getProjectTrustStatus(cwd, agentDir).trusted,
};
}
Four operations define the workflow:
- Construction —
new DefaultResourceLoader({ cwd, agentDir })binds the loader to both global (agentDir) and project-specific (cwd) filesystem roots - Trust Application —
loader.reload()consumes options fromprojectTrustReloadOptions()to enforce security policies without breaking runtime parity - Skill Extraction —
loader.getSkills()returnsSkillInfoobjects with embedded path metadata - Metadata Enrichment —
annotateSkillsWithInstallInfoadds installation status, version data, and origin tracking
Project Trust and Security Boundaries
The reload options originate from lib/project-trust.ts (referenced indirectly in the service). These options distinguish between restricted and trusted project modes:
- Trusted projects allow unrestricted access to
.agents/skillsand fullsettings.jsoninheritance - Restricted projects sandbox skill discovery to prevent exfiltration or execution of untrusted code
The getProjectTrustStatus function returns a trusted boolean that the API exposes as projectResourcesLoaded, letting the frontend warn users when operating in degraded mode.
Client Integration and Usage Patterns
Fetching Skills from a React Component
// Example client call
const cwd = "/home/user/my-project";
const response = await fetch(`/api/skills?cwd=${encodeURIComponent(cwd)}`);
const data = await response.json();
console.log(data.skills); // array of SkillInfo objects
console.log(data.diagnostics); // loader warnings or errors
console.log(data.projectResourcesLoaded); // trust status boolean
Adding a Project-Local Skill
Local skills dropped into .agents/skills appear automatically on the next API call:
import { writeFileSync } from "fs";
import path from "path";
const skillPath = path.join(cwd, ".agents", "skills", "my-skill", "Skill.md");
writeFileSync(
skillPath,
"---\nname: My Skill\nversion: 1.0.0\n---\n\nYour skill description here."
);
// Subsequent GET /api/skills?cwd=... includes this skill
No rebuild or restart is required—the DefaultResourceLoader scans the directory on each request.
Key Implementation Files
| File | Responsibility |
|---|---|
app/api/skills/route.ts |
HTTP endpoint, request validation, security checks |
lib/skills-service.ts |
Loader instantiation, reload orchestration, response assembly |
lib/project-trust.ts |
Trust policy definitions and status queries |
AGENTS.md |
Architecture documentation noting runtime parity guarantees |
Summary
- The Pi Web skills service uses
DefaultResourceLoaderto unify skill discovery across global, packaged, and project-local sources - Construction requires
cwdandagentDir; reload applies trust constraints fromprojectTrustReloadOptions - The API at
/api/skillsenforces filesystem access controls before exposing loader results - Project trust status propagates to the frontend via
projectResourcesLoaded, enabling transparent security UX - Local development workflows place skills in
.agents/skills/for automatic detection without restarts
Frequently Asked Questions
What parameters does DefaultResourceLoader require?
The constructor accepts an object with cwd (project root) and agentDir (global agent configuration, typically ~/.agents). Both paths constrain where the loader searches for settings.json, npm packages, and local skill directories.
Why does the skills service call loader.reload() instead of using the constructor directly?
The constructor establishes base paths; reload() applies dynamic trust policies that may change per-request. This separation allows the same loader instance to adapt to different security contexts without reconstruction, and ensures the API matches runtime behavior exactly.
How can I verify if my project's skills are trusted?
Check the projectResourcesLoaded field in the API response. When false, the project runs in restricted mode and some skills may be filtered. Review lib/project-trust.ts to adjust trust policies for your workspace.
Where should I place custom skills for Pi Web to detect them?
Create a .agents/skills/ directory in your project root (or any Git worktree root). The DefaultResourceLoader treats this as a trusted skill source automatically. Package-installed skills should declare pi-agent-skills in their package.json entry points.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →