@capyclaw/ppt-skill-narrative 0.0.0-stage → 2.2.0
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 +2 -2
- package/contract/ppt-contract.json +1529 -0
- package/cordis.patch.yml +4 -0
- package/lib/index.mjs +23 -0
- package/lib/provider.mjs +82 -0
- package/package.json +28 -6
- package/skills/pptmaster-narrative/SKILL.md +66 -0
- package/skills/pptmaster-narrative/references/deck-structure.md +42 -0
- package/skills/pptmaster-narrative/references/page-craft.md +31 -0
- package/skills/pptmaster-narrative/references/scqa-pyramid.md +26 -0
package/cordis.patch.yml
ADDED
package/lib/index.mjs
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** dsh-ppt-skill-narrative:单 skill provider。 */
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import { createSkillProvider } from './provider.mjs'
|
|
4
|
+
|
|
5
|
+
export const name = 'ppt-skill-narrative'
|
|
6
|
+
export const inject = ['skills']
|
|
7
|
+
const SKILL = "pptmaster-narrative"
|
|
8
|
+
|
|
9
|
+
function defaultProvider(config = {}) {
|
|
10
|
+
return createSkillProvider({
|
|
11
|
+
assetRoot: config.assetRoot ?? fileURLToPath(new URL('../skills/', import.meta.url)),
|
|
12
|
+
contractRoot: config.contractRoot ?? fileURLToPath(new URL('../contract/', import.meta.url)),
|
|
13
|
+
contractFile: 'ppt-contract.json',
|
|
14
|
+
providerName: "dsh-ppt-narrative",
|
|
15
|
+
skillDirs: [SKILL],
|
|
16
|
+
contractNote: (version, skillName) => (String(skillName || '').startsWith('pptmaster-')
|
|
17
|
+
? ''
|
|
18
|
+
: '契约:PPTist Workbench ' + version + '。动作前先调用 ppt_capabilities;工具缺失或 major 不一致时停止,并提示用户安装/升级 dsh-ppt-suite。'),
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function createProvider(config = {}) { return defaultProvider(config) }
|
|
23
|
+
export function apply(ctx, config = {}) { ctx.skills.registerProvider(() => defaultProvider(config)) }
|
package/lib/provider.mjs
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/** 通用 skill provider:从目录扫描 kebab-case SKILL.md,按需加载正文。 */
|
|
2
|
+
import { readFileSync } from 'node:fs'
|
|
3
|
+
import { readFile } from 'node:fs/promises'
|
|
4
|
+
import { isAbsolute, join, sep } from 'node:path'
|
|
5
|
+
|
|
6
|
+
const NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/
|
|
7
|
+
const RANK = 600
|
|
8
|
+
|
|
9
|
+
function parseSkill(raw, path) {
|
|
10
|
+
const frontmatter = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/u.exec(raw)
|
|
11
|
+
if (!frontmatter?.[1]) throw new Error(`skill ${path} 缺少 YAML frontmatter`)
|
|
12
|
+
const meta = {}
|
|
13
|
+
for (const line of frontmatter[1].split(/\r?\n/)) {
|
|
14
|
+
const match = /^([A-Za-z0-9_-]+):\s*(.*)$/.exec(line)
|
|
15
|
+
if (match) meta[match[1]] = match[2].replace(/^['"]|['"]$/g, '').trim()
|
|
16
|
+
}
|
|
17
|
+
if (!meta.name || !meta.description) throw new Error(`skill ${path} 缺少 name/description`)
|
|
18
|
+
if (!NAME_RE.test(meta.name)) throw new Error(`skill ${path} name 必须是 kebab-case`)
|
|
19
|
+
return {
|
|
20
|
+
name: meta.name,
|
|
21
|
+
description: meta.description,
|
|
22
|
+
whenToUse: meta.whenToUse || undefined,
|
|
23
|
+
content: raw.slice(frontmatter[0].length).trim(),
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {{
|
|
29
|
+
* assetRoot: string,
|
|
30
|
+
* skillDirs: string[],
|
|
31
|
+
* providerName?: string,
|
|
32
|
+
* contractRoot?: string,
|
|
33
|
+
* contractNote?: (version: string) => string
|
|
34
|
+
* }} config
|
|
35
|
+
*/
|
|
36
|
+
export function createSkillProvider(config) {
|
|
37
|
+
const assetRoot = config.assetRoot
|
|
38
|
+
if (!assetRoot || !isAbsolute(assetRoot)) throw new Error('createSkillProvider: assetRoot 必须是绝对路径')
|
|
39
|
+
let contract = null
|
|
40
|
+
if (config.contractRoot) {
|
|
41
|
+
try {
|
|
42
|
+
contract = JSON.parse(readFileSync(join(config.contractRoot, config.contractFile || 'contract.json'), 'utf8'))
|
|
43
|
+
} catch { /* 非技能包可以没有契约快照 */ }
|
|
44
|
+
}
|
|
45
|
+
const dirs = config.skillDirs || []
|
|
46
|
+
if (!dirs.length) throw new Error('createSkillProvider: skillDirs 不能为空')
|
|
47
|
+
const candidates = dirs.map((dir) => {
|
|
48
|
+
const directory = join(assetRoot, dir)
|
|
49
|
+
const locator = join(directory, 'SKILL.md')
|
|
50
|
+
const parsed = parseSkill(readFileSync(locator, 'utf8'), locator)
|
|
51
|
+
if (parsed.name !== dir) throw new Error(`skill ${locator} name 必须是 ${dir}`)
|
|
52
|
+
return {
|
|
53
|
+
name: parsed.name,
|
|
54
|
+
description: parsed.description,
|
|
55
|
+
whenToUse: parsed.whenToUse,
|
|
56
|
+
invocation: { modelInvocable: true, userInvocable: true },
|
|
57
|
+
provider: config.providerName || 'dsh-plugin',
|
|
58
|
+
source: 'bundled',
|
|
59
|
+
rank: RANK,
|
|
60
|
+
resourceBase: { kind: 'directory', path: directory.split(sep).join('/') },
|
|
61
|
+
locator,
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
const byName = new Map(candidates.map((candidate) => [candidate.name, candidate]))
|
|
65
|
+
const noteFor = (name) => (contract && config.contractNote
|
|
66
|
+
? config.contractNote(contract.contractVersion, name)
|
|
67
|
+
: '')
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
name: config.providerName || 'dsh-plugin',
|
|
71
|
+
list: () => Promise.resolve(candidates),
|
|
72
|
+
async get(candidate, options = {}) {
|
|
73
|
+
const hit = byName.get(candidate.name)
|
|
74
|
+
if (!hit) throw new Error(`未知 skill ${candidate.name}`)
|
|
75
|
+
const raw = await readFile(hit.locator, { encoding: 'utf8', signal: options.signal })
|
|
76
|
+
const parsed = parseSkill(raw, hit.locator)
|
|
77
|
+
const { rank: _rank, locator: _locator, ...summary } = hit
|
|
78
|
+
const note = noteFor(candidate.name)
|
|
79
|
+
return { ...summary, content: parsed.content + (note ? `\n\n---\n\n${note}` : '') }
|
|
80
|
+
},
|
|
81
|
+
}
|
|
82
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,28 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@capyclaw/ppt-skill-narrative",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@capyclaw/ppt-skill-narrative",
|
|
3
|
+
"version": "2.2.0",
|
|
4
|
+
"description": "PPT skill:SCQA、金字塔、一页一观点、deck_brief。",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./lib/index.mjs",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./lib/index.mjs",
|
|
9
|
+
"./package.json": "./package.json"
|
|
10
|
+
},
|
|
11
|
+
"dsh": {
|
|
12
|
+
"bundle": {
|
|
13
|
+
"patch": "./cordis.patch.yml"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"meta": {
|
|
17
|
+
"title": "PPT Skill · PPT 叙事",
|
|
18
|
+
"description": "SCQA、金字塔、一页一观点、deck_brief。"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"lib/",
|
|
22
|
+
"skills/",
|
|
23
|
+
"contract/",
|
|
24
|
+
"cordis.patch.yml",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"license": "MIT"
|
|
28
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pptmaster-narrative
|
|
3
|
+
description: 方案叙事架构:SCQA 开场、金字塔结构、一页一观点、判断句标题、证据引用、章首过渡与收束。输出 deck 骨架,不负责 PPTist 工具调用与视觉渲染。
|
|
4
|
+
whenToUse: 用户要做方案型/汇报型/提案型 PPT,或要求梳理故事线、目录、章节、页面结构、把资料变成有结论的 deck 时。
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# PPTMaster 叙事架构
|
|
8
|
+
|
|
9
|
+
本技能只负责 **内容结构**,不调用 `ppt_*` 工具,也不决定配色与版式。视觉/工具分别交给独立的视觉技能和工具技能。
|
|
10
|
+
|
|
11
|
+
## 硬纪律
|
|
12
|
+
|
|
13
|
+
| 纪律 | 对治 |
|
|
14
|
+
|---|---|
|
|
15
|
+
| **一页一观点**:一页只讲一个核心判断 + ≤4 条支撑论据 | 信息密度爆炸 |
|
|
16
|
+
| **消化不罗列**:每条论据挂在判断下,并标来源 | 资料一页页堆 |
|
|
17
|
+
| **SCQA 开场**:现状 → 冲突 → 核心问题 | 上来就讲内容 |
|
|
18
|
+
| **金字塔**:先顶层结论,再 3–5 个论点(=章节),每章 2–4 页 | 平铺、无结论 |
|
|
19
|
+
| **判断句标题**:标题是结论,不是话题词 | “品牌定位” vs “应占据日常可及的专业精品定位” |
|
|
20
|
+
| **章章递进**:章首过渡问题,章末收束判断,下一章承接 | 章节硬切 |
|
|
21
|
+
|
|
22
|
+
## 流程
|
|
23
|
+
|
|
24
|
+
1. **拆 brief**:目标 / 预算 / KPI / 时间 / YES / NO;提炼真正的根问题。
|
|
25
|
+
2. **SCQA**:写 `situation`、`complication`、`question`,顶层主张 `answer`。
|
|
26
|
+
3. **金字塔**:把 answer 拆成 3–5 个支撑论点;各章标题连读应能讲通 answer。
|
|
27
|
+
4. **切页**:每页写 `governing_thought`(判断句)+ ≤4 条 `points` + `evidence_refs`。
|
|
28
|
+
5. **补结构件**:封面、目录、brief 开场、章节过渡、章节收束、总结论、行动页。
|
|
29
|
+
6. **自检**:逐条对照硬纪律;结构件缺一不可,判断句标题不能是话题词。
|
|
30
|
+
|
|
31
|
+
## 输出:deck_brief JSON(内部使用)
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"cover": { "title": "方案标题(可含主张)", "subtitle": "客户/项目/日期" },
|
|
36
|
+
"toc": ["第1章 ...", "第2章 ..."],
|
|
37
|
+
"brief_opening": {
|
|
38
|
+
"situation": "现状事实",
|
|
39
|
+
"complication": "变化/矛盾",
|
|
40
|
+
"question": "本方案要回答的那一个核心问题"
|
|
41
|
+
},
|
|
42
|
+
"sections": [{
|
|
43
|
+
"section_no": 1,
|
|
44
|
+
"title": "章节标题",
|
|
45
|
+
"transition_question": "章首引导问题",
|
|
46
|
+
"pages": [{
|
|
47
|
+
"page_no": 1,
|
|
48
|
+
"governing_thought": "完整判断句",
|
|
49
|
+
"points": ["论据1", "论据2"],
|
|
50
|
+
"evidence_refs": ["ind-01", "comp-03"],
|
|
51
|
+
"layout_hint": "big-number|comparison|timeline|matrix|statement|quote|diagram|bullets"
|
|
52
|
+
}],
|
|
53
|
+
"closing_judgment": "本章收束判断"
|
|
54
|
+
}],
|
|
55
|
+
"conclusion": { "governing_thought": "全案顶层结论", "action_items": ["下一步"] }
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
把 `deck_brief` 交给成稿技能:它负责选版式、把 `layout_hint` 映射到 PPTist 页型,并调用工具。不要让本技能直接写工具参数。
|
|
60
|
+
|
|
61
|
+
## 写作
|
|
62
|
+
|
|
63
|
+
- 用具体细节和数字,不用空洞形容词;不会的数据标“待验证/假设”,不编。
|
|
64
|
+
- 简洁、短句、动词;避免“赋能/抓手/闭环/全方位”等黑话。
|
|
65
|
+
- 每页标题能单独读懂;读者扫标题能得到完整论证线。
|
|
66
|
+
- 详细方法:`references/scqa-pyramid.md`、`references/page-craft.md`、`references/deck-structure.md`。
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Deck 结构件与骨架
|
|
2
|
+
|
|
3
|
+
## 结构件(方案型必备)
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
封面 标题可直接打核心主张
|
|
7
|
+
目录 方案地图(纯创意案可省)
|
|
8
|
+
Brief 开场 S 现状 → C 冲突 → Q 根问题(SCQA)
|
|
9
|
+
第 N 章 过渡页 用一个问题引出本章
|
|
10
|
+
内容页 ×K 一页一观点
|
|
11
|
+
本章收束判断 本章结论,下一章承接
|
|
12
|
+
总结论页 SCQA 的 A / 金字塔顶
|
|
13
|
+
行动页 必须做 vs 可以做,给下一步
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
结尾不要只写“谢谢”;回到核心主张或留一句记忆点。
|
|
17
|
+
|
|
18
|
+
## 页面字段
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"page_no": 1,
|
|
23
|
+
"governing_thought": "完整判断句",
|
|
24
|
+
"points": ["论据 1", "论据 2"],
|
|
25
|
+
"evidence_refs": ["ind-01", "comp-03"],
|
|
26
|
+
"layout_hint": "big-number|comparison|timeline|matrix|statement|quote|diagram|bullets"
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
硬规则:
|
|
31
|
+
|
|
32
|
+
- 每页有且仅有一个 `governing_thought`,必须是判断句。
|
|
33
|
+
- `points` ≤ 4;超过说明是两个观点,拆页。
|
|
34
|
+
- 每个内容页 `evidence_refs` 非空;没有来源的判断标假设或砍掉。
|
|
35
|
+
- 章节必须有 `transition_question` 与 `closing_judgment`。
|
|
36
|
+
|
|
37
|
+
## 简版 / 详版
|
|
38
|
+
|
|
39
|
+
- 简版 10–15 页:brief → 各章顶层判断 → 总结论 → 行动。
|
|
40
|
+
- 详版 20–50 页:每个判断展开论据与推导。
|
|
41
|
+
|
|
42
|
+
先按详版搭,简版 = 每章只留顶层判断页。
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# 切页与页面文案
|
|
2
|
+
|
|
3
|
+
## 一页一观点
|
|
4
|
+
|
|
5
|
+
1. 把一章的核心判断列出来,每个独立判断一页。
|
|
6
|
+
2. 每页配 ≤4 条论据;超过 4 条 = 两个观点,拆页。
|
|
7
|
+
3. 每条论据挂 `evidence_refs`;无出处的判断标假设或砍掉。
|
|
8
|
+
4. 与本页判断无关的论据,再有趣也不放。
|
|
9
|
+
|
|
10
|
+
## 判断句标题
|
|
11
|
+
|
|
12
|
+
- ❌ 话题词:“品牌定位”“竞品分析”“市场现状”
|
|
13
|
+
- ✅ 判断句:“竞品在高质平价与匠人叙事间留下中间带”“增长正从开店红利切到复购红利”
|
|
14
|
+
|
|
15
|
+
标题就是这页的结论;读者扫标题应能获得论证主线。
|
|
16
|
+
|
|
17
|
+
## 文案公式
|
|
18
|
+
|
|
19
|
+
| 页型 | 公式 |
|
|
20
|
+
|---|---|
|
|
21
|
+
| 问题/痛点 | PAS:痛点 → 代价 → 方案 |
|
|
22
|
+
| 紧迫 | Cost of Inaction:不行动每周期损失什么 |
|
|
23
|
+
| 转变 | Before-After-Bridge:之前 → 之后 → 方案是桥 |
|
|
24
|
+
| 优势 | FAB:能力 → 优势 → 利益 |
|
|
25
|
+
| 对比 | 前后对比:痛点 vs 效果,感性 + 理性 |
|
|
26
|
+
|
|
27
|
+
## 情绪弧
|
|
28
|
+
|
|
29
|
+
好奇 → 焦虑/不满(摆冲突)→ 希望(给方向)→ 信任(证据)→ 笃定/紧迫(行动)。
|
|
30
|
+
|
|
31
|
+
避免“首先/其次/最后”“综上所述”“赋能/抓手/闭环”等 AI 味套话;用具体细节和数字。
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# SCQA 与金字塔
|
|
2
|
+
|
|
3
|
+
## SCQA
|
|
4
|
+
|
|
5
|
+
- **S 现状**:对方认可的事实。
|
|
6
|
+
- **C 冲突**:变化/矛盾/威胁,让现状不可持续。
|
|
7
|
+
- **Q 问题**:因此必须回答的那一个核心问题(根问题)。
|
|
8
|
+
- **A 答案**:你的顶层主张,后面所有章节为它服务。
|
|
9
|
+
|
|
10
|
+
S+C+Q 落进 `brief_opening`;A 落进 `conclusion.governing_thought`,并在开场预告。
|
|
11
|
+
|
|
12
|
+
## 金字塔
|
|
13
|
+
|
|
14
|
+
```text
|
|
15
|
+
顶层结论 A
|
|
16
|
+
├─ 论点 1(章节 1)
|
|
17
|
+
│ ├─ 论据(页)
|
|
18
|
+
│ └─ 论据(页)
|
|
19
|
+
├─ 论点 2
|
|
20
|
+
└─ 论点 3
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
- 章节 3–5 个;把章节标题连起来读,应能讲通 A。
|
|
24
|
+
- 每页标题写判断句,内容是支撑它的论据。
|
|
25
|
+
- 动笔前先想清楚“读完最希望对方记住的那一句话”,那就是 A。
|
|
26
|
+
- 设计一个可记忆抓手(核心策略名/金句/比喻),开场、章节、结尾重复。
|