@tanlinfeng/project-docs-workflow 2.2.0 → 3.0.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.
Files changed (45) hide show
  1. package/NOTICE +8 -10
  2. package/README.md +97 -143
  3. package/bin/install.js +731 -139
  4. package/package.json +2 -2
  5. package/skills/docs-html-view/SKILL.md +119 -0
  6. package/skills/docs-html-view/agents/openai.yaml +5 -0
  7. package/skills/docs-html-view/assets/conf.py.template +44 -0
  8. package/skills/docs-html-view/assets/guard.py +128 -0
  9. package/skills/docs-html-view/references/technical.md +151 -0
  10. package/skills/docs-html-view/requirements.txt +5 -0
  11. package/skills/docs-html-view/scripts/gen_nav.py +306 -0
  12. package/skills/docs-html-view/scripts/prepare_env.py +154 -0
  13. package/skills/project-docs-workflow/SKILL.md +27 -12
  14. package/skills/project-docs-workflow/assets/templates/entries/current-module.md +19 -0
  15. package/skills/project-docs-workflow/assets/templates/entries/pdr-spec.md +1 -1
  16. package/skills/project-docs-workflow/assets/templates/project/adr-readme.md +2 -1
  17. package/skills/project-docs-workflow/assets/templates/project/agents-block.md +1 -1
  18. package/skills/project-docs-workflow/assets/templates/project/agents-domain.md +3 -3
  19. package/skills/project-docs-workflow/assets/templates/project/agents-issues.md +6 -5
  20. package/skills/project-docs-workflow/assets/templates/project/agents-workflow.md +9 -6
  21. package/skills/project-docs-workflow/assets/templates/project/current-00-readme.md +42 -0
  22. package/skills/project-docs-workflow/assets/templates/project/pdr-00-readme.md +6 -7
  23. package/skills/project-docs-workflow/references/adr.md +5 -1
  24. package/skills/project-docs-workflow/references/coexistence.md +8 -6
  25. package/skills/project-docs-workflow/references/current.md +39 -0
  26. package/skills/project-docs-workflow/references/deps/boundaries.md +4 -2
  27. package/skills/project-docs-workflow/references/deps/domain-modeling.md +1 -1
  28. package/skills/project-docs-workflow/references/deps/grilling.md +3 -1
  29. package/skills/project-docs-workflow/references/deps/tdd.md +2 -2
  30. package/skills/project-docs-workflow/references/issues.md +3 -2
  31. package/skills/project-docs-workflow/references/pdr.md +15 -10
  32. package/skills/project-docs-workflow/references/rendering.md +29 -0
  33. package/skills/project-docs-workflow/references/workflow.md +6 -5
  34. package/skills/deepwork/SKILL.md +0 -15
  35. package/skills/domain-modeling/SKILL.md +0 -83
  36. package/skills/domain-modeling/agents/openai.yaml +0 -3
  37. package/skills/grill-me/SKILL.md +0 -13
  38. package/skills/grill-with-docs/SKILL.md +0 -18
  39. package/skills/grilling/SKILL.md +0 -46
  40. package/skills/grilling/agents/openai.yaml +0 -3
  41. package/skills/improve-codebase-architecture/SKILL.md +0 -15
  42. package/skills/project-docs-workflow/assets/templates/entries/pdr-current.md +0 -19
  43. package/skills/retro/SKILL.md +0 -19
  44. package/skills/tdd/SKILL.md +0 -50
  45. package/skills/tdd/agents/openai.yaml +0 -3
