@xiashe/skill 0.1.1 → 0.1.4

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.
Files changed (2) hide show
  1. package/bin/xiashe-skill.mjs +187 -27
  2. package/package.json +1 -1
@@ -1,24 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { createHash } from 'node:crypto';
3
+ import { createHash, createHmac } from 'node:crypto';
4
4
  import { existsSync } from 'node:fs';
5
5
  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.1';
9
+ const VERSION = '0.1.4';
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');
13
13
  const DEFAULT_BASE_URL = process.env.XIASHE_SKILL_DEFAULT_BASE_URL || (COMMAND_NAME === 'agentpie-skill'
14
- ? 'https://api.agentpie.app'
15
- : 'https://api.xiashe.chat');
14
+ ? 'https://actions.agentpie.app'
15
+ : 'https://actions.xiashe.chat');
16
16
  const MANIFEST_FILE = process.env.XIASHE_SKILL_MANIFEST_FILE || (COMMAND_NAME === 'agentpie-skill'
17
17
  ? 'agentpie.skill.json'
18
18
  : 'xiashe.skill.json');
19
19
  const WORK_DIR = process.env.XIASHE_SKILL_WORK_DIR || (COMMAND_NAME === 'agentpie-skill' ? '.agentpie' : '.xiashe');
20
20
  const DEFAULT_REGISTRY_URL = process.env.XIASHE_SKILL_REGISTRY_URL || `${DEFAULT_BASE_URL}/registry/skill-events`;
21
21
  const DEFAULT_CLAIM_URL = process.env.XIASHE_SKILL_CLAIM_URL || `${DEFAULT_BASE_URL}/registry/skill/claim`;
