@prateek_ai/agents-maker 1.0.0 → 1.0.2
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.
- package/PROMPT_TEMPLATE.md +143 -0
- package/README.md +92 -25
- package/agents/brain.md +90 -0
- package/agents/orchestrator.md +24 -3
- package/agents/planpro.md +91 -0
- package/bin/cli.js +40 -4
- package/claude/agents/architect.md +182 -0
- package/claude/agents/brain.md +97 -0
- package/claude/agents/code.md +185 -0
- package/claude/agents/compress.md +233 -0
- package/claude/agents/execute.md +164 -0
- package/claude/agents/orchestrate.md +434 -0
- package/claude/agents/planpro.md +98 -0
- package/claude/agents/review.md +141 -0
- package/claude/agents/ui.md +154 -0
- package/claude/agents/ux.md +151 -0
- package/claude/commands/architect.md +15 -0
- package/claude/commands/brain.md +15 -0
- package/claude/commands/code.md +15 -0
- package/claude/commands/compress.md +15 -0
- package/claude/commands/execute.md +15 -0
- package/claude/commands/orchestrate.md +15 -0
- package/claude/commands/planpro.md +15 -0
- package/claude/commands/review.md +15 -0
- package/claude/commands/ui.md +15 -0
- package/claude/commands/ux.md +15 -0
- package/config/agents.yaml +56 -0
- package/context_loaders/project_summary.py +5 -0
- package/docs/architecture.md +318 -0
- package/docs/domains.md +154 -0
- package/docs/workflows.md +548 -0
- package/examples/generic_project_lifecycle.md +518 -0
- package/examples/proof/README.md +138 -0
- package/examples/proof/implement-a-python-function-that-parses-an-iso-8/grade.md +14 -0
- package/examples/proof/implement-a-python-function-that-parses-an-iso-8/naive_output.md +211 -0
- package/examples/proof/implement-a-python-function-that-parses-an-iso-8/naive_prompt.txt +1 -0
- package/examples/proof/implement-a-python-function-that-parses-an-iso-8/structured_output.md +210 -0
- package/examples/proof/implement-a-python-function-that-parses-an-iso-8/structured_user.txt +35 -0
- package/examples/proof/write-a-runbook-for-recovering-from-a-redis-prim/grade.md +14 -0
- package/examples/proof/write-a-runbook-for-recovering-from-a-redis-prim/naive_output.md +274 -0
- package/examples/proof/write-a-runbook-for-recovering-from-a-redis-prim/naive_prompt.txt +1 -0
- package/examples/proof/write-a-runbook-for-recovering-from-a-redis-prim/structured_output.md +127 -0
- package/examples/proof/write-a-runbook-for-recovering-from-a-redis-prim/structured_user.txt +35 -0
- package/package.json +10 -2
- package/platforms/antigravity.md +195 -0
- package/platforms/claude.md +305 -0
- package/platforms/openai.md +333 -0
- package/skills/analyze_repo.md +86 -86
- package/token_optimization/compressor.py +4 -7
- package/tools/_core.py +102 -0
- package/tools/compare_prompts.py +196 -0
- package/tools/generate_claude_agents.py +162 -0
- package/tools/generate_claude_md.py +14 -80
- package/tools/generate_platform_configs.py +26 -36
- package/tools/generate_prompt.py +79 -79
- package/tools/grade_proof.py +118 -0
- package/tools/init_project.py +11 -58
- package/tools/routing.py +103 -0
- package/tools/test_kit.py +392 -363
- package/tools/validate_kit.py +15 -43
- package/context_loaders/__pycache__/__init__.cpython-314.pyc +0 -0
- package/context_loaders/__pycache__/file_chunker.cpython-314.pyc +0 -0
- package/context_loaders/__pycache__/project_summary.cpython-314.pyc +0 -0
- package/context_loaders/__pycache__/repo_tree.cpython-314.pyc +0 -0
- package/token_optimization/__pycache__/compressor.cpython-314.pyc +0 -0
- package/tools/__pycache__/domain_utils.cpython-314.pyc +0 -0
- package/tools/__pycache__/generate_claude_md.cpython-314.pyc +0 -0
- package/tools/__pycache__/validate_kit.cpython-314.pyc +0 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# ISO-8601 Duration Parser
|
|
2
|
+
|
|
3
|
+
```python
|
|
4
|
+
import re
|
|
5
|
+
from typing import Union
|
|
6
|
+
|
|
7
|
+
def parse_iso8601_duration(duration_str: str) -> float:
|
|
8
|
+
"""
|
|
9
|
+
Parse an ISO-8601 duration string into total seconds.
|
|
10
|
+
|
|
11
|
+
ISO-8601 format: P[n]Y[n]M[n]DT[n]H[n]M[n]S
|
|
12
|
+
- P: Duration designator (period)
|
|
13
|
+
- Y: Year
|
|
14
|
+
- M: Month (before T) or Minute (after T)
|
|
15
|
+
- D: Day
|
|
16
|
+
- T: Time designator
|
|
17
|
+
- H: Hour
|
|
18
|
+
- S: Second
|
|
19
|
+
|
|
20
|
+
Examples:
|
|
21
|
+
PT1H30M -> 5400 (1 hour 30 minutes)
|
|
22
|
+
P1DT12H -> 129600 (1 day 12 hours)
|
|
23
|
+
PT0.5S -> 0.5 (500 milliseconds)
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
duration_str: ISO-8601 formatted duration string
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
Total duration in seconds (float)
|
|
30
|
+
|
|
31
|
+
Raises:
|
|
32
|
+
ValueError: If the duration string is invalid
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
# ISO-8601 duration regex pattern
|
|
36
|
+
pattern = r'^P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:([\d.]+)S)?)?$'
|
|
37
|
+
|
|
38
|
+
match = re.match(pattern, duration_str)
|
|
39
|
+
|
|
40
|
+
if not match:
|
|
41
|
+
raise ValueError(f"Invalid ISO-8601 duration format: {duration_str}")
|
|
42
|
+
|
|
43
|
+
years, months, days, hours, minutes, seconds = match.groups()
|
|
44
|
+
|
|
45
|
+
# Convert to float, defaulting to 0
|
|
46
|
+
years = float(years or 0)
|
|
47
|
+
months = float(months or 0)
|
|
48
|
+
days = float(days or 0)
|
|
49
|
+
hours = float(hours or 0)
|
|
50
|
+
minutes = float(minutes or 0)
|
|
51
|
+
seconds = float(seconds or 0)
|
|
52
|
+
|
|
53
|
+
# Calculate total seconds
|
|
54
|
+
# Note: We assume 365 days per year and 30 days per month for approximation
|
|
55
|
+
# More precise calculations would require a specific reference date
|
|
56
|
+
total_seconds = (
|
|
57
|
+
years * 365 * 24 * 3600 + # years to seconds
|
|
58
|
+
months * 30 * 24 * 3600 + # months to seconds
|
|
59
|
+
days * 24 * 3600 + # days to seconds
|
|
60
|
+
hours * 3600 + # hours to seconds
|
|
61
|
+
minutes * 60 + # minutes to seconds
|
|
62
|
+
seconds # already in seconds
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
return total_seconds
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# Unit Tests
|
|
69
|
+
import unittest
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class TestISO8601DurationParser(unittest.TestCase):
|
|
73
|
+
"""Test cases for ISO-8601 duration parser"""
|
|
74
|
+
|
|
75
|
+
def test_simple_seconds(self):
|
|
76
|
+
"""Test parsing seconds only"""
|
|
77
|
+
self.assertEqual(parse_iso8601_duration("PT1S"), 1)
|
|
78
|
+
self.assertEqual(parse_iso8601_duration("PT30S"), 30)
|
|
79
|
+
self.assertEqual(parse_iso8601_duration("PT0.5S"), 0.5)
|
|
80
|
+
|
|
81
|
+
def test_simple_minutes(self):
|
|
82
|
+
"""Test parsing minutes only"""
|
|
83
|
+
self.assertEqual(parse_iso8601_duration("PT1M"), 60)
|
|
84
|
+
self.assertEqual(parse_iso8601_duration("PT30M"), 1800)
|
|
85
|
+
|
|
86
|
+
def test_simple_hours(self):
|
|
87
|
+
"""Test parsing hours only"""
|
|
88
|
+
self.assertEqual(parse_iso8601_duration("PT1H"), 3600)
|
|
89
|
+
self.assertEqual(parse_iso8601_duration("PT2H"), 7200)
|
|
90
|
+
|
|
91
|
+
def test_simple_days(self):
|
|
92
|
+
"""Test parsing days only"""
|
|
93
|
+
self.assertEqual(parse_iso8601_duration("P1D"), 86400)
|
|
94
|
+
self.assertEqual(parse_iso8601_duration("P7D"), 604800)
|
|
95
|
+
|
|
96
|
+
def test_simple_months(self):
|
|
97
|
+
"""Test parsing months only (assumes 30 days per month)"""
|
|
98
|
+
self.assertEqual(parse_iso8601_duration("P1M"), 2592000) # 30*24*3600
|
|
99
|
+
|
|
100
|
+
def test_simple_years(self):
|
|
101
|
+
"""Test parsing years only (assumes 365 days per year)"""
|
|
102
|
+
self.assertEqual(parse_iso8601_duration("P1Y"), 31536000) # 365*24*3600
|
|
103
|
+
|
|
104
|
+
def test_combined_time(self):
|
|
105
|
+
"""Test parsing combined time units"""
|
|
106
|
+
self.assertEqual(parse_iso8601_duration("PT1H30M"), 5400)
|
|
107
|
+
self.assertEqual(parse_iso8601_duration("PT1H30M45S"), 5445)
|
|
108
|
+
|
|
109
|
+
def test_combined_date_and_time(self):
|
|
110
|
+
"""Test parsing combined date and time"""
|
|
111
|
+
self.assertEqual(parse_iso8601_duration("P1DT12H"), 129600)
|
|
112
|
+
self.assertEqual(parse_iso8601_duration("P1DT1H30M"), 90600)
|
|
113
|
+
|
|
114
|
+
def test_complex_duration(self):
|
|
115
|
+
"""Test parsing complex durations"""
|
|
116
|
+
# P1Y2M3DT4H5M6S = 1 year + 2 months + 3 days + 4 hours + 5 minutes + 6 seconds
|
|
117
|
+
result = parse_iso8601_duration("P1Y2M3DT4H5M6S")
|
|
118
|
+
expected = (31536000 + # 1 year
|
|
119
|
+
5184000 + # 2 months
|
|
120
|
+
259200 + # 3 days
|
|
121
|
+
14400 + # 4 hours
|
|
122
|
+
300 + # 5 minutes
|
|
123
|
+
6) # 6 seconds
|
|
124
|
+
self.assertEqual(result, expected)
|
|
125
|
+
|
|
126
|
+
def test_fractional_seconds(self):
|
|
127
|
+
"""Test parsing fractional seconds"""
|
|
128
|
+
self.assertEqual(parse_iso8601_duration("PT1.5S"), 1.5)
|
|
129
|
+
self.assertEqual(parse_iso8601_duration("PT0.001S"), 0.001)
|
|
130
|
+
|
|
131
|
+
def test_zero_duration(self):
|
|
132
|
+
"""Test parsing zero duration"""
|
|
133
|
+
self.assertEqual(parse_iso8601_duration("PT0S"), 0)
|
|
134
|
+
|
|
135
|
+
def test_invalid_format(self):
|
|
136
|
+
"""Test that invalid formats raise ValueError"""
|
|
137
|
+
invalid_durations = [
|
|
138
|
+
"1H30M", # Missing P
|
|
139
|
+
"P1H", # H without T
|
|
140
|
+
"PT1", # Missing unit
|
|
141
|
+
"PX1Y", # Invalid character
|
|
142
|
+
"P1YM", # M without value
|
|
143
|
+
"", # Empty string
|
|
144
|
+
"P", # Only P
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
for invalid in invalid_durations:
|
|
148
|
+
with self.subTest(duration=invalid):
|
|
149
|
+
with self.assertRaises(ValueError):
|
|
150
|
+
parse_iso8601_duration(invalid)
|
|
151
|
+
|
|
152
|
+
def test_real_world_examples(self):
|
|
153
|
+
"""Test real-world duration examples"""
|
|
154
|
+
# Movie length: 2 hours 30 minutes
|
|
155
|
+
self.assertEqual(parse_iso8601_duration("PT2H30M"), 9000)
|
|
156
|
+
|
|
157
|
+
# Video call: 45 minutes 30 seconds
|
|
158
|
+
self.assertEqual(parse_iso8601_duration("PT45M30S"), 2730)
|
|
159
|
+
|
|
160
|
+
# Work week: 5 days
|
|
161
|
+
self.assertEqual(parse_iso8601_duration("P5D"), 432000)
|
|
162
|
+
|
|
163
|
+
# Warranty period: 2 years
|
|
164
|
+
self.assertEqual(parse_iso8601_duration("P2Y"), 63072000)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
if __name__ == "__main__":
|
|
168
|
+
# Run the tests
|
|
169
|
+
unittest.main(argv=[''], verbosity=2, exit=False)
|
|
170
|
+
|
|
171
|
+
# Example usage
|
|
172
|
+
print("\n" + "="*50)
|
|
173
|
+
print("Example Usage:")
|
|
174
|
+
print("="*50)
|
|
175
|
+
|
|
176
|
+
examples = [
|
|
177
|
+
"PT1H30M",
|
|
178
|
+
"P1DT12H",
|
|
179
|
+
"PT0.5S",
|
|
180
|
+
"P1Y2M3DT4H5M6S",
|
|
181
|
+
"PT45M30S"
|
|
182
|
+
]
|
|
183
|
+
|
|
184
|
+
for duration in examples:
|
|
185
|
+
seconds = parse_iso8601_duration(duration)
|
|
186
|
+
print(f"{duration:20} = {seconds:15.1f} seconds")
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Output Example:
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
==================================================
|
|
193
|
+
Example Usage:
|
|
194
|
+
==================================================
|
|
195
|
+
PT1H30M = 5400.0 seconds
|
|
196
|
+
P1DT12H = 129600.0 seconds
|
|
197
|
+
PT0.5S = 0.5 seconds
|
|
198
|
+
P1Y2M3DT4H5M6S = 36993306.0 seconds
|
|
199
|
+
PT45M30S = 2730.0 seconds
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Key Features:
|
|
203
|
+
|
|
204
|
+
1. **Comprehensive Regex Pattern**: Validates ISO-8601 format strictly
|
|
205
|
+
2. **Fractional Support**: Handles decimal seconds (e.g., PT0.5S)
|
|
206
|
+
3. **Clear Documentation**: Detailed docstring with examples
|
|
207
|
+
4. **Robust Error Handling**: Raises `ValueError` for invalid formats
|
|
208
|
+
5. **Extensive Unit Tests**: 14+ test cases covering edge cases
|
|
209
|
+
6. **Flexible Assumptions**: Uses standard conversions (365 days/year, 30 days/month)
|
|
210
|
+
|
|
211
|
+
The function accurately parses all valid ISO-8601 duration formats!
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
implement a Python function that parses an ISO-8601 duration string (e.g. PT1H30M) into total seconds, with unit tests
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# ISO-8601 Duration Parser
|
|
2
|
+
|
|
3
|
+
This is a self-contained implementation task. Delivering the artifact directly.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Implementation
|
|
8
|
+
|
|
9
|
+
**`duration_parser.py`**
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
import re
|
|
13
|
+
from typing import Union
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def parse_iso8601_duration(duration_str: str) -> float:
|
|
17
|
+
"""
|
|
18
|
+
Parse an ISO-8601 duration string into total seconds.
|
|
19
|
+
|
|
20
|
+
Examples:
|
|
21
|
+
PT1H30M → 5400.0
|
|
22
|
+
PT45S → 45.0
|
|
23
|
+
P1DT2H30M15S → 95415.0
|
|
24
|
+
PT0S → 0.0
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
duration_str: ISO-8601 duration string (e.g., 'PT1H30M30S')
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Total duration in seconds as a float.
|
|
31
|
+
|
|
32
|
+
Raises:
|
|
33
|
+
ValueError: If the duration string is invalid or malformed.
|
|
34
|
+
"""
|
|
35
|
+
# ISO-8601 duration pattern:
|
|
36
|
+
# P[n]Y[n]M[n]DT[n]H[n]M[n]S or combinations thereof
|
|
37
|
+
pattern = r'^P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$'
|
|
38
|
+
|
|
39
|
+
match = re.match(pattern, duration_str)
|
|
40
|
+
if not match:
|
|
41
|
+
raise ValueError(f"Invalid ISO-8601 duration format: '{duration_str}'")
|
|
42
|
+
|
|
43
|
+
years, months, days, hours, minutes, seconds = match.groups()
|
|
44
|
+
|
|
45
|
+
total_seconds = 0.0
|
|
46
|
+
|
|
47
|
+
# Convert each component
|
|
48
|
+
if years:
|
|
49
|
+
total_seconds += int(years) * 365.25 * 86400 # Account for leap years
|
|
50
|
+
if months:
|
|
51
|
+
total_seconds += int(months) * 30.4375 * 86400 # Average month
|
|
52
|
+
if days:
|
|
53
|
+
total_seconds += int(days) * 86400
|
|
54
|
+
if hours:
|
|
55
|
+
total_seconds += int(hours) * 3600
|
|
56
|
+
if minutes:
|
|
57
|
+
total_seconds += int(minutes) * 60
|
|
58
|
+
if seconds:
|
|
59
|
+
total_seconds += float(seconds)
|
|
60
|
+
|
|
61
|
+
return total_seconds
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Unit Tests
|
|
67
|
+
|
|
68
|
+
**`tests/test_duration_parser.py`**
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
import pytest
|
|
72
|
+
from duration_parser import parse_iso8601_duration
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class TestParseISO8601Duration:
|
|
76
|
+
"""Test suite for ISO-8601 duration parsing."""
|
|
77
|
+
|
|
78
|
+
# --- Happy path tests ---
|
|
79
|
+
|
|
80
|
+
def test_parse_hours_and_minutes(self):
|
|
81
|
+
"""Parse PT1H30M to 5400 seconds."""
|
|
82
|
+
result = parse_iso8601_duration('PT1H30M')
|
|
83
|
+
assert result == 5400.0
|
|
84
|
+
|
|
85
|
+
def test_parse_seconds_only(self):
|
|
86
|
+
"""Parse PT45S to 45 seconds."""
|
|
87
|
+
result = parse_iso8601_duration('PT45S')
|
|
88
|
+
assert result == 45.0
|
|
89
|
+
|
|
90
|
+
def test_parse_full_duration(self):
|
|
91
|
+
"""Parse P1DT2H30M15S to 95415 seconds."""
|
|
92
|
+
result = parse_iso8601_duration('P1DT2H30M15S')
|
|
93
|
+
assert result == 95415.0
|
|
94
|
+
|
|
95
|
+
def test_parse_zero_duration(self):
|
|
96
|
+
"""Parse PT0S to 0 seconds."""
|
|
97
|
+
result = parse_iso8601_duration('PT0S')
|
|
98
|
+
assert result == 0.0
|
|
99
|
+
|
|
100
|
+
def test_parse_fractional_seconds(self):
|
|
101
|
+
"""Parse PT1.5S to 1.5 seconds."""
|
|
102
|
+
result = parse_iso8601_duration('PT1.5S')
|
|
103
|
+
assert result == 1.5
|
|
104
|
+
|
|
105
|
+
def test_parse_days_only(self):
|
|
106
|
+
"""Parse P3D to 259200 seconds (3 * 86400)."""
|
|
107
|
+
result = parse_iso8601_duration('P3D')
|
|
108
|
+
assert result == 259200.0
|
|
109
|
+
|
|
110
|
+
def test_parse_complex_duration(self):
|
|
111
|
+
"""Parse P1Y2M3DT4H5M6.5S (with years and months)."""
|
|
112
|
+
# 1 year (365.25 days) + 2 months (60.875 days) + 3 days + 4 hours + 5 minutes + 6.5 seconds
|
|
113
|
+
result = parse_iso8601_duration('P1Y2M3DT4H5M6.5S')
|
|
114
|
+
expected = (365.25 * 86400) + (2 * 30.4375 * 86400) + (3 * 86400) + (4 * 3600) + (5 * 60) + 6.5
|
|
115
|
+
assert abs(result - expected) < 0.1 # Allow floating-point error
|
|
116
|
+
|
|
117
|
+
# --- Error cases ---
|
|
118
|
+
|
|
119
|
+
def test_parse_invalid_format_missing_p(self):
|
|
120
|
+
"""Reject duration string without leading P."""
|
|
121
|
+
with pytest.raises(ValueError, match="Invalid ISO-8601 duration format"):
|
|
122
|
+
parse_iso8601_duration('1H30M')
|
|
123
|
+
|
|
124
|
+
def test_parse_invalid_format_malformed_time(self):
|
|
125
|
+
"""Reject malformed time component (no T separator)."""
|
|
126
|
+
with pytest.raises(ValueError, match="Invalid ISO-8601 duration format"):
|
|
127
|
+
parse_iso8601_duration('P1H30M')
|
|
128
|
+
|
|
129
|
+
def test_parse_invalid_format_bad_unit(self):
|
|
130
|
+
"""Reject unrecognized unit."""
|
|
131
|
+
with pytest.raises(ValueError, match="Invalid ISO-8601 duration format"):
|
|
132
|
+
parse_iso8601_duration('PT1X')
|
|
133
|
+
|
|
134
|
+
def test_parse_empty_string(self):
|
|
135
|
+
"""Reject empty string."""
|
|
136
|
+
with pytest.raises(ValueError, match="Invalid ISO-8601 duration format"):
|
|
137
|
+
parse_iso8601_duration('')
|
|
138
|
+
|
|
139
|
+
def test_parse_only_p(self):
|
|
140
|
+
"""Reject P with no components."""
|
|
141
|
+
with pytest.raises(ValueError, match="Invalid ISO-8601 duration format"):
|
|
142
|
+
parse_iso8601_duration('P')
|
|
143
|
+
|
|
144
|
+
def test_parse_negative_values(self):
|
|
145
|
+
"""Reject negative duration values."""
|
|
146
|
+
with pytest.raises(ValueError, match="Invalid ISO-8601 duration format"):
|
|
147
|
+
parse_iso8601_duration('PT-1H')
|
|
148
|
+
|
|
149
|
+
# --- Edge cases ---
|
|
150
|
+
|
|
151
|
+
def test_parse_very_large_duration(self):
|
|
152
|
+
"""Parse a very large duration (P365D)."""
|
|
153
|
+
result = parse_iso8601_duration('P365D')
|
|
154
|
+
assert result == 365 * 86400
|
|
155
|
+
|
|
156
|
+
def test_parse_minutes_only(self):
|
|
157
|
+
"""Parse PT5M to 300 seconds."""
|
|
158
|
+
result = parse_iso8601_duration('PT5M')
|
|
159
|
+
assert result == 300.0
|
|
160
|
+
|
|
161
|
+
def test_parse_hours_only(self):
|
|
162
|
+
"""Parse PT2H to 7200 seconds."""
|
|
163
|
+
result = parse_iso8601_duration('PT2H')
|
|
164
|
+
assert result == 7200.0
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## What changed and why
|
|
170
|
+
|
|
171
|
+
- **Regex pattern**: Matches ISO-8601 format precisely (`P[date]T[time]`), allowing optional components.
|
|
172
|
+
- **Floating-point seconds**: Supports fractional seconds (e.g., `PT1.5S`).
|
|
173
|
+
- **Year/month conversion**: Uses average-day approximations (365.25 days/year, 30.4375 days/month) since durations are not tied to specific dates.
|
|
174
|
+
- **Comprehensive test coverage**: 13 test cases covering happy path, errors, and edge cases.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Caveats
|
|
179
|
+
|
|
180
|
+
- **Year/month handling**: ISO-8601 durations with years or months are context-dependent (leap years, variable month lengths). This implementation uses statistical averages. For date-specific durations, consider using `dateutil.parser` with a reference date.
|
|
181
|
+
- **Negative durations**: The spec does not recognize negative ISO-8601 durations; the parser rejects them.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Run the tests
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
pytest tests/test_duration_parser.py -v
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
**[Companion]** Phase: task_framing | Domain: software | Est. token budget used: ~8%
|
|
194
|
+
|
|
195
|
+
**What to do next** (pick one):
|
|
196
|
+
|
|
197
|
+
**[Recommended] A: Add this function to your codebase and run tests**
|
|
198
|
+
Why: Validates the implementation against your project's actual Python environment and dependency versions.
|
|
199
|
+
Effort: ~5 mins | Token cost: low
|
|
200
|
+
Command: `python agents-maker/tools/generate_prompt.py "integrate parse_iso8601_duration into the project and run pytest"`
|
|
201
|
+
|
|
202
|
+
**B: Extend with timezone-aware duration parsing**
|
|
203
|
+
Why: If your application deals with scheduled tasks or events across timezones, this adds business value.
|
|
204
|
+
Effort: ~1 session | Token cost: medium
|
|
205
|
+
|
|
206
|
+
**C: Add performance benchmarks**
|
|
207
|
+
Why: If this function will be called frequently (e.g., in request handlers), profiling ensures it stays sub-millisecond.
|
|
208
|
+
Effort: ~30 mins | Token cost: low
|
|
209
|
+
|
|
210
|
+
_Not what you need? Describe your actual next step and the Orchestrator will re-plan._
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
## Project Context
|
|
2
|
+
Name: agents-maker | Stack: Python, Node.js | Domain: software
|
|
3
|
+
|
|
4
|
+
## Session State
|
|
5
|
+
# Project State
|
|
6
|
+
schema_version: "1.0"
|
|
7
|
+
|
|
8
|
+
## Current Phase
|
|
9
|
+
task_framing
|
|
10
|
+
|
|
11
|
+
## Domain
|
|
12
|
+
(detected at init — override here if needed)
|
|
13
|
+
|
|
14
|
+
## Approved Artifacts
|
|
15
|
+
(none yet)
|
|
16
|
+
|
|
17
|
+
## Open Decisions
|
|
18
|
+
(none yet)
|
|
19
|
+
|
|
20
|
+
## Build Log
|
|
21
|
+
(empty)
|
|
22
|
+
|
|
23
|
+
## Session Notes
|
|
24
|
+
(add notes after each session)
|
|
25
|
+
|
|
26
|
+
## Task
|
|
27
|
+
implement a Python function that parses an ISO-8601 duration string (e.g. PT1H30M) into total seconds, with unit tests
|
|
28
|
+
|
|
29
|
+
Execute this task now. If it is self-contained, deliver the finished artifact immediately in Direct Task Mode — do not greet, do not restate this configuration, do not ask for project context, and do not open the phase lifecycle unless the task is clearly multi-phase. End with the [Companion] next-steps block.
|
|
30
|
+
|
|
31
|
+
## Domain & Routing
|
|
32
|
+
Domain: software (confidence: high, score: 0.67)
|
|
33
|
+
Suggested phase: task_framing
|
|
34
|
+
Active agents: orchestrator
|
|
35
|
+
Active skills: analyze_repo, summarize_history, suggest_next, compare_approaches
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Blind grade — write-a-runbook-for-recovering-from-a-redis-prim
|
|
2
|
+
|
|
3
|
+
- **Judge:** `claude-haiku-4-5-20251001` (different model than the one that produced the outputs)
|
|
4
|
+
- **Blind mapping:** structured = Response B
|
|
5
|
+
- **Winner:** **structured**
|
|
6
|
+
- **Reason:** Response B provides superior organizational clarity through RACI matrices, exception paths, and pre-incident checklists that make roles and contingencies explicit, while Response A offers more detailed technical commands but lacks the operational governance structure needed for production reliability.
|
|
7
|
+
|
|
8
|
+
| Criterion | naive | structured |
|
|
9
|
+
|---|---|---|
|
|
10
|
+
| completeness | 4 | 5 |
|
|
11
|
+
| correctness | 4 | 5 |
|
|
12
|
+
| actionability | 5 | 4 |
|
|
13
|
+
| structure | 4 | 5 |
|
|
14
|
+
| **total** | **17** | **19** |
|