rulesync 7.18.2 → 7.20.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/dist/{chunk-JP3BFD7L.js → chunk-5OPNV62F.js} +152 -36
- package/dist/cli/index.cjs +601 -386
- package/dist/cli/index.js +459 -350
- package/dist/index.cjs +52 -37
- package/dist/index.js +1 -1
- package/package.json +2 -3
package/dist/cli/index.js
CHANGED
|
@@ -3,11 +3,16 @@ import {
|
|
|
3
3
|
ALL_FEATURES,
|
|
4
4
|
ALL_FEATURES_WITH_WILDCARD,
|
|
5
5
|
ALL_TOOL_TARGETS,
|
|
6
|
+
ALL_TOOL_TARGETS_WITH_WILDCARD,
|
|
7
|
+
CLIError,
|
|
6
8
|
CommandsProcessor,
|
|
7
9
|
ConfigResolver,
|
|
10
|
+
ConsoleLogger,
|
|
11
|
+
ErrorCodes,
|
|
8
12
|
FETCH_CONCURRENCY_LIMIT,
|
|
9
13
|
HooksProcessor,
|
|
10
14
|
IgnoreProcessor,
|
|
15
|
+
JsonLogger,
|
|
11
16
|
MAX_FILE_SIZE,
|
|
12
17
|
McpProcessor,
|
|
13
18
|
RULESYNC_AIIGNORE_FILE_NAME,
|
|
@@ -65,14 +70,11 @@ import {
|
|
|
65
70
|
removeTempDirectory,
|
|
66
71
|
stringifyFrontmatter,
|
|
67
72
|
writeFileContent
|
|
68
|
-
} from "../chunk-
|
|
73
|
+
} from "../chunk-5OPNV62F.js";
|
|
69
74
|
|
|
70
75
|
// src/cli/index.ts
|
|
71
76
|
import { Command } from "commander";
|
|
72
77
|
|
|
73
|
-
// src/constants/announcements.ts
|
|
74
|
-
var ANNOUNCEMENT = "".trim();
|
|
75
|
-
|
|
76
78
|
// src/lib/fetch.ts
|
|
77
79
|
import { join } from "path";
|
|
78
80
|
import { Semaphore } from "es-toolkit/promise";
|
|
@@ -979,30 +981,36 @@ function formatFetchSummary(summary) {
|
|
|
979
981
|
}
|
|
980
982
|
|
|
981
983
|
// src/cli/commands/fetch.ts
|
|
982
|
-
async function fetchCommand(options) {
|
|
984
|
+
async function fetchCommand(logger2, options) {
|
|
983
985
|
const { source, ...fetchOptions } = options;
|
|
984
|
-
|
|
985
|
-
verbose: fetchOptions.verbose ?? false,
|
|
986
|
-
silent: fetchOptions.silent ?? false
|
|
987
|
-
});
|
|
988
|
-
logger.debug(`Fetching files from ${source}...`);
|
|
986
|
+
logger2.debug(`Fetching files from ${source}...`);
|
|
989
987
|
try {
|
|
990
988
|
const summary = await fetchFiles({
|
|
991
989
|
source,
|
|
992
990
|
options: fetchOptions
|
|
993
991
|
});
|
|
992
|
+
if (logger2.jsonMode) {
|
|
993
|
+
const createdFiles = summary.files.filter((f) => f.status === "created").map((f) => f.relativePath);
|
|
994
|
+
const overwrittenFiles = summary.files.filter((f) => f.status === "overwritten").map((f) => f.relativePath);
|
|
995
|
+
const skippedFiles = summary.files.filter((f) => f.status === "skipped").map((f) => f.relativePath);
|
|
996
|
+
logger2.captureData("source", source);
|
|
997
|
+
logger2.captureData("path", fetchOptions.path);
|
|
998
|
+
logger2.captureData("created", createdFiles);
|
|
999
|
+
logger2.captureData("overwritten", overwrittenFiles);
|
|
1000
|
+
logger2.captureData("skipped", skippedFiles);
|
|
1001
|
+
logger2.captureData("totalFetched", summary.created + summary.overwritten + summary.skipped);
|
|
1002
|
+
}
|
|
994
1003
|
const output = formatFetchSummary(summary);
|
|
995
|
-
|
|
1004
|
+
logger2.success(output);
|
|
996
1005
|
if (summary.created + summary.overwritten === 0 && summary.skipped === 0) {
|
|
997
|
-
|
|
1006
|
+
logger2.warn("No files were fetched.");
|
|
998
1007
|
}
|
|
999
1008
|
} catch (error) {
|
|
1000
1009
|
if (error instanceof GitHubClientError) {
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
logger.error(formatError(error));
|
|
1010
|
+
const authHint = error.statusCode === 401 || error.statusCode === 403 ? " Tip: Set GITHUB_TOKEN or GH_TOKEN environment variable, or use `GITHUB_TOKEN=$(gh auth token) rulesync fetch ...`" : "";
|
|
1011
|
+
throw new CLIError(`GitHub API Error: ${error.message}.${authHint}`, ErrorCodes.FETCH_FAILED);
|
|
1004
1012
|
}
|
|
1005
|
-
|
|
1013
|
+
throw error;
|
|
1006
1014
|
}
|
|
1007
1015
|
}
|
|
1008
1016
|
|
|
@@ -1012,110 +1020,92 @@ function calculateTotalCount(result) {
|
|
|
1012
1020
|
}
|
|
1013
1021
|
|
|
1014
1022
|
// src/cli/commands/generate.ts
|
|
1015
|
-
function logFeatureResult(params) {
|
|
1023
|
+
function logFeatureResult(logger2, params) {
|
|
1016
1024
|
const { count, paths, featureName, isPreview, modePrefix } = params;
|
|
1017
1025
|
if (count > 0) {
|
|
1018
1026
|
if (isPreview) {
|
|
1019
|
-
|
|
1027
|
+
logger2.info(`${modePrefix} Would write ${count} ${featureName}`);
|
|
1020
1028
|
} else {
|
|
1021
|
-
|
|
1029
|
+
logger2.success(`Written ${count} ${featureName}`);
|
|
1022
1030
|
}
|
|
1023
1031
|
for (const p of paths) {
|
|
1024
|
-
|
|
1032
|
+
logger2.info(` ${p}`);
|
|
1025
1033
|
}
|
|
1026
1034
|
}
|
|
1027
1035
|
}
|
|
1028
|
-
async function generateCommand(options) {
|
|
1036
|
+
async function generateCommand(logger2, options) {
|
|
1029
1037
|
const config = await ConfigResolver.resolve(options);
|
|
1030
|
-
logger.configure({
|
|
1031
|
-
verbose: config.getVerbose(),
|
|
1032
|
-
silent: config.getSilent()
|
|
1033
|
-
});
|
|
1034
1038
|
const check = config.getCheck();
|
|
1035
1039
|
const isPreview = config.isPreviewMode();
|
|
1036
1040
|
const modePrefix = isPreview ? "[DRY RUN]" : "";
|
|
1037
|
-
|
|
1041
|
+
logger2.debug("Generating files...");
|
|
1038
1042
|
if (!await checkRulesyncDirExists({ baseDir: process.cwd() })) {
|
|
1039
|
-
|
|
1040
|
-
|
|
1043
|
+
throw new CLIError(
|
|
1044
|
+
".rulesync directory not found. Run 'rulesync init' first.",
|
|
1045
|
+
ErrorCodes.RULESYNC_DIR_NOT_FOUND
|
|
1046
|
+
);
|
|
1041
1047
|
}
|
|
1042
|
-
|
|
1048
|
+
logger2.debug(`Base directories: ${config.getBaseDirs().join(", ")}`);
|
|
1043
1049
|
const features = config.getFeatures();
|
|
1044
1050
|
if (features.includes("ignore")) {
|
|
1045
|
-
|
|
1051
|
+
logger2.debug("Generating ignore files...");
|
|
1046
1052
|
}
|
|
1047
1053
|
if (features.includes("mcp")) {
|
|
1048
|
-
|
|
1054
|
+
logger2.debug("Generating MCP files...");
|
|
1049
1055
|
}
|
|
1050
1056
|
if (features.includes("commands")) {
|
|
1051
|
-
|
|
1057
|
+
logger2.debug("Generating command files...");
|
|
1052
1058
|
}
|
|
1053
1059
|
if (features.includes("subagents")) {
|
|
1054
|
-
|
|
1060
|
+
logger2.debug("Generating subagent files...");
|
|
1055
1061
|
}
|
|
1056
1062
|
if (features.includes("skills")) {
|
|
1057
|
-
|
|
1063
|
+
logger2.debug("Generating skill files...");
|
|
1058
1064
|
}
|
|
1059
1065
|
if (features.includes("hooks")) {
|
|
1060
|
-
|
|
1066
|
+
logger2.debug("Generating hooks...");
|
|
1061
1067
|
}
|
|
1062
1068
|
if (features.includes("rules")) {
|
|
1063
|
-
|
|
1069
|
+
logger2.debug("Generating rule files...");
|
|
1064
1070
|
}
|
|
1065
1071
|
const result = await generate({ config });
|
|
1066
|
-
logFeatureResult({
|
|
1067
|
-
count: result.ignoreCount,
|
|
1068
|
-
paths: result.ignorePaths,
|
|
1069
|
-
featureName: "ignore file(s)",
|
|
1070
|
-
isPreview,
|
|
1071
|
-
modePrefix
|
|
1072
|
-
});
|
|
1073
|
-
logFeatureResult({
|
|
1074
|
-
count: result.mcpCount,
|
|
1075
|
-
paths: result.mcpPaths,
|
|
1076
|
-
featureName: "MCP configuration(s)",
|
|
1077
|
-
isPreview,
|
|
1078
|
-
modePrefix
|
|
1079
|
-
});
|
|
1080
|
-
logFeatureResult({
|
|
1081
|
-
count: result.commandsCount,
|
|
1082
|
-
paths: result.commandsPaths,
|
|
1083
|
-
featureName: "command(s)",
|
|
1084
|
-
isPreview,
|
|
1085
|
-
modePrefix
|
|
1086
|
-
});
|
|
1087
|
-
logFeatureResult({
|
|
1088
|
-
count: result.subagentsCount,
|
|
1089
|
-
paths: result.subagentsPaths,
|
|
1090
|
-
featureName: "subagent(s)",
|
|
1091
|
-
isPreview,
|
|
1092
|
-
modePrefix
|
|
1093
|
-
});
|
|
1094
|
-
logFeatureResult({
|
|
1095
|
-
count: result.skillsCount,
|
|
1096
|
-
paths: result.skillsPaths,
|
|
1097
|
-
featureName: "skill(s)",
|
|
1098
|
-
isPreview,
|
|
1099
|
-
modePrefix
|
|
1100
|
-
});
|
|
1101
|
-
logFeatureResult({
|
|
1102
|
-
count: result.hooksCount,
|
|
1103
|
-
paths: result.hooksPaths,
|
|
1104
|
-
featureName: "hooks file(s)",
|
|
1105
|
-
isPreview,
|
|
1106
|
-
modePrefix
|
|
1107
|
-
});
|
|
1108
|
-
logFeatureResult({
|
|
1109
|
-
count: result.rulesCount,
|
|
1110
|
-
paths: result.rulesPaths,
|
|
1111
|
-
featureName: "rule(s)",
|
|
1112
|
-
isPreview,
|
|
1113
|
-
modePrefix
|
|
1114
|
-
});
|
|
1115
1072
|
const totalGenerated = calculateTotalCount(result);
|
|
1073
|
+
const featureResults = {
|
|
1074
|
+
ignore: { count: result.ignoreCount, paths: result.ignorePaths },
|
|
1075
|
+
mcp: { count: result.mcpCount, paths: result.mcpPaths },
|
|
1076
|
+
commands: { count: result.commandsCount, paths: result.commandsPaths },
|
|
1077
|
+
subagents: { count: result.subagentsCount, paths: result.subagentsPaths },
|
|
1078
|
+
skills: { count: result.skillsCount, paths: result.skillsPaths },
|
|
1079
|
+
hooks: { count: result.hooksCount, paths: result.hooksPaths },
|
|
1080
|
+
rules: { count: result.rulesCount, paths: result.rulesPaths }
|
|
1081
|
+
};
|
|
1082
|
+
const featureLabels = {
|
|
1083
|
+
rules: (count) => `${count === 1 ? "rule" : "rules"}`,
|
|
1084
|
+
ignore: (count) => `${count === 1 ? "ignore file" : "ignore files"}`,
|
|
1085
|
+
mcp: (count) => `${count === 1 ? "MCP file" : "MCP files"}`,
|
|
1086
|
+
commands: (count) => `${count === 1 ? "command" : "commands"}`,
|
|
1087
|
+
subagents: (count) => `${count === 1 ? "subagent" : "subagents"}`,
|
|
1088
|
+
skills: (count) => `${count === 1 ? "skill" : "skills"}`,
|
|
1089
|
+
hooks: (count) => `${count === 1 ? "hooks file" : "hooks files"}`
|
|
1090
|
+
};
|
|
1091
|
+
for (const [feature, data] of Object.entries(featureResults)) {
|
|
1092
|
+
logFeatureResult(logger2, {
|
|
1093
|
+
count: data.count,
|
|
1094
|
+
paths: data.paths,
|
|
1095
|
+
featureName: featureLabels[feature]?.(data.count) ?? feature,
|
|
1096
|
+
isPreview,
|
|
1097
|
+
modePrefix
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
if (logger2.jsonMode) {
|
|
1101
|
+
logger2.captureData("features", featureResults);
|
|
1102
|
+
logger2.captureData("totalFiles", totalGenerated);
|
|
1103
|
+
logger2.captureData("hasDiff", result.hasDiff);
|
|
1104
|
+
logger2.captureData("skills", result.skills ?? []);
|
|
1105
|
+
}
|
|
1116
1106
|
if (totalGenerated === 0) {
|
|
1117
1107
|
const enabledFeatures = features.join(", ");
|
|
1118
|
-
|
|
1108
|
+
logger2.info(`\u2713 All files are up to date (${enabledFeatures})`);
|
|
1119
1109
|
return;
|
|
1120
1110
|
}
|
|
1121
1111
|
const parts = [];
|
|
@@ -1127,129 +1117,209 @@ async function generateCommand(options) {
|
|
|
1127
1117
|
if (result.skillsCount > 0) parts.push(`${result.skillsCount} skills`);
|
|
1128
1118
|
if (result.hooksCount > 0) parts.push(`${result.hooksCount} hooks`);
|
|
1129
1119
|
if (isPreview) {
|
|
1130
|
-
|
|
1120
|
+
logger2.info(`${modePrefix} Would write ${totalGenerated} file(s) total (${parts.join(" + ")})`);
|
|
1131
1121
|
} else {
|
|
1132
|
-
|
|
1122
|
+
logger2.success(`\u{1F389} All done! Written ${totalGenerated} file(s) total (${parts.join(" + ")})`);
|
|
1133
1123
|
}
|
|
1134
1124
|
if (check) {
|
|
1135
1125
|
if (result.hasDiff) {
|
|
1136
|
-
|
|
1137
|
-
|
|
1126
|
+
throw new CLIError(
|
|
1127
|
+
"Files are not up to date. Run 'rulesync generate' to update.",
|
|
1128
|
+
ErrorCodes.GENERATION_FAILED
|
|
1129
|
+
);
|
|
1138
1130
|
} else {
|
|
1139
|
-
|
|
1131
|
+
logger2.success("\u2713 All files are up to date.");
|
|
1140
1132
|
}
|
|
1141
1133
|
}
|
|
1142
1134
|
}
|
|
1143
1135
|
|
|
1144
1136
|
// src/cli/commands/gitignore.ts
|
|
1145
1137
|
import { join as join2 } from "path";
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
var
|
|
1149
|
-
//
|
|
1150
|
-
`${RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH}
|
|
1138
|
+
|
|
1139
|
+
// src/cli/commands/gitignore-entries.ts
|
|
1140
|
+
var GITIGNORE_ENTRY_REGISTRY = [
|
|
1141
|
+
// Common / general
|
|
1142
|
+
{ target: "common", feature: "general", entry: `${RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH}/` },
|
|
1143
|
+
{ target: "common", feature: "general", entry: ".rulesync/rules/*.local.md" },
|
|
1144
|
+
{ target: "common", feature: "general", entry: "rulesync.local.jsonc" },
|
|
1145
|
+
{ target: "common", feature: "general", entry: "!.rulesync/.aiignore" },
|
|
1151
1146
|
// AGENTS.md
|
|
1152
|
-
"**/AGENTS.md",
|
|
1153
|
-
"**/.agents/",
|
|
1154
|
-
// Augment
|
|
1155
|
-
"**/.
|
|
1156
|
-
"**/.augment
|
|
1157
|
-
"**/.
|
|
1147
|
+
{ target: "agentsmd", feature: "rules", entry: "**/AGENTS.md" },
|
|
1148
|
+
{ target: "agentsmd", feature: "rules", entry: "**/.agents/" },
|
|
1149
|
+
// Augment Code
|
|
1150
|
+
{ target: "augmentcode", feature: "rules", entry: "**/.augment/rules/" },
|
|
1151
|
+
{ target: "augmentcode", feature: "rules", entry: "**/.augment-guidelines" },
|
|
1152
|
+
{ target: "augmentcode", feature: "ignore", entry: "**/.augmentignore" },
|
|
1158
1153
|
// Claude Code
|
|
1159
|
-
"**/CLAUDE.md",
|
|
1160
|
-
"**/CLAUDE.local.md",
|
|
1161
|
-
"**/.claude/CLAUDE.md",
|
|
1162
|
-
"**/.claude/CLAUDE.local.md",
|
|
1163
|
-
"**/.claude/
|
|
1164
|
-
"**/.claude/
|
|
1165
|
-
"**/.claude/
|
|
1166
|
-
"**/.claude/
|
|
1167
|
-
"**/.
|
|
1168
|
-
"**/.claude/
|
|
1169
|
-
"**/.
|
|
1154
|
+
{ target: "claudecode", feature: "rules", entry: "**/CLAUDE.md" },
|
|
1155
|
+
{ target: "claudecode", feature: "rules", entry: "**/CLAUDE.local.md" },
|
|
1156
|
+
{ target: "claudecode", feature: "rules", entry: "**/.claude/CLAUDE.md" },
|
|
1157
|
+
{ target: "claudecode", feature: "rules", entry: "**/.claude/CLAUDE.local.md" },
|
|
1158
|
+
{ target: "claudecode", feature: "rules", entry: "**/.claude/rules/" },
|
|
1159
|
+
{ target: "claudecode", feature: "commands", entry: "**/.claude/commands/" },
|
|
1160
|
+
{ target: "claudecode", feature: "subagents", entry: "**/.claude/agents/" },
|
|
1161
|
+
{ target: "claudecode", feature: "skills", entry: "**/.claude/skills/" },
|
|
1162
|
+
{ target: "claudecode", feature: "mcp", entry: "**/.mcp.json" },
|
|
1163
|
+
{ target: "claudecode", feature: "general", entry: "**/.claude/memories/" },
|
|
1164
|
+
{ target: "claudecode", feature: "general", entry: "**/.claude/settings.local.json" },
|
|
1170
1165
|
// Cline
|
|
1171
|
-
"**/.clinerules/",
|
|
1172
|
-
"**/.clinerules/workflows/",
|
|
1173
|
-
"**/.clineignore",
|
|
1174
|
-
"**/.cline/mcp.json",
|
|
1175
|
-
// Codex
|
|
1176
|
-
"**/.codexignore",
|
|
1177
|
-
"**/.codex/
|
|
1178
|
-
"**/.codex/
|
|
1179
|
-
"**/.codex/
|
|
1180
|
-
"**/.codex/config.toml",
|
|
1166
|
+
{ target: "cline", feature: "rules", entry: "**/.clinerules/" },
|
|
1167
|
+
{ target: "cline", feature: "commands", entry: "**/.clinerules/workflows/" },
|
|
1168
|
+
{ target: "cline", feature: "ignore", entry: "**/.clineignore" },
|
|
1169
|
+
{ target: "cline", feature: "mcp", entry: "**/.cline/mcp.json" },
|
|
1170
|
+
// Codex CLI
|
|
1171
|
+
{ target: "codexcli", feature: "ignore", entry: "**/.codexignore" },
|
|
1172
|
+
{ target: "codexcli", feature: "skills", entry: "**/.codex/skills/" },
|
|
1173
|
+
{ target: "codexcli", feature: "subagents", entry: "**/.codex/agents/" },
|
|
1174
|
+
{ target: "codexcli", feature: "general", entry: "**/.codex/memories/" },
|
|
1175
|
+
{ target: "codexcli", feature: "general", entry: "**/.codex/config.toml" },
|
|
1181
1176
|
// Cursor
|
|
1182
|
-
"**/.cursor/",
|
|
1183
|
-
"**/.cursorignore",
|
|
1177
|
+
{ target: "cursor", feature: "rules", entry: "**/.cursor/" },
|
|
1178
|
+
{ target: "cursor", feature: "ignore", entry: "**/.cursorignore" },
|
|
1184
1179
|
// Factory Droid
|
|
1185
|
-
"**/.factory/rules/",
|
|
1186
|
-
"**/.factory/commands/",
|
|
1187
|
-
"**/.factory/droids/",
|
|
1188
|
-
"**/.factory/skills/",
|
|
1189
|
-
"**/.factory/mcp.json",
|
|
1190
|
-
"**/.factory/settings.json",
|
|
1191
|
-
// Gemini
|
|
1192
|
-
"**/GEMINI.md",
|
|
1193
|
-
"**/.gemini/
|
|
1194
|
-
"**/.gemini/
|
|
1195
|
-
"**/.gemini/
|
|
1196
|
-
"**/.
|
|
1197
|
-
"**/.
|
|
1180
|
+
{ target: "factorydroid", feature: "rules", entry: "**/.factory/rules/" },
|
|
1181
|
+
{ target: "factorydroid", feature: "commands", entry: "**/.factory/commands/" },
|
|
1182
|
+
{ target: "factorydroid", feature: "subagents", entry: "**/.factory/droids/" },
|
|
1183
|
+
{ target: "factorydroid", feature: "skills", entry: "**/.factory/skills/" },
|
|
1184
|
+
{ target: "factorydroid", feature: "mcp", entry: "**/.factory/mcp.json" },
|
|
1185
|
+
{ target: "factorydroid", feature: "general", entry: "**/.factory/settings.json" },
|
|
1186
|
+
// Gemini CLI
|
|
1187
|
+
{ target: "geminicli", feature: "rules", entry: "**/GEMINI.md" },
|
|
1188
|
+
{ target: "geminicli", feature: "commands", entry: "**/.gemini/commands/" },
|
|
1189
|
+
{ target: "geminicli", feature: "subagents", entry: "**/.gemini/subagents/" },
|
|
1190
|
+
{ target: "geminicli", feature: "skills", entry: "**/.gemini/skills/" },
|
|
1191
|
+
{ target: "geminicli", feature: "ignore", entry: "**/.geminiignore" },
|
|
1192
|
+
{ target: "geminicli", feature: "general", entry: "**/.gemini/memories/" },
|
|
1198
1193
|
// Goose
|
|
1199
|
-
"**/.goosehints",
|
|
1200
|
-
"**/.goose/",
|
|
1201
|
-
"**/.gooseignore",
|
|
1194
|
+
{ target: "goose", feature: "rules", entry: "**/.goosehints" },
|
|
1195
|
+
{ target: "goose", feature: "rules", entry: "**/.goose/" },
|
|
1196
|
+
{ target: "goose", feature: "ignore", entry: "**/.gooseignore" },
|
|
1202
1197
|
// GitHub Copilot
|
|
1203
|
-
"**/.github/copilot-instructions.md",
|
|
1204
|
-
"**/.github/instructions/",
|
|
1205
|
-
"**/.github/prompts/",
|
|
1206
|
-
"**/.github/agents/",
|
|
1207
|
-
"**/.github/skills/",
|
|
1208
|
-
"**/.github/hooks/",
|
|
1209
|
-
"**/.vscode/mcp.json",
|
|
1198
|
+
{ target: "copilot", feature: "rules", entry: "**/.github/copilot-instructions.md" },
|
|
1199
|
+
{ target: "copilot", feature: "rules", entry: "**/.github/instructions/" },
|
|
1200
|
+
{ target: "copilot", feature: "commands", entry: "**/.github/prompts/" },
|
|
1201
|
+
{ target: "copilot", feature: "subagents", entry: "**/.github/agents/" },
|
|
1202
|
+
{ target: "copilot", feature: "skills", entry: "**/.github/skills/" },
|
|
1203
|
+
{ target: "copilot", feature: "hooks", entry: "**/.github/hooks/" },
|
|
1204
|
+
{ target: "copilot", feature: "mcp", entry: "**/.vscode/mcp.json" },
|
|
1210
1205
|
// Junie
|
|
1211
|
-
"**/.junie/guidelines.md",
|
|
1212
|
-
"**/.junie/mcp.json",
|
|
1213
|
-
"**/.junie/skills/",
|
|
1214
|
-
"**/.junie/agents/",
|
|
1206
|
+
{ target: "junie", feature: "rules", entry: "**/.junie/guidelines.md" },
|
|
1207
|
+
{ target: "junie", feature: "mcp", entry: "**/.junie/mcp.json" },
|
|
1208
|
+
{ target: "junie", feature: "skills", entry: "**/.junie/skills/" },
|
|
1209
|
+
{ target: "junie", feature: "subagents", entry: "**/.junie/agents/" },
|
|
1215
1210
|
// Kilo Code
|
|
1216
|
-
"**/.kilocode/rules/",
|
|
1217
|
-
"**/.kilocode/skills/",
|
|
1218
|
-
"**/.kilocode/workflows/",
|
|
1219
|
-
"**/.kilocode/mcp.json",
|
|
1220
|
-
"**/.kilocodeignore",
|
|
1211
|
+
{ target: "kilo", feature: "rules", entry: "**/.kilocode/rules/" },
|
|
1212
|
+
{ target: "kilo", feature: "skills", entry: "**/.kilocode/skills/" },
|
|
1213
|
+
{ target: "kilo", feature: "commands", entry: "**/.kilocode/workflows/" },
|
|
1214
|
+
{ target: "kilo", feature: "mcp", entry: "**/.kilocode/mcp.json" },
|
|
1215
|
+
{ target: "kilo", feature: "ignore", entry: "**/.kilocodeignore" },
|
|
1221
1216
|
// Kiro
|
|
1222
|
-
"**/.kiro/steering/",
|
|
1223
|
-
"**/.kiro/prompts/",
|
|
1224
|
-
"**/.kiro/skills/",
|
|
1225
|
-
"**/.kiro/agents/",
|
|
1226
|
-
"**/.kiro/settings/mcp.json",
|
|
1227
|
-
"**/.aiignore",
|
|
1217
|
+
{ target: "kiro", feature: "rules", entry: "**/.kiro/steering/" },
|
|
1218
|
+
{ target: "kiro", feature: "commands", entry: "**/.kiro/prompts/" },
|
|
1219
|
+
{ target: "kiro", feature: "skills", entry: "**/.kiro/skills/" },
|
|
1220
|
+
{ target: "kiro", feature: "subagents", entry: "**/.kiro/agents/" },
|
|
1221
|
+
{ target: "kiro", feature: "mcp", entry: "**/.kiro/settings/mcp.json" },
|
|
1222
|
+
{ target: "kiro", feature: "ignore", entry: "**/.aiignore" },
|
|
1228
1223
|
// OpenCode
|
|
1229
|
-
"**/.opencode/
|
|
1230
|
-
"**/.opencode/
|
|
1231
|
-
"**/.opencode/
|
|
1232
|
-
"**/.opencode/
|
|
1233
|
-
"**/.opencode/
|
|
1234
|
-
// Qwen
|
|
1235
|
-
"**/QWEN.md",
|
|
1236
|
-
"**/.qwen/memories/",
|
|
1224
|
+
{ target: "opencode", feature: "commands", entry: "**/.opencode/command/" },
|
|
1225
|
+
{ target: "opencode", feature: "subagents", entry: "**/.opencode/agent/" },
|
|
1226
|
+
{ target: "opencode", feature: "skills", entry: "**/.opencode/skill/" },
|
|
1227
|
+
{ target: "opencode", feature: "mcp", entry: "**/.opencode/plugins/" },
|
|
1228
|
+
{ target: "opencode", feature: "general", entry: "**/.opencode/memories/" },
|
|
1229
|
+
// Qwen Code
|
|
1230
|
+
{ target: "qwencode", feature: "rules", entry: "**/QWEN.md" },
|
|
1231
|
+
{ target: "qwencode", feature: "general", entry: "**/.qwen/memories/" },
|
|
1237
1232
|
// Replit
|
|
1238
|
-
"**/replit.md",
|
|
1233
|
+
{ target: "replit", feature: "rules", entry: "**/replit.md" },
|
|
1239
1234
|
// Roo
|
|
1240
|
-
"**/.roo/rules/",
|
|
1241
|
-
"**/.roo/skills/",
|
|
1242
|
-
"**/.rooignore",
|
|
1243
|
-
"**/.roo/mcp.json",
|
|
1244
|
-
"**/.roo/subagents/",
|
|
1235
|
+
{ target: "roo", feature: "rules", entry: "**/.roo/rules/" },
|
|
1236
|
+
{ target: "roo", feature: "skills", entry: "**/.roo/skills/" },
|
|
1237
|
+
{ target: "roo", feature: "ignore", entry: "**/.rooignore" },
|
|
1238
|
+
{ target: "roo", feature: "mcp", entry: "**/.roo/mcp.json" },
|
|
1239
|
+
{ target: "roo", feature: "subagents", entry: "**/.roo/subagents/" },
|
|
1245
1240
|
// Warp
|
|
1246
|
-
"**/.warp/",
|
|
1247
|
-
"**/WARP.md"
|
|
1248
|
-
// Others
|
|
1249
|
-
".rulesync/rules/*.local.md",
|
|
1250
|
-
"rulesync.local.jsonc",
|
|
1251
|
-
"!.rulesync/.aiignore"
|
|
1241
|
+
{ target: "warp", feature: "rules", entry: "**/.warp/" },
|
|
1242
|
+
{ target: "warp", feature: "rules", entry: "**/WARP.md" }
|
|
1252
1243
|
];
|
|
1244
|
+
var ALL_GITIGNORE_ENTRIES = GITIGNORE_ENTRY_REGISTRY.map(
|
|
1245
|
+
(tag) => tag.entry
|
|
1246
|
+
);
|
|
1247
|
+
var isTargetSelected = (target, selectedTargets) => {
|
|
1248
|
+
if (target === "common") return true;
|
|
1249
|
+
if (!selectedTargets || selectedTargets.length === 0) return true;
|
|
1250
|
+
if (selectedTargets.includes("*")) return true;
|
|
1251
|
+
return selectedTargets.includes(target);
|
|
1252
|
+
};
|
|
1253
|
+
var isFeatureSelected = (feature, target, features) => {
|
|
1254
|
+
if (feature === "general") return true;
|
|
1255
|
+
if (!features) return true;
|
|
1256
|
+
if (Array.isArray(features)) {
|
|
1257
|
+
if (features.length === 0) return true;
|
|
1258
|
+
if (features.includes("*")) return true;
|
|
1259
|
+
return features.includes(feature);
|
|
1260
|
+
}
|
|
1261
|
+
if (target === "common") return true;
|
|
1262
|
+
const targetFeatures = features[target];
|
|
1263
|
+
if (!targetFeatures) return true;
|
|
1264
|
+
if (targetFeatures.includes("*")) return true;
|
|
1265
|
+
return targetFeatures.includes(feature);
|
|
1266
|
+
};
|
|
1267
|
+
var warnInvalidTargets = (targets) => {
|
|
1268
|
+
const validTargets = new Set(ALL_TOOL_TARGETS_WITH_WILDCARD);
|
|
1269
|
+
for (const target of targets) {
|
|
1270
|
+
if (!validTargets.has(target)) {
|
|
1271
|
+
logger.warn(
|
|
1272
|
+
`Unknown target '${target}'. Valid targets: ${ALL_TOOL_TARGETS_WITH_WILDCARD.join(", ")}`
|
|
1273
|
+
);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
};
|
|
1277
|
+
var warnInvalidFeatures = (features) => {
|
|
1278
|
+
const validFeatures = new Set(ALL_FEATURES_WITH_WILDCARD);
|
|
1279
|
+
if (Array.isArray(features)) {
|
|
1280
|
+
for (const feature of features) {
|
|
1281
|
+
if (!validFeatures.has(feature)) {
|
|
1282
|
+
logger.warn(
|
|
1283
|
+
`Unknown feature '${feature}'. Valid features: ${ALL_FEATURES_WITH_WILDCARD.join(", ")}`
|
|
1284
|
+
);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
} else {
|
|
1288
|
+
for (const targetFeatures of Object.values(features)) {
|
|
1289
|
+
if (!targetFeatures) continue;
|
|
1290
|
+
for (const feature of targetFeatures) {
|
|
1291
|
+
if (!validFeatures.has(feature)) {
|
|
1292
|
+
logger.warn(
|
|
1293
|
+
`Unknown feature '${feature}'. Valid features: ${ALL_FEATURES_WITH_WILDCARD.join(", ")}`
|
|
1294
|
+
);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
};
|
|
1300
|
+
var filterGitignoreEntries = (params) => {
|
|
1301
|
+
const { targets, features } = params ?? {};
|
|
1302
|
+
if (targets && targets.length > 0) {
|
|
1303
|
+
warnInvalidTargets(targets);
|
|
1304
|
+
}
|
|
1305
|
+
if (features) {
|
|
1306
|
+
warnInvalidFeatures(features);
|
|
1307
|
+
}
|
|
1308
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1309
|
+
const result = [];
|
|
1310
|
+
for (const tag of GITIGNORE_ENTRY_REGISTRY) {
|
|
1311
|
+
if (!isTargetSelected(tag.target, targets)) continue;
|
|
1312
|
+
if (!isFeatureSelected(tag.feature, tag.target, features)) continue;
|
|
1313
|
+
if (seen.has(tag.entry)) continue;
|
|
1314
|
+
seen.add(tag.entry);
|
|
1315
|
+
result.push(tag.entry);
|
|
1316
|
+
}
|
|
1317
|
+
return result;
|
|
1318
|
+
};
|
|
1319
|
+
|
|
1320
|
+
// src/cli/commands/gitignore.ts
|
|
1321
|
+
var RULESYNC_HEADER = "# Generated by Rulesync";
|
|
1322
|
+
var LEGACY_RULESYNC_HEADER = "# Generated by rulesync - AI tool configuration files";
|
|
1253
1323
|
var isRulesyncHeader = (line) => {
|
|
1254
1324
|
const trimmed = line.trim();
|
|
1255
1325
|
return trimmed === RULESYNC_HEADER || trimmed === LEGACY_RULESYNC_HEADER;
|
|
@@ -1259,7 +1329,7 @@ var isRulesyncEntry = (line) => {
|
|
|
1259
1329
|
if (trimmed === "" || isRulesyncHeader(line)) {
|
|
1260
1330
|
return false;
|
|
1261
1331
|
}
|
|
1262
|
-
return
|
|
1332
|
+
return ALL_GITIGNORE_ENTRIES.includes(trimmed);
|
|
1263
1333
|
};
|
|
1264
1334
|
var removeExistingRulesyncEntries = (content) => {
|
|
1265
1335
|
const lines = content.split("\n");
|
|
@@ -1299,63 +1369,89 @@ var removeExistingRulesyncEntries = (content) => {
|
|
|
1299
1369
|
}
|
|
1300
1370
|
return result;
|
|
1301
1371
|
};
|
|
1302
|
-
var gitignoreCommand = async () => {
|
|
1372
|
+
var gitignoreCommand = async (logger2, options) => {
|
|
1303
1373
|
const gitignorePath = join2(process.cwd(), ".gitignore");
|
|
1304
1374
|
let gitignoreContent = "";
|
|
1305
1375
|
if (await fileExists(gitignorePath)) {
|
|
1306
1376
|
gitignoreContent = await readFileContent(gitignorePath);
|
|
1307
1377
|
}
|
|
1308
1378
|
const cleanedContent = removeExistingRulesyncEntries(gitignoreContent);
|
|
1309
|
-
const
|
|
1379
|
+
const filteredEntries = filterGitignoreEntries({
|
|
1380
|
+
targets: options?.targets,
|
|
1381
|
+
features: options?.features
|
|
1382
|
+
});
|
|
1383
|
+
const existingEntries = new Set(
|
|
1384
|
+
gitignoreContent.split("\n").map((line) => line.trim()).filter((line) => line !== "" && !isRulesyncHeader(line))
|
|
1385
|
+
);
|
|
1386
|
+
const alreadyExistedEntries = filteredEntries.filter((entry) => existingEntries.has(entry));
|
|
1387
|
+
const entriesToAdd = filteredEntries.filter((entry) => !existingEntries.has(entry));
|
|
1388
|
+
const rulesyncBlock = [RULESYNC_HEADER, ...filteredEntries].join("\n");
|
|
1310
1389
|
const newContent = cleanedContent.trim() ? `${cleanedContent.trimEnd()}
|
|
1311
1390
|
|
|
1312
1391
|
${rulesyncBlock}
|
|
1313
1392
|
` : `${rulesyncBlock}
|
|
1314
1393
|
`;
|
|
1315
1394
|
if (gitignoreContent === newContent) {
|
|
1316
|
-
|
|
1395
|
+
if (logger2.jsonMode) {
|
|
1396
|
+
logger2.captureData("entriesAdded", []);
|
|
1397
|
+
logger2.captureData("gitignorePath", gitignorePath);
|
|
1398
|
+
logger2.captureData("alreadyExisted", filteredEntries);
|
|
1399
|
+
}
|
|
1400
|
+
logger2.success(".gitignore is already up to date");
|
|
1317
1401
|
return;
|
|
1318
1402
|
}
|
|
1319
1403
|
await writeFileContent(gitignorePath, newContent);
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1404
|
+
if (logger2.jsonMode) {
|
|
1405
|
+
logger2.captureData("entriesAdded", entriesToAdd);
|
|
1406
|
+
logger2.captureData("gitignorePath", gitignorePath);
|
|
1407
|
+
logger2.captureData("alreadyExisted", alreadyExistedEntries);
|
|
1323
1408
|
}
|
|
1324
|
-
|
|
1325
|
-
|
|
1409
|
+
logger2.success("Updated .gitignore with rulesync entries:");
|
|
1410
|
+
for (const entry of filteredEntries) {
|
|
1411
|
+
logger2.info(` ${entry}`);
|
|
1412
|
+
}
|
|
1413
|
+
logger2.info("");
|
|
1414
|
+
logger2.info(
|
|
1326
1415
|
"\u{1F4A1} If you're using Google Antigravity, note that rules, workflows, and skills won't load if they're gitignored."
|
|
1327
1416
|
);
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1417
|
+
logger2.info(" You can add the following to .git/info/exclude instead:");
|
|
1418
|
+
logger2.info(" **/.agent/rules/");
|
|
1419
|
+
logger2.info(" **/.agent/workflows/");
|
|
1420
|
+
logger2.info(" **/.agent/skills/");
|
|
1421
|
+
logger2.info(" For more details: https://github.com/dyoshikawa/rulesync/issues/981");
|
|
1333
1422
|
};
|
|
1334
1423
|
|
|
1335
1424
|
// src/cli/commands/import.ts
|
|
1336
|
-
async function importCommand(options) {
|
|
1425
|
+
async function importCommand(logger2, options) {
|
|
1337
1426
|
if (!options.targets) {
|
|
1338
|
-
|
|
1339
|
-
process.exit(1);
|
|
1427
|
+
throw new CLIError("No tools found in --targets", ErrorCodes.IMPORT_FAILED);
|
|
1340
1428
|
}
|
|
1341
1429
|
if (options.targets.length > 1) {
|
|
1342
|
-
|
|
1343
|
-
process.exit(1);
|
|
1430
|
+
throw new CLIError("Only one tool can be imported at a time", ErrorCodes.IMPORT_FAILED);
|
|
1344
1431
|
}
|
|
1345
1432
|
const config = await ConfigResolver.resolve(options);
|
|
1346
|
-
logger.configure({
|
|
1347
|
-
verbose: config.getVerbose(),
|
|
1348
|
-
silent: config.getSilent()
|
|
1349
|
-
});
|
|
1350
1433
|
const tool = config.getTargets()[0];
|
|
1351
|
-
|
|
1434
|
+
logger2.debug(`Importing files from ${tool}...`);
|
|
1352
1435
|
const result = await importFromTool({ config, tool });
|
|
1353
1436
|
const totalImported = calculateTotalCount(result);
|
|
1354
1437
|
if (totalImported === 0) {
|
|
1355
1438
|
const enabledFeatures = config.getFeatures().join(", ");
|
|
1356
|
-
|
|
1439
|
+
logger2.warn(`No files imported for enabled features: ${enabledFeatures}`);
|
|
1357
1440
|
return;
|
|
1358
1441
|
}
|
|
1442
|
+
if (logger2.jsonMode) {
|
|
1443
|
+
logger2.captureData("tool", tool);
|
|
1444
|
+
logger2.captureData("features", {
|
|
1445
|
+
rules: { count: result.rulesCount },
|
|
1446
|
+
ignore: { count: result.ignoreCount },
|
|
1447
|
+
mcp: { count: result.mcpCount },
|
|
1448
|
+
commands: { count: result.commandsCount },
|
|
1449
|
+
subagents: { count: result.subagentsCount },
|
|
1450
|
+
skills: { count: result.skillsCount },
|
|
1451
|
+
hooks: { count: result.hooksCount }
|
|
1452
|
+
});
|
|
1453
|
+
logger2.captureData("totalFiles", totalImported);
|
|
1454
|
+
}
|
|
1359
1455
|
const parts = [];
|
|
1360
1456
|
if (result.rulesCount > 0) parts.push(`${result.rulesCount} rules`);
|
|
1361
1457
|
if (result.ignoreCount > 0) parts.push(`${result.ignoreCount} ignore files`);
|
|
@@ -1364,7 +1460,7 @@ async function importCommand(options) {
|
|
|
1364
1460
|
if (result.subagentsCount > 0) parts.push(`${result.subagentsCount} subagents`);
|
|
1365
1461
|
if (result.skillsCount > 0) parts.push(`${result.skillsCount} skills`);
|
|
1366
1462
|
if (result.hooksCount > 0) parts.push(`${result.hooksCount} hooks`);
|
|
1367
|
-
|
|
1463
|
+
logger2.success(`Imported ${totalImported} file(s) total (${parts.join(" + ")})`);
|
|
1368
1464
|
}
|
|
1369
1465
|
|
|
1370
1466
|
// src/lib/init.ts
|
|
@@ -1593,28 +1689,38 @@ async function writeIfNotExists(path2, content) {
|
|
|
1593
1689
|
}
|
|
1594
1690
|
|
|
1595
1691
|
// src/cli/commands/init.ts
|
|
1596
|
-
async function initCommand() {
|
|
1597
|
-
|
|
1692
|
+
async function initCommand(logger2) {
|
|
1693
|
+
logger2.debug("Initializing rulesync...");
|
|
1598
1694
|
await ensureDir(RULESYNC_RELATIVE_DIR_PATH);
|
|
1599
1695
|
const result = await init();
|
|
1696
|
+
const createdFiles = [];
|
|
1697
|
+
const skippedFiles = [];
|
|
1600
1698
|
for (const file of result.sampleFiles) {
|
|
1601
1699
|
if (file.created) {
|
|
1602
|
-
|
|
1700
|
+
createdFiles.push(file.path);
|
|
1701
|
+
logger2.success(`Created ${file.path}`);
|
|
1603
1702
|
} else {
|
|
1604
|
-
|
|
1703
|
+
skippedFiles.push(file.path);
|
|
1704
|
+
logger2.info(`Skipped ${file.path} (already exists)`);
|
|
1605
1705
|
}
|
|
1606
1706
|
}
|
|
1607
1707
|
if (result.configFile.created) {
|
|
1608
|
-
|
|
1708
|
+
createdFiles.push(result.configFile.path);
|
|
1709
|
+
logger2.success(`Created ${result.configFile.path}`);
|
|
1609
1710
|
} else {
|
|
1610
|
-
|
|
1711
|
+
skippedFiles.push(result.configFile.path);
|
|
1712
|
+
logger2.info(`Skipped ${result.configFile.path} (already exists)`);
|
|
1611
1713
|
}
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1714
|
+
if (logger2.jsonMode) {
|
|
1715
|
+
logger2.captureData("created", createdFiles);
|
|
1716
|
+
logger2.captureData("skipped", skippedFiles);
|
|
1717
|
+
}
|
|
1718
|
+
logger2.success("rulesync initialized successfully!");
|
|
1719
|
+
logger2.info("Next steps:");
|
|
1720
|
+
logger2.info(
|
|
1615
1721
|
`1. Edit ${RULESYNC_RELATIVE_DIR_PATH}/**/*.md, ${RULESYNC_RELATIVE_DIR_PATH}/skills/*/${SKILL_FILE_NAME}, ${RULESYNC_MCP_RELATIVE_FILE_PATH}, ${RULESYNC_HOOKS_RELATIVE_FILE_PATH} and ${RULESYNC_AIIGNORE_RELATIVE_FILE_PATH}`
|
|
1616
1722
|
);
|
|
1617
|
-
|
|
1723
|
+
logger2.info("2. Run 'rulesync generate' to create configuration files");
|
|
1618
1724
|
}
|
|
1619
1725
|
|
|
1620
1726
|
// src/lib/sources.ts
|
|
@@ -2340,11 +2446,7 @@ async function fetchSourceViaGit(params) {
|
|
|
2340
2446
|
}
|
|
2341
2447
|
|
|
2342
2448
|
// src/cli/commands/install.ts
|
|
2343
|
-
async function installCommand(options) {
|
|
2344
|
-
logger.configure({
|
|
2345
|
-
verbose: options.verbose ?? false,
|
|
2346
|
-
silent: options.silent ?? false
|
|
2347
|
-
});
|
|
2449
|
+
async function installCommand(logger2, options) {
|
|
2348
2450
|
const config = await ConfigResolver.resolve({
|
|
2349
2451
|
configPath: options.configPath,
|
|
2350
2452
|
verbose: options.verbose,
|
|
@@ -2352,10 +2454,10 @@ async function installCommand(options) {
|
|
|
2352
2454
|
});
|
|
2353
2455
|
const sources = config.getSources();
|
|
2354
2456
|
if (sources.length === 0) {
|
|
2355
|
-
|
|
2457
|
+
logger2.warn("No sources defined in configuration. Nothing to install.");
|
|
2356
2458
|
return;
|
|
2357
2459
|
}
|
|
2358
|
-
|
|
2460
|
+
logger2.debug(`Installing skills from ${sources.length} source(s)...`);
|
|
2359
2461
|
const result = await resolveAndFetchSources({
|
|
2360
2462
|
sources,
|
|
2361
2463
|
baseDir: process.cwd(),
|
|
@@ -2365,12 +2467,16 @@ async function installCommand(options) {
|
|
|
2365
2467
|
token: options.token
|
|
2366
2468
|
}
|
|
2367
2469
|
});
|
|
2470
|
+
if (logger2.jsonMode) {
|
|
2471
|
+
logger2.captureData("sourcesProcessed", result.sourcesProcessed);
|
|
2472
|
+
logger2.captureData("skillsFetched", result.fetchedSkillCount);
|
|
2473
|
+
}
|
|
2368
2474
|
if (result.fetchedSkillCount > 0) {
|
|
2369
|
-
|
|
2475
|
+
logger2.success(
|
|
2370
2476
|
`Installed ${result.fetchedSkillCount} skill(s) from ${result.sourcesProcessed} source(s).`
|
|
2371
2477
|
);
|
|
2372
2478
|
} else {
|
|
2373
|
-
|
|
2479
|
+
logger2.success(`All skills up to date (${result.sourcesProcessed} source(s) checked).`);
|
|
2374
2480
|
}
|
|
2375
2481
|
}
|
|
2376
2482
|
|
|
@@ -3785,7 +3891,7 @@ var rulesyncTool = {
|
|
|
3785
3891
|
};
|
|
3786
3892
|
|
|
3787
3893
|
// src/cli/commands/mcp.ts
|
|
3788
|
-
async function mcpCommand({ version }) {
|
|
3894
|
+
async function mcpCommand(logger2, { version }) {
|
|
3789
3895
|
const server = new FastMCP({
|
|
3790
3896
|
name: "Rulesync MCP Server",
|
|
3791
3897
|
// eslint-disable-next-line no-type-assertion/no-type-assertion
|
|
@@ -3793,7 +3899,7 @@ async function mcpCommand({ version }) {
|
|
|
3793
3899
|
instructions: "This server handles Rulesync files including rules, commands, MCP, ignore files, subagents and skills for any AI agents. It should be used when you need those files."
|
|
3794
3900
|
});
|
|
3795
3901
|
server.addTool(rulesyncTool);
|
|
3796
|
-
|
|
3902
|
+
logger2.info("Rulesync MCP server started via stdio");
|
|
3797
3903
|
void server.start({
|
|
3798
3904
|
transportType: "stdio"
|
|
3799
3905
|
});
|
|
@@ -4109,158 +4215,182 @@ To upgrade, run:
|
|
|
4109
4215
|
}
|
|
4110
4216
|
|
|
4111
4217
|
// src/cli/commands/update.ts
|
|
4112
|
-
async function updateCommand(currentVersion, options) {
|
|
4113
|
-
const { check = false, force = false,
|
|
4114
|
-
logger.configure({ verbose, silent });
|
|
4218
|
+
async function updateCommand(logger2, currentVersion, options) {
|
|
4219
|
+
const { check = false, force = false, token } = options;
|
|
4115
4220
|
try {
|
|
4116
4221
|
const environment = detectExecutionEnvironment();
|
|
4117
|
-
|
|
4222
|
+
logger2.debug(`Detected environment: ${environment}`);
|
|
4118
4223
|
if (environment === "npm") {
|
|
4119
|
-
|
|
4224
|
+
logger2.info(getNpmUpgradeInstructions());
|
|
4120
4225
|
return;
|
|
4121
4226
|
}
|
|
4122
4227
|
if (environment === "homebrew") {
|
|
4123
|
-
|
|
4228
|
+
logger2.info(getHomebrewUpgradeInstructions());
|
|
4124
4229
|
return;
|
|
4125
4230
|
}
|
|
4126
4231
|
if (check) {
|
|
4127
|
-
|
|
4232
|
+
logger2.info("Checking for updates...");
|
|
4128
4233
|
const updateCheck = await checkForUpdate(currentVersion, token);
|
|
4234
|
+
if (logger2.jsonMode) {
|
|
4235
|
+
logger2.captureData("currentVersion", updateCheck.currentVersion);
|
|
4236
|
+
logger2.captureData("latestVersion", updateCheck.latestVersion);
|
|
4237
|
+
logger2.captureData("updateAvailable", updateCheck.hasUpdate);
|
|
4238
|
+
logger2.captureData(
|
|
4239
|
+
"message",
|
|
4240
|
+
updateCheck.hasUpdate ? `Update available: ${updateCheck.currentVersion} -> ${updateCheck.latestVersion}` : `Already at the latest version (${updateCheck.currentVersion})`
|
|
4241
|
+
);
|
|
4242
|
+
}
|
|
4129
4243
|
if (updateCheck.hasUpdate) {
|
|
4130
|
-
|
|
4244
|
+
logger2.success(
|
|
4131
4245
|
`Update available: ${updateCheck.currentVersion} -> ${updateCheck.latestVersion}`
|
|
4132
4246
|
);
|
|
4133
4247
|
} else {
|
|
4134
|
-
|
|
4248
|
+
logger2.info(`Already at the latest version (${updateCheck.currentVersion})`);
|
|
4135
4249
|
}
|
|
4136
4250
|
return;
|
|
4137
4251
|
}
|
|
4138
|
-
|
|
4252
|
+
logger2.info("Checking for updates...");
|
|
4139
4253
|
const message = await performBinaryUpdate(currentVersion, { force, token });
|
|
4140
|
-
|
|
4254
|
+
logger2.success(message);
|
|
4141
4255
|
} catch (error) {
|
|
4142
4256
|
if (error instanceof GitHubClientError) {
|
|
4143
|
-
|
|
4257
|
+
const authHint = error.statusCode === 401 || error.statusCode === 403 ? " Tip: Set GITHUB_TOKEN or GH_TOKEN environment variable, or use `GITHUB_TOKEN=$(gh auth token) rulesync update ...`" : "";
|
|
4258
|
+
throw new CLIError(
|
|
4259
|
+
`GitHub API Error: ${error.message}.${authHint}`,
|
|
4260
|
+
ErrorCodes.UPDATE_FAILED
|
|
4261
|
+
);
|
|
4144
4262
|
} else if (error instanceof UpdatePermissionError) {
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4263
|
+
throw new CLIError(
|
|
4264
|
+
`${error.message} Tip: Run with elevated privileges (e.g., sudo rulesync update)`,
|
|
4265
|
+
ErrorCodes.UPDATE_FAILED
|
|
4266
|
+
);
|
|
4149
4267
|
}
|
|
4150
|
-
|
|
4268
|
+
throw error;
|
|
4151
4269
|
}
|
|
4152
4270
|
}
|
|
4153
4271
|
|
|
4154
4272
|
// src/cli/index.ts
|
|
4155
|
-
var getVersion = () => "7.
|
|
4273
|
+
var getVersion = () => "7.20.0";
|
|
4274
|
+
function wrapCommand(name, errorCode, handler) {
|
|
4275
|
+
return async (...args) => {
|
|
4276
|
+
const command = args[args.length - 1];
|
|
4277
|
+
const options = args[args.length - 2];
|
|
4278
|
+
const positionalArgs = args.slice(0, -2);
|
|
4279
|
+
const globalOpts = command.parent?.opts() ?? {};
|
|
4280
|
+
const logger2 = globalOpts.json ? new JsonLogger({ command: name, version: getVersion() }) : new ConsoleLogger();
|
|
4281
|
+
logger2.configure({
|
|
4282
|
+
verbose: Boolean(globalOpts.verbose) || Boolean(options.verbose),
|
|
4283
|
+
silent: Boolean(globalOpts.silent) || Boolean(options.silent)
|
|
4284
|
+
});
|
|
4285
|
+
try {
|
|
4286
|
+
await handler(logger2, options, globalOpts, positionalArgs);
|
|
4287
|
+
logger2.outputJson(true);
|
|
4288
|
+
} catch (error) {
|
|
4289
|
+
const code = error instanceof CLIError ? error.code : errorCode;
|
|
4290
|
+
const errorArg = error instanceof Error ? error : formatError(error);
|
|
4291
|
+
logger2.error(errorArg, code);
|
|
4292
|
+
process.exit(error instanceof CLIError ? error.exitCode : 1);
|
|
4293
|
+
}
|
|
4294
|
+
};
|
|
4295
|
+
}
|
|
4156
4296
|
var main = async () => {
|
|
4157
4297
|
const program = new Command();
|
|
4158
4298
|
const version = getVersion();
|
|
4159
|
-
program.
|
|
4160
|
-
|
|
4161
|
-
|
|
4299
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version(version, "-v, --version", "Show version").option("-j, --json", "Output results as JSON");
|
|
4300
|
+
program.command("init").description("Initialize rulesync in current directory").option("-V, --verbose", "Verbose output").option("-s, --silent", "Suppress all output").action(
|
|
4301
|
+
wrapCommand("init", "INIT_FAILED", async (logger2) => {
|
|
4302
|
+
await initCommand(logger2);
|
|
4303
|
+
})
|
|
4304
|
+
);
|
|
4305
|
+
program.command("gitignore").description("Add generated files to .gitignore").option(
|
|
4306
|
+
"-t, --targets <tools>",
|
|
4307
|
+
"Comma-separated list of tools to include (e.g., 'claudecode,copilot' or '*' for all)",
|
|
4308
|
+
(value) => {
|
|
4309
|
+
return value.split(",").map((t) => t.trim()).filter(Boolean);
|
|
4162
4310
|
}
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4311
|
+
).option(
|
|
4312
|
+
"-f, --features <features>",
|
|
4313
|
+
`Comma-separated list of features to include (${ALL_FEATURES.join(",")}) or '*' for all`,
|
|
4314
|
+
(value) => {
|
|
4315
|
+
return value.split(",").map((f) => f.trim()).filter(Boolean);
|
|
4316
|
+
}
|
|
4317
|
+
).option("-V, --verbose", "Verbose output").option("-s, --silent", "Suppress all output").action(
|
|
4318
|
+
wrapCommand("gitignore", "GITIGNORE_FAILED", async (logger2, options) => {
|
|
4319
|
+
await gitignoreCommand(logger2, {
|
|
4320
|
+
// eslint-disable-next-line no-type-assertion/no-type-assertion
|
|
4321
|
+
targets: options.targets,
|
|
4322
|
+
// eslint-disable-next-line no-type-assertion/no-type-assertion
|
|
4323
|
+
features: options.features
|
|
4324
|
+
});
|
|
4325
|
+
})
|
|
4326
|
+
);
|
|
4167
4327
|
program.command("fetch <source>").description("Fetch files from a Git repository (GitHub/GitLab)").option(
|
|
4168
4328
|
"-t, --target <target>",
|
|
4169
4329
|
"Target format to interpret files as (e.g., 'rulesync', 'claudecode'). Default: rulesync"
|
|
4170
4330
|
).option(
|
|
4171
4331
|
"-f, --features <features>",
|
|
4172
4332
|
`Comma-separated list of features to fetch (${ALL_FEATURES.join(",")}) or '*' for all`,
|
|
4173
|
-
(value) =>
|
|
4174
|
-
return value.split(",").map((f) => f.trim());
|
|
4175
|
-
}
|
|
4333
|
+
(value) => value.split(",").map((f) => f.trim())
|
|
4176
4334
|
).option("-r, --ref <ref>", "Branch, tag, or commit SHA to fetch from").option("-p, --path <path>", "Subdirectory path within the repository").option("-o, --output <dir>", "Output directory (default: .rulesync)").option(
|
|
4177
4335
|
"-c, --conflict <strategy>",
|
|
4178
4336
|
"Conflict resolution strategy: skip, overwrite (default: overwrite)"
|
|
4179
|
-
).option("--token <token>", "Git provider token for private repositories").option("-V, --verbose", "Verbose output").option("-s, --silent", "Suppress all output").action(
|
|
4180
|
-
|
|
4181
|
-
source
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
path: options.path,
|
|
4186
|
-
output: options.output,
|
|
4187
|
-
conflict: options.conflict,
|
|
4188
|
-
token: options.token,
|
|
4189
|
-
verbose: options.verbose,
|
|
4190
|
-
silent: options.silent
|
|
4191
|
-
});
|
|
4192
|
-
});
|
|
4337
|
+
).option("--token <token>", "Git provider token for private repositories").option("-V, --verbose", "Verbose output").option("-s, --silent", "Suppress all output").action(
|
|
4338
|
+
wrapCommand("fetch", "FETCH_FAILED", async (logger2, options, _globalOpts, positionalArgs) => {
|
|
4339
|
+
const source = positionalArgs[0];
|
|
4340
|
+
await fetchCommand(logger2, { ...options, source });
|
|
4341
|
+
})
|
|
4342
|
+
);
|
|
4193
4343
|
program.command("import").description("Import configurations from AI tools to rulesync format").option(
|
|
4194
4344
|
"-t, --targets <tool>",
|
|
4195
4345
|
"Tool to import from (e.g., 'copilot', 'cursor', 'cline')",
|
|
4196
|
-
(value) =>
|
|
4197
|
-
return value.split(",").map((t) => t.trim());
|
|
4198
|
-
}
|
|
4346
|
+
(value) => value.split(",").map((t) => t.trim())
|
|
4199
4347
|
).option(
|
|
4200
4348
|
"-f, --features <features>",
|
|
4201
4349
|
`Comma-separated list of features to import (${ALL_FEATURES.join(",")}) or '*' for all`,
|
|
4202
|
-
(value) =>
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
global: options.global
|
|
4214
|
-
});
|
|
4215
|
-
} catch (error) {
|
|
4216
|
-
logger.error(formatError(error));
|
|
4217
|
-
process.exit(1);
|
|
4218
|
-
}
|
|
4219
|
-
});
|
|
4220
|
-
program.command("mcp").description("Start MCP server for rulesync").action(async () => {
|
|
4221
|
-
try {
|
|
4222
|
-
await mcpCommand({ version });
|
|
4223
|
-
} catch (error) {
|
|
4224
|
-
logger.error(formatError(error));
|
|
4225
|
-
process.exit(1);
|
|
4226
|
-
}
|
|
4227
|
-
});
|
|
4350
|
+
(value) => value.split(",").map((f) => f.trim())
|
|
4351
|
+
).option("-V, --verbose", "Verbose output").option("-s, --silent", "Suppress all output").option("-g, --global", "Import for global(user scope) configuration files").action(
|
|
4352
|
+
wrapCommand("import", "IMPORT_FAILED", async (logger2, options) => {
|
|
4353
|
+
await importCommand(logger2, options);
|
|
4354
|
+
})
|
|
4355
|
+
);
|
|
4356
|
+
program.command("mcp").description("Start MCP server for rulesync").action(
|
|
4357
|
+
wrapCommand("mcp", "MCP_FAILED", async (logger2, _options) => {
|
|
4358
|
+
await mcpCommand(logger2, { version });
|
|
4359
|
+
})
|
|
4360
|
+
);
|
|
4228
4361
|
program.command("install").description("Install skills from declarative sources in rulesync.jsonc").option("--update", "Force re-resolve all source refs, ignoring lockfile").option(
|
|
4229
4362
|
"--frozen",
|
|
4230
4363
|
"Fail if lockfile is missing or out of sync (for CI); fetches missing skills using locked refs"
|
|
4231
|
-
).option("--token <token>", "GitHub token for private repos").option("-c, --config <path>", "Path to configuration file").option("-V, --verbose", "Verbose output").option("-s, --silent", "Suppress all output").action(
|
|
4232
|
-
|
|
4233
|
-
await installCommand({
|
|
4364
|
+
).option("--token <token>", "GitHub token for private repos").option("-c, --config <path>", "Path to configuration file").option("-V, --verbose", "Verbose output").option("-s, --silent", "Suppress all output").action(
|
|
4365
|
+
wrapCommand("install", "INSTALL_FAILED", async (logger2, options) => {
|
|
4366
|
+
await installCommand(logger2, {
|
|
4367
|
+
// eslint-disable-next-line no-type-assertion/no-type-assertion
|
|
4234
4368
|
update: options.update,
|
|
4369
|
+
// eslint-disable-next-line no-type-assertion/no-type-assertion
|
|
4235
4370
|
frozen: options.frozen,
|
|
4371
|
+
// eslint-disable-next-line no-type-assertion/no-type-assertion
|
|
4236
4372
|
token: options.token,
|
|
4373
|
+
// eslint-disable-next-line no-type-assertion/no-type-assertion
|
|
4237
4374
|
configPath: options.config,
|
|
4375
|
+
// eslint-disable-next-line no-type-assertion/no-type-assertion
|
|
4238
4376
|
verbose: options.verbose,
|
|
4377
|
+
// eslint-disable-next-line no-type-assertion/no-type-assertion
|
|
4239
4378
|
silent: options.silent
|
|
4240
4379
|
});
|
|
4241
|
-
}
|
|
4242
|
-
|
|
4243
|
-
process.exit(1);
|
|
4244
|
-
}
|
|
4245
|
-
});
|
|
4380
|
+
})
|
|
4381
|
+
);
|
|
4246
4382
|
program.command("generate").description("Generate configuration files for AI tools").option(
|
|
4247
4383
|
"-t, --targets <tools>",
|
|
4248
4384
|
"Comma-separated list of tools to generate for (e.g., 'copilot,cursor,cline' or '*' for all)",
|
|
4249
|
-
(value) =>
|
|
4250
|
-
return value.split(",").map((t) => t.trim());
|
|
4251
|
-
}
|
|
4385
|
+
(value) => value.split(",").map((t) => t.trim())
|
|
4252
4386
|
).option(
|
|
4253
4387
|
"-f, --features <features>",
|
|
4254
4388
|
`Comma-separated list of features to generate (${ALL_FEATURES.join(",")}) or '*' for all`,
|
|
4255
|
-
(value) =>
|
|
4256
|
-
return value.split(",").map((f) => f.trim());
|
|
4257
|
-
}
|
|
4389
|
+
(value) => value.split(",").map((f) => f.trim())
|
|
4258
4390
|
).option("--delete", "Delete all existing files in output directories before generating").option(
|
|
4259
4391
|
"-b, --base-dir <paths>",
|
|
4260
4392
|
"Base directories to generate files (comma-separated for multiple paths)",
|
|
4261
|
-
(value) =>
|
|
4262
|
-
return value.split(",").map((p) => p.trim());
|
|
4263
|
-
}
|
|
4393
|
+
(value) => value.split(",").map((p) => p.trim())
|
|
4264
4394
|
).option("-V, --verbose", "Verbose output").option("-s, --silent", "Suppress all output").option("-c, --config <path>", "Path to configuration file").option("-g, --global", "Generate for global(user scope) configuration files").option(
|
|
4265
4395
|
"--simulate-commands",
|
|
4266
4396
|
"Generate simulated commands. This feature is only available for copilot, cursor and codexcli."
|
|
@@ -4270,40 +4400,19 @@ var main = async () => {
|
|
|
4270
4400
|
).option(
|
|
4271
4401
|
"--simulate-skills",
|
|
4272
4402
|
"Generate simulated skills. This feature is only available for copilot, cursor and codexcli."
|
|
4273
|
-
).option("--dry-run", "Dry run: show changes without writing files").option("--check", "Check if files are up to date (exits with code 1 if changes needed)").action(
|
|
4274
|
-
|
|
4275
|
-
await generateCommand(
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
global: options.global,
|
|
4284
|
-
simulateCommands: options.simulateCommands,
|
|
4285
|
-
simulateSubagents: options.simulateSubagents,
|
|
4286
|
-
simulateSkills: options.simulateSkills,
|
|
4287
|
-
dryRun: options.dryRun,
|
|
4288
|
-
check: options.check
|
|
4289
|
-
});
|
|
4290
|
-
} catch (error) {
|
|
4291
|
-
logger.error(formatError(error));
|
|
4292
|
-
process.exit(1);
|
|
4293
|
-
}
|
|
4294
|
-
});
|
|
4295
|
-
program.command("update").description("Update rulesync to the latest version").option("--check", "Check for updates without installing").option("--force", "Force update even if already at latest version").option("--token <token>", "GitHub token for API access").option("-V, --verbose", "Verbose output").option("-s, --silent", "Suppress all output").action(async (options) => {
|
|
4296
|
-
await updateCommand(version, {
|
|
4297
|
-
check: options.check,
|
|
4298
|
-
force: options.force,
|
|
4299
|
-
token: options.token,
|
|
4300
|
-
verbose: options.verbose,
|
|
4301
|
-
silent: options.silent
|
|
4302
|
-
});
|
|
4303
|
-
});
|
|
4403
|
+
).option("--dry-run", "Dry run: show changes without writing files").option("--check", "Check if files are up to date (exits with code 1 if changes needed)").action(
|
|
4404
|
+
wrapCommand("generate", "GENERATION_FAILED", async (logger2, options) => {
|
|
4405
|
+
await generateCommand(logger2, options);
|
|
4406
|
+
})
|
|
4407
|
+
);
|
|
4408
|
+
program.command("update").description("Update rulesync to the latest version").option("--check", "Check for updates without installing").option("--force", "Force update even if already at latest version").option("--token <token>", "GitHub token for API access").option("-V, --verbose", "Verbose output").option("-s, --silent", "Suppress all output").action(
|
|
4409
|
+
wrapCommand("update", "UPDATE_FAILED", async (logger2, options) => {
|
|
4410
|
+
await updateCommand(logger2, version, options);
|
|
4411
|
+
})
|
|
4412
|
+
);
|
|
4304
4413
|
program.parse();
|
|
4305
4414
|
};
|
|
4306
4415
|
main().catch((error) => {
|
|
4307
|
-
|
|
4416
|
+
console.error(formatError(error));
|
|
4308
4417
|
process.exit(1);
|
|
4309
4418
|
});
|