heymark 1.1.3 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +63 -110
- package/README.md +65 -110
- package/package.json +3 -3
- package/scripts/cli.js +375 -0
- package/scripts/lib/config.js +30 -14
- package/scripts/lib/repo.js +14 -14
- package/scripts/tools/antigravity.js +5 -9
- package/scripts/tools/claude.js +5 -9
- package/scripts/tools/codex.js +5 -9
- package/scripts/tools/copilot.js +5 -9
- package/scripts/tools/cursor.js +5 -9
- package/scripts/sync.js +0 -312
package/scripts/sync.js
DELETED
|
@@ -1,312 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
const fs = require("fs");
|
|
5
|
-
const path = require("path");
|
|
6
|
-
const { CONFIG_RELATIVE, DEFAULT_BRANCH, loadConfig, writeConfig } = require("./lib/config");
|
|
7
|
-
const { loadRules } = require("./lib/parser");
|
|
8
|
-
const { getRulesDirFromRepo } = require("./lib/repo");
|
|
9
|
-
|
|
10
|
-
const SCRIPT_DIR = __dirname;
|
|
11
|
-
const PROJECT_ROOT = process.cwd();
|
|
12
|
-
|
|
13
|
-
const INIT_SUBCOMMAND = "init";
|
|
14
|
-
const OPTION_TOOLS = "--tools";
|
|
15
|
-
const OPTION_SOURCE = "--source";
|
|
16
|
-
const OPTION_CLEAN = "--clean";
|
|
17
|
-
const OPTION_PREVIEW = "--preview";
|
|
18
|
-
const OPTION_HELP = "--help";
|
|
19
|
-
const SHORT_TOOLS = "-t";
|
|
20
|
-
const SHORT_SOURCE = "-s";
|
|
21
|
-
const SHORT_CLEAN = "-c";
|
|
22
|
-
const SHORT_PREVIEW = "-p";
|
|
23
|
-
const SHORT_HELP = "-h";
|
|
24
|
-
const OPTION_BRANCH = "--branch";
|
|
25
|
-
const OPTION_DIR = "--dir";
|
|
26
|
-
const SHORT_BRANCH = "-b";
|
|
27
|
-
const SHORT_DIR = "-d";
|
|
28
|
-
|
|
29
|
-
function exitWithError(message, details = []) {
|
|
30
|
-
console.error(`[Error] ${message}`);
|
|
31
|
-
details.forEach((detail) => console.error(` ${detail}`));
|
|
32
|
-
process.exit(1);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function discoverTools() {
|
|
36
|
-
const toolsDir = path.join(SCRIPT_DIR, "tools");
|
|
37
|
-
const registry = {};
|
|
38
|
-
|
|
39
|
-
fs.readdirSync(toolsDir)
|
|
40
|
-
.filter((fileName) => fileName.endsWith(".js"))
|
|
41
|
-
.sort()
|
|
42
|
-
.forEach((file) => {
|
|
43
|
-
const key = path.basename(file, ".js");
|
|
44
|
-
registry[key] = require(path.join(toolsDir, file));
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
return registry;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function parseToolList(rawValue) {
|
|
51
|
-
const parsed = rawValue
|
|
52
|
-
.split(",")
|
|
53
|
-
.map((tool) => tool.trim().toLowerCase())
|
|
54
|
-
.filter(Boolean);
|
|
55
|
-
|
|
56
|
-
if (parsed.length === 0) {
|
|
57
|
-
exitWithError("--tools requires a comma-separated list.");
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return parsed;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function parseArgs(availableTools) {
|
|
64
|
-
const args = process.argv.slice(2);
|
|
65
|
-
const config = {
|
|
66
|
-
tools: Object.keys(availableTools),
|
|
67
|
-
clean: false,
|
|
68
|
-
preview: false,
|
|
69
|
-
help: false,
|
|
70
|
-
source: null,
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
for (let i = 0; i < args.length; i++) {
|
|
74
|
-
const arg = args[i];
|
|
75
|
-
|
|
76
|
-
if (arg === OPTION_TOOLS || arg === SHORT_TOOLS) {
|
|
77
|
-
const value = args[++i];
|
|
78
|
-
if (!value) {
|
|
79
|
-
exitWithError("--tools requires a comma-separated list.");
|
|
80
|
-
}
|
|
81
|
-
config.tools = parseToolList(value);
|
|
82
|
-
} else if (arg === OPTION_CLEAN || arg === SHORT_CLEAN) {
|
|
83
|
-
config.clean = true;
|
|
84
|
-
} else if (arg === OPTION_PREVIEW || arg === SHORT_PREVIEW) {
|
|
85
|
-
config.preview = true;
|
|
86
|
-
} else if (arg === OPTION_SOURCE || arg === SHORT_SOURCE) {
|
|
87
|
-
const value = args[++i];
|
|
88
|
-
if (!value) {
|
|
89
|
-
exitWithError("--source requires a GitHub repository URL.");
|
|
90
|
-
}
|
|
91
|
-
config.source = value.trim();
|
|
92
|
-
} else if (arg === OPTION_HELP || arg === SHORT_HELP) {
|
|
93
|
-
config.help = true;
|
|
94
|
-
} else {
|
|
95
|
-
exitWithError(`Unknown option: ${arg}`, ["Use --help for usage information."]);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const invalidTools = config.tools.filter((tool) => !availableTools[tool]);
|
|
100
|
-
if (invalidTools.length > 0) {
|
|
101
|
-
exitWithError(`Unknown tool(s): ${invalidTools.join(", ")}`, [
|
|
102
|
-
`Available: ${Object.keys(availableTools).join(", ")}`,
|
|
103
|
-
]);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return config;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function showHelp(tools) {
|
|
110
|
-
const toolLines = Object.entries(tools)
|
|
111
|
-
.map(([toolKey, toolDefinition]) => {
|
|
112
|
-
const paddedKey = toolKey.padEnd(10);
|
|
113
|
-
const paddedName = toolDefinition.name.padEnd(16);
|
|
114
|
-
return ` ${paddedKey} ${paddedName} -> ${toolDefinition.output}`;
|
|
115
|
-
})
|
|
116
|
-
.join("\n");
|
|
117
|
-
|
|
118
|
-
console.log(`
|
|
119
|
-
AI Coding Tool Convention Sync
|
|
120
|
-
|
|
121
|
-
Reads *.md from a GitHub repository (public or private) and generates
|
|
122
|
-
tool-specific configuration files for various AI coding assistants.
|
|
123
|
-
Same rules everywhere: A computer, B computer, same remote repo.
|
|
124
|
-
|
|
125
|
-
Usage:
|
|
126
|
-
heymark init <repo-url> Set rules source (creates ${CONFIG_RELATIVE})
|
|
127
|
-
heymark [options] Sync from configured or --source repo
|
|
128
|
-
|
|
129
|
-
Options:
|
|
130
|
-
--source, -s <url> GitHub repo URL for this run (overrides ${CONFIG_RELATIVE})
|
|
131
|
-
--tools, -t <list> Comma-separated tool names (default: all)
|
|
132
|
-
--clean, -c Remove all generated files
|
|
133
|
-
--preview, -p Preview what will be generated without writing
|
|
134
|
-
--help, -h Show this help message
|
|
135
|
-
|
|
136
|
-
Rules source (in order):
|
|
137
|
-
1. --source <repo-url>
|
|
138
|
-
2. ${CONFIG_RELATIVE} (set via 'heymark init <repo-url>')
|
|
139
|
-
Private repos: use SSH (git@github.com:org/repo.git) or HTTPS with token.
|
|
140
|
-
|
|
141
|
-
Available tools:
|
|
142
|
-
${toolLines}
|
|
143
|
-
|
|
144
|
-
Examples:
|
|
145
|
-
heymark init https://github.com/org/my-rules.git
|
|
146
|
-
heymark init https://github.com/org/my-rules.git --dir rules --branch main
|
|
147
|
-
heymark
|
|
148
|
-
heymark -s https://github.com/org/other-rules.git
|
|
149
|
-
heymark -t cursor,claude
|
|
150
|
-
heymark -c
|
|
151
|
-
heymark -p
|
|
152
|
-
`);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function parseInitArgs(initArgs) {
|
|
156
|
-
const repoUrl = initArgs[0];
|
|
157
|
-
if (!repoUrl || repoUrl.startsWith("--")) {
|
|
158
|
-
exitWithError("init requires a GitHub repository URL.", [
|
|
159
|
-
"Example: heymark init https://github.com/org/my-rules.git",
|
|
160
|
-
"Example: heymark init git@github.com:org/my-rules.git",
|
|
161
|
-
"Optional: --branch <branch> --dir <subdir> (e.g. --dir rules)",
|
|
162
|
-
]);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
let branch = DEFAULT_BRANCH;
|
|
166
|
-
let rulesSourceDir = "";
|
|
167
|
-
|
|
168
|
-
for (let i = 1; i < initArgs.length; i++) {
|
|
169
|
-
const arg = initArgs[i];
|
|
170
|
-
|
|
171
|
-
if (arg === OPTION_BRANCH || arg === SHORT_BRANCH) {
|
|
172
|
-
const value = initArgs[++i];
|
|
173
|
-
if (!value) {
|
|
174
|
-
exitWithError("--branch requires a branch name.");
|
|
175
|
-
}
|
|
176
|
-
branch = value.trim();
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (arg === OPTION_DIR || arg === SHORT_DIR) {
|
|
181
|
-
const value = initArgs[++i];
|
|
182
|
-
if (!value) {
|
|
183
|
-
exitWithError("--dir requires a directory path.");
|
|
184
|
-
}
|
|
185
|
-
rulesSourceDir = value.trim();
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
exitWithError(`Unknown option for init: ${arg}`);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return { rulesSource: repoUrl.trim(), branch, rulesSourceDir };
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function runInit(initArgs) {
|
|
196
|
-
const config = parseInitArgs(initArgs);
|
|
197
|
-
const configPath = writeConfig(PROJECT_ROOT, config);
|
|
198
|
-
|
|
199
|
-
console.log(
|
|
200
|
-
`[Init] Rules source saved to ${path.relative(PROJECT_ROOT, configPath) || configPath}`
|
|
201
|
-
);
|
|
202
|
-
console.log(` rulesSource: ${config.rulesSource}`);
|
|
203
|
-
if (config.branch !== DEFAULT_BRANCH) {
|
|
204
|
-
console.log(` branch: ${config.branch}`);
|
|
205
|
-
}
|
|
206
|
-
if (config.rulesSourceDir) {
|
|
207
|
-
console.log(` rulesSourceDir: ${config.rulesSourceDir}`);
|
|
208
|
-
}
|
|
209
|
-
console.log("");
|
|
210
|
-
console.log("Run 'heymark' to fetch rules from the repo and generate tool configs.");
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function resolveRulesDir(config) {
|
|
214
|
-
const repoConfig = config.source
|
|
215
|
-
? { rulesSource: config.source, branch: DEFAULT_BRANCH, rulesSourceDir: "" }
|
|
216
|
-
: loadConfig(PROJECT_ROOT);
|
|
217
|
-
|
|
218
|
-
if (!repoConfig) {
|
|
219
|
-
return null;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return getRulesDirFromRepo(PROJECT_ROOT, repoConfig);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function cleanGeneratedFiles(tools, selectedTools, ruleNames, onlyPrintWhenDeleted) {
|
|
226
|
-
for (const toolKey of selectedTools) {
|
|
227
|
-
const cleanedPaths = tools[toolKey].clean(ruleNames, PROJECT_ROOT);
|
|
228
|
-
if (onlyPrintWhenDeleted && cleanedPaths.length === 0) {
|
|
229
|
-
continue;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
cleanedPaths.forEach((filePath) => {
|
|
233
|
-
console.log(` Deleted: ${filePath}`);
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
function main() {
|
|
239
|
-
const args = process.argv.slice(2);
|
|
240
|
-
const subcommand = args[0];
|
|
241
|
-
if (subcommand === INIT_SUBCOMMAND) {
|
|
242
|
-
runInit(args.slice(1));
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const tools = discoverTools();
|
|
247
|
-
const config = parseArgs(tools);
|
|
248
|
-
|
|
249
|
-
if (config.help) {
|
|
250
|
-
showHelp(tools);
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const rulesSourceDir = resolveRulesDir(config);
|
|
255
|
-
if (!rulesSourceDir) {
|
|
256
|
-
console.error("[Error] Rules source not set.");
|
|
257
|
-
console.error(" Run: heymark init <github-repo-url>");
|
|
258
|
-
console.error(" Example: heymark init https://github.com/org/my-rules.git");
|
|
259
|
-
console.error(" Or use: heymark --source <repo-url>");
|
|
260
|
-
process.exit(1);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const rulesRelPath = path.relative(PROJECT_ROOT, rulesSourceDir) || ".";
|
|
264
|
-
|
|
265
|
-
console.log("[Sync] Starting convention sync...");
|
|
266
|
-
console.log(` Source: ${rulesRelPath} (from remote repo)`);
|
|
267
|
-
console.log(` Target: ${PROJECT_ROOT}`);
|
|
268
|
-
console.log(` Tools: ${config.tools.join(", ")}`);
|
|
269
|
-
console.log("");
|
|
270
|
-
|
|
271
|
-
const rules = loadRules(rulesSourceDir);
|
|
272
|
-
console.log(`[Load] ${rules.length} rule(s): ${rules.map((rule) => rule.name).join(", ")}`);
|
|
273
|
-
console.log("");
|
|
274
|
-
|
|
275
|
-
const ruleNames = rules.map((rule) => rule.name);
|
|
276
|
-
|
|
277
|
-
if (config.clean) {
|
|
278
|
-
console.log("[Clean] Removing generated files...");
|
|
279
|
-
cleanGeneratedFiles(tools, config.tools, ruleNames, false);
|
|
280
|
-
console.log("");
|
|
281
|
-
console.log(`[Done] Cleaned ${config.tools.length} tool(s) successfully.`);
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
if (config.preview) {
|
|
286
|
-
console.log("[Preview] Would generate:");
|
|
287
|
-
for (const toolKey of config.tools) {
|
|
288
|
-
const toolDefinition = tools[toolKey];
|
|
289
|
-
const summary = `${toolDefinition.name.padEnd(16)} -> ${toolDefinition.output}`;
|
|
290
|
-
console.log(` ${summary} (${rules.length} rules)`);
|
|
291
|
-
}
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Ensure regenerated output is always fresh.
|
|
296
|
-
console.log("[Clean] Removing existing generated files...");
|
|
297
|
-
cleanGeneratedFiles(tools, config.tools, ruleNames, true);
|
|
298
|
-
console.log("");
|
|
299
|
-
|
|
300
|
-
console.log("[Generate]");
|
|
301
|
-
for (const toolKey of config.tools) {
|
|
302
|
-
const toolDefinition = tools[toolKey];
|
|
303
|
-
const count = toolDefinition.generate(rules, PROJECT_ROOT);
|
|
304
|
-
const summary = `${toolDefinition.name.padEnd(16)} -> ${toolDefinition.output}`;
|
|
305
|
-
console.log(` ${summary} (${count} rules)`);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
console.log("");
|
|
309
|
-
console.log(`[Done] ${config.tools.length} tool(s) synced successfully.`);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
main();
|