# How the Skills Service Uses DefaultResourceLoader for Settings Paths and Project Skills

> Learn how the Pi Web skills service leverages DefaultResourceLoader to find settings paths and project skills from settings.json, npm packages, and the .agents/skills folder. Discover skill locations efficiently.

- Repository: [Alex Yang/pi-web](https://github.com/agegr/pi-web)
- Tags: internals
- Published: 2026-08-16

---

**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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/settings.json) / [`settings.yaml`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/app/api/skills/route.ts). This Next.js API route validates the request before delegating to the service layer.

```ts
// 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`](https://github.com/agegr/pi-web/blob/main/lib/skills-service.ts).

## Skills Service: Initializing and Reloading the Loader

The `loadSkillsWithInstallInfo` function in [`lib/skills-service.ts`](https://github.com/agegr/pi-web/blob/main/lib/skills-service.ts) orchestrates the `DefaultResourceLoader` lifecycle. It constructs the loader, applies trust configuration, and extracts annotated results.

```ts
// 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:

1. **Construction** — `new DefaultResourceLoader({ cwd, agentDir })` binds the loader to both global (`agentDir`) and project-specific (`cwd`) filesystem roots
2. **Trust Application** — `loader.reload()` consumes options from `projectTrustReloadOptions()` to enforce security policies without breaking runtime parity
3. **Skill Extraction** — `loader.getSkills()` returns `SkillInfo` objects with embedded path metadata
4. **Metadata Enrichment** — `annotateSkillsWithInstallInfo` adds installation status, version data, and origin tracking

## Project Trust and Security Boundaries

The reload options originate from [`lib/project-trust.ts`](https://github.com/agegr/pi-web/blob/main/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/skills` and full [`settings.json`](https://github.com/agegr/pi-web/blob/main/settings.json) inheritance
- **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

```ts
// 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:

```ts
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`](https://github.com/agegr/pi-web/blob/main/app/api/skills/route.ts) | HTTP endpoint, request validation, security checks |
| [`lib/skills-service.ts`](https://github.com/agegr/pi-web/blob/main/lib/skills-service.ts) | Loader instantiation, reload orchestration, response assembly |
| [`lib/project-trust.ts`](https://github.com/agegr/pi-web/blob/main/lib/project-trust.ts) | Trust policy definitions and status queries |
| [`AGENTS.md`](https://github.com/agegr/pi-web/blob/main/AGENTS.md) | Architecture documentation noting runtime parity guarantees |

## Summary

- The **Pi Web skills service** uses `DefaultResourceLoader` to unify skill discovery across global, packaged, and project-local sources
- **Construction** requires `cwd` and `agentDir`; **reload** applies trust constraints from `projectTrustReloadOptions`
- The API at `/api/skills` enforces 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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/package.json) entry points.