@loviusc/sa-spec 0.2.2 → 0.2.4

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 CHANGED
@@ -102,18 +102,18 @@ To solve sequential ID collisions and Git merge conflicts in team environments,
102
102
 
103
103
  ```mermaid
104
104
  flowchart TD
105
- A[New Requirement: sa-spec new --name cart --branch] --> B[Create & switch to feat/cart branch<br/>Draft directory: project/cart/]
106
- B --> C[AI Analysis: sca<br/>Uses {{REQ_ID}} placeholder<br/>Pre-check against master spec baseline]
107
- C --> D{User Approved?<br/>Execute scc or finalize}
108
- D --> E[Query latest sequential ID N from master]
109
- E --> F[Rename to project/REQ-N+1-cart/<br/>Substitute {{REQ_ID}} with actual ID]
110
- F --> G[sa-spec reindex: Dynamically rebuild index.md]
111
- G --> H[Merge into master: Zero conflicts, Continuous IDs!]
105
+ A["New Requirement: scp or sa-spec new --branch"] --> B["Create & switch to feat/cart branch<br/>Draft directory: project/cart/"]
106
+ B --> C["AI Analysis: sca<br/>Uses {{REQ_ID}} placeholder<br/>Pre-check against master spec baseline"]
107
+ C --> D{"User Approved?<br/>Execute scc or finalize"}
108
+ D --> E["Auto query latest sequential ID N from master"]
109
+ E --> F["Auto assign project/REQ-N+1-cart/<br/>Replace {{REQ_ID}} and commit on feature branch"]
110
+ F --> G["Auto merge into master branch<br/>Dynamically rebuild project/index.md"]
111
+ G --> H["Closed Loop: Zero conflicts, Continuous IDs!"]
112
112
  ```
113
113
 
114
- 1. **Draft Phase (No ID Contention)**: Run `sa-spec new --name checkout --branch` to create `feat/checkout` and `project/checkout/` using `{{REQ_ID}}`. Parallel branches never collide. If an idea is abandoned, deleting the branch leaves zero ghost IDs.
114
+ 1. **Draft Phase (No ID Contention)**: Run `sa-spec new --name checkout --branch` or prompt `scp checkout` in AI chat. It creates `feat/checkout` and `project/checkout/` using `{{REQ_ID}}`. Parallel branches never collide. If an idea is abandoned, deleting the branch leaves zero ghost IDs.
115
115
  2. **Baseline Protection**: `spec:analyze` fetches latest `master` before analyzing to prevent missing newly merged API or table changes.
116
- 3. **Late-Binding Finalize**: Running `sa-spec finalize --name checkout` inspects master's latest ID, renames the folder, replaces placeholders, and updates `index.md`.
116
+ 3. **Late-Binding Finalize & Auto-Merge**: Running `scc` in AI chat (or `sa-spec finalize --name checkout`) automatically inspects master's latest ID, assigns `REQ-xxx`, replaces placeholders, commits, and **automatically merges into master** (`git checkout master && git merge feat/checkout`), then updates `project/index.md`.
117
117
  4. **Conflict-Free Reindex**: `sa-spec reindex` builds `project/index.md` on the fly from disk.
118
118
 
119
119
  ---
package/README.ja.md CHANGED
@@ -102,18 +102,18 @@ sa-spec --version
102
102
 
103
103
  ```mermaid
104
104
  flowchart TD
105
- A[要件提案: sa-spec new --name cart --branch] --> B[feat/cart ブランチを作成して切替<br/>ドラフトディレクトリ: project/cart/]
106
- B --> C[AI による分析: sca<br/>内部で {{REQ_ID}} プレースホルダー使用<br/>master の最新仕様との競合予防チェック]
107
- C --> D{ユーザー承認?<br/>scc または finalize を実行}
108
- D --> E[master から最新の利用可能番号 N を取得]
109
- E --> F[project/REQ-N+1-cart/ にリネーム<br/>{{REQ_ID}} を正式番号に一括置換]
110
- F --> G[sa-spec reindex: index.md を動的に再生成]
111
- G --> H[master へマージ: 競合ゼロ、連番維持!]
105
+ A["要件提案: scp または sa-spec new --branch"] --> B["feat/cart ブランチを作成して切替<br/>ドラフトディレクトリ: project/cart/"]
106
+ B --> C["AI による分析: sca<br/>内部で {{REQ_ID}} プレースホルダー使用<br/>master の最新仕様との競合予防チェック"]
107
+ C --> D{"ユーザー承認?<br/>scc または finalize を実行"}
108
+ D --> E["master から最新の利用可能番号 N を自動取得"]
109
+ E --> F["project/REQ-N+1-cart/ に自動正式採番<br/>{{REQ_ID}} を置換してコミット"]
110
+ F --> G["master ブランチへ自動マージ<br/>project/index.md を動的に再生成"]
111
+ G --> H["自動閉ループ:競合ゼロ、連番維持!"]
112
112
  ```
113
113
 
114
- 1. **ドラフト時は番号を奪い合わない**: `sa-spec new --name cart --branch` でブランチを切り、`{{REQ_ID}}` を使って作業します。途中で要件を破棄する場合も、ブランチを削除するだけで済み、欠番(ゴーストID)を残しません。
114
+ 1. **ドラフト時は番号を奪い合わない**: `scp` または `sa-spec new --branch` でブランチを切り、`project/cart/` と `{{REQ_ID}}` を使って作業します。途中で要件を破棄する場合も、ブランチを削除するだけで済み、欠番(ゴーストID)を残しません。
115
115
  2. **基線同期で競合見落としを防止**: `spec:analyze` の実行前に master の最新状態を取得し、他メンバーがマージした最新の API やテーブル変更を反映して競合チェックを行います。
116
- 3. **マージ確定時に正式採番**: `sa-spec finalize` または `scc` で master 上の最新番号を取得してリネームします。
116
+ 3. **自動採番と主ブランチへの自動マージ**: `scc` または `sa-spec finalize` を実行すると、**master から次の番号を自動取得して採番・置換・コミット**し、**master ブランチへ自動切り替え&自動マージ**を行い、`project/index.md` を最新状態に再生成します。
117
117
  4. **インデックスのコンフリクト解消**: `sa-spec reindex` でディレクトリから常に決定論的に `project/index.md` を再構築します。
118
118
 
119
119
  ---
package/README.ko.md CHANGED
@@ -102,18 +102,18 @@ sa-spec --version
102
102
 
103
103
  ```mermaid
104
104
  flowchart TD
105
- A[요구사항 제안: sa-spec new --name cart --branch] --> B[feat/cart 브랜치 생성 및 전환<br/>초안 디렉토리: project/cart/]
106
- B --> C[AI 분석 및 초안: sca<br/>내부에서 {{REQ_ID}} 플레이스홀더 사용<br/>master 최신 사양 기반 충돌 검사]
107
- C --> D{사용자 승인 완료?<br/>scc 또는 finalize 실행}
108
- D --> E[master에서 최신 사용 가능 번호 N 확인]
109
- E --> F[project/REQ-N+1-cart/ 폴더명 변경<br/>{{REQ_ID}}를 공식 번호로 일괄 치환]
110
- F --> G[sa-spec reindex: index.md 동적 재생성]
111
- G --> H[master로 머지: 충돌 제로, 연속된 번호 유지!]
105
+ A["요구사항 제안: scp 또는 sa-spec new --branch"] --> B["feat/cart 브랜치 생성 및 전환<br/>초안 디렉토리: project/cart/"]
106
+ B --> C["AI 분석 및 초안: sca<br/>내부에서 {{REQ_ID}} 플레이스홀더 사용<br/>master 최신 사양 기반 충돌 검사"]
107
+ C --> D{"사용자 승인 완료?<br/>scc 또는 finalize 실행"}
108
+ D --> E["master에서 최신 사용 가능 번호 N 자동 확인"]
109
+ E --> F["project/REQ-N+1-cart/ 자동 채번 및 커밋<br/>{{REQ_ID}}를 공식 번호로 일괄 치환"]
110
+ F --> G["master 브랜치로 자동 머지<br/>project/index.md 동적 재생성"]
111
+ G --> H["자동 루프 완성: 충돌 제로, 연속된 번호 유지!"]
112
112
  ```
113
113
 
114
- 1. **초안 단계 번호 미선점**: `sa-spec new --name cart --branch` 명령으로 독립 브랜치를 생성하고 `{{REQ_ID}}` 플레이스홀더를 사용합니다. 요구사항이 중도 폐기되어도 브랜치만 삭제하면 되며, 결번(고스트 번호)이 발생하지 않습니다.
114
+ 1. **초안 단계 번호 미선점**: `scp` 또는 `sa-spec new --branch` 명령으로 독립 브랜치를 생성하고 `project/cart/`와 `{{REQ_ID}}` 플레이스홀더를 사용합니다. 요구사항이 중도 폐기되어도 브랜치만 삭제하면 되며, 결번(고스트 번호)이 발생하지 않습니다.
115
115
  2. **사양 기준선 동기화**: `spec:analyze` 실행 전 master의 최신 상태를 동기화하여 다른 팀원이 머지한 API나 테이블과의 충돌 누락을 방지합니다.