@@ -0,0 +1,306 @@
1
+ #!/usr/bin/env python3
2
+ """Generate docs-html-view navigation under docs/docsHtmlView.
3
+
4
+ Writes only marked files inside the workspace: ``docsHtmlView/index.md`` and
5
+ ``docsHtmlView/nav/**/index.md``. Never writes into source trees. Validates every
6
+ symlink and collision in a preflight pass *before* the first write or prune, so a
7
+ refusal leaves the tree unchanged. Removes only stale files carrying the marker.
8
+ Runs before ``make html``; it never invokes Sphinx itself.
9
+
10
+ Exit codes: 0 = generated (or nothing needed); 1 = refused, or no documents in
11
+ the approved trees (so a later ``make`` cannot silently build a stale site).
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import argparse
16
+ import json
17
+ import os
18
+ import posixpath
19
+ import re
20
+ import sys
21
+
22
+ MARK = "% docs-html-view: generated navigation; do not edit"
23
+ DEFAULT_ROOTS = ["pdr", "current", "adr"]
24
+ SUFFIXES = (".md", ".rst")
25
+ RESERVED = {"build", "docsHtmlView"}
26
+ BIG = float("inf")
27
+
28
+
29
+ def die(msg: str) -> None:
30
+ sys.exit("refusing: " + msg)
31
+
32
+
33
+ def warn(msg: str) -> None:
34
+ print("warning: " + msg, file=sys.stderr)
35
+
36
+
37
+ def sanitize(text: str) -> str:
38
+ text = re.sub(r"[<>`{}\[\]|]", "", text)
39
+ return re.sub(r"\s+", " ", text).strip()
40
+
41
+
42
+ def display_name(name: str) -> str:
43
+ name = re.sub(r"^\d+[-_. ]*", "", name).replace("-", " ").replace("_", " ").strip()
44
+ return sanitize(name) or "section"
45
+
46
+
47
+ def num_key(name: str):
48
+ match = re.match(r"^(\d+)", name)
49
+ return (0, int(match.group(1))) if match else (1, 0)
50
+
51
+
52
+ def has_docs(path: str) -> bool:
53
+ for _dirpath, _dirnames, filenames in os.walk(path, followlinks=False):
54
+ if any(f.lower().endswith(SUFFIXES) for f in filenames):
55
+ return True
56
+ return False
57
+
58
+
59
+ def file_owned(path: str) -> bool:
60
+ try:
61
+ with open(path, encoding="utf-8") as fh:
62
+ return fh.read().lstrip().startswith(MARK)
63
+ except OSError:
64
+ die("cannot read " + path)
65
+
66
+
67
+ def read_config(workspace: str) -> dict:
68
+ path = os.path.join(workspace, "docs-html-view.config.json")
69
+ if not os.path.lexists(path):
70
+ return {}
71
+ if os.path.islink(path):
72
+ die("config is a symlink: " + path)
73
+ try:
74
+ with open(path, encoding="utf-8") as fh:
75
+ data = json.load(fh)
76
+ except (OSError, ValueError) as exc:
77
+ die("cannot parse config %s: %s" % (path, exc))
78
+ if not isinstance(data, dict):
79
+ die("config must be a JSON object: " + path)
80
+ return data
81
+
82
+
83
+ def validate_dir_name(name: str) -> None:
84
+ if not name or name in (".", ".."):
85
+ die("invalid allowed_dirs entry: %r" % (name,))
86
+ if "/" in name or "\\" in name:
87
+ die("allowed_dirs entry must be one directory name, no separators: %r" % (name,))
88
+ if name.startswith("."):
89
+ die("allowed_dirs entry must not be hidden: %r" % (name,))
90
+ if name in RESERVED:
91
+ die("allowed_dirs entry may not be %r" % (name,))
92
+
93
+
94
+ def resolve_roots(cfg: dict, docs: str):
95
+ allowed = cfg.get("allowed_dirs")
96
+ if allowed is None:
97
+ candidates = list(DEFAULT_ROOTS)
98
+ else:
99
+ if not isinstance(allowed, list):
100
+ die("allowed_dirs must be a list of simple directory names")
101
+ for name in allowed:
102
+ if not isinstance(name, str):
103
+ die("allowed_dirs entries must be strings")
104
+ validate_dir_name(name)
105
+ candidates = list(dict.fromkeys(allowed))
106
+
107
+ present, missing = [], []
108
+ for name in candidates:
109
+ path = os.path.join(docs, name)
110
+ if os.path.islink(path):
111
+ die("approved dir is a symlink: " + path)
112
+ if os.path.isdir(path) and has_docs(path):
113
+ present.append(name)
114
+ else:
115
+ missing.append(name)
116
+ return present, missing
117
+
118
+
119
+ def assert_no_symlinks(base: str, label: str) -> None:
120
+ if not os.path.isdir(base):
121
+ return
122
+ for dirpath, dirnames, filenames in os.walk(base, followlinks=False):
123
+ for name in list(dirnames) + filenames:
124
+ path = os.path.join(dirpath, name)
125
+ if os.path.islink(path):
126
+ die("symlink under %s: %s" % (label, path))
127
+
128
+
129
+ def toctree(entries, maxdepth=1) -> str:
130
+ if not entries:
131
+ return "<!-- (no documents) -->\n"
132
+ lines = ["```{toctree}", ":maxdepth: %d" % maxdepth, ""] + list(entries) + ["```"]
133
+ return "\n".join(lines) + "\n"
134
+
135
+
136
+ def build_outputs(docs, workspace, roots, order_map, site_title):
137
+ outputs = {}
138
+
139
+ def order_key(base_rel):
140
+ def key(name):
141
+ rel = posixpath.join(base_rel, name) if base_rel else name
142
+ return (order_map.get(rel, BIG), num_key(name), name.casefold())
143
+ return key
144
+
145
+ def generate(source_root, rel_parts, src_dir):
146
+ base_rel = posixpath.join(source_root, *rel_parts)
147
+ out_rel = posixpath.join("docsHtmlView", "nav", source_root, *rel_parts)
148
+ children = []
149
+ for name in sorted(os.listdir(src_dir)):
150
+ path = os.path.join(src_dir, name)
151
+ if os.path.islink(path):
152
+ die("symlink in source tree: " + path)
153
+ if os.path.isdir(path):
154
+ if has_docs(path):
155
+ children.append((name, "dir"))
156
+ elif name.lower().endswith(SUFFIXES):
157
+ children.append((name, "file"))
158
+ children.sort(key=lambda item: order_key(base_rel)(item[0]))
159
+
160
+ entries = []
161
+ for name, kind in children:
162
+ if kind == "dir":
163
+ entries.append(posixpath.join(name, "index"))
164
+ else:
165
+ target = posixpath.join(source_root, *rel_parts, name)
166
+ entries.append(posixpath.relpath(target, out_rel))
167
+
168
+ title = display_name(rel_parts[-1]) if rel_parts else display_name(source_root)
169
+ out_file = os.path.join(docs, *out_rel.split("/"), "index.md")
170
+ outputs[out_file] = MARK + "\n\n# " + title + "\n\n" + toctree(entries)
171
+
172
+ for name, kind in children:
173
+ if kind == "dir":
174
+ generate(source_root, list(rel_parts) + [name], os.path.join(src_dir, name))
175
+
176
+ for source_root in roots:
177
+ generate(source_root, [], os.path.join(docs, source_root))
178
+
179
+ index_entries = [posixpath.join("nav", r, "index") for r in roots]
180
+ outputs[os.path.join(workspace, "index.md")] = (
181
+ MARK + "\n\n# " + site_title + "\n\n" + toctree(index_entries, maxdepth=2)
182
+ )
183
+ return outputs
184
+
185
+
186
+ def collision_preflight(outputs, nav_root) -> None:
187
+ out_abs = {os.path.abspath(p) for p in outputs}
188
+ for path in sorted(out_abs):
189
+ if os.path.islink(path):
190
+ die("symlinked output: " + path)
191
+ if os.path.lexists(path) and not file_owned(path):
192
+ die("existing unmarked file: " + path)
193
+ if os.path.isdir(nav_root):
194
+ for dirpath, _dirnames, filenames in os.walk(nav_root, followlinks=False):
195
+ for name in filenames:
196
+ path = os.path.abspath(os.path.join(dirpath, name))
197
+ if path in out_abs:
198
+ continue
199
+ if os.path.islink(path):
200
+ die("symlinked nav file: " + path)
201
+ if not file_owned(path):
202
+ die("unmarked file under nav/: " + path)
203
+
204
+
205
+ def verify_inside(outputs, workspace) -> None:
206
+ workspace_real = os.path.realpath(workspace)
207
+ for path in outputs:
208
+ real = os.path.realpath(path)
209
+ if real != workspace_real and not real.startswith(workspace_real + os.sep):
210
+ die("output escapes workspace: " + path)
211
+
212
+
213
+ def write_outputs(outputs) -> None:
214
+ for path in sorted(outputs):
215
+ os.makedirs(os.path.dirname(path), exist_ok=True)
216
+ with open(path, "w", encoding="utf-8", newline="\n") as fh:
217
+ fh.write(outputs[path])
218
+
219
+
220
+ def prune(nav_root, outputs) -> None:
221
+ out_abs = {os.path.abspath(p) for p in outputs}
222
+ if not os.path.isdir(nav_root):
223
+ return
224
+ for dirpath, _dirnames, filenames in os.walk(nav_root, followlinks=False):
225
+ for name in filenames:
226
+ path = os.path.abspath(os.path.join(dirpath, name))
227
+ if path not in out_abs and file_owned(path):
228
+ os.remove(path)
229
+ for dirpath, _dirs, _files in os.walk(nav_root, topdown=False):
230
+ if dirpath == nav_root:
231
+ continue
232
+ try:
233
+ os.rmdir(dirpath) # only succeeds when empty
234
+ except OSError:
235
+ pass
236
+
237
+
238
+ def main() -> None:
239
+ ap = argparse.ArgumentParser(description="Generate docs-html-view navigation (marked files only).")
240
+ ap.add_argument("--project-root", default=os.getcwd())
241
+ ap.add_argument("--title", default=None, help="site title override")
242
+ ap.add_argument("--json", action="store_true", help="print a machine-readable summary on stdout")
243
+ args = ap.parse_args()
244
+
245
+ root = os.path.abspath(args.project_root)
246
+ docs = os.path.join(root, "docs")
247
+ if not os.path.isdir(docs):
248
+ die("no docs/ directory at " + root)
249
+ if os.path.islink(docs):
250
+ die("docs/ is a symlink")
251
+
252
+ workspace = os.path.join(docs, "docsHtmlView")
253
+ if not os.path.isdir(workspace):
254
+ die("no docs/docsHtmlView workspace; run quickstart/setup first")
255
+ if os.path.islink(workspace):
256
+ die("docsHtmlView/ is a symlink")
257
+
258
+ cfg = read_config(workspace)
259
+ site_title_cfg = cfg.get("site_title")
260
+ if site_title_cfg is not None and not isinstance(site_title_cfg, str):
261
+ die("site_title must be a string")
262
+ order = cfg.get("navigation_order")
263
+ if order is None:
264
+ order = []
265
+ elif not isinstance(order, list) or not all(isinstance(x, str) for x in order):
266
+ die("navigation_order must be a list of strings")
267
+ order_map = {rel.strip("/"): i for i, rel in enumerate(order)}
268
+
269
+ roots, missing = resolve_roots(cfg, docs)
270
+ if missing:
271
+ warn("missing/empty trees: " + ", ".join(missing))
272
+ if not roots:
273
+ print(
274
+ "no documents in the approved trees; nothing to scaffold (run after adding docs)",
275
+ file=sys.stderr,
276
+ )
277
+ sys.exit(1)
278
+
279
+ for name in roots:
280
+ assert_no_symlinks(os.path.join(docs, name), "source tree " + name)
281
+ nav_root = os.path.join(workspace, "nav")
282
+ if os.path.islink(nav_root):
283
+ die("nav/ is a symlink")
284
+ assert_no_symlinks(nav_root, "nav/")
285
+
286
+ site_title = sanitize(str(site_title_cfg or args.title or "Documentation"))
287
+ outputs = build_outputs(docs, workspace, roots, order_map, site_title)
288
+ collision_preflight(outputs, nav_root)
289
+ verify_inside(outputs, workspace)
290
+
291
+ write_outputs(outputs)
292
+ prune(nav_root, outputs)
293
+
294
+ if args.json:
295
+ print(
296
+ json.dumps(
297
+ {"roots": roots, "generated": sorted(os.path.relpath(p, root) for p in outputs)},
298
+ indent=2,
299
+ )
300
+ )
301
+ else:
302
+ print("generated %d nav file(s) for roots: %s" % (len(outputs), ", ".join(roots)))
303
+
304
+
305
+ if __name__ == "__main__":
306
+ main()
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env python3
2
+ """Prepare the docs render environment for docs-html-view.
3
+
4
+ Stdlib only. Never installs Python or uv, and never touches global/system
5
+ site-packages. It creates or reuses one *recognized* virtualenv and, only when
6
+ explicitly asked, installs the pinned requirements into that venv.
7
+
8
+ Run this only AFTER the user has approved the packages, the environment, and the
9
+ files (the Skill asks; this helper stores no consent state).
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import argparse
14
+ import os
15
+ import subprocess
16
+ import sys
17
+
18
+ MIN_PY = (3, 12)
19
+
20
+
21
+ def venv_python(venv: str) -> str:
22
+ if os.name == "nt":
23
+ return os.path.join(venv, "Scripts", "python.exe")
24
+ return os.path.join(venv, "bin", "python")
25
+
26
+
27
+ def is_recognized_venv(venv: str) -> bool:
28
+ return os.path.isfile(os.path.join(venv, "pyvenv.cfg"))
29
+
30
+
31
+ def python_version(python: str):
32
+ code = "import sys;print('%d.%d' % sys.version_info[:2])"
33
+ try:
34
+ out = subprocess.run(
35
+ [python, "-I", "-c", code], capture_output=True, text=True, check=True
36
+ ).stdout.strip()
37
+ except (OSError, subprocess.CalledProcessError) as exc:
38
+ return None, str(exc)
39
+ try:
40
+ return tuple(int(x) for x in out.split(".")[:2]), out
41
+ except ValueError:
42
+ return None, out
43
+
44
+
45
+ def run(cmd, cwd=None):
46
+ print("+ " + " ".join(cmd))
47
+ return subprocess.run(cmd, cwd=cwd)
48
+
49
+
50
+ def has_pip(python: str) -> bool:
51
+ try:
52
+ return subprocess.run(
53
+ [python, "-m", "pip", "--version"], capture_output=True, text=True
54
+ ).returncode == 0
55
+ except OSError:
56
+ return False
57
+
58
+
59
+ def venv_prefix(python: str):
60
+ try:
61
+ out = subprocess.run(
62
+ [python, "-c", "import sys;print(sys.prefix)"], capture_output=True, text=True
63
+ )
64
+ except OSError:
65
+ return None
66
+ return out.stdout.strip() if out.returncode == 0 else None
67
+
68
+
69
+ def pip_in_venv(python: str, venv: str):
70
+ if not has_pip(python):
71
+ return False, "pip is not available in the interpreter"
72
+ prefix = venv_prefix(python)
73
+ if prefix is None or os.path.realpath(prefix) != os.path.realpath(venv):
74
+ return False, "interpreter does not belong to %s (sys.prefix=%s)" % (venv, prefix)
75
+ return True, ""
76
+
77
+
78
+ def main() -> None:
79
+ default_req = os.path.join(
80
+ os.path.dirname(os.path.abspath(__file__)), os.pardir, "requirements.txt"
81
+ )
82
+ ap = argparse.ArgumentParser(
83
+ description="Create/reuse the docs render venv and optionally install pinned requirements."
84
+ )
85
+ ap.add_argument("--project-root", default=os.getcwd())
86
+ ap.add_argument("--venv", default=".venv-docs", help="venv path (relative to project root unless absolute)")
87
+ ap.add_argument("--base-python", default=None, help="interpreter used to create the venv (default: this one)")
88
+ ap.add_argument("--requirements", default=default_req)
89
+ ap.add_argument("--install", action="store_true", help="install the pinned requirements (post-consent)")
90
+ ap.add_argument("--freeze", metavar="PATH", default=None, help="write `pip freeze` output to PATH (post-consent)")
91
+ args = ap.parse_args()
92
+
93
+ root = os.path.abspath(args.project_root)
94
+ venv = args.venv if os.path.isabs(args.venv) else os.path.join(root, args.venv)
95
+ requirements = os.path.abspath(args.requirements)
96
+
97
+ if os.path.lexists(venv) and not is_recognized_venv(venv):
98
+ sys.exit("refusing: %s exists but is not a virtualenv (no pyvenv.cfg); move it aside first" % venv)
99
+
100
+ if not is_recognized_venv(venv):
101
+ base = args.base_python or sys.executable
102
+ ver, raw = python_version(base)
103
+ if ver is None or ver < MIN_PY:
104
+ sys.exit("refusing: %s reports %s; need Python >= %d.%d" % (base, raw, *MIN_PY))
105
+ print("+ %s -m venv %s" % (base, venv))
106
+ created = subprocess.run(
107
+ [base, "-m", "venv", venv], capture_output=True, text=True
108
+ )
109
+ if created.returncode != 0:
110
+ detail = (created.stderr or created.stdout or "").strip()
111
+ sys.exit(
112
+ "failed to create virtualenv at %s (exit %d).\n%s\n"
113
+ "Ensure python3-venv/ensurepip is available, or pass --venv pointing at "
114
+ "an existing project docs environment." % (venv, created.returncode, detail)
115
+ )
116
+ else:
117
+ print("reusing virtualenv: " + venv)
118
+
119
+ py = venv_python(venv)
120
+ ver, raw = python_version(py)
121
+ if ver is None or ver < MIN_PY:
122
+ sys.exit("refusing: venv python reports %s; need Python >= %d.%d" % (raw, *MIN_PY))
123
+
124
+ if args.install or args.freeze:
125
+ ok, why = pip_in_venv(py, venv)
126
+ if not ok:
127
+ sys.exit("refusing: %s; point --venv at a working docs environment" % why)
128
+
129
+ if args.install:
130
+ if not os.path.isfile(requirements):
131
+ sys.exit("requirements not found: " + requirements)
132
+ result = run(
133
+ [py, "-m", "pip", "install", "--no-input", "--disable-pip-version-check", "-r", requirements],
134
+ cwd=root,
135
+ )
136
+ if result.returncode != 0:
137
+ sys.exit("pip install failed (exit %d)" % result.returncode)
138
+
139
+ if args.freeze:
140
+ result = subprocess.run([py, "-m", "pip", "freeze"], capture_output=True, text=True)
141
+ if result.returncode != 0:
142
+ sys.exit("pip freeze failed (exit %d)" % result.returncode)
143
+ dest = args.freeze if os.path.isabs(args.freeze) else os.path.join(root, args.freeze)
144
+ os.makedirs(os.path.dirname(dest) or ".", exist_ok=True)
145
+ with open(dest, "w", encoding="utf-8", newline="\n") as fh:
146
+ fh.write(result.stdout)
147
+ print("wrote freeze: " + dest)
148
+
149
+ print("python: " + py)
150
+ print("install: " + ("yes" if args.install else "no"))
151
+
152
+
153
+ if __name__ == "__main__":
154
+ main()
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: project-docs-workflow
3
- description: Initialize or align a project's documentation workflow — issue tracker, ADRs, PDR product specs (goal/current), recommended five-step development workflow, and domain terminology. Run manually once per project (idempotent); creates missing files and reports alignment without overwriting.
3
+ description: Initialize or align a project's documentation workflow — issue tracker, ADRs, PDR target specs with a separate Current status tree, recommended five-step development workflow, and domain terminology. Run manually once per project (idempotent); creates missing files and reports alignment without overwriting.
4
4
  disable-model-invocation: true
