@lightdash/cli 0.2638.0 → 0.2639.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.
|
@@ -5,6 +5,7 @@ type InstallSkillsOptions = {
|
|
|
5
5
|
global: boolean;
|
|
6
6
|
path?: string;
|
|
7
7
|
};
|
|
8
|
+
export declare function getVersionWithSkills(): string;
|
|
8
9
|
export declare const installSkillsHandler: (options: InstallSkillsOptions) => Promise<void>;
|
|
9
10
|
export {};
|
|
10
11
|
//# sourceMappingURL=installSkills.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installSkills.d.ts","sourceRoot":"","sources":["../../src/handlers/installSkills.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"installSkills.d.ts","sourceRoot":"","sources":["../../src/handlers/installSkills.ts"],"names":[],"mappings":"AAgBA,KAAK,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE/C,KAAK,oBAAoB,GAAG;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AA8SF,wBAAgB,oBAAoB,IAAI,MAAM,CA8B7C;AAED,eAAO,MAAM,oBAAoB,GAC7B,SAAS,oBAAoB,KAC9B,OAAO,CAAC,IAAI,CA8Ed,CAAC"}
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.installSkillsHandler = void 0;
|
|
4
|
+
exports.getVersionWithSkills = getVersionWithSkills;
|
|
4
5
|
const tslib_1 = require("tslib");
|
|
5
6
|
const fs = tslib_1.__importStar(require("fs"));
|
|
6
7
|
const node_fetch_1 = tslib_1.__importDefault(require("node-fetch"));
|
|
7
8
|
const os = tslib_1.__importStar(require("os"));
|
|
8
9
|
const path = tslib_1.__importStar(require("path"));
|
|
9
10
|
const analytics_1 = require("../analytics/analytics");
|
|
11
|
+
const env_1 = require("../env");
|
|
10
12
|
const globalState_1 = tslib_1.__importDefault(require("../globalState"));
|
|
11
13
|
const styles = tslib_1.__importStar(require("../styles"));
|
|
14
|
+
const SKILL_MANIFEST_FILENAME = '.lightdash-skill-manifest.json';
|
|
12
15
|
const GITHUB_API_BASE = 'https://api.github.com/repos/lightdash/lightdash/contents';
|
|
13
|
-
const GITHUB_RAW_BASE =
|
|
16
|
+
const GITHUB_RAW_BASE = `https://raw.githubusercontent.com/lightdash/lightdash/${env_1.CLI_VERSION}`;
|
|
14
17
|
function getAgentSkillsDir(agent) {
|
|
15
18
|
switch (agent) {
|
|
16
19
|
case 'claude':
|
|
@@ -59,7 +62,7 @@ function getInstallPath(options) {
|
|
|
59
62
|
return path.join(cwd, skillsDir);
|
|
60
63
|
}
|
|
61
64
|
async function fetchGitHubDirectory(repoPath) {
|
|
62
|
-
const url = `${GITHUB_API_BASE}/${repoPath}`;
|
|
65
|
+
const url = `${GITHUB_API_BASE}/${repoPath}?ref=${env_1.CLI_VERSION}`;
|
|
63
66
|
globalState_1.default.debug(`> Fetching GitHub directory: ${url}`);
|
|
64
67
|
const response = await (0, node_fetch_1.default)(url, {
|
|
65
68
|
headers: {
|
|
@@ -88,6 +91,9 @@ function resolveSymlinkTarget(symlinkPath, target) {
|
|
|
88
91
|
const symlinkDir = path.posix.dirname(symlinkPath);
|
|
89
92
|
return path.posix.normalize(path.posix.join(symlinkDir, target.trim()));
|
|
90
93
|
}
|
|
94
|
+
function interpolateVersionPlaceholders(content) {
|
|
95
|
+
return content.replace(/\{\{CLI_VERSION\}\}/g, env_1.CLI_VERSION);
|
|
96
|
+
}
|
|
91
97
|
/* eslint-disable no-await-in-loop */
|
|
92
98
|
// Sequential downloads are intentional to avoid GitHub rate limits and handle symlinks
|
|
93
99
|
async function downloadSkillFiles(repoPath, localDir, visited = new Set()) {
|
|
@@ -121,7 +127,7 @@ async function downloadSkillFiles(repoPath, localDir, visited = new Set()) {
|
|
|
121
127
|
}
|
|
122
128
|
else if (targetItem.download_url) {
|
|
123
129
|
const content = await fetchFileContent(targetItem.download_url);
|
|
124
|
-
fs.writeFileSync(targetLocalPath, content);
|
|
130
|
+
fs.writeFileSync(targetLocalPath, interpolateVersionPlaceholders(content));
|
|
125
131
|
}
|
|
126
132
|
}
|
|
127
133
|
}
|
|
@@ -131,7 +137,7 @@ async function downloadSkillFiles(repoPath, localDir, visited = new Set()) {
|
|
|
131
137
|
const content = await fetchFileContent(downloadUrl);
|
|
132
138
|
// Ensure parent directory exists
|
|
133
139
|
fs.mkdirSync(path.dirname(localPath), { recursive: true });
|
|
134
|
-
fs.writeFileSync(localPath, content);
|
|
140
|
+
fs.writeFileSync(localPath, interpolateVersionPlaceholders(content));
|
|
135
141
|
}
|
|
136
142
|
}
|
|
137
143
|
else if (item.type === 'file' && item.download_url) {
|
|
@@ -143,7 +149,7 @@ async function downloadSkillFiles(repoPath, localDir, visited = new Set()) {
|
|
|
143
149
|
content = await fetchFileContent(`${GITHUB_RAW_BASE}/${resolvedPath}`);
|
|
144
150
|
}
|
|
145
151
|
fs.mkdirSync(path.dirname(localPath), { recursive: true });
|
|
146
|
-
fs.writeFileSync(localPath, content);
|
|
152
|
+
fs.writeFileSync(localPath, interpolateVersionPlaceholders(content));
|
|
147
153
|
}
|
|
148
154
|
}
|
|
149
155
|
}
|
|
@@ -152,6 +158,95 @@ async function listAvailableSkills() {
|
|
|
152
158
|
const items = await fetchGitHubDirectory('skills');
|
|
153
159
|
return items.filter((item) => item.type === 'dir').map((item) => item.name);
|
|
154
160
|
}
|
|
161
|
+
function writeSkillManifest(skillDir) {
|
|
162
|
+
const manifest = {
|
|
163
|
+
version: env_1.CLI_VERSION,
|
|
164
|
+
installed_at: new Date().toISOString(),
|
|
165
|
+
};
|
|
166
|
+
fs.writeFileSync(path.join(skillDir, SKILL_MANIFEST_FILENAME), JSON.stringify(manifest, null, 2));
|
|
167
|
+
}
|
|
168
|
+
function readSkillManifest(skillDir) {
|
|
169
|
+
const manifestPath = path.join(skillDir, SKILL_MANIFEST_FILENAME);
|
|
170
|
+
if (!fs.existsSync(manifestPath)) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
return JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function findInstalledSkills() {
|
|
181
|
+
const agents = ['claude', 'cursor', 'codex'];
|
|
182
|
+
const results = [];
|
|
183
|
+
const roots = [
|
|
184
|
+
{ path: os.homedir(), scope: 'global' },
|
|
185
|
+
];
|
|
186
|
+
const cwd = process.cwd();
|
|
187
|
+
const gitRoot = findGitRoot(cwd);
|
|
188
|
+
roots.push({ path: gitRoot || cwd, scope: 'project' });
|
|
189
|
+
for (const root of roots) {
|
|
190
|
+
for (const agent of agents) {
|
|
191
|
+
const skillsDir = path.join(root.path, getAgentSkillsDir(agent));
|
|
192
|
+
if (!fs.existsSync(skillsDir)) {
|
|
193
|
+
// eslint-disable-next-line no-continue
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
let entries;
|
|
197
|
+
try {
|
|
198
|
+
entries = fs.readdirSync(skillsDir);
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
// eslint-disable-next-line no-continue
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
entries
|
|
205
|
+
.filter((e) => fs.statSync(path.join(skillsDir, e)).isDirectory())
|
|
206
|
+
.forEach((entry) => {
|
|
207
|
+
const manifest = readSkillManifest(path.join(skillsDir, entry));
|
|
208
|
+
if (manifest) {
|
|
209
|
+
results.push({
|
|
210
|
+
name: entry,
|
|
211
|
+
version: manifest.version,
|
|
212
|
+
agent,
|
|
213
|
+
scope: root.scope,
|
|
214
|
+
isOutdated: manifest.version !== env_1.CLI_VERSION,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return results;
|
|
221
|
+
}
|
|
222
|
+
function getVersionWithSkills() {
|
|
223
|
+
const lines = [env_1.CLI_VERSION];
|
|
224
|
+
const skills = findInstalledSkills();
|
|
225
|
+
if (skills.length > 0) {
|
|
226
|
+
lines.push('');
|
|
227
|
+
lines.push('Installed skills:');
|
|
228
|
+
for (const skill of skills) {
|
|
229
|
+
const line = ` ${skill.name} v${skill.version} [${skill.agent}, ${skill.scope}]`;
|
|
230
|
+
lines.push(skill.isOutdated ? styles.warning(line) : line);
|
|
231
|
+
}
|
|
232
|
+
const outdated = skills.filter((s) => s.isOutdated);
|
|
233
|
+
if (outdated.length > 0) {
|
|
234
|
+
lines.push('');
|
|
235
|
+
lines.push(styles.warning('Update with:'));
|
|
236
|
+
const seen = new Set();
|
|
237
|
+
for (const skill of outdated) {
|
|
238
|
+
const globalFlag = skill.scope === 'global' ? ' --global' : '';
|
|
239
|
+
const agentFlag = ` --agent ${skill.agent}`;
|
|
240
|
+
const cmd = `lightdash install-skills${globalFlag}${agentFlag}`;
|
|
241
|
+
if (!seen.has(cmd)) {
|
|
242
|
+
seen.add(cmd);
|
|
243
|
+
lines.push(styles.warning(` ${cmd}`));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return lines.join('\n');
|
|
249
|
+
}
|
|
155
250
|
const installSkillsHandler = async (options) => {
|
|
156
251
|
const startTime = Date.now();
|
|
157
252
|
let success = true;
|
|
@@ -160,6 +255,7 @@ const installSkillsHandler = async (options) => {
|
|
|
160
255
|
console.error(styles.title('\n⚡ Lightdash Skills Installer\n'));
|
|
161
256
|
console.error(`Agent: ${styles.bold(options.agent)}`);
|
|
162
257
|
console.error(`Scope: ${styles.bold(options.global ? 'global' : 'project')}`);
|
|
258
|
+
console.error(`Version: ${styles.bold(env_1.CLI_VERSION)}`);
|
|
163
259
|
console.error(`Install path: ${styles.bold(installPath)}\n`);
|
|
164
260
|
const spinner = globalState_1.default.startSpinner('Fetching available skills...');
|
|
165
261
|
try {
|
|
@@ -184,7 +280,8 @@ const installSkillsHandler = async (options) => {
|
|
|
184
280
|
}
|
|
185
281
|
fs.mkdirSync(skillLocalPath, { recursive: true });
|
|
186
282
|
await downloadSkillFiles(`skills/${skill}`, skillLocalPath);
|
|
187
|
-
|
|
283
|
+
writeSkillManifest(skillLocalPath);
|
|
284
|
+
skillSpinner.succeed(`Installed skill: ${skill} (v${env_1.CLI_VERSION})`);
|
|
188
285
|
}
|
|
189
286
|
catch (err) {
|
|
190
287
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
package/dist/index.js
CHANGED
|
@@ -68,7 +68,7 @@ function parseProjectArgument(value) {
|
|
|
68
68
|
return value;
|
|
69
69
|
}
|
|
70
70
|
commander_1.program
|
|
71
|
-
.version(
|
|
71
|
+
.version((0, installSkills_1.getVersionWithSkills)())
|
|
72
72
|
.name(styles.title('⚡️lightdash'))
|
|
73
73
|
.description('Developer tools for dbt and Lightdash.\nSee https://docs.lightdash.com for more help and examples')
|
|
74
74
|
.option('--non-interactive', 'Disable all interactive prompts. Commands fail with helpful error if required input is missing.')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightdash/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2639.0",
|
|
4
4
|
"description": "Lightdash CLI tool",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"unique-names-generator": "^4.7.1",
|
|
41
41
|
"uuid": "^11.0.3",
|
|
42
42
|
"yaml": "^2.7.0",
|
|
43
|
-
"@lightdash/
|
|
44
|
-
"@lightdash/
|
|
43
|
+
"@lightdash/common": "0.2639.0",
|
|
44
|
+
"@lightdash/warehouses": "0.2639.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/inquirer": "^8.2.1",
|