reskill 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +90 -31
  2. package/README.zh-CN.md +97 -25
  3. package/dist/cli/commands/index.d.ts +4 -0
  4. package/dist/cli/commands/index.d.ts.map +1 -1
  5. package/dist/cli/commands/login.d.ts +10 -0
  6. package/dist/cli/commands/login.d.ts.map +1 -0
  7. package/dist/cli/commands/logout.d.ts +9 -0
  8. package/dist/cli/commands/logout.d.ts.map +1 -0
  9. package/dist/cli/commands/publish.d.ts +32 -0
  10. package/dist/cli/commands/publish.d.ts.map +1 -0
  11. package/dist/cli/commands/uninstall.d.ts.map +1 -1
  12. package/dist/cli/commands/whoami.d.ts +9 -0
  13. package/dist/cli/commands/whoami.d.ts.map +1 -0
  14. package/dist/cli/index.js +2492 -32
  15. package/dist/core/auth-manager.d.ts +71 -0
  16. package/dist/core/auth-manager.d.ts.map +1 -0
  17. package/dist/core/config-loader.d.ts +12 -1
  18. package/dist/core/config-loader.d.ts.map +1 -1
  19. package/dist/core/extractor.d.ts +62 -0
  20. package/dist/core/extractor.d.ts.map +1 -0
  21. package/dist/core/git-resolver.d.ts +2 -0
  22. package/dist/core/git-resolver.d.ts.map +1 -1
  23. package/dist/core/http-resolver.d.ts +6 -2
  24. package/dist/core/http-resolver.d.ts.map +1 -1
  25. package/dist/core/index.d.ts +8 -0
  26. package/dist/core/index.d.ts.map +1 -1
  27. package/dist/core/install-directory.d.ts +81 -0
  28. package/dist/core/install-directory.d.ts.map +1 -0
  29. package/dist/core/installer.d.ts +4 -0
  30. package/dist/core/installer.d.ts.map +1 -1
  31. package/dist/core/publisher.d.ts +85 -0
  32. package/dist/core/publisher.d.ts.map +1 -0
  33. package/dist/core/registry-client.d.ts +141 -0
  34. package/dist/core/registry-client.d.ts.map +1 -0
  35. package/dist/core/registry-resolver.d.ts +62 -0
  36. package/dist/core/registry-resolver.d.ts.map +1 -0
  37. package/dist/core/skill-manager.d.ts +29 -0
  38. package/dist/core/skill-manager.d.ts.map +1 -1
  39. package/dist/core/skill-parser.d.ts +5 -1
  40. package/dist/core/skill-parser.d.ts.map +1 -1
  41. package/dist/core/skill-validator.d.ts +110 -0
  42. package/dist/core/skill-validator.d.ts.map +1 -0
  43. package/dist/index.js +971 -18
  44. package/dist/types/index.d.ts +30 -0
  45. package/dist/types/index.d.ts.map +1 -1
  46. package/dist/utils/git.d.ts +4 -3
  47. package/dist/utils/git.d.ts.map +1 -1
  48. package/dist/utils/index.d.ts +1 -0
  49. package/dist/utils/index.d.ts.map +1 -1
  50. package/dist/utils/registry-scope.d.ts +150 -0
  51. package/dist/utils/registry-scope.d.ts.map +1 -0
  52. package/dist/utils/registry.d.ts +22 -0
  53. package/dist/utils/registry.d.ts.map +1 -0
  54. package/package.json +4 -2
@@ -46,6 +46,8 @@ export interface SkillsDefaults {
46
46
  targetAgents?: string[];
47
47
  /** Installation mode: symlink | copy */
48
48
  installMode?: 'symlink' | 'copy';
49
+ /** Registry URL for publishing skills (no default - must be explicitly configured) */
50
+ publishRegistry?: string;
49
51
  }
50
52
  /**
51
53
  * Skill override configuration
@@ -226,4 +228,32 @@ export interface ListOptions {
226
228
  /** JSON format output */
227
229
  json?: boolean;
228
230
  }
