speccore 8.3.295 → 8.3.296

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.
@@ -0,0 +1,52 @@
1
+ # ═══════════════════════════════════════════════════════════
2
+ # Node.js 后端 Docker 多阶段构建示例(Express / NestJS / Koa)
3
+ # ═══════════════════════════════════════════════════════════
4
+ #
5
+ # 用法:
6
+ # 1. 将本文件复制到后端工程根目录,重命名为 Dockerfile
7
+ # 2. 在 PROJECT.yaml 中配置 docker 类型部署:
8
+ # type: docker
9
+ # dockerfile: ./Dockerfile
10
+ # registry: <镜像仓库>
11
+ # image: <镜像名>
12
+ # tag: staging
13
+ # 3. 执行: speccore deploy --env staging --platform <后端端名>
14
+ #
15
+ # 多阶段构建:Node 编译 → 精简运行镜像
16
+ # ═══════════════════════════════════════════════════════════
17
+
18
+ # ── 阶段 1:依赖安装 + 构建 ──
19
+ FROM node:20-alpine AS builder
20
+ WORKDIR /app
21
+
22
+ # 先复制依赖文件(利用 Docker 缓存层)
23
+ COPY package*.json ./
24
+ RUN npm ci
25
+
26
+ # 复制源码并构建
27
+ COPY . .
28
+ RUN npm run build
29
+
30
+ # ── 阶段 2:运行(只装生产依赖,镜像更小)──
31
+ FROM node:20-alpine
32
+ WORKDIR /app
33
+
34
+ ENV NODE_ENV=production
35
+
36
+ # 复制依赖与构建产物
37
+ COPY --from=builder /app/package*.json ./
38
+ RUN npm ci --omit=dev
39
+ COPY --from=builder /app/dist ./dist
40
+
41
+ # 非 root 用户运行(安全最佳实践)
42
+ RUN addgroup -S app && adduser -S app -G app
43
+ USER app
44
+
45
+ # 健康检查(按实际端口调整,Express 项目可用 /healthz 路由)
46
+ HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
47
+ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/healthz || exit 1
48
+
49
+ EXPOSE 3000
50
+
51
+ # 启动命令(按实际入口调整,如 dist/main.js、dist/server.js)
52
+ CMD ["node", "dist/main.js"]
@@ -297,6 +297,70 @@ java_docker_example:
297
297
  image: my-project/order-service
298
298
  tag: staging
299
299
 
300
+ # ═══════════════════════════════════════════════════════════
301
+ # 13. Nginx 站点配置(纯服务器托管前端,ssh 类型配套)
302
+ # ═══════════════════════════════════════════════════════════
303
+ #
304
+ # 适用场景:前端 ssh 类型部署到服务器后,由服务器 Nginx 托管。
305
+ # 服务器端完整站点配置模板见 templates/deploy-nginx/nginx-site.conf
306
+ # (含 SPA 回退、gzip、静态缓存、API 反代到本地后端、HTTPS/WebSocket)。
307
+ #
308
+ # 服务器端准备(只需一次):
309
+ # sudo cp templates/deploy-nginx/nginx-site.conf /etc/nginx/conf.d/<站点>.conf
310
+ # # 替换 {{DOMAIN}} / {{APP_DIR}} / {{BACKEND_HOST}} 后:
311
+ # sudo nginx -t && sudo nginx -s reload
312
+ #
313
+ nginx_site_example:
314
+ type: ssh
315
+ build_cmd: npm run build:staging
316
+ output_dir: dist
317
+ host: deployer@staging-server.example.com:22
318
+ key: ~/.ssh/id_rsa
319
+ remote_dir: /var/www/html/admin-web # 与站点配置的 {{APP_DIR}} 一致
320
+ script: sudo nginx -s reload # 部署完成后重载 Nginx
321
+
322
+ # ═══════════════════════════════════════════════════════════
323
+ # 14. Go 后端部署(systemd 管理,方式同 Java)
324
+ # ═══════════════════════════════════════════════════════════
325
+ #
326
+ # 适用场景:Go 服务编译为单个二进制,上传服务器由 systemd 管理。
327
+ # systemd service 模板参考 templates/deploy-java/spring-boot.service
328
+ # (将 ExecStart 改为二进制路径即可)。
329
+ #
330
+ go_systemd_example:
331
+ type: ssh
332
+ build_cmd: CGO_ENABLED=0 go build -o bin/gateway ./cmd/gateway
333
+ output_dir: bin
334
+ host: deployer@staging-server.example.com:22
335
+ key: ~/.ssh/id_rsa
336
+ remote_dir: /opt/services/gateway
337
+ script: sudo systemctl restart gateway
338
+
339
+ # Go 服务容器化部署
340
+ # Dockerfile 要点:多阶段(golang:1.22-alpine 编译 → alpine 运行),
341
+ # 运行阶段仅复制二进制,参考 deploy-java/Dockerfile 结构。
342
+ go_docker_example:
343
+ type: docker
344
+ dockerfile: ./Dockerfile
345
+ registry: registry.example.com
346
+ image: my-project/gateway
347
+ tag: staging
348
+
349
+ # ═══════════════════════════════════════════════════════════
350
+ # 15. Python 后端部署(gunicorn + systemd)
351
+ # ═══════════════════════════════════════════════════════════
352
+ #
353
+ # 适用场景:Django / Flask / FastAPI 服务,服务器上用 gunicorn 运行。
354
+ # 建议直接使用 ssh 类型 + 自定义脚本完成:构建(打包依赖)→ 上传 → 重启。
355
+ #
356
+ python_gunicorn_example:
357
+ type: script
358
+ build_cmd: pip install -r requirements.txt -t dist/
359
+ script: ./scripts/deploy-python.sh
360
+ # deploy-python.sh 内容示例(上传 + 重启 gunicorn):
361
+ # rsync -az --delete dist/ deployer@server:/opt/services/api/app/
362
+ # ssh deployer@server "sudo systemctl restart api-gunicorn"
363
+
300
364
  # ═══════════════════════════════════════════════════════════
301
365
  # 完整 PROJECT.yaml 示例(含部署配置)
302
366
  # ═══════════════════════════════════════════════════════════
@@ -357,16 +421,27 @@ deploy:
357
421
  remote_dir: /var/www/html/sr-web-operation
358
422
 
359
423
  # ═══════════════════════════════════════════════════════════
360
- # 常用脚本示例
424
+ # 常用脚本示例(实体文件见 templates/deploy-scripts/)
361
425
  # ═══════════════════════════════════════════════════════════
426
+ #
427
+ # 脚本实体与说明:
428
+ # templates/deploy-scripts/trigger-jenkins.sh Jenkins 构建触发
429
+ # templates/deploy-scripts/deploy-ftp.sh lftp FTP 上传
430
+ # templates/deploy-scripts/deploy-password-ssh.sh sshpass 密码 SSH
431
+ # templates/deploy-scripts/deploy-compose.sh docker-compose 编排
432
+ # templates/deploy-scripts/deploy-python.sh Python gunicorn 部署
433
+ # templates/deploy-java/deploy.sh 服务器端 Java 部署(备份→替换→重启)
434
+ # templates/deploy-docker/deploy-remote.sh 服务器端 Docker 容器管理
435
+ #
436
+ # 以下为脚本要点摘录(完整版以实体文件为准):
362
437
 
363
- # --- scripts/trigger-jenkins.sh ---
438
+ # --- trigger-jenkins.sh(要点)---
364
439
  # #!/bin/bash
365
440
  # curl -X POST \
366
441
  # -u "$JENKINS_USER:$JENKINS_TOKEN" \
367
442
  # "http://jenkins.example.com/job/my-project-$1/build"
368
443
 
369
- # --- scripts/deploy-ftp.sh ---
444
+ # --- deploy-ftp.sh(要点,完整版含环境变量校验)---
370
445
  # #!/bin/bash
