com.elestrago.unity.package-tools 2.0.11 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CAHNGELOG.md +8 -0
- package/Documentation~/api.md +502 -0
- package/Documentation~/manual.md +140 -0
- package/Documentation~/samples.md +73 -0
- package/Editor/Drawers/CopyEntryPropertyDrawer.cs +95 -0
- package/Editor/Drawers/CopyEntryPropertyDrawer.cs.meta +2 -0
- package/Editor/EditorConstants.cs +6 -0
- package/Editor/Inspectors/PackageManifestConfigInspector.cs +31 -0
- package/Editor/PackageManifestConfig.cs +20 -0
- package/Editor/Tools/FileTools.cs +73 -0
- package/Samples~/ClaudeSkills/unity-package-docs/SKILL.md +309 -0
- package/Samples~/ClaudeSkills/unity-package-docs/assets/README.md.template +42 -0
- package/Samples~/ClaudeSkills/unity-package-docs/assets/api-chunk.md.template +41 -0
- package/Samples~/ClaudeSkills/unity-package-docs/assets/api-index.md.template +26 -0
- package/Samples~/ClaudeSkills/unity-package-docs/assets/api.md.template +43 -0
- package/Samples~/ClaudeSkills/unity-package-docs/assets/manual.md.template +57 -0
- package/Samples~/ClaudeSkills/unity-package-docs/assets/samples.md.template +56 -0
- package/Samples~/ClaudeSkills/unity-package-docs/scripts/scan_package.py +504 -0
- package/Samples~/ClaudeSkills/unity-package-docs/unity-package-docs/SKILL.md +309 -0
- package/Samples~/ClaudeSkills/unity-package-docs/unity-package-docs/assets/README.md.template +42 -0
- package/Samples~/ClaudeSkills/unity-package-docs/unity-package-docs/assets/api-chunk.md.template +41 -0
- package/Samples~/ClaudeSkills/unity-package-docs/unity-package-docs/assets/api-index.md.template +26 -0
- package/Samples~/ClaudeSkills/unity-package-docs/unity-package-docs/assets/api.md.template +43 -0
- package/Samples~/ClaudeSkills/unity-package-docs/unity-package-docs/assets/manual.md.template +57 -0
- package/Samples~/ClaudeSkills/unity-package-docs/unity-package-docs/assets/samples.md.template +56 -0
- package/Samples~/ClaudeSkills/unity-package-docs/unity-package-docs/scripts/scan_package.py +504 -0
- package/package.json +9 -4
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: unity-package-docs
|
|
3
|
+
description: Create or update AI-first documentation for a Unity package at the repository root — README.md plus Documentation~/manual.md, api.md, samples.md, all structured for dense AI ingestion with explicit file paths and explicit mapping between the package source and the sample project. Auto-chunks large API/manual/samples by namespace/section to keep navigation intact. Use when the user wants to generate or refresh Unity package docs from source. Works in any Unity package repo with one package per repo (discovered via package.json, with a fallback to PackageManifestConfig.asset for package-tool-based projects).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# unity-package-docs
|
|
7
|
+
|
|
8
|
+
Generate a strict, AI-first documentation set for a Unity package at the **repository root** (cwd). The output is consumed primarily by AI agents (Claude, Copilot, Cursor) and secondarily by humans.
|
|
9
|
+
|
|
10
|
+
## Output contract (strict)
|
|
11
|
+
|
|
12
|
+
Files created/updated at the repository root — **never** inside the package folder:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
<repo-root>/
|
|
16
|
+
├── README.md
|
|
17
|
+
└── Documentation~/
|
|
18
|
+
├── manual.md
|
|
19
|
+
├── api.md
|
|
20
|
+
└── samples.md
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The `Documentation~` folder name MUST include the trailing tilde (Unity convention — excludes it from asset import). Pre-existing files inside `Documentation~/` (e.g. screenshots) are left untouched; only the three `.md` files listed are managed.
|
|
24
|
+
|
|
25
|
+
Every generated `.md` file begins with this exact marker on line 1:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
<!-- generated by unity-package-docs; safe to regenerate -->
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## AI-first content rules (non-negotiable, apply to every generated file)
|
|
32
|
+
|
|
33
|
+
1. **Dense over decorative.** No emojis. No marketing prose. No horizontal rules (`---`) used as visual separators. Use heading hierarchy (H1 → H2 → H3) semantically, never as styling.
|
|
34
|
+
2. **Predictable structure.** Use the exact section names and order specified in each step below. Same anchor slugs every run so agents crawling multiple package docs can rely on the shape.
|
|
35
|
+
3. **Explicit paths everywhere.** Every reference to source code uses `path/to/File.cs` relative to the repo root, with `:line` when pointing at a specific declaration. Never write "see the inspector class" — write `see Assets/Package/.../Inspectors/Foo.cs:33`.
|
|
36
|
+
4. **Complete, tagged code blocks.** Every fenced block has a language tag (` ```csharp `, ` ```json `, ` ```yaml `, ` ```bash `). Snippets are paste-ready — no `// ...` elisions that lose context.
|
|
37
|
+
5. **Explicit package ↔ sample mapping.** In `samples.md`, every sample file is mapped to the exact package type(s) it instantiates or extends, with file paths on both sides. An agent reading the docs must be able to write equivalent code in a fresh project.
|
|
38
|
+
6. **No forward references to undocumented things.** If `manual.md` mentions a type, that type appears in `api.md` with a stable anchor. `samples.md` links into `api.md` rather than re-describing types.
|
|
39
|
+
|
|
40
|
+
## Skill args
|
|
41
|
+
|
|
42
|
+
Parse from the user input (default values shown):
|
|
43
|
+
|
|
44
|
+
- `force=false` — when `true`, overwrite files even if they lack the generator marker.
|
|
45
|
+
- `dry-run=false` — when `true`, print the would-be output to the conversation without writing any files.
|
|
46
|
+
- `package=<path>` — override package-root auto-discovery (for repos with multiple packages or a non-standard layout).
|
|
47
|
+
- `chunk-threshold-lines=800` — when a rendered Documentation~ file's draft exceeds this many lines, the file is split into chunks (see Step 3.5). Below the threshold, output is identical to today's single-file form. Set very high (e.g. `100000`) to disable chunking entirely.
|
|
48
|
+
- `chunk-threshold-api=<n>`, `chunk-threshold-manual=<n>`, `chunk-threshold-samples=<n>` — per-file overrides of `chunk-threshold-lines`. Each defaults to the global value.
|
|
49
|
+
|
|
50
|
+
## Pipeline (execute these steps in order)
|
|
51
|
+
|
|
52
|
+
### Step 1 — Locate the package
|
|
53
|
+
|
|
54
|
+
From cwd, find `package.json` files via:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
find . -type f -name package.json \
|
|
58
|
+
-not -path './Library/*' \
|
|
59
|
+
-not -path './Temp/*' \
|
|
60
|
+
-not -path './Logs/*' \
|
|
61
|
+
-not -path './obj/*' \
|
|
62
|
+
-not -path './Build/*' \
|
|
63
|
+
-not -path './Builds/*' \
|
|
64
|
+
-not -path './Packages/manifest.json' \
|
|
65
|
+
-not -path './Packages/packages-lock.json'
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- **Exactly one match** → that file is the package manifest. Its containing directory is the **package root**. Read it.
|
|
69
|
+
- **Zero matches but `Assets/**/PackageManifestConfig.asset` exists** (package-tool projects pre-first-export) → use the config asset as the metadata source. The `sourcePath` field is the package root. Note in `manual.md` that the `package.json` is generated to `<packageDestinationPath>/` on export.
|
|
70
|
+
- **More than one match, or zero matches with no config** → abort. Print the matches found and ask the user to disambiguate via `package=<path>`.
|
|
71
|
+
|
|
72
|
+
### Step 2 — Read metadata
|
|
73
|
+
|
|
74
|
+
From `package.json` (preferred) or `PackageManifestConfig.asset`, extract:
|
|
75
|
+
|
|
76
|
+
| Field | `package.json` key | Config key |
|
|
77
|
+
|-------|-------------------|------------|
|
|
78
|
+
| Package name | `name` | `packageName` |
|
|
79
|
+
| Display name | `displayName` | `displayName` |
|
|
80
|
+
| Version | `version` | `packageVersion` |
|
|
81
|
+
| Description | `description` | `description` |
|
|
82
|
+
| Unity version | `unity` | `unityVersion` |
|
|
83
|
+
| Author | `author` | `author.name` |
|
|
84
|
+
| Dependencies | `dependencies` (object) | `dependencies` (array of `{packageName, packageVersion}`) |
|
|
85
|
+
| Keywords | `keywords` | `keywords` |
|
|
86
|
+
| Samples | `samples` (array of `{path, displayName, description}`) | `samples` (array of `{sourcePath, folderName, displayName, description}`) |
|
|
87
|
+
| Registry | `publishConfig.registry` | (not in config; omit) |
|
|
88
|
+
|
|
89
|
+
For the config fallback, the **sample source paths** on disk are taken from `samples[].sourcePath`. For `package.json` projects, the on-disk sample paths are inside the package dir at `<package-root>/Samples~/<folderName>` (only present if previously exported); if absent, scan `Assets/Samples/`, `Samples/`, and `Assets/Example/` as fallback candidates.
|
|
90
|
+
|
|
91
|
+
### Step 3 — Scan source
|
|
92
|
+
|
|
93
|
+
Run the bundled Python scanner. Its absolute path is `${SKILL_DIR}/scripts/scan_package.py` where `${SKILL_DIR}` is the directory containing this SKILL.md. Resolve it relative to where this skill is installed; do not hardcode.
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
python3 "${SKILL_DIR}/scripts/scan_package.py" \
|
|
97
|
+
--package "<package-root>" \
|
|
98
|
+
--samples "<sample-dir-1>" --samples "<sample-dir-2>" \
|
|
99
|
+
--repo-root .
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The scanner emits JSON to stdout with this shape:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"packageDir": "Assets/Package/PackageTool",
|
|
107
|
+
"asmdefs": [{"path": "...", "name": "...", "rootNamespace": "...", "includePlatforms": [...], "excludePlatforms": [...], "references": [...], "precompiledReferences": [...], "defineConstraints": [...], "autoReferenced": true}],
|
|
108
|
+
"namespaces": ["PackageTool", "PackageTool.Tools", ...],
|
|
109
|
+
"types": [
|
|
110
|
+
{
|
|
111
|
+
"file": "Assets/.../Foo.cs",
|
|
112
|
+
"line": 37,
|
|
113
|
+
"namespace": "PackageTool",
|
|
114
|
+
"name": "Foo",
|
|
115
|
+
"kind": "class|struct|interface|enum",
|
|
116
|
+
"modifiers": ["public", "sealed"],
|
|
117
|
+
"baseList": ["ScriptableObject"],
|
|
118
|
+
"attributes": ["CreateAssetMenu(...)", "Serializable"],
|
|
119
|
+
"summary": "...",
|
|
120
|
+
"containingType": null,
|
|
121
|
+
"members": [
|
|
122
|
+
{"line": 88, "kind": "method|property|field", "name": "Bar", "signature": "public string Bar()", "summary": "...", "attributes": ["MenuItem(\"Tools/...\")"]}
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
],
|
|
126
|
+
"samples": [
|
|
127
|
+
{"path": "Assets/.../Foo.cs", "kind": "script", "namespace": "...", "packageTypesReferenced": ["Foo", "Bar"]},
|
|
128
|
+
{"path": "Assets/.../Scene.unity", "kind": "scene|prefab", "scriptsReferenced": [{"guid": "...", "resolvedTo": "Assets/.../Foo.cs"}]}
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Capture this JSON; the rest of the pipeline consumes it.
|
|
134
|
+
|
|
135
|
+
### Step 3.5 — Plan chunking
|
|
136
|
+
|
|
137
|
+
For each Documentation~ file (`api.md`, `manual.md`, `samples.md`), decide whether to emit it as a single file or as a chunk index plus per-chunk files. Decision procedure:
|
|
138
|
+
|
|
139
|
+
1. **Render to an in-memory buffer first.** Run the full Step 4/5/6 rendering logic into a string. Count its lines (`\n`-separated, EOF-trimmed).
|
|
140
|
+
2. **Resolve the threshold** for this file: use the file-specific arg if supplied (`chunk-threshold-api`, `chunk-threshold-manual`, `chunk-threshold-samples`), else the global `chunk-threshold-lines` (default 800).
|
|
141
|
+
3. **Apply the structural floor.** Skip chunking even when the threshold is exceeded if the file is structurally unsplittable:
|
|
142
|
+
- `api.md`: chunk only when `namespaces.length > 1`.
|
|
143
|
+
- `samples.md`: chunk only when the sample mapping covers more than one file (i.e. `samples.length > 1`).
|
|
144
|
+
- `manual.md`: chunk only when at least one H2 section in the draft individually exceeds the threshold. (Splitting a single oversized section yields no benefit.)
|
|
145
|
+
4. **Decide:** if `linesDrafted > threshold` AND the structural floor passes → **chunked**; else → **single-file** (write the draft as-is at Step 4/5/6's write step).
|
|
146
|
+
|
|
147
|
+
For each file decided to chunk, plan the chunk set:
|
|
148
|
+
|
|
149
|
+
- `api.md` → one chunk per namespace: `Documentation~/api/<Namespace>.md` (literal namespace, no slugification — e.g. `Documentation~/api/PackageTool.Tools.md`). Do NOT sub-split a single huge namespace.
|
|
150
|
+
- `samples.md` → one chunk per top-level sample folder: `Documentation~/samples/<SampleFolderName>.md`. Sanitize `<SampleFolderName>` to `[A-Za-z0-9._-]` (replace any other character with `-`).
|
|
151
|
+
- `manual.md` → one chunk per H2 section using this fixed slug set: `overview.md`, `architecture.md`, `assemblies.md`, `entry-points.md`, `data-model.md`, `editor-ui.md`, `extension-points.md` (these correspond 1:1 to the H2 sections in `manual.md.template`). Sections that render empty are omitted from the chunk set.
|
|
152
|
+
|
|
153
|
+
Build a **`crossRefMap`**: `{ typeAnchor → "<destination-relative-to-Documentation~>#<anchor>" }`, populated during the api decision:
|
|
154
|
+
|
|
155
|
+
- When api is single-file: every entry is `api.md#<anchor>` (identity with the legacy shape).
|
|
156
|
+
- When api is chunked: each entry is `api/<Namespace>.md#<anchor>`, where `<Namespace>` is the namespace that contains the type (from `types[i].namespace`). Nested types use `containingType`'s namespace.
|
|
157
|
+
|
|
158
|
+
`crossRefMap` is consumed by Steps 5 and 6 when emitting `[Type](…)` references. Renderers MUST NOT hard-code `api.md` in link destinations — always resolve via `crossRefMap`.
|
|
159
|
+
|
|
160
|
+
Print one line per file decision, e.g. `chunk-plan: api.md → chunked (12 namespaces, draft=2143 lines, threshold=800)` or `chunk-plan: samples.md → single (draft=78 lines)`.
|
|
161
|
+
|
|
162
|
+
### Step 4 — Render `Documentation~/api.md` first
|
|
163
|
+
|
|
164
|
+
Render this file first because `manual.md` and `samples.md` link into its anchors (via `crossRefMap` from Step 3.5) and you want those anchors to exist.
|
|
165
|
+
|
|
166
|
+
Read `${SKILL_DIR}/assets/api.md.template`. Replace `{{displayName}}` with the discovered value. Replace `{{namespaceContentsList}}` with a bulleted index of namespaces, each linking to its anchor. Replace `{{perNamespaceSections}}` with one section per namespace following the structure documented in the template's HTML comment block.
|
|
167
|
+
|
|
168
|
+
For each type entry:
|
|
169
|
+
|
|
170
|
+
- Heading: `### {TypeName}` (stable; the anchor is `{typename-lowercase}` by default Markdown rules; for nested types use `### {ContainingType}.{TypeName}`).
|
|
171
|
+
- ` ```csharp ` block with the declaration line copied verbatim from source (use the file + line in the manifest to extract the line; do not paraphrase).
|
|
172
|
+
- `**Source:** ` + `path:line`.
|
|
173
|
+
- `**Attributes:** ` + comma-separated list (omit the line if none).
|
|
174
|
+
- `**Base:** ` + comma-separated `baseList` (omit if empty).
|
|
175
|
+
- Summary verbatim if present.
|
|
176
|
+
- Members table: only include public/internal/protected members; omit private. Strip XML tags from member summaries.
|
|
177
|
+
|
|
178
|
+
Omit types with no public members and no XML summary unless they carry a Unity attribute (`[MenuItem]`, `[CustomEditor]`, etc.). Omit empty namespaces entirely.
|
|
179
|
+
|
|
180
|
+
**Output (single-file branch, default):** if Step 3.5 decided api is single-file, write the rendered draft to `Documentation~/api.md` as-is.
|
|
181
|
+
|
|
182
|
+
**Output (chunked branch):** if Step 3.5 decided api is chunked, split the draft by namespace and emit:
|
|
183
|
+
|
|
184
|
+
1. **One chunk per namespace** at `Documentation~/api/<Namespace>.md` using `${SKILL_DIR}/assets/api-chunk.md.template`. Each chunk contains the H1 (`# {{displayName}} — API Reference: \`{{namespace}}\``), the namespace's section body (same `### TypeName` blocks as the single-file form), and the generator marker on line 1. Anchors inside the chunk stay `{typename-lowercase}` — they do NOT include the namespace prefix, because cross-doc links resolve via `crossRefMap` (which carries the path-plus-anchor pair).
|
|
185
|
+
2. **One index** at `Documentation~/api.md` using `${SKILL_DIR}/assets/api-index.md.template`. The index keeps the generator marker, H1, a `## Contents` Markdown table linking each chunk (`| Namespace | Description |`), and a one-line `## Notes` paragraph: `This API reference is split by namespace; see files under \`api/\`.`. The one-line description per namespace is the same blurb you would have placed in the single-file `{{namespaceContentsList}}`.
|
|
186
|
+
3. **Stale chunks:** before writing, list `Documentation~/api/*.md`. For each existing file that starts with the generator marker and is NOT in the current chunk set, delete it and print `Removed stale: <path>`. (See Idempotency.)
|
|
187
|
+
|
|
188
|
+
### Step 5 — Render `Documentation~/manual.md`
|
|
189
|
+
|
|
190
|
+
Read `${SKILL_DIR}/assets/manual.md.template`. Fill each placeholder:
|
|
191
|
+
|
|
192
|
+
- `{{overviewProse}}` — 2–4 dense paragraphs synthesizing what the package does. Read `description` plus the XML summaries of the top 3–5 most-referenced types (highest member counts or carrying Unity attributes).
|
|
193
|
+
- `{{namespaceTree}}` — Plain ASCII tree of namespaces, sorted, with no decoration.
|
|
194
|
+
- `{{architectureProse}}` — One short paragraph per top-level namespace explaining its responsibility. Reference at least one representative file path per namespace.
|
|
195
|
+
- `{{assembliesTableRows}}` — One Markdown table row per `.asmdef` (name, root namespace or `—`, platforms joined with `,` or `Any`, references joined with `,` or `—`).
|
|
196
|
+
- `{{entryPointsList}}` — Bulleted list. One bullet per `[MenuItem]` (show menu path), one per `[CreateAssetMenu]` (show `menuName`), one per public static method on a public static class (CI/CLI candidates). Each bullet ends with `— path:line`. Include the type's XML summary if non-empty.
|
|
197
|
+
- `{{dataModelList}}` — Bulleted list of every type with base `ScriptableObject` and every type with `[Serializable]`. For each, list its public fields as a sub-bulleted list with their types.
|
|
198
|
+
- `{{editorUiProse}}` — Detect IMGUI vs UI Toolkit by checking for `.uxml`/`.uss` files under the package root and for types inheriting `EditorWindow` / `PropertyDrawer` / `UnityEditor.Editor`. State which approach is used and list representative file paths.
|
|
199
|
+
- `{{extensionPointsList}}` — Public abstract types, public virtual methods, types ending in `Base`, or those marked `partial`. If none, write `None.`.
|
|
200
|
+
|
|
201
|
+
**Resolving `[Type](…)` links.** When rendering any link to a package type, look it up in `crossRefMap` (built in Step 3.5). Use the value verbatim. Do NOT hard-code `api.md#anchor`. If the link is emitted from a manual chunk file (chunked branch), the value must be prefixed with `../` (because chunks live under `manual/`); the renderer prepends `../` to every `crossRefMap` value when emitting from a chunk file, and emits the bare value when emitting from the index-level `manual.md`. Same rule applies to samples in Step 6.
|
|
202
|
+
|
|
203
|
+
**Output (single-file branch, default):** if Step 3.5 decided manual is single-file, write the rendered draft to `Documentation~/manual.md` as-is.
|
|
204
|
+
|
|
205
|
+
**Output (chunked branch):** if Step 3.5 decided manual is chunked, split the draft along its H2 boundaries and emit:
|
|
206
|
+
|
|
207
|
+
1. **One chunk per non-empty H2 section** at `Documentation~/manual/<slug>.md`, using the fixed slug set from Step 3.5 (`overview.md`, `architecture.md`, `assemblies.md`, `entry-points.md`, `data-model.md`, `editor-ui.md`, `extension-points.md`). Each chunk contains the generator marker on line 1, an H1 echoing the section title (`# {{displayName}} — Manual: <Section Title>`), then the section body (everything that was below the H2 in the single-file draft, with the H2 line itself dropped). Within a chunk, all `[Type](…)` link values from `crossRefMap` are prefixed with `../`.
|
|
208
|
+
2. **One index** at `Documentation~/manual.md` containing the generator marker, the H1 (`# {{displayName}} — Manual`), a `## Contents` Markdown table (`| Section | Description |`) linking each chunk (one row per emitted slug; description = the first sentence of that section's body), and one-line `## Notes`: `This manual is split by section; see files under \`manual/\`.`
|
|
209
|
+
3. **Stale chunks:** before writing, list `Documentation~/manual/*.md`. For each existing file that starts with the generator marker and is NOT in the current chunk set, delete it and print `Removed stale: <path>`.
|
|
210
|
+
|
|
211
|
+
### Step 6 — Render `Documentation~/samples.md`
|
|
212
|
+
|
|
213
|
+
Read `${SKILL_DIR}/assets/samples.md.template`. Fill placeholders:
|
|
214
|
+
|
|
215
|
+
- `{{sampleTree}}` — ASCII tree of the discovered sample dir(s), one line per file.
|
|
216
|
+
- `{{sampleMappingSections}}` — One `### path` subsection per sample file, following the structure documented in the template's HTML comment. For scripts, list referenced package types as links resolved through `crossRefMap` (Step 3.5). For scenes/prefabs, list resolved script GUIDs as links resolved through `crossRefMap`.
|
|
217
|
+
- `{{reproductionProse}}` — One paragraph explaining how a downstream user reproduces the sample setup from scratch.
|
|
218
|
+
- `{{reproductionSnippet}}` — Paste-ready `csharp` snippet using only public API documented in api.md (or its chunks). If the sample is empty, omit the snippet section entirely and write a stub (see edge cases).
|
|
219
|
+
- `{{notesList}}` — Any sample-only conventions worth knowing.
|
|
220
|
+
|
|
221
|
+
**Resolving `[Type](…)` links.** Same rule as Step 5: look up every type link in `crossRefMap`. From chunked output, prepend `../` (chunks live under `samples/`); from the index-level `samples.md`, emit the bare value.
|
|
222
|
+
|
|
223
|
+
**Edge case — empty sample:** if no sample files exist (the dir is empty or absent), emit a single-file `Documentation~/samples.md` with the `<!-- generated -->` marker, the H1, and a single section: `## No sample content` containing one sentence: `No sample content present. Add files under \`<expected sample path>\` and re-run this skill.`. Do not fail and do not chunk.
|
|
224
|
+
|
|
225
|
+
**Output (single-file branch, default):** if Step 3.5 decided samples is single-file, write the rendered draft to `Documentation~/samples.md` as-is.
|
|
226
|
+
|
|
227
|
+
**Output (chunked branch):** if Step 3.5 decided samples is chunked, group sample files by their **top-level sample folder** (the first path segment under the sample root, e.g. `Assets/Example/Sample/Foo.cs` → folder `Sample`) and emit:
|
|
228
|
+
|
|
229
|
+
1. **One chunk per top-level sample folder** at `Documentation~/samples/<SampleFolderName>.md` (sanitized to `[A-Za-z0-9._-]`). Each chunk contains the generator marker on line 1, H1 `# {{displayName}} — Samples: <SampleFolderName>`, the per-sample `### path` subsections that belong to that folder, the per-folder `## Reproducing the Sample` prose + snippet, and `## Notes`. Inside chunks, `crossRefMap` link values are prefixed with `../`.
|
|
230
|
+
2. **One index** at `Documentation~/samples.md` containing the generator marker, H1, the global `## Sample Layout` tree, a `## Contents` Markdown table (`| Sample | Description |`) linking each chunk, and `## Notes`: `Samples are split by sample folder; see files under \`samples/\`.`
|
|
231
|
+
3. **Stale chunks:** before writing, list `Documentation~/samples/*.md`. For each existing file that starts with the generator marker and is NOT in the current chunk set, delete it and print `Removed stale: <path>`.
|
|
232
|
+
|
|
233
|
+
### Step 7 — Render `README.md`
|
|
234
|
+
|
|
235
|
+
Read `${SKILL_DIR}/assets/README.md.template`. Fill placeholders:
|
|
236
|
+
|
|
237
|
+
- `{{displayName}}`, `{{description}}` — from metadata.
|
|
238
|
+
- `{{platformConstraintsBullets}}` — bullet per `.asmdef` with a non-empty `includePlatforms` or `excludePlatforms`. Omit the whole line if all asmdefs allow all platforms.
|
|
239
|
+
- `{{dependenciesTable}}` — Markdown table (`| Package | Version |`) of dependencies. If none, write `None.`.
|
|
240
|
+
- `{{installManifestSnippet}}` — JSON snippet showing the `manifest.json` edit. Include `scopedRegistries` with the URL from `publishConfig.registry` only if present in `package.json`; otherwise omit and add a one-line note in `{{scopedRegistryNote}}` reading `Replace the registry URL above with your scoped registry, or remove the scopedRegistries block when consuming a published package.` (when present, the note is empty).
|
|
241
|
+
- `{{gettingStartedProse}}` — one paragraph. Identify the primary entry point:
|
|
242
|
+
1. The first type with `[CreateAssetMenu]` → describe `Assets > Create > <menuName>`.
|
|
243
|
+
2. Else the first class with a `[MenuItem]` method → describe the menu path.
|
|
244
|
+
3. Else the first public static method on a public type → describe a direct call.
|
|
245
|
+
- `{{gettingStartedSnippet}}` — minimal `csharp` snippet exercising the primary entry point.
|
|
246
|
+
- `{{licensePath}}` — path to the root `LICENSE` file (typically `LICENSE`).
|
|
247
|
+
|
|
248
|
+
Write `README.md` at the repo root.
|
|
249
|
+
|
|
250
|
+
### Step 8 — Verify
|
|
251
|
+
|
|
252
|
+
After all files (including any chunks) are written:
|
|
253
|
+
|
|
254
|
+
1. Grep `README.md` for `Documentation~/manual.md`, `Documentation~/api.md`, `Documentation~/samples.md`. Confirm each file exists. Abort with a clear error if any link is broken. (These three top-level paths are unchanged whether the file is single or chunked — when chunked the file is a Contents-table index, but the path still resolves.)
|
|
255
|
+
2. Walk every emitted `.md` under `Documentation~/` (index files and chunk files). For each `[Type](…)` link extract the destination path (relative to the link's own file) and anchor. Resolve the destination to an absolute path under `Documentation~/`; confirm the file exists and contains an `### {anchor}` heading (case-insensitive, hyphen-normalized per Markdown's auto-slug rule). Print warnings (not errors) for any broken anchors. Replaces the legacy "grep `api.md#`" check, which is no longer sufficient because anchors now live in either `api.md` or `api/<Namespace>.md`.
|
|
256
|
+
3. Confirm every generated file — including every chunk under `Documentation~/api/`, `Documentation~/manual/`, `Documentation~/samples/` — starts with the generator marker on line 1.
|
|
257
|
+
4. Confirm no chunk file outside the current chunk set survived (the stale-removal in Steps 4/5/6 should have deleted them; this is a safety re-check).
|
|
258
|
+
|
|
259
|
+
Print a summary: which files were created vs updated, which were skipped (and why), and which chunks were removed as stale.
|
|
260
|
+
|
|
261
|
+
## Idempotency
|
|
262
|
+
|
|
263
|
+
For each output file:
|
|
264
|
+
|
|
265
|
+
1. If the file does not exist → write it.
|
|
266
|
+
2. If the file exists and starts with the generator marker → overwrite it.
|
|
267
|
+
3. If the file exists and does NOT start with the marker → **do not overwrite** unless `force=true` was passed. Print: `Skipped <path>: marker absent (file is hand-edited or pre-existing). Re-run with force=true to overwrite.`
|
|
268
|
+
|
|
269
|
+
`Documentation~/` pre-existing assets (PNG, JPG, etc.) are never touched regardless of `force`.
|
|
270
|
+
|
|
271
|
+
### Stale chunks
|
|
272
|
+
|
|
273
|
+
Chunk files (under `Documentation~/api/`, `Documentation~/manual/`, `Documentation~/samples/`) follow the same marker-on-line-1 contract as top-level generated files. Additionally:
|
|
274
|
+
|
|
275
|
+
4. **Stale removal (marker-gated).** Before writing the chunk set for an area, list every `.md` file in that area's chunk directory. For each existing file that **starts with the generator marker** AND is **not in the current chunk set**, delete it and print `Removed stale: <path>`. Files without the marker are left in place and a warning is printed (matches rule 3 — never silently destroy hand-edited content).
|
|
276
|
+
5. **Mode flip (single ↔ chunked).** When a Documentation~ file flips from chunked to single-file between runs (e.g. the user removes namespaces until the threshold is no longer exceeded), every file in that area's chunk directory is treated as stale by rule 4: marker-bearing chunks are deleted, marker-absent files are warned about. The chunk directory itself is left in place even when empty (Unity treats empty directories as no-ops; do not `rmdir`).
|
|
277
|
+
6. **Force.** `force=true` extends rules 3 and 4 — marker-absent files are overwritten (rule 3) and marker-absent chunks are also deleted when stale (rule 4 extension), matching the existing semantics for top-level files.
|
|
278
|
+
|
|
279
|
+
The pipeline as a whole is idempotent: re-running on an unchanged repo produces a no-op diff.
|
|
280
|
+
|
|
281
|
+
## Examples of correct output style
|
|
282
|
+
|
|
283
|
+
Good (rule 3 — explicit paths):
|
|
284
|
+
|
|
285
|
+
> The CI entry point is `CIUtils.Generate` at `Assets/Package/PackageTool/Editor/CIUtils.cs:65`. It is invoked in batch mode and reads command-line keys parsed by `CommandLineTools.GetKVPCommandLineArguments` at `Assets/Package/PackageTool/Editor/Tools/CommandLineTools.cs:43`.
|
|
286
|
+
|
|
287
|
+
Bad (rule 3 violation):
|
|
288
|
+
|
|
289
|
+
> The CI entry point is `CIUtils.Generate`. It reads command-line keys from a helper.
|
|
290
|
+
|
|
291
|
+
Good (rule 4 — tagged, complete code blocks):
|
|
292
|
+
|
|
293
|
+
````
|
|
294
|
+
```csharp
|
|
295
|
+
var config = ScriptableObject.CreateInstance<PackageManifestConfig>();
|
|
296
|
+
config.packageName = "com.example.foo";
|
|
297
|
+
FileTools.CreateOrUpdatePackageSource(config);
|
|
298
|
+
```
|
|
299
|
+
````
|
|
300
|
+
|
|
301
|
+
Bad (rule 4 violation):
|
|
302
|
+
|
|
303
|
+
````
|
|
304
|
+
```
|
|
305
|
+
var config = ...
|
|
306
|
+
// ... fill in fields ...
|
|
307
|
+
FileTools.CreateOrUpdatePackageSource(config);
|
|
308
|
+
```
|
|
309
|
+
````
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<!-- generated by unity-package-docs; safe to regenerate -->
|
|
2
|
+
|
|
3
|
+
# {{displayName}}
|
|
4
|
+
|
|
5
|
+
{{description}}
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- Unity {{unityVersion}} or newer.
|
|
10
|
+
{{platformConstraintsBullets}}
|
|
11
|
+
|
|
12
|
+
## Dependencies
|
|
13
|
+
|
|
14
|
+
{{dependenciesTable}}
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Add the package to `Packages/manifest.json`:
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{{installManifestSnippet}}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
{{scopedRegistryNote}}
|
|
25
|
+
|
|
26
|
+
## Getting Started
|
|
27
|
+
|
|
28
|
+
{{gettingStartedProse}}
|
|
29
|
+
|
|
30
|
+
```csharp
|
|
31
|
+
{{gettingStartedSnippet}}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Documentation
|
|
35
|
+
|
|
36
|
+
- [Manual](Documentation~/manual.md) — concepts and architecture.
|
|
37
|
+
- [API Reference](Documentation~/api.md) — every public type and member.
|
|
38
|
+
- [Samples](Documentation~/samples.md) — how the sample project in this repo uses the package.
|
|
39
|
+
|
|
40
|
+
## License
|
|
41
|
+
|
|
42
|
+
See [{{licensePath}}]({{licensePath}}).
|
package/Samples~/ClaudeSkills/unity-package-docs/unity-package-docs/assets/api-chunk.md.template
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<!-- generated by unity-package-docs; safe to regenerate -->
|
|
2
|
+
|
|
3
|
+
# {{displayName}} — API Reference: `{{namespace}}`
|
|
4
|
+
|
|
5
|
+
{{typeSections}}
|
|
6
|
+
|
|
7
|
+
<!--
|
|
8
|
+
Used only when api.md is chunked (Step 4 chunked branch).
|
|
9
|
+
{{namespace}} is the literal namespace, matching the chunk filename
|
|
10
|
+
(Documentation~/api/<Namespace>.md).
|
|
11
|
+
|
|
12
|
+
{{typeSections}} is one section per type in this namespace, identical
|
|
13
|
+
in shape to a per-type block in the single-file api.md template:
|
|
14
|
+
|
|
15
|
+
### {TypeName}
|
|
16
|
+
|
|
17
|
+
```csharp
|
|
18
|
+
{declaration line copied verbatim from source}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Source:** `{file}:{line}`
|
|
22
|
+
**Attributes:** `[Attr1]`, `[Attr2]` (omit line if none)
|
|
23
|
+
**Base:** `{baseList}` (omit if empty)
|
|
24
|
+
|
|
25
|
+
{summary}
|
|
26
|
+
|
|
27
|
+
**Members:**
|
|
28
|
+
|
|
29
|
+
| Kind | Name | Signature | Summary |
|
|
30
|
+
|------|------|-----------|---------|
|
|
31
|
+
| method | Foo | `public void Foo()` | … |
|
|
32
|
+
|
|
33
|
+
Anchors are `{typename-lowercase}` (no namespace prefix). Cross-doc links
|
|
34
|
+
from manual.md / samples.md resolve via crossRefMap, which carries the
|
|
35
|
+
chunk-relative path-plus-anchor pair (e.g. `api/PackageTool.Tools.md#filetools`).
|
|
36
|
+
For nested types use `### {ContainingType}.{TypeName}` and anchor
|
|
37
|
+
`{containingtype}.{typename}`.
|
|
38
|
+
|
|
39
|
+
Omit types with no public members and no XML summary unless they carry a
|
|
40
|
+
Unity attribute (`[MenuItem]`, `[CustomEditor]`, etc.).
|
|
41
|
+
-->
|
package/Samples~/ClaudeSkills/unity-package-docs/unity-package-docs/assets/api-index.md.template
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<!-- generated by unity-package-docs; safe to regenerate -->
|
|
2
|
+
|
|
3
|
+
# {{displayName}} — API Reference
|
|
4
|
+
|
|
5
|
+
This API reference is split by namespace because the rendered single-file draft exceeded the chunk threshold (see `chunk-threshold-api` / `chunk-threshold-lines` in the unity-package-docs skill).
|
|
6
|
+
|
|
7
|
+
## Contents
|
|
8
|
+
|
|
9
|
+
| Namespace | Description |
|
|
10
|
+
|-----------|-------------|
|
|
11
|
+
{{namespaceTableRows}}
|
|
12
|
+
|
|
13
|
+
## Notes
|
|
14
|
+
|
|
15
|
+
This API reference is split by namespace; see files under `api/`. Cross-doc links from `manual.md` and `samples.md` point directly into the relevant chunk file (e.g. `api/PackageTool.Tools.md#filetools`); this index is intentionally thin and stable so external links to `Documentation~/api.md` keep resolving.
|
|
16
|
+
|
|
17
|
+
<!--
|
|
18
|
+
Used only when api.md is chunked (Step 4 chunked branch).
|
|
19
|
+
|
|
20
|
+
{{namespaceTableRows}} is one Markdown table row per namespace:
|
|
21
|
+
|
|
22
|
+
| [`PackageTool`](api/PackageTool.md) | Root namespace; holds the configuration ScriptableObject. |
|
|
23
|
+
|
|
24
|
+
The description is the same one-line blurb that would have appeared in
|
|
25
|
+
the single-file template's {{namespaceContentsList}}.
|
|
26
|
+
-->
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<!-- generated by unity-package-docs; safe to regenerate -->
|
|
2
|
+
|
|
3
|
+
# {{displayName}} — API Reference
|
|
4
|
+
|
|
5
|
+
Comprehensive reference for every public/internal type and member. Generated from source; do not edit by hand.
|
|
6
|
+
|
|
7
|
+
## Contents
|
|
8
|
+
|
|
9
|
+
{{namespaceContentsList}}
|
|
10
|
+
|
|
11
|
+
{{perNamespaceSections}}
|
|
12
|
+
|
|
13
|
+
<!--
|
|
14
|
+
Used only when api.md is single-file (Step 4 default branch). When chunking
|
|
15
|
+
is active (Step 4 chunked branch), api.md becomes a Contents-table index
|
|
16
|
+
rendered from api-index.md.template, and per-namespace bodies are emitted
|
|
17
|
+
to Documentation~/api/<Namespace>.md from api-chunk.md.template.
|
|
18
|
+
|
|
19
|
+
Each namespace section follows this exact shape:
|
|
20
|
+
|
|
21
|
+
## `{namespace}`
|
|
22
|
+
|
|
23
|
+
### {TypeName}
|
|
24
|
+
|
|
25
|
+
```csharp
|
|
26
|
+
{declaration line copied verbatim from source}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Source:** `{file}:{line}`
|
|
30
|
+
**Attributes:** `[Attr1]`, `[Attr2]` (omit line if none)
|
|
31
|
+
**Base:** `{baseList}` (omit if empty)
|
|
32
|
+
|
|
33
|
+
{summary}
|
|
34
|
+
|
|
35
|
+
**Members:**
|
|
36
|
+
|
|
37
|
+
| Kind | Name | Signature | Summary |
|
|
38
|
+
|------|------|-----------|---------|
|
|
39
|
+
| method | Foo | `public void Foo()` | … |
|
|
40
|
+
|
|
41
|
+
Omit types with no public members and no XML summary unless they carry a Unity attribute.
|
|
42
|
+
Omit empty namespaces entirely.
|
|
43
|
+
-->
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<!-- generated by unity-package-docs; safe to regenerate -->
|
|
2
|
+
<!--
|
|
3
|
+
Chunking note (Step 5 chunked branch): when this file is split, each H2
|
|
4
|
+
section below becomes Documentation~/manual/<slug>.md. The H2 → slug map
|
|
5
|
+
is fixed:
|
|
6
|
+
|
|
7
|
+
## Overview → manual/overview.md
|
|
8
|
+
## Architecture → manual/architecture.md
|
|
9
|
+
## Assemblies → manual/assemblies.md
|
|
10
|
+
## Entry Points → manual/entry-points.md
|
|
11
|
+
## Data Model → manual/data-model.md
|
|
12
|
+
## Editor UI Approach → manual/editor-ui.md
|
|
13
|
+
## Extension Points → manual/extension-points.md
|
|
14
|
+
|
|
15
|
+
Sections that render empty are omitted from the chunk set. Inside chunk
|
|
16
|
+
files, every `[Type](…)` link prepends `../` to the crossRefMap value
|
|
17
|
+
(because chunks live one directory deeper than Documentation~/).
|
|
18
|
+
-->
|
|
19
|
+
|
|
20
|
+
# {{displayName}} — Manual
|
|
21
|
+
|
|
22
|
+
## Overview
|
|
23
|
+
|
|
24
|
+
{{overviewProse}}
|
|
25
|
+
|
|
26
|
+
Package name: `{{packageName}}`
|
|
27
|
+
Package source: `{{packageDir}}`
|
|
28
|
+
|
|
29
|
+
## Architecture
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
{{namespaceTree}}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
{{architectureProse}}
|
|
36
|
+
|
|
37
|
+
## Assemblies
|
|
38
|
+
|
|
39
|
+
| Assembly | Root Namespace | Platforms | References |
|
|
40
|
+
|----------|----------------|-----------|------------|
|
|
41
|
+
{{assembliesTableRows}}
|
|
42
|
+
|
|
43
|
+
## Entry Points
|
|
44
|
+
|
|
45
|
+
{{entryPointsList}}
|
|
46
|
+
|
|
47
|
+
## Data Model
|
|
48
|
+
|
|
49
|
+
{{dataModelList}}
|
|
50
|
+
|
|
51
|
+
## Editor UI Approach
|
|
52
|
+
|
|
53
|
+
{{editorUiProse}}
|
|
54
|
+
|
|
55
|
+
## Extension Points
|
|
56
|
+
|
|
57
|
+
{{extensionPointsList}}
|
package/Samples~/ClaudeSkills/unity-package-docs/unity-package-docs/assets/samples.md.template
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<!-- generated by unity-package-docs; safe to regenerate -->
|
|
2
|
+
|
|
3
|
+
# {{displayName}} — Samples
|
|
4
|
+
|
|
5
|
+
How the sample content in this repository uses the package. Each entry maps a sample file to the package types it exercises, so a reader can recreate the same setup against the public API.
|
|
6
|
+
|
|
7
|
+
## Sample Layout
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
{{sampleTree}}
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Sample → Package Type Mapping
|
|
14
|
+
|
|
15
|
+
{{sampleMappingSections}}
|
|
16
|
+
|
|
17
|
+
<!--
|
|
18
|
+
Each sample entry follows this exact shape:
|
|
19
|
+
|
|
20
|
+
### `{sample file path}`
|
|
21
|
+
|
|
22
|
+
Kind: script | scene | prefab
|
|
23
|
+
|
|
24
|
+
Package types referenced:
|
|
25
|
+
|
|
26
|
+
- [`{TypeName}`]({crossRefMap[TypeName]}) — `{package source path}:{line}`
|
|
27
|
+
|
|
28
|
+
For scenes/prefabs, list each MonoBehaviour script resolved via m_Script.guid:
|
|
29
|
+
|
|
30
|
+
- GameObject `{name}` uses [`{TypeName}`]({crossRefMap[TypeName]}) — `{package source path}`
|
|
31
|
+
|
|
32
|
+
Type links resolve through crossRefMap (Step 3.5), NOT a hard-coded
|
|
33
|
+
api.md anchor — the destination may be api.md#foo (api single-file) or
|
|
34
|
+
api/PackageTool.Tools.md#foo (api chunked).
|
|
35
|
+
|
|
36
|
+
Omit "Package types referenced" if the file references none from this package.
|
|
37
|
+
|
|
38
|
+
Chunking note (Step 6 chunked branch): when this file is split, entries
|
|
39
|
+
are grouped by top-level sample folder into Documentation~/samples/<SampleFolderName>.md
|
|
40
|
+
(filename sanitized to [A-Za-z0-9._-], non-matching chars replaced with `-`).
|
|
41
|
+
Each chunk carries its own `## Reproducing the Sample` and `## Notes`
|
|
42
|
+
sections. Inside chunk files, every `[Type](…)` link prepends `../` to
|
|
43
|
+
the crossRefMap value.
|
|
44
|
+
-->
|
|
45
|
+
|
|
46
|
+
## Reproducing the Sample
|
|
47
|
+
|
|
48
|
+
{{reproductionProse}}
|
|
49
|
+
|
|
50
|
+
```csharp
|
|
51
|
+
{{reproductionSnippet}}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Notes
|
|
55
|
+
|
|
56
|
+
{{notesList}}
|