feat(fs): add a model-facing directory listing tool

`ctx.fs.listDir` has shipped since the filesystem seam gained it, with skill
discovery as its only consumer; the model-facing tool was deferred to a
separate decision. Nothing else could answer "what is in this directory":
`rg --files` backs glob and grep and never emits a directory entry, so an
empty directory is invisible, no output says which names are directories,
and no output gives an entry count.

`list` takes an optional `path`, defaulting to the session workspace so the
common question needs no argument, and returns the direct children of one
directory with their type. Two presentation rules carry it: directories sort
first, then files, then non-regular children, each alphabetically — so
truncation loses leaves rather than the tree — and the footer always states
the complete listing's size and composition, so a capped view can never read
as a whole directory.

It emits no `fs/observed`: seeing a filename is not reading a file, and a
listing must never satisfy the read-before-write gate.
This commit is contained in:
NI0317
2026-07-28 11:59:52 +08:00
parent 5888503d5f
commit d4614f92d6
48 changed files with 761 additions and 46 deletions

View File

@@ -1449,6 +1449,8 @@ Requires: `tools` · `fs` · `systemPrompt`
```ts config-catalog
/** Plugin config (all optional — `Config` supplies the defaults). */
export interface Config {
/** Maximum entries one `list` call renders inline; the footer still reports the complete count. */
listMaxEntries?: number
/** Default and maximum number of lines returned by one `read` call. */
readLimit?: number
/** Maximum characters returned for a single line before truncation. */
@@ -1460,7 +1462,7 @@ export interface Config {
}
```
Source: [`packages/fs/tool-fs/src/index.ts:24`](../packages/fs/tool-fs/src/index.ts)
Source: [`packages/fs/tool-fs/src/index.ts:27`](../packages/fs/tool-fs/src/index.ts)
## `@deepseek-ai/dsh-tool-fs-search`

View File