116
- 3. **머지 시점 공식 채번**: `sa-spec finalize` 또는 `scc` 실행 시점에 master의 최신 번호를 부여받아 폴더명을 확정합니다.
116
+ 3. **자동 신규 번호 채번 및 메인 브랜치 자동 머지**: `scc` 또는 `sa-spec finalize` 실행 시 **master에서 다음 번호를 자동 조회하여 채번·치환·커밋**하고, **master 브랜치로 자동 전환 후 자동 머지**를 수행하며 `project/index.md`를 최신 상태로 정렬 재생성합니다.
117
117
  4. **인덱스 머지 충돌 제거**: `sa-spec reindex` 명령으로 디렉토리 상태에서 `project/index.md`를 언제든 완벽하게 재생성합니다.
118
118
 
119
119
  ---
package/README.zh-CN.md CHANGED
@@ -102,18 +102,18 @@ sa-spec --version
102
102
 
103
103
  ```mermaid
104
104
  flowchart TD
105
- A[提出需求: sa-spec new --name cart --branch] --> B[建立并切换至 feat/cart 分支<br/>创建草稿目录 project/cart/]
106
- B --> C[AI 分析与起草: sca<br/>内部使用 {{REQ_ID}} 占位<br/>自动同步 master 最新规格防冲突]
107
- C --> D{用户确认通过<br/>执行 scc 或 finalize}
108
- D --> E[向 master 查询最新可用序号 N]
109
- E --> F[重命名为 project/REQ-N+1-cart/<br/>批量替换 {{REQ_ID}} 为正式编号]
110
- F --> G[sa-spec reindex: 动态重构 index.md]
111
- G --> H[合并回 master: 零冲突、序号连续!]
105
+ A["提出需求: scp 或 sa-spec new --branch"] --> B["建立并切换至 feat/cart 分支<br/>创建草稿目录 project/cart/"]
106
+ B --> C["AI 分析与起草: sca<br/>内部使用 {{REQ_ID}} 占位<br/>自动同步 master 最新规格防冲突"]
107
+ C --> D{"用户确认通过<br/>执行 scc 或 finalize"}
108
+ D --> E["自动向主分支查询最新可用序号 N"]
109
+ E --> F["自动定号为 project/REQ-N+1-cart/<br/>批量替换 {{REQ_ID}} 并建立提交"]
110
+ F --> G["自动合并至 master 主分支<br/>动态重新生成 project/index.md"]
111
+ G --> H["完成闭环:零冲突、序号永远连续!"]
112
112
  ```
113
113
 
114
- 1. **草稿不抢号**:在分支开发时使用 `project/cart/` 与 `{{REQ_ID}}` 占位符。多人同时提出需求互不冲突。如果需求放弃,直接删除分支,**绝不遗留幽灵跳号**。
114
+ 1. **草稿不抢号**:在分支开发时使用 `scp` 或 `sa-spec new --branch`,建立 `project/cart/` 与 `{{REQ_ID}}` 占位符。多人同时提出需求互不冲突。如果需求放弃,直接删除分支,**绝不遗留幽灵跳号**。
115
115
  2. **基线同步防漏检**:执行 `spec:analyze` 前先确认同步远端 master 规格,确保以全系统最新状态进行冲突检查。
116
- 3. **归档时原子定号**:执行 `sa-spec finalize` 或 `scc` 时向 master 申请下一个号(例如 `REQ-003`),批量替换并更新。
116
+ 3. **自动拿新号与自动合并**:执行 `scc`(或 `sa-spec finalize --name cart`)时,系统**自动向 master 申请下一个新号定号**、批量替换占位符、完成提交,并**自动切换回 master 并合并该 feature 分支**,随后自动重建 `project/index.md`。
117
117
  4. **动态重建索引**:`sa-spec reindex` 随时可由目录动态排序生成总表,消除 `index.md` 的 merge conflict。
118
118
 
119
119
  ---
package/README.zh-TW.md CHANGED
@@ -17,23 +17,23 @@
17
17
 
18
18
  ---
19
19
 
20
- ### Quick Start (English)
20
+ ### Quick Start
21
21
 
22
22
  ```bash
23
- # Global installation via npm
23
+ # 1. Global installation via npm
24
24
  npm install -g @loviusc/sa-spec
25
25
 
26
- # Initialize project with English specification rules
27
- sa-spec init --full --lang en
28
-
29
- # Create a feature draft branch
30
- sa-spec new --name cart-checkout --branch
26
+ # 2. Initialize project specification rules (supports zh-TW, en, zh-CN, ja, ko)
27
+ sa-spec init --full
31
28
  ```
32
29
 
33
- **Core AI Workflow in VS Code:**
34
- `spec:init (sci)` $\to$ `spec:propose (scp)` $\to$ `spec:analyze (sca)` $\to$ `spec:commit (scc)`
30
+ **🤖 Core AI Workflow in Chat (VS Code / Claude / Antigravity / Cursor):**
31
+ - `spec:init` (`sci`) $\to$ 鎖定專案主要語言與系統規格基準
32
+ - `spec:propose` (`scp`) $\to$ **AI 自動建立 feature 分支與草稿目錄**(零手動指令)
33
+ - `spec:analyze` (`sca`) $\to$ AI 衝突檢查並產出規格文件草稿
34
+ - `spec:commit` (`scc`) $\to$ **AI 自動向主分支領取新號並自動合併回主分支!**
35
35
 
36
- 👉 **Read full English documentation: [README.en.md](./README.en.md)**
36
+ 👉 **Language Guides: [English](./README.en.md) | [繁體中文](./README.zh-TW.md) | [简体中文](./README.zh-CN.md) | [日本語](./README.ja.md) | [한국어](./README.ko.md)**
37
37
 
38
38
  ---
39
39
 
@@ -397,45 +397,51 @@ docs(REQ-001): 番茄時鐘 - 新增專注與休息計時需求
397
397
 
398
398
  ```mermaid
399
399
  flowchart TD
400
- A[開新需求: sa-spec new --name cart --branch] --> B[建立並切換至 feat/cart 分支<br/>以 project/cart/ 建立草稿目錄]
401
- B --> C[AI 分析與起草: sca<br/>內部使用 {{REQ_ID}} 佔位<br/>自動同步 master 規格防護]
402
- C --> D{使用者確認通過<br/>執行 scc 或 finalize}
403
- D --> E[查詢 master 最新序號 N]
404
- E --> F[重命名為 project/REQ-N+1-cart/<br/>批次置換 {{REQ_ID}} 為正式號碼]
405
- F --> G[sa-spec reindex: 動態重構 index.md]
406
- G --> H[合併回 master: 零衝突、序號永遠連續!]
400
+ A["開新需求: scp 或 sa-spec new --branch"] --> B["建立並切換至 feat/cart 分支<br/>以 project/cart/ 建立草稿目錄"]
401
+ B --> C["AI 分析與起草: sca<br/>內部使用 {{REQ_ID}} 佔位<br/>自動同步 master 規格防護"]
402
+ C --> D{"使用者確認通過<br/>執行 scc 或 finalize"}
403
+ D --> E["自動查詢主分支最新序號 N"]
404
+ E --> F["自動定號為 project/REQ-N+1-cart/<br/>批次置換 {{REQ_ID}} 並建立 commit"]
405
+ F --> G["自動合併至 master 主分支<br/>動態重新生成 project/index.md"]
406
+ G --> H["完成閉環:零衝突、序號永遠連續!"]
407
407
  ```
408
408
 
409
409
  ### 多人協作四步流程
410
410
 
411
411
  #### 1. 提出需求(草稿與分支隔離)
412
- 開需求時**不搶序號**,直接以語意名稱建立分支與草稿目錄:
413
-
414
- ```bash
415
- # 自動建立並切換至 feat/cart-checkout 分支,建立 project/cart-checkout/ 草稿目錄
416
- sa-spec new --name cart-checkout --branch
417
- ```
418
- 在 VS Code 側邊欄輸入:
419
- ```text
420
- scp 購物車結帳需求
421
- ```
412
+ 開需求時**不搶序號**,直接以語意名稱建立分支與草稿目錄。支援兩種方式:
413
+
414
+ * **方式 A(純 AI 自動化,最推薦)**:在 AI 側邊欄直接輸入:
415
+ ```text
416
+ scp 購物車結帳需求
417
+ ```
418
+ AI 收到後,會**自動在背景執行 `git checkout -b feat/cart-checkout` 建立並切換分支**,並在 `project/cart-checkout/` 建立草稿目錄!
419
+ * **方式 B(CLI 終端指令)**:
420
+ ```bash
421
+ sa-spec new --name cart-checkout --branch
422
+ ```
422
423
  * **特點**:目錄名稱為 `project/cart-checkout/`,內部所有檔案皆使用 `{{REQ_ID}}` 佔位符。多人同時提出需求互不干擾、零衝突。即使需求中途放棄,直接刪除分支即可,絕不留下幽靈跳號。
423
424
 
424
425
  #### 2. 分析與衝突檢查(規格基線同步)
