@laitszkin/apollo-toolkit 2.14.23 → 3.0.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 (55) hide show
  1. package/AGENTS.md +3 -0
  2. package/CHANGELOG.md +11 -0
  3. package/README.md +9 -0
  4. package/analyse-app-logs/README.md +5 -5
  5. package/analyse-app-logs/SKILL.md +7 -5
  6. package/analyse-app-logs/scripts/__pycache__/filter_logs_by_time.cpython-312.pyc +0 -0
  7. package/analyse-app-logs/scripts/__pycache__/log_cli_utils.cpython-312.pyc +0 -0
  8. package/analyse-app-logs/scripts/__pycache__/search_logs.cpython-312.pyc +0 -0
  9. package/codex/codex-memory-manager/README.md +2 -2
  10. package/codex/codex-memory-manager/SKILL.md +5 -5
  11. package/codex/codex-memory-manager/tests/test_extract_recent_conversations.py +3 -2
  12. package/codex/learn-skill-from-conversations/README.md +1 -1
  13. package/codex/learn-skill-from-conversations/SKILL.md +2 -2
  14. package/codex/learn-skill-from-conversations/tests/test_extract_recent_conversations.py +3 -2
  15. package/docs-to-voice/README.md +3 -3
  16. package/docs-to-voice/SKILL.md +4 -4
  17. package/docs-to-voice/scripts/__pycache__/docs_to_voice.cpython-312.pyc +0 -0
  18. package/docs-to-voice/tests/test_docs_to_voice_shell_wrapper.py +51 -0
  19. package/feature-propose/SKILL.md +1 -0
  20. package/generate-spec/README.md +3 -6
  21. package/generate-spec/SKILL.md +2 -3
  22. package/generate-spec/scripts/__pycache__/create-specscpython-312.pyc +0 -0
  23. package/generate-spec/tests/test_create_specs.py +166 -0
  24. package/katex/SKILL.md +3 -3
  25. package/katex/scripts/__pycache__/render_katex.cpython-312.pyc +0 -0
  26. package/katex/tests/test_render_katex.py +174 -0
  27. package/learning-error-book/SKILL.md +2 -2
  28. package/learning-error-book/tests/test_render_error_book_json_to_pdf.py +134 -0
  29. package/lib/cli.js +66 -0
  30. package/lib/tool-runner.js +214 -0
  31. package/maintain-project-constraints/SKILL.md +3 -3
  32. package/maintain-skill-catalog/SKILL.md +2 -2
  33. package/novel-to-short-video/SKILL.md +2 -2
  34. package/open-github-issue/README.md +31 -22
  35. package/open-github-issue/SKILL.md +54 -40
  36. package/open-github-issue/scripts/__pycache__/open_github_issue.cpython-312.pyc +0 -0
  37. package/open-github-issue/scripts/open_github_issue.py +130 -3
  38. package/open-github-issue/tests/test_open_github_issue.py +95 -0
  39. package/openai-text-to-image-storyboard/README.md +1 -1
  40. package/openai-text-to-image-storyboard/SKILL.md +1 -1
  41. package/openai-text-to-image-storyboard/tests/test_generate_storyboard_images.py +177 -0
  42. package/package.json +1 -1
  43. package/read-github-issue/SKILL.md +9 -9
  44. package/read-github-issue/scripts/__pycache__/find_issues.cpython-312.pyc +0 -0
  45. package/read-github-issue/scripts/__pycache__/read_issue.cpython-312.pyc +0 -0
  46. package/resolve-review-comments/SKILL.md +8 -8
  47. package/resolve-review-comments/scripts/__pycache__/review_threads.cpython-312.pyc +0 -0
  48. package/review-codebases/README.md +2 -0
  49. package/review-codebases/SKILL.md +1 -0
  50. package/text-to-short-video/README.md +1 -1
  51. package/text-to-short-video/SKILL.md +1 -1
  52. package/text-to-short-video/scripts/__pycache__/enforce_video_aspect_ratio.cpython-312.pyc +0 -0
  53. package/text-to-short-video/tests/test_enforce_video_aspect_ratio.py +194 -0
  54. package/weekly-financial-event-report/SKILL.md +2 -2
  55. package/weekly-financial-event-report/tests/test_extract_pdf_text_pdfkit.py +64 -0
@@ -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
- python ~/.codex/skills/openai-text-to-image-storyboard/scripts/generate_storyboard_images.py \
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
- python ~/.codex/skills/docs-to-voice/scripts/docs_to_voice.py \
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 script can also be called directly:
39
+ The bundled CLI can also be called directly:
40
40
 
