rulesync 3.23.5 → 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 +815 -119
- package/dist/index.js +815 -119
- 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:
|
|
@@ -2551,7 +2576,12 @@ var IgnoreProcessor = class extends FeatureProcessor {
|
|
|
2551
2576
|
const toolIgnores = await this.loadToolIgnores();
|
|
2552
2577
|
return toolIgnores;
|
|
2553
2578
|
} catch (error) {
|
|
2554
|
-
|
|
2579
|
+
const errorMessage = `Failed to load tool files: ${formatError(error)}`;
|
|
2580
|
+
if (error instanceof Error && error.message.includes("no such file or directory")) {
|
|
2581
|
+
logger.debug(errorMessage);
|
|
2582
|
+
} else {
|
|
2583
|
+
logger.error(errorMessage);
|
|
2584
|
+
}
|
|
2555
2585
|
return [];
|
|
2556
2586
|
}
|
|
2557
2587
|
}
|
|
@@ -3572,9 +3602,7 @@ var McpProcessor = class extends FeatureProcessor {
|
|
|
3572
3602
|
try {
|
|
3573
3603
|
return [await RulesyncMcp.fromFile({ modularMcp: this.modularMcp })];
|
|
3574
3604
|
} catch (error) {
|
|
3575
|
-
logger.error(
|
|
3576
|
-
`Failed to load MCP files for tool target: ${this.toolTarget}: ${formatError(error)}`
|
|
3577
|
-
);
|
|
3605
|
+
logger.error(`Failed to load a Rulesync MCP file: ${formatError(error)}`);
|
|
3578
3606
|
return [];
|
|
3579
3607
|
}
|
|
3580
3608
|
}
|
|
@@ -3668,9 +3696,12 @@ var McpProcessor = class extends FeatureProcessor {
|
|
|
3668
3696
|
logger.info(`Successfully loaded ${toolMcps.length} ${this.toolTarget} MCP files`);
|
|
3669
3697
|
return toolMcps;
|
|
3670
3698
|
} catch (error) {
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3699
|
+
const errorMessage = `Failed to load MCP files for tool target: ${this.toolTarget}: ${formatError(error)}`;
|
|
3700
|
+
if (error instanceof Error && error.message.includes("no such file or directory")) {
|
|
3701
|
+
logger.debug(errorMessage);
|
|
3702
|
+
} else {
|
|
3703
|
+
logger.error(errorMessage);
|
|
3704
|
+
}
|
|
3674
3705
|
return [];
|
|
3675
3706
|
}
|
|
3676
3707
|
}
|
|
@@ -4541,7 +4572,7 @@ var SubagentsProcessor = class extends FeatureProcessor {
|
|
|
4541
4572
|
fromFile
|
|
4542
4573
|
}) {
|
|
4543
4574
|
const paths = await findFilesByGlobs(join44(this.baseDir, relativeDirPath, "*.md"));
|
|
4544
|
-
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);
|
|
4545
4576
|
logger.info(`Successfully loaded ${subagents.length} ${relativeDirPath} subagents`);
|
|
4546
4577
|
return subagents;
|
|
4547
4578
|
}
|
|
@@ -7063,6 +7094,614 @@ For example, if the user instructs \`Call planner subagent to plan the refactori
|
|
|
7063
7094
|
}
|
|
7064
7095
|
};
|
|
7065
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
|
+
|
|
7066
7705
|
// src/cli/commands/generate.ts
|
|
7067
7706
|
async function generateCommand(options) {
|
|
7068
7707
|
const config = await ConfigResolver.resolve(options);
|
|
@@ -7078,7 +7717,8 @@ async function generateCommand(options) {
|
|
|
7078
7717
|
const totalMcpOutputs = await generateMcp(config);
|
|
7079
7718
|
const totalCommandOutputs = await generateCommands(config);
|
|
7080
7719
|
const totalSubagentOutputs = await generateSubagents(config);
|
|
7081
|
-
const
|
|
7720
|
+
const totalSkillOutputs = await generateSkills(config);
|
|
7721
|
+
const totalGenerated = totalRulesOutputs + totalMcpOutputs + totalCommandOutputs + totalIgnoreOutputs + totalSubagentOutputs + totalSkillOutputs;
|
|
7082
7722
|
if (totalGenerated === 0) {
|
|
7083
7723
|
const enabledFeatures = config.getFeatures().join(", ");
|
|
7084
7724
|
logger.warn(`\u26A0\uFE0F No files generated for enabled features: ${enabledFeatures}`);
|
|
@@ -7091,6 +7731,7 @@ async function generateCommand(options) {
|
|
|
7091
7731
|
if (totalMcpOutputs > 0) parts.push(`${totalMcpOutputs} MCP files`);
|
|
7092
7732
|
if (totalCommandOutputs > 0) parts.push(`${totalCommandOutputs} commands`);
|
|
7093
7733
|
if (totalSubagentOutputs > 0) parts.push(`${totalSubagentOutputs} subagents`);
|
|
7734
|
+
if (totalSkillOutputs > 0) parts.push(`${totalSkillOutputs} skills`);
|
|
7094
7735
|
logger.success(`\u{1F389} All done! Generated ${totalGenerated} file(s) total (${parts.join(" + ")})`);
|
|
7095
7736
|
}
|
|
7096
7737
|
}
|
|
@@ -7265,11 +7906,39 @@ async function generateSubagents(config) {
|
|
|
7265
7906
|
}
|
|
7266
7907
|
return totalSubagentOutputs;
|
|
7267
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
|
+
}
|
|
7268
7937
|
|
|
7269
7938
|
// src/cli/commands/gitignore.ts
|
|
7270
|
-
import { join as
|
|
7939
|
+
import { join as join70 } from "path";
|
|
7271
7940
|
var gitignoreCommand = async () => {
|
|
7272
|
-
const gitignorePath =
|
|
7941
|
+
const gitignorePath = join70(process.cwd(), ".gitignore");
|
|
7273
7942
|
const rulesFilesToIgnore = [
|
|
7274
7943
|
"# Generated by rulesync - AI tool configuration files",
|
|
7275
7944
|
// AGENTS.md
|
|
@@ -7286,6 +7955,7 @@ var gitignoreCommand = async () => {
|
|
|
7286
7955
|
"**/.claude/memories/",
|
|
7287
7956
|
"**/.claude/commands/",
|
|
7288
7957
|
"**/.claude/agents/",
|
|
7958
|
+
"**/.claude/skills/",
|
|
7289
7959
|
"**/.claude/settings.local.json",
|
|
7290
7960
|
"**/.mcp.json",
|
|
7291
7961
|
// Cline
|
|
@@ -7379,6 +8049,7 @@ async function importCommand(options) {
|
|
|
7379
8049
|
await importMcp(config, tool);
|
|
7380
8050
|
await importCommands(config, tool);
|
|
7381
8051
|
await importSubagents(config, tool);
|
|
8052
|
+
await importSkills(config, tool);
|
|
7382
8053
|
}
|
|
7383
8054
|
async function importRules(config, tool) {
|
|
7384
8055
|
if (!config.getFeatures().includes("rules")) {
|
|
@@ -7508,9 +8179,34 @@ async function importSubagents(config, tool) {
|
|
|
7508
8179
|
}
|
|
7509
8180
|
return writtenCount;
|
|
7510
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
|
+
}
|
|
7511
8207
|
|
|
7512
8208
|
// src/cli/commands/init.ts
|
|
7513
|
-
import { join as
|
|
8209
|
+
import { join as join71 } from "path";
|
|
7514
8210
|
async function initCommand() {
|
|
7515
8211
|
logger.info("Initializing rulesync...");
|
|
7516
8212
|
await ensureDir(RULESYNC_RELATIVE_DIR_PATH);
|
|
@@ -7673,14 +8369,14 @@ Attention, again, you are just the planner, so though you can read any files and
|
|
|
7673
8369
|
await ensureDir(commandPaths.relativeDirPath);
|
|
7674
8370
|
await ensureDir(subagentPaths.relativeDirPath);
|
|
7675
8371
|
await ensureDir(ignorePaths.relativeDirPath);
|
|
7676
|
-
const ruleFilepath =
|
|
8372
|
+
const ruleFilepath = join71(rulePaths.recommended.relativeDirPath, sampleRuleFile.filename);
|
|
7677
8373
|
if (!await fileExists(ruleFilepath)) {
|
|
7678
8374
|
await writeFileContent(ruleFilepath, sampleRuleFile.content);
|
|
7679
8375
|
logger.success(`Created ${ruleFilepath}`);
|
|
7680
8376
|
} else {
|
|
7681
8377
|
logger.info(`Skipped ${ruleFilepath} (already exists)`);
|
|
7682
8378
|
}
|
|
7683
|
-
const mcpFilepath =
|
|
8379
|
+
const mcpFilepath = join71(
|
|
7684
8380
|
mcpPaths.recommended.relativeDirPath,
|
|
7685
8381
|
mcpPaths.recommended.relativeFilePath
|
|
7686
8382
|
);
|
|
@@ -7690,21 +8386,21 @@ Attention, again, you are just the planner, so though you can read any files and
|
|
|
7690
8386
|
} else {
|
|
7691
8387
|
logger.info(`Skipped ${mcpFilepath} (already exists)`);
|
|
7692
8388
|
}
|
|
7693
|
-
const commandFilepath =
|
|
8389
|
+
const commandFilepath = join71(commandPaths.relativeDirPath, sampleCommandFile.filename);
|
|
7694
8390
|
if (!await fileExists(commandFilepath)) {
|
|
7695
8391
|
await writeFileContent(commandFilepath, sampleCommandFile.content);
|
|
7696
8392
|
logger.success(`Created ${commandFilepath}`);
|
|
7697
8393
|
} else {
|
|
7698
8394
|
logger.info(`Skipped ${commandFilepath} (already exists)`);
|
|
7699
8395
|
}
|
|
7700
|
-
const subagentFilepath =
|
|
8396
|
+
const subagentFilepath = join71(subagentPaths.relativeDirPath, sampleSubagentFile.filename);
|
|
7701
8397
|
if (!await fileExists(subagentFilepath)) {
|
|
7702
8398
|
await writeFileContent(subagentFilepath, sampleSubagentFile.content);
|
|
7703
8399
|
logger.success(`Created ${subagentFilepath}`);
|
|
7704
8400
|
} else {
|
|
7705
8401
|
logger.info(`Skipped ${subagentFilepath} (already exists)`);
|
|
7706
8402
|
}
|
|
7707
|
-
const ignoreFilepath =
|
|
8403
|
+
const ignoreFilepath = join71(ignorePaths.relativeDirPath, ignorePaths.relativeFilePath);
|
|
7708
8404
|
if (!await fileExists(ignoreFilepath)) {
|
|
7709
8405
|
await writeFileContent(ignoreFilepath, sampleIgnoreFile.content);
|
|
7710
8406
|
logger.success(`Created ${ignoreFilepath}`);
|
|
@@ -7717,12 +8413,12 @@ Attention, again, you are just the planner, so though you can read any files and
|
|
|
7717
8413
|
import { FastMCP } from "fastmcp";
|
|
7718
8414
|
|
|
7719
8415
|
// src/mcp/commands.ts
|
|
7720
|
-
import { basename as
|
|
7721
|
-
import { z as
|
|
8416
|
+
import { basename as basename20, join as join72 } from "path";
|
|
8417
|
+
import { z as z28 } from "zod/mini";
|
|
7722
8418
|
var maxCommandSizeBytes = 1024 * 1024;
|
|
7723
8419
|
var maxCommandsCount = 1e3;
|
|
7724
8420
|
async function listCommands() {
|
|
7725
|
-
const commandsDir =
|
|
8421
|
+
const commandsDir = join72(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH);
|
|
7726
8422
|
try {
|
|
7727
8423
|
const files = await listDirectoryFiles(commandsDir);
|
|
7728
8424
|
const mdFiles = files.filter((file) => file.endsWith(".md"));
|
|
@@ -7734,7 +8430,7 @@ async function listCommands() {
|
|
|
7734
8430
|
});
|
|
7735
8431
|
const frontmatter = command.getFrontmatter();
|
|
7736
8432
|
return {
|
|
7737
|
-
relativePathFromCwd:
|
|
8433
|
+
relativePathFromCwd: join72(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, file),
|
|
7738
8434
|
frontmatter
|
|
7739
8435
|
};
|
|
7740
8436
|
} catch (error) {
|
|
@@ -7754,13 +8450,13 @@ async function getCommand({ relativePathFromCwd }) {
|
|
|
7754
8450
|
relativePath: relativePathFromCwd,
|
|
7755
8451
|
intendedRootDir: process.cwd()
|
|
7756
8452
|
});
|
|
7757
|
-
const filename =
|
|
8453
|
+
const filename = basename20(relativePathFromCwd);
|
|
7758
8454
|
try {
|
|
7759
8455
|
const command = await RulesyncCommand.fromFile({
|
|
7760
8456
|
relativeFilePath: filename
|
|
7761
8457
|
});
|
|
7762
8458
|
return {
|
|
7763
|
-
relativePathFromCwd:
|
|
8459
|
+
relativePathFromCwd: join72(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename),
|
|
7764
8460
|
frontmatter: command.getFrontmatter(),
|
|
7765
8461
|
body: command.getBody()
|
|
7766
8462
|
};
|
|
@@ -7779,7 +8475,7 @@ async function putCommand({
|
|
|
7779
8475
|
relativePath: relativePathFromCwd,
|
|
7780
8476
|
intendedRootDir: process.cwd()
|
|
7781
8477
|
});
|
|
7782
|
-
const filename =
|
|
8478
|
+
const filename = basename20(relativePathFromCwd);
|
|
7783
8479
|
const estimatedSize = JSON.stringify(frontmatter).length + body.length;
|
|
7784
8480
|
if (estimatedSize > maxCommandSizeBytes) {
|
|
7785
8481
|
throw new Error(
|
|
@@ -7789,7 +8485,7 @@ async function putCommand({
|
|
|
7789
8485
|
try {
|
|
7790
8486
|
const existingCommands = await listCommands();
|
|
7791
8487
|
const isUpdate = existingCommands.some(
|
|
7792
|
-
(command2) => command2.relativePathFromCwd ===
|
|
8488
|
+
(command2) => command2.relativePathFromCwd === join72(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename)
|
|
7793
8489
|
);
|
|
7794
8490
|
if (!isUpdate && existingCommands.length >= maxCommandsCount) {
|
|
7795
8491
|
throw new Error(`Maximum number of commands (${maxCommandsCount}) reached`);
|
|
@@ -7804,11 +8500,11 @@ async function putCommand({
|
|
|
7804
8500
|
fileContent,
|
|
7805
8501
|
validate: true
|
|
7806
8502
|
});
|
|
7807
|
-
const commandsDir =
|
|
8503
|
+
const commandsDir = join72(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH);
|
|
7808
8504
|
await ensureDir(commandsDir);
|
|
7809
8505
|
await writeFileContent(command.getFilePath(), command.getFileContent());
|
|
7810
8506
|
return {
|
|
7811
|
-
relativePathFromCwd:
|
|
8507
|
+
relativePathFromCwd: join72(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename),
|
|
7812
8508
|
frontmatter: command.getFrontmatter(),
|
|
7813
8509
|
body: command.getBody()
|
|
7814
8510
|
};
|
|
@@ -7823,12 +8519,12 @@ async function deleteCommand({ relativePathFromCwd }) {
|
|
|
7823
8519
|
relativePath: relativePathFromCwd,
|
|
7824
8520
|
intendedRootDir: process.cwd()
|
|
7825
8521
|
});
|
|
7826
|
-
const filename =
|
|
7827
|
-
const fullPath =
|
|
8522
|
+
const filename = basename20(relativePathFromCwd);
|
|
8523
|
+
const fullPath = join72(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename);
|
|
7828
8524
|
try {
|
|
7829
8525
|
await removeFile(fullPath);
|
|
7830
8526
|
return {
|
|
7831
|
-
relativePathFromCwd:
|
|
8527
|
+
relativePathFromCwd: join72(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename)
|
|
7832
8528
|
};
|
|
7833
8529
|
} catch (error) {
|
|
7834
8530
|
throw new Error(`Failed to delete command file ${relativePathFromCwd}: ${formatError(error)}`, {
|
|
@@ -7837,23 +8533,23 @@ async function deleteCommand({ relativePathFromCwd }) {
|
|
|
7837
8533
|
}
|
|
7838
8534
|
}
|
|
7839
8535
|
var commandToolSchemas = {
|
|
7840
|
-
listCommands:
|
|
7841
|
-
getCommand:
|
|
7842
|
-
relativePathFromCwd:
|
|
8536
|
+
listCommands: z28.object({}),
|
|
8537
|
+
getCommand: z28.object({
|
|
8538
|
+
relativePathFromCwd: z28.string()
|
|
7843
8539
|
}),
|
|
7844
|
-
putCommand:
|
|
7845
|
-
relativePathFromCwd:
|
|
8540
|
+
putCommand: z28.object({
|
|
8541
|
+
relativePathFromCwd: z28.string(),
|
|
7846
8542
|
frontmatter: RulesyncCommandFrontmatterSchema,
|
|
7847
|
-
body:
|
|
8543
|
+
body: z28.string()
|
|
7848
8544
|
}),
|
|
7849
|
-
deleteCommand:
|
|
7850
|
-
relativePathFromCwd:
|
|
8545
|
+
deleteCommand: z28.object({
|
|
8546
|
+
relativePathFromCwd: z28.string()
|
|
7851
8547
|
})
|
|
7852
8548
|
};
|
|
7853
8549
|
var commandTools = {
|
|
7854
8550
|
listCommands: {
|
|
7855
8551
|
name: "listCommands",
|
|
7856
|
-
description: `List all commands from ${
|
|
8552
|
+
description: `List all commands from ${join72(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, "*.md")} with their frontmatter.`,
|
|
7857
8553
|
parameters: commandToolSchemas.listCommands,
|
|
7858
8554
|
execute: async () => {
|
|
7859
8555
|
const commands = await listCommands();
|
|
@@ -7895,11 +8591,11 @@ var commandTools = {
|
|
|
7895
8591
|
};
|
|
7896
8592
|
|
|
7897
8593
|
// src/mcp/ignore.ts
|
|
7898
|
-
import { join as
|
|
7899
|
-
import { z as
|
|
8594
|
+
import { join as join73 } from "path";
|
|
8595
|
+
import { z as z29 } from "zod/mini";
|
|
7900
8596
|
var maxIgnoreFileSizeBytes = 100 * 1024;
|
|
7901
8597
|
async function getIgnoreFile() {
|
|
7902
|
-
const ignoreFilePath =
|
|
8598
|
+
const ignoreFilePath = join73(process.cwd(), RULESYNC_IGNORE_RELATIVE_FILE_PATH);
|
|
7903
8599
|
try {
|
|
7904
8600
|
const content = await readFileContent(ignoreFilePath);
|
|
7905
8601
|
return {
|
|
@@ -7913,7 +8609,7 @@ async function getIgnoreFile() {
|
|
|
7913
8609
|
}
|
|
7914
8610
|
}
|
|
7915
8611
|
async function putIgnoreFile({ content }) {
|
|
7916
|
-
const ignoreFilePath =
|
|
8612
|
+
const ignoreFilePath = join73(process.cwd(), RULESYNC_IGNORE_RELATIVE_FILE_PATH);
|
|
7917
8613
|
const contentSizeBytes = Buffer.byteLength(content, "utf8");
|
|
7918
8614
|
if (contentSizeBytes > maxIgnoreFileSizeBytes) {
|
|
7919
8615
|
throw new Error(
|
|
@@ -7934,7 +8630,7 @@ async function putIgnoreFile({ content }) {
|
|
|
7934
8630
|
}
|
|
7935
8631
|
}
|
|
7936
8632
|
async function deleteIgnoreFile() {
|
|
7937
|
-
const ignoreFilePath =
|
|
8633
|
+
const ignoreFilePath = join73(process.cwd(), RULESYNC_IGNORE_RELATIVE_FILE_PATH);
|
|
7938
8634
|
try {
|
|
7939
8635
|
await removeFile(ignoreFilePath);
|
|
7940
8636
|
return {
|
|
@@ -7947,11 +8643,11 @@ async function deleteIgnoreFile() {
|
|
|
7947
8643
|
}
|
|
7948
8644
|
}
|
|
7949
8645
|
var ignoreToolSchemas = {
|
|
7950
|
-
getIgnoreFile:
|
|
7951
|
-
putIgnoreFile:
|
|
7952
|
-
content:
|
|
8646
|
+
getIgnoreFile: z29.object({}),
|
|
8647
|
+
putIgnoreFile: z29.object({
|
|
8648
|
+
content: z29.string()
|
|
7953
8649
|
}),
|
|
7954
|
-
deleteIgnoreFile:
|
|
8650
|
+
deleteIgnoreFile: z29.object({})
|
|
7955
8651
|
};
|
|
7956
8652
|
var ignoreTools = {
|
|
7957
8653
|
getIgnoreFile: {
|
|
@@ -7984,8 +8680,8 @@ var ignoreTools = {
|
|
|
7984
8680
|
};
|
|
7985
8681
|
|
|
7986
8682
|
// src/mcp/mcp.ts
|
|
7987
|
-
import { join as
|
|
7988
|
-
import { z as
|
|
8683
|
+
import { join as join74 } from "path";
|
|
8684
|
+
import { z as z30 } from "zod/mini";
|
|
7989
8685
|
var maxMcpSizeBytes = 1024 * 1024;
|
|
7990
8686
|
async function getMcpFile() {
|
|
7991
8687
|
const config = await ConfigResolver.resolve({});
|
|
@@ -7994,7 +8690,7 @@ async function getMcpFile() {
|
|
|
7994
8690
|
validate: true,
|
|
7995
8691
|
modularMcp: config.getModularMcp()
|
|
7996
8692
|
});
|
|
7997
|
-
const relativePathFromCwd =
|
|
8693
|
+
const relativePathFromCwd = join74(
|
|
7998
8694
|
rulesyncMcp.getRelativeDirPath(),
|
|
7999
8695
|
rulesyncMcp.getRelativeFilePath()
|
|
8000
8696
|
);
|
|
@@ -8027,7 +8723,7 @@ async function putMcpFile({ content }) {
|
|
|
8027
8723
|
const paths = RulesyncMcp.getSettablePaths();
|
|
8028
8724
|
const relativeDirPath = paths.recommended.relativeDirPath;
|
|
8029
8725
|
const relativeFilePath = paths.recommended.relativeFilePath;
|
|
8030
|
-
const fullPath =
|
|
8726
|
+
const fullPath = join74(baseDir, relativeDirPath, relativeFilePath);
|
|
8031
8727
|
const rulesyncMcp = new RulesyncMcp({
|
|
8032
8728
|
baseDir,
|
|
8033
8729
|
relativeDirPath,
|
|
@@ -8036,9 +8732,9 @@ async function putMcpFile({ content }) {
|
|
|
8036
8732
|
validate: true,
|
|
8037
8733
|
modularMcp: config.getModularMcp()
|
|
8038
8734
|
});
|
|
8039
|
-
await ensureDir(
|
|
8735
|
+
await ensureDir(join74(baseDir, relativeDirPath));
|
|
8040
8736
|
await writeFileContent(fullPath, content);
|
|
8041
|
-
const relativePathFromCwd =
|
|
8737
|
+
const relativePathFromCwd = join74(relativeDirPath, relativeFilePath);
|
|
8042
8738
|
return {
|
|
8043
8739
|
relativePathFromCwd,
|
|
8044
8740
|
content: rulesyncMcp.getFileContent()
|
|
@@ -8053,15 +8749,15 @@ async function deleteMcpFile() {
|
|
|
8053
8749
|
try {
|
|
8054
8750
|
const baseDir = process.cwd();
|
|
8055
8751
|
const paths = RulesyncMcp.getSettablePaths();
|
|
8056
|
-
const recommendedPath =
|
|
8752
|
+
const recommendedPath = join74(
|
|
8057
8753
|
baseDir,
|
|
8058
8754
|
paths.recommended.relativeDirPath,
|
|
8059
8755
|
paths.recommended.relativeFilePath
|
|
8060
8756
|
);
|
|
8061
|
-
const legacyPath =
|
|
8757
|
+
const legacyPath = join74(baseDir, paths.legacy.relativeDirPath, paths.legacy.relativeFilePath);
|
|
8062
8758
|
await removeFile(recommendedPath);
|
|
8063
8759
|
await removeFile(legacyPath);
|
|
8064
|
-
const relativePathFromCwd =
|
|
8760
|
+
const relativePathFromCwd = join74(
|
|
8065
8761
|
paths.recommended.relativeDirPath,
|
|
8066
8762
|
paths.recommended.relativeFilePath
|
|
8067
8763
|
);
|
|
@@ -8075,11 +8771,11 @@ async function deleteMcpFile() {
|
|
|
8075
8771
|
}
|
|
8076
8772
|
}
|
|
8077
8773
|
var mcpToolSchemas = {
|
|
8078
|
-
getMcpFile:
|
|
8079
|
-
putMcpFile:
|
|
8080
|
-
content:
|
|
8774
|
+
getMcpFile: z30.object({}),
|
|
8775
|
+
putMcpFile: z30.object({
|
|
8776
|
+
content: z30.string()
|
|
8081
8777
|
}),
|
|
8082
|
-
deleteMcpFile:
|
|
8778
|
+
deleteMcpFile: z30.object({})
|
|
8083
8779
|
};
|
|
8084
8780
|
var mcpTools = {
|
|
8085
8781
|
getMcpFile: {
|
|
@@ -8112,12 +8808,12 @@ var mcpTools = {
|
|
|
8112
8808
|
};
|
|
8113
8809
|
|
|
8114
8810
|
// src/mcp/rules.ts
|
|
8115
|
-
import { basename as
|
|
8116
|
-
import { z as
|
|
8811
|
+
import { basename as basename21, join as join75 } from "path";
|
|
8812
|
+
import { z as z31 } from "zod/mini";
|
|
8117
8813
|
var maxRuleSizeBytes = 1024 * 1024;
|
|
8118
8814
|
var maxRulesCount = 1e3;
|
|
8119
8815
|
async function listRules() {
|
|
8120
|
-
const rulesDir =
|
|
8816
|
+
const rulesDir = join75(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH);
|
|
8121
8817
|
try {
|
|
8122
8818
|
const files = await listDirectoryFiles(rulesDir);
|
|
8123
8819
|
const mdFiles = files.filter((file) => file.endsWith(".md"));
|
|
@@ -8130,7 +8826,7 @@ async function listRules() {
|
|
|
8130
8826
|
});
|
|
8131
8827
|
const frontmatter = rule.getFrontmatter();
|
|
8132
8828
|
return {
|
|
8133
|
-
relativePathFromCwd:
|
|
8829
|
+
relativePathFromCwd: join75(RULESYNC_RULES_RELATIVE_DIR_PATH, file),
|
|
8134
8830
|
frontmatter
|
|
8135
8831
|
};
|
|
8136
8832
|
} catch (error) {
|
|
@@ -8150,14 +8846,14 @@ async function getRule({ relativePathFromCwd }) {
|
|
|
8150
8846
|
relativePath: relativePathFromCwd,
|
|
8151
8847
|
intendedRootDir: process.cwd()
|
|
8152
8848
|
});
|
|
8153
|
-
const filename =
|
|
8849
|
+
const filename = basename21(relativePathFromCwd);
|
|
8154
8850
|
try {
|
|
8155
8851
|
const rule = await RulesyncRule.fromFile({
|
|
8156
8852
|
relativeFilePath: filename,
|
|
8157
8853
|
validate: true
|
|
8158
8854
|
});
|
|
8159
8855
|
return {
|
|
8160
|
-
relativePathFromCwd:
|
|
8856
|
+
relativePathFromCwd: join75(RULESYNC_RULES_RELATIVE_DIR_PATH, filename),
|
|
8161
8857
|
frontmatter: rule.getFrontmatter(),
|
|
8162
8858
|
body: rule.getBody()
|
|
8163
8859
|
};
|
|
@@ -8176,7 +8872,7 @@ async function putRule({
|
|
|
8176
8872
|
relativePath: relativePathFromCwd,
|
|
8177
8873
|
intendedRootDir: process.cwd()
|
|
8178
8874
|
});
|
|
8179
|
-
const filename =
|
|
8875
|
+
const filename = basename21(relativePathFromCwd);
|
|
8180
8876
|
const estimatedSize = JSON.stringify(frontmatter).length + body.length;
|
|
8181
8877
|
if (estimatedSize > maxRuleSizeBytes) {
|
|
8182
8878
|
throw new Error(
|
|
@@ -8186,7 +8882,7 @@ async function putRule({
|
|
|
8186
8882
|
try {
|
|
8187
8883
|
const existingRules = await listRules();
|
|
8188
8884
|
const isUpdate = existingRules.some(
|
|
8189
|
-
(rule2) => rule2.relativePathFromCwd ===
|
|
8885
|
+
(rule2) => rule2.relativePathFromCwd === join75(RULESYNC_RULES_RELATIVE_DIR_PATH, filename)
|
|
8190
8886
|
);
|
|
8191
8887
|
if (!isUpdate && existingRules.length >= maxRulesCount) {
|
|
8192
8888
|
throw new Error(`Maximum number of rules (${maxRulesCount}) reached`);
|
|
@@ -8199,11 +8895,11 @@ async function putRule({
|
|
|
8199
8895
|
body,
|
|
8200
8896
|
validate: true
|
|
8201
8897
|
});
|
|
8202
|
-
const rulesDir =
|
|
8898
|
+
const rulesDir = join75(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH);
|
|
8203
8899
|
await ensureDir(rulesDir);
|
|
8204
8900
|
await writeFileContent(rule.getFilePath(), rule.getFileContent());
|
|
8205
8901
|
return {
|
|
8206
|
-
relativePathFromCwd:
|
|
8902
|
+
relativePathFromCwd: join75(RULESYNC_RULES_RELATIVE_DIR_PATH, filename),
|
|
8207
8903
|
frontmatter: rule.getFrontmatter(),
|
|
8208
8904
|
body: rule.getBody()
|
|
8209
8905
|
};
|
|
@@ -8218,12 +8914,12 @@ async function deleteRule({ relativePathFromCwd }) {
|
|
|
8218
8914
|
relativePath: relativePathFromCwd,
|
|
8219
8915
|
intendedRootDir: process.cwd()
|
|
8220
8916
|
});
|
|
8221
|
-
const filename =
|
|
8222
|
-
const fullPath =
|
|
8917
|
+
const filename = basename21(relativePathFromCwd);
|
|
8918
|
+
const fullPath = join75(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH, filename);
|
|
8223
8919
|
try {
|
|
8224
8920
|
await removeFile(fullPath);
|
|
8225
8921
|
return {
|
|
8226
|
-
relativePathFromCwd:
|
|
8922
|
+
relativePathFromCwd: join75(RULESYNC_RULES_RELATIVE_DIR_PATH, filename)
|
|
8227
8923
|
};
|
|
8228
8924
|
} catch (error) {
|
|
8229
8925
|
throw new Error(`Failed to delete rule file ${relativePathFromCwd}: ${formatError(error)}`, {
|
|
@@ -8232,23 +8928,23 @@ async function deleteRule({ relativePathFromCwd }) {
|
|
|
8232
8928
|
}
|
|
8233
8929
|
}
|
|
8234
8930
|
var ruleToolSchemas = {
|
|
8235
|
-
listRules:
|
|
8236
|
-
getRule:
|
|
8237
|
-
relativePathFromCwd:
|
|
8931
|
+
listRules: z31.object({}),
|
|
8932
|
+
getRule: z31.object({
|
|
8933
|
+
relativePathFromCwd: z31.string()
|
|
8238
8934
|
}),
|
|
8239
|
-
putRule:
|
|
8240
|
-
relativePathFromCwd:
|
|
8935
|
+
putRule: z31.object({
|
|
8936
|
+
relativePathFromCwd: z31.string(),
|
|
8241
8937
|
frontmatter: RulesyncRuleFrontmatterSchema,
|
|
8242
|
-
body:
|
|
8938
|
+
body: z31.string()
|
|
8243
8939
|
}),
|
|
8244
|
-
deleteRule:
|
|
8245
|
-
relativePathFromCwd:
|
|
8940
|
+
deleteRule: z31.object({
|
|
8941
|
+
relativePathFromCwd: z31.string()
|
|
8246
8942
|
})
|
|
8247
8943
|
};
|
|
8248
8944
|
var ruleTools = {
|
|
8249
8945
|
listRules: {
|
|
8250
8946
|
name: "listRules",
|
|
8251
|
-
description: `List all rules from ${
|
|
8947
|
+
description: `List all rules from ${join75(RULESYNC_RULES_RELATIVE_DIR_PATH, "*.md")} with their frontmatter.`,
|
|
8252
8948
|
parameters: ruleToolSchemas.listRules,
|
|
8253
8949
|
execute: async () => {
|
|
8254
8950
|
const rules = await listRules();
|
|
@@ -8290,12 +8986,12 @@ var ruleTools = {
|
|
|
8290
8986
|
};
|
|
8291
8987
|
|
|
8292
8988
|
// src/mcp/subagents.ts
|
|
8293
|
-
import { basename as
|
|
8294
|
-
import { z as
|
|
8989
|
+
import { basename as basename22, join as join76 } from "path";
|
|
8990
|
+
import { z as z32 } from "zod/mini";
|
|
8295
8991
|
var maxSubagentSizeBytes = 1024 * 1024;
|
|
8296
8992
|
var maxSubagentsCount = 1e3;
|
|
8297
8993
|
async function listSubagents() {
|
|
8298
|
-
const subagentsDir =
|
|
8994
|
+
const subagentsDir = join76(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH);
|
|
8299
8995
|
try {
|
|
8300
8996
|
const files = await listDirectoryFiles(subagentsDir);
|
|
8301
8997
|
const mdFiles = files.filter((file) => file.endsWith(".md"));
|
|
@@ -8308,7 +9004,7 @@ async function listSubagents() {
|
|
|
8308
9004
|
});
|
|
8309
9005
|
const frontmatter = subagent.getFrontmatter();
|
|
8310
9006
|
return {
|
|
8311
|
-
relativePathFromCwd:
|
|
9007
|
+
relativePathFromCwd: join76(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, file),
|
|
8312
9008
|
frontmatter
|
|
8313
9009
|
};
|
|
8314
9010
|
} catch (error) {
|
|
@@ -8330,14 +9026,14 @@ async function getSubagent({ relativePathFromCwd }) {
|
|
|
8330
9026
|
relativePath: relativePathFromCwd,
|
|
8331
9027
|
intendedRootDir: process.cwd()
|
|
8332
9028
|
});
|
|
8333
|
-
const filename =
|
|
9029
|
+
const filename = basename22(relativePathFromCwd);
|
|
8334
9030
|
try {
|
|
8335
9031
|
const subagent = await RulesyncSubagent.fromFile({
|
|
8336
9032
|
relativeFilePath: filename,
|
|
8337
9033
|
validate: true
|
|
8338
9034
|
});
|
|
8339
9035
|
return {
|
|
8340
|
-
relativePathFromCwd:
|
|
9036
|
+
relativePathFromCwd: join76(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename),
|
|
8341
9037
|
frontmatter: subagent.getFrontmatter(),
|
|
8342
9038
|
body: subagent.getBody()
|
|
8343
9039
|
};
|
|
@@ -8356,7 +9052,7 @@ async function putSubagent({
|
|
|
8356
9052
|
relativePath: relativePathFromCwd,
|
|
8357
9053
|
intendedRootDir: process.cwd()
|
|
8358
9054
|
});
|
|
8359
|
-
const filename =
|
|
9055
|
+
const filename = basename22(relativePathFromCwd);
|
|
8360
9056
|
const estimatedSize = JSON.stringify(frontmatter).length + body.length;
|
|
8361
9057
|
if (estimatedSize > maxSubagentSizeBytes) {
|
|
8362
9058
|
throw new Error(
|
|
@@ -8366,7 +9062,7 @@ async function putSubagent({
|
|
|
8366
9062
|
try {
|
|
8367
9063
|
const existingSubagents = await listSubagents();
|
|
8368
9064
|
const isUpdate = existingSubagents.some(
|
|
8369
|
-
(subagent2) => subagent2.relativePathFromCwd ===
|
|
9065
|
+
(subagent2) => subagent2.relativePathFromCwd === join76(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename)
|
|
8370
9066
|
);
|
|
8371
9067
|
if (!isUpdate && existingSubagents.length >= maxSubagentsCount) {
|
|
8372
9068
|
throw new Error(`Maximum number of subagents (${maxSubagentsCount}) reached`);
|
|
@@ -8379,11 +9075,11 @@ async function putSubagent({
|
|
|
8379
9075
|
body,
|
|
8380
9076
|
validate: true
|
|
8381
9077
|
});
|
|
8382
|
-
const subagentsDir =
|
|
9078
|
+
const subagentsDir = join76(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH);
|
|
8383
9079
|
await ensureDir(subagentsDir);
|
|
8384
9080
|
await writeFileContent(subagent.getFilePath(), subagent.getFileContent());
|
|
8385
9081
|
return {
|
|
8386
|
-
relativePathFromCwd:
|
|
9082
|
+
relativePathFromCwd: join76(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename),
|
|
8387
9083
|
frontmatter: subagent.getFrontmatter(),
|
|
8388
9084
|
body: subagent.getBody()
|
|
8389
9085
|
};
|
|
@@ -8398,12 +9094,12 @@ async function deleteSubagent({ relativePathFromCwd }) {
|
|
|
8398
9094
|
relativePath: relativePathFromCwd,
|
|
8399
9095
|
intendedRootDir: process.cwd()
|
|
8400
9096
|
});
|
|
8401
|
-
const filename =
|
|
8402
|
-
const fullPath =
|
|
9097
|
+
const filename = basename22(relativePathFromCwd);
|
|
9098
|
+
const fullPath = join76(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename);
|
|
8403
9099
|
try {
|
|
8404
9100
|
await removeFile(fullPath);
|
|
8405
9101
|
return {
|
|
8406
|
-
relativePathFromCwd:
|
|
9102
|
+
relativePathFromCwd: join76(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename)
|
|
8407
9103
|
};
|
|
8408
9104
|
} catch (error) {
|
|
8409
9105
|
throw new Error(
|
|
@@ -8415,23 +9111,23 @@ async function deleteSubagent({ relativePathFromCwd }) {
|
|
|
8415
9111
|
}
|
|
8416
9112
|
}
|
|
8417
9113
|
var subagentToolSchemas = {
|
|
8418
|
-
listSubagents:
|
|
8419
|
-
getSubagent:
|
|
8420
|
-
relativePathFromCwd:
|
|
9114
|
+
listSubagents: z32.object({}),
|
|
9115
|
+
getSubagent: z32.object({
|
|
9116
|
+
relativePathFromCwd: z32.string()
|
|
8421
9117
|
}),
|
|
8422
|
-
putSubagent:
|
|
8423
|
-
relativePathFromCwd:
|
|
9118
|
+
putSubagent: z32.object({
|
|
9119
|
+
relativePathFromCwd: z32.string(),
|
|
8424
9120
|
frontmatter: RulesyncSubagentFrontmatterSchema,
|
|
8425
|
-
body:
|
|
9121
|
+
body: z32.string()
|
|
8426
9122
|
}),
|
|
8427
|
-
deleteSubagent:
|
|
8428
|
-
relativePathFromCwd:
|
|
9123
|
+
deleteSubagent: z32.object({
|
|
9124
|
+
relativePathFromCwd: z32.string()
|
|
8429
9125
|
})
|
|
8430
9126
|
};
|
|
8431
9127
|
var subagentTools = {
|
|
8432
9128
|
listSubagents: {
|
|
8433
9129
|
name: "listSubagents",
|
|
8434
|
-
description: `List all subagents from ${
|
|
9130
|
+
description: `List all subagents from ${join76(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, "*.md")} with their frontmatter.`,
|
|
8435
9131
|
parameters: subagentToolSchemas.listSubagents,
|
|
8436
9132
|
execute: async () => {
|
|
8437
9133
|
const subagents = await listSubagents();
|
|
@@ -8505,7 +9201,7 @@ async function mcpCommand({ version }) {
|
|
|
8505
9201
|
}
|
|
8506
9202
|
|
|
8507
9203
|
// src/cli/index.ts
|
|
8508
|
-
var getVersion = () => "3.
|
|
9204
|
+
var getVersion = () => "3.24.0";
|
|
8509
9205
|
var main = async () => {
|
|
8510
9206
|
const program = new Command();
|
|
8511
9207
|
const version = getVersion();
|