@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.
Files changed (237) hide show
  1. package/LICENSE +25 -0
  2. package/README.md +197 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/ajv.d.ts +3 -0
  5. package/dist/ajv.d.ts.map +1 -0
  6. package/dist/ajv.js +13 -0
  7. package/dist/analytics/analytics.d.ts +370 -0
  8. package/dist/analytics/analytics.d.ts.map +1 -0
  9. package/dist/analytics/analytics.js +143 -0
  10. package/dist/config.d.ts +34 -0
  11. package/dist/config.d.ts.map +1 -0
  12. package/dist/config.js +134 -0
  13. package/dist/dbt/context.d.ts +14 -0
  14. package/dist/dbt/context.d.ts.map +1 -0
  15. package/dist/dbt/context.js +76 -0
  16. package/dist/dbt/context.test.d.ts +2 -0
  17. package/dist/dbt/context.test.d.ts.map +1 -0
  18. package/dist/dbt/context.test.js +152 -0
  19. package/dist/dbt/manifest.d.ts +7 -0
  20. package/dist/dbt/manifest.d.ts.map +1 -0
  21. package/dist/dbt/manifest.js +23 -0
  22. package/dist/dbt/models.d.ts +43 -0
  23. package/dist/dbt/models.d.ts.map +1 -0
  24. package/dist/dbt/models.js +256 -0
  25. package/dist/dbt/models.test.d.ts +2 -0
  26. package/dist/dbt/models.test.d.ts.map +1 -0
  27. package/dist/dbt/models.test.js +19 -0
  28. package/dist/dbt/profile.d.ts +9 -0
  29. package/dist/dbt/profile.d.ts.map +1 -0
  30. package/dist/dbt/profile.js +86 -0
  31. package/dist/dbt/profiles.test.d.ts +2 -0
  32. package/dist/dbt/profiles.test.d.ts.map +1 -0
  33. package/dist/dbt/profiles.test.js +50 -0
  34. package/dist/dbt/schema.d.ts +31 -0
  35. package/dist/dbt/schema.d.ts.map +1 -0
  36. package/dist/dbt/schema.js +49 -0
  37. package/dist/dbt/targets/Bigquery/index.d.ts +18 -0
  38. package/dist/dbt/targets/Bigquery/index.d.ts.map +1 -0
  39. package/dist/dbt/targets/Bigquery/index.js +105 -0
  40. package/dist/dbt/targets/Bigquery/oauth.d.ts +2 -0
  41. package/dist/dbt/targets/Bigquery/oauth.d.ts.map +1 -0
  42. package/dist/dbt/targets/Bigquery/oauth.js +43 -0
  43. package/dist/dbt/targets/Bigquery/serviceAccount.d.ts +35 -0
  44. package/dist/dbt/targets/Bigquery/serviceAccount.d.ts.map +1 -0
  45. package/dist/dbt/targets/Bigquery/serviceAccount.js +149 -0
  46. package/dist/dbt/targets/Databricks/oauth.d.ts +21 -0
  47. package/dist/dbt/targets/Databricks/oauth.d.ts.map +1 -0
  48. package/dist/dbt/targets/Databricks/oauth.js +184 -0
  49. package/dist/dbt/targets/athena.d.ts +21 -0
  50. package/dist/dbt/targets/athena.d.ts.map +1 -0
  51. package/dist/dbt/targets/athena.js +91 -0
  52. package/dist/dbt/targets/athena.test.d.ts +2 -0
  53. package/dist/dbt/targets/athena.test.d.ts.map +1 -0
  54. package/dist/dbt/targets/athena.test.js +60 -0
  55. package/dist/dbt/targets/clickhouse.d.ts +24 -0
  56. package/dist/dbt/targets/clickhouse.d.ts.map +1 -0
  57. package/dist/dbt/targets/clickhouse.js +90 -0
  58. package/dist/dbt/targets/databricks.d.ts +27 -0
  59. package/dist/dbt/targets/databricks.d.ts.map +1 -0
  60. package/dist/dbt/targets/databricks.js +138 -0
  61. package/dist/dbt/targets/duckdb.d.ts +16 -0
  62. package/dist/dbt/targets/duckdb.d.ts.map +1 -0
  63. package/dist/dbt/targets/duckdb.js +63 -0
  64. package/dist/dbt/targets/duckdb.test.d.ts +2 -0
  65. package/dist/dbt/targets/duckdb.test.d.ts.map +1 -0
  66. package/dist/dbt/targets/duckdb.test.js +37 -0
  67. package/dist/dbt/targets/postgres.d.ts +26 -0
  68. package/dist/dbt/targets/postgres.d.ts.map +1 -0
  69. package/dist/dbt/targets/postgres.js +142 -0
  70. package/dist/dbt/targets/redshift.d.ts +23 -0
  71. package/dist/dbt/targets/redshift.d.ts.map +1 -0
  72. package/dist/dbt/targets/redshift.js +96 -0
  73. package/dist/dbt/targets/snowflake.d.ts +4 -0
  74. package/dist/dbt/targets/snowflake.d.ts.map +1 -0
  75. package/dist/dbt/targets/snowflake.js +134 -0
  76. package/dist/dbt/targets/trino.d.ts +16 -0
  77. package/dist/dbt/targets/trino.d.ts.map +1 -0
  78. package/dist/dbt/targets/trino.js +65 -0
  79. package/dist/dbt/templating.d.ts +15 -0
  80. package/dist/dbt/templating.d.ts.map +1 -0
  81. package/dist/dbt/templating.js +50 -0
  82. package/dist/dbt/templating.test.d.ts +2 -0
  83. package/dist/dbt/templating.test.d.ts.map +1 -0
  84. package/dist/dbt/templating.test.js +51 -0
  85. package/dist/dbt/types.d.ts +17 -0
  86. package/dist/dbt/types.d.ts.map +1 -0
  87. package/dist/dbt/types.js +2 -0
  88. package/dist/dbt/validation.d.ts +9 -0
  89. package/dist/dbt/validation.d.ts.map +1 -0
  90. package/dist/dbt/validation.js +54 -0
  91. package/dist/env.d.ts +12 -0
  92. package/dist/env.d.ts.map +1 -0
  93. package/dist/env.js +40 -0
  94. package/dist/error.d.ts +2 -0
  95. package/dist/error.d.ts.map +1 -0
  96. package/dist/error.js +12 -0
  97. package/dist/globalState.d.ts +29 -0
  98. package/dist/globalState.d.ts.map +1 -0
  99. package/dist/globalState.js +67 -0
  100. package/dist/handlers/asyncQuery.d.ts +7 -0
  101. package/dist/handlers/asyncQuery.d.ts.map +1 -0
  102. package/dist/handlers/asyncQuery.js +50 -0
  103. package/dist/handlers/compile.d.ts +16 -0
  104. package/dist/handlers/compile.d.ts.map +1 -0
  105. package/dist/handlers/compile.js +277 -0
  106. package/dist/handlers/compile.test.d.ts +2 -0
  107. package/dist/handlers/compile.test.d.ts.map +1 -0
  108. package/dist/handlers/compile.test.js +201 -0
  109. package/dist/handlers/createProject.d.ts +37 -0
  110. package/dist/handlers/createProject.d.ts.map +1 -0
  111. package/dist/handlers/createProject.js +272 -0
  112. package/dist/handlers/dbt/apiClient.d.ts +14 -0
  113. package/dist/handlers/dbt/apiClient.d.ts.map +1 -0
  114. package/dist/handlers/dbt/apiClient.js +167 -0
  115. package/dist/handlers/dbt/compile.d.ts +35 -0
  116. package/dist/handlers/dbt/compile.d.ts.map +1 -0
  117. package/dist/handlers/dbt/compile.js +220 -0
  118. package/dist/handlers/dbt/getDbtProfileTargetName.d.ts +9 -0
  119. package/dist/handlers/dbt/getDbtProfileTargetName.d.ts.map +1 -0
  120. package/dist/handlers/dbt/getDbtProfileTargetName.js +44 -0
  121. package/dist/handlers/dbt/getDbtVersion.d.ts +16 -0
  122. package/dist/handlers/dbt/getDbtVersion.d.ts.map +1 -0
  123. package/dist/handlers/dbt/getDbtVersion.js +141 -0
  124. package/dist/handlers/dbt/getDbtVersion.mocks.d.ts +11 -0
  125. package/dist/handlers/dbt/getDbtVersion.mocks.d.ts.map +1 -0
  126. package/dist/handlers/dbt/getDbtVersion.mocks.js +70 -0
  127. package/dist/handlers/dbt/getDbtVersion.test.d.ts +2 -0
  128. package/dist/handlers/dbt/getDbtVersion.test.d.ts.map +1 -0
  129. package/dist/handlers/dbt/getDbtVersion.test.js +97 -0
  130. package/dist/handlers/dbt/getWarehouseClient.d.ts +24 -0
  131. package/dist/handlers/dbt/getWarehouseClient.d.ts.map +1 -0
  132. package/dist/handlers/dbt/getWarehouseClient.js +312 -0
  133. package/dist/handlers/dbt/refresh.d.ts +11 -0
  134. package/dist/handlers/dbt/refresh.d.ts.map +1 -0
  135. package/dist/handlers/dbt/refresh.js +114 -0
  136. package/dist/handlers/dbt/run.d.ts +14 -0
  137. package/dist/handlers/dbt/run.d.ts.map +1 -0
  138. package/dist/handlers/dbt/run.js +67 -0
  139. package/dist/handlers/deploy.d.ts +26 -0
  140. package/dist/handlers/deploy.d.ts.map +1 -0
  141. package/dist/handlers/deploy.js +377 -0
  142. package/dist/handlers/diagnostics.d.ts +11 -0
  143. package/dist/handlers/diagnostics.d.ts.map +1 -0
  144. package/dist/handlers/diagnostics.js +194 -0
  145. package/dist/handlers/download.d.ts +29 -0
  146. package/dist/handlers/download.d.ts.map +1 -0
  147. package/dist/handlers/download.js +955 -0
  148. package/dist/handlers/exportChartImage.d.ts +7 -0
  149. package/dist/handlers/exportChartImage.d.ts.map +1 -0
  150. package/dist/handlers/exportChartImage.js +33 -0
  151. package/dist/handlers/generate.d.ts +13 -0
  152. package/dist/handlers/generate.d.ts.map +1 -0
  153. package/dist/handlers/generate.js +159 -0
  154. package/dist/handlers/generateExposures.d.ts +8 -0
  155. package/dist/handlers/generateExposures.d.ts.map +1 -0
  156. package/dist/handlers/generateExposures.js +100 -0
  157. package/dist/handlers/getProject.d.ts +6 -0
  158. package/dist/handlers/getProject.d.ts.map +1 -0
  159. package/dist/handlers/getProject.js +43 -0
  160. package/dist/handlers/installSkills.d.ts +12 -0
  161. package/dist/handlers/installSkills.d.ts.map +1 -0
  162. package/dist/handlers/installSkills.js +321 -0
  163. package/dist/handlers/lint/ajvToSarif.d.ts +66 -0
  164. package/dist/handlers/lint/ajvToSarif.d.ts.map +1 -0
  165. package/dist/handlers/lint/ajvToSarif.js +222 -0
  166. package/dist/handlers/lint/sarifFormatter.d.ts +14 -0
  167. package/dist/handlers/lint/sarifFormatter.d.ts.map +1 -0
  168. package/dist/handlers/lint/sarifFormatter.js +111 -0
  169. package/dist/handlers/lint.d.ts +8 -0
  170. package/dist/handlers/lint.d.ts.map +1 -0
  171. package/dist/handlers/lint.js +308 -0
  172. package/dist/handlers/listProjects.d.ts +6 -0
  173. package/dist/handlers/listProjects.d.ts.map +1 -0
  174. package/dist/handlers/listProjects.js +53 -0
  175. package/dist/handlers/login/oauth.d.ts +2 -0
  176. package/dist/handlers/login/oauth.d.ts.map +1 -0
  177. package/dist/handlers/login/oauth.js +27 -0
  178. package/dist/handlers/login/pat.d.ts +2 -0
  179. package/dist/handlers/login/pat.d.ts.map +1 -0
  180. package/dist/handlers/login/pat.js +31 -0
  181. package/dist/handlers/login.d.ts +15 -0
  182. package/dist/handlers/login.d.ts.map +1 -0
  183. package/dist/handlers/login.js +239 -0
  184. package/dist/handlers/metadataFile.d.ts +9 -0
  185. package/dist/handlers/metadataFile.d.ts.map +1 -0
  186. package/dist/handlers/metadataFile.js +34 -0
  187. package/dist/handlers/oauthLogin.d.ts +6 -0
  188. package/dist/handlers/oauthLogin.d.ts.map +1 -0
  189. package/dist/handlers/oauthLogin.js +191 -0
  190. package/dist/handlers/preview.d.ts +29 -0
  191. package/dist/handlers/preview.d.ts.map +1 -0
  192. package/dist/handlers/preview.js +415 -0
  193. package/dist/handlers/renameHandler.d.ts +16 -0
  194. package/dist/handlers/renameHandler.d.ts.map +1 -0
  195. package/dist/handlers/renameHandler.js +160 -0
  196. package/dist/handlers/runChart.d.ts +10 -0
  197. package/dist/handlers/runChart.d.ts.map +1 -0
  198. package/dist/handlers/runChart.js +105 -0
  199. package/dist/handlers/selectProject.d.ts +20 -0
  200. package/dist/handlers/selectProject.d.ts.map +1 -0
  201. package/dist/handlers/selectProject.js +91 -0
  202. package/dist/handlers/setProject.d.ts +14 -0
  203. package/dist/handlers/setProject.d.ts.map +1 -0
  204. package/dist/handlers/setProject.js +131 -0
  205. package/dist/handlers/setWarehouse.d.ts +14 -0
  206. package/dist/handlers/setWarehouse.d.ts.map +1 -0
  207. package/dist/handlers/setWarehouse.js +94 -0
  208. package/dist/handlers/sql.d.ts +9 -0
  209. package/dist/handlers/sql.d.ts.map +1 -0
  210. package/dist/handlers/sql.js +89 -0
  211. package/dist/handlers/utils.d.ts +11 -0
  212. package/dist/handlers/utils.d.ts.map +1 -0
  213. package/dist/handlers/utils.js +36 -0
  214. package/dist/handlers/validate.d.ts +22 -0
  215. package/dist/handlers/validate.d.ts.map +1 -0
  216. package/dist/handlers/validate.js +201 -0
  217. package/dist/index.d.ts +3 -0
  218. package/dist/index.d.ts.map +1 -0
  219. package/dist/index.js +581 -0
  220. package/dist/lightdash/loader.d.ts +21 -0
  221. package/dist/lightdash/loader.d.ts.map +1 -0
  222. package/dist/lightdash/loader.js +122 -0
  223. package/dist/lightdash/projectType.d.ts +84 -0
  224. package/dist/lightdash/projectType.d.ts.map +1 -0
  225. package/dist/lightdash/projectType.js +75 -0
  226. package/dist/lightdash-config/index.d.ts +2 -0
  227. package/dist/lightdash-config/index.d.ts.map +1 -0
  228. package/dist/lightdash-config/index.js +41 -0
  229. package/dist/lightdash-config/lightdash-config.test.d.ts +2 -0
  230. package/dist/lightdash-config/lightdash-config.test.d.ts.map +1 -0
  231. package/dist/lightdash-config/lightdash-config.test.js +70 -0
  232. package/dist/styles.d.ts +10 -0
  233. package/dist/styles.d.ts.map +1 -0
  234. package/dist/styles.js +14 -0
  235. package/entitlements.plist +33 -0
  236. package/package.json +71 -0
  237. 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"}