feat/sdlc-analyze-feasibility/main #23

Merged
admin merged 6 commits from feat/sdlc-analyze-feasibility/main into master 2026-09-17 05:35:05 +00:00
2 changed files with 67 additions and 34 deletions
Showing only changes of commit 930270f545 - Show all commits
+62
View File
@@ -0,0 +1,62 @@
/**
* 流程正本與規則正本的共用檢查。
*
* 這些檔案是文件不是程式,但它們是各指令實際交付的東西:正本的平台中立性決定
* 轉接檔能不能一份寫到底,段落結構決定下游解析得到什麼。靠人記不牢,用測試釘住。
*/
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { repoRoot } from './run-script.js';
/** 讀一份流程正本 */
export function readPrompt(name) {
return readFileSync(join(repoRoot, 'prompts', `${name}.md`), 'utf8');
}
/** 讀一份規則正本 */
export function readReference(name) {
return readFileSync(join(repoRoot, 'references', `${name}.md`), 'utf8');
}
/** 讀一份輸出模板 */
export function readTemplate(name) {
return readFileSync(join(repoRoot, 'templates', `${name}.md`), 'utf8');
}
/**
* 正本裡不該出現的字樣:任何一家助理的工具名、目錄名或呼叫語法。
* 出現任何一個,就代表這份正本已經綁死在某個平台上。
*/
export const PLATFORM_SPECIFIC = [
'AskUserQuestion',
'Claude',
'Codex',
'Antigravity',
'Copilot',
'Kiro',
'OpenCode',
'oh-my-pi',
'.claude',
'.codex',
];
/**
* 斷言一份正本是平台中立的,且 description 帶上指定前綴。
* @param {string} prompt 正本內容
* @param {string} command 指令名,例如 sdlc-plan
*/
export function assertNeutralPrompt(prompt, command) {
const description = prompt.match(/^description:\s*(.+)$/m)?.[1];
assert.ok(description, '正本需要一行 description 供轉接檔取用');
assert.ok(
description.startsWith(`僅由 /${command} 指令叫用。`),
`description 前綴不符:${description}`,
);
assert.equal(prompt.startsWith('---'), false, '正本不該有 YAML frontmatter,那是轉接檔的事');
for (const token of [...PLATFORM_SPECIFIC, `$${command}`]) {
assert.equal(prompt.includes(token), false, `正本不該出現平台專屬字樣:${token}`);
}
}
+5 -34
View File
@@ -7,12 +7,10 @@
*/
import test from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { repoRoot } from './helpers/run-script.js';
import { assertNeutralPrompt, readPrompt, readTemplate } from './helpers/prompt-doc.js';
const template = readFileSync(join(repoRoot, 'templates', 'requirement-issue.md'), 'utf8');
const prompt = readFileSync(join(repoRoot, 'prompts', 'sdlc-plan.md'), 'utf8');
const template = readTemplate('requirement-issue');
const prompt = readPrompt('sdlc-plan');
/** 需求議題的九個段落,順序即議題裡的順序 */
const SECTIONS = [
@@ -50,35 +48,8 @@ test('總覽段落預留了總覽網頁的連結佔位', () => {
// ── 流程正本 ───────────────────────────────────────────────────────
test('正本的 description 以「僅由 /sdlc-plan 指令叫用。」起頭', () => {
const description = prompt.match(/^description:\s*(.+)$/m)?.[1];
assert.ok(description, '正本需要一行 description 供轉接檔取用');
assert.ok(
description.startsWith('僅由 /sdlc-plan 指令叫用。'),
`description 前綴不符:${description}`,
);
});
test('正本是平台中立的:不出現任何特定助理的語法或名稱', () => {
const platformSpecific = [
'AskUserQuestion',
'Claude',
'Codex',
'Antigravity',
'Copilot',
'Kiro',
'OpenCode',
'oh-my-pi',
'.claude',
'$sdlc-plan',
];
for (const token of platformSpecific) {
assert.equal(prompt.includes(token), false, `正本不該出現平台專屬字樣:${token}`);
}
});
test('正本沒有 YAML frontmatter:那是各平台轉接檔的事', () => {
assert.equal(prompt.startsWith('---'), false);
test('正本平台中立,description 前綴正確', () => {
assertNeutralPrompt(prompt, 'sdlc-plan');
});
test('正本交代了三種輸入都要能吃', () => {