Packages

fnord

0.8.74

AI code archaeology

Current section

Files

Jump to
fnord lib cmd conversations.ex
Raw

lib/cmd/conversations.ex

defmodule Cmd.Conversations do
@behaviour Cmd
@impl Cmd
def requires_project?(), do: true
@impl Cmd
def spec do
[
conversations: [
name: "conversations",
about: "List all conversations in the project",
options: [
project: Cmd.project_arg(),
prune: [
value_name: "PRUNE",
long: "--prune",
short: "-P",
help: "Prune conversations older than this many days",
parser: :integer
]
]
]
]
end
@impl Cmd
def run(%{prune: days} = opts, _subcommands, _unknown) when is_integer(days) do
with {:ok, project} <- Store.get_project() do
case prune(opts, project) do
:ok -> :ok
{:error, :cancelled} -> UI.error("Operation cancelled.")
{:error, :invalid_prune_value} -> UI.error("Invalid --prune value: #{days}")
end
else
{:error, :project_not_set} ->
UI.error("No project selected; use --project or run in a project directory.")
end
end
@impl Cmd
def run(opts, _subcommands, _unknown) do
with {:ok, project} <- Store.get_project(),
:ok <- display(opts, project) do
:ok
else
{:error, :project_not_set} ->
UI.error(
"No project selected; please specify --project or run inside a project directory."
)
end
end
defp prune(%{prune: days}, project) when is_integer(days) and days >= 0 do
cutoff = DateTime.utc_now() |> DateTime.add(-days, :day)
UI.info("Pruning conversations older than #{days} days")
to_delete =
project
|> Store.Project.conversations()
|> Enum.reduce([], fn conversation, acc ->
timestamp = Store.Project.Conversation.timestamp(conversation)
if DateTime.compare(timestamp, cutoff) == :lt do
[conversation | acc]
else
acc
end
end)
if to_delete == [] do
UI.info("No conversations to prune.")
:ok
else
UI.info("Preparing to delete the following conversations:")
to_delete
|> Enum.each(fn conversation ->
UI.info(conversation.id)
end)
if UI.confirm("Confirm deletion of the listed conversations. This action cannot be undone.") do
to_delete |> Enum.each(&Store.Project.Conversation.delete/1)
count = length(to_delete)
UI.info("Deleted #{count} conversation(s).")
:ok
else
{:error, :cancelled}
end
end
end
defp prune(%{prune: days}, _project) when is_integer(days) and days < 0 do
{:error, :invalid_prune_value}
end
defp prune(_opts, _project), do: :ok
defp display(_opts, project) do
project
|> Store.Project.conversations()
|> case do
[] ->
UI.puts("No conversations found.")
conversations ->
conversations
|> Enum.map(fn conversation ->
%{
id: conversation.id,
timestamp: Store.Project.Conversation.timestamp(conversation),
file: conversation.store_path,
question: get_question(conversation),
length: Store.Project.Conversation.num_messages(conversation)
}
end)
|> Jason.encode!(pretty: true)
|> UI.puts()
end
:ok
end
defp get_question(conversation) do
with {:ok, question} <- Store.Project.Conversation.question(conversation) do
question
else
{:error, :no_question} -> "(not found)"
end
end
end