@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
package/AGENTS.md
CHANGED
|
@@ -21,6 +21,7 @@ This repository enables users to install and run a curated set of reusable agent
|
|
|
21
21
|
- Users can research the latest completed market week and produce a PDF watchlist of tradeable instruments for the coming week.
|
|
22
22
|
- Users can turn a marked weekly finance PDF into a concise evidence-based financial event report.
|
|
23
23
|
- Users can install Apollo Toolkit through npm or npx and interactively choose one or more target skill directories to populate with copied skills.
|
|
24
|
+
- Users can run bundled helper tools through `apltk tools` and direct `apltk <tool>` commands for selected packaged skill scripts.
|
|
24
25
|
- Users can design and implement new features through a spec-first workflow.
|
|
25
26
|
- Users can generate shared feature planning artifacts for approval-gated workflows, including parallel multi-spec batches coordinated through one batch-level `coordination.md`.
|
|
26
27
|
- Users can convert text or documents into audio files with subtitle timelines.
|
|
@@ -64,6 +65,8 @@ This repository enables users to install and run a curated set of reusable agent
|
|
|
64
65
|
- `npm test` - 執行 Node 測試套件。
|
|
65
66
|
- `node bin/apollo-toolkit.js` - 直接從倉庫啟動 Apollo Toolkit CLI。
|
|
66
67
|
- `node bin/apollo-toolkit.js codex openclaw trae` - 以非互動方式將技能安裝到指定目標。
|
|
68
|
+
- `node bin/apollo-toolkit.js tools` - 列出 Apollo Toolkit 內建 CLI 工具。
|
|
69
|
+
- `node bin/apollo-toolkit.js filter-logs app.log --start 2026-03-24T10:00:00Z` - 透過內建工具包裝器執行技能腳本。
|
|
67
70
|
- `python3 scripts/validate_skill_frontmatter.py` - 驗證所有頂層技能 `SKILL.md` 的 frontmatter。
|
|
68
71
|
- `python3 scripts/validate_openai_agent_config.py` - 驗證所有技能 `agents/openai.yaml` 設定。
|
|
69
72
|
- `./scripts/install_skills.sh codex` - 用本地安裝腳本把技能安裝到 Codex 目錄。
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,23 @@ All notable changes to this repository are documented in this file.
|
|
|
7
7
|
### Changed
|
|
8
8
|
- None yet.
|
|
9
9
|
|
|
10
|
+
## [v3.0.1] - 2026-04-19
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Strengthen `jupiter-development` so Jupiter program registries are treated as discovery and observability inputs rather than automatic signing allowlists, preserving fail-closed local transaction grammar for wallet flows.
|
|
14
|
+
- Strengthen `scheduled-runtime-health-check` and `systematic-debug` so bounded runtime follow-ups compare only complete like-for-like run artifacts, derive missing-business-event causes from structured funnels, and report per-stage latency instead of vague wall-clock duration.
|
|
15
|
+
|
|
16
|
+
## [v3.0.0] - 2026-04-18
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- Add bundled `apltk` tool dispatch so packaged skill scripts can be listed with `apltk tools` and executed directly through `apltk <tool> ...`.
|
|
20
|
+
- Update skill and repository docs to prefer bundled `apltk` tool commands over direct script paths for log filtering, spec generation, KaTeX rendering, audio generation, error-book rendering, GitHub issue publishing, and related helpers.
|
|
21
|
+
- Harden `open-github-issue` with `--payload-file` and `@file` support so Markdown-rich fields containing backticks or shell metacharacters survive CLI invocation without shell corruption.
|
|
22
|
+
- Skip Python tests that require optional media/PDF modules when those dependencies are unavailable so release CI stays aligned with the repository's optional tooling contract.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- Add `lib/tool-runner.js` plus Node and Python regression tests that cover bundled tool discovery, CLI dispatch, safe wrapper behavior, and new helper entrypoints.
|
|
26
|
+
|
|
10
27
|
## [v2.14.23] - 2026-04-18
|
|
11
28
|
|
|
12
29
|
### Changed
|
package/README.md
CHANGED
|
@@ -77,6 +77,15 @@ apollo-toolkit
|
|
|
77
77
|
|
|
78
78
|
Global install 後,`apltk` 與 `apollo-toolkit` 都會啟動同一個 Apollo Toolkit CLI。直接執行 `apltk` 會打開互動安裝頁,並在互動模式下先檢查 npm registry 是否有新版可用;若有,CLI 會先詢問,再自動執行全域更新。
|
|
79
79
|
|
|
80
|
+
除了安裝模式之外,`apltk` 也會把技能內常用腳本暴露成簡單 CLI 工具,例如:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
apltk tools
|
|
84
|
+
apltk filter-logs app.log --start "2026-03-24T10:00:00Z"
|
|
85
|
+
apltk create-specs "Membership upgrade flow" --change-name membership-upgrade-flow
|
|
86
|
+
apltk open-github-issue --help
|
|
87
|
+
```
|
|
88
|
+
|
|
80
89
|
### Non-interactive install
|
|
81
90
|
|
|
82
91
|
```bash
|
|
@@ -21,8 +21,8 @@ This skill helps agents analyze logs end-to-end, correlate runtime signals with
|
|
|
21
21
|
- `agents/openai.yaml`: Agent interface metadata and default prompt.
|
|
22
22
|
- `references/investigation-checklist.md`: Investigation validation checklist.
|
|
23
23
|
- `references/log-signal-patterns.md`: Log signal pattern reference.
|
|
24
|
-
- `scripts/filter_logs_by_time.py`: Time-window log filtering helper
|
|
25
|
-
- `scripts/search_logs.py`: Keyword / regex search helper with optional context
|
|
24
|
+
- `scripts/filter_logs_by_time.py`: Time-window log filtering helper, exposed as `apltk filter-logs`.
|
|
25
|
+
- `scripts/search_logs.py`: Keyword / regex search helper with optional context, exposed as `apltk search-logs`.
|
|
26
26
|
- `scripts/log_cli_utils.py`: Shared timestamp parsing utilities.
|
|
27
27
|
- Dependency skill: `open-github-issue` for deterministic issue publishing.
|
|
28
28
|
|
|
@@ -72,9 +72,9 @@ When the time window is not explicitly provided, the skill should first derive a
|
|
|
72
72
|
Useful bundled commands:
|
|
73
73
|
|
|
74
74
|
```bash
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
apltk filter-logs app.log --start "2026-03-24T10:00:00Z" --end "2026-03-24T10:30:00Z"
|
|
76
|
+
apltk search-logs app.log --keyword timeout --keyword payment --mode all --after-context 3
|
|
77
|
+
apltk search-logs app.log --regex "request_id=ab12.*ERROR" --start "2026-03-24T10:00:00Z" --end "2026-03-24T10:15:00Z"
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
## Example
|
|
@@ -15,7 +15,7 @@ description: Comprehensive application log investigation workflow that reads log
|
|
|
15
15
|
## Standards
|
|
16
16
|
|
|
17
17
|
- Evidence: Use a bounded investigation window and correlate log lines with code, runtime context, and concrete identifiers.
|
|
18
|
-
- Execution: Scope the incident, use the bundled
|
|
18
|
+
- Execution: Scope the incident, use the bundled CLI tools to cut logs down by time window or search terms, build a timeline, validate candidate issues, then prioritize and optionally publish them.
|
|
19
19
|
- Quality: Separate confirmed issues from hypotheses and include time-window, log, code, impact, and confidence evidence for each report.
|
|
20
20
|
- Output: Return incident summary, confirmed issues, hypotheses, monitoring improvements, and publication status.
|
|
21
21
|
|
|
@@ -38,11 +38,11 @@ Use this skill to analyze application logs systematically with the codebase and
|
|
|
38
38
|
- If the user does not provide a trustworthy window, derive one from a concrete runtime boundary first, such as the last container restart, pod recreation, deploy start, worker boot, or first failure after a known healthy state.
|
|
39
39
|
- Prefer analyzing logs only inside that bounded window first (for example, from the last restart until now) to avoid stale logs polluting the diagnosis; widen the window only when the bounded slice cannot explain the symptom.
|
|
40
40
|
- Identify relevant identifiers (trace ID, request ID, user ID, job ID, tx hash).
|
|
41
|
-
- Use `
|
|
41
|
+
- Use `apltk filter-logs` first when the raw log set is large and the incident window can be bounded.
|
|
42
42
|
2. Build a timeline from logs
|
|
43
43
|
- Extract key events in chronological order within the chosen window: deploys, config changes, warnings, errors, retries, and recoveries.
|
|
44
44
|
- Group repeated symptoms by signature (error type, message prefix, stack frame, endpoint).
|
|
45
|
-
- Use `
|
|
45
|
+
- Use `apltk search-logs` to narrow by error signature, IDs, endpoint names, or repeated keywords before summarizing the timeline.
|
|
46
46
|
3. Correlate across context
|
|
47
47
|
- Link related log lines using identifiers and timestamps.
|
|
48
48
|
- Map stack traces and log messages to exact code locations.
|
|
@@ -83,6 +83,8 @@ Pass these fields to the dependency skill:
|
|
|
83
83
|
- `reproduction`: steps/conditions if known; otherwise leave empty
|
|
84
84
|
- `repo`: target repository in `owner/repo` format when known
|
|
85
85
|
|
|
86
|
+
If invoking the publisher CLI directly, pass these fields through `apltk open-github-issue --payload-file <json>` or `@file` inputs rather than inline shell arguments, because log evidence can contain backticks or shell metacharacters.
|
|
87
|
+
|
|
86
88
|
Issue body sections must always include these three parts:
|
|
87
89
|
|
|
88
90
|
- Chinese-language repositories: use localized equivalents of
|
|
@@ -120,7 +122,7 @@ Use this structure in responses:
|
|
|
120
122
|
|
|
121
123
|
- `references/investigation-checklist.md`: Step-by-step checklist for evidence-driven log investigations.
|
|
122
124
|
- `references/log-signal-patterns.md`: Common log signatures, likely causes, validation hints, and false-positive guards.
|
|
123
|
-
- `scripts/filter_logs_by_time.py`: Filter raw logs to a bounded incident window from files or stdin
|
|
124
|
-
- `scripts/search_logs.py`: Search logs by keyword or regex with optional time-window filtering and context lines
|
|
125
|
+
- `scripts/filter_logs_by_time.py`: Filter raw logs to a bounded incident window from files or stdin; exposed as `apltk filter-logs`.
|
|
126
|
+
- `scripts/search_logs.py`: Search logs by keyword or regex with optional time-window filtering and context lines; exposed as `apltk search-logs`.
|
|
125
127
|
- `scripts/log_cli_utils.py`: Shared timestamp parsing and stdin/file iteration utilities for the bundled log scripts.
|
|
126
128
|
- Dependency skill: `open-github-issue` for deterministic issue publishing with auth fallback and README language detection.
|
|
Binary file
|
|
@@ -45,13 +45,13 @@ Persist durable user preferences from recent Codex conversations into reusable,
|
|
|
45
45
|
Extract the recent conversations:
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
|
|
48
|
+
apltk extract-codex-conversations --lookback-minutes 1440
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
Refresh the AGENTS memory index after updating the memory files:
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
|
-
|
|
54
|
+
apltk sync-codex-memory-index --agents-file ~/.codex/AGENTS.md --memory-dir ~/.codex/memory
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
Use the bundled memory template when creating or refactoring category files:
|
|
@@ -25,8 +25,8 @@ Keep a durable, categorized memory of user preferences so future agents can quic
|
|
|
25
25
|
|
|
26
26
|
## Required Resources
|
|
27
27
|
|
|
28
|
-
- `scripts/extract_recent_conversations.py` to read the last 24 hours of Codex sessions, including archived sessions
|
|
29
|
-
- `scripts/sync_memory_index.py` to maintain a normalized memory index section at the end of `~/.codex/AGENTS.md`.
|
|
28
|
+
- `scripts/extract_recent_conversations.py` to read the last 24 hours of Codex sessions, including archived sessions, exposed as `apltk extract-codex-conversations`.
|
|
29
|
+
- `scripts/sync_memory_index.py` to maintain a normalized memory index section at the end of `~/.codex/AGENTS.md`, exposed as `apltk sync-codex-memory-index`.
|
|
30
30
|
|
|
31
31
|
## Workflow
|
|
32
32
|
|
|
@@ -35,7 +35,7 @@ Keep a durable, categorized memory of user preferences so future agents can quic
|
|
|
35
35
|
- Run:
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
|
|
38
|
+
apltk extract-codex-conversations --lookback-minutes 1440
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
- The extractor reads both `~/.codex/sessions` and `~/.codex/archived_sessions`.
|
|
@@ -97,14 +97,14 @@ User preferences about how engineering tasks should be investigated, planned, im
|
|
|
97
97
|
### 4) Refresh the AGENTS memory index at the end of `~/.codex/AGENTS.md`
|
|
98
98
|
|
|
99
99
|
- First inspect `~/.codex/AGENTS.md` and mirror its existing language in the memory section instructions.
|
|
100
|
-
- After updating memory files, run `
|
|
100
|
+
- After updating memory files, run `apltk sync-codex-memory-index` to rewrite the managed section at the end of the file.
|
|
101
101
|
- The section must do both of these things explicitly:
|
|
102
102
|
- instruct future agents to review the index before starting work
|
|
103
103
|
- instruct future agents to update the matching memory files and refresh the index when a new category appears
|
|
104
104
|
- Example command in English AGENTS files:
|
|
105
105
|
|
|
106
106
|
```bash
|
|
107
|
-
|
|
107
|
+
apltk sync-codex-memory-index \
|
|
108
108
|
--agents-file ~/.codex/AGENTS.md \
|
|
109
109
|
--memory-dir ~/.codex/memory \
|
|
110
110
|
--section-title "## User Memory Index" \
|
|
@@ -43,14 +43,15 @@ def run_extractor(
|
|
|
43
43
|
archived_dir: Path | None = None,
|
|
44
44
|
*extra_args: str,
|
|
45
45
|
) -> str:
|
|
46
|
+
effective_archived_dir = archived_dir or (sessions_dir.parent / "__isolated_archived_sessions__")
|
|
46
47
|
cmd = [
|
|
47
48
|
sys.executable,
|
|
48
49
|
str(SCRIPT_PATH),
|
|
49
50
|
"--sessions-dir",
|
|
50
51
|
str(sessions_dir),
|
|
52
|
+
"--archived-sessions-dir",
|
|
53
|
+
str(effective_archived_dir),
|
|
51
54
|
]
|
|
52
|
-
if archived_dir is not None:
|
|
53
|
-
cmd.extend(["--archived-sessions-dir", str(archived_dir)])
|
|
54
55
|
cmd.extend(extra_args)
|
|
55
56
|
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
|
56
57
|
return result.stdout
|
|
@@ -41,7 +41,7 @@ This skill extracts the latest conversations from `~/.codex/sessions` and `~/.co
|
|
|
41
41
|
Run the extractor:
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
|
-
|
|
44
|
+
apltk extract-skill-conversations --lookback-minutes 1440
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
- If output is `NO_RECENT_CONVERSATIONS`, no action is required.
|
|
@@ -25,7 +25,7 @@ Extract recent conversations, identify reusable lessons, and convert those lesso
|
|
|
25
25
|
|
|
26
26
|
## Required Resources
|
|
27
27
|
|
|
28
|
-
- `scripts/extract_recent_conversations.py` for deterministic session extraction
|
|
28
|
+
- `scripts/extract_recent_conversations.py` for deterministic session extraction, exposed as `apltk extract-skill-conversations`.
|
|
29
29
|
- `$skill-creator` for all skill creation/update work.
|
|
30
30
|
|
|
31
31
|
## Workflow
|
|
@@ -35,7 +35,7 @@ Extract recent conversations, identify reusable lessons, and convert those lesso
|
|
|
35
35
|
- Run:
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
|
|
38
|
+
apltk extract-skill-conversations --lookback-minutes 1440
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
- If output is exactly `NO_RECENT_CONVERSATIONS`, stop immediately and report that no action is required.
|
|
@@ -43,14 +43,15 @@ def run_extractor(
|
|
|
43
43
|
archived_dir: Path | None = None,
|
|
44
44
|
*extra_args: str,
|
|
45
45
|
) -> str:
|
|
46
|
+
effective_archived_dir = archived_dir or (sessions_dir.parent / "__isolated_archived_sessions__")
|
|
46
47
|
cmd = [
|
|
47
48
|
sys.executable,
|
|
48
49
|
str(SCRIPT_PATH),
|
|
49
50
|
"--sessions-dir",
|
|
50
51
|
str(sessions_dir),
|
|
52
|
+
"--archived-sessions-dir",
|
|
53
|
+
str(effective_archived_dir),
|
|
51
54
|
]
|
|
52
|
-
if archived_dir is not None:
|
|
53
|
-
cmd.extend(["--archived-sessions-dir", str(archived_dir)])
|
|
54
55
|
cmd.extend(extra_args)
|
|
55
56
|
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
|
56
57
|
return result.stdout
|
package/docs-to-voice/README.md
CHANGED
|
@@ -39,7 +39,7 @@ Two modes are supported:
|
|
|
39
39
|
### 1) say mode
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
|
|
42
|
+
apltk docs-to-voice \
|
|
43
43
|
--project-dir "/path/to/project" \
|
|
44
44
|
--mode say \
|
|
45
45
|
--text "Hello, this is a voice synthesis test."
|
|
@@ -48,13 +48,13 @@ python3 scripts/docs_to_voice.py \
|
|
|
48
48
|
### 2) api mode (Model Studio)
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
|
-
|
|
51
|
+
apltk docs-to-voice \
|
|
52
52
|
--project-dir "/path/to/project" \
|
|
53
53
|
--mode api \
|
|
54
54
|
--text "Hello, this is a qwen3-tts test."
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
> Compatibility note: `scripts/docs_to_voice.sh` still works
|
|
57
|
+
> Compatibility note: `scripts/docs_to_voice.sh` still works, while `apltk docs-to-voice` is the preferred entrypoint.
|
|
58
58
|
|
|
59
59
|
## `.env` settings
|
|
60
60
|
|
package/docs-to-voice/SKILL.md
CHANGED
|
@@ -15,13 +15,13 @@ description: Convert text and document content into audio files and sentence-lev
|
|
|
15
15
|
## Standards
|
|
16
16
|
|
|
17
17
|
- Evidence: Confirm `project_dir`, input source, mode, and environment-backed settings before generation.
|
|
18
|
-
- Execution: Use `
|
|
18
|
+
- Execution: Use `apltk docs-to-voice` to write audio plus matching timeline and subtitle files under `project_dir/audio/{project_name}/`.
|
|
19
19
|
- Quality: Respect mode-specific options, sentence splitting rules, and post-process requirements such as `ffmpeg` for speed changes.
|
|
20
20
|
- Output: Return the absolute output audio path together with the generated `.timeline.json` and `.srt` companions.
|
|
21
21
|
|
|
22
22
|
## Overview
|
|
23
23
|
|
|
24
|
-
Use `
|
|
24
|
+
Use `apltk docs-to-voice` to convert raw text or text files into audio and always save under:
|
|
25
25
|
|
|
26
26
|
`project_dir/audio/{project_name}/`
|
|
27
27
|
|
|
@@ -69,7 +69,7 @@ Modes:
|
|
|
69
69
|
|
|
70
70
|
## Script Reference
|
|
71
71
|
|
|
72
|
-
`
|
|
72
|
+
`apltk docs-to-voice` flags:
|
|
73
73
|
|
|
74
74
|
- `--project-dir` (required)
|
|
75
75
|
- `--project-name` (optional)
|
|
@@ -104,4 +104,4 @@ Environment variables:
|
|
|
104
104
|
- `api` mode: confirm `command -v python3` and valid `DASHSCOPE_API_KEY`.
|
|
105
105
|
- Long-text chunk merge (especially AIFF output): recommend `command -v ffmpeg`.
|
|
106
106
|
- If output exists, use `--force` or a new `--output-name`.
|
|
107
|
-
- `scripts/docs_to_voice.sh` is kept as a compatibility wrapper for existing workflows
|
|
107
|
+
- `scripts/docs_to_voice.sh` is kept as a compatibility wrapper for existing workflows, but prefer `apltk docs-to-voice`.
|
|
Binary file
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
import tempfile
|
|
10
|
+
import unittest
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
SCRIPT_PATH = Path(__file__).resolve().parents[1] / "scripts" / "docs_to_voice.py"
|
|
15
|
+
SHELL_SCRIPT_PATH = Path(__file__).resolve().parents[1] / "scripts" / "docs_to_voice.sh"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class DocsToVoiceShellWrapperTests(unittest.TestCase):
|
|
19
|
+
def test_shell_wrapper_execs_python_script_with_same_arguments(self) -> None:
|
|
20
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
21
|
+
capture_path = Path(temp_dir) / "argv.json"
|
|
22
|
+
fake_python = Path(temp_dir) / "python3"
|
|
23
|
+
fake_python.write_text(
|
|
24
|
+
f"#!{sys.executable}\n"
|
|
25
|
+
"import json, os, sys\n"
|
|
26
|
+
"with open(os.environ['CAPTURE_PATH'], 'w', encoding='utf-8') as handle:\n"
|
|
27
|
+
" json.dump(sys.argv[1:], handle)\n",
|
|
28
|
+
encoding="utf-8",
|
|
29
|
+
)
|
|
30
|
+
fake_python.chmod(0o755)
|
|
31
|
+
|
|
32
|
+
env = dict(os.environ)
|
|
33
|
+
env["PATH"] = f"{temp_dir}:{env['PATH']}"
|
|
34
|
+
env["CAPTURE_PATH"] = str(capture_path)
|
|
35
|
+
|
|
36
|
+
result = subprocess.run(
|
|
37
|
+
["bash", str(SHELL_SCRIPT_PATH), "--input", "notes.md", "--project-dir", "/tmp/project"],
|
|
38
|
+
capture_output=True,
|
|
39
|
+
text=True,
|
|
40
|
+
env=env,
|
|
41
|
+
check=False,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
self.assertEqual(result.returncode, 0, result.stderr)
|
|
45
|
+
argv = json.loads(capture_path.read_text(encoding="utf-8"))
|
|
46
|
+
self.assertEqual(argv[0], str(SCRIPT_PATH))
|
|
47
|
+
self.assertEqual(argv[1:], ["--input", "notes.md", "--project-dir", "/tmp/project"])
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
if __name__ == "__main__":
|
|
51
|
+
unittest.main()
|
package/feature-propose/SKILL.md
CHANGED
|
@@ -92,6 +92,7 @@ Load these references as needed during classification:
|
|
|
92
92
|
- `reason`: why this feature should exist now
|
|
93
93
|
- `suggested-architecture`: minimal architecture and module plan
|
|
94
94
|
- `repo`: target repository in `owner/repo` format when known
|
|
95
|
+
- If invoking the publisher CLI directly, pass accepted proposal details through `apltk open-github-issue --payload-file <json>` or `@file` inputs rather than inline shell arguments so Markdown and code identifiers remain literal.
|
|
95
96
|
- Reuse the returned `mode`, `issue_url`, and `publish_error` in the response.
|
|
96
97
|
- After the related feature is implemented, remove that feature entry from `## Accepted Feature Proposals` in `AGENTS.md`.
|
|
97
98
|
- Remove only implemented items; keep unimplemented accepted items untouched.
|
package/generate-spec/README.md
CHANGED
|
@@ -36,11 +36,9 @@ A shared planning skill for feature work. It centralizes creation and maintenanc
|
|
|
36
36
|
## Quick start
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
|
-
SKILL_ROOT=/path/to/generate-spec
|
|
40
39
|
WORKSPACE_ROOT=/path/to/target-project
|
|
41
|
-
|
|
40
|
+
apltk create-specs "Membership upgrade flow" \
|
|
42
41
|
--change-name membership-upgrade-flow \
|
|
43
|
-
--template-dir "$SKILL_ROOT/references/templates" \
|
|
44
42
|
--output-dir "$WORKSPACE_ROOT/docs/plans"
|
|
45
43
|
```
|
|
46
44
|
|
|
@@ -58,11 +56,10 @@ docs/plans/<today>/membership-upgrade-flow/
|
|
|
58
56
|
Parallel batch output:
|
|
59
57
|
|
|
60
58
|
```bash
|
|
61
|
-
|
|
59
|
+
apltk create-specs "Membership write path" \
|
|
62
60
|
--change-name membership-write-path \
|
|
63
61
|
--batch-name membership-cutover \
|
|
64
62
|
--with-coordination \
|
|
65
|
-
--template-dir "$SKILL_ROOT/references/templates" \
|
|
66
63
|
--output-dir "$WORKSPACE_ROOT/docs/plans"
|
|
67
64
|
```
|
|
68
65
|
|
|
@@ -89,6 +86,6 @@ docs/plans/<today>/membership-cutover/
|
|
|
89
86
|
|
|
90
87
|
## Notes
|
|
91
88
|
|
|
92
|
-
- `scripts/...` and `references/...`
|
|
89
|
+
- `scripts/...` and `references/...` remain skill-folder paths when you need the raw assets, but `apltk create-specs` is the preferred command surface.
|
|
93
90
|
- The generator replaces `[YYYY-MM-DD]`, `[Feature Name]`, `[功能名稱]`, `[change_name]`, and `[batch_name]` placeholders.
|
|
94
91
|
- If a batch split produces specs that must land in a functional sequence, or still leaves unresolved shared-file collisions, re-slice the work so each spec becomes independently implementable, testable, mergeable, and parallel-safe before coding starts.
|
package/generate-spec/SKILL.md
CHANGED
|
@@ -50,9 +50,8 @@ Own the shared planning-doc lifecycle for feature work so other skills can reuse
|
|
|
50
50
|
- Treat any unresolved shared-file collision, overlapping ownership, or incompatible contract change across spec sets as a planning bug to resolve before approval, not an implementation-time surprise.
|
|
51
51
|
- If two candidate spec sets would still need to edit the same non-additive surface, either merge them into one spec set or record a concrete ownership split plus additive-only rule that makes parallel work safe.
|
|
52
52
|
- Use:
|
|
53
|
-
- `SKILL_ROOT=<path_to_generate-spec_skill>`
|
|
54
53
|
- `WORKSPACE_ROOT=<target_project_root>`
|
|
55
|
-
- `
|
|
54
|
+
- `apltk create-specs "<feature_name>" --change-name <kebab-case> --output-dir "$WORKSPACE_ROOT/docs/plans"`
|
|
56
55
|
- For parallel multi-spec generation, also use:
|
|
57
56
|
- `--batch-name <kebab-case-batch-name>`
|
|
58
57
|
- `--with-coordination`
|
|
@@ -170,7 +169,7 @@ Own the shared planning-doc lifecycle for feature work so other skills can reuse
|
|
|
170
169
|
|
|
171
170
|
## References
|
|
172
171
|
|
|
173
|
-
- `scripts/create-specs`: shared planning file generator
|
|
172
|
+
- `scripts/create-specs`: shared planning file generator, exposed as `apltk create-specs`.
|
|
174
173
|
- `references/templates/spec.md`: BDD requirement template.
|
|
175
174
|
- `references/templates/tasks.md`: task breakdown template.
|
|
176
175
|
- `references/templates/checklist.md`: behavior-to-test alignment template.
|
|
Binary file
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib.machinery
|
|
6
|
+
import importlib.util
|
|
7
|
+
import io
|
|
8
|
+
import random
|
|
9
|
+
import re
|
|
10
|
+
import string
|
|
11
|
+
import sys
|
|
12
|
+
import tempfile
|
|
13
|
+
import unittest
|
|
14
|
+
from datetime import date
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from unittest.mock import patch
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
SCRIPT_PATH = Path(__file__).resolve().parents[1] / "scripts" / "create-specs"
|
|
20
|
+
LOADER = importlib.machinery.SourceFileLoader("create_specs", str(SCRIPT_PATH))
|
|
21
|
+
SPEC = importlib.util.spec_from_loader("create_specs", LOADER)
|
|
22
|
+
MODULE = importlib.util.module_from_spec(SPEC)
|
|
23
|
+
SPEC.loader.exec_module(MODULE)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class FixedDate(date):
|
|
27
|
+
@classmethod
|
|
28
|
+
def today(cls) -> "FixedDate":
|
|
29
|
+
return cls(2026, 4, 18)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CreateSpecsTests(unittest.TestCase):
|
|
33
|
+
def test_slugify_property_keeps_safe_slug_shape(self) -> None:
|
|
34
|
+
alphabet = string.ascii_letters + string.digits + string.punctuation + " \t中文"
|
|
35
|
+
generator = random.Random(20260418)
|
|
36
|
+
|
|
37
|
+
for _ in range(250):
|
|
38
|
+
raw = "".join(generator.choice(alphabet) for _ in range(generator.randint(0, 40)))
|
|
39
|
+
slug = MODULE._slugify(raw)
|
|
40
|
+
with self.subTest(raw=raw, slug=slug):
|
|
41
|
+
self.assertEqual(slug, slug.lower())
|
|
42
|
+
self.assertNotIn("--", slug)
|
|
43
|
+
self.assertFalse(slug.startswith("-"))
|
|
44
|
+
self.assertFalse(slug.endswith("-"))
|
|
45
|
+
self.assertRegex(slug, r"^[a-z0-9-]*$")
|
|
46
|
+
|
|
47
|
+
def test_render_replaces_all_placeholders(self) -> None:
|
|
48
|
+
rendered = MODULE._render(
|
|
49
|
+
content=(
|
|
50
|
+
"[YYYY-MM-DD]\n"
|
|
51
|
+
"[Feature Name]\n"
|
|
52
|
+
"[功能名稱]\n"
|
|
53
|
+
"[change_name]\n"
|
|
54
|
+
"[batch_name]\n"
|
|
55
|
+
),
|
|
56
|
+
today="2026-04-18",
|
|
57
|
+
feature_name="Batch-safe planner",
|
|
58
|
+
change_name="batch-safe-planner",
|
|
59
|
+
batch_name="parallel-batch",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
self.assertIn("2026-04-18", rendered)
|
|
63
|
+
self.assertEqual(rendered.count("Batch-safe planner"), 2)
|
|
64
|
+
self.assertIn("batch-safe-planner", rendered)
|
|
65
|
+
self.assertIn("parallel-batch", rendered)
|
|
66
|
+
|
|
67
|
+
def test_main_creates_spec_files_and_coordination_file(self) -> None:
|
|
68
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
69
|
+
root = Path(temp_dir)
|
|
70
|
+
template_dir = root / "templates"
|
|
71
|
+
output_dir = root / "docs" / "plans"
|
|
72
|
+
template_dir.mkdir(parents=True)
|
|
73
|
+
|
|
74
|
+
for name in MODULE.TEMPLATE_FILENAMES:
|
|
75
|
+
(template_dir / name).write_text(
|
|
76
|
+
f"{name} [YYYY-MM-DD] [Feature Name] [change_name] [batch_name]\n",
|
|
77
|
+
encoding="utf-8",
|
|
78
|
+
)
|
|
79
|
+
(template_dir / MODULE.COORDINATION_TEMPLATE).write_text(
|
|
80
|
+
"coordination [YYYY-MM-DD] [Feature Name] [change_name] [batch_name]\n",
|
|
81
|
+
encoding="utf-8",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
argv = [
|
|
85
|
+
"create-specs",
|
|
86
|
+
"My Feature",
|
|
87
|
+
"--batch-name",
|
|
88
|
+
"parallel-batch",
|
|
89
|
+
"--with-coordination",
|
|
90
|
+
"--output-dir",
|
|
91
|
+
str(output_dir),
|
|
92
|
+
"--template-dir",
|
|
93
|
+
str(template_dir),
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
with patch.object(MODULE, "date", FixedDate), patch.object(sys, "argv", argv), patch(
|
|
97
|
+
"sys.stdout", new_callable=io.StringIO
|
|
98
|
+
) as stdout:
|
|
99
|
+
exit_code = MODULE.main()
|
|
100
|
+
|
|
101
|
+
self.assertEqual(exit_code, 0)
|
|
102
|
+
change_root = output_dir / "2026-04-18" / "parallel-batch" / "my-feature"
|
|
103
|
+
for name in MODULE.TEMPLATE_FILENAMES:
|
|
104
|
+
content = (change_root / name).read_text(encoding="utf-8")
|
|
105
|
+
self.assertIn("2026-04-18", content)
|
|
106
|
+
self.assertIn("My Feature", content)
|
|
107
|
+
self.assertIn("my-feature", content)
|
|
108
|
+
self.assertIn("parallel-batch", content)
|
|
109
|
+
|
|
110
|
+
coordination = output_dir / "2026-04-18" / "parallel-batch" / "coordination.md"
|
|
111
|
+
self.assertTrue(coordination.is_file())
|
|
112
|
+
self.assertIn(str(coordination), stdout.getvalue())
|
|
113
|
+
|
|
114
|
+
def test_main_rejects_existing_files_without_force(self) -> None:
|
|
115
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
116
|
+
root = Path(temp_dir)
|
|
117
|
+
template_dir = root / "templates"
|
|
118
|
+
output_dir = root / "docs" / "plans"
|
|
119
|
+
template_dir.mkdir(parents=True)
|
|
120
|
+
for name in MODULE.TEMPLATE_FILENAMES:
|
|
121
|
+
(template_dir / name).write_text("template\n", encoding="utf-8")
|
|
122
|
+
|
|
123
|
+
existing_root = output_dir / "2026-04-18" / "existing-change"
|
|
124
|
+
existing_root.mkdir(parents=True)
|
|
125
|
+
(existing_root / "spec.md").write_text("existing\n", encoding="utf-8")
|
|
126
|
+
|
|
127
|
+
argv = [
|
|
128
|
+
"create-specs",
|
|
129
|
+
"Existing Change",
|
|
130
|
+
"--change-name",
|
|
131
|
+
"existing-change",
|
|
132
|
+
"--output-dir",
|
|
133
|
+
str(output_dir),
|
|
134
|
+
"--template-dir",
|
|
135
|
+
str(template_dir),
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
with patch.object(MODULE, "date", FixedDate), patch.object(sys, "argv", argv):
|
|
139
|
+
with self.assertRaises(SystemExit) as context:
|
|
140
|
+
MODULE.main()
|
|
141
|
+
|
|
142
|
+
self.assertIn("Files already exist", str(context.exception))
|
|
143
|
+
|
|
144
|
+
def test_main_requires_explicit_ascii_change_name_when_slug_is_empty(self) -> None:
|
|
145
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
146
|
+
template_dir = Path(temp_dir) / "templates"
|
|
147
|
+
template_dir.mkdir(parents=True)
|
|
148
|
+
for name in MODULE.TEMPLATE_FILENAMES:
|
|
149
|
+
(template_dir / name).write_text("template\n", encoding="utf-8")
|
|
150
|
+
|
|
151
|
+
argv = [
|
|
152
|
+
"create-specs",
|
|
153
|
+
"功能名稱",
|
|
154
|
+
"--template-dir",
|
|
155
|
+
str(template_dir),
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
with patch.object(sys, "argv", argv):
|
|
159
|
+
with self.assertRaises(SystemExit) as context:
|
|
160
|
+
MODULE.main()
|
|
161
|
+
|
|
162
|
+
self.assertIn("Unable to build change_name", str(context.exception))
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
if __name__ == "__main__":
|
|
166
|
+
unittest.main()
|
|
@@ -66,6 +66,10 @@ Implement Jupiter-backed Solana features safely by following the current officia
|
|
|
66
66
|
- Treat Jupiter token and price data as curated but evolving.
|
|
67
67
|
- Tokens V2 responses can change as Jupiter improves the schema.
|
|
68
68
|
- Price V3 intentionally withholds unreliable prices.
|
|
69
|
+
- Treat Jupiter routing program metadata as discovery data, not signer policy.
|
|
70
|
+
- Official program-label mappings such as `program-id-to-label` may help build observability, drift detection, and review queues for newly observed router programs.
|
|
71
|
+
- Do not automatically convert a Jupiter-maintained program list into a signing allowlist for wallet, hot-wallet, or `/swap-to-sol` style flows.
|
|
72
|
+
- Keep local transaction grammar fail-closed around allowed program classes, signer/writable scope, fee/output policy, instruction discriminators, and receiver semantics; only promote newly discovered programs after the local safety contract is understood and tested.
|
|
69
73
|
- For Jupiter Lend advanced recipes, expect versioned transactions, address lookup tables, and sometimes extra compute budget.
|
|
70
74
|
- Never commit private keys. Use environment variables, wallet adapters, secure signers, or managed key systems.
|
|
71
75
|
|
|
@@ -74,6 +78,7 @@ Implement Jupiter-backed Solana features safely by following the current officia
|
|
|
74
78
|
- Confirm the base URL, auth header, and required parameters match the official docs you used.
|
|
75
79
|
- Verify that any routing, payer, referral, or fee assumptions still hold after optional parameters are added.
|
|
76
80
|
- When building transactions manually, verify quote endpoint compatibility, instruction order, compute budget, address lookup tables, and signing flow.
|
|
81
|
+
- When using Jupiter-maintained program registries, verify that registry drift handling is observability-first: record unknown program labels and route context, alert or fail closed on unsafe transaction shapes, and keep signing decisions owned by the local policy layer rather than by the remote registry response.
|
|
77
82
|
- When the task involves on-chain actions, report any remaining environment needs clearly, such as API keys, RPC endpoints, or wallet secrets that were intentionally not embedded.
|
|
78
83
|
|
|
79
84
|
## Reference Files
|