@xiashe/skill 0.1.7 → 0.1.8
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 +6 -2
- package/bin/xiashe-skill.mjs +377 -22
- package/package.json +1 -1
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
|
|
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
|
|
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
|
package/bin/xiashe-skill.mjs
CHANGED
|
@@ -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}
|
|
51
|
-
${COMMAND_NAME} setup [skill-dir] --
|
|
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
|
-
|
|
398
|
-
uploadPromptFile: `${WORK_DIR}/upload-${safeSkillKey(hub)}.md`,
|
|
399
|
-
eventDefaults: {
|
|
400
|
-
schemaVersion: EVENT_SCHEMA_VERSION,
|
|
620
|
+
{
|
|
401
621
|
hub,
|
|
402
622
|
sourceSurface: hub,
|
|
403
|
-
|
|
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 回传能力。';
|
|
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 '如果用户要发布到虾舍商店,请优先创建虾舍商店草稿;不要把第三方平台限制套用到虾舍完整 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
|
|
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
|
|
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.',
|
|
@@ -1120,15 +1429,27 @@ async function setupAgentWorkflow(root, flags) {
|
|
|
1120
1429
|
});
|
|
1121
1430
|
|
|
1122
1431
|
const promptResults = [];
|
|
1432
|
+
const packagePlans = [];
|
|
1123
1433
|
for (const hub of hubs) {
|
|
1124
1434
|
const promptPath = path.join(outDir, `upload-${safeSkillKey(hub)}.md`);
|
|
1125
1435
|
const promptResult = await writePrompt(absoluteRoot, { ...flags, hub, out: promptPath });
|
|
1436
|
+
const inspectedForPlan = await inspectSkill(absoluteRoot, flags);
|
|
1437
|
+
const plan = packagePlanForHub(inspectedForPlan, hub);
|
|
1438
|
+
const planPath = path.join(outDir, `package-${safeSkillKey(hub)}.json`);
|
|
1439
|
+
await writeFile(planPath, `${JSON.stringify(plan, null, 2)}\n`, { mode: 0o600 });
|
|
1126
1440
|
promptResults.push({
|
|
1127
1441
|
hub,
|
|
1128
1442
|
promptPath: typeof promptResult === 'string' ? promptPath : promptResult.outPath
|
|
1129
1443
|
});
|
|
1444
|
+
packagePlans.push({ hub, planPath, plan });
|
|
1130
1445
|
}
|
|
1131
1446
|
const inspectedAfterManifest = await inspectSkill(absoluteRoot, flags);
|
|
1447
|
+
const profilesPath = path.join(outDir, 'platform-profiles.json');
|
|
1448
|
+
await writeFile(
|
|
1449
|
+
profilesPath,
|
|
1450
|
+
`${JSON.stringify(Object.fromEntries(hubs.map((hub) => [hub, reviewProfileForHub(hub)])), null, 2)}\n`,
|
|
1451
|
+
{ mode: 0o600 }
|
|
1452
|
+
);
|
|
1132
1453
|
const shouldEmbedSkillMd = Boolean(flags['embed-skill-md']) || (hubs.includes('red') && !flags['no-skill-md']);
|
|
1133
1454
|
const skillMdPath = shouldEmbedSkillMd
|
|
1134
1455
|
? await writeSkillMdRegistryBlock(absoluteRoot, inspectedAfterManifest, hubs.join(','))
|
|
@@ -1154,7 +1475,8 @@ async function setupAgentWorkflow(root, flags) {
|
|
|
1154
1475
|
outDir,
|
|
1155
1476
|
hubs,
|
|
1156
1477
|
inspected: inspectedAfterManifest,
|
|
1157
|
-
promptResults
|
|
1478
|
+
promptResults,
|
|
1479
|
+
packagePlans
|
|
1158
1480
|
});
|
|
1159
1481
|
|
|
1160
1482
|
const warnings = [];
|
|
@@ -1184,6 +1506,8 @@ async function setupAgentWorkflow(root, flags) {
|
|
|
1184
1506
|
manifestPath: manifestResult.manifestPath,
|
|
1185
1507
|
handoffPath,
|
|
1186
1508
|
promptPaths: promptResults,
|
|
1509
|
+
packagePlanPaths: packagePlans.map((item) => ({ hub: item.hub, planPath: item.planPath })),
|
|
1510
|
+
profilesPath,
|
|
1187
1511
|
skillMdPath,
|
|
1188
1512
|
disclosurePath,
|
|
1189
1513
|
snippetPath: snippetResult ? snippetResult.outPath : null,
|
|
@@ -1191,6 +1515,7 @@ async function setupAgentWorkflow(root, flags) {
|
|
|
1191
1515
|
warnings,
|
|
1192
1516
|
next: [
|
|
1193
1517
|
`Use ${path.relative(absoluteRoot, handoffPath)} as the single Agent handoff. The Agent should not ask the user to manually pick registry files.`,
|
|
1518
|
+
`Use ${path.relative(absoluteRoot, profilesPath)} and package-<hub>.json as the platform review profiles and upload allowlists.`,
|
|
1194
1519
|
'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
1520
|
'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
1521
|
skillMdPath
|
|
@@ -1240,6 +1565,34 @@ async function main() {
|
|
|
1240
1565
|
print(flags.json ? result : formatVerify(result), flags.json);
|
|
1241
1566
|
return;
|
|
1242
1567
|
}
|
|
1568
|
+
if (command === 'publish') {
|
|
1569
|
+
const result = await setupAgentWorkflow(root, flags);
|
|
1570
|
+
if (flags.json) {
|
|
1571
|
+
print({ ok: true, mode: 'xiashe-publish', ...result }, true);
|
|
1572
|
+
} else {
|
|
1573
|
+
const lines = [
|
|
1574
|
+
`${PRODUCT_NAME} publish handoff prepared.`,
|
|
1575
|
+
`Targets: ${result.hubs.join(', ')}`,
|
|
1576
|
+
`Manifest: ${result.manifestPath}`,
|
|
1577
|
+
`Agent handoff: ${result.handoffPath}`,
|
|
1578
|
+
`Platform profiles: ${result.profilesPath}`,
|
|
1579
|
+
...result.packagePlanPaths.map((item) => `Package plan (${item.hub}): ${item.planPath}`),
|
|
1580
|
+
result.skillMdPath ? `Updated: ${result.skillMdPath}` : null,
|
|
1581
|
+
`Disclosure: ${result.disclosurePath}`,
|
|
1582
|
+
result.snippetPath ? `Runtime snippet: ${result.snippetPath}` : null,
|
|
1583
|
+
...result.warnings.map((warning) => `Warning: ${warning}`),
|
|
1584
|
+
'',
|
|
1585
|
+
'Next:',
|
|
1586
|
+
'- Treat the target platform official upload flow as authoritative.',
|
|
1587
|
+
'- Use the handoff file as the only XiaShe registry checklist.',
|
|
1588
|
+
'- For XiaShe Store, create or update the store draft after the manifest is written.',
|
|
1589
|
+
'- For Markdown/TXT-only platforms, copy the disclosure text into an allowed public field instead of uploading registry JSON.',
|
|
1590
|
+
'- Only count runtime usage when the Skill or Agent callback sends real runtime events.'
|
|
1591
|
+
].filter(Boolean);
|
|
1592
|
+
print(lines.join('\n'), false);
|
|
1593
|
+
}
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1243
1596
|
if (command === 'setup' || command === 'handoff') {
|
|
1244
1597
|
const result = await setupAgentWorkflow(root, flags);
|
|
1245
1598
|
if (flags.json) {
|
|
@@ -1249,6 +1602,8 @@ async function main() {
|
|
|
1249
1602
|
`Wrote ${result.manifestPath}`,
|
|
1250
1603
|
`Wrote ${result.handoffPath}`,
|
|
1251
1604
|
...result.promptPaths.map((item) => `Wrote ${item.promptPath}`),
|
|
1605
|
+
`Wrote ${result.profilesPath}`,
|
|
1606
|
+
...result.packagePlanPaths.map((item) => `Wrote ${item.planPath}`),
|
|
1252
1607
|
`Wrote ${result.disclosurePath}`,
|
|
1253
1608
|
result.snippetPath ? `Wrote ${result.snippetPath}` : null,
|
|
1254
1609
|
...result.warnings.map((warning) => `Warning: ${warning}`),
|