bigpowers 2.86.7 → 2.87.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/.agents/skills/simple-english/SKILL.md +346 -0
  2. package/.agents/skills/write-document/SKILL.md +1 -1
  3. package/.cline/skills/simple-english/SKILL.md +346 -0
  4. package/.cline/skills/write-document/SKILL.md +1 -1
  5. package/.codebuddy/skills/simple-english/SKILL.md +346 -0
  6. package/.codebuddy/skills/write-document/SKILL.md +1 -1
  7. package/.codex/skills/simple-english/SKILL.md +346 -0
  8. package/.codex/skills/write-document/SKILL.md +1 -1
  9. package/.copilot/skills/simple-english/SKILL.md +346 -0
  10. package/.copilot/skills/write-document/SKILL.md +1 -1
  11. package/.hermes/skills/simple-english/SKILL.md +346 -0
  12. package/.hermes/skills/write-document/SKILL.md +1 -1
  13. package/.kilocode/rules/simple-english.md +346 -0
  14. package/.kilocode/rules/write-document.md +1 -1
  15. package/.mimocode/skills/simple-english/SKILL.md +346 -0
  16. package/.mimocode/skills/write-document/SKILL.md +1 -1
  17. package/.opencode/skills/simple-english/SKILL.md +346 -0
  18. package/.opencode/skills/write-document/SKILL.md +1 -1
  19. package/.pi/package.json +2 -2
  20. package/.pi/prompts/simple-english.md +345 -0
  21. package/.pi/prompts/write-document.md +1 -1
  22. package/.pi/skills/simple-english/SKILL.md +346 -0
  23. package/.pi/skills/write-document/SKILL.md +1 -1
  24. package/.qwen/skills/simple-english/SKILL.md +346 -0
  25. package/.qwen/skills/write-document/SKILL.md +1 -1
  26. package/.trae/skills/simple-english/SKILL.md +346 -0
  27. package/.trae/skills/write-document/SKILL.md +1 -1
  28. package/.windsurf/rules/simple-english.md +346 -0
  29. package/.windsurf/rules/write-document.md +1 -1
  30. package/.zcode/skills/simple-english/SKILL.md +346 -0
  31. package/.zcode/skills/write-document/SKILL.md +1 -1
  32. package/CHANGELOG.md +15 -0
  33. package/CONVENTIONS.md +1 -0
  34. package/NOTICES.md +62 -0
  35. package/README.md +1 -1
  36. package/SKILL-INDEX.md +11 -10
  37. package/package.json +1 -1
  38. package/scripts/check-skill-links.py +2 -1
  39. package/scripts/lib/__pycache__/link_utils.cpython-312.pyc +0 -0
  40. package/scripts/lib/__pycache__/srp_engine_links.cpython-312.pyc +0 -0
  41. package/skills/simple-english/REFERENCE.md +275 -0
  42. package/skills/simple-english/SKILL.md +71 -0
  43. package/skills/simple-english/scripts/ste_lint.py +446 -0
  44. package/skills/write-document/SKILL.md +1 -1
  45. package/skills-lock.json +6 -1
