fmddr-skills 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -27
- package/bin/install.js +115 -34
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# fmddr —
|
|
1
|
+
# fmddr — agent skills
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Skills for working with [`fmddr`](https://github.com/proofsh/fmddr) from
|
|
4
|
+
Claude Code, Cursor, and any agent that reads `AGENTS.md`.
|
|
5
5
|
|
|
6
6
|
| Skill | Triggers on | Purpose |
|
|
7
7
|
| --------------- | ------------------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
@@ -10,47 +10,50 @@ from Claude Code:
|
|
|
10
10
|
| `fmddr-issue` | "report a bug", "file an issue", "feature request" — for fmddr | Open a high-quality issue at proofsh/fmddr (with severity) |
|
|
11
11
|
| `fmddr-release` | "release fmddr", "cut a patch", "publish to PyPI" | End-to-end release flow: bump, changelog, build, tag, publish |
|
|
12
12
|
|
|
13
|
-
**Typical first-time flow:**
|
|
14
|
-
1. `npx fmddr-skills` — installs all four skills
|
|
15
|
-
2. In a Claude Code session: "set up fmddr for my DDR" → `fmddr-setup` activates
|
|
16
|
-
3. Now any FileMaker question → `fmddr` activates automatically
|
|
17
|
-
|
|
18
13
|
## Install
|
|
19
14
|
|
|
20
|
-
###
|
|
15
|
+
### Claude Code
|
|
21
16
|
|
|
22
17
|
```sh
|
|
23
|
-
npx fmddr-skills #
|
|
24
|
-
npx fmddr-skills fmddr fmddr-setup # install specific skills only
|
|
18
|
+
npx fmddr-skills # personal (~/.claude/skills/)
|
|
25
19
|
npx fmddr-skills --project # project-scoped (.claude/skills/)
|
|
20
|
+
npx fmddr-skills fmddr fmddr-setup # specific skills only
|
|
26
21
|
```
|
|
27
22
|
|
|
28
|
-
|
|
23
|
+
In a Claude Code session type `/` to confirm they appear, or ask
|
|
24
|
+
"is the fmddr skill loaded?".
|
|
25
|
+
|
|
26
|
+
### Cursor
|
|
29
27
|
|
|
30
28
|
```sh
|
|
31
|
-
|
|
32
|
-
cp -R skills/fmddr ~/.claude/skills/
|
|
33
|
-
cp -R skills/fmddr-setup ~/.claude/skills/
|
|
34
|
-
cp -R skills/fmddr-issue ~/.claude/skills/
|
|
35
|
-
cp -R skills/fmddr-release ~/.claude/skills/
|
|
29
|
+
npx fmddr-skills --cursor # writes .cursor/rules/fmddr-*.mdc
|
|
36
30
|
```
|
|
37
31
|
|
|
38
|
-
|
|
32
|
+
Installs each skill as a `.mdc` rule file in `.cursor/rules/` of the
|
|
33
|
+
current directory. Rules for `fmddr`, `fmddr-setup`, and
|
|
34
|
+
`fmddr-generate-docs` auto-activate on `.fmp12` / `.xml` / `.fmddr.db`
|
|
35
|
+
files; the others are available on demand. Restart Cursor or open the
|
|
36
|
+
Rules panel after installing.
|
|
37
|
+
|
|
38
|
+
### AGENTS.md (Codex and other agents)
|
|
39
39
|
|
|
40
40
|
```sh
|
|
41
|
-
|
|
42
|
-
cp -R skills/fmddr .claude/skills/
|
|
43
|
-
cp -R skills/fmddr-setup .claude/skills/
|
|
44
|
-
cp -R skills/fmddr-issue .claude/skills/
|
|
45
|
-
cp -R skills/fmddr-release .claude/skills/
|
|
41
|
+
npx fmddr-skills --agents-md # writes AGENTS.md in the current directory
|
|
46
42
|
```
|
|
47
43
|
|
|
48
|
-
|
|
44
|
+
Writes a single `AGENTS.md` file combining all skill instructions.
|
|
45
|
+
Compatible with any agent that picks up `AGENTS.md` from the project root
|
|
46
|
+
(OpenAI Codex, etc.). Re-run to update after a new version is published.
|
|
49
47
|
|
|
50
|
-
|
|
48
|
+
### Manual — Claude Code personal
|
|
51
49
|
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
```sh
|
|
51
|
+
mkdir -p ~/.claude/skills
|
|
52
|
+
cp -R skills/fmddr ~/.claude/skills/
|
|
53
|
+
cp -R skills/fmddr-setup ~/.claude/skills/
|
|
54
|
+
cp -R skills/fmddr-issue ~/.claude/skills/
|
|
55
|
+
cp -R skills/fmddr-release ~/.claude/skills/
|
|
56
|
+
```
|
|
54
57
|
|
|
55
58
|
## Layout
|
|
56
59
|
|
package/bin/install.js
CHANGED
|
@@ -7,22 +7,25 @@ const os = require("os");
|
|
|
7
7
|
|
|
8
8
|
const ALL_SKILLS = ["fmddr", "fmddr-generate-docs", "fmddr-issue", "fmddr-release", "fmddr-setup"];
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
const args = process.argv.slice(2);
|
|
11
|
+
const showHelp = args.includes("--help") || args.includes("-h");
|
|
12
|
+
const cursorFlag = args.includes("--cursor");
|
|
13
|
+
const agentsMdFlag = args.includes("--agents-md");
|
|
14
|
+
const scopeFlag = args.includes("--project") || args.includes("-p") ? "project" : "personal";
|
|
15
|
+
|
|
16
|
+
// Positional args are skill names
|
|
17
|
+
const requested = args.filter(a => !a.startsWith("-"));
|
|
16
18
|
|
|
17
19
|
if (showHelp) {
|
|
18
20
|
console.log(`
|
|
19
|
-
fmddr-skills — install
|
|
21
|
+
fmddr-skills — install fmddr agent skills
|
|
20
22
|
|
|
21
23
|
Usage:
|
|
22
|
-
npx fmddr-skills
|
|
23
|
-
npx fmddr-skills fmddr
|
|
24
|
-
npx fmddr-skills
|
|
25
|
-
npx fmddr-skills --
|
|
24
|
+
npx fmddr-skills install all skills for Claude Code (personal)
|
|
25
|
+
npx fmddr-skills fmddr fmddr-issue install specific skills only
|
|
26
|
+
npx fmddr-skills --project install into .claude/skills/ (project-scoped)
|
|
27
|
+
npx fmddr-skills --cursor install as Cursor rules (.cursor/rules/)
|
|
28
|
+
npx fmddr-skills --agents-md write AGENTS.md in the current directory
|
|
26
29
|
|
|
27
30
|
Available skills: ${ALL_SKILLS.join(", ")}
|
|
28
31
|
`);
|
|
@@ -31,7 +34,6 @@ Available skills: ${ALL_SKILLS.join(", ")}
|
|
|
31
34
|
|
|
32
35
|
const toInstall = requested.length > 0 ? requested : ALL_SKILLS;
|
|
33
36
|
|
|
34
|
-
// Validate
|
|
35
37
|
for (const name of toInstall) {
|
|
36
38
|
if (!ALL_SKILLS.includes(name)) {
|
|
37
39
|
console.error(`Unknown skill: "${name}". Available: ${ALL_SKILLS.join(", ")}`);
|
|
@@ -39,38 +41,117 @@ for (const name of toInstall) {
|
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
const dest =
|
|
44
|
-
scopeFlag === "project"
|
|
45
|
-
? path.join(process.cwd(), ".claude", "skills")
|
|
46
|
-
: path.join(os.homedir(), ".claude", "skills");
|
|
44
|
+
const skillsRoot = path.join(__dirname, "..");
|
|
47
45
|
|
|
48
|
-
|
|
46
|
+
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
49
47
|
|
|
50
|
-
// Copy a directory tree recursively
|
|
51
48
|
function copyDir(src, dst) {
|
|
52
49
|
fs.mkdirSync(dst, { recursive: true });
|
|
53
50
|
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
54
51
|
const srcPath = path.join(src, entry.name);
|
|
55
52
|
const dstPath = path.join(dst, entry.name);
|
|
56
|
-
if (entry.isDirectory())
|
|
57
|
-
|
|
58
|
-
} else {
|
|
59
|
-
fs.copyFileSync(srcPath, dstPath);
|
|
60
|
-
}
|
|
53
|
+
if (entry.isDirectory()) copyDir(srcPath, dstPath);
|
|
54
|
+
else fs.copyFileSync(srcPath, dstPath);
|
|
61
55
|
}
|
|
62
56
|
}
|
|
63
57
|
|
|
64
|
-
|
|
65
|
-
|
|
58
|
+
// Parse the YAML frontmatter block at the top of a SKILL.md.
|
|
59
|
+
// Returns { meta: { key: value, ... }, body: "content after frontmatter" }
|
|
60
|
+
function parseFrontmatter(text) {
|
|
61
|
+
const match = text.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
|
|
62
|
+
if (!match) return { meta: {}, body: text };
|
|
63
|
+
const meta = {};
|
|
64
|
+
for (const line of match[1].split(/\r?\n/)) {
|
|
65
|
+
const kv = line.match(/^(\w+):\s*(.*)/);
|
|
66
|
+
if (kv) meta[kv[1]] = kv[2].trim();
|
|
67
|
+
}
|
|
68
|
+
return { meta, body: match[2] };
|
|
69
|
+
}
|
|
66
70
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
// ── Claude Code install ────────────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
function installClaudeCode(skills) {
|
|
74
|
+
const dest =
|
|
75
|
+
scopeFlag === "project"
|
|
76
|
+
? path.join(process.cwd(), ".claude", "skills")
|
|
77
|
+
: path.join(os.homedir(), ".claude", "skills");
|
|
78
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
79
|
+
for (const name of skills) {
|
|
80
|
+
copyDir(path.join(skillsRoot, name), path.join(dest, name));
|
|
81
|
+
console.log(` ✓ ${name} → ${dest}/${name}`);
|
|
82
|
+
}
|
|
83
|
+
console.log(`\nInstalled ${skills.length} skill${skills.length === 1 ? "" : "s"} into ${dest}`);
|
|
84
|
+
console.log("Open a Claude Code session and type / to confirm they appear.");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ── Cursor rules install ───────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
// Globs that make sense for each skill — auto-activates the rule when
|
|
90
|
+
// the user is working on matching files.
|
|
91
|
+
const CURSOR_GLOBS = {
|
|
92
|
+
"fmddr": "**/*.fmp12, **/*.xml, **/*.fmddr.db",
|
|
93
|
+
"fmddr-setup": "**/*.fmp12, **/*.xml, **/*.fmddr.db",
|
|
94
|
+
"fmddr-issue": "",
|
|
95
|
+
"fmddr-release": "",
|
|
96
|
+
"fmddr-generate-docs": "**/*.fmp12, **/*.xml, **/*.fmddr.db",
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
function installCursor(skills) {
|
|
100
|
+
const dest = path.join(process.cwd(), ".cursor", "rules");
|
|
101
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
102
|
+
for (const name of skills) {
|
|
103
|
+
const skillMd = path.join(skillsRoot, name, "SKILL.md");
|
|
104
|
+
if (!fs.existsSync(skillMd)) continue;
|
|
105
|
+
const { meta, body } = parseFrontmatter(fs.readFileSync(skillMd, "utf8"));
|
|
106
|
+
const description = meta.description
|
|
107
|
+
? meta.description.slice(0, 200) // Cursor shows ~120 chars in the UI
|
|
108
|
+
: `fmddr skill: ${name}`;
|
|
109
|
+
const globs = CURSOR_GLOBS[name] ?? "";
|
|
110
|
+
const frontmatter = [
|
|
111
|
+
"---",
|
|
112
|
+
`description: ${description}`,
|
|
113
|
+
globs ? `globs: ${globs}` : "globs:",
|
|
114
|
+
"alwaysApply: false",
|
|
115
|
+
"---",
|
|
116
|
+
"",
|
|
117
|
+
].join("\n");
|
|
118
|
+
const outPath = path.join(dest, `${name}.mdc`);
|
|
119
|
+
fs.writeFileSync(outPath, frontmatter + body, "utf8");
|
|
120
|
+
console.log(` ✓ ${name} → ${outPath}`);
|
|
121
|
+
}
|
|
122
|
+
console.log(`\nInstalled ${skills.length} Cursor rule${skills.length === 1 ? "" : "s"} into ${dest}`);
|
|
123
|
+
console.log("Restart Cursor or open the Rules panel to confirm they appear.");
|
|
73
124
|
}
|
|
74
125
|
|
|
75
|
-
|
|
76
|
-
|
|
126
|
+
// ── AGENTS.md install ──────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
function installAgentsMd(skills) {
|
|
129
|
+
const outPath = path.join(process.cwd(), "AGENTS.md");
|
|
130
|
+
const sections = [];
|
|
131
|
+
|
|
132
|
+
sections.push(
|
|
133
|
+
"# fmddr Agent Instructions\n",
|
|
134
|
+
"> Generated by `npx fmddr-skills --agents-md`. Re-run to update.\n",
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
for (const name of skills) {
|
|
138
|
+
const skillMd = path.join(skillsRoot, name, "SKILL.md");
|
|
139
|
+
if (!fs.existsSync(skillMd)) continue;
|
|
140
|
+
const { body } = parseFrontmatter(fs.readFileSync(skillMd, "utf8"));
|
|
141
|
+
sections.push(`---\n\n${body.trim()}\n`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
fs.writeFileSync(outPath, sections.join("\n"), "utf8");
|
|
145
|
+
console.log(` ✓ Wrote ${outPath}`);
|
|
146
|
+
console.log(`\nAGENTS.md contains instructions for ${skills.length} skill${skills.length === 1 ? "" : "s"}.`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ── Dispatch ───────────────────────────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
if (cursorFlag) {
|
|
152
|
+
installCursor(toInstall);
|
|
153
|
+
} else if (agentsMdFlag) {
|
|
154
|
+
installAgentsMd(toInstall);
|
|
155
|
+
} else {
|
|
156
|
+
installClaudeCode(toInstall);
|
|
157
|
+
}
|