Skip to content
This repository was archived by the owner on Nov 3, 2025. It is now read-only.

Commit 36ef14a

Browse files
committed
fix: ensure newline at end of files in configuration and source files
1 parent 798b230 commit 36ef14a

File tree

5 files changed

+150
-164
lines changed

5 files changed

+150
-164
lines changed

packages/setup-mode/biome.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
22
"extends": ["@sylphlab/biome-config"]
3-
}
3+
}

packages/setup-mode/package.json

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,12 @@
1818
"validate": "pnpm run check && pnpm run build && pnpm run test",
1919
"start": "node ./dist/index.js"
2020
},
21-
"keywords": [
22-
"roo",
23-
"cline",
24-
"sylphx",
25-
"custom-mode",
26-
"setup"
27-
],
21+
"keywords": ["roo", "cline", "sylphx", "custom-mode", "setup"],
2822
"author": "Sylph AI (via Roo)",
2923
"license": "MIT",
3024
"dependencies": {
3125
"fs-extra": "^11.2.0",
32-
"node-fetch": "^3.3.2"
26+
"node-fetch": "^3.3.2"
3327
},
3428
"devDependencies": {
3529
"@biomejs/biome": "^1.8.0",
@@ -48,4 +42,4 @@
4842
"publishConfig": {
4943
"access": "public"
5044
}
51-
}
45+
}

packages/setup-mode/src/index.ts

Lines changed: 139 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env node
2-
import * as fs from 'fs-extra';
32
import * as path from 'node:path';
3+
import * as fs from 'fs-extra';
44
import fetch from 'node-fetch'; // Use node-fetch v3 which supports ESM
55

66
// --- Constants ---
@@ -16,48 +16,55 @@ const CUSTOM_MODES_FILENAME = 'custom_modes.json';
1616
const ROO_EXTENSION_ID = 'rooveterinaryinc.roo-cline';
1717
// For now, using the provided Windows path. This WILL break on other systems.
1818
// biome-ignore lint/style: Required by TS's noUncheckedIndexedAccess for process.env
19-
const CUSTOM_MODES_DIR_WINDOWS = path.join(process.env['APPDATA'] || '', 'Code', 'User', 'globalStorage', ROO_EXTENSION_ID, 'settings');
19+
const CUSTOM_MODES_DIR_WINDOWS = path.join(
20+
process.env.APPDATA || '',
21+
'Code',
22+
'User',
23+
'globalStorage',
24+
ROO_EXTENSION_ID,
25+
'settings',
26+
);
2027
const CUSTOM_MODES_PATH = path.join(CUSTOM_MODES_DIR_WINDOWS, CUSTOM_MODES_FILENAME); // Adjust this based on OS detection later
2128

2229
const SYLPHX_MODE_SLUG = 'sylphx';
2330
const SYLPHX_MODE_NAME = '🪽 Sylphx';
24-
const SYLPHX_MODE_GROUPS = ["read", "edit", "browser", "command", "mcp"];
31+
const SYLPHX_MODE_GROUPS = ['read', 'edit', 'browser', 'command', 'mcp'];
2532
const SYLPHX_MODE_SOURCE = 'global'; // Assuming 'global' is correct
2633

2734
/**
2835
* Represents the response structure from the GitHub Contents API.
2936
*/
3037
interface GitHubContentResponse {
31-
/** Base64 encoded content of the file. */
32-
content?: string;
33-
/** Encoding type (should be 'base64'). */
34-
encoding?: string;
35-
/** Error message if the request failed. */
36-
message?: string;
38+
/** Base64 encoded content of the file. */
39+
content?: string;
40+
/** Encoding type (should be 'base64'). */
41+
encoding?: string;
42+
/** Error message if the request failed. */
43+
message?: string;
3744
}
3845

3946
/**
4047
* Represents the structure of a single custom mode definition.
4148
*/
4249
interface CustomMode {
43-
/** Unique identifier for the mode. */
44-
slug: string;
45-
/** Display name of the mode. */
46-
name: string;
47-
/** The core instructions or prompt defining the mode's behavior. */
48-
roleDefinition: string;
49-
/** List of capability groups enabled for the mode. */
50-
groups: string[];
51-
/** Source of the mode definition (e.g., 'global', 'user'). */
52-
source: string;
50+
/** Unique identifier for the mode. */
51+
slug: string;
52+
/** Display name of the mode. */
53+
name: string;
54+
/** The core instructions or prompt defining the mode's behavior. */
55+
roleDefinition: string;
56+
/** List of capability groups enabled for the mode. */
57+
groups: string[];
58+
/** Source of the mode definition (e.g., 'global', 'user'). */
59+
source: string;
5360
}
5461