5
5
  ---
6
6
 
@@ -20,10 +20,16 @@ Run it manually, once per project — on adoption, or again after the skill's te
20
20
 
21
21
  ## 依赖解析(两级)
22
22
 
23
- External skill references resolve in two levels:
23
+ External capability references resolve in two levels:
24
24
 
25
- 1. **Invoke the skill by name** — the user has the full version installed, or the bundled lite version ships with the package (canonical names: `grilling`, `domain-modeling`, `tdd`, `deepwork`, `retro`, `doc-opt`, `improve-codebase-architecture`; the full version wins when present).
26
- 2. **Fallback** — if that skill is absent, follow `references/deps/<name>.md`; `deepwork` / `retro` / `improve-codebase-architecture` share `references/deps/boundaries.md`.
25
+ 1. **Invoke the installed skill by name** — when the user has the standalone skill installed (canonical names: `grilling`, `grill-me`, `grill-with-docs`, `domain-modeling`, `tdd`, `deepwork`, `retro`, `improve-codebase-architecture`, plus the first-party `doc-opt`); the installed version wins.
26
+ 2. **Internal fallback** — otherwise **execute** the bundled reference(s) as instructions; do **not** invoke them as skills (there are none). Most names map to `references/deps/<name>.md`; the aliases map explicitly:
27
+ - `grill-me` → `references/deps/grilling.md` (entry alias of `grilling`, no separate flow)
28
+ - `grill-with-docs` → `references/deps/grilling.md` **+** `references/deps/domain-modeling.md`, interleaved (grilling rounds + immediate term/ADR writes)
29
+ - `deepwork` / `retro` / `improve-codebase-architecture` → `references/deps/boundaries.md`
30
+ The package ships **no** standalone dependency skills; these internal references are the always-available fallback.
31
+
32
+ > **Trigger policy.** Follow each installed skill's invocation metadata; wrappers such as `grill-me` / `grill-with-docs` may be user-invoked only. An optional name does **not** imply availability: if it is not installed, use the internal fallback — never invoke a missing skill or autonomously invoke a user-only wrapper. Per-name routing lives in `references/workflow.md`.
27
33
 
