agentic-dev 0.2.2 → 0.2.3
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/.claude/skills/sdd/SKILL.md +178 -7
- package/.claude/skills/sdd/agents/openai.yaml +4 -0
- package/.claude/skills/sdd/references/section-map.md +67 -0
- package/package.json +1 -1
- package/.claude/skills/commit/SKILL.md +0 -37
- package/.claude/skills/dev-browser/SKILL.md +0 -30
- package/.claude/skills/otro/SKILL.md +0 -43
- package/.claude/skills/planning-with-files/SKILL.md +0 -37
- package/.claude/skills/prd/SKILL.md +0 -27
- package/.claude/skills/ralph-loop/SKILL.md +0 -42
- package/.claude/skills/sdd-dev/SKILL.md +0 -71
- package/.claude/skills/sdd-development/SKILL.md +0 -13
- package/.codex/skills/agents/openai.yaml +0 -4
- package/.codex/skills/commit/SKILL.md +0 -219
- package/.codex/skills/commit/references/commit_examples.md +0 -292
- package/.codex/skills/dev-browser/SKILL.md +0 -211
- package/.codex/skills/dev-browser/bun.lock +0 -443
- package/.codex/skills/dev-browser/package-lock.json +0 -2988
- package/.codex/skills/dev-browser/package.json +0 -31
- package/.codex/skills/dev-browser/references/scraping.md +0 -155
- package/.codex/skills/dev-browser/scripts/start-relay.ts +0 -32
- package/.codex/skills/dev-browser/scripts/start-server.ts +0 -117
- package/.codex/skills/dev-browser/server.sh +0 -24
- package/.codex/skills/dev-browser/src/client.ts +0 -474
- package/.codex/skills/dev-browser/src/index.ts +0 -287
- package/.codex/skills/dev-browser/src/relay.ts +0 -731
- package/.codex/skills/dev-browser/src/snapshot/__tests__/snapshot.test.ts +0 -223
- package/.codex/skills/dev-browser/src/snapshot/browser-script.ts +0 -877
- package/.codex/skills/dev-browser/src/snapshot/index.ts +0 -14
- package/.codex/skills/dev-browser/src/snapshot/inject.ts +0 -13
- package/.codex/skills/dev-browser/src/types.ts +0 -34
- package/.codex/skills/dev-browser/tsconfig.json +0 -36
- package/.codex/skills/dev-browser/vitest.config.ts +0 -12
- package/.codex/skills/otro/SKILL.md +0 -74
- package/.codex/skills/otro/agents/openai.yaml +0 -4
- package/.codex/skills/otro/references/agent-prompts.md +0 -61
- package/.codex/skills/otro/references/contracts.md +0 -146
- package/.codex/skills/otro/references/orchestration-loop.md +0 -51
- package/.codex/skills/otro/references/runtime.md +0 -79
- package/.codex/skills/otro/runs/README.md +0 -11
- package/.codex/skills/otro/schemas/step_plan.schema.json +0 -289
- package/.codex/skills/otro/schemas/task_result.schema.json +0 -142
- package/.codex/skills/otro/schemas/wave_plan.schema.json +0 -4
- package/.codex/skills/otro/scripts/README.md +0 -38
- package/.codex/skills/otro/scripts/bump_validation_header.py +0 -179
- package/.codex/skills/otro/scripts/check_validation_header.py +0 -84
- package/.codex/skills/otro/scripts/common.py +0 -303
- package/.codex/skills/otro/scripts/init_run.sh +0 -68
- package/.codex/skills/otro/scripts/plan_loop.py +0 -8
- package/.codex/skills/otro/scripts/plan_step.py +0 -367
- package/.codex/skills/otro/scripts/plan_wave.py +0 -8
- package/.codex/skills/otro/scripts/reconcile_loop.py +0 -8
- package/.codex/skills/otro/scripts/reconcile_step.py +0 -37
- package/.codex/skills/otro/scripts/reconcile_wave.py +0 -8
- package/.codex/skills/otro/scripts/run_loop.py +0 -300
- package/.codex/skills/otro/scripts/run_loop_step.py +0 -8
- package/.codex/skills/otro/scripts/run_step.py +0 -246
- package/.codex/skills/otro/scripts/run_wave.py +0 -8
- package/.codex/skills/otro/validation/validation.md +0 -15
- package/.codex/skills/planning-with-files/SKILL.md +0 -42
- package/.codex/skills/planning-with-files/agents/openai.yaml +0 -4
- package/.codex/skills/planning-with-files/assets/plan-template.md +0 -37
- package/.codex/skills/planning-with-files/references/plan-rules.md +0 -35
- package/.codex/skills/planning-with-files/scripts/new_plan.sh +0 -65
- package/.codex/skills/prd/SKILL.md +0 -235
- package/.codex/skills/ralph-loop/SKILL.md +0 -46
- package/.codex/skills/ralph-loop/agents/openai.yaml +0 -4
- package/.codex/skills/ralph-loop/references/failure-triage.md +0 -32
- package/.codex/skills/ralph-loop/scripts/loop_until_success.sh +0 -97
- package/sdd/99_toolchain/02_policies/otro-orchestration-policy.md +0 -30
|
@@ -1,289 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"type": "object",
|
|
4
|
-
"additionalProperties": false,
|
|
5
|
-
"required": [
|
|
6
|
-
"run_name",
|
|
7
|
-
"plan_version",
|
|
8
|
-
"goal",
|
|
9
|
-
"summary",
|
|
10
|
-
"anchor_alignment",
|
|
11
|
-
"relevant_files",
|
|
12
|
-
"assumptions",
|
|
13
|
-
"risks",
|
|
14
|
-
"open_questions",
|
|
15
|
-
"completion_policy",
|
|
16
|
-
"tasks",
|
|
17
|
-
"steps"
|
|
18
|
-
],
|
|
19
|
-
"properties": {
|
|
20
|
-
"run_name": {
|
|
21
|
-
"type": "string",
|
|
22
|
-
"minLength": 1
|
|
23
|
-
},
|
|
24
|
-
"plan_version": {
|
|
25
|
-
"type": "integer",
|
|
26
|
-
"minimum": 1
|
|
27
|
-
},
|
|
28
|
-
"goal": {
|
|
29
|
-
"type": "string",
|
|
30
|
-
"minLength": 1
|
|
31
|
-
},
|
|
32
|
-
"summary": {
|
|
33
|
-
"type": "string",
|
|
34
|
-
"minLength": 1
|
|
35
|
-
},
|
|
36
|
-
"anchor_alignment": {
|
|
37
|
-
"type": "object",
|
|
38
|
-
"additionalProperties": false,
|
|
39
|
-
"required": [
|
|
40
|
-
"status",
|
|
41
|
-
"notes"
|
|
42
|
-
],
|
|
43
|
-
"properties": {
|
|
44
|
-
"status": {
|
|
45
|
-
"type": "string",
|
|
46
|
-
"enum": [
|
|
47
|
-
"aligned",
|
|
48
|
-
"drifted"
|
|
49
|
-
]
|
|
50
|
-
},
|
|
51
|
-
"notes": {
|
|
52
|
-
"type": "array",
|
|
53
|
-
"items": {
|
|
54
|
-
"type": "string"
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
"relevant_files": {
|
|
60
|
-
"type": "array",
|
|
61
|
-
"minItems": 1,
|
|
62
|
-
"items": {
|
|
63
|
-
"type": "object",
|
|
64
|
-
"additionalProperties": false,
|
|
65
|
-
"required": [
|
|
66
|
-
"path",
|
|
67
|
-
"reason"
|
|
68
|
-
],
|
|
69
|
-
"properties": {
|
|
70
|
-
"path": {
|
|
71
|
-
"type": "string",
|
|
72
|
-
"minLength": 1
|
|
73
|
-
},
|
|
74
|
-
"reason": {
|
|
75
|
-
"type": "string",
|
|
76
|
-
"minLength": 1
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
"assumptions": {
|
|
82
|
-
"type": "array",
|
|
83
|
-
"items": {
|
|
84
|
-
"type": "string"
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
"risks": {
|
|
88
|
-
"type": "array",
|
|
89
|
-
"items": {
|
|
90
|
-
"type": "string"
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
"open_questions": {
|
|
94
|
-
"type": "array",
|
|
95
|
-
"items": {
|
|
96
|
-
"type": "string"
|
|
97
|
-
}
|
|
98
|
-
},
|
|
99
|
-
"completion_policy": {
|
|
100
|
-
"type": "object",
|
|
101
|
-
"additionalProperties": false,
|
|
102
|
-
"required": [
|
|
103
|
-
"done_when",
|
|
104
|
-
"loop_done",
|
|
105
|
-
"run_done",
|
|
106
|
-
"repository_done",
|
|
107
|
-
"replan_when"
|
|
108
|
-
],
|
|
109
|
-
"properties": {
|
|
110
|
-
"done_when": {
|
|
111
|
-
"type": "array",
|
|
112
|
-
"minItems": 1,
|
|
113
|
-
"items": {
|
|
114
|
-
"type": "string"
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
"loop_done": {
|
|
118
|
-
"type": "array",
|
|
119
|
-
"items": {
|
|
120
|
-
"type": "string"
|
|
121
|
-
}
|
|
122
|
-
},
|
|
123
|
-
"run_done": {
|
|
124
|
-
"type": "array",
|
|
125
|
-
"items": {
|
|
126
|
-
"type": "string"
|
|
127
|
-
}
|
|
128
|
-
},
|
|
129
|
-
"repository_done": {
|
|
130
|
-
"type": "array",
|
|
131
|
-
"items": {
|
|
132
|
-
"type": "string"
|
|
133
|
-
}
|
|
134
|
-
},
|
|
135
|
-
"replan_when": {
|
|
136
|
-
"type": "array",
|
|
137
|
-
"minItems": 1,
|
|
138
|
-
"items": {
|
|
139
|
-
"type": "string"
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
},
|
|
144
|
-
"tasks": {
|
|
145
|
-
"type": "array",
|
|
146
|
-
"minItems": 1,
|
|
147
|
-
"items": {
|
|
148
|
-
"type": "object",
|
|
149
|
-
"additionalProperties": false,
|
|
150
|
-
"required": [
|
|
151
|
-
"id",
|
|
152
|
-
"step",
|
|
153
|
-
"kind",
|
|
154
|
-
"title",
|
|
155
|
-
"objective",
|
|
156
|
-
"owned_paths",
|
|
157
|
-
"read_paths",
|
|
158
|
-
"depends_on",
|
|
159
|
-
"deliverables",
|
|
160
|
-
"acceptance_criteria",
|
|
161
|
-
"verification_commands",
|
|
162
|
-
"status",
|
|
163
|
-
"worker_prompt"
|
|
164
|
-
],
|
|
165
|
-
"properties": {
|
|
166
|
-
"id": {
|
|
167
|
-
"type": "string",
|
|
168
|
-
"pattern": "^T[0-9]{3,9}$"
|
|
169
|
-
},
|
|
170
|
-
"step": {
|
|
171
|
-
"type": "integer",
|
|
172
|
-
"minimum": 1
|
|
173
|
-
},
|
|
174
|
-
"kind": {
|
|
175
|
-
"type": "string",
|
|
176
|
-
"enum": [
|
|
177
|
-
"analysis",
|
|
178
|
-
"edit",
|
|
179
|
-
"verify",
|
|
180
|
-
"integration",
|
|
181
|
-
"cleanup",
|
|
182
|
-
"planning"
|
|
183
|
-
]
|
|
184
|
-
},
|
|
185
|
-
"title": {
|
|
186
|
-
"type": "string",
|
|
187
|
-
"minLength": 1
|
|
188
|
-
},
|
|
189
|
-
"objective": {
|
|
190
|
-
"type": "string",
|
|
191
|
-
"minLength": 1
|
|
192
|
-
},
|
|
193
|
-
"owned_paths": {
|
|
194
|
-
"type": "array",
|
|
195
|
-
"items": {
|
|
196
|
-
"type": "string"
|
|
197
|
-
}
|
|
198
|
-
},
|
|
199
|
-
"read_paths": {
|
|
200
|
-
"type": "array",
|
|
201
|
-
"items": {
|
|
202
|
-
"type": "string"
|
|
203
|
-
}
|
|
204
|
-
},
|
|
205
|
-
"depends_on": {
|
|
206
|
-
"type": "array",
|
|
207
|
-
"items": {
|
|
208
|
-
"type": "string",
|
|
209
|
-
"pattern": "^T[0-9]{3,9}$"
|
|
210
|
-
}
|
|
211
|
-
},
|
|
212
|
-
"deliverables": {
|
|
213
|
-
"type": "array",
|
|
214
|
-
"minItems": 1,
|
|
215
|
-
"items": {
|
|
216
|
-
"type": "string"
|
|
217
|
-
}
|
|
218
|
-
},
|
|
219
|
-
"acceptance_criteria": {
|
|
220
|
-
"type": "array",
|
|
221
|
-
"minItems": 1,
|
|
222
|
-
"items": {
|
|
223
|
-
"type": "string"
|
|
224
|
-
}
|
|
225
|
-
},
|
|
226
|
-
"verification_commands": {
|
|
227
|
-
"type": "array",
|
|
228
|
-
"items": {
|
|
229
|
-
"type": "string"
|
|
230
|
-
}
|
|
231
|
-
},
|
|
232
|
-
"status": {
|
|
233
|
-
"type": "string",
|
|
234
|
-
"enum": [
|
|
235
|
-
"pending",
|
|
236
|
-
"running",
|
|
237
|
-
"completed",
|
|
238
|
-
"blocked",
|
|
239
|
-
"failed",
|
|
240
|
-
"skipped"
|
|
241
|
-
]
|
|
242
|
-
},
|
|
243
|
-
"worker_prompt": {
|
|
244
|
-
"type": "string",
|
|
245
|
-
"minLength": 1
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
},
|
|
250
|
-
"steps": {
|
|
251
|
-
"type": "array",
|
|
252
|
-
"minItems": 1,
|
|
253
|
-
"items": {
|
|
254
|
-
"type": "object",
|
|
255
|
-
"additionalProperties": false,
|
|
256
|
-
"required": [
|
|
257
|
-
"step",
|
|
258
|
-
"goal",
|
|
259
|
-
"task_ids",
|
|
260
|
-
"merge_checks"
|
|
261
|
-
],
|
|
262
|
-
"properties": {
|
|
263
|
-
"step": {
|
|
264
|
-
"type": "integer",
|
|
265
|
-
"minimum": 1
|
|
266
|
-
},
|
|
267
|
-
"goal": {
|
|
268
|
-
"type": "string",
|
|
269
|
-
"minLength": 1
|
|
270
|
-
},
|
|
271
|
-
"task_ids": {
|
|
272
|
-
"type": "array",
|
|
273
|
-
"minItems": 1,
|
|
274
|
-
"items": {
|
|
275
|
-
"type": "string",
|
|
276
|
-
"pattern": "^T[0-9]{3,9}$"
|
|
277
|
-
}
|
|
278
|
-
},
|
|
279
|
-
"merge_checks": {
|
|
280
|
-
"type": "array",
|
|
281
|
-
"items": {
|
|
282
|
-
"type": "string"
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"type": "object",
|
|
4
|
-
"additionalProperties": false,
|
|
5
|
-
"required": [
|
|
6
|
-
"task_id",
|
|
7
|
-
"status",
|
|
8
|
-
"summary",
|
|
9
|
-
"changed_files",
|
|
10
|
-
"verification",
|
|
11
|
-
"blockers",
|
|
12
|
-
"integration_notes",
|
|
13
|
-
"proposed_follow_up_tasks",
|
|
14
|
-
"residual_signals"
|
|
15
|
-
],
|
|
16
|
-
"properties": {
|
|
17
|
-
"task_id": {
|
|
18
|
-
"type": "string",
|
|
19
|
-
"pattern": "^T[0-9]{3,9}$"
|
|
20
|
-
},
|
|
21
|
-
"status": {
|
|
22
|
-
"type": "string",
|
|
23
|
-
"enum": [
|
|
24
|
-
"completed",
|
|
25
|
-
"partial",
|
|
26
|
-
"blocked",
|
|
27
|
-
"failed"
|
|
28
|
-
]
|
|
29
|
-
},
|
|
30
|
-
"summary": {
|
|
31
|
-
"type": "string",
|
|
32
|
-
"minLength": 1
|
|
33
|
-
},
|
|
34
|
-
"changed_files": {
|
|
35
|
-
"type": "array",
|
|
36
|
-
"items": {
|
|
37
|
-
"type": "string"
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
"verification": {
|
|
41
|
-
"type": "array",
|
|
42
|
-
"items": {
|
|
43
|
-
"type": "object",
|
|
44
|
-
"additionalProperties": false,
|
|
45
|
-
"required": [
|
|
46
|
-
"command",
|
|
47
|
-
"status",
|
|
48
|
-
"details"
|
|
49
|
-
],
|
|
50
|
-
"properties": {
|
|
51
|
-
"command": {
|
|
52
|
-
"type": "string",
|
|
53
|
-
"minLength": 1
|
|
54
|
-
},
|
|
55
|
-
"status": {
|
|
56
|
-
"type": "string",
|
|
57
|
-
"enum": [
|
|
58
|
-
"passed",
|
|
59
|
-
"failed",
|
|
60
|
-
"not_run"
|
|
61
|
-
]
|
|
62
|
-
},
|
|
63
|
-
"details": {
|
|
64
|
-
"type": "string",
|
|
65
|
-
"minLength": 1
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
"blockers": {
|
|
71
|
-
"type": "array",
|
|
72
|
-
"items": {
|
|
73
|
-
"type": "string"
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
"integration_notes": {
|
|
77
|
-
"type": "array",
|
|
78
|
-
"items": {
|
|
79
|
-
"type": "string"
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
"residual_signals": {
|
|
83
|
-
"type": "array",
|
|
84
|
-
"items": {
|
|
85
|
-
"type": "string"
|
|
86
|
-
}
|
|
87
|
-
},
|
|
88
|
-
"proposed_follow_up_tasks": {
|
|
89
|
-
"type": "array",
|
|
90
|
-
"items": {
|
|
91
|
-
"type": "object",
|
|
92
|
-
"additionalProperties": false,
|
|
93
|
-
"required": [
|
|
94
|
-
"title",
|
|
95
|
-
"kind",
|
|
96
|
-
"objective",
|
|
97
|
-
"owned_paths",
|
|
98
|
-
"depends_on",
|
|
99
|
-
"reason"
|
|
100
|
-
],
|
|
101
|
-
"properties": {
|
|
102
|
-
"title": {
|
|
103
|
-
"type": "string",
|
|
104
|
-
"minLength": 1
|
|
105
|
-
},
|
|
106
|
-
"kind": {
|
|
107
|
-
"type": "string",
|
|
108
|
-
"enum": [
|
|
109
|
-
"analysis",
|
|
110
|
-
"edit",
|
|
111
|
-
"verify",
|
|
112
|
-
"integration",
|
|
113
|
-
"cleanup",
|
|
114
|
-
"planning"
|
|
115
|
-
]
|
|
116
|
-
},
|
|
117
|
-
"objective": {
|
|
118
|
-
"type": "string",
|
|
119
|
-
"minLength": 1
|
|
120
|
-
},
|
|
121
|
-
"owned_paths": {
|
|
122
|
-
"type": "array",
|
|
123
|
-
"items": {
|
|
124
|
-
"type": "string"
|
|
125
|
-
}
|
|
126
|
-
},
|
|
127
|
-
"depends_on": {
|
|
128
|
-
"type": "array",
|
|
129
|
-
"items": {
|
|
130
|
-
"type": "string",
|
|
131
|
-
"pattern": "^T[0-9]{3,9}$"
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
"reason": {
|
|
135
|
-
"type": "string",
|
|
136
|
-
"minLength": 1
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
Validation Header Bump Helper
|
|
2
|
-
================================
|
|
3
|
-
|
|
4
|
-
Purpose
|
|
5
|
-
- Reduce manual drift by automating the header section of the durable OTRO validation guide.
|
|
6
|
-
- Fields managed:
|
|
7
|
-
- `Status:` (optional replacement or token update)
|
|
8
|
-
- `Last updated:` (always refreshed)
|
|
9
|
-
|
|
10
|
-
Script
|
|
11
|
-
- Path: `.codex/skills/otro/scripts/bump_validation_header.py`
|
|
12
|
-
- Default target file:
|
|
13
|
-
`.codex/skills/otro/validation/validation.md`
|
|
14
|
-
|
|
15
|
-
Local usage
|
|
16
|
-
- Dry‑run preview:
|
|
17
|
-
`python .codex/skills/otro/scripts/bump_validation_header.py --dry-run`
|
|
18
|
-
- Replace both fields explicitly:
|
|
19
|
-
`python .codex/skills/otro/scripts/bump_validation_header.py --status "loop 20 complete" --task-id T900123 --timezone Asia/Seoul`
|
|
20
|
-
- Only refresh date and add/update task token:
|
|
21
|
-
`python .codex/skills/otro/scripts/bump_validation_header.py --task-id T900123`
|
|
22
|
-
|
|
23
|
-
GitHub Actions (manual)
|
|
24
|
-
- Workflow: `.github/workflows/bump_validation_header.yml`
|
|
25
|
-
- Trigger via “Run workflow” and optionally supply:
|
|
26
|
-
- `status` (without the leading `Status:`)
|
|
27
|
-
- `task_id` (e.g., `T900123`)
|
|
28
|
-
- `path` (override target path if needed)
|
|
29
|
-
|
|
30
|
-
Commit policy
|
|
31
|
-
- The workflow commits only when the file actually changes, with message:
|
|
32
|
-
`docs(validation): bump header [skip ci]`
|
|
33
|
-
- It targets the branch used to trigger the workflow (typ. `main`).
|
|
34
|
-
|
|
35
|
-
Notes
|
|
36
|
-
- Timezone defaults to `Asia/Seoul` to match the template's SDD/admin validation docs.
|
|
37
|
-
- If the `Status:` line already contains a `(Txxxxxx)` token, providing `--task-id` replaces it; otherwise the token is appended.
|
|
38
|
-
- If the `Last updated:` key is missing, it is inserted immediately after the `Status:` line.
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Small helper to bump the header of validation.md.
|
|
4
|
-
|
|
5
|
-
Updates the following keys near the top of the file:
|
|
6
|
-
- "Status:" (optional replacement)
|
|
7
|
-
- "Last updated:" (always refreshed)
|
|
8
|
-
|
|
9
|
-
Usage (local):
|
|
10
|
-
python .codex/skills/otro/scripts/bump_validation_header.py \
|
|
11
|
-
--file .codex/skills/otro/validation/validation.md \
|
|
12
|
-
--status "loop 20 complete" --task-id T900123 --timezone Asia/Seoul
|
|
13
|
-
|
|
14
|
-
Workflow usage: see .github/workflows/bump_validation_header.yml
|
|
15
|
-
|
|
16
|
-
Notes:
|
|
17
|
-
- If --status is omitted, the existing Status line is preserved.
|
|
18
|
-
- If --task-id is provided without --status, the helper replaces or appends
|
|
19
|
-
the trailing (Txxxxxx) token in the existing Status line.
|
|
20
|
-
- The Last updated line is set to YYYY-MM-DD (TZNAME), defaulting to Asia/Seoul.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
from __future__ import annotations
|
|
24
|
-
|
|
25
|
-
import argparse
|
|
26
|
-
import os
|
|
27
|
-
import re
|
|
28
|
-
from dataclasses import dataclass
|
|
29
|
-
from datetime import datetime, timezone
|
|
30
|
-
|
|
31
|
-
try:
|
|
32
|
-
from zoneinfo import ZoneInfo # Python 3.9+
|
|
33
|
-
except Exception: # pragma: no cover
|
|
34
|
-
ZoneInfo = None # type: ignore
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
HEADER_STATUS_RE = re.compile(r"^Status:\s*(?P<value>.*)$")
|
|
38
|
-
HEADER_UPDATED_RE = re.compile(r"^Last updated:\s*(?P<value>.*)$")
|
|
39
|
-
TASK_TOKEN_RE = re.compile(r"\(T\d+\)")
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
@dataclass
|
|
43
|
-
class HeaderUpdate:
|
|
44
|
-
line_no: int
|
|
45
|
-
old: str
|
|
46
|
-
new: str
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def _now_str(tz_name: str) -> str:
|
|
50
|
-
tz_display = tz_name
|
|
51
|
-
if ZoneInfo is not None:
|
|
52
|
-
try:
|
|
53
|
-
tz = ZoneInfo(tz_name)
|
|
54
|
-
now = datetime.now(tz)
|
|
55
|
-
except Exception:
|
|
56
|
-
now = datetime.now(timezone.utc)
|
|
57
|
-
tz_display = "UTC"
|
|
58
|
-
else:
|
|
59
|
-
now = datetime.utcnow().replace(tzinfo=timezone.utc)
|
|
60
|
-
tz_display = "UTC"
|
|
61
|
-
return f"{now.strftime('%Y-%m-%d')} ({tz_display})"
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def bump_header(
|
|
65
|
-
path: str,
|
|
66
|
-
status: str | None,
|
|
67
|
-
task_id: str | None,
|
|
68
|
-
tz_name: str,
|
|
69
|
-
) -> list[HeaderUpdate]:
|
|
70
|
-
with open(path, "r", encoding="utf-8") as f:
|
|
71
|
-
lines = f.readlines()
|
|
72
|
-
|
|
73
|
-
updates: list[HeaderUpdate] = []
|
|
74
|
-
|
|
75
|
-
# Search only the early header area (first 60 lines) for safety.
|
|
76
|
-
header_limit = min(60, len(lines))
|
|
77
|
-
status_idx = updated_idx = None
|
|
78
|
-
|
|
79
|
-
for i in range(header_limit):
|
|
80
|
-
if status_idx is None and HEADER_STATUS_RE.match(lines[i]):
|
|
81
|
-
status_idx = i
|
|
82
|
-
if updated_idx is None and HEADER_UPDATED_RE.match(lines[i]):
|
|
83
|
-
updated_idx = i
|
|
84
|
-
if status_idx is not None and updated_idx is not None:
|
|
85
|
-
break
|
|
86
|
-
|
|
87
|
-
# Compute new Status line text if requested or task_id given.
|
|
88
|
-
if status_idx is not None:
|
|
89
|
-
m = HEADER_STATUS_RE.match(lines[status_idx])
|
|
90
|
-
assert m
|
|
91
|
-
current_status = m.group("value").strip()
|
|
92
|
-
new_status = current_status
|
|
93
|
-
if status is not None:
|
|
94
|
-
new_status = status.strip()
|
|
95
|
-
if task_id and not TASK_TOKEN_RE.search(new_status):
|
|
96
|
-
new_status = f"{new_status} ({task_id})"
|
|
97
|
-
elif task_id:
|
|
98
|
-
# Replace trailing (Txxxx) if present; else append.
|
|
99
|
-
if TASK_TOKEN_RE.search(current_status):
|
|
100
|
-
new_status = TASK_TOKEN_RE.sub(f"({task_id})", current_status)
|
|
101
|
-
else:
|
|
102
|
-
new_status = f"{current_status} ({task_id})"
|
|
103
|
-
|
|
104
|
-
if new_status != current_status:
|
|
105
|
-
new_line = f"Status: {new_status}\n"
|
|
106
|
-
updates.append(HeaderUpdate(status_idx + 1, lines[status_idx], new_line))
|
|
107
|
-
lines[status_idx] = new_line
|
|
108
|
-
|
|
109
|
-
# Always update Last updated
|
|
110
|
-
updated_value = _now_str(tz_name)
|
|
111
|
-
if updated_idx is not None:
|
|
112
|
-
old_line = lines[updated_idx]
|
|
113
|
-
new_line = f"Last updated: {updated_value}\n"
|
|
114
|
-
if old_line != new_line:
|
|
115
|
-
updates.append(HeaderUpdate(updated_idx + 1, old_line, new_line))
|
|
116
|
-
lines[updated_idx] = new_line
|
|
117
|
-
else:
|
|
118
|
-
# If no Last updated, insert after Status (or line 2)
|
|
119
|
-
insert_at = (status_idx + 1) if status_idx is not None else 1
|
|
120
|
-
new_line = f"Last updated: {updated_value}\n"
|
|
121
|
-
lines.insert(insert_at, new_line)
|
|
122
|
-
updates.append(HeaderUpdate(insert_at + 1, "<insert>", new_line))
|
|
123
|
-
|
|
124
|
-
if updates:
|
|
125
|
-
with open(path, "w", encoding="utf-8") as f:
|
|
126
|
-
f.writelines(lines)
|
|
127
|
-
|
|
128
|
-
return updates
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
def main() -> int:
|
|
132
|
-
parser = argparse.ArgumentParser(description="Bump validation.md header fields")
|
|
133
|
-
parser.add_argument(
|
|
134
|
-
"--file",
|
|
135
|
-
default=(
|
|
136
|
-
".codex/skills/otro/validation/validation.md"
|
|
137
|
-
),
|
|
138
|
-
help="Path to validation.md",
|
|
139
|
-
)
|
|
140
|
-
parser.add_argument("--status", help="New Status value (without leading 'Status:')")
|
|
141
|
-
parser.add_argument("--task-id", dest="task_id", help="Task ID like T900123")
|
|
142
|
-
parser.add_argument(
|
|
143
|
-
"--timezone",
|
|
144
|
-
default=os.environ.get("VALIDATION_TZ", "Asia/Seoul"),
|
|
145
|
-
help="IANA timezone name for Last updated (default: Asia/Seoul)",
|
|
146
|
-
)
|
|
147
|
-
parser.add_argument(
|
|
148
|
-
"--dry-run",
|
|
149
|
-
action="store_true",
|
|
150
|
-
help="Show what would change but do not modify the file",
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
args = parser.parse_args()
|
|
154
|
-
|
|
155
|
-
if args.dry_run:
|
|
156
|
-
# Copy to a temp buffer to preview without persisting.
|
|
157
|
-
with open(args.file, "r", encoding="utf-8") as f:
|
|
158
|
-
original = f.read()
|
|
159
|
-
updates = bump_header(args.file, args.status, args.task_id, args.timezone)
|
|
160
|
-
# Restore original content after preview.
|
|
161
|
-
with open(args.file, "w", encoding="utf-8") as f:
|
|
162
|
-
f.write(original)
|
|
163
|
-
else:
|
|
164
|
-
updates = bump_header(args.file, args.status, args.task_id, args.timezone)
|
|
165
|
-
|
|
166
|
-
if not updates:
|
|
167
|
-
print("No header changes were necessary.")
|
|
168
|
-
return 0
|
|
169
|
-
|
|
170
|
-
print("Updated lines:")
|
|
171
|
-
for u in updates:
|
|
172
|
-
old = u.old.rstrip("\n")
|
|
173
|
-
new = u.new.rstrip("\n")
|
|
174
|
-
print(f" {u.line_no}: '{old}' -> '{new}'")
|
|
175
|
-
return 0
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if __name__ == "__main__":
|
|
179
|
-
raise SystemExit(main())
|