@booklib/skills 1.2.0 → 1.3.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 (100) hide show
  1. package/CONTRIBUTING.md +122 -0
  2. package/README.md +20 -2
  3. package/ROADMAP.md +36 -0
  4. package/animation-at-work/evals/evals.json +44 -0
  5. package/animation-at-work/examples/after.md +64 -0
  6. package/animation-at-work/examples/before.md +35 -0
  7. package/animation-at-work/scripts/audit_animations.py +295 -0
  8. package/bin/skills.js +552 -42
  9. package/clean-code-reviewer/SKILL.md +109 -1
  10. package/clean-code-reviewer/evals/evals.json +121 -3
  11. package/clean-code-reviewer/examples/after.md +48 -0
  12. package/clean-code-reviewer/examples/before.md +33 -0
  13. package/clean-code-reviewer/references/api_reference.md +158 -0
  14. package/clean-code-reviewer/references/practices-catalog.md +282 -0
  15. package/clean-code-reviewer/references/review-checklist.md +254 -0
  16. package/clean-code-reviewer/scripts/pre-review.py +206 -0
  17. package/data-intensive-patterns/evals/evals.json +43 -0
  18. package/data-intensive-patterns/examples/after.md +61 -0
  19. package/data-intensive-patterns/examples/before.md +38 -0
  20. package/data-intensive-patterns/scripts/adr.py +213 -0
  21. package/data-pipelines/evals/evals.json +45 -0
  22. package/data-pipelines/examples/after.md +97 -0
  23. package/data-pipelines/examples/before.md +37 -0
  24. package/data-pipelines/scripts/new_pipeline.py +444 -0
  25. package/design-patterns/evals/evals.json +46 -0
  26. package/design-patterns/examples/after.md +52 -0
  27. package/design-patterns/examples/before.md +29 -0
  28. package/design-patterns/scripts/scaffold.py +807 -0
  29. package/domain-driven-design/SKILL.md +120 -0
  30. package/domain-driven-design/evals/evals.json +48 -0
  31. package/domain-driven-design/examples/after.md +80 -0
  32. package/domain-driven-design/examples/before.md +43 -0
  33. package/domain-driven-design/scripts/scaffold.py +421 -0
  34. package/effective-java/evals/evals.json +46 -0
  35. package/effective-java/examples/after.md +83 -0
  36. package/effective-java/examples/before.md +37 -0
  37. package/effective-java/scripts/checkstyle_setup.py +211 -0
  38. package/effective-kotlin/evals/evals.json +45 -0
  39. package/effective-kotlin/examples/after.md +36 -0
  40. package/effective-kotlin/examples/before.md +38 -0
  41. package/effective-python/evals/evals.json +44 -0
  42. package/effective-python/examples/after.md +56 -0
  43. package/effective-python/examples/before.md +40 -0
  44. package/effective-python/references/api_reference.md +218 -0
  45. package/effective-python/references/practices-catalog.md +483 -0
  46. package/effective-python/references/review-checklist.md +190 -0
  47. package/effective-python/scripts/lint.py +173 -0
  48. package/kotlin-in-action/evals/evals.json +43 -0
  49. package/kotlin-in-action/examples/after.md +53 -0
  50. package/kotlin-in-action/examples/before.md +39 -0
  51. package/kotlin-in-action/scripts/setup_detekt.py +224 -0
  52. package/lean-startup/evals/evals.json +43 -0
  53. package/lean-startup/examples/after.md +80 -0
  54. package/lean-startup/examples/before.md +34 -0
  55. package/lean-startup/scripts/new_experiment.py +286 -0
  56. package/microservices-patterns/SKILL.md +140 -0
  57. package/microservices-patterns/evals/evals.json +45 -0
  58. package/microservices-patterns/examples/after.md +69 -0
  59. package/microservices-patterns/examples/before.md +40 -0
  60. package/microservices-patterns/scripts/new_service.py +583 -0
  61. package/package.json +2 -8
  62. package/refactoring-ui/evals/evals.json +45 -0
  63. package/refactoring-ui/examples/after.md +85 -0
  64. package/refactoring-ui/examples/before.md +58 -0
  65. package/refactoring-ui/scripts/audit_css.py +250 -0
  66. package/skill-router/SKILL.md +142 -0
  67. package/skill-router/evals/evals.json +38 -0
  68. package/skill-router/examples/after.md +63 -0
  69. package/skill-router/examples/before.md +39 -0
  70. package/skill-router/references/api_reference.md +24 -0
  71. package/skill-router/references/routing-heuristics.md +89 -0
  72. package/skill-router/references/skill-catalog.md +156 -0
  73. package/skill-router/scripts/route.py +266 -0
  74. package/storytelling-with-data/evals/evals.json +47 -0
  75. package/storytelling-with-data/examples/after.md +50 -0
  76. package/storytelling-with-data/examples/before.md +33 -0
  77. package/storytelling-with-data/scripts/chart_review.py +301 -0
  78. package/system-design-interview/evals/evals.json +45 -0
  79. package/system-design-interview/examples/after.md +94 -0
  80. package/system-design-interview/examples/before.md +27 -0
  81. package/system-design-interview/scripts/new_design.py +421 -0
  82. package/using-asyncio-python/evals/evals.json +43 -0
  83. package/using-asyncio-python/examples/after.md +68 -0
  84. package/using-asyncio-python/examples/before.md +39 -0
  85. package/using-asyncio-python/scripts/check_blocking.py +270 -0
  86. package/web-scraping-python/evals/evals.json +46 -0
  87. package/web-scraping-python/examples/after.md +109 -0
  88. package/web-scraping-python/examples/before.md +40 -0
  89. package/web-scraping-python/scripts/new_scraper.py +231 -0
  90. /package/{effective-python-skill → effective-python}/SKILL.md +0 -0
  91. /package/{effective-python-skill → effective-python}/ref-01-pythonic-thinking.md +0 -0
  92. /package/{effective-python-skill → effective-python}/ref-02-lists-and-dicts.md +0 -0
  93. /package/{effective-python-skill → effective-python}/ref-03-functions.md +0 -0
  94. /package/{effective-python-skill → effective-python}/ref-04-comprehensions-generators.md +0 -0
  95. /package/{effective-python-skill → effective-python}/ref-05-classes-interfaces.md +0 -0
  96. /package/{effective-python-skill → effective-python}/ref-06-metaclasses-attributes.md +0 -0
  97. /package/{effective-python-skill → effective-python}/ref-07-concurrency.md +0 -0
  98. /package/{effective-python-skill → effective-python}/ref-08-robustness-performance.md +0 -0
  99. /package/{effective-python-skill → effective-python}/ref-09-testing-debugging.md +0 -0
  100. /package/{effective-python-skill → effective-python}/ref-10-collaboration.md +0 -0
