|
1 | 1 | import path from 'path' |
2 | 2 | import os from 'os' |
| 3 | +import { fileURLToPath } from 'url' |
3 | 4 | import fs from 'fs-extra' |
4 | 5 | import { getLogger } from '../utils/logger-context.js' |
5 | 6 | import { TelemetryService } from '../lib/TelemetryService.js' |
@@ -27,11 +28,9 @@ export class OpenclawCommand { |
27 | 28 | const workspace = options.workspace ?? 'workspace' |
28 | 29 | const force = options.force ?? false |
29 | 30 |
|
30 | | - // 1. Resolve and verify openclaw-skill/ exists in the project |
31 | | - const skillSourceDir = path.join(this.projectRoot, 'openclaw-skill') |
32 | | - if (!(await fs.pathExists(skillSourceDir))) { |
33 | | - throw new Error(`openclaw-skill/ directory not found in ${this.projectRoot}`) |
34 | | - } |
| 31 | + // 1. Resolve and verify openclaw-skill/ exists |
| 32 | + const skillSourceDir = await this.resolveSkillDir() |
| 33 | + logger.debug(`Resolved openclaw-skill directory: ${skillSourceDir}`) |
35 | 34 |
|
36 | 35 | // 2. Check ~/.openclaw exists |
37 | 36 | const openclawHome = path.join(os.homedir(), '.openclaw') |
@@ -77,6 +76,37 @@ export class OpenclawCommand { |
77 | 76 | } |
78 | 77 | } |
79 | 78 |
|
| 79 | + /** |
| 80 | + * Resolve the openclaw-skill directory. |
| 81 | + * 1. Check relative to the package install location (dist/openclaw-skill/ for npm installs) |
| 82 | + * 2. Fall back to projectRoot/openclaw-skill/ (for local dev / repo clones) |
| 83 | + * 3. Throw if neither exists |
| 84 | + */ |
| 85 | + private async resolveSkillDir(): Promise<string> { |
| 86 | + // 1. Relative to the installed package (works for npm installs) |
| 87 | + const __filename = fileURLToPath(import.meta.url) |
| 88 | + const __dirname = path.dirname(__filename) |
| 89 | + // In dist: dist/commands/openclaw.js -> walk up to dist/, then openclaw-skill/ |
| 90 | + let packageDir = __dirname |
| 91 | + while (packageDir !== path.dirname(packageDir)) { |
| 92 | + const candidate = path.join(packageDir, 'openclaw-skill') |
| 93 | + if (await fs.pathExists(candidate)) { |
| 94 | + return candidate |
| 95 | + } |
| 96 | + packageDir = path.dirname(packageDir) |
| 97 | + } |
| 98 | + |
| 99 | + // 2. Relative to projectRoot (for local dev / repo clones) |
| 100 | + const devCandidate = path.join(this.projectRoot, 'openclaw-skill') |
| 101 | + if (await fs.pathExists(devCandidate)) { |
| 102 | + return devCandidate |
| 103 | + } |
| 104 | + |
| 105 | + throw new Error( |
| 106 | + `openclaw-skill/ directory not found. Searched from package location (${__dirname}) and project root (${this.projectRoot}).` |
| 107 | + ) |
| 108 | + } |
| 109 | + |
80 | 110 | /** |
81 | 111 | * Handle an existing file/symlink at the target path. |
82 | 112 | * Returns true if already correctly linked (no action needed). |
|
0 commit comments