@@ -20,7 +20,7 @@ This table connects model-visible tool names to the plugin package and service s
| `@deepseek-ai/dsh-plan-mode` | `exit_plan_mode` | `ctx.tools`, `ctx.systemPrompt`, `ctx.userInteraction (execution time, opportunistic)` | `tool/call`, `plan/mode inactive on an approved review`, `tool/result` | - | exit_plan_mode stays in the model-facing schema while planning is inactive so transitions add no tool-catalog churn on top of the plan-policy change. Its execute path rejects calls outside plan mode; in plan mode it presents the plan over the user-interaction seam (approve / keep planning with feedback), and approval logs plan mode inactive at the step boundary. |
| `@deepseek-ai/dsh-tool-bash` | `bash` | `ctx.tools`, `ctx.bash`, `ctx.tasks at call time for run_in_background` | `tool/call`, `tool/result` | - | The bash tool is the model-facing consumer of the bash executor seam. A `run_in_background` run registers with the generic `ctx.tasks` runtime and is collected/stopped through the `task_*` tools from `@deepseek-ai/dsh-tool-tasks`; the `enableRunInBackground` config (default true) removes the parameter entirely when disabled. |
| `@deepseek-ai/dsh-tool-cordis` | `cordis_inspect`, `cordis_mount`, `cordis_unmount` | `ctx.tools` | `tool/call`, `tool/result`, `live plugin-tree mutations (mount/unmount)` | - | Ships in examples/cordis-agent only (a deliberate opt-in — mounted code gets the real ctx, see .agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md). Plugins the model mounts may register ADDITIONAL model-visible tools at runtime; a full changed request header logs those tool-set changes. |
| `@deepseek-ai/dsh-tool-fs` | `edit`, `read`, `write` | `ctx.tools`, `ctx.fs`, `ctx.systemPrompt` | `tool/call`, `fs/write-intent or fs/edit-intent for mutations`, `fs/observed after successful file operations`, `tool/result` | - | The read-before-write/edit policy is added by `@deepseek-ai/dsh-fs-policy` (an `fs/*` event-gate plugin, no schema change); a deployment that loads these tools is expected to also load it. The tool schemas above are identical with or without the policy plugin. |
| `@deepseek-ai/dsh-tool-fs` | `edit`, `list`, `read`, `write` | `ctx.tools`, `ctx.fs`, `ctx.systemPrompt` | `tool/call`, `fs/write-intent or fs/edit-intent for mutations`, `fs/observed after successful file operations`, `tool/result` | - | The read-before-write/edit policy is added by `@deepseek-ai/dsh-fs-policy` (an `fs/*` event-gate plugin, no schema change); a deployment that loads these tools is expected to also load it. The tool schemas above are identical with or without the policy plugin. `list` enumerates one directory level through `ctx.fs.listDir` and records no observation, so seeing a filename never satisfies that read-before-write gate. |
| `@deepseek-ai/dsh-tool-fs-search` | `glob`, `grep` | `ctx.tools`, `ctx.bash`, `ctx.systemPrompt` | `tool/call`, `tool/result` | - | glob and grep are conditional bash-backed discovery tools: they register only when ctx.bash can find `rg`, then run fixed ripgrep commands through ctx.bash as ordinary foreground calls (never background tasks). Capped results save the complete formatted list through the optional ctx.spillStore backend; returned locators are follow-up-readable/searchable when the backend exposes local paths in co-located deployments. |
| `@deepseek-ai/dsh-tool-pty` | `terminal_close`, `terminal_list`, `terminal_open`, `terminal_read`, `terminal_send`, `terminal_signal` | `ctx.tools`, `ctx.pty`, `ctx.systemPrompt`, `ctx.tasks at call time for run_in_background` | `tool/call`, `tool/result` | - | The six terminal tools are opt-in and complement one-shot bash/filesystem tools. `terminal_send(run_in_background: true)` registers with `ctx.tasks`; TUI, named key sequences, BEL, resize, auto-start, and cross-agent sharing are absent from the schema. |
| `@deepseek-ai/dsh-tool-goal` | `create_goal`, `get_goal`, `update_goal` | `ctx.tools`, `ctx.agents`, `ctx.goals`, `ctx.systemPrompt`, `a calling Agent in an authorized open turn` | `tool/call`, `user/message goal snapshot for mutations`, `tool/result` | - | create, edit, pause, and resume require direct-human root authority; complete and blocked also accept the exact current goal round. The default blocked lower bound is three admitted rounds. |
@@ -316,6 +316,24 @@ Edit an existing UTF-8 text file by replacing literal text.
Source: [`packages/fs/tool-fs/src/index.ts`](../packages/fs/tool-fs/src/index.ts)
### `list`
List the direct children of one directory, with their type. Entries are directories first, then files, each alphabetical; the first 200 are returned inline and the footer reports the complete count. Unlike glob, this shows subdirectories, so it is how to see what a directory contains.
```json
{
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "Directory to list. Defaults to the session workspace; a relative path resolves against it."
}
}
}
```
Source: [`packages/fs/tool-fs/src/index.ts`](../packages/fs/tool-fs/src/index.ts)
### `read`
Read a UTF-8 text file and return line-numbered content.
@@ -371,7 +389,7 @@ Create or fully replace a UTF-8 text file.
Source: [`packages/fs/tool-fs/src/index.ts`](../packages/fs/tool-fs/src/index.ts)
The read-before-write/edit policy is added by `@deepseek-ai/dsh-fs-policy` (an `fs/*` event-gate plugin, no schema change); a deployment that loads these tools is expected to also load it. The tool schemas above are identical with or without the policy plugin.
The read-before-write/edit policy is added by `@deepseek-ai/dsh-fs-policy` (an `fs/*` event-gate plugin, no schema change); a deployment that loads these tools is expected to also load it. The tool schemas above are identical with or without the policy plugin. `list` enumerates one directory level through `ctx.fs.listDir` and records no observation, so seeing a filename never satisfies that read-before-write gate.
## `@deepseek-ai/dsh-tool-fs-search`