ima-claude 2.9.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 (182) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +463 -0
  3. package/dist/cli.js +1064 -0
  4. package/package.json +49 -0
  5. package/platforms/claude/adapter.ts +115 -0
  6. package/platforms/junie/adapter.ts +254 -0
  7. package/platforms/junie/agents-template.md +113 -0
  8. package/platforms/junie/hook-translations.md +84 -0
  9. package/platforms/shared/detector.ts +27 -0
  10. package/platforms/shared/installer.ts +202 -0
  11. package/platforms/shared/types.ts +78 -0
  12. package/plugins/ima-claude/.claude-plugin/plugin.json +25 -0
  13. package/plugins/ima-claude/agents/explorer.md +30 -0
  14. package/plugins/ima-claude/agents/implementer.md +30 -0
  15. package/plugins/ima-claude/agents/memory.md +42 -0
  16. package/plugins/ima-claude/agents/reviewer.md +53 -0
  17. package/plugins/ima-claude/agents/tester.md +33 -0
  18. package/plugins/ima-claude/agents/wp-developer.md +46 -0
  19. package/plugins/ima-claude/hooks/README.md +145 -0
  20. package/plugins/ima-claude/hooks/atlassian_prereqs.py +112 -0
  21. package/plugins/ima-claude/hooks/block_sed_edits.py +59 -0
  22. package/plugins/ima-claude/hooks/bootstrap.sh +90 -0
  23. package/plugins/ima-claude/hooks/bootstrap_utility_check.py +94 -0
  24. package/plugins/ima-claude/hooks/composer_autoload_check.py +70 -0
  25. package/plugins/ima-claude/hooks/docs_organization.py +104 -0
  26. package/plugins/ima-claude/hooks/enforce_rg_over_grep.py +56 -0
  27. package/plugins/ima-claude/hooks/fp_utility_check.py +90 -0
  28. package/plugins/ima-claude/hooks/hook_logger.py +69 -0
  29. package/plugins/ima-claude/hooks/hooks.json +239 -0
  30. package/plugins/ima-claude/hooks/jira_issue_fetch.py +79 -0
  31. package/plugins/ima-claude/hooks/jquery_in_wordpress.py +92 -0
  32. package/plugins/ima-claude/hooks/memory_bootstrap.py +79 -0
  33. package/plugins/ima-claude/hooks/memory_store_reminder.py +75 -0
  34. package/plugins/ima-claude/hooks/prompt_coach.py +125 -0
  35. package/plugins/ima-claude/hooks/prompt_coach_digest.md +48 -0
  36. package/plugins/ima-claude/hooks/prompt_coach_system.md +30 -0
  37. package/plugins/ima-claude/hooks/sequential_thinking_check.py +81 -0
  38. package/plugins/ima-claude/hooks/serena_over_grep.py +96 -0
  39. package/plugins/ima-claude/hooks/serena_over_read.py +66 -0
  40. package/plugins/ima-claude/hooks/serena_project_check.py +133 -0
  41. package/plugins/ima-claude/hooks/sql_injection_check.py +73 -0
  42. package/plugins/ima-claude/hooks/task_master_after_plan.py +31 -0
  43. package/plugins/ima-claude/hooks/task_master_before_impl.py +93 -0
  44. package/plugins/ima-claude/hooks/tavily_extract_advanced.py +48 -0
  45. package/plugins/ima-claude/hooks/vestige_before_external.py +86 -0
  46. package/plugins/ima-claude/hooks/webfetch_to_tavily.py +42 -0
  47. package/plugins/ima-claude/hooks/websearch_to_tavily.py +41 -0
  48. package/plugins/ima-claude/hooks/wp_security_check.py +150 -0
  49. package/plugins/ima-claude/personalities/README.md +45 -0
  50. package/plugins/ima-claude/personalities/enable-40k.md +69 -0
  51. package/plugins/ima-claude/personalities/enable-templars.md +69 -0
  52. package/plugins/ima-claude/skills/.research-summary.md +340 -0
  53. package/plugins/ima-claude/skills/architect/SKILL.md +304 -0
  54. package/plugins/ima-claude/skills/compound-bridge/SKILL.md +200 -0
  55. package/plugins/ima-claude/skills/discourse/SKILL.md +440 -0
  56. package/plugins/ima-claude/skills/discourse-admin/SKILL.md +192 -0
  57. package/plugins/ima-claude/skills/discourse-admin/references/api-endpoints.md +441 -0
  58. package/plugins/ima-claude/skills/discourse-admin/references/gotchas.md +107 -0
  59. package/plugins/ima-claude/skills/discourse-admin/references/staging-defaults.md +98 -0
  60. package/plugins/ima-claude/skills/discourse-admin/scripts/discourse-admin.py +319 -0
  61. package/plugins/ima-claude/skills/docs-organize/SKILL.md +254 -0
  62. package/plugins/ima-claude/skills/docs-organize/templates/active-README.md +50 -0
  63. package/plugins/ima-claude/skills/docs-organize/templates/archive-README.md +57 -0
  64. package/plugins/ima-claude/skills/docs-organize/templates/docs-README.md +43 -0
  65. package/plugins/ima-claude/skills/docs-organize/templates/phase-archive-README.md +83 -0
  66. package/plugins/ima-claude/skills/docs-organize/templates/section-README.md +48 -0
  67. package/plugins/ima-claude/skills/docs-organize/templates/transient-README.md +79 -0
  68. package/plugins/ima-claude/skills/docs-organize/templates/transient-gitignore +9 -0
  69. package/plugins/ima-claude/skills/ember-discourse/SKILL.md +496 -0
  70. package/plugins/ima-claude/skills/functional-programmer/SKILL.md +258 -0
  71. package/plugins/ima-claude/skills/ima-bootstrap/SKILL.md +278 -0
  72. package/plugins/ima-claude/skills/ima-bootstrap/references/bootstrap-patterns.md +356 -0
  73. package/plugins/ima-claude/skills/ima-bootstrap/references/ima-brand.md +273 -0
  74. package/plugins/ima-claude/skills/ima-bootstrap/references/theme-integration.md +212 -0
  75. package/plugins/ima-claude/skills/ima-brand/SKILL.md +108 -0
  76. package/plugins/ima-claude/skills/ima-brand/references/brand-identity.md +140 -0
  77. package/plugins/ima-claude/skills/ima-brand/references/digital-standards.md +180 -0
  78. package/plugins/ima-claude/skills/ima-brand/references/visual-system.md +173 -0
  79. package/plugins/ima-claude/skills/ima-forms-expert/SKILL.md +175 -0
  80. package/plugins/ima-claude/skills/ima-forms-expert/references/container-components.md +154 -0
  81. package/plugins/ima-claude/skills/ima-forms-expert/references/examples.md +328 -0
  82. package/plugins/ima-claude/skills/ima-forms-expert/references/field-components.md +298 -0
  83. package/plugins/ima-claude/skills/ima-forms-expert/references/form-factory.md +193 -0
  84. package/plugins/ima-claude/skills/ima-forms-expert/references/quick-reference.md +153 -0
  85. package/plugins/ima-claude/skills/ima-forms-expert/references/validation-engine.md +336 -0
  86. package/plugins/ima-claude/skills/jira-checkpoint/SKILL.md +178 -0
  87. package/plugins/ima-claude/skills/jquery/SKILL.md +413 -0
  88. package/plugins/ima-claude/skills/js-fp/SKILL.md +463 -0
  89. package/plugins/ima-claude/skills/js-fp/core-principles.md +487 -0
  90. package/plugins/ima-claude/skills/js-fp/examples/pure-functions.js +260 -0
  91. package/plugins/ima-claude/skills/js-fp/examples/tests/pure-functions.test.js +262 -0
  92. package/plugins/ima-claude/skills/js-fp/references/anti-patterns.md +120 -0
  93. package/plugins/ima-claude/skills/js-fp/references/performance-patterns.md +116 -0
  94. package/plugins/ima-claude/skills/js-fp/references/testing-patterns.md +134 -0
  95. package/plugins/ima-claude/skills/js-fp-api/SKILL.md +280 -0
  96. package/plugins/ima-claude/skills/js-fp-api/examples/crud-endpoint.js +258 -0
  97. package/plugins/ima-claude/skills/js-fp-api/references/middleware-patterns.md +134 -0
  98. package/plugins/ima-claude/skills/js-fp-api/references/security-sql.md +110 -0
  99. package/plugins/ima-claude/skills/js-fp-api/references/validation-patterns.md +165 -0
  100. package/plugins/ima-claude/skills/js-fp-react/SKILL.md +447 -0
  101. package/plugins/ima-claude/skills/js-fp-react/examples/ProductCard.tsx +65 -0
  102. package/plugins/ima-claude/skills/js-fp-react/references/hooks-advanced.md +136 -0
  103. package/plugins/ima-claude/skills/js-fp-react/references/performance-patterns.md +175 -0
  104. package/plugins/ima-claude/skills/js-fp-vue/SKILL.md +322 -0
  105. package/plugins/ima-claude/skills/js-fp-vue/references/complete-examples.md +397 -0
  106. package/plugins/ima-claude/skills/js-fp-vue/references/composables-advanced.md +282 -0
  107. package/plugins/ima-claude/skills/js-fp-vue/references/reactivity-patterns.md +348 -0
  108. package/plugins/ima-claude/skills/js-fp-vue/references/testing.md +314 -0
  109. package/plugins/ima-claude/skills/js-fp-wordpress/SKILL.md +301 -0
  110. package/plugins/ima-claude/skills/js-fp-wordpress/references/ajax-patterns.md +192 -0
  111. package/plugins/ima-claude/skills/js-fp-wordpress/references/event-patterns.md +136 -0
  112. package/plugins/ima-claude/skills/js-fp-wordpress/references/wp-integration.md +248 -0
  113. package/plugins/ima-claude/skills/livecanvas/SKILL.md +209 -0
  114. package/plugins/ima-claude/skills/livecanvas/references/livecanvas-features.md +311 -0
  115. package/plugins/ima-claude/skills/livecanvas/references/loops-and-logic.md +730 -0
  116. package/plugins/ima-claude/skills/livecanvas/references/picostrap.md +227 -0
  117. package/plugins/ima-claude/skills/mcp-atlassian/SKILL.md +339 -0
  118. package/plugins/ima-claude/skills/mcp-context7/SKILL.md +109 -0
  119. package/plugins/ima-claude/skills/mcp-memory/SKILL.md +182 -0
  120. package/plugins/ima-claude/skills/mcp-qdrant/SKILL.md +233 -0
  121. package/plugins/ima-claude/skills/mcp-sequential/SKILL.md +149 -0
  122. package/plugins/ima-claude/skills/mcp-serena/SKILL.md +174 -0
  123. package/plugins/ima-claude/skills/mcp-tavily/SKILL.md +118 -0
  124. package/plugins/ima-claude/skills/mcp-vestige/SKILL.md +259 -0
  125. package/plugins/ima-claude/skills/php-authnet/SKILL.md +275 -0
  126. package/plugins/ima-claude/skills/php-authnet/references/api-reference.md +624 -0
  127. package/plugins/ima-claude/skills/php-authnet/references/sandbox-testing.md +424 -0
  128. package/plugins/ima-claude/skills/php-fp/SKILL.md +333 -0
  129. package/plugins/ima-claude/skills/php-fp/examples/pure-functions.php +403 -0
  130. package/plugins/ima-claude/skills/php-fp/examples/tests/PureFunctionsTest.php +515 -0
  131. package/plugins/ima-claude/skills/php-fp/references/core-principles.md +277 -0
  132. package/plugins/ima-claude/skills/php-fp/references/testing-patterns.md +374 -0
  133. package/plugins/ima-claude/skills/php-fp-wordpress/SKILL.md +216 -0
  134. package/plugins/ima-claude/skills/php-fp-wordpress/references/fp-patterns.md +275 -0
  135. package/plugins/ima-claude/skills/php-fp-wordpress/references/plugin-architecture.md +295 -0
  136. package/plugins/ima-claude/skills/php-fp-wordpress/references/security-examples.md +203 -0
  137. package/plugins/ima-claude/skills/php-fp-wordpress/references/testing-strategy.md +259 -0
  138. package/plugins/ima-claude/skills/phpunit-wp/SKILL.md +716 -0
  139. package/plugins/ima-claude/skills/playwright/SKILL.md +434 -0
  140. package/plugins/ima-claude/skills/playwright/references/accessibility-testing.md +153 -0
  141. package/plugins/ima-claude/skills/playwright/references/ci-cd.md +268 -0
  142. package/plugins/ima-claude/skills/playwright/references/network-mocking.md +270 -0
  143. package/plugins/ima-claude/skills/playwright/references/visual-regression.md +215 -0
  144. package/plugins/ima-claude/skills/py-fp/SKILL.md +663 -0
  145. package/plugins/ima-claude/skills/py-fp/examples/pure-functions.py +185 -0
  146. package/plugins/ima-claude/skills/py-fp/examples/tests/test_pure_functions.py +244 -0
  147. package/plugins/ima-claude/skills/py-fp/references/core-principles.md +381 -0
  148. package/plugins/ima-claude/skills/py-fp/references/testing-patterns.md +283 -0
  149. package/plugins/ima-claude/skills/quasar-fp/SKILL.md +327 -0
  150. package/plugins/ima-claude/skills/quasar-fp/metadata.json +85 -0
  151. package/plugins/ima-claude/skills/quasar-fp/references/component-patterns.md +257 -0
  152. package/plugins/ima-claude/skills/quasar-fp/references/theme-integration.md +233 -0
  153. package/plugins/ima-claude/skills/quasar-fp/references/utility-classes.md +237 -0
  154. package/plugins/ima-claude/skills/quickstart/SKILL.md +129 -0
  155. package/plugins/ima-claude/skills/rails/SKILL.md +359 -0
  156. package/plugins/ima-claude/skills/resume-session/SKILL.md +68 -0
  157. package/plugins/ima-claude/skills/rg/SKILL.md +205 -0
  158. package/plugins/ima-claude/skills/ruby-fp/SKILL.md +336 -0
  159. package/plugins/ima-claude/skills/save-session/SKILL.md +81 -0
  160. package/plugins/ima-claude/skills/scorecard/SKILL.md +96 -0
  161. package/plugins/ima-claude/skills/skill-analyzer/SKILL.md +127 -0
  162. package/plugins/ima-claude/skills/skill-analyzer/references/advanced-checklist.md +44 -0
  163. package/plugins/ima-claude/skills/skill-analyzer/references/core-checklist.md +60 -0
  164. package/plugins/ima-claude/skills/skill-analyzer/scripts/analyze_skill.py +418 -0
  165. package/plugins/ima-claude/skills/skill-creator/LICENSE.txt +202 -0
  166. package/plugins/ima-claude/skills/skill-creator/SKILL.md +343 -0
  167. package/plugins/ima-claude/skills/skill-creator/references/output-patterns.md +82 -0
  168. package/plugins/ima-claude/skills/skill-creator/references/workflows.md +28 -0
  169. package/plugins/ima-claude/skills/skill-creator/scripts/init_skill.py +303 -0
  170. package/plugins/ima-claude/skills/skill-creator/scripts/package_skill.py +110 -0
  171. package/plugins/ima-claude/skills/skill-creator/scripts/quick_validate.py +103 -0
  172. package/plugins/ima-claude/skills/task-master/SKILL.md +51 -0
  173. package/plugins/ima-claude/skills/task-planner/SKILL.md +228 -0
  174. package/plugins/ima-claude/skills/task-runner/SKILL.md +192 -0
  175. package/plugins/ima-claude/skills/unit-testing/SKILL.md +198 -0
  176. package/plugins/ima-claude/skills/unit-testing/references/mock-patterns.md +181 -0
  177. package/plugins/ima-claude/skills/unit-testing/references/tdd-workflow.md +177 -0
  178. package/plugins/ima-claude/skills/unit-testing/references/test-strategy.md +126 -0
  179. package/plugins/ima-claude/skills/wp-local/SKILL.md +246 -0
  180. package/plugins/ima-claude/skills/wp-local/references/configuration.md +198 -0
  181. package/plugins/ima-claude/skills/wp-local/references/wp-cli-reference.md +406 -0
  182. package/plugins/ima-claude/skills/wp-local/scripts/wp-local.sh +61 -0
