heymark 1.1.1 → 1.1.3
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/LICENSE +21 -0
- package/README.ko.md +170 -0
- package/README.md +170 -189
- package/package.json +56 -57
- package/scripts/lib/config.js +110 -73
- package/scripts/lib/parser.js +125 -82
- package/scripts/lib/repo.js +97 -80
- package/scripts/sync.js +312 -239
- package/scripts/tools/antigravity.js +53 -41
- package/scripts/tools/claude.js +53 -42
- package/scripts/tools/codex.js +53 -41
- package/scripts/tools/copilot.js +65 -41
- package/scripts/tools/cursor.js +52 -41
package/package.json
CHANGED
|
@@ -1,57 +1,56 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "heymark",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "heymark",
|
|
3
|
+
"version": "1.1.3",
|
|
4
|
+
"description": "Rules & Skills Hub for AI Coding Assistants",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"main": "scripts/sync.js",
|
|
7
|
+
"bin": "scripts/sync.js",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"ai-coding-assistant",
|
|
10
|
+
"ai-agent",
|
|
11
|
+
"ai-skill",
|
|
12
|
+
"ai-rule",
|
|
13
|
+
"skill-management",
|
|
14
|
+
"cursor",
|
|
15
|
+
"claude-code",
|
|
16
|
+
"copilot",
|
|
17
|
+
"codex",
|
|
18
|
+
"antigravity"
|
|
19
|
+
],
|
|
20
|
+
"homepage": "https://github.com/MosslandOpenDevs/heymark#readme",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/MosslandOpenDevs/heymark/issues",
|
|
23
|
+
"email": "yena@moss.land"
|
|
24
|
+
},
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"author": {
|
|
27
|
+
"name": "i-2na",
|
|
28
|
+
"email": "yena@moss.land",
|
|
29
|
+
"url": "https://github.com/i2na"
|
|
30
|
+
},
|
|
31
|
+
"contributors": [
|
|
32
|
+
{
|
|
33
|
+
"name": "i-2na",
|
|
34
|
+
"email": "yena@moss.land",
|
|
35
|
+
"url": "https://github.com/i2na"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "yezzero",
|
|
39
|
+
"email": "yeyeonggim06@gmail.com",
|
|
40
|
+
"url": "https://github.com/yezzero"
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=14.0.0"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"scripts/"
|
|
48
|
+
],
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "git+https://github.com/MosslandOpenDevs/heymark.git"
|
|
52
|
+
},
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"access": "public"
|
|
55
|
+
}
|
|
56
|
+
}
|
package/scripts/lib/config.js
CHANGED
|
@@ -1,73 +1,110 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const fs = require("fs");
|
|
4
|
-
const path = require("path");
|
|
5
|
-
|
|
6
|
-
const CONFIG_DIR = ".heymark";
|
|
7
|
-
const CONFIG_FILENAME = "config.json";
|
|
8
|
-
|
|
9
|
-
const CONFIG_RELATIVE = path.join(CONFIG_DIR, CONFIG_FILENAME);
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* rulesSource:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
|
|
6
|
+
const CONFIG_DIR = ".heymark";
|
|
7
|
+
const CONFIG_FILENAME = "config.json";
|
|
8
|
+
const DEFAULT_BRANCH = "main";
|
|
9
|
+
const CONFIG_RELATIVE = path.join(CONFIG_DIR, CONFIG_FILENAME);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {{ rulesSource: string, branch?: string, rulesSourceDir?: string }} RuleBookConfig
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Resolve config file path from project root.
|
|
17
|
+
* @param {string} projectRoot
|
|
18
|
+
* @returns {string}
|
|
19
|
+
*/
|
|
20
|
+
function getConfigPath(projectRoot) {
|
|
21
|
+
return path.join(projectRoot, CONFIG_DIR, CONFIG_FILENAME);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Normalize and validate config payload.
|
|
26
|
+
* @param {unknown} value
|
|
27
|
+
* @returns {RuleBookConfig | null}
|
|
28
|
+
*/
|
|
29
|
+
function normalizeConfig(value) {
|
|
30
|
+
if (!value || typeof value !== "object") {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const raw =
|
|
35
|
+
/** @type {{ rulesSource?: unknown, branch?: unknown, rulesSourceDir?: unknown }} */ (
|
|
36
|
+
value
|
|
37
|
+
);
|
|
38
|
+
if (typeof raw.rulesSource !== "string" || !raw.rulesSource.trim()) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
rulesSource: raw.rulesSource.trim(),
|
|
44
|
+
branch:
|
|
45
|
+
typeof raw.branch === "string" && raw.branch.trim()
|
|
46
|
+
? raw.branch.trim()
|
|
47
|
+
: DEFAULT_BRANCH,
|
|
48
|
+
rulesSourceDir: typeof raw.rulesSourceDir === "string" ? raw.rulesSourceDir.trim() : "",
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Read config from project root.
|
|
54
|
+
* @param {string} projectRoot
|
|
55
|
+
* @returns {RuleBookConfig | null}
|
|
56
|
+
*/
|
|
57
|
+
function loadConfig(projectRoot) {
|
|
58
|
+
const configPath = getConfigPath(projectRoot);
|
|
59
|
+
if (!fs.existsSync(configPath)) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const raw = fs.readFileSync(configPath, "utf8");
|
|
65
|
+
return normalizeConfig(JSON.parse(raw));
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Create initial config file in .heymark/config.json.
|
|
73
|
+
* @param {string} projectRoot
|
|
74
|
+
* @param {RuleBookConfig} config
|
|
75
|
+
* @returns {string}
|
|
76
|
+
*/
|
|
77
|
+
function writeConfig(projectRoot, config) {
|
|
78
|
+
const configDir = path.join(projectRoot, CONFIG_DIR);
|
|
79
|
+
if (!fs.existsSync(configDir)) {
|
|
80
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const normalized = normalizeConfig(config);
|
|
84
|
+
if (!normalized) {
|
|
85
|
+
throw new Error("Invalid config payload");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const configPath = path.join(configDir, CONFIG_FILENAME);
|
|
89
|
+
const toWrite = {
|
|
90
|
+
rulesSource: normalized.rulesSource,
|
|
91
|
+
branch: normalized.branch || DEFAULT_BRANCH,
|
|
92
|
+
};
|
|
93
|
+
if (normalized.rulesSourceDir) {
|
|
94
|
+
toWrite.rulesSourceDir = normalized.rulesSourceDir;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
fs.writeFileSync(configPath, JSON.stringify(toWrite, null, 2), "utf8");
|
|
98
|
+
return configPath;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
module.exports = {
|
|
102
|
+
CONFIG_DIR,
|
|
103
|
+
CONFIG_FILENAME,
|
|
104
|
+
CONFIG_RELATIVE,
|
|
105
|
+
DEFAULT_BRANCH,
|
|
106
|
+
getConfigPath,
|
|
107
|
+
loadConfig,
|
|
108
|
+
normalizeConfig,
|
|
109
|
+
writeConfig,
|
|
110
|
+
};
|
package/scripts/lib/parser.js
CHANGED
|
@@ -1,82 +1,125 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const fs = require("fs");
|
|
4
|
-
const path = require("path");
|
|
5
|
-
|
|
6
|
-
const GENERATED_MARKER = "<!-- @generated by sync-rules - DO NOT EDIT -->";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
parseFrontmatter
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
|
|
6
|
+
const GENERATED_MARKER = "<!-- @generated by sync-rules - DO NOT EDIT -->";
|
|
7
|
+
const FRONTMATTER_REGEX = /^---\r?\n([\s\S]+?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
8
|
+
const MARKDOWN_EXTENSION = ".md";
|
|
9
|
+
const RULE_SEPARATOR = "\n\n---\n\n";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} rawValue
|
|
13
|
+
* @returns {string | boolean}
|
|
14
|
+
*/
|
|
15
|
+
function parseMetadataValue(rawValue) {
|
|
16
|
+
let value = rawValue.trim();
|
|
17
|
+
if (
|
|
18
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
19
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
20
|
+
) {
|
|
21
|
+
value = value.slice(1, -1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (value === "true") {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
if (value === "false") {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {string} metadataBlock
|
|
36
|
+
* @returns {Record<string, string | boolean>}
|
|
37
|
+
*/
|
|
38
|
+
function parseMetadataBlock(metadataBlock) {
|
|
39
|
+
const metadata = {};
|
|
40
|
+
|
|
41
|
+
metadataBlock.split(/\r?\n/).forEach((line) => {
|
|
42
|
+
const separatorIndex = line.indexOf(":");
|
|
43
|
+
if (separatorIndex === -1) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const key = line.slice(0, separatorIndex).trim();
|
|
48
|
+
const rawValue = line.slice(separatorIndex + 1);
|
|
49
|
+
metadata[key] = parseMetadataValue(rawValue);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return metadata;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function parseFrontmatter(content) {
|
|
56
|
+
const match = content.match(FRONTMATTER_REGEX);
|
|
57
|
+
if (!match) {
|
|
58
|
+
return { metadata: {}, body: content.trim() };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const [, metadataBlock, body] = match;
|
|
62
|
+
return {
|
|
63
|
+
metadata: parseMetadataBlock(metadataBlock),
|
|
64
|
+
body: body.trim(),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function loadMarkdownFiles(rulesDir) {
|
|
69
|
+
return fs
|
|
70
|
+
.readdirSync(rulesDir)
|
|
71
|
+
.filter((fileName) => fileName.endsWith(MARKDOWN_EXTENSION))
|
|
72
|
+
.sort();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function createRuleFromFile(rulesDir, fileName) {
|
|
76
|
+
const filePath = path.join(rulesDir, fileName);
|
|
77
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
78
|
+
const { metadata, body } = parseFrontmatter(raw);
|
|
79
|
+
const baseName = path.basename(fileName, MARKDOWN_EXTENSION);
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
fileName,
|
|
83
|
+
name: metadata.name || baseName,
|
|
84
|
+
description: metadata.description || baseName,
|
|
85
|
+
globs: metadata.globs || "",
|
|
86
|
+
alwaysApply: metadata.alwaysApply === true,
|
|
87
|
+
metadata,
|
|
88
|
+
body,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function loadRules(rulesDir) {
|
|
93
|
+
if (!fs.existsSync(rulesDir)) {
|
|
94
|
+
console.error(`[Error] Rules directory not found: ${rulesDir}`);
|
|
95
|
+
console.error(" Ensure 'rules/' exists relative to the script location.");
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const files = loadMarkdownFiles(rulesDir);
|
|
100
|
+
|
|
101
|
+
if (files.length === 0) {
|
|
102
|
+
console.error(`[Error] No .md files found in: ${rulesDir}`);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return files.map((fileName) => createRuleFromFile(rulesDir, fileName));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function mergeRuleBodies(rules) {
|
|
110
|
+
return rules.map((rule) => rule.body).join(RULE_SEPARATOR);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function writeMergedFile(filePath, rules) {
|
|
114
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
115
|
+
const content = `${GENERATED_MARKER}\n\n${mergeRuleBodies(rules)}\n`;
|
|
116
|
+
fs.writeFileSync(filePath, content, "utf8");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
module.exports = {
|
|
120
|
+
GENERATED_MARKER,
|
|
121
|
+
parseFrontmatter,
|
|
122
|
+
loadRules,
|
|
123
|
+
mergeRuleBodies,
|
|
124
|
+
writeMergedFile,
|
|
125
|
+
};
|