Coverage for little_loops / cli / docs.py: 11%

47 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2026-03-18 16:18 -0500

1"""ll-verify-docs and ll-check-links: Documentation verification commands.""" 

2 

3from __future__ import annotations 

4 

5import argparse 

6from pathlib import Path 

7 

8 

9def main_verify_docs() -> int: 

10 """Entry point for ll-verify-docs command. 

11 

12 Verify that documented counts (commands, agents, skills) match actual file counts. 

13 

14 Returns: 

15 Exit code (0 = all match, 1 = mismatches found) 

16 """ 

17 from little_loops.doc_counts import ( 

18 fix_counts, 

19 format_result_json, 

20 format_result_markdown, 

21 format_result_text, 

22 verify_documentation, 

23 ) 

24 

25 parser = argparse.ArgumentParser( 

26 prog="ll-verify-docs", 

27 description="Verify documented counts match actual file counts", 

28 formatter_class=argparse.RawDescriptionHelpFormatter, 

29 epilog=""" 

30Examples: 

31 %(prog)s # Check and show results 

32 %(prog)s --json # Output as JSON 

33 %(prog)s --format markdown # Markdown report 

34 %(prog)s --fix # Auto-fix mismatches 

35 

36Exit codes: 

37 0 - All counts match 

38 1 - Mismatches found 

39 2 - Error occurred 

40""", 

41 ) 

42 

43 parser.add_argument( 

44 "-j", 

45 "--json", 

46 action="store_true", 

47 help="Output as JSON", 

48 ) 

49 

50 parser.add_argument( 

51 "-f", 

52 "--format", 

53 choices=["text", "json", "markdown"], 

54 default="text", 

55 help="Output format (default: text)", 

56 ) 

57 

58 parser.add_argument( 

59 "--fix", 

60 action="store_true", 

61 help="Auto-fix count mismatches in documentation files", 

62 ) 

63 

64 parser.add_argument( 

65 "-C", 

66 "--directory", 

67 type=Path, 

68 default=None, 

69 help="Base directory (default: current directory)", 

70 ) 

71 

72 args = parser.parse_args() 

73 

74 # Determine base directory 

75 base_dir = args.directory or Path.cwd() 

76 

77 # Run verification 

78 result = verify_documentation(base_dir) 

79 

80 # Format output 

81 if args.json or args.format == "json": 

82 output = format_result_json(result) 

83 elif args.format == "markdown": 

84 output = format_result_markdown(result) 

85 else: 

86 output = format_result_text(result) 

87 

88 print(output) 

89 

90 # Auto-fix if requested 

91 if args.fix and not result.all_match: 

92 fix_result = fix_counts(base_dir, result) 

93 print( 

94 f"\nFixed {fix_result.fixed_count} count(s) in {len(fix_result.files_modified)} file(s)" 

95 ) 

96 

97 # Return exit code based on results 

98 return 0 if result.all_match else 1 

99 

100 

101def main_check_links() -> int: 

102 """Entry point for ll-check-links command. 

103 

104 Check markdown documentation for broken links. 

105 

106 Returns: 

107 Exit code (0 = all links valid, 1 = broken links found, 2 = error) 

108 """ 

109 from little_loops.link_checker import ( 

110 check_markdown_links, 

111 format_result_json, 

112 format_result_markdown, 

113 format_result_text, 

114 load_ignore_patterns, 

115 ) 

116 

117 parser = argparse.ArgumentParser( 

118 prog="ll-check-links", 

119 description="Check markdown documentation for broken links", 

120 formatter_class=argparse.RawDescriptionHelpFormatter, 

121 epilog=""" 

122Examples: 

123 %(prog)s # Check all markdown files 

124 %(prog)s --json # Output as JSON 

125 %(prog)s --format markdown # Markdown report 

126 %(prog)s docs/ # Check specific directory 

127 %(prog)s --ignore 'http://localhost.*' # Ignore pattern 

128 

129Exit codes: 

130 0 - All links valid 

131 1 - Broken links found 

132 2 - Error occurred 

133""", 

134 ) 

135 

136 parser.add_argument( 

137 "-j", 

138 "--json", 

139 action="store_true", 

140 help="Output as JSON", 

141 ) 

142 

143 parser.add_argument( 

144 "-f", 

145 "--format", 

146 choices=["text", "json", "markdown"], 

147 default="text", 

148 help="Output format (default: text)", 

149 ) 

150 

151 parser.add_argument( 

152 "-C", 

153 "--directory", 

154 type=Path, 

155 default=None, 

156 help="Base directory (default: current directory)", 

157 ) 

158 

159 parser.add_argument( 

160 "--ignore", 

161 action="append", 

162 default=[], 

163 help="Ignore URL patterns (can be used multiple times)", 

164 ) 

165 

166 parser.add_argument( 

167 "--timeout", 

168 type=int, 

169 default=10, 

170 help="Request timeout in seconds (default: 10)", 

171 ) 

172 

173 parser.add_argument( 

174 "-w", 

175 "--workers", 

176 type=int, 

177 default=10, 

178 help="Maximum concurrent HTTP requests (default: 10)", 

179 ) 

180 

181 parser.add_argument( 

182 "-v", 

183 "--verbose", 

184 action="store_true", 

185 help="Show verbose output", 

186 ) 

187 

188 args = parser.parse_args() 

189 

190 # Determine base directory 

191 base_dir = args.directory or Path.cwd() 

192 

193 # Load ignore patterns from config + CLI args 

194 ignore_patterns = load_ignore_patterns(base_dir) 

195 ignore_patterns.extend(args.ignore) 

196 

197 # Run link check 

198 result = check_markdown_links( 

199 base_dir, ignore_patterns, args.timeout, args.verbose, args.workers 

200 ) 

201 

202 # Format output 

203 if args.json or args.format == "json": 

204 output = format_result_json(result) 

205 elif args.format == "markdown": 

206 output = format_result_markdown(result) 

207 else: 

208 output = format_result_text(result) 

209 

210 print(output) 

211 

212 # Return exit code based on results 

213 if result.has_errors: 

214 return 1 

215 return 0