@convext/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 (42) hide show
  1. package/README.md +180 -0
  2. package/dist/api.d.ts +121 -0
  3. package/dist/api.d.ts.map +1 -0
  4. package/dist/api.js +218 -0
  5. package/dist/api.js.map +1 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +165 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/commands/check.d.ts +8 -0
  11. package/dist/commands/check.d.ts.map +1 -0
  12. package/dist/commands/check.js +164 -0
  13. package/dist/commands/check.js.map +1 -0
  14. package/dist/commands/init.d.ts +10 -0
  15. package/dist/commands/init.d.ts.map +1 -0
  16. package/dist/commands/init.js +294 -0
  17. package/dist/commands/init.js.map +1 -0
  18. package/dist/commands/sync.d.ts +10 -0
  19. package/dist/commands/sync.d.ts.map +1 -0
  20. package/dist/commands/sync.js +328 -0
  21. package/dist/commands/sync.js.map +1 -0
  22. package/dist/config.d.ts +9 -0
  23. package/dist/config.d.ts.map +1 -0
  24. package/dist/config.js +52 -0
  25. package/dist/config.js.map +1 -0
  26. package/dist/index.d.ts +6 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +24 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/utils/fileManager.d.ts +3 -0
  31. package/dist/utils/fileManager.d.ts.map +1 -0
  32. package/dist/utils/fileManager.js +35 -0
  33. package/dist/utils/fileManager.js.map +1 -0
  34. package/dist/utils/prompts.d.ts +9 -0
  35. package/dist/utils/prompts.d.ts.map +1 -0
  36. package/dist/utils/prompts.js +78 -0
  37. package/dist/utils/prompts.js.map +1 -0
  38. package/dist/utils/scanner.d.ts +10 -0
  39. package/dist/utils/scanner.d.ts.map +1 -0
  40. package/dist/utils/scanner.js +196 -0
  41. package/dist/utils/scanner.js.map +1 -0
  42. package/package.json +42 -0
