Skip to content

Commit 5b2e27e

Browse files
committed
Refactor plugin config and mgmt into per-instance config files
1 parent a2da74f commit 5b2e27e

File tree

4 files changed

+112
-8
lines changed

4 files changed

+112
-8
lines changed

src/faff_cli/main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import typer
22
import humanize
33

4-
from faff_cli import log, id, source, plan, compiler, start, timesheet, intent, field
4+
from faff_cli import log, id, plan, compiler, start, timesheet, intent, field, remote
55
from faff_cli.utils import edit_file
66

77
import faff_core
@@ -12,14 +12,14 @@
1212
cli = typer.Typer()
1313

1414
cli.add_typer(log.app, name="log")
15-
cli.add_typer(source.app, name="source")
1615
cli.add_typer(compiler.app, name="compiler")
1716
cli.add_typer(id.app, name="id")
1817
cli.add_typer(plan.app, name="plan")
1918
cli.add_typer(start.app, name="start")
2019
cli.add_typer(timesheet.app, name="timesheet")
2120
cli.add_typer(intent.app, name="intent")
2221
cli.add_typer(field.app, name="field")
22+
cli.add_typer(remote.app, name="remote")
2323

2424
@cli.callback()
2525
def main(ctx: typer.Context):

src/faff_cli/remote.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import typer
2+
from pathlib import Path
3+
4+
from rich.console import Console
5+
from rich.table import Table
6+
7+
from faff_core import Workspace
8+
9+
app = typer.Typer(help="Manage remote plugin instances")
10+
11+
12+
@app.command(name="list")
13+
def list_remotes(ctx: typer.Context):
14+
"""
15+
List all configured remotes.
16+
"""
17+
try:
18+
ws: Workspace = ctx.obj
19+
console = Console()
20+
21+
remotes_dir = Path(ws.storage().remotes_dir())
22+
remote_files = list(remotes_dir.glob("*.toml"))
23+
24+
if not remote_files:
25+
console.print("[yellow]No remotes configured[/yellow]")
26+
console.print(f"\nRemotes are configured in: {remotes_dir}")
27+
console.print("Create a .toml file there to configure a remote.")
28+
return
29+
30+
table = Table(title="Configured Remotes")
31+
table.add_column("ID", style="cyan")
32+
table.add_column("Plugin", style="green")
33+
table.add_column("Config File", style="dim")
34+
35+
import toml
36+
37+
for remote_file in sorted(remote_files):
38+
try:
39+
remote_data = toml.load(remote_file)
40+
remote_id = remote_data.get("id", remote_file.stem)
41+
plugin = remote_data.get("plugin", "unknown")
42+
table.add_row(remote_id, plugin, remote_file.name)
43+
except Exception as e:
44+
console.print(
45+
f"[yellow]Warning: Failed to read {remote_file.name}: {e}[/yellow]"
46+
)
47+
48+
console.print(table)
49+
console.print(f"\nRemotes directory: {remotes_dir}")
50+
51+
except Exception as e:
52+
typer.echo(f"Error listing remotes: {e}", err=True)
53+
raise typer.Exit(1)
54+
55+
56+
@app.command()
57+
def show(ctx: typer.Context, remote_id: str = typer.Argument(..., help="Remote ID to show")):
58+
"""
59+
Show detailed configuration for a remote.
60+
"""
61+
try:
62+
ws: Workspace = ctx.obj
63+
console = Console()
64+
65+
remotes_dir = Path(ws.storage().remotes_dir())
66+
remote_file = remotes_dir / f"{remote_id}.toml"
67+
68+
if not remote_file.exists():
69+
console.print(f"[red]Remote '{remote_id}' not found[/red]")
70+
console.print(f"\nLooking for: {remote_file}")
71+
raise typer.Exit(1)
72+
73+
import toml
74+
75+
remote_data = toml.load(remote_file)
76+
77+
console.print(f"[bold cyan]Remote: {remote_id}[/bold cyan]\n")
78+
console.print(f"[bold]Plugin:[/bold] {remote_data.get('plugin', 'unknown')}")
79+
console.print(f"[bold]Config file:[/bold] {remote_file}\n")
80+
81+
# Show connection config
82+
if "connection" in remote_data and remote_data["connection"]:
83+
console.print("[bold]Connection:[/bold]")
84+
for key, value in remote_data["connection"].items():
85+
# Hide sensitive values
86+
if "key" in key.lower() or "token" in key.lower() or "password" in key.lower():
87+
console.print(f" {key}: [dim]<hidden>[/dim]")
88+
else:
89+
console.print(f" {key}: {value}")
90+
console.print()
91+
92+
# Show vocabulary
93+
if "vocabulary" in remote_data and remote_data["vocabulary"]:
94+
console.print("[bold]Vocabulary:[/bold]")
95+
vocab = remote_data["vocabulary"]
96+
for field_name in ["roles", "objectives", "actions", "subjects"]:
97+
if field_name in vocab and vocab[field_name]:
98+
console.print(f" {field_name}: {len(vocab[field_name])} items")
99+
for item in vocab[field_name]:
100+
console.print(f" - {item}")
101+
else:
102+
console.print("[dim]No vocabulary configured[/dim]")
103+
104+
except Exception as e:
105+
typer.echo(f"Error showing remote: {e}", err=True)
106+
raise typer.Exit(1)

src/faff_cli/source.py

Lines changed: 0 additions & 4 deletions
This file was deleted.

src/faff_cli/start.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,14 @@ def input_new_intent(alias: str, ws: Workspace) -> Intent:
8686
action=action.value if action else None,
8787
subject=subject.value if subject else None,
8888
trackers=trackers
89-
)
89+
)
9090

9191
new_plan = local_plan.add_intent(new_intent)
9292
ws.plans.write_plan(new_plan)
9393

94-
return new_intent
94+
# Get the intent back from the plan - it now has a generated ID
95+
intent_with_id = [i for i in new_plan.intents if i.alias == alias][-1]
96+
return intent_with_id
9597

9698
@app.callback(invoke_without_command=True)
9799
def start(ctx: typer.Context):

0 commit comments

Comments
 (0)