以 npm 佈署並提供 tea-sdlc 指令 #25

Closed
opened 2026-09-17 05:51:05 +00:00 by jiantw83 · 2 comments
Member

母議題

#1 — tea-sdlc:以 tea 驅動 SDLC 全流程的跨平台指令組

Problem Statement

使用者要在一台新機器上用到這六個流程指令,目前得為七個 agent 平台各學一套安裝流程:Claude Code 走 marketplace、Antigravity 得先 clone 再給本地路徑、OpenCode 得手動 cp -r 到 skills 目錄。裝完之後其實什麼都沒有——skills/ 是空的,真正會生效的轉接檔要靠尚未實作的 install.js 產生。

更根本的是,這七套流程都只解決「把檔案放到哪」,沒有一套解決「放進去的檔案要指向哪個正本」。議題 #1 原本的答案是「路徑於產生時替換為絕對路徑」,但那個答案在用版本管理器管 Node 的機器上是壞的:fnm 把 Node 版號寫進全域安裝路徑(.../node-versions/v26.7.0/installation/lib/node_modules),升一次 Node,七個平台的轉接檔會同時指向不存在的檔案,而使用者不會收到任何錯誤——只會發現 /sdlc-plan 不動了。

Solution

用 npm 當佈署管道,並讓轉接檔不再持有路徑。

使用者跑一次 npm i -g https://gitea.jsc.idv.tw/plugins/tea-sdlc.git,得到一個 tea-sdlc 指令;跑一次 tea-sdlc install,六個流程指令就在他裝了的每個平台上可用。轉接檔裡沒有路徑,只有一句「執行 tea-sdlc prompt --name sdlc-plan 並遵照其輸出」——正本在哪由 PATH 上的 tea-sdlc 自己回推,Node 升級、換版本管理器、改 npm prefix 都不影響它。

這個設計讓更新變成兩件粗細不同的事:改流程正本、腳本、模板或規則,只要重跑 npm i -g,下一次叫用就讀到新的,不必重新佈署;只有指令數量或轉接檔模板本身變了,才需要再跑一次 tea-sdlc install。因此不需要 update 子指令。

本工作包負責 npm 打包與 tea-sdlc 指令本身(prompt、status、薄殼),轉接檔的產生與移除留在 #17。

User Stories

  1. 身為第一次接觸 tea-sdlc 的使用者,我想用一行 npm 指令裝好,以便不用為七個平台各學一套安裝流程。
  2. 身為使用者,我不想為了安裝而設定任何 registry 或憑證,以便在新機器上直接裝起來。
  3. 身為使用者,我想用 git tag 指定要裝的版本,以便出問題時退回已知可用的版本。
  4. 身為使用者,我想安裝後得到單一入口指令 tea-sdlc,以便佈署、移除、查狀態都從同一個地方進去。
  5. 身為使用者,我想跑一次 tea-sdlc status 就知道目前版本、正本位置與環境缺什麼,以便在出事前先知道。
  6. 身為使用者,我想讓 status 順便告訴我 Gitea 登入還有沒有效,以便不用等到跑流程指令才發現 token 過期。
  7. 身為腳本使用者,我希望 status 在環境不健康時指令本身仍算成功,以便分辨「查詢失敗」與「成功查到問題」這兩件下一步完全不同的事。
  8. 身為 agent,我想用 tea-sdlc prompt --name <指令名> 取得流程正本原文,以便照著執行而不必知道檔案放在哪。
  9. 身為 agent,我想拿到的是原樣 markdown 而不是 JSON 字串,以便不必反跳脫幾百行內容、也不會因此讀錯。
  10. 身為 agent,我想在指令失敗時拿到結構化錯誤,以便判斷該回報什麼給使用者。
  11. 身為使用者,我想改完流程正本就立即生效、不必重跑佈署,以便迭代規則的成本夠低到我真的會去改。
  12. 身為使用者,我想在轉接檔已經過時的時候被主動提醒,以便不會沉默地用著舊的指令清單。
  13. 身為使用者,我想在轉接檔版本與套件不符時,提醒直接出現在 agent 每次都會讀到的地方,以便不必依賴我記得去查狀態。
  14. 身為使用者,我想知道移除要先跑哪一個指令,以便不會先砍掉 npm 套件、留下一堆再也刪不掉的孤兒轉接檔。
  15. 身為開發者,我想用 npm link 開發,改動 working tree 立即生效,以便不必每改一行就重裝。
  16. 身為開發者,我想讓 status 告訴我目前 tea-sdlc 實際解析到哪個目錄,以便確認自己是不是在 link 模式。
  17. 身為維護者,我想用白名單決定套件內容,以便測試暫存與 .tmp/ 不會被打包給使用者。
  18. 身為維護者,我想在套件缺檔時於安裝當下就炸開,以便不會拖到使用者跑流程指令才發現。
  19. 身為維護者,我想 bin 的參數與輸出契約和既有十五支腳本完全一致,以便只維護一套契約而不是兩套。
  20. 身為維護者,我想版本字串只有一個來源,以便產生標記、版本比對與狀態回報不會各說各話。
  21. 身為使用者,我想在還沒裝 tea 的情況下先把 plugin 裝起來看看有哪些指令,以便不被逼著先裝一堆東西才能評估。
  22. 身為使用者,我想在缺少執行環境時仍被明確告知缺什麼,以便不會誤以為一切就緒。
  23. 身為使用者,我不想讓套件在 npm 安裝過程中自動改我的 agent 設定,以便安裝行為完全可預測。
  24. 身為 CI,我想安裝過程不會進入互動提示,以便自動化流程不會卡在等待輸入。
  25. 身為使用者,我想 README 明說更新就是重跑安裝那兩行,以便不會用 npm update -g 然後得到一個沒有真的更新的結果。
  26. 身為使用者,我想 README 把既有的七套 marketplace 安裝法留在附錄,以便日後 skills/ 有內容時還查得到。
  27. 身為使用者,我想每個會寫入的子指令都支援 --dry-run,以便先看清楚會發生什麼再決定。
  28. 身為使用者,我想在 tea-sdlc 不在 PATH 上時,agent 停下來叫我安裝,而不是自己憑空實作一套看起來像那麼回事、卻完全沒碰 Gitea 的流程。
  29. 身為使用者,我想在升級 Node 之後 tea-sdlc 失效時拿到明確訊息與修復指引,以便知道那是安裝位置問題而不是流程壞了。
  30. 身為維護者,我想 bin 的測試走真實子行程,以便測到的東西就是使用者真正會執行的東西。
  31. 身為維護者,我想 prompt 的原樣輸出有自己的斷言,以便不必為了它而稀釋其餘子指令的單行 JSON 契約。
  32. 身為維護者,我想有一條測試守住打包白名單,以便日後新增目錄時不會忘記把它加進去、也不會把測試檔案漏出去。

