visual-spec 0.1.11 → 0.1.12
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.md +1 -1
- package/docs/en-US/workflows.md +1 -1
- package/docs/ja-JP/workflows.md +1 -1
- package/docs/zh-CN/complicated.md +1 -1
- package/docs/zh-CN/workflows.md +1 -1
- package/package.json +1 -1
- package/skills/visual-spec/SKILL-ja-JP.md +4 -3
- package/skills/visual-spec/SKILL-zh-CN.md +4 -3
- package/skills/visual-spec/SKILL.md +5 -4
- package/skills/visual-spec/prompts/vspec_detail/index.html +352 -18
- package/skills/visual-spec/prompts/vspec_detail/index.md +7 -3
- package/skills/visual-spec/prompts/vspec_doc/doc.md +4 -3
- package/skills/visual-spec/prompts/vspec_more_q/more_q.md +5 -0
- package/skills/visual-spec/prompts/vspec_new/background.md +6 -0
- package/skills/visual-spec/prompts/vspec_new/questions.md +12 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
This repo provides a requirements analysis and delivery assistant Skill. It offers a `/vspec:*` command-driven workflow that turns raw requirements into reviewable artifacts: specs, data models, runnable prototypes, detailed design, acceptance cases, tests, and integrated implementation inputs.
|
|
2
2
|
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.12 (2026-04-12)
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
package/docs/en-US/workflows.md
CHANGED
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
|
|
51
51
|
- Use when you need a deliverable Word doc for review/circulation/archiving based on current artifacts
|
|
52
52
|
- Input: existing `/specs/**` artifacts (original/functions/details/models/flows, when available)
|
|
53
|
-
- Output: `/docs/current/requirement_detail.
|
|
53
|
+
- Output: `/docs/current/requirement_detail.docx` (Word-openable `.docx`, single-file HTML)
|
|
54
54
|
- Note: this Word file is a read-only summary; make changes in the corresponding markdown files and rerun `/vspec:doc` to regenerate
|
|
55
55
|
|
|
56
56
|
### 7. Solution Verification (`/vspec:verify`)
|
package/docs/ja-JP/workflows.md
CHANGED
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
|
|
47
47
|
- 既存成果物を集約してレビュー/回覧/保管用の Word 文書を生成する
|
|
48
48
|
- 入力:既存の `/specs/**`(original/functions/details/models/flows など。存在するものを読む)
|
|
49
|
-
- 出力:`/docs/current/requirement_detail.
|
|
49
|
+
- 出力:`/docs/current/requirement_detail.docx`(Word で開ける `.docx`、単一 HTML)
|
|
50
50
|
- 注意:この Word は要約用(直接編集しない)。修正は対応する markdown を更新し、`/vspec:doc` を再実行して再生成する
|
|
51
51
|
|
|
52
52
|
### 7. 検証(`/vspec:verify`)
|
|
@@ -57,4 +57,4 @@
|
|
|
57
57
|
2. 如材料较多:先整理 `/docs/current/file_list.md`(按“权威性/优先级”排序)
|
|
58
58
|
3. 运行 `/vspec:new`(生成 `/specs/background/original.md` 与问题清单)
|
|
59
59
|
4. 需要继承 legacy/当前材料并生成新 specs:运行 `/vspec:upgrade`
|
|
60
|
-
5. 需要汇总成 Word:运行 `/vspec:doc` 输出到 `/docs/current/requirement_detail.
|
|
60
|
+
5. 需要汇总成 Word:运行 `/vspec:doc` 输出到 `/docs/current/requirement_detail.docx`
|
package/docs/zh-CN/workflows.md
CHANGED
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
|
|
51
51
|
- 适用场景:把当前已有的规格产物汇总成可交付的 Word 文档,用于评审/流转/归档
|
|
52
52
|
- 输入:现有 `/specs/**` 产物(original/functions/details/models/flows 等,存在则读取)
|
|
53
|
-
- 输出:`/docs/current/requirement_detail.
|
|
53
|
+
- 输出:`/docs/current/requirement_detail.docx`(Word 可打开的 `.docx`,HTML 单文件)
|
|
54
54
|
- 提示:该 Word 只是汇总,不建议直接修改;修改应回到对应的 markdown 文件,修改后重新 `/vspec:doc` 生成新版本
|
|
55
55
|
|
|
56
56
|
### 7. 方案验证(`/vspec:verify`)
|
package/package.json
CHANGED
|
@@ -47,8 +47,9 @@ Flow:
|
|
|
47
47
|
2. 入力を raw requirement として扱う
|
|
48
48
|
3. `prompts/vspec_new/background.md` を読み込み、要件分析と背景補完を実行
|
|
49
49
|
4. `/specs/background/original.md` に書き込み
|
|
50
|
-
5.
|
|
51
|
-
6.
|
|
50
|
+
5. `/specs/background/question_and_answer.html` を生成(`prompts/vspec_new/question_and_answer.html` をそのままコピー)。このページで `original.md` / `questions.md` の質問に回答し、md に書き戻せるようにする。
|
|
51
|
+
6. Open Questions/要確認事項の回答を促す(`/specs/background/question_and_answer.html` を開き、`/specs/background/original.md` を選択して回答・保存)。この時点で必ず停止して待機し、回答が揃うまで次のステップへ進まない(「続けて/继续/continue」等で回答完了を明示してもらう)。
|
|
52
|
+
6. ユーザーの回答後(または「続けて/继续/continue」等で回答完了を明示した後)、`prompts/vspec_new/*` に従い `/specs/` 配下の成果物を生成
|
|
52
53
|
|
|
53
54
|
### `/vspec:refine`
|
|
54
55
|
|
|
@@ -87,7 +88,7 @@ Flow:
|
|
|
87
88
|
|
|
88
89
|
### `/vspec:doc`
|
|
89
90
|
|
|
90
|
-
要件成果物(`/specs/**`)を集約し、Word で直接開ける単一ファイルの Word 文書(HTML 形式の `.
|
|
91
|
+
要件成果物(`/specs/**`)を集約し、Word で直接開ける単一ファイルの Word 文書(HTML 形式の `.docx`)を `/docs/current/requirement_detail.docx` に生成します。
|
|
91
92
|
|
|
92
93
|
### `/vspec:verify`
|
|
93
94
|
|
|
@@ -42,7 +42,8 @@ description: "将原始需求分析为可评审的视觉规格,并生成相关
|
|
|
42
42
|
3. 加载提示词文件 `prompts/vspec_new/background.md`。
|
|
43
43
|
4. 使用该提示词分析需求并扩展业务背景。
|
|
44
44
|
5. 将原始需求与背景分析写入 `/specs/background/original.md`。
|
|
45
|
-
|
|
45
|
+
5.5 同时生成交互式问答页面:`/specs/background/question_and_answer.html`(从 `prompts/vspec_new/question_and_answer.html` 复制,单文件 HTML,内联 CSS/JS),用于回答 `original.md` 与 `questions.md` 并回写。
|
|
46
|
+
6. 提示用户回答“待确认问题/Open Questions/要確認事項”章节中的问题(按所选语言对应章节标题),并要求通过 `/specs/background/question_and_answer.html` 完成(在页面中选择 `/specs/background/original.md`,填写并保存回写)。然后必须暂停并等待用户回复“继续/continue/続けて”。不得继续加载后续提示词或生成后续产物(不得越过问答直接进入 stakeholders/roles/terms 等阶段)。用户明确回复后再进入下一步。
|
|
46
47
|
7. 用户回复后,加载 `prompts/vspec_new/stakeholders.md` 分析干系人。
|
|
47
48
|
8. 将干系人结果写入 `/specs/background/stakeholder.md`(markdown 表格)。
|
|
48
49
|
9. 加载 `prompts/vspec_new/roles.md` 分析系统用户角色(直接用户)及其工作任务。
|
|
@@ -178,7 +179,7 @@ description: "将原始需求分析为可评审的视觉规格,并生成相关
|
|
|
178
179
|
- 外部依赖:`/specs/background/dependencies.md`
|
|
179
180
|
- 数据模型:`/specs/models/*.md`
|
|
180
181
|
3. 加载 `prompts/vspec_doc/doc.md`,生成 Word 可打开的单文件 HTML 文档。
|
|
181
|
-
4. 将输出写入:`/docs/current/requirement_detail.
|
|
182
|
+
4. 将输出写入:`/docs/current/requirement_detail.docx`。
|
|
182
183
|
|
|
183
184
|
### `/vspec:verify`
|
|
184
185
|
|
|
@@ -324,7 +325,7 @@ Note:Pro 版支持更广泛的质量检测能力(例如更完整的原型/
|
|
|
324
325
|
- `prompts/vspec_new/questions.md`:生成 `/specs/background/questions.md` 的提示词。
|
|
325
326
|
- `prompts/vspec_refine/refine.md`:`/vspec:refine` 使用的提示词。
|
|
326
327
|
- `prompts/vspec_refine/refine_q.md`:`/vspec:refine-q` 使用的提示词。
|
|
327
|
-
- `prompts/vspec_doc/doc.md`:`/vspec:doc` 使用的提示词,用于生成 Word 可打开的 `.
|
|
328
|
+
- `prompts/vspec_doc/doc.md`:`/vspec:doc` 使用的提示词,用于生成 Word 可打开的 `.docx`(HTML)需求详细文档到 `/docs/current/`。
|
|
328
329
|
- `prompts/vspec_verify/model.md`:生成 `/specs/models/*.md` 的提示词。
|
|
329
330
|
- `prompts/vspec_verify/prototype.md`:生成按选栈的可运行原型工程(`/specs/prototypes/`)的提示词(必须遵循 `scheme.yaml`)。
|
|
330
331
|
- `prompts/vspec_verify/validation.md`:生成 `scenario.html` 场景评审页的提示词。
|
|
@@ -49,8 +49,9 @@ Flow:
|
|
|
49
49
|
3. Load the prompt file at `prompts/vspec_new/background.md`.
|
|
50
50
|
4. Use that prompt to analyze the requirement and expand the business context.
|
|
51
51
|
5. Write the raw requirement and background analysis output to `/specs/background/original.md`.
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
5.5 Create `/specs/background/question_and_answer.html` by copying `prompts/vspec_new/question_and_answer.html` (single-file HTML with inline CSS/JS) so the user can answer questions and write back to markdown.
|
|
53
|
+
6. Ask the user to answer the questions from the Open Questions section (use the section title in the selected language). The user should answer via `/specs/background/question_and_answer.html` (select `/specs/background/original.md` in the page and save back), then reply with a continuation signal (e.g. `继续` / `continue`). Then STOP. Do not load any subsequent prompts or generate any further artifacts before that.
|
|
54
|
+
7. After the user replies (answers or confirmed), load `prompts/vspec_new/stakeholders.md` to analyze stakeholders.
|
|
54
55
|
8. Write the stakeholder result to `/specs/background/stakeholder.md` (markdown table).
|
|
55
56
|
9. Load `prompts/vspec_new/roles.md` to analyze system user roles (direct users) and their work tasks.
|
|
56
57
|
10. Write the roles result to `/specs/background/roles.md`.
|
|
@@ -185,7 +186,7 @@ Flow:
|
|
|
185
186
|
- Dependencies: `/specs/background/dependencies.md`
|
|
186
187
|
- Models: `/specs/models/*.md`
|
|
187
188
|
3. Load `prompts/vspec_doc/doc.md` and generate the doc as a Word-openable single HTML file.
|
|
188
|
-
4. Write the output file to: `/docs/current/requirement_detail.
|
|
189
|
+
4. Write the output file to: `/docs/current/requirement_detail.docx`.
|
|
189
190
|
|
|
190
191
|
### `/vspec:verify`
|
|
191
192
|
|
|
@@ -333,7 +334,7 @@ Flow:
|
|
|
333
334
|
- `prompts/vspec_mrd/mrd.md`: the prompt used by `/vspec:mrd` to generate market/user/competitor/product docs under `/docs/market/`.
|
|
334
335
|
- `prompts/vspec_refine/refine.md`: the prompt used by `/vspec:refine` to refine the requirement based on `refine.md`.
|
|
335
336
|
- `prompts/vspec_refine/refine_q.md`: the prompt used by `/vspec:refine-q` to refine the requirement based on answered questions.
|
|
336
|
-
- `prompts/vspec_doc/doc.md`: the prompt used by `/vspec:doc` to generate a Word-openable `.
|
|
337
|
+
- `prompts/vspec_doc/doc.md`: the prompt used by `/vspec:doc` to generate a Word-openable `.docx` (HTML) requirement detail document under `/docs/current/`.
|
|
337
338
|
- `prompts/vspec_verify/model.md`: the prompt used by `/vspec:verify` to generate `/specs/models/*.md`.
|
|
338
339
|
- `prompts/vspec_verify/prototype.md`: the prompt used by `/vspec:verify` to generate the stack-selected runnable prototype under `/specs/prototypes/` (must follow `scheme.yaml`).
|
|
339
340
|
- `prompts/vspec_verify/validation.md`: the prompt used by `/vspec:verify` to generate the validation web page with a `scenario.html` entry.
|
|
@@ -319,13 +319,98 @@
|
|
|
319
319
|
font-size: 13px;
|
|
320
320
|
line-height: 1.5;
|
|
321
321
|
}
|
|
322
|
+
.tree-sep {
|
|
323
|
+
height: 1px;
|
|
324
|
+
background: var(--border);
|
|
325
|
+
margin: 10px 6px;
|
|
326
|
+
}
|
|
327
|
+
.outline-title {
|
|
328
|
+
padding: 8px 10px 2px;
|
|
329
|
+
color: var(--muted);
|
|
330
|
+
font-size: 12px;
|
|
331
|
+
}
|
|
332
|
+
.outline {
|
|
333
|
+
padding: 6px 6px 0;
|
|
334
|
+
}
|
|
335
|
+
.outline a {
|
|
336
|
+
display: block;
|
|
337
|
+
padding: 6px 10px;
|
|
338
|
+
border-radius: 10px;
|
|
339
|
+
color: rgba(230, 238, 252, 0.92);
|
|
340
|
+
text-decoration: none;
|
|
341
|
+
border: 1px solid transparent;
|
|
342
|
+
font-size: 13px;
|
|
343
|
+
}
|
|
344
|
+
.outline a:hover {
|
|
345
|
+
background: rgba(255, 255, 255, 0.04);
|
|
346
|
+
}
|
|
347
|
+
.outline a.active {
|
|
348
|
+
background: rgba(34, 197, 94, 0.12);
|
|
349
|
+
border-color: rgba(34, 197, 94, 0.28);
|
|
350
|
+
}
|
|
351
|
+
.file.missing {
|
|
352
|
+
opacity: 0.55;
|
|
353
|
+
cursor: not-allowed;
|
|
354
|
+
}
|
|
355
|
+
.md-toolbar {
|
|
356
|
+
display: flex;
|
|
357
|
+
flex-wrap: wrap;
|
|
358
|
+
gap: 8px;
|
|
359
|
+
align-items: center;
|
|
360
|
+
justify-content: space-between;
|
|
361
|
+
padding: 10px 12px;
|
|
362
|
+
border: 1px solid var(--border);
|
|
363
|
+
border-radius: 14px;
|
|
364
|
+
background: rgba(10, 16, 28, 0.82);
|
|
365
|
+
margin-bottom: 12px;
|
|
366
|
+
}
|
|
367
|
+
.md-toolbar .left,
|
|
368
|
+
.md-toolbar .right {
|
|
369
|
+
display: flex;
|
|
370
|
+
flex-wrap: wrap;
|
|
371
|
+
gap: 8px;
|
|
372
|
+
align-items: center;
|
|
373
|
+
}
|
|
374
|
+
.md-toolbar .hint {
|
|
375
|
+
margin: 0;
|
|
376
|
+
color: var(--muted);
|
|
377
|
+
font-size: 12px;
|
|
378
|
+
}
|
|
379
|
+
.md-split {
|
|
380
|
+
display: grid;
|
|
381
|
+
grid-template-columns: 1fr 1fr;
|
|
382
|
+
gap: 12px;
|
|
383
|
+
}
|
|
384
|
+
@media (max-width: 980px) {
|
|
385
|
+
.md-split {
|
|
386
|
+
grid-template-columns: 1fr;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
.md-text {
|
|
390
|
+
width: 100%;
|
|
391
|
+
min-height: calc(100vh - 330px);
|
|
392
|
+
border-radius: 14px;
|
|
393
|
+
border: 1px solid var(--border);
|
|
394
|
+
background: rgba(10, 16, 28, 0.78);
|
|
395
|
+
color: var(--text);
|
|
396
|
+
padding: 12px;
|
|
397
|
+
font-size: 13px;
|
|
398
|
+
line-height: 1.45;
|
|
399
|
+
outline: none;
|
|
400
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
|
|
401
|
+
monospace;
|
|
402
|
+
}
|
|
403
|
+
.mode-btn.active {
|
|
404
|
+
background: rgba(59, 130, 246, 0.18);
|
|
405
|
+
border-color: rgba(59, 130, 246, 0.45);
|
|
406
|
+
}
|
|
322
407
|
</style>
|
|
323
408
|
</head>
|
|
324
409
|
<body>
|
|
325
410
|
<div class="top-banner">
|
|
326
411
|
<div class="inner">
|
|
327
412
|
<div class="msg">
|
|
328
|
-
<b>Word export:</b> run <b>/vspec:doc</b> to generate <b>/docs/current/requirement_detail.
|
|
413
|
+
<b>Word export:</b> run <b>/vspec:doc</b> to generate <b>/docs/current/requirement_detail.docx</b>
|
|
329
414
|
</div>
|
|
330
415
|
</div>
|
|
331
416
|
</div>
|
|
@@ -344,7 +429,12 @@
|
|
|
344
429
|
<button id="collapseAll">Collapse</button>
|
|
345
430
|
</div>
|
|
346
431
|
</div>
|
|
347
|
-
<div class="tree"
|
|
432
|
+
<div class="tree">
|
|
433
|
+
<div class="outline-title">Outline</div>
|
|
434
|
+
<div id="outline" class="outline"></div>
|
|
435
|
+
<div class="tree-sep"></div>
|
|
436
|
+
<div id="nav"></div>
|
|
437
|
+
</div>
|
|
348
438
|
</div>
|
|
349
439
|
|
|
350
440
|
<div class="panel">
|
|
@@ -362,7 +452,7 @@
|
|
|
362
452
|
<div class="content">
|
|
363
453
|
<div class="doc" id="viewer">
|
|
364
454
|
<div class="empty">
|
|
365
|
-
Select a
|
|
455
|
+
Select a document from the left navigation. Markdown supports editing + live preview.
|
|
366
456
|
<ul>
|
|
367
457
|
<li><code>.md</code> rendered as markdown</li>
|
|
368
458
|
<li><code>.html</code> rendered via iframe</li>
|
|
@@ -381,7 +471,8 @@
|
|
|
381
471
|
const PLANTUML_SERVER = "https://www.plantuml.com/plantuml/svg/";
|
|
382
472
|
|
|
383
473
|
const dom = {
|
|
384
|
-
|
|
474
|
+
nav: document.getElementById("nav"),
|
|
475
|
+
outline: document.getElementById("outline"),
|
|
385
476
|
search: document.getElementById("search"),
|
|
386
477
|
fileCount: document.getElementById("fileCount"),
|
|
387
478
|
viewer: document.getElementById("viewer"),
|
|
@@ -396,6 +487,9 @@
|
|
|
396
487
|
paths: [],
|
|
397
488
|
activePath: null,
|
|
398
489
|
activeButton: null,
|
|
490
|
+
handles: new Map(),
|
|
491
|
+
activeHeadings: [],
|
|
492
|
+
activeHeadingId: null,
|
|
399
493
|
};
|
|
400
494
|
|
|
401
495
|
function escapeHtml(str) {
|
|
@@ -446,6 +540,45 @@
|
|
|
446
540
|
return s.length ? s[s.length - 1] : path;
|
|
447
541
|
}
|
|
448
542
|
|
|
543
|
+
function firstHeadingTitle(md) {
|
|
544
|
+
const m = String(md || "").match(/^#{1,6}\s+(.+)\s*$/m);
|
|
545
|
+
return m ? m[1].trim() : "";
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function displayTitle(path) {
|
|
549
|
+
const t = fileType(path);
|
|
550
|
+
if (t === "Markdown") {
|
|
551
|
+
const title = firstHeadingTitle(state.files[path] || "");
|
|
552
|
+
return title || basename(path);
|
|
553
|
+
}
|
|
554
|
+
return basename(path);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function slugify(text) {
|
|
558
|
+
return String(text || "")
|
|
559
|
+
.trim()
|
|
560
|
+
.toLowerCase()
|
|
561
|
+
.replace(/[`"'<>]/g, "")
|
|
562
|
+
.replace(/\s+/g, "-")
|
|
563
|
+
.replace(/[^a-z0-9\u4e00-\u9fff\u3040-\u30ff_-]/g, "");
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function extractHeadings(md) {
|
|
567
|
+
const lines = String(md || "").replace(/\r\n/g, "\n").split("\n");
|
|
568
|
+
const out = [];
|
|
569
|
+
let idx = 0;
|
|
570
|
+
for (const line of lines) {
|
|
571
|
+
const m = line.match(/^(#{1,6})\s+(.+)\s*$/);
|
|
572
|
+
if (!m) continue;
|
|
573
|
+
idx++;
|
|
574
|
+
const level = m[1].length;
|
|
575
|
+
const text = m[2].trim();
|
|
576
|
+
const id = `h-${slugify(text) || "section"}-${idx}`;
|
|
577
|
+
out.push({ level, text, id });
|
|
578
|
+
}
|
|
579
|
+
return out;
|
|
580
|
+
}
|
|
581
|
+
|
|
449
582
|
function filterMatch(filterLower, label, path) {
|
|
450
583
|
if (!filterLower) return true;
|
|
451
584
|
return `${label || ""} ${path || ""}`.toLowerCase().includes(filterLower);
|
|
@@ -524,13 +657,23 @@
|
|
|
524
657
|
chapters.push({
|
|
525
658
|
label: "Requirement",
|
|
526
659
|
children: [
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
660
|
+
{ type: "file", label: "Original Requirement", path: original || "/specs/background/original.md", missing: !original },
|
|
661
|
+
{
|
|
662
|
+
type: "file",
|
|
663
|
+
label: "Stakeholders",
|
|
664
|
+
path: stakeholder || "/specs/background/stakeholders.md",
|
|
665
|
+
missing: !stakeholder,
|
|
666
|
+
},
|
|
667
|
+
{ type: "file", label: "Roles", path: roles || "/specs/background/roles.md", missing: !roles },
|
|
668
|
+
{ type: "file", label: "Terms", path: terms || "/specs/background/terms.md", missing: !terms },
|
|
669
|
+
{ type: "file", label: "Scenarios", path: scenarios || "/specs/background/scenarios.md", missing: !scenarios },
|
|
670
|
+
{
|
|
671
|
+
type: "file",
|
|
672
|
+
label: "Dependencies",
|
|
673
|
+
path: dependencies || "/specs/background/dependencies.md",
|
|
674
|
+
missing: !dependencies,
|
|
675
|
+
},
|
|
676
|
+
{ type: "file", label: "Questions", path: questions || "/specs/background/questions.md", missing: !questions },
|
|
534
677
|
].filter(Boolean),
|
|
535
678
|
});
|
|
536
679
|
|
|
@@ -553,7 +696,7 @@
|
|
|
553
696
|
|
|
554
697
|
chapters.push({
|
|
555
698
|
label: "Functions",
|
|
556
|
-
children: functions.map((p) => ({ type: "file", label:
|
|
699
|
+
children: functions.map((p) => ({ type: "file", label: displayTitle(p), path: p })),
|
|
557
700
|
});
|
|
558
701
|
|
|
559
702
|
chapters.push({
|
|
@@ -563,7 +706,7 @@
|
|
|
563
706
|
|
|
564
707
|
chapters.push({
|
|
565
708
|
label: "Models",
|
|
566
|
-
children: models.map((p) => ({ type: "file", label:
|
|
709
|
+
children: models.map((p) => ({ type: "file", label: displayTitle(p), path: p })),
|
|
567
710
|
});
|
|
568
711
|
|
|
569
712
|
return chapters.filter((c) => (c.children || []).length);
|
|
@@ -581,8 +724,14 @@
|
|
|
581
724
|
if (!filterMatch(filterLower, node.label, node.path)) return;
|
|
582
725
|
const btn = document.createElement("button");
|
|
583
726
|
btn.className = "file";
|
|
584
|
-
|
|
585
|
-
btn.
|
|
727
|
+
const title = exists(node.path) ? displayTitle(node.path) : node.label;
|
|
728
|
+
btn.textContent = title;
|
|
729
|
+
if (!exists(node.path) || node.missing) {
|
|
730
|
+
btn.classList.add("missing");
|
|
731
|
+
btn.disabled = true;
|
|
732
|
+
} else {
|
|
733
|
+
btn.addEventListener("click", () => openFile(node.path, btn));
|
|
734
|
+
}
|
|
586
735
|
if (state.activePath === node.path) btn.classList.add("active");
|
|
587
736
|
container.appendChild(btn);
|
|
588
737
|
return;
|
|
@@ -602,11 +751,40 @@
|
|
|
602
751
|
}
|
|
603
752
|
|
|
604
753
|
function renderSidebar() {
|
|
605
|
-
dom.
|
|
754
|
+
dom.nav.innerHTML = "";
|
|
606
755
|
const filterLower = (dom.search.value || "").trim().toLowerCase();
|
|
607
756
|
const chapters = buildChapters();
|
|
608
757
|
for (const ch of chapters) {
|
|
609
|
-
renderNavNode({ type: "group", label: ch.label, children: ch.children }, dom.
|
|
758
|
+
renderNavNode({ type: "group", label: ch.label, children: ch.children }, dom.nav, filterLower);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
function renderOutline(headings) {
|
|
763
|
+
dom.outline.innerHTML = "";
|
|
764
|
+
const hs = (headings || []).filter((h) => h.level >= 1 && h.level <= 4);
|
|
765
|
+
if (!state.activePath || fileType(state.activePath) !== "Markdown" || !hs.length) {
|
|
766
|
+
const a = document.createElement("a");
|
|
767
|
+
a.href = "javascript:void(0)";
|
|
768
|
+
a.textContent = "—";
|
|
769
|
+
a.style.cursor = "default";
|
|
770
|
+
dom.outline.appendChild(a);
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
for (const h of hs) {
|
|
774
|
+
const a = document.createElement("a");
|
|
775
|
+
a.href = "javascript:void(0)";
|
|
776
|
+
a.textContent = h.text;
|
|
777
|
+
a.style.paddingLeft = `${10 + (h.level - 1) * 12}px`;
|
|
778
|
+
if (state.activeHeadingId === h.id) a.classList.add("active");
|
|
779
|
+
a.addEventListener("click", () => {
|
|
780
|
+
const el = document.getElementById(h.id);
|
|
781
|
+
if (el) {
|
|
782
|
+
state.activeHeadingId = h.id;
|
|
783
|
+
el.scrollIntoView({ block: "start", behavior: "smooth" });
|
|
784
|
+
renderOutline(state.activeHeadings);
|
|
785
|
+
}
|
|
786
|
+
});
|
|
787
|
+
dom.outline.appendChild(a);
|
|
610
788
|
}
|
|
611
789
|
}
|
|
612
790
|
|
|
@@ -762,9 +940,15 @@
|
|
|
762
940
|
async function renderMarkdown(md) {
|
|
763
941
|
const root = document.createElement("div");
|
|
764
942
|
const blocks = parseMarkdown(md);
|
|
943
|
+
let hIndex = 0;
|
|
944
|
+
const headings = [];
|
|
765
945
|
for (const b of blocks) {
|
|
766
946
|
if (b.type === "heading") {
|
|
947
|
+
hIndex++;
|
|
948
|
+
const id = `h-${slugify(b.text) || "section"}-${hIndex}`;
|
|
949
|
+
headings.push({ level: b.level, text: b.text, id });
|
|
767
950
|
const h = document.createElement("h" + b.level);
|
|
951
|
+
h.id = id;
|
|
768
952
|
h.innerHTML = renderInline(b.text);
|
|
769
953
|
root.appendChild(h);
|
|
770
954
|
continue;
|
|
@@ -849,10 +1033,46 @@
|
|
|
849
1033
|
continue;
|
|
850
1034
|
}
|
|
851
1035
|
}
|
|
1036
|
+
state.activeHeadings = headings;
|
|
1037
|
+
state.activeHeadingId = null;
|
|
852
1038
|
return root;
|
|
853
1039
|
}
|
|
854
1040
|
|
|
1041
|
+
async function linkLocalFileForWriteBack(path) {
|
|
1042
|
+
if (!("showOpenFilePicker" in window)) {
|
|
1043
|
+
alert("Browser does not support File System Access API. Use Download instead.");
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
const [handle] = await window.showOpenFilePicker({
|
|
1047
|
+
multiple: false,
|
|
1048
|
+
types: [{ description: "Files", accept: { "text/plain": [".md", ".puml", ".txt", ".html"] } }],
|
|
1049
|
+
});
|
|
1050
|
+
state.handles.set(path, handle);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
async function saveToLinkedHandle(path, text) {
|
|
1054
|
+
const handle = state.handles.get(path);
|
|
1055
|
+
if (!handle) return false;
|
|
1056
|
+
const writable = await handle.createWritable();
|
|
1057
|
+
await writable.write(text);
|
|
1058
|
+
await writable.close();
|
|
1059
|
+
return true;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
function downloadText(filename, text, type) {
|
|
1063
|
+
const blob = new Blob([text], { type: type || "text/plain;charset=utf-8" });
|
|
1064
|
+
const url = URL.createObjectURL(blob);
|
|
1065
|
+
const a = document.createElement("a");
|
|
1066
|
+
a.href = url;
|
|
1067
|
+
a.download = filename;
|
|
1068
|
+
document.body.appendChild(a);
|
|
1069
|
+
a.click();
|
|
1070
|
+
a.remove();
|
|
1071
|
+
URL.revokeObjectURL(url);
|
|
1072
|
+
}
|
|
1073
|
+
|
|
855
1074
|
async function openFile(path, btn) {
|
|
1075
|
+
if (!exists(path)) return;
|
|
856
1076
|
const content = state.files[path];
|
|
857
1077
|
setActive(path, btn);
|
|
858
1078
|
dom.viewer.innerHTML = "";
|
|
@@ -881,8 +1101,121 @@
|
|
|
881
1101
|
}
|
|
882
1102
|
return;
|
|
883
1103
|
}
|
|
1104
|
+
if (t === "Markdown") {
|
|
1105
|
+
const toolbar = document.createElement("div");
|
|
1106
|
+
toolbar.className = "md-toolbar";
|
|
1107
|
+
const left = document.createElement("div");
|
|
1108
|
+
left.className = "left";
|
|
1109
|
+
const right = document.createElement("div");
|
|
1110
|
+
right.className = "right";
|
|
1111
|
+
|
|
1112
|
+
const modeSplit = document.createElement("button");
|
|
1113
|
+
modeSplit.className = "mode-btn active";
|
|
1114
|
+
modeSplit.textContent = "Split";
|
|
1115
|
+
const modeEdit = document.createElement("button");
|
|
1116
|
+
modeEdit.className = "mode-btn";
|
|
1117
|
+
modeEdit.textContent = "Edit";
|
|
1118
|
+
const modePreview = document.createElement("button");
|
|
1119
|
+
modePreview.className = "mode-btn";
|
|
1120
|
+
modePreview.textContent = "Preview";
|
|
1121
|
+
|
|
1122
|
+
const linkBtn = document.createElement("button");
|
|
1123
|
+
linkBtn.textContent = "Link File";
|
|
1124
|
+
linkBtn.addEventListener("click", async () => {
|
|
1125
|
+
try {
|
|
1126
|
+
await linkLocalFileForWriteBack(path);
|
|
1127
|
+
alert("Linked. You can Save back now.");
|
|
1128
|
+
} catch (e) {}
|
|
1129
|
+
});
|
|
1130
|
+
|
|
1131
|
+
const saveBtn = document.createElement("button");
|
|
1132
|
+
saveBtn.className = "primary";
|
|
1133
|
+
saveBtn.textContent = "Save";
|
|
1134
|
+
|
|
1135
|
+
const dlBtn = document.createElement("button");
|
|
1136
|
+
dlBtn.textContent = "Download";
|
|
1137
|
+
|
|
1138
|
+
const hint = document.createElement("div");
|
|
1139
|
+
hint.className = "hint";
|
|
1140
|
+
hint.textContent = "Edit markdown and preview on the right. Save requires linking a local file.";
|
|
1141
|
+
|
|
1142
|
+
left.appendChild(modeSplit);
|
|
1143
|
+
left.appendChild(modeEdit);
|
|
1144
|
+
left.appendChild(modePreview);
|
|
1145
|
+
left.appendChild(hint);
|
|
1146
|
+
right.appendChild(linkBtn);
|
|
1147
|
+
right.appendChild(saveBtn);
|
|
1148
|
+
right.appendChild(dlBtn);
|
|
1149
|
+
toolbar.appendChild(left);
|
|
1150
|
+
toolbar.appendChild(right);
|
|
1151
|
+
|
|
1152
|
+
const split = document.createElement("div");
|
|
1153
|
+
split.className = "md-split";
|
|
1154
|
+
const ta = document.createElement("textarea");
|
|
1155
|
+
ta.className = "md-text";
|
|
1156
|
+
ta.value = content || "";
|
|
1157
|
+
const previewWrap = document.createElement("div");
|
|
1158
|
+
previewWrap.className = "doc";
|
|
1159
|
+
split.appendChild(ta);
|
|
1160
|
+
split.appendChild(previewWrap);
|
|
1161
|
+
|
|
1162
|
+
let timer = null;
|
|
1163
|
+
async function refresh() {
|
|
1164
|
+
previewWrap.innerHTML = "";
|
|
1165
|
+
const rendered = await renderMarkdown(ta.value || "");
|
|
1166
|
+
previewWrap.appendChild(rendered);
|
|
1167
|
+
renderOutline(state.activeHeadings);
|
|
1168
|
+
}
|
|
1169
|
+
await refresh();
|
|
1170
|
+
ta.addEventListener("input", () => {
|
|
1171
|
+
if (timer) clearTimeout(timer);
|
|
1172
|
+
timer = setTimeout(() => refresh(), 180);
|
|
1173
|
+
});
|
|
1174
|
+
|
|
1175
|
+
function setMode(mode) {
|
|
1176
|
+
modeSplit.classList.remove("active");
|
|
1177
|
+
modeEdit.classList.remove("active");
|
|
1178
|
+
modePreview.classList.remove("active");
|
|
1179
|
+
if (mode === "split") modeSplit.classList.add("active");
|
|
1180
|
+
if (mode === "edit") modeEdit.classList.add("active");
|
|
1181
|
+
if (mode === "preview") modePreview.classList.add("active");
|
|
1182
|
+
if (mode === "split") {
|
|
1183
|
+
ta.style.display = "";
|
|
1184
|
+
previewWrap.style.display = "";
|
|
1185
|
+
} else if (mode === "edit") {
|
|
1186
|
+
ta.style.display = "";
|
|
1187
|
+
previewWrap.style.display = "none";
|
|
1188
|
+
} else {
|
|
1189
|
+
ta.style.display = "none";
|
|
1190
|
+
previewWrap.style.display = "";
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
modeSplit.addEventListener("click", () => setMode("split"));
|
|
1194
|
+
modeEdit.addEventListener("click", () => setMode("edit"));
|
|
1195
|
+
modePreview.addEventListener("click", () => setMode("preview"));
|
|
1196
|
+
|
|
1197
|
+
saveBtn.addEventListener("click", async () => {
|
|
1198
|
+
try {
|
|
1199
|
+
const ok = await saveToLinkedHandle(path, ta.value || "");
|
|
1200
|
+
if (!ok) alert("No linked file. Click Link File first, or use Download.");
|
|
1201
|
+
else alert("Saved.");
|
|
1202
|
+
} catch (e) {
|
|
1203
|
+
alert("Save failed.");
|
|
1204
|
+
}
|
|
1205
|
+
});
|
|
1206
|
+
dlBtn.addEventListener("click", () => {
|
|
1207
|
+
downloadText(basename(path), ta.value || "", "text/markdown;charset=utf-8");
|
|
1208
|
+
});
|
|
1209
|
+
|
|
1210
|
+
dom.viewer.className = "";
|
|
1211
|
+
dom.viewer.appendChild(toolbar);
|
|
1212
|
+
dom.viewer.appendChild(split);
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
dom.viewer.className = "doc";
|
|
884
1216
|
const rendered = await renderMarkdown(content || "");
|
|
885
1217
|
dom.viewer.appendChild(rendered);
|
|
1218
|
+
renderOutline(state.activeHeadings);
|
|
886
1219
|
}
|
|
887
1220
|
|
|
888
1221
|
function setFiles(files) {
|
|
@@ -892,6 +1225,7 @@
|
|
|
892
1225
|
.sort((a, b) => a.localeCompare(b));
|
|
893
1226
|
dom.fileCount.textContent = `${state.paths.length} files`;
|
|
894
1227
|
renderSidebar();
|
|
1228
|
+
renderOutline([]);
|
|
895
1229
|
const fromHash = decodeURIComponent((location.hash || "").replace(/^#/, "")) || "";
|
|
896
1230
|
if (fromHash && state.files[fromHash] != null) {
|
|
897
1231
|
openFile(fromHash, null);
|
|
@@ -913,7 +1247,7 @@
|
|
|
913
1247
|
} catch (e) {}
|
|
914
1248
|
});
|
|
915
1249
|
dom.collapseAll.addEventListener("click", () => {
|
|
916
|
-
const ds = dom.
|
|
1250
|
+
const ds = dom.nav.querySelectorAll("details");
|
|
917
1251
|
for (const d of ds) d.open = false;
|
|
918
1252
|
});
|
|
919
1253
|
|
|
@@ -19,8 +19,12 @@
|
|
|
19
19
|
2. HTML 必须是完整单文件(包含内联 CSS 与内联 JS),不得依赖外部脚本/样式资源
|
|
20
20
|
2.1 为了保证生成稳定性:`/specs/details/index.html` 必须以模板为基础生成——从 `prompts/vspec_detail/index.html` 复制整份内容作为起始模板,然后仅做必要的替换/填充(例如内嵌文件内容 JSON、默认打开文件等)。不要从零开始“自由发挥”生成 HTML。
|
|
21
21
|
3. 页面布局:
|
|
22
|
-
-
|
|
23
|
-
|
|
22
|
+
- 左侧:章节导航 + 标题层级目录(必须)
|
|
23
|
+
- 章节导航按“章节结构”分组,而不是按文件名;必须包含并链接到:背景(original.md)、干系人(stakeholders.md)、角色(roles.md)、术语(terms.md)、流程(flows)、场景(scenarios)、数据模型(models)、功能清单(functions)、以及 details 下的各类细节文档
|
|
24
|
+
- 对当前选中的 Markdown 文档:左侧必须展示该文档的“标题与层级结构目录”(基于 `#`~`####`),可点击跳转到右侧预览对应标题位置
|
|
25
|
+
- 右侧:Markdown Editor(必须)
|
|
26
|
+
- 必须支持在线编辑与预览(至少提供 Split/Edit/Preview 三种模式)
|
|
27
|
+
- 支持把编辑结果保存回本地文件(若浏览器不支持写入,则提供下载替换的兼容方案)
|
|
24
28
|
4. 渲染规则:
|
|
25
29
|
- `*.md`:以 Markdown 渲染方式显示(不是纯文本)
|
|
26
30
|
- `*.html`:使用 `iframe srcdoc` 渲染(而不是当作文本显示)
|
|
@@ -39,7 +43,7 @@
|
|
|
39
43
|
- 支持通过 URL hash 直接打开文件(例如 `index.html#module/a/b.md`)
|
|
40
44
|
7. 文档导出提示(必须):
|
|
41
45
|
- 页面顶部必须有一个明显提示条(banner/notice),说明:可通过 `/vspec:doc` 生成 Word 版需求文档
|
|
42
|
-
- 提示条需要包含输出路径:`/docs/current/requirement_detail.
|
|
46
|
+
- 提示条需要包含输出路径:`/docs/current/requirement_detail.docx`
|
|
43
47
|
- 提示条文案要简短、醒目,不随目录滚动消失(建议固定在顶部)
|
|
44
48
|
|
|
45
49
|
实现约束(必须):
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
你是一名资深产品经理与交付文档编辑。你的任务是:把项目已有的需求产物(`/specs/**`)汇总成一份可直接用 Microsoft Word 打开的“需求详细文档”,并输出为 Word 可识别的单文件 `.
|
|
1
|
+
你是一名资深产品经理与交付文档编辑。你的任务是:把项目已有的需求产物(`/specs/**`)汇总成一份可直接用 Microsoft Word 打开的“需求详细文档”,并输出为 Word 可识别的单文件 `.docx`(HTML 格式),写入到 `/docs/current/` 目录下。
|
|
2
2
|
|
|
3
3
|
语言与本地化(必须):
|
|
4
4
|
- 读取 `/scheme.yaml` 的 `selected.language`(支持 `en`、`zh`、`ja`;若缺失/非法则按 `en` 处理;`zh-CN` 视为 `zh` 的别名)
|
|
@@ -16,8 +16,9 @@
|
|
|
16
16
|
|
|
17
17
|
输出与写入要求(必须):
|
|
18
18
|
1. 确保目录 `/docs/current/` 存在(若不存在则创建)
|
|
19
|
-
2. 仅写入 1 个 Word 文档文件(路径固定):`/docs/current/requirement_detail.
|
|
19
|
+
2. 仅写入 1 个 Word 文档文件(路径固定):`/docs/current/requirement_detail.docx`
|
|
20
20
|
3. 该文件必须是完整的 HTML 文档(包含 `<!doctype html>`、`<html>`、`<head>`、`<body>`),并使用内联 CSS(不得依赖外部资源)
|
|
21
|
+
- 若 Word 弹出“文件格式与扩展名不匹配”的提示:允许打开(该文件内容为 HTML,扩展名为 `.docx`,用于便于传阅与归档)
|
|
21
22
|
4. 文件内容必须保证在 Word 中可打开且排版可读:
|
|
22
23
|
- 使用清晰的层级标题(H1/H2/H3)
|
|
23
24
|
- 表格必须有边框与表头样式
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
1. 封面
|
|
41
42
|
- 文档标题:需求详细文档 / Requirement Detail / 要件詳細ドキュメント
|
|
42
43
|
- 项目名称(若原始需求中可推断则填写;否则留空)
|
|
43
|
-
- 版本号:固定写 `0.1.
|
|
44
|
+
- 版本号:固定写 `0.1.12`
|
|
44
45
|
- 生成日期:使用今天日期(YYYY-MM-DD)
|
|
45
46
|
2. 目录(手工目录即可)
|
|
46
47
|
- 以可点击的锚点链接到各章节
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
- 边界与范围(包含/不包含、上下限、默认值、空值、异常)
|
|
24
24
|
- 状态与操作(暂停/继续/撤回/取消/驳回/变更/紧急叫停等的口径)
|
|
25
25
|
- 数据口径(字段含义、单位、精度、舍入、时间口径、唯一性)
|
|
26
|
+
- 操作体验与交互模式(一步一步向导 vs 一次性表格/表单、列表/详情/批量操作、行内编辑/弹窗/抽屉、草稿/自动保存/撤销、Web 与 Mobile 的差异)
|
|
26
27
|
- 权限与数据权限(角色、组织范围、越权处理、审计)
|
|
27
28
|
- 外部依赖(系统职责边界、接口清单、失败策略、幂等、对账与补偿)
|
|
28
29
|
- 资料与模板(导入/导出模板、通知文案、协议、证书等)
|
|
@@ -90,3 +91,7 @@
|
|
|
90
91
|
- 建议的回答方式(在 `questions.md` 逐条填写“回答/回答者/回答时间/状态”)
|
|
91
92
|
- 填写完成后执行 `/vspec:refine-q` 合并答案进入 `original.md`
|
|
92
93
|
- 若暂时无法回答:允许先保留未回答,但需要标注原因/预计时间(写在“回答”里即可)
|
|
94
|
+
5. 同时写入固定的 HTML 交互问答页面(用于更容易回答并回写 md 文件):
|
|
95
|
+
- 写入:`/specs/background/question_and_answer.html`
|
|
96
|
+
- 该 HTML 必须为完整可直接打开的单文件(包含内联 CSS 与 JS),无需外部资源
|
|
97
|
+
- HTML 内容要求:从 `prompts/vspec_more_q/question_and_answer.html` 复制(保持一致),用于读取/编辑 `original.md` 与 `questions.md` 并回写
|
|
@@ -542,7 +542,13 @@ catalog:
|
|
|
542
542
|
写入要求:
|
|
543
543
|
- 将本次完整输出(包含“原始需求、分析内容、待确认问题”)追加写入到:`/specs/background/original.md`
|
|
544
544
|
- 文件中必须保留原始需求原文与本次分析结果,便于后续 stakeholders/roles 阶段引用
|
|
545
|
+
- 同时写入固定的 HTML 交互问答页面(用于回答 `original.md` 中的“待确认问题/Open Questions/要確認事項”,并可回写 markdown):
|
|
546
|
+
- 写入:`/specs/background/question_and_answer.html`
|
|
547
|
+
- 该 HTML 必须为完整可直接打开的单文件(包含内联 CSS 与 JS),无需外部资源
|
|
548
|
+
- HTML 内容要求:从 `prompts/vspec_new/question_and_answer.html` 复制(保持一致)
|
|
545
549
|
- 交互提示(必须在对话中输出;不要写入 `/specs/background/original.md`):
|
|
550
|
+
- 重要:在你输出并写入 `original.md` 之后,必须立刻停止;不要继续生成 stakeholders/roles/terms/flows/scenarios 等后续产物。必须等待用户先把“待确认问题/Open Questions/要確認事項”回答完毕(或用户明确回复“继续/continue/続けて”表示已完成问答)后,才能进入下一步。
|
|
551
|
+
- 告知用户:请打开 `/specs/background/question_and_answer.html`,选择 `/specs/background/original.md`,在 HTML 表单中回答“待确认问题”并保存回写;完成后再回复“继续/continue/続けて”
|
|
546
552
|
- 告知用户:回答“待确认问题”可以自己逐条回答,也可以委托 AI 基于当前信息先给出一版“建议答案”,用户再逐条确认/修改
|
|
547
553
|
- 给出可复制的建议话术(按所选语言输出对应版本):
|
|
548
554
|
- 语言=en:`Please propose suggested answers for the Open Questions based on current information. I will confirm or edit each answer.`
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
- 角色与权限(谁能做什么,审批链路,越权处理)
|
|
17
17
|
- 数据与口径(关键字段、主数据来源、术语歧义)
|
|
18
18
|
- 流程与场景(取消/变更/驳回/紧急叫停/换人执行等)
|
|
19
|
+
- 操作体验与交互模式(必须明确“如何操作”而不只是“有什么功能”):一步一步向导 vs 一次性表格/表单、列表/详情/批量操作、批量编辑与导入导出、表格行内编辑/弹窗/抽屉、是否支持草稿/自动保存/撤销、移动端与 Web 的差异体验
|
|
19
20
|
- 约束与合规(管理制度、法律法规、留痕留存)
|
|
20
21
|
- 外部依赖(对接系统、数据方向、失败处理、时效)
|
|
21
22
|
- 交付物资料(需要业务提供的文档、模板、文案、协议等)
|
|
@@ -87,3 +88,14 @@
|
|
|
87
88
|
|
|
88
89
|
4. 编号从 1 开始递增
|
|
89
90
|
5. 提问者默认填“BA/系统分析”
|
|
91
|
+
6. 操作体验强制覆盖(必须):
|
|
92
|
+
- 至少生成 4 条与“操作体验/交互模式”相关的问题,且必须覆盖以下主题中的至少 3 个:
|
|
93
|
+
- 一步一步向导(Wizard)还是一次性表格/表单展示(Single-page)
|
|
94
|
+
- 核心列表是否需要支持批量操作(批量通过/批量驳回/批量分派/批量导出等)
|
|
95
|
+
- 表格编辑方式:行内编辑 vs 弹窗/抽屉;是否需要批量编辑
|
|
96
|
+
- 草稿/自动保存/恢复草稿/撤销(Undo)与二次确认(Confirm)的口径
|
|
97
|
+
- Web 与 Mobile 的差异:哪些步骤必须在移动端完成、哪些在 Web 完成、是否需要“只读提示/置灰”
|
|
98
|
+
6. 同时写入固定的 HTML 交互问答页面(用于更容易回答并回写 md 文件):
|
|
99
|
+
- 写入:`/specs/background/question_and_answer.html`
|
|
100
|
+
- 该 HTML 必须为完整可直接打开的单文件(包含内联 CSS 与 JS),无需外部资源
|
|
101
|
+
- HTML 内容要求:从 `prompts/vspec_new/question_and_answer.html` 复制(保持一致),用于读取/编辑 `original.md` 与 `questions.md` 并回写
|