@dzhechkov/skills-idea2prd 0.1.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 (34) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +71 -0
  3. package/bin/cli.js +5 -0
  4. package/package.json +49 -0
  5. package/sources.json +25 -0
  6. package/src/cli.js +108 -0
  7. package/src/commands/doctor.js +340 -0
  8. package/src/commands/init.js +168 -0
  9. package/src/commands/list.js +146 -0
  10. package/src/commands/remove.js +182 -0
  11. package/src/commands/update.js +170 -0
  12. package/src/utils.js +154 -0
  13. package/templates/.claude/commands/idea2prd-manual.md +35 -0
  14. package/templates/.claude/skills/explore/SKILL.md +218 -0
  15. package/templates/.claude/skills/explore/references/questioning-techniques.md +151 -0
  16. package/templates/.claude/skills/explore/references/task-brief-templates.md +355 -0
  17. package/templates/.claude/skills/goap-research-ed25519/SKILL.md +418 -0
  18. package/templates/.claude/skills/goap-research-ed25519/references/ed25519-verification.md +658 -0
  19. package/templates/.claude/skills/goap-research-ed25519/references/research-actions.md +544 -0
  20. package/templates/.claude/skills/goap-research-ed25519/references/source-evaluation.md +560 -0
  21. package/templates/.claude/skills/goap-research-ed25519/scripts/ed25519_verifier.py +662 -0
  22. package/templates/.claude/skills/goap-research-ed25519/scripts/goap_planner.py +720 -0
  23. package/templates/.claude/skills/idea2prd-manual/SKILL.md +695 -0
  24. package/templates/.claude/skills/idea2prd-manual/references/adr-catalog.md +288 -0
  25. package/templates/.claude/skills/idea2prd-manual/references/c4-model.md +277 -0
  26. package/templates/.claude/skills/idea2prd-manual/references/completion-checklist-template.md +446 -0
  27. package/templates/.claude/skills/idea2prd-manual/references/ddd-patterns.md +261 -0
  28. package/templates/.claude/skills/idea2prd-manual/references/fitness-functions-catalog.md +414 -0
  29. package/templates/.claude/skills/idea2prd-manual/references/pseudocode-style.md +404 -0
  30. package/templates/.claude/skills/idea2prd-manual/scripts/ai_context_builder.py +491 -0
  31. package/templates/.claude/skills/idea2prd-manual/scripts/c4_generator.py +311 -0
  32. package/templates/.claude/skills/idea2prd-manual/scripts/fitness_validator.py +451 -0
  33. package/templates/.claude/skills/idea2prd-manual/scripts/pseudocode_generator.py +430 -0
  34. package/templates/.claude/skills/problem-solver-enhanced/SKILL.md +565 -0