28
34
  Canonical wording (templates use the same line with the deps path parameterized): 调用 `<skill>`(未安装则按 `<deps 路径>/<file>.md` 执行)— skill-internal docs use `references/deps/<file>.md`, materialized project files use `docs/agents/deps/<file>.md`.
29
35
 
@@ -32,7 +38,7 @@ Canonical wording (templates use the same line with the deps path parameterized)
32
38
  Run from the target project root (`cwd`).
33
39
 
34
40
  1. **Inventory** every artifact in the checklist below. Classify each as *missing* or *existing*. Done when every row has a classification.
35
- 2. **Create missing** files from their template, and create the issue directory tree.
41
+ 2. **Create missing** files from their template, and create the issue directory tree. Create the `docs/current/00-README.md` overview when missing — do not force module files.
36
42
  2b. **Fill placeholders**: fill only obvious, determinable *fill-in* placeholders (e.g. project name from the repo/directory name); list every remaining fill-in placeholder under `Next:` in the report. Format notation such as `<标题>` is documentation, not a placeholder — skip it.
37
43
  3. **Compare existing** files against their template: classify *aligned*, or *diff* with the specific differences listed.
38
44
  4. **Report** using the format below.
@@ -49,13 +55,16 @@ Run from the target project root (`cwd`).
49
55
  | `docs/issues/` directory tree | none — create dirs (below) |
