Lesson 10: The Code Map: Navigating the Project Structure
In one sentence: DSH is a monorepo —
apps/holds the entry points (cli, web, acp),packages/holds all the capabilities (each package is a swappable plugin brick), anddocs/is the manual; whatever capability you are looking for, go to the package with the matching name underpackages/xxx, and with the "ctx key → package → responsibility" table indocs/architecture.zh.mdplus themodule-graphdependency graph, the whole repo becomes navigable like a map.
1. User Story: The First Day with the Repo
Little D cloned the DSH source repo and stood in front of the root directory, a bit lost: a pile of files and directories at the root, with no idea where to start. He really only had two questions:
- "How does DSH actually run? Where is the entry point?"
- "I want to swap out a capability of the agent (for example, change the sandbox or add a search tool). Which directory should I go to?"
A veteran only told him one sentence: **First recognize the three top-level directories — apps, packages, docs; then remember one navigation rule — whatever capability you want, go find the package with the matching name under packages. Then he pointed him to a table in docs/architecture.zh.md, and both questions were solved.
This lesson expands on "that one sentence from the veteran" and explains it clearly. After reading it, when you face this repo you will know at least three things: where the entry point is, where the capabilities are, and how to look up dependencies.
2. The Monorepo Panorama: apps Is the Entry, packages Is the Capability, docs Is the Manual
First, look at the repo root (source: ls of the repo root). The top level is not a pile of scattered files but three blocks with clear divisions of labor:
| Top-level directory | Role | What's inside |
|---|---|---|
apps/ | Entry point (things that can be started) | cli (the dsh command itself), web (the browser side of the Web UI); the automation entry point ACP lives at packages/acp and can be started with pnpm run demo:acp (source: root README.zh.md) |
packages/ | All capabilities (plugin bricks) | 200+ packages spread across 49 "capability groups", e.g. core/, shell/, sandbox/, skill/, web/, llm/ |
docs/ | Manual (architecture, tutorials, maps) | architecture.zh.md, module-graph.md, graph-atlas.md, tool-catalog.md, etc. |
apps 是入口,packages/core 是默认流程,其余全是可替换的能力插件
How to read it: apps/ decides "how to start", packages/ decides "what capabilities exist", docs/ decides "where to look things up".
The three entry points each have their own job:
- Command line:
apps/cliis thedshcommand itself. The docs define it as "the product launcher for profiles" (source:apps/cli/README.zh.md) —dsh web,dsh --profile headless "task"are all parsed by it, which then starts plugins combined per profile. - Web UI:
apps/webis the browser side (a Vite project, source:apps/web/directory), working together withpackages/host(the GUI host half: API gateway + HTTP routing) andpackages/client(the browser half: shell, protocol layer,ui-*plugins). - Automation:
packages/acpis an ACP (Agent Client Protocol) server "for automation only" (source:packages/acp/README.zh.md), exposing agents to programmatic clients through a standardized protocol.
🎁 Analogy: apps are the "power button", packages are "the ingredients in the fridge", docs are "the recipe book". Press the power button, pick ingredients, consult the recipe — the three things never interfere with each other. This is exactly what "everything is a plugin" looks like projected onto the directory structure.
3. Two Navigation Lines: core's Default Flow + the Seams of Capability Families
3.1 The First Line: packages/core Is the Default Flow
Once inside packages/, the first directory to recognize is core/. The package index doc calls it the "product API backbone": session logs, system prompt assembly, the tool registry, agent vocabulary, default model selection, the concrete loop — "constituting the harness's default control backbone" (source: packages/core/README.zh.md). It contains only 7 packages:
| Packages in core | Responsibility (ctx key in parentheses) |
|---|---|
scope/ | Scope context registration primitives (library, no ctx key) |
session/ | Event-sourced session logs and in-memory storage (ctx.sessions) |
system-prompt/ | Registry for assembling prompts and tool schemas (ctx.systemPrompt) |
tools/ | Scoped tool registry and execution pipeline (ctx.tools) |
agent/ | Agent interface, registry, and event vocabulary (ctx.agents) |
agent-default-model/ | Default model selection shared by agent entry points (ctx.agentDefaultModel) |
agent-loop/ | Default concrete agent driver (ctx.agentLoop) |
A one-sentence memory hook: core is the minimal skeleton that gets an agent running — session, prompts, tools, agent, model, loop, all right here. The concepts from previous lessons land on these 7 packages in the code.
3.2 The Second Line: All Other packages Are "Swappable Capability Families" (Seams)
Beyond core there are dozens of packages, which are not the core flow but capabilities the core flow can plug in and out. The architecture doc puts it this way: "packages/core/ brings together the default flow; the individual capabilities still exist as plugins" (source: docs/architecture.zh.md). Each capability is a "seam": the capability definition, provider, and consumer are separated, and any end can be replaced on its own (echoing "capability as seam" from Lesson 1).
| Capability family (seam) | What it does (source: the hierarchy table in packages/README.zh.md) |
|---|---|
llm/ | LLM capability family: abstract services + provider adapters |
shell/ | Bash capability family: executor seam, local/sandboxed/PowerShell implementations, model-facing tools |
terminal/ | Persistent PTY family: owner-isolated sessions, local implementation, and terminal_* tools |
code-runtime/ | Code execution family: Service Definition + worker-thread provider + the Code Mode consumer |
sandbox/ | Process confinement seam: bwrap / Landlock / Seatbelt backends |
fs/ | Filesystem: seam, local implementation, model-facing file tools, discovery tools backed by packaged ripgrep |
lsp/ | LSP semantic navigation: seam, generic stdio provider and lsp tool |
skill/ | Skills: provider registry, filesystem provider, catalog and loader |
web/ | Web capabilities: search and fetch provider implementations, model-facing Web tools |
subagent/, workflow/, jobs/, goal/, schedule/ | Collaboration and task management (delegation, multi-agent orchestration, background jobs, persistent goals, in-session scheduling) |
session/, session-query/ | Persisted session data plane, session retrieval |
storage/, spill/, attachment/ | Non-session storage hub, oversized tool-output spill, durable attachments |
typert/, api/, sdk/, acp/, mcp/ | The outward protocol surface: type-graph RPC, BFF gateway, JSON-RPC SDK, ACP server, MCP client |
host/, client/ | The host half and the browser half of the Web GUI |
The navigation rule is hidden in this table: whatever capability you want, go find the package with the matching name under packages/ — want to swap the sandbox? packages/sandbox. Want to add search? packages/web. Want to study skill loading? packages/skill. The name is the index.
3.3 Table-Based Navigation: A Three-Layer Index (Architecture Doc → Group README → Subsystem Page)
The directory names alone are not enough — a single capability group may hold several services. DSH now splits "looking things up" into three layers, each owning one segment:
Layer 1: the "Core packages" table in docs/architecture.zh.md. It keeps only the seven spine rows, answering "who is the minimum needed to run an agent":
| Package | Owns | ctx key |
|---|---|---|
core/session | The append-only SessionEvent log and in-memory store | ctx.sessions |
core/system-prompt | Prompt-section and tool-schema assembly | ctx.systemPrompt |
core/tools | The scoped tool registry and guarded execution pipeline | ctx.tools |
core/agent | The Agent interface, live registry, and agent/* events | ctx.agents |
core/agent-loop | The default driver implementing that interface | ctx.agentLoop |
core/scope | The per-agent scoped-registration primitive | library, no ctx key |
llm/llm | Message and stream vocabulary plus the adapter seam | ctx.llm |
Layer 2: the group README is the authority for ctx-key mapping. The package index doc says it plainly: "the group README is responsible for the package/ctx key mapping" (source: packages/README.zh.md). So to find which packages a capability has and which ctx key each hangs on, open packages/<group>/README.zh.md rather than going back to the architecture doc. The common ones:
| ctx key | Package group | Responsibility |
|---|---|---|
ctx.shell | shell/ | Foreground command execution and background process starts |
ctx.terminals | terminal/ | Owner-isolated persistent PTY sessions |
ctx.jobs | jobs/ | The kind-agnostic background-job registry |
ctx.sandbox | sandbox/ | Restricting processes via argv wrapping and per-call policies |
ctx.fs | fs/ | Executing-world paths, bounded I/O, and policy events |
ctx.skills | skill/ | Skill provider registry and progressive disclosure |
ctx.web | web/ | Search and fetch provider registry |
ctx.subagents | subagent/ | Named delegation providers |
ctx.workflowEngine | workflow/ | Script-driven multi-agent orchestration |
ctx.compaction | compaction/ | When to compact history and how to summarize it |
ctx.codeRuntime | code-runtime/ | Running model-written programs (the backend of Code Mode) |
ctx.sessionQuery | session-query/ | Bounded reads and retrieval over the session corpus |
ctx.storage / ctx.spillStore | storage/ / spill/ | Non-session storage hub / oversized-output spill |
Layer 3: the subsystem pages under docs/subsystems/. There are now 40-odd of them, one per capability (shell.md, terminal.md, jobs.md, code-runtime.md, session-query.md, spill.md, typert.md, …), each carrying a generated Cordis API region — go straight here when you want a capability's complete types and events.
💡 One naming convention that saves a lot of guessing: the repository now has an explicit naming contract — a singular ctx key means one engine / runtime / policy / controller; a plural ctx key means a registry, and the class's role name must agree with the key's number. So
ctx.workflowEnginetells you it is an engine (not a registry), whilectx.terminals,ctx.agents, andctx.jobstell you they own many named members. In the same spirit,localis used only when same-host execution is part of the contract — which is why the fetch implementation isweb-fetch-http(named by protocol) rather thanweb-fetch-local, and the LSP provider islsp-stdio(named by transport) rather thanlsp-local.
How to use it:
- Forward lookup: you see
ctx.tools,ctx.sandboxin plugin code and want to know which package implements it → look up the package group by ctx key → read the group README → go todocs/subsystems/<capability>.mdwhen you need detail. - Reverse lookup: you want to replace a capability → find the package group in the table → go to the
packages/<group>/<pkg>directory, reading the group's README first and then individual packages.
4. Reading Dependencies from Graphs: module-graph and graph-atlas
The last tool in the kit is graphs. The docs put it plainly: the graphs under docs/ "form a relationship layer above the generated directories" — to figure out dependencies between packages, you don't need to dig through code by hand, just look at the graphs (source: docs/graph-atlas.zh.md).
docs/module-graph.md (module dependency graph): automatically generated by a tool from each package's peerDependencies (the canonical runtime dependency signal), grouped hierarchically by packages/<group>/<pkg>; each edge a --> b means "package a depends on package b" (source: docs/module-graph.zh.md). Note how to read it: the package names in the graph have the @deepseek-ai/dsh- prefix stripped. To regenerate, run pnpm run gen-module-graph — and CI has a "freshness gate": commits are blocked when the graph is stale (source: the dependencies section of packages/README.zh.md).
docs/graph-atlas.md (atlas of doc graphs): a list of graphs that organizes the scattered graphs into an "atlas": the module dependency graph, the tool schema catalog with package mappings (tool-catalog.md), capability seams and core services (capability-seams.md), application composition graph, the event producer/consumer matrix, the agent turn and step lifecycle, the tool execution pipeline (source: docs/graph-atlas.zh.md).
💡 Practical tip: before changing
packages/fs, first check who depends on it in the module graph — if bothbashandsandboxpoint to it, you know the blast radius of your change; if you want to know "which steps exactly does an agent go through in one turn", look at the agent lifecycle graph. The atlas is the catalog for "finding graphs"; module-graph is the graph for "finding dependencies".
Key Points Recap
- Three top-level directories:
apps/is the entry point (cli, web, acp),packages/is all the capabilities,docs/is the manual. packages/coreis the default flow: the seven-piece set of scope, session, system-prompt, tools, agent, agent-default-model, agent-loop is the minimal skeleton that gets an agent running.- All other packages are swappable capability families (seams): navigation rule = whatever capability you want → go find the package with the matching name under
packages/xxx; the name is the index. - Table-based navigation (three layers): the "Core packages" table in
docs/architecture.zh.mdcovers the seven spine rows; the group README is the authority for the package/ctx key mapping;docs/subsystems/<capability>.mdgives one capability's complete types and events. - Reading dependencies from graphs:
module-graph.mdshows package dependencies (tool-generated, kept fresh by CI);graph-atlas.mdis the index of these graphs.
🚀 Starting next lesson, we go deep into the code with this map in hand: beginning with
packages/core/agent-loop— how the default loop actually "spins up".
Self-Test · Code Map
Answer each question, then submit to check your result.
