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