@xiashe/skill 0.1.7 → 0.1.9

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 CHANGED
@@ -9,14 +9,17 @@ This package is intentionally separate from the full `@xiashe/cli` product CLI a
9
9
  `xiashe-skill` helps creators prepare a Skill folder for third-party Skill hubs:
10
10
 
11
11
  - inspect a local Skill project
12
- - run one Agent-friendly registry setup command that prepares all supported hub handoffs
12
+ - run one high-level Agent-friendly publish handoff command
13
+ - run lower-level registry setup commands that prepare all supported hub handoffs
13
14
  - diagnose whether a local Skill has the expected registry disclosure and runtime callback wiring
14
15
  - send labeled local verification events so the Dashboard can confirm integration health
15
16
  - write an explicit `xiashe.skill.json` registry manifest
16
17
  - generate one unified `UPLOAD_HANDOFF.md` that the creator's Agent can use after the user pastes a third-party official upload prompt
17
18
  - generate optional runtime analytics snippets
18
19
 
19
- It does not create upload packages, copy source directories, install background services, run postinstall hooks, read secrets, or upload to third-party hubs. The creator's Agent is responsible for packaging and uploading according to the target Skill hub's official rules.
20
+ It does not install background services, run postinstall hooks, read secrets, or override third-party hub upload rules. The creator's Agent is responsible for packaging and uploading according to the target Skill hub's official rules.
21
+
22
+ The user-facing product flow should point creators at the official publish Markdown page and the `xiashe-publish` Skill. Direct CLI commands are implementation details for local Agents and developers.
20
23
 
21
24
  `setup --hub all` still writes platform-specific `upload-<hub>.md` checklists for source attribution, but those are internal Agent checklists. Users should not need to pick files manually. When a platform such as Red Skill provides its own upload prompt, paste that official prompt to the Agent and tell it to follow `.xiashe/UPLOAD_HANDOFF.md`.
22
25
 
@@ -26,6 +29,7 @@ It does not create upload packages, copy source directories, install background
26
29
  node packages/xiashe-skill-cli/bin/xiashe-skill.mjs --help
27
30
  node packages/xiashe-skill-cli/bin/xiashe-skill.mjs inspect .
28
31
  node packages/xiashe-skill-cli/bin/xiashe-skill.mjs doctor .
32
+ node packages/xiashe-skill-cli/bin/xiashe-skill.mjs publish . --code XS-XXXX-XXXX --to xiashe,claude
29
33
  node packages/xiashe-skill-cli/bin/xiashe-skill.mjs setup . --code XS-XXXX-XXXX --hub all
30
34
  node packages/xiashe-skill-cli/bin/xiashe-skill.mjs setup . --code XS-XXXX-XXXX --hub red
31
35
  node packages/xiashe-skill-cli/bin/xiashe-skill.mjs verify . --hub red --dry-run --json
@@ -6,7 +6,7 @@ import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';
6
6
  import os from 'node:os';
7
7
  import path from 'node:path';
8
8
 
9
- const VERSION = '0.1.7';
9
+ const VERSION = '0.1.9';
10
10
  const COMMAND_NAME = process.env.XIASHE_SKILL_CLI_NAME || 'xiashe-skill';
11
11
  const PRODUCT_NAME = process.env.XIASHE_SKILL_PRODUCT_NAME || (COMMAND_NAME === 'agentpie-skill' ? 'AgentPie' : 'XiaShe');
12
12
  const REGISTRY_PROVIDER = process.env.XIASHE_SKILL_REGISTRY_PROVIDER || (COMMAND_NAME === 'agentpie-skill' ? 'agentpie' : 'xiashe');