@@ -0,0 +1,807 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ GoF Pattern Scaffold — generates complete, idiomatic boilerplate for design patterns.
4
+ Usage: python scaffold.py <pattern> <ClassName> [--lang python|kotlin|java]
5
+
6
+ Supported patterns: strategy, observer, factory, decorator, command, singleton
7
+ """
8
+
9
+ import argparse
10
+ import sys
11
+ from pathlib import Path
12
+ from string import Template
13
+
14
+ # ---------------------------------------------------------------------------
15
+ # Pattern templates per language
16
+ # ---------------------------------------------------------------------------
17
+
18
+ PATTERNS: dict[str, dict] = {}
19
+
20
+ # ---- STRATEGY --------------------------------------------------------------
21
+ PATTERNS["strategy"] = {
22
+ "python": Template('''\
23
+ #!/usr/bin/env python3
24
+ """Strategy pattern — ${Name}."""
25
+ from abc import ABC, abstractmethod
26
+
27
+
28
+ # Interface
29
+ class ${Name}Strategy(ABC):
30
+ @abstractmethod
31
+ def execute(self, data: str) -> str: ...
32
+
33
+
34
+ # Concrete strategy A
35
+ class ${Name}StrategyA(${Name}Strategy):
36
+ def execute(self, data: str) -> str:
37
+ return f"[StrategyA] {data.upper()}"
38
+
39
+
40
+ # Concrete strategy B
41
+ class ${Name}StrategyB(${Name}Strategy):
42
+ def execute(self, data: str) -> str:
43
+ return f"[StrategyB] {data[::-1]}"
44
+
45
+
46
+ # Context
47
+ class ${Name}Context:
48
+ def __init__(self, strategy: ${Name}Strategy) -> None:
49
+ self._strategy = strategy
50
+
51
+ def set_strategy(self, strategy: ${Name}Strategy) -> None:
52
+ self._strategy = strategy
53
+
54
+ def run(self, data: str) -> str:
55
+ return self._strategy.execute(data)
56
+
57
+
58
+ # Usage example
59
+ if __name__ == "__main__":
60
+ ctx = ${Name}Context(${Name}StrategyA())
61
+ print(ctx.run("hello")) # [StrategyA] HELLO
62
+ ctx.set_strategy(${Name}StrategyB())
63
+ print(ctx.run("hello")) # [StrategyB] olleh
64
+ '''),
65
+ "kotlin": Template('''\
66
+ package com.example.patterns
67
+
68
+ // Interface
69
+ interface ${Name}Strategy {
70
+ fun execute(data: String): String
71
+ }
72
+
73
+ // Concrete A
74
+ class ${Name}StrategyA : ${Name}Strategy {
75
+ override fun execute(data: String) = "[StrategyA] ${dollar}{data.uppercase()}"
76
+ }
77
+
78
+ // Concrete B
79
+ class ${Name}StrategyB : ${Name}Strategy {
80
+ override fun execute(data: String) = "[StrategyB] ${dollar}{data.reversed()}"
81
+ }
82
+
83
+ // Context
84
+ class ${Name}Context(private var strategy: ${Name}Strategy) {
85
+ fun setStrategy(s: ${Name}Strategy) { strategy = s }
86
+ fun run(data: String): String = strategy.execute(data)
87
+ }
88
+
89
+ fun main() {
90
+ val ctx = ${Name}Context(${Name}StrategyA())
91
+ println(ctx.run("hello"))
92
+ ctx.setStrategy(${Name}StrategyB())
93
+ println(ctx.run("hello"))
94
+ }
95
+ '''),
96
+ "java": Template('''\
97
+ package com.example.patterns;
98
+
99
+ // Interface
100
+ public interface ${Name}Strategy {
101
+ String execute(String data);
102
+ }
103
+
104
+ // Concrete A
105
+ class ${Name}StrategyA implements ${Name}Strategy {
106
+ public String execute(String data) { return "[StrategyA] " + data.toUpperCase(); }
107
+ }
108
+
109
+ // Concrete B
110
+ class ${Name}StrategyB implements ${Name}Strategy {
111
+ public String execute(String data) {
112
+ return "[StrategyB] " + new StringBuilder(data).reverse();
113
+ }
114
+ }
115
+
116
+ // Context
117
+ class ${Name}Context {
118
+ private ${Name}Strategy strategy;
119
+ public ${Name}Context(${Name}Strategy s) { this.strategy = s; }
120
+ public void setStrategy(${Name}Strategy s) { this.strategy = s; }
121
+ public String run(String data) { return strategy.execute(data); }
122
+
123
+ public static void main(String[] args) {
124
+ var ctx = new ${Name}Context(new ${Name}StrategyA());
125
+ System.out.println(ctx.run("hello"));
126
+ ctx.setStrategy(new ${Name}StrategyB());
127
+ System.out.println(ctx.run("hello"));
128
+ }
129
+ }
130
+ '''),
131
+ }
132
+
133
+ # ---- OBSERVER --------------------------------------------------------------
134
+ PATTERNS["observer"] = {
135
+ "python": Template('''\
136
+ #!/usr/bin/env python3
137
+ """Observer pattern — ${Name}."""
138
+ from abc import ABC, abstractmethod
139
+ from typing import List
140
+
141
+
142
+ class ${Name}Observer(ABC):
143
+ @abstractmethod
144
+ def update(self, event: str, payload: object) -> None: ...
145
+
146
+
147
+ class ${Name}Subject:
148
+ def __init__(self) -> None:
149
+ self._observers: List[${Name}Observer] = []
150
+
151
+ def subscribe(self, obs: ${Name}Observer) -> None:
152
+ self._observers.append(obs)
153
+
154
+ def unsubscribe(self, obs: ${Name}Observer) -> None:
155
+ self._observers.remove(obs)
156
+
157
+ def notify(self, event: str, payload: object = None) -> None:
158
+ for obs in self._observers:
159
+ obs.update(event, payload)
160
+
161
+
162
+ class ${Name}LogObserver(${Name}Observer):
163
+ def update(self, event: str, payload: object) -> None:
164
+ print(f"[LOG] event={event!r} payload={payload!r}")
165
+
166
+
167
+ class ${Name}MetricsObserver(${Name}Observer):
168
+ def __init__(self) -> None:
169
+ self.counts: dict[str, int] = {}
170
+
171
+ def update(self, event: str, payload: object) -> None:
172
+ self.counts[event] = self.counts.get(event, 0) + 1
173
+ print(f"[METRICS] {event} count={self.counts[event]}")
174
+
175
+
176
+ if __name__ == "__main__":
177
+ subject = ${Name}Subject()
178
+ subject.subscribe(${Name}LogObserver())
179
+ metrics = ${Name}MetricsObserver()
180
+ subject.subscribe(metrics)
181
+ subject.notify("created", {"id": 1})
182
+ subject.notify("updated", {"id": 1, "field": "name"})
183
+ subject.notify("created", {"id": 2})
184
+ '''),
185
+ "kotlin": Template('''\
186
+ package com.example.patterns
187
+
188
+ interface ${Name}Observer {
189
+ fun update(event: String, payload: Any?)
190
+ }
191
+
192
+ class ${Name}Subject {
193
+ private val observers = mutableListOf<${Name}Observer>()
194
+ fun subscribe(o: ${Name}Observer) { observers.add(o) }
195
+ fun unsubscribe(o: ${Name}Observer) { observers.remove(o) }
196
+ fun notify(event: String, payload: Any? = null) =
197
+ observers.forEach { it.update(event, payload) }
198
+ }
199
+
200
+ class ${Name}LogObserver : ${Name}Observer {
201
+ override fun update(event: String, payload: Any?) =
202
+ println("[LOG] event=$event payload=$payload")
203
+ }
204
+
205
+ class ${Name}MetricsObserver : ${Name}Observer {
206
+ private val counts = mutableMapOf<String, Int>()
207
+ override fun update(event: String, payload: Any?) {
208
+ counts[event] = (counts[event] ?: 0) + 1
209
+ println("[METRICS] $event count=${dollar}{counts[event]}")
210
+ }
211
+ }
212
+
213
+ fun main() {
214
+ val subject = ${Name}Subject()
215
+ subject.subscribe(${Name}LogObserver())
216
+ subject.subscribe(${Name}MetricsObserver())
217
+ subject.notify("created", mapOf("id" to 1))
218
+ subject.notify("updated", mapOf("id" to 1))
219
+ }
220
+ '''),
221
+ "java": Template('''\
222
+ package com.example.patterns;
223
+ import java.util.ArrayList;
224
+ import java.util.HashMap;
225
+ import java.util.List;
226
+ import java.util.Map;
227
+
228
+ public interface ${Name}Observer {
229
+ void update(String event, Object payload);
230
+ }
231
+
232
+ class ${Name}Subject {
233
+ private final List<${Name}Observer> observers = new ArrayList<>();
234
+ public void subscribe(${Name}Observer o) { observers.add(o); }
235
+ public void unsubscribe(${Name}Observer o) { observers.remove(o); }
236
+ public void notify(String event, Object payload) {
237
+ observers.forEach(o -> o.update(event, payload));
238
+ }
239
+ }
240
+
241
+ class ${Name}LogObserver implements ${Name}Observer {
242
+ public void update(String event, Object payload) {
243
+ System.out.printf("[LOG] event=%s payload=%s%n", event, payload);
244
+ }
245
+ }
246
+
247
+ class ${Name}MetricsObserver implements ${Name}Observer {
248
+ private final Map<String, Integer> counts = new HashMap<>();
249
+ public void update(String event, Object payload) {
250
+ counts.merge(event, 1, Integer::sum);
251
+ System.out.printf("[METRICS] %s count=%d%n", event, counts.get(event));
252
+ }
253
+ public static void main(String[] args) {
254
+ var s = new ${Name}Subject();
255
+ s.subscribe(new ${Name}LogObserver());
256
+ s.subscribe(new ${Name}MetricsObserver());
257
+ s.notify("created", Map.of("id", 1));
258
+ s.notify("updated", Map.of("id", 1));
259
+ }
260
+ }
261
+ '''),
262
+ }
263
+
264
+ # ---- FACTORY ---------------------------------------------------------------
265
+ PATTERNS["factory"] = {
266
+ "python": Template('''\
267
+ #!/usr/bin/env python3
268
+ """Factory Method pattern — ${Name}."""
269
+ from abc import ABC, abstractmethod
270
+
271
+
272
+ class ${Name}Product(ABC):
273
+ @abstractmethod
274
+ def operation(self) -> str: ...
275
+
276
+
277
+ class ${Name}ConcreteProductA(${Name}Product):
278
+ def operation(self) -> str:
279
+ return "${Name}ConcreteProductA result"
280
+
281
+
282
+ class ${Name}ConcreteProductB(${Name}Product):
283
+ def operation(self) -> str:
284
+ return "${Name}ConcreteProductB result"
285
+
286
+
287
+ class ${Name}Creator(ABC):
288
+ @abstractmethod
289
+ def create_product(self) -> ${Name}Product: ...
290
+
291
+ def deliver(self) -> str:
292
+ product = self.create_product()
293
+ return f"Creator: {product.operation()}"
294
+
295
+
296
+ class ${Name}CreatorA(${Name}Creator):
297
+ def create_product(self) -> ${Name}Product:
298
+ return ${Name}ConcreteProductA()
299
+
300
+
301
+ class ${Name}CreatorB(${Name}Creator):
302
+ def create_product(self) -> ${Name}Product:
303
+ return ${Name}ConcreteProductB()
304
+
305
+
306
+ def client(creator: ${Name}Creator) -> None:
307
+ print(creator.deliver())
308
+
309
+
310
+ if __name__ == "__main__":
311
+ client(${Name}CreatorA())
312
+ client(${Name}CreatorB())
313
+ '''),
314
+ "kotlin": Template('''\
315
+ package com.example.patterns
316
+
317
+ interface ${Name}Product { fun operation(): String }
318
+
319
+ class ${Name}ProductA : ${Name}Product {
320
+ override fun operation() = "${Name}ProductA result"
321
+ }
322
+ class ${Name}ProductB : ${Name}Product {
323
+ override fun operation() = "${Name}ProductB result"
324
+ }
325
+
326
+ abstract class ${Name}Creator {
327
+ abstract fun createProduct(): ${Name}Product
328
+ fun deliver() = "Creator: ${dollar}{createProduct().operation()}"
329
+ }
330
+
331
+ class ${Name}CreatorA : ${Name}Creator() {
332
+ override fun createProduct() = ${Name}ProductA()
333
+ }
334
+ class ${Name}CreatorB : ${Name}Creator() {
335
+ override fun createProduct() = ${Name}ProductB()
336
+ }
337
+
338
+ fun main() {
339
+ println(${Name}CreatorA().deliver())
340
+ println(${Name}CreatorB().deliver())
341
+ }
342
+ '''),
343
+ "java": Template('''\
344
+ package com.example.patterns;
345
+
346
+ public interface ${Name}Product { String operation(); }
347
+
348
+ class ${Name}ProductA implements ${Name}Product {
349
+ public String operation() { return "${Name}ProductA result"; }
350
+ }
351
+ class ${Name}ProductB implements ${Name}Product {
352
+ public String operation() { return "${Name}ProductB result"; }
353
+ }
354
+
355
+ abstract class ${Name}Creator {
356
+ public abstract ${Name}Product createProduct();
357
+ public String deliver() { return "Creator: " + createProduct().operation(); }
358
+ }
359
+
360
+ class ${Name}CreatorA extends ${Name}Creator {
361
+ public ${Name}Product createProduct() { return new ${Name}ProductA(); }
362
+ }
363
+ class ${Name}CreatorB extends ${Name}Creator {
364
+ public ${Name}Product createProduct() { return new ${Name}ProductB(); }
365
+ public static void main(String[] args) {
366
+ System.out.println(new ${Name}CreatorA().deliver());
367
+ System.out.println(new ${Name}CreatorB().deliver());
368
+ }
369
+ }
370
+ '''),
371
+ }
372
+
373
+ # ---- DECORATOR -------------------------------------------------------------
374
+ PATTERNS["decorator"] = {
375
+ "python": Template('''\
376
+ #!/usr/bin/env python3
377
+ """Decorator pattern — ${Name}."""
378
+ from abc import ABC, abstractmethod
379
+
380
+
381
+ class ${Name}Component(ABC):
382
+ @abstractmethod
383
+ def execute(self) -> str: ...
384
+
385
+
386
+ class ${Name}ConcreteComponent(${Name}Component):
387
+ def execute(self) -> str:
388
+ return "base-result"
389
+
390
+
391
+ class ${Name}Decorator(${Name}Component, ABC):
392
+ def __init__(self, wrapped: ${Name}Component) -> None:
393
+ self._wrapped = wrapped
394
+
395
+ def execute(self) -> str:
396
+ return self._wrapped.execute()
397
+
398
+
399
+ class ${Name}LoggingDecorator(${Name}Decorator):
400
+ def execute(self) -> str:
401
+ result = super().execute()
402
+ print(f"[LOG] result={result!r}")
403
+ return result
404
+
405
+
406
+ class ${Name}CachingDecorator(${Name}Decorator):
407
+ def __init__(self, wrapped: ${Name}Component) -> None:
408
+ super().__init__(wrapped)
409
+ self._cache: str | None = None
410
+
411
+ def execute(self) -> str:
412
+ if self._cache is None:
413
+ self._cache = super().execute()
414
+ print("[CACHE] miss — computed")
415
+ else:
416
+ print("[CACHE] hit")
417
+ return self._cache
418
+
419
+
420
+ if __name__ == "__main__":
421
+ base = ${Name}ConcreteComponent()
422
+ cached = ${Name}CachingDecorator(base)
423
+ logged = ${Name}LoggingDecorator(cached)
424
+ print(logged.execute())
425
+ print(logged.execute()) # second call: cache hit
426
+ '''),
427
+ "kotlin": Template('''\
428
+ package com.example.patterns
429
+
430
+ interface ${Name}Component { fun execute(): String }
431
+
432
+ class ${Name}ConcreteComponent : ${Name}Component {
433
+ override fun execute() = "base-result"
434
+ }
435
+
436
+ open class ${Name}Decorator(private val wrapped: ${Name}Component) : ${Name}Component {
437
+ override fun execute() = wrapped.execute()
438
+ }
439
+
440
+ class ${Name}LoggingDecorator(wrapped: ${Name}Component) : ${Name}Decorator(wrapped) {
441
+ override fun execute(): String {
442
+ val r = super.execute()
443
+ println("[LOG] result=$r")
444
+ return r
445
+ }
446
+ }
447
+
448
+ class ${Name}CachingDecorator(wrapped: ${Name}Component) : ${Name}Decorator(wrapped) {
449
+ private var cache: String? = null
450
+ override fun execute(): String {
451
+ if (cache == null) { cache = super.execute(); println("[CACHE] miss") }
452
+ else println("[CACHE] hit")
453
+ return cache!!
454
+ }
455
+ }
456
+
457
+ fun main() {
458
+ val comp = ${Name}LoggingDecorator(${Name}CachingDecorator(${Name}ConcreteComponent()))
459
+ println(comp.execute())
460
+ println(comp.execute())
461
+ }
462
+ '''),
463
+ "java": Template('''\
464
+ package com.example.patterns;
465
+
466
+ public interface ${Name}Component { String execute(); }
467
+
468
+ class ${Name}ConcreteComponent implements ${Name}Component {
469
+ public String execute() { return "base-result"; }
470
+ }
471
+
472
+ abstract class ${Name}Decorator implements ${Name}Component {
473
+ protected final ${Name}Component wrapped;
474
+ protected ${Name}Decorator(${Name}Component wrapped) { this.wrapped = wrapped; }
475
+ public String execute() { return wrapped.execute(); }
476
+ }
477
+
478
+ class ${Name}LoggingDecorator extends ${Name}Decorator {
479
+ ${Name}LoggingDecorator(${Name}Component w) { super(w); }
480
+ public String execute() {
481
+ String r = super.execute();
482
+ System.out.println("[LOG] result=" + r);
483
+ return r;
484
+ }
485
+ }
486
+
487
+ class ${Name}CachingDecorator extends ${Name}Decorator {
488
+ private String cache;
489
+ ${Name}CachingDecorator(${Name}Component w) { super(w); }
490
+ public String execute() {
491
+ if (cache == null) { cache = super.execute(); System.out.println("[CACHE] miss"); }
492
+ else System.out.println("[CACHE] hit");
493
+ return cache;
494
+ }
495
+ public static void main(String[] args) {
496
+ var c = new ${Name}LoggingDecorator(new ${Name}CachingDecorator(new ${Name}ConcreteComponent()));
497
+ System.out.println(c.execute());
498
+ System.out.println(c.execute());
499
+ }
500
+ }
501
+ '''),
502
+ }
503
+
504
+ # ---- COMMAND ---------------------------------------------------------------
505
+ PATTERNS["command"] = {
506
+ "python": Template('''\
507
+ #!/usr/bin/env python3
508
+ """Command pattern — ${Name}."""
509
+ from abc import ABC, abstractmethod
510
+ from typing import List
511
+
512
+
513
+ class ${Name}Command(ABC):
514
+ @abstractmethod
515
+ def execute(self) -> None: ...
516
+
517
+ @abstractmethod
518
+ def undo(self) -> None: ...
519
+
520
+
521
+ class ${Name}Receiver:
522
+ def __init__(self) -> None:
523
+ self.state: List[str] = []
524
+
525
+ def add(self, item: str) -> None:
526
+ self.state.append(item)
527
+ print(f"Added {item!r}. State: {self.state}")
528
+
529
+ def remove(self, item: str) -> None:
530
+ self.state.remove(item)
531
+ print(f"Removed {item!r}. State: {self.state}")
532
+
533
+
534
+ class ${Name}AddCommand(${Name}Command):
535
+ def __init__(self, receiver: ${Name}Receiver, item: str) -> None:
536
+ self._receiver = receiver
537
+ self._item = item
538
+
539
+ def execute(self) -> None:
540
+ self._receiver.add(self._item)
541
+
542
+ def undo(self) -> None:
543
+ self._receiver.remove(self._item)
544
+
545
+
546
+ class ${Name}Invoker:
547
+ def __init__(self) -> None:
548
+ self._history: List[${Name}Command] = []
549
+
550
+ def run(self, cmd: ${Name}Command) -> None:
551
+ cmd.execute()
552
+ self._history.append(cmd)
553
+
554
+ def undo_last(self) -> None:
555
+ if self._history:
556
+ self._history.pop().undo()
557
+
558
+
559
+ if __name__ == "__main__":
560
+ receiver = ${Name}Receiver()
561
+ invoker = ${Name}Invoker()
562
+ invoker.run(${Name}AddCommand(receiver, "apple"))
563
+ invoker.run(${Name}AddCommand(receiver, "banana"))
564
+ invoker.undo_last()
565
+ '''),
566
+ "kotlin": Template('''\
567
+ package com.example.patterns
568
+
569
+ interface ${Name}Command { fun execute(); fun undo() }
570
+
571
+ class ${Name}Receiver {
572
+ val state = mutableListOf<String>()
573
+ fun add(item: String) { state.add(item); println("Added $item. State=$state") }
574
+ fun remove(item: String) { state.remove(item); println("Removed $item. State=$state") }
575
+ }
576
+
577
+ class ${Name}AddCommand(private val r: ${Name}Receiver, private val item: String) : ${Name}Command {
578
+ override fun execute() = r.add(item)
579
+ override fun undo() = r.remove(item)
580
+ }
581
+
582
+ class ${Name}Invoker {
583
+ private val history = mutableListOf<${Name}Command>()
584
+ fun run(cmd: ${Name}Command) { cmd.execute(); history.add(cmd) }
585
+ fun undoLast() { history.removeLastOrNull()?.undo() }
586
+ }
587
+
588
+ fun main() {
589
+ val r = ${Name}Receiver(); val inv = ${Name}Invoker()
590
+ inv.run(${Name}AddCommand(r, "apple"))
591
+ inv.run(${Name}AddCommand(r, "banana"))
592
+ inv.undoLast()
593
+ }
594
+ '''),
595
+ "java": Template('''\
596
+ package com.example.patterns;
597
+ import java.util.ArrayDeque;
598
+ import java.util.ArrayList;
599
+ import java.util.Deque;
600
+ import java.util.List;
601
+
602
+ public interface ${Name}Command { void execute(); void undo(); }
603
+
604
+ class ${Name}Receiver {
605
+ List<String> state = new ArrayList<>();
606
+ void add(String i) { state.add(i); System.out.println("Added " + i + " state=" + state); }
607
+ void remove(String i) { state.remove(i); System.out.println("Removed " + i + " state=" + state); }
608
+ }
609
+
610
+ class ${Name}AddCommand implements ${Name}Command {
611
+ private final ${Name}Receiver r; private final String item;
612
+ ${Name}AddCommand(${Name}Receiver r, String item) { this.r = r; this.item = item; }
613
+ public void execute() { r.add(item); }
614
+ public void undo() { r.remove(item); }
615
+ }
616
+
617
+ class ${Name}Invoker {
618
+ private final Deque<${Name}Command> history = new ArrayDeque<>();
619
+ public void run(${Name}Command c) { c.execute(); history.push(c); }
620
+ public void undoLast() { if (!history.isEmpty()) history.pop().undo(); }
621
+ public static void main(String[] args) {
622
+ var r = new ${Name}Receiver(); var inv = new ${Name}Invoker();
623
+ inv.run(new ${Name}AddCommand(r, "apple"));
624
+ inv.run(new ${Name}AddCommand(r, "banana"));
625
+ inv.undoLast();
626
+ }
627
+ }
628
+ '''),
629
+ }
630
+
631
+ # ---- SINGLETON -------------------------------------------------------------
632
+ PATTERNS["singleton"] = {
633
+ "python": Template('''\
634
+ #!/usr/bin/env python3
635
+ """Singleton pattern — ${Name}.
636
+
637
+ WARNING: Singleton is heavily overused. Prefer dependency injection wherever
638
+ possible. Use this pattern only for truly process-wide unique resources such
639
+ as a logging registry or a hardware interface.
640
+ """
641
+ import threading
642
+
643
+
644
+ class ${Name}(metaclass=type):
645
+ _instance: "${Name} | None" = None
646
+ _lock: threading.Lock = threading.Lock()
647
+
648
+ def __new__(cls) -> "${Name}":
649
+ if cls._instance is None:
650
+ with cls._lock:
651
+ if cls._instance is None:
652
+ cls._instance = super().__new__(cls)
653
+ cls._instance._initialized = False
654
+ return cls._instance
655
+
656
+ def __init__(self) -> None:
657
+ if self._initialized:
658
+ return
659
+ self._initialized = True
660
+ self._data: dict = {}
661
+
662
+ # --- public API ---
663
+ def set(self, key: str, value: object) -> None:
664
+ self._data[key] = value
665
+
666
+ def get(self, key: str) -> object:
667
+ return self._data.get(key)
668
+
669
+
670
+ # Alternative: module-level instance (simpler, idiomatic Python)
671
+ class _${Name}Impl:
672
+ def __init__(self) -> None:
673
+ self._data: dict = {}
674
+ def set(self, key: str, value: object) -> None: self._data[key] = value
675
+ def get(self, key: str) -> object: return self._data.get(key)
676
+
677
+ # Prefer this over the class-based singleton above
678
+ ${lname}_instance = _${Name}Impl()
679
+
680
+
681
+ if __name__ == "__main__":
682
+ a = ${Name}()
683
+ b = ${Name}()
684
+ a.set("x", 42)
685
+ assert b.get("x") == 42, "Must be same instance"
686
+ assert a is b
687
+ print("Singleton check passed — a is b:", a is b)
688
+ '''),
689
+ "kotlin": Template('''\
690
+ package com.example.patterns
691
+
692
+ // Kotlin object declaration is the idiomatic singleton.
693
+ // WARNING: Hard to mock in tests — prefer constructor injection.
694
+ object ${Name} {
695
+ private val data = mutableMapOf<String, Any?>()
696
+ fun set(key: String, value: Any?) { data[key] = value }
697
+ fun get(key: String): Any? = data[key]
698
+ }
699
+
700
+ fun main() {
701
+ ${Name}.set("x", 42)
702
+ println("x = ${dollar}{${Name}.get("x")}")
703
+ }
704
+ '''),
705
+ "java": Template('''\
706
+ package com.example.patterns;
707
+ import java.util.HashMap;
708
+ import java.util.Map;
709
+
710
+ /**
711
+ * Thread-safe lazy singleton via initialization-on-demand holder.
712
+ * WARNING: Singleton is frequently overused; prefer dependency injection.
713
+ */
714
+ public final class ${Name} {
715
+ private final Map<String, Object> data = new HashMap<>();
716
+
717
+ private ${Name}() {}
718
+
719
+ private static final class Holder {
720
+ private static final ${Name} INSTANCE = new ${Name}();
721
+ }
722
+
723
+ public static ${Name} getInstance() { return Holder.INSTANCE; }
724
+
725
+ public void set(String key, Object value) { data.put(key, value); }
726
+ public Object get(String key) { return data.get(key); }
727
+
728
+ public static void main(String[] args) {
729
+ ${Name}.getInstance().set("x", 42);
730
+ System.out.println("x = " + ${Name}.getInstance().get("x"));
731
+ System.out.println("same? " + (${Name}.getInstance() == ${Name}.getInstance()));
732
+ }
733
+ }
734
+ '''),
735
+ }
736
+
737
+ SINGLETON_WARNING = """\
738
+ WARNING: You generated a Singleton. Effective Design Patterns cautions that
739
+ Singleton is the most overused pattern in the GoF catalogue. Problems include:
740
+
741
+ - Global mutable state makes code hard to test and reason about.
742
+ - Hidden dependencies violate the explicit-dependencies principle.
743
+ - Concurrency issues arise when state is mutated from multiple threads.
744
+
745
+ Consider instead:
746
+ - Dependency Injection: pass the shared object as a constructor parameter.
747
+ - Module-level instance (Python): a single module import is naturally unique.
748
+ - IoC containers (Spring, Guice, Hilt): manage lifetime explicitly.
749
+
750
+ Use Singleton only for truly process-wide resources with no reasonable
751
+ alternative (e.g., a hardware driver, a logging sink, an OS-level handle).
752
+ """
753
+
754
+ EXT = {"python": "py", "kotlin": "kt", "java": "java"}
755
+
756
+
757
+ def write(path: Path, content: str) -> None:
758
+ path.parent.mkdir(parents=True, exist_ok=True)
759
+ path.write_text(content)
760
+ print(f" Created: {path}")
761
+
762
+
763
+ def scaffold(pattern: str, class_name: str, lang: str, output_dir: Path) -> None:
764
+ if pattern == "singleton":
765
+ print("\n" + "=" * 70)
766
+ print(SINGLETON_WARNING.strip())
767
+ print("=" * 70 + "\n")
768
+
769
+ lang_templates = PATTERNS[pattern]
770
+ if lang not in lang_templates:
771
+ print(f"ERROR: lang '{lang}' not supported for pattern '{pattern}'.", file=sys.stderr)
772
+ sys.exit(1)
773
+
774
+ tmpl = lang_templates[lang]
775
+ ext = EXT[lang]
776
+ filename = f"{class_name}_{pattern}.{ext}"
777
+ ctx = {"Name": class_name, "lname": class_name.lower(), "dollar": "$"}
778
+ content = tmpl.substitute(ctx)
779
+ write(output_dir / filename, content)
780
+
781
+ print(f"\nGenerated {pattern} pattern for '{class_name}' ({lang}): {output_dir / filename}")
782
+ print("\nNext steps:")
783
+ print(f" 1. Rename {class_name}Strategy/Observer/etc. to match your domain.")
784
+ print(f" 2. Replace the placeholder execute() logic with real behaviour.")
785
+ print(f" 3. Wire into your application via dependency injection.\n")
786
+
787
+
788
+ def main() -> None:
789
+ supported = sorted(PATTERNS.keys())
790
+ parser = argparse.ArgumentParser(
791
+ description="Scaffold GoF design pattern boilerplate."
792
+ )
793
+ parser.add_argument("pattern", choices=supported, help=f"Pattern: {', '.join(supported)}")
794
+ parser.add_argument("class_name", metavar="ClassName", help="Base name for generated classes")
795
+ parser.add_argument("--lang", choices=["python", "kotlin", "java"], default="python")
796
+ parser.add_argument("--output-dir", default=".", type=Path)
797
+ args = parser.parse_args()
798
+
799
+ if not args.class_name[0].isupper():
800
+ print(f"ERROR: ClassName should be PascalCase (got '{args.class_name}').", file=sys.stderr)
801
+ sys.exit(1)
802
+
803
+ scaffold(args.pattern, args.class_name, args.lang, args.output_dir)
804
+
805
+
806
+ if __name__ == "__main__":
807
+ main()