fmddr-skills 0.4.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.md ADDED
@@ -0,0 +1,83 @@
1
+ # fmddr — Claude Code skills
2
+
3
+ Four agent skills for working with [`fmddr`](https://github.com/proofsh/fmddr)
4
+ from Claude Code:
5
+
6
+ | Skill | Triggers on | Purpose |
7
+ | --------------- | ------------------------------------------------------------------------- | -------------------------------------------------------------- |
8
+ | `fmddr-setup` | "set up fmddr", "install fmddr", "index my DDR", fmddr not found in PATH | Install fmddr and build the index for a solution |
9
+ | `fmddr` | FileMaker / DDR / fmp12 / Profile.xml / "where is X used" / refactor talk | Recognize when to reach for `fmddr` and use it correctly |
10
+ | `fmddr-issue` | "report a bug", "file an issue", "feature request" — for fmddr | Open a high-quality issue at proofsh/fmddr (with severity) |
11
+ | `fmddr-release` | "release fmddr", "cut a patch", "publish to PyPI" | End-to-end release flow: bump, changelog, build, tag, publish |
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
+ ## Install
19
+
20
+ ### npx (no git required)
21
+
22
+ ```sh
23
+ npx fmddr-skills # install all four skills (personal)
24
+ npx fmddr-skills fmddr fmddr-setup # install specific skills only
25
+ npx fmddr-skills --project # project-scoped (.claude/skills/)
26
+ ```
27
+
28
+ ### Manual — personal (one user, all projects)
29
+
30
+ ```sh
31
+ mkdir -p ~/.claude/skills
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/
36
+ ```
37
+
38
+ ### Manual — project-scoped (this repo only)
39
+
40
+ ```sh
41
+ mkdir -p .claude/skills
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/
46
+ ```
47
+
48
+ Project skills override personal skills with the same name.
49
+
50
+ ## Verify
51
+
52
+ In a Claude Code session, type `/` and look for the skills, or ask
53
+ "is the fmddr skill loaded?" — Claude will list available skills.
54
+
55
+ ## Layout
56
+
57
+ ```
58
+ skills/
59
+ ├── package.json ← npm package (fmddr-skills)
60
+ ├── bin/
61
+ │ └── install.js ← npx entry point
62
+ ├── fmddr-setup/
63
+ │ └── SKILL.md
64
+ ├── fmddr/
65
+ │ └── SKILL.md
66
+ ├── fmddr-issue/
67
+ │ ├── SKILL.md
68
+ │ └── templates/
69
+ │ ├── bug-report.md
70
+ │ └── feature-request.md
71
+ └── fmddr-release/
72
+ └── SKILL.md
73
+ ```
74
+
75
+ ## Updating
76
+
77
+ ```sh
78
+ npx fmddr-skills@latest # via npm (when a new version is published)
79
+
80
+ # or from a local clone:
81
+ cd /path/to/fmddr && git pull
82
+ npx --prefix skills . fmddr-skills
83
+ ```
package/bin/install.js ADDED
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const os = require("os");
7
+
8
+ const ALL_SKILLS = ["fmddr", "fmddr-issue", "fmddr-release", "fmddr-setup"];
9
+
10
+ // Parse args: `npx fmddr-skills [skill1 skill2 ...]`
11
+ const requested = process.argv.slice(2).filter(a => !a.startsWith("-"));
12
+ const showHelp = process.argv.includes("--help") || process.argv.includes("-h");
13
+ const scopeFlag = process.argv.includes("--project") || process.argv.includes("-p")
14
+ ? "project"
15
+ : "personal";
16
+
17
+ if (showHelp) {
18
+ console.log(`
19
+ fmddr-skills — install Claude Code skills for fmddr
20
+
21
+ Usage:
22
+ npx fmddr-skills install all skills (personal)
23
+ npx fmddr-skills fmddr install one skill
24
+ npx fmddr-skills fmddr fmddr-issue install two skills
25
+ npx fmddr-skills --project install into .claude/skills/ (project-scoped)
26
+
27
+ Available skills: ${ALL_SKILLS.join(", ")}
28
+ `);
29
+ process.exit(0);
30
+ }
31
+
32
+ const toInstall = requested.length > 0 ? requested : ALL_SKILLS;
33
+
34
+ // Validate
35
+ for (const name of toInstall) {
36
+ if (!ALL_SKILLS.includes(name)) {
37
+ console.error(`Unknown skill: "${name}". Available: ${ALL_SKILLS.join(", ")}`);
38
+ process.exit(1);
39
+ }
40
+ }
41
+
42
+ // Resolve destination
43
+ const dest =
44
+ scopeFlag === "project"
45
+ ? path.join(process.cwd(), ".claude", "skills")
46
+ : path.join(os.homedir(), ".claude", "skills");
47
+
48
+ fs.mkdirSync(dest, { recursive: true });
49
+
50
+ // Copy a directory tree recursively
51
+ function copyDir(src, dst) {
52
+ fs.mkdirSync(dst, { recursive: true });
53
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
54
+ const srcPath = path.join(src, entry.name);
55
+ const dstPath = path.join(dst, entry.name);
56
+ if (entry.isDirectory()) {
57
+ copyDir(srcPath, dstPath);
58
+ } else {
59
+ fs.copyFileSync(srcPath, dstPath);
60
+ }
61
+ }
62
+ }
63
+
64
+ const skillsRoot = path.join(__dirname, "..");
65
+ let installed = 0;
66
+
67
+ for (const name of toInstall) {
68
+ const src = path.join(skillsRoot, name);
69
+ const dst = path.join(dest, name);
70
+ copyDir(src, dst);
71
+ console.log(` ✓ ${name} → ${dst}`);
72
+ installed++;
73
+ }
74
+
75
+ console.log(`\nInstalled ${installed} skill${installed === 1 ? "" : "s"} into ${dest}`);
76
+ console.log("Open a Claude Code session and type / to confirm they appear.");
package/fmddr/SKILL.md ADDED
@@ -0,0 +1,174 @@
1
+ ---
2
+ name: fmddr
3
+ description: Reach for this skill whenever the user is working with a FileMaker solution — analyzing, auditing, refactoring, documenting, or cleaning up an `.fmp12` file or its DDR (Database Design Report). Triggers on FileMaker Pro, FMP, fmp12, Claris FileMaker, DDR, Profile.xml, Summary.xml, fmxmlsnippet, custom functions, table occurrences, layouts, scripts, ExecuteSQL audits, "where is field X used", "what scripts call this", "find unused fields/scripts/layouts", "what breaks if I delete this", impact analysis, dead-code hunts, FileMaker refactors, and anything that smells like a FileMaker DDR XML (look for `<FMPReport type="Report">`, `<FMPReport type="Summary">`, files ending `.xml` near `.fmp12`, or directories like `<solution>_Files/`). The `fmddr` CLI parses a DDR once into a queryable SQLite index, then answers structural and cross-reference questions in milliseconds with stable JSON output. Also covers filing bug reports and feature requests against proofsh/fmddr on GitHub.
4
+ ---
5
+
6
+ # fmddr — FileMaker DDR analyzer
7
+
8
+ `fmddr` turns a FileMaker DDR XML into a SQLite index, then answers
9
+ structural questions about the solution: where is this field used, what
10
+ scripts call this script, what breaks if I remove this, what's dead code,
11
+ where do `ExecuteSQL` and `Evaluate` show up. Output is JSON-first and
12
+ stable, so it composes with shell pipelines and agent loops.
13
+
14
+ This skill exists for two reasons:
15
+
16
+ 1. **Recognize when to reach for it.** A grep over a multi-thousand-script
17
+ FileMaker solution is the wrong tool. `fmddr` is the right one.
18
+ 2. **Use it well.** Index once, query many times, dispatch on the JSON
19
+ `kind` field rather than parsing prose.
20
+
21
+ ## When to reach for fmddr
22
+
23
+ Trigger on **any** of these signals — don't wait for the user to name
24
+ the tool:
25
+
26
+ ### Surface signals (filenames / contents)
27
+ - `Profile.xml`, `Summary.xml`, anything matching `<FMPReport type="Report">`
28
+ or `<FMPReport type="Summary">` at the top of an XML file.
29
+ - A `*.fmp12` file in the working tree (FileMaker solution).
30
+ - An `<solution>_Files/` directory next to a `Summary.xml` (multi-file
31
+ DDR export).
32
+ - Mentions of "DDR", "Database Design Report", "fmxmlsnippet",
33
+ "BaseTable", "TableOccurrence", "ScriptStep", "CustomFunction",
34
+ "ValueList", "ScriptTrigger" — these are FileMaker schema terms.
35
+ - An existing `*.fmddr.db` SQLite file.
36
+
37
+ ### Question shapes
38
+ | If the user asks… | Use… |
39
+ | ---------------------------------------------------------- | --------------------------------------------------- |
40
+ | "Where is `Customer::Email` used?" | `fmddr field references` / `script-refs` / `on-layouts` |
41
+ | "What scripts call *Save Case Request*?" | `fmddr script called-by` |
42
+ | "What does this script call?" | `fmddr script calls` / `chain --format mermaid` |
43
+ | "If I delete this field, what breaks?" | `fmddr impact field` |
44
+ | "What's dead in this file?" | `fmddr unused --kind {field,script,layout,custom_function,value_list,table_occurrence}` |
45
+ | "Where do we use `ExecuteSQL` / `Evaluate`?" | `fmddr risky-steps --has execute_sql` |
46
+ | "What custom functions does X use?" | `fmddr cf called-by` / `fmddr cf calls` |
47
+ | "What relationships connect these two TOs?" | `fmddr graph path <from-TO> <to-TO>` |
48
+ | "Find scripts named like *_OLD or BACKUP_*" | `fmddr cleanup-candidates` |
49
+ | "Are there orphan refs?" | `fmddr orphans` |
50
+ | "Search for anything called `…`" | `fmddr search "<fts5-query>"` |
51
+
52
+ ### Anti-signals (do **not** invoke)
53
+ - The user is working in a FileMaker GUI and just wants to chat about
54
+ design. `fmddr` is for analysis of an exported DDR — it doesn't talk
55
+ to FileMaker Server.
56
+ - The XML is from a non-FileMaker tool (no `<FMPReport>` root).
57
+
58
+ ## Install fmddr (if missing)
59
+
60
+ ```sh
61
+ fmddr --version || pipx install fmddr
62
+ # or, in a project venv:
63
+ fmddr --version || pip install fmddr
64
+ ```
65
+
66
+ Python 3.11+ required. Optional `[lxml]` extra speeds up parsing on huge
67
+ DDRs.
68
+
69
+ ## The two-step contract
70
+
71
+ Every analysis follows the same shape:
72
+
73
+ ### 1. Index (once per DDR)
74
+
75
+ ```sh
76
+ fmddr index path/to/Profile.xml --out ./profile.fmddr.db
77
+ # multi-file solution:
78
+ fmddr index path/to/Summary.xml --out ./solution.fmddr.db
79
+ ```
80
+
81
+ `index` is idempotent and streaming — safe to re-run. ~7s per 500MB DDR.
82
+
83
+ If a `*.fmddr.db` already exists in CWD and is newer than the source XML,
84
+ skip re-indexing.
85
+
86
+ ### 2. Query (millisecond-cheap)
87
+
88
+ ```sh
89
+ fmddr stats --db ./profile.fmddr.db
90
+ fmddr field script-refs "Customer::Email"
91
+ fmddr impact field "Customer::__UID"
92
+ fmddr unused --kind script
93
+ ```
94
+
95
+ DB resolution order: `--db` flag → `$FMDDR_DB` → unique `*.fmddr.db` in
96
+ CWD → error. Set `FMDDR_DB` once at the start of an agent loop and drop
97
+ the flag.
98
+
99
+ ## Output contract
100
+
101
+ Every command emits a stable envelope. Dispatch on `kind`, not prose.
102
+
103
+ ```json
104
+ {
105
+ "kind": "field.script-refs",
106
+ "version": 1,
107
+ "ddr": {"path": "...", "sha256": "...", "indexed_at": "..."},
108
+ "query": {"field": {"id": 8712, "name": "...", "table": "..."}},
109
+ "result": [...],
110
+ "result_count": 1,
111
+ "truncated": false
112
+ }
113
+ ```
114
+
115
+ Format flags: `-o {json,jsonl,table,markdown,csv,tsv}`. JSON is the
116
+ default when stdout is not a TTY.
117
+
118
+ Exit codes worth handling:
119
+
120
+ - `0` ok
121
+ - `1` not found (no field/script matched the token)
122
+ - `2` ambiguous (qualify with `Table::Field` or use `--by-id`)
123
+ - `64` bad usage
124
+ - `70` internal error
125
+
126
+ ## Common multi-step recipes
127
+
128
+ **"Find every place this field is used, including inside calc text on
129
+ other fields and CFs":**
130
+
131
+ ```sh
132
+ fmddr field references "Customer::Email" # all refs, denormalized
133
+ fmddr field script-refs "Customer::Email" # scripts + step index
134
+ fmddr field on-layouts "Customer::Email" # layouts
135
+ fmddr field field-refs "Customer::Email" # other fields whose calcs name it
136
+ ```
137
+
138
+ **"Audit risky scripts":**
139
+
140
+ ```sh
141
+ fmddr risky-steps --has execute_sql
142
+ fmddr risky-steps --has evaluate
143
+ ```
144
+
145
+ **"Map a script's call chain":**
146
+
147
+ ```sh
148
+ fmddr script chain "Save Case Request" --depth 3 --format mermaid
149
+ ```
150
+
151
+ **"What can I safely delete?":**
152
+
153
+ ```sh
154
+ for kind in field script layout custom_function value_list table_occurrence; do
155
+ fmddr unused --kind "$kind" -o json
156
+ done
157
+ ```
158
+
159
+ ## Filing bugs & feature requests against fmddr
160
+
161
+ If `fmddr` itself misbehaves, ask the user whether to file an issue at
162
+ **https://github.com/proofsh/fmddr**. Use the companion skill
163
+ `fmddr-issue` (same install bundle). It includes:
164
+
165
+ - Bug-report and feature-request templates with the exact diagnostics
166
+ to capture.
167
+ - A four-level severity scale (`critical` / `high` / `medium` / `low`)
168
+ mapped to GitHub labels.
169
+ - A privacy checklist — DDRs leak business logic, so the skill scrubs
170
+ table/field names, paths, and calc text before posting.
171
+
172
+ The skill posts via the GitHub MCP `mcp__github__issue_write` (scoped to
173
+ `proofsh/fmddr`), with `gh issue create` as a fallback. Always confirm
174
+ title and body with the user before posting.
@@ -0,0 +1,121 @@
1
+ ---
2
+ name: fmddr-issue
3
+ description: Companion to the `fmddr` skill. Use when the user wants to report a bug, file an issue, or request a feature for the `fmddr` CLI/library (FileMaker DDR analyzer). Gathers diagnostics, classifies severity for bugs on a four-level scale, then opens an issue at proofsh/fmddr on GitHub. Triggers on phrases like "fmddr bug", "report fmddr issue", "fmddr feature request", "file an issue for fmddr", or when an fmddr command crashed and the user wants it filed upstream.
4
+ ---
5
+
6
+ # fmddr — file a bug report or feature request
7
+
8
+ Help the user open a high-quality issue at https://github.com/proofsh/fmddr.
9
+ Two paths: **bug report** and **feature request**. Always confirm the title
10
+ and body with the user before posting.
11
+
12
+ ## When to invoke
13
+
14
+ - User says "report this as a bug to fmddr", "file an issue", "open an
15
+ fmddr ticket", "this should be fixed upstream", etc.
16
+ - An `fmddr` command just crashed or returned a wrong result and the user
17
+ wants it tracked.
18
+ - User asks for a new query/command/output format and wants it on the
19
+ roadmap.
20
+
21
+ If unsure whether the user wants a bug or a feature, ask once — the two
22
+ templates differ.
23
+
24
+ ## Workflow
25
+
26
+ ### 1. Classify
27
+
28
+ Ask the user (or infer from context) which one this is:
29
+
30
+ - **Bug** — something behaves incorrectly, crashes, returns wrong data,
31
+ or violates the documented contract.
32
+ - **Feature request** — new command, new flag, new output format, new
33
+ schema, performance ask, docs gap.
34
+
35
+ ### 2. Gather diagnostics (bugs only)
36
+
37
+ Run these in the user's working directory and capture the output. Skip
38
+ gracefully if a command isn't available.
39
+
40
+ ```sh
41
+ fmddr --version
42
+ python --version
43
+ uname -srm # or `sw_vers` on macOS
44
+ fmddr stats --db <their db> -o json # if they have an indexed DB
45
+ ```
46
+
47
+ For a crash, also collect:
48
+
49
+ - The exact command they ran (with paths redacted if sensitive).
50
+ - The full traceback or error output.
51
+ - DDR shape if relevant: file size, FileMaker version that produced it,
52
+ whether it's a single `Profile.xml` or a multi-file `Summary.xml`.
53
+ - Whether `--db`, `$FMDDR_DB`, or auto-discovery was in use.
54
+
55
+ **Never** include DDR contents, table/field/script names, or proprietary
56
+ schema in the issue body unless the user explicitly approves. DDRs leak
57
+ business logic. Offer to redact identifiers to `Table_A`, `Field_1`, etc.
58
+
59
+ ### 3. Severity scale (bugs only)
60
+
61
+ Pick one with the user. Map it to a GitHub label.
62
+
63
+ | Severity | Label | When to use |
64
+ | ------------ | ------------------ | ------------------------------------------------------------------------------------------------- |
65
+ | **Critical** | `severity:critical`| Data corruption, wrong query results, index build silently drops rows, security issue. No workaround. |
66
+ | **High** | `severity:high` | Common command crashes or returns no results when it should. Workaround exists but is painful. |
67
+ | **Medium** | `severity:medium` | Edge-case crash, output format glitch, misleading error message, perf regression on large DDRs. |
68
+ | **Low** | `severity:low` | Cosmetic, typo, doc inaccuracy, minor UX nit. |
69
+
70
+ If the repo doesn't have these labels yet, fall back to the closest
71
+ existing one (`bug`, `enhancement`) and mention the severity in the body.
72
+
73
+ ### 4. Draft the issue
74
+
75
+ Use the matching template under `templates/`:
76
+
77
+ - `templates/bug-report.md`
78
+ - `templates/feature-request.md`
79
+
80
+ Fill in every section. Strip empty headings. Keep titles under 70 chars
81
+ and front-load the affected command:
82
+
83
+ - Bug: `script chain: crashes on cyclic call graph` ✓
84
+ - Bug: `there is a problem when I run a script chain command` ✗
85
+ - Feature: `field references: add --since flag for time-windowed audits` ✓
86
+
87
+ ### 5. Confirm with the user
88
+
89
+ Show the rendered title, labels, and body. Ask: "Post this to
90
+ proofsh/fmddr?" Wait for an explicit yes before calling any GitHub tool.
91
+
92
+ ### 6. Post the issue
93
+
94
+ Use the GitHub MCP tool, scoped to this repo:
95
+
96
+ ```
97
+ mcp__github__issue_write
98
+ owner: "proofsh"
99
+ repo: "fmddr"
100
+ action: "create"
101
+ title: "<title>"
102
+ body: "<rendered template>"
103
+ labels: ["bug", "severity:high"] # or ["enhancement"] for features
104
+ ```
105
+
106
+ If `mcp__github__issue_write` is unavailable in the session, fall back to
107
+ `gh issue create --repo proofsh/fmddr --title ... --body-file ... --label ...`.
108
+ Do **not** attempt to push an issue file into the repo as a commit.
109
+
110
+ After posting, return the issue URL to the user.
111
+
112
+ ## Privacy checklist (run before posting)
113
+
114
+ - [ ] No DDR file contents pasted (XML snippets, calc text, script bodies).
115
+ - [ ] No real table/field/script/layout names unless the user okayed it.
116
+ - [ ] No file paths containing usernames or client names (`/Users/jane/AcmeCorp/...`).
117
+ - [ ] No customer data in error messages.
118
+ - [ ] Tracebacks scrubbed of absolute paths if they leak identity.
119
+
120
+ If anything's unclear, ask the user before posting — once an issue is up
121
+ it's effectively public.
@@ -0,0 +1,56 @@
1
+ ## Summary
2
+
3
+ <one sentence: what command, what went wrong>
4
+
5
+ ## Severity
6
+
7
+ **<critical | high | medium | low>** — <one-line justification>
8
+
9
+ ## Reproduction
10
+
11
+ ```sh
12
+ # exact command(s); redact paths and identifiers as needed
13
+ fmddr <subcommand> ...
14
+ ```
15
+
16
+ **Input:** <single Profile.xml | Summary.xml multi-file | indexed DB only>
17
+ **DDR size (if relevant):** <MB>
18
+ **FileMaker version that produced the DDR:** <e.g. FMP 2024 19.6.1>
19
+
20
+ ## Expected behavior
21
+
22
+ <what should have happened — quote docs/README/--help if applicable>
23
+
24
+ ## Actual behavior
25
+
26
+ <what happened instead>
27
+
28
+ ```
29
+ <error output / traceback / wrong JSON, fenced>
30
+ ```
31
+
32
+ ## Environment
33
+
34
+ - `fmddr --version`: <x.y.z>
35
+ - Python: <3.11.x>
36
+ - OS: <macOS 14.5 / Ubuntu 22.04 / Windows 11 + WSL2 / ...>
37
+ - Install method: <pip | pipx | from source>
38
+ - Optional extras: <none | [lxml]>
39
+ - DB resolution: <--db flag | $FMDDR_DB | auto-discovery>
40
+
41
+ ## Index metadata (if applicable)
42
+
43
+ Output of `fmddr stats -o json` (or note "no DB built yet"):
44
+
45
+ ```json
46
+ <paste here>
47
+ ```
48
+
49
+ ## Workaround
50
+
51
+ <known workaround, or "none">
52
+
53
+ ## Additional context
54
+
55
+ <links to related issues, prior discussion, screenshots of Rich-table
56
+ output if a rendering bug, etc.>
@@ -0,0 +1,50 @@
1
+ ## Summary
2
+
3
+ <one sentence: what new capability, on which subcommand>
4
+
5
+ ## Problem / use case
6
+
7
+ <the concrete analysis you're trying to do that fmddr can't currently
8
+ support, or supports awkwardly. Real-world scenarios beat abstract
9
+ "it would be nice if…" framing.>
10
+
11
+ ## Proposed shape
12
+
13
+ <sketch the CLI surface — subcommand, flags, a sample invocation — and
14
+ what the JSON envelope `result` array would contain. Don't over-design;
15
+ a rough shape is fine.>
16
+
17
+ ```sh
18
+ fmddr <subcommand> ... --new-flag ...
19
+ ```
20
+
21
+ ```json
22
+ {
23
+ "kind": "<proposed kind>",
24
+ "result": [
25
+ { "...": "..." }
26
+ ]
27
+ }
28
+ ```
29
+
30
+ ## Alternatives considered
31
+
32
+ <existing fmddr commands you tried, shell pipelines that almost work,
33
+ why they fall short>
34
+
35
+ ## Scope hints (optional)
36
+
37
+ - Which query module would own this? (`tables`, `fields`, `scripts`,
38
+ `layouts`, `cf`, `graph`, `impact`, `search`)
39
+ - Does it need new index columns or only new SQL over existing tables?
40
+ - Does it need a new ref kind?
41
+
42
+ ## Environment
43
+
44
+ - `fmddr --version`: <x.y.z>
45
+ - DDR shape this would run against: <single Profile.xml | Summary.xml>
46
+
47
+ ## Additional context
48
+
49
+ <links, prior art from other DDR tools (FMPerception, BaseElements,
50
+ InspectorPro), references to FileMaker docs>
@@ -0,0 +1,265 @@
1
+ ---
2
+ name: fmddr-release
3
+ description: Cut a new fmddr release end-to-end — bump version, update CHANGELOG, refresh docs if user-visible surface changed, tag, build sdist+wheel, publish to PyPI, and create the GitHub release with artifacts attached. Use when the user says "release fmddr", "cut a patch release", "release X.Y.Z", "ship 0.2.x", "publish to PyPI", "tag a release", or after a bugfix/feature has landed on `main` and they want it shipped. Handles both bugfix → PR → merge → release flows and direct-on-main releases. Knows which docs auto-sync from the repo root (version banner, CHANGELOG page) and which are hand-written and must be updated for new commands/flags/output shapes before shipping.
4
+ ---
5
+
6
+ # fmddr — cut a release
7
+
8
+ The release process touches **four** version surfaces that must all agree.
9
+ Forgetting one (commonly `src/fmddr/__init__.py`) ships a release where
10
+ `fmddr --version` lies. Walk every step in order; do not skip ahead.
11
+
12
+ ## Version surfaces — keep these in sync
13
+
14
+ | File | Format |
15
+ | -------------------------- | ---------------------------- |
16
+ | `pyproject.toml` | `version = "X.Y.Z"` |
17
+ | `src/fmddr/__init__.py` | `__version__ = "X.Y.Z"` |
18
+ | `CHANGELOG.md` | `## [X.Y.Z] — YYYY-MM-DD` |
19
+ | Git tag | `vX.Y.Z` (note the `v`) |
20
+
21
+ Pre-1.0 versioning per `CHANGELOG.md`: `0.MINOR.PATCH` — `MINOR` bumps for
22
+ new public surface, `PATCH` for bugfixes. Pick the right one before
23
+ starting; don't ask mid-flow.
24
+
25
+ ## Docs — what self-syncs vs. what doesn't
26
+
27
+ The Fumadocs site under `docs/` auto-syncs two things from the repo root
28
+ at build time, so the release process never edits them by hand:
29
+
30
+ - `docs/lib/version.ts` reads `version` from `pyproject.toml` → drives
31
+ the home-page version banner.
32
+ - `docs/scripts/sync-changelog.mjs` mirrors `CHANGELOG.md` →
33
+ `docs/content/docs/changelog.md` (gitignored). Runs before `dev` /
34
+ `build`.
35
+
36
+ So a pure bugfix patch release needs **no** docs commits — once the docs
37
+ deploy fires, both surfaces pick up the new version and changelog
38
+ automatically.
39
+
40
+ Everything else is hand-written and **must** be updated before cutting
41
+ the release. The doc edits belong in the feature/fix PR, not the
42
+ `Release X.Y.Z` commit. Check each that applies to the release:
43
+
44
+ - New command, subcommand, or flag → `docs/content/docs/reference/commands.md`
45
+ - Output envelope / JSON shape change → `docs/content/docs/reference/envelope.md`
46
+ - Architectural change (index schema, build sequence) →
47
+ `docs/content/docs/reference/architecture.md`
48
+ - New workflow worth a guide → `docs/content/docs/guides/*.md`
49
+ - Quickstart-affecting change → `docs/content/docs/quickstart.md`
50
+ - Landing-page copy change → `docs/app/(home)/page.tsx`
51
+
52
+ If the release contains user-visible changes and the PR didn't touch
53
+ docs, **stop the release** and ask the user whether docs should be
54
+ updated first. Don't ship undocumented public surface.
55
+
56
+ ## Pre-flight
57
+
58
+ Run these and stop if any fail:
59
+
60
+ ```sh
61
+ git branch --show-current # must be `main`
62
+ git status # must be clean
63
+ git pull --ff-only # latest main
64
+ python -m pytest -q # green
65
+ ```
66
+
67
+ If the release is for a bugfix that's still on a feature branch, finish
68
+ the PR → merge → checkout main → pull first. **Don't release from a
69
+ feature branch.**
70
+
71
+ Decide the new version (`X.Y.Z`) and today's date (`YYYY-MM-DD`) up front.
72
+
73
+ ## Release steps
74
+
75
+ ### 1. Bump versions and update CHANGELOG
76
+
77
+ Edit all three files in one commit:
78
+
79
+ - `pyproject.toml` → `version = "X.Y.Z"`
80
+ - `src/fmddr/__init__.py` → `__version__ = "X.Y.Z"`
81
+ - `CHANGELOG.md`:
82
+ - Insert a new section `## [X.Y.Z] — YYYY-MM-DD` directly after
83
+ `## [Unreleased]`.
84
+ - Move any unreleased entries into it (or write the section fresh if
85
+ `[Unreleased]` was empty).
86
+ - Use the same `### Added` / `### Fixed` / `### Changed` headings as
87
+ prior releases.
88
+ - Link to the closing PR(s)/issue(s) inline:
89
+ `([#6](https://github.com/proofsh/fmddr/pull/6))` or
90
+ `([#5](https://github.com/proofsh/fmddr/issues/5))`.
91
+
92
+ Sanity-check after editing:
93
+
94
+ ```sh
95
+ grep -E '"0\.[0-9]+\.[0-9]+"|__version__' pyproject.toml src/fmddr/__init__.py
96
+ grep -E '^## \[' CHANGELOG.md | head -3
97
+ ```
98
+
99
+ All three should show the new version; the CHANGELOG's first dated entry
100
+ should be `[X.Y.Z]`.
101
+
102
+ ### 2. Commit
103
+
104
+ ```sh
105
+ git add pyproject.toml src/fmddr/__init__.py CHANGELOG.md
106
+ git commit -m "Release X.Y.Z"
107
+ ```
108
+
109
+ The message is literally `Release X.Y.Z` — match prior commits
110
+ (`Release 0.2.2`, `Release 0.2.3`). The user will push when they're
111
+ ready; do **not** push without explicit instruction.
112
+
113
+ ### 3. Build artifacts
114
+
115
+ ```sh
116
+ rm -f dist/fmddr-X.Y.Z* # clean any stale local builds
117
+ python -m build
118
+ ls -lh dist/fmddr-X.Y.Z*
119
+ ```
120
+
121
+ Expected output: `fmddr-X.Y.Z.tar.gz` and `fmddr-X.Y.Z-py3-none-any.whl`.
122
+ If `python -m build` isn't available, install it: `pip install build`.
123
+
124
+ Verify the wheel actually contains the bumped version:
125
+
126
+ ```sh
127
+ python -c "import zipfile, sys; z=zipfile.ZipFile('dist/fmddr-X.Y.Z-py3-none-any.whl'); print(z.read('fmddr/__init__.py').decode())"
128
+ ```
129
+
130
+ Must print `__version__ = "X.Y.Z"`. If it prints the old version, step 1
131
+ missed `__init__.py`.
132
+
133
+ ### 4. Push the release commit
134
+
135
+ Only after the user authorizes the push:
136
+
137
+ ```sh
138
+ git push origin main
139
+ ```
140
+
141
+ Per the user's global git-push policy, every push needs explicit
142
+ authorization in the current message. A previous "yes, release it"
143
+ doesn't carry forward.
144
+
145
+ ### 5. Tag and push the tag
146
+
147
+ ```sh
148
+ git tag -a vX.Y.Z -m "fmddr X.Y.Z"
149
+ git push origin vX.Y.Z
150
+ ```
151
+
152
+ Annotated tag (`-a`), `v`-prefixed name. Match prior tags (`v0.2.2`,
153
+ `v0.2.3`).
154
+
155
+ ### 6. Upload to PyPI
156
+
157
+ ```sh
158
+ twine upload dist/fmddr-X.Y.Z*
159
+ ```
160
+
161
+ Uses the user's `~/.pypirc` or env vars for credentials. If twine isn't
162
+ installed: `pip install twine`. **Never** print or commit credentials.
163
+
164
+ If the user wants to dry-run first:
165
+ `twine upload --repository testpypi dist/fmddr-X.Y.Z*`.
166
+
167
+ ### 7. Create the GitHub release
168
+
169
+ ```sh
170
+ gh release create vX.Y.Z \
171
+ --repo proofsh/fmddr \
172
+ --title "vX.Y.Z" \
173
+ --generate-notes \
174
+ dist/fmddr-X.Y.Z.tar.gz \
175
+ dist/fmddr-X.Y.Z-py3-none-any.whl
176
+ ```
177
+
178
+ `--generate-notes` produces the "What's Changed" + compare-link body that
179
+ matches prior releases. If the user wants custom notes, replace
180
+ `--generate-notes` with `--notes-file <file>` or `--notes "..."` and
181
+ paste the relevant `CHANGELOG.md` section.
182
+
183
+ Both built artifacts must be attached — that's how prior releases look
184
+ (`gh release view v0.2.3` shows `.whl` + `.tar.gz` assets).
185
+
186
+ ### 8. Verify
187
+
188
+ ```sh
189
+ gh release view vX.Y.Z --repo proofsh/fmddr
190
+ pip index versions fmddr 2>/dev/null || pip install --dry-run fmddr==X.Y.Z
191
+ ```
192
+
193
+ Confirm:
194
+ - The release page lists both artifacts.
195
+ - PyPI serves the new version (may take ~30s after `twine upload`).
196
+ - The git tag points at the `Release X.Y.Z` commit (`git show vX.Y.Z`).
197
+
198
+ Report the release URL and PyPI URL back to the user.
199
+
200
+ ### 9. Upgrade the user's local install
201
+
202
+ The user has `fmddr` installed as a `uv` tool at `~/.local/bin/fmddr`
203
+ (symlink into `~/.local/share/uv/tools/fmddr/`). It is **separate** from
204
+ this repo's `.venv` and won't pick up the new release on its own.
205
+
206
+ After PyPI confirms the new version is live:
207
+
208
+ ```sh
209
+ uv tool upgrade fmddr
210
+ ~/.local/bin/fmddr --version # must print X.Y.Z
211
+ ```
212
+
213
+ If `uv` isn't available or the install method has changed, fall back
214
+ based on what `which fmddr` resolves to:
215
+
216
+ - `~/.local/share/uv/tools/...` → `uv tool upgrade fmddr`
217
+ - `pipx`-managed (`pipx list` shows it) → `pipx upgrade fmddr`
218
+ - Plain `pip install --user` → `pip install --user --upgrade fmddr`
219
+
220
+ Run `fmddr --version` (no path) at the end to confirm the user's `$PATH`
221
+ resolves to the upgraded binary, not the dev `.venv` shim.
222
+
223
+ ## When the bugfix is still on a feature branch
224
+
225
+ The full flow when starting from an open issue:
226
+
227
+ 1. Create branch `fix/<slug>` (or `feat/<slug>` for minor bumps).
228
+ 2. Implement + tests + CHANGELOG entry under `[Unreleased]` only —
229
+ **don't** bump version numbers in the bugfix commit.
230
+ 3. PR with `Closes #<issue>`; wait for merge.
231
+ 4. `git checkout main && git pull --ff-only`.
232
+ 5. Run this skill from step 1, moving the `[Unreleased]` block into the
233
+ new versioned section.
234
+
235
+ This keeps the bugfix PR reviewable on its own merits and the release
236
+ commit small and mechanical.
237
+
238
+ ## Common failure modes
239
+
240
+ - **Version drift between `pyproject.toml` and `__init__.py`.** Has
241
+ happened on this repo before. The wheel-version check in step 3 is the
242
+ guardrail — run it.
243
+ - **Tagged the wrong commit.** If the tag is on a commit that doesn't
244
+ bump versions (e.g. you tagged the merge commit before bumping
245
+ `__init__.py`), delete the local tag (`git tag -d vX.Y.Z`), retag, and
246
+ force-push the tag. Only acceptable if no one has pulled it yet —
247
+ otherwise cut a `X.Y.(Z+1)` instead.
248
+ - **Forgot to build before tagging.** The tag should point at the commit
249
+ whose source matches what you upload to PyPI. Build from the tagged
250
+ commit, not from a dirty tree.
251
+ - **Twine 401 / 403.** Token wrong or scope mismatch. Don't paste the
252
+ token into chat. Tell the user to refresh `~/.pypirc` or
253
+ `TWINE_PASSWORD` and re-run step 6.
254
+ - **`gh release create` fails on tag not found.** Push the tag (step 5)
255
+ before step 7.
256
+
257
+ ## Privacy / safety
258
+
259
+ - Don't run `twine upload` or `gh release create` without explicit user
260
+ go-ahead — both are public, irreversible publishes.
261
+ - Don't print PyPI tokens, GitHub tokens, or any credential. If a
262
+ command fails with an auth error, ask the user to fix their env; never
263
+ ask them to paste the secret.
264
+ - Don't force-push `main` to "fix" a botched release. Cut a new patch
265
+ version instead.
@@ -0,0 +1,106 @@
1
+ ---
2
+ name: fmddr-setup
3
+ description: Install and initialize fmddr for a FileMaker solution. Use when the user asks to "set up fmddr", "install fmddr", "get fmddr ready", "index my DDR", "index my SaveAsXML", or when fmddr commands fail because the CLI isn't installed or no index exists yet. Also triggers when the user drops a .xml DDR/SaveAsXML into the working directory and wants to start analyzing it, or when `fmddr` is not found in PATH.
4
+ ---
5
+
6
+ # fmddr-setup — install fmddr and index a FileMaker solution
7
+
8
+ This skill gets fmddr installed and an index built in one pass, so the
9
+ user is ready to run any fmddr query immediately after.
10
+
11
+ ## Step 1 — Check whether fmddr is installed
12
+
13
+ ```sh
14
+ fmddr --version
15
+ ```
16
+
17
+ - If it prints a version → already installed, skip to Step 2.
18
+ - If it fails (`command not found`) → install it (Step 1a).
19
+
20
+ ### Step 1a — Install fmddr
21
+
22
+ Try installers in this order, stopping at the first that works:
23
+
24
+ ```sh
25
+ uv tool install fmddr # preferred (fast, isolated)
26
+ pipx install fmddr # good alternative
27
+ pip install --user fmddr # fallback
28
+ ```
29
+
30
+ After installing, confirm:
31
+ ```sh
32
+ fmddr --version
33
+ ```
34
+
35
+ If `fmddr` still isn't on `$PATH` after `uv tool install`, the `uv`
36
+ tools bin dir may not be in `$PATH`. Tell the user:
37
+ ```
38
+ Add ~/.local/bin to your PATH, or run: export PATH="$HOME/.local/bin:$PATH"
39
+ ```
40
+
41
+ ## Step 2 — Find the DDR or SaveAsXML
42
+
43
+ Look in the current directory and one level down for:
44
+
45
+ 1. `*.xml` files whose first 200 bytes match `<FMPReport` — these are
46
+ DDR or SaveAsXML exports.
47
+ 2. A `Summary.xml` next to a `*_Files/` directory — multi-file DDR.
48
+ 3. Any file the user has already named (trust user-provided paths).
49
+
50
+ Use `find . -maxdepth 2 -name "*.xml"` if nothing is immediately visible,
51
+ then peek the first line of candidates with `head -c 200`.
52
+
53
+ **DDR vs. SaveAsXML — how to tell:**
54
+ - `<FMPReport type="Report">` → standard DDR → use `fmddr index`
55
+ - `<FMPReport type="Summary">` → multi-file DDR Summary → use `fmddr index` (auto-detected)
56
+ - File is UTF-16 LE (BOM `FF FE`) AND contains `<FMSaveAsXML` or the
57
+ `<FMPReport` tag is absent → SaveAsXML → use `fmddr index-savexml`
58
+ - If `fmddr index` fails with a ParseError on a UTF-16 file, retry with
59
+ `fmddr index-savexml`
60
+
61
+ ## Step 3 — Build the index
62
+
63
+ ```sh
64
+ # Standard DDR (Report or Summary):
65
+ fmddr index path/to/Profile.xml
66
+
67
+ # SaveAsXML (whole-file clone export):
68
+ fmddr index-savexml path/to/MyFile.xml
69
+ ```
70
+
71
+ This writes `<basename>.fmddr.db` next to the source file (or in the
72
+ current directory if the source is read-only).
73
+
74
+ Large files (300–700 MB) take 5–20 seconds. Tell the user what's
75
+ happening:
76
+ ```
77
+ Indexing… (this takes ~10s for a 300 MB DDR)
78
+ ```
79
+
80
+ ## Step 4 — Confirm and orient the user
81
+
82
+ Run `fmddr stats` to show what was indexed:
83
+
84
+ ```sh
85
+ fmddr stats
86
+ ```
87
+
88
+ Then report a one-line summary, e.g.:
89
+ ```
90
+ Index ready: 247 tables, 2304 scripts, 870 layouts — run any fmddr command to start.
91
+ ```
92
+
93
+ Suggest two starter commands based on what's in the file:
94
+ - If the solution has many scripts: `fmddr unused --kind script`
95
+ - If it has a big field count: `fmddr unused --kind field`
96
+ - Always: `fmddr risky-steps --has execute_sql`
97
+
98
+ ## Common problems
99
+
100
+ | Symptom | Fix |
101
+ |---------|-----|
102
+ | `command not found: fmddr` after `uv tool install` | `export PATH="$HOME/.local/bin:$PATH"` |
103
+ | `ParseError: not well-formed` on index | File is SaveAsXML → use `fmddr index-savexml` |
104
+ | `No .fmddr.db found` on query commands | Index not built yet — run Step 3 |
105
+ | `Ambiguous: multiple scripts match` on a query | Add the script ID: `fmddr script show 900` |
106
+ | Very slow index on a 600 MB file | Normal — large SaveAsXML files take up to 30s |
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "fmddr-skills",
3
+ "version": "0.4.0",
4
+ "description": "Claude Code skills for fmddr — FileMaker DDR analyzer",
5
+ "keywords": ["claude-code", "claude", "skills", "filemaker", "fmddr", "ddr"],
6
+ "homepage": "https://fmddr.dev",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/proofsh/fmddr.git",
10
+ "directory": "skills"
11
+ },
12
+ "license": "MIT",
13
+ "bin": {
14
+ "fmddr-skills": "./bin/install.js"
15
+ },
16
+ "files": [
17
+ "bin/",
18
+ "fmddr/",
19
+ "fmddr-issue/",
20
+ "fmddr-release/",
21
+ "fmddr-setup/"
22
+ ],
23
+ "engines": {
24
+ "node": ">=18"
25
+ }
26
+ }