@nick848/sf-cli 1.0.16 → 1.0.17

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/CHANGELOG.md CHANGED
@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## v1.0.16 (2026-03-22)
9
+
10
+ **新增参考资源分析阶段**
11
+
12
+ - ✨ 新增「阶段 3/9: 参考资源分析」- 自动提取需求中的 URL 并分析
13
+ - ✨ 支持网页、设计稿、图片、API 等多种参考类型
14
+ - ✨ 使用 AI 分析参考内容,提取功能点、UI 结构、交互方式
15
+ - 🔧 规格拆分前先获取并理解参考内容,生成更精准的任务
16
+
17
+ **新工作流**:
18
+ ```
19
+ 阶段 1: 项目上下文获取
20
+ 阶段 2: 需求澄清
21
+ 阶段 3: 参考资源分析 ← 新增
22
+ - 自动提取 URL
23
+ - 获取资源内容
24
+ - AI 分析功能/结构
25
+ - 追加到精炼需求
26
+ 阶段 4: 复杂度评估
27
+ 阶段 5: BDD 场景拆解
28
+ 阶段 6: OpenSpec 规格
29
+ 阶段 7-9: TDD → 开发 → 审核
30
+ ```
31
+
8
32
  ## v1.0.13 (2026-03-22)
9
33
 
10
34
  **修复 DeepSeek API Key 验证**
