@claryai/cli 0.1.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/LICENSE +25 -0
- package/README.md +197 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/ajv.d.ts +3 -0
- package/dist/ajv.d.ts.map +1 -0
- package/dist/ajv.js +13 -0
- package/dist/analytics/analytics.d.ts +370 -0
- package/dist/analytics/analytics.d.ts.map +1 -0
- package/dist/analytics/analytics.js +143 -0
- package/dist/config.d.ts +34 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +134 -0
- package/dist/dbt/context.d.ts +14 -0
- package/dist/dbt/context.d.ts.map +1 -0
- package/dist/dbt/context.js +76 -0
- package/dist/dbt/context.test.d.ts +2 -0
- package/dist/dbt/context.test.d.ts.map +1 -0
- package/dist/dbt/context.test.js +152 -0
- package/dist/dbt/manifest.d.ts +7 -0
- package/dist/dbt/manifest.d.ts.map +1 -0
- package/dist/dbt/manifest.js +23 -0
- package/dist/dbt/models.d.ts +43 -0
- package/dist/dbt/models.d.ts.map +1 -0
- package/dist/dbt/models.js +256 -0
- package/dist/dbt/models.test.d.ts +2 -0
- package/dist/dbt/models.test.d.ts.map +1 -0
- package/dist/dbt/models.test.js +19 -0
- package/dist/dbt/profile.d.ts +9 -0
- package/dist/dbt/profile.d.ts.map +1 -0
- package/dist/dbt/profile.js +86 -0
- package/dist/dbt/profiles.test.d.ts +2 -0
- package/dist/dbt/profiles.test.d.ts.map +1 -0
- package/dist/dbt/profiles.test.js +50 -0
- package/dist/dbt/schema.d.ts +31 -0
- package/dist/dbt/schema.d.ts.map +1 -0
- package/dist/dbt/schema.js +49 -0
- package/dist/dbt/targets/Bigquery/index.d.ts +18 -0
- package/dist/dbt/targets/Bigquery/index.d.ts.map +1 -0
- package/dist/dbt/targets/Bigquery/index.js +105 -0
- package/dist/dbt/targets/Bigquery/oauth.d.ts +2 -0
- package/dist/dbt/targets/Bigquery/oauth.d.ts.map +1 -0
- package/dist/dbt/targets/Bigquery/oauth.js +43 -0
- package/dist/dbt/targets/Bigquery/serviceAccount.d.ts +35 -0
- package/dist/dbt/targets/Bigquery/serviceAccount.d.ts.map +1 -0
- package/dist/dbt/targets/Bigquery/serviceAccount.js +149 -0
- package/dist/dbt/targets/Databricks/oauth.d.ts +21 -0
- package/dist/dbt/targets/Databricks/oauth.d.ts.map +1 -0
- package/dist/dbt/targets/Databricks/oauth.js +184 -0
- package/dist/dbt/targets/athena.d.ts +21 -0
- package/dist/dbt/targets/athena.d.ts.map +1 -0
- package/dist/dbt/targets/athena.js +91 -0
- package/dist/dbt/targets/athena.test.d.ts +2 -0
- package/dist/dbt/targets/athena.test.d.ts.map +1 -0
- package/dist/dbt/targets/athena.test.js +60 -0
- package/dist/dbt/targets/clickhouse.d.ts +24 -0
- package/dist/dbt/targets/clickhouse.d.ts.map +1 -0
- package/dist/dbt/targets/clickhouse.js +90 -0
- package/dist/dbt/targets/databricks.d.ts +27 -0
- package/dist/dbt/targets/databricks.d.ts.map +1 -0
- package/dist/dbt/targets/databricks.js +138 -0
- package/dist/dbt/targets/duckdb.d.ts +16 -0
- package/dist/dbt/targets/duckdb.d.ts.map +1 -0
- package/dist/dbt/targets/duckdb.js +63 -0
- package/dist/dbt/targets/duckdb.test.d.ts +2 -0
- package/dist/dbt/targets/duckdb.test.d.ts.map +1 -0
- package/dist/dbt/targets/duckdb.test.js +37 -0
- package/dist/dbt/targets/postgres.d.ts +26 -0
- package/dist/dbt/targets/postgres.d.ts.map +1 -0
- package/dist/dbt/targets/postgres.js +142 -0
- package/dist/dbt/targets/redshift.d.ts +23 -0
- package/dist/dbt/targets/redshift.d.ts.map +1 -0
- package/dist/dbt/targets/redshift.js +96 -0
- package/dist/dbt/targets/snowflake.d.ts +4 -0
- package/dist/dbt/targets/snowflake.d.ts.map +1 -0
- package/dist/dbt/targets/snowflake.js +134 -0
- package/dist/dbt/targets/trino.d.ts +16 -0
- package/dist/dbt/targets/trino.d.ts.map +1 -0
- package/dist/dbt/targets/trino.js +65 -0
- package/dist/dbt/templating.d.ts +15 -0
- package/dist/dbt/templating.d.ts.map +1 -0
- package/dist/dbt/templating.js +50 -0
- package/dist/dbt/templating.test.d.ts +2 -0
- package/dist/dbt/templating.test.d.ts.map +1 -0
- package/dist/dbt/templating.test.js +51 -0
- package/dist/dbt/types.d.ts +17 -0
- package/dist/dbt/types.d.ts.map +1 -0
- package/dist/dbt/types.js +2 -0
- package/dist/dbt/validation.d.ts +9 -0
- package/dist/dbt/validation.d.ts.map +1 -0
- package/dist/dbt/validation.js +54 -0
- package/dist/env.d.ts +12 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +40 -0
- package/dist/error.d.ts +2 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +12 -0
- package/dist/globalState.d.ts +29 -0
- package/dist/globalState.d.ts.map +1 -0
- package/dist/globalState.js +67 -0
- package/dist/handlers/asyncQuery.d.ts +7 -0
- package/dist/handlers/asyncQuery.d.ts.map +1 -0
- package/dist/handlers/asyncQuery.js +50 -0
- package/dist/handlers/compile.d.ts +16 -0
- package/dist/handlers/compile.d.ts.map +1 -0
- package/dist/handlers/compile.js +277 -0
- package/dist/handlers/compile.test.d.ts +2 -0
- package/dist/handlers/compile.test.d.ts.map +1 -0
- package/dist/handlers/compile.test.js +201 -0
- package/dist/handlers/createProject.d.ts +37 -0
- package/dist/handlers/createProject.d.ts.map +1 -0
- package/dist/handlers/createProject.js +272 -0
- package/dist/handlers/dbt/apiClient.d.ts +14 -0
- package/dist/handlers/dbt/apiClient.d.ts.map +1 -0
- package/dist/handlers/dbt/apiClient.js +167 -0
- package/dist/handlers/dbt/compile.d.ts +35 -0
- package/dist/handlers/dbt/compile.d.ts.map +1 -0
- package/dist/handlers/dbt/compile.js +220 -0
- package/dist/handlers/dbt/getDbtProfileTargetName.d.ts +9 -0
- package/dist/handlers/dbt/getDbtProfileTargetName.d.ts.map +1 -0
- package/dist/handlers/dbt/getDbtProfileTargetName.js +44 -0
- package/dist/handlers/dbt/getDbtVersion.d.ts +16 -0
- package/dist/handlers/dbt/getDbtVersion.d.ts.map +1 -0
- package/dist/handlers/dbt/getDbtVersion.js +141 -0
- package/dist/handlers/dbt/getDbtVersion.mocks.d.ts +11 -0
- package/dist/handlers/dbt/getDbtVersion.mocks.d.ts.map +1 -0
- package/dist/handlers/dbt/getDbtVersion.mocks.js +70 -0
- package/dist/handlers/dbt/getDbtVersion.test.d.ts +2 -0
- package/dist/handlers/dbt/getDbtVersion.test.d.ts.map +1 -0
- package/dist/handlers/dbt/getDbtVersion.test.js +97 -0
- package/dist/handlers/dbt/getWarehouseClient.d.ts +24 -0
- package/dist/handlers/dbt/getWarehouseClient.d.ts.map +1 -0
- package/dist/handlers/dbt/getWarehouseClient.js +312 -0
- package/dist/handlers/dbt/refresh.d.ts +11 -0
- package/dist/handlers/dbt/refresh.d.ts.map +1 -0
- package/dist/handlers/dbt/refresh.js +114 -0
- package/dist/handlers/dbt/run.d.ts +14 -0
- package/dist/handlers/dbt/run.d.ts.map +1 -0
- package/dist/handlers/dbt/run.js +67 -0
- package/dist/handlers/deploy.d.ts +26 -0
- package/dist/handlers/deploy.d.ts.map +1 -0
- package/dist/handlers/deploy.js +377 -0
- package/dist/handlers/diagnostics.d.ts +11 -0
- package/dist/handlers/diagnostics.d.ts.map +1 -0
- package/dist/handlers/diagnostics.js +194 -0
- package/dist/handlers/download.d.ts +29 -0
- package/dist/handlers/download.d.ts.map +1 -0
- package/dist/handlers/download.js +955 -0
- package/dist/handlers/exportChartImage.d.ts +7 -0
- package/dist/handlers/exportChartImage.d.ts.map +1 -0
- package/dist/handlers/exportChartImage.js +33 -0
- package/dist/handlers/generate.d.ts +13 -0
- package/dist/handlers/generate.d.ts.map +1 -0
- package/dist/handlers/generate.js +159 -0
- package/dist/handlers/generateExposures.d.ts +8 -0
- package/dist/handlers/generateExposures.d.ts.map +1 -0
- package/dist/handlers/generateExposures.js +100 -0
- package/dist/handlers/getProject.d.ts +6 -0
- package/dist/handlers/getProject.d.ts.map +1 -0
- package/dist/handlers/getProject.js +43 -0
- package/dist/handlers/installSkills.d.ts +12 -0
- package/dist/handlers/installSkills.d.ts.map +1 -0
- package/dist/handlers/installSkills.js +321 -0
- package/dist/handlers/lint/ajvToSarif.d.ts +66 -0
- package/dist/handlers/lint/ajvToSarif.d.ts.map +1 -0
- package/dist/handlers/lint/ajvToSarif.js +222 -0
- package/dist/handlers/lint/sarifFormatter.d.ts +14 -0
- package/dist/handlers/lint/sarifFormatter.d.ts.map +1 -0
- package/dist/handlers/lint/sarifFormatter.js +111 -0
- package/dist/handlers/lint.d.ts +8 -0
- package/dist/handlers/lint.d.ts.map +1 -0
- package/dist/handlers/lint.js +308 -0
- package/dist/handlers/listProjects.d.ts +6 -0
- package/dist/handlers/listProjects.d.ts.map +1 -0
- package/dist/handlers/listProjects.js +53 -0
- package/dist/handlers/login/oauth.d.ts +2 -0
- package/dist/handlers/login/oauth.d.ts.map +1 -0
- package/dist/handlers/login/oauth.js +27 -0
- package/dist/handlers/login/pat.d.ts +2 -0
- package/dist/handlers/login/pat.d.ts.map +1 -0
- package/dist/handlers/login/pat.js +31 -0
- package/dist/handlers/login.d.ts +15 -0
- package/dist/handlers/login.d.ts.map +1 -0
- package/dist/handlers/login.js +239 -0
- package/dist/handlers/metadataFile.d.ts +9 -0
- package/dist/handlers/metadataFile.d.ts.map +1 -0
- package/dist/handlers/metadataFile.js +34 -0
- package/dist/handlers/oauthLogin.d.ts +6 -0
- package/dist/handlers/oauthLogin.d.ts.map +1 -0
- package/dist/handlers/oauthLogin.js +191 -0
- package/dist/handlers/preview.d.ts +29 -0
- package/dist/handlers/preview.d.ts.map +1 -0
- package/dist/handlers/preview.js +415 -0
- package/dist/handlers/renameHandler.d.ts +16 -0
- package/dist/handlers/renameHandler.d.ts.map +1 -0
- package/dist/handlers/renameHandler.js +160 -0
- package/dist/handlers/runChart.d.ts +10 -0
- package/dist/handlers/runChart.d.ts.map +1 -0
- package/dist/handlers/runChart.js +105 -0
- package/dist/handlers/selectProject.d.ts +20 -0
- package/dist/handlers/selectProject.d.ts.map +1 -0
- package/dist/handlers/selectProject.js +91 -0
- package/dist/handlers/setProject.d.ts +14 -0
- package/dist/handlers/setProject.d.ts.map +1 -0
- package/dist/handlers/setProject.js +131 -0
- package/dist/handlers/setWarehouse.d.ts +14 -0
- package/dist/handlers/setWarehouse.d.ts.map +1 -0
- package/dist/handlers/setWarehouse.js +94 -0
- package/dist/handlers/sql.d.ts +9 -0
- package/dist/handlers/sql.d.ts.map +1 -0
- package/dist/handlers/sql.js +89 -0
- package/dist/handlers/utils.d.ts +11 -0
- package/dist/handlers/utils.d.ts.map +1 -0
- package/dist/handlers/utils.js +36 -0
- package/dist/handlers/validate.d.ts +22 -0
- package/dist/handlers/validate.d.ts.map +1 -0
- package/dist/handlers/validate.js +201 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +581 -0
- package/dist/lightdash/loader.d.ts +21 -0
- package/dist/lightdash/loader.d.ts.map +1 -0
- package/dist/lightdash/loader.js +122 -0
- package/dist/lightdash/projectType.d.ts +84 -0
- package/dist/lightdash/projectType.d.ts.map +1 -0
- package/dist/lightdash/projectType.js +75 -0
- package/dist/lightdash-config/index.d.ts +2 -0
- package/dist/lightdash-config/index.d.ts.map +1 -0
- package/dist/lightdash-config/index.js +41 -0
- package/dist/lightdash-config/lightdash-config.test.d.ts +2 -0
- package/dist/lightdash-config/lightdash-config.test.d.ts.map +1 -0
- package/dist/lightdash-config/lightdash-config.test.js +70 -0
- package/dist/styles.d.ts +10 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/styles.js +14 -0
- package/entitlements.plist +33 -0
- package/package.json +71 -0
- package/track.sh +116 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.installSkillsHandler = void 0;
|
|
4
|
+
exports.getVersionWithSkills = getVersionWithSkills;
|
|
5
|
+
const tslib_1 = require("tslib");
|
|
6
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
7
|
+
const node_fetch_1 = tslib_1.__importDefault(require("node-fetch"));
|
|
8
|
+
const os = tslib_1.__importStar(require("os"));
|
|
9
|
+
const path = tslib_1.__importStar(require("path"));
|
|
10
|
+
const analytics_1 = require("../analytics/analytics");
|
|
11
|
+
const env_1 = require("../env");
|
|
12
|
+
const globalState_1 = tslib_1.__importDefault(require("../globalState"));
|
|
13
|
+
const styles = tslib_1.__importStar(require("../styles"));
|
|
14
|
+
const SKILL_MANIFEST_FILENAME = '.clary-skill-manifest.json';
|
|
15
|
+
const DEFAULT_SOURCE_REPO = 'claryai/lightdash-fork';
|
|
16
|
+
function getGitHubApiBase(repo) {
|
|
17
|
+
return `https://api.github.com/repos/${repo}/contents`;
|
|
18
|
+
}
|
|
19
|
+
function getGitHubRawBase(repo) {
|
|
20
|
+
return `https://raw.githubusercontent.com/${repo}/${env_1.CLI_VERSION}`;
|
|
21
|
+
}
|
|
22
|
+
function getAgentSkillsDir(agent) {
|
|
23
|
+
switch (agent) {
|
|
24
|
+
case 'claude':
|
|
25
|
+
return '.claude/skills';
|
|
26
|
+
case 'cursor':
|
|
27
|
+
return '.cursor/skills';
|
|
28
|
+
case 'codex':
|
|
29
|
+
return '.codex/skills';
|
|
30
|
+
default:
|
|
31
|
+
throw new Error(`Unknown agent type: ${agent}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function findGitRoot(startDir) {
|
|
35
|
+
let currentDir = startDir;
|
|
36
|
+
while (currentDir !== path.parse(currentDir).root) {
|
|
37
|
+
if (fs.existsSync(path.join(currentDir, '.git'))) {
|
|
38
|
+
return currentDir;
|
|
39
|
+
}
|
|
40
|
+
currentDir = path.dirname(currentDir);
|
|
41
|
+
}
|
|
42
|
+
// Check root directory
|
|
43
|
+
if (fs.existsSync(path.join(currentDir, '.git'))) {
|
|
44
|
+
return currentDir;
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
function getInstallPath(options) {
|
|
49
|
+
const skillsDir = getAgentSkillsDir(options.agent);
|
|
50
|
+
// If explicit path provided, use it
|
|
51
|
+
if (options.path) {
|
|
52
|
+
return path.join(options.path, skillsDir);
|
|
53
|
+
}
|
|
54
|
+
// If global, use home directory
|
|
55
|
+
if (options.global) {
|
|
56
|
+
return path.join(os.homedir(), skillsDir);
|
|
57
|
+
}
|
|
58
|
+
// Project install: find git root
|
|
59
|
+
const cwd = process.cwd();
|
|
60
|
+
const gitRoot = findGitRoot(cwd);
|
|
61
|
+
if (gitRoot) {
|
|
62
|
+
globalState_1.default.debug(`> Found git root at: ${gitRoot}`);
|
|
63
|
+
return path.join(gitRoot, skillsDir);
|
|
64
|
+
}
|
|
65
|
+
// No git root found, use current directory
|
|
66
|
+
globalState_1.default.debug(`> No git root found, using current directory: ${cwd}`);
|
|
67
|
+
return path.join(cwd, skillsDir);
|
|
68
|
+
}
|
|
69
|
+
async function fetchGitHubDirectory(repoPath, repo) {
|
|
70
|
+
const url = `${getGitHubApiBase(repo)}/${repoPath}?ref=${env_1.CLI_VERSION}`;
|
|
71
|
+
globalState_1.default.debug(`> Fetching GitHub directory: ${url}`);
|
|
72
|
+
const response = await (0, node_fetch_1.default)(url, {
|
|
73
|
+
headers: {
|
|
74
|
+
Accept: 'application/vnd.github.v3+json',
|
|
75
|
+
'User-Agent': 'clary-cli',
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
throw new Error(`Failed to fetch GitHub directory: ${response.status} ${response.statusText}`);
|
|
80
|
+
}
|
|
81
|
+
return response.json();
|
|
82
|
+
}
|
|
83
|
+
async function fetchFileContent(downloadUrl) {
|
|
84
|
+
globalState_1.default.debug(`> Fetching file: ${downloadUrl}`);
|
|
85
|
+
const response = await (0, node_fetch_1.default)(downloadUrl, {
|
|
86
|
+
headers: {
|
|
87
|
+
'User-Agent': 'clary-cli',
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
if (!response.ok) {
|
|
91
|
+
throw new Error(`Failed to fetch file: ${response.status} ${response.statusText}`);
|
|
92
|
+
}
|
|
93
|
+
return response.text();
|
|
94
|
+
}
|
|
95
|
+
function resolveSymlinkTarget(symlinkPath, target) {
|
|
96
|
+
const symlinkDir = path.posix.dirname(symlinkPath);
|
|
97
|
+
return path.posix.normalize(path.posix.join(symlinkDir, target.trim()));
|
|
98
|
+
}
|
|
99
|
+
function interpolateVersionPlaceholders(content) {
|
|
100
|
+
return content.replace(/\{\{CLI_VERSION\}\}/g, env_1.CLI_VERSION);
|
|
101
|
+
}
|
|
102
|
+
/* eslint-disable no-await-in-loop */
|
|
103
|
+
// Sequential downloads are intentional to avoid GitHub rate limits and handle symlinks
|
|
104
|
+
async function downloadSkillFiles(repoPath, localDir, repo, visited = new Set()) {
|
|
105
|
+
// Prevent infinite loops with symlinks
|
|
106
|
+
if (visited.has(repoPath)) {
|
|
107
|
+
globalState_1.default.debug(`> Skipping already visited path: ${repoPath}`);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
visited.add(repoPath);
|
|
111
|
+
const items = await fetchGitHubDirectory(repoPath, repo);
|
|
112
|
+
for (const item of items) {
|
|
113
|
+
const localPath = path.join(localDir, item.name);
|
|
114
|
+
if (item.type === 'dir') {
|
|
115
|
+
fs.mkdirSync(localPath, { recursive: true });
|
|
116
|
+
await downloadSkillFiles(item.path, localPath, repo, visited);
|
|
117
|
+
}
|
|
118
|
+
else if (item.type === 'symlink' && item.target) {
|
|
119
|
+
// Resolve the symlink and fetch the actual content
|
|
120
|
+
const resolvedPath = resolveSymlinkTarget(item.path, item.target);
|
|
121
|
+
globalState_1.default.debug(`> Resolving symlink: ${item.path} -> ${resolvedPath}`);
|
|
122
|
+
// Check if the target is a directory or file by fetching it
|
|
123
|
+
try {
|
|
124
|
+
const targetItems = await fetchGitHubDirectory(resolvedPath, repo);
|
|
125
|
+
// It's a directory, create it and download contents
|
|
126
|
+
fs.mkdirSync(localPath, { recursive: true });
|
|
127
|
+
for (const targetItem of targetItems) {
|
|
128
|
+
const targetLocalPath = path.join(localPath, targetItem.name);
|
|
129
|
+
if (targetItem.type === 'dir') {
|
|
130
|
+
fs.mkdirSync(targetLocalPath, { recursive: true });
|
|
131
|
+
await downloadSkillFiles(targetItem.path, targetLocalPath, repo, visited);
|
|
132
|
+
}
|
|
133
|
+
else if (targetItem.download_url) {
|
|
134
|
+
const content = await fetchFileContent(targetItem.download_url);
|
|
135
|
+
fs.writeFileSync(targetLocalPath, interpolateVersionPlaceholders(content));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// It's a file, fetch its content directly
|
|
141
|
+
const downloadUrl = `${getGitHubRawBase(repo)}/${resolvedPath}`;
|
|
142
|
+
const content = await fetchFileContent(downloadUrl);
|
|
143
|
+
// Ensure parent directory exists
|
|
144
|
+
fs.mkdirSync(path.dirname(localPath), { recursive: true });
|
|
145
|
+
fs.writeFileSync(localPath, interpolateVersionPlaceholders(content));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else if (item.type === 'file' && item.download_url) {
|
|
149
|
+
let content = await fetchFileContent(item.download_url);
|
|
150
|
+
// GitHub returns symlink content as the target path - resolve it
|
|
151
|
+
if (content.startsWith('../') || content.startsWith('./')) {
|
|
152
|
+
const resolvedPath = resolveSymlinkTarget(item.path, content);
|
|
153
|
+
globalState_1.default.debug(`> Resolving symlink: ${item.path} -> ${resolvedPath}`);
|
|
154
|
+
content = await fetchFileContent(`${getGitHubRawBase(repo)}/${resolvedPath}`);
|
|
155
|
+
}
|
|
156
|
+
fs.mkdirSync(path.dirname(localPath), { recursive: true });
|
|
157
|
+
fs.writeFileSync(localPath, interpolateVersionPlaceholders(content));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/* eslint-enable no-await-in-loop */
|
|
162
|
+
async function listAvailableSkills(repo) {
|
|
163
|
+
const items = await fetchGitHubDirectory('skills', repo);
|
|
164
|
+
return items.filter((item) => item.type === 'dir').map((item) => item.name);
|
|
165
|
+
}
|
|
166
|
+
function writeSkillManifest(skillDir) {
|
|
167
|
+
const manifest = {
|
|
168
|
+
version: env_1.CLI_VERSION,
|
|
169
|
+
installed_at: new Date().toISOString(),
|
|
170
|
+
};
|
|
171
|
+
fs.writeFileSync(path.join(skillDir, SKILL_MANIFEST_FILENAME), JSON.stringify(manifest, null, 2));
|
|
172
|
+
}
|
|
173
|
+
function readSkillManifest(skillDir) {
|
|
174
|
+
const manifestPath = path.join(skillDir, SKILL_MANIFEST_FILENAME);
|
|
175
|
+
if (!fs.existsSync(manifestPath)) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
return JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function findInstalledSkills() {
|
|
186
|
+
const agents = ['claude', 'cursor', 'codex'];
|
|
187
|
+
const results = [];
|
|
188
|
+
const roots = [
|
|
189
|
+
{ path: os.homedir(), scope: 'global' },
|
|
190
|
+
];
|
|
191
|
+
const cwd = process.cwd();
|
|
192
|
+
const gitRoot = findGitRoot(cwd);
|
|
193
|
+
roots.push({ path: gitRoot || cwd, scope: 'project' });
|
|
194
|
+
for (const root of roots) {
|
|
195
|
+
for (const agent of agents) {
|
|
196
|
+
const skillsDir = path.join(root.path, getAgentSkillsDir(agent));
|
|
197
|
+
if (!fs.existsSync(skillsDir)) {
|
|
198
|
+
// eslint-disable-next-line no-continue
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
let entries;
|
|
202
|
+
try {
|
|
203
|
+
entries = fs.readdirSync(skillsDir);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// eslint-disable-next-line no-continue
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
entries
|
|
210
|
+
.filter((e) => fs.statSync(path.join(skillsDir, e)).isDirectory())
|
|
211
|
+
.forEach((entry) => {
|
|
212
|
+
const manifest = readSkillManifest(path.join(skillsDir, entry));
|
|
213
|
+
if (manifest) {
|
|
214
|
+
results.push({
|
|
215
|
+
name: entry,
|
|
216
|
+
version: manifest.version,
|
|
217
|
+
agent,
|
|
218
|
+
scope: root.scope,
|
|
219
|
+
isOutdated: manifest.version !== env_1.CLI_VERSION,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return results;
|
|
226
|
+
}
|
|
227
|
+
function getVersionWithSkills() {
|
|
228
|
+
const lines = [env_1.CLI_VERSION];
|
|
229
|
+
const skills = findInstalledSkills();
|
|
230
|
+
if (skills.length > 0) {
|
|
231
|
+
lines.push('');
|
|
232
|
+
lines.push('Installed skills:');
|
|
233
|
+
for (const skill of skills) {
|
|
234
|
+
const line = ` ${skill.name} v${skill.version} [${skill.agent}, ${skill.scope}]`;
|
|
235
|
+
lines.push(skill.isOutdated ? styles.warning(line) : line);
|
|
236
|
+
}
|
|
237
|
+
const outdated = skills.filter((s) => s.isOutdated);
|
|
238
|
+
if (outdated.length > 0) {
|
|
239
|
+
lines.push('');
|
|
240
|
+
lines.push(styles.warning('Update with:'));
|
|
241
|
+
const seen = new Set();
|
|
242
|
+
for (const skill of outdated) {
|
|
243
|
+
const globalFlag = skill.scope === 'global' ? ' --global' : '';
|
|
244
|
+
const agentFlag = ` --agent ${skill.agent}`;
|
|
245
|
+
const cmd = `clary install-skills${globalFlag}${agentFlag}`;
|
|
246
|
+
if (!seen.has(cmd)) {
|
|
247
|
+
seen.add(cmd);
|
|
248
|
+
lines.push(styles.warning(` ${cmd}`));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return lines.join('\n');
|
|
254
|
+
}
|
|
255
|
+
const installSkillsHandler = async (options) => {
|
|
256
|
+
const startTime = Date.now();
|
|
257
|
+
let success = true;
|
|
258
|
+
globalState_1.default.setVerbose(options.verbose);
|
|
259
|
+
const installPath = getInstallPath(options);
|
|
260
|
+
const sourceRepo = options.source ?? DEFAULT_SOURCE_REPO;
|
|
261
|
+
console.error(styles.title('\n⚡ Clary Skills Installer\n'));
|
|
262
|
+
console.error(`Agent: ${styles.bold(options.agent)}`);
|
|
263
|
+
console.error(`Scope: ${styles.bold(options.global ? 'global' : 'project')}`);
|
|
264
|
+
console.error(`Version: ${styles.bold(env_1.CLI_VERSION)}`);
|
|
265
|
+
if (sourceRepo !== DEFAULT_SOURCE_REPO) {
|
|
266
|
+
console.error(`Source: ${styles.bold(sourceRepo)}`);
|
|
267
|
+
}
|
|
268
|
+
console.error(`Install path: ${styles.bold(installPath)}\n`);
|
|
269
|
+
const spinner = globalState_1.default.startSpinner('Fetching available skills...');
|
|
270
|
+
try {
|
|
271
|
+
const skills = await listAvailableSkills(sourceRepo);
|
|
272
|
+
if (skills.length === 0) {
|
|
273
|
+
spinner.fail('No skills found in the repository');
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
spinner.text = `Found ${skills.length} skill(s): ${skills.join(', ')}`;
|
|
277
|
+
spinner.succeed();
|
|
278
|
+
// Create install directory
|
|
279
|
+
fs.mkdirSync(installPath, { recursive: true });
|
|
280
|
+
// Install skills sequentially to provide meaningful progress feedback
|
|
281
|
+
/* eslint-disable no-await-in-loop */
|
|
282
|
+
for (const skill of skills) {
|
|
283
|
+
const skillSpinner = globalState_1.default.startSpinner(`Installing skill: ${skill}...`);
|
|
284
|
+
const skillLocalPath = path.join(installPath, skill);
|
|
285
|
+
try {
|
|
286
|
+
// Remove existing skill directory if it exists
|
|
287
|
+
if (fs.existsSync(skillLocalPath)) {
|
|
288
|
+
fs.rmSync(skillLocalPath, { recursive: true, force: true });
|
|
289
|
+
}
|
|
290
|
+
fs.mkdirSync(skillLocalPath, { recursive: true });
|
|
291
|
+
await downloadSkillFiles(`skills/${skill}`, skillLocalPath, sourceRepo);
|
|
292
|
+
writeSkillManifest(skillLocalPath);
|
|
293
|
+
skillSpinner.succeed(`Installed skill: ${skill} (v${env_1.CLI_VERSION})`);
|
|
294
|
+
}
|
|
295
|
+
catch (err) {
|
|
296
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
297
|
+
skillSpinner.fail(`Failed to install skill ${skill}: ${errorMessage}`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
/* eslint-enable no-await-in-loop */
|
|
301
|
+
console.error(styles.success('\n✓ Skills installed successfully!\n'));
|
|
302
|
+
console.error(`Skills are available at: ${styles.bold(installPath)}\n`);
|
|
303
|
+
}
|
|
304
|
+
catch (err) {
|
|
305
|
+
success = false;
|
|
306
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
307
|
+
spinner.fail(`Failed to fetch skills: ${errorMessage}`);
|
|
308
|
+
throw err;
|
|
309
|
+
}
|
|
310
|
+
finally {
|
|
311
|
+
await analytics_1.LightdashAnalytics.track({
|
|
312
|
+
event: 'command.executed',
|
|
313
|
+
properties: {
|
|
314
|
+
command: 'install-skills',
|
|
315
|
+
durationMs: Date.now() - startTime,
|
|
316
|
+
success,
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
exports.installSkillsHandler = installSkillsHandler;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ErrorObject } from 'ajv';
|
|
2
|
+
type LocationMap = Map<string, {
|
|
3
|
+
line: number;
|
|
4
|
+
column: number;
|
|
5
|
+
}>;
|
|
6
|
+
type FileValidationResult = {
|
|
7
|
+
filePath: string;
|
|
8
|
+
errors: ErrorObject[];
|
|
9
|
+
fileContent: string;
|
|
10
|
+
locationMap?: LocationMap;
|
|
11
|
+
schemaType: 'chart' | 'dashboard' | 'model';
|
|
12
|
+
};
|
|
13
|
+
export type SarifLog = {
|
|
14
|
+
version: '2.1.0';
|
|
15
|
+
$schema: string;
|
|
16
|
+
runs: SarifRun[];
|
|
17
|
+
};
|
|
18
|
+
type SarifRun = {
|
|
19
|
+
tool: {
|
|
20
|
+
driver: {
|
|
21
|
+
name: string;
|
|
22
|
+
version: string;
|
|
23
|
+
informationUri?: string;
|
|
24
|
+
rules?: SarifRule[];
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
results: SarifResult[];
|
|
28
|
+
};
|
|
29
|
+
type SarifRule = {
|
|
30
|
+
id: string;
|
|
31
|
+
shortDescription: {
|
|
32
|
+
text: string;
|
|
33
|
+
};
|
|
34
|
+
fullDescription?: {
|
|
35
|
+
text: string;
|
|
36
|
+
};
|
|
37
|
+
help?: {
|
|
38
|
+
text: string;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
export type SarifResult = {
|
|
42
|
+
ruleId: string;
|
|
43
|
+
level: 'error' | 'warning' | 'note';
|
|
44
|
+
message: {
|
|
45
|
+
text: string;
|
|
46
|
+
};
|
|
47
|
+
locations: SarifLocation[];
|
|
48
|
+
properties?: Record<string, unknown>;
|
|
49
|
+
};
|
|
50
|
+
type SarifLocation = {
|
|
51
|
+
physicalLocation: {
|
|
52
|
+
artifactLocation: {
|
|
53
|
+
uri: string;
|
|
54
|
+
};
|
|
55
|
+
region: {
|
|
56
|
+
startLine: number;
|
|
57
|
+
startColumn: number;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Convert validation results to SARIF format
|
|
63
|
+
*/
|
|
64
|
+
export declare function createSarifReport(results: FileValidationResult[]): SarifLog;
|
|
65
|
+
export {};
|
|
66
|
+
//# sourceMappingURL=ajvToSarif.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ajvToSarif.d.ts","sourceRoot":"","sources":["../../../src/handlers/lint/ajvToSarif.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAEvC,KAAK,WAAW,GAAG,GAAG,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEjE,KAAK,oBAAoB,GAAG;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,UAAU,EAAE,OAAO,GAAG,WAAW,GAAG,OAAO,CAAC;CAC/C,CAAC;AAGF,MAAM,MAAM,QAAQ,GAAG;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,QAAQ,EAAE,CAAC;CACpB,CAAC;AAEF,KAAK,QAAQ,GAAG;IACZ,IAAI,EAAE;QACF,MAAM,EAAE;YACJ,IAAI,EAAE,MAAM,CAAC;YACb,OAAO,EAAE,MAAM,CAAC;YAChB,cAAc,CAAC,EAAE,MAAM,CAAC;YACxB,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC;SACvB,CAAC;KACL,CAAC;IACF,OAAO,EAAE,WAAW,EAAE,CAAC;CAC1B,CAAC;AAEF,KAAK,SAAS,GAAG;IACb,EAAE,EAAE,MAAM,CAAC;IACX,gBAAgB,EAAE;QACd,IAAI,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,eAAe,CAAC,EAAE;QACd,IAAI,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,IAAI,CAAC,EAAE;QACH,IAAI,EAAE,MAAM,CAAC;KAChB,CAAC;CACL,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACpC,OAAO,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC,CAAC;AAEF,KAAK,aAAa,GAAG;IACjB,gBAAgB,EAAE;QACd,gBAAgB,EAAE;YACd,GAAG,EAAE,MAAM,CAAC;SACf,CAAC;QACF,MAAM,EAAE;YACJ,SAAS,EAAE,MAAM,CAAC;YAClB,WAAW,EAAE,MAAM,CAAC;SACvB,CAAC;KACL,CAAC;CACL,CAAC;AAmJF;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,QAAQ,CAwG3E"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSarifReport = createSarifReport;
|
|
4
|
+
/**
|
|
5
|
+
* Find the line and column number in YAML content for a given data path using regex-based search.
|
|
6
|
+
*
|
|
7
|
+
* This is a FALLBACK strategy used when the locationMap doesn't have an entry for the error path.
|
|
8
|
+
* Primary use case: root-level missing required properties (dataPath='/' which isn't in locationMap).
|
|
9
|
+
*
|
|
10
|
+
* The function uses regex patterns to search through the YAML content and locate:
|
|
11
|
+
* - Array items by counting '-' markers
|
|
12
|
+
* - Nested properties within array items
|
|
13
|
+
* - Simple top-level keys
|
|
14
|
+
*
|
|
15
|
+
* Returns line 1, column 1 if no match is found (used for root-level errors).
|
|
16
|
+
*/
|
|
17
|
+
function findLocationForPath(yamlContent, dataPath) {
|
|
18
|
+
const lines = yamlContent.split('\n');
|
|
19
|
+
// Remove leading slash and split path
|
|
20
|
+
const pathParts = dataPath
|
|
21
|
+
.replace(/^\//, '')
|
|
22
|
+
.split('/')
|
|
23
|
+
.filter((p) => p !== '');
|
|
24
|
+
if (pathParts.length === 0) {
|
|
25
|
+
// Error at root level
|
|
26
|
+
return { line: 1, column: 1 };
|
|
27
|
+
}
|
|
28
|
+
// Check if we have an array index in the path (e.g., /dimensions/1/type)
|
|
29
|
+
let arrayParentKey = null;
|
|
30
|
+
let arrayIndex = null;
|
|
31
|
+
let propertyInArray = null;
|
|
32
|
+
// Look for pattern: parent/index/property
|
|
33
|
+
if (pathParts.length >= 3) {
|
|
34
|
+
const secondToLast = pathParts[pathParts.length - 2];
|
|
35
|
+
if (/^\d+$/.test(secondToLast)) {
|
|
36
|
+
// We have an array index
|
|
37
|
+
arrayParentKey = pathParts[pathParts.length - 3];
|
|
38
|
+
arrayIndex = parseInt(secondToLast, 10);
|
|
39
|
+
propertyInArray = pathParts[pathParts.length - 1];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else if (pathParts.length === 2) {
|
|
43
|
+
const [firstPart, lastPart] = pathParts;
|
|
44
|
+
if (/^\d+$/.test(lastPart)) {
|
|
45
|
+
// Path is like /dimensions/1
|
|
46
|
+
arrayParentKey = firstPart;
|
|
47
|
+
arrayIndex = parseInt(lastPart, 10);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// For array items with nested property errors (e.g., /dimensions/1/type)
|
|
51
|
+
if (arrayParentKey && arrayIndex !== null && propertyInArray) {
|
|
52
|
+
const parentPattern = new RegExp(`^\\s*${arrayParentKey}\\s*:`);
|
|
53
|
+
let foundParent = false;
|
|
54
|
+
let arrayItemCount = 0;
|
|
55
|
+
let arrayItemStartLine = -1;
|
|
56
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
57
|
+
if (!foundParent && parentPattern.test(lines[i])) {
|
|
58
|
+
foundParent = true;
|
|
59
|
+
}
|
|
60
|
+
else if (foundParent && /^\s*-\s/.test(lines[i])) {
|
|
61
|
+
if (arrayItemCount === arrayIndex) {
|
|
62
|
+
arrayItemStartLine = i;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
arrayItemCount += 1;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Now find the nested property within this array item
|
|
69
|
+
if (arrayItemStartLine >= 0) {
|
|
70
|
+
// Check if property is on the same line as the array marker (e.g., "- id: value")
|
|
71
|
+
const sameLinePattern = new RegExp(`^\\s*-\\s+${propertyInArray}\\s*:`);
|
|
72
|
+
if (sameLinePattern.test(lines[arrayItemStartLine])) {
|
|
73
|
+
const match = lines[arrayItemStartLine].match(/^(\s*-\s+)/);
|
|
74
|
+
const column = match ? match[1].length + 1 : 1;
|
|
75
|
+
return { line: arrayItemStartLine + 1, column };
|
|
76
|
+
}
|
|
77
|
+
// Otherwise, look for the property on subsequent lines
|
|
78
|
+
const propPattern = new RegExp(`^\\s*${propertyInArray}\\s*:`);
|
|
79
|
+
for (let i = arrayItemStartLine + 1; i < Math.min(lines.length, arrayItemStartLine + 20); i += 1) {
|
|
80
|
+
if (propPattern.test(lines[i])) {
|
|
81
|
+
const match = lines[i].match(/^(\s*)/);
|
|
82
|
+
const column = match ? match[1].length + 1 : 1;
|
|
83
|
+
return { line: i + 1, column };
|
|
84
|
+
}
|
|
85
|
+
if (/^\s*-\s/.test(lines[i])) {
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Simple search for the last key in the path
|
|
92
|
+
const lastPart = pathParts[pathParts.length - 1];
|
|
93
|
+
if (!/^\d+$/.test(lastPart)) {
|
|
94
|
+
const keyPattern = new RegExp(`^\\s*${lastPart}\\s*:`);
|
|
95
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
96
|
+
if (keyPattern.test(lines[i])) {
|
|
97
|
+
const match = lines[i].match(/^(\s*)/);
|
|
98
|
+
const column = match ? match[1].length + 1 : 1;
|
|
99
|
+
return { line: i + 1, column };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return { line: 1, column: 1 };
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Get a friendly error message for an AJV error
|
|
107
|
+
*/
|
|
108
|
+
function getFriendlyMessage(error) {
|
|
109
|
+
if (error.keyword === 'required' && error.params.missingProperty) {
|
|
110
|
+
return `Missing required property '${error.params.missingProperty}'`;
|
|
111
|
+
}
|
|
112
|
+
if (error.keyword === 'additionalProperties' &&
|
|
113
|
+
error.params.additionalProperty) {
|
|
114
|
+
return `Property '${error.params.additionalProperty}' is not allowed`;
|
|
115
|
+
}
|
|
116
|
+
if (error.keyword === 'type') {
|
|
117
|
+
return `Expected type '${error.params.type}'`;
|
|
118
|
+
}
|
|
119
|
+
if (error.keyword === 'enum' && error.params.allowedValues) {
|
|
120
|
+
return `Value must be one of: ${error.params.allowedValues.join(', ')}`;
|
|
121
|
+
}
|
|
122
|
+
if (error.keyword === 'const') {
|
|
123
|
+
return `Value must be '${error.params.allowedValue}'`;
|
|
124
|
+
}
|
|
125
|
+
return error.message || 'Validation error';
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Convert validation results to SARIF format
|
|
129
|
+
*/
|
|
130
|
+
function createSarifReport(results) {
|
|
131
|
+
const sarifResults = [];
|
|
132
|
+
const rules = new Map();
|
|
133
|
+
for (const result of results) {
|
|
134
|
+
for (const error of result.errors) {
|
|
135
|
+
// For additionalProperties errors, append the property name to the path
|
|
136
|
+
// Handle root-level errors carefully to avoid double slashes (//propertyName)
|
|
137
|
+
let dataPath = error.instancePath || '/';
|
|
138
|
+
if (error.keyword === 'additionalProperties' &&
|
|
139
|
+
error.params.additionalProperty) {
|
|
140
|
+
dataPath =
|
|
141
|
+
dataPath === '/'
|
|
142
|
+
? `/${error.params.additionalProperty}`
|
|
143
|
+
: `${dataPath}/${error.params.additionalProperty}`;
|
|
144
|
+
}
|
|
145
|
+
// Determine error location using a two-strategy approach:
|
|
146
|
+
// 1. PRIMARY: Use locationMap (built from YAML AST during parsing)
|
|
147
|
+
// - Fast O(1) lookup for ~95% of cases
|
|
148
|
+
// - Works for: additional properties, type errors, enum errors, nested errors, array items
|
|
149
|
+
// - Works for: nested missing required fields (e.g., missing 'exploreName' in 'metricQuery')
|
|
150
|
+
//
|
|
151
|
+
// 2. FALLBACK: Use regex-based search when locationMap lookup fails
|
|
152
|
+
// - Needed for: root-level missing required properties (e.g., missing 'name', 'version')
|
|
153
|
+
// These have dataPath='/' which doesn't exist in locationMap since it stores actual YAML keys
|
|
154
|
+
// - Also provides defensive error handling if AST traversal misses any edge cases
|
|
155
|
+
let location = null;
|
|
156
|
+
if (result.locationMap) {
|
|
157
|
+
location = result.locationMap.get(dataPath) || null;
|
|
158
|
+
}
|
|
159
|
+
if (!location) {
|
|
160
|
+
// Fallback to regex search - primarily for root-level missing required properties
|
|
161
|
+
location = findLocationForPath(result.fileContent, dataPath);
|
|
162
|
+
}
|
|
163
|
+
const message = getFriendlyMessage(error);
|
|
164
|
+
const ruleId = `${result.schemaType}/${error.keyword}`;
|
|
165
|
+
// Add rule if we haven't seen it before
|
|
166
|
+
if (!rules.has(ruleId)) {
|
|
167
|
+
rules.set(ruleId, {
|
|
168
|
+
id: ruleId,
|
|
169
|
+
shortDescription: {
|
|
170
|
+
text: `${error.keyword} validation error`,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
const sarifResult = {
|
|
175
|
+
ruleId,
|
|
176
|
+
level: 'error',
|
|
177
|
+
message: {
|
|
178
|
+
text: message,
|
|
179
|
+
},
|
|
180
|
+
locations: [
|
|
181
|
+
{
|
|
182
|
+
physicalLocation: {
|
|
183
|
+
artifactLocation: {
|
|
184
|
+
uri: result.filePath,
|
|
185
|
+
},
|
|
186
|
+
region: {
|
|
187
|
+
startLine: location?.line || 1,
|
|
188
|
+
startColumn: location?.column || 1,
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
};
|
|
194
|
+
// Add additional context
|
|
195
|
+
if (error.params) {
|
|
196
|
+
const properties = {};
|
|
197
|
+
Object.entries(error.params).forEach(([key, value]) => {
|
|
198
|
+
properties[key] = value;
|
|
199
|
+
});
|
|
200
|
+
sarifResult.properties = { errorParams: properties };
|
|
201
|
+
}
|
|
202
|
+
sarifResults.push(sarifResult);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
version: '2.1.0',
|
|
207
|
+
$schema: 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
|
|
208
|
+
runs: [
|
|
209
|
+
{
|
|
210
|
+
tool: {
|
|
211
|
+
driver: {
|
|
212
|
+
name: 'lightdash-lint',
|
|
213
|
+
version: '1.0.0',
|
|
214
|
+
informationUri: 'https://github.com/lightdash/lightdash',
|
|
215
|
+
rules: Array.from(rules.values()),
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
results: sarifResults,
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
};
|
|
222
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SarifLog } from './ajvToSarif';
|
|
2
|
+
/**
|
|
3
|
+
* Format SARIF results for CLI output
|
|
4
|
+
*/
|
|
5
|
+
export declare function formatSarifForCli(sarifLog: SarifLog, searchPath: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Get summary statistics from SARIF log
|
|
8
|
+
*/
|
|
9
|
+
export declare function getSarifSummary(sarifLog: SarifLog): {
|
|
10
|
+
totalFiles: number;
|
|
11
|
+
totalErrors: number;
|
|
12
|
+
hasErrors: boolean;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=sarifFormatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sarifFormatter.d.ts","sourceRoot":"","sources":["../../../src/handlers/lint/sarifFormatter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,cAAc,CAAC;AAgC1D;;GAEG;AACH,wBAAgB,iBAAiB,CAC7B,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,MAAM,GACnB,MAAM,CA2ER;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,GAAG;IACjD,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACtB,CAsBA"}
|