svharness 0.14.13 → 0.14.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.
Files changed (32) hide show
  1. package/README.md +148 -9
  2. package/assets/codechat-agent.ico +0 -0
  3. package/dist/adapters/codex.js +19 -0
  4. package/dist/adapters/index.js +2 -0
  5. package/dist/commands/doctor/utils.js +2 -0
  6. package/dist/commands/init.js +37 -129
  7. package/dist/commands/shell-integration.js +40 -3
  8. package/dist/commands/wiki.js +127 -0
  9. package/dist/config/index.js +2 -1
  10. package/dist/config/merge-options.js +19 -0
  11. package/dist/config/normalize.js +18 -0
  12. package/dist/core/state.js +39 -0
  13. package/dist/index.js +58 -10
  14. package/dist/lib/agent-launcher.js +14 -16
  15. package/dist/lib/codechat-runner-resolver.js +208 -0
  16. package/dist/lib/wiki/run-wiki-generation.js +161 -0
  17. package/dist/lib/win-registry.js +10 -0
  18. package/dist/utils/validate-args.js +1 -0
  19. package/dist/wiki/repowikiIndexer.js +19 -150
  20. package/dist/wiki/repowikiPrompts.js +296 -0
  21. package/dist/wiki/repowikiStructureNormalize.js +31 -1
  22. package/dist/wiki/wikiTasksWriter.js +59 -22
  23. package/docs/agent-launcher-design.md +127 -26
  24. package/docs/standalone-codechat-ps1.md +123 -0
  25. package/package.json +2 -1
  26. package/scripts/preuninstall.js +45 -5
  27. package/templates/_shared/build-rules/harness-build-rule-agent-agnostic.md +2 -2
  28. package/templates/_shared/build-rules/harness-build-rule-specs-schema.md +1 -1
  29. package/templates/_shared/build-skills/harness-build-skill-orchestrator.md +1 -1
  30. package/templates/_shared/build-skills/harness-build-skill-wiki-writer.md +15 -6
  31. package/templates/codechat/Start-CodeChat.ps1 +359 -0
  32. package/templates/svharness.config.example.yaml +7 -0