@@ -0,0 +1,294 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.initCommand = initCommand;
7
+ const node_fs_1 = require("node:fs");
8
+ const node_path_1 = require("node:path");
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const ora_1 = __importDefault(require("ora"));
11
+ const api_js_1 = require("../api.js");
12
+ const scanner_js_1 = require("../utils/scanner.js");
13
+ const prompts_js_1 = require("../utils/prompts.js");
14
+ function detectProjectName() {
15
+ const cwd = process.cwd();
16
+ // Try package.json
17
+ const packageJsonPath = (0, node_path_1.join)(cwd, "package.json");
18
+ if ((0, node_fs_1.existsSync)(packageJsonPath)) {
19
+ try {
20
+ const pkg = JSON.parse((0, node_fs_1.readFileSync)(packageJsonPath, "utf-8"));
21
+ if (pkg.name)
22
+ return pkg.name;
23
+ }
24
+ catch {
25
+ // Ignore parse errors
26
+ }
27
+ }
28
+ // Try Gemfile (look for gem name)
29
+ const gemspecFiles = [(0, node_path_1.join)(cwd, "*.gemspec")];
30
+ for (const pattern of gemspecFiles) {
31
+ // Simple detection - fall back to directory name
32
+ }
33
+ // Try pyproject.toml
34
+ const pyprojectPath = (0, node_path_1.join)(cwd, "pyproject.toml");
35
+ if ((0, node_fs_1.existsSync)(pyprojectPath)) {
36
+ try {
37
+ const content = (0, node_fs_1.readFileSync)(pyprojectPath, "utf-8");
38
+ const nameMatch = content.match(/name\s*=\s*["']([^"']+)["']/);
39
+ if (nameMatch)
40
+ return nameMatch[1];
41
+ }
42
+ catch {
43
+ // Ignore parse errors
44
+ }
45
+ }
46
+ // Fall back to directory name
47
+ return (0, node_path_1.basename)(cwd);
48
+ }
49
+ function detectRepoUrl() {
50
+ const cwd = process.cwd();
51
+ const gitConfigPath = (0, node_path_1.join)(cwd, ".git/config");
52
+ if (!(0, node_fs_1.existsSync)(gitConfigPath)) {
53
+ return undefined;
54
+ }
55
+ try {
56
+ const content = (0, node_fs_1.readFileSync)(gitConfigPath, "utf-8");
57
+ const urlMatch = content.match(/url\s*=\s*(.+)/);
58
+ if (urlMatch) {
59
+ let url = urlMatch[1].trim();
60
+ // Convert SSH URLs to HTTPS
61
+ if (url.startsWith("git@github.com:")) {
62
+ url = url.replace("git@github.com:", "https://github.com/");
63
+ }
64
+ if (url.endsWith(".git")) {
65
+ url = url.slice(0, -4);
66
+ }
67
+ return url;
68
+ }
69
+ }
70
+ catch {
71
+ // Ignore errors
72
+ }
73
+ return undefined;
74
+ }
75
+ function detectProjectDescription() {
76
+ const cwd = process.cwd();
77
+ // Try package.json
78
+ const packageJsonPath = (0, node_path_1.join)(cwd, "package.json");
79
+ if ((0, node_fs_1.existsSync)(packageJsonPath)) {
80
+ try {
81
+ const pkg = JSON.parse((0, node_fs_1.readFileSync)(packageJsonPath, "utf-8"));
82
+ if (pkg.description)
83
+ return pkg.description;
84
+ }
85
+ catch {
86
+ // Ignore parse errors
87
+ }
88
+ }
89
+ // Try README first line
90
+ for (const readme of ["README.md", "README.rst", "README.txt", "README"]) {
91
+ const readmePath = (0, node_path_1.join)(cwd, readme);
92
+ if ((0, node_fs_1.existsSync)(readmePath)) {
93
+ try {
94
+ const content = (0, node_fs_1.readFileSync)(readmePath, "utf-8");
95
+ const lines = content.split("\n").filter((l) => l.trim() && !l.startsWith("#"));
96
+ if (lines[0]) {
97
+ return lines[0].slice(0, 200);
98
+ }
99
+ }
100
+ catch {
101
+ // Ignore errors
102
+ }
103
+ }
104
+ }
105
+ return undefined;
106
+ }
107
+ async function initCommand(options) {
108
+ const spinner = (0, ora_1.default)("Scanning repository...").start();
109
+ try {
110
+ // Step 1: Scan repository
111
+ const insights = (0, scanner_js_1.scanRepository)();
112
+ const detectedRepoUrl = options.repoUrl || detectRepoUrl();
113
+ spinner.succeed("Scanned repository");
114
+ console.log("\n" + chalk_1.default.bold("Detected Stack:"));
115
+ logInsight("Languages", insights.languages);
116
+ logInsight("Frameworks", insights.frameworks);
117
+ logInsight("Package Managers", insights.packageManagers);
118
+ logInsight("Testing", insights.testing);
119
+ logInsight("Linters", insights.linters);
120
+ if (insights.aiFiles.length > 0) {
121
+ console.log(chalk_1.default.yellow(`\nExisting AI context files:\n - ${insights.aiFiles.join("\n - ")}`));
122
+ }
123
+ // Step 2: Fetch organizations and projects
124
+ spinner.start("Fetching your workspaces and projects...");
125
+ let organizations = [];
126
+ let projects = [];
127
+ try {
128
+ const [orgsResult, projectsResult] = await Promise.all([
129
+ (0, api_js_1.getOrganizations)(),
130
+ (0, api_js_1.getProjects)(),
131
+ ]);
132
+ organizations = orgsResult.organizations;
133
+ projects = projectsResult.projects;
134
+ spinner.succeed(`Found ${organizations.length} workspace(s) and ${projects.length} project(s)`);
135
+ }
136
+ catch (error) {
137
+ spinner.fail("Could not fetch workspaces/projects");
138
+ throw error;
139
+ }
140
+ // Step 3: Show organizations
141
+ if (organizations.length === 0) {
142
+ console.log(chalk_1.default.yellow("\nNo workspaces found. Please create one in the Convext web interface first."));
143
+ return;
144
+ }
145
+ console.log("\n" + chalk_1.default.bold("Your Workspaces:"));
146
+ organizations.forEach((org, idx) => {
147
+ console.log(` ${idx + 1}. ${chalk_1.default.cyan(org.name)} ${chalk_1.default.gray(`(${org.projects_count} projects)`)}`);
148
+ });
149
+ // Step 4: Check for matching project by repo URL
150
+ const matchingProject = detectedRepoUrl
151
+ ? projects.find(p => p.repo_url === detectedRepoUrl)
152
+ : undefined;
153
+ if (matchingProject) {
154
+ console.log("\n" + chalk_1.default.green("✓ Found existing project for this repository:"));
155
+ console.log(` ${chalk_1.default.cyan(matchingProject.name)} ${chalk_1.default.gray(`(${matchingProject.organization_name || 'No org'})`)}`);
156
+ if (await (0, prompts_js_1.promptConfirm)("Link to this project?", true)) {
157
+ await writeProjectConfig(matchingProject.id, matchingProject.name, detectedRepoUrl);
158
+ console.log(chalk_1.default.green("\n✓ Linked to existing project"));
159
+ return;
160
+ }
161
+ }
162
+ // Step 5: Show existing projects grouped by org
163
+ if (projects.length > 0) {
164
+ console.log("\n" + chalk_1.default.bold("Existing Projects:"));
165
+ // Group projects by organization
166
+ const projectsByOrg = new Map();
167
+ for (const proj of projects) {
168
+ const orgName = proj.organization_name || "No Organization";
169
+ if (!projectsByOrg.has(orgName)) {
170
+ projectsByOrg.set(orgName, []);
171
+ }
172
+ projectsByOrg.get(orgName).push(proj);
173
+ }
174
+ // Display grouped
175
+ for (const [orgName, orgProjects] of projectsByOrg) {
176
+ console.log(chalk_1.default.gray(`\n ${orgName}:`));
177
+ for (const proj of orgProjects) {
178
+ console.log(` • ${chalk_1.default.cyan(proj.name)}`);
179
+ }
180
+ }
181
+ }
182
+ const actionOptions = [
183
+ { label: chalk_1.default.green("+ Create new project"), value: { type: "create" } },
184
+ ...projects.map(proj => ({
185
+ label: `Link to: ${proj.name}${proj.organization_name ? ` (${proj.organization_name})` : ""}`,
186
+ value: { type: "link", project: proj },
187
+ })),
188
+ ];
189
+ const action = await (0, prompts_js_1.promptSelect)("\nWhat would you like to do?", actionOptions, 0);
190
+ if (action.type === "link") {
191
+ await writeProjectConfig(action.project.id, action.project.name, action.project.repo_url);
192
+ console.log(chalk_1.default.green(`\n✓ Linked to project: ${action.project.name}`));
193
+ return;
194
+ }
195
+ // Step 7: Create new project
196
+ console.log("\n" + chalk_1.default.bold("Create New Project"));
197
+ const detectedName = options.name || detectProjectName();
198
+ const detectedDescription = options.description || detectProjectDescription();
199
+ const finalName = (await (0, prompts_js_1.promptInput)("Project name", detectedName)).trim() || detectedName;
200
+ const finalRepoUrl = (await (0, prompts_js_1.promptInput)("Repository URL", detectedRepoUrl)).trim() || detectedRepoUrl;
201
+ const finalDescription = (await (0, prompts_js_1.promptInput)("Description (optional)", detectedDescription)).trim() || detectedDescription;
202
+ // Select organization
203
+ let orgSlug = options.organization;
204
+ if (!orgSlug) {
205
+ if (organizations.length === 1) {
206
+ orgSlug = organizations[0].slug;
207
+ console.log(chalk_1.default.gray(`\nUsing workspace: ${organizations[0].name}`));
208
+ }
209
+ else {
210
+ const selectedOrg = await (0, prompts_js_1.promptSelect)("Select a workspace:", organizations.map(org => ({
211
+ label: `${org.name} (${org.projects_count} projects)`,
212
+ value: org,
213
+ })), 0);
214
+ orgSlug = selectedOrg.slug;
215
+ }
216
+ }
217
+ spinner.start("Creating project...");
218
+ const result = await (0, api_js_1.registerProject)(finalName, finalRepoUrl, finalDescription, orgSlug);
219
+ spinner.succeed(`Project created with ID: ${result.project_id}`);
220
+ await writeProjectConfig(result.project_id, finalName, finalRepoUrl);
221
+ summarizeProject(result.project_id, finalName, finalRepoUrl, finalDescription);
222
+ await showRulesetSummary();
223
+ console.log("\n" + chalk_1.default.bold("Next Steps:"));
224
+ console.log(` 1. Assign rule sets in the Convext web interface.`);
225
+ console.log(` 2. Run ${chalk_1.default.cyan("convext sync")} to generate AI context files.`);
226
+ console.log(` 3. Commit ${chalk_1.default.cyan(".convext/")} directory and generated files.`);
227
+ console.log("\n" + chalk_1.default.green("✓ Project initialized successfully"));
228
+ }
229
+ catch (error) {
230
+ spinner.fail("Initialization failed");
231
+ if (error instanceof Error) {
232
+ console.error(chalk_1.default.red(`Error: ${error.message}`));
233
+ }
234
+ process.exit(1);
235
+ }
236
+ }
237
+ function logInsight(label, values) {
238
+ const data = Array.from(values);
239
+ if (data.length === 0) {
240
+ console.log(` ${label}: ${chalk_1.default.gray("none detected")}`);
241
+ }
242
+ else {
243
+ console.log(` ${label}: ${chalk_1.default.cyan(data.join(", "))}`);
244
+ }
245
+ }
246
+ async function writeProjectConfig(projectId, name, repoUrl) {
247
+ const cwd = process.cwd();
248
+ const convextDir = (0, node_path_1.join)(cwd, ".convext");
249
+ const localConfigPath = (0, node_path_1.join)(convextDir, "config.json");
250
+ const { writeFileSync, mkdirSync, existsSync } = await import("node:fs");
251
+ if (!existsSync(convextDir)) {
252
+ mkdirSync(convextDir, { recursive: true });
253
+ }
254
+ const localConfig = {
255
+ projectId,
256
+ name,
257
+ repoUrl,
258
+ createdAt: new Date().toISOString(),
259
+ };
260
+ writeFileSync(localConfigPath, JSON.stringify(localConfig, null, 2));
261
+ }
262
+ function summarizeProject(projectId, name, repoUrl, description) {
263
+ console.log("\n" + chalk_1.default.bold("Project Configuration:"));
264
+ console.log(` Name: ${chalk_1.default.cyan(name)}`);
265
+ console.log(` Project ID: ${chalk_1.default.cyan(projectId)}`);
266
+ if (repoUrl) {
267
+ console.log(` Repository: ${chalk_1.default.cyan(repoUrl)}`);
268
+ }
269
+ if (description) {
270
+ console.log(` Description: ${chalk_1.default.gray(description.slice(0, 60))}${description.length > 60 ? "..." : ""}`);
271
+ }
272
+ }
273
+ async function showRulesetSummary() {
274
+ console.log("\n" + chalk_1.default.bold("Available Rulesets:"));
275
+ try {
276
+ const { rulesets } = await (0, api_js_1.getRuleSets)();
277
+ if (rulesets.length > 0) {
278
+ for (const rs of rulesets.slice(0, 5)) {
279
+ console.log(` • ${chalk_1.default.cyan(rs.name)} - ${rs.rules_count} rules, ${rs.standards_count} standards`);
280
+ }
281
+ if (rulesets.length > 5) {
282
+ console.log(chalk_1.default.gray(` ... and ${rulesets.length - 5} more`));
283
+ }
284
+ console.log(chalk_1.default.gray("\n Assign rule sets via the Code Charter web interface before syncing."));
285
+ }
286
+ else {
287
+ console.log(chalk_1.default.gray(" No rulesets available"));
288
+ }
289
+ }
290
+ catch {
291
+ console.log(chalk_1.default.gray(" Could not fetch rulesets"));
292
+ }
293
+ }
294
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":";;;;;AAkHA,kCAgKC;AAlRD,qCAAmD;AACnD,yCAA2C;AAC3C,kDAA0B;AAC1B,8CAAsB;AACtB,sCAAgI;AAChI,oDAAqD;AACrD,oDAA+E;AAU/E,SAAS,iBAAiB;IACxB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAE1B,mBAAmB;IACnB,MAAM,eAAe,GAAG,IAAA,gBAAI,EAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IAClD,IAAI,IAAA,oBAAU,EAAC,eAAe,CAAC,EAAE,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAA,sBAAY,EAAC,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC;YAC/D,IAAI,GAAG,CAAC,IAAI;gBAAE,OAAO,GAAG,CAAC,IAAI,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,sBAAsB;QACxB,CAAC;IACH,CAAC;IAED,kCAAkC;IAClC,MAAM,YAAY,GAAG,CAAC,IAAA,gBAAI,EAAC,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC;IAC9C,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE,CAAC;QACnC,iDAAiD;IACnD,CAAC;IAED,qBAAqB;IACrB,MAAM,aAAa,GAAG,IAAA,gBAAI,EAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;IAClD,IAAI,IAAA,oBAAU,EAAC,aAAa,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAA,sBAAY,EAAC,aAAa,EAAE,OAAO,CAAC,CAAC;YACrD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;YAC/D,IAAI,SAAS;gBAAE,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC;QACrC,CAAC;QAAC,MAAM,CAAC;YACP,sBAAsB;QACxB,CAAC;IACH,CAAC;IAED,8BAA8B;IAC9B,OAAO,IAAA,oBAAQ,EAAC,GAAG,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,aAAa;IACpB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,aAAa,GAAG,IAAA,gBAAI,EAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAE/C,IAAI,CAAC,IAAA,oBAAU,EAAC,aAAa,CAAC,EAAE,CAAC;QAC/B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAA,sBAAY,EAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QACrD,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;QACjD,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC7B,4BAA4B;YAC5B,IAAI,GAAG,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBACtC,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,iBAAiB,EAAE,qBAAqB,CAAC,CAAC;YAC9D,CAAC;YACD,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACzB,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACzB,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,gBAAgB;IAClB,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,wBAAwB;IAC/B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAE1B,mBAAmB;IACnB,MAAM,eAAe,GAAG,IAAA,gBAAI,EAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IAClD,IAAI,IAAA,oBAAU,EAAC,eAAe,CAAC,EAAE,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAA,sBAAY,EAAC,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC;YAC/D,IAAI,GAAG,CAAC,WAAW;gBAAE,OAAO,GAAG,CAAC,WAAW,CAAC;QAC9C,CAAC;QAAC,MAAM,CAAC;YACP,sBAAsB;QACxB,CAAC;IACH,CAAC;IAED,wBAAwB;IACxB,KAAK,MAAM,MAAM,IAAI,CAAC,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,CAAC;QACzE,MAAM,UAAU,GAAG,IAAA,gBAAI,EAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACrC,IAAI,IAAA,oBAAU,EAAC,UAAU,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,IAAA,sBAAY,EAAC,UAAU,EAAE,OAAO,CAAC,CAAC;gBAClD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;gBAChF,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;oBACb,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;gBAChC,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,gBAAgB;YAClB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAEM,KAAK,UAAU,WAAW,CAAC,OAAoB;IACpD,MAAM,OAAO,GAAG,IAAA,aAAG,EAAC,wBAAwB,CAAC,CAAC,KAAK,EAAE,CAAC;IAEtD,IAAI,CAAC;QACH,0BAA0B;QAC1B,MAAM,QAAQ,GAAG,IAAA,2BAAc,GAAE,CAAC;QAClC,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,IAAI,aAAa,EAAE,CAAC;QAC3D,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;QAEtC,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,eAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC;QAClD,UAAU,CAAC,WAAW,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC5C,UAAU,CAAC,YAAY,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;QAC9C,UAAU,CAAC,kBAAkB,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;QACzD,UAAU,CAAC,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;QACxC,UAAU,CAAC,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChC,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,MAAM,CAAC,qCAAqC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;QACpG,CAAC;QAED,2CAA2C;QAC3C,OAAO,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAC1D,IAAI,aAAa,GAAmB,EAAE,CAAC;QACvC,IAAI,QAAQ,GAAqB,EAAE,CAAC;QAEpC,IAAI,CAAC;YACH,MAAM,CAAC,UAAU,EAAE,cAAc,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBACrD,IAAA,yBAAgB,GAAE;gBAClB,IAAA,oBAAW,GAAE;aACd,CAAC,CAAC;YACH,aAAa,GAAG,UAAU,CAAC,aAAa,CAAC;YACzC,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAC;YACnC,OAAO,CAAC,OAAO,CAAC,SAAS,aAAa,CAAC,MAAM,qBAAqB,QAAQ,CAAC,MAAM,aAAa,CAAC,CAAC;QAClG,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;YACpD,MAAM,KAAK,CAAC;QACd,CAAC;QAED,6BAA6B;QAC7B,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,MAAM,CAAC,8EAA8E,CAAC,CAAC,CAAC;YAC1G,OAAO;QACT,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,eAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC;QACnD,aAAa,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACjC,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,eAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,eAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,cAAc,YAAY,CAAC,EAAE,CAAC,CAAC;QACzG,CAAC,CAAC,CAAC;QAEH,iDAAiD;QACjD,MAAM,eAAe,GAAG,eAAe;YACrC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,eAAe,CAAC;YACpD,CAAC,CAAC,SAAS,CAAC;QAEd,IAAI,eAAe,EAAE,CAAC;YACpB,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,eAAK,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC,CAAC;YACjF,OAAO,CAAC,GAAG,CAAC,KAAK,eAAK,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,eAAK,CAAC,IAAI,CAAC,IAAI,eAAe,CAAC,iBAAiB,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC,CAAC;YAEzH,IAAI,MAAM,IAAA,0BAAa,EAAC,uBAAuB,EAAE,IAAI,CAAC,EAAE,CAAC;gBACvD,MAAM,kBAAkB,CAAC,eAAe,CAAC,EAAE,EAAE,eAAe,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;gBACpF,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC,CAAC;gBAC3D,OAAO;YACT,CAAC;QACH,CAAC;QAED,gDAAgD;QAChD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,eAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC;YAErD,iCAAiC;YACjC,MAAM,aAAa,GAAG,IAAI,GAAG,EAA4B,CAAC;YAC1D,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;gBAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,iBAAiB,IAAI,iBAAiB,CAAC;gBAC5D,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;oBAChC,aAAa,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;gBACjC,CAAC;gBACD,aAAa,CAAC,GAAG,CAAC,OAAO,CAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzC,CAAC;YAED,kBAAkB;YAClB,KAAK,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,IAAI,aAAa,EAAE,CAAC;gBACnD,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,OAAO,OAAO,GAAG,CAAC,CAAC,CAAC;gBAC3C,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;oBAC/B,OAAO,CAAC,GAAG,CAAC,SAAS,eAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAChD,CAAC;YACH,CAAC;QACH,CAAC;QAKD,MAAM,aAAa,GAAgD;YACjE,EAAE,KAAK,EAAE,eAAK,CAAC,KAAK,CAAC,sBAAsB,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;YACzE,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACvB,KAAK,EAAE,YAAY,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC7F,KAAK,EAAE,EAAE,IAAI,EAAE,MAAe,EAAE,OAAO,EAAE,IAAI,EAAE;aAChD,CAAC,CAAC;SACJ,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAA,yBAAY,EAC/B,8BAA8B,EAC9B,aAAa,EACb,CAAC,CACF,CAAC;QAEF,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC3B,MAAM,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAC1F,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,0BAA0B,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YAC1E,OAAO;QACT,CAAC;QAED,6BAA6B;QAC7B,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,eAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC;QAErD,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,IAAI,iBAAiB,EAAE,CAAC;QACzD,MAAM,mBAAmB,GAAG,OAAO,CAAC,WAAW,IAAI,wBAAwB,EAAE,CAAC;QAE9E,MAAM,SAAS,GAAG,CAAC,MAAM,IAAA,wBAAW,EAAC,cAAc,EAAE,YAAY,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,YAAY,CAAC;QAC3F,MAAM,YAAY,GAAG,CAAC,MAAM,IAAA,wBAAW,EAAC,gBAAgB,EAAE,eAAe,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,eAAe,CAAC;QACtG,MAAM,gBAAgB,GAAG,CAAC,MAAM,IAAA,wBAAW,EAAC,wBAAwB,EAAE,mBAAmB,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,mBAAmB,CAAC;QAE1H,sBAAsB;QACtB,IAAI,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC;QACnC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC/B,OAAO,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;gBAChC,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,sBAAsB,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YACzE,CAAC;iBAAM,CAAC;gBACN,MAAM,WAAW,GAAG,MAAM,IAAA,yBAAY,EACpC,qBAAqB,EACrB,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;oBACxB,KAAK,EAAE,GAAG,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,cAAc,YAAY;oBACrD,KAAK,EAAE,GAAG;iBACX,CAAC,CAAC,EACH,CAAC,CACF,CAAC;gBACF,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC;YAC7B,CAAC;QACH,CAAC;QAED,OAAO,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;QACrC,MAAM,MAAM,GAAG,MAAM,IAAA,wBAAe,EAAC,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAE,OAAO,CAAC,CAAC;QACzF,OAAO,CAAC,OAAO,CAAC,4BAA4B,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;QAEjE,MAAM,kBAAkB,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;QACrE,gBAAgB,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,CAAC,CAAC;QAC/E,MAAM,kBAAkB,EAAE,CAAC;QAE3B,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,eAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,qDAAqD,CAAC,CAAC;QACnE,OAAO,CAAC,GAAG,CAAC,YAAY,eAAK,CAAC,IAAI,CAAC,cAAc,CAAC,gCAAgC,CAAC,CAAC;QACpF,OAAO,CAAC,GAAG,CAAC,eAAe,eAAK,CAAC,IAAI,CAAC,WAAW,CAAC,iCAAiC,CAAC,CAAC;QAErF,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,eAAK,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC,CAAC;IACxE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACtC,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,OAAO,CAAC,KAAK,CAAC,eAAK,CAAC,GAAG,CAAC,UAAU,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACtD,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,KAAa,EAAE,MAAmB;IACpD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,KAAK,eAAK,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;IAC5D,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,KAAK,eAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;IAC5D,CAAC;AACH,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,SAAiB,EAAE,IAAY,EAAE,OAAgB;IACjF,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,UAAU,GAAG,IAAA,gBAAI,EAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACzC,MAAM,eAAe,GAAG,IAAA,gBAAI,EAAC,UAAU,EAAE,aAAa,CAAC,CAAC;IACxD,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;IAEzE,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,WAAW,GAAG;QAClB,SAAS;QACT,IAAI;QACJ,OAAO;QACP,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC;IACF,aAAa,CAAC,eAAe,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,gBAAgB,CAAC,SAAiB,EAAE,IAAY,EAAE,OAAgB,EAAE,WAAoB;IAC/F,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,eAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,CAAC,kBAAkB,eAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClD,OAAO,CAAC,GAAG,CAAC,kBAAkB,eAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACvD,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,kBAAkB,eAAK,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACvD,CAAC;IACD,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,kBAAkB,eAAK,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,WAAW,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/G,CAAC;AACH,CAAC;AAED,KAAK,UAAU,kBAAkB;IAC/B,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,eAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC;IACtD,IAAI,CAAC;QACH,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAA,oBAAW,GAAE,CAAC;QACzC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,KAAK,MAAM,EAAE,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;gBACtC,OAAO,CAAC,GAAG,CAAC,OAAO,eAAK,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,WAAW,WAAW,EAAE,CAAC,eAAe,YAAY,CAAC,CAAC;YACvG,CAAC;YACD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,aAAa,QAAQ,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;YACnE,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,yEAAyE,CAAC,CAAC,CAAC;QACrG,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC,CAAC;IACxD,CAAC;AACH,CAAC"}
@@ -0,0 +1,10 @@
1
+ interface SyncOptions {
2
+ projectId?: number;
3
+ languages?: string;
4
+ format?: "all" | "claude" | "cursor" | "copilot";
5
+ dryRun?: boolean;
6
+ verbose?: boolean;
7
+ }
8
+ export declare function syncCommand(options: SyncOptions): Promise<void>;
9
+ export {};
10
+ //# sourceMappingURL=sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAuCA,UAAU,WAAW;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IACjD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAwID,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA2NrE"}
@@ -0,0 +1,328 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.syncCommand = syncCommand;
7
+ const node_fs_1 = require("node:fs");
8
+ const node_path_1 = require("node:path");
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const ora_1 = __importDefault(require("ora"));
11
+ const api_js_1 = require("../api.js");
12
+ const fileManager_js_1 = require("../utils/fileManager.js");
13
+ const prompts_js_1 = require("../utils/prompts.js");
14
+ const scanner_js_1 = require("../utils/scanner.js");
15
+ const init_js_1 = require("./init.js");
16
+ function loadLocalProjectConfig() {
17
+ const configPath = (0, node_path_1.join)(process.cwd(), ".convext", "config.json");
18
+ if (!(0, node_fs_1.existsSync)(configPath)) {
19
+ return null;
20
+ }
21
+ try {
22
+ const content = (0, node_fs_1.readFileSync)(configPath, "utf-8");
23
+ const config = JSON.parse(content);
24
+ if (config.projectId && typeof config.projectId === "number") {
25
+ return config;
26
+ }
27
+ return null;
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ }
33
+ const DOTFILE_CONFIGS = {
34
+ claude: {
35
+ filename: "CLAUDE.md",
36
+ // CLAUDE.md uses the server markdown as-is
37
+ transform: (md) => md,
38
+ },
39
+ cursor: {
40
+ filename: ".cursorrules",
41
+ // .cursorrules adds a comment header
42
+ transform: (md) => `# Cursor Rules\n\n# Generated by Convext - Do not edit manually.\n\n${md}`,
43
+ },
44
+ copilot: {
45
+ filename: ".github/copilot-instructions.md",
46
+ // Copilot uses HTML comment
47
+ transform: (md) => `<!-- Generated by Convext - Do not edit manually -->\n\n${md}`,
48
+ },
49
+ };
50
+ function ensureDirectoryExists(filePath) {
51
+ (0, fileManager_js_1.ensureParentDir)(filePath);
52
+ }
53
+ async function maybeAssignRuleSets(projectId, context, insights) {
54
+ const available = dedupeRuleSets([
55
+ ...(context.available_rule_sets?.organization ?? []),
56
+ ...(context.available_rule_sets?.global ?? []),
57
+ ]);
58
+ if (available.length === 0) {
59
+ console.log(chalk_1.default.yellow("No rule sets available. Create some in the Convext UI first."));
60
+ return false;
61
+ }
62
+ const suggestions = suggestRuleSets(available, insights);
63
+ // Pre-select suggested rulesets based on detected tech
64
+ const suggestedSlugs = new Set(suggestions.map(s => s.slug));
65
+ const defaultSelected = available
66
+ .map((rs, idx) => suggestedSlugs.has(rs.slug) ? idx : -1)
67
+ .filter(idx => idx >= 0);
68
+ // Use checkbox for multi-select with arrow keys and spacebar
69
+ const selection = await (0, prompts_js_1.promptCheckbox)("Select rule sets to assign (↑↓ navigate, space toggle, enter confirm):", available.map(rs => ({
70
+ label: `${rs.name}${rs.slug ? chalk_1.default.gray(` (${rs.slug})`) : ""}`,
71
+ value: rs,
72
+ })), defaultSelected);
73
+ if (selection.length === 0) {
74
+ console.log(chalk_1.default.gray("No rule sets selected."));
75
+ return false;
76
+ }
77
+ const slugs = selection
78
+ .map((rs) => rs.slug)
79
+ .filter((slug) => Boolean(slug));
80
+ if (slugs.length === 0) {
81
+ console.log(chalk_1.default.red("No valid rule set slugs selected."));
82
+ return false;
83
+ }
84
+ const assignment = await (0, api_js_1.assignRuleSets)(projectId, slugs);
85
+ if (assignment.assigned > 0) {
86
+ console.log(chalk_1.default.green(`\n✓ Assigned ${assignment.assigned} rule set(s): ${assignment.rule_sets.map((rs) => rs.name).join(", ")}`));
87
+ return true;
88
+ }
89
+ console.log(chalk_1.default.gray("No new rule sets were assigned."));
90
+ return false;
91
+ }
92
+ function suggestRuleSets(available, insights) {
93
+ const keywords = Array.from(new Set([
94
+ ...Array.from(insights.languages),
95
+ ...Array.from(insights.frameworks),
96
+ ...Array.from(insights.packageManagers),
97
+ ])).map((kw) => kw.toLowerCase());
98
+ if (keywords.length === 0) {
99
+ return available.slice(0, 5);
100
+ }
101
+ const matches = available.filter((rs) => {
102
+ const name = rs.name.toLowerCase();
103
+ return keywords.some((kw) => name.includes(kw));
104
+ });
105
+ return (matches.length > 0 ? matches : available).slice(0, 5);
106
+ }
107
+ function dedupeRuleSets(ruleSets) {
108
+ const seen = new Set();
109
+ const result = [];
110
+ for (const rs of ruleSets) {
111
+ const key = rs.slug || rs.name;
112
+ if (seen.has(key))
113
+ continue;
114
+ seen.add(key);
115
+ result.push(rs);
116
+ }
117
+ return result;
118
+ }
119
+ function hasChanges(filePath, newContent) {
120
+ if (!(0, node_fs_1.existsSync)(filePath)) {
121
+ return true;
122
+ }
123
+ const existing = (0, node_fs_1.readFileSync)(filePath, "utf-8");
124
+ return existing !== newContent;
125
+ }
126
+ async function syncCommand(options) {
127
+ const verbose = options.verbose ?? false;
128
+ const log = (msg) => verbose && console.log(chalk_1.default.gray(`[verbose] ${msg}`));
129
+ // Check for local project config first
130
+ const localConfig = loadLocalProjectConfig();
131
+ let projectId = options.projectId;
132
+ if (!projectId && localConfig) {
133
+ projectId = localConfig.projectId;
134
+ log(`Using project ID ${projectId} from .convext.json`);
135
+ }
136
+ // If no project ID, prompt to initialize
137
+ if (!projectId) {
138
+ console.log(chalk_1.default.yellow("\nNo project configured for this directory."));
139
+ console.log(chalk_1.default.gray("A .convext.json file links this repo to your Convext project.\n"));
140
+ const shouldInit = await (0, prompts_js_1.promptConfirm)("Would you like to initialize/connect this project now?", true);
141
+ if (shouldInit) {
142
+ await (0, init_js_1.initCommand)({});
143
+ // Re-load the config after init
144
+ const newConfig = loadLocalProjectConfig();
145
+ if (newConfig) {
146
+ projectId = newConfig.projectId;
147
+ console.log(""); // Add spacing
148
+ }
149
+ else {
150
+ console.log(chalk_1.default.yellow("\nProject initialization was cancelled or failed."));
151
+ console.log(`Run ${chalk_1.default.cyan("convext init")} to set up the project manually.`);
152
+ return;
153
+ }
154
+ }
155
+ else {
156
+ console.log(chalk_1.default.gray("\nTo initialize later, run: ") + chalk_1.default.cyan("convext init"));
157
+ console.log(chalk_1.default.gray("Or specify a project ID: ") + chalk_1.default.cyan("convext sync --project <id>"));
158
+ return;
159
+ }
160
+ }
161
+ const spinner = (0, ora_1.default)("Fetching engineering rules and standards...").start();
162
+ try {
163
+ log("Scanning repository for insights...");
164
+ const insights = (0, scanner_js_1.scanRepository)();
165
+ log(`Found languages: ${Array.from(insights.languages).join(", ") || "none"}`);
166
+ log(`Found frameworks: ${Array.from(insights.frameworks).join(", ") || "none"}`);
167
+ let projectMeta;
168
+ spinner.text = "Fetching project context...";
169
+ log(`Fetching context for project ID: ${projectId}`);
170
+ try {
171
+ projectMeta = await (0, api_js_1.getProjectContext)(projectId);
172
+ log(`Project context response: ${JSON.stringify(projectMeta, null, 2)}`);
173
+ // Check if API returned an error (project not found)
174
+ if (projectMeta.error) {
175
+ spinner.fail("Project not found on server");
176
+ console.log(chalk_1.default.yellow(`\nProject ID ${projectId} no longer exists on the server.`));
177
+ console.log(chalk_1.default.gray("This can happen if the project was deleted or the database was reset.\n"));
178
+ const shouldReinit = await (0, prompts_js_1.promptConfirm)("Would you like to re-register this project?", true);
179
+ if (shouldReinit) {
180
+ // Delete the old config and run init
181
+ const configPath = (0, node_path_1.join)(process.cwd(), ".convext", "config.json");
182
+ const { unlinkSync } = await import("node:fs");
183
+ try {
184
+ unlinkSync(configPath);
185
+ }
186
+ catch { /* ignore */ }
187
+ await (0, init_js_1.initCommand)({});
188
+ // Re-load the config after init
189
+ const newConfig = loadLocalProjectConfig();
190
+ if (newConfig) {
191
+ projectId = newConfig.projectId;
192
+ spinner.start("Fetching project context...");
193
+ projectMeta = await (0, api_js_1.getProjectContext)(projectId);
194
+ }
195
+ else {
196
+ console.log(chalk_1.default.yellow("\nProject registration was cancelled."));
197
+ return;
198
+ }
199
+ }
200
+ else {
201
+ console.log(chalk_1.default.gray("\nTo re-register later, delete .convext.json and run: ") + chalk_1.default.cyan("convext init"));
202
+ return;
203
+ }
204
+ }
205
+ }
206
+ catch (error) {
207
+ spinner.fail("Failed to fetch project context");
208
+ throw error;
209
+ }
210
+ const rulesCount = (projectMeta.rules ?? []).length;
211
+ const standardsCount = (projectMeta.standards ?? projectMeta.language_standards ?? []).length;
212
+ log(`Project has ${rulesCount} rules and ${standardsCount} standards`);
213
+ spinner.succeed(`Fetched project context (${rulesCount} rules, ${standardsCount} standards)`);
214
+ if (insights.aiFiles.length > 0) {
215
+ console.log(chalk_1.default.gray(`Detected existing AI files:\n - ${insights.aiFiles.join("\n - ")}`));
216
+ }
217
+ // Check if project has explicitly assigned rulesets (not just inheriting globals)
218
+ const assignedRuleSets = projectMeta.assigned_rule_sets ?? [];
219
+ const hasExplicitAssignments = assignedRuleSets.length > 0;
220
+ if (!hasExplicitAssignments && projectId && projectMeta) {
221
+ console.log(chalk_1.default.yellow("\nNo rule sets are explicitly assigned to this project."));
222
+ console.log(chalk_1.default.gray("Select rule sets based on your tech stack:\n"));
223
+ const assigned = await maybeAssignRuleSets(projectId, projectMeta, insights);
224
+ if (assigned) {
225
+ spinner.start("Reloading project context...");
226
+ projectMeta = await (0, api_js_1.getProjectContext)(projectId);
227
+ spinner.succeed("Rule sets assigned and context refreshed");
228
+ }
229
+ else {
230
+ // User declined - don't use global rules by default
231
+ console.log(chalk_1.default.yellow("\nNo rule sets selected. Skipping file generation."));
232
+ console.log(chalk_1.default.gray("Run 'convext sync' again to select rule sets, or assign them in the Convext UI."));
233
+ return;
234
+ }
235
+ }
236
+ // Determine which formats to generate
237
+ const formats = options.format === "all" || !options.format
238
+ ? Object.keys(DOTFILE_CONFIGS)
239
+ : [options.format];
240
+ // Generate and write files
241
+ const cwd = process.cwd();
242
+ const results = [];
243
+ // Fetch pre-rendered markdown from the server (matches server preview exactly)
244
+ log("Fetching pre-rendered markdown from server...");
245
+ let serverMarkdown;
246
+ try {
247
+ const contextResponse = await (0, api_js_1.getProjectMarkdown)(projectMeta.project?.slug || String(projectId));
248
+ serverMarkdown = contextResponse.markdown;
249
+ log(`Got ${contextResponse.rules_count} rules, ${contextResponse.standards_count} standards`);
250
+ }
251
+ catch (error) {
252
+ // Fallback: if API endpoint not available, we can't sync
253
+ console.error(chalk_1.default.red("Failed to fetch project context from server"));
254
+ throw error;
255
+ }
256
+ for (const format of formats) {
257
+ const config = DOTFILE_CONFIGS[format];
258
+ if (!config)
259
+ continue;
260
+ const filePath = (0, node_path_1.join)(cwd, config.filename);
261
+ const content = config.transform(serverMarkdown);
262
+ if (options.dryRun) {
263
+ console.log(chalk_1.default.cyan(`\n[DRY RUN] Would write to ${config.filename}:`));
264
+ console.log(chalk_1.default.gray("─".repeat(60)));
265
+ console.log(content.slice(0, 500) + (content.length > 500 ? "\n..." : ""));
266
+ console.log(chalk_1.default.gray("─".repeat(60)));
267
+ results.push({ file: config.filename, status: "unchanged" });
268
+ }
269
+ else {
270
+ const changed = hasChanges(filePath, content);
271
+ if (changed) {
272
+ ensureDirectoryExists(filePath);
273
+ const backupPath = (0, fileManager_js_1.backupFile)(filePath);
274
+ if (backupPath) {
275
+ console.log(chalk_1.default.gray(` ↻ Backed up existing ${config.filename} to ${backupPath}`));
276
+ }
277
+ (0, node_fs_1.writeFileSync)(filePath, content, "utf-8");
278
+ results.push({
279
+ file: config.filename,
280
+ status: (0, node_fs_1.existsSync)(filePath) ? "updated" : "created",
281
+ });
282
+ }
283
+ else {
284
+ results.push({ file: config.filename, status: "unchanged" });
285
+ }
286
+ }
287
+ }
288
+ // Summary
289
+ console.log("\n" + chalk_1.default.bold("Sync Results:"));
290
+ for (const result of results) {
291
+ const icon = result.status === "created"
292
+ ? chalk_1.default.green("✓")
293
+ : result.status === "updated"
294
+ ? chalk_1.default.yellow("↻")
295
+ : chalk_1.default.gray("○");
296
+ const status = result.status === "created"
297
+ ? chalk_1.default.green("created")
298
+ : result.status === "updated"
299
+ ? chalk_1.default.yellow("updated")
300
+ : chalk_1.default.gray("unchanged");
301
+ console.log(` ${icon} ${result.file} - ${status}`);
302
+ }
303
+ const changedCount = results.filter((r) => r.status !== "unchanged").length;
304
+ if (changedCount > 0 && !options.dryRun) {
305
+ console.log("\n" + chalk_1.default.green(`✓ Synced ${changedCount} file(s) with Convext`));
306
+ }
307
+ else if (options.dryRun) {
308
+ console.log("\n" + chalk_1.default.cyan("Dry run complete - no files were modified"));
309
+ }
310
+ else {
311
+ console.log("\n" + chalk_1.default.gray("All files are up to date"));
312
+ }
313
+ }
314
+ catch (error) {
315
+ spinner.fail("Sync failed");
316
+ if (error instanceof Error) {
317
+ console.error(chalk_1.default.red(`Error: ${error.message}`));
318
+ if (verbose) {
319
+ console.error(chalk_1.default.gray(`Stack trace:\n${error.stack}`));
320
+ }
321
+ }
322
+ else {
323
+ console.error(chalk_1.default.red(`Unknown error: ${String(error)}`));
324
+ }
325
+ process.exit(1);
326
+ }
327
+ }
328
+ //# sourceMappingURL=sync.js.map