425
426
  在分支上執行 `spec:analyze`(或 `sca`)前,工具會確認已取得 `master`(或 `origin/master`)的最新規格,確保是以全專案最新基準進行影響評估,避免因分支落後而漏檢權限、API 或資料表衝突。
426
427
 
427
- #### 3. 確認歸檔與延遲定號(Late-Binding)
428
- 當需求規格確認無誤,準備歸檔時:
429
-
430
- ```bash
431
- # 自動向 master 查詢最新可用序號(例如 REQ-006)
432
- sa-spec finalize --name cart-checkout
433
- ```
434
- * **自動化執行**:
435
- 1. 取得最新序號(例如目前最大是 `REQ-005`,自動取得 `REQ-006`)。
436
- 2. 使用 `git mv` 將目錄重命名為 `project/REQ-006-cart-checkout`。
437
- 3. 自動掃描並將目錄內所有檔案的 `{{REQ_ID}}` 批次置換為 `REQ-006`。
438
- 4. 自動執行 `reindex` 重建需求總表。
428
+ #### 3. 確認歸檔與延遲定號(自動領號 + 自動合併回主分支)
429
+ 當需求規格經使用者確認無誤,準備歸檔時:
430
+
431
+ * **方式 A(純 AI 自動化,最推薦)**:在 AI 側邊欄直接輸入:
432
+ ```text
433
+ scc
434
+ ```
435
+ AI 收到後會自動完成全自動 Git-Flow 閉環作業:
436
+ 1. **自動向主分支查詢並取得新號**:查詢 master/main 目前最大序號(例如最大是 `REQ-005`,自動領取最新號 `REQ-006`),避免號碼競爭。
437
+ 2. **自動定名更名**:使用 `git mv` 將目錄重命名為 `project/REQ-006-cart-checkout`,並將目錄內所有檔案的 `{{REQ_ID}}` 批次置換為 `REQ-006`。
438
+ 3. **自動建立本機提交**:在 feature 分支自動建立 commit `docs(REQ-006): cart-checkout - 規格確認歸檔`。
439
+ 4. **自動合併至主分支**:自動切換至主分支(`git checkout master`),將 feature 分支自動合併回主分支(`git merge feat/cart-checkout`),並重新生成主分支的 `project/index.md`,保證主分支規格最新且永遠不衝突!
440
+ * **方式 B(CLI 終端指令)**:
441
+ ```bash
442
+ sa-spec finalize --name cart-checkout
443
+ ```
444
+ CLI 同樣會自動查詢主分支最新序號定號、置換佔位符、完成提交並自動合併回 master!若想保留在當前分支手動處理,可加上 `--no-merge` 參數。
439
445
 
440
446
  #### 4. 消除索引合併衝突(`sa-spec reindex`)
441
447
  `project/index.md` 改為由目錄動態生成的「衍生清單」,不再依賴脆弱的手動 append:
@@ -718,6 +724,18 @@ git push origin master
718
724
 
719
725
  ## 更新紀錄
720
726
 
727
+ ### v0.2.4
728
+ - **序號重複主動告警與重新定號自癒支援**:
729
+ - **唯一性主動檢驗**:`sa-spec check` 與 `sa-spec reindex` 加入重複序號檢測,遇到衝突時即時發出 WARNING 告警並列出衝突目錄與修復建議。
730
+ - **重新定號(Re-number)自癒**:`sa-spec finalize` 智慧支援既有 `REQ-xxx` 目錄重新定號,自動計算最新序號並批次更新目錄內所有檔案的舊編號,零手動修復負擔。
731
+ - **Quick Start 精簡**:移除頂部容易引起誤解的手動建立分支指令,聚焦 AI 4 指令全自動工作流。
732
+
733
+ ### v0.2.3
734
+ - **自動領號與自動合併至主分支(Git-Flow 自動化閉環)**:
735
+ - **自動從主分支取新號**:執行 `spec:commit`(`scc`)或 `sa-spec finalize` 時,自動向主分支(`master`/`main`)查詢最新已使用最大序號並定號(`REQ-xxx`),自動置換內部佔位符 `{{REQ_ID}}`,徹底防止多人協作時在 feature 分支發生的號碼撞車與落後問題。
736
+ - **自動合併回主分支**:完成 feature 分支 commit 後,自動切換至主分支(`git checkout master`)並將該 feature 分支自動合併回主分支(`git merge feat/<name>`),並在主分支自動重新整理 `project/index.md`,打造完全自動化的 Git-Flow 零衝突閉環。
737
+ - **CLI 自動化支援**:`sa-spec finalize --name <name>` 預設自動執行主分支序號運算、定號置換、提交與合併回主分支(支援 `--no-merge` 參數跳過自動合併)。
738
+
721
739
  ### v0.2.2
722
740
  - **npm 首頁與專案文件全多語系化(Multi-Language Documentation on npm)**:
723
741
  - **5 國獨立完整說明文件**:新增 `README.en.md`(英文)、`README.zh-CN.md`(簡中)、`README.ja.md`(日文)、`README.ko.md`(韓文)與 `README.zh-TW.md`(繁中)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loviusc/sa-spec",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Personal system analysis documentation workspace CLI",
