@pjmendonca/devflow 1.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 (124) hide show
  1. package/CHANGELOG.md +526 -0
  2. package/LICENSE +21 -0
  3. package/README.md +620 -0
  4. package/bin/devflow-checkpoint.js +10 -0
  5. package/bin/devflow-collab.js +10 -0
  6. package/bin/devflow-cost.js +10 -0
  7. package/bin/devflow-create-persona.js +10 -0
  8. package/bin/devflow-init.js +10 -0
  9. package/bin/devflow-memory.js +10 -0
  10. package/bin/devflow-new-doc.js +10 -0
  11. package/bin/devflow-personalize.js +10 -0
  12. package/bin/devflow-setup-checkpoint.js +10 -0
  13. package/bin/devflow-story.js +10 -0
  14. package/bin/devflow-tech-debt.js +10 -0
  15. package/bin/devflow-validate-overrides.js +10 -0
  16. package/bin/devflow-validate.js +10 -0
  17. package/bin/devflow-version.js +10 -0
  18. package/lib/constants.js +30 -0
  19. package/lib/exec-python.js +78 -0
  20. package/lib/python-check.js +178 -0
  21. package/package.json +64 -0
  22. package/tooling/.automation/agents/architect.md +135 -0
  23. package/tooling/.automation/agents/ba.md +70 -0
  24. package/tooling/.automation/agents/dev.md +79 -0
  25. package/tooling/.automation/agents/maintainer.md +97 -0
  26. package/tooling/.automation/agents/pm.md +116 -0
  27. package/tooling/.automation/agents/reviewer.md +141 -0
  28. package/tooling/.automation/agents/sm.md +61 -0
  29. package/tooling/.automation/agents/writer.md +193 -0
  30. package/tooling/.automation/config.ps1.template +61 -0
  31. package/tooling/.automation/config.sh.template +48 -0
  32. package/tooling/.automation/memory/.gitkeep +6 -0
  33. package/tooling/.automation/memory/knowledge/kg_integration-test.json +94 -0
  34. package/tooling/.automation/memory/knowledge/kg_test-story.json +300 -0
  35. package/tooling/.automation/memory/shared/shared_integration-test.json +30 -0
  36. package/tooling/.automation/memory/shared/shared_test-story.json +78 -0
  37. package/tooling/.automation/overrides/templates/README.md +113 -0
  38. package/tooling/.automation/overrides/templates/architect/README.md +27 -0
  39. package/tooling/.automation/overrides/templates/architect/cloud-native.yaml +92 -0
  40. package/tooling/.automation/overrides/templates/architect/enterprise-architect.yaml +85 -0
  41. package/tooling/.automation/overrides/templates/architect/pragmatic-minimalist.yaml +88 -0
  42. package/tooling/.automation/overrides/templates/ba/README.md +27 -0
  43. package/tooling/.automation/overrides/templates/ba/agile-storyteller.yaml +86 -0
  44. package/tooling/.automation/overrides/templates/ba/domain-expert.yaml +91 -0
  45. package/tooling/.automation/overrides/templates/ba/requirements-engineer.yaml +89 -0
  46. package/tooling/.automation/overrides/templates/dev/README.md +32 -0
  47. package/tooling/.automation/overrides/templates/dev/junior-mentored.yaml +39 -0
  48. package/tooling/.automation/overrides/templates/dev/performance-engineer.yaml +43 -0
  49. package/tooling/.automation/overrides/templates/dev/rapid-prototyper.yaml +52 -0
  50. package/tooling/.automation/overrides/templates/dev/security-focused.yaml +43 -0
  51. package/tooling/.automation/overrides/templates/dev/senior-fullstack.yaml +39 -0
  52. package/tooling/.automation/overrides/templates/maintainer/README.md +27 -0
  53. package/tooling/.automation/overrides/templates/maintainer/devops-maintainer.yaml +113 -0
  54. package/tooling/.automation/overrides/templates/maintainer/legacy-steward.yaml +94 -0
  55. package/tooling/.automation/overrides/templates/maintainer/oss-maintainer.yaml +94 -0
  56. package/tooling/.automation/overrides/templates/pm/README.md +27 -0
  57. package/tooling/.automation/overrides/templates/pm/agile-pm.yaml +91 -0
  58. package/tooling/.automation/overrides/templates/pm/hybrid-delivery.yaml +87 -0
  59. package/tooling/.automation/overrides/templates/pm/traditional-pm.yaml +91 -0
  60. package/tooling/.automation/overrides/templates/reviewer/README.md +11 -0
  61. package/tooling/.automation/overrides/templates/reviewer/mentoring-reviewer.yaml +45 -0
  62. package/tooling/.automation/overrides/templates/reviewer/quick-sanity.yaml +50 -0
  63. package/tooling/.automation/overrides/templates/reviewer/thorough-critic.yaml +48 -0
  64. package/tooling/.automation/overrides/templates/sm/README.md +11 -0
  65. package/tooling/.automation/overrides/templates/sm/agile-coach.yaml +52 -0
  66. package/tooling/.automation/overrides/templates/sm/startup-pm.yaml +50 -0
  67. package/tooling/.automation/overrides/templates/sm/technical-lead.yaml +47 -0
  68. package/tooling/.automation/overrides/templates/user-profile.template.yaml +62 -0
  69. package/tooling/.automation/overrides/templates/writer/README.md +27 -0
  70. package/tooling/.automation/overrides/templates/writer/api-documentarian.yaml +99 -0
  71. package/tooling/.automation/overrides/templates/writer/docs-as-code.yaml +108 -0
  72. package/tooling/.automation/overrides/templates/writer/user-guide-author.yaml +100 -0
  73. package/tooling/completions/DevflowCompletion.ps1 +213 -0
  74. package/tooling/completions/_run-story +116 -0
  75. package/tooling/completions/run-story-completion.bash +136 -0
  76. package/tooling/docs/DOC-STANDARD.md +717 -0
  77. package/tooling/docs/sprint-status.yaml.template +24 -0
  78. package/tooling/docs/templates/bug-report.md +234 -0
  79. package/tooling/docs/templates/migration-spec.md +274 -0
  80. package/tooling/docs/templates/refactor-spec.md +86 -0
  81. package/tooling/docs/templates/tech-debt.md +86 -0
  82. package/tooling/scripts/context_checkpoint.py +556 -0
  83. package/tooling/scripts/cost_dashboard.py +617 -0
  84. package/tooling/scripts/create-persona.py +690 -0
  85. package/tooling/scripts/create-persona.sh +435 -0
  86. package/tooling/scripts/init-project-workflow.ps1 +651 -0
  87. package/tooling/scripts/init-project-workflow.py +70 -0
  88. package/tooling/scripts/init-project-workflow.sh +746 -0
  89. package/tooling/scripts/lib/__init__.py +35 -0
  90. package/tooling/scripts/lib/agent_handoff.py +526 -0
  91. package/tooling/scripts/lib/agent_router.py +698 -0
  92. package/tooling/scripts/lib/checkpoint-integration.ps1 +245 -0
  93. package/tooling/scripts/lib/checkpoint-integration.sh +191 -0
  94. package/tooling/scripts/lib/claude-cli.ps1 +952 -0
  95. package/tooling/scripts/lib/claude-cli.sh +1293 -0
  96. package/tooling/scripts/lib/cost_config.py +222 -0
  97. package/tooling/scripts/lib/cost_display.py +443 -0
  98. package/tooling/scripts/lib/cost_tracker.py +710 -0
  99. package/tooling/scripts/lib/currency_converter.py +328 -0
  100. package/tooling/scripts/lib/errors.py +438 -0
  101. package/tooling/scripts/lib/override-loader.sh +286 -0
  102. package/tooling/scripts/lib/pair_programming.py +589 -0
  103. package/tooling/scripts/lib/shared_memory.py +637 -0
  104. package/tooling/scripts/lib/swarm_orchestrator.py +689 -0
  105. package/tooling/scripts/memory_summarize.py +324 -0
  106. package/tooling/scripts/new-doc.ps1 +405 -0
  107. package/tooling/scripts/new-doc.py +93 -0
  108. package/tooling/scripts/new-doc.sh +534 -0
  109. package/tooling/scripts/personalize_agent.py +385 -0
  110. package/tooling/scripts/rollback-migration.sh +540 -0
  111. package/tooling/scripts/run-collab.ps1 +251 -0
  112. package/tooling/scripts/run-collab.py +605 -0
  113. package/tooling/scripts/run-collab.sh +110 -0
  114. package/tooling/scripts/run-story.ps1 +490 -0
  115. package/tooling/scripts/run-story.py +387 -0
  116. package/tooling/scripts/run-story.sh +467 -0
  117. package/tooling/scripts/setup-checkpoint-service.ps1 +219 -0
  118. package/tooling/scripts/setup-checkpoint-service.py +87 -0
  119. package/tooling/scripts/setup-checkpoint-service.sh +236 -0
  120. package/tooling/scripts/tech-debt-tracker.py +608 -0
  121. package/tooling/scripts/update_version.py +244 -0
  122. package/tooling/scripts/validate-overrides.py +511 -0
  123. package/tooling/scripts/validate-overrides.sh +432 -0
  124. package/tooling/scripts/validate_setup.py +539 -0