231
+ /**
232
+ * Skill 来源类型
233
+ * - registry: CLI 发布(支持版本管理)
234
+ * - github/gitlab: 页面发布 Remote URL(Git 仓库)
235
+ * - oss_url/custom_url: 页面发布 Remote URL(HTTP 链接)
236
+ * - local: 页面发布 Local Folder(上传到 OSS)
237
+ */
238
+ export type SourceType = 'registry' | 'github' | 'gitlab' | 'oss_url' | 'custom_url' | 'local';
239
+ /**
240
+ * Registry API 返回的 Skill 基本信息
241
+ * 用于 install 命令判断安装逻辑分支
242
+ */
243
+ export interface SkillInfo {
244
+ /** Skill 完整名称,如 @kanyun/my-skill */
245
+ name: string;
246
+ /** 描述 */
247
+ description?: string;
248
+ /** 来源类型,默认 'registry'(CLI 发布) */
249
+ source_type?: SourceType;
250
+ /** 来源 URL(仅页面发布时有值) */
251
+ source_url?: string;
252
+ /** 发布者 ID */
253
+ publisher_id?: string;
254
+ /** 创建时间 */
255
+ created_at?: string;
256
+ /** 更新时间 */
257
+ updated_at?: string;
258
+ }
229
259
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
@@ -97,16 +97,17 @@ export interface ParsedGitUrl {
97
97
  repo: string;
98
98
  /** Original URL */
99
99
  url: string;
100
- /** URL type: ssh, https, or git */
101
- type: 'ssh' | 'https' | 'git';
100
+ /** URL type: ssh, https, git, or file (for local testing) */
101
+ type: 'ssh' | 'https' | 'git' | 'file';
102
102
  }
103
103
  /**
104
- * Check if a source string is a complete Git URL (SSH, HTTPS, or git://)
104
+ * Check if a source string is a complete Git URL (SSH, HTTPS, git://, or file://)
105
105
  *
106
106
  * Supported formats:
107
107
  * - SSH: git@github.com:user/repo.git
108
108
  * - HTTPS: https://github.com/user/repo.git
109
109
  * - Git protocol: git://github.com/user/repo.git
110
+ * - File protocol: file:///path/to/repo (for local testing)
110
111
  * - URLs ending with .git
111
112
  */
112
113
  export declare function isGitUrl(source: string): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"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"}
@@ -2,5 +2,6 @@ export * from './fs.js';
2
2
  export * from './git.js';
3
3
  export * from './http.js';
4
4
  export { logger } from './logger.js';
