Conversation
scripts/import-to-lmstudio.py reads prompts.csv and writes each prompt as a config preset JSON file to ~/.lmstudio/config-presets/, making all 1645 prompts available as system-prompt presets in LM Studio. Supports --dev-only (for_devs=TRUE only) and --dry-run flags. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthroughA new Python script is introduced to bulk-import prompt entries from a CSV file into LM Studio configuration preset files. The script reads Changes
Sequence DiagramsequenceDiagram
participant User
participant Script as import-to-lmstudio.py
participant CSV as prompts.csv
participant FSys as File System
participant LMS as LM Studio Config
User->>Script: Run with flags (--dev-only, --dry-run)
Script->>CSV: Open and read
CSV-->>Script: CSV data
loop For each row
Script->>Script: Parse act & prompt
Script->>Script: Apply dev filter if --dev-only
Script->>Script: Slugify act name
Script->>Script: Handle slug collisions
Script->>Script: Build JSON preset
end
alt --dry-run mode
Script->>User: Print preview & exit
else Normal mode
Script->>FSys: Check presets directory
FSys-->>Script: Directory status
loop For each preset
Script->>FSys: Check if file exists
alt File does not exist
Script->>LMS: Write preset JSON
LMS-->>FSys: File created
else File exists
Script->>Script: Count as skipped
end
end
Script->>User: Print summary (found/imported/skipped)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 48da967a36
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| slug = slugify(act) | ||
|
|
||
| # deduplicate: if slug collides, append a counter | ||
| if slug in seen_slugs: | ||
| seen_slugs[slug] += 1 |
There was a problem hiding this comment.
Deduplicate slugs with a case-insensitive key
The collision map currently keys on the original slug casing, which breaks on case-insensitive filesystems (default macOS/Windows): slugs like Life-Coach and Life-coach are treated as different until dest.exists() is checked, so the later prompt is skipped as “already existed.” This repo’s prompts.csv already has case-only name pairs (for example Life Coach/Life coach), so some prompts are silently dropped on those platforms.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
scripts/import-to-lmstudio.py (2)
72-72: Use_for the unused loop variable in dry-run preview.Ruff B007 is valid here;
promptisn’t used in the loop body.🔧 Proposed fix
- for act, prompt in prompts[:5]: + for act, _ in prompts[:5]: print(f" • {act!r} → {slugify(act)}.preset.json")🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/import-to-lmstudio.py` at line 72, The loop "for act, prompt in prompts[:5]:" uses the second tuple element only as an unused variable in the dry-run preview; change it to use the conventional discard identifier by replacing "prompt" with "_" (i.e., "for act, _ in prompts[:5]:") so the unused variable is explicit and Ruff B007 is satisfied; locate this in the dry-run preview loop that iterates over the "prompts" sequence.
55-56: Add explicit handling for missing/unreadable CSV file.A friendly error + non-zero exit is better than a raw traceback for CLI usage.
🔧 Proposed fix
- with open(CSV_PATH, newline="", encoding="utf-8") as f: - reader = csv.DictReader(f) + try: + f = open(CSV_PATH, newline="", encoding="utf-8") + except OSError as exc: + print(f"Error reading {CSV_PATH}: {exc}", file=sys.stderr) + sys.exit(1) + + with f: + reader = csv.DictReader(f)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/import-to-lmstudio.py` around lines 55 - 56, Wrap the CSV open/read block (the "with open(CSV_PATH, newline='', encoding='utf-8') as f: reader = csv.DictReader(f)" code) in a try/except that catches FileNotFoundError, PermissionError, and UnicodeDecodeError (or a generic OSError) and prints a friendly error to stderr (or uses an existing logger) including CSV_PATH and the exception message, then exit with a non-zero status (sys.exit(1)); ensure sys is imported if not already and avoid printing a full traceback.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scripts/import-to-lmstudio.py`:
- Around line 23-26: slugify may return an empty string which leads to invalid
filenames/ids like ".preset.json" and preset ids like "@local:". Update slugify
(or its caller) to provide a fallback when the normalized result is empty (e.g.,
"default" or "preset-{timestamp}"); specifically, change the slugify function to
return a non-empty fallback when text normalizes to "" and ensure the code that
builds the preset filename/ID (the code that uses slugify to form ".preset.json"
and "@local:{slug}") uses that guaranteed non-empty slug.
- Around line 29-33: The identifier currently uses slugify(act) inside
make_preset, which diverges from the deduplicated filename slug; change
make_preset to accept the already-deduplicated slug (e.g., add a slug parameter)
and build the identifier using that slug (replace f"@local:{slugify(act)}" with
f"@local:{slug}"), and update all calls that create presets (where the filename
slug is computed/uniquified) to pass the deduplicated slug into make_preset so
the JSON identifier matches the output filename.
---
Nitpick comments:
In `@scripts/import-to-lmstudio.py`:
- Line 72: The loop "for act, prompt in prompts[:5]:" uses the second tuple
element only as an unused variable in the dry-run preview; change it to use the
conventional discard identifier by replacing "prompt" with "_" (i.e., "for act,
_ in prompts[:5]:") so the unused variable is explicit and Ruff B007 is
satisfied; locate this in the dry-run preview loop that iterates over the
"prompts" sequence.
- Around line 55-56: Wrap the CSV open/read block (the "with open(CSV_PATH,
newline='', encoding='utf-8') as f: reader = csv.DictReader(f)" code) in a
try/except that catches FileNotFoundError, PermissionError, and
UnicodeDecodeError (or a generic OSError) and prints a friendly error to stderr
(or uses an existing logger) including CSV_PATH and the exception message, then
exit with a non-zero status (sys.exit(1)); ensure sys is imported if not already
and avoid printing a full traceback.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: b71104e8-5a4a-4985-84a5-1b20b3be01bf
📒 Files selected for processing (1)
scripts/import-to-lmstudio.py
| def slugify(text: str) -> str: | ||
| text = re.sub(r"[^\w\s-]", "", text).strip() | ||
| text = re.sub(r"[\s_-]+", "-", text) | ||
| return text[:60] |
There was a problem hiding this comment.
Guard against empty slugs before generating identifiers/files.
If act normalizes to an empty slug, Line 95 can write .preset.json and the preset id becomes @local:. Add a fallback slug.
🔧 Proposed fix
def slugify(text: str) -> str:
text = re.sub(r"[^\w\s-]", "", text).strip()
text = re.sub(r"[\s_-]+", "-", text)
- return text[:60]
+ slug = text[:60]
+ return slug or "preset"Also applies to: 95-95
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/import-to-lmstudio.py` around lines 23 - 26, slugify may return an
empty string which leads to invalid filenames/ids like ".preset.json" and preset
ids like "@local:". Update slugify (or its caller) to provide a fallback when
the normalized result is empty (e.g., "default" or "preset-{timestamp}");
specifically, change the slugify function to return a non-empty fallback when
text normalizes to "" and ensure the code that builds the preset filename/ID
(the code that uses slugify to form ".preset.json" and "@local:{slug}") uses
that guaranteed non-empty slug.
| def make_preset(act: str, prompt: str) -> dict: | ||
| return { | ||
| "identifier": f"@local:{slugify(act)}", | ||
| "name": act, | ||
| "changed": True, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify whether base slug collisions exist in prompts.csv (which would trigger identifier duplication risk).
python - <<'PY'
import csv, re, collections
def slugify(text: str) -> str:
text = re.sub(r"[^\w\s-]", "", text).strip()
text = re.sub(r"[\s_-]+", "-", text)
return text[:60]
acts = []
with open("prompts.csv", newline="", encoding="utf-8") as f:
for row in csv.DictReader(f):
act = (row.get("act") or "").strip()
prompt = (row.get("prompt") or "").strip()
if act and prompt:
acts.append(act)
counter = collections.Counter(slugify(a) for a in acts)
dupes = [(k, v) for k, v in counter.items() if v > 1]
print(f"valid_rows={len(acts)}")
print(f"duplicate_base_slugs={len(dupes)}")
for slug, n in dupes[:25]:
print(f"{slug}\t{n}")
PYRepository: f/prompts.chat
Length of output: 297
🏁 Script executed:
cat -n scripts/import-to-lmstudio.pyRepository: f/prompts.chat
Length of output: 4089
🏁 Script executed:
#!/bin/bash
# Identify key sections: lines 29-33 and 85-103
sed -n '20,110p' scripts/import-to-lmstudio.py | cat -nRepository: f/prompts.chat
Length of output: 3422
🏁 Script executed:
#!/bin/bash
# Check prompts.csv structure and size
ls -lh prompts.csv
head -1 prompts.csv | wc -c
# Try reading with increased field size limit
python - <<'PY'
import csv, re, collections
def slugify(text: str) -> str:
text = re.sub(r"[^\w\s-]", "", text).strip()
text = re.sub(r"[\s_-]+", "-", text)
return text[:60]
acts = []
try:
with open("prompts.csv", newline="", encoding="utf-8") as f:
# Increase field size limit
csv.field_size_limit(10_000_000)
for row in csv.DictReader(f):
act = (row.get("act") or "").strip()
prompt = (row.get("prompt") or "").strip()
if act and prompt:
acts.append(act)
counter = collections.Counter(slugify(a) for a in acts)
dupes = [(k, v) for k, v in counter.items() if v > 1]
print(f"valid_rows={len(acts)}")
print(f"duplicate_base_slugs={len(dupes)}")
if dupes:
print("\nDuplicate slugs found:")
for slug, n in dupes[:25]:
print(f" {slug}\t{n}")
else:
print("No duplicate base slugs found")
except Exception as e:
print(f"Error: {e}")
PYRepository: f/prompts.chat
Length of output: 190
Pass the deduplicated slug to make_preset() to ensure the identifier matches the filename.
The identifier field (line 31) always uses slugify(act), but the filename slug (lines 86–91) gets a -<n> suffix when collisions occur. This causes multiple files to have identical identifiers in their JSON, risking preset conflicts in LM Studio. While the current prompts.csv data contains no duplicate base slugs (verified across 1645 rows), the code structure is vulnerable and should be fixed.
🔧 Proposed fix
-def make_preset(act: str, prompt: str) -> dict:
+def make_preset(act: str, prompt: str, slug: str) -> dict:
return {
- "identifier": f"@local:{slugify(act)}",
+ "identifier": f"@local:{slug}",
"name": act,
"changed": True,
"operation": {
@@ -101,2 +101,2 @@ for act, prompt in prompts:
- preset = make_preset(act, prompt)
+ preset = make_preset(act, prompt, slug)
dest.write_text(json.dumps(preset, indent=2, ensure_ascii=False), encoding="utf-8")🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/import-to-lmstudio.py` around lines 29 - 33, The identifier currently
uses slugify(act) inside make_preset, which diverges from the deduplicated
filename slug; change make_preset to accept the already-deduplicated slug (e.g.,
add a slug parameter) and build the identifier using that slug (replace
f"@local:{slugify(act)}" with f"@local:{slug}"), and update all calls that
create presets (where the filename slug is computed/uniquified) to pass the
deduplicated slug into make_preset so the JSON identifier matches the output
filename.
There was a problem hiding this comment.
Pull request overview
Adds a standalone utility script to import the repository’s prompts.csv entries into LM Studio by generating per-prompt config preset JSON files under the user’s LM Studio presets directory.
Changes:
- Introduces
scripts/import-to-lmstudio.pyto parseprompts.csvand write LM Studio config preset JSON files. - Adds CLI flags
--dev-only(filterfor_devs=TRUE) and--dry-run(preview without writing). - Implements basic filename slugging + per-run deduplication for output preset filenames.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| import os | ||
| import re | ||
| import sys |
There was a problem hiding this comment.
os and sys are imported but never used in this script. Removing unused imports will avoid lint/IDE warnings and keeps dependencies clear.
| import os | |
| import re | |
| import sys | |
| import re |
|
|
||
|
|
||
| def main(): | ||
| parser = argparse.ArgumentParser(description="Import prompts.chat CSV into LM Studio presets") |
There was a problem hiding this comment.
The argparse description says "Import prompts.chat CSV..." but the script reads prompts.csv (and the module docstring also refers to prompts.csv). This looks like a copy/paste typo; update the description so --help output matches actual behavior.
| parser = argparse.ArgumentParser(description="Import prompts.chat CSV into LM Studio presets") | |
| parser = argparse.ArgumentParser(description="Import prompts.csv into LM Studio presets") |
|
This looks nice. Can you update the PR according to the agent reviews? |
Summary
scripts/import-to-lmstudio.py— a utility script that readsprompts.csvand writes each prompt as a config preset JSON file to~/.lmstudio/config-presets/--dev-onlyflag to import onlyfor_devs=TRUEprompts, and--dry-runto preview without writing filesUsage
Then restart LM Studio — presets appear in the presets panel, each pre-loaded with the corresponding system prompt.
Test plan
--dry-runand verify expected preset names are printed~/.lmstudio/config-presets/--dev-onlyonly importsfor_devs=TRUErows🤖 Generated with Claude Code
Summary by CodeRabbit