Packages
fnord
0.8.44
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
221 Versions
Jump to
Current section
221 Versions
Compare versions
44
files changed
+2595
additions
-659
deletions
| @@ -21,6 +21,11 @@ test: ## Run tests | |
| 21 21 | mix test |
| 22 22 | @echo |
| 23 23 | |
| 24 | + .PHONY: cover |
| 25 | + cover: ## Run tests with coverage |
| 26 | + mix coveralls |
| 27 | + @echo |
| 28 | + |
| 24 29 | .PHONY: dialyzer |
| 25 30 | dialyzer: ## Run Dialyzer |
| 26 31 | MIX_ENV=dev mix dialyzer |
| @@ -1,6 +1,6 @@ | |
| 1 1 | {<<"links">>,[{<<"GitHub">>,<<"https://github.com/sysread/fnord">>}]}. |
| 2 2 | {<<"name">>,<<"fnord">>}. |
| 3 | - {<<"version">>,<<"0.8.43">>}. |
| 3 | + {<<"version">>,<<"0.8.44">>}. |
| 4 4 | {<<"description">>, |
| 5 5 | <<"An AI powered, conversational interface for your project that learns">>}. |
| 6 6 | {<<"elixir">>,<<"~> 1.18">>}. |
| @@ -48,10 +48,11 @@ | |
| 48 48 | {<<"requirement">>,<<"~> 0.1">>}, |
| 49 49 | {<<"repository">>,<<"hexpm">>}]]}. |
| 50 50 | {<<"files">>, |
| 51 | - [<<"lib">>,<<"lib/ui">>,<<"lib/ui/queue.ex">>,<<"lib/ui/formatter.ex">>, |
| 52 | - <<"lib/ui/output">>,<<"lib/ui/output/production.ex">>, |
| 53 | - <<"lib/ui/output.ex">>,<<"lib/util.ex">>,<<"lib/cmd">>,<<"lib/cmd/ask.ex">>, |
| 54 | - <<"lib/cmd/notes.ex">>,<<"lib/cmd/upgrade.ex">>,<<"lib/cmd/config">>, |
| 51 | + [<<"lib">>,<<"lib/fnord.ex.0.0.bak">>,<<"lib/ui">>,<<"lib/ui/queue.ex">>, |
| 52 | + <<"lib/ui/formatter.ex">>,<<"lib/ui/output">>, |
| 53 | + <<"lib/ui/output/production.ex">>,<<"lib/ui/output.ex">>,<<"lib/util.ex">>, |
| 54 | + <<"lib/cmd">>,<<"lib/cmd/ask.ex">>,<<"lib/cmd/notes.ex">>, |
| 55 | + <<"lib/cmd/upgrade.ex">>,<<"lib/cmd/config">>, |
| 55 56 | <<"lib/cmd/config/approvals.ex">>,<<"lib/cmd/config/mcp.ex">>, |
| 56 57 | <<"lib/cmd/config/utils.ex">>,<<"lib/cmd/prime.ex">>,<<"lib/cmd/files.ex">>, |
| 57 58 | <<"lib/cmd/config.ex">>,<<"lib/cmd/index.ex">>,<<"lib/cmd/frobs.ex">>, |
| @@ -61,13 +62,17 @@ | |
| 61 62 | <<"lib/settings/approvals.ex">>,<<"lib/settings/mcp.ex">>, |
| 62 63 | <<"lib/settings/approvals">>,<<"lib/settings/approvals/regex_matcher.ex">>, |
| 63 64 | <<"lib/ui.ex">>,<<"lib/notifier.ex">>,<<"lib/util">>,<<"lib/util/temp.ex">>, |
| 64 | - <<"lib/store.ex">>,<<"lib/services.ex">>,<<"lib/timed.ex">>, |
| 65 | - <<"lib/cmd.ex">>,<<"lib/git_cli.ex">>,<<"lib/mcp">>, |
| 65 | + <<"lib/store.ex">>,<<"lib/services.ex">>,<<"lib/http.ex.0.0.bak">>, |
| 66 | + <<"lib/timed.ex">>,<<"lib/cmd.ex">>,<<"lib/git_cli.ex">>,<<"lib/mcp">>, |
| 66 67 | <<"lib/mcp/supervisor.ex">>,<<"lib/mcp/fnord_client.ex">>, |
| 67 | - <<"lib/mcp/tools.ex">>,<<"lib/mcp/transport.ex">>,<<"lib/frobs.ex">>, |
| 68 | - <<"lib/spinner.ex">>,<<"lib/ai">>,<<"lib/ai/util.ex">>, |
| 68 | + <<"lib/mcp/tools.ex">>,<<"lib/mcp/transport.ex">>,<<"lib/README.md">>, |
| 69 | + <<"lib/frobs.ex">>,<<"lib/spinner.ex">>,<<"lib/ai">>,<<"lib/ai/util.ex">>, |
| 69 70 | <<"lib/ai/splitter.ex">>,<<"lib/ai/tools.ex">>,<<"lib/ai/tools">>, |
| 70 | - <<"lib/ai/tools/shell.ex">>,<<"lib/ai/tools/notify.ex">>, |
| 71 | + <<"lib/ai/tools/shell.ex">>,<<"lib/ai/tools/tasks">>, |
| 72 | + <<"lib/ai/tools/tasks/push_task.ex">>,<<"lib/ai/tools/tasks/add_task.ex">>, |
| 73 | + <<"lib/ai/tools/tasks/create_list.ex">>, |
| 74 | + <<"lib/ai/tools/tasks/resolve_task.ex">>, |
| 75 | + <<"lib/ai/tools/tasks/show_list.ex">>,<<"lib/ai/tools/notify.ex">>, |
| 71 76 | <<"lib/ai/tools/coder.ex">>,<<"lib/ai/tools/notes.ex">>, |
| 72 77 | <<"lib/ai/tools/file">>,<<"lib/ai/tools/file/edit.ex">>, |
| 73 78 | <<"lib/ai/tools/file/notes.ex">>,<<"lib/ai/tools/file/spelunker.ex">>, |
| @@ -76,13 +81,12 @@ | |
| 76 81 | <<"lib/ai/tools/file/search.ex">>,<<"lib/ai/tools/research.ex">>, |
| 77 82 | <<"lib/ai/tools/apply_patch.ex">>,<<"lib/ai/tools/troubleshooter.ex">>, |
| 78 83 | <<"lib/ai/tools/list_projects.ex">>,<<"lib/ai/notes.ex">>, |
| 79 | - <<"lib/ai/tool_call_deduper.ex">>,<<"lib/ai/completion">>, |
| 80 | - <<"lib/ai/completion/output.ex">>,<<"lib/ai/agent">>, |
| 81 | - <<"lib/ai/agent/code_mapper.ex">>,<<"lib/ai/agent/compactor.ex">>, |
| 82 | - <<"lib/ai/agent/coordinator.ex">>,<<"lib/ai/agent/nomenclater.ex">>, |
| 83 | - <<"lib/ai/agent/spelunker.ex">>,<<"lib/ai/agent/code">>, |
| 84 | - <<"lib/ai/agent/code/task_implementor.ex">>, |
| 85 | - <<"lib/ai/agent/code/planner.ex">>, |
| 84 | + <<"lib/ai/completion">>,<<"lib/ai/completion/output.ex">>, |
| 85 | + <<"lib/ai/agent">>,<<"lib/ai/agent/code_mapper.ex">>, |
| 86 | + <<"lib/ai/agent/compactor.ex">>,<<"lib/ai/agent/coordinator.ex">>, |
| 87 | + <<"lib/ai/agent/nomenclater.ex">>,<<"lib/ai/agent/spelunker.ex">>, |
| 88 | + <<"lib/ai/agent/code">>,<<"lib/ai/agent/code/task_implementor.ex">>, |
| 89 | + <<"lib/ai/agent/code/task_planner.ex">>, |
| 86 90 | <<"lib/ai/agent/code/task_validator.ex">>, |
| 87 91 | <<"lib/ai/agent/code/repatcher.ex">>,<<"lib/ai/agent/code/common.ex">>, |
| 88 92 | <<"lib/ai/agent/code/patcher.ex">>,<<"lib/ai/agent/troubleshooter.ex">>, |
| @@ -90,20 +94,23 @@ | |
| 90 94 | <<"lib/ai/agent/intuition.ex">>,<<"lib/ai/agent/motd.ex">>, |
| 91 95 | <<"lib/ai/agent/file_summary.ex">>,<<"lib/ai/completion.ex">>, |
| 92 96 | <<"lib/ai/notes">>,<<"lib/ai/notes/external_docs.ex">>, |
| 97 | + <<"lib/ai/agent.ex.0.1.bak">>,<<"lib/ai/agent.ex.0.0.bak">>, |
| 93 98 | <<"lib/ai/accumulator.ex">>,<<"lib/ai/model.ex">>, |
| 94 99 | <<"lib/ai/embeddings.ex">>,<<"lib/ai/completion_api.ex">>, |
| 95 100 | <<"lib/ai/pretend_tokenizer.ex">>,<<"lib/ai/agent.ex">>,<<"lib/fnord.ex">>, |
| 96 | - <<"lib/indexer.ex">>,<<"lib/http.ex">>,<<"lib/services">>, |
| 97 | - <<"lib/services/conversation.ex">>,<<"lib/services/notes.ex">>, |
| 101 | + <<"lib/indexer.ex">>,<<"lib/http.ex">>,<<"lib/http_pool.ex">>, |
| 102 | + <<"lib/services">>,<<"lib/services/conversation.ex">>, |
| 103 | + <<"lib/services/background_indexer.ex">>,<<"lib/services/notes.ex">>, |
| 98 104 | <<"lib/services/task.ex">>,<<"lib/services/backup_file.ex">>, |
| 99 | - <<"lib/services/name_pool.ex">>,<<"lib/services/mcp">>, |
| 100 | - <<"lib/services/mcp/client.ex">>,<<"lib/services/approvals.ex">>, |
| 105 | + <<"lib/services/name_pool.ex">>,<<"lib/services/approvals.ex">>, |
| 101 106 | <<"lib/services/model_performance_tracker.ex">>,<<"lib/services/once.ex">>, |
| 102 107 | <<"lib/services/mcp.ex">>,<<"lib/services/approvals">>, |
| 103 108 | <<"lib/services/approvals/shell.ex">>,<<"lib/services/approvals/edit.ex">>, |
| 104 109 | <<"lib/services/approvals/shell">>, |
| 105 110 | <<"lib/services/approvals/shell/prefix.ex">>, |
| 106 | - <<"lib/services/approvals/workflow.ex">>,<<"lib/services/conversation">>, |
| 111 | + <<"lib/services/approvals/workflow.ex">>, |
| 112 | + <<"lib/services/background_indexer.ex.0.0.bak">>, |
| 113 | + <<"lib/services/conversation">>, |
| 107 114 | <<"lib/services/conversation/interrupts.ex">>,<<"lib/search.ex">>, |
| 108 115 | <<"lib/store">>,<<"lib/store/project">>, |
| 109 116 | <<"lib/store/project/conversation.ex">>,<<"lib/store/project/notes.ex">>, |
| @@ -0,0 +1,18 @@ | |
| 1 | + # Module Organization |
| 2 | + |
| 3 | + This project follows Perl-style module organization where module names correspond directly to file paths. |
| 4 | + |
| 5 | + ## Structure |
| 6 | + |
| 7 | + - `Foo` → `lib/foo.ex` |
| 8 | + - `Foo.Bar` → `lib/foo/bar.ex` |
| 9 | + - `Foo.Bar.Baz` → `lib/foo/bar/baz.ex` |
| 10 | + |
| 11 | + ## Examples |
| 12 | + |
| 13 | + - `UI` module is in `lib/ui.ex` |
| 14 | + - `UI.Queue` module is in `lib/ui/queue.ex` |
| 15 | + - `AI.Tools.Shell` module is in `lib/ai/tools/shell.ex` |
| 16 | + - `Services.Approvals.Shell` module is in `lib/services/approvals/shell.ex` |
| 17 | + |
| 18 | + This organization makes it easy to locate modules and understand the codebase hierarchy at a glance. |
| \ No newline at end of file |
| @@ -63,7 +63,11 @@ defmodule AI.Agent do | |
| 63 63 | """ |
| 64 64 | @spec get_response(t, map) :: {:ok, any} | {:error, any} |
| 65 65 | def get_response(agent, args) do |
| 66 | + parent_pool = HttpPool.get() |
| 67 | + |
| 66 68 | Task.async(fn -> |
| 69 | + HttpPool.set(parent_pool) |
| 70 | + |
| 67 71 | agent = |
| 68 72 | cond do |
| 69 73 | agent.named? && is_binary(agent.name) -> |
| @@ -0,0 +1,129 @@ | |
| 1 | + defmodule AI.Agent do |
| 2 | + @moduledoc """ |
| 3 | + Behavior for AI agents that process instructions and return responses. |
| 4 | + |
| 5 | + This behavior defines the contract between the coordinator and specialized |
| 6 | + agents, ensuring consistent interfaces and proper error handling across the |
| 7 | + agent system. |
| 8 | + """ |
| 9 | + |
| 10 | + # ---------------------------------------------------------------------------- |
| 11 | + # Behavior Callbacks |
| 12 | + # ---------------------------------------------------------------------------- |
| 13 | + @callback get_response(map) :: {:ok, any} | {:error, any} |
| 14 | + |
| 15 | + # ---------------------------------------------------------------------------- |
| 16 | + # Public API |
| 17 | + # ---------------------------------------------------------------------------- |
| 18 | + defstruct [ |
| 19 | + :name, |
| 20 | + :named?, |
| 21 | + :impl |
| 22 | + ] |
| 23 | + |
| 24 | + @type t :: %__MODULE__{ |
| 25 | + name: nil | binary, |
| 26 | + named?: boolean, |
| 27 | + impl: module |
| 28 | + } |
| 29 | + |
| 30 | + @doc """ |
| 31 | + Create a new agent instance. |
| 32 | + |
| 33 | + If `:named?` is set to `false`, the agent will not be assigned a name. This |
| 34 | + is intended specifically for `AI.Agent.Nomenclater`, which does the naming on |
| 35 | + behalf of `Services.NamePool`, which can't be used directly by `Nomenclater` |
| 36 | + because that would create a circular dependency. |
| 37 | + """ |
| 38 | + @spec new(module, keyword) :: t |
| 39 | + def new(impl, opts \\ []) do |
| 40 | + with :ok <- validate_agent_module(impl) do |
| 41 | + %__MODULE__{ |
| 42 | + name: Keyword.get(opts, :name, nil), |
| 43 | + named?: Keyword.get(opts, :named?, true), |
| 44 | + impl: impl |
| 45 | + } |
| 46 | + else |
| 47 | + {:error, reason} -> |
| 48 | + raise "Failed to create agent: #{inspect(reason)}" |
| 49 | + end |
| 50 | + end |
| 51 | + |
| 52 | + @doc """ |
| 53 | + Delegate to the agent implementation's `get_response/1` function. Includes |
| 54 | + the agent in the args map. |
| 55 | + |
| 56 | + The agent's name is managed here, checking out a name from |
| 57 | + `Services.NamePool` if the agent is named but doesn't yet have a name, or |
| 58 | + associating the agent's existing name if it does. The name is checked back in |
| 59 | + after the response is generated. |
| 60 | + |
| 61 | + The call is wrapped in a `Task` to provide a global identifier for logging |
| 62 | + and tracing purposes, which is associated with the agent's name. |
| 63 | + """ |
| 64 | + @spec get_response(t, map) :: {:ok, any} | {:error, any} |
| 65 | + def get_response(agent, args) do |
| 66 | + Task.async(fn -> |
| 67 | + agent = |
| 68 | + cond do |
| 69 | + agent.named? && is_binary(agent.name) -> |
| 70 | + Services.NamePool.associate_name(agent.name) |
| 71 | + agent |
| 72 | + |
| 73 | + agent.named? -> |
| 74 | + Services.NamePool.checkout_name() |
| 75 | + |> case do |
| 76 | + {:ok, name} -> %{agent | name: name} |
| 77 | + {:error, _} -> %{agent | name: Services.NamePool.default_name()} |
| 78 | + end |
| 79 | + |
| 80 | + true -> |
| 81 | + name = Services.NamePool.default_name() |
| 82 | + %{agent | name: name} |
| 83 | + end |
| 84 | + |
| 85 | + try do |
| 86 | + args |
| 87 | + |> Map.put(:agent, agent) |
| 88 | + |> agent.impl.get_response() |
| 89 | + after |
| 90 | + if agent.named? && is_binary(agent.name) do |
| 91 | + Services.NamePool.checkin_name(agent.name) |
| 92 | + end |
| 93 | + end |
| 94 | + end) |
| 95 | + |> Task.await(:infinity) |
| 96 | + end |
| 97 | + |
| 98 | + @doc """ |
| 99 | + Delegate to `AI.Completion.get/1` with the agent's name included in the args. |
| 100 | + Intended to be called by implementors of `AI.Agent` when they need to |
| 101 | + generate completions as part of their response processing. |
| 102 | + """ |
| 103 | + @spec get_completion(t, keyword) :: {:ok, AI.Completion.t()} | {:error, any} |
| 104 | + def get_completion(agent, args) do |
| 105 | + args |
| 106 | + |> Keyword.put(:name, agent.name) |
| 107 | + |> AI.Completion.get() |
| 108 | + end |
| 109 | + |
| 110 | + @doc """ |
| 111 | + Delegate to `AI.Completion.tools_used/1` to extract the tools used from a |
| 112 | + completion. |
| 113 | + """ |
| 114 | + @spec tools_used(AI.Completion.t()) :: %{binary => non_neg_integer()} |
| 115 | + def tools_used(completion) do |
| 116 | + AI.Completion.tools_used(completion) |
| 117 | + end |
| 118 | + |
| 119 | + # ---------------------------------------------------------------------------- |
| 120 | + # Private functions |
| 121 | + # ---------------------------------------------------------------------------- |
| 122 | + defp validate_agent_module(mod) do |
| 123 | + cond do |
| 124 | + !Code.ensure_loaded?(mod) -> {:error, :module_not_found} |
| 125 | + !function_exported?(mod, :__info__, 1) -> {:error, :not_a_module} |
| 126 | + true -> :ok |
| 127 | + end |
| 128 | + end |
| 129 | + end |
Loading more files…