41
41
  ```bash
42
- python scripts/open_github_issue.py \
43
- --issue-type problem \
44
- --title "[Log] Payment timeout spike" \
45
- --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\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.' \
46
- --suspected-cause "payment-api/handler.py:84 retries immediately against a slow upstream with no jitter; confidence high." \
47
- --reproduction "Not yet reliably reproducible; more runtime evidence is required." \
48
- --repo owner/repo
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
- python scripts/open_github_issue.py \
53
- --issue-type feature \
54
- --title "[Feature] Add incident timeline export" \
55
- --proposal "Allow users to export incident timelines as Markdown and CSV from the incident detail page." \
56
- --reason "Support handoff to on-call engineers and postmortem writing without copy-paste." \
57
- --suggested-architecture "Add an export action in the incident UI, reuse timeline query service, and centralize renderers in a shared export module." \
58
- --repo owner/repo
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
- python scripts/open_github_issue.py \
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 "The admin export endpoint can be reached without verifying the caller's admin role." \
73
+ --problem-description @/tmp/security-risk.md \
66
74
  --severity high \
67
75
  --affected-scope "/admin/export endpoint and exported customer data" \
68
- --impact "Unauthorized users may access privileged exports containing sensitive business data." \
69
- --evidence "Code path review and reproduced requests show the handler validates session presence but not the admin permission gate." \
70
- --suggested-action "Add explicit authorization enforcement, regression tests, and audit logging for denied access attempts." \
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 `python3` plus an absolute helper path when invoking bundled scripts; do not assume `python`, relative paths, or the caller's cwd are wired correctly.
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
- First resolve:
100
+ Safe problem issue payload:
100
101
 
101
102
  ```bash
102
- SKILL_ROOT=~/.codex/skills/open-github-issue
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
- Problem issue:
116
+ Safe feature proposal payload:
106
117
 
107
118
  ```bash
108
- python3 "$SKILL_ROOT/scripts/open_github_issue.py" \
109
- --issue-type problem \
110
- --title "[Log] <short symptom>" \
111
- --problem-description $'Expected Behavior (BDD)\nGiven ...\nWhen ...\nThen ...\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: ...' \
112
- --suspected-cause "<path:line + causal chain + confidence>" \
113
- --reproduction "<steps/conditions or leave empty>" \
114
- --repo <owner/repo>
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
- Feature proposal issue:
132
+ Safe individual field files are also supported:
118
133
 
119
134
  ```bash
120
- python3 "$SKILL_ROOT/scripts/open_github_issue.py" \
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
- --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
- python3 "$SKILL_ROOT/scripts/open_github_issue.py" \
145
+ apltk open-github-issue \
133
146
  --issue-type performance \
134
147
  --title "[Performance] Slow dashboard query under large tenants" \
135
- --problem-description "Dashboard loading time degrades sharply once tenant data exceeds current pagination assumptions." \
136
- --impact "Users wait 8-12 seconds before the page becomes interactive; this blocks support workflows." \
137
- --evidence "Profiler output, slow-query logs, and production timings all point to repeated full-table scans in the summary query path." \
138
- --suggested-action "Add bounded pagination, pre-aggregated summaries, and an index review for the offending query path." \
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
- python3 "$SKILL_ROOT/scripts/open_github_issue.py" \
158
+ apltk open-github-issue \
146
159
  --issue-type security \
147
160
  --title "[Security] Missing authorization check on admin export" \
148
- --problem-description "The admin export endpoint can be reached without verifying the caller's admin role." \
161
+ --problem-description @/tmp/security-risk.md \
149
162
  --severity high \
150
163
  --affected-scope "/admin/export endpoint and exported customer data" \
151
- --impact "Unauthorized users may access privileged exports containing sensitive business data." \
152
- --evidence "Code path review and reproduced requests show the handler validates session presence but not the admin permission gate." \
153
- --suggested-action "Add explicit authorization enforcement, regression tests, and audit logging for denied access attempts." \
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
- python3 "$SKILL_ROOT/scripts/open_github_issue.py" \
173
+ apltk open-github-issue \
161
174
  --issue-type docs \
162
175
  --title "[Docs] Deployment guide omits required Redis configuration" \
163
- --problem-description "The deployment guide does not mention the required Redis URL and worker startup order." \
164
- --evidence "README deploy steps differ from the actual compose file and runtime startup checks." \
165
- --suggested-action "Update deployment docs with required env vars, startup order, and a minimal validation checklist." \
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
- python3 "$SKILL_ROOT/scripts/open_github_issue.py" \
185
+ apltk open-github-issue \
173
186
  --issue-type observability \
174
187
  --title "[Observability] Missing request identifiers in payment retry logs" \
175
- --problem-description "Retry logs do not include stable request or trace identifiers, so multi-line failures cannot be correlated quickly." \
176
- --impact "On-call engineers cannot isolate a single failing payment flow without manual log stitching." \
177
- --evidence "Current retry log lines include endpoint and error text only; incident review required manual timestamp matching." \
178
- --suggested-action "Add request_id, trace_id, upstream target, and retry attempt fields to retry logs and dashboard facets." \
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("--title", required=True, help="Issue title")
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=ISSUE_TYPE_PROBLEM,
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
- python scripts/generate_storyboard_images.py \
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
- python ~/.codex/skills/openai-text-to-image-storyboard/scripts/generate_storyboard_images.py \
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" \