50
56
  | `docs/adr/README.md` | `assets/templates/project/adr-readme.md` |
51
57
  | `docs/pdr/00-README.md` | `assets/templates/project/pdr-00-readme.md` |
58
+ | `docs/current/00-README.md` | `assets/templates/project/current-00-readme.md` |
52
59
  | `CONTEXT.md` | `assets/templates/project/context.md` |
53
60
 
54
61
  - Create `AGENTS.md` only if missing — its whole content is this block; a file containing only this section is intentional (projects add their own guidance around it). If `AGENTS.md` exists: heading `## Agent skills` present → compare the block in place (never edit); absent → append the block at end of file, separated by one blank line. Only `AGENTS.md` is touched, never `CLAUDE.md`.
55
62
  - The provenance line sits immediately under the `## Agent skills` heading and is intentional — keep it (it identifies materialized files and their generation).
56
63
  - Issue tree: `bug/{human,agent,history}`, `feature/{human,agent,history}`, `chore/{human,agent,history}`, `suggestion/{human,agent}`; add a `.gitkeep` to each leaf directory (one containing no files). Do not run `git init`.
57
64
  - Dep fallbacks: init copies `references/deps/*` into the project's `docs/agents/deps/` as a whole-directory copy (content unchanged), so target projects are self-sufficient even without this skill installed.