@@ -0,0 +1,690 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Create Persona - Custom Agent Persona Builder
4
+
5
+ This interactive tool helps create custom agent personas for the Devflow
6
+ workflow system. It generates properly formatted agent files that can be
7
+ used with the run-story.sh automation.
8
+
9
+ Usage:
10
+ python create-persona.py # Interactive mode
11
+ python create-persona.py --name qa # Quick create with defaults
12
+ python create-persona.py --from-template # Create from existing template
13
+ python create-persona.py --list # List existing personas
14
+ python create-persona.py --validate <name> # Validate a persona file
15
+ """
16
+
17
+ import argparse
18
+ import sys
19
+ from dataclasses import dataclass, field
20
+ from pathlib import Path
21
+
22
+
23
+ # Colors for terminal output
24
+ class Colors:
25
+ RED = "\033[0;31m"
26
+ GREEN = "\033[0;32m"
27
+ YELLOW = "\033[1;33m"
28
+ BLUE = "\033[0;34m"
29
+ CYAN = "\033[0;36m"
30
+ MAGENTA = "\033[0;35m"
31
+ BOLD = "\033[1m"
32
+ NC = "\033[0m"
33
+
34
+
35
+ # Persona templates
36
+ PERSONA_TEMPLATES = {
37
+ "developer": {
38
+ "role": "Software Developer",
39
+ "focus": "Writing clean, maintainable code",
40
+ "model": "sonnet",
41
+ "responsibilities": [
42
+ "Implement features according to specifications",
43
+ "Write unit and integration tests",
44
+ "Follow coding standards and best practices",
45
+ "Document code appropriately",
46
+ ],
47
+ "principles": [
48
+ "Write code that is easy to understand and maintain",
49
+ "Test first when possible",
50
+ "Keep functions small and focused",
51
+ "Handle errors gracefully",
52
+ ],
53
+ },
54
+ "reviewer": {
55
+ "role": "Code Reviewer",
56
+ "focus": "Ensuring code quality and best practices",
57
+ "model": "sonnet",
58
+ "responsibilities": [
59
+ "Review code for correctness and quality",
60
+ "Identify potential bugs and security issues",
61
+ "Suggest improvements and optimizations",
62
+ "Ensure coding standards compliance",
63
+ ],
64
+ "principles": [
65
+ "Be constructive, not critical",
66
+ "Focus on important issues first",
67
+ "Explain the 'why' behind suggestions",
68
+ "Acknowledge good patterns",
69
+ ],
70
+ },
71
+ "architect": {
72
+ "role": "Software Architect",
73
+ "focus": "System design and technical decisions",
74
+ "model": "opus",
75
+ "responsibilities": [
76
+ "Design system architecture",
77
+ "Make technology decisions",
78
+ "Document architectural decisions (ADRs)",
79
+ "Ensure scalability and maintainability",
80
+ ],
81
+ "principles": [
82
+ "Separation of concerns",
83
+ "Dependency inversion",
84
+ "Design for change",
85
+ "Keep it simple",
86
+ ],
87
+ },
88
+ "tester": {
89
+ "role": "QA Engineer",
90
+ "focus": "Quality assurance and testing",
91
+ "model": "sonnet",
92
+ "responsibilities": [
93
+ "Design test strategies",
94
+ "Write automated tests",
95
+ "Perform exploratory testing",
96
+ "Report and track bugs",
97
+ ],
98
+ "principles": [
99
+ "Test early, test often",
100
+ "Cover edge cases",
101
+ "Automate repetitive tests",
102
+ "Think like a user",
103
+ ],
104
+ },
105
+ "security": {
106
+ "role": "Security Engineer",
107
+ "focus": "Application security and vulnerability prevention",
108
+ "model": "opus",
109
+ "responsibilities": [
110
+ "Review code for security vulnerabilities",
111
+ "Recommend security best practices",
112
+ "Audit authentication and authorization",
113
+ "Identify potential attack vectors",
114
+ ],
115
+ "principles": [
116
+ "Defense in depth",
117
+ "Principle of least privilege",
118
+ "Never trust user input",
119
+ "Secure by default",
120
+ ],
121
+ },
122
+ "devops": {
123
+ "role": "DevOps Engineer",
124
+ "focus": "CI/CD, infrastructure, and deployment",
125
+ "model": "sonnet",
126
+ "responsibilities": [
127
+ "Set up and maintain CI/CD pipelines",
128
+ "Manage infrastructure as code",
129
+ "Monitor and optimize deployments",
130
+ "Automate operational tasks",
131
+ ],
132
+ "principles": [
133
+ "Automate everything",
134
+ "Infrastructure as code",
135
+ "Monitor proactively",
136
+ "Fail fast, recover faster",
137
+ ],
138
+ },
139
+ "documentation": {
140
+ "role": "Technical Writer",
141
+ "focus": "Clear and comprehensive documentation",
142
+ "model": "sonnet",
143
+ "responsibilities": [
144
+ "Write user documentation",
145
+ "Create API documentation",
146
+ "Maintain README files",
147
+ "Document processes and procedures",
148
+ ],
149
+ "principles": [
150
+ "Write for the audience",
151
+ "Keep it simple and clear",
152
+ "Include examples",
153
+ "Keep docs up to date",
154
+ ],
155
+ },
156
+ }
157
+
158
+
159
+ @dataclass
160
+ class PersonaConfig:
161
+ """Configuration for a custom persona."""
162
+
163
+ name: str
164
+ role: str
165
+ focus: str
166
+ model: str = "sonnet"
167
+ responsibilities: list[str] = field(default_factory=list)
168
+ principles: list[str] = field(default_factory=list)
169
+ working_directories: dict[str, str] = field(default_factory=dict)
170
+ tech_stack: list[str] = field(default_factory=list)
171
+ critical_rules: list[str] = field(default_factory=list)
172
+ communication_style: str = ""
173
+ custom_sections: dict[str, str] = field(default_factory=dict)
174
+
175
+
176
+ def print_header():
177
+ """Print the tool header."""
178
+ print()
179
+ print(f"{Colors.CYAN}{'═' * 70}{Colors.NC}")
180
+ print(f"{Colors.CYAN} CUSTOM PERSONA BUILDER{Colors.NC}")
181
+ print(f"{Colors.CYAN}{'═' * 70}{Colors.NC}")
182
+ print()
183
+
184
+
185
+ def prompt(message: str, default: str = "", required: bool = False) -> str:
186
+ """Prompt for user input."""
187
+ if default:
188
+ display = f"{Colors.BLUE}{message}{Colors.NC} [{default}]: "
189
+ else:
190
+ display = f"{Colors.BLUE}{message}{Colors.NC}: "
191
+
192
+ while True:
193
+ value = input(display).strip()
194
+ if not value and default:
195
+ return default
196
+ if not value and required:
197
+ print(f"{Colors.YELLOW}This field is required.{Colors.NC}")
198
+ continue
199
+ return value
200
+
201
+
202
+ def prompt_list(message: str, min_items: int = 0) -> list[str]:
203
+ """Prompt for a list of items."""
204
+ print(f"{Colors.BLUE}{message}{Colors.NC}")
205
+ print(" (Enter items one per line, empty line to finish)")
206
+
207
+ items = []
208
+ while True:
209
+ item = input(f" {len(items) + 1}. ").strip()
210
+ if not item:
211
+ if len(items) >= min_items:
212
+ break
213
+ print(f"{Colors.YELLOW}Please enter at least {min_items} item(s).{Colors.NC}")
214
+ continue
215
+ items.append(item)
216
+
217
+ return items
218
+
219
+
220
+ def prompt_choice(message: str, choices: list[str], default: str = "") -> str:
221
+ """Prompt for a choice from a list."""
222
+ print(f"{Colors.BLUE}{message}{Colors.NC}")
223
+ for i, choice in enumerate(choices, 1):
224
+ marker = " (default)" if choice == default else ""
225
+ print(f" {i}. {choice}{marker}")
226
+
227
+ while True:
228
+ value = input("Choice: ").strip()
229
+ if not value and default:
230
+ return default
231
+
232
+ # Accept number or text
233
+ if value.isdigit():
234
+ idx = int(value) - 1
235
+ if 0 <= idx < len(choices):
236
+ return choices[idx]
237
+ elif value in choices:
238
+ return value
239
+
240
+ print(f"{Colors.YELLOW}Please enter a valid choice.{Colors.NC}")
241
+
242
+
243
+ def interactive_create() -> PersonaConfig:
244
+ """Interactive persona creation wizard."""
245
+ print(f"{Colors.BOLD}Let's create your custom agent persona!{Colors.NC}")
246
+ print()
247
+
248
+ # Basic info
249
+ name = prompt("Persona name (e.g., 'qa', 'frontend-dev')", required=True)
250
+ name = name.lower().replace(" ", "-")
251
+
252
+ # Check for template
253
+ print()
254
+ print(f"{Colors.BOLD}Would you like to start from a template?{Colors.NC}")
255
+ templates = list(PERSONA_TEMPLATES.keys())
256
+ templates.insert(0, "none (start fresh)")
257
+ template = prompt_choice("Select template", templates, "none (start fresh)")
258
+
259
+ if template != "none (start fresh)" and template in PERSONA_TEMPLATES:
260
+ tpl = PERSONA_TEMPLATES[template]
261
+ role = prompt("Role", tpl["role"])
262
+ focus = prompt("Focus", tpl["focus"])
263
+ model = prompt_choice("Model", ["sonnet", "opus", "haiku"], tpl["model"])
264
+ responsibilities = tpl["responsibilities"].copy()
265
+ principles = tpl["principles"].copy()
266
+
267
+ print()
268
+ print(
269
+ f"{Colors.GREEN}Template loaded!{Colors.NC} Default responsibilities and principles applied."
270
+ )
271
+ modify = prompt("Would you like to modify them? (y/n)", "n")
272
+
273
+ if modify.lower() == "y":
274
+ print()
275
+ print("Current responsibilities:")
276
+ for r in responsibilities:
277
+ print(f" - {r}")
278
+ if prompt("Modify responsibilities? (y/n)", "n").lower() == "y":
279
+ responsibilities = prompt_list("Enter responsibilities", 1)
280
+
281
+ print()
282
+ print("Current principles:")
283
+ for p in principles:
284
+ print(f" - {p}")
285
+ if prompt("Modify principles? (y/n)", "n").lower() == "y":
286
+ principles = prompt_list("Enter principles", 1)
287
+ else:
288
+ role = prompt("Role (e.g., 'Senior QA Engineer')", required=True)
289
+ focus = prompt("Focus (one-line description)", required=True)
290
+ model = prompt_choice("Model", ["sonnet", "opus", "haiku"], "sonnet")
291
+
292
+ print()
293
+ responsibilities = prompt_list("What are this agent's responsibilities?", 2)
294
+
295
+ print()
296
+ principles = prompt_list("What principles should this agent follow?", 2)
297
+
298
+ # Communication style
299
+ print()
300
+ communication_style = prompt(
301
+ "Communication style (e.g., 'Technical and detailed', 'Friendly and explanatory')",
302
+ "Professional and clear",
303
+ )
304
+
305
+ # Working directories (optional)
306
+ print()
307
+ add_dirs = prompt("Add working directories? (y/n)", "n")
308
+ working_dirs = {}
309
+ if add_dirs.lower() == "y":
310
+ print("Enter directory mappings (name: path), empty to finish:")
311
+ while True:
312
+ dir_name = input(" Name: ").strip()
313
+ if not dir_name:
314
+ break
315
+ dir_path = input(" Path: ").strip()
316
+ working_dirs[dir_name] = dir_path
317
+
318
+ # Tech stack (optional)
319
+ print()
320
+ add_tech = prompt("Add tech stack context? (y/n)", "n")
321
+ tech_stack = []
322
+ if add_tech.lower() == "y":
323
+ tech_stack = prompt_list("Enter technologies this agent works with")
324
+
325
+ # Critical rules
326
+ print()
327
+ add_rules = prompt("Add critical rules (must-do actions)? (y/n)", "n")
328
+ critical_rules = []
329
+ if add_rules.lower() == "y":
330
+ critical_rules = prompt_list("Enter critical rules")
331
+
332
+ return PersonaConfig(
333
+ name=name,
334
+ role=role,
335
+ focus=focus,
336
+ model=model,
337
+ responsibilities=responsibilities,
338
+ principles=principles,
339
+ working_directories=working_dirs,
340
+ tech_stack=tech_stack,
341
+ critical_rules=critical_rules,
342
+ communication_style=communication_style,
343
+ )
344
+
345
+
346
+ def generate_persona_markdown(config: PersonaConfig) -> str:
347
+ """Generate the persona markdown file content."""
348
+ lines = [
349
+ f"# {config.role} Agent",
350
+ "",
351
+ f"You are a {config.role}. {config.focus}",
352
+ "",
353
+ ]
354
+
355
+ # Responsibilities
356
+ lines.extend(
357
+ [
358
+ "## Responsibilities",
359
+ "",
360
+ ]
361
+ )
362
+ for i, resp in enumerate(config.responsibilities, 1):
363
+ lines.append(f"{i}. {resp}")
364
+ lines.append("")
365
+
366
+ # Working directories
367
+ if config.working_directories:
368
+ lines.extend(
369
+ [
370
+ "## Working Directory",
371
+ "",
372
+ ]
373
+ )
374
+ for name, path in config.working_directories.items():
375
+ lines.append(f"- **{name}**: `{path}`")
376
+ lines.append("")
377
+
378
+ # Tech stack
379
+ if config.tech_stack:
380
+ lines.extend(
381
+ [
382
+ "## Tech Stack",
383
+ "",
384
+ ]
385
+ )
386
+ for tech in config.tech_stack:
387
+ lines.append(f"- {tech}")
388
+ lines.append("")
389
+
390
+ # Principles
391
+ lines.extend(
392
+ [
393
+ "## Principles",
394
+ "",
395
+ ]
396
+ )
397
+ for i, principle in enumerate(config.principles, 1):
398
+ lines.append(
399
+ f"{i}. **{principle.split(':')[0]}**"
400
+ + (f": {':'.join(principle.split(':')[1:])}" if ":" in principle else "")
401
+ )
402
+ lines.append("")
403
+
404
+ # Communication style
405
+ if config.communication_style:
406
+ lines.extend(
407
+ [
408
+ "## Communication Style",
409
+ "",
410
+ config.communication_style,
411
+ "",
412
+ ]
413
+ )
414
+
415
+ # Critical rules
416
+ if config.critical_rules:
417
+ lines.extend(
418
+ [
419
+ "## Critical Rules",
420
+ "",
421
+ ]
422
+ )
423
+ for rule in config.critical_rules:
424
+ lines.append(f"- {rule}")
425
+ lines.append("")
426
+
427
+ # Context management (standard section)
428
+ lines.extend(
429
+ [
430
+ "## Context Management",
431
+ "",
432
+ "You are running in an automated pipeline with limited context window. To avoid losing work:",
433
+ "",
434
+ "1. **Work incrementally** - Complete and save files one at a time",
435
+ "2. **Checkpoint frequently** - After each significant change, ensure the file is written",
436
+ "3. **Monitor your progress** - If you notice you've been working for a while, prioritize critical items",
437
+ "4. **Self-assess context usage** - If you estimate you're past 80% of your context:",
438
+ " - Finish the current file you're working on",
439
+ " - Write a summary of remaining work",
440
+ " - Complete what you can rather than leaving partial work",
441
+ "",
442
+ "If you sense context is running low, output a warning:",
443
+ "```",
444
+ "⚠️ CONTEXT WARNING: Approaching context limit. Prioritizing completion of current task.",
445
+ "```",
446
+ "",
447
+ ]
448
+ )
449
+
450
+ # Model recommendation
451
+ lines.extend(
452
+ [
453
+ "## Model",
454
+ "",
455
+ f"Recommended model: `{config.model}`",
456
+ "",
457
+ ]
458
+ )
459
+
460
+ return "\n".join(lines)
461
+
462
+
463
+ def generate_override_yaml(config: PersonaConfig) -> str:
464
+ """Generate an override YAML file for the persona."""
465
+ lines = [
466
+ f"# {config.name.upper()} Agent Override",
467
+ "# Customize this agent's behavior without modifying the core agent file",
468
+ "# These settings survive updates to the core agent",
469
+ "",
470
+ "# Additional rules (appended to base agent rules)",
471
+ "additional_rules:",
472
+ ]
473
+
474
+ for rule in config.critical_rules or config.principles[:2]:
475
+ lines.append(f' - "{rule}"')
476
+
477
+ lines.extend(
478
+ [
479
+ "",
480
+ "# Memories - facts this agent should always remember",
481
+ "memories:",
482
+ ' - "Example memory: add project-specific context here"',
483
+ "",
484
+ "# Critical actions - must be done before completing any task",
485
+ "critical_actions:",
486
+ ]
487
+ )
488
+
489
+ for rule in config.critical_rules or ["Verify work meets requirements"]:
490
+ lines.append(f' - "{rule}"')
491
+
492
+ lines.extend(
493
+ [
494
+ "",
495
+ f"# Model override (recommended: {config.model})",
496
+ f'# model: "{config.model}"',
497
+ "",
498
+ "# Budget override (optional)",
499
+ "# max_budget_usd: 10.00",
500
+ "",
501
+ ]
502
+ )
503
+
504
+ return "\n".join(lines)
505
+
506
+
507
+ def save_persona(config: PersonaConfig, project_root: Path) -> tuple:
508
+ """Save the persona files."""
509
+ agents_dir = project_root / "tooling" / ".automation" / "agents"
510
+ overrides_dir = project_root / "tooling" / ".automation" / "overrides"
511
+
512
+ agents_dir.mkdir(parents=True, exist_ok=True)
513
+ overrides_dir.mkdir(parents=True, exist_ok=True)
514
+
515
+ # Save agent file
516
+ agent_file = agents_dir / f"{config.name}.md"
517
+ agent_content = generate_persona_markdown(config)
518
+ agent_file.write_text(agent_content)
519
+
520
+ # Save override template
521
+ override_file = overrides_dir / f"{config.name}.override.yaml"
522
+ if not override_file.exists():
523
+ override_content = generate_override_yaml(config)
524
+ override_file.write_text(override_content)
525
+
526
+ return agent_file, override_file
527
+
528
+
529
+ def list_personas(project_root: Path):
530
+ """List existing personas."""
531
+ agents_dir = project_root / "tooling" / ".automation" / "agents"
532
+
533
+ print(f"{Colors.BOLD}Available Agent Personas:{Colors.NC}")
534
+ print()
535
+
536
+ if not agents_dir.exists():
537
+ print(f" {Colors.YELLOW}No agents directory found.{Colors.NC}")
538
+ return
539
+
540
+ for agent_file in sorted(agents_dir.glob("*.md")):
541
+ name = agent_file.stem
542
+
543
+ # Extract first line (role)
544
+ content = agent_file.read_text()
545
+ first_line = content.split("\n")[0]
546
+ role = first_line.replace("# ", "").replace(" Agent", "")
547
+
548
+ # Check for override
549
+ override_file = (
550
+ project_root / "tooling" / ".automation" / "overrides" / f"{name}.override.yaml"
551
+ )
552
+ has_override = "✓" if override_file.exists() else " "
553
+
554
+ print(f" {Colors.GREEN}{name:15}{Colors.NC} │ {role:25} │ Override: {has_override}")
555
+
556
+ print()
557
+
558
+
559
+ def validate_persona(name: str, project_root: Path) -> bool:
560
+ """Validate a persona file."""
561
+ agent_file = project_root / "tooling" / ".automation" / "agents" / f"{name}.md"
562
+
563
+ if not agent_file.exists():
564
+ print(f"{Colors.RED}✗ Agent file not found: {agent_file}{Colors.NC}")
565
+ return False
566
+
567
+ print(f"{Colors.BOLD}Validating persona: {name}{Colors.NC}")
568
+ print()
569
+
570
+ content = agent_file.read_text()
571
+ errors = []
572
+ warnings = []
573
+
574
+ # Check for required sections
575
+ required_sections = ["Responsibilities", "Principles"]
576
+ for section in required_sections:
577
+ if f"## {section}" not in content:
578
+ errors.append(f"Missing required section: {section}")
579
+
580
+ # Check for role definition
581
+ if not content.startswith("# "):
582
+ errors.append("Missing role heading (should start with '# Role Agent')")
583
+
584
+ # Check for context management
585
+ if "Context Management" not in content:
586
+ warnings.append("Missing Context Management section (recommended)")
587
+
588
+ # Print results
589
+ for error in errors:
590
+ print(f" {Colors.RED}✗ ERROR:{Colors.NC} {error}")
591
+
592
+ for warning in warnings:
593
+ print(f" {Colors.YELLOW}⚠ WARNING:{Colors.NC} {warning}")
594
+
595
+ if not errors and not warnings:
596
+ print(f" {Colors.GREEN}✓ Persona is valid!{Colors.NC}")
597
+
598
+ print()
599
+ return len(errors) == 0
600
+
601
+
602
+ def main():
603
+ parser = argparse.ArgumentParser(description="Create custom agent personas")
604
+ parser.add_argument("--name", help="Quick create with this name")
605
+ parser.add_argument(
606
+ "--template", help="Use this template (developer, reviewer, architect, etc.)"
607
+ )
608
+ parser.add_argument("--list", action="store_true", help="List existing personas")
609
+ parser.add_argument("--validate", help="Validate a persona file")
610
+ parser.add_argument(
611
+ "--from-template", action="store_true", help="Create from template selection"
612
+ )
613
+ args = parser.parse_args()
614
+
615
+ # Find project root
616
+ script_dir = Path(__file__).parent
617
+ project_root = script_dir.parent.parent
618
+
619
+ print_header()
620
+
621
+ if args.list:
622
+ list_personas(project_root)
623
+ return
624
+
625
+ if args.validate:
626
+ valid = validate_persona(args.validate, project_root)
627
+ sys.exit(0 if valid else 1)
628
+
629
+ if args.name and args.template:
630
+ # Quick create with template
631
+ if args.template not in PERSONA_TEMPLATES:
632
+ print(f"{Colors.RED}Unknown template: {args.template}{Colors.NC}")
633
+ print(f"Available: {', '.join(PERSONA_TEMPLATES.keys())}")
634
+ sys.exit(1)
635
+
636
+ tpl = PERSONA_TEMPLATES[args.template]
637
+ config = PersonaConfig(
638
+ name=args.name,
639
+ role=tpl["role"],
640
+ focus=tpl["focus"],
641
+ model=tpl["model"],
642
+ responsibilities=tpl["responsibilities"],
643
+ principles=tpl["principles"],
644
+ )
645
+ elif args.from_template:
646
+ # Template selection mode
647
+ print(f"{Colors.BOLD}Available Templates:{Colors.NC}")
648
+ print()
649
+ for name, tpl in PERSONA_TEMPLATES.items():
650
+ print(f" {Colors.GREEN}{name:15}{Colors.NC} - {tpl['role']}: {tpl['focus']}")
651
+ print()
652
+
653
+ template = prompt_choice(
654
+ "Select a template",
655
+ list(PERSONA_TEMPLATES.keys()),
656
+ )
657
+
658
+ persona_name = prompt("Persona name", template)
659
+ tpl = PERSONA_TEMPLATES[template]
660
+
661
+ config = PersonaConfig(
662
+ name=persona_name,
663
+ role=tpl["role"],
664
+ focus=tpl["focus"],
665
+ model=tpl["model"],
666
+ responsibilities=tpl["responsibilities"],
667
+ principles=tpl["principles"],
668
+ )
669
+ else:
670
+ # Interactive mode
671
+ config = interactive_create()
672
+
673
+ # Save files
674
+ print()
675
+ agent_file, override_file = save_persona(config, project_root)
676
+
677
+ print(f"{Colors.GREEN}✓ Persona created successfully!{Colors.NC}")
678
+ print()
679
+ print(f" Agent file: {agent_file}")
680
+ print(f" Override file: {override_file}")
681
+ print()
682
+ print(f"{Colors.BOLD}Next steps:{Colors.NC}")
683
+ print(f" 1. Review and customize: {agent_file}")
684
+ print(f" 2. Add project-specific memories: {override_file}")
685
+ print(f" 3. Use with: ./run-story.sh <story> --agent {config.name}")
686
+ print()
687
+
688
+
689
+ if __name__ == "__main__":
690
+ main()