@elvis1513/auto-coding-skill 0.1.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.
- package/LICENSE +21 -0
- package/README.md +110 -0
- package/cli/assets/skill/SKILL.md +73 -0
- package/cli/assets/skill/data/templates/ENGINEERING.md +63 -0
- package/cli/assets/skill/data/templates/bridges/CLAUDE.md +1 -0
- package/cli/assets/skill/data/templates/bridges/CODEX.md +1 -0
- package/cli/assets/skill/data/templates/docs/adr/0001-adr-template.md +9 -0
- package/cli/assets/skill/data/templates/docs/autocoding/config.example.yaml +35 -0
- package/cli/assets/skill/data/templates/docs/bugs/bug-list.md +5 -0
- package/cli/assets/skill/data/templates/docs/deployment/deploy-records/_TEMPLATE-DEPLOY-RECORD.md +13 -0
- package/cli/assets/skill/data/templates/docs/deployment/deploy-runbook.md +13 -0
- package/cli/assets/skill/data/templates/docs/deployment/targets.example.yaml +23 -0
- package/cli/assets/skill/data/templates/docs/design/_TEMPLATE-DD.md +43 -0
- package/cli/assets/skill/data/templates/docs/interfaces/api-change-log.md +11 -0
- package/cli/assets/skill/data/templates/docs/interfaces/api.md +36 -0
- package/cli/assets/skill/data/templates/docs/requirements/regression-matrix.md +5 -0
- package/cli/assets/skill/data/templates/docs/reviews/_TEMPLATE-REVIEW.md +14 -0
- package/cli/assets/skill/data/templates/docs/specs/security-baseline.md +5 -0
- package/cli/assets/skill/data/templates/docs/tasks/summaries/_TEMPLATE-TASK-SUMMARY.md +50 -0
- package/cli/assets/skill/data/templates/docs/tasks/taskbook.md +33 -0
- package/cli/assets/skill/requirements.txt +2 -0
- package/cli/assets/skill/scripts/ap.py +254 -0
- package/cli/assets/skill/scripts/core.py +107 -0
- package/cli/src/index.js +127 -0
- package/cli/src/sync-assets.js +26 -0
- package/package.json +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# auto-coding-skill
|
|
2
|
+
|
|
3
|
+
Portable, framework-agnostic engineering workflow skill for:
|
|
4
|
+
|
|
5
|
+
- Claude Code
|
|
6
|
+
- Codex CLI
|
|
7
|
+
|
|
8
|
+
It enforces an end-to-end gate:
|
|
9
|
+
|
|
10
|
+
Task -> DD -> Implement -> Build/Test -> Static Analysis -> Review -> API Docs (Markdown) -> Deploy -> Smoke -> Full Regression -> Bug Log -> Summary -> Commit -> Push
|
|
11
|
+
|
|
12
|
+
## Install (npm)
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g @elvis1513/auto-coding-skill
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
If npm registry install is not available yet, install directly from GitHub:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install -g git+https://github.com/elvis1513/auto-coding-skill.git
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick start (any repo)
|
|
25
|
+
|
|
26
|
+
From your target repo root:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# install skill files for Claude
|
|
30
|
+
autocoding init --ai claude
|
|
31
|
+
|
|
32
|
+
# install skill files for Codex
|
|
33
|
+
autocoding init --ai codex
|
|
34
|
+
|
|
35
|
+
# install both
|
|
36
|
+
autocoding init --ai all
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Skill install locations:
|
|
40
|
+
|
|
41
|
+
- `.claude/skills/auto-coding-skill`
|
|
42
|
+
- `.codex/skills/auto-coding-skill`
|
|
43
|
+
|
|
44
|
+
Then initialize the project scaffold (run one path that exists):
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
python3 .claude/skills/auto-coding-skill/scripts/ap.py --repo . install
|
|
48
|
+
# or
|
|
49
|
+
python3 .codex/skills/auto-coding-skill/scripts/ap.py --repo . install
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This creates:
|
|
53
|
+
|
|
54
|
+
- `ENGINEERING.md`
|
|
55
|
+
- `docs/**` (taskbook, DD/review templates, API docs, deployment/runbook, regression matrix, bug list)
|
|
56
|
+
- `tools/autopipeline/ap.py` + `tools/autopipeline/core.py`
|
|
57
|
+
- `.gitignore` rule: `docs/deployment/targets.yaml`
|
|
58
|
+
|
|
59
|
+
## Configure project commands
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
cp docs/autocoding/config.example.yaml autocoding.config.yaml
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Edit at least:
|
|
66
|
+
|
|
67
|
+
- `commands.build`
|
|
68
|
+
- `commands.test`
|
|
69
|
+
- `commands.lint`
|
|
70
|
+
- `commands.typecheck`
|
|
71
|
+
- `commands.smoke`
|
|
72
|
+
- `commands.regression`
|
|
73
|
+
|
|
74
|
+
## Gate commands
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
python3 tools/autopipeline/ap.py run build
|
|
78
|
+
python3 tools/autopipeline/ap.py run test
|
|
79
|
+
python3 tools/autopipeline/ap.py verify-api-docs
|
|
80
|
+
python3 tools/autopipeline/ap.py check-matrix
|
|
81
|
+
python3 tools/autopipeline/ap.py gen-summary T0001-1
|
|
82
|
+
python3 tools/autopipeline/ap.py commit-push T0001-1 --msg "T0001-1: <summary>" --require-matrix
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## CLI options
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
autocoding init --ai claude|codex|all [--mode project|global] [--dest <path>] [--force]
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
When `--ai all` and `--dest` are both used, output will be:
|
|
92
|
+
|
|
93
|
+
- `<dest>/claude`
|
|
94
|
+
- `<dest>/codex`
|
|
95
|
+
|
|
96
|
+
## Publish
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
npm login
|
|
100
|
+
npm whoami
|
|
101
|
+
npm run release:check
|
|
102
|
+
npm publish --access public
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Optional automation: this repo includes `/Users/elvis/Product/auto-coding-skill/.github/workflows/npm-publish.yml`.
|
|
106
|
+
Set GitHub secret `NPM_TOKEN`, then publish a GitHub Release to auto-publish npm.
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
MIT
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: auto-coding-skill
|
|
3
|
+
description: Framework-agnostic engineering workflow skill for Claude Code and Codex CLI. Use it to scaffold docs and enforce gates from task intake to design, implementation, test, review, API docs, deployment, regression, summary, commit and push.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Auto Coding Skill (Claude + Codex Only)
|
|
7
|
+
|
|
8
|
+
This skill is portable across projects and does not depend on any specific scaffold.
|
|
9
|
+
|
|
10
|
+
## Supported clients
|
|
11
|
+
|
|
12
|
+
- Claude Code
|
|
13
|
+
- Codex CLI
|
|
14
|
+
|
|
15
|
+
## Workflow gates
|
|
16
|
+
|
|
17
|
+
Taskbook -> DD -> Implement -> Build/Test -> Static Analysis -> Review -> API Docs -> Deploy -> Smoke -> Regression Matrix (0 fail) -> Summary -> Commit -> Push
|
|
18
|
+
|
|
19
|
+
## Install into a target repo
|
|
20
|
+
|
|
21
|
+
From the target repo root:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Claude Code
|
|
25
|
+
autocoding init --ai claude
|
|
26
|
+
|
|
27
|
+
# Codex CLI
|
|
28
|
+
autocoding init --ai codex
|
|
29
|
+
|
|
30
|
+
# Both
|
|
31
|
+
autocoding init --ai all
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Initialize project scaffold
|
|
35
|
+
|
|
36
|
+
Run one of the following (depending on where the skill was installed):
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
python3 .claude/skills/auto-coding-skill/scripts/ap.py --repo . install
|
|
40
|
+
# or
|
|
41
|
+
python3 .codex/skills/auto-coding-skill/scripts/ap.py --repo . install
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
This will create `ENGINEERING.md`, `docs/**`, `tools/autopipeline/ap.py`, and update `.gitignore` with `docs/deployment/targets.yaml`.
|
|
45
|
+
|
|
46
|
+
## Configure project commands
|
|
47
|
+
|
|
48
|
+
Copy and edit config:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
cp docs/autocoding/config.example.yaml autocoding.config.yaml
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Set at least:
|
|
55
|
+
|
|
56
|
+
- `commands.build`
|
|
57
|
+
- `commands.test`
|
|
58
|
+
- `commands.lint`
|
|
59
|
+
- `commands.typecheck`
|
|
60
|
+
- `commands.smoke`
|
|
61
|
+
- `commands.regression`
|
|
62
|
+
|
|
63
|
+
## Common commands
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
python3 tools/autopipeline/ap.py run build
|
|
67
|
+
python3 tools/autopipeline/ap.py run test
|
|
68
|
+
python3 tools/autopipeline/ap.py run lint
|
|
69
|
+
python3 tools/autopipeline/ap.py verify-api-docs
|
|
70
|
+
python3 tools/autopipeline/ap.py check-matrix
|
|
71
|
+
python3 tools/autopipeline/ap.py gen-summary T0001-1
|
|
72
|
+
python3 tools/autopipeline/ap.py commit-push T0001-1 --msg "T0001-1: <summary>" --require-matrix
|
|
73
|
+
```
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# ENGINEERING.md — AutoPipeline Gates (Source of Truth)
|
|
2
|
+
|
|
3
|
+
目标:把一次任务固化为不可跳过的流水线:
|
|
4
|
+
读任务 → 写DD → 实现 → 本地测试通过 → 静态分析+Review落盘 → 更新API Markdown+接口变更清单 →
|
|
5
|
+
部署(systemd/jar) → 重启验证 → 按API Markdown全量回归 + 回归矩阵0fail →
|
|
6
|
+
记录Bug并新增自动化回归 → 任务总结落盘 → commit → push
|
|
7
|
+
|
|
8
|
+
规则:任一步骤失败或缺产物,禁止进入下一步;**未 push 视为任务未完成**,禁止开始下一个任务/子任务。
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 0. 权威输入与冲突裁决(优先级)
|
|
13
|
+
## 0.1 工程命令配置(必须)
|
|
14
|
+
|
|
15
|
+
- 构建/测试/静态分析/回归命令必须由项目提供并配置在:
|
|
16
|
+
- `autocoding.config.yaml`(推荐放 repo 根目录),或
|
|
17
|
+
- `docs/autocoding/config.yaml`
|
|
18
|
+
|
|
19
|
+
任何 gate 只允许通过这些配置的命令执行(避免把流程绑死在某个脚手架)。
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
1) ENGINEERING.md(本文件)
|
|
23
|
+
2) docs/tasks/taskbook.md
|
|
24
|
+
3) docs/design/**
|
|
25
|
+
4) docs/interfaces/api.md
|
|
26
|
+
5) docs/interfaces/api-change-log.md
|
|
27
|
+
6) docs/requirements/regression-matrix.md(必须 0 FAIL)
|
|
28
|
+
7) docs/bugs/bug-list.md(长期积累,回归必测)
|
|
29
|
+
8) docs/tasks/summaries/**(每任务一份,强制产物)
|
|
30
|
+
9) docs/deployment/**
|
|
31
|
+
10) 代码实现(不得反向覆盖 1~9)
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 1. Gate 流水线(强制、不可跳过)
|
|
36
|
+
|
|
37
|
+
Gate-1 读任务:只从 taskbook 取范围与验收;缺信息先补 taskbook
|
|
38
|
+
Gate-2 写DD:无DD禁止写代码;DD必须含 时序图/ER图/接口时序(Mermaid)
|
|
39
|
+
Gate-3 实现:严格按DD;接口变更必须同步 API Markdown
|
|
40
|
+
Gate-4 本地CI:必须通过(ci-local)
|
|
41
|
+
Gate-5 静态分析+Review:静态分析通过;docs/reviews/ 生成记录
|
|
42
|
+
Gate-6 文档:更新 api.md + 追加 api-change-log.md
|
|
43
|
+
Gate-7 部署:单机 systemd/jar;允许 root+密码;service目录固定 /usr/lib/systemd/system;只保留1份备份;失败自动回滚
|
|
44
|
+
Gate-8 重启+健康:restart后必须健康检查通过
|
|
45
|
+
Gate-9 全量回归:按 API Markdown 全量回归;回归矩阵全量 PASS(0 fail);发现问题必须写 bug-list 并新增自动化回归用例(纳入 Gate-9)
|
|
46
|
+
Gate-10 任务总结:必须生成 docs/tasks/summaries/<TASK_ID>.md(可用工具生成初稿)
|
|
47
|
+
Gate-11 完成:全门禁通过后必须 commit+push(未push=未完成)
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 2. 部署约束(来自你的固定要求)
|
|
52
|
+
|
|
53
|
+
- 允许 sshpass / 或 python 读取 targets.yaml 的 root 密码(非交互)
|
|
54
|
+
- systemd 目录固定:/usr/lib/systemd/system
|
|
55
|
+
- 只保留一个最新备份(*.bak / *.tgz.bak),失败自动回滚并重启服务
|
|
56
|
+
- 健康检查端点不固定:targets.yaml 配置 base_url + health_path
|
|
57
|
+
- 部署可更新项:app/config/、app/bin/、/usr/lib/systemd/system/*.service、jar
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 3. Repo 工具入口(建议)
|
|
62
|
+
|
|
63
|
+
统一用 `python3 tools/autopipeline/ap.py <command>` 执行。
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Follow ENGINEERING.md strictly. Source of truth: ENGINEERING.md.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Follow ENGINEERING.md strictly. Source of truth: ENGINEERING.md.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Project-specific gate commands.
|
|
2
|
+
# This file is intentionally NOT a “universal standard” — you MUST adapt commands to each repo.
|
|
3
|
+
# You can copy this to repo root as `autocoding.config.yaml` (recommended).
|
|
4
|
+
|
|
5
|
+
commands:
|
|
6
|
+
# Build / packaging must succeed (no errors)
|
|
7
|
+
build: "npm run build"
|
|
8
|
+
|
|
9
|
+
# Tests must be 0 fail
|
|
10
|
+
test: "npm test"
|
|
11
|
+
|
|
12
|
+
# TS/JS static analysis (recommend both)
|
|
13
|
+
lint: "npm run lint"
|
|
14
|
+
typecheck: "npm run typecheck"
|
|
15
|
+
|
|
16
|
+
# Optional
|
|
17
|
+
format: "npm run format"
|
|
18
|
+
|
|
19
|
+
# Post-deploy smoke check (can call curl or a node script)
|
|
20
|
+
smoke: "node ./scripts/smoke.mjs"
|
|
21
|
+
|
|
22
|
+
# Full regression (can be API regression, e2e, integration, etc.)
|
|
23
|
+
regression: "npm run test:regression"
|
|
24
|
+
|
|
25
|
+
docs:
|
|
26
|
+
# authoritative API doc (Markdown)
|
|
27
|
+
api_doc: "docs/interfaces/api.md"
|
|
28
|
+
api_change_log: "docs/interfaces/api-change-log.md"
|
|
29
|
+
taskbook: "docs/tasks/taskbook.md"
|
|
30
|
+
regression_matrix: "docs/requirements/regression-matrix.md"
|
|
31
|
+
bug_list: "docs/bugs/bug-list.md"
|
|
32
|
+
|
|
33
|
+
deployment:
|
|
34
|
+
targets_file: "docs/deployment/targets.yaml"
|
|
35
|
+
systemd_dir: "/usr/lib/systemd/system"
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Bug List(长期积累:回归必测)
|
|
2
|
+
|
|
3
|
+
| Bug ID | Date | Area | Summary | Steps to Reproduce | Expected | Actual | Severity | Status | Fix Commit/PR | Regression Test ID |
|
|
4
|
+
|---|---|---|---|---|---|---|---|---|---|---|
|
|
5
|
+
| B-0001 | YYYY-MM-DD | API | <desc> | <steps> | <expected> | <actual> | P1 | Open | | R-??? |
|
package/cli/assets/skill/data/templates/docs/deployment/deploy-records/_TEMPLATE-DEPLOY-RECORD.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Deploy Record — <Task ID> — YYYY-MM-DD
|
|
2
|
+
|
|
3
|
+
- Target host:
|
|
4
|
+
- Service name:
|
|
5
|
+
- Artifact (local):
|
|
6
|
+
- Remote jar path:
|
|
7
|
+
- Updated items: jar / config / bin / service
|
|
8
|
+
- Backup files (single latest): *.bak / *.tgz.bak
|
|
9
|
+
|
|
10
|
+
## Evidence
|
|
11
|
+
- systemctl status:
|
|
12
|
+
- smoke_test:
|
|
13
|
+
- api_regression:
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Deploy Runbook(单机 systemd / jar)
|
|
2
|
+
|
|
3
|
+
targets.yaml 必填:
|
|
4
|
+
- target.host/user/password/ssh_port
|
|
5
|
+
- service.name(systemd service 文件名)
|
|
6
|
+
- service.systemd_dir(固定:/usr/lib/systemd/system)
|
|
7
|
+
- paths.remote_*(远端目录与 jar 路径)
|
|
8
|
+
- health.base_url + health_path(可配置)
|
|
9
|
+
|
|
10
|
+
部署后必须:
|
|
11
|
+
- smoke-test
|
|
12
|
+
- api-regression
|
|
13
|
+
- 回归矩阵全量 PASS(0 fail)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
target:
|
|
2
|
+
host: "192.168.1.10"
|
|
3
|
+
user: "root"
|
|
4
|
+
password: "CHANGE_ME"
|
|
5
|
+
ssh_port: 22
|
|
6
|
+
|
|
7
|
+
service:
|
|
8
|
+
name: "myapp.service"
|
|
9
|
+
systemd_dir: "/usr/lib/systemd/system"
|
|
10
|
+
local_file: "app/systemd/myapp.service"
|
|
11
|
+
|
|
12
|
+
paths:
|
|
13
|
+
remote_app_root: "/opt/app/myapp"
|
|
14
|
+
remote_jar_path: "/opt/app/myapp/app.jar"
|
|
15
|
+
remote_config_dir: "/opt/app/myapp/config"
|
|
16
|
+
remote_bin_dir: "/opt/app/myapp/bin"
|
|
17
|
+
|
|
18
|
+
artifact:
|
|
19
|
+
local_path: ""
|
|
20
|
+
|
|
21
|
+
health:
|
|
22
|
+
base_url: "http://127.0.0.1:8080"
|
|
23
|
+
health_path: "/health"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# DD — <Task ID> <Title>
|
|
2
|
+
|
|
3
|
+
- Task ID:T0001 / T0001-1
|
|
4
|
+
- 状态:Draft | Reviewed | Approved
|
|
5
|
+
- 关联:`docs/tasks/taskbook.md` 中对应段落
|
|
6
|
+
|
|
7
|
+
## 1. 背景与目标
|
|
8
|
+
## 2. 范围与非目标
|
|
9
|
+
## 3. 现状与问题
|
|
10
|
+
## 4. 方案与取舍
|
|
11
|
+
## 5. 接口设计(与 docs/interfaces/api.md 对齐)
|
|
12
|
+
## 6. 数据设计(迁移/回滚)
|
|
13
|
+
|
|
14
|
+
## 7. 时序图(强制)
|
|
15
|
+
```mermaid
|
|
16
|
+
sequenceDiagram
|
|
17
|
+
participant C as Client
|
|
18
|
+
participant A as API
|
|
19
|
+
participant S as Service
|
|
20
|
+
participant D as DB
|
|
21
|
+
C->>A: request
|
|
22
|
+
A->>S: call
|
|
23
|
+
S->>D: query/tx
|
|
24
|
+
D-->>S: result
|
|
25
|
+
S-->>A: response
|
|
26
|
+
A-->>C: response
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 8. ER 图(强制)
|
|
30
|
+
```mermaid
|
|
31
|
+
erDiagram
|
|
32
|
+
ENTITY_A ||--o{ ENTITY_B : relates
|
|
33
|
+
ENTITY_A { string id }
|
|
34
|
+
ENTITY_B { string id string a_id }
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 9. 接口时序/调用编排(强制)
|
|
38
|
+
## 10. 测试方案(必须自动化 + 覆盖历史 bug)
|
|
39
|
+
## 11. 安全与合规
|
|
40
|
+
## 12. 性能与容量
|
|
41
|
+
## 13. 风险清单
|
|
42
|
+
## 14. 回滚方案
|
|
43
|
+
## 15. 影响面与发布计划
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# API Documentation(权威接口文档)
|
|
2
|
+
|
|
3
|
+
> 本文件为权威接口文档。任何接口新增/修改/废弃,都必须同步更新本文件,并在 `api-change-log.md` 追加记录。
|
|
4
|
+
|
|
5
|
+
## 1. 基本约定
|
|
6
|
+
- Base URL:
|
|
7
|
+
- Content-Type:
|
|
8
|
+
- 认证方式(如有):
|
|
9
|
+
- 时区/时间格式:
|
|
10
|
+
- 分页/排序(如有):
|
|
11
|
+
- 错误码规范:
|
|
12
|
+
|
|
13
|
+
## 2. Endpoint Index(目录)
|
|
14
|
+
| Method | Path | Summary | Auth | Request | Response |
|
|
15
|
+
|---|---|---|---|---|---|
|
|
16
|
+
| GET | /health | Health check | No | - | 200 OK |
|
|
17
|
+
|
|
18
|
+
## 3. Endpoints
|
|
19
|
+
|
|
20
|
+
### 3.1 GET /health
|
|
21
|
+
**Summary**: Health check
|
|
22
|
+
|
|
23
|
+
**Request**
|
|
24
|
+
- Headers: -
|
|
25
|
+
- Query: -
|
|
26
|
+
- Body: -
|
|
27
|
+
|
|
28
|
+
**Response**
|
|
29
|
+
- 200: OK
|
|
30
|
+
- Example:
|
|
31
|
+
```json
|
|
32
|
+
{"status":"ok"}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 4. Changelog
|
|
36
|
+
- See `api-change-log.md`
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Regression Matrix(回归矩阵:必须全量 PASS,0 fail)
|
|
2
|
+
|
|
3
|
+
| ID | Area | Endpoint/Feature | Test Type | Steps / Command | Expected | Status(PASS/FAIL) | Evidence |
|
|
4
|
+
|---|---|---|---|---|---|---|---|
|
|
5
|
+
| R-001 | API | GET /health | smoke | commands.smoke | 200 OK | PASS | <paste log path> |
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Review — <Task ID> — YYYY-MM-DD HH:MM
|
|
2
|
+
|
|
3
|
+
## 1. 静态分析结果(必须)
|
|
4
|
+
- Command:lint + typecheck(来自 autocoding.config.yaml)
|
|
5
|
+
- Summary:
|
|
6
|
+
- Issues:
|
|
7
|
+
- Resolved/Deferred(deferred 必须 ADR):
|
|
8
|
+
|
|
9
|
+
## 2. 代码质量
|
|
10
|
+
## 3. 测试质量(含新增回归用例)
|
|
11
|
+
## 4. 接口契约(API Markdown + change-log)
|
|
12
|
+
## 5. 安全与性能
|
|
13
|
+
## 6. 风险与回滚
|
|
14
|
+
## 7. 结论(Pass / Blocked)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Task Summary — <TASK_ID> — <Title>
|
|
2
|
+
|
|
3
|
+
- Task ID:<TASK_ID>
|
|
4
|
+
- Date:YYYY-MM-DD
|
|
5
|
+
- Scope(本次范围):
|
|
6
|
+
- Out of scope(明确未做):
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. 目标与验收结论
|
|
11
|
+
- 目标:
|
|
12
|
+
- 验收结论:PASS / FAIL(FAIL 必须说明原因与后续计划)
|
|
13
|
+
|
|
14
|
+
## 2. 变更概览(代码/配置/部署)
|
|
15
|
+
- 关键改动点:
|
|
16
|
+
- 影响模块:
|
|
17
|
+
- 兼容性影响(是否破坏兼容、迁移方案):
|
|
18
|
+
|
|
19
|
+
## 3. 接口变更(以 API Markdown 为准)
|
|
20
|
+
- 新增:
|
|
21
|
+
- 修改:
|
|
22
|
+
- 废弃:
|
|
23
|
+
- 变更记录位置:`docs/interfaces/api-change-log.md`(对应条目)
|
|
24
|
+
|
|
25
|
+
## 4. 数据变更(如有)
|
|
26
|
+
- 表/字段变更:
|
|
27
|
+
- 迁移方式:
|
|
28
|
+
- 回滚方式:
|
|
29
|
+
|
|
30
|
+
## 5. 质量门禁证据(必须可追溯)
|
|
31
|
+
- 本地CI:`ci-local`
|
|
32
|
+
- 静态分析:`static`
|
|
33
|
+
- Review 文档:`docs/reviews/<TASK_ID>-<timestamp>.md`
|
|
34
|
+
- API 文档:`docs/interfaces/api.md`
|
|
35
|
+
- 回归矩阵:`docs/requirements/regression-matrix.md`(全量 PASS,0 fail)
|
|
36
|
+
|
|
37
|
+
## 6. Bug 清单与回归用例
|
|
38
|
+
- 新增/确认的 Bug(写入 `docs/bugs/bug-list.md`):
|
|
39
|
+
- 新增自动化回归用例(引用回归矩阵ID):
|
|
40
|
+
|
|
41
|
+
## 7. 部署记录(如有部署)
|
|
42
|
+
- 部署记录:`docs/deployment/deploy-records/<TASK_ID>-YYYYMMDD.md`
|
|
43
|
+
- systemd/service/config/bin 变更(若有):
|
|
44
|
+
|
|
45
|
+
## 8. 风险与回滚
|
|
46
|
+
- 风险:
|
|
47
|
+
- 回滚点(备份/回滚步骤):
|
|
48
|
+
|
|
49
|
+
## 9. 后续行动(如有)
|
|
50
|
+
- TODO:
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Taskbook(任务本:持续续写,权威任务来源)
|
|
2
|
+
|
|
3
|
+
规则:
|
|
4
|
+
1) 所有任务都写在本文件(持续续写,不另起任务文件)
|
|
5
|
+
2) 允许拆子任务:每个子任务也必须走全流程(DD→实现→测试→review→接口文档→部署→回归→总结→commit→push)
|
|
6
|
+
3) 每个任务必须有明确验收与证据(日志/报告/文件路径)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Task T0001 — <Title>
|
|
11
|
+
- 状态:Planned | Designing | Implementing | Testing | Reviewing | Deploying | Done
|
|
12
|
+
- 范围(In scope):
|
|
13
|
+
- 非目标(Out of scope):
|
|
14
|
+
- 验收标准(必须可执行):
|
|
15
|
+
- 依赖/约束:
|
|
16
|
+
- 子任务:
|
|
17
|
+
- [ ] T0001-1 <subtask>
|
|
18
|
+
- [ ] T0001-2 <subtask>
|
|
19
|
+
|
|
20
|
+
### 证据(完成后填写)
|
|
21
|
+
- DD:`docs/design/T0001-<slug>.md`
|
|
22
|
+
- Review:`docs/reviews/T0001-YYYYMMDD-HHMM.md`
|
|
23
|
+
- API 文档:`docs/interfaces/api.md`
|
|
24
|
+
- API Change Log:`docs/interfaces/api-change-log.md`
|
|
25
|
+
- 本地CI:粘贴摘要或给出文件路径
|
|
26
|
+
- 部署记录(如部署):`docs/deployment/deploy-records/T0001-YYYYMMDD.md`
|
|
27
|
+
- 回归矩阵:`docs/requirements/regression-matrix.md`(全量PASS)
|
|
28
|
+
- Bug清单(如有):`docs/bugs/bug-list.md`
|
|
29
|
+
- 任务总结(强制):`docs/tasks/summaries/T0001.md`
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
继续在下方追加任务(不要删除历史记录)
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""AutoPipeline Pro Max - repo automation CLI (python)"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
import datetime as _dt
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Optional, List
|
|
11
|
+
|
|
12
|
+
from core import APError, ensure_git_repo, copy_tree, run, load_yaml, find_config, run_shell
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _skill_root() -> Path:
|
|
16
|
+
return Path(__file__).resolve().parent.parent
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def cmd_install(args: argparse.Namespace) -> None:
|
|
20
|
+
repo = Path(args.repo).resolve()
|
|
21
|
+
templates = _skill_root() / "data" / "templates"
|
|
22
|
+
|
|
23
|
+
copy_tree(templates / "docs", repo / "docs")
|
|
24
|
+
copy_tree(templates / "ENGINEERING.md", repo / "ENGINEERING.md")
|
|
25
|
+
|
|
26
|
+
if args.bridges:
|
|
27
|
+
copy_tree(templates / "bridges", repo)
|
|
28
|
+
|
|
29
|
+
tools_dir = repo / "tools" / "autopipeline"
|
|
30
|
+
tools_dir.mkdir(parents=True, exist_ok=True)
|
|
31
|
+
copy_tree(Path(__file__).resolve(), tools_dir / "ap.py")
|
|
32
|
+
copy_tree(Path(__file__).resolve().parent / "core.py", tools_dir / "core.py")
|
|
33
|
+
|
|
34
|
+
gi = repo / ".gitignore"
|
|
35
|
+
secret_line = "docs/deployment/targets.yaml"
|
|
36
|
+
if gi.exists():
|
|
37
|
+
txt = gi.read_text(encoding="utf-8")
|
|
38
|
+
if secret_line not in txt:
|
|
39
|
+
gi.write_text(txt.rstrip() + "\n" + secret_line + "\n", encoding="utf-8")
|
|
40
|
+
else:
|
|
41
|
+
gi.write_text(secret_line + "\n", encoding="utf-8")
|
|
42
|
+
|
|
43
|
+
print(f"[install] OK: scaffold installed into {repo}")
|
|
44
|
+
print("[install] Next: cp docs/deployment/targets.example.yaml docs/deployment/targets.yaml (fill secrets locally)")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _infer_title(repo: Path, task_id: str) -> str:
|
|
48
|
+
taskbook = repo / "docs" / "tasks" / "taskbook.md"
|
|
49
|
+
if not taskbook.exists():
|
|
50
|
+
return "<Title>"
|
|
51
|
+
for line in taskbook.read_text(encoding="utf-8").splitlines():
|
|
52
|
+
if line.strip().startswith("## Task ") and task_id in line:
|
|
53
|
+
for sep in ["—", "-"]:
|
|
54
|
+
if sep in line:
|
|
55
|
+
return line.split(sep, 1)[1].strip()
|
|
56
|
+
return "<Title>"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def cmd_gen_summary(args: argparse.Namespace) -> None:
|
|
60
|
+
repo = Path(args.repo).resolve()
|
|
61
|
+
ensure_git_repo(repo)
|
|
62
|
+
task_id = args.task_id
|
|
63
|
+
|
|
64
|
+
out_dir = repo / "docs" / "tasks" / "summaries"
|
|
65
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
66
|
+
out_file = out_dir / f"{task_id}.md"
|
|
67
|
+
if out_file.exists() and not args.force:
|
|
68
|
+
raise APError(f"Summary already exists: {out_file} (use --force to overwrite)")
|
|
69
|
+
|
|
70
|
+
title = _infer_title(repo, task_id)
|
|
71
|
+
date = _dt.date.today().isoformat()
|
|
72
|
+
|
|
73
|
+
staged = run(["git", "diff", "--cached", "--name-only"], cwd=repo, check=False).stdout.strip()
|
|
74
|
+
unstaged = run(["git", "diff", "--name-only"], cwd=repo, check=False).stdout.strip()
|
|
75
|
+
status = run(["git", "status", "--porcelain=v1"], cwd=repo, check=False).stdout.strip()
|
|
76
|
+
|
|
77
|
+
content = f"""# Task Summary — {task_id} — {title}
|
|
78
|
+
|
|
79
|
+
- Task ID:{task_id}
|
|
80
|
+
- Date:{date}
|
|
81
|
+
- Scope(本次范围):TODO
|
|
82
|
+
- Out of scope(明确未做):TODO
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 1. 目标与验收结论
|
|
87
|
+
- 目标:TODO
|
|
88
|
+
- 验收结论:PASS / FAIL — TODO
|
|
89
|
+
|
|
90
|
+
## 2. 变更概览(代码/配置/部署)
|
|
91
|
+
### Git change snapshot
|
|
92
|
+
- Staged files:
|
|
93
|
+
{('- ' + staged.replace('\n','\n- ')) if staged else '- (none)'}
|
|
94
|
+
- Unstaged files:
|
|
95
|
+
{('- ' + unstaged.replace('\n','\n- ')) if unstaged else '- (none)'}
|
|
96
|
+
- Status:
|
|
97
|
+
```text
|
|
98
|
+
{status}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## 3. 接口变更(以 API Markdown 为准)
|
|
102
|
+
- 变更记录位置:`docs/interfaces/api-change-log.md`
|
|
103
|
+
|
|
104
|
+
## 5. 质量门禁证据(必须可追溯)
|
|
105
|
+
- 本地CI:TODO
|
|
106
|
+
- 静态分析:TODO
|
|
107
|
+
- Review 文档:TODO
|
|
108
|
+
- DD 文档:TODO
|
|
109
|
+
- 回归矩阵:`docs/requirements/regression-matrix.md`(全量 PASS,0 fail)
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
out_file.write_text(content, encoding="utf-8")
|
|
113
|
+
print(f"[gen-summary] OK: {out_file}")
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def cmd_check_matrix(args: argparse.Namespace) -> None:
|
|
117
|
+
repo = Path(args.repo).resolve()
|
|
118
|
+
matrix = repo / "docs" / "requirements" / "regression-matrix.md"
|
|
119
|
+
if not matrix.exists():
|
|
120
|
+
raise APError(f"Matrix not found: {matrix}")
|
|
121
|
+
|
|
122
|
+
rows = 0
|
|
123
|
+
fail = []
|
|
124
|
+
for line in matrix.read_text(encoding="utf-8").splitlines():
|
|
125
|
+
s = line.strip()
|
|
126
|
+
if not s.startswith("|"):
|
|
127
|
+
continue
|
|
128
|
+
cols = [c.strip() for c in s.split("|")[1:-1]]
|
|
129
|
+
if len(cols) < 8:
|
|
130
|
+
continue
|
|
131
|
+
rid = cols[0]
|
|
132
|
+
status = cols[6].upper()
|
|
133
|
+
if rid in {"ID", "---"} or rid.startswith("---"):
|
|
134
|
+
continue
|
|
135
|
+
if not rid.startswith("R-"):
|
|
136
|
+
continue
|
|
137
|
+
rows += 1
|
|
138
|
+
if status != "PASS":
|
|
139
|
+
fail.append((rid, status or "(empty)"))
|
|
140
|
+
|
|
141
|
+
if rows == 0:
|
|
142
|
+
raise APError(f"No regression rows found in matrix: {matrix}")
|
|
143
|
+
|
|
144
|
+
if fail:
|
|
145
|
+
msg = "\n".join([f"- {rid}: {st}" for rid, st in fail])
|
|
146
|
+
raise APError(f"Regression matrix not 0-fail:\n{msg}")
|
|
147
|
+
|
|
148
|
+
print("[check-matrix] OK (0-fail)")
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _load_cfg(repo: Path) -> dict:
|
|
152
|
+
cfg_path = find_config(repo)
|
|
153
|
+
return load_yaml(cfg_path)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def cmd_run(args: argparse.Namespace) -> None:
|
|
157
|
+
"""
|
|
158
|
+
Run a configured gate command:
|
|
159
|
+
build | test | lint | typecheck | format | smoke | regression
|
|
160
|
+
Commands are read from autocoding.config.yaml (or docs/autocoding/config.yaml).
|
|
161
|
+
"""
|
|
162
|
+
repo = Path(args.repo).resolve()
|
|
163
|
+
cfg = _load_cfg(repo)
|
|
164
|
+
commands = (cfg.get("commands") or {})
|
|
165
|
+
name = args.name
|
|
166
|
+
if name not in commands:
|
|
167
|
+
raise APError(f"Command not configured: commands.{name}. Edit autocoding.config.yaml. Available: {', '.join(commands.keys()) or '(none)'}")
|
|
168
|
+
cmd = str(commands[name])
|
|
169
|
+
print(f"[run] {name}: {cmd}")
|
|
170
|
+
run_shell(cmd, cwd=repo)
|
|
171
|
+
print(f"[run] OK: {name}")
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def cmd_verify_api_docs(args: argparse.Namespace) -> None:
|
|
175
|
+
"""Ensure API markdown doc and change-log exist."""
|
|
176
|
+
repo = Path(args.repo).resolve()
|
|
177
|
+
cfg = _load_cfg(repo)
|
|
178
|
+
docs = (cfg.get("docs") or {})
|
|
179
|
+
api_doc = Path(repo, str(docs.get("api_doc", "docs/interfaces/api.md")))
|
|
180
|
+
change_log = Path(repo, str(docs.get("api_change_log", "docs/interfaces/api-change-log.md")))
|
|
181
|
+
missing = [p for p in [api_doc, change_log] if not p.exists()]
|
|
182
|
+
if missing:
|
|
183
|
+
raise APError("Missing API docs: " + ", ".join([str(p) for p in missing]))
|
|
184
|
+
print(f"[verify-api-docs] OK: {api_doc} + {change_log}")
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def cmd_commit_push(args: argparse.Namespace) -> None:
|
|
188
|
+
repo = Path(args.repo).resolve()
|
|
189
|
+
ensure_git_repo(repo)
|
|
190
|
+
|
|
191
|
+
task_id = args.task_id
|
|
192
|
+
msg = args.msg
|
|
193
|
+
|
|
194
|
+
summary = repo / "docs" / "tasks" / "summaries" / f"{task_id}.md"
|
|
195
|
+
if not summary.exists():
|
|
196
|
+
raise APError(
|
|
197
|
+
f"Task summary missing: {summary}\n"
|
|
198
|
+
f"Generate: python3 tools/autopipeline/ap.py gen-summary {task_id}"
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
if args.require_matrix:
|
|
202
|
+
cmd_check_matrix(argparse.Namespace(repo=str(repo)))
|
|
203
|
+
|
|
204
|
+
run(["git", "add", "-A"], cwd=repo)
|
|
205
|
+
diff = run(["git", "diff", "--cached", "--name-only"], cwd=repo).stdout.strip()
|
|
206
|
+
if not diff:
|
|
207
|
+
raise APError("Nothing to commit.")
|
|
208
|
+
|
|
209
|
+
run(["git", "commit", "-m", msg], cwd=repo)
|
|
210
|
+
run(["git", "push"], cwd=repo)
|
|
211
|
+
print("[commit-push] OK")
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def main(argv: Optional[List[str]] = None) -> int:
|
|
215
|
+
p = argparse.ArgumentParser(prog="autopipeline")
|
|
216
|
+
p.add_argument("--repo", default=".")
|
|
217
|
+
sp = p.add_subparsers(dest="cmd", required=True)
|
|
218
|
+
|
|
219
|
+
s = sp.add_parser("install")
|
|
220
|
+
s.add_argument("--bridges", action="store_true")
|
|
221
|
+
s.set_defaults(func=cmd_install)
|
|
222
|
+
|
|
223
|
+
s = sp.add_parser("gen-summary")
|
|
224
|
+
s.add_argument("task_id")
|
|
225
|
+
s.add_argument("--force", action="store_true")
|
|
226
|
+
s.set_defaults(func=cmd_gen_summary)
|
|
227
|
+
|
|
228
|
+
s = sp.add_parser("check-matrix")
|
|
229
|
+
s.set_defaults(func=cmd_check_matrix)
|
|
230
|
+
|
|
231
|
+
s = sp.add_parser("run")
|
|
232
|
+
s.add_argument("name")
|
|
233
|
+
s.set_defaults(func=cmd_run)
|
|
234
|
+
|
|
235
|
+
s = sp.add_parser("verify-api-docs")
|
|
236
|
+
s.set_defaults(func=cmd_verify_api_docs)
|
|
237
|
+
|
|
238
|
+
s = sp.add_parser("commit-push")
|
|
239
|
+
s.add_argument("task_id")
|
|
240
|
+
s.add_argument("--msg", required=True)
|
|
241
|
+
s.add_argument("--require-matrix", action="store_true")
|
|
242
|
+
s.set_defaults(func=cmd_commit_push)
|
|
243
|
+
|
|
244
|
+
try:
|
|
245
|
+
args = p.parse_args(argv)
|
|
246
|
+
args.func(args)
|
|
247
|
+
return 0
|
|
248
|
+
except APError as e:
|
|
249
|
+
print(f"[ERROR] {e}")
|
|
250
|
+
return 2
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
if __name__ == "__main__":
|
|
254
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""AutoPipeline Pro Max - Core helpers"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import shutil
|
|
9
|
+
import subprocess
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Iterable, Optional, Dict, Any
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
import yaml # type: ignore
|
|
15
|
+
except Exception:
|
|
16
|
+
yaml = None
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
import requests # type: ignore
|
|
20
|
+
except Exception:
|
|
21
|
+
requests = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class APError(RuntimeError):
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def run(cmd: Iterable[str], cwd: Optional[Path] = None, check: bool = True) -> subprocess.CompletedProcess:
|
|
29
|
+
p = subprocess.run(list(cmd), cwd=str(cwd) if cwd else None, text=True, capture_output=True)
|
|
30
|
+
if check and p.returncode != 0:
|
|
31
|
+
raise APError(
|
|
32
|
+
f"Command failed ({p.returncode}): {' '.join(cmd)}\nSTDOUT:\n{p.stdout}\nSTDERR:\n{p.stderr}"
|
|
33
|
+
)
|
|
34
|
+
return p
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def ensure_git_repo(repo: Path) -> None:
|
|
38
|
+
try:
|
|
39
|
+
run(["git", "rev-parse", "--is-inside-work-tree"], cwd=repo)
|
|
40
|
+
except APError as e:
|
|
41
|
+
raise APError(f"Not a git repository: {repo}\n{e}") from e
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def load_yaml(path: Path) -> Dict[str, Any]:
|
|
45
|
+
if not path.exists():
|
|
46
|
+
raise APError(f"YAML not found: {path}")
|
|
47
|
+
if yaml is None:
|
|
48
|
+
raise APError(
|
|
49
|
+
"PyYAML not installed. Install dependencies with: pip install pyyaml requests"
|
|
50
|
+
)
|
|
51
|
+
with path.open("r", encoding="utf-8") as f:
|
|
52
|
+
data = yaml.safe_load(f)
|
|
53
|
+
return data or {}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def copy_tree(src: Path, dst: Path) -> None:
|
|
57
|
+
if src.is_file():
|
|
58
|
+
if src.name == ".DS_Store" or src.name.endswith(".pyc"):
|
|
59
|
+
return
|
|
60
|
+
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
61
|
+
shutil.copy2(src, dst)
|
|
62
|
+
return
|
|
63
|
+
for root, _, files in os.walk(src):
|
|
64
|
+
root_p = Path(root)
|
|
65
|
+
if "__pycache__" in root_p.parts:
|
|
66
|
+
continue
|
|
67
|
+
rel = root_p.relative_to(src)
|
|
68
|
+
for fn in files:
|
|
69
|
+
if fn == ".DS_Store" or fn.endswith(".pyc"):
|
|
70
|
+
continue
|
|
71
|
+
s = root_p / fn
|
|
72
|
+
d = dst / rel / fn
|
|
73
|
+
d.parent.mkdir(parents=True, exist_ok=True)
|
|
74
|
+
shutil.copy2(s, d)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def http_get_status(url: str, timeout_s: int = 5) -> int:
|
|
78
|
+
if requests is None:
|
|
79
|
+
raise APError(
|
|
80
|
+
"requests not installed. Install dependencies with: pip install pyyaml requests"
|
|
81
|
+
)
|
|
82
|
+
r = requests.get(url, timeout=timeout_s)
|
|
83
|
+
return r.status_code
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def find_config(repo: Path) -> Path:
|
|
87
|
+
"""Find autocoding config file (repo root preferred)."""
|
|
88
|
+
candidates = [
|
|
89
|
+
repo / "autocoding.config.yaml",
|
|
90
|
+
repo / "docs" / "autocoding" / "config.yaml",
|
|
91
|
+
repo / "docs" / "autocoding" / "config.yml",
|
|
92
|
+
]
|
|
93
|
+
for c in candidates:
|
|
94
|
+
if c.exists():
|
|
95
|
+
return c
|
|
96
|
+
# fall back to example if exists (for first-time users)
|
|
97
|
+
ex = repo / "docs" / "autocoding" / "config.example.yaml"
|
|
98
|
+
if ex.exists():
|
|
99
|
+
return ex
|
|
100
|
+
raise APError("autocoding config not found. Create autocoding.config.yaml (recommended) or docs/autocoding/config.yaml. Template: docs/autocoding/config.example.yaml")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def run_shell(command: str, cwd: Optional[Path] = None) -> None:
|
|
104
|
+
"""Run shell command via bash -lc for cross-tool compatibility."""
|
|
105
|
+
p = subprocess.run(["bash", "-lc", command], cwd=str(cwd) if cwd else None, text=True)
|
|
106
|
+
if p.returncode != 0:
|
|
107
|
+
raise APError(f"Command failed ({p.returncode}): {command}")
|
package/cli/src/index.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
function die(msg){
|
|
8
|
+
console.error(`\n[autocoding] ERROR: ${msg}\n`);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function parseArgs(argv){
|
|
13
|
+
const args = { cmd: null, ai: null, mode: "project", dest: null, force: false };
|
|
14
|
+
const [,, cmd, ...rest] = argv;
|
|
15
|
+
args.cmd = cmd ?? "help";
|
|
16
|
+
for (let i = 0; i < rest.length; i++) {
|
|
17
|
+
const a = rest[i];
|
|
18
|
+
if (a === "--ai") args.ai = rest[++i];
|
|
19
|
+
else if (a === "--mode") args.mode = rest[++i];
|
|
20
|
+
else if (a === "--dest") args.dest = rest[++i];
|
|
21
|
+
else if (a === "--force") args.force = true;
|
|
22
|
+
else if (a === "-h" || a === "--help") args.cmd = "help";
|
|
23
|
+
}
|
|
24
|
+
return args;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function exists(p){ try { fs.accessSync(p); return true; } catch { return false; } }
|
|
28
|
+
function rmrf(p){ fs.rmSync(p, { recursive: true, force: true }); }
|
|
29
|
+
function shouldSkip(name){
|
|
30
|
+
return name === "__pycache__" || name === ".DS_Store" || name.endsWith(".pyc");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function copyDir(src, dst){
|
|
34
|
+
fs.mkdirSync(dst, { recursive: true });
|
|
35
|
+
for (const ent of fs.readdirSync(src, { withFileTypes: true })) {
|
|
36
|
+
if (shouldSkip(ent.name)) continue;
|
|
37
|
+
const s = path.join(src, ent.name);
|
|
38
|
+
const d = path.join(dst, ent.name);
|
|
39
|
+
if (ent.isDirectory()) copyDir(s, d);
|
|
40
|
+
else fs.copyFileSync(s, d);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function projectRoot(){ return process.cwd(); }
|
|
45
|
+
|
|
46
|
+
function resolveTargetDir(ai, mode, destOverride){
|
|
47
|
+
if (destOverride) return destOverride;
|
|
48
|
+
if (mode !== "project" && mode !== "global") die(`--mode must be 'project' or 'global'`);
|
|
49
|
+
|
|
50
|
+
if (ai === "claude") {
|
|
51
|
+
return mode === "project"
|
|
52
|
+
? path.join(projectRoot(), ".claude", "skills", "auto-coding-skill")
|
|
53
|
+
: path.join(os.homedir(), ".claude", "skills", "auto-coding-skill");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (ai === "codex") {
|
|
57
|
+
return mode === "project"
|
|
58
|
+
? path.join(projectRoot(), ".codex", "skills", "auto-coding-skill")
|
|
59
|
+
: path.join(os.homedir(), ".codex", "skills", "auto-coding-skill");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
die(`unknown ai: ${ai}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function ensureGitignore(projectDir){
|
|
66
|
+
const gi = path.join(projectDir, ".gitignore");
|
|
67
|
+
const line = "docs/deployment/targets.yaml";
|
|
68
|
+
if (!exists(gi)) {
|
|
69
|
+
fs.writeFileSync(gi, `${line}\n`, "utf-8");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const txt = fs.readFileSync(gi, "utf-8");
|
|
73
|
+
if (!txt.includes(line)) {
|
|
74
|
+
fs.appendFileSync(gi, (txt.endsWith("\n") ? "" : "\n") + line + "\n");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function main(){
|
|
79
|
+
const args = parseArgs(process.argv);
|
|
80
|
+
|
|
81
|
+
if (args.cmd === "help" || !args.cmd) {
|
|
82
|
+
console.log(`
|
|
83
|
+
autocoding - install auto-coding-skill (Claude Code + Codex CLI)
|
|
84
|
+
|
|
85
|
+
Usage:
|
|
86
|
+
autocoding init --ai claude|codex|all [--mode project|global] [--dest <path>] [--force]
|
|
87
|
+
|
|
88
|
+
Examples:
|
|
89
|
+
autocoding init --ai claude
|
|
90
|
+
autocoding init --ai codex
|
|
91
|
+
autocoding init --ai all --mode project
|
|
92
|
+
autocoding init --ai all --dest /tmp/skills
|
|
93
|
+
`);
|
|
94
|
+
process.exit(0);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (args.cmd !== "init") die(`unknown command: ${args.cmd}`);
|
|
98
|
+
|
|
99
|
+
const ai = (args.ai ?? "").toLowerCase();
|
|
100
|
+
const targets = ai === "all" ? ["claude", "codex"] : [ai];
|
|
101
|
+
if (!(["claude", "codex", "all"].includes(ai))) die(`--ai must be claude|codex|all`);
|
|
102
|
+
|
|
103
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
104
|
+
const assetSkill = path.resolve(here, "..", "assets", "skill");
|
|
105
|
+
if (!exists(assetSkill)) die(`missing assets at ${assetSkill}`);
|
|
106
|
+
|
|
107
|
+
const proj = projectRoot();
|
|
108
|
+
|
|
109
|
+
for (const t of targets) {
|
|
110
|
+
const dstOverride = args.dest
|
|
111
|
+
? (targets.length > 1 ? path.join(args.dest, t) : args.dest)
|
|
112
|
+
: null;
|
|
113
|
+
const dst = resolveTargetDir(t, args.mode, dstOverride);
|
|
114
|
+
if (exists(dst)) {
|
|
115
|
+
if (!args.force) die(`target exists: ${dst}\nRe-run with --force to overwrite.`);
|
|
116
|
+
rmrf(dst);
|
|
117
|
+
}
|
|
118
|
+
copyDir(assetSkill, dst);
|
|
119
|
+
console.log(`[autocoding] installed skill to: ${dst}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (args.mode === "project") ensureGitignore(proj);
|
|
123
|
+
|
|
124
|
+
console.log("[autocoding] done.");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
main();
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
6
|
+
const srcSkill = path.join(repoRoot, "src", "auto-coding-skill");
|
|
7
|
+
const dstSkill = path.join(repoRoot, "cli", "assets", "skill");
|
|
8
|
+
|
|
9
|
+
function rmrf(p){ fs.rmSync(p, { recursive:true, force:true }); }
|
|
10
|
+
function shouldSkip(name){
|
|
11
|
+
return name === "__pycache__" || name === ".DS_Store" || name.endsWith(".pyc");
|
|
12
|
+
}
|
|
13
|
+
function copyDir(src, dst){
|
|
14
|
+
fs.mkdirSync(dst, { recursive:true });
|
|
15
|
+
for (const ent of fs.readdirSync(src, { withFileTypes:true })) {
|
|
16
|
+
if (shouldSkip(ent.name)) continue;
|
|
17
|
+
const s = path.join(src, ent.name);
|
|
18
|
+
const d = path.join(dst, ent.name);
|
|
19
|
+
if (ent.isDirectory()) copyDir(s, d);
|
|
20
|
+
else fs.copyFileSync(s, d);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
rmrf(dstSkill);
|
|
25
|
+
copyDir(srcSkill, dstSkill);
|
|
26
|
+
console.log("[sync-assets] updated cli/assets/skill from src/auto-coding-skill");
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@elvis1513/auto-coding-skill",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI installer for auto-coding-skill (Claude Code + Codex CLI).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"autocoding": "cli/src/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"cli/src",
|
|
11
|
+
"cli/assets/skill",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"test": "node -c cli/src/index.js",
|
|
17
|
+
"sync-assets": "node cli/src/sync-assets.js",
|
|
18
|
+
"release:check": "npm run sync-assets && npm run test && npm pack --dry-run"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"claude",
|
|
26
|
+
"codex",
|
|
27
|
+
"skill",
|
|
28
|
+
"automation",
|
|
29
|
+
"engineering",
|
|
30
|
+
"workflow",
|
|
31
|
+
"ci",
|
|
32
|
+
"deployment"
|
|
33
|
+
],
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/elvis1513/auto-coding-skill.git"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/elvis1513/auto-coding-skill#readme",
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/elvis1513/auto-coding-skill/issues"
|
|
41
|
+
}
|
|
42
|
+
}
|