|
1 | 1 | import { BaseCommand } from './baseCommand.js' |
2 | | -import { readFile, writeFile, access, mkdir } from 'fs/promises' |
3 | | -import path, { join } from 'path' |
4 | | -import { constants } from 'fs' |
5 | | -import prompts from 'prompts' |
6 | | -import { fileURLToPath } from 'node:url' |
7 | | - |
8 | | -const __dirname = path.dirname(fileURLToPath(import.meta.url)) |
9 | | - |
10 | | -const BASE_RULES_FILE_PATH = join(__dirname, '../ai-context/checkly.rules.md') |
11 | | - |
12 | | -// AI IDE configurations mapping |
13 | | -const AI_IDE_CONFIGS = { |
14 | | - 'Windsurf': { |
15 | | - rulesFolder: '.windsurf/rules', |
16 | | - rulesFileName: 'checkly.md', |
17 | | - }, |
18 | | - 'GitHub Copilot': { |
19 | | - rulesFolder: '.github/instructions', |
20 | | - rulesFileName: 'checkly.instructions.md', |
21 | | - }, |
22 | | - 'Cursor': { |
23 | | - rulesFolder: '.cursor/rules', |
24 | | - rulesFileName: 'checkly.mdc', |
25 | | - }, |
26 | | - 'Plain Markdown (checkly.md)': { |
27 | | - rulesFolder: '.', |
28 | | - rulesFileName: 'checkly.md', |
29 | | - }, |
30 | | -} as const |
31 | 2 |
|
32 | 3 | export default class Rules extends BaseCommand { |
33 | 4 | static hidden = false |
34 | 5 | static readOnly = true |
35 | 6 | static idempotent = true |
| 7 | + static state = 'deprecated' |
36 | 8 | static description = |
37 | | - 'Generate a rules file to use with AI IDEs and Copilots.' |
38 | | - |
39 | | - async run (): Promise<void> { |
40 | | - // Read the base rules file |
41 | | - const rulesContent = await this.readBaseRulesFile() |
42 | | - if (!rulesContent) { |
43 | | - this.error(`Failed to read rules file at ${BASE_RULES_FILE_PATH}`) |
44 | | - } |
45 | | - |
46 | | - // In non-interactive mode, print rules to stdout and exit |
47 | | - const isNonInteractive = !process.stdin.isTTY |
48 | | - || !process.stdout.isTTY |
49 | | - || process.env.CI |
50 | | - || process.env.CHECKLY_NON_INTERACTIVE |
51 | | - if (isNonInteractive) { |
52 | | - this.log(rulesContent) |
53 | | - return |
54 | | - } |
55 | | - |
56 | | - try { |
57 | | - // Create options for multiselect - offer all configs from AI_IDE_CONFIGS |
58 | | - const choices = Object.entries(AI_IDE_CONFIGS).map(([ideName, ideConfig]) => { |
59 | | - return { |
60 | | - title: `${ideName} (${path.join(ideConfig.rulesFolder, ideConfig.rulesFileName)})`, |
61 | | - value: ideConfig, |
62 | | - selected: false, |
63 | | - } |
64 | | - }) |
65 | | - |
66 | | - const isNonInteractive = !process.stdin.isTTY |
67 | | - || !process.stdout.isTTY |
68 | | - || process.env.CI |
69 | | - || process.env.CHECKLY_NON_INTERACTIVE |
70 | | - |
71 | | - // Interactive mode - show multiselect |
72 | | - const { configs: selectedConfig } = await prompts({ |
73 | | - type: 'select', |
74 | | - name: 'configs', |
75 | | - message: 'Select the AI IDE configurations to generate rules for:', |
76 | | - choices, |
77 | | - initial: 0, |
78 | | - }) |
79 | | - |
80 | | - if (!selectedConfig) { |
81 | | - this.log('Operation cancelled.') |
82 | | - return |
83 | | - } |
84 | | - |
85 | | - this.log(`Generating rules`) |
86 | | - |
87 | | - // Create rules directory if it doesn't exist |
88 | | - const rulesDir = join(process.cwd(), selectedConfig.rulesFolder) |
89 | | - try { |
90 | | - await mkdir(rulesDir, { recursive: true }) |
91 | | - } catch { |
92 | | - // Directory might already exist, ignore error |
93 | | - } |
94 | | - |
95 | | - // Determine the target file path |
96 | | - const rulesFilePath = join(rulesDir, selectedConfig.rulesFileName) |
97 | | - |
98 | | - // Check if file already exists and ask for confirmation (only in interactive mode) |
99 | | - let shouldOverwrite = true |
100 | | - if (!isNonInteractive) { |
101 | | - shouldOverwrite = await this.confirmOverwrite(rulesFilePath) |
102 | | - } |
103 | | - |
104 | | - if (!shouldOverwrite) { |
105 | | - this.log(`Skipped ${rulesFilePath}`) |
106 | | - return |
107 | | - } |
108 | | - |
109 | | - // Save the rules file |
110 | | - await writeFile(rulesFilePath, rulesContent, 'utf8') |
111 | | - |
112 | | - this.log(`✅ Successfully saved Checkly rules file to: ${rulesFilePath}`) |
113 | | - } catch (error) { |
114 | | - this.error(`Failed to generate rules file: ${error}`) |
115 | | - } |
116 | | - } |
117 | | - |
118 | | - private async readBaseRulesFile (): Promise<string> { |
119 | | - try { |
120 | | - return await readFile(BASE_RULES_FILE_PATH, 'utf8') |
121 | | - } catch (error) { |
122 | | - throw new Error( |
123 | | - `Failed to read base rules file at ${BASE_RULES_FILE_PATH}: ${error}`, |
124 | | - { cause: error }, |
125 | | - ) |
126 | | - } |
127 | | - } |
128 | | - |
129 | | - private async confirmOverwrite (targetPath: string): Promise<boolean> { |
130 | | - try { |
131 | | - await access(targetPath, constants.F_OK) |
132 | | - |
133 | | - // File exists, ask for confirmation |
134 | | - const { overwrite } = await prompts({ |
135 | | - type: 'confirm', |
136 | | - name: 'overwrite', |
137 | | - message: `Rules file already exists at ${targetPath}. Do you want to overwrite it?`, |
138 | | - initial: false, |
139 | | - }) |
| 9 | + 'Deprecated. Use `checkly skills` instead.' |
140 | 10 |
|
141 | | - return overwrite ?? false |
142 | | - } catch { |
143 | | - // File doesn't exist, no need to confirm |
144 | | - return true |
145 | | - } |
| 11 | + run (): Promise<void> { |
| 12 | + this.log('The `rules` command is deprecated. Use `checkly skills` instead.') |
| 13 | + return Promise.resolve() |
146 | 14 | } |
147 | 15 | } |
0 commit comments