22
+ const EVENT_SCHEMA_VERSION = process.env.XIASHE_SKILL_EVENT_SCHEMA_VERSION || (REGISTRY_PROVIDER === 'agentpie'
23
+ ? 'agentpie.skill.event.v1'
24
+ : 'xiashe.skill.event.v1');
22
25
  const DEFAULT_IGNORE = new Set([
23
26
  '.git',
24
27
  '.xiashe',
@@ -41,11 +44,11 @@ function usage() {
41
44
 
42
45
  Usage:
43
46
  ${COMMAND_NAME} inspect [skill-dir] [--json]
44
- ${COMMAND_NAME} setup [skill-dir] --code <dynamic-code> --hub <red|clawhub|skillhub|generic>
45
- ${COMMAND_NAME} setup [skill-dir] --public-token <token> --skill-id <id> --hub <red|clawhub|skillhub|generic>
47
+ ${COMMAND_NAME} setup [skill-dir] --code <dynamic-code> --hub <red|clawhub|skillhub|claude|dify|generic>
48
+ ${COMMAND_NAME} setup [skill-dir] --public-token <token> --skill-id <id> --hub <red|clawhub|skillhub|claude|dify|generic>
46
49
  ${COMMAND_NAME} attach [skill-dir] --code <dynamic-code> [--name <name>]
47
50
  ${COMMAND_NAME} attach [skill-dir] --public-token <token> [--skill-id <id>] [--name <name>]
48
- ${COMMAND_NAME} prompt [skill-dir] --hub <red|clawhub|skillhub|generic> [--source-url <url>] [--out <file>]
51
+ ${COMMAND_NAME} prompt [skill-dir] --hub <red|clawhub|skillhub|claude|dify|generic> [--source-url <url>] [--out <file>]
49
52
  ${COMMAND_NAME} snippet [skill-dir] [--target js|curl] [--out <file>]
50
53
  ${COMMAND_NAME} track [skill-dir] --event <event> [--hub <hub>] [--installation-id <id>] [--invocation-id <id>]
51
54
 
@@ -57,7 +60,7 @@ Options:
57
60
  --registry-url <url> Event endpoint written to the manifest.
58
61
  --name <name> Skill display name override.
59
62
  --description <text> Skill description override.
60
- --hub <hub> Target Skill hub prompt style: red, clawhub, skillhub, or generic.
63
+ --hub <hub> Target Skill hub prompt style: red, clawhub, skillhub, claude, dify, or generic.
61
64
  --event <event> Runtime event to submit for testing.
62
65
  --installation-id <id> Stable anonymous install id for runtime analytics.
63
66
  --invocation-id <id> Unique invocation id for one skill call.
@@ -67,10 +70,13 @@ Options:
67
70
  --platform-prompt-file <p> Official third-party hub prompt file to embed in the handoff.
68
71
  --platform-command <cmd> Official third-party hub CLI/upload command to include.
69
72
  --manifest-path <path> Registry manifest output path. Defaults to ${MANIFEST_FILE} for attach, ${WORK_DIR}/${MANIFEST_FILE} for setup.
73
+ --signing-secret <secret> Optional HMAC secret for signed runtime events.
70
74
  --source-url <url> User-provided source URL an Agent can use during third-party upload.
71
75
  --package-url <url> Deprecated alias for --source-url.
72
76
  --out <path> Output prompt or snippet file.
73
77
  --out-dir <path> Output directory for setup artifacts. Defaults to ${WORK_DIR}/.
78
+ --embed-skill-md Also write a clearly marked registry section into SKILL.md.
79
+ --no-skill-md Do not write registry text into SKILL.md. Red hub embeds by default.
74
80
  --no-snippet Setup should skip writing the runtime analytics snippet.
75
81
  --no-track Setup should skip sending upload_prompt_generated.
76
82
  --dry-run Print planned event payloads without network writes.
@@ -87,7 +93,7 @@ function fail(message, code = 1) {
87
93
 
88
94
  function parseArgs(argv) {
89
95
  const args = { _: [], json: false, dryRun: false };
90
- const booleanFlags = new Set(['no-snippet', 'no-track']);
96
+ const booleanFlags = new Set(['embed-skill-md', 'no-skill-md', 'no-snippet', 'no-track']);
91
97
  for (let i = 0; i < argv.length; i += 1) {
92
98
  const value = argv[i];
93
99
  if (value === '--help' || value === '-h') {
@@ -144,6 +150,26 @@ function safeSkillKey(value) {
144
150
  .replace(/^-+|-+$/g, '') || 'skill';
145
151
  }
146
152
 
153
+ function canonicalSignaturePayload(payload) {
154
+ return [
155
+ payload.schemaVersion || EVENT_SCHEMA_VERSION,
156
+ payload.eventType || '',
157
+ payload.idempotencyKey || '',
158
+ Number.isFinite(payload.occurredAt) ? String(Math.floor(payload.occurredAt)) : '',
159
+ payload.installationId || '',
160
+ payload.invocationId || '',
161
+ payload.hub || '',
162
+ payload.skillKey || ''
163
+ ].join('\n');
164
+ }
165
+
166
+ function signEventPayload(payload, signingSecret) {
167
+ if (!signingSecret) return undefined;
168
+ return createHmac('sha256', signingSecret)
169
+ .update(canonicalSignaturePayload(payload))
170
+ .digest('hex');
171
+ }
172
+
147
173
  async function readJsonFile(filePath) {
148
174
  if (!existsSync(filePath)) return null;
149
175
  try {
@@ -307,6 +333,11 @@ async function writeManifest(root, flags) {
307
333
  skillId: normalizeText(flags['skill-id'] || claim?.skillId, 160) || null,
308
334
  publicToken,
309
335
  registryUrl: normalizeText(flags['registry-url'] || claim?.registryUrl, 800) || DEFAULT_REGISTRY_URL,
336
+ eventSchemaVersion: EVENT_SCHEMA_VERSION,
337
+ signing: {
338
+ mode: normalizeText(flags['signing-secret'], 20) ? 'optional' : 'off',
339
+ secretEnv: `${REGISTRY_PROVIDER.toUpperCase()}_SKILL_SIGNING_SECRET`
340
+ },
310
341
  analytics: {
311
342
  enabled: true,
312
343
  allowedEvents: [
@@ -349,6 +380,12 @@ function hubInstruction(hub) {
349
380
  if (hub === 'skillhub') {
350
381
  return 'SkillHub 的官方 CLI、prompt 或页面流程是主流程。请先确认 SkillHub 当前要求,再把下面的虾舍 registry 要求作为附加说明合并进去。';
351
382
  }
383
+ if (hub === 'claude') {
384
+ return 'Claude Skills 的官方上传/导入流程是主流程。请先确认 Claude 当前 Skill 格式和共享要求,再把下面的 registry 披露文本放入允许的说明文件。';
385
+ }
386
+ if (hub === 'dify') {
387
+ return 'Dify Plugin/Tool 的官方打包和发布流程是主流程。请先按 Dify 插件规范处理 manifest 和工具定义,再把下面的 registry 披露文本放入允许的 README/说明字段。';
388
+ }
352
389
  return '目标 Skill Hub 的官方 CLI、prompt 或页面流程是主流程。请先确认平台当前要求,再把下面的虾舍 registry 要求作为附加说明合并进去。';
353
390
  }
354
391
 
@@ -362,6 +399,12 @@ function platformPromptPlaceholder(hub) {
362
399
  if (hub === 'skillhub') {
363
400
  return '如果 SkillHub 提供官方 CLI 命令或上传 prompt,请优先使用官方命令/prompt;不要用虾舍 prompt 替代 SkillHub 官方流程。';
364
401
  }
402
+ if (hub === 'claude') {
403
+ return '如果 Claude 提供官方 Skill 导入/上传说明,请优先使用官方说明;不要用虾舍 prompt 替代 Claude 官方流程。';
404
+ }
405
+ if (hub === 'dify') {
406
+ return '如果 Dify 提供官方插件打包/发布命令,请优先使用官方命令;不要用虾舍 prompt 替代 Dify 官方流程。';
407
+ }
365
408
  return '如果目标平台提供官方 CLI 命令或上传 prompt,请优先使用官方命令/prompt;不要用虾舍 prompt 替代平台官方流程。';
366
409
  }
367
410
 
@@ -401,6 +444,55 @@ function publicDisclosure(inspected, registryUrl) {
401
444
  ];
402
445
  }
403
446
 
447
+ function skillMdRegistryBlock(inspected, hub) {
448
+ const registry = inspected.registry || {};
449
+ const registryUrl = registry.registryUrl || DEFAULT_REGISTRY_URL;
450
+ const publicToken = registry.publicToken || '<public token from registry manifest>';
451
+ const marker = `${REGISTRY_PROVIDER}-registry`;
452
+ return [
453
+ `<!-- ${marker}:start -->`,
454
+ `## ${PRODUCT_NAME} Registry / Analytics`,
455
+ '',
456
+ `This Skill is explicitly registered by its creator with ${PRODUCT_NAME} for public attribution and aggregated analytics.`,
457
+ '',
458
+ `- Registry provider: ${REGISTRY_PROVIDER}`,
459
+ `- Skill key: ${inspected.skillKey}`,
460
+ `- Target hub: ${hub}`,
461
+ `- Registry event endpoint: ${registryUrl}`,
462
+ `- Public write token: ${publicToken}`,
463
+ `- Event schema version: ${registry.eventSchemaVersion || EVENT_SCHEMA_VERSION}`,
464
+ `- Source fingerprint: ${inspected.package.sha256}`,
465
+ '',
466
+ 'Analytics boundary:',
467
+ '- The public token can only submit allowed aggregate analytics events. It cannot read creator data or administer the account.',
468
+ '- Events should include only public metadata: hub, sourceSurface, campaign, scenario, anonymous installationId, invocationId, and status.',
469
+ '- Do not send user prompts, private files, credentials, environment variables, account tokens, raw business content, or personal identifiers.',
470
+ '- The creator can rotate/revoke the token or disable analytics from the dashboard.',
471
+ '',
472
+ 'Recommended runtime events, if the target platform supports callbacks:',
473
+ '- `runtime_install_seen` when a stable anonymous installation is first observed.',
474
+ '- `skill_invoked` when a Skill invocation starts.',
475
+ '- `skill_completed` when an invocation completes.',
476
+ '- `skill_failed` when an invocation fails.',
477
+ '',
478
+ `<!-- ${marker}:end -->`
479
+ ].join('\n');
480
+ }
481
+
482
+ async function writeSkillMdRegistryBlock(root, inspected, hub) {
483
+ const skillMdPath = path.join(root, 'SKILL.md');
484
+ const existing = existsSync(skillMdPath)
485
+ ? await readFile(skillMdPath, 'utf8')
486
+ : `# ${inspected.name}\n\n${inspected.description || ''}\n`;
487
+ const block = skillMdRegistryBlock(inspected, hub);
488
+ const pattern = /<!-- (?:xiashe|agentpie)-registry:start -->[\s\S]*?<!-- (?:xiashe|agentpie)-registry:end -->/m;
489
+ const next = pattern.test(existing)
490
+ ? existing.replace(pattern, block)
491
+ : `${existing.trimEnd()}\n\n${block}\n`;
492
+ await writeFile(skillMdPath, next, { mode: 0o600 });
493
+ return skillMdPath;
494
+ }
495
+
404
496
  async function readPlatformPrompt(flags) {
405
497
  const promptPath = normalizeText(flags['platform-prompt-file'], 1000);
406
498
  if (!promptPath) return '';
@@ -424,15 +516,18 @@ async function uploadPrompt(inspected, flags) {
424
516
  const registryUrl = registry.registryUrl || DEFAULT_REGISTRY_URL;
425
517
  const eventPayload = {
426
518
  publicToken: registry.publicToken || '<xiashe public token from xiashe.skill.json>',
519
+ schemaVersion: registry.eventSchemaVersion || EVENT_SCHEMA_VERSION,
427
520
  eventType: 'hub_upload_succeeded',
428
521
  hub,
429
522
  skillKey: inspected.skillKey,
523
+ idempotencyKey: '<hub-upload-succeeded-stable-key>',
430
524
  packageSha256: inspected.package.sha256,
431
525
  platformSkillUrl: '<published skill url>',
432
526
  scenario: '<short usage scenario if the hub asks for one>'
433
527
  };
434
528
  const runtimePayload = {
435
529
  publicToken: registry.publicToken || '<public token from registry manifest>',
530
+ schemaVersion: registry.eventSchemaVersion || EVENT_SCHEMA_VERSION,
436
531
  eventType: 'skill_invoked',
437
532
  eventSource: 'runtime',
438
533
  runtimeKind: '<mcp|api|agent|webhook|platform>',
@@ -440,6 +535,7 @@ async function uploadPrompt(inspected, flags) {
440
535
  skillKey: inspected.skillKey,
441
536
  installationId: '<stable anonymous install id>',
442
537
  invocationId: '<unique invocation id>',
538
+ idempotencyKey: '<unique invocation id>:skill_invoked',
443
539
  scenario: '<short usage scenario label only>',
444
540
  sourceSurface: '<profile|hub|agent|direct>',
445
541
  campaign: '<optional promotion label>'
@@ -527,6 +623,7 @@ async function writeRuntimeSnippet(root, flags) {
527
623
  const registry = inspected.registry || {};
528
624
  const registryUrl = registry.registryUrl || DEFAULT_REGISTRY_URL;
529
625
  const publicToken = registry.publicToken || '<public token from registry manifest>';
626
+ const eventSchemaVersion = registry.eventSchemaVersion || EVENT_SCHEMA_VERSION;
530
627
  const target = normalizeText(flags.target || 'js', 20).toLowerCase();
531
628
  const snippet = target === 'curl'
532
629
  ? [
@@ -534,6 +631,7 @@ async function writeRuntimeSnippet(root, flags) {
534
631
  " -H 'content-type: application/json' \\",
535
632
  ' -d ' + JSON.stringify(JSON.stringify({
536
633
  publicToken,
634
+ schemaVersion: eventSchemaVersion,
537
635
  eventType: 'skill_invoked',
538
636
  eventSource: 'runtime',
539
637
  runtimeKind: 'mcp',
@@ -541,31 +639,62 @@ async function writeRuntimeSnippet(root, flags) {
541
639
  skillKey: inspected.skillKey,
542
640
  installationId: '<anonymous-stable-install-id>',
543
641
  invocationId: '<unique-call-id>',
642
+ idempotencyKey: '<unique-call-id>:skill_invoked',
544
643
  scenario: '<short-scenario-label>'
545
644
  }))
546
645
  ].join('\n')
547
646
  : [
548
647
  `const XIASHE_SKILL_REGISTRY_URL = ${JSON.stringify(registryUrl)};`,
549
648
  `const XIASHE_SKILL_PUBLIC_TOKEN = ${JSON.stringify(publicToken)};`,
649
+ `const XIASHE_SKILL_EVENT_SCHEMA_VERSION = ${JSON.stringify(eventSchemaVersion)};`,
650
+ `const XIASHE_SKILL_SIGNING_SECRET = ${JSON.stringify(normalizeText(flags['signing-secret'], 512))} || globalThis.process?.env?.XIASHE_SKILL_SIGNING_SECRET || globalThis.process?.env?.AGENTPIE_SKILL_SIGNING_SECRET || '';`,
651
+ '',
652
+ 'async function hmacSha256(secret, message) {',
653
+ ' if (!secret || !globalThis.crypto?.subtle) return undefined;',
654
+ " const key = await crypto.subtle.importKey('raw', new TextEncoder().encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);",
655
+ ' const signature = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(message));',
656
+ " return Array.from(new Uint8Array(signature)).map((byte) => byte.toString(16).padStart(2, '0')).join('');",
657
+ '}',
658
+ '',
659
+ 'function canonicalSignaturePayload(payload) {',
660
+ ' return [',
661
+ ' payload.schemaVersion || XIASHE_SKILL_EVENT_SCHEMA_VERSION,',
662
+ " payload.eventType || '',",
663
+ " payload.idempotencyKey || '',",
664
+ " Number.isFinite(payload.occurredAt) ? String(Math.floor(payload.occurredAt)) : '',",
665
+ " payload.installationId || '',",
666
+ " payload.invocationId || '',",
667
+ " payload.hub || '',",
668
+ " payload.skillKey || ''",
669
+ " ].join('\\n');",
670
+ '}',
550
671
  '',
551
672
  'export async function trackSkillEvent(eventType, options = {}) {',
673
+ ' const occurredAt = options.occurredAt || Date.now();',
674
+ ' const invocationId = options.invocationId;',
675
+ ' const payload = {',
676
+ ' publicToken: XIASHE_SKILL_PUBLIC_TOKEN,',
677
+ ' schemaVersion: XIASHE_SKILL_EVENT_SCHEMA_VERSION,',
678
+ ' eventType,',
679
+ " eventSource: options.eventSource || 'runtime',",
680
+ " runtimeKind: options.runtimeKind || 'mcp',",
681
+ ` skillKey: ${JSON.stringify(inspected.skillKey)},`,
682
+ ' hub: options.hub,',
683
+ ' installationId: options.installationId,',
684
+ ' invocationId,',
685
+ ' idempotencyKey: options.idempotencyKey || `${invocationId || options.installationId || "runtime"}:${eventType}:${occurredAt}`,',
686
+ ' scenario: options.scenario,',
687
+ ' sourceSurface: options.sourceSurface,',
688
+ ' campaign: options.campaign,',
689
+ ' packageSha256: options.packageSha256,',
690
+ ' occurredAt',
691
+ ' };',
692
+ ' const signature = await hmacSha256(XIASHE_SKILL_SIGNING_SECRET, canonicalSignaturePayload(payload));',
693
+ ' if (signature) payload.signature = signature;',
552
694
  ' await fetch(XIASHE_SKILL_REGISTRY_URL, {',
553
695
  " method: 'POST',",
554
696
  " headers: { 'content-type': 'application/json' },",
555
- ' body: JSON.stringify({',
556
- ' publicToken: XIASHE_SKILL_PUBLIC_TOKEN,',
557
- ' eventType,',
558
- " eventSource: options.eventSource || 'runtime',",
559
- " runtimeKind: options.runtimeKind || 'mcp',",
560
- ` skillKey: ${JSON.stringify(inspected.skillKey)},`,
561
- ' hub: options.hub,',
562
- ' installationId: options.installationId,',
563
- ' invocationId: options.invocationId,',
564
- ' scenario: options.scenario,',
565
- ' sourceSurface: options.sourceSurface,',
566
- ' campaign: options.campaign,',
567
- ' packageSha256: options.packageSha256',
568
- ' })',
697
+ ' body: JSON.stringify(payload)',
569
698
  ' });',
570
699
  '}',
571
700
  '',
@@ -595,22 +724,33 @@ async function submitTrackEvent(root, flags) {
595
724
  if (!eventType) {
596
725
  throw new Error('Missing --event.');
597
726
  }
727
+ const occurredAt = Number(flags['occurred-at'] || Date.now());
728
+ const invocationId = normalizeText(flags['invocation-id'] || `cli-${Date.now()}`, 160);
729
+ const installationId = normalizeText(flags['installation-id'] || 'local-test-install', 220);
730
+ const hub = normalizeText(flags.hub || 'local-test', 80);
598
731
  const payload = {
599
732
  publicToken,
733
+ schemaVersion: normalizeText(flags['schema-version'], 80) || EVENT_SCHEMA_VERSION,
600
734
  eventType,
601
735
  eventSource: normalizeText(flags['event-source'] || 'manual', 40),
602
736
  runtimeKind: normalizeText(flags['runtime-kind'] || 'cli-test', 80),
603
- hub: normalizeText(flags.hub || 'local-test', 80),
737
+ hub,
604
738
  skillKey: inspected.skillKey,
605
- installationId: normalizeText(flags['installation-id'] || 'local-test-install', 220),
606
- invocationId: normalizeText(flags['invocation-id'] || `cli-${Date.now()}`, 160),
739
+ installationId,
740
+ invocationId,
607
741
  scenario: normalizeText(flags.scenario || 'local-test', 120),
608
742
  sourceSurface: normalizeText(flags['source-surface'] || 'cli', 120),
609
743
  campaign: normalizeText(flags.campaign, 120) || undefined,
610
744
  platformSkillUrl: normalizeText(flags['platform-skill-url'], 1000) || undefined,
611
745
  packageSha256: inspected.package.sha256,
612
- idempotencyKey: normalizeText(flags.idempotencyKey || flags['idempotency-key'], 220) || undefined
746
+ idempotencyKey: normalizeText(flags.idempotencyKey || flags['idempotency-key'], 220) || `${invocationId}:${eventType}`,
747
+ occurredAt: Number.isFinite(occurredAt) ? occurredAt : Date.now()
613
748
  };
749
+ const signingSecret = normalizeText(flags['signing-secret'], 512);
750
+ const signature = signEventPayload(payload, signingSecret);
751
+ if (signature) {
752
+ payload.signature = signature;
753
+ }
614
754
  if (flags.dryRun) {
615
755
  return { ok: true, dryRun: true, registryUrl, payload };
616
756
  }
@@ -631,6 +771,19 @@ async function setupAgentWorkflow(root, flags) {
631
771
 
632
772
  const promptPath = path.join(outDir, `upload-${safeSkillKey(hub)}.md`);
633
773
  const promptResult = await writePrompt(absoluteRoot, { ...flags, hub, out: promptPath });
774
+ const inspectedAfterManifest = await inspectSkill(absoluteRoot, flags);
775
+ const shouldEmbedSkillMd = Boolean(flags['embed-skill-md']) || (hub === 'red' && !flags['no-skill-md']);
776
+ const skillMdPath = shouldEmbedSkillMd
777
+ ? await writeSkillMdRegistryBlock(absoluteRoot, inspectedAfterManifest, hub)
778
+ : null;
779
+ const disclosurePath = path.join(outDir, 'REGISTRY_DISCLOSURE.md');
780
+ await writeFile(
781
+ disclosurePath,
782
+ `${publicDisclosure(inspectedAfterManifest, inspectedAfterManifest.registry?.registryUrl || DEFAULT_REGISTRY_URL)
783
+ .filter((line) => line !== '公开披露文本:' && line !== '```markdown' && line !== '```')
784
+ .join('\n')}\n`,
785
+ { mode: 0o600 }
786
+ );
634
787
  const snippetPath = path.join(outDir, 'runtime-events.js');
635
788
  let snippetResult = null;
636
789
  if (!flags['no-snippet']) {
@@ -663,12 +816,18 @@ async function setupAgentWorkflow(root, flags) {
663
816
  hub,
664
817
  manifestPath: manifestResult.manifestPath,
665
818
  promptPath: typeof promptResult === 'string' ? promptPath : promptResult.outPath,
819
+ skillMdPath,
820
+ disclosurePath,
666
821
  snippetPath: snippetResult ? snippetResult.outPath : null,
667
822
  promptEvent,
668
823
  warnings,
669
824
  next: [
670
825
  `Open ${path.relative(absoluteRoot, promptPath)} and merge it with the official ${hub} upload prompt/CLI flow.`,
671
826
  `If ${hub} provides its own upload prompt or CLI, treat that official flow as authoritative.`,
827
+ skillMdPath
828
+ ? `Registry disclosure was also embedded into ${path.relative(absoluteRoot, skillMdPath)} for restrictive hubs.`
829
+ : `If the hub rejects extra files, rerun with --embed-skill-md to place registry disclosure inside SKILL.md.`,
830
+ `Use ${path.relative(absoluteRoot, disclosurePath)} as the read-only analytics disclosure for platform review.`,
672
831
  'Do not upload secrets, .env files, browser data, SSH keys, node_modules, dist, build, or unrelated local files.',
673
832
  `If the Skill is published, record the public URL with: ${COMMAND_NAME} track . --event hub_upload_succeeded --hub ${hub} --platform-skill-url <url>`,
674
833
  snippetResult
@@ -707,6 +866,7 @@ async function main() {
707
866
  const lines = [
708
867
  `Wrote ${result.manifestPath}`,
709
868
  `Wrote ${result.promptPath}`,
869
+ `Wrote ${result.disclosurePath}`,
710
870
  result.snippetPath ? `Wrote ${result.snippetPath}` : null,
711
871
  ...result.warnings.map((warning) => `Warning: ${warning}`),
712
872
  '',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiashe/skill",
3
- "version": "0.1.1",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "xiashe-skill": "bin/xiashe-skill.mjs"