65
+ - Current tree: init creates the `docs/current/00-README.md` overview when missing; it does **not** force module files — add them as modules need describing. The Current tree is peer to PDR, never embedded in it.
58
66
  - Domain glossaries (e.g. `docs/common/glossary.md`) are **not** scaffolded; the rules cover them and projects create them on demand.
67
+ - Rendering is **out of scope for init**: init creates no renderer config, artifacts, or hooks. HTML rendering is an optional, manual first-party skill (`docs-html-view`); see `references/rendering.md`.
59
68
 
60
69
  ### Alignment comparison (existing files)
61
70
 
@@ -63,6 +72,8 @@ Compare **rules, not bytes**. An existing file is **aligned** when every require
63
72
 
64
73
  **diff** = a required rule/section is missing, or a default rule is contradicted. Diffs are informational; init never auto-fixes. One bullet per file: `- <file>: <missing or contradicted rule>`.
65
74
 
75
+ **Legacy drift** (existing projects): embedded current/progress material found in an existing project — e.g. per-module `04-current.md`, a `当前进展` section in the PDR README — is reported as informational legacy drift. init never moves, rewrites, or deletes it, and there is no installation migration script; migrating to a separate Current tree requires the user's explicit authorization.
76
+
66
77
  Ignore in every comparison: the provenance line; filled placeholders; index/table rows; module/term/entry names; language; extra content.
67
78
 
68
79
  Required elements per artifact:
@@ -70,9 +81,10 @@ Required elements per artifact:
70
81
  - `AGENTS.md` block: `## Agent skills` heading + three subsections (`### Issue tracker`, `### Domain docs`, `### Development workflow`).
71
82
  - `docs/agents/issues.md`: directory contract; human/agent responsibility; entry format (`Status:` line, `## 完成判据`, `## 步骤`, `## Log`; the `## 结果` section itself is optional); archiving suffixes (`-done`/`-drop`); suggestion routing.
72
83
  - `docs/agents/domain.md`: read CONTEXT.md + docs/adr before exploring; silently continue if missing; glossary vocabulary rule; ADR-conflict flagging.
73
- - `docs/agents/workflow.md`: recommended five-step flow (规划 / 骨架 / 细节 / 异常 / 回顾 with 回顾 = docs-sync wrap-up); align-before-build ordering; conversational alignment (no freeze); elastic granularity suggestion (≈0.5 人天 heuristic); issue `## 步骤` tracking + resume anchor; PDR goal/current gap→issue derivation; boundaries (deepwork / tdd / retro / grilling 调用 + 兜底指针).
74
- - `docs/adr/README.md`: lifecycle rules (edit+Log, delete→tombstone stub, no renumber); catalog table present.
75
- - `docs/pdr/00-README.md`: purpose; 最终目标 / 当前进展 overview sections; module index; reading order.
84
+ - `docs/agents/workflow.md`: recommended five-step flow (规划 / 骨架 / 细节 / 异常 / 回顾 with 回顾 = docs-sync wrap-up); align-before-build ordering; conversational alignment (no freeze); elastic granularity suggestion (≈0.5 人天 heuristic); issue `## 步骤` tracking + resume anchor; PDR-target / Current gap→issue candidate derivation; wrap-up updates Current in place (facts only, no self-link or task history); boundaries (deepwork / tdd / retro / grilling 调用 + 兜底指针).
85
+ - `docs/adr/README.md`: lifecycle rules (edit+Log, delete→tombstone stub, no renumber); catalog table present; reference sweep covers both `ADR-XXXX` and `adr/<NNNN>-<slug>.md` path links (any relative prefix).
86
+ - `docs/pdr/00-README.md`: purpose; end-state overview; module index; reading order; link to the peer Current tree (no progress section, no embedded current); optional descriptive ADR link rule.
87
+ - `docs/current/00-README.md`: purpose; the baseline (revision/date) the tree describes; module/topic index; reading order; link to the peer PDR tree; records as-built capabilities/limits, not task logs or planned work; optional descriptive ADR link rule.
76
88
  - `CONTEXT.md`: glossary structure (term blocks with `_Avoid_`).
77
89
  - `docs/issues/` tree: leaf directories exist (`.gitkeep` may be checked); extras informational only.
78
90
  - `docs/agents/deps/`: leaf files exist (`grilling.md`, `domain-modeling.md`, `tdd.md`, `doc-opt.md`, `boundaries.md`); extras informational only.
@@ -86,6 +98,7 @@ Write the report in the project's documentation language (default: Chinese, matc
86
98
  - Created: …
