@brookmind/ai-toolkit 1.1.6 → 1.2.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/LICENSE +21 -0
- package/README.md +42 -14
- package/dist/__tests__/index.test.d.ts +2 -0
- package/dist/__tests__/index.test.d.ts.map +1 -0
- package/dist/__tests__/index.test.js +214 -0
- package/dist/__tests__/index.test.js.map +1 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +39 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +52 -335
- package/dist/index.js.map +1 -1
- package/dist/services/installers.d.ts +8 -0
- package/dist/services/installers.d.ts.map +1 -0
- package/dist/services/installers.js +79 -0
- package/dist/services/installers.js.map +1 -0
- package/dist/services/opencode.d.ts +3 -0
- package/dist/services/opencode.d.ts.map +1 -0
- package/dist/services/opencode.js +33 -0
- package/dist/services/opencode.js.map +1 -0
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/categorize.d.ts +6 -0
- package/dist/ui/categorize.d.ts.map +1 -0
- package/dist/ui/categorize.js +69 -0
- package/dist/ui/categorize.js.map +1 -0
- package/dist/ui/choices.d.ts +6 -0
- package/dist/ui/choices.d.ts.map +1 -0
- package/dist/ui/choices.js +70 -0
- package/dist/ui/choices.js.map +1 -0
- package/dist/ui/display.d.ts +8 -0
- package/dist/ui/display.d.ts.map +1 -0
- package/dist/ui/display.js +84 -0
- package/dist/ui/display.js.map +1 -0
- package/dist/utils/fs.d.ts +5 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js +40 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/terminal.d.ts +5 -0
- package/dist/utils/terminal.d.ts.map +1 -0
- package/dist/utils/terminal.js +18 -0
- package/dist/utils/terminal.js.map +1 -0
- package/package.json +27 -5
- package/agents/code-reviewer.md +0 -35
- package/agents/code-simplifier.md +0 -52
- package/commands/create-pr-description.md +0 -102
- package/commands/create-pr.md +0 -76
- package/commands/create-react-tests.md +0 -207
- package/mcps/context7/.mcp.json +0 -13
- package/mcps/expo-mcp/.mcp.json +0 -13
- package/mcps/figma-mcp/.mcp.json +0 -10
- package/skills/github-cli/SKILL.md +0 -125
- package/skills/pdf-processing-pro/FORMS.md +0 -610
- package/skills/pdf-processing-pro/OCR.md +0 -137
- package/skills/pdf-processing-pro/SKILL.md +0 -296
- package/skills/pdf-processing-pro/TABLES.md +0 -626
- package/skills/pdf-processing-pro/scripts/analyze_form.py +0 -307
- package/skills/react-best-practices/AGENTS.md +0 -915
- package/skills/react-best-practices/README.md +0 -127
- package/skills/react-best-practices/SKILL.md +0 -110
- package/skills/react-best-practices/metadata.json +0 -14
- package/skills/react-best-practices/rules/_sections.md +0 -41
- package/skills/react-best-practices/rules/_template.md +0 -28
- package/skills/react-best-practices/rules/advanced-event-handler-refs.md +0 -80
- package/skills/react-best-practices/rules/advanced-use-latest.md +0 -76
- package/skills/react-best-practices/rules/async-defer-await.md +0 -80
- package/skills/react-best-practices/rules/async-dependencies.md +0 -36
- package/skills/react-best-practices/rules/async-parallel.md +0 -28
- package/skills/react-best-practices/rules/async-suspense-boundaries.md +0 -100
- package/skills/react-best-practices/rules/bundle-barrel-imports.md +0 -42
- package/skills/react-best-practices/rules/bundle-conditional.md +0 -106
- package/skills/react-best-practices/rules/bundle-preload.md +0 -44
- package/skills/react-best-practices/rules/client-event-listeners.md +0 -131
- package/skills/react-best-practices/rules/client-swr-dedup.md +0 -133
- package/skills/react-best-practices/rules/js-batch-dom-css.md +0 -82
- package/skills/react-best-practices/rules/js-cache-function-results.md +0 -80
- package/skills/react-best-practices/rules/js-cache-property-access.md +0 -28
- package/skills/react-best-practices/rules/js-cache-storage.md +0 -70
- package/skills/react-best-practices/rules/js-combine-iterations.md +0 -32
- package/skills/react-best-practices/rules/js-early-exit.md +0 -50
- package/skills/react-best-practices/rules/js-hoist-regexp.md +0 -45
- package/skills/react-best-practices/rules/js-index-maps.md +0 -37
- package/skills/react-best-practices/rules/js-length-check-first.md +0 -49
- package/skills/react-best-practices/rules/js-min-max-loop.md +0 -82
- package/skills/react-best-practices/rules/js-set-map-lookups.md +0 -24
- package/skills/react-best-practices/rules/js-tosorted-immutable.md +0 -57
- package/skills/react-best-practices/rules/rendering-activity.md +0 -90
- package/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -47
- package/skills/react-best-practices/rules/rendering-conditional-render.md +0 -40
- package/skills/react-best-practices/rules/rendering-content-visibility.md +0 -38
- package/skills/react-best-practices/rules/rendering-hoist-jsx.md +0 -65
- package/skills/react-best-practices/rules/rendering-svg-precision.md +0 -28
- package/skills/react-best-practices/rules/rerender-defer-reads.md +0 -39
- package/skills/react-best-practices/rules/rerender-dependencies.md +0 -45
- package/skills/react-best-practices/rules/rerender-derived-state.md +0 -29
- package/skills/react-best-practices/rules/rerender-functional-setstate.md +0 -74
- package/skills/react-best-practices/rules/rerender-lazy-state-init.md +0 -58
- package/skills/react-best-practices/rules/rerender-memo.md +0 -85
- package/skills/react-best-practices/rules/rerender-transitions.md +0 -40
- package/skills/skill-creator/LICENSE.txt +0 -202
- package/skills/skill-creator/SKILL.md +0 -209
- package/skills/skill-creator/scripts/init_skill.py +0 -303
- package/skills/skill-creator/scripts/package_skill.py +0 -110
- package/skills/skill-creator/scripts/quick_validate.py +0 -65
- package/skills/spring-boot-development/EXAMPLES.md +0 -2346
- package/skills/spring-boot-development/README.md +0 -595
- package/skills/spring-boot-development/SKILL.md +0 -1519
- package/themes/README.md +0 -68
- package/themes/claude-vivid.json +0 -72
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Analyze PDF form fields and structure.
|
|
4
|
-
|
|
5
|
-
Usage:
|
|
6
|
-
python analyze_form.py input.pdf [--output fields.json] [--verbose]
|
|
7
|
-
|
|
8
|
-
Returns:
|
|
9
|
-
JSON with all form fields, types, positions, and metadata
|
|
10
|
-
|
|
11
|
-
Exit codes:
|
|
12
|
-
0 - Success
|
|
13
|
-
1 - File not found
|
|
14
|
-
2 - Invalid PDF
|
|
15
|
-
3 - Processing error
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
import sys
|
|
19
|
-
import json
|
|
20
|
-
import logging
|
|
21
|
-
import argparse
|
|
22
|
-
from pathlib import Path
|
|
23
|
-
from typing import Dict, List, Optional, Any
|
|
24
|
-
|
|
25
|
-
try:
|
|
26
|
-
from pypdf import PdfReader
|
|
27
|
-
except ImportError:
|
|
28
|
-
print("Error: pypdf not installed. Run: pip install pypdf", file=sys.stderr)
|
|
29
|
-
sys.exit(3)
|
|
30
|
-
|
|
31
|
-
# Configure logging
|
|
32
|
-
logging.basicConfig(
|
|
33
|
-
level=logging.INFO,
|
|
34
|
-
format='%(asctime)s - %(levelname)s - %(message)s'
|
|
35
|
-
)
|
|
36
|
-
logger = logging.getLogger(__name__)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
class FormField:
|
|
40
|
-
"""Represents a PDF form field."""
|
|
41
|
-
|
|
42
|
-
def __init__(self, name: str, field_dict: Dict[str, Any]):
|
|
43
|
-
self.name = name
|
|
44
|
-
self.raw_data = field_dict
|
|
45
|
-
|
|
46
|
-
@property
|
|
47
|
-
def field_type(self) -> str:
|
|
48
|
-
"""Get field type."""
|
|
49
|
-
ft = self.raw_data.get('/FT', '')
|
|
50
|
-
type_map = {
|
|
51
|
-
'/Tx': 'text',
|
|
52
|
-
'/Btn': 'button', # checkbox or radio
|
|
53
|
-
'/Ch': 'choice', # dropdown or list
|
|
54
|
-
'/Sig': 'signature'
|
|
55
|
-
}
|
|
56
|
-
return type_map.get(ft, 'unknown')
|
|
57
|
-
|
|
58
|
-
@property
|
|
59
|
-
def value(self) -> Optional[str]:
|
|
60
|
-
"""Get current field value."""
|
|
61
|
-
val = self.raw_data.get('/V')
|
|
62
|
-
return str(val) if val else None
|
|
63
|
-
|
|
64
|
-
@property
|
|
65
|
-
def default_value(self) -> Optional[str]:
|
|
66
|
-
"""Get default field value."""
|
|
67
|
-
dv = self.raw_data.get('/DV')
|
|
68
|
-
return str(dv) if dv else None
|
|
69
|
-
|
|
70
|
-
@property
|
|
71
|
-
def is_required(self) -> bool:
|
|
72
|
-
"""Check if field is required."""
|
|
73
|
-
flags = self.raw_data.get('/Ff', 0)
|
|
74
|
-
# Bit 2 indicates required
|
|
75
|
-
return bool(flags & 2)
|
|
76
|
-
|
|
77
|
-
@property
|
|
78
|
-
def is_readonly(self) -> bool:
|
|
79
|
-
"""Check if field is read-only."""
|
|
80
|
-
flags = self.raw_data.get('/Ff', 0)
|
|
81
|
-
# Bit 1 indicates read-only
|
|
82
|
-
return bool(flags & 1)
|
|
83
|
-
|
|
84
|
-
@property
|
|
85
|
-
def options(self) -> List[str]:
|
|
86
|
-
"""Get options for choice fields."""
|
|
87
|
-
if self.field_type != 'choice':
|
|
88
|
-
return []
|
|
89
|
-
|
|
90
|
-
opts = self.raw_data.get('/Opt', [])
|
|
91
|
-
if isinstance(opts, list):
|
|
92
|
-
return [str(opt) for opt in opts]
|
|
93
|
-
return []
|
|
94
|
-
|
|
95
|
-
@property
|
|
96
|
-
def max_length(self) -> Optional[int]:
|
|
97
|
-
"""Get max length for text fields."""
|
|
98
|
-
if self.field_type == 'text':
|
|
99
|
-
return self.raw_data.get('/MaxLen')
|
|
100
|
-
return None
|
|
101
|
-
|
|
102
|
-
@property
|
|
103
|
-
def rect(self) -> Optional[List[float]]:
|
|
104
|
-
"""Get field position and size [x0, y0, x1, y1]."""
|
|
105
|
-
return self.raw_data.get('/Rect')
|
|
106
|
-
|
|
107
|
-
def to_dict(self) -> Dict[str, Any]:
|
|
108
|
-
"""Convert to dictionary."""
|
|
109
|
-
result = {
|
|
110
|
-
'name': self.name,
|
|
111
|
-
'type': self.field_type,
|
|
112
|
-
'required': self.is_required,
|
|
113
|
-
'readonly': self.is_readonly
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if self.value is not None:
|
|
117
|
-
result['value'] = self.value
|
|
118
|
-
|
|
119
|
-
if self.default_value is not None:
|
|
120
|
-
result['default_value'] = self.default_value
|
|
121
|
-
|
|
122
|
-
if self.options:
|
|
123
|
-
result['options'] = self.options
|
|
124
|
-
|
|
125
|
-
if self.max_length is not None:
|
|
126
|
-
result['max_length'] = self.max_length
|
|
127
|
-
|
|
128
|
-
if self.rect:
|
|
129
|
-
result['position'] = {
|
|
130
|
-
'x0': float(self.rect[0]),
|
|
131
|
-
'y0': float(self.rect[1]),
|
|
132
|
-
'x1': float(self.rect[2]),
|
|
133
|
-
'y1': float(self.rect[3]),
|
|
134
|
-
'width': float(self.rect[2] - self.rect[0]),
|
|
135
|
-
'height': float(self.rect[3] - self.rect[1])
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return result
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
class PDFFormAnalyzer:
|
|
142
|
-
"""Analyzes PDF forms and extracts field information."""
|
|
143
|
-
|
|
144
|
-
def __init__(self, pdf_path: str):
|
|
145
|
-
self.pdf_path = Path(pdf_path)
|
|
146
|
-
self.reader: Optional[PdfReader] = None
|
|
147
|
-
self._validate_file()
|
|
148
|
-
|
|
149
|
-
def _validate_file(self) -> None:
|
|
150
|
-
"""Validate PDF file exists and is readable."""
|
|
151
|
-
if not self.pdf_path.exists():
|
|
152
|
-
logger.error(f"PDF not found: {self.pdf_path}")
|
|
153
|
-
raise FileNotFoundError(f"PDF not found: {self.pdf_path}")
|
|
154
|
-
|
|
155
|
-
if not self.pdf_path.is_file():
|
|
156
|
-
logger.error(f"Not a file: {self.pdf_path}")
|
|
157
|
-
raise ValueError(f"Not a file: {self.pdf_path}")
|
|
158
|
-
|
|
159
|
-
if self.pdf_path.suffix.lower() != '.pdf':
|
|
160
|
-
logger.error(f"Not a PDF file: {self.pdf_path}")
|
|
161
|
-
raise ValueError(f"Not a PDF file: {self.pdf_path}")
|
|
162
|
-
|
|
163
|
-
def analyze(self) -> Dict[str, Dict[str, Any]]:
|
|
164
|
-
"""
|
|
165
|
-
Analyze PDF and extract all form fields.
|
|
166
|
-
|
|
167
|
-
Returns:
|
|
168
|
-
Dictionary mapping field names to field information
|
|
169
|
-
"""
|
|
170
|
-
try:
|
|
171
|
-
self.reader = PdfReader(str(self.pdf_path))
|
|
172
|
-
|
|
173
|
-
if not self.reader.pages:
|
|
174
|
-
logger.warning("PDF has no pages")
|
|
175
|
-
return {}
|
|
176
|
-
|
|
177
|
-
logger.info(f"Analyzing PDF with {len(self.reader.pages)} pages")
|
|
178
|
-
|
|
179
|
-
# Get form fields
|
|
180
|
-
raw_fields = self.reader.get_fields()
|
|
181
|
-
|
|
182
|
-
if not raw_fields:
|
|
183
|
-
logger.warning("PDF has no form fields")
|
|
184
|
-
return {}
|
|
185
|
-
|
|
186
|
-
logger.info(f"Found {len(raw_fields)} form fields")
|
|
187
|
-
|
|
188
|
-
# Process fields
|
|
189
|
-
fields = {}
|
|
190
|
-
for field_name, field_dict in raw_fields.items():
|
|
191
|
-
try:
|
|
192
|
-
field = FormField(field_name, field_dict)
|
|
193
|
-
fields[field_name] = field.to_dict()
|
|
194
|
-
except Exception as e:
|
|
195
|
-
logger.warning(f"Error processing field {field_name}: {e}")
|
|
196
|
-
continue
|
|
197
|
-
|
|
198
|
-
return fields
|
|
199
|
-
|
|
200
|
-
except Exception as e:
|
|
201
|
-
logger.error(f"Error analyzing PDF: {e}")
|
|
202
|
-
raise
|
|
203
|
-
|
|
204
|
-
def get_summary(self) -> Dict[str, Any]:
|
|
205
|
-
"""Get summary statistics."""
|
|
206
|
-
fields = self.analyze()
|
|
207
|
-
|
|
208
|
-
summary = {
|
|
209
|
-
'total_fields': len(fields),
|
|
210
|
-
'field_types': {},
|
|
211
|
-
'required_fields': [],
|
|
212
|
-
'readonly_fields': [],
|
|
213
|
-
'fields_with_values': []
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
for field_name, field_data in fields.items():
|
|
217
|
-
# Count by type
|
|
218
|
-
field_type = field_data['type']
|
|
219
|
-
summary['field_types'][field_type] = summary['field_types'].get(field_type, 0) + 1
|
|
220
|
-
|
|
221
|
-
# Required fields
|
|
222
|
-
if field_data.get('required'):
|
|
223
|
-
summary['required_fields'].append(field_name)
|
|
224
|
-
|
|
225
|
-
# Read-only fields
|
|
226
|
-
if field_data.get('readonly'):
|
|
227
|
-
summary['readonly_fields'].append(field_name)
|
|
228
|
-
|
|
229
|
-
# Fields with values
|
|
230
|
-
if field_data.get('value'):
|
|
231
|
-
summary['fields_with_values'].append(field_name)
|
|
232
|
-
|
|
233
|
-
return summary
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
def main():
|
|
237
|
-
"""Main entry point."""
|
|
238
|
-
parser = argparse.ArgumentParser(
|
|
239
|
-
description='Analyze PDF form fields',
|
|
240
|
-
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
241
|
-
epilog='''
|
|
242
|
-
Examples:
|
|
243
|
-
%(prog)s form.pdf
|
|
244
|
-
%(prog)s form.pdf --output fields.json
|
|
245
|
-
%(prog)s form.pdf --output fields.json --verbose
|
|
246
|
-
%(prog)s form.pdf --summary
|
|
247
|
-
|
|
248
|
-
Exit codes:
|
|
249
|
-
0 - Success
|
|
250
|
-
1 - File not found
|
|
251
|
-
2 - Invalid PDF
|
|
252
|
-
3 - Processing error
|
|
253
|
-
'''
|
|
254
|
-
)
|
|
255
|
-
|
|
256
|
-
parser.add_argument('input', help='Input PDF file')
|
|
257
|
-
parser.add_argument('--output', '-o', help='Output JSON file (default: stdout)')
|
|
258
|
-
parser.add_argument('--summary', '-s', action='store_true', help='Show summary only')
|
|
259
|
-
parser.add_argument('--verbose', '-v', action='store_true', help='Verbose output')
|
|
260
|
-
|
|
261
|
-
args = parser.parse_args()
|
|
262
|
-
|
|
263
|
-
# Set log level
|
|
264
|
-
if args.verbose:
|
|
265
|
-
logger.setLevel(logging.DEBUG)
|
|
266
|
-
else:
|
|
267
|
-
logger.setLevel(logging.WARNING)
|
|
268
|
-
|
|
269
|
-
try:
|
|
270
|
-
# Analyze form
|
|
271
|
-
analyzer = PDFFormAnalyzer(args.input)
|
|
272
|
-
|
|
273
|
-
if args.summary:
|
|
274
|
-
result = analyzer.get_summary()
|
|
275
|
-
else:
|
|
276
|
-
result = analyzer.analyze()
|
|
277
|
-
|
|
278
|
-
# Output
|
|
279
|
-
json_output = json.dumps(result, indent=2)
|
|
280
|
-
|
|
281
|
-
if args.output:
|
|
282
|
-
with open(args.output, 'w', encoding='utf-8') as f:
|
|
283
|
-
f.write(json_output)
|
|
284
|
-
logger.info(f"Saved to {args.output}")
|
|
285
|
-
else:
|
|
286
|
-
print(json_output)
|
|
287
|
-
|
|
288
|
-
return 0
|
|
289
|
-
|
|
290
|
-
except FileNotFoundError:
|
|
291
|
-
logger.error(f"File not found: {args.input}")
|
|
292
|
-
return 1
|
|
293
|
-
|
|
294
|
-
except ValueError as e:
|
|
295
|
-
logger.error(f"Invalid input: {e}")
|
|
296
|
-
return 2
|
|
297
|
-
|
|
298
|
-
except Exception as e:
|
|
299
|
-
logger.error(f"Error: {e}")
|
|
300
|
-
if args.verbose:
|
|
301
|
-
import traceback
|
|
302
|
-
traceback.print_exc()
|
|
303
|
-
return 3
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
if __name__ == '__main__':
|
|
307
|
-
sys.exit(main())
|