llm-ide-rules 0.4.0__py3-none-any.whl → 0.5.0__py3-none-any.whl
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.
- llm_ide_rules/__init__.py +6 -4
- llm_ide_rules/commands/explode.py +101 -9
- llm_ide_rules/commands/implode.py +209 -27
- llm_ide_rules/sections.json +10 -1
- {llm_ide_rules-0.4.0.dist-info → llm_ide_rules-0.5.0.dist-info}/METADATA +1 -1
- llm_ide_rules-0.5.0.dist-info/RECORD +12 -0
- llm_ide_rules-0.4.0.dist-info/RECORD +0 -12
- {llm_ide_rules-0.4.0.dist-info → llm_ide_rules-0.5.0.dist-info}/WHEEL +0 -0
- {llm_ide_rules-0.4.0.dist-info → llm_ide_rules-0.5.0.dist-info}/entry_points.txt +0 -0
llm_ide_rules/__init__.py
CHANGED
|
@@ -4,11 +4,11 @@ import typer
|
|
|
4
4
|
from typing_extensions import Annotated
|
|
5
5
|
|
|
6
6
|
from llm_ide_rules.commands.explode import explode_main
|
|
7
|
-
from llm_ide_rules.commands.implode import cursor, github
|
|
7
|
+
from llm_ide_rules.commands.implode import cursor, github, claude, gemini
|
|
8
8
|
from llm_ide_rules.commands.download import download_main
|
|
9
9
|
from llm_ide_rules.commands.delete import delete_main
|
|
10
10
|
|
|
11
|
-
__version__ = "0.
|
|
11
|
+
__version__ = "0.5.0"
|
|
12
12
|
|
|
13
13
|
app = typer.Typer(
|
|
14
14
|
name="llm_ide_rules",
|
|
@@ -23,8 +23,10 @@ app.command("delete", help="Remove downloaded LLM instruction files")(delete_mai
|
|
|
23
23
|
|
|
24
24
|
# Create implode sub-typer
|
|
25
25
|
implode_app = typer.Typer(help="Bundle rule files into a single instruction file")
|
|
26
|
-
implode_app.command("cursor", help="Bundle Cursor rules into a single file")(cursor)
|
|
27
|
-
implode_app.command("github", help="Bundle GitHub/Copilot instructions into a single file")(github)
|
|
26
|
+
implode_app.command("cursor", help="Bundle Cursor rules and commands into a single file")(cursor)
|
|
27
|
+
implode_app.command("github", help="Bundle GitHub/Copilot instructions and prompts into a single file")(github)
|
|
28
|
+
implode_app.command("claude", help="Bundle Claude Code commands into a single file")(claude)
|
|
29
|
+
implode_app.command("gemini", help="Bundle Gemini CLI commands into a single file")(gemini)
|
|
28
30
|
app.add_typer(implode_app, name="implode")
|
|
29
31
|
|
|
30
32
|
def main():
|
|
@@ -192,8 +192,87 @@ description: '{description}'
|
|
|
192
192
|
f.write(line)
|
|
193
193
|
|
|
194
194
|
|
|
195
|
-
def
|
|
196
|
-
"""
|
|
195
|
+
def write_cursor_command(content_lines, filename, commands_dir, section_name=None):
|
|
196
|
+
"""Write a Cursor command file (plain markdown, no frontmatter)."""
|
|
197
|
+
filepath = os.path.join(commands_dir, filename + ".md")
|
|
198
|
+
|
|
199
|
+
trimmed = trim_content(content_lines)
|
|
200
|
+
|
|
201
|
+
# Strip the header from content (first line starting with ##)
|
|
202
|
+
filtered_content = []
|
|
203
|
+
found_header = False
|
|
204
|
+
for line in trimmed:
|
|
205
|
+
if not found_header and line.startswith("## "):
|
|
206
|
+
found_header = True
|
|
207
|
+
continue
|
|
208
|
+
filtered_content.append(line)
|
|
209
|
+
|
|
210
|
+
# Trim again after removing header
|
|
211
|
+
filtered_content = trim_content(filtered_content)
|
|
212
|
+
|
|
213
|
+
with open(filepath, "w") as f:
|
|
214
|
+
for line in filtered_content:
|
|
215
|
+
f.write(line)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def write_claude_command(content_lines, filename, commands_dir, section_name=None):
|
|
219
|
+
"""Write a Claude Code command file (plain markdown, no frontmatter)."""
|
|
220
|
+
filepath = os.path.join(commands_dir, filename + ".md")
|
|
221
|
+
|
|
222
|
+
trimmed = trim_content(content_lines)
|
|
223
|
+
|
|
224
|
+
# Strip the header from content (first line starting with ##)
|
|
225
|
+
filtered_content = []
|
|
226
|
+
found_header = False
|
|
227
|
+
for line in trimmed:
|
|
228
|
+
if not found_header and line.startswith("## "):
|
|
229
|
+
found_header = True
|
|
230
|
+
continue
|
|
231
|
+
filtered_content.append(line)
|
|
232
|
+
|
|
233
|
+
# Trim again after removing header
|
|
234
|
+
filtered_content = trim_content(filtered_content)
|
|
235
|
+
|
|
236
|
+
with open(filepath, "w") as f:
|
|
237
|
+
for line in filtered_content:
|
|
238
|
+
f.write(line)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def write_gemini_command(content_lines, filename, commands_dir, section_name=None):
|
|
242
|
+
"""Write a Gemini CLI command file (TOML format)."""
|
|
243
|
+
filepath = os.path.join(commands_dir, filename + ".toml")
|
|
244
|
+
|
|
245
|
+
description, filtered_content = extract_description_and_filter_content(
|
|
246
|
+
content_lines, ""
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
# Strip the header from content (first line starting with ##)
|
|
250
|
+
final_content = []
|
|
251
|
+
found_header = False
|
|
252
|
+
for line in filtered_content:
|
|
253
|
+
if not found_header and line.startswith("## "):
|
|
254
|
+
found_header = True
|
|
255
|
+
continue
|
|
256
|
+
final_content.append(line)
|
|
257
|
+
|
|
258
|
+
# Trim and convert to string
|
|
259
|
+
final_content = trim_content(final_content)
|
|
260
|
+
content_str = "".join(final_content).strip()
|
|
261
|
+
|
|
262
|
+
with open(filepath, "w") as f:
|
|
263
|
+
f.write(f'name = "{filename}"\n')
|
|
264
|
+
if description:
|
|
265
|
+
f.write(f'description = "{description}"\n')
|
|
266
|
+
else:
|
|
267
|
+
f.write(f'description = "{section_name or filename}"\n')
|
|
268
|
+
f.write('\n[command]\n')
|
|
269
|
+
f.write('shell = """\n')
|
|
270
|
+
f.write(content_str)
|
|
271
|
+
f.write('\n"""\n')
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def process_unmapped_section(lines, section_name, cursor_commands_dir, github_prompts_dir, claude_commands_dir, gemini_commands_dir):
|
|
275
|
+
"""Process an unmapped section as a manually applied rule (command)."""
|
|
197
276
|
section_content = extract_section(lines, f"## {section_name}")
|
|
198
277
|
if any(line.strip() for line in section_content):
|
|
199
278
|
filename = header_to_filename(section_name)
|
|
@@ -203,9 +282,11 @@ def process_unmapped_section(lines, section_name, rules_dir, github_prompts_dir)
|
|
|
203
282
|
section_content, section_name
|
|
204
283
|
)
|
|
205
284
|
|
|
206
|
-
# Create
|
|
207
|
-
|
|
285
|
+
# Create command files (same as None case in SECTION_GLOBS)
|
|
286
|
+
write_cursor_command(section_content, filename, cursor_commands_dir, section_name)
|
|
208
287
|
write_github_prompt(section_content, filename, github_prompts_dir, section_name)
|
|
288
|
+
write_claude_command(section_content, filename, claude_commands_dir, section_name)
|
|
289
|
+
write_gemini_command(section_content, filename, gemini_commands_dir, section_name)
|
|
209
290
|
return True
|
|
210
291
|
return False
|
|
211
292
|
|
|
@@ -235,12 +316,18 @@ def explode_main(
|
|
|
235
316
|
|
|
236
317
|
# Work in current directory ($PWD)
|
|
237
318
|
rules_dir = os.path.join(os.getcwd(), ".cursor", "rules")
|
|
319
|
+
cursor_commands_dir = os.path.join(os.getcwd(), ".cursor", "commands")
|
|
238
320
|
copilot_dir = os.path.join(os.getcwd(), ".github", "instructions")
|
|
239
321
|
github_prompts_dir = os.path.join(os.getcwd(), ".github", "prompts")
|
|
322
|
+
claude_commands_dir = os.path.join(os.getcwd(), ".claude", "commands")
|
|
323
|
+
gemini_commands_dir = os.path.join(os.getcwd(), ".gemini", "commands")
|
|
240
324
|
|
|
241
325
|
os.makedirs(rules_dir, exist_ok=True)
|
|
326
|
+
os.makedirs(cursor_commands_dir, exist_ok=True)
|
|
242
327
|
os.makedirs(copilot_dir, exist_ok=True)
|
|
243
328
|
os.makedirs(github_prompts_dir, exist_ok=True)
|
|
329
|
+
os.makedirs(claude_commands_dir, exist_ok=True)
|
|
330
|
+
os.makedirs(gemini_commands_dir, exist_ok=True)
|
|
244
331
|
|
|
245
332
|
input_path = os.path.join(os.getcwd(), input_file)
|
|
246
333
|
|
|
@@ -295,18 +382,20 @@ alwaysApply: true
|
|
|
295
382
|
section_content,
|
|
296
383
|
)
|
|
297
384
|
else:
|
|
298
|
-
# It's a
|
|
299
|
-
|
|
385
|
+
# It's a command - create command files using the original section name for header
|
|
386
|
+
write_cursor_command(section_content, filename, cursor_commands_dir, section_name)
|
|
300
387
|
write_github_prompt(
|
|
301
388
|
section_content, filename, github_prompts_dir, section_name
|
|
302
389
|
)
|
|
390
|
+
write_claude_command(section_content, filename, claude_commands_dir, section_name)
|
|
391
|
+
write_gemini_command(section_content, filename, gemini_commands_dir, section_name)
|
|
303
392
|
|
|
304
393
|
# Check for sections in mapping that don't exist in the file
|
|
305
394
|
for section_name in SECTION_GLOBS:
|
|
306
395
|
if section_name not in found_sections:
|
|
307
396
|
logger.warning("Section not found in file", section=section_name)
|
|
308
397
|
|
|
309
|
-
# Process unmapped sections as manually applied rules (
|
|
398
|
+
# Process unmapped sections as manually applied rules (commands)
|
|
310
399
|
processed_unmapped = set()
|
|
311
400
|
for line in lines:
|
|
312
401
|
if line.startswith("## "):
|
|
@@ -321,16 +410,19 @@ alwaysApply: true
|
|
|
321
410
|
and section_name not in processed_unmapped
|
|
322
411
|
):
|
|
323
412
|
if process_unmapped_section(
|
|
324
|
-
lines, section_name,
|
|
413
|
+
lines, section_name, cursor_commands_dir, github_prompts_dir, claude_commands_dir, gemini_commands_dir
|
|
325
414
|
):
|
|
326
415
|
processed_unmapped.add(section_name)
|
|
327
416
|
|
|
328
417
|
logger.info(
|
|
329
418
|
"Explode operation completed",
|
|
330
419
|
cursor_rules=rules_dir,
|
|
420
|
+
cursor_commands=cursor_commands_dir,
|
|
331
421
|
copilot_instructions=copilot_dir,
|
|
332
422
|
github_prompts=github_prompts_dir,
|
|
423
|
+
claude_commands=claude_commands_dir,
|
|
424
|
+
gemini_commands=gemini_commands_dir,
|
|
333
425
|
)
|
|
334
426
|
typer.echo(
|
|
335
|
-
"Created
|
|
427
|
+
"Created rules and commands in .cursor/, .claude/, .github/, and .gemini/ directories"
|
|
336
428
|
)
|
|
@@ -57,15 +57,18 @@ def get_ordered_files_github(file_list, section_globs_keys):
|
|
|
57
57
|
return ordered_files
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
def bundle_cursor_rules(rules_dir, output_file, section_globs):
|
|
61
|
-
"""Bundle Cursor rule files into a single file."""
|
|
60
|
+
def bundle_cursor_rules(rules_dir, commands_dir, output_file, section_globs):
|
|
61
|
+
"""Bundle Cursor rule and command files into a single file."""
|
|
62
62
|
rule_files = list(Path(rules_dir).glob("*.mdc"))
|
|
63
|
+
command_files = list(Path(commands_dir).glob("*.md"))
|
|
64
|
+
|
|
63
65
|
general = [f for f in rule_files if f.stem == "general"]
|
|
64
66
|
others = [f for f in rule_files if f.stem != "general"]
|
|
65
67
|
|
|
66
68
|
# Order the non-general files based on section_globs
|
|
67
69
|
ordered_others = get_ordered_files(others, section_globs.keys())
|
|
68
|
-
|
|
70
|
+
ordered_commands = get_ordered_files(command_files, section_globs.keys())
|
|
71
|
+
ordered = general + ordered_others + ordered_commands
|
|
69
72
|
|
|
70
73
|
def resolve_header_from_stem(stem):
|
|
71
74
|
"""Return the canonical header for a given filename stem.
|
|
@@ -117,14 +120,32 @@ def strip_header(text):
|
|
|
117
120
|
return text
|
|
118
121
|
|
|
119
122
|
|
|
120
|
-
def bundle_github_instructions(instructions_dir, output_file, section_globs):
|
|
121
|
-
"""Bundle GitHub instruction files into a single file."""
|
|
123
|
+
def bundle_github_instructions(instructions_dir, prompts_dir, output_file, section_globs):
|
|
124
|
+
"""Bundle GitHub instruction and prompt files into a single file."""
|
|
122
125
|
copilot_general = Path(os.getcwd()) / ".github" / "copilot-instructions.md"
|
|
123
126
|
instr_files = list(Path(instructions_dir).glob("*.instructions.md"))
|
|
127
|
+
prompt_files = list(Path(prompts_dir).glob("*.prompt.md"))
|
|
124
128
|
|
|
125
129
|
# Order the instruction files based on section_globs
|
|
126
130
|
# We need to create a modified version that strips .instructions from stems for ordering
|
|
127
|
-
|
|
131
|
+
ordered_instructions = get_ordered_files_github(instr_files, section_globs.keys())
|
|
132
|
+
|
|
133
|
+
# For prompts, we need to handle .prompt suffix similarly
|
|
134
|
+
ordered_prompts = []
|
|
135
|
+
prompt_dict = {}
|
|
136
|
+
for f in prompt_files:
|
|
137
|
+
base_stem = f.stem.replace(".prompt", "")
|
|
138
|
+
prompt_dict[base_stem] = f
|
|
139
|
+
|
|
140
|
+
for section_name in section_globs.keys():
|
|
141
|
+
filename = header_to_filename(section_name)
|
|
142
|
+
if filename in prompt_dict:
|
|
143
|
+
ordered_prompts.append(prompt_dict[filename])
|
|
144
|
+
del prompt_dict[filename]
|
|
145
|
+
|
|
146
|
+
# Add remaining prompts alphabetically
|
|
147
|
+
remaining_prompts = sorted(prompt_dict.values(), key=lambda p: p.name)
|
|
148
|
+
ordered_prompts.extend(remaining_prompts)
|
|
128
149
|
|
|
129
150
|
def resolve_header_from_stem(stem):
|
|
130
151
|
"""Return the canonical header for a given filename stem.
|
|
@@ -144,7 +165,7 @@ def bundle_github_instructions(instructions_dir, output_file, section_globs):
|
|
|
144
165
|
if content:
|
|
145
166
|
out.write(content)
|
|
146
167
|
out.write("\n\n")
|
|
147
|
-
for instr_file in
|
|
168
|
+
for instr_file in ordered_instructions:
|
|
148
169
|
with open(instr_file, "r") as f:
|
|
149
170
|
content = f.read().strip()
|
|
150
171
|
if not content:
|
|
@@ -157,6 +178,107 @@ def bundle_github_instructions(instructions_dir, output_file, section_globs):
|
|
|
157
178
|
out.write(f"## {header}\n\n")
|
|
158
179
|
out.write(content)
|
|
159
180
|
out.write("\n\n")
|
|
181
|
+
for prompt_file in ordered_prompts:
|
|
182
|
+
with open(prompt_file, "r") as f:
|
|
183
|
+
content = f.read().strip()
|
|
184
|
+
if not content:
|
|
185
|
+
continue
|
|
186
|
+
content = strip_yaml_frontmatter(content)
|
|
187
|
+
content = strip_header(content)
|
|
188
|
+
# Use canonical header names from SECTION_GLOBS when available
|
|
189
|
+
base_stem = prompt_file.stem.replace(".prompt", "")
|
|
190
|
+
header = resolve_header_from_stem(base_stem)
|
|
191
|
+
out.write(f"## {header}\n\n")
|
|
192
|
+
out.write(content)
|
|
193
|
+
out.write("\n\n")
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def bundle_claude_commands(commands_dir, output_file, section_globs):
|
|
197
|
+
"""Bundle Claude Code command files into a single file."""
|
|
198
|
+
command_files = list(Path(commands_dir).glob("*.md"))
|
|
199
|
+
ordered_commands = get_ordered_files(command_files, section_globs.keys())
|
|
200
|
+
|
|
201
|
+
def resolve_header_from_stem(stem):
|
|
202
|
+
"""Return the canonical header for a given filename stem.
|
|
203
|
+
|
|
204
|
+
Prefer exact header names from section_globs (preserves acronyms like FastAPI, TypeScript).
|
|
205
|
+
Fallback to title-casing the filename when not found in section_globs.
|
|
206
|
+
"""
|
|
207
|
+
for section_name in section_globs.keys():
|
|
208
|
+
if header_to_filename(section_name) == stem:
|
|
209
|
+
return section_name
|
|
210
|
+
return filename_to_header(stem)
|
|
211
|
+
|
|
212
|
+
with open(output_file, "w") as out:
|
|
213
|
+
for command_file in ordered_commands:
|
|
214
|
+
with open(command_file, "r") as f:
|
|
215
|
+
content = f.read().strip()
|
|
216
|
+
if not content:
|
|
217
|
+
continue
|
|
218
|
+
# Claude commands don't have frontmatter, just content
|
|
219
|
+
header = resolve_header_from_stem(command_file.stem)
|
|
220
|
+
out.write(f"## {header}\n\n")
|
|
221
|
+
out.write(content)
|
|
222
|
+
out.write("\n\n")
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def strip_toml_metadata(text):
|
|
226
|
+
"""Extract content from TOML command.shell block."""
|
|
227
|
+
lines = text.splitlines()
|
|
228
|
+
in_shell_block = False
|
|
229
|
+
content_lines = []
|
|
230
|
+
|
|
231
|
+
for line in lines:
|
|
232
|
+
if line.strip() == '[command]':
|
|
233
|
+
in_shell_block = True
|
|
234
|
+
continue
|
|
235
|
+
if in_shell_block:
|
|
236
|
+
if line.strip().startswith('shell = """'):
|
|
237
|
+
# Start of shell content
|
|
238
|
+
# Check if content is on same line
|
|
239
|
+
after_start = line.split('"""', 1)[1] if '"""' in line else ""
|
|
240
|
+
if after_start.strip():
|
|
241
|
+
content_lines.append(after_start)
|
|
242
|
+
continue
|
|
243
|
+
if line.strip() == '"""' or line.strip().endswith('"""'):
|
|
244
|
+
# End of shell content
|
|
245
|
+
if line.strip() != '"""':
|
|
246
|
+
# Content on same line as closing
|
|
247
|
+
content_lines.append(line.rsplit('"""', 1)[0])
|
|
248
|
+
break
|
|
249
|
+
content_lines.append(line)
|
|
250
|
+
|
|
251
|
+
return "\n".join(content_lines).strip()
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def bundle_gemini_commands(commands_dir, output_file, section_globs):
|
|
255
|
+
"""Bundle Gemini CLI command files into a single file."""
|
|
256
|
+
command_files = list(Path(commands_dir).glob("*.toml"))
|
|
257
|
+
ordered_commands = get_ordered_files(command_files, section_globs.keys())
|
|
258
|
+
|
|
259
|
+
def resolve_header_from_stem(stem):
|
|
260
|
+
"""Return the canonical header for a given filename stem.
|
|
261
|
+
|
|
262
|
+
Prefer exact header names from section_globs (preserves acronyms like FastAPI, TypeScript).
|
|
263
|
+
Fallback to title-casing the filename when not found in section_globs.
|
|
264
|
+
"""
|
|
265
|
+
for section_name in section_globs.keys():
|
|
266
|
+
if header_to_filename(section_name) == stem:
|
|
267
|
+
return section_name
|
|
268
|
+
return filename_to_header(stem)
|
|
269
|
+
|
|
270
|
+
with open(output_file, "w") as out:
|
|
271
|
+
for command_file in ordered_commands:
|
|
272
|
+
with open(command_file, "r") as f:
|
|
273
|
+
content = f.read().strip()
|
|
274
|
+
if not content:
|
|
275
|
+
continue
|
|
276
|
+
# Extract content from TOML shell block
|
|
277
|
+
content = strip_toml_metadata(content)
|
|
278
|
+
header = resolve_header_from_stem(command_file.stem)
|
|
279
|
+
out.write(f"## {header}\n\n")
|
|
280
|
+
out.write(content)
|
|
281
|
+
out.write("\n\n")
|
|
160
282
|
|
|
161
283
|
|
|
162
284
|
def cursor(
|
|
@@ -164,28 +286,29 @@ def cursor(
|
|
|
164
286
|
verbose: Annotated[bool, typer.Option("--verbose", "-v", help="Enable verbose logging")] = False,
|
|
165
287
|
config: Annotated[str, typer.Option("--config", "-c", help="Custom configuration file path")] = None,
|
|
166
288
|
):
|
|
167
|
-
"""Bundle Cursor rules into a single file."""
|
|
289
|
+
"""Bundle Cursor rules and commands into a single file."""
|
|
168
290
|
if verbose:
|
|
169
291
|
logging.basicConfig(level=logging.DEBUG)
|
|
170
292
|
structlog.configure(
|
|
171
293
|
wrapper_class=structlog.make_filtering_bound_logger(logging.DEBUG),
|
|
172
294
|
)
|
|
173
|
-
|
|
295
|
+
|
|
174
296
|
# Load section globs (with optional custom config)
|
|
175
297
|
SECTION_GLOBS = load_section_globs(config)
|
|
176
|
-
|
|
298
|
+
|
|
177
299
|
rules_dir = os.path.join(os.getcwd(), ".cursor", "rules")
|
|
300
|
+
commands_dir = os.path.join(os.getcwd(), ".cursor", "commands")
|
|
178
301
|
output_path = os.path.join(os.getcwd(), output)
|
|
179
|
-
|
|
180
|
-
logger.info("Bundling Cursor rules", rules_dir=rules_dir, output_file=output_path, config=config)
|
|
181
|
-
|
|
302
|
+
|
|
303
|
+
logger.info("Bundling Cursor rules and commands", rules_dir=rules_dir, commands_dir=commands_dir, output_file=output_path, config=config)
|
|
304
|
+
|
|
182
305
|
if not Path(rules_dir).exists():
|
|
183
306
|
logger.error("Cursor rules directory not found", rules_dir=rules_dir)
|
|
184
307
|
raise typer.Exit(1)
|
|
185
|
-
|
|
186
|
-
bundle_cursor_rules(rules_dir, output_path, SECTION_GLOBS)
|
|
187
|
-
logger.info("Cursor rules bundled successfully", output_file=output_path)
|
|
188
|
-
typer.echo(f"Bundled cursor rules into {output}")
|
|
308
|
+
|
|
309
|
+
bundle_cursor_rules(rules_dir, commands_dir, output_path, SECTION_GLOBS)
|
|
310
|
+
logger.info("Cursor rules and commands bundled successfully", output_file=output_path)
|
|
311
|
+
typer.echo(f"Bundled cursor rules and commands into {output}")
|
|
189
312
|
|
|
190
313
|
|
|
191
314
|
def github(
|
|
@@ -193,26 +316,85 @@ def github(
|
|
|
193
316
|
verbose: Annotated[bool, typer.Option("--verbose", "-v", help="Enable verbose logging")] = False,
|
|
194
317
|
config: Annotated[str, typer.Option("--config", "-c", help="Custom configuration file path")] = None,
|
|
195
318
|
):
|
|
196
|
-
"""Bundle GitHub/Copilot instructions into a single file."""
|
|
319
|
+
"""Bundle GitHub/Copilot instructions and prompts into a single file."""
|
|
197
320
|
if verbose:
|
|
198
321
|
logging.basicConfig(level=logging.DEBUG)
|
|
199
322
|
structlog.configure(
|
|
200
323
|
wrapper_class=structlog.make_filtering_bound_logger(logging.DEBUG),
|
|
201
324
|
)
|
|
202
|
-
|
|
325
|
+
|
|
203
326
|
# Load section globs (with optional custom config)
|
|
204
327
|
SECTION_GLOBS = load_section_globs(config)
|
|
205
|
-
|
|
328
|
+
|
|
206
329
|
instructions_dir = os.path.join(os.getcwd(), ".github", "instructions")
|
|
330
|
+
prompts_dir = os.path.join(os.getcwd(), ".github", "prompts")
|
|
207
331
|
output_path = os.path.join(os.getcwd(), output)
|
|
208
|
-
|
|
209
|
-
logger.info("Bundling GitHub instructions", instructions_dir=instructions_dir, output_file=output_path, config=config)
|
|
210
|
-
|
|
332
|
+
|
|
333
|
+
logger.info("Bundling GitHub instructions and prompts", instructions_dir=instructions_dir, prompts_dir=prompts_dir, output_file=output_path, config=config)
|
|
334
|
+
|
|
211
335
|
if not Path(instructions_dir).exists():
|
|
212
336
|
logger.error("GitHub instructions directory not found", instructions_dir=instructions_dir)
|
|
213
337
|
raise typer.Exit(1)
|
|
214
|
-
|
|
215
|
-
bundle_github_instructions(instructions_dir, output_path, SECTION_GLOBS)
|
|
216
|
-
logger.info("GitHub instructions bundled successfully", output_file=output_path)
|
|
217
|
-
typer.echo(f"Bundled github instructions into {output}")
|
|
338
|
+
|
|
339
|
+
bundle_github_instructions(instructions_dir, prompts_dir, output_path, SECTION_GLOBS)
|
|
340
|
+
logger.info("GitHub instructions and prompts bundled successfully", output_file=output_path)
|
|
341
|
+
typer.echo(f"Bundled github instructions and prompts into {output}")
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def claude(
|
|
345
|
+
output: Annotated[str, typer.Argument(help="Output file")] = "instructions.md",
|
|
346
|
+
verbose: Annotated[bool, typer.Option("--verbose", "-v", help="Enable verbose logging")] = False,
|
|
347
|
+
config: Annotated[str, typer.Option("--config", "-c", help="Custom configuration file path")] = None,
|
|
348
|
+
):
|
|
349
|
+
"""Bundle Claude Code commands into a single file."""
|
|
350
|
+
if verbose:
|
|
351
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
352
|
+
structlog.configure(
|
|
353
|
+
wrapper_class=structlog.make_filtering_bound_logger(logging.DEBUG),
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
# Load section globs (with optional custom config)
|
|
357
|
+
SECTION_GLOBS = load_section_globs(config)
|
|
358
|
+
|
|
359
|
+
commands_dir = os.path.join(os.getcwd(), ".claude", "commands")
|
|
360
|
+
output_path = os.path.join(os.getcwd(), output)
|
|
361
|
+
|
|
362
|
+
logger.info("Bundling Claude Code commands", commands_dir=commands_dir, output_file=output_path, config=config)
|
|
363
|
+
|
|
364
|
+
if not Path(commands_dir).exists():
|
|
365
|
+
logger.error("Claude Code commands directory not found", commands_dir=commands_dir)
|
|
366
|
+
raise typer.Exit(1)
|
|
367
|
+
|
|
368
|
+
bundle_claude_commands(commands_dir, output_path, SECTION_GLOBS)
|
|
369
|
+
logger.info("Claude Code commands bundled successfully", output_file=output_path)
|
|
370
|
+
typer.echo(f"Bundled claude commands into {output}")
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def gemini(
|
|
374
|
+
output: Annotated[str, typer.Argument(help="Output file")] = "instructions.md",
|
|
375
|
+
verbose: Annotated[bool, typer.Option("--verbose", "-v", help="Enable verbose logging")] = False,
|
|
376
|
+
config: Annotated[str, typer.Option("--config", "-c", help="Custom configuration file path")] = None,
|
|
377
|
+
):
|
|
378
|
+
"""Bundle Gemini CLI commands into a single file."""
|
|
379
|
+
if verbose:
|
|
380
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
381
|
+
structlog.configure(
|
|
382
|
+
wrapper_class=structlog.make_filtering_bound_logger(logging.DEBUG),
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
# Load section globs (with optional custom config)
|
|
386
|
+
SECTION_GLOBS = load_section_globs(config)
|
|
387
|
+
|
|
388
|
+
commands_dir = os.path.join(os.getcwd(), ".gemini", "commands")
|
|
389
|
+
output_path = os.path.join(os.getcwd(), output)
|
|
390
|
+
|
|
391
|
+
logger.info("Bundling Gemini CLI commands", commands_dir=commands_dir, output_file=output_path, config=config)
|
|
392
|
+
|
|
393
|
+
if not Path(commands_dir).exists():
|
|
394
|
+
logger.error("Gemini CLI commands directory not found", commands_dir=commands_dir)
|
|
395
|
+
raise typer.Exit(1)
|
|
396
|
+
|
|
397
|
+
bundle_gemini_commands(commands_dir, output_path, SECTION_GLOBS)
|
|
398
|
+
logger.info("Gemini CLI commands bundled successfully", output_file=output_path)
|
|
399
|
+
typer.echo(f"Bundled gemini commands into {output}")
|
|
218
400
|
|
llm_ide_rules/sections.json
CHANGED
|
@@ -13,6 +13,15 @@
|
|
|
13
13
|
"Shell": "**/*.sh",
|
|
14
14
|
"TypeScript": "**/*.ts,**/*.tsx",
|
|
15
15
|
"TypeScript DocString": null,
|
|
16
|
-
"Secrets": null
|
|
16
|
+
"Secrets": null,
|
|
17
|
+
"Dev In Browser": null,
|
|
18
|
+
"Fastapi Stripe": null,
|
|
19
|
+
"Fix Tests": null,
|
|
20
|
+
"Implement Fastapi Routes": null,
|
|
21
|
+
"Plan Only": null,
|
|
22
|
+
"Python Command": null,
|
|
23
|
+
"Refactor On Instructions": null,
|
|
24
|
+
"Standalone Python Scripts": null,
|
|
25
|
+
"Stripe Backend": null
|
|
17
26
|
}
|
|
18
27
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
llm_ide_rules/__init__.py,sha256=494c58df084291e4e427a1a36f78306224ea83305531dcff5c566c2fb53e16ce,1472
|
|
2
|
+
llm_ide_rules/__main__.py,sha256=881447dbad0fe837590fa8224f1d0dd70416fd5344779978d07744ff8e0cf868,144
|
|
3
|
+
llm_ide_rules/commands/delete.py,sha256=6ed0a44b1be1d950180831681d79e3a0be91cd37296e30e7a0d821d49292ebf4,5812
|
|
4
|
+
llm_ide_rules/commands/download.py,sha256=06c2a841db5fe0b0f7f3e8c99ad198c8e07e53d8801fb4d95e802ecee60cda31,11022
|
|
5
|
+
llm_ide_rules/commands/explode.py,sha256=59df4bb542397c9204332ba563a95c49d236a82ebc6af81378b2798f8adc2e77,14779
|
|
6
|
+
llm_ide_rules/commands/implode.py,sha256=f3e9ff754530c06d7eacb20fc37ae8d958218f02df2a0f48eac1d5c6a9a749be,16743
|
|
7
|
+
llm_ide_rules/constants.py,sha256=1182b9757ca58d038a4878d1b55570aac4f0287f1a5ab5af8d68331b799b0686,1118
|
|
8
|
+
llm_ide_rules/sections.json,sha256=e82bf37f8a7bd919c881d7cc084cdfd82e1f9e6ff5385c74b18c14996bcac3c1,822
|
|
9
|
+
llm_ide_rules-0.5.0.dist-info/WHEEL,sha256=0f7d664a881437bddec71c703c3c2f01fd13581519f95130abcc96e296ef0426,79
|
|
10
|
+
llm_ide_rules-0.5.0.dist-info/entry_points.txt,sha256=c6c00b5d607048489fcfed8c8a47bbb364b0a8dbb5b5a2ecffe11c986ace35e3,54
|
|
11
|
+
llm_ide_rules-0.5.0.dist-info/METADATA,sha256=af671c42110d55d0670a3aef9007b890dcb5d6335add807d36d8baadf3e802a3,4898
|
|
12
|
+
llm_ide_rules-0.5.0.dist-info/RECORD,,
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
llm_ide_rules/__init__.py,sha256=03b18e9945a42a481e3f4c345ee9550792b83496bee0a708bafad8d7c6dfcf17,1246
|
|
2
|
-
llm_ide_rules/__main__.py,sha256=881447dbad0fe837590fa8224f1d0dd70416fd5344779978d07744ff8e0cf868,144
|
|
3
|
-
llm_ide_rules/commands/delete.py,sha256=6ed0a44b1be1d950180831681d79e3a0be91cd37296e30e7a0d821d49292ebf4,5812
|
|
4
|
-
llm_ide_rules/commands/download.py,sha256=06c2a841db5fe0b0f7f3e8c99ad198c8e07e53d8801fb4d95e802ecee60cda31,11022
|
|
5
|
-
llm_ide_rules/commands/explode.py,sha256=a8ace601155055a0061e47da7e99a672334796f1ebb91b24c3adf905f65e1645,11196
|
|
6
|
-
llm_ide_rules/commands/implode.py,sha256=35282bfc83be808f12e9e9ce7af1bae4069e53bd8f441948e3551339b2e9418d,8847
|
|
7
|
-
llm_ide_rules/constants.py,sha256=1182b9757ca58d038a4878d1b55570aac4f0287f1a5ab5af8d68331b799b0686,1118
|
|
8
|
-
llm_ide_rules/sections.json,sha256=9ed1725e44d8a29364e5ac3f01e0e6e03ff17cd45c280846659b7f73940b668b,549
|
|
9
|
-
llm_ide_rules-0.4.0.dist-info/WHEEL,sha256=0f7d664a881437bddec71c703c3c2f01fd13581519f95130abcc96e296ef0426,79
|
|
10
|
-
llm_ide_rules-0.4.0.dist-info/entry_points.txt,sha256=c6c00b5d607048489fcfed8c8a47bbb364b0a8dbb5b5a2ecffe11c986ace35e3,54
|
|
11
|
-
llm_ide_rules-0.4.0.dist-info/METADATA,sha256=dfa81f3d0100761f18547dd23e3c15343c6f8341ab27622b6b89b59860bb3ebe,4898
|
|
12
|
-
llm_ide_rules-0.4.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|