pltr-cli 0.1.2__py3-none-any.whl → 0.3.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.
pltr/commands/alias.py ADDED
@@ -0,0 +1,241 @@
1
+ """Command alias management commands."""
2
+
3
+ import json
4
+ from typing import Optional
5
+
6
+ import typer
7
+ from rich import print as rprint
8
+
9
+ from pltr.config.aliases import AliasManager
10
+ from pltr.utils.completion import complete_alias_names
11
+
12
+ app = typer.Typer(name="alias", help="Manage command aliases", no_args_is_help=True)
13
+
14
+
15
+ @app.command()
16
+ def add(
17
+ name: str = typer.Argument(..., help="Alias name"),
18
+ command: str = typer.Argument(..., help="Command to alias"),
19
+ force: bool = typer.Option(False, "--force", "-f", help="Overwrite existing alias"),
20
+ ) -> None:
21
+ """Create a new command alias."""
22
+ manager = AliasManager()
23
+
24
+ # Check if alias already exists
25
+ if manager.get_alias(name) and not force:
26
+ rprint(f"[red]Alias '{name}' already exists. Use --force to overwrite.[/red]")
27
+ raise typer.Exit(1)
28
+
29
+ # Check for reserved command names
30
+ reserved_commands = [
31
+ "configure",
32
+ "verify",
33
+ "dataset",
34
+ "ontology",
35
+ "sql",
36
+ "admin",
37
+ "shell",
38
+ "completion",
39
+ "alias",
40
+ "help",
41
+ "--version",
42
+ ]
43
+ if name in reserved_commands:
44
+ rprint(f"[red]Cannot use reserved command name '{name}' as alias[/red]")
45
+ raise typer.Exit(1)
46
+
47
+ try:
48
+ if force and manager.get_alias(name):
49
+ success = manager.edit_alias(name, command)
50
+ action = "Updated"
51
+ else:
52
+ success = manager.add_alias(name, command)
53
+ action = "Created"
54
+
55
+ if success:
56
+ rprint(f"[green]{action} alias:[/green] {name} → {command}")
57
+ else:
58
+ rprint(f"[red]Failed to create alias '{name}'[/red]")
59
+ raise typer.Exit(1)
60
+ except ValueError as e:
61
+ rprint(f"[red]Error: {e}[/red]")
62
+ raise typer.Exit(1)
63
+
64
+
65
+ @app.command()
66
+ def remove(
67
+ name: str = typer.Argument(
68
+ ..., help="Alias name to remove", autocompletion=complete_alias_names
69
+ ),
70
+ confirm: bool = typer.Option(
71
+ True, "--confirm/--no-confirm", help="Confirm removal"
72
+ ),
73
+ ) -> None:
74
+ """Remove a command alias."""
75
+ manager = AliasManager()
76
+
77
+ command = manager.get_alias(name)
78
+ if not command:
79
+ rprint(f"[red]Alias '{name}' not found[/red]")
80
+ raise typer.Exit(1)
81
+
82
+ if confirm:
83
+ confirmation = typer.confirm(f"Remove alias '{name}' → {command}?")
84
+ if not confirmation:
85
+ rprint("[yellow]Removal cancelled[/yellow]")
86
+ return
87
+
88
+ if manager.remove_alias(name):
89
+ rprint(f"[green]Removed alias '{name}'[/green]")
90
+ else:
91
+ rprint(f"[red]Failed to remove alias '{name}'[/red]")
92
+ raise typer.Exit(1)
93
+
94
+
95
+ @app.command()
96
+ def edit(
97
+ name: str = typer.Argument(
98
+ ..., help="Alias name to edit", autocompletion=complete_alias_names
99
+ ),
100
+ command: str = typer.Argument(..., help="New command for the alias"),
101
+ ) -> None:
102
+ """Edit an existing command alias."""
103
+ manager = AliasManager()
104
+
105
+ if not manager.get_alias(name):
106
+ rprint(f"[red]Alias '{name}' not found[/red]")
107
+ raise typer.Exit(1)
108
+
109
+ try:
110
+ if manager.edit_alias(name, command):
111
+ rprint(f"[green]Updated alias:[/green] {name} → {command}")
112
+ else:
113
+ rprint(f"[red]Failed to edit alias '{name}'[/red]")
114
+ raise typer.Exit(1)
115
+ except ValueError as e:
116
+ rprint(f"[red]Error: {e}[/red]")
117
+ raise typer.Exit(1)
118
+
119
+
120
+ @app.command("list")
121
+ def list_aliases() -> None:
122
+ """List all command aliases."""
123
+ manager = AliasManager()
124
+ manager.display_aliases()
125
+
126
+
127
+ @app.command()
128
+ def show(
129
+ name: str = typer.Argument(
130
+ ..., help="Alias name to show", autocompletion=complete_alias_names
131
+ ),
132
+ ) -> None:
133
+ """Show details of a specific alias."""
134
+ manager = AliasManager()
135
+ manager.display_aliases(name)
136
+
137
+
138
+ @app.command()
139
+ def clear(
140
+ confirm: bool = typer.Option(
141
+ True, "--confirm/--no-confirm", help="Confirm clearing all aliases"
142
+ ),
143
+ ) -> None:
144
+ """Clear all command aliases."""
145
+ manager = AliasManager()
146
+
147
+ aliases = manager.list_aliases()
148
+ if not aliases:
149
+ rprint("[yellow]No aliases to clear[/yellow]")
150
+ return
151
+
152
+ if confirm:
153
+ confirmation = typer.confirm(f"Clear all {len(aliases)} aliases?")
154
+ if not confirmation:
155
+ rprint("[yellow]Clear cancelled[/yellow]")
156
+ return
157
+
158
+ count = manager.clear_all()
159
+ rprint(f"[green]Cleared {count} aliases[/green]")
160
+
161
+
162
+ @app.command("export")
163
+ def export_aliases(
164
+ output: Optional[str] = typer.Option(
165
+ None, "--output", "-o", help="Output file (default: stdout)"
166
+ ),
167
+ ) -> None:
168
+ """Export aliases to JSON format."""
169
+ manager = AliasManager()
170
+ aliases = manager.export_aliases()
171
+
172
+ if not aliases:
173
+ rprint("[yellow]No aliases to export[/yellow]")
174
+ return
175
+
176
+ json_data = json.dumps(aliases, indent=2, sort_keys=True)
177
+
178
+ if output:
179
+ with open(output, "w") as f:
180
+ f.write(json_data)
181
+ rprint(f"[green]Exported {len(aliases)} aliases to {output}[/green]")
182
+ else:
183
+ print(json_data)
184
+
185
+
186
+ @app.command("import")
187
+ def import_aliases(
188
+ input_file: str = typer.Argument(..., help="JSON file containing aliases"),
189
+ merge: bool = typer.Option(
190
+ False, "--merge", "-m", help="Merge with existing aliases (default: replace)"
191
+ ),
192
+ ) -> None:
193
+ """Import aliases from a JSON file."""
194
+ manager = AliasManager()
195
+
196
+ try:
197
+ with open(input_file, "r") as f:
198
+ data = json.load(f)
199
+ except (json.JSONDecodeError, IOError) as e:
200
+ rprint(f"[red]Error reading file: {e}[/red]")
201
+ raise typer.Exit(1)
202
+
203
+ if not isinstance(data, dict):
204
+ rprint("[red]Invalid format: expected JSON object with alias mappings[/red]")
205
+ raise typer.Exit(1)
206
+
207
+ # Clear existing aliases if not merging
208
+ if not merge and manager.list_aliases():
209
+ confirmation = typer.confirm("Replace existing aliases?")
210
+ if not confirmation:
211
+ rprint("[yellow]Import cancelled[/yellow]")
212
+ return
213
+ manager.clear_all()
214
+
215
+ count = manager.import_aliases(data)
216
+ rprint(f"[green]Imported {count} aliases from {input_file}[/green]")
217
+
218
+
219
+ @app.command()
220
+ def resolve(
221
+ command: str = typer.Argument(
222
+ ..., help="Command to resolve", autocompletion=complete_alias_names
223
+ ),
224
+ ) -> None:
225
+ """Resolve an alias to show the actual command."""
226
+ manager = AliasManager()
227
+
228
+ resolved = manager.resolve_alias(command)
229
+ if resolved == command:
230
+ if command in manager.list_aliases():
231
+ rprint(
232
+ f"[yellow]'{command}' is an alias but may have circular references[/yellow]"
233
+ )
234
+ else:
235
+ rprint(f"[yellow]'{command}' is not an alias[/yellow]")
236
+ else:
237
+ rprint(f"[green]{command}[/green] → {resolved}")
238
+
239
+
240
+ if __name__ == "__main__":
241
+ app()
@@ -0,0 +1,383 @@
1
+ """Shell completion commands for pltr CLI."""
2
+
3
+ import os
4
+ from pathlib import Path
5
+ from typing import Optional
6
+
7
+ import typer
8
+ from rich.console import Console
9
+ from rich.panel import Panel
10
+ from rich.syntax import Syntax
11
+
12
+ console = Console()
13
+ app = typer.Typer(help="Manage shell completions for pltr CLI")
14
+
15
+
16
+ def get_shell_from_env() -> Optional[str]:
17
+ """Detect the current shell from environment."""
18
+ shell_path = os.environ.get("SHELL", "")
19
+ if "bash" in shell_path:
20
+ return "bash"
21
+ elif "zsh" in shell_path:
22
+ return "zsh"
23
+ elif "fish" in shell_path:
24
+ return "fish"
25
+ return None
26
+
27
+
28
+ @app.command()
29
+ def install(
30
+ shell: Optional[str] = typer.Option(
31
+ None,
32
+ "--shell",
33
+ "-s",
34
+ help="Shell type (bash, zsh, fish). Auto-detected if not specified.",
35
+ ),
36
+ path: Optional[Path] = typer.Option(
37
+ None,
38
+ "--path",
39
+ "-p",
40
+ help="Custom path to install completion file.",
41
+ ),
42
+ ):
43
+ """Install shell completions for pltr CLI.
44
+
45
+ This command will install shell completions for the specified shell
46
+ (or auto-detect from your environment). After installation, restart
47
+ your shell or source the appropriate file to enable completions.
48
+
49
+ Examples:
50
+ pltr completion install
51
+ pltr completion install --shell bash
52
+ pltr completion install --shell zsh --path ~/.config/custom/completions
53
+ """
54
+ # Auto-detect shell if not specified
55
+ if shell is None:
56
+ shell = get_shell_from_env()
57
+ if shell is None:
58
+ console.print(
59
+ "[red]Could not detect shell type. Please specify with --shell option.[/red]"
60
+ )
61
+ console.print("Supported shells: bash, zsh, fish")
62
+ raise typer.Exit(1)
63
+ console.print(f"[cyan]Auto-detected shell: {shell}[/cyan]")
64
+
65
+ shell = shell.lower()
66
+ if shell not in ["bash", "zsh", "fish"]:
67
+ console.print(f"[red]Unsupported shell: {shell}[/red]")
68
+ console.print("Supported shells: bash, zsh, fish")
69
+ raise typer.Exit(1)
70
+
71
+ # Generate completion script
72
+ completion_script = generate_completion_script(shell)
73
+
74
+ # Determine installation path
75
+ if path is None:
76
+ path = get_default_completion_path(shell)
77
+
78
+ # Ensure directory exists
79
+ path.parent.mkdir(parents=True, exist_ok=True)
80
+
81
+ # Write completion file
82
+ try:
83
+ path.write_text(completion_script)
84
+ console.print(f"[green]✓[/green] Completion file written to: {path}")
85
+ except Exception as e:
86
+ console.print(f"[red]Failed to write completion file: {e}[/red]")
87
+ raise typer.Exit(1)
88
+
89
+ # Show instructions for activation
90
+ show_activation_instructions(shell, path)
91
+
92
+
93
+ @app.command()
94
+ def show(
95
+ shell: Optional[str] = typer.Option(
96
+ None,
97
+ "--shell",
98
+ "-s",
99
+ help="Shell type (bash, zsh, fish). Auto-detected if not specified.",
100
+ ),
101
+ ):
102
+ """Show the completion script for the specified shell.
103
+
104
+ This displays the completion script without installing it,
105
+ useful for manual installation or debugging.
106
+ """
107
+ # Auto-detect shell if not specified
108
+ if shell is None:
109
+ shell = get_shell_from_env()
110
+ if shell is None:
111
+ console.print(
112
+ "[red]Could not detect shell type. Please specify with --shell option.[/red]"
113
+ )
114
+ console.print("Supported shells: bash, zsh, fish")
115
+ raise typer.Exit(1)
116
+
117
+ shell = shell.lower()
118
+ if shell not in ["bash", "zsh", "fish"]:
119
+ console.print(f"[red]Unsupported shell: {shell}[/red]")
120
+ console.print("Supported shells: bash, zsh, fish")
121
+ raise typer.Exit(1)
122
+
123
+ # Generate and display completion script
124
+ completion_script = generate_completion_script(shell)
125
+ syntax = Syntax(completion_script, "bash", theme="monokai", line_numbers=True)
126
+
127
+ panel = Panel(
128
+ syntax,
129
+ title=f"{shell.capitalize()} Completion Script",
130
+ border_style="cyan",
131
+ )
132
+ console.print(panel)
133
+
134
+
135
+ def generate_completion_script(shell: str) -> str:
136
+ """Generate completion script for the specified shell."""
137
+ if shell == "bash":
138
+ return generate_bash_completion()
139
+ elif shell == "zsh":
140
+ return generate_zsh_completion()
141
+ elif shell == "fish":
142
+ return generate_fish_completion()
143
+ else:
144
+ raise ValueError(f"Unsupported shell: {shell}")
145
+
146
+
147
+ def generate_bash_completion() -> str:
148
+ """Generate Bash completion script."""
149
+ return """# pltr bash completion script
150
+ # Generated by pltr completion install
151
+
152
+ _pltr_completion() {
153
+ local IFS=$'\\n'
154
+ local response
155
+
156
+ response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD _PLTR_COMPLETE=bash_complete pltr)
157
+
158
+ for completion in $response; do
159
+ IFS=',' read type value <<< "$completion"
160
+
161
+ if [[ $type == 'dir' ]]; then
162
+ COMPREPLY+=("$value/")
163
+ compopt -o dirnames
164
+ elif [[ $type == 'file' ]]; then
165
+ COMPREPLY+=("$value")
166
+ compopt -o filenames
167
+ elif [[ $type == 'plain' ]]; then
168
+ COMPREPLY+=("$value")
169
+ fi
170
+ done
171
+
172
+ return 0
173
+ }
174
+
175
+ _pltr_completion_setup() {
176
+ complete -o nosort -F _pltr_completion pltr
177
+ }
178
+
179
+ _pltr_completion_setup
180
+ """
181
+
182
+
183
+ def generate_zsh_completion() -> str:
184
+ """Generate Zsh completion script."""
185
+ return """#compdef pltr
186
+ # pltr zsh completion script
187
+ # Generated by pltr completion install
188
+
189
+ _pltr() {
190
+ local -a completions
191
+ local -a completions_with_descriptions
192
+ local -a response
193
+ (( ! $+commands[pltr] )) && return 1
194
+
195
+ response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) _PLTR_COMPLETE=zsh_complete pltr)}")
196
+
197
+ for type key descr in ${response}; do
198
+ if [[ "$type" == "plain" ]]; then
199
+ if [[ "$descr" == "_" ]]; then
200
+ completions+=("$key")
201
+ else
202
+ completions_with_descriptions+=("$key":"$descr")
203
+ fi
204
+ elif [[ "$type" == "dir" ]]; then
205
+ _path_files -/
206
+ elif [[ "$type" == "file" ]]; then
207
+ _path_files -f
208
+ fi
209
+ done
210
+
211
+ if [ -n "$completions_with_descriptions" ]; then
212
+ _describe -V unsorted completions_with_descriptions -U
213
+ fi
214
+
215
+ if [ -n "$completions" ]; then
216
+ compadd -U -V unsorted -a completions
217
+ fi
218
+ }
219
+
220
+ if [[ $zsh_eval_context[-1] == loadautofunc ]]; then
221
+ _pltr "$@"
222
+ else
223
+ compdef _pltr pltr
224
+ fi
225
+ """
226
+
227
+
228
+ def generate_fish_completion() -> str:
229
+ """Generate Fish completion script."""
230
+ return """# pltr fish completion script
231
+ # Generated by pltr completion install
232
+
233
+ function _pltr_completion
234
+ set -l response (env COMP_WORDS=(commandline -cp) COMP_CWORD=(commandline -t) _PLTR_COMPLETE=fish_complete pltr)
235
+
236
+ for completion in $response
237
+ set -l metadata (string split "," $completion)
238
+
239
+ if test $metadata[1] = "dir"
240
+ __fish_complete_directories $metadata[2]
241
+ else if test $metadata[1] = "file"
242
+ __fish_complete_path $metadata[2]
243
+ else if test $metadata[1] = "plain"
244
+ echo $metadata[2]
245
+ end
246
+ end
247
+ end
248
+
249
+ complete -c pltr -f -a "(_pltr_completion)"
250
+ """
251
+
252
+
253
+ def get_default_completion_path(shell: str) -> Path:
254
+ """Get the default path for completion file installation."""
255
+ home = Path.home()
256
+
257
+ if shell == "bash":
258
+ # Try bash-completion paths
259
+ paths = [
260
+ home / ".local" / "share" / "bash-completion" / "completions" / "pltr",
261
+ home / ".bash_completion.d" / "pltr",
262
+ Path("/etc/bash_completion.d/pltr"),
263
+ ]
264
+ # Use first writable path
265
+ for p in paths[:-1]: # Skip system path for user installs
266
+ if p.parent.exists() or not p.parent.parent.exists():
267
+ return p
268
+ return paths[0]
269
+
270
+ elif shell == "zsh":
271
+ # Zsh completion paths
272
+ return home / ".zfunc" / "_pltr"
273
+
274
+ elif shell == "fish":
275
+ # Fish completion path
276
+ return home / ".config" / "fish" / "completions" / "pltr.fish"
277
+
278
+ else:
279
+ raise ValueError(f"Unknown shell: {shell}")
280
+
281
+
282
+ def show_activation_instructions(shell: str, path: Path):
283
+ """Show instructions for activating completions."""
284
+ console.print("\n[yellow]Activation Instructions:[/yellow]")
285
+
286
+ if shell == "bash":
287
+ console.print(
288
+ Panel(
289
+ f"""To activate completions, add this to your ~/.bashrc:
290
+
291
+ [cyan]# Load pltr completions
292
+ if [ -f {path} ]; then
293
+ source {path}
294
+ fi[/cyan]
295
+
296
+ Then reload your shell:
297
+ [cyan]source ~/.bashrc[/cyan]
298
+
299
+ Or start a new terminal session.""",
300
+ title="Bash Activation",
301
+ border_style="green",
302
+ )
303
+ )
304
+
305
+ elif shell == "zsh":
306
+ console.print(
307
+ Panel(
308
+ f"""To activate completions, add this to your ~/.zshrc:
309
+
310
+ [cyan]# Add custom completion directory
311
+ fpath=({path.parent} $fpath)
312
+
313
+ # Initialize completions (if not already done)
314
+ autoload -Uz compinit && compinit[/cyan]
315
+
316
+ Then reload your shell:
317
+ [cyan]source ~/.zshrc[/cyan]
318
+
319
+ Or start a new terminal session.""",
320
+ title="Zsh Activation",
321
+ border_style="green",
322
+ )
323
+ )
324
+
325
+ elif shell == "fish":
326
+ console.print(
327
+ Panel(
328
+ f"""Completions should be automatically loaded from:
329
+ {path}
330
+
331
+ If not working, reload your shell:
332
+ [cyan]exec fish[/cyan]
333
+
334
+ Or start a new terminal session.""",
335
+ title="Fish Activation",
336
+ border_style="green",
337
+ )
338
+ )
339
+
340
+
341
+ @app.command()
342
+ def uninstall(
343
+ shell: Optional[str] = typer.Option(
344
+ None,
345
+ "--shell",
346
+ "-s",
347
+ help="Shell type (bash, zsh, fish). Auto-detected if not specified.",
348
+ ),
349
+ ):
350
+ """Uninstall shell completions for pltr CLI."""
351
+ # Auto-detect shell if not specified
352
+ if shell is None:
353
+ shell = get_shell_from_env()
354
+ if shell is None:
355
+ console.print(
356
+ "[red]Could not detect shell type. Please specify with --shell option.[/red]"
357
+ )
358
+ raise typer.Exit(1)
359
+
360
+ shell = shell.lower()
361
+ if shell not in ["bash", "zsh", "fish"]:
362
+ console.print(f"[red]Unsupported shell: {shell}[/red]")
363
+ raise typer.Exit(1)
364
+
365
+ # Get default completion path
366
+ path = get_default_completion_path(shell)
367
+
368
+ if path.exists():
369
+ try:
370
+ path.unlink()
371
+ console.print(f"[green]✓[/green] Removed completion file: {path}")
372
+ console.print(
373
+ "\n[yellow]Remember to remove any sourcing commands from your shell config.[/yellow]"
374
+ )
375
+ except Exception as e:
376
+ console.print(f"[red]Failed to remove completion file: {e}[/red]")
377
+ raise typer.Exit(1)
378
+ else:
379
+ console.print(f"[yellow]No completion file found at: {path}[/yellow]")
380
+
381
+
382
+ if __name__ == "__main__":
383
+ app()
pltr/commands/dataset.py CHANGED
@@ -10,6 +10,12 @@ from ..services.dataset import DatasetService
10
10
  from ..utils.formatting import OutputFormatter