@@ -23,8 +23,120 @@ const DEFAULT_CLAIM_URL = process.env.XIASHE_SKILL_CLAIM_URL || `${DEFAULT_BASE_
23
23
  const EVENT_SCHEMA_VERSION = process.env.XIASHE_SKILL_EVENT_SCHEMA_VERSION || (REGISTRY_PROVIDER === 'agentpie'
24
24
  ? 'agentpie.skill.event.v1'
25
25
  : 'xiashe.skill.event.v1');
26
- const SUPPORTED_HUBS = ['red', 'clawhub', 'skillhub', 'claude', 'dify', 'coze', 'generic'];
27
- const DEFAULT_SETUP_HUBS = ['red', 'clawhub', 'skillhub', 'claude', 'dify', 'coze', 'generic'];
26
+ const SUPPORTED_HUBS = ['xiashe', 'red', 'clawhub', 'skillhub', 'claude', 'codex', 'cursor', 'workbuddy', 'dify', 'coze', 'generic'];
27
+ const DEFAULT_SETUP_HUBS = ['xiashe', 'red', 'clawhub', 'skillhub', 'claude', 'codex', 'cursor', 'workbuddy', 'dify', 'coze', 'generic'];
28
+ const PLATFORM_REVIEW_PROFILES = {
29
+ xiashe: {
30
+ label: 'XiaShe Store',
31
+ packageMode: 'full_skill_package',
32
+ manifestPolicy: 'upload_allowed',
33
+ disclosurePlacement: ['Skill public page', 'README', `${WORK_DIR}/REGISTRY_DISCLOSURE.md`],
34
+ runtimeDataPolicy: 'runtime_capable',
35
+ defaultDataSource: 'runtime',
36
+ allowedFilePolicy: 'Upload the complete reviewed Skill package, including the local registry manifest.',
37
+ forbiddenPublicFiles: ['.env', 'credentials', 'node_modules', 'dist', 'build', 'browser profiles', 'SSH keys']
38
+ },
39
+ red: {
40
+ label: 'Red Skill',
41
+ packageMode: 'markdown_or_text_only',
42
+ manifestPolicy: 'local_only',
43
+ disclosurePlacement: ['SKILL.md', 'README', 'platform description field'],
44
+ runtimeDataPolicy: 'attribution_only_by_default',
45
+ defaultDataSource: 'attribution',
46
+ allowedFilePolicy: 'Upload only Markdown/TXT or fields accepted by the official Red Skill flow.',
47
+ forbiddenPublicFiles: [WORK_DIR, `${WORK_DIR}/${MANIFEST_FILE}`, `${WORK_DIR}/runtime-events.js`, 'public tokens', 'signing secrets', 'internal handoff/checklists']
48
+ },
49
+ clawhub: {
50
+ label: 'ClawHub',
51
+ packageMode: 'markdown_or_text_only',
52
+ manifestPolicy: 'local_only',
53
+ disclosurePlacement: ['SKILL.md', 'README', 'platform description field'],
54
+ runtimeDataPolicy: 'attribution_only_by_default',
55
+ defaultDataSource: 'attribution',
56
+ allowedFilePolicy: 'Follow ClawHub official file rules. Treat XiaShe registry files as local-only unless ClawHub explicitly accepts them.',
57
+ forbiddenPublicFiles: [WORK_DIR, `${WORK_DIR}/${MANIFEST_FILE}`, `${WORK_DIR}/runtime-events.js`, 'public tokens', 'signing secrets']
58
+ },
59
+ skillhub: {
60
+ label: 'SkillHub',
61
+ packageMode: 'markdown_or_text_only',
62
+ manifestPolicy: 'local_only',
63
+ disclosurePlacement: ['SKILL.md', 'README', 'platform description field'],
64
+ runtimeDataPolicy: 'attribution_only_by_default',
65
+ defaultDataSource: 'attribution',
66
+ allowedFilePolicy: 'Upload only files accepted by SkillHub. For Markdown/TXT-only flows, copy disclosure text into an allowed field.',
67
+ forbiddenPublicFiles: [WORK_DIR, `${WORK_DIR}/${MANIFEST_FILE}`, `${WORK_DIR}/runtime-events.js`, 'public tokens', 'signing secrets']
68
+ },
69
+ claude: {
70
+ label: 'Claude Code',
71
+ packageMode: 'local_agent_install',
72
+ manifestPolicy: 'platform_dependent',
73
+ disclosurePlacement: ['SKILL.md', 'README', 'agent install note'],
74
+ runtimeDataPolicy: 'runtime_if_http_or_mcp',
75
+ defaultDataSource: 'runtime',
76
+ allowedFilePolicy: 'Install the Skill in Claude-compatible local Skill format. Add MCP/HTTP ack only when the runtime supports it.',
77
+ forbiddenPublicFiles: ['public tokens in public docs', 'signing secrets', 'hidden background processes']
78
+ },
79
+ codex: {
80
+ label: 'Codex',
81
+ packageMode: 'local_agent_install',
82
+ manifestPolicy: 'platform_dependent',
83
+ disclosurePlacement: ['SKILL.md', 'README', 'agent install note'],
84
+ runtimeDataPolicy: 'runtime_if_http_or_mcp',
85
+ defaultDataSource: 'runtime',
86
+ allowedFilePolicy: 'Install as a local Agent Skill and wire MCP/HTTP ack where supported.',
87
+ forbiddenPublicFiles: ['public tokens in public docs', 'signing secrets', 'hidden background processes']
88
+ },
89
+ cursor: {
90
+ label: 'Cursor',
91
+ packageMode: 'local_agent_install',
92
+ manifestPolicy: 'platform_dependent',
93
+ disclosurePlacement: ['rules/skill docs', 'README', 'agent install note'],
94
+ runtimeDataPolicy: 'runtime_if_http_or_mcp',
95
+ defaultDataSource: 'runtime',
96
+ allowedFilePolicy: 'Follow Cursor rule/agent capability conventions and keep callbacks explicit.',
97
+ forbiddenPublicFiles: ['public tokens in public docs', 'signing secrets', 'hidden background processes']
98
+ },
99
+ workbuddy: {
100
+ label: 'WorkBuddy',
101
+ packageMode: 'local_agent_install',
102
+ manifestPolicy: 'platform_dependent',
103
+ disclosurePlacement: ['SKILL.md', 'README', 'agent install note'],
104
+ runtimeDataPolicy: 'runtime_if_http_or_mcp',
105
+ defaultDataSource: 'runtime',
106
+ allowedFilePolicy: 'Follow WorkBuddy agent capability rules and wire ack only through approved runtime boundaries.',
107
+ forbiddenPublicFiles: ['public tokens in public docs', 'signing secrets', 'hidden background processes']
108
+ },
109
+ coze: {
110
+ label: 'Coze',
111
+ packageMode: 'static_prompt_or_api_tool',
112
+ manifestPolicy: 'local_only',
113
+ disclosurePlacement: ['workflow description', 'tool description', 'README'],
114
+ runtimeDataPolicy: 'runtime_if_http_or_mcp',
115
+ defaultDataSource: 'attribution',
116
+ allowedFilePolicy: 'Static prompt mode is attribution-only. Use an HTTP/API Tool step for real runtime analytics.',
117
+ forbiddenPublicFiles: [WORK_DIR, `${WORK_DIR}/${MANIFEST_FILE}`, 'public tokens in static prompts', 'signing secrets']
118
+ },
119
+ dify: {
120
+ label: 'Dify',
121
+ packageMode: 'static_prompt_or_api_tool',
122
+ manifestPolicy: 'local_only',
123
+ disclosurePlacement: ['workflow description', 'tool description', 'README'],
124
+ runtimeDataPolicy: 'runtime_if_http_or_mcp',
125
+ defaultDataSource: 'attribution',
126
+ allowedFilePolicy: 'Static prompt mode is attribution-only. Use an HTTP/API Tool node for real runtime analytics.',
127
+ forbiddenPublicFiles: [WORK_DIR, `${WORK_DIR}/${MANIFEST_FILE}`, 'public tokens in static prompts', 'signing secrets']
128
+ },
129
+ generic: {
130
+ label: 'Generic Skill Hub',
131
+ packageMode: 'external_platform_rules',
132
+ manifestPolicy: 'platform_dependent',
133
+ disclosurePlacement: ['README', 'SKILL.md', 'platform description field'],
134
+ runtimeDataPolicy: 'runtime_if_http_or_mcp',
135
+ defaultDataSource: 'attribution',
136
+ allowedFilePolicy: 'Follow the target platform official rules first; merge only safe XiaShe disclosure and callbacks.',
137
+ forbiddenPublicFiles: ['public tokens in public docs', 'signing secrets', 'hidden background processes']
138
+ }
139
+ };
28
140
  const DEFAULT_IGNORE = new Set([
29
141
  '.git',
30
142
  '.xiashe',
@@ -47,13 +159,14 @@ function usage() {
47
159
 
48
160
  Usage:
49
161
  ${COMMAND_NAME} inspect [skill-dir] [--json]
50
- ${COMMAND_NAME} setup [skill-dir] --code <dynamic-code> [--hub all|red|clawhub|skillhub|claude|dify|coze|generic]
51
- ${COMMAND_NAME} setup [skill-dir] --public-token <token> --skill-id <id> [--hub all|red|clawhub|skillhub|claude|dify|coze|generic]
162
+ ${COMMAND_NAME} publish [skill-dir] --code <dynamic-code> [--to xiashe,red,claude,codex,cursor,workbuddy,dify,coze]
163
+ ${COMMAND_NAME} setup [skill-dir] --code <dynamic-code> [--hub all|xiashe|red|clawhub|skillhub|claude|codex|cursor|workbuddy|dify|coze|generic]
164
+ ${COMMAND_NAME} setup [skill-dir] --public-token <token> --skill-id <id> [--hub all|xiashe|red|clawhub|skillhub|claude|codex|cursor|workbuddy|dify|coze|generic]
52
165
  ${COMMAND_NAME} doctor [skill-dir] [--json]
53
166
  ${COMMAND_NAME} verify [skill-dir] [--hub <hub>] [--scenario <label>] [--dry-run]
54
167
  ${COMMAND_NAME} attach [skill-dir] --code <dynamic-code> [--name <name>]
55
168
  ${COMMAND_NAME} attach [skill-dir] --public-token <token> [--skill-id <id>] [--name <name>]
56
- ${COMMAND_NAME} prompt [skill-dir] --hub <red|clawhub|skillhub|claude|dify|coze|generic> [--source-url <url>] [--out <file>]
169
+ ${COMMAND_NAME} prompt [skill-dir] --hub <xiashe|red|clawhub|skillhub|claude|codex|cursor|workbuddy|dify|coze|generic> [--source-url <url>] [--out <file>]
57
170
  ${COMMAND_NAME} snippet [skill-dir] [--target js|curl] [--out <file>]
58
171
  ${COMMAND_NAME} track [skill-dir] --event <event> [--hub <hub>] [--installation-id <id>] [--invocation-id <id>]
59
172
 
@@ -66,6 +179,7 @@ Options:
66
179
  --name <name> Skill display name override.
67
180
  --description <text> Skill description override.
68
181
  --hub <hub> Target Skill hub prompt style. setup defaults to all supported hubs.
182
+ --to <hub[,hub]> High-level publish targets. Alias for --hub in publish mode.
69
183
  --event <event> Runtime event to submit for testing.
70
184
  --installation-id <id> Stable anonymous install id for runtime analytics.
71
185
  --invocation-id <id> Unique invocation id for one skill call.
@@ -166,11 +280,23 @@ function normalizeHub(value) {
166
280
  if (hub === 'skill-hub') {
167
281
  return 'skillhub';
168
282
  }
283
+ if (hub === 'xiashechat' || hub === 'xia-she' || hub === 'xiashe-store') {
284
+ return 'xiashe';
285
+ }
286
+ if (hub === 'claudecode' || hub === 'claude-code') {
287
+ return 'claude';
288
+ }
289
+ if (hub === 'openaicodex' || hub === 'openai-codex') {
290
+ return 'codex';
291
+ }
292
+ if (hub === 'work-buddy') {
293
+ return 'workbuddy';
294
+ }
169
295
  return hub;
170
296
  }
171
297
 
172
298
  function setupHubsFromFlags(flags) {
173
- const raw = normalizeText(flags.hub || 'all', 240).toLowerCase();
299
+ const raw = normalizeText(flags.hub || flags.to || 'all', 240).toLowerCase();
174
300
  if (!raw || raw === 'all' || raw === '*') {
175
301
  return [...DEFAULT_SETUP_HUBS];
176
302
  }
@@ -186,6 +312,103 @@ function setupHubsFromFlags(flags) {
186
312
  return unique.length > 0 ? unique : [...DEFAULT_SETUP_HUBS];
187
313
  }
188
314
 
315
+ function reviewProfileForHub(hub) {
316
+ const normalized = normalizeHub(hub) || 'generic';
317
+ return {
318
+ hub: normalized,
319
+ ...(PLATFORM_REVIEW_PROFILES[normalized] || PLATFORM_REVIEW_PROFILES.generic)
320
+ };
321
+ }
322
+
323
+ function isPublicDocFile(filePath) {
324
+ return /(^|\/)(SKILL|README|LICENSE|CHANGELOG|CONTRIBUTING)(\.[a-z0-9]+)?$/i.test(filePath) ||
325
+ /\.(md|mdx|txt)$/i.test(filePath);
326
+ }
327
+
328
+ function isLikelyEntrypointFile(filePath) {
329
+ return /(^|\/)(package\.json|requirements\.txt|pyproject\.toml|deno\.json|mcp\.json|manifest\.json|skill\.json|index\.(js|mjs|cjs|ts|tsx|py)|main\.(js|mjs|cjs|ts|tsx|py)|server\.(js|mjs|cjs|ts|tsx|py))$/i.test(filePath);
330
+ }
331
+
332
+ function packagePlanForHub(inspected, hub) {
333
+ const profile = reviewProfileForHub(hub);
334
+ const localOnly = [];
335
+ const candidateUploadFiles = [];
336
+ const forbiddenPatterns = [
337
+ '.env*',
338
+ `${WORK_DIR}/`,
339
+ '.agentpie/',
340
+ 'node_modules/',
341
+ 'dist/',
342
+ 'build/',
343
+ 'coverage/',
344
+ '*.pem',
345
+ '*.key',
346
+ '*secret*',
347
+ '*token*'
348
+ ];
349
+ for (const file of inspected.package.files) {
350
+ if (file.path.startsWith(`${WORK_DIR}/`) || file.path.startsWith('.agentpie/')) {
351
+ localOnly.push(file.path);
352
+ continue;
353
+ }
354
+ if (profile.packageMode === 'full_skill_package') {
355
+ candidateUploadFiles.push(file.path);
356
+ continue;
357
+ }
358
+ if (profile.packageMode === 'markdown_or_text_only') {
359
+ if (isPublicDocFile(file.path)) {
360
+ candidateUploadFiles.push(file.path);
361
+ }
362
+ continue;
363
+ }
364
+ if (profile.packageMode === 'local_agent_install') {
365
+ if (isPublicDocFile(file.path) || isLikelyEntrypointFile(file.path)) {
366
+ candidateUploadFiles.push(file.path);
367
+ }
368
+ continue;
369
+ }
370
+ if (profile.packageMode === 'static_prompt_or_api_tool') {
371
+ if (isPublicDocFile(file.path) || /openapi|swagger|tool|workflow|plugin/i.test(file.path)) {
372
+ candidateUploadFiles.push(file.path);
373
+ }
374
+ continue;
375
+ }
376
+ if (isPublicDocFile(file.path) || isLikelyEntrypointFile(file.path)) {
377
+ candidateUploadFiles.push(file.path);
378
+ }
379
+ }
380
+ return {
381
+ hub: profile.hub,
382
+ label: profile.label,
383
+ reviewProfile: profile,
384
+ dataSourcePolicy: {
385
+ runtime: 'Only real Skill execution callbacks, MCP/HTTP/API Tool calls, webhooks, or external Agent ack.',
386
+ attribution: 'Publish/upload workflow, public disclosure, and tracking-link clicks.',
387
+ reported: 'Manual import or public platform numbers such as downloads, installs, favorites, or displayed usage.'
388
+ },
389
+ candidateUploadFiles: Array.from(new Set(candidateUploadFiles)).sort(),
390
+ localOnlyFiles: Array.from(new Set([
391
+ ...localOnly,
392
+ `${WORK_DIR}/${MANIFEST_FILE}`,
393
+ `${WORK_DIR}/UPLOAD_HANDOFF.md`,
394
+ `${WORK_DIR}/runtime-events.js`,
395
+ `${WORK_DIR}/platform-profiles.json`
396
+ ])).sort(),
397
+ disclosureTargets: profile.disclosurePlacement,
398
+ forbiddenPatterns,
399
+ requiresUserConfirmation: true,
400
+ notes: [
401
+ profile.allowedFilePolicy,
402
+ profile.manifestPolicy === 'local_only'
403
+ ? `Keep ${WORK_DIR}/${MANIFEST_FILE} local. Do not upload it unless the target platform explicitly accepts registry JSON and the user confirms.`
404
+ : 'Confirm platform file rules before uploading auxiliary registry metadata.',
405
+ profile.runtimeDataPolicy === 'attribution_only_by_default'
406
+ ? 'Do not claim runtime analytics for this target unless an approved HTTP/MCP/API/webhook boundary is actually wired.'
407
+ : 'Runtime analytics require an explicit callback at the real invocation boundary.'
408
+ ]
409
+ };
410
+ }
411
+
189
412
  function canonicalSignaturePayload(payload) {
190
413
  return [
191
414
  payload.schemaVersion || EVENT_SCHEMA_VERSION,
@@ -390,20 +613,31 @@ async function writeManifest(root, flags) {
390
613
  ]
391
614
  }
392
615
  },
393
- platforms: Object.fromEntries(SUPPORTED_HUBS.map((hub) => [
394
- hub,
395
- {
616
+ platforms: Object.fromEntries(SUPPORTED_HUBS.map((hub) => {
617
+ const reviewProfile = reviewProfileForHub(hub);
618
+ return [
396
619
  hub,
397
- sourceSurface: hub,
398
- uploadPromptFile: `${WORK_DIR}/upload-${safeSkillKey(hub)}.md`,
399
- eventDefaults: {
400
- schemaVersion: EVENT_SCHEMA_VERSION,
620
+ {
401
621
  hub,
402
622
  sourceSurface: hub,
403
- skillKey: safeSkillKey(claim?.skillKey || inspected.skillKey)
623
+ uploadPromptFile: `${WORK_DIR}/upload-${safeSkillKey(hub)}.md`,
624
+ packagePlanFile: `${WORK_DIR}/package-${safeSkillKey(hub)}.json`,
625
+ reviewProfile,
626
+ dataSourcePolicy: {
627
+ default: reviewProfile.defaultDataSource,
628
+ runtime: 'Only real Skill execution callbacks, MCP/HTTP/API Tool calls, webhooks, or external Agent ack.',
629
+ attribution: 'Publish/upload workflow, public disclosure, and tracking-link clicks.',
630
+ reported: 'Manual import or public platform numbers such as downloads, installs, favorites, or displayed usage.'
631
+ },
632
+ eventDefaults: {
633
+ schemaVersion: EVENT_SCHEMA_VERSION,
634
+ hub,
635
+ sourceSurface: hub,
636
+ skillKey: safeSkillKey(claim?.skillKey || inspected.skillKey)
637
+ }
404
638
  }
405
- }
406
- ])),
639
+ ];
640
+ })),
407
641
  sourceFingerprint: {
408
642
  sha256: inspected.package.sha256,
409
643
  fileCount: inspected.package.fileCount,
@@ -422,6 +656,9 @@ async function writeManifest(root, flags) {
422
656
  }
423
657
 
424
658
  function hubInstruction(hub) {
659
+ if (hub === 'xiashe') {
660
+ return '虾舍商店发布是完整 Skill 包和 registry manifest 的主流程。请保留公开披露、归因和 runtime analytics 回传能力;如果本地 manifest 有 registry public token 或已有创作者认证 CLI/API 会话,就创建或复用虾舍商店草稿。';
661
+ }
425
662
  if (hub === 'red') {
426
663
  return 'Red Skill 的官方上传 prompt / 页面流程是主流程。请先从小红书创作服务平台获取或使用用户提供的 Red Skill 上传 prompt,再把下面的虾舍 registry 要求作为附加说明合并进去。';
427
664
  }
@@ -434,6 +671,15 @@ function hubInstruction(hub) {
434
671
  if (hub === 'claude') {
435
672
  return 'Claude Skills 的官方上传/导入流程是主流程。请先确认 Claude 当前 Skill 格式和共享要求,再把下面的 registry 披露文本放入允许的说明文件。';
436
673
  }
674
+ if (hub === 'codex') {
675
+ return 'Codex 的本地 Skill 或 Agent 能力安装流程是主流程。请把 registry disclosure 放进公开说明,并优先通过 MCP/HTTP ack 回传真实调用。';
676
+ }
677
+ if (hub === 'cursor') {
678
+ return 'Cursor 的本地 Agent 能力、规则或工具安装流程是主流程。请把 registry disclosure 放进公开说明,并优先通过 HTTP/MCP callback 回传真实调用。';
679
+ }
680
+ if (hub === 'workbuddy') {
681
+ return 'WorkBuddy 的 Agent 能力安装和发布流程是主流程。请按 WorkBuddy 当前规则放置 SKILL.md/工具说明,并通过 HTTP/MCP callback 回传真实调用。';
682
+ }
437
683
  if (hub === 'dify') {
438
684
  return 'Dify Plugin/Tool 的官方打包和发布流程是主流程。请先按 Dify 插件规范处理 manifest 和工具定义,再把下面的 registry 披露文本放入允许的 README/说明字段。';
439
685
  }
@@ -444,6 +690,9 @@ function hubInstruction(hub) {
444
690
  }
445
691
 
446
692
  function platformPromptPlaceholder(hub) {
693
+ if (hub === 'xiashe') {
694
+ return '如果用户要发布到虾舍商店,并且当前环境已有创作者认证 CLI/API 会话,请优先创建虾舍商店草稿;如果没有认证会话,请完成 registry setup、披露、平台 profile 和 handoff 文件,不要要求 Agent 宿主登录或索取账号凭证。不要把第三方平台限制套用到虾舍完整 Skill 包。';
695
+ }
447
696
  if (hub === 'red') {
448
697
  return '如果用户还没有提供 Red Skill 官方上传 prompt,请先请用户从小红书创作服务平台复制该 prompt;不要用虾舍 prompt 替代 Red Skill 官方 prompt。';
449
698
  }
@@ -456,6 +705,15 @@ function platformPromptPlaceholder(hub) {
456
705
  if (hub === 'claude') {
457
706
  return '如果 Claude 提供官方 Skill 导入/上传说明,请优先使用官方说明;不要用虾舍 prompt 替代 Claude 官方流程。';
458
707
  }
708
+ if (hub === 'codex') {
709
+ return '如果 Codex 提供官方 Skill 或 Agent 能力安装说明,请优先使用官方说明;不要用虾舍 prompt 替代 Codex 官方流程。';
710
+ }
711
+ if (hub === 'cursor') {
712
+ return '如果 Cursor 提供官方规则、扩展或 Agent 能力安装说明,请优先使用官方说明;不要用虾舍 prompt 替代 Cursor 官方流程。';
713
+ }
714
+ if (hub === 'workbuddy') {
715
+ return '如果 WorkBuddy 提供官方上传 prompt、网页或 API 流程,请优先使用官方流程;不要用虾舍 prompt 替代 WorkBuddy 官方流程。';
716
+ }
459
717
  if (hub === 'dify') {
460
718
  return '如果 Dify 提供官方插件打包/发布命令,请优先使用官方命令;不要用虾舍 prompt 替代 Dify 官方流程。';
461
719
  }
@@ -467,19 +725,29 @@ function platformPromptPlaceholder(hub) {
467
725
 
468
726
  function uploadCompatibilityLines(hub, manifestFile) {
469
727
  const localManifest = `${WORK_DIR}/${manifestFile}`;
728
+ if (hub === 'xiashe') {
729
+ return [
730
+ '虾舍商店兼容性要求:',
731
+ `- 虾舍商店可以接收完整 Skill 包和 ${localManifest} registry manifest。`,
732
+ '- 保留公开披露文本、归因链接和 runtime event callback;不要删除 registry manifest。',
733
+ '- 提交草稿前仍需排除 .env、密钥、node_modules、dist/build 和无关本地文件。'
734
+ ];
735
+ }
470
736
  if (hub === 'red') {
471
737
  return [
472
738
  'Red Skill 兼容性要求:',
473
739
  '- Red Skill 如果只接受 Markdown/TXT,请不要把 JSON manifest 当作源码文件上传。',
474
740
  `- ${localManifest} 保留在用户本地用于虾舍 registry claim 和 analytics;只有在 Red Skill 官方流程明确允许附加 JSON 文件时才上传。`,
475
- '- 如需在 Red Skill 页面披露 registry,请把下面的“公开披露文本”写入 Skill 介绍、README 或平台允许的 Markdown/TXT 字段。'
741
+ '- 如需在 Red Skill 页面披露 registry,请把下面的“公开披露文本”写入 Skill 介绍、README 或平台允许的 Markdown/TXT 字段。',
742
+ '- 不要把 public token、signing secret、runtime-events.js 或 .xiashe 内部 handoff/checklist 文件发布到 Markdown/TXT-only 平台。'
476
743
  ];
477
744
  }
478
745
  return [
479
746
  '第三方平台兼容性要求:',
480
747
  `- 如果目标平台不接受 ${localManifest} 这类 JSON manifest,请不要强行上传该文件。`,
481
748
  '- 保留本地 registry manifest,并把“公开披露文本”写入平台允许的说明字段、README、SKILL.md 或 Markdown/TXT 附件。',
482
- '- 如果平台支持额外 manifest / metadata 文件,可以在提交前向用户确认是否一并上传。'
749
+ '- 如果平台支持额外 manifest / metadata 文件,可以在提交前向用户确认是否一并上传。',
750
+ '- 不要把 public token、signing secret、runtime-events.js 或内部 handoff/checklist 文件发布到不支持运行时回调的平台。'
483
751
  ];
484
752
  }
485
753
 
@@ -504,7 +772,8 @@ function publicDisclosure(inspected, registryUrl) {
504
772
  function skillMdRegistryBlock(inspected, hub) {
505
773
  const registry = inspected.registry || {};
506
774
  const registryUrl = registry.registryUrl || DEFAULT_REGISTRY_URL;
507
- const publicToken = registry.publicToken || '<public token from registry manifest>';
775
+ const publicSkillId = registry.skillId || registry.publicSkillId || inspected.registry?.skillId || '<public registry skill id>';
776
+ const publicTokenHint = registry.publicToken ? `${String(registry.publicToken).slice(0, 4)}...${String(registry.publicToken).slice(-4)}` : '<stored in local registry manifest>';
508
777
  const marker = `${REGISTRY_PROVIDER}-registry`;
509
778
  return [
510
779
  `<!-- ${marker}:start -->`,
@@ -516,12 +785,14 @@ function skillMdRegistryBlock(inspected, hub) {
516
785
  `- Skill key: ${inspected.skillKey}`,
517
786
  `- Target hub: ${hub}`,
518
787
  `- Registry event endpoint: ${registryUrl}`,
519
- `- Public write token: ${publicToken}`,
788
+ `- Public Skill ID: ${publicSkillId}`,
789
+ `- Public token location: local ${WORK_DIR}/${MANIFEST_FILE} only (${publicTokenHint})`,
520
790
  `- Event schema version: ${registry.eventSchemaVersion || EVENT_SCHEMA_VERSION}`,
521
791
  `- Source fingerprint: ${inspected.package.sha256}`,
522
792
  '',
523
793
  'Analytics boundary:',
524
- '- The public token can only submit allowed aggregate analytics events. It cannot read creator data or administer the account.',
794
+ '- The local public token can only submit allowed aggregate analytics events. It cannot read creator data or administer the account.',
795
+ '- Do not publish the local public token in Markdown-only hubs. Keep it in the local manifest or runtime configuration.',
525
796
  '- Events should include only public metadata: hub, sourceSurface, campaign, scenario, anonymous installationId, invocationId, and status.',
526
797
  '- Do not send user prompts, private files, credentials, environment variables, account tokens, raw business content, or personal identifiers.',
527
798
  '- The creator can rotate/revoke the token or disable analytics from the dashboard.',
@@ -566,6 +837,8 @@ async function readPlatformPrompt(flags) {
566
837
 
567
838
  async function uploadPrompt(inspected, flags) {
568
839
  const hub = normalizeHub(flags.hub || 'generic') || 'generic';
840
+ const reviewProfile = reviewProfileForHub(hub);
841
+ const packagePlan = packagePlanForHub(inspected, hub);
569
842
  const sourceUrl = normalizeText(flags['source-url'] || flags['package-url'], 1000);
570
843
  const platformCommand = normalizeText(flags['platform-command'], 1000);
571
844
  const platformPrompt = await readPlatformPrompt(flags);
@@ -625,6 +898,31 @@ async function uploadPrompt(inspected, flags) {
625
898
  '- 最终上传文件、目录结构、压缩包、表单字段和上线版本,必须由你按照目标 Skill 平台的官方要求处理。',
626
899
  '- 如果目标平台要求重新整理源码、生成 markdown、压缩包或填写表单,请你在用户授权范围内完成,并在提交前向用户展示将上传的文件和字段。',
627
900
  '',
901
+ '平台审核 profile:',
902
+ '```json',
903
+ JSON.stringify(reviewProfile, null, 2),
904
+ '```',
905
+ '',
906
+ '平台包清单 plan:',
907
+ '```json',
908
+ JSON.stringify({
909
+ hub: packagePlan.hub,
910
+ packageMode: packagePlan.reviewProfile.packageMode,
911
+ candidateUploadFiles: packagePlan.candidateUploadFiles,
912
+ localOnlyFiles: packagePlan.localOnlyFiles,
913
+ disclosureTargets: packagePlan.disclosureTargets,
914
+ forbiddenPatterns: packagePlan.forbiddenPatterns,
915
+ dataSourcePolicy: packagePlan.dataSourcePolicy,
916
+ notes: packagePlan.notes
917
+ }, null, 2),
918
+ '```',
919
+ '',
920
+ '数据口径必须分开:',
921
+ '- runtime:只有真实 Skill 执行 callback、MCP/HTTP/API Tool、webhook 或外部 Agent ack 才能计入。',
922
+ '- attribution:发布、上传、归因链接点击、公开披露访问,只代表来源漏斗。',
923
+ '- reported:平台公开数字或手动导入,例如收藏、下载、安装、静态平台显示使用数。',
924
+ '- 如果无法确认 runtime callback 已经接在真实调用边界,就把该平台标记为 attribution 或 reported,不要伪造成 runtime。',
925
+ '',
628
926
  ...compatibilityLines,
