@loviusc/sa-spec 0.2.6 → 0.2.7
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/README.en.md +17 -1
- package/README.ja.md +17 -1
- package/README.ko.md +17 -1
- package/README.zh-CN.md +17 -1
- package/README.zh-TW.md +30 -1
- package/install.bat +43 -0
- package/install.sh +43 -0
- package/package.json +8 -2
- package/readme.md +30 -1
- package/src/cli.js +176 -0
- package/src/pdf.js +605 -0
- package/templates/AGENTS.md +2 -1
- package/templates/readme.md +30 -1
- package/templates/skills/system-analysis/SKILL.md +2 -1
- package/uninstall.bat +22 -0
- package/uninstall.sh +15 -0
- package/update.bat +43 -0
- package/update.sh +30 -0
package/README.en.md
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
Before code-generation AI tools (OpenSpec, Claude Code, OpenAI Codex, Gemini CLI, Cursor) start implementation, `sa-spec` clarifies scopes, boundaries, data schemas, API contracts, permission matrices, and acceptance criteria to ensure that subsequent coding proceeds within safe, well-defined boundaries.
|
|
24
24
|
|
|
25
|
-
The AI workflow operates on
|
|
25
|
+
The AI workflow operates on five standard commands (with shorthand aliases):
|
|
26
26
|
|
|
27
27
|
| Command | Alias | Purpose | Description |
|
|
28
28
|
|---|---|---|---|
|
|
@@ -30,6 +30,7 @@ The AI workflow operates on four standard commands (with shorthand aliases):
|
|
|
30
30
|
| `spec:propose` | `scp` | Propose | Summarizes objectives, user stories, workflows, and open items without altering existing requirements. |
|
|
31
31
|
| `spec:analyze` | `sca` | Analyze & Draft | Checks for conflicts against existing requirements, APIs, and DB schemas; drafts SRS, UML, OpenAPI, and DB schema. |
|
|
32
32
|
| `spec:commit` | `scc` | Commit & Archive | Archives the user-verified version, assigns sequential ID, updates indexes, and generates a local Git commit. |
|
|
33
|
+
| `spec:pdf` | `spd` | Export PDF | Exports a single `.md` file or an entire requirement folder (`REQ-001`) into a print-ready PDF specification. |
|
|
33
34
|
|
|
34
35
|
Each finalized requirement produces a standardized, self-contained directory:
|
|
35
36
|
|
|
@@ -66,6 +67,20 @@ sa-spec defines what to build and its constraints; AI code tools build it right.
|
|
|
66
67
|
- npm
|
|
67
68
|
- Git
|
|
68
69
|
|
|
70
|
+
### Cross-Platform Standalone Scripts (`.sh` / `.bat`)
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# macOS / Linux
|
|
74
|
+
./install.sh # Install sa-spec
|
|
75
|
+
./update.sh # Update sa-spec to latest version
|
|
76
|
+
./uninstall.sh # Uninstall sa-spec
|
|
77
|
+
|
|
78
|
+
# Windows (double-click or run via CMD / PowerShell)
|
|
79
|
+
install.bat
|
|
80
|
+
update.bat
|
|
81
|
+
uninstall.bat
|
|
82
|
+
```
|
|
83
|
+
|
|
69
84
|
### Global Installation via npm
|
|
70
85
|
|
|
71
86
|
```bash
|
|
@@ -88,6 +103,7 @@ sa-spec --version
|
|
|
88
103
|
| `sa-spec init [--full] [--lang <lang>]` | Scaffolds the workspace skeleton (Supports `zh-TW`, `en`, `zh-CN`, `ja`, `ko`). |
|
|
89
104
|
| `sa-spec new [--id REQ-001] --name login [--branch]` | Creates a requirement folder template. Omitting `--id` starts a draft in a feature branch (`feat/<name>`). |
|
|
90
105
|
| `sa-spec finalize --name login` | Assigns the latest available sequential ID to a draft requirement, renames directory, substitutes `{{REQ_ID}}`, and regenerates index. |
|
|
106
|
+
| `sa-spec pdf <target> [--split] [--output out.pdf]` | Converts a `.md` file or requirement (`REQ-001`) to a merged or split PDF (`--all` exports all). |
|
|
91
107
|
| `sa-spec reindex` | Scans `project/REQ-*` directories and dynamically rebuilds `project/index.md` sorted by numerical ID. |
|
|
92
108
|
| `sa-spec check` | Validates required project files and system spec status. |
|
|
93
109
|
| `sa-spec status` | Displays overview status of requirements and specs. |
|
package/README.ja.md
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
OpenSpec、Claude Code、OpenAI Codex、Gemini CLI などの AI 開発ツールがコード実装を始める前に、要件のスコープ、境界線、データモデル、API 規約、権限マトリクス、受入条件を事前に明確化し、安全で堅牢な開発境界を定義します。
|
|
24
24
|
|
|
25
|
-
AI ワークフローは
|
|
25
|
+
AI ワークフローは 5 つの標準コマンド(エイリアス対応)で動作します:
|
|
26
26
|
|
|
27
27
|
| コマンド | エイリアス | 目的 | 説明 |
|
|
28
28
|
|---|---|---|---|
|
|
@@ -30,6 +30,7 @@ AI ワークフローは 4 つの標準コマンド(エイリアス対応)
|
|
|
30
30
|
| `spec:propose` | `scp` | 要件提案 | 既存の仕様を変更せず、目的・概要・初期フロー・確認事項を整理します。 |
|
|
31
31
|
| `spec:analyze` | `sca` | 分析と起草 | 既存の要件、API、DB スキーマとの競合をチェックし、問題がなければ SRS、UML、OpenAPI、DB スキーマのドラフトを作成します。 |
|
|
32
32
|
| `spec:commit` | `scc` | 確認と保存 | ユーザーが承認したバージョンを正式に採番・保存し、インデックスを更新して Git コミットを作成します。 |
|
|
33
|
+
| `spec:pdf` | `spd` | PDF 出力 | 指定した `.md` ファイルまたは要件ディレクトリ(`REQ-001` など)を PDF 仕様書として出力します。 |
|
|
33
34
|
|
|
34
35
|
各要件フォルダーには以下の標準成果物が生成されます:
|
|
35
36
|
|
|
@@ -66,6 +67,20 @@ sa-spec が仕様と境界を明確にし、AI 開発ツールが正しく実装
|
|
|
66
67
|
- npm
|
|
67
68
|
- Git
|
|
68
69
|
|
|
70
|
+
### クロスプラットフォーム独立スクリプト (`.sh` / `.bat`)
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# macOS / Linux
|
|
74
|
+
./install.sh # インストール
|
|
75
|
+
./update.sh # 最新版へアップデート
|
|
76
|
+
./uninstall.sh # アンインストール
|
|
77
|
+
|
|
78
|
+
# Windows(ダブルクリック、または CMD / PowerShell で実行)
|
|
79
|
+
install.bat
|
|
80
|
+
update.bat
|
|
81
|
+
uninstall.bat
|
|
82
|
+
```
|
|
83
|
+
|
|
69
84
|
### グローバルインストール
|
|
70
85
|
|
|
71
86
|
```bash
|
|
@@ -88,6 +103,7 @@ sa-spec --version
|
|
|
88
103
|
| `sa-spec init [--full] [--lang <lang>]` | ワークスペースの骨格を初期化します(`zh-TW`、`en`、`zh-CN`、`ja`、`ko` に対応)。 |
|
|
89
104
|
| `sa-spec new [--id REQ-001] --name login [--branch]` | 要件テンプレートを作成します。`--id` を省略すると、Git 分岐を活用したドラフトモード(`feat/<name>`)で開始します。 |
|
|
90
105
|
| `sa-spec finalize --name login` | ドラフト要件に最新の連続番号を割り当て、フォルダー名を変更し、`{{REQ_ID}}` を置換してインデックスを再生成します。 |
|
|
106
|
+
| `sa-spec pdf <target> [--split] [--output out.pdf]` | 指定した `.md` または要件(`REQ-001`)を統合または個別 PDF として出力します(`--all` で全件出力)。 |
|
|
91
107
|
| `sa-spec reindex` | `project/REQ-*` ディレクトリをスキャンし、数値順にソートされた `project/index.md` を自動再構築します。 |
|
|
92
108
|
| `sa-spec check` | 必須ファイルとシステム仕様の状態を検証します。 |
|
|
93
109
|
| `sa-spec status` | 要件とシステム仕様の概要ステータスを表示します。 |
|
package/README.ko.md
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
OpenSpec, Claude Code, OpenAI Codex, Gemini CLI 등 코드 생성 AI 도구가 구현을 시작하기 전에, 요구사항의 범위, 경계, 데이터 모델, API 인터페이스, 권한 매트릭스, 인수 조건을 사전에 명확히 정의하여 후속 개발이 안전하고 명확한 경계 내에서 진행되도록 보장합니다.
|
|
24
24
|
|
|
25
|
-
AI 워크플로는
|
|
25
|
+
AI 워크플로는 5가지 표준 명령어(별칭 지원)를 기반으로 동작합니다:
|
|
26
26
|
|
|
27
27
|
| 표준 명령어 | 별칭 | 단계 | 설명 |
|
|
28
28
|
|---|---|---|---|
|
|
@@ -30,6 +30,7 @@ AI 워크플로는 4가지 표준 명령어(별칭 지원)를 기반으로 동
|
|
|
30
30
|
| `spec:propose` | `scp` | 요구사항 제안 | 기존 사양을 변경하지 않고 목표, 요약, 초기 흐름 및 미확인 사항을 정리합니다. |
|
|
31
31
|
| `spec:analyze` | `sca` | 분석 및 초안 작성 | 기존 요구사항, API, DB와의 충돌 여부를 검사하고, 문제가 없을 때만 SRS, UML, OpenAPI, DB Schema 초안을 작성합니다. |
|
|
32
32
|
| `spec:commit` | `scc` | 확인 및 보관 | 사용자가 승인한 버전을 정식 번호로 보관하고, 인덱스를 갱신하며 로컬 Git 커밋을 생성합니다. |
|
|
33
|
+
| `spec:pdf` | `spd` | PDF 내보내기 | 지정한 `.md` 파일이나 요구사항 폴더(`REQ-001` 등)를 PDF 사양서로 변환합니다. |
|
|
33
34
|
|
|
34
35
|
보관된 각 요구사항은 독립적이고 완전한 폴더로 구성됩니다:
|
|
35
36
|
|
|
@@ -66,6 +67,20 @@ sa-spec이 요구사항과 경계를 명확히 정의하고, AI 개발 도구가
|
|
|
66
67
|
- npm
|
|
67
68
|
- Git
|
|
68
69
|
|
|
70
|
+
### 크로스 플랫폼 독립 스크립트 (`.sh` / `.bat`)
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# macOS / Linux
|
|
74
|
+
./install.sh # 설치 (install)
|
|
75
|
+
./update.sh # 최신 버전 업데이트 (update)
|
|
76
|
+
./uninstall.sh # 제거 (uninstall)
|
|
77
|
+
|
|
78
|
+
# Windows (더블클릭 또는 CMD / PowerShell 실행)
|
|
79
|
+
install.bat
|
|
80
|
+
update.bat
|
|
81
|
+
uninstall.bat
|
|
82
|
+
```
|
|
83
|
+
|
|
69
84
|
### npm 글로벌 설치
|
|
70
85
|
|
|
71
86
|
```bash
|
|
@@ -88,6 +103,7 @@ sa-spec --version
|
|
|
88
103
|
| `sa-spec init [--full] [--lang <lang>]` | 워크스페이스 골격을 생성합니다 (`zh-TW`, `en`, `zh-CN`, `ja`, `ko` 지원, 기본값 `zh-TW`). |
|
|
89
104
|
| `sa-spec new [--id REQ-001] --name login [--branch]` | 요구사항 템플릿 폴더를 생성합니다. `--id` 생략 시 다중 사용자 초안 브랜치(`feat/<name>`)로 생성됩니다. |
|
|
90
105
|
| `sa-spec finalize --name login` | 초안 요구사항에 최신 연속 번호를 할당하고 폴더명 변경, `{{REQ_ID}}` 치환 및 인덱스를 자동 재구축합니다. |
|
|
106
|
+
| `sa-spec pdf <target> [--split] [--output out.pdf]` | 지정한 `.md` 파일 또는 요구사항(`REQ-001`)을 통합 또는 개별 PDF로 변환합니다 (`--all` 전체 내보내기). |
|
|
91
107
|
| `sa-spec reindex` | `project/REQ-*` 디렉토리를 스캔하여 번호순으로 정렬된 `project/index.md`를 동적으로 재생성합니다. |
|
|
92
108
|
| `sa-spec check` | 필수 파일과 시스템 사양 상태를 검증합니다. |
|
|
93
109
|
| `sa-spec status` | 요구사항 및 시스템 사양 현황을 확인합니다. |
|
package/README.zh-CN.md
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
在 OpenSpec、Claude Code、OpenAI Codex、Gemini CLI 等代码生成工具介入之前,`sa-spec` 先行厘清需求边界、业务规则、数据模型、API 契约、权限矩阵与验收条件,避免 AI 编码时随心所欲破坏系统架构。
|
|
24
24
|
|
|
25
|
-
AI
|
|
25
|
+
AI 工作流使用五个标准指令(支持简写别名):
|
|
26
26
|
|
|
27
27
|
| 标准指令 | 简写 | 阶段 | 说明 |
|
|
28
28
|
|---|---|---|---|
|
|
@@ -30,6 +30,7 @@ AI 工作流使用四个标准指令(支持简写别名):
|
|
|
30
30
|
| `spec:propose` | `scp` | 提出需求 | 整理需求摘要、目标、初步流程与待确认事项,不修改已有规格。 |
|
|
31
31
|
| `spec:analyze` | `sca` | 分析与起草 | 检查新需求是否与既有需求、API、DB 发生冲突;无冲突才产出 SRS、UML、OpenAPI 与 DB Schema 草案。 |
|
|
32
32
|
| `spec:commit` | `scc` | 确认归档 | 用户确认后正式定号归档,更新索引与系统总表,并自动创建本地 Git commit。 |
|
|
33
|
+
| `spec:pdf` | `spd` | 导出 PDF | 将指定的 `.md` 文件或需求目录(如 `REQ-001`)一键导出为 PDF 规格说明书。 |
|
|
33
34
|
|
|
34
35
|
每个归档的需求固定包含一个独立完整的文件夹:
|
|
35
36
|
|
|
@@ -66,6 +67,20 @@ sa-spec 先把需求说清楚、定好边界;AI 代码工具再把需求实现
|
|
|
66
67
|
- npm
|
|
67
68
|
- Git
|
|
68
69
|
|
|
70
|
+
### 跨平台独立脚本 (`.sh` / `.bat`)
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# macOS / Linux
|
|
74
|
+
./install.sh # 安装 sa-spec
|
|
75
|
+
./update.sh # 更新 sa-spec 至最新版本
|
|
76
|
+
./uninstall.sh # 卸载 sa-spec
|
|
77
|
+
|
|
78
|
+
# Windows(直接双击或在 CMD / PowerShell 执行)
|
|
79
|
+
install.bat
|
|
80
|
+
update.bat
|
|
81
|
+
uninstall.bat
|
|
82
|
+
```
|
|
83
|
+
|
|
69
84
|
### 全局安装
|
|
70
85
|
|
|
71
86
|
```bash
|
|
@@ -88,6 +103,7 @@ sa-spec --version
|
|
|
88
103
|
| `sa-spec init [--full] [--lang <lang>]` | 初始化工作台骨架(支持 `zh-TW`、`en`、`zh-CN`、`ja`、`ko`,默认 `zh-TW`)。 |
|
|
89
104
|
| `sa-spec new [--id REQ-001] --name login [--branch]` | 创建需求模板文件夹。省略 `--id` 时自动切入多人协作草稿分支(`feat/<name>`)。 |
|
|
90
105
|
| `sa-spec finalize --name login` | 将草稿需求正式定号归档为最新序号,自动重命名目录、替换 `{{REQ_ID}}` 并重构索引。 |
|
|
106
|
+
| `sa-spec pdf <target> [--split] [--output out.pdf]` | 将指定 `.md` 或需求(如 `REQ-001`)合并或拆分导出为 PDF(`--all` 导出全项目)。 |
|
|
91
107
|
| `sa-spec reindex` | 扫描 `project/REQ-*` 目录,按序数字顺序动态重新生成 `project/index.md`。 |
|
|
92
108
|
| `sa-spec check` | 检查项目必要文件与规格完整性。 |
|
|
93
109
|
| `sa-spec status` | 查看系统规格与需求总览状态。 |
|
package/README.zh-TW.md
CHANGED
|
@@ -32,6 +32,7 @@ sa-spec init --full
|
|
|
32
32
|
- `spec:propose` (`scp`) $\to$ **AI 自動建立 feature 分支與草稿目錄**(零手動指令)
|
|
33
33
|
- `spec:analyze` (`sca`) $\to$ AI 衝突檢查並產出規格文件草稿
|
|
34
34
|
- `spec:commit` (`scc`) $\to$ **AI 自動向主分支領取新號並自動合併回主分支!**
|
|
35
|
+
- `spec:pdf` (`spd`) $\to$ **一鍵將單一 `.md` 或整個需求規格書轉為高品質 PDF!**
|
|
35
36
|
|
|
36
37
|
👉 **Language Guides: [English](./README.en.md) | [繁體中文](./README.zh-TW.md) | [简体中文](./README.zh-CN.md) | [日本語](./README.ja.md) | [한국어](./README.ko.md)**
|
|
37
38
|
|
|
@@ -39,7 +40,7 @@ sa-spec init --full
|
|
|
39
40
|
|
|
40
41
|
`sa-spec` 是一個輕量的系統分析文件工作台,用固定流程把需求想法整理成可確認、可歸檔、可檢查的文件。
|
|
41
42
|
|
|
42
|
-
AI
|
|
43
|
+
AI 工作流使用五個指令,並支援簡寫別名(兩者等價):
|
|
43
44
|
|
|
44
45
|
| 標準指令 | 簡寫 | 說明 |
|
|
45
46
|
|---|---|---|
|
|
@@ -47,6 +48,7 @@ AI 工作流只使用四個指令,並支援簡寫別名(兩者等價):
|
|
|
47
48
|
| `spec:propose` | `scp` | 提出需求並整理摘要 |
|
|
48
49
|
| `spec:analyze` | `sca` | 衝突檢查與規格起草 |
|
|
49
50
|
| `spec:commit` | `scc` | 確認歸檔並建立 Git commit |
|
|
51
|
+
| `spec:pdf` | `spd` | 將指定 `.md` 或需求目錄匯出為 PDF |
|
|
50
52
|
|
|
51
53
|
每個需求歸檔後固定包含:
|
|
52
54
|
|
|
@@ -96,6 +98,25 @@ npm --version
|
|
|
96
98
|
git --version
|
|
97
99
|
```
|
|
98
100
|
|
|
101
|
+
## 一鍵腳本管理(安裝 / 更新 / 解除安裝)
|
|
102
|
+
|
|
103
|
+
專案內建獨立的跨平台腳本,直接執行對應檔案即可完成安裝、更新或解除安裝:
|
|
104
|
+
|
|
105
|
+
### macOS / Linux (`.sh`)
|
|
106
|
+
```bash
|
|
107
|
+
./install.sh # 安裝 sa-spec
|
|
108
|
+
./update.sh # 更新 sa-spec 至最新版本
|
|
109
|
+
./uninstall.sh # 解除安裝 sa-spec
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Windows (`.bat`)
|
|
113
|
+
可直接雙擊執行,或在 CMD / PowerShell 輸入:
|
|
114
|
+
```bat
|
|
115
|
+
install.bat &:: 安裝 sa-spec
|
|
116
|
+
update.bat &:: 更新 sa-spec 至最新版本
|
|
117
|
+
uninstall.bat &:: 解除安裝 sa-spec
|
|
118
|
+
```
|
|
119
|
+
|
|
99
120
|
## macOS 安裝
|
|
100
121
|
|
|
101
122
|
使用 npm 安裝:
|
|
@@ -275,6 +296,7 @@ sa-spec init --full
|
|
|
275
296
|
| `sa-spec init [--full] [--lang zh-TW|en|zh-CN|ja|ko]` | 建立文件骨架與規則文件(支援 5 種語言,預設 `zh-TW`)。 |
|
|
276
297
|
| `sa-spec new [--id REQ-001] --name login [--branch]` | 建立需求資料夾與空白文件模板(未帶 `--id` 時啟用多人草稿模式,可選 `--branch` 自動建立並切換至 `feat/<name>` 分支)。 |
|
|
277
298
|
| `sa-spec finalize --name login` | 將草稿需求定號歸檔為最新序號(自動計算最新號碼、重命名目錄、批次置換 `{{REQ_ID}}` 並重建索引)。 |
|
|
299
|
+
| `sa-spec pdf <target> [--split] [--output out.pdf]` | 將指定 `.md` 轉為同名 `.pdf`,或將需求編號/目錄(如 `REQ-001`)整包合併匯出成一份完整規格書 PDF(加 `--split` 則各檔獨立轉出,`--all` 匯出全專案)。 |
|
|
278
300
|
| `sa-spec reindex` | 掃描 `project/REQ-*` 目錄,依序號重新生成乾淨無衝突的 `project/index.md`。 |
|
|
279
301
|
| `sa-spec check` | 檢查基本文件是否存在。 |
|
|
280
302
|
| `sa-spec status` | 顯示系統規格與需求索引狀態。 |
|
|
@@ -324,6 +346,7 @@ Markdown、PlantUML、YAML、GitLens 這類套件可以提升閱讀與檢查體
|
|
|
324
346
|
| AI 工作流程 | `spec:propose` / `scp` | VS Code AI 對話側邊欄 | 整理使用者提出的需求。 |
|
|
325
347
|
| AI 工作流程 | `spec:analyze` / `sca` | VS Code AI 對話側邊欄 | 檢查衝突並產出待確認草稿。 |
|
|
326
348
|
| AI 工作流程 | `spec:commit` / `scc` | VS Code AI 對話側邊欄 | 歸檔已確認版本並建立本機 Git commit。 |
|
|
349
|
+
| AI 工作流程 | `spec:pdf` / `spd` | VS Code AI 對話側邊欄 | 將指定 `.md` 或需求目錄匯出為 PDF 規格書。 |
|
|
327
350
|
|
|
328
351
|
建議操作順序:
|
|
329
352
|
|
|
@@ -353,6 +376,7 @@ spec:commit # 或簡寫 scc
|
|
|
353
376
|
| `spec:propose` | `scp` | 使用者提出需求後,整理需求摘要、目標、初步流程與待確認事項。 |
|
|
354
377
|
| `spec:analyze` | `sca` | 檢查是否與既有需求衝突;無衝突才產出 SRS、UML、OpenAPI、DB Schema 草稿。 |
|
|
355
378
|
| `spec:commit` | `scc` | 使用者確認無衝突版本後,將版本歸檔到 `project/`,並建立本機 Git commit。 |
|
|
379
|
+
| `spec:pdf` | `spd` | 將指定的 `.md` 檔或需求(如 `REQ-001`)匯出為高品質 PDF。 |
|
|
356
380
|
|
|
357
381
|
狀態機:
|
|
358
382
|
|
|
@@ -725,6 +749,11 @@ git push origin master
|
|
|
725
749
|
|
|
726
750
|
## 更新紀錄
|
|
727
751
|
|
|
752
|
+
### v0.2.7
|
|
753
|
+
- **新增 Markdown 轉 PDF 指令(`spec:pdf` / `spd` / `sa-spec pdf`)與跨平台獨立安裝腳本**:
|
|
754
|
+
- **Markdown 轉 PDF 支援**:新增 AI 指令 `spec:pdf`(簡寫 `spd`)與 CLI 指令 `sa-spec pdf <target> [--split] [--output out.pdf]`(及 `--all`),支援單一 `.md` 轉換、需求目錄整包合併匯出成一份完整規格書 PDF,或加 `--split` 各檔獨立匯出。
|
|
755
|
+
- **跨平台獨立管理腳本**:新增 macOS / Linux(`install.sh`、`update.sh`、`uninstall.sh`)與 Windows(`install.bat`、`update.bat`、`uninstall.bat`)6 個獨立執行腳本,一鍵完成安裝、更新與解除安裝。
|
|
756
|
+
|
|
728
757
|
### v0.2.6
|
|
729
758
|
- **Git Remote 自動推送鐵律(Remote Auto-Push)**:
|
|
730
759
|
- **`scp` 建立分支自動推上遠端**:執行 `spec:propose`(`scp`)或 `sa-spec new --branch` 建立 feature 分支後,自動偵測是否有設定 Git remote;若有則立即將新分支推送至遠端(`git push -u <remote> feat/<name>`)。
|
package/install.bat
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
setlocal EnableDelayedExpansion
|
|
3
|
+
chcp 65001 >nul 2>&1
|
|
4
|
+
|
|
5
|
+
set "PACKAGE_NAME=@loviusc/sa-spec"
|
|
6
|
+
set "SCRIPT_DIR=%~dp0"
|
|
7
|
+
|
|
8
|
+
echo [sa-spec] 開始安裝 (Windows)...
|
|
9
|
+
|
|
10
|
+
where node >nul 2>&1
|
|
11
|
+
if %errorlevel% neq 0 (
|
|
12
|
+
echo [錯誤] 未偵測到 Node.js,請先安裝 Node.js 18 或以上版本: https://nodejs.org/
|
|
13
|
+
pause
|
|
14
|
+
exit /b 1
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
where npm >nul 2>&1
|
|
18
|
+
if %errorlevel% neq 0 (
|
|
19
|
+
echo [錯誤] 未偵測到 npm,請確認 Node.js 安裝是否完整。
|
|
20
|
+
pause
|
|
21
|
+
exit /b 1
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
set "IS_SOURCE=0"
|
|
25
|
+
if exist "%SCRIPT_DIR%package.json" (
|
|
26
|
+
findstr /C:"@loviusc/sa-spec" "%SCRIPT_DIR%package.json" >nul 2>&1
|
|
27
|
+
if !errorlevel! equ 0 set "IS_SOURCE=1"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
if "%IS_SOURCE%"=="1" (
|
|
31
|
+
echo [安裝] 偵測到 sa-spec 原始碼目錄,執行 npm install 與 npm link...
|
|
32
|
+
pushd "%SCRIPT_DIR%"
|
|
33
|
+
call npm install
|
|
34
|
+
call npm link
|
|
35
|
+
popd
|
|
36
|
+
) else (
|
|
37
|
+
echo [安裝] 從 npm 全域安裝 %PACKAGE_NAME%...
|
|
38
|
+
call npm install -g %PACKAGE_NAME%
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
echo [完成] sa-spec 安裝完成!
|
|
42
|
+
call sa-spec --version 2>nul
|
|
43
|
+
pause
|
package/install.sh
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
PACKAGE_NAME="@loviusc/sa-spec"
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
|
|
7
|
+
echo "[sa-spec] 開始安裝 (${OSTYPE:-macOS/Linux})..."
|
|
8
|
+
|
|
9
|
+
if ! command -v node >/dev/null 2>&1; then
|
|
10
|
+
echo "[錯誤] 未偵測到 Node.js,請先安裝 Node.js 18 或以上版本: https://nodejs.org/"
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
if ! command -v npm >/dev/null 2>&1; then
|
|
15
|
+
echo "[錯誤] 未偵測到 npm,請確認 Node.js 與 npm 是否安裝完整。"
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# 若全域目錄無寫入權限,自動切換至 ~/.npm-global 避免 sudo 權限問題
|
|
20
|
+
NPM_PREFIX="$(npm prefix -g 2>/dev/null || echo "")"
|
|
21
|
+
if [ -n "${NPM_PREFIX}" ] && [ ! -w "${NPM_PREFIX}" ] && [ ! -w "${NPM_PREFIX}/lib" ]; then
|
|
22
|
+
echo "[權限設定] 偵測到全域目錄 (${NPM_PREFIX}) 無寫入權限,自動設定 ~/.npm-global..."
|
|
23
|
+
mkdir -p "${HOME}/.npm-global"
|
|
24
|
+
npm config set prefix "${HOME}/.npm-global"
|
|
25
|
+
export PATH="${HOME}/.npm-global/bin:${PATH}"
|
|
26
|
+
for rc in "${HOME}/.zshrc" "${HOME}/.bashrc"; do
|
|
27
|
+
if [ -f "${rc}" ] && ! grep -q '.npm-global/bin' "${rc}" 2>/dev/null; then
|
|
28
|
+
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> "${rc}"
|
|
29
|
+
fi
|
|
30
|
+
done
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
if [ -f "${SCRIPT_DIR}/package.json" ] && grep -q '"@loviusc/sa-spec"' "${SCRIPT_DIR}/package.json" 2>/dev/null; then
|
|
34
|
+
echo "[安裝] 偵測到 sa-spec 原始碼目錄,執行 npm install 與 npm link..."
|
|
35
|
+
cd "${SCRIPT_DIR}"
|
|
36
|
+
npm install
|
|
37
|
+
npm link
|
|
38
|
+
else
|
|
39
|
+
echo "[安裝] 從 npm 全域安裝 ${PACKAGE_NAME}..."
|
|
40
|
+
npm install -g "${PACKAGE_NAME}"
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
echo "[完成] sa-spec 安裝成功!目前版本: $(sa-spec --version 2>/dev/null || echo '請重開終端機以套用 PATH')"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loviusc/sa-spec",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "Personal system analysis documentation workspace CLI",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -21,11 +21,17 @@
|
|
|
21
21
|
"bin/",
|
|
22
22
|
"src/",
|
|
23
23
|
"templates/",
|
|
24
|
+
"install.sh",
|
|
25
|
+
"update.sh",
|
|
26
|
+
"uninstall.sh",
|
|
27
|
+
"install.bat",
|
|
28
|
+
"update.bat",
|
|
29
|
+
"uninstall.bat",
|
|
24
30
|
"readme.md",
|
|
25
31
|
"README*.md"
|
|
26
32
|
],
|
|
27
33
|
"scripts": {
|
|
28
|
-
"check": "node --check src/cli.js && node --check bin/sa-spec.js"
|
|
34
|
+
"check": "node --check src/cli.js && node --check src/pdf.js && node --check bin/sa-spec.js"
|
|
29
35
|
},
|
|
30
36
|
"keywords": [
|
|
31
37
|
"system-analysis",
|
package/readme.md
CHANGED
|
@@ -32,6 +32,7 @@ sa-spec init --full
|
|
|
32
32
|
- `spec:propose` (`scp`) $\to$ **AI 自動建立 feature 分支與草稿目錄**(零手動指令)
|
|
33
33
|
- `spec:analyze` (`sca`) $\to$ AI 衝突檢查並產出規格文件草稿
|
|
34
34
|
- `spec:commit` (`scc`) $\to$ **AI 自動向主分支領取新號並自動合併回主分支!**
|
|
35
|
+
- `spec:pdf` (`spd`) $\to$ **一鍵將單一 `.md` 或整個需求規格書轉為高品質 PDF!**
|
|
35
36
|
|
|
36
37
|
👉 **Language Guides: [English](./README.en.md) | [繁體中文](./README.zh-TW.md) | [简体中文](./README.zh-CN.md) | [日本語](./README.ja.md) | [한국어](./README.ko.md)**
|
|
37
38
|
|
|
@@ -39,7 +40,7 @@ sa-spec init --full
|
|
|
39
40
|
|
|
40
41
|
`sa-spec` 是一個輕量的系統分析文件工作台,用固定流程把需求想法整理成可確認、可歸檔、可檢查的文件。
|
|
41
42
|
|
|
42
|
-
AI
|
|
43
|
+
AI 工作流使用五個指令,並支援簡寫別名(兩者等價):
|
|
43
44
|
|
|
44
45
|
| 標準指令 | 簡寫 | 說明 |
|
|
45
46
|
|---|---|---|
|
|
@@ -47,6 +48,7 @@ AI 工作流只使用四個指令,並支援簡寫別名(兩者等價):
|
|
|
47
48
|
| `spec:propose` | `scp` | 提出需求並整理摘要 |
|
|
48
49
|
| `spec:analyze` | `sca` | 衝突檢查與規格起草 |
|
|
49
50
|
| `spec:commit` | `scc` | 確認歸檔並建立 Git commit |
|
|
51
|
+
| `spec:pdf` | `spd` | 將指定 `.md` 或需求目錄匯出為 PDF |
|
|
50
52
|
|
|
51
53
|
每個需求歸檔後固定包含:
|
|
52
54
|
|
|
@@ -96,6 +98,25 @@ npm --version
|
|
|
96
98
|
git --version
|
|
97
99
|
```
|
|
98
100
|
|
|
101
|
+
## 一鍵腳本管理(安裝 / 更新 / 解除安裝)
|
|
102
|
+
|
|
103
|
+
專案內建獨立的跨平台腳本,直接執行對應檔案即可完成安裝、更新或解除安裝:
|
|
104
|
+
|
|
105
|
+
### macOS / Linux (`.sh`)
|
|
106
|
+
```bash
|
|
107
|
+
./install.sh # 安裝 sa-spec
|
|
108
|
+
./update.sh # 更新 sa-spec 至最新版本
|
|
109
|
+
./uninstall.sh # 解除安裝 sa-spec
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Windows (`.bat`)
|
|
113
|
+
可直接雙擊執行,或在 CMD / PowerShell 輸入:
|
|
114
|
+
```bat
|
|
115
|
+
install.bat &:: 安裝 sa-spec
|
|
116
|
+
update.bat &:: 更新 sa-spec 至最新版本
|
|
117
|
+
uninstall.bat &:: 解除安裝 sa-spec
|
|
118
|
+
```
|
|
119
|
+
|
|
99
120
|
## macOS 安裝
|
|
100
121
|
|
|
101
122
|
使用 npm 安裝:
|
|
@@ -275,6 +296,7 @@ sa-spec init --full
|
|
|
275
296
|
| `sa-spec init [--full] [--lang zh-TW|en|zh-CN|ja|ko]` | 建立文件骨架與規則文件(支援 5 種語言,預設 `zh-TW`)。 |
|
|
276
297
|
| `sa-spec new [--id REQ-001] --name login [--branch]` | 建立需求資料夾與空白文件模板(未帶 `--id` 時啟用多人草稿模式,可選 `--branch` 自動建立並切換至 `feat/<name>` 分支)。 |
|
|
277
298
|
| `sa-spec finalize --name login` | 將草稿需求定號歸檔為最新序號(自動計算最新號碼、重命名目錄、批次置換 `{{REQ_ID}}` 並重建索引)。 |
|
|
299
|
+
| `sa-spec pdf <target> [--split] [--output out.pdf]` | 將指定 `.md` 轉為同名 `.pdf`,或將需求編號/目錄(如 `REQ-001`)整包合併匯出成一份完整規格書 PDF(加 `--split` 則各檔獨立轉出,`--all` 匯出全專案)。 |
|
|
278
300
|
| `sa-spec reindex` | 掃描 `project/REQ-*` 目錄,依序號重新生成乾淨無衝突的 `project/index.md`。 |
|
|
279
301
|
| `sa-spec check` | 檢查基本文件是否存在。 |
|
|
280
302
|
| `sa-spec status` | 顯示系統規格與需求索引狀態。 |
|
|
@@ -324,6 +346,7 @@ Markdown、PlantUML、YAML、GitLens 這類套件可以提升閱讀與檢查體
|
|
|
324
346
|
| AI 工作流程 | `spec:propose` / `scp` | VS Code AI 對話側邊欄 | 整理使用者提出的需求。 |
|
|
325
347
|
| AI 工作流程 | `spec:analyze` / `sca` | VS Code AI 對話側邊欄 | 檢查衝突並產出待確認草稿。 |
|
|
326
348
|
| AI 工作流程 | `spec:commit` / `scc` | VS Code AI 對話側邊欄 | 歸檔已確認版本並建立本機 Git commit。 |
|
|
349
|
+
| AI 工作流程 | `spec:pdf` / `spd` | VS Code AI 對話側邊欄 | 將指定 `.md` 或需求目錄匯出為 PDF 規格書。 |
|
|
327
350
|
|
|
328
351
|
建議操作順序:
|
|
329
352
|
|
|
@@ -353,6 +376,7 @@ spec:commit # 或簡寫 scc
|
|
|
353
376
|
| `spec:propose` | `scp` | 使用者提出需求後,整理需求摘要、目標、初步流程與待確認事項。 |
|
|
354
377
|
| `spec:analyze` | `sca` | 檢查是否與既有需求衝突;無衝突才產出 SRS、UML、OpenAPI、DB Schema 草稿。 |
|
|
355
378
|
| `spec:commit` | `scc` | 使用者確認無衝突版本後,將版本歸檔到 `project/`,並建立本機 Git commit。 |
|
|
379
|
+
| `spec:pdf` | `spd` | 將指定的 `.md` 檔或需求(如 `REQ-001`)匯出為高品質 PDF。 |
|
|
356
380
|
|
|
357
381
|
狀態機:
|
|
358
382
|
|
|
@@ -725,6 +749,11 @@ git push origin master
|
|
|
725
749
|
|
|
726
750
|
## 更新紀錄
|
|
727
751
|
|
|
752
|
+
### v0.2.7
|
|
753
|
+
- **新增 Markdown 轉 PDF 指令(`spec:pdf` / `spd` / `sa-spec pdf`)與跨平台獨立安裝腳本**:
|
|
754
|
+
- **Markdown 轉 PDF 支援**:新增 AI 指令 `spec:pdf`(簡寫 `spd`)與 CLI 指令 `sa-spec pdf <target> [--split] [--output out.pdf]`(及 `--all`),支援單一 `.md` 轉換、需求目錄整包合併匯出成一份完整規格書 PDF,或加 `--split` 各檔獨立匯出。
|
|
755
|
+
- **跨平台獨立管理腳本**:新增 macOS / Linux(`install.sh`、`update.sh`、`uninstall.sh`)與 Windows(`install.bat`、`update.bat`、`uninstall.bat`)6 個獨立執行腳本,一鍵完成安裝、更新與解除安裝。
|
|
756
|
+
|
|
728
757
|
### v0.2.6
|
|
729
758
|
- **Git Remote 自動推送鐵律(Remote Auto-Push)**:
|
|
730
759
|
- **`scp` 建立分支自動推上遠端**:執行 `spec:propose`(`scp`)或 `sa-spec new --branch` 建立 feature 分支後,自動偵測是否有設定 Git remote;若有則立即將新分支推送至遠端(`git push -u <remote> feat/<name>`)。
|
package/src/cli.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const { spawnSync } = require("child_process");
|
|
4
|
+
const {
|
|
5
|
+
buildHtmlDocument,
|
|
6
|
+
convertMarkdownFileToPdf,
|
|
7
|
+
buildMergedRequirementMarkdown,
|
|
8
|
+
renderHtmlToPdf
|
|
9
|
+
} = require("./pdf");
|
|
4
10
|
|
|
5
11
|
const cwd = process.cwd();
|
|
6
12
|
const rootDir = path.resolve(__dirname, "..");
|
|
@@ -38,6 +44,9 @@ function main() {
|
|
|
38
44
|
case "template":
|
|
39
45
|
runTemplate(args.slice(1));
|
|
40
46
|
return;
|
|
47
|
+
case "pdf":
|
|
48
|
+
runPdf(args.slice(1));
|
|
49
|
+
return;
|
|
41
50
|
case "version":
|
|
42
51
|
case "--version":
|
|
43
52
|
case "-v":
|
|
@@ -1021,6 +1030,171 @@ function printResultList(label, results) {
|
|
|
1021
1030
|
}
|
|
1022
1031
|
}
|
|
1023
1032
|
|
|
1033
|
+
function resolveReqTargetDir(target) {
|
|
1034
|
+
const directAbs = path.resolve(cwd, target);
|
|
1035
|
+
if (fs.existsSync(directAbs) && fs.statSync(directAbs).isDirectory()) {
|
|
1036
|
+
return directAbs;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
const projectDir = path.join(cwd, "project");
|
|
1040
|
+
if (!fs.existsSync(projectDir)) return null;
|
|
1041
|
+
|
|
1042
|
+
const inProjectAbs = path.join(projectDir, target);
|
|
1043
|
+
if (fs.existsSync(inProjectAbs) && fs.statSync(inProjectAbs).isDirectory()) {
|
|
1044
|
+
return inProjectAbs;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
const norm = target.toLowerCase().trim();
|
|
1048
|
+
const entries = fs.readdirSync(projectDir, { withFileTypes: true });
|
|
1049
|
+
for (const entry of entries) {
|
|
1050
|
+
if (!entry.isDirectory()) continue;
|
|
1051
|
+
const nameLower = entry.name.toLowerCase();
|
|
1052
|
+
if (
|
|
1053
|
+
nameLower === norm ||
|
|
1054
|
+
nameLower.startsWith(`${norm}-`) ||
|
|
1055
|
+
nameLower.endsWith(`-${slugify(target)}`)
|
|
1056
|
+
) {
|
|
1057
|
+
return path.join(projectDir, entry.name);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
return null;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
function exportReqDirToPdf(reqAbsDir, options = {}) {
|
|
1064
|
+
const { split = false, output = null, browser = null } = options;
|
|
1065
|
+
const relDir = path.relative(cwd, reqAbsDir) || path.basename(reqAbsDir);
|
|
1066
|
+
const results = [];
|
|
1067
|
+
|
|
1068
|
+
if (split) {
|
|
1069
|
+
const entries = fs.readdirSync(reqAbsDir, { withFileTypes: true });
|
|
1070
|
+
const mdFiles = entries
|
|
1071
|
+
.filter((e) => e.isFile() && e.name.toLowerCase().endsWith(".md"))
|
|
1072
|
+
.map((e) => path.join(reqAbsDir, e.name));
|
|
1073
|
+
|
|
1074
|
+
for (const mdFile of mdFiles) {
|
|
1075
|
+
const res = convertMarkdownFileToPdf(mdFile, null, browser);
|
|
1076
|
+
const relPdf = path.relative(cwd, mdFile.replace(/\.md$/i, ".pdf"));
|
|
1077
|
+
if (res.ok) {
|
|
1078
|
+
results.push({ action: "create", target: relPdf, message: `由 ${path.relative(cwd, mdFile)} 轉換 (${res.engine})` });
|
|
1079
|
+
} else {
|
|
1080
|
+
results.push({ action: "warn", target: relPdf, message: res.error });
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
return results;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
const merged = buildMergedRequirementMarkdown(reqAbsDir);
|
|
1087
|
+
if (!merged.markdown.trim()) {
|
|
1088
|
+
results.push({ action: "warn", target: relDir, message: "目錄內無可匯出的規格文件內容" });
|
|
1089
|
+
return results;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
const defaultPdfPath = path.join(reqAbsDir, `${path.basename(reqAbsDir)}.pdf`);
|
|
1093
|
+
const targetPdfPath = output ? path.resolve(cwd, output) : defaultPdfPath;
|
|
1094
|
+
const html = buildHtmlDocument(merged.title, merged.markdown);
|
|
1095
|
+
const res = renderHtmlToPdf(html, targetPdfPath, browser);
|
|
1096
|
+
const relPdf = path.relative(cwd, targetPdfPath);
|
|
1097
|
+
|
|
1098
|
+
if (res.ok) {
|
|
1099
|
+
results.push({
|
|
1100
|
+
action: "create",
|
|
1101
|
+
target: relPdf,
|
|
1102
|
+
message: `已整包合併匯出完整需求規格書 PDF (${res.engine})`
|
|
1103
|
+
});
|
|
1104
|
+
} else {
|
|
1105
|
+
results.push({ action: "warn", target: relPdf, message: res.error });
|
|
1106
|
+
}
|
|
1107
|
+
return results;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
function runPdf(args = []) {
|
|
1111
|
+
const split = args.includes("--split");
|
|
1112
|
+
const all = args.includes("--all");
|
|
1113
|
+
const output = getOption(args, "--output") || getOption(args, "-o");
|
|
1114
|
+
const browser = getOption(args, "--browser");
|
|
1115
|
+
|
|
1116
|
+
const positional = [];
|
|
1117
|
+
for (let i = 0; i < args.length; i++) {
|
|
1118
|
+
const a = args[i];
|
|
1119
|
+
if (a === "--output" || a === "-o" || a === "--browser" || a === "--name" || a === "--target") {
|
|
1120
|
+
i++;
|
|
1121
|
+
continue;
|
|
1122
|
+
}
|
|
1123
|
+
if (a.startsWith("-")) continue;
|
|
1124
|
+
positional.push(a);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
const target = getOption(args, "--name") || getOption(args, "--target") || positional[0];
|
|
1128
|
+
|
|
1129
|
+
if (!target && !all) {
|
|
1130
|
+
fail(
|
|
1131
|
+
"Usage:\n sa-spec pdf <file.md> [--output out.pdf]\n sa-spec pdf <REQ-001|name|dir> [--split] [--output out.pdf]\n sa-spec pdf --all [--split]"
|
|
1132
|
+
);
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
const results = [];
|
|
1137
|
+
|
|
1138
|
+
if (all) {
|
|
1139
|
+
const projectDir = path.join(cwd, "project");
|
|
1140
|
+
if (!fs.existsSync(projectDir)) {
|
|
1141
|
+
fail("找不到 project/ 目錄。");
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
for (const rootMd of ["system-spec.md", "index.md"]) {
|
|
1145
|
+
const mdPath = path.join(projectDir, rootMd);
|
|
1146
|
+
if (fs.existsSync(mdPath)) {
|
|
1147
|
+
const res = convertMarkdownFileToPdf(mdPath, null, browser);
|
|
1148
|
+
const relPdf = path.relative(cwd, mdPath.replace(/\.md$/i, ".pdf"));
|
|
1149
|
+
results.push(
|
|
1150
|
+
res.ok
|
|
1151
|
+
? { action: "create", target: relPdf, message: `已轉換 (${res.engine})` }
|
|
1152
|
+
: { action: "warn", target: relPdf, message: res.error }
|
|
1153
|
+
);
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
const entries = fs.readdirSync(projectDir, { withFileTypes: true });
|
|
1157
|
+
for (const entry of entries) {
|
|
1158
|
+
if (entry.isDirectory()) {
|
|
1159
|
+
const reqDir = path.join(projectDir, entry.name);
|
|
1160
|
+
results.push(...exportReqDirToPdf(reqDir, { split, browser }));
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
printResultList("pdf", results);
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// 1. Check if target is a single .md file
|
|
1168
|
+
const directFileAbs = path.resolve(cwd, target);
|
|
1169
|
+
if (fs.existsSync(directFileAbs) && fs.statSync(directFileAbs).isFile()) {
|
|
1170
|
+
const res = convertMarkdownFileToPdf(directFileAbs, output, browser);
|
|
1171
|
+
const targetPdf = output
|
|
1172
|
+
? path.relative(cwd, path.resolve(cwd, output))
|
|
1173
|
+
: path.relative(cwd, directFileAbs.replace(/\.[^.]+$/, ".pdf"));
|
|
1174
|
+
if (res.ok) {
|
|
1175
|
+
results.push({ action: "create", target: targetPdf, message: `單檔轉換完成 (${res.engine})` });
|
|
1176
|
+
} else {
|
|
1177
|
+
results.push({ action: "warn", target: targetPdf, message: res.error });
|
|
1178
|
+
process.exitCode = 1;
|
|
1179
|
+
}
|
|
1180
|
+
printResultList("pdf", results);
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
// 2. Check if target resolves to a requirement directory
|
|
1185
|
+
const reqDirAbs = resolveReqTargetDir(target);
|
|
1186
|
+
if (reqDirAbs) {
|
|
1187
|
+
results.push(...exportReqDirToPdf(reqDirAbs, { split, output, browser }));
|
|
1188
|
+
if (results.some((r) => r.action === "warn")) {
|
|
1189
|
+
process.exitCode = 1;
|
|
1190
|
+
}
|
|
1191
|
+
printResultList("pdf", results);
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
fail(`找不到指定的 Markdown 檔案或需求目錄: ${target}`);
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1024
1198
|
function showHelp() {
|
|
1025
1199
|
console.log(`sa-spec
|
|
1026
1200
|
|
|
@@ -1028,6 +1202,8 @@ Usage:
|
|
|
1028
1202
|
sa-spec init [--full] [--lang zh-TW|en|zh-CN|ja|ko]
|
|
1029
1203
|
sa-spec new [--id REQ-001] --name login [--branch]
|
|
1030
1204
|
sa-spec finalize --name login [--no-merge]
|
|
1205
|
+
sa-spec pdf <file.md|REQ-001|name> [--split] [--output out.pdf]
|
|
1206
|
+
sa-spec pdf --all [--split]
|
|
1031
1207
|
sa-spec reindex
|
|
1032
1208
|
sa-spec check
|
|
1033
1209
|
sa-spec upgrade [--dry-run]
|