@@ -0,0 +1,296 @@
1
+ "use strict";
2
+ /**
3
+ * Dual-audience LLM prompts for wiki generation.
4
+ *
5
+ * - `human`: standalone `svharness wiki` — developer-readable documentation.
6
+ * - `agent`: `svharness build` / `svharness wiki --harness` — coding-agent knowledge map.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.structurePrompt = structurePrompt;
10
+ exports.structureSystemPrompt = structureSystemPrompt;
11
+ exports.pageMarkdownPrompt = pageMarkdownPrompt;
12
+ exports.pageGenerationSystemPrompt = pageGenerationSystemPrompt;
13
+ const wikiStructureXml_1 = require("./wikiStructureXml");
14
+ function zhOutlineExtra(structureLang) {
15
+ return structureLang.folder === "zh"
16
+ ? `
17
+
18
+ 【简体中文硬性要求 — 纲要步骤】凡是给人看的文字(仓库级 <title>/ <description>、每个 <section> 的 <title>、每个 <page> 的 <title> 与 <description>)必须全部是简体中文(zh-CN)。禁止使用英文句子或段落;仅路径、文件名、代码标识符/API 符号可保留英文。`
19
+ : "";
20
+ }
21
+ function enOutlineExtra(structureLang) {
22
+ return structureLang.folder === "en"
23
+ ? `
24
+
25
+ 【English hard requirement — outline step】All human-readable text in this XML (repo-level <title>/<description>, each <section>'s <title>, each <page>'s <title> and <description>) MUST be fluent English. Do not write Chinese, Japanese, or other non-English prose for those fields. Only code identifiers, filenames, paths, API symbols, and unavoidable proper nouns may stay non-English where appropriate.`
26
+ : "";
27
+ }
28
+ function humanComprehensiveSectionBlock(structureLang) {
29
+ return `Use **multiple** top-level \`<section>\` blocks under \`<sections>\`: **between 2 and 7** (inclusive). **Never** return only **one** section when the wiki has **two or more** pages — split topics into **distinct** sections (do **not** collapse everything into a single bucket named after the wiki title).
30
+ Use **at most 7** sections total. For very small repos with only **one** page, **one** section is acceptable.
31
+ Each section MUST have a **unique** \`id\` attribute (letters, digits, hyphens only — stable identifier).
32
+ Each section's \`<title>\` MUST be **in the outline language** (${structureLang.label}), **repo-specific**, and suitable as a section heading (this becomes the subdirectory name under each language folder).
33
+ **Filesystem folders** under the language directory are derived from each section \`<title>\` (slugified), **not** from \`id\`.
34
+ Distribute pages across sections: each section's \`<pages>\` lists \`<page_ref>\` ids; use **at most ${wikiStructureXml_1.REPOWIKI_MAX_PAGES}** pages **total** across all sections.
35
+
36
+ Return ONLY valid XML in this shape:
37
+
38
+ <wiki_structure>
39
+ <title>[Overall title]</title>
40
+ <description>[Brief]</description>
41
+ <sections>
42
+ <section id="getting-started">
43
+ <title>[Section title in outline language]</title>
44
+ <pages>
45
+ <page_ref>page-1</page_ref>
46
+ </pages>
47
+ <subsections/>
48
+ </section>
49
+ <!-- more sections with unique ids -->
50
+ </sections>
51
+ <pages>
52
+ <page id="page-1">
53
+ <title>[Title]</title>
54
+ <description>[What this covers]</description>
55
+ <importance>high</importance>
56
+ <relevant_files>
57
+ <file_path>[repo-relative/path]</file_path>
58
+ </relevant_files>
59
+ <related_pages/>
60
+ <parent_section>getting-started</parent_section>
61
+ </page>
62
+ </pages>
63
+ </wiki_structure>`;
64
+ }
65
+ function agentComprehensiveSectionBlock(structureLang) {
66
+ return `Produce an **agent knowledge map** (not a human tutorial). Use **multiple** top-level \`<section>\` blocks under \`<sections>\`: **between 2 and 7** (inclusive).
67
+ Organize sections by **coding workflow**, not marketing names. Prefer repo-specific titles such as (in ${structureLang.label}):
68
+ - module boundaries / navigation
69
+ - architecture & extension points (Module, Config, DI entry)
70
+ - data & state flow (Repository, signals, UseCase)
71
+ - UI & MVI patterns (Screen, ViewModel, Contract)
72
+ - feature-domain slices (one section per major feature area when applicable)
73
+
74
+ Each section MUST have a **unique** \`id\` (letters, digits, hyphens only).
75
+ Each section's \`<title>\` MUST be in ${structureLang.label}, filesystem-friendly (becomes subdirectory name under each language folder).
76
+ Use **at most ${wikiStructureXml_1.REPOWIKI_MAX_PAGES}** pages **total** across all sections.
77
+
78
+ Return ONLY valid XML in this shape:
79
+
80
+ <wiki_structure>
81
+ <title>[Overall title]</title>
82
+ <description>[Brief — what this knowledge map helps a coding agent do]</description>
83
+ <sections>
84
+ <section id="module-boundaries">
85
+ <title>[Section title in outline language]</title>
86
+ <pages>
87
+ <page_ref>page-1</page_ref>
88
+ </pages>
89
+ <subsections/>
90
+ </section>
91
+ </sections>
92
+ <pages>
93
+ <page id="page-1">
94
+ <title>[Title]</title>
95
+ <description>[When a coding agent needs ___, read this page. Keywords: ClassName, path/hint]</description>
96
+ <importance>high</importance>
97
+ <relevant_files>
98
+ <file_path>[entry-point path, read order]</file_path>
99
+ </relevant_files>
100
+ <related_pages/>
101
+ <parent_section>module-boundaries</parent_section>
102
+ </page>
103
+ </pages>
104
+ </wiki_structure>`;
105
+ }
106
+ function simpleSectionBlock() {
107
+ return `<wiki_structure>
108
+ <title>[Overall title]</title>
109
+ <description>[Brief]</description>
110
+ <pages>
111
+ <page id="page-1">
112
+ <title>[Title]</title>
113
+ <description>[What]</description>
114
+ <importance>high</importance>
115
+ <relevant_files>
116
+ <file_path>[path]</file_path>
117
+ </relevant_files>
118
+ <related_pages/>
119
+ </page>
120
+ </pages>
121
+ </wiki_structure>`;
122
+ }
123
+ function agentSimpleSectionBlock() {
124
+ return `<wiki_structure>
125
+ <title>[Overall title]</title>
126
+ <description>[Brief — agent knowledge map scope]</description>
127
+ <pages>
128
+ <page id="page-1">
129
+ <title>[Title]</title>
130
+ <description>[When a coding agent needs ___, read this page]</description>
131
+ <importance>high</importance>
132
+ <relevant_files>
133
+ <file_path>[entry-point path]</file_path>
134
+ </relevant_files>
135
+ <related_pages/>
136
+ </page>
137
+ </pages>
138
+ </wiki_structure>`;
139
+ }
140
+ function comprehensiveRules(comprehensive) {
141
+ if (comprehensive) {
142
+ return `- Every \`<page>\` MUST include \`<parent_section>\` with the **exact** \`id\` of the section that owns this page (the section whose \`<page_ref>\` lists this page id).
143
+ - Each page id must appear in exactly one section's \`page_ref\` list.
144
+ - Each section \`<title>\` becomes a subdirectory name (slugified); keep titles concise and filesystem-friendly where possible.
145
+ - CRITICAL: Every tag must be closed. After \`<sections>\`, you MUST include one final \`<pages>...</pages>\` block containing every full \`<page id="...">...</page>\` with title, description, parent_section, and relevant_files. Section-level \`<pages>\` may only list \`<page_ref>\` — never omit the outer \`<page>\` definitions.`;
146
+ }
147
+ return `- Without a \`<sections>\` block, every page is stored under **one** folder derived from the wiki \`<title>\`; omit \`<parent_section>\` or ignore it for paths.
148
+ - CRITICAL: Every tag must be closed. Include a \`<pages>\` block listing every full \`<page id="...">...</page>\` with title, description, and relevant_files.`;
149
+ }
150
+ function agentExtraRules(comprehensive) {
151
+ return `- Every \`<page><description>\` MUST start with a routing sentence: when a **coding agent** needs [specific task], read this page (in ${comprehensive ? "outline" : "wiki"} language).
152
+ - \`<relevant_files>\`: list **3–8 entry-point files** (Module/Config/Contract/Repository/Application) in **recommended read order**; skip generated/vendor dirs.
153
+ - \`<importance>\`: \`high\` = read before any new task; \`medium\` = domain-specific; \`low\` = deep detail.`;
154
+ }
155
+ function structurePrompt(audience, repoName, treeText, readmeChunk, structureLang, comprehensive) {
156
+ const opening = audience === "agent"
157
+ ? `Analyze the repository "${repoName}" and produce an **agent knowledge map** (not a human tutorial). Each page must help a **coding AI agent** decide **when to read it**, **which files to open first**, and **what patterns/constraints apply**.`
158
+ : `Analyze the repository "${repoName}" and propose a concise developer wiki outline.`;
159
+ const sectionBlock = comprehensive
160
+ ? audience === "agent"
161
+ ? agentComprehensiveSectionBlock(structureLang)
162
+ : humanComprehensiveSectionBlock(structureLang)
163
+ : audience === "agent"
164
+ ? agentSimpleSectionBlock()
165
+ : simpleSectionBlock();
166
+ const agentRules = audience === "agent" ? `\n${agentExtraRules(comprehensive)}` : "";
167
+ return `${opening}
168
+
169
+ <file_tree>
170
+ ${treeText}
171
+ </file_tree>
172
+
173
+ <readme_excerpt>
174
+ ${readmeChunk.slice(0, 12_000)}
175
+ </readme_excerpt>
176
+
177
+ Target language for human-readable titles/descriptions ONLY in this outline step: ${structureLang.label}.${zhOutlineExtra(structureLang)}${enOutlineExtra(structureLang)}
178
+
179
+ ${sectionBlock}
180
+
181
+ Rules:
182
+ - Produce **at most ${wikiStructureXml_1.REPOWIKI_MAX_PAGES}** wiki pages total with **stable** ids \`page-1\`, \`page-2\`, …, \`page-${wikiStructureXml_1.REPOWIKI_MAX_PAGES}\` (use consecutive ids; fewer pages is OK).
183
+ ${comprehensiveRules(comprehensive)}
184
+ - Ensure \`relevant_files\` cite real paths from file_tree where possible (skip generated/vendor dirs).${agentRules}
185
+ - Return ONLY XML. No markdown fences. No HTML. Start with <wiki_structure> end </wiki_structure>.`;
186
+ }
187
+ function structureSystemPrompt(audience, langFolder) {
188
+ if (audience === "agent") {
189
+ if (langFolder === "zh") {
190
+ return "你只输出格式正确的 wiki_structure XML(符合用户给定模板);不要使用 markdown 代码围栏。本纲要面向编码 Agent:标题与描述须可路由、可执行(何时读、改哪些文件),禁止 onboarding 式空话。凡是给人阅读的标题与描述必须使用简体中文(zh-CN)。";
191
+ }
192
+ if (langFolder === "en") {
193
+ return "You output ONLY well-formed wiki_structure XML matching the user's template. Never use markdown fences. This outline is for coding agents: titles/descriptions must be routable and actionable (when to read, which files to open). No onboarding fluff. Human-readable fields must be fluent English.";
194
+ }
195
+ return "You output ONLY well-formed wiki_structure XML for coding agents. Never use markdown fences.";
196
+ }
197
+ if (langFolder === "zh") {
198
+ return "你只输出格式正确的 wiki_structure XML(符合用户给定模板);不要使用 markdown 代码围栏。纲要中凡是给人阅读的标题与描述必须使用简体中文(zh-CN)。";
199
+ }
200
+ if (langFolder === "en") {
201
+ return "You output ONLY well-formed wiki_structure XML matching the user's template. Never use markdown fences. Every repo title, wiki title/description, section titles, page titles, and page descriptions meant for humans must be fluent English (no Chinese prose in those strings unless it is unavoidable quoted content from the repository).";
202
+ }
203
+ return "You output ONLY well-formed wiki_structure XML matching the user's template. Never use markdown fences.";
204
+ }
205
+ function zhBodyExtra(langFolder) {
206
+ return langFolder === "zh"
207
+ ? `
208
+
209
+ 【简体中文硬性要求 — 正文】全部正文、各级标题(# / ## / ###)、表格单元格、Mermaid 与其它图中的说明文字一律使用简体中文(zh-CN)。禁止英文句子,除非直接引用代码、路径、标识符或 API 名称。`
210
+ : "";
211
+ }
212
+ function enBodyExtra(langFolder) {
213
+ return langFolder === "en"
214
+ ? `
215
+
216
+ 【English hard requirement — body】All prose, headings (# / ## / ###), table cell text, and explanatory labels inside Mermaid/diagrams MUST be English. Do not write Chinese prose (or mixed-language explanations) unless you are quoting literal content from repo files/strings. Preserve non-English only in fenced code excerpts, paths, identifiers, and API symbols as they appear in the repo.`
217
+ : "";
218
+ }
219
+ function h1Rule(langFolder, ruleNum) {
220
+ if (langFolder === "zh") {
221
+ return `${ruleNum}. Start with '# [title]' (the H1 must match the topic; for a zh-CN wiki, use Simplified Chinese for that title line).`;
222
+ }
223
+ if (langFolder === "en") {
224
+ return `${ruleNum}. Start with '# [title]' (the H1 must match the topic in English prose).`;
225
+ }
226
+ return `${ruleNum}. Start with '# [title]' (the H1 must match the topic).`;
227
+ }
228
+ function pageMarkdownPrompt(audience, langFolder, langDescriptor, pageTopic, ctx) {
229
+ const zhExtra = zhBodyExtra(langFolder);
230
+ const enExtra = enBodyExtra(langFolder);
231
+ if (audience === "agent") {
232
+ return `You produce a **coding-agent knowledge page** (not a human tutorial). Ground every claim in bundled files only.
233
+
234
+ Output language: ${langDescriptor}.${zhExtra}${enExtra}
235
+
236
+ Repository context bundled as tagged files:
237
+
238
+ ${ctx}
239
+
240
+ Write ONE wiki page for topic: "${pageTopic}".
241
+
242
+ Strict rules:
243
+ 1. Ground claims only in supplied files.
244
+ ${h1Rule(langFolder, 2)}
245
+ 3. Immediately after the H1, add one line: \`Sources: path/to/file:start-end, ...\` (repo-relative paths, comma-separated).
246
+ 4. Use **exactly** these ## sections in order (omit none; use "见关键路径" or "暂无" when empty):
247
+ - \`## 阅读时机\` — 1–3 bullets: when a coding agent should read this page
248
+ - \`## 关键路径\` — table: 用途 | 路径 | 符号/类
249
+ - \`## 扩展与修改入口\` — bullets: how to add/change similar functionality
250
+ - \`## 数据流 / 调用链\` — short text or \`\`\`mermaid graph TD\`\`\` **only** if flow is non-obvious; otherwise "见关键路径"
251
+ - \`## 约定与禁区\` — must-follow and must-not rules from the code
252
+ - \`## 关联\` — related pages or modules if inferable from context
253
+ 5. Do **not** add a TOC block. Do **not** write long onboarding paragraphs (max 5 lines prose per section excluding code).
254
+ 6. Fenced code: ≤40 lines per block; prefer signatures/interfaces over full implementations.
255
+ 7. Mermaid is optional — use only when the call chain cannot be expressed as a table or bullets.
256
+
257
+ If bundled files lack coverage, state gaps briefly — do not invent APIs.`;
258
+ }
259
+ return `You write accurate technical Markdown documentation for developers.
260
+
261
+ Output language: ${langDescriptor}.${zhExtra}${enExtra}
262
+
263
+ Repository context bundled as tagged files:
264
+
265
+ ${ctx}
266
+
267
+ Write ONE wiki page for topic: "${pageTopic}".
268
+
269
+ Strict rules:
270
+ 1. Ground claims only in supplied files.
271
+ ${h1Rule(langFolder, 2)}
272
+ 3. After the introduction (first paragraph or short opener) and **before** the first \`##\`, add a concise **table of contents**: a bullet or numbered list of links to the \`##\` sections that follow (use markdown links with anchors, e.g. \`[Section](#section-slug)\`, matching your real headings). **Omit this TOC** if the page is very short or only has a single \`##\` section (avoid a redundant mini-TOC).
273
+ 4. Use '##','###', tables, fenced code from files when helpful.
274
+ 5. Prefer Mermaid with \`\`\`mermaid diagrams using TOP-DOWN flow (\`graph TD\`, not LR).
275
+ 6. Where useful cite repository files on one line: \`Sources: path/to/file:line-range\` (comma-separated if multiple). Paths are repo-relative with \`/\` separators.
276
+
277
+ If bundled files lack coverage, acknowledge gaps briefly — do not invent APIs.`;
278
+ }
279
+ function pageGenerationSystemPrompt(audience, langFolder) {
280
+ if (audience === "agent") {
281
+ if (langFolder === "zh") {
282
+ return "你是编码 Agent 的知识索引员,只根据提供的 <file path=…> 摘录产出可执行知识页。Wiki 须含固定章节(阅读时机、关键路径、扩展与修改入口、数据流/调用链、约定与禁区、关联)。除代码摘录、路径与标识符外禁止使用英文 prose。";
283
+ }
284
+ if (langFolder === "en") {
285
+ return "You index knowledge for coding agents. Answer ONLY from bundled <file path=…> excerpts. The page MUST use the fixed section template (When to read, Key paths, Extension entry points, Data flow, Conventions/constraints, Related). English prose throughout except code, paths, identifiers.";
286
+ }
287
+ return "You produce agent-oriented Markdown grounded strictly in bundled <file path=…> excerpts.";
288
+ }
289
+ if (langFolder === "zh") {
290
+ return "你是技术文档撰写者,只根据提供的 <file path=…> 摘录作答。Wiki 页的各级标题、正文、表格文字、图中的文字说明必须全部是简体中文(zh-CN)。除代码摘录、仓库路径与标识符/API 英文名外禁止使用英文 prose。";
291
+ }
292
+ if (langFolder === "en") {
293
+ return "You are a technical writer. Answer ONLY from bundled <file path=…> excerpts. The entire Markdown page MUST be English: headings, body, table text, diagram labels/explanations. Use non-English prose only inside literal code fences or when verbatim-quoting user-facing strings from the repository; identifiers, paths, and API symbols follow the codebase.";
294
+ }
295
+ return "You write factual Markdown grounded strictly in bundled <file path=…> excerpts.";
296
+ }
@@ -187,8 +187,38 @@ function normalizeComprehensiveWikiStructure(opts) {
187
187
  groups,
188
188
  };
189
189
  }
190
- function structureRepairUserAppendix(previousRaw, langFolder) {
190
+ function structureRepairUserAppendix(previousRaw, langFolder, audience = "human") {
191
191
  const preview = previousRaw.slice(0, 8_000);
192
+ if (audience === "agent") {
193
+ if (langFolder === "zh") {
194
+ return `
195
+
196
+ 【修订要求 — 上一条 Agent 知识地图无效】上一次的 wiki_structure **小节数量不足**(少于 2 个顶层 <section>,或缺失 <sections>),但需要 **2 至 7 个** 顶层小节。请重新输出 **完整** 的 wiki_structure XML(不要用"见上文"省略)。
197
+
198
+ 硬性要求:
199
+ - 在 <sections> 下提供 **至少 2 个、至多 7 个** 平级 <section>,每个有唯一 id;按编码工作流划分,非教程式命名。
200
+ - **禁止** 把所有 <page> 放进同一个小节;将 ≤20 页分配到多个小节。
201
+ - 每个 <page><description> 须含「当编码 Agent 需要 ___ 时阅读本页」路由句。
202
+ - 每个 <page> 必须有 <parent_section>,其值必须等于拥有该页的 section 的 id(且该 section 的 <page_ref> 引用此页)。
203
+ - 仍须符合此前的 Agent 知识地图模板(先 <sections>,再完整 <pages> 块等)。
204
+
205
+ 上一次输出摘要(仅供参考,请修正而非照抄):
206
+ ${preview}`;
207
+ }
208
+ return `
209
+
210
+ 【Revision required — previous agent knowledge map invalid】The last wiki_structure had **too few** top-level <section> blocks under <sections> (or <sections> was missing). Return a **complete new** wiki_structure XML with **between 2 and 7** sibling <section> elements.
211
+
212
+ Hard requirements:
213
+ - Provide **at least 2 and at most 7** <section> elements under <sections>, each with a **unique** id; organize by coding workflow, not tutorial naming.
214
+ - **Do not** place every <page> in a single section; distribute up to 20 pages across multiple sections.
215
+ - Every <page><description> MUST include a routing sentence for coding agents (when to read this page).
216
+ - Every <page> MUST include <parent_section> equal to the owning section id (that section MUST list the page via <page_ref>).
217
+ - Follow the same agent knowledge map template as before (<sections> first, then a full <pages> block).
218
+
219
+ Previous output (for reference only — fix it, do not copy):
220
+ ${preview}`;
221
+ }
192
222
  if (langFolder === "zh") {
193
223
  return `
194
224
 
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  /**
3
3
  * Writes a `TASKS.md` inside `<wikiBase>/` based on an outline computed by
4
- * `runRepowikiOutlineOnly`. The file is consumed by the downstream
5
- * harness-build-skill-wiki-writer skill which fills page content iteratively.
4
+ * `runRepowikiOutlineOnly`. Agent-audience tasks are consumed by
5
+ * harness-build-skill-wiki-writer; human-audience tasks are for standalone wiki maintenance.
6
6
  */
