Packages

fnord

0.8.44

AI code archaeology

Current section

221 Versions

Jump to

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…