371
446
  # # 使用 lftp 进行 FTP 上传(支持密码)
372
447
  # lftp -u "$FTP_USER,$FTP_PASS" "$FTP_HOST" <<EOF
@@ -375,7 +450,7 @@ deploy:
375
450
  # bye
376
451
  # EOF
377
452
 
378
- # --- scripts/deploy-password-ssh.sh ---
453
+ # --- deploy-password-ssh.sh(要点,完整版含环境变量校验)---
379
454
  # #!/bin/bash
380
455
  # # 使用 sshpass 进行密码认证的 SSH 部署(需安装 sshpass)
381
456
  # # macOS: brew install sshpass
@@ -383,7 +458,7 @@ deploy:
383
458
  # sshpass -p "$SSH_PASSWORD" scp -r dist/* "$SSH_USER@$SSH_HOST:$REMOTE_DIR/"
384
459
  # sshpass -p "$SSH_PASSWORD" ssh "$SSH_USER@$SSH_HOST" "sudo nginx -s reload"
385
460
 
386
- # --- scripts/deploy-java.sh(服务器端执行) ---
461
+ # --- deploy-java.sh(实体:templates/deploy-java/deploy.sh,服务器端执行)---
387
462
  # #!/bin/bash
388
463
  # # Java 后端部署脚本(放在服务器上,由 SSH 调用或手动执行)
389
464
  # APP_NAME="${1:-order-service}"