範圍邊界

本工作包做:npm 打包設定、bin/tea-sdlc.js 薄殼、prompt 與 status 兩個子指令、runBin() 測試 helper、README 的安裝/更新/移除段落。

本工作包不做:轉接檔的產生與移除(install.js 的實作、平台偵測、勾選介面、產生標記的寫入),那些是 #17。本工作包只提供 install / uninstall 兩個子指令的薄殼 dispatch 與 flag 解析,動作實作由 #17 補上。

因此本工作包阻擋 #17:#17 的轉接檔內容是「執行 tea-sdlc prompt --name …」,那條驗收標準要能真的驗過,prompt 必須先存在。

Implementation Decisions

佈署管道

以 git URL 安裝,不發佈到任何 registry:npm i -g https://gitea.jsc.idv.tw/plugins/tea-sdlc.git。版本以 git tag 指定(…git#v0.1.0)。選這條是因為 README 其餘七個平台的安裝指令本來就都用同一個 git URL,多一條佈署管道不該多一套憑證與 registry 設定;日後若需要真 semver,升級到 Gitea 自架 npm registry 只是補 publishConfig,不是重寫。

只支援全域安裝,明確拒絕 npx。npx 的套件落在會被 npm 自動清掉的快取目錄,雖然本設計的轉接檔不持有路徑、不會因此斷裂,但 npx 每次叫用重新解析套件的行為會讓「正本是哪一版」無法回答,與 status 的職責直接衝突。

套件內容

package.json 移除 private: true、新增 bin、新增 files 白名單(涵蓋 prompts / scripts / templates / references / skills / install.js / bin / README.md / AGENTS.md)。選白名單而非 .npmignore 黑名單,是因為兩者的失敗模式不對稱:白名單漏掉東西會在安裝當下被既有的 PLUGIN_LAYOUT_BROKEN 檢查立刻炸出來,黑名單漏掉排除則是沉默地把測試檔案發給使用者。

不新增 prepare script:本專案零外部套件、無建置步驟,加了只是多一個會在 git 安裝時跑的未定義行為。「零外部套件」這條約束不變,dependencies 與 devDependencies 一律不得出現。

指令介面

單一 bin tea-sdlc,入口是薄殼 bin/tea-sdlc.js,只負責取第一個位置參數當子指令、把其餘 argv 交給 lib.js 的 parseFlags()、再 dispatch 到 install.js 的具名 export。動作實作不放進薄殼,因為 install.js 在模組邊界上的職責是「平台偵測與轉接檔產生,唯一知道各平台目錄結構的地方」,把參數解析與狀態比對塞進去會讓它變成兩件事。

薄殼複用 lib.js 的 parseFlags 與 main,因此 bin 的輸入輸出契約與既有十五支腳本自動一致:只接受具名 flag、不認得的 flag 一律拒絕、輸出單行 JSON {ok, data, error:{code, message}}、exit 0 或 1。注意 parseFlags 明確拒絕位置參數,所以子指令必須由薄殼在交棒前自行取走,而 prompt 的目標以 --name 指定,不是位置參數。

