Projects¶
See also: Skills and Hooks (what gets installed into a project), Repositories (where installed skills come from), and MCP Servers (MCP installs use the same per-project targets).
Overview¶
A project is a tracked folder on disk - nothing more. Its record is
{ id, path, name, addedAt }. Adding a project only stores that record;
SkillKeeper never writes anything into the folder just because it was added.
Removing a project only drops the record; the folder and every file in it
are left untouched.
Projects give the desktop app a project scope for skill and MCP installs -
an install "for project P, agent A" writes into a directory inside P (see
the per-agent skills-root table in
Skills and Hooks),
as opposed to global scope, which writes into the user's home directory
and is not tied to any project.
Adding, editing, and removing projects¶
Adding a project checks for a duplicate path first (the same folder cannot be tracked twice) and then, immediately after recording it, runs both skill and MCP reconciliation against it - so a folder that already contains SkillKeeper-managed skills or MCP ledgers (for example, cloned from a teammate's Git history) is adopted right away instead of waiting for the next app-wide reconcile.
Editing a project can change its folder path and/or its display name; both are optional per edit (an empty value keeps the current one). Editing does not itself trigger reconciliation.
Removing a project drops its record only. Nothing on disk is touched, and its previously recorded skill and MCP installs simply become inert - they still exist as real files on disk, still work, and reappear if the same folder is re-added and reconciled again.
Per-project agent selection¶
A project has no single fixed "agent list" of its own. Instead, each install
independently targets (agent, scope: 'project', projectId), so a project
can have skills installed for some agents and not others, and different
skills within the same project can be installed for different agent sets.
The desktop app's project view seeds its agent picker from whichever agents
are already installed for that project (installedAgentsByProject), so
picking up where you left off does not require re-selecting agents you
already used.
This is distinct from agent detection: detectProjectAgents scans a
handful of marker files/directories in the project folder (CLAUDE.md or
.claude for Claude, AGENTS.md or .codex for Codex, .cursor or
.cursorrules for Cursor, .github/copilot-instructions.md for Copilot,
.opencode or opencode.json for OpenCode) to guess which agents have ever
been used in that folder at all - a heuristic used only for the project
card's "N agents" badge, independent of which agents actually have
SkillKeeper-managed skills installed.
Project info (card badges)¶
describeProject computes, for one project:
- skill count - the number of distinct skills installed across every
agent for that project. A skill's identity for this count is
(sourceRepoId, group, name), so a skill installed for three agents still counts once. - from-repos count - of those, how many trace back to a repository that is currently tracked (excludes unmanaged skills and skills whose source repository was removed).
- agent count - the number of agents
detectProjectAgentsfound markers for, as described above. - icon - see "Project icon" below.
Project icon¶
A project can supply its own icon, resolved from a short, fixed list of
candidate files relative to the project root, in order: icon.png,
icon.svg, .idea/icon.png, .idea/icon.svg. The first candidate that
exists and passes a safety check is returned as a data URL; otherwise the
card falls back to a generated placeholder (a tile colored from a hash of
the project's name, showing its first letter).
The safety check is defence-in-depth (the renderer also only ever displays
the result through an <img>, which does not execute scripts or fetch
external resources from an SVG):
- Files over 1 MB are rejected outright (also guards against decompression bombs).
- A
.pngcandidate must start with the real PNG file signature - a declared.pngwhose content is something else is rejected. - A
.svgcandidate is rejected if it contains a<script>tag, anon*=event-handler attribute,<foreignObject>, ajavascript:URL, a<!DOCTYPEor<!ENTITY>declaration, or an absolute/protocol-relativehttp(s)reference inhref/src(blocking both active content and remote fetches at render time).
Folder-missing handling¶
Because a project is just a path, that path can stop existing - the folder
gets deleted, moved, or is on a drive that is not currently mounted.
projectExists is the ground-truth check; the desktop app polls it for
every tracked project on startup and on a configurable interval
(projects.checkIntervalMinutes in config.yaml, 1 minute by default, 1
minute to 23 hours), and also on demand right before an action that needs
the folder (ensureProjectAvailable), surfacing a notification if it is
gone. The project card shows a distinct missing-folder indicator and, while
missing, offers only a remove action (edit is not useful when there is
nothing to point it at).
Reconciliation treats a missing folder as untouched, not empty:
- Skill reconciliation (see Skills and Hooks) explicitly checks whether each tracked project's folder exists before scanning it; when it does not, every install manifest already recorded for that project is carried forward unchanged rather than scanned (and, since nothing was found, potentially pruned).
- MCP reconciliation has no separate central record to protect in the first
place - the installed-MCP list is recomputed each time by reading each
project's own
.skmcp.yml/.skmcp.params.ymlledger files directly (there is no equivalent of the skills' persistedInstallManifestlist). A missing folder simply means those ledger files cannot be read for this pass, so that project contributes nothing to the surviving list this time - nothing on disk is deleted, and the ledger is picked up again as soon as the folder is back.
Either way, a temporarily unreachable project (an unmounted drive, a folder mid-move) never loses its recorded skill installs, and never has its MCP ledger files touched.
Reconciliation¶
Reconciliation is what keeps the desktop app's view of "what is installed" honest against the filesystem. It runs at three points:
- On app load (
loadAll), alongside loading config, repositories, and the available-skill catalog. - After a repository sync finishes (a sync can add, remove, or change the skills a project's installs should be compared against).
- After a project is added (so a folder that already has skills or MCP ledgers is adopted immediately, as described above).
Skill reconciliation is described in detail in Skills and Hooks: it rebuilds each project's install manifests from what is actually in each agent's skills directory, matching skills to tracked repositories by their (normalized) source remote, and preserving projects whose folder is currently unreachable. MCP reconciliation prunes ledger entries whose native server entry has disappeared from the agent's own config file, mirroring the same "never destroy what you cannot currently verify" posture; see MCP Servers for the ledger format it reads.
Opening a project¶
A project can be opened in the OS's own file manager, or in any editor the desktop app detects installed on the machine (offered as a list; the last choice is remembered). Before opening, the app re-confirms the folder still exists, so a stale card cannot silently open nothing.