@@ -0,0 +1,663 @@
1
+ ---
2
+ name: "py-fp"
3
+ description: "Python FP principles with anti-over-engineering focus - Simple > Complex | Evidence > Assumptions"
4
+ ---
5
+
6
+ # Python Functional Programming
7
+
8
+ Core functional programming principles for Python with anti-over-engineering enforcement. Python is a multi-paradigm language with solid FP support — but it's not Haskell. This skill teaches you to be functional in spirit, Pythonic in expression.
9
+
10
+ ## When to Use This Skill
11
+
12
+ - Implementing pure, testable Python functions
13
+ - Data transformation pipelines (pandas, polars, ML preprocessing)
14
+ - Need FP architectural guidance for Python
15
+ - Preventing over-engineering and custom FP utility creation
16
+ - Comprehensive testing strategies with pytest
17
+ - Evidence-based performance optimization
18
+
19
+ ## CRITICAL: Anti-Over-Engineering (PRIMARY FOCUS)
20
+
21
+ **Core Principle**: "Simple > Complex | Evidence > Assumptions"
22
+
23
+ > **Clarification**: This skill prevents CREATING custom FP utility functions (pipe, compose, curry, monads) to make Python "feel" like Haskell. Using established libraries (toolz, pandas, itertools, etc.) is perfectly fine. FP is a mindset — pure functions, immutability, composition — not a rigid API signature.
24
+
25
+ ### Don't Create Custom FP Utilities
26
+
27
+ ```python
28
+ # DON'T CREATE: pipe() utility
29
+ def pipe(value, *functions):
30
+ for fn in functions:
31
+ value = fn(value)
32
+ return value
33
+
34
+ # INSTEAD: Native function calls with early returns
35
+ def validate_user(user_data: dict) -> dict:
36
+ required_check = validate_required(["email", "name"], user_data)
37
+ if not required_check["valid"]:
38
+ return required_check
39
+
40
+ email_check = validate_email(user_data)
41
+ if not email_check["valid"]:
42
+ return email_check
43
+
44
+ return validate_name_length(user_data)
45
+
46
+ # DON'T CREATE: compose() utility
47
+ def compose(*fns):
48
+ def composed(x):
49
+ for fn in reversed(fns):
50
+ x = fn(x)
51
+ return x
52
+ return composed
53
+
54
+ # INSTEAD: Direct function calls
55
+ def process_data(raw: dict) -> dict:
56
+ normalized = normalize(raw)
57
+ validated = validate(normalized)
58
+ return transform(validated)
59
+
60
+ # DON'T CREATE: curry() utility
61
+ # INSTEAD: functools.partial or closures
62
+ from functools import partial
63
+
64
+ validate_min_length = partial(validate_length, min_len=3)
65
+
66
+ # Or closures for more complex cases
67
+ def create_validator(rules: list) -> callable:
68
+ def validate(value):
69
+ errors = [r["message"] for r in rules if not r["check"](value)]
70
+ return {"valid": True} if not errors else {"valid": False, "errors": errors}
71
+ return validate
72
+
73
+ # DON'T CREATE: Custom monads (Maybe, Either, IO)
74
+ # INSTEAD: Native error handling with result dicts
75
+ def get_user(user_id: int, db) -> dict:
76
+ try:
77
+ user = db.find(user_id)
78
+ return {"success": True, "data": user}
79
+ except Exception as e:
80
+ return {"success": False, "error": str(e)}
81
+ ```
82
+
83
+ ### Context-Appropriate Complexity
84
+
85
+ ```python
86
+ # CLI Script: Simple and direct
87
+ def process_file(file_path: str) -> list[str]:
88
+ with open(file_path) as f:
89
+ return [line.upper() for line in f if line.strip()]
90
+
91
+ # Production Service: Appropriate error handling
92
+ def process_file(file_path: str, logger) -> dict:
93
+ try:
94
+ with open(file_path) as f:
95
+ lines = [line.strip() for line in f if line.strip()]
96
+ logger.info("File processed", extra={"path": file_path, "lines": len(lines)})
97
+ return {"success": True, "data": [line.upper() for line in lines]}
98
+ except OSError as e:
99
+ logger.error("File processing failed", extra={"path": file_path, "error": str(e)})
100
+ return {"success": False, "error": str(e)}
101
+
102
+ # Data Pipeline: Composable transformations
103
+ def create_file_processor(transforms: list[callable]):
104
+ compiled = [compile_transform(t) for t in transforms]
105
+
106
+ def process(file_path: str, logger) -> dict:
107
+ with open(file_path) as f:
108
+ data = f.read()
109
+ for transform in compiled:
110
+ data = transform(data)
111
+ return {"success": True, "data": data}
112
+
113
+ return process
114
+ ```
115
+
116
+ ## Core FP Patterns (Error-Preventing Essentials)
117
+
118
+ ### 1. Purity and Side Effect Isolation
119
+
120
+ **Rule**: Separate business logic from side effects.
121
+
122
+ ```python
123
+ # IMPURE - side effects mixed with logic
124
+ def calculate_total(items: list[dict]) -> float:
125
+ print("Processing items") # Side effect
126
+ global running_total
127
+ running_total += sum(i["price"] for i in items) # Mutation
128
+ return running_total
129
+
130
+ # PURE: business logic
131
+ def calculate_total(items: list[dict]) -> float:
132
+ return sum(item["price"] for item in items)
133
+
134
+ # ISOLATED: side effects separate
135
+ def log_and_calculate(items: list[dict], logger) -> float:
136
+ total = calculate_total(items) # Pure calculation
137
+ logger.info(f"Total: {total}") # Side effect isolated
138
+ return total
139
+ ```
140
+
141
+ **Benefits**:
142
+ - 100% testable with all edge cases
143
+ - Predictable behavior and debugging
144
+ - Safe for `multiprocessing` (pure functions serialize trivially)
145
+ - Enables memoization via `@lru_cache`
146
+
147
+ ### 2. Composition Over Inheritance
148
+
149
+ **Rule**: Build complex behavior from simple functions.
150
+
151
+ ```python
152
+ # Class hierarchy approach (avoid)
153
+ class BaseValidator:
154
+ def validate(self, value): raise NotImplementedError
155
+
156
+ class EmailValidator(BaseValidator):
157
+ def validate(self, value): ...
158
+
159
+ # Function composition (no utilities needed)
160
+ def validate_required(value) -> bool:
161
+ return value is not None and value != ""
162
+
163
+ def validate_email(value: str) -> bool:
164
+ return "@" in value and "." in value.split("@")[-1]
165
+
166
+ def validate_length(min_len: int, max_len: int):
167
+ return lambda value: min_len <= len(value) <= max_len
168
+
169
+ # Simple composition without pipe() utility
170
+ def validate_user_email(email: str) -> dict:
171
+ if not validate_required(email):
172
+ return {"valid": False, "error": "Required"}
173
+ if not validate_email(email):
174
+ return {"valid": False, "error": "Invalid email"}
175
+ if not validate_length(5, 100)(email):
176
+ return {"valid": False, "error": "Length"}
177
+ return {"valid": True}
178
+ ```
179
+
180
+ ### 3. Dependency Injection Through Parameters
181
+
182
+ **Rule**: Pass dependencies explicitly, avoid global state.
183
+
184
+ ```python
185
+ # Hidden dependencies, hard to test
186
+ def save_user(user_data: dict) -> dict:
187
+ hashed = bcrypt.hash(user_data["password"]) # Hidden
188
+ return database.save({**user_data, "password": hashed}) # Hidden
189
+
190
+ # Explicit dependencies, fully testable
191
+ def save_user(user_data: dict, hasher, database) -> dict:
192
+ hashed = hasher.hash(user_data["password"])
193
+ return database.save({**user_data, "password": hashed})
194
+
195
+ # Factory for repeated use (closure-based DI)
196
+ def create_user_service(hasher, database):
197
+ def save(user_data: dict) -> dict:
198
+ return save_user(user_data, hasher, database)
199
+
200
+ def find(user_id: int) -> dict:
201
+ return database.find_by_id(user_id)
202
+
203
+ return {"save": save, "find": find}
204
+ ```
205
+
206
+ ### 4. Immutability Patterns
207
+
208
+ **Rule**: Don't mutate input data. Create new structures.
209
+
210
+ ```python
211
+ from dataclasses import dataclass, replace, field
212
+ from typing import NamedTuple
213
+
214
+ # Frozen dataclasses — the primary immutable record
215
+ @dataclass(frozen=True)
216
+ class User:
217
+ name: str
218
+ email: str
219
+ settings: dict = field(default_factory=dict)
220
+
221
+ # "Update" via replace() — returns new instance
222
+ def update_email(user: User, new_email: str) -> User:
223
+ return replace(user, email=new_email)
224
+
225
+ # NamedTuple — lighter weight alternative
226
+ class Point(NamedTuple):
227
+ x: float
228
+ y: float
229
+
230
+ def translate(point: Point, dx: float, dy: float) -> Point:
231
+ return Point(point.x + dx, point.y + dy)
232
+
233
+ # Dict immutability — spread with {**d}
234
+ def update_settings(user: dict, settings: dict) -> dict:
235
+ return {**user, "settings": {**user.get("settings", {}), **settings}}
236
+
237
+ # List operations without mutation
238
+ def add_item(items: list, new_item) -> list:
239
+ return [*items, new_item]
240
+
241
+ def remove_item(items: list[dict], item_id: int) -> list[dict]:
242
+ return [i for i in items if i["id"] != item_id]
243
+
244
+ def update_item(items: list[dict], item_id: int, updates: dict) -> list[dict]:
245
+ return [{**i, **updates} if i["id"] == item_id else i for i in items]
246
+ ```
247
+
248
+ > **Important**: `frozen=True` is shallow — it prevents attribute reassignment but doesn't freeze mutable attribute values (like dicts or lists inside). For deep immutability, use `pyrsistent` or keep nested values as tuples/frozensets.
249
+
250
+ ## Python-Specific Patterns
251
+
252
+ ### Comprehensions Over map/filter (Pythonic FP)
253
+
254
+ ```python
255
+ # map/filter style (less Pythonic)
256
+ active_names = list(map(
257
+ lambda u: u["name"],
258
+ filter(lambda u: u["active"], users)
259
+ ))
260
+
261
+ # Comprehension style (Pythonic FP)
262
+ active_names = [u["name"] for u in users if u["active"]]
263
+
264
+ # Dict comprehension for transformations
265
+ name_by_id = {u["id"]: u["name"] for u in users}
266
+
267
+ # Generator expression for lazy evaluation (large data)
268
+ totals = sum(item["price"] for item in items if item["taxable"])
269
+ ```
270
+
271
+ ### Generators for Lazy Pipelines
272
+
273
+ ```python
274
+ # Generators are Python's answer to lazy evaluation
275
+ def read_records(path: str):
276
+ with open(path) as f:
277
+ for line in f:
278
+ yield parse_record(line)
279
+
280
+ def filter_valid(records):
281
+ for record in records:
282
+ if record["status"] == "active":
283
+ yield record
284
+
285
+ def transform(records):
286
+ for record in records:
287
+ yield {**record, "name": record["name"].upper()}
288
+
289
+ # Compose lazily — no intermediate lists
290
+ pipeline = transform(filter_valid(read_records("data.csv")))
291
+ results = list(pipeline) # Materializes only when needed
292
+ ```
293
+
294
+ ### itertools and functools (The FP Standard Library)
295
+
296
+ ```python
297
+ from functools import partial, lru_cache, reduce
298
+ from itertools import chain, groupby, islice, takewhile
299
+ from operator import itemgetter
300
+
301
+ # partial — the Pythonic way to "curry"
302
+ multiply_by_tax = partial(lambda rate, price: price * (1 + rate), 0.08)
303
+
304
+ # lru_cache — memoize pure functions (MUST be pure!)
305
+ @lru_cache(maxsize=256)
306
+ def fibonacci(n: int) -> int:
307
+ if n < 2:
308
+ return n
309
+ return fibonacci(n - 1) + fibonacci(n - 2)
310
+
311
+ # operator module — eliminate trivial lambdas
312
+ from operator import itemgetter, attrgetter
313
+
314
+ sorted_by_name = sorted(users, key=itemgetter("name"))
315
+ # vs: sorted(users, key=lambda u: u["name"])
316
+
317
+ # itertools for complex transformations
318
+ from itertools import groupby
319
+
320
+ def group_by_category(items: list[dict]) -> dict:
321
+ sorted_items = sorted(items, key=itemgetter("category"))
322
+ return {
323
+ k: list(v)
324
+ for k, v in groupby(sorted_items, key=itemgetter("category"))
325
+ }
326
+ ```
327
+
328
+ ### Pattern Matching (Python 3.10+)
329
+
330
+ ```python
331
+ # Structural pattern matching for clean dispatch
332
+ def process_event(event: dict) -> dict:
333
+ match event:
334
+ case {"type": "click", "target": target}:
335
+ return handle_click(target)
336
+ case {"type": "submit", "data": data}:
337
+ return handle_submit(data)
338
+ case {"type": "error", "code": code, "message": msg}:
339
+ return handle_error(code, msg)
340
+ case _:
341
+ return {"error": f"Unknown event type: {event.get('type')}"}
342
+
343
+ # Pattern matching with guards
344
+ def categorize_score(score: int) -> str:
345
+ match score:
346
+ case n if n >= 90: return "A"
347
+ case n if n >= 80: return "B"
348
+ case n if n >= 70: return "C"
349
+ case _: return "F"
350
+ ```
351
+
352
+ ### Type Hints for FP Clarity
353
+
354
+ ```python
355
+ from typing import TypeVar, Callable, Optional
356
+ from collections.abc import Iterable, Iterator
357
+
358
+ T = TypeVar("T")
359
+ U = TypeVar("U")
360
+
361
+ # Type hints make pure function contracts explicit
362
+ def transform_all(items: Iterable[T], fn: Callable[[T], U]) -> list[U]:
363
+ return [fn(item) for item in items]
364
+
365
+ # Union types (3.10+) for result patterns
366
+ def divide(a: float, b: float) -> dict:
367
+ if b == 0:
368
+ return {"success": False, "error": "Division by zero"}
369
+ return {"success": True, "data": a / b}
370
+
371
+ # Optional signals nullable return
372
+ def find_user(users: list[dict], user_id: int) -> Optional[dict]:
373
+ return next((u for u in users if u["id"] == user_id), None)
374
+ ```
375
+
376
+ ## Data Science / ML Patterns
377
+
378
+ ### pandas pipe() for Transformation Chains
379
+
380
+ ```python
381
+ import pandas as pd
382
+
383
+ # Each function: DataFrame in, DataFrame out (pure transforms)
384
+ def clean_nulls(df: pd.DataFrame) -> pd.DataFrame:
385
+ return df.dropna(subset=["email", "name"])
386
+
387
+ def normalize_names(df: pd.DataFrame) -> pd.DataFrame:
388
+ return df.assign(name=df["name"].str.strip().str.title())
389
+
390
+ def add_age_group(df: pd.DataFrame) -> pd.DataFrame:
391
+ bins = [0, 18, 35, 55, 120]
392
+ labels = ["youth", "young_adult", "adult", "senior"]
393
+ return df.assign(age_group=pd.cut(df["age"], bins=bins, labels=labels))
394
+
395
+ # Compose via pipe() — FP pipeline, Pythonic syntax
396
+ result = (
397
+ raw_df
398
+ .pipe(clean_nulls)
399
+ .pipe(normalize_names)
400
+ .pipe(add_age_group)
401
+ )
402
+ ```
403
+
404
+ ### Pure Feature Engineering
405
+
406
+ ```python
407
+ # Pure transformation functions for ML pipelines
408
+ def extract_features(df: pd.DataFrame) -> pd.DataFrame:
409
+ return df.assign(
410
+ word_count=df["text"].str.split().str.len(),
411
+ has_url=df["text"].str.contains(r"https?://", regex=True),
412
+ text_length=df["text"].str.len(),
413
+ )
414
+
415
+ # scikit-learn FunctionTransformer for pipeline integration
416
+ from sklearn.preprocessing import FunctionTransformer
417
+ from sklearn.pipeline import Pipeline
418
+
419
+ feature_extractor = FunctionTransformer(extract_features)
420
+
421
+ pipeline = Pipeline([
422
+ ("features", feature_extractor),
423
+ ("scaler", StandardScaler()),
424
+ ("model", LogisticRegression()),
425
+ ])
426
+ ```
427
+
428
+ ### Polars (FP-Friendly Alternative to pandas)
429
+
430
+ ```python
431
+ import polars as pl
432
+
433
+ # Polars expressions are lazy and composable by design
434
+ result = (
435
+ pl.scan_csv("data.csv") # Lazy — nothing runs yet
436
+ .filter(pl.col("status") == "active")
437
+ .with_columns(
438
+ full_name=pl.col("first_name") + " " + pl.col("last_name"),
439
+ age_group=pl.when(pl.col("age") < 30).then(pl.lit("young"))
440
+ .otherwise(pl.lit("senior")),
441
+ )
442
+ .group_by("department")
443
+ .agg(pl.col("salary").mean().alias("avg_salary"))
444
+ .collect() # Execute the entire plan at once
445
+ )
446
+ ```
447
+
448
+ ## Result Type Pattern
449
+
450
+ ```python
451
+ # Standard result shape — consistent across the codebase
452
+ def success(data=None) -> dict:
453
+ return {"success": True, "data": data}
454
+
455
+ def failure(error: str) -> dict:
456
+ return {"success": False, "error": error}
457
+
458
+ # Chain results with early return
459
+ def process_order(order: dict) -> dict:
460
+ validated = validate_order(order)
461
+ if not validated["success"]:
462
+ return validated
463
+
464
+ priced = calculate_pricing(validated["data"])
465
+ if not priced["success"]:
466
+ return priced
467
+
468
+ return submit_order(priced["data"])
469
+ ```
470
+
471
+ ## Testing Essentials (Enabled by Purity)
472
+
473
+ **Philosophy**: Pure functions enable testing all edge cases systematically.
474
+
475
+ ```python
476
+ import pytest
477
+
478
+ # Parametrized testing — the FP way
479
+ @pytest.mark.parametrize("price,rate,expected", [
480
+ (100.0, 0.1, 10.0),
481
+ (50.0, 0.2, 10.0),
482
+ (0.0, 0.1, 0.0),
483
+ (100.0, 0.0, 0.0),
484
+ ])
485
+ def test_calculate_discount(price, rate, expected):
486
+ assert calculate_discount(price, rate) == expected
487
+
488
+ # Edge case testing — pure functions handle all inputs
489
+ @pytest.mark.parametrize("invalid_input", [None, "", [], {}, 0])
490
+ def test_validate_required_rejects_invalid(invalid_input):
491
+ assert not validate_required(invalid_input)
492
+
493
+ # Testing result chains
494
+ def test_process_order_validation_failure():
495
+ result = process_order({"items": []})
496
+ assert not result["success"]
497
+ assert "empty" in result["error"].lower()
498
+
499
+ # Frozen dataclass testability
500
+ def test_update_email_returns_new_instance():
501
+ user = User(name="Alice", email="old@test.com")
502
+ updated = update_email(user, "new@test.com")
503
+ assert updated.email == "new@test.com"
504
+ assert user.email == "old@test.com" # Original unchanged
505
+ ```
506
+
507
+ ## Performance Patterns (Evidence-Based)
508
+
509
+ **IMPORTANT**: Optimize only when needed with evidence.
510
+
511
+ ### Memoization for Expensive Pure Functions
512
+
513
+ ```python
514
+ from functools import lru_cache
515
+
516
+ # ONLY memoize pure functions with hashable arguments
517
+ @lru_cache(maxsize=128)
518
+ def parse_template(template: str) -> dict:
519
+ # Expensive parsing — cached after first call
520
+ return heavy_parse(template)
521
+
522
+ # For unhashable args, manually cache
523
+ _cache = {}
524
+ def get_config(key: str) -> dict:
525
+ if key not in _cache:
526
+ _cache[key] = load_config(key)
527
+ return _cache[key]
528
+ ```
529
+
530
+ ### Generator Pipelines for Large Data
531
+
532
+ ```python
533
+ # Memory-efficient: processes one record at a time
534
+ def process_large_file(path: str):
535
+ records = read_records(path) # Generator
536
+ valid = filter_valid(records) # Generator
537
+ transformed = transform(valid) # Generator
538
+
539
+ # Only materializes in batches
540
+ batch = []
541
+ for record in transformed:
542
+ batch.append(record)
543
+ if len(batch) >= 1000:
544
+ yield batch
545
+ batch = []
546
+ if batch:
547
+ yield batch
548
+ ```
549
+
550
+ ### multiprocessing with Pure Functions
551
+
552
+ ```python
553
+ from multiprocessing import Pool
554
+
555
+ # Pure functions parallelize trivially
556
+ def process_item(item: dict) -> dict:
557
+ return {**item, "score": compute_score(item)}
558
+
559
+ # No shared state, no locks, no bugs
560
+ with Pool(4) as pool:
561
+ results = pool.map(process_item, items)
562
+ ```
563
+
564
+ ## Python-Specific Gotchas
565
+
566
+ ### Recursion Limit
567
+
568
+ ```python
569
+ # Python has a 1000-call recursion limit. No tail call optimization.
570
+ # DON'T: recursive algorithms for large inputs
571
+ def factorial(n):
572
+ return 1 if n <= 1 else n * factorial(n - 1) # Blows up at n > 1000
573
+
574
+ # DO: iterative or reduce
575
+ from functools import reduce
576
+ from operator import mul
577
+
578
+ def factorial(n: int) -> int:
579
+ return reduce(mul, range(1, n + 1), 1)
580
+ ```
581
+
582
+ ### Mutable Default Arguments
583
+
584
+ ```python
585
+ # NEVER use mutable defaults
586
+ def add_item(item, items=[]): # BUG: shared mutable default
587
+ items.append(item)
588
+ return items
589
+
590
+ # INSTEAD: None sentinel
591
+ def add_item(item, items=None):
592
+ items = items if items is not None else []
593
+ return [*items, item] # Return new list, don't mutate
594
+ ```
595
+
596
+ ### Shallow vs Deep Copy
597
+
598
+ ```python
599
+ # frozen=True is SHALLOW
600
+ @dataclass(frozen=True)
601
+ class Config:
602
+ settings: dict # This dict is still mutable!
603
+
604
+ config = Config(settings={"debug": True})
605
+ # config.settings = {} # FrozenInstanceError
606
+ config.settings["debug"] = False # This WORKS — shallow freeze
607
+
608
+ # For deep immutability: use tuples, frozensets, or pyrsistent
609
+ from types import MappingProxyType
610
+
611
+ def freeze_dict(d: dict):
612
+ return MappingProxyType(d) # Read-only view
613
+ ```
614
+
615
+ ## Quality Gates (Pre-Implementation Checklist)
616
+
617
+ 1. **"Can this be pure?"** - Separate business logic from side effects
618
+ 2. **"Can this use native patterns?"** - Comprehensions, generators, functools, itertools
619
+ 3. **"Can this be simplified?"** - Choose simple solution over complex abstraction
620
+ 4. **"Is this complexity justified?"** - Evidence-based complexity decisions
621
+ 5. **"Is this testable?"** - Pure functions enable comprehensive testing
622
+ 6. **"Are type hints used?"** - Type hints on all public function signatures
623
+
624
+ ## When to Load Reference Files
625
+
626
+ ### Deep Principles and Explanations
627
+ **File**: `references/core-principles.md`
628
+ **Load when**:
629
+ - Learning mode or explaining WHY behind patterns
630
+ - Making architectural decisions
631
+ - Need complete Result Type patterns
632
+ - Anti-pattern recognition details
633
+ - Python-specific FP philosophy deep-dive
634
+
635
+ ### Testing Methodology
636
+ **File**: `references/testing-patterns.md`
637
+ **Load when**:
638
+ - Building comprehensive test suites with pytest
639
+ - Improving test coverage
640
+ - Edge case analysis and boundary testing
641
+ - Testing data pipelines and ML code
642
+ - Property-based testing with Hypothesis
643
+
644
+ ### Working Examples
645
+ **Directory**: `examples/`
646
+ **Load when**:
647
+ - Need complete working code
648
+ - Integration examples
649
+ - Learning implementation patterns
650
+
651
+ ## Integration with Domain Skills
652
+
653
+ This core skill provides the foundation for:
654
+
655
+ - **py-fp-django**: Django patterns with FP principles (future)
656
+ - **py-fp-fastapi**: FastAPI patterns with FP principles (future)
657
+ - **py-fp-ml**: ML/data science patterns with FP principles (future)
658
+
659
+ Each domain skill references this core and adds domain-specific patterns.
660
+
661
+ ## Philosophy
662
+
663
+ *"Pure functions, Pythonic patterns, type hints, appropriate complexity, and comprehensive testing for maintainable, predictable code. Be functional in spirit, Pythonic in expression — don't fight the language, work with it."*