@@ -0,0 +1,59 @@
1
+ # Nginx 服务器部署指南(纯服务器,非 Docker)
2
+
3
+ > 本目录提供 **ssh 类型部署**场景下前端站点的 Nginx 服务器端配置模板。
4
+ > Docker 场景(Nginx 容器托管前端)请使用 `templates/deploy-docker/nginx.conf`。
5
+
6
+ ---
7
+
8
+ ## 两种 Nginx 场景怎么选
9
+
10
+ | 场景 | 部署类型 | 配置文件 | Nginx 位置 |
11
+ |:---|:---|:---|:---|
12
+ | **纯服务器**:构建产物上传到服务器 Nginx 静态目录,后端跑进程/systemd | `ssh`(推荐)/ `sftp` | 本目录 `nginx-site.conf` | 服务器 `/etc/nginx/conf.d/` |
13
+ | **容器化**:前端打成镜像由 Nginx 容器托管 | `docker` | `deploy-docker/nginx.conf` | 镜像内 `/etc/nginx/conf.d/` |
14
+
15
+ ## 快速开始(前端 ssh 部署 + 服务器 Nginx 托管)
16
+
17
+ ```bash
18
+ # 1. 服务器端(只需一次):复制站点配置并替换占位符
19
+ sudo cp nginx-site.conf /etc/nginx/conf.d/admin-web.conf
20
+ sudo sed -i 's/{{DOMAIN}}/admin.example.com/; s|{{APP_DIR}}|/var/www/html/admin-web|; s|{{BACKEND_HOST}}|127.0.0.1:8080|' /etc/nginx/conf.d/admin-web.conf
21
+ sudo nginx -t && sudo nginx -s reload
22
+
23
+ # 2. PROJECT.yaml 配置 ssh 类型部署
24
+ # remote_dir: /var/www/html/admin-web
25
+ # script: sudo nginx -s reload
26
+
27
+ # 3. 部署(构建 → 上传 → 重载 Nginx)
28
+ speccore deploy --env staging --platform admin-web
29
+ ```
30
+
31
+ ## 模板包含内容
32
+
33
+ - SPA 路由回退(`try_files` → `/index.html`)
34
+ - gzip 压缩
35
+ - 静态资源长期缓存(`immutable`)
36
+ - `/api/` 反向代理到后端(同机 `127.0.0.1:8080` 或跨机内网地址)
37
+ - WebSocket 升级(注释备用)
38
+ - 健康检查端点透传(`/healthz` → 后端 `/actuator/health`,注释备用)
39
+ - HTTPS server 块(证书就绪后启用)
40
+ - 隐藏文件访问拦截
41
+
42
+ ## 占位符
43
+
44
+ | 占位符 | 说明 | 示例 |
45
+ |:---|:---|:---|
46
+ | `{{DOMAIN}}` | 站点域名或 IP | `admin.example.com` |
47
+ | `{{APP_DIR}}` | 静态资源目录(= `remote_dir`) | `/var/www/html/admin-web` |
48
+ | `{{BACKEND_HOST}}` | 后端地址 | `127.0.0.1:8080` |
49
+
50
+ ## 与冒烟测试联动
51
+
52
+ 部署完成后可在 `.speccore/environments/{env}.yaml` 的 `tests.base_urls` 配置站点地址,
53
+ `speccore verify` 的部署后冒烟将自动访问验证:
54
+
55
+ ```yaml
56
+ tests:
57
+ base_urls:
58
+ admin-web: https://admin.example.com
59
+ ```
@@ -0,0 +1,103 @@
1
+ # ═══════════════════════════════════════════════════════════
2
+ # Nginx 服务器站点配置模板(纯服务器部署,非 Docker 场景)
3
+ # ═══════════════════════════════════════════════════════════
4
+ #
5
+ # 适用场景:speccore deploy 的 ssh 类型部署,前端构建产物
6
+ # 上传到 Nginx 服务器静态目录,后端以进程/服务方式跑在
7
+ # 同一台(或另一台)服务器上。
8
+ #
9
+ # 用法:
10
+ # 1. 复制本文件到服务器: /etc/nginx/conf.d/<your-site>.conf
11
+ # (或 /etc/nginx/sites-available/ + sites-enabled 软链)
12
+ # 2. 替换 {{占位符}}
13
+ # 3. 校验并重载: sudo nginx -t && sudo nginx -s reload
14
+ #
15
+ # 与 PROJECT.yaml 的对应关系(ssh 类型部署):
16
+ # remote_dir → 下方 root 指向的目录
17
+ # script → sudo nginx -s reload(部署完成后重载配置)
18
+ #
19
+ # 占位符说明:
20
+ # {{DOMAIN}} 站点域名(或服务器 IP)
21
+ # {{APP_DIR}} 静态资源目录(= remote_dir,如 /var/www/html/admin-web)
22
+ # {{BACKEND_HOST}} 后端地址(同机为 127.0.0.1:8080,跨机为内网 IP:端口)
23
+ # ═══════════════════════════════════════════════════════════
24
+
25
+ # ── HTTP(开发/测试环境,或 HTTPS 前的重定向入口)──
26
+ server {
27
+ listen 80;
28
+ server_name {{DOMAIN}};
29
+
30
+ # 有证书后启用 HTTPS,取消注释并将整块 HTTP 改为重定向:
31
+ # return 301 https://$host$request_uri;
32
+
33
+ include /etc/nginx/conf.d/_site-common.conf;
34
+ }
35
+
36
+ # ── HTTPS(生产环境,证书就绪后启用)──
37
+ # server {
38
+ # listen 443 ssl http2;
39
+ # server_name {{DOMAIN}};
40
+ #
41
+ # ssl_certificate /etc/nginx/ssl/{{DOMAIN}}.pem;
42
+ # ssl_certificate_key /etc/nginx/ssl/{{DOMAIN}}.key;
43
+ # ssl_protocols TLSv1.2 TLSv1.3;
44
+ # ssl_session_cache shared:SSL:10m;
45
+ #
46
+ # include /etc/nginx/conf.d/_site-common.conf;
47
+ # }
48
+
49
+ # ── 站点公共配置(HTTP/HTTPS 共用,独立成文件避免重复)──
50
+ # 保存为 /etc/nginx/conf.d/_site-common.conf 后 include,
51
+ # 或直接展开合并进上方 server 块。
52
+
53
+ # 静态资源根目录(= PROJECT.yaml 中 ssh 类型的 remote_dir)
54
+ root {{APP_DIR}};
55
+ index index.html;
56
+
57
+ # gzip 压缩(减少传输体积)
58
+ gzip on;
59
+ gzip_min_length 1k;
60
+ gzip_comp_level 5;
61
+ gzip_types text/plain text/css application/javascript application/json image/svg+xml;
62
+
63
+ # ── 前端路由(SPA 应用)──
64
+ location / {
65
+ try_files $uri $uri/ /index.html;
66
+ }
67
+
68
+ # ── 静态资源长期缓存 ──
69
+ location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
70
+ expires 1y;
71
+ add_header Cache-Control "public, immutable";
72
+ }
73
+
74
+ # ── API 反向代理到后端(同机后端)──
75
+ location /api/ {
76
+ proxy_pass http://{{BACKEND_HOST}}/api/;
77
+ proxy_set_header Host $host;
78
+ proxy_set_header X-Real-IP $remote_addr;
79
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
80
+ proxy_set_header X-Forwarded-Proto $scheme;
81
+ proxy_connect_timeout 10s;
82
+ proxy_read_timeout 60s;
83
+ }
84
+
85
+ # ── WebSocket 升级(后端含长连接/推送时启用)──
86
+ # location /ws {
87
+ # proxy_pass http://{{BACKEND_HOST}};
88
+ # proxy_http_version 1.1;
89
+ # proxy_set_header Upgrade $http_upgrade;
90
+ # proxy_set_header Connection "upgrade";
91
+ # proxy_read_timeout 3600s;
92
+ # }
93
+
94
+ # ── 后端健康检查端点透传(部署后验证用)──
95
+ # location = /healthz {
96
+ # proxy_pass http://{{BACKEND_HOST}}/actuator/health;
97
+ # access_log off;
98
+ # }
99
+
100
+ # ── 禁止访问隐藏文件 ──
101
+ location ~ /\. {
102
+ deny all;
103
+ }
@@ -0,0 +1,61 @@
1
+ # 通用部署脚本模板
2
+
3
+ > 本目录提供 `script` 类型部署的**开箱即用脚本实体**,覆盖 Jenkins 触发、FTP 上传、密码 SSH、Compose 编排、Python 服务五类场景。
4
+ > 对应 `templates/deploy-examples.yaml` 中 script 类型各节的注释示例。
5
+
6
+ ---
7
+
8
+ ## 脚本清单
9
+
10
+ | 脚本 | 场景 | 依赖 | 关键环境变量 |
11
+ |:---|:---|:---|:---|
12
+ | `trigger-jenkins.sh <job>` | 触发 Jenkins 构建发布 | `curl` | `JENKINS_URL` `JENKINS_USER` `JENKINS_TOKEN` |
13
+ | `deploy-ftp.sh [dir]` | 虚拟主机 FTP 上传(无 SSH 权限) | `lftp` | `FTP_USER` `FTP_PASS` `FTP_HOST` `FTP_REMOTE_DIR` |
14
+ | `deploy-password-ssh.sh [dir]` | 密码认证 SSH 部署(无密钥) | `sshpass` | `SSH_USER` `SSH_PASSWORD` `SSH_HOST` `SSH_REMOTE_DIR` |
15
+ | `deploy-compose.sh [file]` | 服务器端 Compose 编排部署 | `docker-compose` | — |
16
+ | `deploy-python.sh [dir]` | Python gunicorn + systemd 部署 | `rsync` | `DEPLOY_HOST` `DEPLOY_DIR` `SERVICE_NAME` |
17
+
18
+ ## 目录组织约定
19
+
20
+ 部署脚本按「执行位置」分三处存放,各司其职:
21
+
22
+ ```
23
+ ① 包内模板 templates/deploy-*/
24
+ ├── deploy-scripts/ ← 通用脚本实体(本目录,script 类型直接引用)
25
+ ├── deploy-docker/ ← Docker 构建素材 + 服务器端容器管理脚本(deploy-remote.sh)
26
+ ├── deploy-java/ ← Java systemd/Docker 素材 + 服务器端脚本(deploy.sh)
27
+ └── deploy-nginx/ ← 纯服务器 Nginx 站点配置
28
+ ↓(npm 安装后用户手动 cp 到工程)
29
+
30
+ ② 用户项目 .speccore/environments/scripts/(init 自动生成)
31
+ ├── deploy.sh ← 通用 script 类型骨架(TODO 待填)
32
+ ├── ecosystem.config.js ← pm2 类型
33
+ ├── k8s-deployment.yaml ← k8s 类型
34
+ ├── java-systemd.service / java-restart.sh ← ssh 类型 Java
35
+ └── values-{env}.yaml ← helm 类型(按环境生成)
36
+ ↓(speccore deploy 按 script/pm2_config/output_dir 字段引用)
37
+
38
+ ③ 端工程内 scripts/(用户自维护)
39
+ ← 端专属脚本,config.script 用 ./scripts/xxx.sh 相对路径引用
40
+ ```
41
+
42
+ > **路径解析(v8.3.296+)**:引擎对 `script` / `pm2_config` / k8s manifest / helm values 做双基准解析——先在端目录(code_path)找,回退项目根。`./scripts/xxx.sh` 与 `.speccore/environments/scripts/xxx` 两种写法都通。
43
+
44
+ ## 使用方式
45
+
46
+ ```yaml
47
+ # PROJECT.yaml(script 类型)
48
+ deploy:
49
+ staging:
50
+ type: script
51
+ build_cmd: npm run build
52
+ script: ./scripts/deploy-password-ssh.sh # 端工程内脚本(推荐)
53
+ # 或引用项目根资产:
54
+ # script: .speccore/environments/scripts/deploy.sh
55
+ ```
56
+
57
+ 敏感信息(密码/token)一律通过环境变量注入,勿写入配置文件或 git:
58
+
59
+ ```bash
60
+ SSH_USER=deployer SSH_PASSWORD=xxx SSH_HOST=server ./scripts/deploy-password-ssh.sh dist
61
+ ```
@@ -0,0 +1,19 @@
1
+ #!/bin/bash
2
+ # ═══════════════════════════════════════════════════════════
3
+ # Docker Compose 部署脚本(服务器端执行)
4
+ # ═══════════════════════════════════════════════════════════
5
+ # 用途:script 类型部署,服务器端统一拉取并启动前后端编排
6
+ # 用法:./deploy-compose.sh [compose-file]
7
+ # 示例:./deploy-compose.sh /opt/deploy/docker-compose.yml
8
+ # ═══════════════════════════════════════════════════════════
9
+ set -euo pipefail
10
+
11
+ COMPOSE_FILE="${1:-/opt/deploy/docker-compose.yml}"
12
+
13
+ echo "🚀 Compose 部署: $COMPOSE_FILE"
14
+ docker-compose -f "$COMPOSE_FILE" pull
15
+ docker-compose -f "$COMPOSE_FILE" up -d
16
+
17
+ echo "📋 运行状态:"
18
+ docker-compose -f "$COMPOSE_FILE" ps
19
+ echo "✅ 部署完成"
@@ -0,0 +1,28 @@
1
+ #!/bin/bash
2
+ # ═══════════════════════════════════════════════════════════
3
+ # FTP 上传部署脚本(lftp)
4
+ # ═══════════════════════════════════════════════════════════
5
+ # 用途:script 类型部署,无 SSH 权限只有 FTP 的虚拟主机场景
6
+ # 环境变量(由用户 shell 环境注入,勿写入 git):
7
+ # FTP_USER / FTP_PASS / FTP_HOST / FTP_REMOTE_DIR
8
+ # 用法:FTP_USER=u FTP_PASS=p FTP_HOST=h ./deploy-ftp.sh [本地目录]
9
+ # ═══════════════════════════════════════════════════════════
10
+ set -euo pipefail
11
+
12
+ LOCAL_DIR="${1:-dist}"
13
+ FTP_REMOTE_DIR="${FTP_REMOTE_DIR:-/public_html}"
14
+
15
+ if [ -z "${FTP_USER:-}" ] || [ -z "${FTP_PASS:-}" ] || [ -z "${FTP_HOST:-}" ]; then
16
+ echo "❌ 缺少环境变量: FTP_USER / FTP_PASS / FTP_HOST"
17
+ exit 1
18
+ fi
19
+
20
+ echo "🚀 FTP 上传: $LOCAL_DIR → $FTP_HOST:$FTP_REMOTE_DIR"
21
+
22
+ lftp -u "$FTP_USER,$FTP_PASS" "$FTP_HOST" <<INNER_EOF
23
+ set ssl:verify-certificate no
24
+ mirror -R "$LOCAL_DIR" "$FTP_REMOTE_DIR"
25
+ bye
26
+ INNER_EOF
27
+
28
+ echo "✅ 上传完成"
@@ -0,0 +1,26 @@
1
+ #!/bin/bash
2
+ # ═══════════════════════════════════════════════════════════
3
+ # 密码认证 SSH 部署脚本(sshpass)
4
+ # ═══════════════════════════════════════════════════════════
5
+ # 用途:script 类型部署,无密钥只有密码的 SSH 场景
6
+ # 前置:安装 sshpass(macOS: brew install sshpass;Ubuntu: apt-get install sshpass)
7
+ # 环境变量(由用户 shell 环境注入,勿写入 git):
8
+ # SSH_USER / SSH_PASSWORD / SSH_HOST / SSH_REMOTE_DIR
9
+ # 用法:SSH_USER=u SSH_PASSWORD=p ./deploy-password-ssh.sh [本地目录]
10
+ # ═══════════════════════════════════════════════════════════
11
+ set -euo pipefail
12
+
13
+ LOCAL_DIR="${1:-dist}"
14
+ SSH_REMOTE_DIR="${SSH_REMOTE_DIR:-/var/www/html}"
15
+
16
+ if [ -z "${SSH_USER:-}" ] || [ -z "${SSH_PASSWORD:-}" ] || [ -z "${SSH_HOST:-}" ]; then
17
+ echo "❌ 缺少环境变量: SSH_USER / SSH_PASSWORD / SSH_HOST"
18
+ exit 1
19
+ fi
20
+
21
+ echo "🚀 SSH 上传: $LOCAL_DIR → $SSH_USER@$SSH_HOST:$SSH_REMOTE_DIR"
22
+
23
+ sshpass -p "$SSH_PASSWORD" scp -r "$LOCAL_DIR"/* "$SSH_USER@$SSH_HOST:$SSH_REMOTE_DIR/"
24
+ sshpass -p "$SSH_PASSWORD" ssh "$SSH_USER@$SSH_HOST" "sudo nginx -s reload"
25
+
26
+ echo "✅ 部署完成"
@@ -0,0 +1,30 @@
1
+ #!/bin/bash
2
+ # ═══════════════════════════════════════════════════════════
3
+ # Python 服务部署脚本(gunicorn + systemd)
4
+ # ═══════════════════════════════════════════════════════════
5
+ # 用途:script 类型部署,上传打包产物并重启 gunicorn 服务
6
+ # 环境变量:
7
+ # DEPLOY_HOST 服务器地址(user@host)
8
+ # DEPLOY_DIR 服务器部署目录(默认 /opt/services/api)
9
+ # SERVICE_NAME systemd 服务名(默认 api-gunicorn)
10
+ # 用法:./deploy-python.sh [本地产物目录]
11
+ # ═══════════════════════════════════════════════════════════
12
+ set -euo pipefail
13
+
14
+ LOCAL_DIR="${1:-dist}"
15
+ DEPLOY_HOST="${DEPLOY_HOST:-}"
16
+ DEPLOY_DIR="${DEPLOY_DIR:-/opt/services/api}"
17
+ SERVICE_NAME="${SERVICE_NAME:-api-gunicorn}"
18
+
19
+ if [ -z "$DEPLOY_HOST" ]; then
20
+ echo "❌ 缺少环境变量: DEPLOY_HOST(格式 user@host)"
21
+ exit 1
22
+ fi
23
+
24
+ echo "🚀 Python 部署: $LOCAL_DIR → $DEPLOY_HOST:$DEPLOY_DIR"
25
+
26
+ rsync -az --delete "$LOCAL_DIR"/ "$DEPLOY_HOST:$DEPLOY_DIR/app/"
27
+ ssh "$DEPLOY_HOST" "sudo systemctl restart $SERVICE_NAME"
28
+ ssh "$DEPLOY_HOST" "sudo systemctl status $SERVICE_NAME --no-pager"
29
+
30
+ echo "✅ 部署完成"
@@ -0,0 +1,30 @@
1
+ #!/bin/bash
2
+ # ═══════════════════════════════════════════════════════════
3
+ # Jenkins 构建触发脚本
4
+ # ═══════════════════════════════════════════════════════════
5
+ # 用途:script 类型部署,触发 Jenkins 任务完成后续构建/发布
6
+ # 环境变量:JENKINS_URL / JENKINS_USER / JENKINS_TOKEN
7
+ # 用法:./trigger-jenkins.sh <job-name>
8
+ # ═══════════════════════════════════════════════════════════
9
+ set -euo pipefail
10
+
11
+ JOB_NAME="${1:-}"
12
+
13
+ if [ -z "$JOB_NAME" ]; then
14
+ echo "用法: $0 <job-name>"
15
+ exit 1
16
+ fi
17
+
18
+ JENKINS_URL="${JENKINS_URL:-http://jenkins.example.com}"
19
+ JENKINS_USER="${JENKINS_USER:-}"
20
+ JENKINS_TOKEN="${JENKINS_TOKEN:-}"
21
+
22
+ echo "🚀 触发 Jenkins: $JOB_NAME"
23
+
24
+ if [ -n "$JENKINS_USER" ] && [ -n "$JENKINS_TOKEN" ]; then
25
+ curl -X POST -u "$JENKINS_USER:$JENKINS_TOKEN" "$JENKINS_URL/job/$JOB_NAME/build"
26
+ else
27
+ curl -X POST "$JENKINS_URL/job/$JOB_NAME/build"
28
+ fi
29
+
30
+ echo "✅ 已触发(结果请到 Jenkins 控制台查看)"
@@ -7,4 +7,4 @@
7
7
  .match-result{padding:10px 0}.match-summary{font-weight:600;font-size:14px;margin-bottom:12px}.match-detail{color:var(--muted);font-size:12px;line-height:1.6}
8
8
  /* Pipeline */
