rulesync 3.23.6 → 3.24.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.
- package/README.md +53 -23
- package/dist/index.cjs +802 -112
- package/dist/index.js +802 -112
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ var ANNOUNCEMENT = "".trim();
|
|
|
8
8
|
|
|
9
9
|
// src/types/features.ts
|
|
10
10
|
import { z } from "zod/mini";
|
|
11
|
-
var ALL_FEATURES = ["rules", "ignore", "mcp", "subagents", "commands"];
|
|
11
|
+
var ALL_FEATURES = ["rules", "ignore", "mcp", "subagents", "commands", "skills"];
|
|
12
12
|
var ALL_FEATURES_WITH_WILDCARD = [...ALL_FEATURES, "*"];
|
|
13
13
|
var FeatureSchema = z.enum(ALL_FEATURES);
|
|
14
14
|
var FeaturesSchema = z.array(FeatureSchema);
|
|
@@ -137,6 +137,10 @@ async function readFileContent(filepath) {
|
|
|
137
137
|
logger.debug(`Reading file: ${filepath}`);
|
|
138
138
|
return readFile(filepath, "utf-8");
|
|
139
139
|
}
|
|
140
|
+
async function readFileBuffer(filepath) {
|
|
141
|
+
logger.debug(`Reading file buffer: ${filepath}`);
|
|
142
|
+
return readFile(filepath);
|
|
143
|
+
}
|
|
140
144
|
function addTrailingNewline(content) {
|
|
141
145
|
if (!content) {
|
|
142
146
|
return "\n";
|
|
@@ -164,11 +168,32 @@ async function listDirectoryFiles(dir) {
|
|
|
164
168
|
}
|
|
165
169
|
}
|
|
166
170
|
async function findFilesByGlobs(globs, options = {}) {
|
|
171
|
+
const { type = "all" } = options;
|
|
167
172
|
const items = globSync(globs, { withFileTypes: true });
|
|
168
|
-
|
|
169
|
-
|
|
173
|
+
switch (type) {
|
|
174
|
+
case "file":
|
|
175
|
+
return items.filter((item) => item.isFile()).map((item) => join(item.parentPath, item.name));
|
|
176
|
+
case "dir":
|
|
177
|
+
return items.filter((item) => item.isDirectory()).map((item) => join(item.parentPath, item.name));
|
|
178
|
+
case "all":
|
|
179
|
+
return items.map((item) => join(item.parentPath, item.name));
|
|
180
|
+
default:
|
|
181
|
+
throw new Error(`Invalid type: ${type}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async function removeDirectory(dirPath) {
|
|
185
|
+
const dangerousPaths = [".", "/", "~", "src", "node_modules"];
|
|
186
|
+
if (dangerousPaths.includes(dirPath) || dirPath === "") {
|
|
187
|
+
logger.warn(`Skipping deletion of dangerous path: ${dirPath}`);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
if (await fileExists(dirPath)) {
|
|
192
|
+
await rm(dirPath, { recursive: true, force: true });
|
|
193
|
+
}
|
|
194
|
+
} catch (error) {
|
|
195
|
+
logger.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
170
196
|
}
|
|
171
|
-
return items.filter((item) => item.isFile()).map((item) => join(item.parentPath, item.name));
|
|
172
197
|
}
|
|
173
198
|
async function removeFile(filepath) {
|
|
174
199
|
logger.debug(`Removing file: ${filepath}`);
|
|
@@ -1674,7 +1699,7 @@ var CommandsProcessor = class extends FeatureProcessor {
|
|
|
1674
1699
|
);
|
|
1675
1700
|
const rulesyncCommands = (await Promise.allSettled(
|
|
1676
1701
|
rulesyncCommandPaths.map(
|
|
1677
|
-
(
|
|
1702
|
+
(path3) => RulesyncCommand.fromFile({ relativeFilePath: basename11(path3) })
|
|
1678
1703
|
)
|
|
1679
1704
|
)).filter((result) => result.status === "fulfilled").map((result) => result.value);
|
|
1680
1705
|
logger.info(`Successfully loaded ${rulesyncCommands.length} rulesync commands`);
|
|
@@ -1716,45 +1741,45 @@ var CommandsProcessor = class extends FeatureProcessor {
|
|
|
1716
1741
|
join12(this.baseDir, relativeDirPath, `*.${extension}`)
|
|
1717
1742
|
);
|
|
1718
1743
|
const toolCommands = (await Promise.allSettled(
|
|
1719
|
-
commandFilePaths.map((
|
|
1744
|
+
commandFilePaths.map((path3) => {
|
|
1720
1745
|
switch (toolTarget) {
|
|
1721
1746
|
case "agentsmd":
|
|
1722
1747
|
return AgentsmdCommand.fromFile({
|
|
1723
1748
|
baseDir: this.baseDir,
|
|
1724
|
-
relativeFilePath: basename11(
|
|
1749
|
+
relativeFilePath: basename11(path3)
|
|
1725
1750
|
});
|
|
1726
1751
|
case "claudecode":
|
|
1727
1752
|
return ClaudecodeCommand.fromFile({
|
|
1728
1753
|
baseDir: this.baseDir,
|
|
1729
|
-
relativeFilePath: basename11(
|
|
1754
|
+
relativeFilePath: basename11(path3),
|
|
1730
1755
|
global: this.global
|
|
1731
1756
|
});
|
|
1732
1757
|
case "geminicli":
|
|
1733
1758
|
return GeminiCliCommand.fromFile({
|
|
1734
1759
|
baseDir: this.baseDir,
|
|
1735
|
-
relativeFilePath: basename11(
|
|
1760
|
+
relativeFilePath: basename11(path3),
|
|
1736
1761
|
global: this.global
|
|
1737
1762
|
});
|
|
1738
1763
|
case "roo":
|
|
1739
1764
|
return RooCommand.fromFile({
|
|
1740
1765
|
baseDir: this.baseDir,
|
|
1741
|
-
relativeFilePath: basename11(
|
|
1766
|
+
relativeFilePath: basename11(path3)
|
|
1742
1767
|
});
|
|
1743
1768
|
case "copilot":
|
|
1744
1769
|
return CopilotCommand.fromFile({
|
|
1745
1770
|
baseDir: this.baseDir,
|
|
1746
|
-
relativeFilePath: basename11(
|
|
1771
|
+
relativeFilePath: basename11(path3)
|
|
1747
1772
|
});
|
|
1748
1773
|
case "cursor":
|
|
1749
1774
|
return CursorCommand.fromFile({
|
|
1750
1775
|
baseDir: this.baseDir,
|
|
1751
|
-
relativeFilePath: basename11(
|
|
1776
|
+
relativeFilePath: basename11(path3),
|
|
1752
1777
|
global: this.global
|
|
1753
1778
|
});
|
|
1754
1779
|
case "codexcli":
|
|
1755
1780
|
return CodexcliCommand.fromFile({
|
|
1756
1781
|
baseDir: this.baseDir,
|
|
1757
|
-
relativeFilePath: basename11(
|
|
1782
|
+
relativeFilePath: basename11(path3),
|
|
1758
1783
|
global: this.global
|
|
1759
1784
|
});
|
|
1760
1785
|
default:
|
|
@@ -4547,7 +4572,7 @@ var SubagentsProcessor = class extends FeatureProcessor {
|
|
|
4547
4572
|
fromFile
|
|
4548
4573
|
}) {
|
|
4549
4574
|
const paths = await findFilesByGlobs(join44(this.baseDir, relativeDirPath, "*.md"));
|
|
4550
|
-
const subagents = (await Promise.allSettled(paths.map((
|
|
4575
|
+
const subagents = (await Promise.allSettled(paths.map((path3) => fromFile(basename14(path3))))).filter((r) => r.status === "fulfilled").map((r) => r.value);
|
|
4551
4576
|
logger.info(`Successfully loaded ${subagents.length} ${relativeDirPath} subagents`);
|
|
4552
4577
|
return subagents;
|
|
4553
4578
|
}
|
|
@@ -7069,6 +7094,614 @@ For example, if the user instructs \`Call planner subagent to plan the refactori
|
|
|
7069
7094
|
}
|
|
7070
7095
|
};
|
|
7071
7096
|
|
|
7097
|
+
// src/features/skills/skills-processor.ts
|
|
7098
|
+
import { basename as basename19, join as join69 } from "path";
|
|
7099
|
+
import { z as z27 } from "zod/mini";
|
|
7100
|
+
|
|
7101
|
+
// src/types/dir-feature-processor.ts
|
|
7102
|
+
import { join as join65 } from "path";
|
|
7103
|
+
var DirFeatureProcessor = class {
|
|
7104
|
+
baseDir;
|
|
7105
|
+
constructor({ baseDir = process.cwd() }) {
|
|
7106
|
+
this.baseDir = baseDir;
|
|
7107
|
+
}
|
|
7108
|
+
/**
|
|
7109
|
+
* Return tool targets that this feature supports.
|
|
7110
|
+
*/
|
|
7111
|
+
static getToolTargets(_params = {}) {
|
|
7112
|
+
throw new Error("Not implemented");
|
|
7113
|
+
}
|
|
7114
|
+
/**
|
|
7115
|
+
* Once converted to rulesync/tool dirs, write them to the filesystem.
|
|
7116
|
+
* Returns the number of directories written.
|
|
7117
|
+
*/
|
|
7118
|
+
async writeAiDirs(aiDirs) {
|
|
7119
|
+
for (const aiDir of aiDirs) {
|
|
7120
|
+
const dirPath = aiDir.getDirPath();
|
|
7121
|
+
await ensureDir(dirPath);
|
|
7122
|
+
const mainFile = aiDir.getMainFile();
|
|
7123
|
+
if (mainFile) {
|
|
7124
|
+
const mainFilePath = join65(dirPath, mainFile.name);
|
|
7125
|
+
const contentWithNewline = addTrailingNewline(mainFile.body);
|
|
7126
|
+
await writeFileContent(mainFilePath, contentWithNewline);
|
|
7127
|
+
}
|
|
7128
|
+
const otherFiles = aiDir.getOtherFiles();
|
|
7129
|
+
for (const file of otherFiles) {
|
|
7130
|
+
const filePath = join65(dirPath, file.relativeFilePathToDirPath);
|
|
7131
|
+
const contentWithNewline = addTrailingNewline(file.fileBuffer.toString("utf-8"));
|
|
7132
|
+
await writeFileContent(filePath, contentWithNewline);
|
|
7133
|
+
}
|
|
7134
|
+
}
|
|
7135
|
+
return aiDirs.length;
|
|
7136
|
+
}
|
|
7137
|
+
async removeAiDirs(aiDirs) {
|
|
7138
|
+
for (const aiDir of aiDirs) {
|
|
7139
|
+
await removeDirectory(aiDir.getDirPath());
|
|
7140
|
+
}
|
|
7141
|
+
}
|
|
7142
|
+
};
|
|
7143
|
+
|
|
7144
|
+
// src/features/skills/claudecode-skill.ts
|
|
7145
|
+
import { join as join68 } from "path";
|
|
7146
|
+
import { z as z26 } from "zod/mini";
|
|
7147
|
+
|
|
7148
|
+
// src/constants/general.ts
|
|
7149
|
+
var SKILL_FILE_NAME = "SKILL.md";
|
|
7150
|
+
|
|
7151
|
+
// src/features/skills/rulesync-skill.ts
|
|
7152
|
+
import { join as join67 } from "path";
|
|
7153
|
+
import { z as z25 } from "zod/mini";
|
|
7154
|
+
|
|
7155
|
+
// src/types/ai-dir.ts
|
|
7156
|
+
import path2, { basename as basename18, join as join66, relative as relative3, resolve as resolve4 } from "path";
|
|
7157
|
+
var AiDir = class {
|
|
7158
|
+
/**
|
|
7159
|
+
* @example "."
|
|
7160
|
+
*/
|
|
7161
|
+
baseDir;
|
|
7162
|
+
/**
|
|
7163
|
+
* @example ".rulesync/skills"
|
|
7164
|
+
*/
|
|
7165
|
+
relativeDirPath;
|
|
7166
|
+
/**
|
|
7167
|
+
* @example "my-skill"
|
|
7168
|
+
*/
|
|
7169
|
+
dirName;
|
|
7170
|
+
/**
|
|
7171
|
+
* Optional main file with frontmatter support
|
|
7172
|
+
*/
|
|
7173
|
+
mainFile;
|
|
7174
|
+
/**
|
|
7175
|
+
* Additional files in the directory
|
|
7176
|
+
*/
|
|
7177
|
+
otherFiles;
|
|
7178
|
+
/**
|
|
7179
|
+
* @example false
|
|
7180
|
+
*/
|
|
7181
|
+
global;
|
|
7182
|
+
constructor({
|
|
7183
|
+
baseDir = process.cwd(),
|
|
7184
|
+
relativeDirPath,
|
|
7185
|
+
dirName,
|
|
7186
|
+
mainFile,
|
|
7187
|
+
otherFiles = [],
|
|
7188
|
+
global = false
|
|
7189
|
+
}) {
|
|
7190
|
+
if (dirName.includes(path2.sep) || dirName.includes("/") || dirName.includes("\\")) {
|
|
7191
|
+
throw new Error(`Directory name cannot contain path separators: dirName="${dirName}"`);
|
|
7192
|
+
}
|
|
7193
|
+
this.baseDir = baseDir;
|
|
7194
|
+
this.relativeDirPath = relativeDirPath;
|
|
7195
|
+
this.dirName = dirName;
|
|
7196
|
+
this.mainFile = mainFile;
|
|
7197
|
+
this.otherFiles = otherFiles;
|
|
7198
|
+
this.global = global;
|
|
7199
|
+
}
|
|
7200
|
+
static async fromDir(_params) {
|
|
7201
|
+
throw new Error("Please implement this method in the subclass.");
|
|
7202
|
+
}
|
|
7203
|
+
getBaseDir() {
|
|
7204
|
+
return this.baseDir;
|
|
7205
|
+
}
|
|
7206
|
+
getRelativeDirPath() {
|
|
7207
|
+
return this.relativeDirPath;
|
|
7208
|
+
}
|
|
7209
|
+
getDirName() {
|
|
7210
|
+
return this.dirName;
|
|
7211
|
+
}
|
|
7212
|
+
getDirPath() {
|
|
7213
|
+
const fullPath = path2.join(this.baseDir, this.relativeDirPath, this.dirName);
|
|
7214
|
+
const resolvedFull = resolve4(fullPath);
|
|
7215
|
+
const resolvedBase = resolve4(this.baseDir);
|
|
7216
|
+
const rel = relative3(resolvedBase, resolvedFull);
|
|
7217
|
+
if (rel.startsWith("..") || path2.isAbsolute(rel)) {
|
|
7218
|
+
throw new Error(
|
|
7219
|
+
`Path traversal detected: Final path escapes baseDir. baseDir="${this.baseDir}", relativeDirPath="${this.relativeDirPath}", dirName="${this.dirName}"`
|
|
7220
|
+
);
|
|
7221
|
+
}
|
|
7222
|
+
return fullPath;
|
|
7223
|
+
}
|
|
7224
|
+
getMainFile() {
|
|
7225
|
+
return this.mainFile;
|
|
7226
|
+
}
|
|
7227
|
+
getOtherFiles() {
|
|
7228
|
+
return this.otherFiles;
|
|
7229
|
+
}
|
|
7230
|
+
getRelativePathFromCwd() {
|
|
7231
|
+
return path2.join(this.relativeDirPath, this.dirName);
|
|
7232
|
+
}
|
|
7233
|
+
getGlobal() {
|
|
7234
|
+
return this.global;
|
|
7235
|
+
}
|
|
7236
|
+
setMainFile(name, body, frontmatter) {
|
|
7237
|
+
this.mainFile = { name, body, frontmatter };
|
|
7238
|
+
}
|
|
7239
|
+
/**
|
|
7240
|
+
* Recursively collects all files from a directory, excluding the specified main file.
|
|
7241
|
+
* This is a common utility for loading additional files alongside the main file.
|
|
7242
|
+
*
|
|
7243
|
+
* @param baseDir - The base directory path
|
|
7244
|
+
* @param relativeDirPath - The relative path to the directory containing the skill
|
|
7245
|
+
* @param dirName - The name of the directory
|
|
7246
|
+
* @param excludeFileName - The name of the file to exclude (typically the main file)
|
|
7247
|
+
* @returns Array of files with their relative paths and buffers
|
|
7248
|
+
*/
|
|
7249
|
+
static async collectOtherFiles(baseDir, relativeDirPath, dirName, excludeFileName) {
|
|
7250
|
+
const dirPath = join66(baseDir, relativeDirPath, dirName);
|
|
7251
|
+
const glob = join66(dirPath, "**", "*");
|
|
7252
|
+
const filePaths = await findFilesByGlobs(glob, { type: "file" });
|
|
7253
|
+
const filteredPaths = filePaths.filter((filePath) => basename18(filePath) !== excludeFileName);
|
|
7254
|
+
const files = await Promise.all(
|
|
7255
|
+
filteredPaths.map(async (filePath) => {
|
|
7256
|
+
const fileBuffer = await readFileBuffer(filePath);
|
|
7257
|
+
return {
|
|
7258
|
+
relativeFilePathToDirPath: relative3(dirPath, filePath),
|
|
7259
|
+
fileBuffer
|
|
7260
|
+
};
|
|
7261
|
+
})
|
|
7262
|
+
);
|
|
7263
|
+
return files;
|
|
7264
|
+
}
|
|
7265
|
+
};
|
|
7266
|
+
|
|
7267
|
+
// src/features/skills/rulesync-skill.ts
|
|
7268
|
+
var RulesyncSkillFrontmatterSchema = z25.object({
|
|
7269
|
+
name: z25.string(),
|
|
7270
|
+
description: z25.string(),
|
|
7271
|
+
claudecode: z25.optional(
|
|
7272
|
+
z25.object({
|
|
7273
|
+
"allowed-tools": z25.optional(z25.array(z25.string()))
|
|
7274
|
+
})
|
|
7275
|
+
)
|
|
7276
|
+
});
|
|
7277
|
+
var RulesyncSkill = class _RulesyncSkill extends AiDir {
|
|
7278
|
+
constructor({
|
|
7279
|
+
baseDir = process.cwd(),
|
|
7280
|
+
relativeDirPath = RULESYNC_SKILLS_RELATIVE_DIR_PATH,
|
|
7281
|
+
dirName,
|
|
7282
|
+
frontmatter,
|
|
7283
|
+
body,
|
|
7284
|
+
otherFiles = [],
|
|
7285
|
+
validate = true,
|
|
7286
|
+
global = false
|
|
7287
|
+
}) {
|
|
7288
|
+
super({
|
|
7289
|
+
baseDir,
|
|
7290
|
+
relativeDirPath,
|
|
7291
|
+
dirName,
|
|
7292
|
+
mainFile: {
|
|
7293
|
+
name: SKILL_FILE_NAME,
|
|
7294
|
+
body,
|
|
7295
|
+
frontmatter: { ...frontmatter }
|
|
7296
|
+
},
|
|
7297
|
+
otherFiles,
|
|
7298
|
+
global
|
|
7299
|
+
});
|
|
7300
|
+
if (validate) {
|
|
7301
|
+
const result = this.validate();
|
|
7302
|
+
if (!result.success) {
|
|
7303
|
+
throw result.error;
|
|
7304
|
+
}
|
|
7305
|
+
}
|
|
7306
|
+
}
|
|
7307
|
+
static getSettablePaths() {
|
|
7308
|
+
return {
|
|
7309
|
+
relativeDirPath: RULESYNC_SKILLS_RELATIVE_DIR_PATH
|
|
7310
|
+
};
|
|
7311
|
+
}
|
|
7312
|
+
getFrontmatter() {
|
|
7313
|
+
if (!this.mainFile?.frontmatter) {
|
|
7314
|
+
throw new Error("Frontmatter is not defined");
|
|
7315
|
+
}
|
|
7316
|
+
const result = RulesyncSkillFrontmatterSchema.parse(this.mainFile.frontmatter);
|
|
7317
|
+
return result;
|
|
7318
|
+
}
|
|
7319
|
+
getBody() {
|
|
7320
|
+
return this.mainFile?.body ?? "";
|
|
7321
|
+
}
|
|
7322
|
+
validate() {
|
|
7323
|
+
const result = RulesyncSkillFrontmatterSchema.safeParse(this.mainFile?.frontmatter);
|
|
7324
|
+
if (!result.success) {
|
|
7325
|
+
return {
|
|
7326
|
+
success: false,
|
|
7327
|
+
error: new Error(
|
|
7328
|
+
`Invalid frontmatter in ${this.getDirPath()}: ${formatError(result.error)}`
|
|
7329
|
+
)
|
|
7330
|
+
};
|
|
7331
|
+
}
|
|
7332
|
+
return { success: true, error: null };
|
|
7333
|
+
}
|
|
7334
|
+
static async fromDir({
|
|
7335
|
+
baseDir = process.cwd(),
|
|
7336
|
+
relativeDirPath = RULESYNC_SKILLS_RELATIVE_DIR_PATH,
|
|
7337
|
+
dirName,
|
|
7338
|
+
global = false
|
|
7339
|
+
}) {
|
|
7340
|
+
const skillDirPath = join67(baseDir, relativeDirPath, dirName);
|
|
7341
|
+
const skillFilePath = join67(skillDirPath, SKILL_FILE_NAME);
|
|
7342
|
+
if (!await fileExists(skillFilePath)) {
|
|
7343
|
+
throw new Error(`${SKILL_FILE_NAME} not found in ${skillDirPath}`);
|
|
7344
|
+
}
|
|
7345
|
+
const fileContent = await readFileContent(skillFilePath);
|
|
7346
|
+
const { frontmatter, body: content } = parseFrontmatter(fileContent);
|
|
7347
|
+
const result = RulesyncSkillFrontmatterSchema.safeParse(frontmatter);
|
|
7348
|
+
if (!result.success) {
|
|
7349
|
+
throw new Error(`Invalid frontmatter in ${skillFilePath}: ${formatError(result.error)}`);
|
|
7350
|
+
}
|
|
7351
|
+
const otherFiles = await this.collectOtherFiles(
|
|
7352
|
+
baseDir,
|
|
7353
|
+
relativeDirPath,
|
|
7354
|
+
dirName,
|
|
7355
|
+
SKILL_FILE_NAME
|
|
7356
|
+
);
|
|
7357
|
+
return new _RulesyncSkill({
|
|
7358
|
+
baseDir,
|
|
7359
|
+
relativeDirPath,
|
|
7360
|
+
dirName,
|
|
7361
|
+
frontmatter: result.data,
|
|
7362
|
+
body: content.trim(),
|
|
7363
|
+
otherFiles,
|
|
7364
|
+
validate: true,
|
|
7365
|
+
global
|
|
7366
|
+
});
|
|
7367
|
+
}
|
|
7368
|
+
};
|
|
7369
|
+
|
|
7370
|
+
// src/features/skills/tool-skill.ts
|
|
7371
|
+
var ToolSkill = class extends AiDir {
|
|
7372
|
+
/**
|
|
7373
|
+
* Get the settable paths for this tool's skill directories.
|
|
7374
|
+
*
|
|
7375
|
+
* @param options - Optional configuration including global mode
|
|
7376
|
+
* @returns Object containing the relative directory path
|
|
7377
|
+
*/
|
|
7378
|
+
static getSettablePaths(_options) {
|
|
7379
|
+
throw new Error("Please implement this method in the subclass.");
|
|
7380
|
+
}
|
|
7381
|
+
/**
|
|
7382
|
+
* Load a skill from a tool-specific directory.
|
|
7383
|
+
*
|
|
7384
|
+
* This method should:
|
|
7385
|
+
* 1. Read the SKILL.md file content
|
|
7386
|
+
* 2. Parse tool-specific frontmatter format
|
|
7387
|
+
* 3. Validate the parsed data
|
|
7388
|
+
* 4. Collect other skill files in the directory
|
|
7389
|
+
* 5. Return a concrete ToolSkill instance
|
|
7390
|
+
*
|
|
7391
|
+
* @param params - Parameters including the skill directory name
|
|
7392
|
+
* @returns Promise resolving to a concrete ToolSkill instance
|
|
7393
|
+
*/
|
|
7394
|
+
static async fromDir(_params) {
|
|
7395
|
+
throw new Error("Please implement this method in the subclass.");
|
|
7396
|
+
}
|
|
7397
|
+
/**
|
|
7398
|
+
* Convert a RulesyncSkill to the tool-specific skill format.
|
|
7399
|
+
*
|
|
7400
|
+
* This method should:
|
|
7401
|
+
* 1. Extract relevant data from the RulesyncSkill
|
|
7402
|
+
* 2. Transform frontmatter to tool-specific format
|
|
7403
|
+
* 3. Transform body content if needed
|
|
7404
|
+
* 4. Preserve other skill files
|
|
7405
|
+
* 5. Return a concrete ToolSkill instance
|
|
7406
|
+
*
|
|
7407
|
+
* @param params - Parameters including the RulesyncSkill to convert
|
|
7408
|
+
* @returns A concrete ToolSkill instance
|
|
7409
|
+
*/
|
|
7410
|
+
static fromRulesyncSkill(_params) {
|
|
7411
|
+
throw new Error("Please implement this method in the subclass.");
|
|
7412
|
+
}
|
|
7413
|
+
/**
|
|
7414
|
+
* Check if this tool is targeted by a RulesyncSkill.
|
|
7415
|
+
* Since skills don't have targets field like commands/subagents,
|
|
7416
|
+
* the default behavior may vary by tool.
|
|
7417
|
+
*
|
|
7418
|
+
* @param rulesyncSkill - The RulesyncSkill to check
|
|
7419
|
+
* @returns True if this tool should use the skill
|
|
7420
|
+
*/
|
|
7421
|
+
static isTargetedByRulesyncSkill(_rulesyncSkill) {
|
|
7422
|
+
throw new Error("Please implement this method in the subclass.");
|
|
7423
|
+
}
|
|
7424
|
+
};
|
|
7425
|
+
|
|
7426
|
+
// src/features/skills/claudecode-skill.ts
|
|
7427
|
+
var ClaudecodeSkillFrontmatterSchema = z26.object({
|
|
7428
|
+
name: z26.string(),
|
|
7429
|
+
description: z26.string(),
|
|
7430
|
+
"allowed-tools": z26.optional(z26.array(z26.string()))
|
|
7431
|
+
});
|
|
7432
|
+
var ClaudecodeSkill = class _ClaudecodeSkill extends ToolSkill {
|
|
7433
|
+
constructor({
|
|
7434
|
+
baseDir = process.cwd(),
|
|
7435
|
+
relativeDirPath = join68(".claude", "skills"),
|
|
7436
|
+
dirName,
|
|
7437
|
+
frontmatter,
|
|
7438
|
+
body,
|
|
7439
|
+
otherFiles = [],
|
|
7440
|
+
validate = true,
|
|
7441
|
+
global = false
|
|
7442
|
+
}) {
|
|
7443
|
+
super({
|
|
7444
|
+
baseDir,
|
|
7445
|
+
relativeDirPath,
|
|
7446
|
+
dirName,
|
|
7447
|
+
mainFile: {
|
|
7448
|
+
name: SKILL_FILE_NAME,
|
|
7449
|
+
body,
|
|
7450
|
+
frontmatter: { ...frontmatter }
|
|
7451
|
+
},
|
|
7452
|
+
otherFiles,
|
|
7453
|
+
global
|
|
7454
|
+
});
|
|
7455
|
+
if (validate) {
|
|
7456
|
+
const result = this.validate();
|
|
7457
|
+
if (!result.success) {
|
|
7458
|
+
throw result.error;
|
|
7459
|
+
}
|
|
7460
|
+
}
|
|
7461
|
+
}
|
|
7462
|
+
static getSettablePaths({
|
|
7463
|
+
global: _global = false
|
|
7464
|
+
} = {}) {
|
|
7465
|
+
return {
|
|
7466
|
+
relativeDirPath: join68(".claude", "skills")
|
|
7467
|
+
};
|
|
7468
|
+
}
|
|
7469
|
+
getFrontmatter() {
|
|
7470
|
+
if (!this.mainFile?.frontmatter) {
|
|
7471
|
+
throw new Error("Frontmatter is not defined");
|
|
7472
|
+
}
|
|
7473
|
+
const result = ClaudecodeSkillFrontmatterSchema.parse(this.mainFile.frontmatter);
|
|
7474
|
+
return result;
|
|
7475
|
+
}
|
|
7476
|
+
getBody() {
|
|
7477
|
+
return this.mainFile?.body ?? "";
|
|
7478
|
+
}
|
|
7479
|
+
validate() {
|
|
7480
|
+
if (this.mainFile === void 0) {
|
|
7481
|
+
return {
|
|
7482
|
+
success: false,
|
|
7483
|
+
error: new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
|
|
7484
|
+
};
|
|
7485
|
+
}
|
|
7486
|
+
const result = ClaudecodeSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
|
|
7487
|
+
if (!result.success) {
|
|
7488
|
+
return {
|
|
7489
|
+
success: false,
|
|
7490
|
+
error: new Error(
|
|
7491
|
+
`Invalid frontmatter in ${this.getDirPath()}: ${formatError(result.error)}`
|
|
7492
|
+
)
|
|
7493
|
+
};
|
|
7494
|
+
}
|
|
7495
|
+
return { success: true, error: null };
|
|
7496
|
+
}
|
|
7497
|
+
toRulesyncSkill() {
|
|
7498
|
+
const frontmatter = this.getFrontmatter();
|
|
7499
|
+
const rulesyncFrontmatter = {
|
|
7500
|
+
name: frontmatter.name,
|
|
7501
|
+
description: frontmatter.description,
|
|
7502
|
+
...frontmatter["allowed-tools"] && {
|
|
7503
|
+
claudecode: {
|
|
7504
|
+
"allowed-tools": frontmatter["allowed-tools"]
|
|
7505
|
+
}
|
|
7506
|
+
}
|
|
7507
|
+
};
|
|
7508
|
+
return new RulesyncSkill({
|
|
7509
|
+
baseDir: this.baseDir,
|
|
7510
|
+
relativeDirPath: this.relativeDirPath,
|
|
7511
|
+
dirName: this.getDirName(),
|
|
7512
|
+
frontmatter: rulesyncFrontmatter,
|
|
7513
|
+
body: this.getBody(),
|
|
7514
|
+
otherFiles: this.getOtherFiles(),
|
|
7515
|
+
validate: true,
|
|
7516
|
+
global: this.global
|
|
7517
|
+
});
|
|
7518
|
+
}
|
|
7519
|
+
static fromRulesyncSkill({
|
|
7520
|
+
rulesyncSkill,
|
|
7521
|
+
validate = true,
|
|
7522
|
+
global = false
|
|
7523
|
+
}) {
|
|
7524
|
+
const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
|
|
7525
|
+
const claudecodeFrontmatter = {
|
|
7526
|
+
name: rulesyncFrontmatter.name,
|
|
7527
|
+
description: rulesyncFrontmatter.description,
|
|
7528
|
+
"allowed-tools": rulesyncFrontmatter.claudecode?.["allowed-tools"]
|
|
7529
|
+
};
|
|
7530
|
+
const settablePaths = _ClaudecodeSkill.getSettablePaths({ global });
|
|
7531
|
+
return new _ClaudecodeSkill({
|
|
7532
|
+
baseDir: rulesyncSkill.getBaseDir(),
|
|
7533
|
+
relativeDirPath: settablePaths.relativeDirPath,
|
|
7534
|
+
dirName: rulesyncSkill.getDirName(),
|
|
7535
|
+
frontmatter: claudecodeFrontmatter,
|
|
7536
|
+
body: rulesyncSkill.getBody(),
|
|
7537
|
+
otherFiles: rulesyncSkill.getOtherFiles(),
|
|
7538
|
+
validate,
|
|
7539
|
+
global
|
|
7540
|
+
});
|
|
7541
|
+
}
|
|
7542
|
+
static isTargetedByRulesyncSkill(_rulesyncSkill) {
|
|
7543
|
+
return true;
|
|
7544
|
+
}
|
|
7545
|
+
static async fromDir({
|
|
7546
|
+
baseDir = process.cwd(),
|
|
7547
|
+
relativeDirPath,
|
|
7548
|
+
dirName,
|
|
7549
|
+
global = false
|
|
7550
|
+
}) {
|
|
7551
|
+
const settablePaths = this.getSettablePaths({ global });
|
|
7552
|
+
const actualRelativeDirPath = relativeDirPath ?? settablePaths.relativeDirPath;
|
|
7553
|
+
const skillDirPath = join68(baseDir, actualRelativeDirPath, dirName);
|
|
7554
|
+
const skillFilePath = join68(skillDirPath, SKILL_FILE_NAME);
|
|
7555
|
+
if (!await fileExists(skillFilePath)) {
|
|
7556
|
+
throw new Error(`${SKILL_FILE_NAME} not found in ${skillDirPath}`);
|
|
7557
|
+
}
|
|
7558
|
+
const fileContent = await readFileContent(skillFilePath);
|
|
7559
|
+
const { frontmatter, body: content } = parseFrontmatter(fileContent);
|
|
7560
|
+
const result = ClaudecodeSkillFrontmatterSchema.safeParse(frontmatter);
|
|
7561
|
+
if (!result.success) {
|
|
7562
|
+
throw new Error(`Invalid frontmatter in ${skillFilePath}: ${formatError(result.error)}`);
|
|
7563
|
+
}
|
|
7564
|
+
const otherFiles = await this.collectOtherFiles(
|
|
7565
|
+
baseDir,
|
|
7566
|
+
actualRelativeDirPath,
|
|
7567
|
+
dirName,
|
|
7568
|
+
SKILL_FILE_NAME
|
|
7569
|
+
);
|
|
7570
|
+
return new _ClaudecodeSkill({
|
|
7571
|
+
baseDir,
|
|
7572
|
+
relativeDirPath: actualRelativeDirPath,
|
|
7573
|
+
dirName,
|
|
7574
|
+
frontmatter: result.data,
|
|
7575
|
+
body: content.trim(),
|
|
7576
|
+
otherFiles,
|
|
7577
|
+
validate: true,
|
|
7578
|
+
global
|
|
7579
|
+
});
|
|
7580
|
+
}
|
|
7581
|
+
};
|
|
7582
|
+
|
|
7583
|
+
// src/features/skills/skills-processor.ts
|
|
7584
|
+
var skillsProcessorToolTargets = ["claudecode"];
|
|
7585
|
+
var skillsProcessorToolTargetsGlobal = ["claudecode"];
|
|
7586
|
+
var SkillsProcessorToolTargetSchema = z27.enum(skillsProcessorToolTargets);
|
|
7587
|
+
var SkillsProcessor = class extends DirFeatureProcessor {
|
|
7588
|
+
toolTarget;
|
|
7589
|
+
global;
|
|
7590
|
+
constructor({
|
|
7591
|
+
baseDir = process.cwd(),
|
|
7592
|
+
toolTarget,
|
|
7593
|
+
global = false
|
|
7594
|
+
}) {
|
|
7595
|
+
super({ baseDir });
|
|
7596
|
+
const result = SkillsProcessorToolTargetSchema.safeParse(toolTarget);
|
|
7597
|
+
if (!result.success) {
|
|
7598
|
+
throw new Error(
|
|
7599
|
+
`Invalid tool target for SkillsProcessor: ${toolTarget}. ${formatError(result.error)}`
|
|
7600
|
+
);
|
|
7601
|
+
}
|
|
7602
|
+
this.toolTarget = result.data;
|
|
7603
|
+
this.global = global;
|
|
7604
|
+
}
|
|
7605
|
+
async convertRulesyncDirsToToolDirs(rulesyncDirs) {
|
|
7606
|
+
const rulesyncSkills = rulesyncDirs.filter(
|
|
7607
|
+
(dir) => dir instanceof RulesyncSkill
|
|
7608
|
+
);
|
|
7609
|
+
const toolSkills = rulesyncSkills.map((rulesyncSkill) => {
|
|
7610
|
+
switch (this.toolTarget) {
|
|
7611
|
+
case "claudecode":
|
|
7612
|
+
if (!ClaudecodeSkill.isTargetedByRulesyncSkill(rulesyncSkill)) {
|
|
7613
|
+
return null;
|
|
7614
|
+
}
|
|
7615
|
+
return ClaudecodeSkill.fromRulesyncSkill({
|
|
7616
|
+
rulesyncSkill,
|
|
7617
|
+
global: this.global
|
|
7618
|
+
});
|
|
7619
|
+
default:
|
|
7620
|
+
throw new Error(`Unsupported tool target: ${this.toolTarget}`);
|
|
7621
|
+
}
|
|
7622
|
+
}).filter((skill) => skill !== null);
|
|
7623
|
+
return toolSkills;
|
|
7624
|
+
}
|
|
7625
|
+
async convertToolDirsToRulesyncDirs(toolDirs) {
|
|
7626
|
+
const toolSkills = toolDirs.filter((dir) => dir instanceof ToolSkill);
|
|
7627
|
+
const rulesyncSkills = toolSkills.map((toolSkill) => {
|
|
7628
|
+
return toolSkill.toRulesyncSkill();
|
|
7629
|
+
});
|
|
7630
|
+
return rulesyncSkills;
|
|
7631
|
+
}
|
|
7632
|
+
/**
|
|
7633
|
+
* Implementation of abstract method from DirFeatureProcessor
|
|
7634
|
+
* Load and parse rulesync skill directories from .rulesync/skills/ directory
|
|
7635
|
+
*/
|
|
7636
|
+
async loadRulesyncDirs() {
|
|
7637
|
+
const paths = RulesyncSkill.getSettablePaths();
|
|
7638
|
+
const rulesyncSkillsDirPath = join69(this.baseDir, paths.relativeDirPath);
|
|
7639
|
+
const dirPaths = await findFilesByGlobs(join69(rulesyncSkillsDirPath, "*"), { type: "dir" });
|
|
7640
|
+
const dirNames = dirPaths.map((path3) => basename19(path3));
|
|
7641
|
+
const results = await Promise.allSettled(
|
|
7642
|
+
dirNames.map(
|
|
7643
|
+
(dirName) => RulesyncSkill.fromDir({ baseDir: this.baseDir, dirName, global: this.global })
|
|
7644
|
+
)
|
|
7645
|
+
);
|
|
7646
|
+
const rulesyncSkills = [];
|
|
7647
|
+
for (const result of results) {
|
|
7648
|
+
if (result.status === "fulfilled") {
|
|
7649
|
+
rulesyncSkills.push(result.value);
|
|
7650
|
+
}
|
|
7651
|
+
}
|
|
7652
|
+
logger.info(`Successfully loaded ${rulesyncSkills.length} rulesync skills`);
|
|
7653
|
+
return rulesyncSkills;
|
|
7654
|
+
}
|
|
7655
|
+
/**
|
|
7656
|
+
* Implementation of abstract method from DirFeatureProcessor
|
|
7657
|
+
* Load tool-specific skill configurations and parse them into ToolSkill instances
|
|
7658
|
+
*/
|
|
7659
|
+
async loadToolDirs() {
|
|
7660
|
+
switch (this.toolTarget) {
|
|
7661
|
+
case "claudecode":
|
|
7662
|
+
return await this.loadClaudecodeSkills();
|
|
7663
|
+
default:
|
|
7664
|
+
throw new Error(`Unsupported tool target: ${this.toolTarget}`);
|
|
7665
|
+
}
|
|
7666
|
+
}
|
|
7667
|
+
async loadToolDirsToDelete() {
|
|
7668
|
+
return this.loadToolDirs();
|
|
7669
|
+
}
|
|
7670
|
+
/**
|
|
7671
|
+
* Load Claude Code skill configurations from .claude/skills/ directory
|
|
7672
|
+
*/
|
|
7673
|
+
async loadClaudecodeSkills() {
|
|
7674
|
+
const paths = ClaudecodeSkill.getSettablePaths({ global: this.global });
|
|
7675
|
+
const skillsDirPath = join69(this.baseDir, paths.relativeDirPath);
|
|
7676
|
+
const dirPaths = await findFilesByGlobs(join69(skillsDirPath, "*"), { type: "dir" });
|
|
7677
|
+
const dirNames = dirPaths.map((path3) => basename19(path3));
|
|
7678
|
+
const toolSkills = (await Promise.allSettled(
|
|
7679
|
+
dirNames.map(
|
|
7680
|
+
(dirName) => ClaudecodeSkill.fromDir({
|
|
7681
|
+
baseDir: this.baseDir,
|
|
7682
|
+
dirName,
|
|
7683
|
+
global: this.global
|
|
7684
|
+
})
|
|
7685
|
+
)
|
|
7686
|
+
)).filter((result) => result.status === "fulfilled").map((result) => result.value);
|
|
7687
|
+
logger.info(`Successfully loaded ${toolSkills.length} ${paths.relativeDirPath} skills`);
|
|
7688
|
+
return toolSkills;
|
|
7689
|
+
}
|
|
7690
|
+
/**
|
|
7691
|
+
* Implementation of abstract method from DirFeatureProcessor
|
|
7692
|
+
* Return the tool targets that this processor supports
|
|
7693
|
+
*/
|
|
7694
|
+
static getToolTargets(_params = {}) {
|
|
7695
|
+
return skillsProcessorToolTargets;
|
|
7696
|
+
}
|
|
7697
|
+
/**
|
|
7698
|
+
* Return the tool targets that this processor supports in global mode
|
|
7699
|
+
*/
|
|
7700
|
+
static getToolTargetsGlobal() {
|
|
7701
|
+
return skillsProcessorToolTargetsGlobal;
|
|
7702
|
+
}
|
|
7703
|
+
};
|
|
7704
|
+
|
|
7072
7705
|
// src/cli/commands/generate.ts
|
|
7073
7706
|
async function generateCommand(options) {
|
|
7074
7707
|
const config = await ConfigResolver.resolve(options);
|
|
@@ -7084,7 +7717,8 @@ async function generateCommand(options) {
|
|
|
7084
7717
|
const totalMcpOutputs = await generateMcp(config);
|
|
7085
7718
|
const totalCommandOutputs = await generateCommands(config);
|
|
7086
7719
|
const totalSubagentOutputs = await generateSubagents(config);
|
|
7087
|
-
const
|
|
7720
|
+
const totalSkillOutputs = await generateSkills(config);
|
|
7721
|
+
const totalGenerated = totalRulesOutputs + totalMcpOutputs + totalCommandOutputs + totalIgnoreOutputs + totalSubagentOutputs + totalSkillOutputs;
|
|
7088
7722
|
if (totalGenerated === 0) {
|
|
7089
7723
|
const enabledFeatures = config.getFeatures().join(", ");
|
|
7090
7724
|
logger.warn(`\u26A0\uFE0F No files generated for enabled features: ${enabledFeatures}`);
|
|
@@ -7097,6 +7731,7 @@ async function generateCommand(options) {
|
|
|
7097
7731
|
if (totalMcpOutputs > 0) parts.push(`${totalMcpOutputs} MCP files`);
|
|
7098
7732
|
if (totalCommandOutputs > 0) parts.push(`${totalCommandOutputs} commands`);
|
|
7099
7733
|
if (totalSubagentOutputs > 0) parts.push(`${totalSubagentOutputs} subagents`);
|
|
7734
|
+
if (totalSkillOutputs > 0) parts.push(`${totalSkillOutputs} skills`);
|
|
7100
7735
|
logger.success(`\u{1F389} All done! Generated ${totalGenerated} file(s) total (${parts.join(" + ")})`);
|
|
7101
7736
|
}
|
|
7102
7737
|
}
|
|
@@ -7271,11 +7906,39 @@ async function generateSubagents(config) {
|
|
|
7271
7906
|
}
|
|
7272
7907
|
return totalSubagentOutputs;
|
|
7273
7908
|
}
|
|
7909
|
+
async function generateSkills(config) {
|
|
7910
|
+
if (!config.getFeatures().includes("skills")) {
|
|
7911
|
+
logger.debug("Skipping skill generation (not in --features)");
|
|
7912
|
+
return 0;
|
|
7913
|
+
}
|
|
7914
|
+
let totalSkillOutputs = 0;
|
|
7915
|
+
logger.info("Generating skill files...");
|
|
7916
|
+
const toolTargets = config.getGlobal() ? intersection(config.getTargets(), SkillsProcessor.getToolTargetsGlobal()) : intersection(config.getTargets(), SkillsProcessor.getToolTargets());
|
|
7917
|
+
for (const baseDir of config.getBaseDirs()) {
|
|
7918
|
+
for (const toolTarget of toolTargets) {
|
|
7919
|
+
const processor = new SkillsProcessor({
|
|
7920
|
+
baseDir,
|
|
7921
|
+
toolTarget,
|
|
7922
|
+
global: config.getGlobal()
|
|
7923
|
+
});
|
|
7924
|
+
if (config.getDelete()) {
|
|
7925
|
+
const oldToolDirs = await processor.loadToolDirsToDelete();
|
|
7926
|
+
await processor.removeAiDirs(oldToolDirs);
|
|
7927
|
+
}
|
|
7928
|
+
const rulesyncDirs = await processor.loadRulesyncDirs();
|
|
7929
|
+
const toolDirs = await processor.convertRulesyncDirsToToolDirs(rulesyncDirs);
|
|
7930
|
+
const writtenCount = await processor.writeAiDirs(toolDirs);
|
|
7931
|
+
totalSkillOutputs += writtenCount;
|
|
7932
|
+
logger.success(`Generated ${writtenCount} ${toolTarget} skill(s) in ${baseDir}`);
|
|
7933
|
+
}
|
|
7934
|
+
}
|
|
7935
|
+
return totalSkillOutputs;
|
|
7936
|
+
}
|
|
7274
7937
|
|
|
7275
7938
|
// src/cli/commands/gitignore.ts
|
|
7276
|
-
import { join as
|
|
7939
|
+
import { join as join70 } from "path";
|
|
7277
7940
|
var gitignoreCommand = async () => {
|
|
7278
|
-
const gitignorePath =
|
|
7941
|
+
const gitignorePath = join70(process.cwd(), ".gitignore");
|
|
7279
7942
|
const rulesFilesToIgnore = [
|
|
7280
7943
|
"# Generated by rulesync - AI tool configuration files",
|
|
7281
7944
|
// AGENTS.md
|
|
@@ -7292,6 +7955,7 @@ var gitignoreCommand = async () => {
|
|
|
7292
7955
|
"**/.claude/memories/",
|
|
7293
7956
|
"**/.claude/commands/",
|
|
7294
7957
|
"**/.claude/agents/",
|
|
7958
|
+
"**/.claude/skills/",
|
|
7295
7959
|
"**/.claude/settings.local.json",
|
|
7296
7960
|
"**/.mcp.json",
|
|
7297
7961
|
// Cline
|
|
@@ -7385,6 +8049,7 @@ async function importCommand(options) {
|
|
|
7385
8049
|
await importMcp(config, tool);
|
|
7386
8050
|
await importCommands(config, tool);
|
|
7387
8051
|
await importSubagents(config, tool);
|
|
8052
|
+
await importSkills(config, tool);
|
|
7388
8053
|
}
|
|
7389
8054
|
async function importRules(config, tool) {
|
|
7390
8055
|
if (!config.getFeatures().includes("rules")) {
|
|
@@ -7514,9 +8179,34 @@ async function importSubagents(config, tool) {
|
|
|
7514
8179
|
}
|
|
7515
8180
|
return writtenCount;
|
|
7516
8181
|
}
|
|
8182
|
+
async function importSkills(config, tool) {
|
|
8183
|
+
if (!config.getFeatures().includes("skills")) {
|
|
8184
|
+
return 0;
|
|
8185
|
+
}
|
|
8186
|
+
const global = config.getGlobal();
|
|
8187
|
+
const supportedTargets = SkillsProcessor.getToolTargets();
|
|
8188
|
+
if (!supportedTargets.includes(tool)) {
|
|
8189
|
+
return 0;
|
|
8190
|
+
}
|
|
8191
|
+
const skillsProcessor = new SkillsProcessor({
|
|
8192
|
+
baseDir: config.getBaseDirs()[0] ?? ".",
|
|
8193
|
+
toolTarget: tool,
|
|
8194
|
+
global
|
|
8195
|
+
});
|
|
8196
|
+
const toolDirs = await skillsProcessor.loadToolDirs();
|
|
8197
|
+
if (toolDirs.length === 0) {
|
|
8198
|
+
return 0;
|
|
8199
|
+
}
|
|
8200
|
+
const rulesyncDirs = await skillsProcessor.convertToolDirsToRulesyncDirs(toolDirs);
|
|
8201
|
+
const writtenCount = await skillsProcessor.writeAiDirs(rulesyncDirs);
|
|
8202
|
+
if (config.getVerbose() && writtenCount > 0) {
|
|
8203
|
+
logger.success(`Created ${writtenCount} skill directories`);
|
|
8204
|
+
}
|
|
8205
|
+
return writtenCount;
|
|
8206
|
+
}
|
|
7517
8207
|
|
|
7518
8208
|
// src/cli/commands/init.ts
|
|
7519
|
-
import { join as
|
|
8209
|
+
import { join as join71 } from "path";
|
|
7520
8210
|
async function initCommand() {
|
|
7521
8211
|
logger.info("Initializing rulesync...");
|
|
7522
8212
|
await ensureDir(RULESYNC_RELATIVE_DIR_PATH);
|
|
@@ -7679,14 +8369,14 @@ Attention, again, you are just the planner, so though you can read any files and
|
|
|
7679
8369
|
await ensureDir(commandPaths.relativeDirPath);
|
|
7680
8370
|
await ensureDir(subagentPaths.relativeDirPath);
|
|
7681
8371
|
await ensureDir(ignorePaths.relativeDirPath);
|
|
7682
|
-
const ruleFilepath =
|
|
8372
|
+
const ruleFilepath = join71(rulePaths.recommended.relativeDirPath, sampleRuleFile.filename);
|
|
7683
8373
|
if (!await fileExists(ruleFilepath)) {
|
|
7684
8374
|
await writeFileContent(ruleFilepath, sampleRuleFile.content);
|
|
7685
8375
|
logger.success(`Created ${ruleFilepath}`);
|
|
7686
8376
|
} else {
|
|
7687
8377
|
logger.info(`Skipped ${ruleFilepath} (already exists)`);
|
|
7688
8378
|
}
|
|
7689
|
-
const mcpFilepath =
|
|
8379
|
+
const mcpFilepath = join71(
|
|
7690
8380
|
mcpPaths.recommended.relativeDirPath,
|
|
7691
8381
|
mcpPaths.recommended.relativeFilePath
|
|
7692
8382
|
);
|
|
@@ -7696,21 +8386,21 @@ Attention, again, you are just the planner, so though you can read any files and
|
|
|
7696
8386
|
} else {
|
|
7697
8387
|
logger.info(`Skipped ${mcpFilepath} (already exists)`);
|
|
7698
8388
|
}
|
|
7699
|
-
const commandFilepath =
|
|
8389
|
+
const commandFilepath = join71(commandPaths.relativeDirPath, sampleCommandFile.filename);
|
|
7700
8390
|
if (!await fileExists(commandFilepath)) {
|
|
7701
8391
|
await writeFileContent(commandFilepath, sampleCommandFile.content);
|
|
7702
8392
|
logger.success(`Created ${commandFilepath}`);
|
|
7703
8393
|
} else {
|
|
7704
8394
|
logger.info(`Skipped ${commandFilepath} (already exists)`);
|
|
7705
8395
|
}
|
|
7706
|
-
const subagentFilepath =
|
|
8396
|
+
const subagentFilepath = join71(subagentPaths.relativeDirPath, sampleSubagentFile.filename);
|
|
7707
8397
|
if (!await fileExists(subagentFilepath)) {
|
|
7708
8398
|
await writeFileContent(subagentFilepath, sampleSubagentFile.content);
|
|
7709
8399
|
logger.success(`Created ${subagentFilepath}`);
|
|
7710
8400
|
} else {
|
|
7711
8401
|
logger.info(`Skipped ${subagentFilepath} (already exists)`);
|
|
7712
8402
|
}
|
|
7713
|
-
const ignoreFilepath =
|
|
8403
|
+
const ignoreFilepath = join71(ignorePaths.relativeDirPath, ignorePaths.relativeFilePath);
|
|
7714
8404
|
if (!await fileExists(ignoreFilepath)) {
|
|
7715
8405
|
await writeFileContent(ignoreFilepath, sampleIgnoreFile.content);
|
|
7716
8406
|
logger.success(`Created ${ignoreFilepath}`);
|
|
@@ -7723,12 +8413,12 @@ Attention, again, you are just the planner, so though you can read any files and
|
|
|
7723
8413
|
import { FastMCP } from "fastmcp";
|
|
7724
8414
|
|
|
7725
8415
|
// src/mcp/commands.ts
|
|
7726
|
-
import { basename as
|
|
7727
|
-
import { z as
|
|
8416
|
+
import { basename as basename20, join as join72 } from "path";
|
|
8417
|
+
import { z as z28 } from "zod/mini";
|
|
7728
8418
|
var maxCommandSizeBytes = 1024 * 1024;
|
|
7729
8419
|
var maxCommandsCount = 1e3;
|
|
7730
8420
|
async function listCommands() {
|
|
7731
|
-
const commandsDir =
|
|
8421
|
+
const commandsDir = join72(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH);
|
|
7732
8422
|
try {
|
|
7733
8423
|
const files = await listDirectoryFiles(commandsDir);
|
|
7734
8424
|
const mdFiles = files.filter((file) => file.endsWith(".md"));
|
|
@@ -7740,7 +8430,7 @@ async function listCommands() {
|
|
|
7740
8430
|
});
|
|
7741
8431
|
const frontmatter = command.getFrontmatter();
|
|
7742
8432
|
return {
|
|
7743
|
-
relativePathFromCwd:
|
|
8433
|
+
relativePathFromCwd: join72(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, file),
|
|
7744
8434
|
frontmatter
|
|
7745
8435
|
};
|
|
7746
8436
|
} catch (error) {
|
|
@@ -7760,13 +8450,13 @@ async function getCommand({ relativePathFromCwd }) {
|
|
|
7760
8450
|
relativePath: relativePathFromCwd,
|
|
7761
8451
|
intendedRootDir: process.cwd()
|
|
7762
8452
|
});
|
|
7763
|
-
const filename =
|
|
8453
|
+
const filename = basename20(relativePathFromCwd);
|
|
7764
8454
|
try {
|
|
7765
8455
|
const command = await RulesyncCommand.fromFile({
|
|
7766
8456
|
relativeFilePath: filename
|
|
7767
8457
|
});
|
|
7768
8458
|
return {
|
|
7769
|
-
relativePathFromCwd:
|
|
8459
|
+
relativePathFromCwd: join72(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename),
|
|
7770
8460
|
frontmatter: command.getFrontmatter(),
|
|
7771
8461
|
body: command.getBody()
|
|
7772
8462
|
};
|
|
@@ -7785,7 +8475,7 @@ async function putCommand({
|
|
|
7785
8475
|
relativePath: relativePathFromCwd,
|
|
7786
8476
|
intendedRootDir: process.cwd()
|
|
7787
8477
|
});
|
|
7788
|
-
const filename =
|
|
8478
|
+
const filename = basename20(relativePathFromCwd);
|
|
7789
8479
|
const estimatedSize = JSON.stringify(frontmatter).length + body.length;
|
|
7790
8480
|
if (estimatedSize > maxCommandSizeBytes) {
|
|
7791
8481
|
throw new Error(
|
|
@@ -7795,7 +8485,7 @@ async function putCommand({
|
|
|
7795
8485
|
try {
|
|
7796
8486
|
const existingCommands = await listCommands();
|
|
7797
8487
|
const isUpdate = existingCommands.some(
|
|
7798
|
-
(command2) => command2.relativePathFromCwd ===
|
|
8488
|
+
(command2) => command2.relativePathFromCwd === join72(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename)
|
|
7799
8489
|
);
|
|
7800
8490
|
if (!isUpdate && existingCommands.length >= maxCommandsCount) {
|
|
7801
8491
|
throw new Error(`Maximum number of commands (${maxCommandsCount}) reached`);
|
|
@@ -7810,11 +8500,11 @@ async function putCommand({
|
|
|
7810
8500
|
fileContent,
|
|
7811
8501
|
validate: true
|
|
7812
8502
|
});
|
|
7813
|
-
const commandsDir =
|
|
8503
|
+
const commandsDir = join72(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH);
|
|
7814
8504
|
await ensureDir(commandsDir);
|
|
7815
8505
|
await writeFileContent(command.getFilePath(), command.getFileContent());
|
|
7816
8506
|
return {
|
|
7817
|
-
relativePathFromCwd:
|
|
8507
|
+
relativePathFromCwd: join72(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename),
|
|
7818
8508
|
frontmatter: command.getFrontmatter(),
|
|
7819
8509
|
body: command.getBody()
|
|
7820
8510
|
};
|
|
@@ -7829,12 +8519,12 @@ async function deleteCommand({ relativePathFromCwd }) {
|
|
|
7829
8519
|
relativePath: relativePathFromCwd,
|
|
7830
8520
|
intendedRootDir: process.cwd()
|
|
7831
8521
|
});
|
|
7832
|
-
const filename =
|
|
7833
|
-
const fullPath =
|
|
8522
|
+
const filename = basename20(relativePathFromCwd);
|
|
8523
|
+
const fullPath = join72(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename);
|
|
7834
8524
|
try {
|
|
7835
8525
|
await removeFile(fullPath);
|
|
7836
8526
|
return {
|
|
7837
|
-
relativePathFromCwd:
|
|
8527
|
+
relativePathFromCwd: join72(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename)
|
|
7838
8528
|
};
|
|
7839
8529
|
} catch (error) {
|
|
7840
8530
|
throw new Error(`Failed to delete command file ${relativePathFromCwd}: ${formatError(error)}`, {
|
|
@@ -7843,23 +8533,23 @@ async function deleteCommand({ relativePathFromCwd }) {
|
|
|
7843
8533
|
}
|
|
7844
8534
|
}
|
|
7845
8535
|
var commandToolSchemas = {
|
|
7846
|
-
listCommands:
|
|
7847
|
-
getCommand:
|
|
7848
|
-
relativePathFromCwd:
|
|
8536
|
+
listCommands: z28.object({}),
|
|
8537
|
+
getCommand: z28.object({
|
|
8538
|
+
relativePathFromCwd: z28.string()
|
|
7849
8539
|
}),
|
|
7850
|
-
putCommand:
|
|
7851
|
-
relativePathFromCwd:
|
|
8540
|
+
putCommand: z28.object({
|
|
8541
|
+
relativePathFromCwd: z28.string(),
|
|
7852
8542
|
frontmatter: RulesyncCommandFrontmatterSchema,
|
|
7853
|
-
body:
|
|
8543
|
+
body: z28.string()
|
|
7854
8544
|
}),
|
|
7855
|
-
deleteCommand:
|
|
7856
|
-
relativePathFromCwd:
|
|
8545
|
+
deleteCommand: z28.object({
|
|
8546
|
+
relativePathFromCwd: z28.string()
|
|
7857
8547
|
})
|
|
7858
8548
|
};
|
|
7859
8549
|
var commandTools = {
|
|
7860
8550
|
listCommands: {
|
|
7861
8551
|
name: "listCommands",
|
|
7862
|
-
description: `List all commands from ${
|
|
8552
|
+
description: `List all commands from ${join72(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, "*.md")} with their frontmatter.`,
|
|
7863
8553
|
parameters: commandToolSchemas.listCommands,
|
|
7864
8554
|
execute: async () => {
|
|
7865
8555
|
const commands = await listCommands();
|
|
@@ -7901,11 +8591,11 @@ var commandTools = {
|
|
|
7901
8591
|
};
|
|
7902
8592
|
|
|
7903
8593
|
// src/mcp/ignore.ts
|
|
7904
|
-
import { join as
|
|
7905
|
-
import { z as
|
|
8594
|
+
import { join as join73 } from "path";
|
|
8595
|
+
import { z as z29 } from "zod/mini";
|
|
7906
8596
|
var maxIgnoreFileSizeBytes = 100 * 1024;
|
|
7907
8597
|
async function getIgnoreFile() {
|
|
7908
|
-
const ignoreFilePath =
|
|
8598
|
+
const ignoreFilePath = join73(process.cwd(), RULESYNC_IGNORE_RELATIVE_FILE_PATH);
|
|
7909
8599
|
try {
|
|
7910
8600
|
const content = await readFileContent(ignoreFilePath);
|
|
7911
8601
|
return {
|
|
@@ -7919,7 +8609,7 @@ async function getIgnoreFile() {
|
|
|
7919
8609
|
}
|
|
7920
8610
|
}
|
|
7921
8611
|
async function putIgnoreFile({ content }) {
|
|
7922
|
-
const ignoreFilePath =
|
|
8612
|
+
const ignoreFilePath = join73(process.cwd(), RULESYNC_IGNORE_RELATIVE_FILE_PATH);
|
|
7923
8613
|
const contentSizeBytes = Buffer.byteLength(content, "utf8");
|
|
7924
8614
|
if (contentSizeBytes > maxIgnoreFileSizeBytes) {
|
|
7925
8615
|
throw new Error(
|
|
@@ -7940,7 +8630,7 @@ async function putIgnoreFile({ content }) {
|
|
|
7940
8630
|
}
|
|
7941
8631
|
}
|
|
7942
8632
|
async function deleteIgnoreFile() {
|
|
7943
|
-
const ignoreFilePath =
|
|
8633
|
+
const ignoreFilePath = join73(process.cwd(), RULESYNC_IGNORE_RELATIVE_FILE_PATH);
|
|
7944
8634
|
try {
|
|
7945
8635
|
await removeFile(ignoreFilePath);
|
|
7946
8636
|
return {
|
|
@@ -7953,11 +8643,11 @@ async function deleteIgnoreFile() {
|
|
|
7953
8643
|
}
|
|
7954
8644
|
}
|
|
7955
8645
|
var ignoreToolSchemas = {
|
|
7956
|
-
getIgnoreFile:
|
|
7957
|
-
putIgnoreFile:
|
|
7958
|
-
content:
|
|
8646
|
+
getIgnoreFile: z29.object({}),
|
|
8647
|
+
putIgnoreFile: z29.object({
|
|
8648
|
+
content: z29.string()
|
|
7959
8649
|
}),
|
|
7960
|
-
deleteIgnoreFile:
|
|
8650
|
+
deleteIgnoreFile: z29.object({})
|
|
7961
8651
|
};
|
|
7962
8652
|
var ignoreTools = {
|
|
7963
8653
|
getIgnoreFile: {
|
|
@@ -7990,8 +8680,8 @@ var ignoreTools = {
|
|
|
7990
8680
|
};
|
|
7991
8681
|
|
|
7992
8682
|
// src/mcp/mcp.ts
|
|
7993
|
-
import { join as
|
|
7994
|
-
import { z as
|
|
8683
|
+
import { join as join74 } from "path";
|
|
8684
|
+
import { z as z30 } from "zod/mini";
|
|
7995
8685
|
var maxMcpSizeBytes = 1024 * 1024;
|
|
7996
8686
|
async function getMcpFile() {
|
|
7997
8687
|
const config = await ConfigResolver.resolve({});
|
|
@@ -8000,7 +8690,7 @@ async function getMcpFile() {
|
|
|
8000
8690
|
validate: true,
|
|
8001
8691
|
modularMcp: config.getModularMcp()
|
|
8002
8692
|
});
|
|
8003
|
-
const relativePathFromCwd =
|
|
8693
|
+
const relativePathFromCwd = join74(
|
|
8004
8694
|
rulesyncMcp.getRelativeDirPath(),
|
|
8005
8695
|
rulesyncMcp.getRelativeFilePath()
|
|
8006
8696
|
);
|
|
@@ -8033,7 +8723,7 @@ async function putMcpFile({ content }) {
|
|
|
8033
8723
|
const paths = RulesyncMcp.getSettablePaths();
|
|
8034
8724
|
const relativeDirPath = paths.recommended.relativeDirPath;
|
|
8035
8725
|
const relativeFilePath = paths.recommended.relativeFilePath;
|
|
8036
|
-
const fullPath =
|
|
8726
|
+
const fullPath = join74(baseDir, relativeDirPath, relativeFilePath);
|
|
8037
8727
|
const rulesyncMcp = new RulesyncMcp({
|
|
8038
8728
|
baseDir,
|
|
8039
8729
|
relativeDirPath,
|
|
@@ -8042,9 +8732,9 @@ async function putMcpFile({ content }) {
|
|
|
8042
8732
|
validate: true,
|
|
8043
8733
|
modularMcp: config.getModularMcp()
|
|
8044
8734
|
});
|
|
8045
|
-
await ensureDir(
|
|
8735
|
+
await ensureDir(join74(baseDir, relativeDirPath));
|
|
8046
8736
|
await writeFileContent(fullPath, content);
|
|
8047
|
-
const relativePathFromCwd =
|
|
8737
|
+
const relativePathFromCwd = join74(relativeDirPath, relativeFilePath);
|
|
8048
8738
|
return {
|
|
8049
8739
|
relativePathFromCwd,
|
|
8050
8740
|
content: rulesyncMcp.getFileContent()
|
|
@@ -8059,15 +8749,15 @@ async function deleteMcpFile() {
|
|
|
8059
8749
|
try {
|
|
8060
8750
|
const baseDir = process.cwd();
|
|
8061
8751
|
const paths = RulesyncMcp.getSettablePaths();
|
|
8062
|
-
const recommendedPath =
|
|
8752
|
+
const recommendedPath = join74(
|
|
8063
8753
|
baseDir,
|
|
8064
8754
|
paths.recommended.relativeDirPath,
|
|
8065
8755
|
paths.recommended.relativeFilePath
|
|
8066
8756
|
);
|
|
8067
|
-
const legacyPath =
|
|
8757
|
+
const legacyPath = join74(baseDir, paths.legacy.relativeDirPath, paths.legacy.relativeFilePath);
|
|
8068
8758
|
await removeFile(recommendedPath);
|
|
8069
8759
|
await removeFile(legacyPath);
|
|
8070
|
-
const relativePathFromCwd =
|
|
8760
|
+
const relativePathFromCwd = join74(
|
|
8071
8761
|
paths.recommended.relativeDirPath,
|
|
8072
8762
|
paths.recommended.relativeFilePath
|
|
8073
8763
|
);
|
|
@@ -8081,11 +8771,11 @@ async function deleteMcpFile() {
|
|
|
8081
8771
|
}
|
|
8082
8772
|
}
|
|
8083
8773
|
var mcpToolSchemas = {
|
|
8084
|
-
getMcpFile:
|
|
8085
|
-
putMcpFile:
|
|
8086
|
-
content:
|
|
8774
|
+
getMcpFile: z30.object({}),
|
|
8775
|
+
putMcpFile: z30.object({
|
|
8776
|
+
content: z30.string()
|
|
8087
8777
|
}),
|
|
8088
|
-
deleteMcpFile:
|
|
8778
|
+
deleteMcpFile: z30.object({})
|
|
8089
8779
|
};
|
|
8090
8780
|
var mcpTools = {
|
|
8091
8781
|
getMcpFile: {
|
|
@@ -8118,12 +8808,12 @@ var mcpTools = {
|
|
|
8118
8808
|
};
|
|
8119
8809
|
|
|
8120
8810
|
// src/mcp/rules.ts
|
|
8121
|
-
import { basename as
|
|
8122
|
-
import { z as
|
|
8811
|
+
import { basename as basename21, join as join75 } from "path";
|
|
8812
|
+
import { z as z31 } from "zod/mini";
|
|
8123
8813
|
var maxRuleSizeBytes = 1024 * 1024;
|
|
8124
8814
|
var maxRulesCount = 1e3;
|
|
8125
8815
|
async function listRules() {
|
|
8126
|
-
const rulesDir =
|
|
8816
|
+
const rulesDir = join75(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH);
|
|
8127
8817
|
try {
|
|
8128
8818
|
const files = await listDirectoryFiles(rulesDir);
|
|
8129
8819
|
const mdFiles = files.filter((file) => file.endsWith(".md"));
|
|
@@ -8136,7 +8826,7 @@ async function listRules() {
|
|
|
8136
8826
|
});
|
|
8137
8827
|
const frontmatter = rule.getFrontmatter();
|
|
8138
8828
|
return {
|
|
8139
|
-
relativePathFromCwd:
|
|
8829
|
+
relativePathFromCwd: join75(RULESYNC_RULES_RELATIVE_DIR_PATH, file),
|
|
8140
8830
|
frontmatter
|
|
8141
8831
|
};
|
|
8142
8832
|
} catch (error) {
|
|
@@ -8156,14 +8846,14 @@ async function getRule({ relativePathFromCwd }) {
|
|
|
8156
8846
|
relativePath: relativePathFromCwd,
|
|
8157
8847
|
intendedRootDir: process.cwd()
|
|
8158
8848
|
});
|
|
8159
|
-
const filename =
|
|
8849
|
+
const filename = basename21(relativePathFromCwd);
|
|
8160
8850
|
try {
|
|
8161
8851
|
const rule = await RulesyncRule.fromFile({
|
|
8162
8852
|
relativeFilePath: filename,
|
|
8163
8853
|
validate: true
|
|
8164
8854
|
});
|
|
8165
8855
|
return {
|
|
8166
|
-
relativePathFromCwd:
|
|
8856
|
+
relativePathFromCwd: join75(RULESYNC_RULES_RELATIVE_DIR_PATH, filename),
|
|
8167
8857
|
frontmatter: rule.getFrontmatter(),
|
|
8168
8858
|
body: rule.getBody()
|
|
8169
8859
|
};
|
|
@@ -8182,7 +8872,7 @@ async function putRule({
|
|
|
8182
8872
|
relativePath: relativePathFromCwd,
|
|
8183
8873
|
intendedRootDir: process.cwd()
|
|
8184
8874
|
});
|
|
8185
|
-
const filename =
|
|
8875
|
+
const filename = basename21(relativePathFromCwd);
|
|
8186
8876
|
const estimatedSize = JSON.stringify(frontmatter).length + body.length;
|
|
8187
8877
|
if (estimatedSize > maxRuleSizeBytes) {
|
|
8188
8878
|
throw new Error(
|
|
@@ -8192,7 +8882,7 @@ async function putRule({
|
|
|
8192
8882
|
try {
|
|
8193
8883
|
const existingRules = await listRules();
|
|
8194
8884
|
const isUpdate = existingRules.some(
|
|
8195
|
-
(rule2) => rule2.relativePathFromCwd ===
|
|
8885
|
+
(rule2) => rule2.relativePathFromCwd === join75(RULESYNC_RULES_RELATIVE_DIR_PATH, filename)
|
|
8196
8886
|
);
|
|
8197
8887
|
if (!isUpdate && existingRules.length >= maxRulesCount) {
|
|
8198
8888
|
throw new Error(`Maximum number of rules (${maxRulesCount}) reached`);
|
|
@@ -8205,11 +8895,11 @@ async function putRule({
|
|
|
8205
8895
|
body,
|
|
8206
8896
|
validate: true
|
|
8207
8897
|
});
|
|
8208
|
-
const rulesDir =
|
|
8898
|
+
const rulesDir = join75(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH);
|
|
8209
8899
|
await ensureDir(rulesDir);
|
|
8210
8900
|
await writeFileContent(rule.getFilePath(), rule.getFileContent());
|
|
8211
8901
|
return {
|
|
8212
|
-
relativePathFromCwd:
|
|
8902
|
+
relativePathFromCwd: join75(RULESYNC_RULES_RELATIVE_DIR_PATH, filename),
|
|
8213
8903
|
frontmatter: rule.getFrontmatter(),
|
|
8214
8904
|
body: rule.getBody()
|
|
8215
8905
|
};
|
|
@@ -8224,12 +8914,12 @@ async function deleteRule({ relativePathFromCwd }) {
|
|
|
8224
8914
|
relativePath: relativePathFromCwd,
|
|
8225
8915
|
intendedRootDir: process.cwd()
|
|
8226
8916
|
});
|
|
8227
|
-
const filename =
|
|
8228
|
-
const fullPath =
|
|
8917
|
+
const filename = basename21(relativePathFromCwd);
|
|
8918
|
+
const fullPath = join75(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH, filename);
|
|
8229
8919
|
try {
|
|
8230
8920
|
await removeFile(fullPath);
|
|
8231
8921
|
return {
|
|
8232
|
-
relativePathFromCwd:
|
|
8922
|
+
relativePathFromCwd: join75(RULESYNC_RULES_RELATIVE_DIR_PATH, filename)
|
|
8233
8923
|
};
|
|
8234
8924
|
} catch (error) {
|
|
8235
8925
|
throw new Error(`Failed to delete rule file ${relativePathFromCwd}: ${formatError(error)}`, {
|
|
@@ -8238,23 +8928,23 @@ async function deleteRule({ relativePathFromCwd }) {
|
|
|
8238
8928
|
}
|
|
8239
8929
|
}
|
|
8240
8930
|
var ruleToolSchemas = {
|
|
8241
|
-
listRules:
|
|
8242
|
-
getRule:
|
|
8243
|
-
relativePathFromCwd:
|
|
8931
|
+
listRules: z31.object({}),
|
|
8932
|
+
getRule: z31.object({
|
|
8933
|
+
relativePathFromCwd: z31.string()
|
|
8244
8934
|
}),
|
|
8245
|
-
putRule:
|
|
8246
|
-
relativePathFromCwd:
|
|
8935
|
+
putRule: z31.object({
|
|
8936
|
+
relativePathFromCwd: z31.string(),
|
|
8247
8937
|
frontmatter: RulesyncRuleFrontmatterSchema,
|
|
8248
|
-
body:
|
|
8938
|
+
body: z31.string()
|
|
8249
8939
|
}),
|
|
8250
|
-
deleteRule:
|
|
8251
|
-
relativePathFromCwd:
|
|
8940
|
+
deleteRule: z31.object({
|
|
8941
|
+
relativePathFromCwd: z31.string()
|
|
8252
8942
|
})
|
|
8253
8943
|
};
|
|
8254
8944
|
var ruleTools = {
|
|
8255
8945
|
listRules: {
|
|
8256
8946
|
name: "listRules",
|
|
8257
|
-
description: `List all rules from ${
|
|
8947
|
+
description: `List all rules from ${join75(RULESYNC_RULES_RELATIVE_DIR_PATH, "*.md")} with their frontmatter.`,
|
|
8258
8948
|
parameters: ruleToolSchemas.listRules,
|
|
8259
8949
|
execute: async () => {
|
|
8260
8950
|
const rules = await listRules();
|
|
@@ -8296,12 +8986,12 @@ var ruleTools = {
|
|
|
8296
8986
|
};
|
|
8297
8987
|
|
|
8298
8988
|
// src/mcp/subagents.ts
|
|
8299
|
-
import { basename as
|
|
8300
|
-
import { z as
|
|
8989
|
+
import { basename as basename22, join as join76 } from "path";
|
|
8990
|
+
import { z as z32 } from "zod/mini";
|
|
8301
8991
|
var maxSubagentSizeBytes = 1024 * 1024;
|
|
8302
8992
|
var maxSubagentsCount = 1e3;
|
|
8303
8993
|
async function listSubagents() {
|
|
8304
|
-
const subagentsDir =
|
|
8994
|
+
const subagentsDir = join76(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH);
|
|
8305
8995
|
try {
|
|
8306
8996
|
const files = await listDirectoryFiles(subagentsDir);
|
|
8307
8997
|
const mdFiles = files.filter((file) => file.endsWith(".md"));
|
|
@@ -8314,7 +9004,7 @@ async function listSubagents() {
|
|
|
8314
9004
|
});
|
|
8315
9005
|
const frontmatter = subagent.getFrontmatter();
|
|
8316
9006
|
return {
|
|
8317
|
-
relativePathFromCwd:
|
|
9007
|
+
relativePathFromCwd: join76(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, file),
|
|
8318
9008
|
frontmatter
|
|
8319
9009
|
};
|
|
8320
9010
|
} catch (error) {
|
|
@@ -8336,14 +9026,14 @@ async function getSubagent({ relativePathFromCwd }) {
|
|
|
8336
9026
|
relativePath: relativePathFromCwd,
|
|
8337
9027
|
intendedRootDir: process.cwd()
|
|
8338
9028
|
});
|
|
8339
|
-
const filename =
|
|
9029
|
+
const filename = basename22(relativePathFromCwd);
|
|
8340
9030
|
try {
|
|
8341
9031
|
const subagent = await RulesyncSubagent.fromFile({
|
|
8342
9032
|
relativeFilePath: filename,
|
|
8343
9033
|
validate: true
|
|
8344
9034
|
});
|
|
8345
9035
|
return {
|
|
8346
|
-
relativePathFromCwd:
|
|
9036
|
+
relativePathFromCwd: join76(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename),
|
|
8347
9037
|
frontmatter: subagent.getFrontmatter(),
|
|
8348
9038
|
body: subagent.getBody()
|
|
8349
9039
|
};
|
|
@@ -8362,7 +9052,7 @@ async function putSubagent({
|
|
|
8362
9052
|
relativePath: relativePathFromCwd,
|
|
8363
9053
|
intendedRootDir: process.cwd()
|
|
8364
9054
|
});
|
|
8365
|
-
const filename =
|
|
9055
|
+
const filename = basename22(relativePathFromCwd);
|
|
8366
9056
|
const estimatedSize = JSON.stringify(frontmatter).length + body.length;
|
|
8367
9057
|
if (estimatedSize > maxSubagentSizeBytes) {
|
|
8368
9058
|
throw new Error(
|
|
@@ -8372,7 +9062,7 @@ async function putSubagent({
|
|
|
8372
9062
|
try {
|
|
8373
9063
|
const existingSubagents = await listSubagents();
|
|
8374
9064
|
const isUpdate = existingSubagents.some(
|
|
8375
|
-
(subagent2) => subagent2.relativePathFromCwd ===
|
|
9065
|
+
(subagent2) => subagent2.relativePathFromCwd === join76(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename)
|
|
8376
9066
|
);
|
|
8377
9067
|
if (!isUpdate && existingSubagents.length >= maxSubagentsCount) {
|
|
8378
9068
|
throw new Error(`Maximum number of subagents (${maxSubagentsCount}) reached`);
|
|
@@ -8385,11 +9075,11 @@ async function putSubagent({
|
|
|
8385
9075
|
body,
|
|
8386
9076
|
validate: true
|
|
8387
9077
|
});
|
|
8388
|
-
const subagentsDir =
|
|
9078
|
+
const subagentsDir = join76(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH);
|
|
8389
9079
|
await ensureDir(subagentsDir);
|
|
8390
9080
|
await writeFileContent(subagent.getFilePath(), subagent.getFileContent());
|
|
8391
9081
|
return {
|
|
8392
|
-
relativePathFromCwd:
|
|
9082
|
+
relativePathFromCwd: join76(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename),
|
|
8393
9083
|
frontmatter: subagent.getFrontmatter(),
|
|
8394
9084
|
body: subagent.getBody()
|
|
8395
9085
|
};
|
|
@@ -8404,12 +9094,12 @@ async function deleteSubagent({ relativePathFromCwd }) {
|
|
|
8404
9094
|
relativePath: relativePathFromCwd,
|
|
8405
9095
|
intendedRootDir: process.cwd()
|
|
8406
9096
|
});
|
|
8407
|
-
const filename =
|
|
8408
|
-
const fullPath =
|
|
9097
|
+
const filename = basename22(relativePathFromCwd);
|
|
9098
|
+
const fullPath = join76(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename);
|
|
8409
9099
|
try {
|
|
8410
9100
|
await removeFile(fullPath);
|
|
8411
9101
|
return {
|
|
8412
|
-
relativePathFromCwd:
|
|
9102
|
+
relativePathFromCwd: join76(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename)
|
|
8413
9103
|
};
|
|
8414
9104
|
} catch (error) {
|
|
8415
9105
|
throw new Error(
|
|
@@ -8421,23 +9111,23 @@ async function deleteSubagent({ relativePathFromCwd }) {
|
|
|
8421
9111
|
}
|
|
8422
9112
|
}
|
|
8423
9113
|
var subagentToolSchemas = {
|
|
8424
|
-
listSubagents:
|
|
8425
|
-
getSubagent:
|
|
8426
|
-
relativePathFromCwd:
|
|
9114
|
+
listSubagents: z32.object({}),
|
|
9115
|
+
getSubagent: z32.object({
|
|
9116
|
+
relativePathFromCwd: z32.string()
|
|
8427
9117
|
}),
|
|
8428
|
-
putSubagent:
|
|
8429
|
-
relativePathFromCwd:
|
|
9118
|
+
putSubagent: z32.object({
|
|
9119
|
+
relativePathFromCwd: z32.string(),
|
|
8430
9120
|
frontmatter: RulesyncSubagentFrontmatterSchema,
|
|
8431
|
-
body:
|
|
9121
|
+
body: z32.string()
|
|
8432
9122
|
}),
|
|
8433
|
-
deleteSubagent:
|
|
8434
|
-
relativePathFromCwd:
|
|
9123
|
+
deleteSubagent: z32.object({
|
|
9124
|
+
relativePathFromCwd: z32.string()
|
|
8435
9125
|
})
|
|
8436
9126
|
};
|
|
8437
9127
|
var subagentTools = {
|
|
8438
9128
|
listSubagents: {
|
|
8439
9129
|
name: "listSubagents",
|
|
8440
|
-
description: `List all subagents from ${
|
|
9130
|
+
description: `List all subagents from ${join76(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, "*.md")} with their frontmatter.`,
|
|
8441
9131
|
parameters: subagentToolSchemas.listSubagents,
|
|
8442
9132
|
execute: async () => {
|
|
8443
9133
|
const subagents = await listSubagents();
|
|
@@ -8511,7 +9201,7 @@ async function mcpCommand({ version }) {
|
|
|
8511
9201
|
}
|
|
8512
9202
|
|
|
8513
9203
|
// src/cli/index.ts
|
|
8514
|
-
var getVersion = () => "3.
|
|
9204
|
+
var getVersion = () => "3.24.0";
|
|
8515
9205
|
var main = async () => {
|
|
8516
9206
|
const program = new Command();
|
|
8517
9207
|
const version = getVersion();
|