@@ -0,0 +1,430 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ pseudocode_generator.py - Генератор pseudocode для агрегатов и сервисов
4
+
5
+ Используется в Phase 4.5 для создания pseudocode файлов на основе
6
+ DDD Tactical design (Phase 4).
7
+
8
+ Usage:
9
+ python pseudocode_generator.py --input tactical.json --output docs/pseudocode/
10
+
11
+ Input Format (tactical.json):
12
+ {
13
+ "aggregates": [
14
+ {
15
+ "name": "Order",
16
+ "boundedContext": "OrderManagement",
17
+ "state": [
18
+ {"name": "id", "type": "OrderId"},
19
+ {"name": "status", "type": "OrderStatus"}
20
+ ],
21
+ "methods": [
22
+ {
23
+ "name": "placeOrder",
24
+ "type": "command",
25
+ "params": [
26
+ {"name": "items", "type": "List<OrderItem>"},
27
+ {"name": "customer", "type": "Customer"}
28
+ ],
29
+ "returns": "OrderId",
30
+ "description": "Place a new order",
31
+ "businessRules": [
32
+ "Items cannot be empty",
33
+ "Customer must be verified",
34
+ "All items must be in stock"
35
+ ],
36
+ "emits": ["OrderPlacedEvent"]
37
+ }
38
+ ]
39
+ }
40
+ ],
41
+ "services": [
42
+ {
43
+ "name": "PricingService",
44
+ "boundedContext": "Pricing",
45
+ "methods": [...]
46
+ }
47
+ ]
48
+ }
49
+ """
50
+
51
+ import json
52
+ import argparse
53
+ from pathlib import Path
54
+ from typing import Dict, List, Any
55
+ from dataclasses import dataclass
56
+ from datetime import datetime
57
+
58
+
59
+ @dataclass
60
+ class MethodParam:
61
+ name: str
62
+ type: str
63
+
64
+
65
+ @dataclass
66
+ class Method:
67
+ name: str
68
+ type: str # command, query, factory
69
+ params: List[MethodParam]
70
+ returns: str
71
+ description: str
72
+ business_rules: List[str]
73
+ emits: List[str]
74
+
75
+
76
+ @dataclass
77
+ class StateField:
78
+ name: str
79
+ type: str
80
+
81
+
82
+ @dataclass
83
+ class Aggregate:
84
+ name: str
85
+ bounded_context: str
86
+ state: List[StateField]
87
+ methods: List[Method]
88
+
89
+
90
+ @dataclass
91
+ class Service:
92
+ name: str
93
+ bounded_context: str
94
+ methods: List[Method]
95
+
96
+
97
+ class PseudocodeGenerator:
98
+ """Generates pseudocode from DDD tactical design."""
99
+
100
+ TEMPLATE_AGGREGATE = '''// File: {filename}
101
+ // Generated: {timestamp}
102
+ // Bounded Context: {bounded_context}
103
+
104
+ AGGREGATE {name}
105
+
106
+ STATE:
107
+ {state_fields}
108
+ END STATE
109
+
110
+ {methods}
111
+ END AGGREGATE
112
+ '''
113
+
114
+ TEMPLATE_SERVICE = '''// File: {filename}
115
+ // Generated: {timestamp}
116
+ // Bounded Context: {bounded_context}
117
+
118
+ SERVICE {name}
119
+
120
+ {methods}
121
+ END SERVICE
122
+ '''
123
+
124
+ TEMPLATE_METHOD = ''' //========================================
125
+ // {method_type}: {description}
126
+ //========================================
127
+ FUNCTION {name}({params}) -> {returns}:
128
+
129
+ // Pre-conditions
130
+ {validations}
131
+
132
+ // Main logic
133
+ {logic}
134
+
135
+ // Post-conditions
136
+ {postconditions}
137
+ {events}
138
+ RETURN {return_value}
139
+ END FUNCTION
140
+ '''
141
+
142
+ def __init__(self, output_dir: Path):
143
+ self.output_dir = output_dir
144
+ self.output_dir.mkdir(parents=True, exist_ok=True)
145
+
146
+ def generate_from_json(self, input_file: Path) -> Dict[str, str]:
147
+ """Generate pseudocode files from JSON input."""
148
+ with open(input_file, 'r', encoding='utf-8') as f:
149
+ data = json.load(f)
150
+
151
+ generated_files = {}
152
+
153
+ # Process aggregates
154
+ for agg_data in data.get('aggregates', []):
155
+ aggregate = self._parse_aggregate(agg_data)
156
+ filename = f"{aggregate.name}Aggregate.pseudo"
157
+ content = self._generate_aggregate(aggregate)
158
+ self._write_file(filename, content)
159
+ generated_files[filename] = content
160
+
161
+ # Process services
162
+ for svc_data in data.get('services', []):
163
+ service = self._parse_service(svc_data)
164
+ filename = f"{service.name}.pseudo"
165
+ content = self._generate_service(service)
166
+ self._write_file(filename, content)
167
+ generated_files[filename] = content
168
+
169
+ # Generate index
170
+ index_content = self._generate_index(generated_files)
171
+ self._write_file('INDEX.md', index_content)
172
+ generated_files['INDEX.md'] = index_content
173
+
174
+ return generated_files
175
+
176
+ def _parse_aggregate(self, data: Dict) -> Aggregate:
177
+ """Parse aggregate from JSON."""
178
+ return Aggregate(
179
+ name=data['name'],
180
+ bounded_context=data.get('boundedContext', 'Unknown'),
181
+ state=[StateField(s['name'], s['type']) for s in data.get('state', [])],
182
+ methods=[self._parse_method(m) for m in data.get('methods', [])]
183
+ )
184
+
185
+ def _parse_service(self, data: Dict) -> Service:
186
+ """Parse service from JSON."""
187
+ return Service(
188
+ name=data['name'],
189
+ bounded_context=data.get('boundedContext', 'Unknown'),
190
+ methods=[self._parse_method(m) for m in data.get('methods', [])]
191
+ )
192
+
193
+ def _parse_method(self, data: Dict) -> Method:
194
+ """Parse method from JSON."""
195
+ return Method(
196
+ name=data['name'],
197
+ type=data.get('type', 'command'),
198
+ params=[MethodParam(p['name'], p['type']) for p in data.get('params', [])],
199
+ returns=data.get('returns', 'void'),
200
+ description=data.get('description', ''),
201
+ business_rules=data.get('businessRules', []),
202
+ emits=data.get('emits', [])
203
+ )
204
+
205
+ def _generate_aggregate(self, aggregate: Aggregate) -> str:
206
+ """Generate pseudocode for aggregate."""
207
+ state_fields = '\n'.join(
208
+ f" {f.name}: {f.type}"
209
+ for f in aggregate.state
210
+ )
211
+
212
+ methods = '\n'.join(
213
+ self._generate_method(m)
214
+ for m in aggregate.methods
215
+ )
216
+
217
+ return self.TEMPLATE_AGGREGATE.format(
218
+ filename=f"{aggregate.name}Aggregate.pseudo",
219
+ timestamp=datetime.now().isoformat(),
220
+ bounded_context=aggregate.bounded_context,
221
+ name=aggregate.name,
222
+ state_fields=state_fields,
223
+ methods=methods
224
+ )
225
+
226
+ def _generate_service(self, service: Service) -> str:
227
+ """Generate pseudocode for service."""
228
+ methods = '\n'.join(
229
+ self._generate_method(m)
230
+ for m in service.methods
231
+ )
232
+
233
+ return self.TEMPLATE_SERVICE.format(
234
+ filename=f"{service.name}.pseudo",
235
+ timestamp=datetime.now().isoformat(),
236
+ bounded_context=service.bounded_context,
237
+ name=service.name,
238
+ methods=methods
239
+ )
240
+
241
+ def _generate_method(self, method: Method) -> str:
242
+ """Generate pseudocode for a single method."""
243
+ # Format parameters
244
+ params = ', '.join(f"{p.name}: {p.type}" for p in method.params)
245
+
246
+ # Generate validations from business rules
247
+ validations = self._generate_validations(method.business_rules)
248
+
249
+ # Generate placeholder logic
250
+ logic = self._generate_logic_placeholder(method)
251
+
252
+ # Generate postconditions
253
+ postconditions = self._generate_postconditions(method)
254
+
255
+ # Generate event emissions
256
+ events = self._generate_events(method.emits)
257
+
258
+ # Determine return value
259
+ return_value = 'result' if method.returns != 'void' else ''
260
+ if return_value == '' and method.returns == 'void':
261
+ # Remove RETURN line for void methods
262
+ return_template = self.TEMPLATE_METHOD.replace(
263
+ ' RETURN {return_value}\n', ''
264
+ )
265
+ else:
266
+ return_template = self.TEMPLATE_METHOD
267
+
268
+ return return_template.format(
269
+ method_type=method.type.upper(),
270
+ description=method.description,
271
+ name=method.name,
272
+ params=params,
273
+ returns=method.returns,
274
+ validations=validations,
275
+ logic=logic,
276
+ postconditions=postconditions,
277
+ events=events,
278
+ return_value=return_value
279
+ )
280
+
281
+ def _generate_validations(self, rules: List[str]) -> str:
282
+ """Generate VALIDATE statements from business rules."""
283
+ if not rules:
284
+ return " // No explicit validations"
285
+
286
+ validations = []
287
+ for rule in rules:
288
+ # Convert natural language rule to pseudocode
289
+ validation = self._rule_to_validation(rule)
290
+ validations.append(f" VALIDATE {validation}")
291
+
292
+ return '\n'.join(validations)
293
+
294
+ def _rule_to_validation(self, rule: str) -> str:
295
+ """Convert business rule to validation pseudocode."""
296
+ rule_lower = rule.lower()
297
+
298
+ # Pattern matching for common rules
299
+ if 'cannot be empty' in rule_lower or 'not empty' in rule_lower:
300
+ subject = rule.split()[0].lower()
301
+ return f"{subject} IS NOT empty ELSE throw ValidationError(\"{rule}\")"
302
+
303
+ if 'must be' in rule_lower:
304
+ parts = rule.split('must be')
305
+ subject = parts[0].strip().lower().replace(' ', '_')
306
+ condition = parts[1].strip()
307
+ return f"{subject}.is{condition.title().replace(' ', '')} ELSE throw ValidationError(\"{rule}\")"
308
+
309
+ if 'must have' in rule_lower:
310
+ parts = rule.split('must have')
311
+ subject = parts[0].strip().lower().replace(' ', '_')
312
+ what = parts[1].strip()
313
+ return f"{subject}.has{what.title().replace(' ', '')} ELSE throw ValidationError(\"{rule}\")"
314
+
315
+ # Default: just use the rule as-is
316
+ return f"/* {rule} */"
317
+
318
+ def _generate_logic_placeholder(self, method: Method) -> str:
319
+ """Generate placeholder logic based on method type."""
320
+ if method.type == 'command':
321
+ return ''' // TODO: Implement business logic
322
+ // 1. Validate current state
323
+ // 2. Apply business rules
324
+ // 3. Update state
325
+ result = PROCESS(params)'''
326
+
327
+ elif method.type == 'query':
328
+ return ''' // TODO: Implement query logic
329
+ result = FETCH data based on criteria'''
330
+
331
+ elif method.type == 'factory':
332
+ return ''' // TODO: Implement creation logic
333
+ entity = CREATE new entity with params
334
+ result = entity'''
335
+
336
+ return ''' // TODO: Implement logic
337
+ result = PROCESS(params)'''
338
+
339
+ def _generate_postconditions(self, method: Method) -> str:
340
+ """Generate postcondition checks."""
341
+ if method.returns == 'void':
342
+ return " // State updated"
343
+
344
+ return f" ENSURE result IS valid"
345
+
346
+ def _generate_events(self, events: List[str]) -> str:
347
+ """Generate event emission statements."""
348
+ if not events:
349
+ return ""
350
+
351
+ lines = ["\n // Emit domain events"]
352
+ for event in events:
353
+ lines.append(f" EMIT {event}(")
354
+ lines.append(f" // Event data")
355
+ lines.append(f" timestamp: NOW()")
356
+ lines.append(f" )")
357
+
358
+ return '\n'.join(lines)
359
+
360
+ def _generate_index(self, files: Dict[str, str]) -> str:
361
+ """Generate index file for all pseudocode."""
362
+ lines = [
363
+ "# Pseudocode Index",
364
+ "",
365
+ f"Generated: {datetime.now().isoformat()}",
366
+ "",
367
+ "## Files",
368
+ "",
369
+ "| File | Type | Methods |",
370
+ "|------|------|---------|"
371
+ ]
372
+
373
+ for filename, content in files.items():
374
+ if filename == 'INDEX.md':
375
+ continue
376
+
377
+ file_type = 'Aggregate' if 'AGGREGATE' in content else 'Service'
378
+ method_count = content.count('FUNCTION ')
379
+ lines.append(f"| {filename} | {file_type} | {method_count} |")
380
+
381
+ lines.extend([
382
+ "",
383
+ "## Usage with Claude Code",
384
+ "",
385
+ "```bash",
386
+ "# Implement specific aggregate",
387
+ "claude \"Implement OrderAggregate from @docs/pseudocode/OrderAggregate.pseudo\"",
388
+ "",
389
+ "# Implement all",
390
+ "claude \"Implement all aggregates from docs/pseudocode/ in TypeScript\"",
391
+ "```"
392
+ ])
393
+
394
+ return '\n'.join(lines)
395
+
396
+ def _write_file(self, filename: str, content: str):
397
+ """Write content to file."""
398
+ filepath = self.output_dir / filename
399
+ with open(filepath, 'w', encoding='utf-8') as f:
400
+ f.write(content)
401
+ print(f"Generated: {filepath}")
402
+
403
+
404
+ def main():
405
+ parser = argparse.ArgumentParser(
406
+ description='Generate pseudocode from DDD tactical design'
407
+ )
408
+ parser.add_argument(
409
+ '--input', '-i',
410
+ type=Path,
411
+ required=True,
412
+ help='Input JSON file with tactical design'
413
+ )
414
+ parser.add_argument(
415
+ '--output', '-o',
416
+ type=Path,
417
+ default=Path('docs/pseudocode'),
418
+ help='Output directory for pseudocode files'
419
+ )
420
+
421
+ args = parser.parse_args()
422
+
423
+ generator = PseudocodeGenerator(args.output)
424
+ files = generator.generate_from_json(args.input)
425
+
426
+ print(f"\nGenerated {len(files)} files in {args.output}")
427
+
428
+
429
+ if __name__ == '__main__':
430
+ main()