@lightdash/cli 0.2395.0 → 0.2397.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/.tsbuildinfo +1 -1
- package/dist/handlers/installSkills.d.ts +10 -0
- package/dist/handlers/installSkills.d.ts.map +1 -0
- package/dist/handlers/installSkills.js +198 -0
- package/dist/handlers/sql.d.ts +9 -0
- package/dist/handlers/sql.d.ts.map +1 -0
- package/dist/handlers/sql.js +115 -0
- package/dist/index.js +40 -0
- package/package.json +3 -3
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type AgentType = 'claude' | 'cursor' | 'codex';
|
|
2
|
+
type InstallSkillsOptions = {
|
|
3
|
+
verbose: boolean;
|
|
4
|
+
agent: AgentType;
|
|
5
|
+
global: boolean;
|
|
6
|
+
path?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare const installSkillsHandler: (options: InstallSkillsOptions) => Promise<void>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=installSkills.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installSkills.d.ts","sourceRoot":"","sources":["../../src/handlers/installSkills.ts"],"names":[],"mappings":"AAYA,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;AAsMF,eAAO,MAAM,oBAAoB,YACpB,oBAAoB,KAC9B,OAAO,CAAC,IAAI,CA8Dd,CAAC"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.installSkillsHandler = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
6
|
+
const node_fetch_1 = tslib_1.__importDefault(require("node-fetch"));
|
|
7
|
+
const os = tslib_1.__importStar(require("os"));
|
|
8
|
+
const path = tslib_1.__importStar(require("path"));
|
|
9
|
+
const globalState_1 = tslib_1.__importDefault(require("../globalState"));
|
|
10
|
+
const styles = tslib_1.__importStar(require("../styles"));
|
|
11
|
+
const GITHUB_API_BASE = 'https://api.github.com/repos/lightdash/lightdash/contents';
|
|
12
|
+
const GITHUB_RAW_BASE = 'https://raw.githubusercontent.com/lightdash/lightdash/main';
|
|
13
|
+
function getAgentSkillsDir(agent) {
|
|
14
|
+
switch (agent) {
|
|
15
|
+
case 'claude':
|
|
16
|
+
return '.claude/skills';
|
|
17
|
+
case 'cursor':
|
|
18
|
+
return '.cursor/skills';
|
|
19
|
+
case 'codex':
|
|
20
|
+
return '.codex/skills';
|
|
21
|
+
default:
|
|
22
|
+
throw new Error(`Unknown agent type: ${agent}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function findGitRoot(startDir) {
|
|
26
|
+
let currentDir = startDir;
|
|
27
|
+
while (currentDir !== path.parse(currentDir).root) {
|
|
28
|
+
if (fs.existsSync(path.join(currentDir, '.git'))) {
|
|
29
|
+
return currentDir;
|
|
30
|
+
}
|
|
31
|
+
currentDir = path.dirname(currentDir);
|
|
32
|
+
}
|
|
33
|
+
// Check root directory
|
|
34
|
+
if (fs.existsSync(path.join(currentDir, '.git'))) {
|
|
35
|
+
return currentDir;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
function getInstallPath(options) {
|
|
40
|
+
const skillsDir = getAgentSkillsDir(options.agent);
|
|
41
|
+
// If explicit path provided, use it
|
|
42
|
+
if (options.path) {
|
|
43
|
+
return path.join(options.path, skillsDir);
|
|
44
|
+
}
|
|
45
|
+
// If global, use home directory
|
|
46
|
+
if (options.global) {
|
|
47
|
+
return path.join(os.homedir(), skillsDir);
|
|
48
|
+
}
|
|
49
|
+
// Project install: find git root
|
|
50
|
+
const cwd = process.cwd();
|
|
51
|
+
const gitRoot = findGitRoot(cwd);
|
|
52
|
+
if (gitRoot) {
|
|
53
|
+
globalState_1.default.debug(`> Found git root at: ${gitRoot}`);
|
|
54
|
+
return path.join(gitRoot, skillsDir);
|
|
55
|
+
}
|
|
56
|
+
// No git root found, use current directory
|
|
57
|
+
globalState_1.default.debug(`> No git root found, using current directory: ${cwd}`);
|
|
58
|
+
return path.join(cwd, skillsDir);
|
|
59
|
+
}
|
|
60
|
+
async function fetchGitHubDirectory(repoPath) {
|
|
61
|
+
const url = `${GITHUB_API_BASE}/${repoPath}`;
|
|
62
|
+
globalState_1.default.debug(`> Fetching GitHub directory: ${url}`);
|
|
63
|
+
const response = await (0, node_fetch_1.default)(url, {
|
|
64
|
+
headers: {
|
|
65
|
+
Accept: 'application/vnd.github.v3+json',
|
|
66
|
+
'User-Agent': 'lightdash-cli',
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
throw new Error(`Failed to fetch GitHub directory: ${response.status} ${response.statusText}`);
|
|
71
|
+
}
|
|
72
|
+
return response.json();
|
|
73
|
+
}
|
|
74
|
+
async function fetchFileContent(downloadUrl) {
|
|
75
|
+
globalState_1.default.debug(`> Fetching file: ${downloadUrl}`);
|
|
76
|
+
const response = await (0, node_fetch_1.default)(downloadUrl, {
|
|
77
|
+
headers: {
|
|
78
|
+
'User-Agent': 'lightdash-cli',
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
if (!response.ok) {
|
|
82
|
+
throw new Error(`Failed to fetch file: ${response.status} ${response.statusText}`);
|
|
83
|
+
}
|
|
84
|
+
return response.text();
|
|
85
|
+
}
|
|
86
|
+
async function resolveSymlinkTarget(symlinkPath, target) {
|
|
87
|
+
// Resolve the symlink relative to its location
|
|
88
|
+
const symlinkDir = path.dirname(symlinkPath);
|
|
89
|
+
const resolvedPath = path.posix.normalize(path.posix.join(symlinkDir, target));
|
|
90
|
+
return resolvedPath;
|
|
91
|
+
}
|
|
92
|
+
/* eslint-disable no-await-in-loop */
|
|
93
|
+
// Sequential downloads are intentional to avoid GitHub rate limits and handle symlinks
|
|
94
|
+
async function downloadSkillFiles(repoPath, localDir, visited = new Set()) {
|
|
95
|
+
// Prevent infinite loops with symlinks
|
|
96
|
+
if (visited.has(repoPath)) {
|
|
97
|
+
globalState_1.default.debug(`> Skipping already visited path: ${repoPath}`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
visited.add(repoPath);
|
|
101
|
+
const items = await fetchGitHubDirectory(repoPath);
|
|
102
|
+
for (const item of items) {
|
|
103
|
+
const localPath = path.join(localDir, item.name);
|
|
104
|
+
if (item.type === 'dir') {
|
|
105
|
+
fs.mkdirSync(localPath, { recursive: true });
|
|
106
|
+
await downloadSkillFiles(item.path, localPath, visited);
|
|
107
|
+
}
|
|
108
|
+
else if (item.type === 'symlink' && item.target) {
|
|
109
|
+
// Resolve the symlink and fetch the actual content
|
|
110
|
+
const resolvedPath = await resolveSymlinkTarget(item.path, item.target);
|
|
111
|
+
globalState_1.default.debug(`> Resolving symlink: ${item.path} -> ${resolvedPath}`);
|
|
112
|
+
// Check if the target is a directory or file by fetching it
|
|
113
|
+
try {
|
|
114
|
+
const targetItems = await fetchGitHubDirectory(resolvedPath);
|
|
115
|
+
// It's a directory, create it and download contents
|
|
116
|
+
fs.mkdirSync(localPath, { recursive: true });
|
|
117
|
+
for (const targetItem of targetItems) {
|
|
118
|
+
const targetLocalPath = path.join(localPath, targetItem.name);
|
|
119
|
+
if (targetItem.type === 'dir') {
|
|
120
|
+
fs.mkdirSync(targetLocalPath, { recursive: true });
|
|
121
|
+
await downloadSkillFiles(targetItem.path, targetLocalPath, visited);
|
|
122
|
+
}
|
|
123
|
+
else if (targetItem.download_url) {
|
|
124
|
+
const content = await fetchFileContent(targetItem.download_url);
|
|
125
|
+
fs.writeFileSync(targetLocalPath, content);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// It's a file, fetch its content directly
|
|
131
|
+
const downloadUrl = `${GITHUB_RAW_BASE}/${resolvedPath}`;
|
|
132
|
+
const content = await fetchFileContent(downloadUrl);
|
|
133
|
+
// Ensure parent directory exists
|
|
134
|
+
fs.mkdirSync(path.dirname(localPath), { recursive: true });
|
|
135
|
+
fs.writeFileSync(localPath, content);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else if (item.type === 'file' && item.download_url) {
|
|
139
|
+
const content = await fetchFileContent(item.download_url);
|
|
140
|
+
// Ensure parent directory exists
|
|
141
|
+
fs.mkdirSync(path.dirname(localPath), { recursive: true });
|
|
142
|
+
fs.writeFileSync(localPath, content);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/* eslint-enable no-await-in-loop */
|
|
147
|
+
async function listAvailableSkills() {
|
|
148
|
+
const items = await fetchGitHubDirectory('skills');
|
|
149
|
+
return items.filter((item) => item.type === 'dir').map((item) => item.name);
|
|
150
|
+
}
|
|
151
|
+
const installSkillsHandler = async (options) => {
|
|
152
|
+
globalState_1.default.setVerbose(options.verbose);
|
|
153
|
+
const installPath = getInstallPath(options);
|
|
154
|
+
console.error(styles.title('\n⚡ Lightdash Skills Installer\n'));
|
|
155
|
+
console.error(`Agent: ${styles.bold(options.agent)}`);
|
|
156
|
+
console.error(`Scope: ${styles.bold(options.global ? 'global' : 'project')}`);
|
|
157
|
+
console.error(`Install path: ${styles.bold(installPath)}\n`);
|
|
158
|
+
const spinner = globalState_1.default.startSpinner('Fetching available skills...');
|
|
159
|
+
try {
|
|
160
|
+
const skills = await listAvailableSkills();
|
|
161
|
+
if (skills.length === 0) {
|
|
162
|
+
spinner.fail('No skills found in the repository');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
spinner.text = `Found ${skills.length} skill(s): ${skills.join(', ')}`;
|
|
166
|
+
spinner.succeed();
|
|
167
|
+
// Create install directory
|
|
168
|
+
fs.mkdirSync(installPath, { recursive: true });
|
|
169
|
+
// Install skills sequentially to provide meaningful progress feedback
|
|
170
|
+
/* eslint-disable no-await-in-loop */
|
|
171
|
+
for (const skill of skills) {
|
|
172
|
+
const skillSpinner = globalState_1.default.startSpinner(`Installing skill: ${skill}...`);
|
|
173
|
+
const skillLocalPath = path.join(installPath, skill);
|
|
174
|
+
try {
|
|
175
|
+
// Remove existing skill directory if it exists
|
|
176
|
+
if (fs.existsSync(skillLocalPath)) {
|
|
177
|
+
fs.rmSync(skillLocalPath, { recursive: true, force: true });
|
|
178
|
+
}
|
|
179
|
+
fs.mkdirSync(skillLocalPath, { recursive: true });
|
|
180
|
+
await downloadSkillFiles(`skills/${skill}`, skillLocalPath);
|
|
181
|
+
skillSpinner.succeed(`Installed skill: ${skill}`);
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
185
|
+
skillSpinner.fail(`Failed to install skill ${skill}: ${errorMessage}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/* eslint-enable no-await-in-loop */
|
|
189
|
+
console.error(styles.success('\n✓ Skills installed successfully!\n'));
|
|
190
|
+
console.error(`Skills are available at: ${styles.bold(installPath)}\n`);
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
194
|
+
spinner.fail(`Failed to fetch skills: ${errorMessage}`);
|
|
195
|
+
throw err;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
exports.installSkillsHandler = installSkillsHandler;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../src/handlers/sql.ts"],"names":[],"mappings":"AAYA,KAAK,iBAAiB,GAAG;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AA0FF,eAAO,MAAM,UAAU,QACd,MAAM,WACF,iBAAiB,KAC3B,OAAO,CAAC,IAAI,CAiEd,CAAC"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sqlHandler = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const common_1 = require("@lightdash/common");
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const config_1 = require("../config");
|
|
8
|
+
const globalState_1 = tslib_1.__importDefault(require("../globalState"));
|
|
9
|
+
const styles = tslib_1.__importStar(require("../styles"));
|
|
10
|
+
const apiClient_1 = require("./dbt/apiClient");
|
|
11
|
+
const DEFAULT_PAGE_SIZE = 500;
|
|
12
|
+
const POLL_INTERVAL_MS = 500;
|
|
13
|
+
/**
|
|
14
|
+
* Convert ResultRow array to CSV string
|
|
15
|
+
*/
|
|
16
|
+
function resultsToCsv(columns, rows) {
|
|
17
|
+
// Escape CSV value: wrap in quotes if contains comma, quote, or newline
|
|
18
|
+
const escapeValue = (value) => {
|
|
19
|
+
if (value === null || value === undefined) {
|
|
20
|
+
return '';
|
|
21
|
+
}
|
|
22
|
+
const str = String(value);
|
|
23
|
+
if (str.includes(',') || str.includes('"') || str.includes('\n')) {
|
|
24
|
+
return `"${str.replace(/"/g, '""')}"`;
|
|
25
|
+
}
|
|
26
|
+
return str;
|
|
27
|
+
};
|
|
28
|
+
const header = columns.map(escapeValue).join(',');
|
|
29
|
+
const dataRows = rows.map((row) => columns.map((col) => escapeValue(row[col]?.value?.raw)).join(','));
|
|
30
|
+
return [header, ...dataRows].join('\n');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Fetch query results once
|
|
34
|
+
*/
|
|
35
|
+
async function fetchQueryResults(projectUuid, queryUuid, pageSize) {
|
|
36
|
+
const url = `/api/v2/projects/${projectUuid}/query/${queryUuid}?pageSize=${pageSize}`;
|
|
37
|
+
return (0, apiClient_1.lightdashApi)({
|
|
38
|
+
method: 'GET',
|
|
39
|
+
url,
|
|
40
|
+
body: undefined,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Sleep for a given duration
|
|
45
|
+
*/
|
|
46
|
+
function sleep(ms) {
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
setTimeout(resolve, ms);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Poll for query results until status is READY or ERROR
|
|
53
|
+
*/
|
|
54
|
+
async function pollQueryResults(projectUuid, queryUuid, pageSize) {
|
|
55
|
+
const poll = async () => {
|
|
56
|
+
const result = await fetchQueryResults(projectUuid, queryUuid, pageSize);
|
|
57
|
+
globalState_1.default.debug(`> Query status: ${result.status}`);
|
|
58
|
+
if (result.status === common_1.QueryHistoryStatus.READY) {
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
if (result.status === common_1.QueryHistoryStatus.ERROR) {
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
if (result.status === common_1.QueryHistoryStatus.CANCELLED) {
|
|
65
|
+
throw new Error('Query was cancelled');
|
|
66
|
+
}
|
|
67
|
+
// Still pending, wait and poll again
|
|
68
|
+
await sleep(POLL_INTERVAL_MS);
|
|
69
|
+
return poll();
|
|
70
|
+
};
|
|
71
|
+
return poll();
|
|
72
|
+
}
|
|
73
|
+
const sqlHandler = async (sql, options) => {
|
|
74
|
+
globalState_1.default.setVerbose(options.verbose ?? false);
|
|
75
|
+
const config = await (0, config_1.getConfig)();
|
|
76
|
+
const projectUuid = config.context?.project;
|
|
77
|
+
if (!projectUuid) {
|
|
78
|
+
throw new Error(`No project selected. Run 'lightdash config set-project' first.`);
|
|
79
|
+
}
|
|
80
|
+
globalState_1.default.debug(`> Running SQL query against project: ${projectUuid}`);
|
|
81
|
+
globalState_1.default.debug(`> SQL: ${sql}`);
|
|
82
|
+
const pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;
|
|
83
|
+
// Submit the query
|
|
84
|
+
const spinner = globalState_1.default.startSpinner('Submitting SQL query...');
|
|
85
|
+
const submitResult = await (0, apiClient_1.lightdashApi)({
|
|
86
|
+
method: 'POST',
|
|
87
|
+
url: `/api/v2/projects/${projectUuid}/query/sql`,
|
|
88
|
+
body: JSON.stringify({
|
|
89
|
+
sql,
|
|
90
|
+
limit: options.limit,
|
|
91
|
+
context: 'cli',
|
|
92
|
+
}),
|
|
93
|
+
});
|
|
94
|
+
globalState_1.default.debug(`> Query UUID: ${submitResult.queryUuid}`);
|
|
95
|
+
spinner.text = 'Waiting for query results...';
|
|
96
|
+
// Poll for results
|
|
97
|
+
const result = await pollQueryResults(projectUuid, submitResult.queryUuid, pageSize);
|
|
98
|
+
if (result.status === common_1.QueryHistoryStatus.ERROR) {
|
|
99
|
+
spinner.fail('Query failed');
|
|
100
|
+
throw new Error(result.error ?? 'Query execution failed');
|
|
101
|
+
}
|
|
102
|
+
if (result.status !== common_1.QueryHistoryStatus.READY) {
|
|
103
|
+
spinner.fail('Unexpected query status');
|
|
104
|
+
throw new Error(`Unexpected query status: ${result.status}`);
|
|
105
|
+
}
|
|
106
|
+
// Extract column names from the columns metadata
|
|
107
|
+
const columns = Object.keys(result.columns);
|
|
108
|
+
const rowCount = result.rows.length;
|
|
109
|
+
spinner.text = `Writing ${rowCount} rows to ${options.output}...`;
|
|
110
|
+
// Convert to CSV and write to file
|
|
111
|
+
const csv = resultsToCsv(columns, result.rows);
|
|
112
|
+
await fs_1.promises.writeFile(options.output, csv, 'utf8');
|
|
113
|
+
spinner.succeed(`${styles.success('Success!')} Wrote ${rowCount} rows to ${options.output}`);
|
|
114
|
+
};
|
|
115
|
+
exports.sqlHandler = sqlHandler;
|
package/dist/index.js
CHANGED
|
@@ -15,12 +15,14 @@ const download_1 = require("./handlers/download");
|
|
|
15
15
|
const generate_1 = require("./handlers/generate");
|
|
16
16
|
const generateExposures_1 = require("./handlers/generateExposures");
|
|
17
17
|
const getProject_1 = require("./handlers/getProject");
|
|
18
|
+
const installSkills_1 = require("./handlers/installSkills");
|
|
18
19
|
const lint_1 = require("./handlers/lint");
|
|
19
20
|
const listProjects_1 = require("./handlers/listProjects");
|
|
20
21
|
const login_1 = require("./handlers/login");
|
|
21
22
|
const preview_1 = require("./handlers/preview");
|
|
22
23
|
const renameHandler_1 = require("./handlers/renameHandler");
|
|
23
24
|
const setProject_1 = require("./handlers/setProject");
|
|
25
|
+
const sql_1 = require("./handlers/sql");
|
|
24
26
|
const validate_1 = require("./handlers/validate");
|
|
25
27
|
const styles = tslib_1.__importStar(require("./styles"));
|
|
26
28
|
// Trigger CLI tests
|
|
@@ -423,6 +425,44 @@ ${styles.bold('Examples:')}
|
|
|
423
425
|
.option('--verbose', 'Show detailed output', false)
|
|
424
426
|
.option('-f, --format <format>', 'Output format: cli (default) or json (SARIF format)', 'cli')
|
|
425
427
|
.action(lint_1.lintHandler);
|
|
428
|
+
commander_1.program
|
|
429
|
+
.command('sql')
|
|
430
|
+
.description('Run raw SQL query against the warehouse using project credentials')
|
|
431
|
+
.argument('<query>', 'SQL query to execute')
|
|
432
|
+
.requiredOption('-o, --output <file>', 'Output file path for CSV results')
|
|
433
|
+
.option('--limit <number>', 'Maximum rows to return from query', parseIntArgument)
|
|
434
|
+
.option('--page-size <number>', 'Number of rows per page (default: 500, max: 5000)', parseIntArgument)
|
|
435
|
+
.option('--verbose', 'Show detailed output', false)
|
|
436
|
+
.action(sql_1.sqlHandler);
|
|
437
|
+
commander_1.program
|
|
438
|
+
.command('install-skills')
|
|
439
|
+
.description('Installs Lightdash skills for AI coding assistants (Claude, Cursor, Codex)')
|
|
440
|
+
.addHelpText('after', `
|
|
441
|
+
${styles.bold('Examples:')}
|
|
442
|
+
${styles.title('⚡')}️lightdash ${styles.bold('install-skills')} ${styles.secondary('-- installs skills for Claude at git root (default)')}
|
|
443
|
+
${styles.title('⚡')}️lightdash ${styles.bold('install-skills')} --agent cursor ${styles.secondary('-- installs skills for Cursor')}
|
|
444
|
+
${styles.title('⚡')}️lightdash ${styles.bold('install-skills')} --global ${styles.secondary('-- installs skills globally to ~/.claude/skills/')}
|
|
445
|
+
${styles.title('⚡')}️lightdash ${styles.bold('install-skills')} --agent codex --global ${styles.secondary('-- installs skills globally for Codex')}
|
|
446
|
+
${styles.title('⚡')}️lightdash ${styles.bold('install-skills')} --path ./my-project ${styles.secondary('-- installs skills to a specific path')}
|
|
447
|
+
|
|
448
|
+
${styles.bold('Installation paths:')}
|
|
449
|
+
${styles.secondary('Project-level (default):')}
|
|
450
|
+
.claude/skills/ (Claude)
|
|
451
|
+
.cursor/skills/ (Cursor)
|
|
452
|
+
.codex/skills/ (Codex)
|
|
453
|
+
|
|
454
|
+
${styles.secondary('Global (--global):')}
|
|
455
|
+
~/.claude/skills/ (Claude)
|
|
456
|
+
~/.cursor/skills/ (Cursor)
|
|
457
|
+
~/.codex/skills/ (Codex)
|
|
458
|
+
`)
|
|
459
|
+
.option('--verbose', 'Show detailed output', false)
|
|
460
|
+
.addOption(new commander_1.Option('--agent <agent>', 'Target agent for skill installation')
|
|
461
|
+
.choices(['claude', 'cursor', 'codex'])
|
|
462
|
+
.default('claude'))
|
|
463
|
+
.option('--global', 'Install skills globally to home directory instead of project', false)
|
|
464
|
+
.option('--path <path>', 'Override the install path (skills directory will be created inside)', undefined)
|
|
465
|
+
.action(installSkills_1.installSkillsHandler);
|
|
426
466
|
const errorHandler = (err) => {
|
|
427
467
|
// Use error message with fallback for safety
|
|
428
468
|
const errorMessage = (0, common_1.getErrorMessage)(err) || 'An unexpected error occurred';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightdash/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2397.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"unique-names-generator": "^4.7.1",
|
|
39
39
|
"uuid": "^11.0.3",
|
|
40
40
|
"yaml": "^2.7.0",
|
|
41
|
-
"@lightdash/common": "0.
|
|
42
|
-
"@lightdash/warehouses": "0.
|
|
41
|
+
"@lightdash/common": "0.2397.0",
|
|
42
|
+
"@lightdash/warehouses": "0.2397.0"
|
|
43
43
|
},
|
|
44
44
|
"description": "Lightdash CLI tool",
|
|
45
45
|
"devDependencies": {
|