5562
/**
5663
* Represents the structure of the custom_modes.json file.
5764
*/
5865
interface CustomModesFile {
59-
/** An array containing all custom mode definitions. */
60-
customModes: CustomMode[];
66+
/** An array containing all custom mode definitions. */
67+
customModes: CustomMode[];
6168
}
6269

6370
// --- Helper Functions ---
@@ -68,36 +75,35 @@ interface CustomModesFile {
6875
* @throws Throws an error if the fetch request fails, the API returns an error, or the content is invalid.
6976
*/
7077
export async function fetchLatestInstructions(): Promise<string> {
71-
console.log(`Fetching latest instructions from ${GITHUB_REPO_OWNER}/${GITHUB_REPO_NAME}...`);
72-
try {
73-
const response = await fetch(GITHUB_API_URL, {
74-
headers: {
75-
'Accept': 'application/vnd.github.v3+json',
76-
// Add 'Authorization': `token YOUR_GITHUB_TOKEN` if hitting rate limits
77-
},
78-
});
79-
80-
if (!response.ok) {
81-
throw new Error(`GitHub API request failed: ${response.status} ${response.statusText}`);
82-
}
83-
84-
const data = await response.json() as GitHubContentResponse;
85-
86-
if (data.message) {
87-
throw new Error(`GitHub API error: ${data.message}`);
88-
}
89-
90-
if (!data.content || data.encoding !== 'base64') {
91-
throw new Error('Invalid content received from GitHub API.');
92-
}
93-
94-
const decodedContent = Buffer.from(data.content, 'base64').toString('utf-8');
95-
console.log('Successfully fetched instructions.');
96-
return decodedContent;
97-
} catch (error) {
98-
console.error('Error fetching from GitHub:', error);
99-
throw new Error(`Failed to fetch instructions from GitHub: ${error instanceof Error ? error.message : String(error)}`);
78+
try {
79+
const response = await fetch(GITHUB_API_URL, {
80+
headers: {
81+
Accept: 'application/vnd.github.v3+json',
82+
// Add 'Authorization': `token YOUR_GITHUB_TOKEN` if hitting rate limits
83+
},
84+
});
85+
86+
if (!response.ok) {
87+
throw new Error(`GitHub API request failed: ${response.status} ${response.statusText}`);
10088
}
89+
90+
const data = (await response.json()) as GitHubContentResponse;
91+
92+
if (data.message) {
93+
throw new Error(`GitHub API error: ${data.message}`);
94+
}
95+
96+
if (!data.content || data.encoding !== 'base64') {
97+
throw new Error('Invalid content received from GitHub API.');
98+
}
99+
100+
const decodedContent = Buffer.from(data.content, 'base64').toString('utf-8');
101+
return decodedContent;
102+
} catch (error) {
103+
throw new Error(
104+
`Failed to fetch instructions from GitHub: ${error instanceof Error ? error.message : String(error)}`,
105+
);
106+
}
101107
}
102108

103109
/**
@@ -108,35 +114,30 @@ export async function fetchLatestInstructions(): Promise<string> {
108114
* @throws Throws an error if reading the file fails (excluding file not found or parse errors, which are handled).
109115
*/
110116
export async function readCustomModes(): Promise<CustomModesFile> {
111-
console.log(`Reading custom modes file: ${CUSTOM_MODES_PATH}`);
112-
try {
113-
// Ensure the directory exists
114-
await fs.ensureDir(path.dirname(CUSTOM_MODES_PATH));
115-
116-
if (!(await fs.pathExists(CUSTOM_MODES_PATH))) {
117-
console.log('custom_modes.json not found. Creating a new one.');
118-
const initialData: CustomModesFile = { customModes: [] };
119-
await fs.writeJson(CUSTOM_MODES_PATH, initialData, { spaces: 2 });
120-
return initialData;
121-
}
122-
123-
const fileContent = await fs.readJson(CUSTOM_MODES_PATH);
124-
// Basic validation
125-
if (!fileContent || !Array.isArray(fileContent.customModes)) {
126-
console.warn('custom_modes.json seems malformed. Resetting to default structure.');
127-
return { customModes: [] }; // Return default structure if format is wrong
128-
}
129-
console.log('Successfully read custom modes file.');
130-
return fileContent as CustomModesFile;
131-
} catch (error) {
132-
if (error instanceof SyntaxError) {
133-
console.error(`Error parsing JSON in ${CUSTOM_MODES_PATH}:`, error);
134-
console.warn('File content might be corrupted. Attempting to reset.');
135-
return { customModes: [] }; // Return default structure on parse error
136-
}
137-
console.error('Error reading custom modes file:', error);
138-
throw new Error(`Failed to read or parse ${CUSTOM_MODES_PATH}: ${error instanceof Error ? error.message : String(error)}`);
117+
try {
118+
// Ensure the directory exists
119+
await fs.ensureDir(path.dirname(CUSTOM_MODES_PATH));
120+
121+
if (!(await fs.pathExists(CUSTOM_MODES_PATH))) {
122+
const initialData: CustomModesFile = { customModes: [] };
123+
await fs.writeJson(CUSTOM_MODES_PATH, initialData, { spaces: 2 });
124+
return initialData;
125+
}
126+
127+
const fileContent = await fs.readJson(CUSTOM_MODES_PATH);
128+
// Basic validation
129+
if (!fileContent || !Array.isArray(fileContent.customModes)) {
130+
return { customModes: [] }; // Return default structure if format is wrong
131+
}
132+
return fileContent as CustomModesFile;
133+
} catch (error) {
134+
if (error instanceof SyntaxError) {
135+
return { customModes: [] }; // Return default structure on parse error
139136
}
137+
throw new Error(
138+
`Failed to read or parse ${CUSTOM_MODES_PATH}: ${error instanceof Error ? error.message : String(error)}`,
139+
);
140+
}
140141
}
141142

142143
/**
@@ -146,39 +147,34 @@ export async function readCustomModes(): Promise<CustomModesFile> {
146147
* @param instructions The latest role definition (instructions) for the Sylphx mode.
147148
* @returns A new CustomModesFile object with the updated/added Sylphx mode.
148149
*/
149-
export function updateModesData(
150-
modesData: CustomModesFile,
151-
instructions: string,
152-
): CustomModesFile {
153-
const existingModeIndex = modesData.customModes.findIndex(
154-
(mode) => mode.slug === SYLPHX_MODE_SLUG,
155-
);
156-
157-
const newMode: CustomMode = {
158-
slug: SYLPHX_MODE_SLUG,
159-
name: SYLPHX_MODE_NAME,
160-
roleDefinition: instructions,
161-
groups: SYLPHX_MODE_GROUPS,
162-
source: SYLPHX_MODE_SOURCE,
163-
};
164-
165-
if (existingModeIndex !== -1) {
166-
console.log(`Updating existing '${SYLPHX_MODE_SLUG}' mode definition.`);
167-
// Create a new array with the updated mode
168-
const updatedCustomModes = modesData.customModes.map((mode, index) =>
169-
index === existingModeIndex ? newMode : mode,
170-
);
171-
return {
172-
...modesData, // Spread other potential properties if any
173-
customModes: updatedCustomModes,
174-
};
175-
}
176-
console.log(`Adding new '${SYLPHX_MODE_SLUG}' mode definition.`);
177-
// Create a new array with the new mode added
178-
return {
179-
...modesData,
180-
customModes: [...modesData.customModes, newMode],
181-
};
150+
export function updateModesData(modesData: CustomModesFile, instructions: string): CustomModesFile {
151+
const existingModeIndex = modesData.customModes.findIndex(
152+
(mode) => mode.slug === SYLPHX_MODE_SLUG,
153+
);
154+
155+
const newMode: CustomMode = {
156+
slug: SYLPHX_MODE_SLUG,
157+
name: SYLPHX_MODE_NAME,
158+
roleDefinition: instructions,
159+
groups: SYLPHX_MODE_GROUPS,
160+
source: SYLPHX_MODE_SOURCE,
161+
};
162+
163+
if (existingModeIndex !== -1) {
164+
// Create a new array with the updated mode
165+
const updatedCustomModes = modesData.customModes.map((mode, index) =>
166+
index === existingModeIndex ? newMode : mode,
167+
);
168+
return {
169+
...modesData, // Spread other potential properties if any
170+
customModes: updatedCustomModes,
171+
};
172+
}
173+
// Create a new array with the new mode added
174+
return {
175+
...modesData,
176+
customModes: [...modesData.customModes, newMode],
177+
};
182178
}
183179

184180
/**
@@ -189,49 +185,45 @@ export function updateModesData(
189185
* @throws Throws an error if writing the file fails.
190186
*/
191187
export async function writeCustomModes(modesData: CustomModesFile): Promise<void> {
192-
console.log(`Writing updated modes to ${CUSTOM_MODES_PATH}`);
193-
try {
194-
await fs.writeJson(CUSTOM_MODES_PATH, modesData, { spaces: 2 });
195-
console.log('Successfully updated custom_modes.json.');
196-
} catch (error) {
197-
console.error('Error writing custom modes file:', error);
198-
throw new Error(`Failed to write updates to ${CUSTOM_MODES_PATH}: ${error instanceof Error ? error.message : String(error)}`);
199-
}
188+
try {
189+
await fs.writeJson(CUSTOM_MODES_PATH, modesData, { spaces: 2 });
190+
} catch (error) {
191+
throw new Error(
192+
`Failed to write updates to ${CUSTOM_MODES_PATH}: ${error instanceof Error ? error.message : String(error)}`,
193+
);
194+
}
200195
}
201196

202197
// --- Main Execution ---
203198
async function main() {
204-
console.log('Starting @sylphx/setup_mode...');
205-
try {
206-
// 1. Fetch latest instructions
207-
const latestInstructions = await fetchLatestInstructions();
208-
209-
// 2. Read existing custom modes file (or create if needed)
210-
let customModesData = await readCustomModes();
211-
212-
// 3. Update the data with the new/updated Sylphx mode
213-
customModesData = updateModesData(customModesData, latestInstructions);
214-
215-
// 4. Write the updated data back to the file
216-
await writeCustomModes(customModesData);
217-
218-
console.log(`✅ Successfully added/updated the '${SYLPHX_MODE_SLUG}' mode in ${CUSTOM_MODES_PATH}`);
219-
process.exit(0); // Success
220-
221-
} catch (error) {
222-
console.error('\n❌ An error occurred during setup:');
223-
console.error(error instanceof Error ? error.message : String(error));
224-
process.exit(1); // Failure
225-
}
199+
try {
200+
// 1. Fetch latest instructions
201+
const latestInstructions = await fetchLatestInstructions();
202+
203+
// 2. Read existing custom modes file (or create if needed)
204+
let customModesData = await readCustomModes();
205+
206+
// 3. Update the data with the new/updated Sylphx mode
207+
customModesData = updateModesData(customModesData, latestInstructions);
208+
209+
// 4. Write the updated data back to the file
210+
await writeCustomModes(customModesData);
211+
process.exit(0); // Success
212+
} catch (_error) {
213+
process.exit(1); // Failure
214+
}
226215
}
227216

228217
// Execute the main function only if the script is run directly
229218
// This prevents it from running when imported as a module (e.g., in tests)
230219
// Note: process.argv[1] might not be reliable in all execution contexts (e.g., pkg).
231220
// A more robust check might be needed depending on packaging/distribution method.
232-
if (import.meta.url.startsWith('file:') && process.argv[1] && import.meta.url.endsWith(path.basename(process.argv[1]))) {
233-
main().catch(err => {
234-
console.error("Unhandled error in main execution:", err);
235-
process.exit(1); // Ensure exit on unhandled main rejection
236-
});
237-
}
221+
if (
222+
import.meta.url.startsWith('file:') &&
223+
process.argv[1] &&
224+
import.meta.url.endsWith(path.basename(process.argv[1]))
225+
) {
226+
main().catch((_err) => {
227+
process.exit(1); // Ensure exit on unhandled main rejection
228+
});
229+
}

packages/setup-mode/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@
1010
"include": ["src/**/*"],
1111
// Exclude build output and node_modules
1212
"exclude": ["node_modules", "dist"]
13-
}
13+
}

0 commit comments

Comments
 (0)