godot-docs — offline Godot documentation tools for DSH
A DSH (DeepSeek Harness) plugin that turns a local
checkout of the official Godot documentation into agent tools. Instead of relying on
model memory (which is usually Godot 3.x or an older 4.x), the agent answers Godot API and
engine questions from the documentation that matches the project's engine build, and quotes
the page and line it used.
Repository: https://github.com/TheoiNabiy/godot-docs-dsh-plugin · 中文文档见
README.zh-CN.md
docs tree ──▶ index (classes, members, body tokens) ──▶ godot_docs_* tools ──▶ the agent
Why
- Correct version, not remembered version. The docs in the tree are the authority for
this project. The plugin detects the documented revision (e.g.
4.7) and reports it.
- Exact signatures, not paraphrases.
godot_docs_member("CharacterBody2D","move_and_slide")
returns the real signature, defaults, flags and description, with the file line.
- Cheap to consult. Search is 25–90 ms per query over 1 600 pages / 32 MiB of RST.
- Offline. No network access at any point; the docs tree is indexed from disk.
Tools
| Tool | Purpose |
|---|
godot_docs_search | Ranked full-text search over the tree (all / classes / tutorials), returns an excerpt and a root-relative path#Lline. Results on a conventions page are flagged with convention_topic. |
godot_docs_class | Class reference lookup: inheritance chain (both directions — ancestors and direct subclasses), description, tutorials, enum members with their values, and the full member map with exact line numbers |
godot_docs_member | One member in full: signature, arguments, defaults, description, file line (kind:name forces a kind). Inherited members resolve through the chain and report declared_on; enums list every value; constants report theirs. |
godot_docs_find_member | Reverse lookup: which classes declare a member with this name (signal:body_entered → every class that has it) |
godot_docs_project_settings | Look up documentation for a setting or a whole branch (debug/gdscript/warnings/ lists every GDScript warning), or validate a real project.godot and report keys that are not documented |
godot_docs_get | Read a page or one section of it as cleaned, line-numbered text (path#L120-L180, path#Method Descriptions) |
godot_docs_code | Extracted documentation code samples (gdscript, csharp, glsl, cpp, …) to adapt instead of inventing API calls |
godot_docs_conventions | The official Godot conventions for a topic (GDScript style, project/scene organisation, static typing, exports, autoloads, signals, warnings, C# style and API differences, shader style, file paths, i18n, performance, threads, editor plugins, …) together with the page that defines it |
godot_docs_project_search | Literal/regex search over the current project's own sources — see how an API is already used, find every call site, check whether a recommended pattern is followed |
godot_docs_api_check | Validate identifiers (Class, Class.member), a pasted GDScript/C# snippet, or whole project directories — scripts and .tscn/.tres scenes and resources — reporting use sites (file:line) for names that do not exist in this docs version |
godot_docs_status | Which docs trees are indexed, the detected docs version, page/class counts, cache state, probes and resolution errors |
godot_set_docs_path | Persist the docs tree location into the plugin configuration (applies immediately) |
The plugin also installs three system-prompt sections, so an agent in this session knows to
consult the docs first, how to wire up the docs path, and — importantly — to follow the
engine's own conventions rather than another engine's habits or remembered advice.
Conventions, not just API
The docs tree already contains this project's coding standards. godot_docs_conventions
maps a topic or alias to the authoritative page (and reports whether that page exists in
this tree), godot_docs_search flags convention pages in its results, and the
godot-docs:conventions prompt section states the rules of thumb the docs establish:
GDScript style guide naming and code order, static typing, @export for tunables, signals
instead of polling, shallow node trees split into scenes, %UniqueName, restraint with
autoloads, writing to user:// rather than res:// at runtime, keeping translatable text
in tr() without concatenation, handing work back to the main thread instead of calling
engine APIs from threads, and not ignoring engine warnings.
40 topics are catalogued, each verified to exist in the docs tree by the test harness:
GDSCRIPT style/typing/exports/doc-comments/warnings/format strings, C# style, C# API
differences and C# features, project/scene organisation, scenes versus scripts, autoloads,
signals, groups, resources, nodes versus objects, logic versus data, data preferences,
interfaces, notifications, idle versus physics processing, overridable functions, pausing,
input, multiple resolutions, file paths, i18n, performance, thread-safe APIs, shader style,
editor plugins, unit testing and more.
Checking real code
godot_docs_api_check(paths=[...]) walks a project's own scripts and scenes and reports
every reference that is not documented for this engine version, with each use site
(file:line). It is deliberately conservative — a false positive costs more than a missed
finding — so it only asserts what it can prove.
For scripts:
Class.member on a documented class, with members resolved through the inheritance
chain (Node.get_property_list is found on Object; TCPServer.new is a language
constructor, not a missing method);
- members of a variable whose type is provable: an
is check, an as cast, a constructor,
or a documented return type;
- never: string literals (
"res://levels/Node2D.tscn"), container keys (Dictionary /
Array property bags), variables shadowed with :=, self./super. members, or
GDScript language built-ins.
For .tscn/.tres scenes and resources:
- node types must be documented classes (
type="Sprite2Dxx" is reported);
- node and sub-resource properties are checked against the node's class, so a typo'd or
renamed property is caught without ever running the editor;
- properties declared by the node's own script —
@export var max_health — are resolved from
the project's scripts, including inherited ones (extends Entity), and so are the
exports of an instanced scene's root script;
- signal connections are checked against the resolving node's class;
- the format/editor furniture that is not class API is recognised and counted, not reported:
layout_mode, anchors_preset, theme_override_*, metadata/*, block headers;
- the
.tres root [resource] block is checked against its gd_resource type.
godot_docs_project_settings(path=<project dir>) does the same for project.godot: it
reports every key that is not documented for this engine version, with its file line and
close matches. config_version and application/config/features (engine-written) plus the
user-defined [input], [autoload], [layer_names] and [editor_plugins] sections are
recognised as structure rather than API.
Across the author's seven real Godot projects the script checker reports about 2 findings per
6 000 API usages, and both were genuine mistakes (Camera3D.get_environment(), which only
exists on World3D, and ThemeDB.get_fallback_font(), which is a property rather than a
method).
Which project a call searches
Tools that read project files take the session workspace as their base: an omitted
path/paths starts there, a relative entry resolves against it, and a project.godot at it
or in one of its parents is what names the project. The host process's own directory is never
used for this — in the desktop app it is the DSH profile directory, and a search that silently
scanned ~/.dsh/profiles/<profile> used to answer "no matches" while calling that the project.
When no project.godot exists at or above the directory that was searched, the answer says so
instead of guessing: godot_docs_project_search returns found_project_godot: false,
resolved_by: "start" and a hint that the root is a fallback, and godot_docs_api_check marks
the path in its summary. That case is normal for a docs tree or a loose script folder; pass
path= explicitly to search a real project.
Install
The plugin is a standard DSH bundle (ESM plugin + cordis.patch.yml). From the directory
that contains this package.json:
dsh plugin --profile <profile> add <path-or-git-url>
For this repository the git URL is:
dsh plugin --profile <profile> add git+https://github.com/TheoiNabiy/godot-docs-dsh-plugin.git
or add it to the profile's package.json dependencies and dsh.profile.bundles by hand,
then boot the profile. The bundle layer registers the row:
- insert:
- id: tool-godot-docs
name: godot-docs
The docs tree can also be pinned in the profile's cordis.patch.yml (or via
godot_set_docs_path, or the docsPath field on the plugin card):
- id: tool-godot-docs
name: godot-docs
config:
docsPath: D:\Godot\godot-docs-master
Where the docs come from
Any of these works, and the first one that resolves wins:
- the
docs_path argument on a tool call,
- the configured
docsPath (several trees may be listed, separated by ; or newlines),
- the session workspace and its parents (so a docs checkout opened as the workspace is
picked up with no configuration at all),
- conventional locations (
~/godot-docs, ~/Documents/godot-docs, C:/godot/godot-docs,
/usr/local/share/godot-docs, /opt/godot-docs).
A directory qualifies as a docs tree when it contains classes/class_node.rst.txt — i.e.
the godot-docs repository checkout, or the same tree exported as *.rst.txt. An explicit
docs_path that names no tree is deliberately authoritative: the tools explain instead of
silently answering from an unrelated checkout.
Index and cache
The first tool call builds the index (~2.5 s for 1 600 pages): per-page metadata (title,
inherited classes, description, headings, plain-text length), every .. _class_X_method_y:
member anchor, and a body-token inverted index. It is then persisted as JSON
(~/.dsh/godot-docs-cache/index-<hash>.json, ~18 MiB) and revalidated by page count +
newest mtime, so later sessions load it in ~70 ms. If no cache location is writable, the
index is simply rebuilt per process and everything keeps working.
Configuration
| Field | Type | Meaning |
|---|
docsPath | string | Docs tree(s) to index. Volatile on DSH ≥ 0.2, so the GUI form edits it in place. |
Indexed data
Everything the tools answer comes from one index built out of the docs tree on first use
(~2.5 s for 1 600 pages) and cached as JSON (~/.dsh/godot-docs-cache/index-<hash>.json,
~30 MiB, reloaded in ~170 ms):
- per-page metadata: title, inheritance chain, description, section headings, plain-text length;
- 25 340 member anchors over 1 078 classes (properties, methods, signals, constants,
constructors, operators, annotations, private callbacks, theme items) with exact line numbers;
- 788 enums with 5 601 values (including dotted names such as
Variant.Type);
- the inherited-member map, so a member that a class only inherits still resolves;
- 941 documented project settings with their keys and lines;
- a body-token inverted index for search.
Two lookups are derived on demand instead of being stored, because they cost ~20 ms and would
otherwise grow the cache: the reverse member index (godot_docs_find_member) and the
subclass map (godot_docs_class).
Development
node test/run.mjs [docsRoot] # 39-check harness: registers tools, exercises every tool
node test/bench.mjs [docsRoot] # index build / cache / search timings
test/node_modules holds minimal stubs for @deepseek-ai/dsh-tools and
@deepseek-ai/schemastery, and test/hooks.mjs redirects those two imports so the plugin
can be booted outside the harness.
License
MIT.