@laitszkin/apollo-toolkit 2.14.23 → 3.0.1
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/AGENTS.md +3 -0
- package/CHANGELOG.md +17 -0
- package/README.md +9 -0
- package/analyse-app-logs/README.md +5 -5
- package/analyse-app-logs/SKILL.md +7 -5
- package/analyse-app-logs/scripts/__pycache__/filter_logs_by_time.cpython-312.pyc +0 -0
- package/analyse-app-logs/scripts/__pycache__/log_cli_utils.cpython-312.pyc +0 -0
- package/analyse-app-logs/scripts/__pycache__/search_logs.cpython-312.pyc +0 -0
- package/codex/codex-memory-manager/README.md +2 -2
- package/codex/codex-memory-manager/SKILL.md +5 -5
- package/codex/codex-memory-manager/tests/test_extract_recent_conversations.py +3 -2
- package/codex/learn-skill-from-conversations/README.md +1 -1
- package/codex/learn-skill-from-conversations/SKILL.md +2 -2
- package/codex/learn-skill-from-conversations/tests/test_extract_recent_conversations.py +3 -2
- package/docs-to-voice/README.md +3 -3
- package/docs-to-voice/SKILL.md +4 -4
- package/docs-to-voice/scripts/__pycache__/docs_to_voice.cpython-312.pyc +0 -0
- package/docs-to-voice/tests/test_docs_to_voice_shell_wrapper.py +51 -0
- package/feature-propose/SKILL.md +1 -0
- package/generate-spec/README.md +3 -6
- package/generate-spec/SKILL.md +2 -3
- package/generate-spec/scripts/__pycache__/create-specscpython-312.pyc +0 -0
- package/generate-spec/tests/test_create_specs.py +166 -0
- package/jupiter-development/SKILL.md +5 -0
- package/katex/SKILL.md +3 -3
- package/katex/scripts/__pycache__/render_katex.cpython-312.pyc +0 -0
- package/katex/tests/test_render_katex.py +174 -0
- package/learning-error-book/SKILL.md +2 -2
- package/learning-error-book/tests/test_render_error_book_json_to_pdf.py +134 -0
- package/lib/cli.js +66 -0
- package/lib/tool-runner.js +214 -0
- package/maintain-project-constraints/SKILL.md +3 -3
- package/maintain-skill-catalog/SKILL.md +2 -2
- package/novel-to-short-video/SKILL.md +2 -2
- package/open-github-issue/README.md +31 -22
- package/open-github-issue/SKILL.md +54 -40
- package/open-github-issue/scripts/__pycache__/open_github_issue.cpython-312.pyc +0 -0
- package/open-github-issue/scripts/open_github_issue.py +130 -3
- package/open-github-issue/tests/test_open_github_issue.py +95 -0
- package/openai-text-to-image-storyboard/README.md +1 -1
- package/openai-text-to-image-storyboard/SKILL.md +1 -1
- package/openai-text-to-image-storyboard/tests/test_generate_storyboard_images.py +177 -0
- package/package.json +1 -1
- package/read-github-issue/SKILL.md +9 -9
- package/read-github-issue/scripts/__pycache__/find_issues.cpython-312.pyc +0 -0
- package/read-github-issue/scripts/__pycache__/read_issue.cpython-312.pyc +0 -0
- package/resolve-review-comments/SKILL.md +8 -8
- package/resolve-review-comments/scripts/__pycache__/review_threads.cpython-312.pyc +0 -0
- package/review-codebases/README.md +2 -0
- package/review-codebases/SKILL.md +1 -0
- package/scheduled-runtime-health-check/SKILL.md +3 -0
- package/systematic-debug/SKILL.md +3 -0
- package/text-to-short-video/README.md +1 -1
- package/text-to-short-video/SKILL.md +1 -1
- package/text-to-short-video/scripts/__pycache__/enforce_video_aspect_ratio.cpython-312.pyc +0 -0
- package/text-to-short-video/tests/test_enforce_video_aspect_ratio.py +194 -0
- package/weekly-financial-event-report/SKILL.md +2 -2
- package/weekly-financial-event-report/tests/test_extract_pdf_text_pdfkit.py +64 -0
|
@@ -70,9 +70,9 @@ Example:
|
|
|
70
70
|
## Common Commands
|
|
71
71
|
|
|
72
72
|
- `npm test` — run the repository's automated test suite.
|
|
73
|
-
- `
|
|
74
|
-
- `
|
|
75
|
-
-
|
|
73
|
+
- `apltk validate-skill-frontmatter` — validate every top-level `SKILL.md` frontmatter block.
|
|
74
|
+
- `apltk validate-openai-agent-config` — validate every `agents/openai.yaml` interface config.
|
|
75
|
+
- `apltk codex` — install the current toolkit into the local Codex skills directory.
|
|
76
76
|
|
|
77
77
|
## Core Business Flow
|
|
78
78
|
|
|
@@ -56,8 +56,8 @@ Keep a skill repository coherent when many top-level skills evolve together.
|
|
|
56
56
|
### 4) Validate the catalog after changes
|
|
57
57
|
|
|
58
58
|
- Run:
|
|
59
|
-
- `
|
|
60
|
-
- `
|
|
59
|
+
- `apltk validate-skill-frontmatter`
|
|
60
|
+
- `apltk validate-openai-agent-config`
|
|
61
61
|
- If the change touched installer or repo-discovery behavior, verify the relevant install scripts or discovery logic as well.
|
|
62
62
|
- Resolve validation failures before finishing; missing `agents/openai.yaml`, stale prompt references, and mismatched skill names are catalog bugs, not follow-up work.
|
|
63
63
|
|
|
@@ -120,7 +120,7 @@ If producing multiple short videos in one request, enforce the same 50-60 second
|
|
|
120
120
|
- Generate images with:
|
|
121
121
|
|
|
122
122
|
```bash
|
|
123
|
-
|
|
123
|
+
apltk generate-storyboard-images \
|
|
124
124
|
--project-dir "<project_dir>" \
|
|
125
125
|
--env-file ~/.codex/skills/openai-text-to-image-storyboard/.env \
|
|
126
126
|
--content-name "<content_name>" \
|
|
@@ -133,7 +133,7 @@ python ~/.codex/skills/openai-text-to-image-storyboard/scripts/generate_storyboa
|
|
|
133
133
|
- Generate audio + timeline + SRT:
|
|
134
134
|
|
|
135
135
|
```bash
|
|
136
|
-
|
|
136
|
+
apltk docs-to-voice \
|
|
137
137
|
--project-dir "<project_dir>" \
|
|
138
138
|
--project-name "<content_name>" \
|
|
139
139
|
--text "<loop_narration_script>"
|
|
@@ -16,7 +16,7 @@ This skill helps agents publish confirmed findings, accepted proposals, document
|
|
|
16
16
|
|
|
17
17
|
- `SKILL.md`: Main skill definition and workflow.
|
|
18
18
|
- `agents/openai.yaml`: Agent interface metadata and default prompt.
|
|
19
|
-
- `scripts/open_github_issue.py`: Deterministic issue publisher
|
|
19
|
+
- `scripts/open_github_issue.py`: Deterministic issue publisher, exposed as `apltk open-github-issue`.
|
|
20
20
|
|
|
21
21
|
## Installation
|
|
22
22
|
|
|
@@ -36,41 +36,50 @@ Invoke the skill in your prompt:
|
|
|
36
36
|
Use $open-github-issue to publish this confirmed finding or accepted proposal to GitHub.
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
The bundled
|
|
39
|
+
The bundled CLI can also be called directly:
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
cat > /tmp/open-github-issue-payload.json <<'JSON'
|
|
43
|
+
{
|
|
44
|
+
"issue_type": "problem",
|
|
45
|
+
"title": "[Log] Payment timeout spike",
|
|
46
|
+
"problem_description": "Expected Behavior (BDD)\nGiven the payment service sees transient upstream latency\nWhen the retry path runs\nThen requests should recover without user-visible failures and keep `request_id` evidence intact\n\nCurrent Behavior (BDD)\nGiven the payment service sees transient upstream latency\nWhen the retry path runs\nThen repeated timeout warnings still escalate into request failures\n\nBehavior Gap\n- Expected: retries absorb transient upstream slowness.\n- Actual: retries still end in request failures.\n- Difference/Impact: customers receive failed payment attempts during the incident window.\n\nEvidence\n- symptom: repeated timeout warnings escalated into request failures.\n- impact: payment attempts failed for end users.\n- key evidence: logs from the incident window show retries without successful recovery.",
|
|
47
|
+
"suspected_cause": "payment-api/handler.py:84 retries immediately against a slow upstream with no jitter; confidence high.",
|
|
48
|
+
"reproduction": "Not yet reliably reproducible; more runtime evidence is required."
|
|
49
|
+
}
|
|
50
|
+
JSON
|
|
51
|
+
|
|
52
|
+
apltk open-github-issue --payload-file /tmp/open-github-issue-payload.json --repo owner/repo
|
|
49
53
|
```
|
|
50
54
|
|
|
51
55
|
```bash
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
cat > /tmp/open-github-issue-payload.json <<'JSON'
|
|
57
|
+
{
|
|
58
|
+
"issue_type": "feature",
|
|
59
|
+
"title": "[Feature] Add incident timeline export",
|
|
60
|
+
"proposal": "Allow users to export incident timelines as Markdown and CSV from the incident detail page.",
|
|
61
|
+
"reason": "Support handoff to on-call engineers and postmortem writing without copy-paste.",
|
|
62
|
+
"suggested_architecture": "Add an export action in the incident UI, reuse timeline query service, and centralize renderers in a shared export module."
|
|
63
|
+
}
|
|
64
|
+
JSON
|
|
65
|
+
|
|
66
|
+
apltk open-github-issue --payload-file /tmp/open-github-issue-payload.json --repo owner/repo
|
|
59
67
|
```
|
|
60
68
|
|
|
61
69
|
```bash
|
|
62
|
-
|
|
70
|
+
apltk open-github-issue --repo owner/repo \
|
|
63
71
|
--issue-type security \
|
|
64
72
|
--title "[Security] Missing authorization check on admin export" \
|
|
65
|
-
--problem-description
|
|
73
|
+
--problem-description @/tmp/security-risk.md \
|
|
66
74
|
--severity high \
|
|
67
75
|
--affected-scope "/admin/export endpoint and exported customer data" \
|
|
68
|
-
--impact
|
|
69
|
-
--evidence
|
|
70
|
-
--suggested-action
|
|
71
|
-
--repo owner/repo
|
|
76
|
+
--impact @/tmp/security-impact.md \
|
|
77
|
+
--evidence @/tmp/security-evidence.md \
|
|
78
|
+
--suggested-action @/tmp/security-action.md
|
|
72
79
|
```
|
|
73
80
|
|
|
81
|
+
Use `--payload-file` or `@file` for Markdown-rich fields. Inline shell arguments can corrupt backticks, `$()`, quotes, and other shell metacharacters before the Python script receives them.
|
|
82
|
+
|
|
74
83
|
## Publication behavior
|
|
75
84
|
|
|
76
85
|
For each issue:
|
|
@@ -38,7 +38,8 @@ It is designed to be reusable by other skills that already know the issue title
|
|
|
38
38
|
- Preserve upstream evidence content; only localize section headers and default fallback text.
|
|
39
39
|
- Make the issue type explicit: `problem`, `feature`, `performance`, `security`, `docs`, or `observability`.
|
|
40
40
|
- For `problem` issues, describe the expected behavior and current behavior with BDD-style `Given / When / Then`, then state the behavioral difference explicitly.
|
|
41
|
-
- Prefer `
|
|
41
|
+
- Prefer the bundled `apltk open-github-issue` command when available; if it is unavailable, fall back to the packaged script with an absolute path instead of assuming `python`, relative paths, or the caller's cwd are wired correctly.
|
|
42
|
+
- Never pass Markdown-rich issue content inline when it may contain backticks, `$()`, quotes, or shell metacharacters. Write a JSON payload file or content files first, then pass `--payload-file` or `@file` references so the shell cannot perform command substitution before Python receives the text.
|
|
42
43
|
|
|
43
44
|
## Workflow
|
|
44
45
|
|
|
@@ -94,88 +95,100 @@ It is designed to be reusable by other skills that already know the issue title
|
|
|
94
95
|
|
|
95
96
|
## Deterministic command
|
|
96
97
|
|
|
97
|
-
Use the bundled script.
|
|
98
|
+
Use the bundled command. Prefer `--payload-file` for all rich issue content because inline shell arguments can corrupt Markdown code spans such as `` `symbol` `` before the script starts.
|
|
98
99
|
|
|
99
|
-
|
|
100
|
+
Safe problem issue payload:
|
|
100
101
|
|
|
101
102
|
```bash
|
|
102
|
-
|
|
103
|
+
cat > /tmp/open-github-issue-payload.json <<'JSON'
|
|
104
|
+
{
|
|
105
|
+
"issue_type": "problem",
|
|
106
|
+
"title": "[Log] <short symptom>",
|
|
107
|
+
"problem_description": "Expected Behavior (BDD)\nGiven ...\nWhen ...\nThen `literal_code_span` should remain unchanged\n\nCurrent Behavior (BDD)\nGiven ...\nWhen ...\nThen ...\n\nBehavior Gap\n- Expected: ...\n- Actual: ...\n- Difference/Impact: ...\n\nEvidence\n- symptom: ...\n- impact: ...\n- key evidence: ...",
|
|
108
|
+
"suspected_cause": "<path:line + causal chain + confidence>",
|
|
109
|
+
"reproduction": "<steps/conditions or leave empty>"
|
|
110
|
+
}
|
|
111
|
+
JSON
|
|
112
|
+
|
|
113
|
+
apltk open-github-issue --payload-file /tmp/open-github-issue-payload.json --repo <owner/repo>
|
|
103
114
|
```
|
|
104
115
|
|
|
105
|
-
|
|
116
|
+
Safe feature proposal payload:
|
|
106
117
|
|
|
107
118
|
```bash
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
119
|
+
cat > /tmp/open-github-issue-payload.json <<'JSON'
|
|
120
|
+
{
|
|
121
|
+
"issue_type": "feature",
|
|
122
|
+
"title": "[Feature] <short proposal>",
|
|
123
|
+
"proposal": "<what should be added or changed>",
|
|
124
|
+
"reason": "<why this matters now, user value, constraints>",
|
|
125
|
+
"suggested_architecture": "<modules, boundaries, implementation direction>"
|
|
126
|
+
}
|
|
127
|
+
JSON
|
|
128
|
+
|
|
129
|
+
apltk open-github-issue --payload-file /tmp/open-github-issue-payload.json --repo <owner/repo>
|
|
115
130
|
```
|
|
116
131
|
|
|
117
|
-
|
|
132
|
+
Safe individual field files are also supported:
|
|
118
133
|
|
|
119
134
|
```bash
|
|
120
|
-
|
|
121
|
-
--issue-type
|
|
122
|
-
--title "[
|
|
123
|
-
--
|
|
124
|
-
--
|
|
125
|
-
--suggested-architecture "<modules, boundaries, implementation direction>" \
|
|
126
|
-
--repo <owner/repo>
|
|
135
|
+
apltk open-github-issue --repo <owner/repo> \
|
|
136
|
+
--issue-type problem \
|
|
137
|
+
--title "[Log] <short symptom>" \
|
|
138
|
+
--problem-description @/tmp/problem-description.md \
|
|
139
|
+
--suspected-cause @/tmp/suspected-cause.md
|
|
127
140
|
```
|
|
128
141
|
|
|
129
142
|
Performance issue:
|
|
130
143
|
|
|
131
144
|
```bash
|
|
132
|
-
|
|
145
|
+
apltk open-github-issue \
|
|
133
146
|
--issue-type performance \
|
|
134
147
|
--title "[Performance] Slow dashboard query under large tenants" \
|
|
135
|
-
--problem-description
|
|
136
|
-
--impact
|
|
137
|
-
--evidence
|
|
138
|
-
--suggested-action
|
|
148
|
+
--problem-description @/tmp/performance-problem.md \
|
|
149
|
+
--impact @/tmp/performance-impact.md \
|
|
150
|
+
--evidence @/tmp/performance-evidence.md \
|
|
151
|
+
--suggested-action @/tmp/performance-action.md \
|
|
139
152
|
--repo <owner/repo>
|
|
140
153
|
```
|
|
141
154
|
|
|
142
155
|
Security issue:
|
|
143
156
|
|
|
144
157
|
```bash
|
|
145
|
-
|
|
158
|
+
apltk open-github-issue \
|
|
146
159
|
--issue-type security \
|
|
147
160
|
--title "[Security] Missing authorization check on admin export" \
|
|
148
|
-
--problem-description
|
|
161
|
+
--problem-description @/tmp/security-risk.md \
|
|
149
162
|
--severity high \
|
|
150
163
|
--affected-scope "/admin/export endpoint and exported customer data" \
|
|
151
|
-
--impact
|
|
152
|
-
--evidence
|
|
153
|
-
--suggested-action
|
|
164
|
+
--impact @/tmp/security-impact.md \
|
|
165
|
+
--evidence @/tmp/security-evidence.md \
|
|
166
|
+
--suggested-action @/tmp/security-action.md \
|
|
154
167
|
--repo <owner/repo>
|
|
155
168
|
```
|
|
156
169
|
|
|
157
170
|
Docs issue:
|
|
158
171
|
|
|
159
172
|
```bash
|
|
160
|
-
|
|
173
|
+
apltk open-github-issue \
|
|
161
174
|
--issue-type docs \
|
|
162
175
|
--title "[Docs] Deployment guide omits required Redis configuration" \
|
|
163
|
-
--problem-description
|
|
164
|
-
--evidence
|
|
165
|
-
--suggested-action
|
|
176
|
+
--problem-description @/tmp/docs-gap.md \
|
|
177
|
+
--evidence @/tmp/docs-evidence.md \
|
|
178
|
+
--suggested-action @/tmp/docs-action.md \
|
|
166
179
|
--repo <owner/repo>
|
|
167
180
|
```
|
|
168
181
|
|
|
169
182
|
Observability issue:
|
|
170
183
|
|
|
171
184
|
```bash
|
|
172
|
-
|
|
185
|
+
apltk open-github-issue \
|
|
173
186
|
--issue-type observability \
|
|
174
187
|
--title "[Observability] Missing request identifiers in payment retry logs" \
|
|
175
|
-
--problem-description
|
|
176
|
-
--impact
|
|
177
|
-
--evidence
|
|
178
|
-
--suggested-action
|
|
188
|
+
--problem-description @/tmp/observability-gap.md \
|
|
189
|
+
--impact @/tmp/observability-impact.md \
|
|
190
|
+
--evidence @/tmp/observability-evidence.md \
|
|
191
|
+
--suggested-action @/tmp/observability-action.md \
|
|
179
192
|
--repo <owner/repo>
|
|
180
193
|
```
|
|
181
194
|
|
|
@@ -198,6 +211,7 @@ When another skill depends on `open-github-issue`:
|
|
|
198
211
|
|
|
199
212
|
- Pass exactly one confirmed issue or one accepted proposal per invocation.
|
|
200
213
|
- Prepare evidence or proposal details before calling this skill; do not ask this skill to infer root cause or architecture.
|
|
214
|
+
- When invoking the CLI directly, write rich Markdown fields into a JSON payload file or `@file` inputs first; do not inline text containing backticks or shell metacharacters.
|
|
201
215
|
- For `problem` issues, pass a `problem-description` that contains `Expected Behavior (BDD)`, `Current Behavior (BDD)`, and `Behavior Gap`; the difference must be explicit, not implied.
|
|
202
216
|
- Reuse the returned `mode`, `issue_url`, and `publish_error` in the parent skill response.
|
|
203
217
|
- For accepted feature proposals, pass `--issue-type feature` plus `--proposal`, `--reason`, and `--suggested-architecture`.
|
|
@@ -205,5 +219,5 @@ When another skill depends on `open-github-issue`:
|
|
|
205
219
|
|
|
206
220
|
## Resources
|
|
207
221
|
|
|
208
|
-
- `scripts/open_github_issue.py`: Deterministic issue publishing helper with auth fallback and README language detection
|
|
222
|
+
- `scripts/open_github_issue.py`: Deterministic issue publishing helper with auth fallback and README language detection, exposed as `apltk open-github-issue`.
|
|
209
223
|
- If the helper path is unavailable or still fails for environment reasons, fall back to direct `gh issue create` or GitHub REST API publishing instead of retrying the same broken relative-path invocation.
|
|
@@ -43,6 +43,38 @@ PROBLEM_BDD_MARKER_GROUPS = (
|
|
|
43
43
|
r"行為(?:落差|差異)",
|
|
44
44
|
),
|
|
45
45
|
)
|
|
46
|
+
TEXT_FIELDS = (
|
|
47
|
+
"title",
|
|
48
|
+
"problem_description",
|
|
49
|
+
"suspected_cause",
|
|
50
|
+
"reproduction",
|
|
51
|
+
"proposal",
|
|
52
|
+
"reason",
|
|
53
|
+
"suggested_architecture",
|
|
54
|
+
"impact",
|
|
55
|
+
"evidence",
|
|
56
|
+
"suggested_action",
|
|
57
|
+
"affected_scope",
|
|
58
|
+
)
|
|
59
|
+
PAYLOAD_FIELDS = frozenset(
|
|
60
|
+
(
|
|
61
|
+
"title",
|
|
62
|
+
"issue_type",
|
|
63
|
+
"problem_description",
|
|
64
|
+
"suspected_cause",
|
|
65
|
+
"reproduction",
|
|
66
|
+
"proposal",
|
|
67
|
+
"reason",
|
|
68
|
+
"suggested_architecture",
|
|
69
|
+
"impact",
|
|
70
|
+
"evidence",
|
|
71
|
+
"suggested_action",
|
|
72
|
+
"severity",
|
|
73
|
+
"affected_scope",
|
|
74
|
+
"repo",
|
|
75
|
+
"dry_run",
|
|
76
|
+
)
|
|
77
|
+
)
|
|
46
78
|
|
|
47
79
|
|
|
48
80
|
def parse_args() -> argparse.Namespace:
|
|
@@ -52,11 +84,18 @@ def parse_args() -> argparse.Namespace:
|
|
|
52
84
|
"Auth order: gh CLI login -> GitHub token -> draft only."
|
|
53
85
|
)
|
|
54
86
|
)
|
|
55
|
-
parser.add_argument(
|
|
87
|
+
parser.add_argument(
|
|
88
|
+
"--payload-file",
|
|
89
|
+
help=(
|
|
90
|
+
"Path to a JSON payload file. Use '-' to read JSON from stdin. "
|
|
91
|
+
"CLI flags override values loaded from the payload."
|
|
92
|
+
),
|
|
93
|
+
)
|
|
94
|
+
parser.add_argument("--title", help="Issue title")
|
|
56
95
|
parser.add_argument(
|
|
57
96
|
"--issue-type",
|
|
58
97
|
choices=ISSUE_TYPES,
|
|
59
|
-
default=
|
|
98
|
+
default=None,
|
|
60
99
|
help="Structured issue type to publish.",
|
|
61
100
|
)
|
|
62
101
|
parser.add_argument(
|
|
@@ -116,6 +155,94 @@ def parse_args() -> argparse.Namespace:
|
|
|
116
155
|
return parser.parse_args()
|
|
117
156
|
|
|
118
157
|
|
|
158
|
+
def normalize_payload_key(key: str) -> str:
|
|
159
|
+
return key.replace("-", "_")
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def read_payload_file(raw_path: str) -> dict[str, object]:
|
|
163
|
+
if raw_path == "-":
|
|
164
|
+
raw_content = sys.stdin.read()
|
|
165
|
+
context = "stdin"
|
|
166
|
+
else:
|
|
167
|
+
path = Path(raw_path).expanduser()
|
|
168
|
+
raw_content = path.read_text(encoding="utf-8")
|
|
169
|
+
context = str(path)
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
payload = json.loads(raw_content)
|
|
173
|
+
except json.JSONDecodeError as exc:
|
|
174
|
+
raise SystemExit(f"Invalid JSON payload in {context}: {exc}") from exc
|
|
175
|
+
|
|
176
|
+
if not isinstance(payload, dict):
|
|
177
|
+
raise SystemExit(f"Invalid JSON payload in {context}: top-level value must be an object.")
|
|
178
|
+
|
|
179
|
+
normalized: dict[str, object] = {}
|
|
180
|
+
for raw_key, value in payload.items():
|
|
181
|
+
key = normalize_payload_key(str(raw_key))
|
|
182
|
+
if key not in PAYLOAD_FIELDS:
|
|
183
|
+
raise SystemExit(f"Unsupported payload key: {raw_key}")
|
|
184
|
+
normalized[key] = value
|
|
185
|
+
return normalized
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def payload_value_to_string(field_name: str, value: object) -> str | None:
|
|
189
|
+
if value is None:
|
|
190
|
+
return None
|
|
191
|
+
if isinstance(value, str):
|
|
192
|
+
return value
|
|
193
|
+
raise SystemExit(f"Payload field '{field_name}' must be a string or null.")
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def read_at_file_value(field_name: str, value: str | None) -> str | None:
|
|
197
|
+
if value is None:
|
|
198
|
+
return None
|
|
199
|
+
if value.startswith("@@"):
|
|
200
|
+
return value[1:]
|
|
201
|
+
if value == "@-":
|
|
202
|
+
return sys.stdin.read()
|
|
203
|
+
if value.startswith("@") and len(value) > 1:
|
|
204
|
+
path = Path(value[1:]).expanduser()
|
|
205
|
+
try:
|
|
206
|
+
return path.read_text(encoding="utf-8")
|
|
207
|
+
except OSError as exc:
|
|
208
|
+
raise SystemExit(f"Unable to read @{field_name} file {path}: {exc}") from exc
|
|
209
|
+
return value
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def hydrate_args(args: argparse.Namespace) -> argparse.Namespace:
|
|
213
|
+
payload_file = getattr(args, "payload_file", None)
|
|
214
|
+
if payload_file:
|
|
215
|
+
payload = read_payload_file(payload_file)
|
|
216
|
+
for field_name, value in payload.items():
|
|
217
|
+
current_value = getattr(args, field_name, None)
|
|
218
|
+
if field_name == "dry_run":
|
|
219
|
+
if not isinstance(value, bool):
|
|
220
|
+
raise SystemExit("Payload field 'dry_run' must be a boolean.")
|
|
221
|
+
if not current_value:
|
|
222
|
+
setattr(args, field_name, value)
|
|
223
|
+
continue
|
|
224
|
+
|
|
225
|
+
if field_name in TEXT_FIELDS:
|
|
226
|
+
value = payload_value_to_string(field_name, value)
|
|
227
|
+
elif not isinstance(value, str):
|
|
228
|
+
raise SystemExit(f"Payload field '{field_name}' must be a string.")
|
|
229
|
+
|
|
230
|
+
if current_value is None or current_value == "":
|
|
231
|
+
setattr(args, field_name, value)
|
|
232
|
+
|
|
233
|
+
if args.issue_type is None:
|
|
234
|
+
args.issue_type = ISSUE_TYPE_PROBLEM
|
|
235
|
+
if args.issue_type not in ISSUE_TYPES:
|
|
236
|
+
raise SystemExit(f"Invalid issue_type: {args.issue_type}")
|
|
237
|
+
|
|
238
|
+
for field_name in TEXT_FIELDS:
|
|
239
|
+
setattr(args, field_name, read_at_file_value(field_name, getattr(args, field_name, None)))
|
|
240
|
+
|
|
241
|
+
if not (args.title or "").strip():
|
|
242
|
+
raise SystemExit("Issue title is required. Pass --title or include title in --payload-file.")
|
|
243
|
+
return args
|
|
244
|
+
|
|
245
|
+
|
|
119
246
|
def validate_issue_content_args(args: argparse.Namespace) -> None:
|
|
120
247
|
if args.issue_type == ISSUE_TYPE_FEATURE:
|
|
121
248
|
if not (args.reason or "").strip():
|
|
@@ -497,7 +624,7 @@ def create_issue_with_token(repo: str, title: str, body: str, token: str) -> str
|
|
|
497
624
|
|
|
498
625
|
|
|
499
626
|
def main() -> int:
|
|
500
|
-
args = parse_args()
|
|
627
|
+
args = hydrate_args(parse_args())
|
|
501
628
|
validate_issue_content_args(args)
|
|
502
629
|
|
|
503
630
|
gh_authenticated = has_gh_auth()
|
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import importlib.util
|
|
6
6
|
import io
|
|
7
7
|
import json
|
|
8
|
+
import tempfile
|
|
8
9
|
import unittest
|
|
9
10
|
from argparse import Namespace
|
|
10
11
|
from pathlib import Path
|
|
@@ -22,6 +23,100 @@ class OpenGitHubIssueTests(unittest.TestCase):
|
|
|
22
23
|
with self.assertRaises(SystemExit):
|
|
23
24
|
MODULE.validate_repo("owner-only")
|
|
24
25
|
|
|
26
|
+
def test_hydrate_args_loads_payload_file_with_literal_backticks(self) -> None:
|
|
27
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
28
|
+
payload_path = Path(temp_dir) / "issue.json"
|
|
29
|
+
payload_path.write_text(
|
|
30
|
+
json.dumps(
|
|
31
|
+
{
|
|
32
|
+
"title": "[Log] backticks",
|
|
33
|
+
"issue_type": MODULE.ISSUE_TYPE_PROBLEM,
|
|
34
|
+
"problem_description": (
|
|
35
|
+
"Expected Behavior (BDD)\n"
|
|
36
|
+
"Given markdown content contains code spans\n"
|
|
37
|
+
"When the issue is published\n"
|
|
38
|
+
"Then `printf should_not_run` remains literal\n\n"
|
|
39
|
+
"Current Behavior (BDD)\n"
|
|
40
|
+
"Given markdown content contains code spans\n"
|
|
41
|
+
"When the issue is published\n"
|
|
42
|
+
"Then `printf should_not_run` can be eaten by shell quoting\n\n"
|
|
43
|
+
"Behavior Gap\n"
|
|
44
|
+
"- Expected: `printf should_not_run` survives.\n"
|
|
45
|
+
"- Actual: inline shell args are fragile.\n"
|
|
46
|
+
"- Difference/Impact: issue evidence can be corrupted.\n"
|
|
47
|
+
),
|
|
48
|
+
"suspected_cause": "Shell command substitution happens before Python receives argv.",
|
|
49
|
+
}
|
|
50
|
+
),
|
|
51
|
+
encoding="utf-8",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
args = Namespace(
|
|
55
|
+
payload_file=str(payload_path),
|
|
56
|
+
title=None,
|
|
57
|
+
issue_type=None,
|
|
58
|
+
problem_description=None,
|
|
59
|
+
suspected_cause=None,
|
|
60
|
+
reproduction=None,
|
|
61
|
+
proposal=None,
|
|
62
|
+
reason=None,
|
|
63
|
+
suggested_architecture=None,
|
|
64
|
+
impact=None,
|
|
65
|
+
evidence=None,
|
|
66
|
+
suggested_action=None,
|
|
67
|
+
severity=None,
|
|
68
|
+
affected_scope=None,
|
|
69
|
+
repo=None,
|
|
70
|
+
dry_run=False,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
hydrated = MODULE.hydrate_args(args)
|
|
74
|
+
|
|
75
|
+
self.assertEqual(hydrated.title, "[Log] backticks")
|
|
76
|
+
self.assertIn("`printf should_not_run` remains literal", hydrated.problem_description)
|
|
77
|
+
self.assertEqual(hydrated.issue_type, MODULE.ISSUE_TYPE_PROBLEM)
|
|
78
|
+
|
|
79
|
+
def test_hydrate_args_reads_at_file_text_values(self) -> None:
|
|
80
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
81
|
+
description_path = Path(temp_dir) / "description.md"
|
|
82
|
+
description_path.write_text(
|
|
83
|
+
"Expected Behavior (BDD)\n"
|
|
84
|
+
"Given a markdown file contains backticks\n"
|
|
85
|
+
"When it is loaded with @file syntax\n"
|
|
86
|
+
"Then `literal` content survives\n\n"
|
|
87
|
+
"Current Behavior (BDD)\n"
|
|
88
|
+
"Given a markdown file contains backticks\n"
|
|
89
|
+
"When inline shell args are avoided\n"
|
|
90
|
+
"Then `literal` content reaches argparse unchanged\n\n"
|
|
91
|
+
"Behavior Gap\n"
|
|
92
|
+
"- Expected: file content is safe.\n"
|
|
93
|
+
"- Actual: inline shell content is fragile.\n"
|
|
94
|
+
"- Difference/Impact: safer invocation is needed.\n",
|
|
95
|
+
encoding="utf-8",
|
|
96
|
+
)
|
|
97
|
+
args = Namespace(
|
|
98
|
+
payload_file=None,
|
|
99
|
+
title="[Log] @file",
|
|
100
|
+
issue_type=MODULE.ISSUE_TYPE_PROBLEM,
|
|
101
|
+
problem_description=f"@{description_path}",
|
|
102
|
+
suspected_cause="Use @file for rich text fields.",
|
|
103
|
+
reproduction=None,
|
|
104
|
+
proposal=None,
|
|
105
|
+
reason=None,
|
|
106
|
+
suggested_architecture=None,
|
|
107
|
+
impact=None,
|
|
108
|
+
evidence=None,
|
|
109
|
+
suggested_action=None,
|
|
110
|
+
severity=None,
|
|
111
|
+
affected_scope=None,
|
|
112
|
+
repo="owner/repo",
|
|
113
|
+
dry_run=True,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
hydrated = MODULE.hydrate_args(args)
|
|
117
|
+
|
|
118
|
+
self.assertIn("Then `literal` content survives", hydrated.problem_description)
|
|
119
|
+
|
|
25
120
|
def test_detect_issue_language_prefers_chinese_when_threshold_met(self) -> None:
|
|
26
121
|
readme = "這是一個中文專案說明。" * 10
|
|
27
122
|
|
|
@@ -55,7 +55,7 @@ OPENAI_IMAGE_MODEL=gpt-image-1
|
|
|
55
55
|
3. Run with JSON prompt file
|
|
56
56
|
|
|
57
57
|
```bash
|
|
58
|
-
|
|
58
|
+
apltk generate-storyboard-images \
|
|
59
59
|
--project-dir /path/to/project \
|
|
60
60
|
--env-file ~/.codex/skills/openai-text-to-image-storyboard/.env \
|
|
61
61
|
--content-name "1_chapter_title" \
|
|
@@ -66,7 +66,7 @@ A template is provided at:
|
|
|
66
66
|
Use JSON prompt file:
|
|
67
67
|
|
|
68
68
|
```bash
|
|
69
|
-
|
|
69
|
+
apltk generate-storyboard-images \
|
|
70
70
|
--project-dir /path/to/project \
|
|
71
71
|
--env-file ~/.codex/skills/openai-text-to-image-storyboard/.env \
|
|
72
72
|
--content-name "1_chapter_title" \
|