Graph-based long-term memory skill for AI (LLM) coding agents — faster context, fewer tokens, safer refactors
SaferSkills independently audited data-structure-protocol (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
DSP builds a dependency graph of project entities in a .dsp/ directory. Each entity (module, function, external dependency) gets a UID, description, import list, and export index. The graph answers: what exists, why it exists, what depends on what, and who uses what.
DSP is NOT documentation for humans or AST dump. It captures _meaning_ (purpose), _boundaries_ (imports/exports), and _reasons for connections_ (why).
Embed this context when working on a DSP-tracked project:
This project uses DSP (Data Structure Protocol). The .dsp/ directory is the entity graph of this project: modules, functions, dependencies, public API. It is your long-term memory of the code structure.>
Core rules:
>
1. Before changing code — find affected entities viadsp-cli search,find-by-source, orread-toc. Read theirdescriptionandimportsto understand context. 2. When creating a file/module — calldsp-cli create-object. For each exported function —create-function(with--owner). Register exports viacreate-shared. 3. When adding an import — calldsp-cli add-importwith a briefwhy. For external dependencies — firstcreate-object --kind externalif the entity doesn't exist yet. 4. When removing import / export / file — callremove-import,remove-shared,remove-entityrespectively. Cascade cleanup is automatic. 5. When renaming/moving a file — callmove-entity. UID does not change. 6. Don't touch DSP if only internal implementation changed without affecting purpose or dependencies. 7. TOC membership — new entities land in every TOC whose root scope covers their path (or pass--tocexplicitly, repeatable). Reshape membership withadd-to-toc/move-to-toc. 8. Bootstrap — if.dsp/is empty: discover roots (--new-root --scope), split files into per-TOC batches balanced by volume, then three waves over the batches in parallel — index all files, then all exports, then all imports. Each file is read exactly once (in Wave 1); later waves reuse that read.
>
Key commands: `` dsp-cli init dsp-cli create-object <source> <purpose> [--kind external] [--uid UID] [--toc TOC ...] [--new-root [--scope DIR]] dsp-cli create-function <source> <purpose> [--owner UID] [--uid UID] [--toc TOC ...] dsp-cli create-shared <exporter_uid> <shared_uid> [<shared_uid> ...] dsp-cli add-import <importer_uid> <imported_uid> <why> [--exporter UID] dsp-cli add-to-toc <uid> [<uid> ...] --toc TOC [--toc TOC ...] dsp-cli move-to-toc <uid> [<uid> ...] --from TOC --to TOC dsp-cli remove-import <importer_uid> <imported_uid> [--exporter UID] dsp-cli remove-shared <exporter_uid> <shared_uid> dsp-cli remove-entity <uid> dsp-cli move-entity <uid> <new_source> dsp-cli update-description <uid> [--source S] [--purpose P] [--kind K] [--scope DIR] dsp-cli get-entity <uid> dsp-cli get-children <uid> [--depth N] dsp-cli get-parents <uid> [--depth N] dsp-cli search <query> dsp-cli find-by-source <path> dsp-cli read-toc [--toc ROOT_UID] dsp-cli get-stats ``>
TOCis a root UID or the literaldefault(the plain.dsp/TOCfile).
The script is at scripts/dsp-cli.py relative to this skill directory.
python <skill-path>/scripts/dsp-cli.py [--root <project-root>] <command> [args]--root defaults to current working directory. All paths in arguments are repo-relative.
imports and shared/exports..dsp — code, images, styles, configs, everything.exports/ directory (reverse index).scope (directory subtree); new entities are auto-assigned to every TOC whose root scope covers their path.kind: external, no deep dive into node_modules/site-packages/etc. But exports index works — shows who imports it..dsp/.cache/ holds the reverse adjacency (imported → importers), one file per imported entity. It makes reverse and traversal commands (get-recipients, get-parents, get-path, and get-entity's "exported to") fast on large graphs without re-scanning. Local and forward commands (get-children, get-shared, read-toc, find-by-source, search) read live files and never touch it. Mutating commands keep it up to date incrementally (only the affected entries), so it stays correct as you build the graph.git checkout/pull moves the cache together with .dsp/. Caveat: a merge/rebase that touches .dsp/ can merge .cache/ files incorrectly or leave conflicts — the cache is not self-validating (it tracks only a sentinel, not content), so run rebuild-cache afterwards to be safe.python <skill-path>/scripts/dsp-cli.py rebuild-cache if .dsp/ was changed outside this CLI — hand-edited files, or a merge/rebase that touched .dsp/. Agents that follow the protocol mutate .dsp/ solely through dsp-cli, so during normal work this is rarely needed.obj-<8 hex> (e.g., obj-a1b2c3d4)func-<8 hex> (e.g., func-7f3a9c12)UID marker in source code — comment @dsp <uid> before declaration:
// @dsp func-7f3a9c12
export function calculateTotal(items) { ... }# @dsp obj-e5f6g7h8
class UserService:Core economy rule: each file is read exactly once, by exactly one subagent — all three waves run on top of that single read. Batches run in parallel; the only sync point is the barrier before Wave 3.
dsp-cli init to create .dsp/ directory.create-object <path> <purpose> --new-root --scope <dir> (. = whole repo). Scopes make TOC assignment automatic for everything that follows.git ls-files | xargs wc -c; skip vendored code, build output, lock files). Group by TOC, split each group into batches of roughly equal volume — one batch per subagent, dispatched in parallel.create-object + create-function --owner for significant inner entities, @dsp markers in source.create-shared per file (batch-local, no waiting on other batches).create-object --kind external + add-to-toc for other roots using it).add-import with usage-based why (dead imports were already filtered at the Wave 1 read); targets resolve via find-by-source.get-stats, get-orphans, detect-cycles; every inventory file resolves via find-by-source. Details: bootstrap.md.Re-indexing a project whose code already has @dsp markers: pass the old UIDs via --uid at every create step — the graph is rebuilt with stable identity.
dsp-cli create-object <path> <purpose> — it lands in every TOC whose root scope covers the path (override with --toc <TOC>, repeatable)dsp-cli create-function <path>#<symbol> <purpose> --owner <module-uid>dsp-cli create-shared <module-uid> <func-uid> [<func-uid> ...] — shared entries are UIDs of existing entities, never export namesdsp-cli add-import <this-uid> <imported-uid> <why> [--exporter <module-uid>] — all UIDs must already existdsp-cli create-object <package-name> <purpose> --kind externaldsp-cli find-by-source <path>dsp-cli search <query>dsp-cli read-toc → get all UIDs, then get-entity for detailsdsp-cli get-children <uid> --depth Ndsp-cli get-parents <uid> --depth Ndsp-cli get-recipients <uid> — who depends on this entitydsp-cli get-path <from> <to>dsp-cli update-description <uid> --purpose <new>dsp-cli move-entity <uid> <new-path>dsp-cli update-import-why <importer> <imported> <new-why>dsp-cli update-description <root-uid> --scope <dir>dsp-cli add-to-toc <uid> [<uid> ...] --toc <TOC> (idempotent; e.g. an external used by a second root)dsp-cli move-to-toc <uid> [<uid> ...] --from <TOC> --to <TOC> — all-or-nothing; a root cannot leave its own TOC<TOC> is a root UID or defaultdsp-cli remove-import <importer> <imported> [--exporter UID]dsp-cli remove-shared <exporter> <shared>dsp-cli remove-entity <uid> (cascading cleanup)dsp-cli detect-cycles — circular dependenciesdsp-cli get-orphans — unused entitiesdsp-cli get-stats — project graph overview| Code Change | DSP Action |
|---|---|
| New file/module | create-object + create-function + create-shared + add-import |
| New import added | add-import (+ create-object --kind external if new external dep) |
| Import removed | remove-import |
| Export added | create-shared (+ create-function if new function) |
| Export removed | remove-shared |
| File renamed/moved | move-entity |
| File deleted | remove-entity |
| Purpose changed | update-description |
| Module moved to another subproject/root | move-to-toc (+ move-entity if the path changed) |
| Entity now used by another root | add-to-toc |
| Internal-only change | No DSP update needed |
.dsp/ directory structure, file formats, TOC~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.