@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,328 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Currency Converter - Multi-currency support for cost display.
4
+
5
+ Converts USD amounts to other currencies and formats them appropriately.
6
+
7
+ Usage:
8
+ from lib.currency_converter import CurrencyConverter
9
+
10
+ converter = CurrencyConverter()
11
+ print(converter.format(1.77, "EUR")) # €1.63
12
+ print(converter.format_all(1.77)) # $1.77 | €1.63 | £1.40 | R$10.80
13
+
14
+ Exchange Rate Notice:
15
+ Default exchange rates are STATIC approximations and will become stale.
16
+ Last updated: December 2025
17
+
18
+ For accurate conversions, you should:
19
+ 1. Use a config file with current rates:
20
+ converter = CurrencyConverter(config_path=Path("currency_config.json"))
21
+
22
+ 2. Set custom rates directly:
23
+ converter.set_rates({"EUR": 0.95, "GBP": 0.82})
24
+
25
+ 3. Use environment variables:
26
+ export CURRENCY_RATE_EUR=0.95
27
+ export CURRENCY_RATE_GBP=0.82
28
+ """
29
+
30
+ import json
31
+ import threading
32
+ from pathlib import Path
33
+ from typing import Optional
34
+
35
+
36
+ class CurrencyConverter:
37
+ """
38
+ Convert and format USD amounts to multiple currencies.
39
+
40
+ Supports customizable exchange rates via config file or direct setting.
41
+
42
+ Warning:
43
+ Default exchange rates are static approximations from December 2025.
44
+ For production use, provide current rates via config file or set_rates().
45
+ """
46
+
47
+ # Default exchange rates (USD to target currency)
48
+ # WARNING: These are approximate rates from December 2025
49
+ # Update via config file or set_rates() for accurate values
50
+ # Source: Approximate market rates - NOT suitable for financial calculations
51
+ DEFAULT_RATES = {
52
+ "USD": 1.0,
53
+ "EUR": 0.92,
54
+ "GBP": 0.79,
55
+ "BRL": 6.10,
56
+ "CAD": 1.36,
57
+ "AUD": 1.53,
58
+ "JPY": 149.50,
59
+ "CNY": 7.24,
60
+ "INR": 83.50,
61
+ "MXN": 17.20,
62
+ }
63
+
64
+ # Currency symbols
65
+ SYMBOLS = {
66
+ "USD": "$",
67
+ "EUR": "€",
68
+ "GBP": "£",
69
+ "BRL": "R$",
70
+ "CAD": "C$",
71
+ "AUD": "A$",
72
+ "JPY": "¥",
73
+ "CNY": "¥",
74
+ "INR": "₹",
75
+ "MXN": "$",
76
+ }
77
+
78
+ # Currency names
79
+ NAMES = {
80
+ "USD": "US Dollar",
81
+ "EUR": "Euro",
82
+ "GBP": "British Pound",
83
+ "BRL": "Brazilian Real",
84
+ "CAD": "Canadian Dollar",
85
+ "AUD": "Australian Dollar",
86
+ "JPY": "Japanese Yen",
87
+ "CNY": "Chinese Yuan",
88
+ "INR": "Indian Rupee",
89
+ "MXN": "Mexican Peso",
90
+ }
91
+
92
+ def __init__(
93
+ self,
94
+ rates: Optional[dict[str, float]] = None,
95
+ display_currencies: Optional[list[str]] = None,
96
+ config_path: Optional[Path] = None,
97
+ ):
98
+ """
99
+ Initialize converter.
100
+
101
+ Args:
102
+ rates: Custom exchange rates (USD to target)
103
+ display_currencies: List of currencies to show in format_all()
104
+ config_path: Path to config file with rates
105
+ """
106
+ self.rates = self.DEFAULT_RATES.copy()
107
+
108
+ # Load from config file if provided
109
+ if config_path and config_path.exists():
110
+ self._load_config(config_path)
111
+
112
+ # Override with provided rates
113
+ if rates:
114
+ self.rates.update(rates)
115
+
116
+ # Currencies to display by default
117
+ self.display_currencies = display_currencies or ["USD", "EUR", "GBP", "BRL"]
118
+
119
+ def _load_config(self, config_path: Path):
120
+ """Load exchange rates from config file."""
121
+ try:
122
+ with open(config_path) as f:
123
+ config = json.load(f)
124
+
125
+ if "currency_rates" in config:
126
+ self.rates.update(config["currency_rates"])
127
+
128
+ if "display_currencies" in config:
129
+ self.display_currencies = config["display_currencies"]
130
+
131
+ except Exception as e:
132
+ print(f"Warning: Could not load currency config: {e}")
133
+
134
+ def convert(self, amount_usd: float, currency: str) -> float:
135
+ """
136
+ Convert USD to target currency.
137
+
138
+ Args:
139
+ amount_usd: Amount in USD
140
+ currency: Target currency code (e.g., "EUR")
141
+
142
+ Returns:
143
+ Amount in target currency
144
+ """
145
+ currency = currency.upper()
146
+ rate = self.rates.get(currency, 1.0)
147
+ return amount_usd * rate
148
+
149
+ def format(
150
+ self, amount_usd: float, currency: str, include_symbol: bool = True, decimal_places: int = 2
151
+ ) -> str:
152
+ """
153
+ Format amount in target currency.
154
+
155
+ Args:
156
+ amount_usd: Amount in USD
157
+ currency: Target currency code
158
+ include_symbol: Include currency symbol
159
+ decimal_places: Number of decimal places
160
+
161
+ Returns:
162
+ Formatted string (e.g., "€1.63")
163
+ """
164
+ currency = currency.upper()
165
+ converted = self.convert(amount_usd, currency)
166
+
167
+ # Special case for JPY (no decimals typically)
168
+ if currency == "JPY":
169
+ decimal_places = 0
170
+
171
+ formatted = f"{converted:,.{decimal_places}f}"
172
+
173
+ if include_symbol:
174
+ symbol = self.SYMBOLS.get(currency, currency)
175
+ return f"{symbol}{formatted}"
176
+
177
+ return formatted
178
+
179
+ def format_all(
180
+ self, amount_usd: float, separator: str = " | ", currencies: Optional[list[str]] = None
181
+ ) -> str:
182
+ """
183
+ Format amount in all display currencies.
184
+
185
+ Args:
186
+ amount_usd: Amount in USD
187
+ separator: Separator between currencies
188
+ currencies: Override list of currencies to display
189
+
190
+ Returns:
191
+ Formatted string (e.g., "$1.77 | €1.63 | £1.40")
192
+ """
193
+ currencies = currencies or self.display_currencies
194
+ parts = [self.format(amount_usd, c) for c in currencies]
195
+ return separator.join(parts)
196
+
197
+ def format_compact(self, amount_usd: float) -> str:
198
+ """
199
+ Format amount in compact style for terminal display.
200
+
201
+ Returns:
202
+ Compact format (e.g., "$1.77/€1.63/£1.40")
203
+ """
204
+ return self.format_all(amount_usd, separator="/")
205
+
206
+ def format_table_row(self, amount_usd: float) -> dict[str, str]:
207
+ """
208
+ Get formatted amounts as dictionary for table display.
209
+
210
+ Returns:
211
+ Dictionary of currency code -> formatted amount
212
+ """
213
+ return {currency: self.format(amount_usd, currency) for currency in self.display_currencies}
214
+
215
+ def set_rates(self, rates: dict[str, float]):
216
+ """Update exchange rates."""
217
+ self.rates.update(rates)
218
+
219
+ def set_display_currencies(self, currencies: list[str]):
220
+ """Set which currencies to display."""
221
+ self.display_currencies = [c.upper() for c in currencies]
222
+
223
+ def get_rate(self, currency: str) -> float:
224
+ """Get exchange rate for a currency."""
225
+ return self.rates.get(currency.upper(), 1.0)
226
+
227
+ def list_currencies(self) -> list[dict]:
228
+ """List all supported currencies with their rates."""
229
+ return [
230
+ {
231
+ "code": code,
232
+ "name": self.NAMES.get(code, code),
233
+ "symbol": self.SYMBOLS.get(code, code),
234
+ "rate": rate,
235
+ }
236
+ for code, rate in sorted(self.rates.items())
237
+ ]
238
+
239
+ def save_config(self, config_path: Path):
240
+ """Save current rates to config file."""
241
+ config = {
242
+ "currency_rates": self.rates,
243
+ "display_currencies": self.display_currencies,
244
+ }
245
+
246
+ with open(config_path, "w") as f:
247
+ json.dump(config, f, indent=2)
248
+
249
+
250
+ # Thread-safe global converter storage
251
+ _converter_lock = threading.Lock()
252
+ _converter: Optional[CurrencyConverter] = None
253
+
254
+
255
+ def get_converter() -> CurrencyConverter:
256
+ """
257
+ Get or create the global converter instance (thread-safe).
258
+
259
+ Returns:
260
+ A shared CurrencyConverter instance.
261
+
262
+ Note:
263
+ The converter is shared across threads since exchange rates
264
+ are read-only after initialization. If you need to modify
265
+ rates for a specific thread, create a new instance instead.
266
+ """
267
+ global _converter
268
+ if _converter is None:
269
+ with _converter_lock:
270
+ # Double-check pattern for thread safety
271
+ if _converter is None:
272
+ _converter = CurrencyConverter()
273
+ return _converter
274
+
275
+
276
+ def set_converter(converter: CurrencyConverter):
277
+ """
278
+ Set the global converter instance (thread-safe).
279
+
280
+ Args:
281
+ converter: The CurrencyConverter instance to use globally.
282
+ """
283
+ global _converter
284
+ with _converter_lock:
285
+ _converter = converter
286
+
287
+
288
+ # Convenience functions
289
+ def convert(amount_usd: float, currency: str) -> float:
290
+ """Convert USD to target currency using global converter."""
291
+ return get_converter().convert(amount_usd, currency)
292
+
293
+
294
+ def format_currency(amount_usd: float, currency: str = "USD") -> str:
295
+ """Format amount using global converter."""
296
+ return get_converter().format(amount_usd, currency)
297
+
298
+
299
+ def format_all_currencies(amount_usd: float) -> str:
300
+ """Format amount in all display currencies using global converter."""
301
+ return get_converter().format_all(amount_usd)
302
+
303
+
304
+ if __name__ == "__main__":
305
+ # Demo/test
306
+ converter = CurrencyConverter()
307
+
308
+ amount = 15.77
309
+
310
+ print(f"Amount: ${amount:.2f} USD\n")
311
+
312
+ print("Individual currencies:")
313
+ for currency in ["USD", "EUR", "GBP", "BRL", "JPY"]:
314
+ print(f" {currency}: {converter.format(amount, currency)}")
315
+
316
+ print("\nAll display currencies:")
317
+ print(f" {converter.format_all(amount)}")
318
+
319
+ print("\nCompact format:")
320
+ print(f" {converter.format_compact(amount)}")
321
+
322
+ print("\nTable row:")
323
+ for code, formatted in converter.format_table_row(amount).items():
324
+ print(f" {code}: {formatted}")
325
+
326
+ print("\nSupported currencies:")
327
+ for curr in converter.list_currencies():
328
+ print(f" {curr['code']}: {curr['name']} ({curr['symbol']}) - Rate: {curr['rate']}")