@capyclaw/ppt-skill-editing 2.2.0 → 2.2.1
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/contract/ppt-contract.json +1 -1
- package/cordis.patch.yml +2 -2
- package/lib/index.mjs +2 -2
- package/lib/provider.mjs +82 -82
- package/package.json +18 -3
package/README.md
CHANGED
package/cordis.patch.yml
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# dsh-ppt-skill-editing bundle patch:只注册 pptist-editing 一个 skill provider。
|
|
1
|
+
# @capyclaw/ppt-skill-editing(目录 dsh-ppt-skill-editing) bundle patch:只注册 pptist-editing 一个 skill provider。
|
|
2
2
|
- insert:
|
|
3
3
|
- id: ppt-skill-editing
|
|
4
|
-
name:
|
|
4
|
+
name: @capyclaw/ppt-skill-editing
|
package/lib/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** dsh-ppt-skill-editing
|
|
1
|
+
/** @capyclaw/ppt-skill-editing(目录 dsh-ppt-skill-editing):单 skill provider。 */
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
3
|
import { createSkillProvider } from './provider.mjs'
|
|
4
4
|
|
|
@@ -15,7 +15,7 @@ function defaultProvider(config = {}) {
|
|
|
15
15
|
skillDirs: [SKILL],
|
|
16
16
|
contractNote: (version, skillName) => (String(skillName || '').startsWith('pptmaster-')
|
|
17
17
|
? ''
|
|
18
|
-
: '契约:PPTist Workbench ' + version + '。动作前先调用 ppt_capabilities;工具缺失或 major 不一致时停止,并提示用户安装/升级
|
|
18
|
+
: '契约:PPTist Workbench ' + version + '。动作前先调用 ppt_capabilities;工具缺失或 major 不一致时停止,并提示用户安装/升级 @capyclaw/ppt-suite。'),
|
|
19
19
|
})
|
|
20
20
|
}
|
|
21
21
|
|
package/lib/provider.mjs
CHANGED
|
@@ -1,82 +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
|
-
}
|
|
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,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capyclaw/ppt-skill-editing",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "PPT skill:指哪打哪的修改执行器:每步落盘、不审美、不审计、不回头验证。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.mjs",
|
|
@@ -8,10 +8,19 @@
|
|
|
8
8
|
".": "./lib/index.mjs",
|
|
9
9
|
"./package.json": "./package.json"
|
|
10
10
|
},
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=18",
|
|
13
|
+
"dsh": "^0.2.0-rc.2"
|
|
14
|
+
},
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
11
18
|
"dsh": {
|
|
12
19
|
"bundle": {
|
|
13
20
|
"patch": "./cordis.patch.yml"
|
|
14
|
-
}
|
|
21
|
+
},
|
|
22
|
+
"category": "skills",
|
|
23
|
+
"platform": "both"
|
|
15
24
|
},
|
|
16
25
|
"meta": {
|
|
17
26
|
"title": "PPT Skill · PPT 纯执行",
|
|
@@ -24,5 +33,11 @@
|
|
|
24
33
|
"cordis.patch.yml",
|
|
25
34
|
"README.md"
|
|
26
35
|
],
|
|
27
|
-
"license": "MIT"
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"scripts": {
|
|
38
|
+
"test": "node --test test/smoke.test.mjs"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@capyclaw/plugin-kit": "^0.2.0"
|
|
42
|
+
}
|
|
28
43
|
}
|