()
| 169 | |
| 170 | |
| 171 | def main(): |
| 172 | parser = argparse.ArgumentParser(description=__doc__) |
| 173 | parser.add_argument("paths", nargs="+", help="Markdown files or directories") |
| 174 | parser.add_argument("--output", help="Write a JSON report to this path") |
| 175 | parser.add_argument( |
| 176 | "--repo-root", |
| 177 | default=os.getcwd(), |
| 178 | help="Root used to relativize file paths in the report (default: cwd)", |
| 179 | ) |
| 180 | args = parser.parse_args() |
| 181 | |
| 182 | files = expand_paths(args.paths) |
| 183 | errors = [] |
| 184 | for path in files: |
| 185 | errors.extend(check_file(path, args.repo_root)) |
| 186 | |
| 187 | if args.output: |
| 188 | os.makedirs(os.path.dirname(args.output) or ".", exist_ok=True) |
| 189 | with open(args.output, "w", encoding="utf-8") as fh: |
| 190 | json.dump({"total": len(errors), "findings": errors}, fh, indent=2) |
| 191 | |
| 192 | if errors: |
| 193 | print( |
| 194 | f"Paragraph capitalization check failed: {len(errors)} paragraph(s) " |
| 195 | f"start with a lowercase word.", |
| 196 | file=sys.stderr, |
| 197 | ) |
| 198 | for e in errors: |
| 199 | print(f" {e['file']}:{e['line']}: '{e['word']}' — {e['excerpt']}", file=sys.stderr) |
| 200 | print( |
| 201 | "\nEach flagged paragraph must be rewritten so its first prose word " |
| 202 | "begins with a capital letter.\n" |
| 203 | 'Example: "many ways to animate..." → "There are many ways to animate..."', |
| 204 | file=sys.stderr, |
| 205 | ) |
| 206 | return 1 |
| 207 | |
| 208 | print(f"Paragraph capitalization check passed: {len(files)} file(s), 0 issue(s).") |
| 209 | return 0 |
| 210 | |
| 211 | |
| 212 | if __name__ == "__main__": |
no test coverage detected