9
9
  .pipeline-title{font-weight:600;margin-bottom:16px}.pipeline-steps{display:flex;flex-direction:column;gap:10px}.pipe-step{display:flex;gap:14px;padding:12px 14px;background:rgba(99,102,241,.04);border-left:2px solid var(--purple);border-radius:0 8px 8px 0}.pipe-step .pipe-num{font-family:Orbitron;font-size:20px;font-weight:900;color:var(--purple);min-width:36px}.pipe-step .pipe-cmd{font-weight:600;color:var(--green)}.pipe-step .pipe-args{color:var(--muted);font-size:11px}.pipe-step .pipe-desc{color:var(--muted);font-size:10px;margin-top:4px}.pipe-step .pipe-dep{font-size:9px;color:var(--orange);margin-top:4px}.confirm-bar{text-align:center;padding:14px;margin-top:16px;background:rgba(99,102,241,.08);border:1px dashed rgba(99,102,241,.2);border-radius:8px;color:var(--purple);font-size:12px}
10
- .footer{text-align:center;color:var(--muted);font-size:10px;margin-top:24px;padding-top:16px;border-top:1px solid rgba(255,255,255,.04)}h1{text-shadow:0 0 20px rgba(14,165,233,.35),0 0 60px rgba(14,165,233,.12)!important}@keyframes cardGlow{0%,100%{opacity:.5;transform:scale(1)}50%{opacity:1;transform:scale(1.6)}}.card-bg{position:absolute;inset:0;pointer-events:none;z-index:0;background:radial-gradient(ellipse at 25% 10%,rgba(14,165,233,.25) 0%,transparent 70%);animation:cardGlow 3s ease-in-out infinite;transform-origin:top center}@keyframes titleGlow{0%,100%{text-shadow:0 0 20px rgba(14,165,233,.4),0 0 60px rgba(14,165,233,.15)}50%{text-shadow:0 0 30px rgba(14,165,233,.7),0 0 80px rgba(14,165,233,.3)}}h1,h2{text-shadow:0 0 20px rgba(14,165,233,.4),0 0 60px rgba(14,165,233,.15);animation:titleGlow 3s ease-in-out infinite}</style></head><body><div class="scanlines"></div><div class="card"><div class="card-bg"></div><div class="vline l"></div><div class="vline r"></div><h1>SPECCORE ASK</h1><div class="query">"我想做一个登录功能"</div><div class="badge">🗺️ 任务指引</div><div class="guide-title">已为你规划「新功能开发全流程」(9 步)</div><div class="guide-steps"><div class="guide-step"><div class="step-num">1</div><div class="step-body"><div class="step-cmd">speccore init</div><div class="step-desc">初始化项目(如果还没有)</div></div></div><div class="guide-step"><div class="step-num">2</div><div class="step-body"><div class="step-cmd">speccore doc2spec -f PRD.docx --iter {iteration}</div><div class="step-desc">导入 PRD 文档,AI 分析生成需求规格</div></div></div><div class="guide-step"><div class="step-num">3</div><div class="step-body"><div class="step-cmd">speccore analyze --iteration {iteration} --audit</div><div class="step-desc">AI 分析需求,生成审计报告</div></div></div><div class="guide-step"><div class="step-num">4</div><div class="step-body"><div class="step-cmd">speccore iteration taskify -f REQUIREMENT.md</div><div class="step-desc">将需求拆分为独立开发任务</div></div></div><div class="guide-step"><div class="step-num">5</div><div class="step-body"><div class="step-cmd">speccore plan --all</div><div class="step-desc">生成任务执行计划,确定优先级和依赖</div></div></div><div class="guide-step"><div class="step-num">6</div><div class="step-body"><div class="step-cmd">speccore execute --auto</div><div class="step-desc">按计划依次执行开发任务</div></div></div><div class="guide-step"><div class="step-num">7</div><div class="step-body"><div class="step-cmd">speccore verify --ui</div><div class="step-desc">本地冒烟测试(自动登录 + 页面冒烟,默认 module 范围)</div></div></div><div class="guide-step"><div class="step-num">8</div><div class="step-body"><div class="step-cmd">speccore pr --auto</div><div class="step-desc">代码提交后创建 Pull Request</div></div></div><div class="guide-step"><div class="step-num">9</div><div class="step-body"><div class="step-cmd">speccore done --all</div><div class="step-desc">全部完成后归档收尾</div></div></div></div><div style="text-align:center;margin-top:16px"><a href="speccore-welcome.html" style="color:var(--cyan);font-size:11px;text-decoration:none">← 返回 ask 引导页</a></div><div class="footer">由 SpecCore 驱动 · v8.3.295 · 2026-08-31</div></div></body></html>
10
+ .footer{text-align:center;color:var(--muted);font-size:10px;margin-top:24px;padding-top:16px;border-top:1px solid rgba(255,255,255,.04)}h1{text-shadow:0 0 20px rgba(14,165,233,.35),0 0 60px rgba(14,165,233,.12)!important}@keyframes cardGlow{0%,100%{opacity:.5;transform:scale(1)}50%{opacity:1;transform:scale(1.6)}}.card-bg{position:absolute;inset:0;pointer-events:none;z-index:0;background:radial-gradient(ellipse at 25% 10%,rgba(14,165,233,.25) 0%,transparent 70%);animation:cardGlow 3s ease-in-out infinite;transform-origin:top center}@keyframes titleGlow{0%,100%{text-shadow:0 0 20px rgba(14,165,233,.4),0 0 60px rgba(14,165,233,.15)}50%{text-shadow:0 0 30px rgba(14,165,233,.7),0 0 80px rgba(14,165,233,.3)}}h1,h2{text-shadow:0 0 20px rgba(14,165,233,.4),0 0 60px rgba(14,165,233,.15);animation:titleGlow 3s ease-in-out infinite}</style></head><body><div class="scanlines"></div><div class="card"><div class="card-bg"></div><div class="vline l"></div><div class="vline r"></div><h1>SPECCORE ASK</h1><div class="query">"我想做一个登录功能"</div><div class="badge">🗺️ 任务指引</div><div class="guide-title">已为你规划「新功能开发全流程」(9 步)</div><div class="guide-steps"><div class="guide-step"><div class="step-num">1</div><div class="step-body"><div class="step-cmd">speccore init</div><div class="step-desc">初始化项目(如果还没有)</div></div></div><div class="guide-step"><div class="step-num">2</div><div class="step-body"><div class="step-cmd">speccore doc2spec -f PRD.docx --iter {iteration}</div><div class="step-desc">导入 PRD 文档,AI 分析生成需求规格</div></div></div><div class="guide-step"><div class="step-num">3</div><div class="step-body"><div class="step-cmd">speccore analyze --iteration {iteration} --audit</div><div class="step-desc">AI 分析需求,生成审计报告</div></div></div><div class="guide-step"><div class="step-num">4</div><div class="step-body"><div class="step-cmd">speccore iteration taskify -f REQUIREMENT.md</div><div class="step-desc">将需求拆分为独立开发任务</div></div></div><div class="guide-step"><div class="step-num">5</div><div class="step-body"><div class="step-cmd">speccore plan --all</div><div class="step-desc">生成任务执行计划,确定优先级和依赖</div></div></div><div class="guide-step"><div class="step-num">6</div><div class="step-body"><div class="step-cmd">speccore execute --auto</div><div class="step-desc">按计划依次执行开发任务</div></div></div><div class="guide-step"><div class="step-num">7</div><div class="step-body"><div class="step-cmd">speccore verify --ui</div><div class="step-desc">本地冒烟测试(自动登录 + 页面冒烟,默认 module 范围)</div></div></div><div class="guide-step"><div class="step-num">8</div><div class="step-body"><div class="step-cmd">speccore pr --auto</div><div class="step-desc">代码提交后创建 Pull Request</div></div></div><div class="guide-step"><div class="step-num">9</div><div class="step-body"><div class="step-cmd">speccore done --all</div><div class="step-desc">全部完成后归档收尾</div></div></div></div><div style="text-align:center;margin-top:16px"><a href="speccore-welcome.html" style="color:var(--cyan);font-size:11px;text-decoration:none">← 返回 ask 引导页</a></div><div class="footer">由 SpecCore 驱动 · v8.3.296 · 2026-08-31</div></div></body></html>
@@ -1 +1 @@
1
- <!DOCTYPE html><html lang="zh-CN" data-theme="ocean"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>SpecCore · ask --pipeline</title><style>@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@500;700;900&family=JetBrains+Mono:wght@400;600;700&display=swap');[data-theme="ocean"]{--cyan:#0ea5e9;--bg:#0b1929;--card:rgba(13,31,56,.95);--border:rgba(14,165,233,.15);--text:#bae6fd;--muted:#5b7fa5;--green:#14b8a6;--orange:#f97316;--purple:#6366f1}*{margin:0;padding:0;box-sizing:border-box}body{font-family:'JetBrains Mono',monospace;background:var(--bg);color:var(--text);min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px}.scanlines{position:fixed;inset:0;pointer-events:none;z-index:99;background:repeating-linear-gradient(0deg,transparent,transparent 2px,rgba(0,240,255,.01) 2px,rgba(0,240,255,.01) 4px)}.card{max-width:900px;width:100%;margin:0 auto;background:var(--card);border:1px solid var(--border);border-radius:16px;padding:32px;position:relative;overflow:hidden;z-index:1}.card::before{content:'';position:absolute;top:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent,var(--cyan),transparent);animation:scanX 3s linear infinite}.card::after{content:'';position:absolute;bottom:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent,var(--cyan),transparent);animation:scanX-rev 3s linear infinite}@keyframes scanX{0%{transform:translateX(-100%)}100%{transform:translateX(100%)}}@keyframes scanX-rev{0%{transform:translateX(100%)}100%{transform:translateX(-100%)}}@keyframes scanY{0%{transform:translateY(-100%)}100%{transform:translateY(100%)}}@keyframes scanY-rev{0%{transform:translateY(100%)}100%{transform:translateY(-100%)}}.vline{position:absolute;top:0;width:1px;bottom:0;pointer-events:none}.vline.l{left:0;background:linear-gradient(180deg,transparent,var(--cyan),transparent);animation:scanY-rev 3s linear infinite}.vline.r{right:0;background:linear-gradient(180deg,transparent,var(--cyan),transparent);animation:scanY 3s linear infinite}.card-bg{position:absolute;inset:0;pointer-events:none;z-index:0;background:radial-gradient(ellipse at 25% 10%,rgba(14,165,233,.25) 0%,transparent 70%);animation:cardGlow 3s ease-in-out infinite;transform-origin:top center}@keyframes cardGlow{0%,100%{opacity:.5;transform:scale(1)}50%{opacity:1;transform:scale(1.6)}}.page-title{font-family:'Orbitron',sans-serif;font-size:22px;font-weight:700;text-align:center;color:var(--cyan);margin-bottom:32px;letter-spacing:1px;text-shadow:0 0 20px rgba(14,165,233,.4),0 0 60px rgba(14,165,233,.15);animation:titleGlow 3s ease-in-out infinite}@keyframes titleGlow{0%,100%{text-shadow:0 0 20px rgba(14,165,233,.4),0 0 60px rgba(14,165,233,.15)}50%{text-shadow:0 0 30px rgba(14,165,233,.7),0 0 80px rgba(14,165,233,.3)}}.page-title span{color:var(--text);font-weight:500}.flow-chart{display:flex;align-items:stretch;gap:0;margin-bottom:28px}.flow-node{flex:1;background:rgba(13,31,56,.7);border:1px solid;border-radius:10px;padding:14px 8px;text-align:center;position:relative}.flow-node .icon{font-size:18px;margin-bottom:6px}.flow-node .name{font-size:12px;font-weight:600;margin-bottom:4px}.flow-node .desc{font-size:10px;color:var(--muted)}.flow-node.n1{border-color:rgba(14,165,233,.4);color:var(--cyan)}.flow-node.n2{border-color:rgba(99,102,241,.4);color:var(--purple)}.flow-node.n3{border-color:rgba(14,165,233,.4);color:var(--cyan)}.flow-node.n4{border-color:rgba(20,184,166,.4);color:var(--green)}.flow-connector{width:24px;display:flex;align-items:center;justify-content:center}.flow-connector::after{content:'';width:100%;height:1px;background:linear-gradient(90deg,var(--border),var(--cyan),var(--border))}.example-box{border:1px dashed rgba(14,165,233,.3);border-radius:12px;padding:16px 20px;margin-bottom:24px;background:rgba(14,165,233,.03)}.example-label{font-size:13px;font-weight:600;color:var(--cyan);margin-bottom:8px}.example-query{font-family:'JetBrains Mono',monospace;font-size:14px;color:var(--text)}.example-query code{color:var(--green);font-weight:600}.steps{display:flex;flex-direction:column;gap:12px;margin-bottom:24px}.step{background:rgba(13,31,56,.8);border:1px solid rgba(14,165,233,.12);border-radius:10px;padding:14px 18px}.step-num{font-family:'Orbitron',sans-serif;font-size:14px;font-weight:700;color:var(--green);margin-bottom:6px}.step-cmd{font-family:'JetBrains Mono',monospace;font-size:14px;color:var(--text);margin-bottom:6px}.step-cmd .cmd-name{color:var(--green)}.step-cmd .cmd-args{color:var(--muted)}.step-parse{font-size:12px;color:var(--muted);line-height:1.5}.step-parse::before{content:'→ 解析:';color:var(--cyan)}.confirm-btn{display:block;width:100%;max-width:420px;margin:0 auto 16px;text-align:center;padding:16px;background:linear-gradient(135deg,var(--cyan),#0284c7);border:none;border-radius:40px;color:#fff;font-size:15px;font-weight:600;cursor:pointer;box-shadow:0 0 30px rgba(14,165,233,.3)}.legend{display:flex;justify-content:center;gap:20px;font-size:11px;color:var(--muted);margin-bottom:16px;flex-wrap:wrap}.legend-item{display:flex;align-items:center;gap:4px}.legend-dot{width:8px;height:8px;border-radius:50%;display:inline-block}.legend-dot.required{background:var(--cyan)}.legend-dot.generated{background:var(--green)}.legend-dot.pending{background:rgba(255,255,255,.3)}.back-link{text-align:center;margin-top:12px}.back-link a{color:var(--cyan);font-size:11px;text-decoration:none}.footer{text-align:center;color:var(--muted);font-size:10px;margin-top:20px}</style></head><body><div class="scanlines"></div><div class="card"><div class="card-bg"></div><div class="vline l"></div><div class="vline r"></div><div class="page-title">ask --pipeline <span>多步骤编排引擎</span></div><div class="flow-chart"><div class="flow-node n1"><div class="icon">🧠</div><div class="name">AI 意图分解</div><div class="desc">LLM 分解多步骤</div></div><div class="flow-connector"></div><div class="flow-node n2"><div class="icon">📐</div><div class="name">命令匹配</div><div class="desc">意图→命令图</div></div><div class="flow-connector"></div><div class="flow-node n3"><div class="icon">🔗</div><div class="name">依赖解析</div><div class="desc">顺序/并行/参数</div></div><div class="flow-connector"></div><div class="flow-node n4"><div class="icon">✅</div><div class="name">预览</div><div class="desc">展示给用户</div></div></div><div class="example-box"><div class="example-label">📋 示例:<span class="example-query"><code>/spec-ask "本期迭代:从 PRD 导入到代码提交,跑完整开发流程"</code></span></div></div><div class="steps"><div class="step"><div class="step-num">1.</div><div class="step-cmd"><span class="cmd-name">speccore doc2spec</span> <span class="cmd-args">-f PRD.docx --iter {iteration}</span></div><div class="step-parse">导入 PRD 文档,AI 分析生成需求规格说明书</div></div><div class="step"><div class="step-num">2.</div><div class="step-cmd"><span class="cmd-name">speccore analyze</span> <span class="cmd-args">--iteration {iteration} --audit</span></div><div class="step-parse">AI 深度分析需求,生成审计报告和可行性评估</div></div><div class="step"><div class="step-num">3.</div><div class="step-cmd"><span class="cmd-name">speccore iteration taskify</span> <span class="cmd-args">-f REQUIREMENT.md</span></div><div class="step-parse">将需求拆分为独立开发任务,确定优先级和依赖关系</div></div><div class="step"><div class="step-num">4.</div><div class="step-cmd"><span class="cmd-name">speccore execute</span> <span class="cmd-args">--auto</span></div><div class="step-parse">按任务计划依次自动执行开发、测试和代码生成</div></div><div class="step"><div class="step-num">5.</div><div class="step-cmd"><span class="cmd-name">speccore verify</span> <span class="cmd-args">--ui</span></div><div class="step-parse">本地冒烟测试:自动登录 + 页面冒烟(默认 module 范围)</div></div><div class="step"><div class="step-num">6.</div><div class="step-cmd"><span class="cmd-name">speccore pr</span> <span class="cmd-args">--auto</span></div><div class="step-parse">代码提交后自动创建 Pull Request,完成交付</div></div></div><div class="confirm-btn">确认执行? (y/N) 或修改参数</div><div class="legend"><div class="legend-item"><span class="legend-dot required"></span> = 必选步骤</div><div class="legend-item"><span class="legend-dot generated"></span> = 已生成</div><div class="legend-item"><span class="legend-dot pending"></span> = 待执行</div><div class="legend-item">→ = 依赖传递</div></div><div class="back-link"><a href="speccore-welcome.html">← 返回 ask 引导页</a></div><div class="footer">由 SpecCore 驱动 · v8.3.295 · 2026-08-31</div></div></body></html>
1
+ <!DOCTYPE html><html lang="zh-CN" data-theme="ocean"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>SpecCore · ask --pipeline</title><style>@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@500;700;900&family=JetBrains+Mono:wght@400;600;700&display=swap');[data-theme="ocean"]{--cyan:#0ea5e9;--bg:#0b1929;--card:rgba(13,31,56,.95);--border:rgba(14,165,233,.15);--text:#bae6fd;--muted:#5b7fa5;--green:#14b8a6;--orange:#f97316;--purple:#6366f1}*{margin:0;padding:0;box-sizing:border-box}body{font-family:'JetBrains Mono',monospace;background:var(--bg);color:var(--text);min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px}.scanlines{position:fixed;inset:0;pointer-events:none;z-index:99;background:repeating-linear-gradient(0deg,transparent,transparent 2px,rgba(0,240,255,.01) 2px,rgba(0,240,255,.01) 4px)}.card{max-width:900px;width:100%;margin:0 auto;background:var(--card);border:1px solid var(--border);border-radius:16px;padding:32px;position:relative;overflow:hidden;z-index:1}.card::before{content:'';position:absolute;top:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent,var(--cyan),transparent);animation:scanX 3s linear infinite}.card::after{content:'';position:absolute;bottom:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent,var(--cyan),transparent);animation:scanX-rev 3s linear infinite}@keyframes scanX{0%{transform:translateX(-100%)}100%{transform:translateX(100%)}}@keyframes scanX-rev{0%{transform:translateX(100%)}100%{transform:translateX(-100%)}}@keyframes scanY{0%{transform:translateY(-100%)}100%{transform:translateY(100%)}}@keyframes scanY-rev{0%{transform:translateY(100%)}100%{transform:translateY(-100%)}}.vline{position:absolute;top:0;width:1px;bottom:0;pointer-events:none}.vline.l{left:0;background:linear-gradient(180deg,transparent,var(--cyan),transparent);animation:scanY-rev 3s linear infinite}.vline.r{right:0;background:linear-gradient(180deg,transparent,var(--cyan),transparent);animation:scanY 3s linear infinite}.card-bg{position:absolute;inset:0;pointer-events:none;z-index:0;background:radial-gradient(ellipse at 25% 10%,rgba(14,165,233,.25) 0%,transparent 70%);animation:cardGlow 3s ease-in-out infinite;transform-origin:top center}@keyframes cardGlow{0%,100%{opacity:.5;transform:scale(1)}50%{opacity:1;transform:scale(1.6)}}.page-title{font-family:'Orbitron',sans-serif;font-size:22px;font-weight:700;text-align:center;color:var(--cyan);margin-bottom:32px;letter-spacing:1px;text-shadow:0 0 20px rgba(14,165,233,.4),0 0 60px rgba(14,165,233,.15);animation:titleGlow 3s ease-in-out infinite}@keyframes titleGlow{0%,100%{text-shadow:0 0 20px rgba(14,165,233,.4),0 0 60px rgba(14,165,233,.15)}50%{text-shadow:0 0 30px rgba(14,165,233,.7),0 0 80px rgba(14,165,233,.3)}}.page-title span{color:var(--text);font-weight:500}.flow-chart{display:flex;align-items:stretch;gap:0;margin-bottom:28px}.flow-node{flex:1;background:rgba(13,31,56,.7);border:1px solid;border-radius:10px;padding:14px 8px;text-align:center;position:relative}.flow-node .icon{font-size:18px;margin-bottom:6px}.flow-node .name{font-size:12px;font-weight:600;margin-bottom:4px}.flow-node .desc{font-size:10px;color:var(--muted)}.flow-node.n1{border-color:rgba(14,165,233,.4);color:var(--cyan)}.flow-node.n2{border-color:rgba(99,102,241,.4);color:var(--purple)}.flow-node.n3{border-color:rgba(14,165,233,.4);color:var(--cyan)}.flow-node.n4{border-color:rgba(20,184,166,.4);color:var(--green)}.flow-connector{width:24px;display:flex;align-items:center;justify-content:center}.flow-connector::after{content:'';width:100%;height:1px;background:linear-gradient(90deg,var(--border),var(--cyan),var(--border))}.example-box{border:1px dashed rgba(14,165,233,.3);border-radius:12px;padding:16px 20px;margin-bottom:24px;background:rgba(14,165,233,.03)}.example-label{font-size:13px;font-weight:600;color:var(--cyan);margin-bottom:8px}.example-query{font-family:'JetBrains Mono',monospace;font-size:14px;color:var(--text)}.example-query code{color:var(--green);font-weight:600}.steps{display:flex;flex-direction:column;gap:12px;margin-bottom:24px}.step{background:rgba(13,31,56,.8);border:1px solid rgba(14,165,233,.12);border-radius:10px;padding:14px 18px}.step-num{font-family:'Orbitron',sans-serif;font-size:14px;font-weight:700;color:var(--green);margin-bottom:6px}.step-cmd{font-family:'JetBrains Mono',monospace;font-size:14px;color:var(--text);margin-bottom:6px}.step-cmd .cmd-name{color:var(--green)}.step-cmd .cmd-args{color:var(--muted)}.step-parse{font-size:12px;color:var(--muted);line-height:1.5}.step-parse::before{content:'→ 解析:';color:var(--cyan)}.confirm-btn{display:block;width:100%;max-width:420px;margin:0 auto 16px;text-align:center;padding:16px;background:linear-gradient(135deg,var(--cyan),#0284c7);border:none;border-radius:40px;color:#fff;font-size:15px;font-weight:600;cursor:pointer;box-shadow:0 0 30px rgba(14,165,233,.3)}.legend{display:flex;justify-content:center;gap:20px;font-size:11px;color:var(--muted);margin-bottom:16px;flex-wrap:wrap}.legend-item{display:flex;align-items:center;gap:4px}.legend-dot{width:8px;height:8px;border-radius:50%;display:inline-block}.legend-dot.required{background:var(--cyan)}.legend-dot.generated{background:var(--green)}.legend-dot.pending{background:rgba(255,255,255,.3)}.back-link{text-align:center;margin-top:12px}.back-link a{color:var(--cyan);font-size:11px;text-decoration:none}.footer{text-align:center;color:var(--muted);font-size:10px;margin-top:20px}</style></head><body><div class="scanlines"></div><div class="card"><div class="card-bg"></div><div class="vline l"></div><div class="vline r"></div><div class="page-title">ask --pipeline <span>多步骤编排引擎</span></div><div class="flow-chart"><div class="flow-node n1"><div class="icon">🧠</div><div class="name">AI 意图分解</div><div class="desc">LLM 分解多步骤</div></div><div class="flow-connector"></div><div class="flow-node n2"><div class="icon">📐</div><div class="name">命令匹配</div><div class="desc">意图→命令图</div></div><div class="flow-connector"></div><div class="flow-node n3"><div class="icon">🔗</div><div class="name">依赖解析</div><div class="desc">顺序/并行/参数</div></div><div class="flow-connector"></div><div class="flow-node n4"><div class="icon">✅</div><div class="name">预览</div><div class="desc">展示给用户</div></div></div><div class="example-box"><div class="example-label">📋 示例:<span class="example-query"><code>/spec-ask "本期迭代:从 PRD 导入到代码提交,跑完整开发流程"</code></span></div></div><div class="steps"><div class="step"><div class="step-num">1.</div><div class="step-cmd"><span class="cmd-name">speccore doc2spec</span> <span class="cmd-args">-f PRD.docx --iter {iteration}</span></div><div class="step-parse">导入 PRD 文档,AI 分析生成需求规格说明书</div></div><div class="step"><div class="step-num">2.</div><div class="step-cmd"><span class="cmd-name">speccore analyze</span> <span class="cmd-args">--iteration {iteration} --audit</span></div><div class="step-parse">AI 深度分析需求,生成审计报告和可行性评估</div></div><div class="step"><div class="step-num">3.</div><div class="step-cmd"><span class="cmd-name">speccore iteration taskify</span> <span class="cmd-args">-f REQUIREMENT.md</span></div><div class="step-parse">将需求拆分为独立开发任务,确定优先级和依赖关系</div></div><div class="step"><div class="step-num">4.</div><div class="step-cmd"><span class="cmd-name">speccore execute</span> <span class="cmd-args">--auto</span></div><div class="step-parse">按任务计划依次自动执行开发、测试和代码生成</div></div><div class="step"><div class="step-num">5.</div><div class="step-cmd"><span class="cmd-name">speccore verify</span> <span class="cmd-args">--ui</span></div><div class="step-parse">本地冒烟测试:自动登录 + 页面冒烟(默认 module 范围)</div></div><div class="step"><div class="step-num">6.</div><div class="step-cmd"><span class="cmd-name">speccore pr</span> <span class="cmd-args">--auto</span></div><div class="step-parse">代码提交后自动创建 Pull Request,完成交付</div></div></div><div class="confirm-btn">确认执行? (y/N) 或修改参数</div><div class="legend"><div class="legend-item"><span class="legend-dot required"></span> = 必选步骤</div><div class="legend-item"><span class="legend-dot generated"></span> = 已生成</div><div class="legend-item"><span class="legend-dot pending"></span> = 待执行</div><div class="legend-item">→ = 依赖传递</div></div><div class="back-link"><a href="speccore-welcome.html">← 返回 ask 引导页</a></div><div class="footer">由 SpecCore 驱动 · v8.3.296 · 2026-08-31</div></div></body></html>
@@ -7,4 +7,4 @@
7
7
  .match-result{padding:10px 0}.match-summary{font-weight:600;font-size:14px;margin-bottom:12px}.match-detail{color:var(--muted);font-size:12px;line-height:1.6}