5
+ export * from './registry.js';
5
6
  export * from './update-notifier.js';
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,cAAc,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Registry-Scope Mapping Utilities
3
+ *
4
+ * Maps registry URLs to their corresponding scopes.
5
+ * Currently hardcoded; TODO: fetch from /api/registry/info in the future.
6
+ */
7
+ /**
8
+ * 公共 Registry URL
9
+ * 用于无 scope 的 skill 安装
10
+ */
11
+ export declare const PUBLIC_REGISTRY = "https://reskill.info/";
12
+ /**
13
+ * Parsed skill name result
14
+ */
15
+ export interface ParsedSkillName {
16
+ /** Scope including @ prefix, e.g., "@kanyun" */
17
+ scope: string | null;
18
+ /** Short name without scope, e.g., "planning-with-files" */
19
+ name: string;
20
+ /** Full name as provided, e.g., "@kanyun/planning-with-files" */
21
+ fullName: string;
22
+ }
23
+ /**
24
+ * Parsed skill identifier result (with version)
25
+ * 用于 install 命令解析 skill 标识
26
+ */
27
+ export interface ParsedSkillIdentifier {
28
+ /** Scope including @ prefix, e.g., "@kanyun"。公共 Registry 时为 null */
29
+ scope: string | null;
30
+ /** Short name without scope, e.g., "planning-with-files" */
31
+ name: string;
32
+ /** Version or tag, e.g., "2.4.5" or "latest"。未指定时为 undefined */
33
+ version: string | undefined;
34
+ /** Full name without version, e.g., "@kanyun/planning-with-files" */
35
+ fullName: string;
36
+ }
37
+ /**
38
+ * Get the scope for a given registry URL
39
+ *
40
+ * @param registry - Registry URL
41
+ * @returns Scope string (e.g., "@kanyun") or null if not found
42
+ *
43
+ * @example
44
+ * getScopeForRegistry('https://reskill-test.zhenguanyu.com') // '@kanyun'
45
+ * getScopeForRegistry('https://unknown.com') // null
46
+ */
47
+ export declare function getScopeForRegistry(registry: string): string | null;
48
+ /**
49
+ * Custom scope registries configuration
50
+ * Maps scope names to registry URLs
51
+ */
52
+ export type ScopeRegistries = Record<string, string>;
53
+ /**
54
+ * Get the registry URL for a given scope (reverse lookup)
55
+ *
56
+ * @param scope - Scope string (with or without @ prefix), e.g., "@kanyun" or "kanyun"
57
+ * @param customRegistries - Optional custom scope-to-registry mapping (from skills.json)
58
+ * @returns Registry URL (with trailing slash) or null if not found
59
+ *
60
+ * @example
61
+ * getRegistryForScope('@kanyun') // 'https://reskill-test.zhenguanyu.com/'
62
+ * getRegistryForScope('kanyun') // 'https://reskill-test.zhenguanyu.com/'
63
+ * getRegistryForScope('@unknown') // null
64
+ * getRegistryForScope('@mycompany', { '@mycompany': 'https://my.registry.com/' }) // 'https://my.registry.com/'
65
+ */
66
+ export declare function getRegistryForScope(scope: string, customRegistries?: ScopeRegistries): string | null;
67
+ /**
68
+ * Get the registry URL for a given scope
69
+ *
70
+ * - With scope → lookup private Registry (throws if not found)
71
+ * - Without scope (null/undefined/'') → returns public Registry
72
+ *
73
+ * @param scope - Scope string (with or without @ prefix), null, undefined, or empty string
74
+ * @param customRegistries - Optional custom scope-to-registry mapping (from skills.json)
75
+ * @returns Registry URL (with trailing slash)
76
+ * @throws Error if scope is provided but not found in the registry map
77
+ *
78
+ * @example
79
+ * getRegistryUrl('@kanyun') // 'https://reskill-test.zhenguanyu.com/'
80
+ * getRegistryUrl('kanyun') // 'https://reskill-test.zhenguanyu.com/'
81
+ * getRegistryUrl(null) // 'https://reskill.info/'
82
+ * getRegistryUrl('') // 'https://reskill.info/'
83
+ * getRegistryUrl('@unknown') // throws Error
84
+ * getRegistryUrl('@mycompany', { '@mycompany': 'https://my.registry.com/' }) // 'https://my.registry.com/'
85
+ */
86
+ export declare function getRegistryUrl(scope: string | null | undefined, customRegistries?: ScopeRegistries): string;
87
+ /**
88
+ * Parse a skill name into its components
89
+ *
90
+ * @param skillName - Full or short skill name
91
+ * @returns Parsed skill name with scope and name
92
+ *
93
+ * @example
94
+ * parseSkillName('@kanyun/planning-with-files')
95
+ * // { scope: '@kanyun', name: 'planning-with-files', fullName: '@kanyun/planning-with-files' }
96
+ *
97
+ * parseSkillName('planning-with-files')
98
+ * // { scope: null, name: 'planning-with-files', fullName: 'planning-with-files' }
99
+ */
100
+ export declare function parseSkillName(skillName: string): ParsedSkillName;
101
+ /**
102
+ * Build full skill name from scope and name
103
+ *
104
+ * @param scope - Scope (with or without @ prefix), or null
105
+ * @param name - Short skill name
106
+ * @returns Full skill name (e.g., "@kanyun/planning-with-files")
107
+ *
108
+ * @example
109
+ * buildFullSkillName('@kanyun', 'planning-with-files') // '@kanyun/planning-with-files'
110
+ * buildFullSkillName('kanyun', 'my-skill') // '@kanyun/my-skill'
111
+ * buildFullSkillName(null, 'my-skill') // 'my-skill'
112
+ */
113
+ export declare function buildFullSkillName(scope: string | null, name: string): string;
114
+ /**
115
+ * Get short name from a skill name (removes scope if present)
116
+ *
117
+ * @param skillName - Full or short skill name
118
+ * @returns Short name without scope
119
+ *
120
+ * @example
121
+ * getShortName('@kanyun/planning-with-files') // 'planning-with-files'
122
+ * getShortName('planning-with-files') // 'planning-with-files'
123
+ */
124
+ export declare function getShortName(skillName: string): string;
125
+ /**
126
+ * Parse a skill identifier into its components (with version support)
127
+ *
128
+ * 支持私有 Registry(带 @scope)和公共 Registry(无 scope)两种格式。
129
+ *
130
+ * @param identifier - Skill identifier string
131
+ * @returns Parsed skill identifier with scope, name, version, and fullName
132
+ * @throws Error if identifier is invalid
133
+ *
134
+ * @example
135
+ * // 私有 Registry
136
+ * parseSkillIdentifier('@kanyun/planning-with-files')
137
+ * // { scope: '@kanyun', name: 'planning-with-files', version: undefined, fullName: '@kanyun/planning-with-files' }
138
+ *
139
+ * parseSkillIdentifier('@kanyun/skill@2.4.5')
140
+ * // { scope: '@kanyun', name: 'skill', version: '2.4.5', fullName: '@kanyun/skill' }
141
+ *
142
+ * // 公共 Registry
143
+ * parseSkillIdentifier('planning-with-files')
144
+ * // { scope: null, name: 'planning-with-files', version: undefined, fullName: 'planning-with-files' }
145
+ *
146
+ * parseSkillIdentifier('skill@latest')
147
+ * // { scope: null, name: 'skill', version: 'latest', fullName: 'skill' }
148
+ */
149
+ export declare function parseSkillIdentifier(identifier: string): ParsedSkillIdentifier;
150
+ //# sourceMappingURL=registry-scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-scope.d.ts","sourceRoot":"","sources":["../../src/utils/registry-scope.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,eAAO,MAAM,eAAe,0BAA0B,CAAC;AAcvD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,gDAAgD;IAChD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,oEAAoE;IACpE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgBnE;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAErD;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,gBAAgB,CAAC,EAAE,eAAe,GACjC,MAAM,GAAG,IAAI,CAwBf;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAChC,gBAAgB,CAAC,EAAE,eAAe,GACjC,MAAM,CAkBR;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,CAiBjE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAS7E;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,qBAAqB,CAwD9E"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Registry URL resolution utilities
3
+ *
4
+ * Shared utility for resolving registry URLs across CLI commands.
5
+ */
6
+ /**
7
+ * Resolve registry URL from multiple sources
8
+ *
9
+ * Priority (highest to lowest):
10
+ * 1. --registry CLI option
11
+ * 2. RESKILL_REGISTRY environment variable
12
+ * 3. defaults.publishRegistry in skills.json
13
+ *
14
+ * Intentionally has NO default - users must explicitly configure their registry.
15
+ *
16
+ * @param cliRegistry - Registry URL from CLI option
17
+ * @param projectRoot - Project root directory (defaults to cwd)
18
+ * @returns Resolved registry URL
19
+ * @throws Exits process with code 1 if no registry is configured
20
+ */
21
+ export declare function resolveRegistry(cliRegistry: string | undefined, projectRoot?: string): string;
22
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/utils/registry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,WAAW,GAAE,MAAsB,GAClC,MAAM,CA6BR"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reskill",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "AI Skills Package Manager - Git-based skills management for AI agents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -53,7 +53,8 @@
53
53
  "gray-matter": "^4.0.3",
54
54
  "ora": "^8.0.1",
55
55
  "semver": "^7.6.3",
56
- "tabtab": "^3.0.2"
56
+ "tabtab": "^3.0.2",
57
+ "tar-stream": "^3.1.7"
57
58
  },
58
59
  "devDependencies": {
59
60
  "@biomejs/biome": "^2.3.11",
@@ -62,6 +63,7 @@
62
63
  "@types/degit": "^2.8.6",
63
64
  "@types/node": "^22.10.0",
64
65
  "@types/semver": "^7.5.8",
66
+ "@types/tar-stream": "^3.1.3",
65
67
  "@vitest/coverage-v8": "^2.1.9",
66
68
  "typescript": "^5.7.2",
67
69
  "vitest": "^2.1.8"