7
7
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
8
  if (k2 === undefined) k2 = k;
@@ -55,10 +55,9 @@ function groupBySection(pages) {
55
55
  }
56
56
  return [...buckets.entries()].map(([folder, list]) => ({ folder, pages: list }));
57
57
  }
58
- function renderTasksMarkdown(input) {
59
- const { outline } = input;
58
+ function renderHeaderBlock(input, audience) {
60
59
  const lines = [];
61
- const groups = groupBySection(outline.pages);
60
+ const { outline } = input;
62
61
  lines.push(`# Baseline Wiki Tasks — ${input.projectName}`);
63
62
  lines.push("");
64
63
  lines.push(`Generated by svharness v${input.cliVersion} on ${input.generatedAtIso}`);
@@ -66,18 +65,59 @@ function renderTasksMarkdown(input) {
66
65
  lines.push(`Outline model: \`${input.model}\``);
67
66
  lines.push(`Outline XML: \`./structure.xml\``);
68
67
  lines.push(`Primary language: \`${outline.primaryLang}\``);
68
+ lines.push(`Wiki audience: \`${audience}\``);
69
69
  lines.push("");
70
- lines.push("> 下游 agent(harness-build-skill-wiki-writer)按本清单逐页生成 wiki:");
71
- lines.push("> 逐条完成 Pages 列表中的任务,每完成一页把 `[ ]` 改为 `[x]`。");
72
- lines.push("> 每页应产出 Markdown 文件到对应 `Output` 路径,内容严格基于基线源码。");
73
- lines.push("");
74
- lines.push("> **源码引用规范(强制,与 --generate-wiki 产出口径保持一致)**:");
75
- lines.push("> 所有源码链接必须指向 `baseline/code/` 下的路径,允许两种写法(可混用):");
76
- lines.push("> 1. 行内链接:`[Foo.java:10-30](../../baseline/code/<repo-relative>/Foo.java#L10-L30)`");
77
- lines.push("> 2. Sources 行:`Sources: <repo-relative>/Foo.java:10-30, <repo-relative>/Bar.kt:5-8`");
78
- lines.push("> `<repo-relative>` 指**相对 `baseline/code/` 的路径**,既非仓库原始绝对路径,");
79
- lines.push("> 也非 wiki md 文件的相对路径。行号使用 `start-end`(闭区间)。");
70
+ if (audience === "agent") {
71
+ lines.push("> wiki 面向**编码 Agent**消费:每页是知识路由卡片,不是人类 onboarding 文档。");
72
+ lines.push("> 下游 agent(harness-build-skill-wiki-writer)按本清单逐页生成 wiki:");
73
+ lines.push("> 逐条完成 Pages 列表中的任务,每完成一页把 `[ ]` 改为 `[x]`。");
74
+ lines.push("> 每页应产出 Markdown 文件到对应 `Output` 路径,内容严格基于基线源码。");
75
+ lines.push("");
76
+ lines.push("> **源码引用规范(强制,与 --generate-wiki 产出口径保持一致)**:");
77
+ lines.push("> 所有源码链接必须指向 `baseline/code/` 下的路径,允许两种写法(可混用):");
78
+ lines.push("> 1. 行内链接:`[Foo.java:10-30](../../baseline/code/<repo-relative>/Foo.java#L10-L30)`");
79
+ lines.push("> 2. Sources 行:`Sources: <repo-relative>/Foo.java:10-30, <repo-relative>/Bar.kt:5-8`");
80
+ lines.push("> `<repo-relative>` 指**相对 `baseline/code/` 的路径**,既非仓库原始绝对路径,");
81
+ lines.push("> 也非 wiki md 文件的相对路径。行号使用 `start-end`(闭区间)。");
82
+ }
83
+ else {
84
+ lines.push("> 本 wiki 面向**人类开发者**阅读与维护。");
85
+ lines.push("> 按 Pages 列表逐页撰写或完善 Markdown,每完成一页把 `[ ]` 改为 `[x]`。");
86
+ lines.push("> 每页应产出到对应 `Output` 路径,内容严格基于源码目录。");
87
+ lines.push("");
88
+ lines.push("> **源码引用规范**:");
89
+ lines.push("> 使用 `Sources: <repo-relative>/path:line-range` 或行内链接指向源码树中的真实文件。");
90
+ lines.push("> `<repo-relative>` 相对 Source tree(见上文)。");
91
+ }
80
92
  lines.push("");
93
+ return lines;
94
+ }
95
+ function renderAcceptance(audience, sourceRootRel) {
96
+ const lines = [];
97
+ lines.push(`- Acceptance:`);
98
+ lines.push(` - 文件存在且非空`);
99
+ lines.push(` - 首段含页面标题(H1 与 title 一致)`);
100
+ lines.push(` - 至少引用 1 个 Relevant files 中的源码片段或路径`);
101
+ if (audience === "agent") {
102
+ lines.push(` - Sources 行或行内链接的路径必须定位到 \`baseline/code/\` 下的真实文件`);
103
+ lines.push(` - 含 \`## 阅读时机\` 且至少 1 条可执行 bullet`);
104
+ lines.push(` - 含 \`## 关键路径\` 表格,且覆盖 Relevant files 中 ≥1 个路径`);
105
+ lines.push(` - 含 \`## 约定与禁区\`(可为「暂无额外禁区」但章节必须存在)`);
106
+ lines.push(` - H1 下须有 \`Sources:\` 行`);
107
+ lines.push(` - 正文以表格/bullet 为主,单段 prose 不超过 5 行(除代码块外)`);
108
+ }
109
+ else {
110
+ const srcHint = sourceRootRel === "." ? "源码树" : `\`${sourceRootRel}\``;
111
+ lines.push(` - Sources 行或行内链接的路径必须定位到 ${srcHint} 下的真实文件`);
112
+ lines.push(` - 简介段后若有 2 个及以上 \`##\` 章节,插入锚点式 TOC`);
113
+ }
114
+ return lines;
115
+ }
116
+ function renderTasksMarkdown(input) {
117
+ const { outline } = input;
118
+ const audience = input.wikiAudience ?? "human";
119
+ const lines = [...renderHeaderBlock(input, audience)];
120
+ const groups = groupBySection(outline.pages);
81
121
  lines.push("## Sections");
82
122
  for (const g of groups) {
83
123
  const ids = g.pages.map((p) => p.id).join(", ");
@@ -103,15 +143,12 @@ function renderTasksMarkdown(input) {
103
143
  }
104
144
  }
105
145
  else {
106
- lines.push(`- Relevant files: _(none declared — agent should infer from tree)_`);
146
+ lines.push(`- Relevant files: _(none declared — ${audience === "agent" ? "agent" : "author"} should infer from tree)_`);
107
147
  }
108
- lines.push(`- Acceptance:`);
109
- lines.push(` - 文件存在且非空`);
110
- lines.push(` - 首段含页面标题(H1 与 title 一致)`);
111
- lines.push(` - 至少引用 1 个 Relevant files 中的源码片段或路径`);
112
- lines.push(` - Sources 行或行内链接的路径必须定位到 \`baseline/code/\` 下的真实文件`);
148
+ lines.push(...renderAcceptance(audience, input.sourceRootRel));
149
+ lines.push("");
113
150
  }
114
- return lines.join("\n");
151
+ return lines.join("\n").trimEnd() + "\n";
115
152
  }
116
153
  /**
117
154
  * Write `TASKS.md` under the wiki base directory and return the absolute path.