feat(佈署): 以 npm 裝出 tea-sdlc 指令並產生各平台轉接檔
單一入口 bin/tea-sdlc.js 認四個子指令。第一個位置參數是子指令,其餘 argv 原樣 交出去——既有的 flag 解析拒絕位置參數,所以子指令必須在那之前就被取走。 轉接檔裡沒有路徑,只有一句 tea-sdlc prompt --name <指令名>。正本在哪由 PATH 上 的 tea-sdlc 自己回推:fnm 把 Node 版號寫進全域安裝路徑,寫死路徑的話升一次 Node,七個平台的轉接檔會同時指向不存在的檔案,而且不會有任何錯誤訊息。 prompt 是全專案唯一輸出非 JSON 的路徑,理由只有一個:它的輸出要餵給模型讀。 失敗仍走 envelope——成功是內容,失敗才需要結構。 status 的 ok 不兼差表達環境好壞,健康與否放在 data.healthy:呼叫端要分得出 「status 掛了」與「status 成功查到你環境有問題」。 install 只寫進偵測得到的平台;缺 git/tea 只警告不中止,因為那兩個完全不影響 轉接檔產生,硬擋等於逼使用者為了裝 plugin 先去裝 tea。uninstall 只刪帶產生標記 的檔案,使用者自己寫的同名檔案一律留著並在輸出裡交代。裝哪些指令以 prompts/ 裡 實際存在的正本為準,不是寫死的六個名字——裝出指向不存在正本的轉接檔,使用者只會 看到 PROMPT_NOT_FOUND。 流程正本的 description 前綴在抄進轉接檔之前就檢查:有三個平台關不掉自動觸發, 全靠那句話把 description 窄到不會被誤判,不能等使用者發現誤觸才知道漏了。 議題 #26 #27 #28 #29 #17 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* prompt 子指令:把一份流程正本原樣交出去。
|
||||
*
|
||||
* 轉接檔裡沒有路徑,只有一句「執行 tea-sdlc prompt --name sdlc-plan」。正本在哪,
|
||||
* 由 PATH 上的 tea-sdlc 自己從檔案位置回推——Node 升級、換版本管理器、改 npm prefix
|
||||
* 都不會讓轉接檔指向不存在的檔案。
|
||||
*
|
||||
* 用法(由 bin/tea-sdlc.js 轉入):
|
||||
* tea-sdlc prompt --name sdlc-plan [--adapter-version 0.0.1]
|
||||
*/
|
||||
import { existsSync, readFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import {
|
||||
RawText,
|
||||
ScriptError,
|
||||
checkPluginLayout,
|
||||
packageVersion,
|
||||
parseFlags,
|
||||
promptsDir,
|
||||
} from './lib.js';
|
||||
|
||||
/** 指令名的合法樣子。擋的是路徑跳脫,順便把打錯的名字擋在讀檔之前。 */
|
||||
const NAME = /^[a-z][a-z0-9-]*$/;
|
||||
|
||||
/**
|
||||
* @param {string[]} argv bin 取走子指令之後剩下的參數
|
||||
* @returns {import('./lib.js').RawText} 正本原文;版本不符時前面多一行警告
|
||||
*/
|
||||
export function runPrompt(argv) {
|
||||
const flags = parseFlags(argv, { required: ['name'], optional: ['adapter-version'] });
|
||||
checkPluginLayout();
|
||||
|
||||
const name = String(flags.name);
|
||||
if (!NAME.test(name)) {
|
||||
throw new ScriptError('BAD_PROMPT_NAME', `--name 只接受小寫英數與連字號,收到的是 ${name}`);
|
||||
}
|
||||
|
||||
const path = join(promptsDir(), `${name}.md`);
|
||||
if (!existsSync(path)) {
|
||||
throw new ScriptError('PROMPT_NOT_FOUND', `沒有名為 ${name} 的流程正本(找不到 ${path})`);
|
||||
}
|
||||
const text = readFileSync(path, 'utf8');
|
||||
|
||||
const adapter = flags['adapter-version'];
|
||||
const current = packageVersion();
|
||||
if (adapter === undefined || adapter === current) return new RawText(text);
|
||||
|
||||
// 正本仍然照給。轉接檔過時只是清單可能不齊,不是流程不能跑;
|
||||
// 警告放在最前面,是因為那是模型每次叫用必定會讀到的位置,不依賴使用者記得去查 status。
|
||||
return new RawText(
|
||||
`> ⚠️ 這份轉接檔是 tea-sdlc ${adapter} 產生的,目前安裝的是 ${current};` +
|
||||
`請重跑 \`tea-sdlc install\` 更新轉接檔。\n\n${text}`,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user