8
8
  /* Pipeline */
9
9
  .pipeline-title{font-weight:600;margin-bottom:16px}.pipeline-steps{display:flex;flex-direction:column;gap:10px}.pipe-step{display:flex;gap:14px;padding:12px 14px;background:rgba(99,102,241,.04);border-left:2px solid var(--purple);border-radius:0 8px 8px 0}.pipe-step .pipe-num{font-family:Orbitron;font-size:20px;font-weight:900;color:var(--purple);min-width:36px}.pipe-step .pipe-cmd{font-weight:600;color:var(--green)}.pipe-step .pipe-args{color:var(--muted);font-size:11px}.pipe-step .pipe-desc{color:var(--muted);font-size:10px;margin-top:4px}.pipe-step .pipe-dep{font-size:9px;color:var(--orange);margin-top:4px}.confirm-bar{text-align:center;padding:14px;margin-top:16px;background:rgba(99,102,241,.08);border:1px dashed rgba(99,102,241,.2);border-radius:8px;color:var(--purple);font-size:12px}
10
- .footer{text-align:center;color:var(--muted);font-size:10px;margin-top:24px;padding-top:16px;border-top:1px solid rgba(255,255,255,.04)}@keyframes cardGlow{0%,100%{opacity:.5;transform:scale(1)}50%{opacity:1;transform:scale(1.6)}}.card-bg{position:absolute;inset:0;pointer-events:none;z-index:0;background:radial-gradient(ellipse at 25% 10%,rgba(14,165,233,.25) 0%,transparent 70%);animation:cardGlow 3s ease-in-out infinite;transform-origin:top center}@keyframes titleGlow{0%,100%{text-shadow:0 0 20px rgba(14,165,233,.4),0 0 60px rgba(14,165,233,.15)}50%{text-shadow:0 0 30px rgba(14,165,233,.7),0 0 80px rgba(14,165,233,.3)}}h1,h2{text-shadow:0 0 20px rgba(14,165,233,.4),0 0 60px rgba(14,165,233,.15);animation:titleGlow 3s ease-in-out infinite}</style></head><body><div class="scanlines"></div><div class="card"><div class="card-bg"></div><div class="vline l"></div><div class="vline r"></div><h1>SPECCORE ASK</h1><div class="query">"dashboard"</div><div class="badge">🎯 意图匹配</div><div class="match-result"><div class="match-summary">✅ 推荐: dashboard</div><div class="match-detail">🎯 推荐命令: <code>speccore dashboard<br>项目仪表盘:期次状态/进度/健康度,--scope</code> global 全量视图<br><br>用法: <code>speccore dashboard</code> [--scope global|iteration] [--export html] [--health] [--lifecycle]<br><br>示例:<br> $ <code>speccore dashboard<br></code> $ <code>speccore dashboard</code> --scope global --export html</div></div><div class="footer">由 SpecCore 驱动 v8.3.295 · 2026-08-31</div></div></body></html>
10
+ .footer{text-align:center;color:var(--muted);font-size:10px;margin-top:24px;padding-top:16px;border-top:1px solid rgba(255,255,255,.04)}@keyframes cardGlow{0%,100%{opacity:.5;transform:scale(1)}50%{opacity:1;transform:scale(1.6)}}.card-bg{position:absolute;inset:0;pointer-events:none;z-index:0;background:radial-gradient(ellipse at 25% 10%,rgba(14,165,233,.25) 0%,transparent 70%);animation:cardGlow 3s ease-in-out infinite;transform-origin:top center}@keyframes titleGlow{0%,100%{text-shadow:0 0 20px rgba(14,165,233,.4),0 0 60px rgba(14,165,233,.15)}50%{text-shadow:0 0 30px rgba(14,165,233,.7),0 0 80px rgba(14,165,233,.3)}}h1,h2{text-shadow:0 0 20px rgba(14,165,233,.4),0 0 60px rgba(14,165,233,.15);animation:titleGlow 3s ease-in-out infinite}</style></head><body><div class="scanlines"></div><div class="card"><div class="card-bg"></div><div class="vline l"></div><div class="vline r"></div><h1>SPECCORE ASK</h1><div class="query">"dashboard"</div><div class="badge">🎯 意图匹配</div><div class="match-result"><div class="match-summary">✅ 推荐: dashboard</div><div class="match-detail">🎯 推荐命令: <code>speccore dashboard<br>项目仪表盘:期次状态/进度/健康度,--scope</code> global 全量视图<br><br>用法: <code>speccore dashboard</code> [--scope global|iteration] [--export html] [--health] [--lifecycle]<br><br>示例:<br> $ <code>speccore dashboard<br></code> $ <code>speccore dashboard</code> --scope global --export html</div></div><div class="footer">由 SpecCore 驱动 v8.3.296 · 2026-08-31</div></div></body></html>