Packages
fnord
0.4.15
0.9.40
0.9.39
0.9.38
0.9.37
0.9.36
0.9.35
0.9.34
0.9.33
0.9.32
0.9.31
0.9.30
0.9.29
0.9.28
0.9.27
0.9.26
0.9.25
0.9.24
0.9.23
0.9.22
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.14
0.9.13
0.9.12
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.99
0.8.98
0.8.97
0.8.96
0.8.95
0.8.94
0.8.93
0.8.92
0.8.91
0.8.90
0.8.89
0.8.88
0.8.87
0.8.86
0.8.85
0.8.84
0.8.83
0.8.82
0.8.81
0.8.80
0.8.79
0.8.78
0.8.77
0.8.76
0.8.75
0.8.74
0.8.73
0.8.72
0.8.71
0.8.70
0.8.69
0.8.68
0.8.67
0.8.66
0.8.65
0.8.64
0.8.63
0.8.62
0.8.61
0.8.60
0.8.59
0.8.58
0.8.57
0.8.56
0.8.55
0.8.54
0.8.53
0.8.52
0.8.51
0.8.50
0.8.49
0.8.48
0.8.47
0.8.46
0.8.45
0.8.44
0.8.43
0.8.42
0.8.41
0.8.40
0.8.39
0.8.38
0.8.37
0.8.36
0.8.35
0.8.34
0.8.33
0.8.32
0.8.31
0.8.30
0.8.29
0.8.27
0.8.26
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.1
0.8.0
0.7.24
0.7.23
0.7.22
0.7.21
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.3
0.7.2
0.7.1
0.7.0
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.1
0.6.0
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.44
0.4.43
0.4.42
0.4.41
0.4.40
0.4.39
0.4.38
0.4.37
0.4.36
0.4.35
0.4.34
0.4.33
0.4.32
0.4.30
0.4.29
0.4.28
0.4.27
0.4.26
0.4.25
0.4.24
0.4.23
0.4.22
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.0
0.2.0
0.1.0
AI code archaeology
Current section
Files
Jump to
Current section
Files
lib/fnord.ex
defmodule Fnord do
@moduledoc """
Fnord is a code search tool that uses OpenAI's embeddings API to index and
search code files.
"""
@doc """
Main entry point for the application. Parses command line arguments and
dispatches to the appropriate subcommand.
"""
def main(args) do
with {:ok, subcommand, opts} <- parse_options(args) do
case subcommand do
:index -> Indexer.new(opts) |> Indexer.run()
:search -> Search.run(opts)
:summary -> Summary.run(opts)
:torch -> Indexer.new(opts) |> Indexer.delete_project()
:projects -> Store.list_projects() |> Enum.each(&IO.puts(&1))
:files -> Store.new(opts.project) |> Store.list_files() |> Enum.each(&IO.puts(&1))
:ask -> Ask.new(opts) |> Ask.run()
end
else
{:error, reason} -> IO.puts("Error: #{reason}")
end
end
defp parse_options(args) do
project = [
value_name: "PROJECT",
long: "--project",
short: "-p",
help: "Project name",
required: true
]
directory = [
value_name: "DIR",
long: "--dir",
short: "-d",
help: "Directory to index",
required: true
]
reindex = [
long: "--reindex",
short: "-r",
help: "Reindex the project",
default: false
]
quiet = [
long: "--quiet",
short: "-q",
help: "Suppress interactive output",
required: false
]
query = [
value_name: "QUERY",
long: "--query",
short: "-q",
help: "Search query",
required: true
]
limit = [
value_name: "LIMIT",
long: "--limit",
short: "-l",
help: "Limit the number of results",
default: 10
]
detail = [
long: "--detail",
help: "Include AI-generated file summary",
default: false
]
file = [
value_name: "FILE",
long: "--file",
short: "-f",
help: "File to summarize",
required: true
]
concurrency = [
value_name: "CONCURRENCY",
long: "--concurrency",
short: "-c",
help: "Number of concurrent threads to use",
parser: :integer,
default: 4
]
question = [
value_name: "QUESTION",
long: "--question",
short: "-q",
help: "The prompt to ask the AI",
required: true
]
debug = [
long: "--debug",
help: "Print debug output to stderr",
default: false
]
parser =
Optimus.new!(
name: "fnord",
description: "fnord - intelligent code index and search",
about: "Index and search code files",
allow_unknown_args: false,
version: get_version(),
subcommands: [
projects: [
name: "projects",
about: "List all projects",
options: []
],
files: [
name: "files",
about: "List files in a project",
options: [
project: project
]
],
torch: [
name: "torch",
about: "Delete a previously indexed project from the database",
options: [
project: project
]
],
index: [
name: "index",
about: "Index the directory",
options: [
directory: directory,
project: project,
concurrency: concurrency
],
flags: [
reindex: reindex,
quiet: quiet
]
],
search: [
name: "search",
about: "Search in the project",
options: [
project: project,
query: query,
limit: limit,
concurrency: concurrency
],
flags: [
detail: detail
]
],
summary: [
name: "summary",
about: "Get a summary of a file from the project index",
options: [
project: project,
file: file
]
],
ask: [
name: "ask",
about: "Conversational interface to the project database",
options: [
concurrency: concurrency
],
flags: [
debug: debug,
quiet: quiet
],
args: [
project: project,
question: question
]
]
]
)
with {[subcommand], result} <- Optimus.parse!(parser, args) do
options =
result.args
|> Map.merge(result.options)
|> Map.merge(result.flags)
|> maybe_override_quiet()
{:ok, subcommand, options}
else
_ -> {:error, "missing or unknown subcommand"}
end
end
defp get_version do
{:ok, vsn} = :application.get_key(:fnord, :vsn)
to_string(vsn)
end
# Overrides the --quiet flag if it was not already specified by the user and
# the escript is not connected to a tty.
defp maybe_override_quiet(opts) do
cond do
opts[:quiet] -> opts
IO.ANSI.enabled?() -> opts
true -> Map.put(opts, :quiet, true)
end
end
end