Skip to content

Commit 90e9c49

Browse files
Factor out get cline rules function (RooCodeInc#2827)
* factor out cline rules functionality * changeset * Update fs.ts --------- Co-authored-by: Saoud Rizwan <[email protected]>
1 parent 0ea8506 commit 90e9c49

File tree

4 files changed

+73
-36
lines changed

4 files changed

+73
-36
lines changed

.changeset/kind-terms-dance.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"claude-dev": patch
3+
---
4+
5+
Refactor: Remove cline rules functions from task/index.ts
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import path from "path"
2+
import { GlobalFileNames } from "../../../storage/disk"
3+
import { fileExistsAtPath, isDirectory, readDirectory } from "../../../../utils/fs"
4+
import { formatResponse } from "../../../prompts/responses"
5+
import fs from "fs/promises"
6+
7+
export const getClineRules = async (cwd: string) => {
8+
const clineRulesFilePath = path.resolve(cwd, GlobalFileNames.clineRules)
9+
10+
let clineRulesFileInstructions: string | undefined
11+
12+
if (await fileExistsAtPath(clineRulesFilePath)) {
13+
if (await isDirectory(clineRulesFilePath)) {
14+
try {
15+
const rulesFilePaths = await readDirectory(path.join(cwd, GlobalFileNames.clineRules))
16+
const rulesFilesTotalContent = await getClineRulesFilesTotalContent(rulesFilePaths, cwd)
17+
clineRulesFileInstructions = formatResponse.clineRulesDirectoryInstructions(cwd, rulesFilesTotalContent)
18+
} catch {
19+
console.error(`Failed to read .clinerules directory at ${clineRulesFilePath}`)
20+
}
21+
} else {
22+
try {
23+
const ruleFileContent = (await fs.readFile(clineRulesFilePath, "utf8")).trim()
24+
if (ruleFileContent) {
25+
clineRulesFileInstructions = formatResponse.clineRulesFileInstructions(cwd, ruleFileContent)
26+
}
27+
} catch {
28+
console.error(`Failed to read .clinerules file at ${clineRulesFilePath}`)
29+
}
30+
}
31+
}
32+
33+
return clineRulesFileInstructions
34+
}
35+
36+
const getClineRulesFilesTotalContent = async (rulesFilePaths: string[], cwd: string) => {
37+
const ruleFilesTotalContent = await Promise.all(
38+
rulesFilePaths.map(async (filePath) => {
39+
const ruleFilePath = path.resolve(cwd, filePath)
40+
const ruleFilePathRelative = path.relative(cwd, ruleFilePath)
41+
return `${ruleFilePathRelative}\n` + (await fs.readFile(ruleFilePath, "utf8")).trim()
42+
}),
43+
).then((contents) => contents.join("\n\n"))
44+
return ruleFilesTotalContent
45+
}

src/core/task/index.ts

Lines changed: 4 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Anthropic } from "@anthropic-ai/sdk"
22
import cloneDeep from "clone-deep"
3-
import fs from "fs/promises"
43
import getFolderSize from "get-folder-size"
54
import { setTimeout as setTimeoutPromise } from "node:timers/promises"
65
import os from "os"
@@ -73,18 +72,17 @@ import {
7372
checkIsAnthropicContextWindowError,
7473
checkIsOpenRouterContextWindowError,
7574
} from "../context/context-management/context-error-handling"
76-
import { Controller } from "../controller"
75+
7776
import {
7877
ensureTaskDirectoryExists,
7978
getSavedApiConversationHistory,
8079
getSavedClineMessages,
8180
saveApiConversationHistory,
8281
saveClineMessages,
83-
GlobalFileNames,
84-
getTaskMetadata,
8582
} from "../storage/disk"
8683
import { McpHub } from "../../services/mcp/McpHub"
8784
import WorkspaceTracker from "../../integrations/workspace/WorkspaceTracker"
85+
import { getClineRules } from "../context/instructions/user-instructions/cline-rules"
8886
import { getGlobalState } from "../storage/state"
8987

9088
const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0) ?? path.join(os.homedir(), "Desktop") // may or may not exist but fs checking existence would immediately ask for permission which would be bad UX, need to come up with a better solution
@@ -1277,38 +1275,8 @@ export class Task {
12771275
preferredLanguage && preferredLanguage !== DEFAULT_LANGUAGE_SETTINGS
12781276
? `# Preferred Language\n\nSpeak in ${preferredLanguage}.`
12791277
: ""
1280-
const clineRulesFilePath = path.resolve(cwd, GlobalFileNames.clineRules)
1281-
let clineRulesFileInstructions: string | undefined
1282-
if (await fileExistsAtPath(clineRulesFilePath)) {
1283-
if (await isDirectory(clineRulesFilePath)) {
1284-
try {
1285-
// Read all files in the .clinerules/ directory.
1286-
const ruleFiles = await fs
1287-
.readdir(clineRulesFilePath, { withFileTypes: true, recursive: true })
1288-
.then((files) => files.filter((file) => file.isFile()))
1289-
.then((files) => files.map((file) => path.resolve(file.parentPath, file.name)))
1290-
const ruleFilesTotalContent = await Promise.all(
1291-
ruleFiles.map(async (file) => {
1292-
const ruleFilePath = path.resolve(clineRulesFilePath, file)
1293-
const ruleFilePathRelative = path.relative(cwd, ruleFilePath)
1294-
return `${ruleFilePathRelative}\n` + (await fs.readFile(ruleFilePath, "utf8")).trim()
1295-
}),
1296-
).then((contents) => contents.join("\n\n"))
1297-
clineRulesFileInstructions = formatResponse.clineRulesDirectoryInstructions(cwd, ruleFilesTotalContent)
1298-
} catch {
1299-
console.error(`Failed to read .clinerules directory at ${clineRulesFilePath}`)
1300-
}
1301-
} else {
1302-
try {
1303-
const ruleFileContent = (await fs.readFile(clineRulesFilePath, "utf8")).trim()
1304-
if (ruleFileContent) {
1305-
clineRulesFileInstructions = formatResponse.clineRulesFileInstructions(cwd, ruleFileContent)
1306-
}
1307-
} catch {
1308-
console.error(`Failed to read .clinerules file at ${clineRulesFilePath}`)
1309-
}
1310-
}
1311-
}
1278+
1279+
const clineRulesFileInstructions = await getClineRules(cwd)
13121280

13131281
const clineIgnoreContent = this.clineIgnoreController.clineIgnoreContent
13141282
let clineIgnoreInstructions: string | undefined

src/utils/fs.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,22 @@ export async function getFileSizeInKB(filePath: string): Promise<number> {
7474
return 0
7575
}
7676
}
77+
78+
/**
79+
* Recursively reads a directory and returns an array of absolute file paths.
80+
*
81+
* @param directoryPath - The path to the directory to read.
82+
* @returns A promise that resolves to an array of absolute file paths.
83+
* @throws Error if the directory cannot be read.
84+
*/
85+
export const readDirectory = async (directoryPath: string) => {
86+
try {
87+
const filePaths = await fs
88+
.readdir(directoryPath, { withFileTypes: true, recursive: true })
89+
.then((files) => files.filter((file) => file.isFile()))
90+
.then((files) => files.map((file) => path.resolve(file.parentPath, file.name)))
91+
return filePaths
92+
} catch {
93+
throw new Error(`Error reading directory at ${directoryPath}`)
94+
}
95+
}

0 commit comments

Comments
 (0)