package/dist/cli/index.js CHANGED
@@ -1414,6 +1414,7 @@ async function handleNew(args, ctx) {
1414
1414
  context: null,
1415
1415
  clarityScore: 0,
1416
1416
  clarificationQuestions: [],
1417
+ referenceResources: [],
1417
1418
  complexity: 0,
1418
1419
  bddScenarios: [],
1419
1420
  specItems: [],
@@ -1456,7 +1457,7 @@ async function executeWorkflow(ctx) {
1456
1457
  const lines = [];
1457
1458
  try {
1458
1459
  if (activeSession.phase === "context") {
1459
- lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 1/8: \u9879\u76EE\u4E0A\u4E0B\u6587\u83B7\u53D6 \u2501\u2501\u2501"));
1460
+ lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 1/9: \u9879\u76EE\u4E0A\u4E0B\u6587\u83B7\u53D6 \u2501\u2501\u2501"));
1460
1461
  lines.push("");
1461
1462
  activeSession.context = await readProjectContext(ctx.options.workingDirectory);
1462
1463
  lines.push(chalk9__default.default.gray(` \u9879\u76EE: ${activeSession.context.name}`));
@@ -1470,7 +1471,7 @@ async function executeWorkflow(ctx) {
1470
1471
  }
1471
1472
  if (activeSession.phase === "clarify") {
1472
1473
  lines.push("");
1473
- lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 2/8: \u9700\u6C42\u6F84\u6E05 \u2501\u2501\u2501"));
1474
+ lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 2/9: \u9700\u6C42\u6F84\u6E05 \u2501\u2501\u2501"));
1474
1475
  lines.push("");
1475
1476
  const clarityResult = analyzeRequirementClarity(
1476
1477
  activeSession.requirement,
@@ -1496,11 +1497,40 @@ async function executeWorkflow(ctx) {
1496
1497
  return { output: lines.join("\n") };
1497
1498
  }
1498
1499
  lines.push(chalk9__default.default.green(" \u2713 \u9700\u6C42\u6E05\u6670\uFF0C\u7EE7\u7EED\u4E0B\u4E00\u6B65"));
1500
+ activeSession.phase = "reference";
1501
+ }
1502
+ if (activeSession.phase === "reference") {
1503
+ lines.push("");
1504
+ lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 3/9: \u53C2\u8003\u8D44\u6E90\u5206\u6790 \u2501\u2501\u2501"));
1505
+ lines.push("");
1506
+ const urls = extractUrls(activeSession.refinedRequirement);
1507
+ if (urls.length > 0) {
1508
+ lines.push(chalk9__default.default.gray(` \u53D1\u73B0 ${urls.length} \u4E2A\u53C2\u8003\u94FE\u63A5`));
1509
+ lines.push("");
1510
+ for (const url of urls) {
1511
+ lines.push(chalk9__default.default.gray(` \u{1F4CE} ${url}`));
1512
+ try {
1513
+ const resource = await fetchAndAnalyzeReference(url, ctx);
1514
+ activeSession.referenceResources.push(resource);
1515
+ lines.push(chalk9__default.default.green(` \u2713 \u5DF2\u5206\u6790`));
1516
+ activeSession.refinedRequirement += `
1517
+
1518
+ \u3010\u53C2\u8003\u8D44\u6E90\u5206\u6790 - ${url}\u3011
1519
+ ${resource.analysis}`;
1520
+ } catch (error) {
1521
+ lines.push(chalk9__default.default.yellow(` \u26A0 \u83B7\u53D6\u5931\u8D25: ${error.message}`));
1522
+ }
1523
+ }
1524
+ lines.push("");
1525
+ lines.push(chalk9__default.default.green(" \u2713 \u53C2\u8003\u8D44\u6E90\u5206\u6790\u5B8C\u6210"));
1526
+ } else {
1527
+ lines.push(chalk9__default.default.gray(" \u65E0\u5916\u90E8\u53C2\u8003\u94FE\u63A5"));
1528
+ }
1499
1529
  activeSession.phase = "analysis";
1500
1530
  }
1501
1531
  if (activeSession.phase === "analysis") {
1502
1532
  lines.push("");
1503
- lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 3/8: \u590D\u6742\u5EA6\u8BC4\u4F30 \u2501\u2501\u2501"));
1533
+ lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 4/9: \u590D\u6742\u5EA6\u8BC4\u4F30 \u2501\u2501\u2501"));
1504
1534
  lines.push("");
1505
1535
  activeSession.complexity = analyzeComplexity(
1506
1536
  activeSession.refinedRequirement,
@@ -1517,12 +1547,13 @@ async function executeWorkflow(ctx) {
1517
1547
  }
1518
1548
  if (activeSession.phase === "bdd") {
1519
1549
  lines.push("");
1520
- lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 4/8: BDD \u573A\u666F\u62C6\u89E3 \u2501\u2501\u2501"));
1550
+ lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 5/9: BDD \u573A\u666F\u62C6\u89E3 \u2501\u2501\u2501"));
1521
1551
  lines.push("");
1522
1552
  activeSession.bddScenarios = generateBDDScenarios(
1523
1553
  activeSession.refinedRequirement,
1524
1554
  activeSession.context,
1525
- activeSession.clarificationQuestions
1555
+ activeSession.clarificationQuestions,
1556
+ activeSession.referenceResources
1526
1557
  );
1527
1558
  for (const scenario of activeSession.bddScenarios) {
1528
1559
  lines.push(chalk9__default.default.white(` Feature: ${scenario.feature}`));
@@ -1537,13 +1568,14 @@ async function executeWorkflow(ctx) {
1537
1568
  }
1538
1569
  if (activeSession.phase === "spec") {
1539
1570
  lines.push("");
1540
- lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 5/8: OpenSpec \u89C4\u683C \u2501\u2501\u2501"));
1571
+ lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 6/9: OpenSpec \u89C4\u683C \u2501\u2501\u2501"));
1541
1572
  lines.push("");
1542
1573
  activeSession.specItems = generateSpecItems(
1543
1574
  activeSession.refinedRequirement,
1544
1575
  activeSession.context,
1545
1576
  activeSession.bddScenarios,
1546
- activeSession.clarificationQuestions
1577
+ activeSession.clarificationQuestions,
1578
+ activeSession.referenceResources
1547
1579
  );
1548
1580
  const specPath = await saveSpecFile(ctx.options.workingDirectory, activeSession);
1549
1581
  lines.push(chalk9__default.default.green(" \u2713 \u89C4\u683C\u6587\u4EF6\u5DF2\u751F\u6210"));
@@ -1567,7 +1599,7 @@ async function executeWorkflow(ctx) {
1567
1599
  }
1568
1600
  if (activeSession.phase === "tdd") {
1569
1601
  lines.push("");
1570
- lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 6/8: TDD \u6D4B\u8BD5\u751F\u6210 \u2501\u2501\u2501"));
1602
+ lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 7/9: TDD \u6D4B\u8BD5\u751F\u6210 \u2501\u2501\u2501"));
1571
1603
  lines.push("");
1572
1604
  activeSession.testFiles = await generateTests(ctx.options.workingDirectory, activeSession);
1573
1605
  lines.push(chalk9__default.default.green(" \u2713 \u6D4B\u8BD5\u6587\u4EF6\u5DF2\u751F\u6210"));
@@ -1578,7 +1610,7 @@ async function executeWorkflow(ctx) {
1578
1610
  }
1579
1611
  if (activeSession.phase === "develop") {
1580
1612
  lines.push("");
1581
- lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 7/8: \u5F00\u53D1\u5B9E\u73B0 \u2501\u2501\u2501"));
1613
+ lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 8/9: \u5F00\u53D1\u5B9E\u73B0 \u2501\u2501\u2501"));
1582
1614
  lines.push("");
1583
1615
  lines.push(chalk9__default.default.yellow(" \u{1F680} \u6B63\u5728\u8C03\u7528 AI \u751F\u6210\u4EE3\u7801..."));
1584
1616
  try {
@@ -1603,7 +1635,7 @@ async function executeWorkflow(ctx) {
1603
1635
  }
1604
1636
  if (activeSession.phase === "review") {
1605
1637
  lines.push("");
1606
- lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 8/8: \u4EE3\u7801\u5BA1\u6838 \u2501\u2501\u2501"));
1638
+ lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 9/9: \u4EE3\u7801\u5BA1\u6838 \u2501\u2501\u2501"));
1607
1639
  lines.push("");
1608
1640
  lines.push(chalk9__default.default.yellow(" \u{1F50D} \u6B63\u5728\u8FDB\u884C\u4EE3\u7801\u5BA1\u6838..."));
1609
1641
  try {
@@ -2145,13 +2177,41 @@ function analyzeComplexity(requirement, context) {
2145
2177
  if (!context.framework) score += 0.5;
2146
2178
  return Math.max(1, Math.min(10, Math.round(score)));
2147
2179
  }
2148
- function generateBDDScenarios(requirement, context, questions) {
2180
+ function generateBDDScenarios(requirement, context, questions, references = []) {
2149
2181
  const scenarios = [];
2150
2182
  questions.find((q) => q.category === "ui" && q.answered)?.answer;
2151
2183
  const interactionAnswer = questions.find((q) => q.category === "interaction" && q.answered)?.answer;
2152
2184
  const edgeAnswer = questions.find((q) => q.category === "edge" && q.answered)?.answer;
2185
+ if (references.length > 0) {
2186
+ for (const ref of references) {
2187
+ const refFeatures = extractFeaturesFromReference(ref);
2188
+ for (const feature of refFeatures) {
2189
+ const scenario = {
2190
+ feature: feature.title,
2191
+ description: feature.description,
2192
+ scenarios: []
2193
+ };
2194
+ scenario.scenarios.push({
2195
+ name: `\u6B63\u5E38\u6D41\u7A0B: ${feature.title}`,
2196
+ given: [`\u7528\u6237\u8FDB\u5165\u76F8\u5173\u9875\u9762`],
2197
+ when: [`\u7528\u6237\u6267\u884C "${feature.title}" \u64CD\u4F5C`],
2198
+ then: [`\u7CFB\u7EDF\u5E94\u6B63\u786E\u5904\u7406\u5E76\u8FD4\u56DE\u9884\u671F\u7ED3\u679C`]
2199
+ });
2200
+ if (feature.hasInput) {
2201
+ scenario.scenarios.push({
2202
+ name: `\u8FB9\u754C\u60C5\u51B5: \u8F93\u5165\u9A8C\u8BC1`,
2203
+ given: [`\u7528\u6237\u8FDB\u5165\u8F93\u5165\u754C\u9762`],
2204
+ when: [`\u7528\u6237\u8F93\u5165\u8FB9\u754C\u503C\u6216\u7A7A\u503C`],
2205
+ then: [`\u7CFB\u7EDF\u5E94\u6B63\u786E\u5904\u7406\u8FB9\u754C\u60C5\u51B5`]
2206
+ });
2207
+ }
2208
+ scenarios.push(scenario);
2209
+ }
2210
+ }
2211
+ }
2153
2212
  const features = extractFeatures(requirement);
2154
2213
  for (const feature of features) {
2214
+ if (scenarios.some((s) => s.feature === feature.title)) continue;
2155
2215
  const scenario = {
2156
2216
  feature: feature.title,
2157
2217
  description: feature.description,
@@ -2183,6 +2243,46 @@ function generateBDDScenarios(requirement, context, questions) {
2183
2243
  }
2184
2244
  return scenarios;
2185
2245
  }
2246
+ function extractFeaturesFromReference(ref) {
2247
+ const features = [];
2248
+ const analysis = ref.analysis.toLowerCase();
2249
+ if (analysis.includes("\u8F93\u5165") || analysis.includes("\u8868\u5355")) {
2250
+ features.push({
2251
+ title: "\u8F93\u5165\u8868\u5355",
2252
+ description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u8F93\u5165\u8868\u5355\u529F\u80FD",
2253
+ hasInput: true
2254
+ });
2255
+ }
2256
+ if (analysis.includes("\u6309\u94AE") || analysis.includes("\u64CD\u4F5C")) {
2257
+ features.push({
2258
+ title: "\u4EA4\u4E92\u6309\u94AE",
2259
+ description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u6309\u94AE\u4EA4\u4E92",
2260
+ hasInput: false
2261
+ });
2262
+ }
2263
+ if (analysis.includes("\u8868\u683C") || analysis.includes("\u5217\u8868")) {
2264
+ features.push({
2265
+ title: "\u6570\u636E\u5217\u8868",
2266
+ description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u6570\u636E\u5C55\u793A",
2267
+ hasInput: false
2268
+ });
2269
+ }
2270
+ if (analysis.includes("\u56FE\u8868") || analysis.includes("\u53EF\u89C6\u5316")) {
2271
+ features.push({
2272
+ title: "\u56FE\u8868\u5C55\u793A",
2273
+ description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u56FE\u8868\u53EF\u89C6\u5316",
2274
+ hasInput: false
2275
+ });
2276
+ }
2277
+ if (features.length === 0) {
2278
+ features.push({
2279
+ title: "\u53C2\u8003\u529F\u80FD\u5B9E\u73B0",
2280
+ description: `\u57FA\u4E8E\u53C2\u8003\u8D44\u6E90 ${ref.url} \u5B9E\u73B0\u7684\u529F\u80FD`,
2281
+ hasInput: true
2282
+ });
2283
+ }
2284
+ return features;
2285
+ }
2186
2286
  function extractFeatures(requirement) {
2187
2287
  const features = [];
2188
2288
  const urlMatch = requirement.match(/https?:\/\/[^\s]+/);
@@ -2223,15 +2323,26 @@ function extractFeatures(requirement) {
2223
2323
  }
2224
2324
  return features;
2225
2325
  }
2226
- function generateSpecItems(requirement, context, bddScenarios, questions) {
2326
+ function generateSpecItems(requirement, context, bddScenarios, questions, references = []) {
2227
2327
  const items = [];
2228
2328
  let id = 1;
2329
+ for (const ref of references) {
2330
+ items.push({
2331
+ id: `T${id.toString().padStart(3, "0")}`,
2332
+ title: `\u53C2\u8003\u5206\u6790: ${ref.type}`,
2333
+ description: `\u5206\u6790\u53C2\u8003\u8D44\u6E90 ${ref.url}`,
2334
+ priority: "high",
2335
+ files: [],
2336
+ tests: []
2337
+ });
2338
+ id++;
2339
+ }
2229
2340
  for (const scenario of bddScenarios) {
2230
2341
  items.push({
2231
2342
  id: `T${id.toString().padStart(3, "0")}`,
2232
2343
  title: scenario.feature,
2233
2344
  description: scenario.description,
2234
- priority: id <= 2 ? "high" : "medium",
2345
+ priority: id <= 3 ? "high" : "medium",
2235
2346
  files: [],
2236
2347
  tests: []
2237
2348
  });
@@ -2274,6 +2385,19 @@ function formatSpecFile(session) {
2274
2385
  lines.push("---");
2275
2386
  lines.push("");
2276
2387
  }
2388
+ if (session.referenceResources.length > 0) {
2389
+ lines.push("## \u53C2\u8003\u8D44\u6E90");
2390
+ lines.push("");
2391
+ for (const ref of session.referenceResources) {
2392
+ lines.push(`### ${ref.url}`);
2393
+ lines.push(`> \u7C7B\u578B: ${ref.type}`);
2394
+ lines.push("");
2395
+ lines.push(ref.analysis);
2396
+ lines.push("");
2397
+ }
2398
+ lines.push("---");
2399
+ lines.push("");
2400
+ }
2277
2401
  if (session.clarificationQuestions.some((q) => q.answered)) {
2278
2402
  lines.push("## \u9700\u6C42\u6F84\u6E05");
2279
2403
  lines.push("");
@@ -2379,6 +2503,96 @@ function generateSessionId() {
2379
2503
  const random = Math.random().toString(36).slice(2, 6);
2380
2504
  return `WF-${timestamp}-${random}`.toUpperCase();
2381
2505
  }
2506
+ function extractUrls(text) {
2507
+ const urlRegex = /https?:\/\/[^\s<>"{}|\\^`\[\]]+/gi;
2508
+ const matches = text.match(urlRegex);
2509
+ return matches ? [...new Set(matches)] : [];
2510
+ }
2511
+ async function fetchAndAnalyzeReference(url, ctx) {
2512
+ const type = detectResourceType(url);
2513
+ let content = "";
2514
+ let analysis = "";
2515
+ try {
2516
+ const response = await fetch(url, {
2517
+ headers: {
2518
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
2519
+ }
2520
+ });
2521
+ if (!response.ok) {
2522
+ throw new Error(`HTTP ${response.status}`);
2523
+ }
2524
+ content = await response.text();
2525
+ if (ctx.modelService.getCurrentModel()) {
2526
+ analysis = await analyzeReferenceContent(url, content, type, ctx);
2527
+ } else {
2528
+ analysis = extractBasicInfo(content, type);
2529
+ }
2530
+ } catch (error) {
2531
+ throw new Error(`\u65E0\u6CD5\u83B7\u53D6\u53C2\u8003\u8D44\u6E90: ${error.message}`);
2532
+ }
2533
+ return { url, type, content: content.slice(0, 1e4), analysis };
2534
+ }
2535
+ function detectResourceType(url) {
2536
+ if (url.includes("figma.com") || url.includes("lanhuapp.com")) {
2537
+ return "design";
2538
+ }
2539
+ if (/\.(png|jpg|jpeg|gif|webp|svg)$/i.test(url)) {
2540
+ return "image";
2541
+ }
2542
+ if (/api\//i.test(url)) {
2543
+ return "api";
2544
+ }
2545
+ return "webpage";
2546
+ }
2547
+ async function analyzeReferenceContent(url, content, type, ctx) {
2548
+ const typePrompts = {
2549
+ webpage: "\u5206\u6790\u8FD9\u4E2A\u7F51\u9875\u7684\u529F\u80FD\u3001UI\u7EC4\u4EF6\u548C\u4EA4\u4E92\u65B9\u5F0F",
2550
+ design: "\u5206\u6790\u8FD9\u4E2A\u8BBE\u8BA1\u7A3F\u7684\u5E03\u5C40\u3001\u7EC4\u4EF6\u548C\u6837\u5F0F",
2551
+ image: "\u63CF\u8FF0\u8FD9\u4E2A\u56FE\u7247\u7684\u5185\u5BB9\u548C\u8BBE\u8BA1\u5143\u7D20",
2552
+ api: "\u5206\u6790\u8FD9\u4E2AAPI\u7684\u7ED3\u6784\u548C\u53C2\u6570"
2553
+ };
2554
+ const prompt2 = `
2555
+ \u8BF7\u5206\u6790\u4EE5\u4E0B\u53C2\u8003\u8D44\u6E90\u7684 URL\uFF0C\u63D0\u53D6\u5BF9\u5F00\u53D1\u6709\u7528\u7684\u4FE1\u606F\uFF1A
2556
+
2557
+ URL: ${url}
2558
+ \u7C7B\u578B: ${type}
2559
+
2560
+ \u5185\u5BB9\u6458\u8981:
2561
+ ${content.slice(0, 5e3)}
2562
+
2563
+ ${typePrompts[type]}
2564
+
2565
+ \u8BF7\u63D0\u53D6\uFF1A
2566
+ 1. \u4E3B\u8981\u529F\u80FD\u70B9
2567
+ 2. UI\u7EC4\u4EF6\u7ED3\u6784
2568
+ 3. \u4EA4\u4E92\u65B9\u5F0F
2569
+ 4. \u6570\u636E\u7ED3\u6784\uFF08\u5982\u679C\u6709\uFF09
2570
+ 5. \u6280\u672F\u5B9E\u73B0\u5EFA\u8BAE
2571
+
2572
+ \u4EE5\u7B80\u6D01\u7684\u8981\u70B9\u5F62\u5F0F\u8F93\u51FA\u3002
2573
+ `;
2574
+ try {
2575
+ const response = await ctx.modelService.sendMessage([
2576
+ { role: "user", content: prompt2 }
2577
+ ], { temperature: 0.3, maxTokens: 2e3 });
2578
+ return response.content;
2579
+ } catch {
2580
+ return extractBasicInfo(content, type);
2581
+ }
2582
+ }
2583
+ function extractBasicInfo(content, type) {
2584
+ const titleMatch = content.match(/<title[^>]*>([^<]+)<\/title>/i);
2585
+ const descMatch = content.match(/<meta[^>]*name=["']description["'][^>]*content=["']([^"']+)["']/i);
2586
+ const parts = [];
2587
+ if (titleMatch) {
2588
+ parts.push(`\u6807\u9898: ${titleMatch[1]}`);
2589
+ }
2590
+ if (descMatch) {
2591
+ parts.push(`\u63CF\u8FF0: ${descMatch[1]}`);
2592
+ }
2593
+ parts.push(`\u8D44\u6E90\u7C7B\u578B: ${type}`);
2594
+ return parts.join("\n");
2595
+ }
2382
2596
  function generateComplexityBar(score) {
2383
2597
  const filled = Math.round(score / 2);
2384
2598
  const empty = 5 - filled;
@@ -2388,6 +2602,7 @@ function getPhaseLabel(phase) {
2388
2602
  const labels = {
2389
2603
  context: "\u9879\u76EE\u4E0A\u4E0B\u6587\u83B7\u53D6",
2390
2604
  clarify: "\u9700\u6C42\u6F84\u6E05",
2605
+ reference: "\u53C2\u8003\u8D44\u6E90\u5206\u6790",
2391
2606
  analysis: "\u590D\u6742\u5EA6\u8BC4\u4F30",
2392
2607
  bdd: "BDD \u573A\u666F\u62C6\u89E3",
2393
2608
  spec: "OpenSpec \u89C4\u683C",
@@ -7468,6 +7683,7 @@ var STEP_DISPLAY_NAMES = {
7468
7683
  "archive": "\u5F52\u6863",
7469
7684
  "context": "\u4E0A\u4E0B\u6587",
7470
7685
  "clarify": "\u6F84\u6E05",
7686
+ "reference": "\u53C2\u8003\u5206\u6790",
7471
7687
  "analysis": "\u5206\u6790",
7472
7688
  "bdd": "BDD",
7473
7689
  "spec": "\u89C4\u683C",
@@ -7484,6 +7700,7 @@ var STEP_COLORS = {
7484
7700
  "archive": chalk9__default.default.gray,
7485
7701
  "context": chalk9__default.default.magenta,
7486
7702
  "clarify": chalk9__default.default.yellow,
7703
+ "reference": chalk9__default.default.blue,
7487
7704
  "analysis": chalk9__default.default.blue,
7488
7705
  "bdd": chalk9__default.default.cyan,
7489
7706
  "spec": chalk9__default.default.green,