5
5
  "publishConfig": {
6
6
  "access": "public",
package/readme.md CHANGED
@@ -17,23 +17,23 @@
17
17
 
18
18
  ---
19
19
 
20
- ### Quick Start (English)
20
+ ### Quick Start
21
21
 
22
22
  ```bash
23
- # Global installation via npm
23
+ # 1. Global installation via npm
24
24
  npm install -g @loviusc/sa-spec
25
25
 
26
- # Initialize project with English specification rules
27
- sa-spec init --full --lang en
28
-
29
- # Create a feature draft branch
30
- sa-spec new --name cart-checkout --branch
26
+ # 2. Initialize project specification rules (supports zh-TW, en, zh-CN, ja, ko)
27
+ sa-spec init --full
31
28
  ```
32
29
 
33
- **Core AI Workflow in VS Code:**
34
- `spec:init (sci)` $\to$ `spec:propose (scp)` $\to$ `spec:analyze (sca)` $\to$ `spec:commit (scc)`
30
+ **🤖 Core AI Workflow in Chat (VS Code / Claude / Antigravity / Cursor):**
31
+ - `spec:init` (`sci`) $\to$ 鎖定專案主要語言與系統規格基準
32
+ - `spec:propose` (`scp`) $\to$ **AI 自動建立 feature 分支與草稿目錄**(零手動指令)
33
+ - `spec:analyze` (`sca`) $\to$ AI 衝突檢查並產出規格文件草稿
34
+ - `spec:commit` (`scc`) $\to$ **AI 自動向主分支領取新號並自動合併回主分支!**
35
35
 
36
- 👉 **Read full English documentation: [README.en.md](./README.en.md)**
36
+ 👉 **Language Guides: [English](./README.en.md) | [繁體中文](./README.zh-TW.md) | [简体中文](./README.zh-CN.md) | [日本語](./README.ja.md) | [한국어](./README.ko.md)**
37
37
 
38
38
  ---
39
39
 
@@ -397,45 +397,51 @@ docs(REQ-001): 番茄時鐘 - 新增專注與休息計時需求
397
397
 
398
398
  ```mermaid
399
399
  flowchart TD
400
- A[開新需求: sa-spec new --name cart --branch] --> B[建立並切換至 feat/cart 分支<br/>以 project/cart/ 建立草稿目錄]
401
- B --> C[AI 分析與起草: sca<br/>內部使用 {{REQ_ID}} 佔位<br/>自動同步 master 規格防護]
402
- C --> D{使用者確認通過<br/>執行 scc 或 finalize}
403
- D --> E[查詢 master 最新序號 N]
404
- E --> F[重命名為 project/REQ-N+1-cart/<br/>批次置換 {{REQ_ID}} 為正式號碼]
405
- F --> G[sa-spec reindex: 動態重構 index.md]
406
- G --> H[合併回 master: 零衝突、序號永遠連續!]
400
+ A["開新需求: scp 或 sa-spec new --branch"] --> B["建立並切換至 feat/cart 分支<br/>以 project/cart/ 建立草稿目錄"]
401
+ B --> C["AI 分析與起草: sca<br/>內部使用 {{REQ_ID}} 佔位<br/>自動同步 master 規格防護"]
402
+ C --> D{"使用者確認通過<br/>執行 scc 或 finalize"}
403
+ D --> E["自動查詢主分支最新序號 N"]
404
+ E --> F["自動定號為 project/REQ-N+1-cart/<br/>批次置換 {{REQ_ID}} 並建立 commit"]
405
+ F --> G["自動合併至 master 主分支<br/>動態重新生成 project/index.md"]
406
+ G --> H["完成閉環:零衝突、序號永遠連續!"]
407
407
  ```
408
408
 
409
409
  ### 多人協作四步流程
410
410
 
411
411
  #### 1. 提出需求(草稿與分支隔離)
412
- 開需求時**不搶序號**,直接以語意名稱建立分支與草稿目錄:
413
-
414
- ```bash
415
- # 自動建立並切換至 feat/cart-checkout 分支,建立 project/cart-checkout/ 草稿目錄
416
- sa-spec new --name cart-checkout --branch
417
- ```
418
- 在 VS Code 側邊欄輸入:
419
- ```text
420
- scp 購物車結帳需求
421
- ```
412
+ 開需求時**不搶序號**,直接以語意名稱建立分支與草稿目錄。支援兩種方式:
413
+
414
+ * **方式 A(純 AI 自動化,最推薦)**:在 AI 側邊欄直接輸入:
415
+ ```text
416
+ scp 購物車結帳需求
417
+ ```
418
+ AI 收到後,會**自動在背景執行 `git checkout -b feat/cart-checkout` 建立並切換分支**,並在 `project/cart-checkout/` 建立草稿目錄!
419
+ * **方式 B(CLI 終端指令)**:
420
+ ```bash
421
+ sa-spec new --name cart-checkout --branch
422
+ ```
422
423
  * **特點**:目錄名稱為 `project/cart-checkout/`,內部所有檔案皆使用 `{{REQ_ID}}` 佔位符。多人同時提出需求互不干擾、零衝突。即使需求中途放棄,直接刪除分支即可,絕不留下幽靈跳號。
423
424
 
424
425
  #### 2. 分析與衝突檢查(規格基線同步)
425
426
  在分支上執行 `spec:analyze`(或 `sca`)前,工具會確認已取得 `master`(或 `origin/master`)的最新規格,確保是以全專案最新基準進行影響評估,避免因分支落後而漏檢權限、API 或資料表衝突。
426
427
 
427
- #### 3. 確認歸檔與延遲定號(Late-Binding)
428
- 當需求規格確認無誤,準備歸檔時:
429
-
430
- ```bash
431
- # 自動向 master 查詢最新可用序號(例如 REQ-006)
432
- sa-spec finalize --name cart-checkout
433
- ```
434
- * **自動化執行**:
435
- 1. 取得最新序號(例如目前最大是 `REQ-005`,自動取得 `REQ-006`)。
436
- 2. 使用 `git mv` 將目錄重命名為 `project/REQ-006-cart-checkout`。
437
- 3. 自動掃描並將目錄內所有檔案的 `{{REQ_ID}}` 批次置換為 `REQ-006`。
438
- 4. 自動執行 `reindex` 重建需求總表。
428
+ #### 3. 確認歸檔與延遲定號(自動領號 + 自動合併回主分支)
429
+ 當需求規格經使用者確認無誤,準備歸檔時:
430
+
431
+ * **方式 A(純 AI 自動化,最推薦)**:在 AI 側邊欄直接輸入:
432
+ ```text
433
+ scc
434
+ ```
435
+ AI 收到後會自動完成全自動 Git-Flow 閉環作業:
436
+ 1. **自動向主分支查詢並取得新號**:查詢 master/main 目前最大序號(例如最大是 `REQ-005`,自動領取最新號 `REQ-006`),避免號碼競爭。
437
+ 2. **自動定名更名**:使用 `git mv` 將目錄重命名為 `project/REQ-006-cart-checkout`,並將目錄內所有檔案的 `{{REQ_ID}}` 批次置換為 `REQ-006`。
438
+ 3. **自動建立本機提交**:在 feature 分支自動建立 commit `docs(REQ-006): cart-checkout - 規格確認歸檔`。
439
+ 4. **自動合併至主分支**:自動切換至主分支(`git checkout master`),將 feature 分支自動合併回主分支(`git merge feat/cart-checkout`),並重新生成主分支的 `project/index.md`,保證主分支規格最新且永遠不衝突!
440
+ * **方式 B(CLI 終端指令)**:
441
+ ```bash
442
+ sa-spec finalize --name cart-checkout
443
+ ```
444
+ CLI 同樣會自動查詢主分支最新序號定號、置換佔位符、完成提交並自動合併回 master!若想保留在當前分支手動處理,可加上 `--no-merge` 參數。
439
445
 
440
446
  #### 4. 消除索引合併衝突(`sa-spec reindex`)
441
447
  `project/index.md` 改為由目錄動態生成的「衍生清單」,不再依賴脆弱的手動 append:
@@ -718,6 +724,18 @@ git push origin master
718
724
 
719
725
  ## 更新紀錄
720
726
 
727
+ ### v0.2.4
728
+ - **序號重複主動告警與重新定號自癒支援**:
729
+ - **唯一性主動檢驗**:`sa-spec check` 與 `sa-spec reindex` 加入重複序號檢測,遇到衝突時即時發出 WARNING 告警並列出衝突目錄與修復建議。
730
+ - **重新定號(Re-number)自癒**:`sa-spec finalize` 智慧支援既有 `REQ-xxx` 目錄重新定號,自動計算最新序號並批次更新目錄內所有檔案的舊編號,零手動修復負擔。
731
+ - **Quick Start 精簡**:移除頂部容易引起誤解的手動建立分支指令,聚焦 AI 4 指令全自動工作流。
732
+
733
+ ### v0.2.3
734
+ - **自動領號與自動合併至主分支(Git-Flow 自動化閉環)**:
735
+ - **自動從主分支取新號**:執行 `spec:commit`(`scc`)或 `sa-spec finalize` 時,自動向主分支(`master`/`main`)查詢最新已使用最大序號並定號(`REQ-xxx`),自動置換內部佔位符 `{{REQ_ID}}`,徹底防止多人協作時在 feature 分支發生的號碼撞車與落後問題。
736
+ - **自動合併回主分支**:完成 feature 分支 commit 後,自動切換至主分支(`git checkout master`)並將該 feature 分支自動合併回主分支(`git merge feat/<name>`),並在主分支自動重新整理 `project/index.md`,打造完全自動化的 Git-Flow 零衝突閉環。
737
+ - **CLI 自動化支援**:`sa-spec finalize --name <name>` 預設自動執行主分支序號運算、定號置換、提交與合併回主分支(支援 `--no-merge` 參數跳過自動合併)。
738
+
721
739
  ### v0.2.2
722
740
  - **npm 首頁與專案文件全多語系化(Multi-Language Documentation on npm)**:
723
741
  - **5 國獨立完整說明文件**:新增 `README.en.md`(英文)、`README.zh-CN.md`(簡中)、`README.ja.md`(日文)、`README.ko.md`(韓文)與 `README.zh-TW.md`(繁中)。
package/src/cli.js CHANGED
@@ -177,10 +177,48 @@ function runCheck() {
177
177
  if (!checkRequiredPaths(null)) {
178
178
  return;
179
179
  }
180
+ checkDuplicateReqIds();
180
181
  console.log("");
181
182
  showStatus(false);
182
183
  }
183
184
 
185
+ function checkDuplicateReqIds() {
186
+ const projectDir = path.join(cwd, "project");
187
+ if (!fs.existsSync(projectDir)) return true;
188
+
189
+ const entries = fs.readdirSync(projectDir, { withFileTypes: true });
190
+ const idDirsMap = new Map();
191
+ for (const entry of entries) {
192
+ if (entry.isDirectory() && entry.name.startsWith("REQ-")) {
193
+ const m = entry.name.match(/^(REQ-\d+)/i);
194
+ if (m) {
195
+ const id = m[1].toUpperCase();
196
+ if (!idDirsMap.has(id)) idDirsMap.set(id, []);
197
+ idDirsMap.get(id).push(entry.name);
198
+ }
199
+ }
200
+ }
201
+
202
+ const duplicates = [];
203
+ for (const [id, dirs] of idDirsMap.entries()) {
204
+ if (dirs.length > 1) {
205
+ duplicates.push({ id, dirs });
206
+ }
207
+ }
208
+
209
+ if (duplicates.length > 0) {
210
+ console.log("序號唯一性: WARNING (發現重複編號)");
211
+ for (const dup of duplicates) {
212
+ console.log(` - ${dup.id} 重複出現在: ${dup.dirs.join(", ")}`);
213
+ }
214
+ console.log(" 提示:請將衝突目錄重新定號以維持序號連續與唯一。");
215
+ return false;
216
+ } else {
217
+ console.log("序號唯一性: OK");
218
+ return true;
219
+ }
220
+ }
221
+
184
222
  function showStatus(showHeading = true) {
185
223
  if (showHeading) {
186
224
  console.log("sa-spec status");
@@ -309,31 +347,27 @@ function createReqTemplate(args, label = "new") {
309
347
  printResultList(label, results);
310
348
  }
311
349
 
312
- function finalizeReq(args) {
313
- const name = getOption(args, "--name") || getOption(args, "--draft") || args[0];
314
- if (!name) {
315
- fail("Usage: sa-spec finalize --name <name-or-slug>");
316
- return;
350
+ function detectMainBranch() {
351
+ const branches = ["main", "master"];
352
+ for (const b of branches) {
353
+ const res = spawnSync("git", ["rev-parse", "--verify", b], { cwd, encoding: "utf8" });
354
+ if (res.status === 0) return b;
317
355
  }
356
+ return null;
357
+ }
318
358
 
319
- const slug = slugify(name);
320
- const projectDir = path.join(cwd, "project");
321
- let draftRelDir = path.join("project", slug);
322
- let draftAbsDir = path.join(cwd, draftRelDir);
323
-
324
- if (!fs.existsSync(draftAbsDir)) {
325
- const altRelDir = path.join("project", `draft-${slug}`);
326
- if (fs.existsSync(path.join(cwd, altRelDir))) {
327
- draftRelDir = altRelDir;
328
- draftAbsDir = path.join(cwd, altRelDir);
329
- } else {
330
- fail(`找不到草稿需求目錄: ${draftRelDir}`);
331
- return;
332
- }
359
+ function getCurrentBranch() {
360
+ const res = spawnSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd, encoding: "utf8" });
361
+ if (res.status === 0) {
362
+ const b = res.stdout.trim();
363
+ return b === "HEAD" ? null : b;
333
364
  }
365
+ return null;
366
+ }
334
367
 
335
- // 掃描既有最大 REQ 編號
368
+ function getLatestMaxReqId(projectDir, mainBranch) {
336
369
  let maxIdNum = 0;
370
+ // 1. 掃描本地工作目錄既有 REQ-xxx
337
371
  if (fs.existsSync(projectDir)) {
338
372
  const entries = fs.readdirSync(projectDir, { withFileTypes: true });
339
373
  for (const entry of entries) {
@@ -341,55 +375,192 @@ function finalizeReq(args) {
341
375
  const m = entry.name.match(/^REQ-(\d+)/i);
342
376
  if (m) {
343
377
  const num = parseInt(m[1], 10);
344
- if (num > maxIdNum) {
345
- maxIdNum = num;
378
+ if (num > maxIdNum) maxIdNum = num;
379
+ }
380
+ }
381
+ }
382
+ }
383
+
384
+ // 2. 掃描主分支 (若在 Git 環境下)
385
+ if (mainBranch) {
386
+ const gitLs = spawnSync("git", ["ls-tree", `${mainBranch}:project`], { cwd, encoding: "utf8" });
387
+ if (gitLs.status === 0 && gitLs.stdout) {
388
+ for (const line of gitLs.stdout.split("\n")) {
389
+ const parts = line.split("\t");
390
+ if (parts.length > 1) {
391
+ const m = parts[1].trim().match(/^REQ-(\d+)/i);
392
+ if (m) {
393
+ const num = parseInt(m[1], 10);
394
+ if (num > maxIdNum) maxIdNum = num;
395
+ }
396
+ }
397
+ }
398
+ }
399
+
400
+ // 3. 掃描遠端主分支 (若有設定 remote)
401
+ const gitRemoteLs = spawnSync("git", ["ls-tree", `origin/${mainBranch}:project`], { cwd, encoding: "utf8" });
402
+ if (gitRemoteLs.status === 0 && gitRemoteLs.stdout) {
403
+ for (const line of gitRemoteLs.stdout.split("\n")) {
404
+ const parts = line.split("\t");
405
+ if (parts.length > 1) {
406
+ const m = parts[1].trim().match(/^REQ-(\d+)/i);
407
+ if (m) {
408
+ const num = parseInt(m[1], 10);
409
+ if (num > maxIdNum) maxIdNum = num;
410
+ }
411
+ }
412
+ }
413
+ }
414
+ }
415
+
416
+ return maxIdNum;
417
+ }
418
+
419
+ function finalizeReq(args) {
420
+ const rawName = getOption(args, "--name") || getOption(args, "--draft") || args[0];
421
+ if (!rawName) {
422
+ fail("Usage: sa-spec finalize --name <name-or-slug> [--no-merge]");
423
+ return;
424
+ }
425
+
426
+ const slug = slugify(rawName);
427
+ const projectDir = path.join(cwd, "project");
428
+ let draftRelDir = null;
429
+ let draftAbsDir = null;
430
+ let cleanSlug = slug;
431
+ let oldId = null;
432
+
433
+ // 1. 優先尋找草稿目錄 (project/<slug> 或 project/draft-<slug>)
434
+ if (fs.existsSync(path.join(cwd, "project", slug))) {
435
+ draftRelDir = path.join("project", slug);
436
+ draftAbsDir = path.join(cwd, draftRelDir);
437
+ } else if (fs.existsSync(path.join(cwd, "project", `draft-${slug}`))) {
438
+ draftRelDir = path.join("project", `draft-${slug}`);
439
+ draftAbsDir = path.join(cwd, draftRelDir);
440
+ } else if (fs.existsSync(projectDir)) {
441
+ // 2. 尋找既有 REQ-xxx 目錄 (支援序號衝突時的重新定號修復)
442
+ const entries = fs.readdirSync(projectDir, { withFileTypes: true });
443
+ for (const entry of entries) {
444
+ if (entry.isDirectory()) {
445
+ const m = entry.name.match(/^(REQ-\d+)-(.*)$/i);
446
+ if (m) {
447
+ const entrySlug = slugify(m[2]);
448
+ if (entrySlug === slug || slugify(entry.name) === slug) {
449
+ oldId = m[1].toUpperCase();
450
+ cleanSlug = entrySlug;
451
+ draftRelDir = path.join("project", entry.name);
452
+ draftAbsDir = path.join(cwd, draftRelDir);
453
+ break;
346
454
  }
347
455
  }
348
456
  }
349
457
  }
350
458
  }
351
459
 
460
+ if (!draftAbsDir || !fs.existsSync(draftAbsDir)) {
461
+ fail(`找不到草稿或既有需求目錄: project/${slug}`);
462
+ return;
463
+ }
464
+
465
+ // 1. 自動向本地與主分支查詢最新已使用的最大 REQ 序號
466
+ const mainBranch = detectMainBranch();
467
+ const currentBranch = getCurrentBranch();
468
+ const maxIdNum = getLatestMaxReqId(projectDir, mainBranch);
469
+
352
470
  const nextIdNum = maxIdNum + 1;
353
471
  const nextId = `REQ-${String(nextIdNum).padStart(3, "0")}`;
354
- const finalDirName = `${nextId}-${slug}`;
472
+ const finalDirName = `${nextId}-${cleanSlug}`;
355
473
  const finalRelDir = path.join("project", finalDirName);
356
474
  const finalAbsDir = path.join(cwd, finalRelDir);
357
475
 
358
- if (fs.existsSync(finalAbsDir)) {
476
+ if (fs.existsSync(finalAbsDir) && finalAbsDir !== draftAbsDir) {
359
477
  fail(`目標目錄 ${finalRelDir} 已存在,請確認序號或目錄名稱。`);
360
478
  return;
361
479
  }
362
480
 
363
- // 重命名目錄 (優先嘗試 git mv)
364
- const gitMv = spawnSync("git", ["mv", draftRelDir, finalRelDir], { cwd, encoding: "utf8" });
365
- if (gitMv.status === 0) {
366
- console.log(`[Git] 已將目錄重命名為: ${finalRelDir}`);
367
- } else {
368
- fs.renameSync(draftAbsDir, finalAbsDir);
369
- console.log(`已將目錄重命名為: ${finalRelDir}`);
481
+ // 2. 重命名目錄 (優先嘗試 git mv)
482
+ if (draftAbsDir !== finalAbsDir) {
483
+ const gitMv = spawnSync("git", ["mv", draftRelDir, finalRelDir], { cwd, encoding: "utf8" });
484
+ if (gitMv.status === 0) {
485
+ console.log(`[Git] 已將目錄重命名為: ${finalRelDir}`);
486
+ } else {
487
+ fs.renameSync(draftAbsDir, finalAbsDir);
488
+ console.log(`已將目錄重命名為: ${finalRelDir}`);
489
+ }
370
490
  }
371
491
 
372
- // 替換目錄內檔案的 {{REQ_ID}} 佔位符
492
+ // 3. 替換目錄內檔案的 {{REQ_ID}} 佔位符及舊序號
373
493
  const files = listFilesRecursive(finalAbsDir);
374
494
  for (const filePath of files) {
375
495
  try {
376
- const content = fs.readFileSync(filePath, "utf8");
496
+ let content = fs.readFileSync(filePath, "utf8");
497
+ let changed = false;
377
498
  if (content.includes("{{REQ_ID}}")) {
378
- const updated = content.split("{{REQ_ID}}").join(nextId);
379
- fs.writeFileSync(filePath, updated, "utf8");
499
+ content = content.split("{{REQ_ID}}").join(nextId);
500
+ changed = true;
501
+ }
502
+ if (oldId && content.includes(oldId)) {
503
+ content = content.split(oldId).join(nextId);
504
+ changed = true;
505
+ }
506
+ if (changed) {
507
+ fs.writeFileSync(filePath, content, "utf8");
380
508
  }
381
509
  } catch (_) {
382
510
  // 略過無法以文字讀取之檔案
383
511
  }
384
512
  }
385
513
 
386
- // 自動重新整理 index.md
514
+ // 4. 自動重新整理 index.md
387
515
  reindexProject();
388
516
 
389
517
  console.log(`\n 需求已成功定號歸檔為: ${nextId} (${finalDirName})`);
390
- console.log(`建議接下來執行:`);
391
- console.log(` git add project/`);
392
- console.log(` git commit -m "docs(${nextId}): ${name} - 規格確認歸檔"`);
518
+
519
+ // 5. 自動提交與自動合併回主分支 (Git-Flow 閉環)
520
+ const shouldAutoMerge = !args.includes("--no-merge");
521
+ if (currentBranch && mainBranch && currentBranch !== mainBranch) {
522
+ console.log(`[Git] 正在為分支 ${currentBranch} 建立歸檔 commit...`);
523
+ spawnSync("git", ["add", "project/"], { cwd, encoding: "utf8" });
524
+ const commitRes = spawnSync("git", ["commit", "-m", `docs(${nextId}): ${name} - 規格確認歸檔`], { cwd, encoding: "utf8" });
525
+ if (commitRes.status === 0) {
526
+ console.log(`[Git] 分支 ${currentBranch} 提交完成: docs(${nextId}): ${name} - 規格確認歸檔`);
527
+ }
528
+
529
+ if (shouldAutoMerge) {
530
+ console.log(`[Git] 自動切換至主分支 ${mainBranch} 並合併 ${currentBranch}...`);
531
+ const checkoutRes = spawnSync("git", ["checkout", mainBranch], { cwd, encoding: "utf8" });
532
+ if (checkoutRes.status !== 0) {
533
+ console.error(`[Git 警告] 切換至主分支 ${mainBranch} 失敗: ${(checkoutRes.stderr || "").trim()}`);
534
+ return;
535
+ }
536
+
537
+ const mergeRes = spawnSync("git", ["merge", currentBranch, "--no-edit"], { cwd, encoding: "utf8" });
538
+ if (mergeRes.status === 0) {
539
+ console.log(`[Git] 已成功將 ${currentBranch} 合併至 ${mainBranch}!`);
540
+ // 主分支重新整理索引確保排序與衝突檢查無誤
541
+ reindexProject();
542
+ const statusRes = spawnSync("git", ["status", "--porcelain", "project/index.md"], { cwd, encoding: "utf8" });
543
+ if (statusRes.stdout && statusRes.stdout.trim()) {
544
+ spawnSync("git", ["add", "project/index.md"], { cwd, encoding: "utf8" });
545
+ spawnSync("git", ["commit", "-m", `chore: update project/index.md for ${nextId}`], { cwd, encoding: "utf8" });
546
+ }
547
+ console.log(` 全部歸檔流程完成!已自動領取新號 ${nextId} 並自動合併回主分支 ${mainBranch}。`);
548
+ } else {
549
+ console.error(`[Git 警告] 合併至 ${mainBranch} 發生衝突,請手動確認衝突狀況:\n${(mergeRes.stderr || mergeRes.stdout || "").trim()}`);
550
+ }
551
+ } else {
552
+ console.log(`提示:已跳過自動合併 (--no-merge)。請在完成後手動執行:`);
553
+ console.log(` git checkout ${mainBranch} && git merge ${currentBranch}`);
554
+ }
555
+ } else if (currentBranch && currentBranch === mainBranch) {
556
+ spawnSync("git", ["add", "project/"], { cwd, encoding: "utf8" });
557
+ spawnSync("git", ["commit", "-m", `docs(${nextId}): ${name} - 規格確認歸檔`], { cwd, encoding: "utf8" });
558
+ console.log(`[Git] 已直接於主分支 ${mainBranch} 建立提交完成。`);
559
+ } else {
560
+ console.log(`建議接下來執行:`);
561
+ console.log(` git add project/`);
562
+ console.log(` git commit -m "docs(${nextId}): ${name} - 規格確認歸檔"`);
563
+ }
393
564
  }
394
565
 
395
566
  function reindexProject() {
@@ -459,6 +630,20 @@ function reindexProject() {
459
630
  // 依數字循序排序
460
631
  rows.sort((a, b) => a.num - b.num);
461
632
 
633
+ // 檢查是否有重複編號衝突
634
+ const idCountMap = new Map();
635
+ for (const r of rows) {
636
+ idCountMap.set(r.id, (idCountMap.get(r.id) || 0) + 1);
637
+ }
638
+ const duplicateIds = Array.from(idCountMap.entries()).filter(([_, count]) => count > 1);
639
+ if (duplicateIds.length > 0) {
640
+ console.warn("\n[警告] 發現重複需求編號衝突:");
641
+ for (const [dupId, count] of duplicateIds) {
642
+ console.warn(` - ${dupId}: 共有 ${count} 個需求目錄共用此編號!`);
643
+ }
644
+ console.warn(" 修復建議:請將衝突目錄之一改回草稿名稱後,重新執行 sa-spec finalize 或 scc 重新領號。\n");
645
+ }
646
+
462
647
  const header = `# 需求索引\n\n| 編號 | 標題 | 敘述 | 狀態 | 建立時間 | 更新時間 |\n|---|---|---|---|---|---|\n`;
463
648
  const tableRows = rows
464
649
  .map((r) => `| ${r.id} | ${r.title} | ${r.description} | ${r.status} | ${r.createdAt} | ${r.updatedAt} |`)
@@ -736,7 +921,7 @@ function showHelp() {
736
921
  Usage:
737
922
  sa-spec init [--full] [--lang zh-TW|en|zh-CN|ja|ko]
738
923
  sa-spec new [--id REQ-001] --name login [--branch]
739
- sa-spec finalize --name login
924
+ sa-spec finalize --name login [--no-merge]
740
925
  sa-spec reindex
741
926
  sa-spec check
742
927
  sa-spec upgrade [--dry-run]
@@ -20,9 +20,9 @@
20
20
  | 指令 | 簡寫 | 用途 | 規則 |
21
21
  |---|---|---|---|
22
22
  | `spec:init` | `sci` | 初始化 | 載入並遵守 `AGENTS.md`、`skills/system-analysis/SKILL.md` 與必要規則,確認 `project/` 結構是否存在。 |
23
- | `spec:propose` | `scp` | 提出需求 | 使用者提出需求後,先整理需求摘要、目標、初步流程與待確認事項,不歸檔、不修改既有需求。 |
23
+ | `spec:propose` | `scp` | 提出需求 | 使用者提出需求後,自動依需求名稱建立並切換至專屬 feature 分支(`feat/<name>`,若已在 feature branch 則沿用),建立草稿目錄(內部使用 `{{REQ_ID}}` 佔位),整理需求摘要、目標、初步流程與待確認事項,不歸檔、不修改既有需求。 |
24
24
  | `spec:analyze` | `sca` | 分析與起草 | 先檢查新需求是否與 `project/` 內所有既有需求衝突;無衝突才產出 SRS、UML、OpenAPI、DB Schema 草稿;有衝突則只列出衝突與待釐清問題,待使用者釐清後再次執行 `spec:analyze`(或 `sca`)。 |
25
- | `spec:commit` | `scc` | 確認歸檔 | 只把使用者已確認的版本歸檔到該需求資料夾,更新 `project/index.md`、`project/system-spec.md` 與 `history.md`,並建立本機 Git commit。 |
25
+ | `spec:commit` | `scc` | 確認歸檔 | 只把使用者已確認的版本歸檔到該需求資料夾,自動向主分支確認最新可用序號定號(`REQ-xxx`)、替換佔位符,更新 `project/index.md`、`project/system-spec.md` 與 `history.md`,建立本機 Git commit 後,自動切換回主分支並完成合併。 |
26
26
 
27
27
  # 重要限制
28
28
 
@@ -43,8 +43,9 @@
43
43
  - `handoff.md` 只能根據已確認的 SRS、UML、OpenAPI、DB Schema 與限制整理,不得新增未確認的開發假設。
44
44
  - 交給 OpenSpec 或其他 AI 開發工具時,必須指定它先閱讀 `handoff.md`,並以 `handoff.md` 作為開發入口與限制摘要;其他規格文件作為詳細來源。
45
45
  - `spec:commit` 完成文件歸檔後,必須自動建立本機 Git commit;commit message 以需求編號與需求名稱為主,並加上簡短需求說明,格式為 `docs(REQ-001): 需求名稱 - 簡短需求說明`。
46
+ - `spec:commit` 自動合併到主分支(Git-Flow 閉環):完成 feature 分支 commit 後,若目前處於 feature 分支(`feat/<name>`),AI 必須自動切換回主分支(優先偵測 `main` 或 `master`),拉取/檢查最新狀態並自動合併該 feature 分支 (`git checkout master && git merge feat/<name>`),隨後在主分支重新生成 `project/index.md` 索引,確保主分支規格清單完整且無衝突。
46
47
  - `spec:commit` 只有在使用者明確要求推送 remote,或專案設定啟用 auto-push 時,才可執行 `git push`;push 失敗時只回報認證或網路問題,不得回滾已完成的文件歸檔與本機 commit。
47
- - 多人協作與 Git-Flow 模式(延遲綁定序號):支援多人平行開發。提出需求時可於 feature branch(如 `feat/<name>`)建立草稿目錄,內部使用 `{{REQ_ID}}` 佔位;執行 `spec:analyze` 前應先確保取得 master 最新規格以防漏檢衝突;執行 `spec:commit` 歸檔時向 master 確認最新可用序號並定號,自動將目錄定名為正式 `REQ-xxx`、替換內部佔位符、重新生成 `project/index.md`,避免多人合併時的序號競爭與索引衝突。
48
+ - 多人協作與 Git-Flow 模式(延遲綁定序號與自動合併):支援多人平行開發。執行 `spec:propose`(或 `scp`)時,若目前處於 master/main 分支,AI 必須自動依需求名稱建立並切換至專屬 feature branch(如 `feat/<name>`),並在 `project/<name>/` 建立草稿目錄,內部使用 `{{REQ_ID}}` 佔位,無需使用者手動敲 CLI;執行 `spec:analyze` 前應先確保取得 master 最新規格以防漏檢衝突;執行 `spec:commit` 歸檔時自動向 master 確認最新可用序號定號,自動將目錄定名為正式 `REQ-xxx`、替換內部佔位符、完成提交並自動合併回 master,避免多人合併時的序號競爭與索引衝突。
48
49
  - 文件未經使用者確認,不得標示為 `已審核`。
49
50
  - `uml/` 內的 UML 圖不限單一 UML 圖型;`spec:analyze` 必須依需求選擇最能說明流程、互動、狀態或資料關係的 UML 圖,例如活動圖、循序圖、使用案例圖、狀態圖或類別圖,不可為了固定格式硬產生活動圖。**嚴禁將多張圖混在同一個檔案中**,必須在需求資料夾下的 `uml/` 目錄拆分成獨立的 `.puml` 檔案(例如 `uml/main.puml`、`uml/sequence.puml`、`uml/state.puml`),且每個檔案只能包含一組 `@startuml` 與 `@enduml`,以避免 PlantUML 渲染或預覽失敗。
50
51
  - 一次最多問 3 個待確認問題,優先詢問會影響範圍、資料、權限、API、DB 或驗收的問題。
@@ -17,23 +17,23 @@
17
17
 
18
18
  ---
19
19
 
20
- ### Quick Start (English)
20
+ ### Quick Start
21
21
 
22
22
  ```bash
23
- # Global installation via npm
23
+ # 1. Global installation via npm
24
24
  npm install -g @loviusc/sa-spec
25
25
 
26
- # Initialize project with English specification rules
27
- sa-spec init --full --lang en
28
-
29
- # Create a feature draft branch
30
- sa-spec new --name cart-checkout --branch
26
+ # 2. Initialize project specification rules (supports zh-TW, en, zh-CN, ja, ko)
27
+ sa-spec init --full
31
28
  ```
32
29
 
33
- **Core AI Workflow in VS Code:**
34
- `spec:init (sci)` $\to$ `spec:propose (scp)` $\to$ `spec:analyze (sca)` $\to$ `spec:commit (scc)`
30
+ **🤖 Core AI Workflow in Chat (VS Code / Claude / Antigravity / Cursor):**
31
+ - `spec:init` (`sci`) $\to$ 鎖定專案主要語言與系統規格基準
32
+ - `spec:propose` (`scp`) $\to$ **AI 自動建立 feature 分支與草稿目錄**(零手動指令)
33
+ - `spec:analyze` (`sca`) $\to$ AI 衝突檢查並產出規格文件草稿
34
+ - `spec:commit` (`scc`) $\to$ **AI 自動向主分支領取新號並自動合併回主分支!**
35
35
 
36
- 👉 **Read full English documentation: [README.en.md](./README.en.md)**
36
+ 👉 **Language Guides: [English](./README.en.md) | [繁體中文](./README.zh-TW.md) | [简体中文](./README.zh-CN.md) | [日本語](./README.ja.md) | [한국어](./README.ko.md)**
37
37
 
38
38
  ---
39
39
 
@@ -397,45 +397,51 @@ docs(REQ-001): 番茄時鐘 - 新增專注與休息計時需求
397
397
 
398
398
  ```mermaid
399
399
  flowchart TD
400
- A[開新需求: sa-spec new --name cart --branch] --> B[建立並切換至 feat/cart 分支<br/>以 project/cart/ 建立草稿目錄]
401
- B --> C[AI 分析與起草: sca<br/>內部使用 {{REQ_ID}} 佔位<br/>自動同步 master 規格防護]
402
- C --> D{使用者確認通過<br/>執行 scc 或 finalize}
403
- D --> E[查詢 master 最新序號 N]
404
- E --> F[重命名為 project/REQ-N+1-cart/<br/>批次置換 {{REQ_ID}} 為正式號碼]
405
- F --> G[sa-spec reindex: 動態重構 index.md]
406
- G --> H[合併回 master: 零衝突、序號永遠連續!]
400
+ A["開新需求: scp 或 sa-spec new --branch"] --> B["建立並切換至 feat/cart 分支<br/>以 project/cart/ 建立草稿目錄"]
401
+ B --> C["AI 分析與起草: sca<br/>內部使用 {{REQ_ID}} 佔位<br/>自動同步 master 規格防護"]
402
+ C --> D{"使用者確認通過<br/>執行 scc 或 finalize"}
403
+ D --> E["自動查詢主分支最新序號 N"]
404
+ E --> F["自動定號為 project/REQ-N+1-cart/<br/>批次置換 {{REQ_ID}} 並建立 commit"]
405
+ F --> G["自動合併至 master 主分支<br/>動態重新生成 project/index.md"]
406
+ G --> H["完成閉環:零衝突、序號永遠連續!"]
407
407
  ```
408
408
 
409
409
  ### 多人協作四步流程
410
410
 
411
411
  #### 1. 提出需求(草稿與分支隔離)
412
- 開需求時**不搶序號**,直接以語意名稱建立分支與草稿目錄:
413
-
414
- ```bash
415
- # 自動建立並切換至 feat/cart-checkout 分支,建立 project/cart-checkout/ 草稿目錄
416
- sa-spec new --name cart-checkout --branch
417
- ```
418
- 在 VS Code 側邊欄輸入:
419
- ```text
420
- scp 購物車結帳需求
421
- ```
412
+ 開需求時**不搶序號**,直接以語意名稱建立分支與草稿目錄。支援兩種方式:
413
+
414
+ * **方式 A(純 AI 自動化,最推薦)**:在 AI 側邊欄直接輸入:
415
+ ```text
416
+ scp 購物車結帳需求
417
+ ```
418
+ AI 收到後,會**自動在背景執行 `git checkout -b feat/cart-checkout` 建立並切換分支**,並在 `project/cart-checkout/` 建立草稿目錄!
419
+ * **方式 B(CLI 終端指令)**:
420
+ ```bash
421
+ sa-spec new --name cart-checkout --branch
422
+ ```
422
423
  * **特點**:目錄名稱為 `project/cart-checkout/`,內部所有檔案皆使用 `{{REQ_ID}}` 佔位符。多人同時提出需求互不干擾、零衝突。即使需求中途放棄,直接刪除分支即可,絕不留下幽靈跳號。
423
424
 
424
425
  #### 2. 分析與衝突檢查(規格基線同步)
425
426
  在分支上執行 `spec:analyze`(或 `sca`)前,工具會確認已取得 `master`(或 `origin/master`)的最新規格,確保是以全專案最新基準進行影響評估,避免因分支落後而漏檢權限、API 或資料表衝突。
426
427
 
427
- #### 3. 確認歸檔與延遲定號(Late-Binding)
428
- 當需求規格確認無誤,準備歸檔時:
429
-
430
- ```bash
431
- # 自動向 master 查詢最新可用序號(例如 REQ-006)
432
- sa-spec finalize --name cart-checkout
433
- ```
434
- * **自動化執行**:
435
- 1. 取得最新序號(例如目前最大是 `REQ-005`,自動取得 `REQ-006`)。
436
- 2. 使用 `git mv` 將目錄重命名為 `project/REQ-006-cart-checkout`。
437
- 3. 自動掃描並將目錄內所有檔案的 `{{REQ_ID}}` 批次置換為 `REQ-006`。
438
- 4. 自動執行 `reindex` 重建需求總表。
428
+ #### 3. 確認歸檔與延遲定號(自動領號 + 自動合併回主分支)
429
+ 當需求規格經使用者確認無誤,準備歸檔時:
430
+
431
+ * **方式 A(純 AI 自動化,最推薦)**:在 AI 側邊欄直接輸入:
432
+ ```text
433
+ scc
434
+ ```
435
+ AI 收到後會自動完成全自動 Git-Flow 閉環作業:
436
+ 1. **自動向主分支查詢並取得新號**:查詢 master/main 目前最大序號(例如最大是 `REQ-005`,自動領取最新號 `REQ-006`),避免號碼競爭。
437
+ 2. **自動定名更名**:使用 `git mv` 將目錄重命名為 `project/REQ-006-cart-checkout`,並將目錄內所有檔案的 `{{REQ_ID}}` 批次置換為 `REQ-006`。
438
+ 3. **自動建立本機提交**:在 feature 分支自動建立 commit `docs(REQ-006): cart-checkout - 規格確認歸檔`。
439
+ 4. **自動合併至主分支**:自動切換至主分支(`git checkout master`),將 feature 分支自動合併回主分支(`git merge feat/cart-checkout`),並重新生成主分支的 `project/index.md`,保證主分支規格最新且永遠不衝突!
440
+ * **方式 B(CLI 終端指令)**:
441
+ ```bash
442
+ sa-spec finalize --name cart-checkout
443
+ ```
444
+ CLI 同樣會自動查詢主分支最新序號定號、置換佔位符、完成提交並自動合併回 master!若想保留在當前分支手動處理,可加上 `--no-merge` 參數。
439
445
 
440
446
  #### 4. 消除索引合併衝突(`sa-spec reindex`)
441
447
  `project/index.md` 改為由目錄動態生成的「衍生清單」,不再依賴脆弱的手動 append:
@@ -718,6 +724,18 @@ git push origin master
718
724
 
719
725
  ## 更新紀錄
720
726
 
727
+ ### v0.2.4
728
+ - **序號重複主動告警與重新定號自癒支援**:
729
+ - **唯一性主動檢驗**:`sa-spec check` 與 `sa-spec reindex` 加入重複序號檢測,遇到衝突時即時發出 WARNING 告警並列出衝突目錄與修復建議。
730
+ - **重新定號(Re-number)自癒**:`sa-spec finalize` 智慧支援既有 `REQ-xxx` 目錄重新定號,自動計算最新序號並批次更新目錄內所有檔案的舊編號,零手動修復負擔。
731
+ - **Quick Start 精簡**:移除頂部容易引起誤解的手動建立分支指令,聚焦 AI 4 指令全自動工作流。
732
+
733
+ ### v0.2.3
734
+ - **自動領號與自動合併至主分支(Git-Flow 自動化閉環)**:
735
+ - **自動從主分支取新號**:執行 `spec:commit`(`scc`)或 `sa-spec finalize` 時,自動向主分支(`master`/`main`)查詢最新已使用最大序號並定號(`REQ-xxx`),自動置換內部佔位符 `{{REQ_ID}}`,徹底防止多人協作時在 feature 分支發生的號碼撞車與落後問題。
736
+ - **自動合併回主分支**:完成 feature 分支 commit 後,自動切換至主分支(`git checkout master`)並將該 feature 分支自動合併回主分支(`git merge feat/<name>`),並在主分支自動重新整理 `project/index.md`,打造完全自動化的 Git-Flow 零衝突閉環。
737
+ - **CLI 自動化支援**:`sa-spec finalize --name <name>` 預設自動執行主分支序號運算、定號置換、提交與合併回主分支(支援 `--no-merge` 參數跳過自動合併)。
738
+
721
739
  ### v0.2.2
722
740
  - **npm 首頁與專案文件全多語系化(Multi-Language Documentation on npm)**:
723
741
  - **5 國獨立完整說明文件**:新增 `README.en.md`(英文)、`README.zh-CN.md`(簡中)、`README.ja.md`(日文)、`README.ko.md`(韓文)與 `README.zh-TW.md`(繁中)。
@@ -21,9 +21,9 @@ description: 用 init、propose、analyze、commit 四段流程整理需求,
21
21
  | 指令 | 簡寫 | 用途 | 執行規則 |
22
22
  |---|---|---|---|
23
23
  | `spec:init` | `sci` | 初始化 | 載入並遵守 `AGENTS.md`、本 skill 與必要規則,確認 `project/` 結構是否存在。 |
24
- | `spec:propose` | `scp` | 提出需求 | 整理使用者需求為摘要、目標、初步流程與待確認事項;不歸檔、不修改既有需求。 |
24
+ | `spec:propose` | `scp` | 提出需求 | 使用者提出需求後,自動依需求名稱建立並切換至專屬 feature 分支(`feat/<name>`,若已在 feature branch 則沿用),建立草稿目錄(內部使用 `{{REQ_ID}}` 佔位),整理需求摘要、目標、初步流程與待確認事項;不歸檔、不修改既有需求。 |
25
25
  | `spec:analyze` | `sca` | 分析與起草 | 先檢查新需求是否與 `project/` 內所有既有需求衝突;無衝突才產出 SRS、UML、OpenAPI、DB Schema 草稿;有衝突則只列出衝突與待釐清問題,待使用者釐清後再次執行 `spec:analyze`(或 `sca`)。 |
26
- | `spec:commit` | `scc` | 確認歸檔 | 只把使用者已確認的版本歸檔到該需求資料夾,更新 `project/index.md`、`project/system-spec.md` 與 `history.md`,並建立本機 Git commit。 |
26
+ | `spec:commit` | `scc` | 確認歸檔 | 只把使用者已確認的版本歸檔到該需求資料夾,自動向主分支確認最新可用序號定號(`REQ-xxx`)、替換佔位符,更新 `project/index.md`、`project/system-spec.md` 與 `history.md`,建立本機 Git commit 後,自動切換回主分支並完成合併。 |
27
27
 
28
28
  ## 硬性限制
29
29
 
@@ -47,8 +47,9 @@ description: 用 init、propose、analyze、commit 四段流程整理需求,
47
47
  - `handoff.md` 只能根據已確認的 SRS、UML、OpenAPI、DB Schema 與限制整理,不得新增未確認的開發假設。
48
48
  - 交給 OpenSpec 或其他 AI 開發工具時,必須指定它先閱讀 `handoff.md`,並以 `handoff.md` 作為開發入口與限制摘要;其他規格文件作為詳細來源。
49
49
  - `spec:commit` 完成文件歸檔後,必須自動建立本機 Git commit;commit message 以需求編號與需求名稱為主,並加上簡短需求說明,格式為 `docs(REQ-001): 需求名稱 - 簡短需求說明`。
50
+ - `spec:commit` 自動合併到主分支(Git-Flow 閉環):完成 feature 分支 commit 後,若目前處於 feature 分支(`feat/<name>`),AI 必須自動切換回主分支(優先偵測 `main` 或 `master`),拉取/檢查最新狀態並自動合併該 feature 分支 (`git checkout master && git merge feat/<name>`),隨後在主分支重新生成 `project/index.md` 索引,確保主分支規格清單完整且無衝突。
50
51
  - `spec:commit` 只有在使用者明確要求推送 remote,或專案設定啟用 auto-push 時,才可執行 `git push`;push 失敗時只回報認證或網路問題,不得回滾已完成的文件歸檔與本機 commit。
51
- - 多人協作與 Git-Flow 模式(延遲綁定序號):支援多人平行開發。提出需求時可於 feature branch(如 `feat/<name>`)建立草稿目錄,內部使用 `{{REQ_ID}}` 佔位;執行 `spec:analyze` 前應先確保取得 master 最新規格以防漏檢衝突;執行 `spec:commit` 歸檔時向 master 確認最新可用序號並定號,自動將目錄定名為正式 `REQ-xxx`、替換內部佔位符、重新生成 `project/index.md`,避免多人合併時的序號競爭與索引衝突。
52
+ - 多人協作與 Git-Flow 模式(延遲綁定序號與自動合併):支援多人平行開發。執行 `spec:propose`(或 `scp`)時,若目前處於 master/main 分支,AI 必須自動依需求名稱建立並切換至專屬 feature branch(如 `feat/<name>`),並在 `project/<name>/` 建立草稿目錄,內部使用 `{{REQ_ID}}` 佔位,無需使用者手動敲 CLI;執行 `spec:analyze` 前應先確保取得 master 最新規格以防漏檢衝突;執行 `spec:commit` 歸檔時自動向 master 確認最新可用序號定號,自動將目錄定名為正式 `REQ-xxx`、替換內部佔位符、完成提交並自動合併回 master,避免多人合併時的序號競爭與索引衝突。
52
53
  - 文件未經使用者確認,不得標示為 `已審核`。
53
54
  - `uml/` 內的 UML 圖不限單一 UML 圖型;`spec:analyze` 必須依需求選擇最能說明流程、互動、狀態或資料關係的 UML 圖,例如活動圖、循序圖、使用案例圖、狀態圖或類別圖,不可為了固定格式硬產生活動圖。**嚴禁將多張圖混在同一個檔案中**,必須在需求資料夾下的 `uml/` 目錄拆分成獨立的 `.puml` 檔案(例如 `uml/main.puml`、`uml/sequence.puml`、`uml/state.puml`),且每個檔案只能包含一組 `@startuml` 與 `@enduml`,以避免 PlantUML 渲染或預覽失敗。
54
55
  - 一次最多問 3 個待確認問題,優先詢問會影響範圍、資料、權限、API、DB 或驗收的問題。
@@ -146,5 +147,5 @@ init (sci) -> propose (scp) -> analyze (sca)
146
147
  analyze 無衝突 -> 產出待確認版本 -> commit (scc)
147
148
  analyze 有衝突 -> 釐清問題 -> analyze (sca)
148
149
 
149
- commit (scc) -> 歸檔 -> 本機 Git commit
150
+ commit (scc) -> 自動拿新號定號 -> feature commit -> 自動合併至主分支 (master/main)
150
151
  ```