11
11
  from ..utils.progress import SpinnerProgressTracker
12
12
  from ..auth.base import ProfileNotFoundError, MissingCredentialsError
13
+ from ..utils.completion import (
14
+ complete_rid,
15
+ complete_profile,
16
+ complete_output_format,
17
+ cache_rid,
18
+ )
13
19
 
14
20
  app = typer.Typer()
15
21
  console = Console()
@@ -18,10 +24,18 @@ formatter = OutputFormatter(console)
18
24
 
19
25
  @app.command("get")
20
26
  def get_dataset(
21
- dataset_rid: str = typer.Argument(..., help="Dataset Resource Identifier"),
22
- profile: Optional[str] = typer.Option(None, "--profile", "-p", help="Profile name"),
27
+ dataset_rid: str = typer.Argument(
28
+ ..., help="Dataset Resource Identifier", autocompletion=complete_rid
29
+ ),
30
+ profile: Optional[str] = typer.Option(
31
+ None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
32
+ ),
23
33
  format: str = typer.Option(
24
- "table", "--format", "-f", help="Output format (table, json, csv)"
34
+ "table",
35
+ "--format",
36
+ "-f",
37
+ help="Output format (table, json, csv)",
38
+ autocompletion=complete_output_format,
25
39
  ),
26
40
  output: Optional[str] = typer.Option(
27
41
  None, "--output", "-o", help="Output file path"
@@ -29,6 +43,9 @@ def get_dataset(
29
43
  ):
30
44
  """Get detailed information about a specific dataset."""
31
45
  try:
46
+ # Cache the RID for future completions
47
+ cache_rid(dataset_rid)
48
+
32
49
  service = DatasetService(profile=profile)
33
50
 
34
51
  with SpinnerProgressTracker().track_spinner(