子指令四個:install、uninstall(薄殼由本工作包提供,動作屬 #17)、prompt、status。沒有 update 子指令,理由見「Further Notes」。

prompt 的契約

tea-sdlc prompt --name <指令名> [--adapter-version <版本>]

成功時把對應的流程正本原樣印到 stdout,exit 0。這是全專案唯一輸出非 JSON 的子指令,例外的理由寫死成一句:它的輸出要餵給模型讀。把幾百行 markdown 包進單行 JSON、再逼模型反跳脫,只會增加它讀錯的機率;JSON envelope 的價值是可程式化判斷成敗,而這條路徑的成功就是內容本身。失敗時仍走 JSON envelope + exit 1(PROMPT_NOT_FOUND)——成功是內容,失敗才需要結構。

--adapter-version 由轉接檔帶入自己被產生時的版本。與套件版本不符時,在輸出的 markdown 最前面加一行警告,指示使用者重跑 tea-sdlc install,其後照常輸出正本原文。選這個機制而非只靠 status,是因為轉接檔過時不會壞掉——它只是繼續指向一個指令名稱,症狀是「新加的指令怎麼沒出現」這種查不出來的東西;而警告出現在模型每次叫用必定會讀到的位置,比任何被動機制都可靠。

status 的契約

回報四類事實:版本與正本位置(readlink -f $(command -v tea-sdlc),順帶揭露是不是 npm link)、環境第一層(node / git / tea 是否在 PATH)、環境第二層(Gitea 登入是否有效,會打網路)、各平台轉接檔現況(偵測到哪些平台、裝了幾份、有沒有被改過)。

第二層要查:status 存在的理由就是「在出事前告訴我現在是什麼狀態」,而 token 失效是實務上最常見的故障,一次 GET /user 很便宜,為它多開一個 --deep 旗標是把判斷推回給使用者。

data 形狀:{version, root, linked, healthy, environment:{node, git, tea, login}, platforms:[{name, dir, detected, adapters:{expected, present, stale}}]}。

ok 與 healthy 分離:ok 在既有契約裡的意思是「這支腳本跑成功了」,不兼差表達環境健康與否;環境不健康時 ok 仍為 true,健康旗標是 data.healthy。混用會讓呼叫端分不出「status 掛了」和「status 成功查到你環境有問題」,而這兩件事的下一步完全不同。

前置檢查的分工

install 只跑四層前置檢查的第一層(執行環境),且只警告不中止。缺 tea 完全不影響轉接檔產生,硬擋等於逼使用者為了裝 plugin 先去裝 tea;但完全不查也不行——安裝是一次性動作,使用者裝完就走,沒有警告他會以為一切就緒。第三、四層(repo issues 寫入權、時間追蹤)在安裝當下沒有 --repo 可查,無從執行。

lib.js 的 checkEnvironment() 不加查 tea-sdlc 自己:那段檢查碼要能執行,tea-sdlc 必然已經跑起來了,查它是自證的廢話。真正會出事的時刻是「轉接檔被讀到、但指令不存在」,那一刻連 Node 都還沒啟動,任何程式內的檢查都攔不到——只能靠轉接檔本身的純文字指引(#17)。status 則要查,因為那正是它的職責。

沒有 --link 旗標

轉接檔不持有路徑,npm link 直接換掉 PATH 上的 tea-sdlc,working tree 自動成為正本,不需要任何旗標配合。留一個什麼都不做的旗標比沒有它更糟。「link 模式要可見」這個需求由 status 的 root / linked 欄位滿足,而且比旗標誠實:它報的是現況,不是安裝當下宣告的意圖。

錯誤碼

沿用 ScriptError。新增 MISSING_SUBCOMMAND、UNKNOWN_SUBCOMMAND、PROMPT_NOT_FOUND。

文件

README 的安裝段改寫為三段,且移除那段要明寫順序反直覺之處:安裝=npm i -g <git url> 後 tea-sdlc install;更新=重跑同樣那兩行,並明寫「不要用 npm update -g,git 相依之下它不保證重新解析 ref」;移除=先 tea-sdlc uninstall 再 npm rm -g tea-sdlc(順序反了就沒有指令可以拿來刪轉接檔了)。

既有七套 marketplace 安裝流程收進附錄「其他安裝方式」,不刪除——那些機制本身有用(自動更新、plugin 清單),等 skills/ 有內容時會回來。但也不與 npm 並列為「兩種都行」,因為現在 skills/ 是空的,照那些流程裝完不會出現任何指令。

AGENTS.md 的模組邊界表新增 bin/ 一列(職責:子指令解析與 dispatch;邊界:不含任何平台目錄知識與動作實作),隨本工作包的 PR 一起改——文件描述現況,議題描述意圖,在 bin/ 還不存在時先寫進邊界表會讓讀它的 AI 助理去找一個不存在的目錄。

Testing Decisions

什麼是好的測試:只測外部行為,不測實作細節。對本工作包而言,外部行為就是「給定一個子指令與一組 flag,tea-sdlc 印出什麼、exit code 是多少」。不測 install.js 的具名 export、不測薄殼的 dispatch 表、不測 JSON 內部的建構過程。

接縫:全專案維持兩個接縫,都在行程邊界,本工作包新增其中第二個。

  • 既有:scripts/*.js 的 CLI 邊界,helper runScript()。
  • 新增:bin/tea-sdlc.js 的 CLI 邊界,helper runBin()。

選在這裡而非函式層,理由與既有接縫相同:它是七個平台共用的實際呼叫方式,測到的東西就是使用者真正會執行的東西,且不會因內部重構而破碎。

為什麼另寫 runBin() 而不擴充 runScript():runScript() 的路徑寫死在 scripts/ 底下,而它的 parseSingleLine() 是刻意在輸出不是恰好一行時主動拋錯的——那段斷言的存在本身就是在測「單行 JSON」這個契約。給它加一個 raw: true 旗標等於在十五支腳本的契約上開一個只有 bin 要用的後門。兩個 helper 各自持有各自的契約斷言,互不污染:install / uninstall / status 斷言單行 JSON,prompt 斷言原樣 markdown。

外部相依的隔離:status 的第二層檢查走 lib.js 既有的單一 HTTP 出口,測試沿用 TEA_SDLC_API_BASE 指向本機 stub server。command -v 類的 PATH 探測沿用既有的 pathWithOnly(),精確造出「缺 git」「缺 tea」的情境。

測試對象與順序

  1. prompt 的輸出形狀——多行原樣 markdown、非 JSON、exit 0;--name 指向不存在的指令時回單行 JSON + exit 1。這是被叫用頻率最高、也是唯一破格的契約。
  2. prompt --adapter-version 的版本比對——相符時輸出與不帶該 flag 時完全一致;不符時第一行為警告、其後逐字等於正本原文。
  3. status 的輸出契約——單行 JSON;登入失效時 ok 仍為 true 而 data.healthy 為 false(這條專門守住 ok 與 healthy 的分離);缺 git / tea 時對應欄位為 false。
  4. 薄殼的參數處理——缺子指令、未知子指令、未知 flag 各回對應錯誤碼 + exit 1。
  5. 打包白名單——以 npm pack --dry-run 的檔案清單斷言 prompts/、scripts/、templates/、references/ 在內,test/、.tmp/ 不在內。

Prior art:test/script-contract.test.js 已用子行程 + stub server 驗過腳本契約與四層前置檢查;helper run-script.js、stub-gitea.js、temp-repo.js 可直接沿用其模式。測試執行器維持 Node 內建 node:test + node:assert,暫存一律寫到 .tmp/。

驗收標準

  • npm i -g <git url> 後 PATH 上出現 tea-sdlc 指令
  • package.json 已移除 private、新增 bin、新增 files 白名單,且未新增 prepare、未出現任何 dependencies / devDependencies
  • npm pack --dry-run 的清單包含 prompts/、scripts/、templates/、references/,不包含 test/、.tmp/
  • 薄殼取第一個位置參數為子指令,其餘交由 parseFlags 處理;缺子指令、未知子指令、未知 flag 各回對應錯誤碼與 exit 1
  • install / uninstall / status 輸出單行 JSON {ok, data, error:{code, message}}
  • tea-sdlc prompt --name <指令名> 原樣輸出流程正本、exit 0;名稱不存在時回單行 JSON PROMPT_NOT_FOUND + exit 1
  • --adapter-version 與套件版本不符時,輸出第一行為重跑 tea-sdlc install 的警告,其後為正本原文
  • tea-sdlc status 回報 version、root、linked、healthy、environment(node/git/tea/login);platforms 因平台偵測屬 #17 的模組邊界,改由 #17 交付
  • 環境不健康時 status 的 ok 仍為 true,健康狀態反映在 data.healthy
  • status 的 root 在 npm link 情境下指向 working tree 且 linked 為 true
  • install 缺少 node / git / tea 時印出警告但不中止
  • 新增 runBin() helper,install/uninstall/status 斷言單行 JSON、prompt 斷言原樣 markdown,且 runScript() 的既有斷言未被放寬
  • README 安裝段以 npm 為唯一建議路徑,並說明 prompt 與 status;更新/移除的完整指令表(含「不要用 npm update -g」與「移除要先 tea-sdlc uninstall 再 npm rm -g」)因需 install/uninstall 實際可用,改由 #17 交付
  • README 既有七套 marketplace 流程移入附錄「其他安裝方式」,未刪除
  • AGENTS.md 模組邊界表新增 bin/ 一列

Out of Scope

  • 轉接檔的產生與移除、平台偵測、勾選介面、產生標記的寫入——全部屬於 #17。
  • 發佈到 npmjs 或 Gitea 自架 npm registry(日後若需要,補 publishConfig 即可)。
  • npx 一次性執行。
  • update 子指令。
  • 自動安裝 Node、git、tea 或任何執行環境。
  • postinstall 之類在 npm 安裝過程中自動佈署的機制。
  • Issue.time_estimate 的填寫(等 #8 的估算機制建立後回填)。
  • Windows PowerShell 以外的殼層適配;README 既有的 PowerShell 對照表沿用。

Further Notes

為什麼沒有 update 子指令。轉接檔不持有路徑也不持有內容副本,所以更新被拆成粗細不同的兩件事:改 prompts/、scripts/、templates/、references/ 時,npm i -g <git url> 之後下一次叫用就讀到新的,完全不必重新佈署;只有指令數量或轉接檔模板本身變了,才需要再跑一次 tea-sdlc install。一個語義等同「重跑 install」的 update 只會讓使用者猶豫該用哪個。

重跑 install 的預設值(實作屬 #17,但決策源自本工作包的更新語義):偵測到既有轉接檔時,預設只勾已經裝過的平台;完全沒有既有轉接檔時才預設全勾。這讓「重跑 install」天然就是更新動作,不會因為使用者半年後裝了新平台就把轉接檔擴散過去——帶擴散副作用的更新動作會讓人不敢隨手重跑,然後轉接檔就一直過時。這條取代了 #17 原本的「預設全勾」。

fnm 的實測(Q6 選擇改採指令叫用的證據):本機 npm root -g 為 /root/.local/share/fnm/node-versions/v26.7.0/installation/lib/node_modules,Node 版號寫在路徑裡,且 fnm 為每個 Node 版本開獨立的全域根——升級 Node 之後那條路徑不只失效,套件根本不在新的全域根裡。

PATH 可用性的實測(指令叫用機制的前提):目前 shell 與 bash -lc 都找得到全域 bin,但走的是不同的 fnm shim 根(/run/user/0/fnm_multishells/… 與 ~/.local/state/fnm_multishells/…);那些 per-session 目錄全是指向 ~/.local/share/fnm/aliases/default 的 symlink,所以舊 session 留下的目錄仍可用。唯一找不到的是 env -i 的裸環境。這個破口的失敗時機在任何 Node 程式啟動之前,因此只能由 #17 的轉接檔第三段純文字承接。

與 #17 的相依:本工作包 blocks #17。#17 的轉接檔內容是「執行 tea-sdlc prompt --name …」,那條驗收標準要能真的驗過,prompt 必須先存在。

與 #1 的關係:議題 #1 的「跨平台佈署」段仍記載原本的絕對路徑替換方案,已被本工作包與 #17 取代。#1 正在實作中,其契約內容不在本工作包動它。

## 母議題 #1 — tea-sdlc:以 tea 驅動 SDLC 全流程的跨平台指令組 ## Problem Statement 使用者要在一台新機器上用到這六個流程指令,目前得為七個 agent 平台各學一套安裝流程:Claude Code 走 marketplace、Antigravity 得先 clone 再給本地路徑、OpenCode 得手動 `cp -r` 到 skills 目錄。裝完之後其實什麼都沒有——`skills/` 是空的,真正會生效的轉接檔要靠尚未實作的 `install.js` 產生。 更根本的是,這七套流程都只解決「把檔案放到哪」,沒有一套解決「放進去的檔案要指向哪個正本」。議題 #1 原本的答案是「路徑於產生時替換為絕對路徑」,但那個答案在用版本管理器管 Node 的機器上是壞的:fnm 把 Node 版號寫進全域安裝路徑(`.../node-versions/v26.7.0/installation/lib/node_modules`),升一次 Node,七個平台的轉接檔會同時指向不存在的檔案,而使用者不會收到任何錯誤——只會發現 `/sdlc-plan` 不動了。 ## Solution 用 npm 當佈署管道,並讓轉接檔不再持有路徑。 使用者跑一次 `npm i -g https://gitea.jsc.idv.tw/plugins/tea-sdlc.git`,得到一個 `tea-sdlc` 指令;跑一次 `tea-sdlc install`,六個流程指令就在他裝了的每個平台上可用。轉接檔裡沒有路徑,只有一句「執行 `tea-sdlc prompt --name sdlc-plan` 並遵照其輸出」——正本在哪由 PATH 上的 `tea-sdlc` 自己回推,Node 升級、換版本管理器、改 npm prefix 都不影響它。 這個設計讓更新變成兩件粗細不同的事:改流程正本、腳本、模板或規則,只要重跑 `npm i -g`,下一次叫用就讀到新的,**不必重新佈署**;只有指令數量或轉接檔模板本身變了,才需要再跑一次 `tea-sdlc install`。因此不需要 `update` 子指令。 本工作包負責 npm 打包與 `tea-sdlc` 指令本身(`prompt`、`status`、薄殼),轉接檔的產生與移除留在 #17。 ## User Stories 1. 身為第一次接觸 tea-sdlc 的使用者,我想用一行 npm 指令裝好,以便不用為七個平台各學一套安裝流程。 2. 身為使用者,我不想為了安裝而設定任何 registry 或憑證,以便在新機器上直接裝起來。 3. 身為使用者,我想用 git tag 指定要裝的版本,以便出問題時退回已知可用的版本。 4. 身為使用者,我想安裝後得到單一入口指令 `tea-sdlc`,以便佈署、移除、查狀態都從同一個地方進去。 5. 身為使用者,我想跑一次 `tea-sdlc status` 就知道目前版本、正本位置與環境缺什麼,以便在出事前先知道。 6. 身為使用者,我想讓 `status` 順便告訴我 Gitea 登入還有沒有效,以便不用等到跑流程指令才發現 token 過期。 7. 身為腳本使用者,我希望 `status` 在環境不健康時指令本身仍算成功,以便分辨「查詢失敗」與「成功查到問題」這兩件下一步完全不同的事。 8. 身為 agent,我想用 `tea-sdlc prompt --name <指令名>` 取得流程正本原文,以便照著執行而不必知道檔案放在哪。 9. 身為 agent,我想拿到的是原樣 markdown 而不是 JSON 字串,以便不必反跳脫幾百行內容、也不會因此讀錯。 10. 身為 agent,我想在指令失敗時拿到結構化錯誤,以便判斷該回報什麼給使用者。 11. 身為使用者,我想改完流程正本就立即生效、不必重跑佈署,以便迭代規則的成本夠低到我真的會去改。 12. 身為使用者,我想在轉接檔已經過時的時候被主動提醒,以便不會沉默地用著舊的指令清單。 13. 身為使用者,我想在轉接檔版本與套件不符時,提醒直接出現在 agent 每次都會讀到的地方,以便不必依賴我記得去查狀態。 14. 身為使用者,我想知道移除要先跑哪一個指令,以便不會先砍掉 npm 套件、留下一堆再也刪不掉的孤兒轉接檔。 15. 身為開發者,我想用 `npm link` 開發,改動 working tree 立即生效,以便不必每改一行就重裝。 16. 身為開發者,我想讓 `status` 告訴我目前 `tea-sdlc` 實際解析到哪個目錄,以便確認自己是不是在 link 模式。 17. 身為維護者,我想用白名單決定套件內容,以便測試暫存與 `.tmp/` 不會被打包給使用者。 18. 身為維護者,我想在套件缺檔時於安裝當下就炸開,以便不會拖到使用者跑流程指令才發現。 19. 身為維護者,我想 bin 的參數與輸出契約和既有十五支腳本完全一致,以便只維護一套契約而不是兩套。 20. 身為維護者,我想版本字串只有一個來源,以便產生標記、版本比對與狀態回報不會各說各話。 21. 身為使用者,我想在還沒裝 tea 的情況下先把 plugin 裝起來看看有哪些指令,以便不被逼著先裝一堆東西才能評估。 22. 身為使用者,我想在缺少執行環境時仍被明確告知缺什麼,以便不會誤以為一切就緒。 23. 身為使用者,我不想讓套件在 npm 安裝過程中自動改我的 agent 設定,以便安裝行為完全可預測。 24. 身為 CI,我想安裝過程不會進入互動提示,以便自動化流程不會卡在等待輸入。 25. 身為使用者,我想 README 明說更新就是重跑安裝那兩行,以便不會用 `npm update -g` 然後得到一個沒有真的更新的結果。 26. 身為使用者,我想 README 把既有的七套 marketplace 安裝法留在附錄,以便日後 `skills/` 有內容時還查得到。 27. 身為使用者,我想每個會寫入的子指令都支援 `--dry-run`,以便先看清楚會發生什麼再決定。 28. 身為使用者,我想在 `tea-sdlc` 不在 PATH 上時,agent 停下來叫我安裝,而不是自己憑空實作一套看起來像那麼回事、卻完全沒碰 Gitea 的流程。 29. 身為使用者,我想在升級 Node 之後 tea-sdlc 失效時拿到明確訊息與修復指引,以便知道那是安裝位置問題而不是流程壞了。 30. 身為維護者,我想 bin 的測試走真實子行程,以便測到的東西就是使用者真正會執行的東西。 31. 身為維護者,我想 `prompt` 的原樣輸出有自己的斷言,以便不必為了它而稀釋其餘子指令的單行 JSON 契約。 32. 身為維護者,我想有一條測試守住打包白名單,以便日後新增目錄時不會忘記把它加進去、也不會把測試檔案漏出去。 ## 範圍邊界 **本工作包做**:npm 打包設定、`bin/tea-sdlc.js` 薄殼、`prompt` 與 `status` 兩個子指令、`runBin()` 測試 helper、README 的安裝/更新/移除段落。 **本工作包不做**:轉接檔的產生與移除(`install.js` 的實作、平台偵測、勾選介面、產生標記的寫入),那些是 #17。本工作包只提供 `install` / `uninstall` 兩個子指令的**薄殼 dispatch 與 flag 解析**,動作實作由 #17 補上。 因此本工作包**阻擋 #17**:#17 的轉接檔內容是「執行 `tea-sdlc prompt --name …`」,那條驗收標準要能真的驗過,`prompt` 必須先存在。 ## Implementation Decisions **佈署管道** 以 git URL 安裝,不發佈到任何 registry:`npm i -g https://gitea.jsc.idv.tw/plugins/tea-sdlc.git`。版本以 git tag 指定(`…git#v0.1.0`)。選這條是因為 README 其餘七個平台的安裝指令本來就都用同一個 git URL,多一條佈署管道不該多一套憑證與 registry 設定;日後若需要真 semver,升級到 Gitea 自架 npm registry 只是補 `publishConfig`,不是重寫。 只支援全域安裝,明確拒絕 npx。npx 的套件落在會被 npm 自動清掉的快取目錄,雖然本設計的轉接檔不持有路徑、不會因此斷裂,但 npx 每次叫用重新解析套件的行為會讓「正本是哪一版」無法回答,與 `status` 的職責直接衝突。 **套件內容** `package.json` 移除 `private: true`、新增 `bin`、新增 `files` 白名單(涵蓋 `prompts` / `scripts` / `templates` / `references` / `skills` / `install.js` / `bin` / `README.md` / `AGENTS.md`)。選白名單而非 `.npmignore` 黑名單,是因為兩者的失敗模式不對稱:白名單漏掉東西會在安裝當下被既有的 `PLUGIN_LAYOUT_BROKEN` 檢查立刻炸出來,黑名單漏掉排除則是沉默地把測試檔案發給使用者。 **不新增** `prepare` script:本專案零外部套件、無建置步驟,加了只是多一個會在 git 安裝時跑的未定義行為。「零外部套件」這條約束不變,`dependencies` 與 `devDependencies` 一律不得出現。 **指令介面** 單一 bin `tea-sdlc`,入口是薄殼 `bin/tea-sdlc.js`,只負責取第一個位置參數當子指令、把其餘 argv 交給 `lib.js` 的 `parseFlags()`、再 dispatch 到 `install.js` 的具名 export。動作實作不放進薄殼,因為 `install.js` 在模組邊界上的職責是「平台偵測與轉接檔產生,唯一知道各平台目錄結構的地方」,把參數解析與狀態比對塞進去會讓它變成兩件事。 薄殼複用 `lib.js` 的 `parseFlags` 與 `main`,因此 bin 的輸入輸出契約與既有十五支腳本自動一致:只接受具名 flag、不認得的 flag 一律拒絕、輸出單行 JSON `{ok, data, error:{code, message}}`、exit 0 或 1。**注意 `parseFlags` 明確拒絕位置參數**,所以子指令必須由薄殼在交棒前自行取走,而 `prompt` 的目標以 `--name` 指定,不是位置參數。 子指令四個:`install`、`uninstall`(薄殼由本工作包提供,動作屬 #17)、`prompt`、`status`。**沒有 `update` 子指令**,理由見「Further Notes」。 **`prompt` 的契約** `tea-sdlc prompt --name <指令名> [--adapter-version <版本>]` 成功時把對應的流程正本**原樣**印到 stdout,exit 0。這是全專案唯一輸出非 JSON 的子指令,例外的理由寫死成一句:**它的輸出要餵給模型讀**。把幾百行 markdown 包進單行 JSON、再逼模型反跳脫,只會增加它讀錯的機率;JSON envelope 的價值是可程式化判斷成敗,而這條路徑的成功就是內容本身。失敗時仍走 JSON envelope + exit 1(`PROMPT_NOT_FOUND`)——成功是內容,失敗才需要結構。 `--adapter-version` 由轉接檔帶入自己被產生時的版本。與套件版本不符時,在輸出的 markdown **最前面加一行警告**,指示使用者重跑 `tea-sdlc install`,其後照常輸出正本原文。選這個機制而非只靠 `status`,是因為轉接檔過時**不會壞掉**——它只是繼續指向一個指令名稱,症狀是「新加的指令怎麼沒出現」這種查不出來的東西;而警告出現在模型每次叫用必定會讀到的位置,比任何被動機制都可靠。 **`status` 的契約** 回報四類事實:版本與正本位置(`readlink -f $(command -v tea-sdlc)`,順帶揭露是不是 `npm link`)、環境第一層(`node` / `git` / `tea` 是否在 PATH)、環境第二層(Gitea 登入是否有效,會打網路)、各平台轉接檔現況(偵測到哪些平台、裝了幾份、有沒有被改過)。 第二層要查:`status` 存在的理由就是「在出事前告訴我現在是什麼狀態」,而 token 失效是實務上最常見的故障,一次 `GET /user` 很便宜,為它多開一個 `--deep` 旗標是把判斷推回給使用者。 `data` 形狀:`{version, root, linked, healthy, environment:{node, git, tea, login}, platforms:[{name, dir, detected, adapters:{expected, present, stale}}]}`。 **`ok` 與 `healthy` 分離**:`ok` 在既有契約裡的意思是「這支腳本跑成功了」,不兼差表達環境健康與否;環境不健康時 `ok` 仍為 `true`,健康旗標是 `data.healthy`。混用會讓呼叫端分不出「status 掛了」和「status 成功查到你環境有問題」,而這兩件事的下一步完全不同。 **前置檢查的分工** `install` 只跑四層前置檢查的第一層(執行環境),且**只警告不中止**。缺 `tea` 完全不影響轉接檔產生,硬擋等於逼使用者為了裝 plugin 先去裝 tea;但完全不查也不行——安裝是一次性動作,使用者裝完就走,沒有警告他會以為一切就緒。第三、四層(repo issues 寫入權、時間追蹤)在安裝當下沒有 `--repo` 可查,無從執行。 `lib.js` 的 `checkEnvironment()` **不**加查 `tea-sdlc` 自己:那段檢查碼要能執行,`tea-sdlc` 必然已經跑起來了,查它是自證的廢話。真正會出事的時刻是「轉接檔被讀到、但指令不存在」,那一刻連 Node 都還沒啟動,任何程式內的檢查都攔不到——只能靠轉接檔本身的純文字指引(#17)。`status` 則要查,因為那正是它的職責。 **沒有 `--link` 旗標** 轉接檔不持有路徑,`npm link` 直接換掉 PATH 上的 `tea-sdlc`,working tree 自動成為正本,不需要任何旗標配合。留一個什麼都不做的旗標比沒有它更糟。「link 模式要可見」這個需求由 `status` 的 `root` / `linked` 欄位滿足,而且比旗標誠實:它報的是現況,不是安裝當下宣告的意圖。 **錯誤碼** 沿用 `ScriptError`。新增 `MISSING_SUBCOMMAND`、`UNKNOWN_SUBCOMMAND`、`PROMPT_NOT_FOUND`。 **文件** README 的安裝段改寫為三段,且移除那段要明寫順序反直覺之處:安裝=`npm i -g <git url>` 後 `tea-sdlc install`;更新=**重跑同樣那兩行**,並明寫「不要用 `npm update -g`,git 相依之下它不保證重新解析 ref」;移除=**先** `tea-sdlc uninstall` **再** `npm rm -g tea-sdlc`(順序反了就沒有指令可以拿來刪轉接檔了)。 既有七套 marketplace 安裝流程收進附錄「其他安裝方式」,不刪除——那些機制本身有用(自動更新、plugin 清單),等 `skills/` 有內容時會回來。但也不與 npm 並列為「兩種都行」,因為現在 `skills/` 是空的,照那些流程裝完不會出現任何指令。 `AGENTS.md` 的模組邊界表新增 `bin/` 一列(職責:子指令解析與 dispatch;邊界:不含任何平台目錄知識與動作實作),隨本工作包的 PR 一起改——文件描述現況,議題描述意圖,在 `bin/` 還不存在時先寫進邊界表會讓讀它的 AI 助理去找一個不存在的目錄。 ## Testing Decisions **什麼是好的測試**:只測外部行為,不測實作細節。對本工作包而言,外部行為就是「給定一個子指令與一組 flag,`tea-sdlc` 印出什麼、exit code 是多少」。不測 `install.js` 的具名 export、不測薄殼的 dispatch 表、不測 JSON 內部的建構過程。 **接縫**:全專案維持兩個接縫,都在行程邊界,本工作包新增其中第二個。 - 既有:`scripts/*.js` 的 CLI 邊界,helper `runScript()`。 - 新增:`bin/tea-sdlc.js` 的 CLI 邊界,helper `runBin()`。 選在這裡而非函式層,理由與既有接縫相同:它是七個平台共用的實際呼叫方式,測到的東西就是使用者真正會執行的東西,且不會因內部重構而破碎。 **為什麼另寫 `runBin()` 而不擴充 `runScript()`**:`runScript()` 的路徑寫死在 `scripts/` 底下,而它的 `parseSingleLine()` 是**刻意**在輸出不是恰好一行時主動拋錯的——那段斷言的存在本身就是在測「單行 JSON」這個契約。給它加一個 `raw: true` 旗標等於在十五支腳本的契約上開一個只有 bin 要用的後門。兩個 helper 各自持有各自的契約斷言,互不污染:`install` / `uninstall` / `status` 斷言單行 JSON,`prompt` 斷言原樣 markdown。 **外部相依的隔離**:`status` 的第二層檢查走 `lib.js` 既有的單一 HTTP 出口,測試沿用 `TEA_SDLC_API_BASE` 指向本機 stub server。`command -v` 類的 PATH 探測沿用既有的 `pathWithOnly()`,精確造出「缺 git」「缺 tea」的情境。 **測試對象與順序** 1. `prompt` 的輸出形狀——多行原樣 markdown、非 JSON、exit 0;`--name` 指向不存在的指令時回單行 JSON + exit 1。這是被叫用頻率最高、也是唯一破格的契約。 2. `prompt --adapter-version` 的版本比對——相符時輸出與不帶該 flag 時完全一致;不符時第一行為警告、其後逐字等於正本原文。 3. `status` 的輸出契約——單行 JSON;登入失效時 `ok` 仍為 `true` 而 `data.healthy` 為 `false`(這條專門守住 `ok` 與 `healthy` 的分離);缺 `git` / `tea` 時對應欄位為 false。 4. 薄殼的參數處理——缺子指令、未知子指令、未知 flag 各回對應錯誤碼 + exit 1。 5. 打包白名單——以 `npm pack --dry-run` 的檔案清單斷言 `prompts/`、`scripts/`、`templates/`、`references/` 在內,`test/`、`.tmp/` 不在內。 **Prior art**:`test/script-contract.test.js` 已用子行程 + stub server 驗過腳本契約與四層前置檢查;helper `run-script.js`、`stub-gitea.js`、`temp-repo.js` 可直接沿用其模式。測試執行器維持 Node 內建 `node:test` + `node:assert`,暫存一律寫到 `.tmp/`。 ## 驗收標準 - [x] `npm i -g <git url>` 後 PATH 上出現 `tea-sdlc` 指令 - [x] `package.json` 已移除 `private`、新增 `bin`、新增 `files` 白名單,且未新增 `prepare`、未出現任何 `dependencies` / `devDependencies` - [x] `npm pack --dry-run` 的清單包含 `prompts/`、`scripts/`、`templates/`、`references/`,不包含 `test/`、`.tmp/` - [x] 薄殼取第一個位置參數為子指令,其餘交由 `parseFlags` 處理;缺子指令、未知子指令、未知 flag 各回對應錯誤碼與 exit 1 - [x] `install` / `uninstall` / `status` 輸出單行 JSON `{ok, data, error:{code, message}}` - [x] `tea-sdlc prompt --name <指令名>` 原樣輸出流程正本、exit 0;名稱不存在時回單行 JSON `PROMPT_NOT_FOUND` + exit 1 - [x] `--adapter-version` 與套件版本不符時,輸出第一行為重跑 `tea-sdlc install` 的警告,其後為正本原文 - [x] `tea-sdlc status` 回報 version、root、linked、healthy、environment(node/git/tea/login);`platforms` 因平台偵測屬 #17 的模組邊界,改由 #17 交付 - [x] 環境不健康時 `status` 的 `ok` 仍為 `true`,健康狀態反映在 `data.healthy` - [x] `status` 的 `root` 在 `npm link` 情境下指向 working tree 且 `linked` 為 `true` - [x] `install` 缺少 `node` / `git` / `tea` 時印出警告但**不中止** - [x] 新增 `runBin()` helper,`install`/`uninstall`/`status` 斷言單行 JSON、`prompt` 斷言原樣 markdown,且 `runScript()` 的既有斷言未被放寬 - [x] README 安裝段以 npm 為唯一建議路徑,並說明 `prompt` 與 `status`;更新/移除的完整指令表(含「不要用 `npm update -g`」與「移除要先 `tea-sdlc uninstall` 再 `npm rm -g`」)因需 `install`/`uninstall` 實際可用,改由 #17 交付 - [x] README 既有七套 marketplace 流程移入附錄「其他安裝方式」,未刪除 - [x] `AGENTS.md` 模組邊界表新增 `bin/` 一列 ## Out of Scope - 轉接檔的產生與移除、平台偵測、勾選介面、產生標記的寫入——全部屬於 #17。 - 發佈到 npmjs 或 Gitea 自架 npm registry(日後若需要,補 `publishConfig` 即可)。 - npx 一次性執行。 - `update` 子指令。 - 自動安裝 Node、git、tea 或任何執行環境。 - `postinstall` 之類在 npm 安裝過程中自動佈署的機制。 - `Issue.time_estimate` 的填寫(等 #8 的估算機制建立後回填)。 - Windows PowerShell 以外的殼層適配;README 既有的 PowerShell 對照表沿用。 ## Further Notes **為什麼沒有 `update` 子指令**。轉接檔不持有路徑也不持有內容副本,所以更新被拆成粗細不同的兩件事:改 `prompts/`、`scripts/`、`templates/`、`references/` 時,`npm i -g <git url>` 之後下一次叫用就讀到新的,**完全不必重新佈署**;只有指令數量或轉接檔模板本身變了,才需要再跑一次 `tea-sdlc install`。一個語義等同「重跑 install」的 `update` 只會讓使用者猶豫該用哪個。 **重跑 `install` 的預設值**(實作屬 #17,但決策源自本工作包的更新語義):偵測到既有轉接檔時,預設只勾**已經裝過的平台**;完全沒有既有轉接檔時才預設全勾。這讓「重跑 install」天然就是更新動作,不會因為使用者半年後裝了新平台就把轉接檔擴散過去——帶擴散副作用的更新動作會讓人不敢隨手重跑,然後轉接檔就一直過時。這條取代了 #17 原本的「預設全勾」。 **fnm 的實測**(Q6 選擇改採指令叫用的證據):本機 `npm root -g` 為 `/root/.local/share/fnm/node-versions/v26.7.0/installation/lib/node_modules`,Node 版號寫在路徑裡,且 fnm 為每個 Node 版本開獨立的全域根——升級 Node 之後那條路徑不只失效,套件根本不在新的全域根裡。 **PATH 可用性的實測**(指令叫用機制的前提):目前 shell 與 `bash -lc` 都找得到全域 bin,但走的是不同的 fnm shim 根(`/run/user/0/fnm_multishells/…` 與 `~/.local/state/fnm_multishells/…`);那些 per-session 目錄全是指向 `~/.local/share/fnm/aliases/default` 的 symlink,所以舊 session 留下的目錄仍可用。唯一找不到的是 `env -i` 的裸環境。這個破口的失敗時機在任何 Node 程式啟動之前,因此只能由 #17 的轉接檔第三段純文字承接。 **與 #17 的相依**:本工作包 blocks #17。#17 的轉接檔內容是「執行 `tea-sdlc prompt --name …`」,那條驗收標準要能真的驗過,`prompt` 必須先存在。 **與 #1 的關係**:議題 #1 的「跨平台佈署」段仍記載原本的絕對路徑替換方案,已被本工作包與 #17 取代。#1 正在實作中,其契約內容不在本工作包動它。
jiantw83 added the ready-for-agent label 2026-09-17 05:51:05 +00:00
jiantw83 added a new dependency 2026-09-17 05:51:29 +00:00
Author
Member

十五條驗收標準勾了十四條。#26 / #27 / #28 / #29 / #30 已全部合併並關閉,#17 的轉接檔產生與移除亦已交付。

未勾選:「npm i -g <git url> 後 PATH 上出現 tea-sdlc 指令」。

套件這一側是好的,但這條路徑目前在本 Gitea 上跑不起來,原因在伺服器端:

npm error command git --no-replace-objects clone https://gitea.jsc.idv.tw/plugins/tea-sdlc.git ...
npm error remote: /data/gitea/home/p0_c7e42bc9.sh: 2: curl: not found
npm error fatal: early EOF
npm error fatal: fetch-pack: invalid index-pack output

Gitea 主機上有一支 hook script 會呼叫 curl,而該主機沒有安裝 curl,因此所有 git clone / git fetch(git pack 傳輸)一律失敗;git ls-remote 與 git push 不走這條路徑,所以仍然正常。這也是本 repo 的本地工作副本當初「clone 失效、改以 archive 取得」的原因。

套件本身已驗證可用,用 npm 實際產生的 tarball 全域安裝:

  • npm i -g ./tea-sdlc-0.0.1.tgz → PATH 上出現 tea-sdlc,root 落在 node_modules 底下、linked 為 false,tea-sdlc prompt --name sdlc-plan 從安裝副本讀得到正本(代表 files 白名單沒漏東西)。
  • npm i -g <本地目錄> → linked 為 true 且 root 指向 working tree。

可用的繞道(走 Gitea 的 archive 端點,不經 git pack 傳輸,實測可裝起來):

npm i -g https://gitea.jsc.idv.tw/plugins/tea-sdlc/archive/master.tar.gz

真正的修法是在 Gitea 主機上補裝 curl(或修掉那支 hook)。修好之後這條驗收標準即可直接驗證並勾選。

十五條驗收標準勾了十四條。#26 / #27 / #28 / #29 / #30 已全部合併並關閉,#17 的轉接檔產生與移除亦已交付。 **未勾選:「`npm i -g <git url>` 後 PATH 上出現 `tea-sdlc` 指令」。** 套件這一側是好的,但**這條路徑目前在本 Gitea 上跑不起來,原因在伺服器端**: ``` npm error command git --no-replace-objects clone https://gitea.jsc.idv.tw/plugins/tea-sdlc.git ... npm error remote: /data/gitea/home/p0_c7e42bc9.sh: 2: curl: not found npm error fatal: early EOF npm error fatal: fetch-pack: invalid index-pack output ``` Gitea 主機上有一支 hook script 會呼叫 `curl`,而該主機沒有安裝 curl,因此所有 `git clone` / `git fetch`(git pack 傳輸)一律失敗;`git ls-remote` 與 `git push` 不走這條路徑,所以仍然正常。這也是本 repo 的本地工作副本當初「clone 失效、改以 archive 取得」的原因。 **套件本身已驗證可用**,用 npm 實際產生的 tarball 全域安裝: - `npm i -g ./tea-sdlc-0.0.1.tgz` → PATH 上出現 `tea-sdlc`,`root` 落在 `node_modules` 底下、`linked` 為 `false`,`tea-sdlc prompt --name sdlc-plan` 從安裝副本讀得到正本(代表 `files` 白名單沒漏東西)。 - `npm i -g <本地目錄>` → `linked` 為 `true` 且 `root` 指向 working tree。 **可用的繞道**(走 Gitea 的 archive 端點,不經 git pack 傳輸,實測可裝起來): ```bash npm i -g https://gitea.jsc.idv.tw/plugins/tea-sdlc/archive/master.tar.gz ``` 真正的修法是在 Gitea 主機上補裝 `curl`(或修掉那支 hook)。修好之後這條驗收標準即可直接驗證並勾選。
Author
Member

最後一條「npm i -g <git url> 後 PATH 上出現 tea-sdlc 指令」已驗證通過,十五條全數達成。

先前擋住驗證的兩個問題都已解決:

1. Gitea 主機缺 curl(伺服器端)。 主機上一支在 upload-pack 期間執行的 script 會呼叫 curl,而主機沒有安裝,導致全站所有 git clone / git fetch 失敗(ls-remote 與 push 不走該路徑,因此正常)。已由管理者修復。

2. README 的安裝指令少了 git+ 前綴(本專案)。 npm 只有看到 git+https:// 才會當成 git repo;純 https://….git 會被歸類成遠端壓縮檔,以 TAR_BAD_ARCHIVE: Unrecognized archive format 失敗。已於 #43 修正,並加測試釘住「指向 .git 的 npm i -g 一律要帶 git+ 前綴」。

註:本票與 #26 的驗收標準原文寫的都是不帶 git+ 的形式,實際正確寫法是 npm i -g git+https://gitea.jsc.idv.tw/plugins/tea-sdlc.git。

端到端驗證(從合併後的 master 全新安裝,逐字照 README 執行):

$ npm i -g git+https://gitea.jsc.idv.tw/plugins/tea-sdlc.git
added 1 package in 3s
$ tea-sdlc status
ok=true healthy=true version=0.0.1 linked=false  root=…/lib/node_modules/tea-sdlc
environment: node/git/tea/login 皆為 true
$ tea-sdlc install --dry-run
ok=true  指令=[sdlc-analyze, sdlc-feat, sdlc-plan, sdlc-report]
$ tea-sdlc prompt --name sdlc-plan
(從安裝副本讀得到流程正本原文)

子工作包 #26 / #27 / #28 / #29 / #30 均已合併關閉;轉接檔的產生與移除由 #17 交付。

最後一條「`npm i -g <git url>` 後 PATH 上出現 `tea-sdlc` 指令」已驗證通過,十五條全數達成。 先前擋住驗證的兩個問題都已解決: **1. Gitea 主機缺 `curl`(伺服器端)。** 主機上一支在 upload-pack 期間執行的 script 會呼叫 `curl`,而主機沒有安裝,導致全站所有 `git clone` / `git fetch` 失敗(`ls-remote` 與 `push` 不走該路徑,因此正常)。已由管理者修復。 **2. README 的安裝指令少了 `git+` 前綴(本專案)。** npm 只有看到 `git+https://` 才會當成 git repo;純 `https://….git` 會被歸類成遠端壓縮檔,以 `TAR_BAD_ARCHIVE: Unrecognized archive format` 失敗。已於 #43 修正,並加測試釘住「指向 `.git` 的 `npm i -g` 一律要帶 `git+` 前綴」。 註:本票與 #26 的驗收標準原文寫的都是不帶 `git+` 的形式,實際正確寫法是 `npm i -g git+https://gitea.jsc.idv.tw/plugins/tea-sdlc.git`。 **端到端驗證**(從合併後的 master 全新安裝,逐字照 README 執行): ``` $ npm i -g git+https://gitea.jsc.idv.tw/plugins/tea-sdlc.git added 1 package in 3s $ tea-sdlc status ok=true healthy=true version=0.0.1 linked=false root=…/lib/node_modules/tea-sdlc environment: node/git/tea/login 皆為 true $ tea-sdlc install --dry-run ok=true 指令=[sdlc-analyze, sdlc-feat, sdlc-plan, sdlc-report] $ tea-sdlc prompt --name sdlc-plan (從安裝副本讀得到流程正本原文) ``` 子工作包 #26 / #27 / #28 / #29 / #30 均已合併關閉;轉接檔的產生與移除由 #17 交付。
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: plugins/tea-sdlc#25