@@ -0,0 +1,446 @@
1
+ #!/usr/bin/env python3
2
+ """Machine gate for ASD-STE100 Simplified Technical English.
3
+
4
+ A deterministic regex pass over technical prose. It counts the violations a
5
+ machine can catch: sentence length, contractions, banned modals, perfect
6
+ tenses, "-ing" clauses, semicolons, Latin abbreviations, slop words, trailing
7
+ conditions, synonym rotation, and terminology drift. It adds two structural
8
+ checks the upstream STE skills omit: heading hierarchy and non-descriptive link
9
+ text. When Vale is on PATH and a `.vale.ini` lives up the tree, it defers the
10
+ style layer to Vale and keeps only the STE checks.
11
+
12
+ Ceiling: this is a regex pass, not a grammar parser. It undercounts (no
13
+ passive-voice detection, no part-of-speech checks) and it can misread sentence
14
+ bounds in unusual markdown. Counts are comparable between two texts run through
15
+ the same version. They are not a compliance verdict. No tool can guarantee STE
16
+ compliance.
17
+
18
+ Synthesis of two MIT sources:
19
+ - ste_lint.py AminBlg/SimpleEnglish (11 STE checks, JSON mode)
20
+ - check_docs.py JuanMarchetto/doc-standards (drift detector, Vale handoff)
21
+
22
+ Usage:
23
+ python3 ste_lint.py --self-test
24
+ python3 ste_lint.py file.md # lint one file (exit 1 on error)
25
+ python3 ste_lint.py path/ # lint a tree of markdown
26
+ python3 ste_lint.py file.md --json # JSON report instead of line output
27
+ python3 ste_lint.py file.md --type procedural # procedural limit (20); default descriptive (25)
28
+ cat text.md | python3 ste_lint.py - # read from stdin
29
+ python3 ste_lint.py file.md --no-vale # force built-in style checks
30
+
31
+ Exit code 1 on any error-level finding, else 0. Warnings never fail the gate.
32
+ """
33
+ from __future__ import annotations
34
+
35
+ import argparse
36
+ import json
37
+ import re
38
+ import shutil
39
+ import subprocess
40
+ import sys
41
+ from pathlib import Path
42
+ from typing import List, Tuple
43
+
44
+ # --------------------------------------------------------------------------
45
+ # Rule constants (paraphrased from ASD-STE100 Issue 9, 2025-01-15)
46
+ # --------------------------------------------------------------------------
47
+
48
+ LIMITS = {"procedural": 20, "descriptive": 25}
49
+ DEFAULT_TYPE = "descriptive"
50
+ LONG_SENTENCE_ERROR = 40 # hard error regardless of mode (prose, not a step)
51
+
52
+ # Approved modals: can, will, must. Banned: should, would, may, might, could.
53
+ BANNED_MODALS = re.compile(r"\b(should|would|may|might|could)\b", re.I)
54
+ PERFECT = re.compile(
55
+ r"\b(has|have|had)\s+been\b|\b(has|have)\s+\w+ed\b", re.I
56
+ )
57
+ PROGRESSIVE_PASSIVE = re.compile(
58
+ r"\b(?:is|are|was|were)\s+being\s+\w+(?:ed|en)?\b", re.I
59
+ )
60
+ CONTRACTION = re.compile(
61
+ r"\b\w+(n't|'ll|'re|'ve|'d)\b|\bit's\b|\byou're\b|\bdon't\b", re.I
62
+ )
63
+ # "-ing" clause used as a verb (Rule 3.5): ", making ...", ", allowing ..."
64
+ ING_CLAUSE = re.compile(
65
+ r",\s*(mak|allow|enabl|ensur|highlight|creat|provid|offer|help|reduc|"
66
+ r"improv|lead|caus|result)ing\b",
67
+ re.I,
68
+ )
69
+ LATIN = re.compile(r"\b(e\.g\.|i\.e\.|etc\.?)(?=[\s,);]|$)", re.I)
70
+ SLOP = re.compile(
71
+ r"\b(simply|seamlessly|effortlessly|robust|leverag\w*|utiliz\w*|"
72
+ r"comprehensive|powerful|blazingly|streamlin\w*|facilitat\w*|"
73
+ r"performant|plethora|myriad|delve|crucial|pivotal)\b",
74
+ re.I,
75
+ )
76
+ # Condition at the wrong end of a sentence (Rule 5.4): "... if X."
77
+ TRAILING_COND = re.compile(r"\w[^.!?\n]{3,}\s\b(if|when)\b\s", re.I)
78
+
79
+ # Positional references break under retrieval: "as mentioned above".
80
+ POSITIONAL = re.compile(
81
+ r"\b(?:as\s+(?:mentioned|shown|described|noted)\s+)?(?:above|below)\b"
82
+ r"(?=[,.\s)])",
83
+ re.I,
84
+ )
85
+
86
+ # Terminology drift: 2+ synonyms each used 2+ times = drift.
87
+ SYNONYM_SETS: List[List[str]] = [
88
+ ["verify", "check", "confirm", "ensure", "make sure", "validate"],
89
+ ["cancel", "abort", "terminate"],
90
+ ["remove", "delete", "erase"],
91
+ ["display", "show", "render"],
92
+ ["directory", "folder"],
93
+ ["parameter", "argument", "flag", "option"],
94
+ ]
95
+
96
+ # Markdown atoms.
97
+ MD_LINK = re.compile(r"\[([^\]]+)\]\(([^)#\s]+)(?:#[^)\s]*)?\)")
98
+ HEADING = re.compile(r"^(#{1,6})\s+(.*)$")
99
+ CODE_FENCE = re.compile(r"^(```|~~~)")
100
+ STEP_ITEM = re.compile(r"^\s*(?:\d+[.)]|[-*+])\s+")
101
+
102
+
103
+ # --------------------------------------------------------------------------
104
+ # Text cleaning
105
+ # --------------------------------------------------------------------------
106
+
107
+
108
+ def strip_inline(text: str) -> str:
109
+ """Remove markdown atoms that would distort word counts and word scans.
110
+
111
+ Order matters: strip images and links before bare URLs, or the closing
112
+ paren of a link gets eaten by the URL pass.
113
+ """
114
+ text = re.sub(r"!\[[^\]]*\]\([^)]*\)", "", text)
115
+ text = re.sub(r"\[([^\]]+)\]\([^)]*\)", r"\1", text)
116
+ text = re.sub(r"`[^`\n]+`", "CODESPAN", text)
117
+ text = re.sub(r"https?://[^\s)]+", "URL", text)
118
+ return text
119
+
120
+
121
+ def strip_code(text: str) -> str:
122
+ """Strip fenced code blocks, headings, and tables before STE scans."""
123
+ text = re.sub(r"```.*?```", " ", text, flags=re.S)
124
+ text = re.sub(r"~~~.*?~~~", " ", text, flags=re.S)
125
+ text = strip_inline(text)
126
+ text = re.sub(r"^#+\s.*$", " ", text, flags=re.M) # headings exempt (8.6)
127
+ text = re.sub(r"^[|].*$", " ", text, flags=re.M) # table rows exempt
128
+ text = re.sub(r"^\s*([-*]|\d+\.)\s+", "", text, flags=re.M) # list markers
129
+ return text
130
+
131
+
132
+ def sentences(text: str) -> List[str]:
133
+ """Split into sentences on terminal punctuation followed by a capital."""
134
+ parts = re.split(r"(?<=[.!?:])\s+(?=[A-Z`\"'])", text)
135
+ return [p.strip() for p in parts if len(p.strip().split()) >= 2]
136
+
137
+
138
+ # --------------------------------------------------------------------------
139
+ # STE checks (file-level counts)
140
+ # --------------------------------------------------------------------------
141
+
142
+
143
+ def ste_counts(body: str, text_type: str) -> dict:
144
+ """Return per-violation counts for the STE checks on a cleaned body."""
145
+ limit = LIMITS[text_type]
146
+ sents = sentences(body)
147
+ lengths = [len(s.split()) for s in sents]
148
+ counts: dict = {}
149
+ counts["sentence_over_limit"] = sum(1 for n in lengths if n > limit)
150
+ counts["contraction"] = len(CONTRACTION.findall(body))
151
+ counts["banned_modal"] = len(BANNED_MODALS.findall(body))
152
+ counts["perfect_tense"] = len(PERFECT.findall(body))
153
+ counts["progressive_passive"] = len(PROGRESSIVE_PASSIVE.findall(body))
154
+ counts["ing_clause"] = len(ING_CLAUSE.findall(body))
155
+ counts["semicolon"] = body.count(";")
156
+ counts["latin_abbrev"] = len(LATIN.findall(body))
157
+ counts["slop_word"] = len(SLOP.findall(body))
158
+ counts["trailing_condition"] = sum(
159
+ 1
160
+ for s in sents
161
+ if TRAILING_COND.search(s) and not re.match(r"^(if|when)\b", s, re.I)
162
+ )
163
+ counts["positional_reference"] = len(POSITIONAL.findall(body))
164
+
165
+ rotation = 0
166
+ for syn_set in SYNONYM_SETS:
167
+ used = [
168
+ w
169
+ for w in syn_set
170
+ if len(re.findall(rf"\b{re.escape(w)}\b", body, re.I)) >= 2
171
+ ]
172
+ if len(used) >= 2:
173
+ rotation += len(used) - 1
174
+ counts["synonym_rotation"] = rotation
175
+
176
+ words = max(1, len(body.split()))
177
+ total = sum(counts.values())
178
+ return {
179
+ "type": text_type,
180
+ "words": words,
181
+ "sentences": len(sents),
182
+ "mean_sentence_words": round(sum(lengths) / max(1, len(lengths)), 1),
183
+ "longest_sentence_words": max(lengths, default=0),
184
+ "violations": counts,
185
+ "violations_total": total,
186
+ "violations_per_100w": round(100.0 * total / words, 2),
187
+ }
188
+
189
+
190
+ # --------------------------------------------------------------------------
191
+ # Structural checks (line-level, file-aware) — from check_docs.py
192
+ # --------------------------------------------------------------------------
193
+
194
+ # Severity: "error" fails the gate; "warning" does not.
195
+ POSITIONAL_FINDING = ("warning", "positional reference breaks under retrieval; link to the section")
196
+
197
+
198
+ def lint_structural(path: Path, lines: List[str]) -> List[Tuple[int, str, str]]:
199
+ """Heading-hierarchy + link-text checks. Returns (line_no, level, msg)."""
200
+ findings: List[Tuple[int, str, str]] = []
201
+ in_code = False
202
+ levels_seen: List[int] = []
203
+ headings_text: List[str] = []
204
+ h1_count = 0
205
+ for i, raw in enumerate(lines, 1):
206
+ if CODE_FENCE.match(raw.strip()):
207
+ in_code = not in_code
208
+ continue
209
+ if in_code:
210
+ continue
211
+ m = HEADING.match(raw)
212
+ if m:
213
+ level, text = len(m.group(1)), m.group(2).strip()
214
+ if level == 1:
215
+ h1_count += 1
216
+ if h1_count > 1:
217
+ findings.append((i, "error", "multiple h1 headings on one page"))
218
+ if levels_seen and level > levels_seen[-1] + 1:
219
+ findings.append(
220
+ (i, "error", f"skipped heading level (h{levels_seen[-1]} -> h{level})")
221
+ )
222
+ levels_seen.append(level)
223
+ low = text.lower()
224
+ if low in headings_text:
225
+ findings.append(
226
+ (i, "warning", f"duplicate heading '{text}' (anchors collide)")
227
+ )
228
+ headings_text.append(low)
229
+ continue
230
+ for link_m in MD_LINK.finditer(raw):
231
+ label = link_m.group(1).strip().lower()
232
+ target = link_m.group(2)
233
+ if label in ("here", "this", "link", "this page"):
234
+ findings.append((i, "error", f"non-descriptive link text '{label}'"))
235
+ if not target.startswith(("http://", "https://", "mailto:", "/")):
236
+ if not (path.parent / target).exists():
237
+ findings.append((i, "error", f"broken relative link '{target}'"))
238
+ return findings
239
+
240
+
241
+ # --------------------------------------------------------------------------
242
+ # Linter
243
+ # --------------------------------------------------------------------------
244
+
245
+
246
+ class Linter:
247
+ def __init__(self, text_type: str, use_vale: bool, style: bool):
248
+ self.text_type = text_type
249
+ self.vale = use_vale and shutil.which("vale") is not None
250
+ # Built-in style checks run only when Vale does not take the style layer.
251
+ self.style = style and not self.vale
252
+ self.findings: List[Tuple[Path, int, str, str]] = []
253
+
254
+ def add(self, path: Path, line: int, level: str, msg: str) -> None:
255
+ self.findings.append((path, line, level, msg))
256
+
257
+ def lint_file(self, path: Path) -> None:
258
+ raw_text = path.read_text(encoding="utf-8", errors="replace")
259
+ lines = raw_text.splitlines()
260
+
261
+ # --- STE checks on the cleaned body ---
262
+ body = strip_code(raw_text)
263
+ rep = ste_counts(body, self.text_type)
264
+ for key, n in rep["violations"].items():
265
+ if n <= 0:
266
+ continue
267
+ level = "error" if key in ("banned_modal", "contraction") else "warning"
268
+ self.add(path, 0, level, f"{key}: {n}")
269
+
270
+ # hard error on runaway sentences regardless of mode
271
+ if rep["longest_sentence_words"] > LONG_SENTENCE_ERROR:
272
+ self.add(
273
+ path,
274
+ 0,
275
+ "error",
276
+ f"sentence has {rep['longest_sentence_words']} words "
277
+ f"(hard limit {LONG_SENTENCE_ERROR})",
278
+ )
279
+
280
+ # --- Structural checks (heading hierarchy, link text) ---
281
+ for line_no, level, msg in lint_structural(path, lines):
282
+ self.add(path, line_no, level, msg)
283
+
284
+ # --- Built-in style layer (only when Vale is absent) ---
285
+ if self.style:
286
+ for sent in sentences(strip_inline(raw_text)):
287
+ if POSITIONAL.search(sent):
288
+ self.add(path, 0, POSITIONAL_FINDING[0], POSITIONAL_FINDING[1])
289
+
290
+ def run_vale(self, target: Path) -> None:
291
+ ini = None
292
+ for parent in [target, *list(target.parents)]:
293
+ if (parent / ".vale.ini").exists():
294
+ ini = parent / ".vale.ini"
295
+ break
296
+ if ini is None:
297
+ # No config: fall back to built-in style checks.
298
+ self.style = True
299
+ self.vale = False
300
+ return
301
+ r = subprocess.run(
302
+ ["vale", "--output=line", str(target)],
303
+ capture_output=True,
304
+ text=True,
305
+ cwd=str(ini.parent),
306
+ )
307
+ for line in r.stdout.splitlines():
308
+ print(line)
309
+ if r.returncode != 0:
310
+ self.findings.append((target, 0, "error", "vale reported errors (above)"))
311
+
312
+
313
+ # --------------------------------------------------------------------------
314
+ # Self-test (used by the skill's `→ verify:` gate)
315
+ # --------------------------------------------------------------------------
316
+
317
+ SLOP_FIXTURE = """Leveraging our robust retry mechanism, failed uploads are automatically
318
+ reattempted, ensuring data integrity is maintained throughout the entire process which has
319
+ been designed from the ground up to gracefully handle even the most challenging network
320
+ interruptions. You should verify your credentials; it's also worth checking the settings,
321
+ e.g. the timeout config. Contact support if the problem persists."""
322
+
323
+ CLEAN_FIXTURE = """The system retries a failed upload automatically. This process keeps the data correct.
324
+
325
+ If failures continue, make sure that your credentials are correct. If the problem continues, contact support."""
326
+
327
+ # Genuine terminology drift: two synonyms (check, verify) each used twice.
328
+ DRIFT_FIXTURE = """To check the build, run the test suite. Then verify the output log.
329
+ Check the exit code. Verify the result against the baseline."""
330
+
331
+
332
+ def self_test() -> None:
333
+ """Assert the linter catches what it must and clears what it should."""
334
+ slop = ste_counts(strip_code(SLOP_FIXTURE), "procedural")
335
+ clean = ste_counts(strip_code(CLEAN_FIXTURE), "procedural")
336
+ drift = ste_counts(strip_code(DRIFT_FIXTURE), "procedural")
337
+ assert slop["violations"]["sentence_over_limit"] >= 1, slop
338
+ assert slop["violations"]["banned_modal"] >= 1, slop
339
+ assert slop["violations"]["contraction"] >= 1, slop
340
+ assert slop["violations"]["perfect_tense"] >= 1, slop
341
+ assert slop["violations"]["ing_clause"] >= 1, slop
342
+ assert slop["violations"]["semicolon"] >= 1, slop
343
+ assert slop["violations"]["latin_abbrev"] >= 1, slop
344
+ assert slop["violations"]["slop_word"] >= 2, slop
345
+ assert slop["violations"]["trailing_condition"] >= 1, slop
346
+ # The slop fixture mentions "verify" and "checking" only once each, so the
347
+ # strict drift detector (2+ synonyms each used 2+ times) must stay silent.
348
+ assert slop["violations"]["synonym_rotation"] == 0, slop
349
+ assert drift["violations"]["synonym_rotation"] >= 1, drift
350
+ assert clean["violations_total"] == 0, clean
351
+
352
+ # Structural: duplicate headings and a skipped level must flag.
353
+ struct = lint_structural(
354
+ Path("x"),
355
+ ["# Title", "## Sub", "#### Skipped", "#### Skipped", ""],
356
+ )
357
+ assert any("skipped heading" in m for _, _, m in struct), struct
358
+ assert any("duplicate heading" in m for _, _, m in struct), struct
359
+
360
+ # Non-descriptive link text must flag as an error.
361
+ link_findings = lint_structural(Path("x"), ["See [here](./x.md) for more."])
362
+ assert any(lvl == "error" for _, lvl, _ in link_findings), link_findings
363
+
364
+ print(
365
+ "self-test OK: "
366
+ f"{slop['violations_total']} violations in slop fixture, "
367
+ "0 in clean, structural checks pass"
368
+ )
369
+
370
+
371
+ # --------------------------------------------------------------------------
372
+ # CLI
373
+ # --------------------------------------------------------------------------
374
+
375
+
376
+ def gather_files(target: Path) -> List[Path]:
377
+ if target.is_file():
378
+ return [target]
379
+ return sorted(target.rglob("*.md"))
380
+
381
+
382
+ def main(argv: List[str] | None = None) -> int:
383
+ ap = argparse.ArgumentParser(description="STE machine gate.")
384
+ ap.add_argument("target", nargs="?", help="file or dir to lint, or '-' for stdin")
385
+ ap.add_argument("--type", choices=sorted(LIMITS), default=DEFAULT_TYPE)
386
+ ap.add_argument("--json", action="store_true", help="emit JSON report only")
387
+ ap.add_argument("--no-vale", action="store_true", help="force built-in style checks")
388
+ ap.add_argument("--self-test", action="store_true", help="run built-in assertions and exit")
389
+ args = ap.parse_args(argv)
390
+
391
+ if args.self_test:
392
+ self_test()
393
+ return 0
394
+
395
+ if args.target is None or args.target == "":
396
+ ap.error("the following arguments are required: target")
397
+
398
+ # stdin path
399
+ if args.target == "-":
400
+ text = sys.stdin.read()
401
+ rep = ste_counts(strip_code(text), args.type)
402
+ print(json.dumps(rep, indent=2))
403
+ return 0 if rep["violations_total"] == 0 else 1
404
+
405
+ target = Path(args.target).resolve()
406
+ if not target.exists():
407
+ print(f"target not found: {target}", file=sys.stderr)
408
+ return 2
409
+
410
+ files = gather_files(target)
411
+ if not files:
412
+ print(f"no markdown files under {target}")
413
+ return 0
414
+
415
+ linter = Linter(args.type, use_vale=not args.no_vale, style=True)
416
+ if linter.vale and target.is_dir():
417
+ linter.run_vale(target)
418
+ for f in files:
419
+ linter.lint_file(f)
420
+
421
+ if args.json:
422
+ rep = {
423
+ "files": len(files),
424
+ "findings": [
425
+ {"path": str(p), "line": n, "level": lvl, "msg": msg}
426
+ for (p, n, lvl, msg) in linter.findings
427
+ ],
428
+ }
429
+ print(json.dumps(rep, indent=2))
430
+ else:
431
+ for path, line, level, msg in linter.findings:
432
+ loc = f"{path}:{line}" if line else f"{path}"
433
+ print(f"{loc}: [{level}] {msg}")
434
+
435
+ errors = sum(1 for *_, lvl, _ in linter.findings if lvl == "error")
436
+ warnings = sum(1 for *_, lvl, _ in linter.findings if lvl == "warning")
437
+ style_layer = "vale" if linter.vale else "built-in"
438
+ print(
439
+ f"\n{errors} error(s), {warnings} warning(s) in {len(files)} file(s) "
440
+ f"[style layer: {style_layer}]"
441
+ )
442
+ return 1 if errors else 0
443
+
444
+
445
+ if __name__ == "__main__":
446
+ sys.exit(main())
@@ -48,7 +48,7 @@ Write the document focusing on "Expert Collaboration":
48
48
 
