@yizhuan-cli/cli 0.1.16-beta.1 → 0.1.16
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/package.json +1 -1
- package/resources/yizhuan-cli/SKILL.md +2 -0
- package/resources/yizhuan-cli/manifest.json +4 -4
- package/src/doctor.js +1 -1
- package/src/index.js +72 -7
- package/src/skill.js +8 -6
package/package.json
CHANGED
|
@@ -68,11 +68,13 @@ yizhuan doctor
|
|
|
68
68
|
- 微信公众号暂不支持全网搜索作品。用户提出该请求时必须明确说明不支持,不得改用作品库检索并把 `empty` 解释为全网没有相关作品;微信公众号作者全网搜索仍可使用 `author_search`。
|
|
69
69
|
- 查询指定作者最近作品时,直接使用 `intent=works_search`,同时提供 `platform`、`authorName` 和 `timeRange`;不得先用 `author_search` 的 `workCount` 判断该作者是否有作品。
|
|
70
70
|
- `author_search` 返回的 `extAuthorId` 是平台外部作者 ID,不是易撰内部 `authorId`。需要按外部 ID 查询作品时只能传 `extAuthorId` / `ext_author_id`(兼容 `author_id`),必须同时提供 `platform`,由服务端完成内部 ID 转换;禁止把它直接作为内部 `authorId` 使用。
|
|
71
|
+
- 关注公开作者时,先用 `author_search` 取得并选定完整作者资料,再调用 `accounts` 的 `mode=follow_public_author`;必须传该结果的 `platform`、`extAuthorId` 和 `nickname`。`nickname` 必须是公开搜索结果中的作者名称,绝不能将 `extAuthorId` 作为名称写入。
|
|
71
72
|
- 公开作者结果中的 `workCount: 0` 可能是上游未返回作品数后的缺省值,不代表作者没有作品;是否有作品必须以 `works_search` 的结果为准。
|
|
72
73
|
- 作品复合筛选优先使用统一协议 `intent=works_search`。平台使用 `platform`,分类使用 `categories`,内容类型使用 `content_type`,最低阅读/播放和评论分别使用 `min_views`、`min_comments`;需要固定顺序时显式传 `sort_by` 和 `order`。不得把作品查询改成作者、雷达或热点查询。
|
|
73
74
|
- 当前 CLI 不支持按单篇作品 URL 或平台侧 `articleId` 精确查询作品详情。用户提供作品链接或文章 ID 时,直接说明该能力暂不支持;不得将完整链接或纯文章 ID 改作 `keyword` 搜索,也不得把这种搜索返回的 `empty` 解读为全网不存在该作品。
|
|
74
75
|
- 作品查询返回后必须逐项核对用户要求的分类、类型和指标阈值;低于阈值或缺少必要指标的项目不得作为符合条件的结果展示,也不得擅自放宽条件。展示总数必须使用返回的 `data.total`,不能把当前页条数当成总数。
|
|
75
76
|
- 作者累计指标筛选使用 `author_query`,不得改成作品指标或擅自降低阈值。必须逐项映射用户给出的所有条件:粉丝使用 `min_follower` / `max_follower`,点赞使用 `min_total_likes` / `max_total_likes`,阅读使用 `min_total_views` / `max_total_views`,关注使用 `min_friends` / `max_friends`,作品数使用 `min_work_count` / `max_work_count`;复合条件不得遗漏任何字段。例如“百家号粉丝大于50万,点赞大于10万”必须同时传 `platform_id:2`、`min_follower:500000` 和 `min_total_likes:100000`。
|
|
77
|
+
- 用户按粉丝、关注、阅读、评论或点赞询问“最高作者”时,始终以作者结果的对应累计字段排序或判断,禁止改查作品、雷达作品或单篇作品指标。若返回作者中目标字段均为 `0`,如实说明“查询到的作者该指标均为 0,无法据此排序或判断最高作者”;不得据此断言平台不支持,也不得以作品数据补充或替代结论。
|
|
76
78
|
- 作者指标查询返回后,必须核对 `data.items` 中所有已请求指标。指标缺失、显示“暂未返回”或低于/高于用户阈值的项目不得作为符合条件的结果展示;一旦发现此类项目,必须先检查实际请求参数是否遗漏对应的 min/max 字段并使用完整参数重新查询,禁止直接报告原总数。
|
|
77
79
|
- “我的关注作者”使用 `radar` 且 `mode=following`,不是作品收藏。
|
|
78
80
|
- “我的灵感”“灵感库”“灵感作品”“我的收藏”和“收藏作品”是同一业务概念,均直接使用 `favorites`;不得尝试 `inspiration` 等不存在的 ability,也不要反问用户是不是指收藏作品。
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"name": "yizhuan-cli",
|
|
4
|
-
"version": "0.1.16
|
|
5
|
-
"compatibleCliVersion": "0.1.16
|
|
6
|
-
"source": "npm:@yizhuan-cli/cli@0.1.16
|
|
4
|
+
"version": "0.1.16",
|
|
5
|
+
"compatibleCliVersion": "0.1.16",
|
|
6
|
+
"source": "npm:@yizhuan-cli/cli@0.1.16",
|
|
7
7
|
"digestAlgorithm": "sha256",
|
|
8
8
|
"files": [
|
|
9
9
|
{
|
|
10
10
|
"path": "SKILL.md",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "a46185057b4fa09503ba1773d3e8912a9a448c845f16c39759e5e19b246a1e39"
|
|
12
12
|
}
|
|
13
13
|
]
|
|
14
14
|
}
|
package/src/doctor.js
CHANGED
|
@@ -210,7 +210,7 @@ export function inspectSkill(
|
|
|
210
210
|
}
|
|
211
211
|
return check('pass', 'skill', `正式 Skill ${metadata.version} 来源和摘要有效:${resolved.target}`)
|
|
212
212
|
} catch (error) {
|
|
213
|
-
return check('fail', 'skill', `正式 Skill 校验失败:${error.message}`, '
|
|
213
|
+
return check('fail', 'skill', `正式 Skill 校验失败:${error.message}`, '人工确认后运行 yizhuan skill sync --repair,或移走冲突内容后重新同步。')
|
|
214
214
|
}
|
|
215
215
|
}
|
|
216
216
|
|
package/src/index.js
CHANGED
|
@@ -59,7 +59,7 @@ Usage:
|
|
|
59
59
|
yizhuan config init [--api-key-stdin] [--api-base-url <url>] [--force]
|
|
60
60
|
yizhuan config path
|
|
61
61
|
yizhuan config show
|
|
62
|
-
yizhuan skill sync [--global]
|
|
62
|
+
yizhuan skill sync [--global] [--host <name>] [--repair]
|
|
63
63
|
yizhuan doctor [--json]
|
|
64
64
|
|
|
65
65
|
Examples:
|
|
@@ -323,6 +323,65 @@ function assignFlagParams(params, args) {
|
|
|
323
323
|
return params
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
+
const AUTHOR_RANKING_METRICS = [
|
|
327
|
+
{
|
|
328
|
+
key: 'friendsCount',
|
|
329
|
+
label: '关注数',
|
|
330
|
+
patterns: [/关注/, /friends(?:Count|_count)?/i]
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
key: 'followerCount',
|
|
334
|
+
label: '粉丝数',
|
|
335
|
+
patterns: [/粉丝/, /follower(?:Count|_count)?/i]
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
key: 'totalViews',
|
|
339
|
+
label: '阅读数',
|
|
340
|
+
patterns: [/阅读|播放/, /(?:total)?views?/i]
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
key: 'totalLikes',
|
|
344
|
+
label: '点赞数',
|
|
345
|
+
patterns: [/点赞/, /(?:total)?likes?/i]
|
|
346
|
+
}
|
|
347
|
+
]
|
|
348
|
+
|
|
349
|
+
function rankingMetric(query, params) {
|
|
350
|
+
const text = [
|
|
351
|
+
query,
|
|
352
|
+
params?.metric,
|
|
353
|
+
params?.sortBy,
|
|
354
|
+
params?.sort_by,
|
|
355
|
+
params?.order
|
|
356
|
+
].filter(Boolean).join(' ')
|
|
357
|
+
if (!/(最高|最多|最大|第一|排行|排名|top)/i.test(text)) return undefined
|
|
358
|
+
return AUTHOR_RANKING_METRICS.find((metric) => metric.patterns.some((pattern) => pattern.test(text)))
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export function annotateUnavailableAuthorRanking(result, { query, params } = {}) {
|
|
362
|
+
const metric = rankingMetric(query, params)
|
|
363
|
+
const items = result?.data?.items
|
|
364
|
+
if (!metric || !Array.isArray(items) || items.length === 0) return result
|
|
365
|
+
|
|
366
|
+
const allValuesAreZero = items.every((item) => Number(item?.[metric.key]) === 0)
|
|
367
|
+
if (!allValuesAreZero) return result
|
|
368
|
+
|
|
369
|
+
const message = `查询到的作者${metric.label}均为 0,无法据此排序或判断最高作者`
|
|
370
|
+
return {
|
|
371
|
+
...result,
|
|
372
|
+
message: result.message || message,
|
|
373
|
+
data: {
|
|
374
|
+
...result.data,
|
|
375
|
+
ranking: {
|
|
376
|
+
metric: metric.key,
|
|
377
|
+
status: 'unavailable',
|
|
378
|
+
reason: 'all_returned_values_zero',
|
|
379
|
+
message
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
326
385
|
export function buildExecuteParams(args) {
|
|
327
386
|
if (args.jsonFile) {
|
|
328
387
|
const conflictingKeys = ['params', 'query', ...PARAM_FLAG_KEYS].filter(
|
|
@@ -372,6 +431,7 @@ async function execute(args) {
|
|
|
372
431
|
throw new Error(`未知 ability:${ability || ''},请执行 yizhuan --help 查看可用能力`)
|
|
373
432
|
}
|
|
374
433
|
|
|
434
|
+
const requestParams = buildExecuteParams(args)
|
|
375
435
|
const config = readConfig()
|
|
376
436
|
const response = await fetch(`${config.apiBaseUrl}/api/cli.execute`, {
|
|
377
437
|
method: 'POST',
|
|
@@ -384,7 +444,7 @@ async function execute(args) {
|
|
|
384
444
|
json: {
|
|
385
445
|
ability,
|
|
386
446
|
query: typeof args.query === 'string' ? args.query : undefined,
|
|
387
|
-
params:
|
|
447
|
+
params: requestParams
|
|
388
448
|
}
|
|
389
449
|
})
|
|
390
450
|
})
|
|
@@ -395,7 +455,6 @@ async function execute(args) {
|
|
|
395
455
|
}
|
|
396
456
|
|
|
397
457
|
let result = payloadResult(JSON.parse(text))
|
|
398
|
-
const requestParams = buildExecuteParams(args)
|
|
399
458
|
if (
|
|
400
459
|
ability === 'detection' &&
|
|
401
460
|
requestParams?.wait === true &&
|
|
@@ -426,6 +485,10 @@ async function execute(args) {
|
|
|
426
485
|
result = payloadResult(JSON.parse(pollText))
|
|
427
486
|
}
|
|
428
487
|
}
|
|
488
|
+
result = annotateUnavailableAuthorRanking(result, {
|
|
489
|
+
query: typeof args.query === 'string' ? args.query : undefined,
|
|
490
|
+
params: requestParams
|
|
491
|
+
})
|
|
429
492
|
console.log(JSON.stringify(result, null, 2))
|
|
430
493
|
}
|
|
431
494
|
|
|
@@ -468,9 +531,11 @@ async function query(args) {
|
|
|
468
531
|
})
|
|
469
532
|
const text = await response.text()
|
|
470
533
|
if (!response.ok) throw new Error(`请求失败 HTTP ${response.status}:${text}`)
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
534
|
+
const result = annotateUnavailableAuthorRanking(payloadResult(JSON.parse(text)), {
|
|
535
|
+
query: request.query || request.keyword,
|
|
536
|
+
params: request
|
|
537
|
+
})
|
|
538
|
+
console.log(renderQuery(result, args.format || 'json'))
|
|
474
539
|
}
|
|
475
540
|
|
|
476
541
|
async function main() {
|
|
@@ -510,7 +575,7 @@ async function main() {
|
|
|
510
575
|
}
|
|
511
576
|
|
|
512
577
|
if (command === 'skill' && args._[1] === 'sync') {
|
|
513
|
-
assertCommandArgs(args, 'skill sync', new Set(['global', 'host', 'help']), 2)
|
|
578
|
+
assertCommandArgs(args, 'skill sync', new Set(['global', 'host', 'repair', 'help']), 2)
|
|
514
579
|
await runSkillSync(args)
|
|
515
580
|
return
|
|
516
581
|
}
|
package/src/skill.js
CHANGED
|
@@ -90,7 +90,7 @@ export function loadOfficialSkill(resourceDir = DEFAULT_RESOURCE_DIR) {
|
|
|
90
90
|
return { manifest, resourceDir }
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
export function readSkillMetadata(target) {
|
|
93
|
+
export function readSkillMetadata(target, { allowModified = false } = {}) {
|
|
94
94
|
const metadataPath = path.join(target, SKILL_METADATA_FILE)
|
|
95
95
|
if (!fs.existsSync(metadataPath)) {
|
|
96
96
|
throw new Error(`目标存在未受易撰管理的同名内容,已停止同步:${target}`)
|
|
@@ -111,7 +111,7 @@ export function readSkillMetadata(target) {
|
|
|
111
111
|
}
|
|
112
112
|
for (const file of metadata.files) {
|
|
113
113
|
const installedPath = safeResourcePath(target, file.path)
|
|
114
|
-
if (!fs.existsSync(installedPath) || sha256File(installedPath) !== file.sha256) {
|
|
114
|
+
if (!allowModified && (!fs.existsSync(installedPath) || sha256File(installedPath) !== file.sha256)) {
|
|
115
115
|
throw new Error(`已安装 Skill 包含未知修改,已停止覆盖:${file.path}`)
|
|
116
116
|
}
|
|
117
117
|
}
|
|
@@ -182,14 +182,15 @@ export function syncOfficialSkill(
|
|
|
182
182
|
{
|
|
183
183
|
loadBundle = loadOfficialSkill,
|
|
184
184
|
installBundle = installStagedSkill,
|
|
185
|
-
now = () => new Date()
|
|
185
|
+
now = () => new Date(),
|
|
186
|
+
repair = false
|
|
186
187
|
} = {}
|
|
187
188
|
) {
|
|
188
189
|
const bundle = loadBundle()
|
|
189
190
|
const { manifest } = bundle
|
|
190
191
|
let installed
|
|
191
192
|
if (fs.existsSync(resolved.target)) {
|
|
192
|
-
installed = readSkillMetadata(resolved.target)
|
|
193
|
+
installed = readSkillMetadata(resolved.target, { allowModified: repair })
|
|
193
194
|
const comparison = compareVersions(installed.version, manifest.version)
|
|
194
195
|
if (comparison > 0) {
|
|
195
196
|
return { action: 'newer', metadata: installed, target: resolved.target }
|
|
@@ -257,11 +258,12 @@ export function resolveSkillTarget(
|
|
|
257
258
|
|
|
258
259
|
export function printSkillSyncHelp() {
|
|
259
260
|
console.log(`Usage:
|
|
260
|
-
yizhuan skill sync [--global] [--host <name>]
|
|
261
|
+
yizhuan skill sync [--global] [--host <name>] [--repair]
|
|
261
262
|
|
|
262
263
|
Options:
|
|
263
264
|
--global 安装到当前受支持宿主的用户级 Skill 目录
|
|
264
265
|
--host 明确指定 codex、agents、trae、claude 或 cursor
|
|
266
|
+
--repair 仅覆盖带官方托管元数据但内容校验失败的 Skill
|
|
265
267
|
--help 显示本帮助`)
|
|
266
268
|
}
|
|
267
269
|
|
|
@@ -272,7 +274,7 @@ export async function runSkillSync(args) {
|
|
|
272
274
|
}
|
|
273
275
|
|
|
274
276
|
const resolved = resolveSkillTarget({ global: Boolean(args.global), host: args.host })
|
|
275
|
-
const result = syncOfficialSkill(resolved)
|
|
277
|
+
const result = syncOfficialSkill(resolved, { repair: Boolean(args.repair) })
|
|
276
278
|
const actionText = {
|
|
277
279
|
installed: '已安装',
|
|
278
280
|
updated: '已更新',
|