reskill 0.17.0 → 0.18.0

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/dist/cli/index.js CHANGED
@@ -831,12 +831,10 @@ class CacheManager {
831
831
  const DEFAULT_SKILLS_JSON = {
832
832
  skills: {},
833
833
  defaults: {
834
- registry: 'github',
835
834
  installDir: '.skills'
836
835
  }
837
836
  };
838
837
  const DEFAULT_VALUES = {
839
- registry: 'github',
840
838
  installDir: '.skills',
841
839
  targetAgents: [],
842
840
  installMode: 'symlink'
@@ -908,7 +906,6 @@ class ConfigLoader {
908
906
  const config = this.getConfigOrDefault();
909
907
  const storedDefaults = config.defaults ?? {};
910
908
  return {
911
- registry: storedDefaults.registry ?? DEFAULT_VALUES.registry,
912
909
  installDir: storedDefaults.installDir ?? DEFAULT_VALUES.installDir,
913
910
  targetAgents: storedDefaults.targetAgents ?? DEFAULT_VALUES.targetAgents,
914
911
  installMode: storedDefaults.installMode ?? DEFAULT_VALUES.installMode
@@ -973,10 +970,7 @@ class ConfigLoader {
973
970
  }
974
971
  }
975
972
  class GitResolver {
976
- defaultRegistry;
977
- constructor(defaultRegistry = 'github'){
978
- this.defaultRegistry = defaultRegistry;
979
- }
973
+ defaultRegistry = 'github';
980
974
  parseRef(ref) {
981
975
  const raw = ref;
982
976
  if (isGitUrl(ref)) return this.parseGitUrlRef(ref);
@@ -1274,8 +1268,7 @@ class SkillManager {
1274
1268
  this.config = new ConfigLoader(this.projectRoot);
1275
1269
  this.lockManager = new LockManager(this.projectRoot);
1276
1270
  this.cache = new CacheManager();
1277
- const defaults = this.config.getDefaults();
1278
- this.resolver = new GitResolver(defaults.registry);
1271
+ this.resolver = new GitResolver();
1279
1272
  }
1280
1273
  isGlobalMode() {
1281
1274
  return this.isGlobal;
@@ -1801,6 +1794,619 @@ function maybeHandleCompletion() {
1801
1794
  }
1802
1795
  return false;
1803
1796
  }
1797
+ function getStatusIcon(status) {
1798
+ switch(status){
1799
+ case 'ok':
1800
+ return __WEBPACK_EXTERNAL_MODULE_chalk__["default"].green('✓');
1801
+ case 'warn':
1802
+ return __WEBPACK_EXTERNAL_MODULE_chalk__["default"].yellow('⚠');
1803
+ case 'error':
1804
+ return __WEBPACK_EXTERNAL_MODULE_chalk__["default"].red('✗');
1805
+ }
1806
+ }
1807
+ function execCommand(command) {
1808
+ try {
1809
+ return (0, __WEBPACK_EXTERNAL_MODULE_node_child_process__.execSync)(command, {
1810
+ encoding: 'utf-8',
1811
+ stdio: [
1812
+ 'pipe',
1813
+ 'pipe',
1814
+ 'pipe'
1815
+ ]
1816
+ }).trim();
1817
+ } catch {
1818
+ return null;
1819
+ }
1820
+ }
1821
+ function getDirSize(dirPath) {
1822
+ if (!(0, external_node_fs_.existsSync)(dirPath)) return 0;
1823
+ let size = 0;
1824
+ try {
1825
+ const items = (0, external_node_fs_.readdirSync)(dirPath, {
1826
+ withFileTypes: true
1827
+ });
1828
+ for (const item of items){
1829
+ const itemPath = (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(dirPath, item.name);
1830
+ if (item.isDirectory()) size += getDirSize(itemPath);
1831
+ else if (item.isFile()) size += (0, external_node_fs_.statSync)(itemPath).size;
1832
+ }
1833
+ } catch {}
1834
+ return size;
1835
+ }
1836
+ function formatBytes(bytes) {
1837
+ if (0 === bytes) return '0 B';
1838
+ const k = 1024;
1839
+ const sizes = [
1840
+ 'B',
1841
+ 'KB',
1842
+ 'MB',
1843
+ 'GB'
1844
+ ];
1845
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
1846
+ return `${Number.parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;
1847
+ }
1848
+ function countCachedSkills(cacheDir) {
1849
+ if (!(0, external_node_fs_.existsSync)(cacheDir)) return 0;
1850
+ let count = 0;
1851
+ try {
1852
+ const registries = (0, external_node_fs_.readdirSync)(cacheDir, {
1853
+ withFileTypes: true
1854
+ });
1855
+ for (const registry of registries){
1856
+ if (!registry.isDirectory()) continue;
1857
+ const registryPath = (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(cacheDir, registry.name);
1858
+ const owners = (0, external_node_fs_.readdirSync)(registryPath, {
1859
+ withFileTypes: true
1860
+ });
1861
+ for (const owner of owners){
1862
+ if (!owner.isDirectory()) continue;
1863
+ const ownerPath = (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(registryPath, owner.name);
1864
+ const repos = (0, external_node_fs_.readdirSync)(ownerPath, {
1865
+ withFileTypes: true
1866
+ });
1867
+ for (const repo of repos){
1868
+ if (!repo.isDirectory()) continue;
1869
+ const repoPath = (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(ownerPath, repo.name);
1870
+ const versions = (0, external_node_fs_.readdirSync)(repoPath, {
1871
+ withFileTypes: true
1872
+ });
1873
+ count += versions.filter((v)=>v.isDirectory()).length;
1874
+ }
1875
+ }
1876
+ }
1877
+ } catch {}
1878
+ return count;
1879
+ }
1880
+ async function checkReskillVersion(currentVersion, packageName) {
1881
+ try {
1882
+ const result = await checkForUpdate(packageName, currentVersion);
1883
+ if (result?.hasUpdate) return {
1884
+ name: 'reskill version',
1885
+ status: 'warn',
1886
+ message: `${currentVersion} (latest: ${result.latest})`,
1887
+ hint: `Run: npm i -g ${packageName}@latest`
1888
+ };
1889
+ return {
1890
+ name: 'reskill version',
1891
+ status: 'ok',
1892
+ message: `${currentVersion} (latest)`
1893
+ };
1894
+ } catch {
1895
+ return {
1896
+ name: 'reskill version',
1897
+ status: 'ok',
1898
+ message: currentVersion
1899
+ };
1900
+ }
1901
+ }
1902
+ function checkNodeVersion() {
1903
+ const version = process.version;
1904
+ const major = Number.parseInt(version.slice(1).split('.')[0], 10);
1905
+ const required = 18;
1906
+ if (major < required) return {
1907
+ name: 'Node.js version',
1908
+ status: 'error',
1909
+ message: `${version} (requires >=${required}.0.0)`,
1910
+ hint: 'Please upgrade Node.js to version 18 or higher'
1911
+ };
1912
+ return {
1913
+ name: 'Node.js version',
1914
+ status: 'ok',
1915
+ message: `${version} (>=${required}.0.0 required)`
1916
+ };
1917
+ }
1918
+ function checkGitVersion() {
1919
+ const version = execCommand('git --version');
1920
+ if (!version) return {
1921
+ name: 'Git',
1922
+ status: 'error',
1923
+ message: 'not found',
1924
+ hint: 'Please install Git: https://git-scm.com/downloads'
1925
+ };
1926
+ const match = version.match(/git version (\d+\.\d+\.\d+)/);
1927
+ const versionNum = match ? match[1] : version;
1928
+ return {
1929
+ name: 'Git',
1930
+ status: 'ok',
1931
+ message: versionNum
1932
+ };
1933
+ }
1934
+ function checkGitAuth() {
1935
+ const home = (0, __WEBPACK_EXTERNAL_MODULE_node_os__.homedir)();
1936
+ const sshDir = (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(home, '.ssh');
1937
+ const sshKeyFiles = [
1938
+ 'id_rsa',
1939
+ 'id_ed25519',
1940
+ 'id_ecdsa',
1941
+ 'id_dsa'
1942
+ ];
1943
+ let hasSSHKey = false;
1944
+ if ((0, external_node_fs_.existsSync)(sshDir)) {
1945
+ for (const keyFile of sshKeyFiles)if ((0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(sshDir, keyFile))) {
1946
+ hasSSHKey = true;
1947
+ break;
1948
+ }
1949
+ }
1950
+ const credentialHelper = execCommand('git config --global credential.helper');
1951
+ const hasCredentialHelper = !!credentialHelper;
1952
+ if (hasSSHKey && hasCredentialHelper) return {
1953
+ name: 'Git authentication',
1954
+ status: 'ok',
1955
+ message: 'SSH key + credential helper configured'
1956
+ };
1957
+ if (hasSSHKey) return {
1958
+ name: 'Git authentication',
1959
+ status: 'ok',
1960
+ message: 'SSH key found'
1961
+ };
1962
+ if (hasCredentialHelper) return {
1963
+ name: 'Git authentication',
1964
+ status: 'ok',
1965
+ message: `credential helper: ${credentialHelper}`
1966
+ };
1967
+ return {
1968
+ name: 'Git authentication',
1969
+ status: 'warn',
1970
+ message: 'no SSH key or credential helper found',
1971
+ hint: 'For private repos, add SSH key: ssh-keygen -t ed25519'
1972
+ };
1973
+ }
1974
+ function checkCacheDir() {
1975
+ const cacheDir = process.env.RESKILL_CACHE_DIR || (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)((0, __WEBPACK_EXTERNAL_MODULE_node_os__.homedir)(), '.reskill-cache');
1976
+ if (!(0, external_node_fs_.existsSync)(cacheDir)) return {
1977
+ name: 'Cache directory',
1978
+ status: 'ok',
1979
+ message: `${cacheDir} (not created yet)`
1980
+ };
1981
+ const size = getDirSize(cacheDir);
1982
+ const count = countCachedSkills(cacheDir);
1983
+ return {
1984
+ name: 'Cache directory',
1985
+ status: 'ok',
1986
+ message: `${cacheDir} (${formatBytes(size)}, ${count} skill${1 !== count ? 's' : ''} cached)`
1987
+ };
1988
+ }
1989
+ function checkSkillsJson(cwd) {
1990
+ const configLoader = new ConfigLoader(cwd);
1991
+ if (!configLoader.exists()) return {
1992
+ name: 'skills.json',
1993
+ status: 'warn',
1994
+ message: 'not found',
1995
+ hint: 'Run: reskill init'
1996
+ };
1997
+ try {
1998
+ const skills = configLoader.getSkills();
1999
+ const count = Object.keys(skills).length;
2000
+ return {
2001
+ name: 'skills.json',
2002
+ status: 'ok',
2003
+ message: `found (${count} skill${1 !== count ? 's' : ''} declared)`
2004
+ };
2005
+ } catch (error) {
2006
+ return {
2007
+ name: 'skills.json',
2008
+ status: 'error',
2009
+ message: `invalid format: ${error.message}`,
2010
+ hint: 'Check skills.json syntax'
2011
+ };
2012
+ }
2013
+ }
2014
+ const RESERVED_REGISTRIES = [
2015
+ 'github',
2016
+ 'gitlab'
2017
+ ];
2018
+ const DANGEROUS_INSTALL_DIRS = [
2019
+ 'src',
2020
+ 'lib',
2021
+ 'dist',
2022
+ 'build',
2023
+ 'out',
2024
+ 'node_modules',
2025
+ '.git',
2026
+ '.github',
2027
+ '.gitlab',
2028
+ '.vscode',
2029
+ '.idea'
2030
+ ];
2031
+ const DANGEROUS_SKILL_NAMES = [
2032
+ '.git',
2033
+ '..',
2034
+ '.',
2035
+ 'node_modules',
2036
+ '__proto__',
2037
+ 'constructor'
2038
+ ];
2039
+ const NETWORK_CHECK_TIMEOUT_MS = 5000;
2040
+ const CHECK_NAME_PAD_WIDTH = 26;
2041
+ function checkRegistryConflicts(cwd) {
2042
+ const results = [];
2043
+ const configLoader = new ConfigLoader(cwd);
2044
+ if (!configLoader.exists()) return results;
2045
+ try {
2046
+ const config = configLoader.load();
2047
+ const registries = config.registries || {};
2048
+ for (const name of Object.keys(registries))if (RESERVED_REGISTRIES.includes(name.toLowerCase())) results.push({
2049
+ name: 'Registry conflict',
2050
+ status: 'warn',
2051
+ message: `"${name}" overrides built-in registry`,
2052
+ hint: 'Consider using a different name for custom registries'
2053
+ });
2054
+ } catch {}
2055
+ return results;
2056
+ }
2057
+ function checkInstallDir(cwd) {
2058
+ const configLoader = new ConfigLoader(cwd);
2059
+ if (!configLoader.exists()) return null;
2060
+ try {
2061
+ const defaults = configLoader.getDefaults();
2062
+ const installDir = defaults.installDir;
2063
+ if (!installDir) return null;
2064
+ const normalizedDir = installDir.replace(/^\.\//, '').replace(/\/$/, '');
2065
+ if (DANGEROUS_INSTALL_DIRS.includes(normalizedDir.toLowerCase())) return {
2066
+ name: 'Dangerous installDir',
2067
+ status: 'error',
2068
+ message: `"${installDir}" may conflict with important directories`,
2069
+ hint: 'Use a dedicated directory like ".skills" or ".agents/skills"'
2070
+ };
2071
+ if (installDir.includes('..')) return {
2072
+ name: 'Dangerous installDir',
2073
+ status: 'error',
2074
+ message: `"${installDir}" contains path traversal`,
2075
+ hint: 'Use a simple directory name without ".."'
2076
+ };
2077
+ } catch {}
2078
+ return null;
2079
+ }
2080
+ function checkTargetAgents(cwd) {
2081
+ const results = [];
2082
+ const configLoader = new ConfigLoader(cwd);
2083
+ if (!configLoader.exists()) return results;
2084
+ try {
2085
+ const defaults = configLoader.getDefaults();
2086
+ const targetAgents = defaults.targetAgents || [];
2087
+ for (const agent of targetAgents)if (!isValidAgentType(agent)) results.push({
2088
+ name: 'Invalid agent',
2089
+ status: 'warn',
2090
+ message: `Unknown agent type: "${agent}"`,
2091
+ hint: 'Run: reskill install --help to see valid agent types'
2092
+ });
2093
+ } catch {}
2094
+ return results;
2095
+ }
2096
+ function checkSkillRefs(cwd) {
2097
+ const results = [];
2098
+ const configLoader = new ConfigLoader(cwd);
2099
+ if (!configLoader.exists()) return results;
2100
+ try {
2101
+ const skills = configLoader.getSkills();
2102
+ const resolver = new GitResolver();
2103
+ for (const [name, ref] of Object.entries(skills)){
2104
+ if (DANGEROUS_SKILL_NAMES.includes(name) || name.includes('/') || name.includes('\\')) {
2105
+ results.push({
2106
+ name: 'Dangerous skill name',
2107
+ status: 'error',
2108
+ message: `"${name}" is not a valid skill name`,
2109
+ hint: 'Use alphanumeric names with hyphens or underscores'
2110
+ });
2111
+ continue;
2112
+ }
2113
+ try {
2114
+ resolver.parseRef(ref);
2115
+ } catch (error) {
2116
+ results.push({
2117
+ name: 'Invalid skill ref',
2118
+ status: 'error',
2119
+ message: `"${name}": ${error.message}`,
2120
+ hint: 'Format: registry:owner/repo@version or owner/repo@version'
2121
+ });
2122
+ }
2123
+ }
2124
+ } catch {}
2125
+ return results;
2126
+ }
2127
+ function checkMonorepoVersions(cwd) {
2128
+ const results = [];
2129
+ const configLoader = new ConfigLoader(cwd);
2130
+ if (!configLoader.exists()) return results;
2131
+ try {
2132
+ const skills = configLoader.getSkills();
2133
+ const resolver = new GitResolver();
2134
+ const repoVersions = new Map();
2135
+ for (const [name, ref] of Object.entries(skills))try {
2136
+ const parsed = resolver.parseRef(ref);
2137
+ if (!parsed.subPath) continue;
2138
+ const repoKey = `${parsed.registry}:${parsed.owner}/${parsed.repo}`;
2139
+ const version = parsed.version || 'default';
2140
+ let versions = repoVersions.get(repoKey);
2141
+ if (!versions) {
2142
+ versions = new Map();
2143
+ repoVersions.set(repoKey, versions);
2144
+ }
2145
+ let skillList = versions.get(version);
2146
+ if (!skillList) {
2147
+ skillList = [];
2148
+ versions.set(version, skillList);
2149
+ }
2150
+ skillList.push(name);
2151
+ } catch {}
2152
+ for (const [repo, versions] of repoVersions)if (versions.size > 1) {
2153
+ const versionList = [
2154
+ ...versions.entries()
2155
+ ].map(([v, skills])=>`${v} (${skills.join(', ')})`).join(', ');
2156
+ results.push({
2157
+ name: 'Version mismatch',
2158
+ status: 'warn',
2159
+ message: `${repo} has multiple versions: ${versionList}`,
2160
+ hint: 'Consider using the same version for all skills from this repo'
2161
+ });
2162
+ }
2163
+ } catch {}
2164
+ return results;
2165
+ }
2166
+ function checkSkillsLock(cwd) {
2167
+ const configLoader = new ConfigLoader(cwd);
2168
+ const lockManager = new LockManager(cwd);
2169
+ if (!configLoader.exists()) return {
2170
+ name: 'skills.lock',
2171
+ status: 'ok',
2172
+ message: 'n/a (no skills.json)'
2173
+ };
2174
+ if (!lockManager.exists()) {
2175
+ const skills = configLoader.getSkills();
2176
+ if (0 === Object.keys(skills).length) return {
2177
+ name: 'skills.lock',
2178
+ status: 'ok',
2179
+ message: 'n/a (no skills declared)'
2180
+ };
2181
+ return {
2182
+ name: 'skills.lock',
2183
+ status: 'warn',
2184
+ message: 'not found',
2185
+ hint: 'Run: reskill install'
2186
+ };
2187
+ }
2188
+ const configSkills = configLoader.getSkills();
2189
+ const lockedSkills = lockManager.getAll();
2190
+ const configNames = new Set(Object.keys(configSkills));
2191
+ const lockedNames = new Set(Object.keys(lockedSkills));
2192
+ const missingInLock = [
2193
+ ...configNames
2194
+ ].filter((n)=>!lockedNames.has(n));
2195
+ const extraInLock = [
2196
+ ...lockedNames
2197
+ ].filter((n)=>!configNames.has(n));
2198
+ if (missingInLock.length > 0) {
2199
+ const displayMissing = missingInLock.length > 3 ? `${missingInLock.slice(0, 3).join(', ')} +${missingInLock.length - 3} more` : missingInLock.join(', ');
2200
+ return {
2201
+ name: 'skills.lock',
2202
+ status: 'warn',
2203
+ message: `out of sync (missing: ${displayMissing})`,
2204
+ hint: 'Run: reskill install'
2205
+ };
2206
+ }
2207
+ if (extraInLock.length > 0) {
2208
+ const displayExtra = extraInLock.length > 3 ? `${extraInLock.slice(0, 3).join(', ')} +${extraInLock.length - 3} more` : extraInLock.join(', ');
2209
+ return {
2210
+ name: 'skills.lock',
2211
+ status: 'warn',
2212
+ message: `out of sync (extra: ${displayExtra})`,
2213
+ hint: 'Run: reskill install'
2214
+ };
2215
+ }
2216
+ return {
2217
+ name: 'skills.lock',
2218
+ status: 'ok',
2219
+ message: `in sync (${lockedNames.size} skill${1 !== lockedNames.size ? 's' : ''} locked)`
2220
+ };
2221
+ }
2222
+ function isValidSymlink(path) {
2223
+ try {
2224
+ const stat = (0, external_node_fs_.lstatSync)(path);
2225
+ if (!stat.isSymbolicLink()) return true;
2226
+ (0, external_node_fs_.realpathSync)(path);
2227
+ return true;
2228
+ } catch {
2229
+ return false;
2230
+ }
2231
+ }
2232
+ function validateSkillJson(skillJsonPath) {
2233
+ if (!(0, external_node_fs_.existsSync)(skillJsonPath)) return {
2234
+ valid: true
2235
+ };
2236
+ try {
2237
+ const content = (0, external_node_fs_.readFileSync)(skillJsonPath, 'utf-8');
2238
+ const json = JSON.parse(content);
2239
+ if (!json.name) return {
2240
+ valid: false,
2241
+ error: 'skill.json missing "name" field'
2242
+ };
2243
+ return {
2244
+ valid: true
2245
+ };
2246
+ } catch (error) {
2247
+ if (error instanceof SyntaxError) return {
2248
+ valid: false,
2249
+ error: 'skill.json is not valid JSON'
2250
+ };
2251
+ return {
2252
+ valid: false,
2253
+ error: `skill.json read error: ${error.message}`
2254
+ };
2255
+ }
2256
+ }
2257
+ function checkInstalledSkills(cwd) {
2258
+ const results = [];
2259
+ const skillManager = new SkillManager(cwd);
2260
+ const installed = skillManager.list();
2261
+ if (0 === installed.length) return [
2262
+ {
2263
+ name: 'Installed skills',
2264
+ status: 'ok',
2265
+ message: 'none'
2266
+ }
2267
+ ];
2268
+ const issues = [];
2269
+ for (const skill of installed){
2270
+ const skillJsonPath = (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(skill.path, 'skill.json');
2271
+ const skillMdPath = (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(skill.path, 'SKILL.md');
2272
+ if (skill.isLinked && !isValidSymlink(skill.path)) {
2273
+ issues.push({
2274
+ name: skill.name,
2275
+ reason: 'symlink target does not exist',
2276
+ severity: 'error'
2277
+ });
2278
+ continue;
2279
+ }
2280
+ const hasSkillJson = (0, external_node_fs_.existsSync)(skillJsonPath);
2281
+ const hasSkillMd = (0, external_node_fs_.existsSync)(skillMdPath);
2282
+ if (!hasSkillJson && !hasSkillMd) {
2283
+ issues.push({
2284
+ name: skill.name,
2285
+ reason: 'missing both skill.json and SKILL.md',
2286
+ severity: 'error'
2287
+ });
2288
+ continue;
2289
+ }
2290
+ if (hasSkillJson) {
2291
+ const validation = validateSkillJson(skillJsonPath);
2292
+ if (!validation.valid) issues.push({
2293
+ name: skill.name,
2294
+ reason: validation.error || 'invalid skill.json',
2295
+ severity: 'warn'
2296
+ });
2297
+ }
2298
+ }
2299
+ const errorCount = issues.filter((i)=>'error' === i.severity).length;
2300
+ const warnCount = issues.filter((i)=>'warn' === i.severity).length;
2301
+ if (0 === issues.length) results.push({
2302
+ name: 'Installed skills',
2303
+ status: 'ok',
2304
+ message: `${installed.length} skill${1 !== installed.length ? 's' : ''} installed`
2305
+ });
2306
+ else {
2307
+ const status = errorCount > 0 ? 'error' : 'warn';
2308
+ const issueText = errorCount > 0 ? `${errorCount} broken` : `${warnCount} with issues`;
2309
+ results.push({
2310
+ name: 'Installed skills',
2311
+ status,
2312
+ message: `${installed.length} installed, ${issueText}`,
2313
+ hint: 'Run: reskill install --force to fix'
2314
+ });
2315
+ for (const issue of issues)results.push({
2316
+ name: ` └─ ${issue.name}`,
2317
+ status: 'error' === issue.severity ? 'error' : 'warn',
2318
+ message: issue.reason
2319
+ });
2320
+ }
2321
+ return results;
2322
+ }
2323
+ async function checkNetwork(host) {
2324
+ const displayName = host.replace('https://', '').replace('http://', '');
2325
+ try {
2326
+ const controller = new AbortController();
2327
+ const timeout = setTimeout(()=>controller.abort(), NETWORK_CHECK_TIMEOUT_MS);
2328
+ const response = await fetch(host, {
2329
+ method: 'HEAD',
2330
+ signal: controller.signal
2331
+ });
2332
+ clearTimeout(timeout);
2333
+ if (response.ok || 301 === response.status || 302 === response.status) return {
2334
+ name: `Network (${displayName})`,
2335
+ status: 'ok',
2336
+ message: 'reachable'
2337
+ };
2338
+ return {
2339
+ name: `Network (${displayName})`,
2340
+ status: 'warn',
2341
+ message: `status ${response.status}`
2342
+ };
2343
+ } catch (error) {
2344
+ const errorMessage = error instanceof Error ? error.message : 'unknown error';
2345
+ return {
2346
+ name: `Network (${displayName})`,
2347
+ status: 'error',
2348
+ message: `unreachable (${errorMessage})`,
2349
+ hint: 'Check your internet connection or firewall settings'
2350
+ };
2351
+ }
2352
+ }
2353
+ function printResult(result) {
2354
+ const icon = getStatusIcon(result.status);
2355
+ const name = result.name.padEnd(CHECK_NAME_PAD_WIDTH);
2356
+ const message = 'ok' === result.status ? result.message : __WEBPACK_EXTERNAL_MODULE_chalk__["default"].dim(result.message);
2357
+ logger_logger.log(`${icon} ${name} ${message}`);
2358
+ if (result.hint) logger_logger.log(` ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].dim('→')} ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].dim(result.hint)}`);
2359
+ }
2360
+ async function runDoctorChecks(options) {
2361
+ const { cwd, packageName, packageVersion, skipNetwork, skipConfigChecks } = options;
2362
+ const results = [];
2363
+ results.push(await checkReskillVersion(packageVersion, packageName));
2364
+ results.push(checkNodeVersion());
2365
+ results.push(checkGitVersion());
2366
+ results.push(checkGitAuth());
2367
+ results.push(checkCacheDir());
2368
+ results.push(checkSkillsJson(cwd));
2369
+ results.push(checkSkillsLock(cwd));
2370
+ results.push(...checkInstalledSkills(cwd));
2371
+ if (!skipConfigChecks) {
2372
+ results.push(...checkRegistryConflicts(cwd));
2373
+ const installDirCheck = checkInstallDir(cwd);
2374
+ if (installDirCheck) results.push(installDirCheck);
2375
+ results.push(...checkTargetAgents(cwd));
2376
+ results.push(...checkSkillRefs(cwd));
2377
+ results.push(...checkMonorepoVersions(cwd));
2378
+ }
2379
+ if (!skipNetwork) {
2380
+ results.push(await checkNetwork('https://github.com'));
2381
+ results.push(await checkNetwork('https://gitlab.com'));
2382
+ }
2383
+ return results;
2384
+ }
2385
+ const doctorCommand = new __WEBPACK_EXTERNAL_MODULE_commander__.Command('doctor').description('Diagnose reskill environment and check for issues').option('--json', 'Output as JSON').option('--skip-network', 'Skip network connectivity checks').action(async (options)=>{
2386
+ const __dirname = (0, __WEBPACK_EXTERNAL_MODULE_node_path__.dirname)((0, __WEBPACK_EXTERNAL_MODULE_node_url__.fileURLToPath)(import.meta.url));
2387
+ const packageJson = JSON.parse((0, external_node_fs_.readFileSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(__dirname, '../../package.json'), 'utf-8'));
2388
+ if (!options.json) logger_logger.log(__WEBPACK_EXTERNAL_MODULE_chalk__["default"].bold('\n🩺 Checking reskill environment...\n'));
2389
+ const results = await runDoctorChecks({
2390
+ cwd: process.cwd(),
2391
+ packageName: packageJson.name,
2392
+ packageVersion: packageJson.version,
2393
+ skipNetwork: options.skipNetwork
2394
+ });
2395
+ if (options.json) {
2396
+ console.log(JSON.stringify(results, null, 2));
2397
+ return;
2398
+ }
2399
+ for (const result of results)printResult(result);
2400
+ const errors = results.filter((r)=>'error' === r.status).length;
2401
+ const warnings = results.filter((r)=>'warn' === r.status).length;
2402
+ logger_logger.newline();
2403
+ if (errors > 0) {
2404
+ logger_logger.error(`Found ${errors} error${1 !== errors ? 's' : ''} and ${warnings} warning${1 !== warnings ? 's' : ''}`);
2405
+ process.exit(1);
2406
+ }
2407
+ if (warnings > 0) logger_logger.warn(`Found ${warnings} warning${1 !== warnings ? 's' : ''}, but reskill should work`);
2408
+ else logger_logger.success('All checks passed! reskill is ready to use.');
2409
+ });
1804
2410
  const infoCommand = new __WEBPACK_EXTERNAL_MODULE_commander__.Command('info').description('Show skill details').argument('<skill>', 'Skill name').option('-j, --json', 'Output as JSON').action((skillName, options)=>{
1805
2411
  const skillManager = new SkillManager();
1806
2412
  const info = skillManager.getInfo(skillName);
@@ -1840,29 +2446,29 @@ const infoCommand = new __WEBPACK_EXTERNAL_MODULE_commander__.Command('info').de
1840
2446
  }
1841
2447
  } else logger_logger.warn(`Skill ${skillName} is not installed`);
1842
2448
  });
1843
- const initCommand = new __WEBPACK_EXTERNAL_MODULE_commander__.Command('init').description('Initialize a new skills.json configuration').option('-n, --name <name>', 'Project name').option('-r, --registry <registry>', 'Default registry', 'github').option('-d, --install-dir <dir>', 'Skills installation directory', '.skills').option('-y, --yes', 'Skip prompts and use defaults').action(async (options)=>{
2449
+ const DEFAULT_INSTALL_DIR = '.skills';
2450
+ function displayConfigSummary(installDir) {
2451
+ logger_logger.success('Created skills.json');
2452
+ logger_logger.newline();
2453
+ logger_logger.log('Configuration:');
2454
+ logger_logger.log(` Install directory: ${installDir}`);
2455
+ logger_logger.newline();
2456
+ logger_logger.log('Next steps:');
2457
+ logger_logger.log(' reskill install <skill> Install a skill');
2458
+ logger_logger.log(' reskill list List installed skills');
2459
+ }
2460
+ const initCommand = new __WEBPACK_EXTERNAL_MODULE_commander__.Command('init').description('Initialize a new skills.json configuration').option('-d, --install-dir <dir>', 'Skills installation directory', DEFAULT_INSTALL_DIR).option('-y, --yes', 'Skip prompts and use defaults').action((options)=>{
1844
2461
  const configLoader = new ConfigLoader();
1845
2462
  if (configLoader.exists()) {
1846
2463
  logger_logger.warn('skills.json already exists');
1847
2464
  return;
1848
2465
  }
1849
- const config = configLoader.create({
1850
- name: options.name,
2466
+ configLoader.create({
1851
2467
  defaults: {
1852
- registry: options.registry,
1853
2468
  installDir: options.installDir
1854
2469
  }
1855
2470
  });
1856
- logger_logger.success('Created skills.json');
1857
- logger_logger.newline();
1858
- logger_logger.log('Configuration:');
1859
- logger_logger.log(` Name: ${config.name || '(not set)'}`);
1860
- logger_logger.log(` Default registry: ${config.defaults?.registry}`);
1861
- logger_logger.log(` Install directory: ${config.defaults?.installDir}`);
1862
- logger_logger.newline();
1863
- logger_logger.log('Next steps:');
1864
- logger_logger.log(' reskill install <skill> Install a skill');
1865
- logger_logger.log(' reskill list List installed skills');
2471
+ displayConfigSummary(options.installDir);
1866
2472
  });
1867
2473
  function formatAgentNames(agentTypes, maxShow = 5) {
1868
2474
  const names = agentTypes.map((a)=>agents[a].displayName);
@@ -2327,9 +2933,9 @@ const updateCommand = new __WEBPACK_EXTERNAL_MODULE_commander__.Command('update'
2327
2933
  });
2328
2934
  if (maybeHandleCompletion()) process.exit(0);
2329
2935
  const cli_rslib_entry_dirname = (0, __WEBPACK_EXTERNAL_MODULE_node_path__.dirname)((0, __WEBPACK_EXTERNAL_MODULE_node_url__.fileURLToPath)(import.meta.url));
2330
- const packageJson = JSON.parse((0, external_node_fs_.readFileSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(cli_rslib_entry_dirname, '../../package.json'), 'utf-8'));
2936
+ const cli_rslib_entry_packageJson = JSON.parse((0, external_node_fs_.readFileSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(cli_rslib_entry_dirname, '../../package.json'), 'utf-8'));
2331
2937
  const program = new __WEBPACK_EXTERNAL_MODULE_commander__.Command();
2332
- program.name('reskill').description('AI Skills Package Manager - Git-based skills management for AI agents').version(packageJson.version);
2938
+ program.name('reskill').description('AI Skills Package Manager - Git-based skills management for AI agents').version(cli_rslib_entry_packageJson.version);
2333
2939
  program.addCommand(initCommand);
2334
2940
  program.addCommand(installCommand);
2335
2941
  program.addCommand(listCommand);
@@ -2338,7 +2944,8 @@ program.addCommand(updateCommand);
2338
2944
  program.addCommand(outdatedCommand);
2339
2945
  program.addCommand(uninstallCommand);
2340
2946
  program.addCommand(completionCommand);
2341
- const updateCheckPromise = checkForUpdate(packageJson.name, packageJson.version);
2947
+ program.addCommand(doctorCommand);
2948
+ const updateCheckPromise = checkForUpdate(cli_rslib_entry_packageJson.name, cli_rslib_entry_packageJson.version);
2342
2949
  program.parseAsync().then(async ()=>{
2343
2950
  const result = await updateCheckPromise;
2344
2951
  if (result?.hasUpdate) logger_logger.log(formatUpdateMessage(result));