87
99
  - Aligned: …
88
100
  - Diff: one bullet per file: `- <file>: <missing or contradicted rule>`(仅供参考,不自动改;multi-file → multiple bullets)
101
+ - Legacy drift: embedded current/progress paths found (e.g. `04-current.md`, `当前进展` section) — informational only, never moved or rewritten; migration needs explicit user authorization.
89
102
  - Next: remaining placeholders plus a one-line "do this first".
90
103
  ```
91
104
 
@@ -95,14 +108,16 @@ Rules carry the detail; read the one you need.
95
108
 
96
109
  - `references/issues.md` — issue tracker directory contract, entry format, `suggestion` routing.
97
110
  - `references/adr.md` — ADR lifecycle (edit + Log, delete → tombstone stub, reference sweep, catalog index), body shape.
98
- - `references/pdr.md` — PDR product-spec structure (`00-README` / `01-spec` / `02-interfaces` / `03-nfr` / `04-current` + `design/`), the goal/current & gap→issues layer, PDR↔ADR boundary.
111
+ - `references/pdr.md` — PDR target-spec structure (`00-README` / `01-spec` / `02-interfaces` / `03-nfr` + `design/`), the target & gap→candidate-issue layer, PDR↔ADR and PDR↔Current boundaries.
112
+ - `references/current.md` — Current as-built tree (`00-README` + module/topic docs), baseline & verification confidence, sync semantics, peer relationship to PDR, legacy-drift adoption.
113
+ - `references/rendering.md` — optional docs HTML rendering contract: render scope (`docs/pdr` + `docs/current` + the complete `docs/adr`, not issues / agents), link-authoring rules (PDR↔Current, optional descriptive ADR path links), sources never rewritten, manual opt-in.
99
114
  - `references/terminology.md` — `CONTEXT.md` primary glossary, domain glossary, usage rules.
100
115
  - `references/workflow.md` — recommended single-issue development flow (five steps, alignment, granularity, tracking, docs-sync hooks, skill boundaries).
101
- - `references/coexistence.md` — how this profile sits beside `domain-modeling`, `grilling`, `deepwork`, `tdd`, `retro`, and the bundled `doc-opt`; and how its docs/issues conventions supersede older `.scratch`-style trackers.
102
- - `references/deps/` — fallback procedures for the two-level resolution: `grilling` / `domain-modeling` / `tdd` / `doc-opt` / `boundaries` (`deepwork` / `retro` / `improve-codebase-architecture` share `boundaries.md`).
116
+ - `references/coexistence.md` — how this profile sits beside `domain-modeling`, `grilling`, `deepwork`, `tdd`, `retro`, and the first-party `doc-opt`; and how its docs/issues conventions supersede older `.scratch`-style trackers.
117
+ - `references/deps/` — internal fallback references for the two-level resolution: `grilling` / `domain-modeling` / `tdd` / `doc-opt` / `boundaries` (`deepwork` / `retro` / `improve-codebase-architecture` share `boundaries.md`).
103
118
 
104
119
  ## Templates
105
120
 
106
121
  - `assets/templates/project/*` — the files init materializes (Chinese). They carry the provenance marker (current generation: `v2`; in `agents-block.md` it sits just under the `## Agent skills` heading) — keep it; future generations update the marker so stale copies are detectable.
107
- - `references/deps/*` — fallback procedures when an external skill is absent. init copies the whole directory as-is into the project's `docs/agents/deps/`; they are not materialized templates and carry no provenance marker.
122
+ - `references/deps/*` — internal fallback references used when a standalone dependency skill is absent. init copies the whole directory as-is into the project's `docs/agents/deps/`; they are not materialized templates and carry no provenance marker.
108
123
  - `assets/templates/entries/*` — day-to-day authoring aids used when this skill is at hand (copy the skeleton when creating a new entry). NOT materialized by init; the rule docs are self-sufficient without them; materialized docs may reference their path opportunistically.
@@ -0,0 +1,19 @@
1
+ <!-- 文件名:<模块>.md,位于 docs/current/;记录实际能力与限制;基线默认继承 00-README.md;不写任务日志 / 待办 / 计划中的能力。 -->
2
+
3
+ # <模块> 现状
4
+
5
+ > 关联:[PDR <模块>](../pdr/NN-<模块>/00-README.md)(需要指向决策时可加描述性短语 + 稳定 ADR 链接,例:支持 [离线使用](../adr/0003-offline.md);不转述 ADR 理由、不自动推断链接)
6
+
7
+ <基线默认继承 `00-README.md`;仅当本文件基于不同基线时在此覆写:基线 <具体 revision / release> | 快照 YYYY-MM-DD。>
8
+
9
+ ## 实际能力
10
+
11
+ - <现已具备的能力,行为口径;对应 PDR 的 FR-<模块>-NN(如有)。>
12
+
13
+ ## 关键行为与限制
14
+
15
+ - <关键行为 / 边界;已知限制、注意事项。>
16
+
17
+ ## 验证信心(仅例外时写)
18
+
19
+ - <仅当某结论的验证信心需单独说明(未验证 / 推断)时列出;已按默认口径验证的结论不必逐条举证。>
@@ -2,7 +2,7 @@
2
2
 
3
3
  # <模块> 功能规格
4
4
 
5
- > 定位:<模块> 目标态功能规格(FR-<模块>-NN,逐条给验收判据)。依据见 [design/](design/)(无 design/ 目录则删去该链接)与相关 ADR。
5
+ > 定位:<模块> 目标态功能规格(FR-<模块>-NN,逐条给验收判据)。依据见 [design/](design/)(无 design/ 目录则删去该链接);需要指向决策时,用描述性短语 + 指向稳定 ADR 文件的普通 Markdown 链接(例:支持 [离线使用](../../adr/0003-offline.md))——不转述 ADR 理由,不要求每条 FR 都挂 ADR,也不自动推断链接。
6
6
 
7
7
  ## 1. <功能分组>
8
8
 
@@ -17,7 +17,8 @@
17
17
  - `> 经 ADR-XXXX 修订(日期):一句话`
18
18
  - `> 已废弃(日期):原因`
19
19
  - `> 已删除(日期):原因`(墓碑桩)
20
- - **删除或语义修改**触发引用核查:优先派 subagent,无则自行 grep `ADR-\d{4}`(范围 = 全仓库;`docs/` 必改,代码注释逐一判定:保留 / 更新 / 就地注释;逐一处理后再收尾)。引用优先改指替代 ADR 编号;无替代则删去该句或就地注明「(ADR-XXXX 已删除)」。
20
+ - **删除或语义修改**触发引用核查:优先派 subagent,无则自行 grep;须覆盖两种写法——`ADR-\d{4}` 编号,以及指向 ADR 文件的 Markdown 路径链接(按稳定文件名 `adr/<NNNN>-<slug>.md` 匹配,相对前缀任意,如 `docs/adr/…`、`../adr/…`、`../../adr/…`,可带 `#fragment`)。范围 = 全仓库;`docs/` 必改,代码注释逐一判定:保留 / 更新 / 就地注释;逐一处理后再收尾。引用优先改指替代 ADR 编号;无替代则删去该句或就地注明「(ADR-XXXX 已删除)」。
21
+ - PDR / Current 可用描述性短语 + 普通 Markdown 链接指向 ADR 文件(例:支持 [离线使用](../adr/0003-offline.md)),不强制写 `ADR-XXXX` 标签;不要求每条需求都挂 ADR,也不自动推断链接。路径链接在墓碑桩化后仍有效(文件名不可变、墓碑桩照常渲染);但语义修改改变了链接所指内容,故语义修改后须重新核对路径链接,而不只是编号。
21
22
 
22
23
  ## 目录
23
24
 
@@ -12,4 +12,4 @@
12
12
 
13
13
  ### Development workflow
14
14
 
15
- 推荐五步流程:规划 / 骨架 / 细节 / 异常 / 回顾。开工前先读 PDR 目标与现状(各模块 `04-current.md`),与用户对齐本次范围与粒度;步骤记录在 issue 的 `## 步骤`。约定见 `docs/agents/workflow.md`。
15
+ 推荐五步流程:规划 / 骨架 / 细节 / 异常 / 回顾。开工前先读 PDR 目标(`docs/pdr/`)与 Current 现状(`docs/current/`),与用户对齐本次范围与粒度;步骤记录在 issue 的 `## 步骤`。约定见 `docs/agents/workflow.md`。
@@ -11,9 +11,9 @@
11
11
  - 根目录的 **`CONTEXT.md`**。
12
12
  - **`docs/adr/`**:读取与将要改动的领域相关的 ADR;规则与目录见 `docs/adr/README.md`。
13
13
 
14
- 如果这些文件不存在,**静默继续**:不要提示缺失,也不要主动建议创建它们。`/domain-modeling`(未安装则按 `docs/agents/deps/domain-modeling.md` 执行;经 `/grill-with-docs` 与 `/improve-codebase-architecture` 到达,后者未安装则按 `docs/agents/deps/boundaries.md` 执行)会在词条或决策真正定案时按需创建。
14
+ 如果这些文件不存在,**静默继续**:不要提示缺失,也不要主动建议创建它们。`domain-modeling`(未安装则按 `docs/agents/deps/domain-modeling.md` 执行)会在词条或决策真正定案时按需创建。
15
15
 
16
- 外部技能未安装时的兜底流程物化在 `docs/agents/deps/`。
16
+ 外部技能未安装时的兜底流程物化在 `docs/agents/deps/`。**组合回退**:`grill-with-docs` 是 `grilling` + `domain-modeling` 的包装(术语确定即写入 `CONTEXT.md`,满足三条判据记 ADR);未安装该包装时,按 `docs/agents/deps/grilling.md` 与 `docs/agents/deps/domain-modeling.md` 交织执行。`improve-codebase-architecture` 未安装时按 `docs/agents/deps/boundaries.md` 执行。以上均为**可选的独立技能名,不代表本包已随附**;本 skill 不会自动安装它们。
17
17
 
18
18
  ## File structure
19
19
 
@@ -35,7 +35,7 @@
35
35
 
36
36
  当你的产出提到某个领域概念(issue 标题、重构提案、假设、测试名)时,使用 `CONTEXT.md` 中定义的术语。不要漂移到术语表明确回避的同义词。
37
37
 
38
- 如果你需要的概念还不在术语表里,这本身是一个信号:要么你在发明项目不使用的语言(重新考虑),要么存在真实缺口(记下来,交给 `/domain-modeling`;未安装则按 `docs/agents/deps/domain-modeling.md` 执行)。
38
+ 如果你需要的概念还不在术语表里,这本身是一个信号:要么你在发明项目不使用的语言(重新考虑),要么存在真实缺口(记下来,交给 `domain-modeling`;未安装则按 `docs/agents/deps/domain-modeling.md` 执行)。
39
39
 
40
40
  ## Flag ADR conflicts
41
41