629
927
  '',
630
928
  ...disclosureLines,
@@ -682,6 +980,9 @@ async function writeUnifiedHandoff(root, args) {
682
980
  const promptIndex = args.promptResults
683
981
  .map((item) => `- ${item.hub}: ${path.relative(root, item.promptPath)}`)
684
982
  .join('\n');
983
+ const profileIndex = args.packagePlans
984
+ .map((item) => `- ${item.hub}: ${path.relative(root, item.planPath)} (${item.plan.reviewProfile.packageMode}, ${item.plan.reviewProfile.defaultDataSource})`)
985
+ .join('\n');
685
986
  const registry = args.inspected.registry || {};
686
987
  const registryUrl = registry.registryUrl || DEFAULT_REGISTRY_URL;
687
988
  const content = [
@@ -710,6 +1011,14 @@ async function writeUnifiedHandoff(root, args) {
710
1011
  '',
711
1012
  promptIndex,
712
1013
  '',
1014
+ '## Platform review profiles',
1015
+ '',
1016
+ `Structured profile index: ${relativeOutDir}/platform-profiles.json`,
1017
+ '',
1018
+ profileIndex,
1019
+ '',
1020
+ 'Use each package-<hub>.json as the upload allowlist/review checklist. Files listed as localOnlyFiles must not be uploaded to restrictive platforms unless the platform explicitly accepts them and the user confirms.',
1021
+ '',
713
1022
  '## Official prompt merge rule',
714
1023
  '',
715
1024
  'If the user gives a Red Skill / ClawHub / SkillHub / Claude / Dify / Coze official prompt, do not replace it. Append these registry requirements as constraints, and follow the official prompt for packaging, file format, and upload steps.',
@@ -758,6 +1067,14 @@ async function writeUnifiedHandoff(root, args) {
758
1067
  '```',
759
1068
  '',
760
1069
  'If the hub cannot run callbacks, keep upload attribution and use campaign links or manual imports. Do not fake runtime events.',
1070
+ '',
1071
+ '## XiaShe Store draft auth',
1072
+ '',
1073
+ `For XiaShe Store, first check whether a submission already exists for this registry. ${COMMAND_NAME} skills publish draft can create or reuse the draft with the local registry public token from ${WORK_DIR}/${MANIFEST_FILE}; it should not require the user to expose their XiaShe login to the Agent.`,
1074
+ '',
1075
+ 'If the registry token is missing or rejected, stop and ask the user to run `xiashe login` locally or generate a new Add Skill task. Do not upload the package as another creator.',
1076
+ '',
1077
+ 'If the claim code expires while you are working, ask for a new code and reuse the existing scan, package plans, disclosure text, and review results instead of starting over.',
761
1078
  ''
762
1079
  ].join('\n');
763
1080
  const handoffPath = path.join(args.outDir, HANDOFF_FILE);
@@ -1120,15 +1437,27 @@ async function setupAgentWorkflow(root, flags) {
1120
1437
  });
1121
1438
 
1122
1439
  const promptResults = [];
1440
+ const packagePlans = [];
1123
1441
  for (const hub of hubs) {
1124
1442
  const promptPath = path.join(outDir, `upload-${safeSkillKey(hub)}.md`);
1125
1443
  const promptResult = await writePrompt(absoluteRoot, { ...flags, hub, out: promptPath });
1444
+ const inspectedForPlan = await inspectSkill(absoluteRoot, flags);
1445
+ const plan = packagePlanForHub(inspectedForPlan, hub);
1446
+ const planPath = path.join(outDir, `package-${safeSkillKey(hub)}.json`);
1447
+ await writeFile(planPath, `${JSON.stringify(plan, null, 2)}\n`, { mode: 0o600 });
1126
1448
  promptResults.push({
1127
1449
  hub,
1128
1450
  promptPath: typeof promptResult === 'string' ? promptPath : promptResult.outPath
1129
1451
  });
1452
+ packagePlans.push({ hub, planPath, plan });
1130
1453
  }
1131
1454
  const inspectedAfterManifest = await inspectSkill(absoluteRoot, flags);
1455
+ const profilesPath = path.join(outDir, 'platform-profiles.json');
1456
+ await writeFile(
1457
+ profilesPath,
1458
+ `${JSON.stringify(Object.fromEntries(hubs.map((hub) => [hub, reviewProfileForHub(hub)])), null, 2)}\n`,
1459
+ { mode: 0o600 }
1460
+ );
1132
1461
  const shouldEmbedSkillMd = Boolean(flags['embed-skill-md']) || (hubs.includes('red') && !flags['no-skill-md']);
1133
1462
  const skillMdPath = shouldEmbedSkillMd
1134
1463
  ? await writeSkillMdRegistryBlock(absoluteRoot, inspectedAfterManifest, hubs.join(','))
@@ -1154,7 +1483,8 @@ async function setupAgentWorkflow(root, flags) {
1154
1483
  outDir,
1155
1484
  hubs,
1156
1485
  inspected: inspectedAfterManifest,
1157
- promptResults
1486
+ promptResults,
1487
+ packagePlans
1158
1488
  });
1159
1489
 
1160
1490
  const warnings = [];
@@ -1184,6 +1514,8 @@ async function setupAgentWorkflow(root, flags) {
1184
1514
  manifestPath: manifestResult.manifestPath,
1185
1515
  handoffPath,
1186
1516
  promptPaths: promptResults,
1517
+ packagePlanPaths: packagePlans.map((item) => ({ hub: item.hub, planPath: item.planPath })),
1518
+ profilesPath,
1187
1519
  skillMdPath,
1188
1520
  disclosurePath,
1189
1521
  snippetPath: snippetResult ? snippetResult.outPath : null,
@@ -1191,6 +1523,7 @@ async function setupAgentWorkflow(root, flags) {
1191
1523
  warnings,
1192
1524
  next: [
1193
1525
  `Use ${path.relative(absoluteRoot, handoffPath)} as the single Agent handoff. The Agent should not ask the user to manually pick registry files.`,
1526
+ `Use ${path.relative(absoluteRoot, profilesPath)} and package-<hub>.json as the platform review profiles and upload allowlists.`,
1194
1527
  'When the user later pastes a Red Skill / ClawHub / SkillHub / Claude / Dify / Coze official prompt, the Agent should identify the platform and merge the matching registry checklist internally.',
1195
1528
  'Each prepared upload-<hub>.md pins the correct hub/sourceSurface value so platform analytics stay separated, but those files are internal checklists for the Agent.',
1196
1529
  skillMdPath
@@ -1240,6 +1573,37 @@ async function main() {
1240
1573
  print(flags.json ? result : formatVerify(result), flags.json);
1241
1574
  return;
1242
1575
  }
1576
+ if (command === 'publish') {
1577
+ const result = await setupAgentWorkflow(root, flags);
1578
+ if (flags.json) {
1579
+ print({ ok: true, mode: 'xiashe-publish', ...result }, true);
1580
+ } else {
1581
+ const lines = [
1582
+ `${PRODUCT_NAME} publish handoff prepared.`,
1583
+ `Targets: ${result.hubs.join(', ')}`,
1584
+ `Manifest: ${result.manifestPath}`,
1585
+ `Agent handoff: ${result.handoffPath}`,
1586
+ `Platform profiles: ${result.profilesPath}`,
1587
+ ...result.packagePlanPaths.map((item) => `Package plan (${item.hub}): ${item.planPath}`),
1588
+ result.skillMdPath ? `Updated: ${result.skillMdPath}` : null,
1589
+ `Disclosure: ${result.disclosurePath}`,
1590
+ result.snippetPath ? `Runtime snippet: ${result.snippetPath}` : null,
1591
+ ...result.warnings.map((warning) => `Warning: ${warning}`),
1592
+ '',
1593
+ 'Next:',
1594
+ '- Treat the target platform official upload flow as authoritative.',
1595
+ '- Use the handoff file as the only XiaShe registry checklist.',
1596
+ '- Before uploading, check whether this Skill already has a draft, submitted review, or published listing for the selected target; skip duplicate uploads and verify/update the existing record when possible.',
1597
+ '- Check upload readiness and runtime readiness separately before submission.',
1598
+ '- For XiaShe Store, create or update the store draft only when an authenticated creator CLI/API session is available.',
1599
+ '- If no authenticated creator session is available, finish registry setup and handoff files without asking the Agent host for account credentials.',
1600
+ '- For Markdown/TXT-only platforms, copy the disclosure text into an allowed public field instead of uploading registry JSON.',
1601
+ '- Only count runtime usage when the Skill or Agent callback sends real runtime events.'
1602
+ ].filter(Boolean);
1603
+ print(lines.join('\n'), false);
1604
+ }
1605
+ return;
1606
+ }
1243
1607
  if (command === 'setup' || command === 'handoff') {
1244
1608
  const result = await setupAgentWorkflow(root, flags);
1245
1609
  if (flags.json) {
@@ -1249,6 +1613,8 @@ async function main() {
1249
1613
  `Wrote ${result.manifestPath}`,
1250
1614
  `Wrote ${result.handoffPath}`,
1251
1615
  ...result.promptPaths.map((item) => `Wrote ${item.promptPath}`),
1616
+ `Wrote ${result.profilesPath}`,
1617
+ ...result.packagePlanPaths.map((item) => `Wrote ${item.planPath}`),
1252
1618
  `Wrote ${result.disclosurePath}`,
1253
1619
  result.snippetPath ? `Wrote ${result.snippetPath}` : null,
1254
1620
  ...result.warnings.map((warning) => `Warning: ${warning}`),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiashe/skill",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "xiashe-skill": "bin/xiashe-skill.mjs"