49
49
  1. Ask: "Project name? One-sentence description?"
50
50
  2. Generate `README.md` at project root using the template in [REFERENCE.md](REFERENCE.md) — no TOC, no second interview round.
51
- 3. Fill gaps from `CLAUDE.md` commands if available; use `TODO` markers otherwise.
51
+ 3. Fill gaps from `CLAUDE.md` / `AGENTS.md` commands if available (prefer `AGENTS.md`); use `TODO` markers otherwise.
52
52
  4. Output and suggest `edit-document` for polish.
53
53
 
54
54
  → verify: `test -f README.md && [ "$(grep -c '^## ' README.md)" -ge 7 ]`
package/skills-lock.json CHANGED
@@ -321,6 +321,11 @@
321
321
  "sha256": "33bc4ecbd63cc9ca",
322
322
  "path": "skills/setup-environmentSKILL.md"
323
323
  },
324
+ "simple-english": {
325
+ "description": "Write or rewrite technical text with the rules of ASD-STE100 Simplified Technical English so the result is clear, unambiguous, and free of AI slop. Use for documentation, READMEs, runbooks, procedures, error messages, release notes, incident reports, and API guides. Also use when the user says \"STE\", \"Simplified Technical English\", \"ASD-STE100\", \"de-slop\", \"make this readable\", \"write for non-native readers\", or asks for docs that translate well. Enforces the 53 rules of the standard (Issue 9) with a deterministic lint gate.",
326
+ "sha256": "a0e904d86d8a7bc3",
327
+ "path": "skills/simple-englishSKILL.md"
328
+ },
324
329
  "simulate-agents": {
325
330
  "description": "Run Mock User and Auditor agents against a feature in fresh contexts before human review. Use after verify-work, before request-review, when user wants pre-review simulation.",
326
331
  "sha256": "499159f692b09b27",
@@ -398,7 +403,7 @@
398
403
  },
399
404
  "write-document": {
400
405
  "description": "Write, organize, and sync high-integrity technical documents using the BMAD methodology. Ensures every document is Bold, Minimal, Actionable, and Durable. Use when creating architectural docs, technical guides, or organizing the specs/ directory.",
401
- "sha256": "445a7d0359fcbedf",
406
+ "sha256": "54f71fb8b725440a",
402
407
  "path": "skills/write-documentSKILL.md"
403
408
  }
404
409
  }