Skip to content
Open

wip #33

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
139 commits
Select commit Hold shift + click to select a range
911bbd8
Router: dispatch bare 'el <name>' as :info
msuarz May 7, 2026
97e8d7e
Fix test name to match assertion: info not start
msuarz May 7, 2026
a36e6cd
Add execute(:info, ...) for el <name> router
msuarz May 7, 2026
2cecde3
help wip
msuarz May 7, 2026
ab4719f
Guard execute(:info) with alive? check; fall back to usage
msuarz May 7, 2026
edd6165
Refactor execute(:info) to pattern-matched clauses
msuarz May 7, 2026
6dd1eee
Add router clause for restart dispatch
msuarz May 7, 2026
f489244
delete_stores/:restart preserves DETS stores
msuarz May 7, 2026
2dd68be
Add restart/2 facade to resume Claude session with preserved ID
msuarz May 7, 2026
721f371
Split restart spec — one assertion per test
msuarz May 7, 2026
b56650c
Wire restart execute clause in CLI
msuarz May 7, 2026
249d934
Split :restart CLI spec — one assertion per test
msuarz May 7, 2026
7a22895
Add glob pattern support to restart command.
msuarz May 7, 2026
3281701
Rename test to match assertion on output text
msuarz May 7, 2026
9810d70
Add restart_daemon route for el restart (no name)
msuarz May 7, 2026
ce5a4cf
Add stop_daemon/0 to Daemon module with RPC and polling
msuarz May 7, 2026
b97f0e0
Inject stop_daemon timing knobs; tighten specs
msuarz May 7, 2026
45e3b96
Drop unused stop_daemon/3 overload
msuarz May 7, 2026
0d9143e
Add restart_daemon/1 to compose stop, spawn, ready
msuarz May 7, 2026
d834b79
Move expect arg check into closure head
msuarz May 7, 2026
24186de
Wire execute(:restart_daemon) in CLI
msuarz May 7, 2026
3d0ce1d
Declare @behaviour on El.CLI.Daemon
msuarz May 7, 2026
0833ca2
Bind name_atom to silence credo on cli.ex
msuarz May 7, 2026
8fe315b
Extract stop_daemon_deps helper
msuarz May 7, 2026
24093a8
Extract restart_daemon_deps helper
msuarz May 7, 2026
846e555
Extract El.CLI.Daemon.Env, reduce daemon.ex module length
msuarz May 7, 2026
f57928b
Wrap El.CLI.Daemon.Env in behaviour with Mox mock
msuarz May 7, 2026
4981884
Refactor wait_until_disconnected to use state map, make env injectable.
msuarz May 7, 2026
41e7109
TCR: Extract El.CLI.Daemon.Connection with London harness.
msuarz May 7, 2026
9820241
TCR: extract El.Pattern with behaviour, mock, injection
msuarz May 7, 2026
5d6eae8
TCR: split log/3 test - side effect and return value
msuarz May 7, 2026
e1354af
Router route for :clear_all
msuarz May 7, 2026
7852266
Add :clear_all CLI execute clause
msuarz May 7, 2026
a4b47ae
Test :log_n glob with all calls log_pattern
msuarz May 7, 2026
4536b42
Update help.md to reflect implemented features
msuarz May 7, 2026
20ebc68
Add :info_json route clause for name -json flag
msuarz May 7, 2026
dfe937f
Add router clause for :log_json with -json flag
msuarz May 7, 2026
d934804
Add router clause for :log_n_json
msuarz May 7, 2026
a2223a9
add jason dep
msuarz May 7, 2026
70b1e10
Add El.CLI.Output.Json.info/1 pure formatter
msuarz May 7, 2026
b46132c
Add dead-session clause to El.CLI.Output.Json.info/1
msuarz May 7, 2026
f3dfee5
wire execute(:info_json) dead-session path via El.CLI.Json
msuarz May 7, 2026
305c19d
wire execute(:info_json) alive-session path in El.CLI.Json
msuarz May 7, 2026
ba78a97
extract :log and :log_n bodies to El.CLI.Log to free cli.ex
msuarz May 7, 2026
a4a467e
wire execute(:log_json) via El.CLI.Json
msuarz May 7, 2026
4f9c0f9
wire execute(:log_n_json) via El.CLI.Json.execute_log_n
msuarz May 7, 2026
b6ca0a5
gate :log_json paths on alive? returning [] for dead session
msuarz May 7, 2026
35dcd27
gate plain log path on alive?
msuarz May 7, 2026
a2277b8
add explicit start keyword to routes
msuarz May 7, 2026
0c6cd34
pin log all -json contract
msuarz May 7, 2026
5ab28d9
refresh help plan with H5/H6/H9/H10/H11 status
msuarz May 7, 2026
6df84e8
rewrite usage_message to match help.feature block
msuarz May 7, 2026
63b5c79
sync README help block with features/help.feature
msuarz May 7, 2026
d0ebe17
restore settings hooks, deny cucumber+push
msuarz May 7, 2026
03af8f4
fix el_ hook regex to match tags without @ symbol
msuarz May 8, 2026
43fc40b
drop hooks_spec, kenny tcr reflex
msuarz May 8, 2026
87e392e
el tag interpolation: append start subcommand
msuarz May 8, 2026
e72987a
Revert "el tag interpolation: append start subcommand"
msuarz May 8, 2026
0b162a6
Lock regression: SessionCard must render model short form
msuarz May 8, 2026
df8286f
fetch agent in info route handler and pass to print_session_info
msuarz May 8, 2026
09ac515
plumb model from session state into opts for print_session_info
msuarz May 8, 2026
314f790
restore session_card_spec line 222 lock assertion
msuarz May 8, 2026
f35df27
TCR: auto-commit
msuarz May 8, 2026
1382a94
TCR: auto-commit
msuarz May 8, 2026
8b99f82
Delete 5 redundant normalize_model unit tests, defp.
msuarz May 8, 2026
02cdd9d
Restore feature files, blocked on design decision
msuarz May 8, 2026
4f14cdf
add guard test for model double-emit regression
msuarz May 8, 2026
9c0fb0d
Fix Before hook: call 'el <name> start' not bare 'el <name>'
msuarz May 8, 2026
fbf2491
TCR: auto-commit
msuarz May 8, 2026
44c37bb
no cukes
msuarz May 8, 2026
a9f81ff
update agent.feature: bare name now requires start subcommand
msuarz May 8, 2026
8f562d3
green cukes deny woes
msuarz May 8, 2026
293f5e7
Remove duplicate model in card when no agent
msuarz May 8, 2026
6a4039f
Revert "Remove duplicate model in card when no agent"
msuarz May 8, 2026
f09106a
fix: emit model once when session has no agent
msuarz May 8, 2026
0bcda71
Fix duplicate model emission in session card
msuarz May 8, 2026
1357be4
Omit id label when session id is nil
msuarz May 8, 2026
f2f6a38
Fix hooks regex to match tag prefix @el_*
msuarz May 8, 2026
2100287
Revert "Omit id label when session id is nil"
msuarz May 8, 2026
b660f1c
Reapply "Omit id label when session id is nil"
msuarz May 8, 2026
58c2afd
card.feature anom: drop id from start expectation
msuarz May 8, 2026
19391f8
Revert "card.feature anom: drop id from start expectation"
msuarz May 8, 2026
5f61e5b
Revert "Reapply "Omit id label when session id is nil""
msuarz May 8, 2026
045dc71
generalize ping_if_agent -> ping_for_session_id
msuarz May 8, 2026
93b3055
wire ping_for_session_id into non-agent start path
msuarz May 8, 2026
39e2112
lock probe filter for non-agent path
msuarz May 8, 2026
00f1065
Revert "lock probe filter for non-agent path"
msuarz May 8, 2026
0751d1b
filter quiet probe from state.messages
msuarz May 8, 2026
ff980c0
Revert "filter quiet probe from state.messages"
msuarz May 8, 2026
32067d8
add probe flag to skip storage at prepare
msuarz May 8, 2026
4416d60
handle probe_ask message in Session GenServer
msuarz May 8, 2026
4db3225
reset_session leaves session_id nil for next probe
msuarz May 8, 2026
cb49d73
pin session_id repopulation after reset
msuarz May 8, 2026
223884f
Revert "pin session_id repopulation after reset"
msuarz May 8, 2026
164f1dc
pin :complete_ask cast handler captures session_id
msuarz May 8, 2026
00b122c
rename extract_resume_or_id to pop_resume
msuarz May 8, 2026
90d7779
inline pop_resume into session.ex
msuarz May 8, 2026
b1699c0
delete session_id/1 fallback
msuarz May 8, 2026
33024ce
delete generate_session_id
msuarz May 8, 2026
a713fd3
delete orphan format_uuid
msuarz May 8, 2026
b6a1ba4
delete empty id.ex module
msuarz May 8, 2026
28c2c25
extract maybe_put helper from print_info
msuarz May 8, 2026
c20bf9d
extract build_opts from print_info
msuarz May 8, 2026
045ea87
add FolderSize credo check skeleton
msuarz May 8, 2026
97ebd87
extract El.CLI.Info module
msuarz May 8, 2026
055dbb1
Revert "add FolderSize credo check skeleton"
msuarz May 8, 2026
c9ead97
repoint cli.ex to El.CLI.Info
msuarz May 8, 2026
223be62
TCR: auto-commit
msuarz May 8, 2026
9eff733
Folder-size B: emit issue when folder exceeds max
msuarz May 8, 2026
59041d3
Revert "Folder-size B: emit issue when folder exceeds max"
msuarz May 8, 2026
614fbb5
Folder-size B: emit issue when folder exceeds max (v2)
msuarz May 8, 2026
7691ad5
Folder-size C: emit issue when folder under min
msuarz May 8, 2026
b2ff863
Folder-size D: add :exempt param
msuarz May 8, 2026
a5df0f9
Folder-size E1: :exempt_names param + behaviours default
msuarz May 8, 2026
14caf98
Folder-size E1b: exempt_names matches any path segment
msuarz May 8, 2026
503c03d
Folder-size E2: exempt lib from FolderSize check
msuarz May 8, 2026
1b1ea28
Folder-size E3: rename Output.Json to OutputJson, drop empty dir
msuarz May 8, 2026
dd3ed7d
Folder-size E4: document FolderSize follow-ups
msuarz May 8, 2026
6f5ead0
remove dead folder_name binding
msuarz May 8, 2026
2b66169
credo only on lib/, drop test/specs from scope
msuarz May 8, 2026
ef231f4
Update spec mocks: ask for agent path, probe_ask for non-agent
msuarz May 8, 2026
a841108
Matcher: substring first, regex fallback
msuarz May 8, 2026
dedac79
Move Tell to Session.Handlers
msuarz May 8, 2026
2b1ded5
Move tell_spec to specs/el/session/handlers
msuarz May 8, 2026
44bebeb
Move Ask to Session.Handlers
msuarz May 8, 2026
fedeb32
Mock session_api.agent in ping_for_session_id specs
msuarz May 8, 2026
efc5e3f
Move Parser behaviour to ClaudePort
msuarz May 8, 2026
8a3aa8f
fold Platform.Parser into ClaudePort.Parser
msuarz May 8, 2026
b30ef9f
flatten Platform.Code to Platform
msuarz May 8, 2026
f613fd9
Delete dead Platform.Parser, drop platform exempt.
msuarz May 8, 2026
e0dea74
route Daemon.Env through Env behaviour
msuarz May 8, 2026
56e0223
route PortSpawn env reads through Env behaviour
msuarz May 8, 2026
4784f01
Tighten port_spawn specs to one assertion each
msuarz May 8, 2026
61a22da
route ClaudePort.State cwd through FileSystem behaviour
msuarz May 8, 2026
426649e
Move fs_module setup out of test bodies
msuarz May 8, 2026
e40bdb9
add Executable behaviour and route find_executable through it
msuarz May 8, 2026
11bf405
Fix Executable typespec and extract spec setup
msuarz May 8, 2026
6cf08b3
cross machine daemon via EL_HOST EL_NODE
msuarz Jul 4, 2026
bd00048
Merge rescue-11bf405 with cross-machine daemon EL_HOST EL_NODE
msuarz Jul 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .claude/plans/folder_size.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# FolderSize follow-ups

`El.Credo.FolderSize` enforces 3 ≤ .ex files per folder ≤ 13.

## Config

`.credo.exs`:

```elixir
{El.Credo.FolderSize, [exempt: ["lib"], exempt_names: ["behaviours"]]}
```

- `exempt: ["lib"]` — package root (`lib/el.ex` only).
- `exempt_names: ["behaviours"]` — any folder with `behaviours` as a path segment. Behaviours dirs mirror their impl folder; refactor the impl, the behaviours follow.

## Outstanding LEAVE-AS-FINDING folders

These are real namespaces flagged by the check. Don't exempt — revisit when scope changes.

| Folder | Files now | Note |
|---|---|---|
| `lib/el/cli/daemon` | 2 (connection.ex, env.ex; sibling `behaviours/`) | Real namespace, grows with daemon work. |
| `lib/el/platform` | 2 (code.ex, parser.ex; sibling `behaviours/`) | Same shape as cli/daemon. |
| `lib/el/session/claude` | 2 (driver.ex, opts.ex) | Cohesive but thin. Revisit when claude/ grows or shrinks. |
| `lib/el/session/commands` | 2 (ask.ex, tell.ex) | Command-pattern namespace, grows with commands. |

Re-evaluate when any of these folders gain/lose a file.
21 changes: 21 additions & 0 deletions .claude/plans/help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Help Plan

Gap list between `features/help.feature` and current implementation.

## Not Implemented

- `Output.usage_message` layout doesn't match the grouped sectioned block in `features/help.feature` (info/log with `[-json]`, `start [args]` with sub-args, `cmds:` group). Tracked as H7.
- README + `~/.claude/skills/el/` docs don't mention `-json`. Tracked as H8.

## Already Working

- `el -v`, `el ls`, `el exit`, `el clear`
- `el <name> start [-m model] [-a agent]` — explicit `start` keyword wired (H9)
- `el <name> <msg>`
- `el <name>` → info (alive ? info : usage)
- `el <name> [-json]` → JSON info (alive: full shape; dead: `{name, alive: false}`)
- `el <name> log [n|all]`, `el <name> log all`
- `el <name> log [n|all] [-json]` → JSON array of `{type, message, response, metadata}` (dead session → `[]`; H6/H11 pin)
- `el <name|glob> clear`, `el <name|glob> exit`, `el <name|glob> restart`, `el <name|glob> log [n|all]`
- `el restart` → daemon restart (kills daemon BEAM + all `claude` subprocesses; `Restorer.restore_sessions` rehydrates every session from DETS on boot — strict superset of "restart all sessions")
- Plain `el <name> log` gated on `alive?` — dead session prints "No sessions running" instead of crashing (H10)
25 changes: 23 additions & 2 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,28 @@
{
"model": "opus",
"permissions": {
"allow": ["Write", "Bash", "Bash(bundle exec cucumber*)"],
"deny": []
"deny": ["Bash(* cucumber *)", "Bash(git push *)"]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "jq -r '[.tool_input.file_path // \"\", .tool_input.new_string // \"\", .tool_input.content // \"\"] | @tsv' | awk -F'\t' '$1 ~ /\\.claude\\/settings(\\.local)?\\.json$/ && ($2 ~ /allow/ || $3 ~ /allow/) {print; exit}' | grep -q . && printf '{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"Blocked: Claude is not allowed to add allow permissions to .claude/settings.json\"}}' || true"
}
]
},
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "cmd=$(jq -r '.tool_input.command // \"\"'); echo \"$cmd\" | grep -q '\\.claude/settings.*\\.json' && echo \"$cmd\" | grep -q 'allow' && printf '{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"Blocked: Claude is not allowed to modify allow permissions in .claude/settings.json\"}}' || true"
}
]
}
]
}
}
17 changes: 4 additions & 13 deletions .credo.exs
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,8 @@
# You can give explicit globs or simply directories.
# In the latter case `**/*.{ex,exs}` will be used.
#
included: [
"lib/",
"specs/",
"src/",
"test/",
"web/",
"apps/*/lib/",
"apps/*/src/",
"apps/*/test/",
"apps/*/web/"
],
excluded: [~r"/_build/", ~r"/deps/", ~r"/node_modules/", ~r"specs/"]
included: ["lib/"],
excluded: [~r"/_build/", ~r"/deps/", ~r"/node_modules/"]
},
#
# Load and configure plugins here:
Expand All @@ -42,7 +32,7 @@
# If you create your own checks, you must specify the source files for
# them here, so they can be loaded by Credo before running the analysis.
#
requires: ["credo_checks/line_check.ex", "credo_checks/max_function_lines.ex", "credo_checks/max_module_lines.ex"],
requires: ["credo_checks/line_check.ex", "credo_checks/max_function_lines.ex", "credo_checks/max_module_lines.ex", "credo_checks/folder_size.ex"],
#
# If you want to enforce a style guide and need a more traditional linting
# experience, you can change `strict` to `true` below:
Expand Down Expand Up @@ -76,6 +66,7 @@
{Credo.Check.Design.DuplicatedCode, mass_threshold: 40},
{El.Credo.MaxFunctionLines, [max_lines: 5]},
{El.Credo.MaxModuleLines, [max_lines: 100]},
{El.Credo.FolderSize, [exempt: ["lib"], exempt_names: ["behaviours"]]},
{Credo.Check.Warning.Dbg, []},
{Credo.Check.Warning.IoInspect, []},
{Credo.Check.Warning.IExPry, []},
Expand Down
26 changes: 18 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,22 @@ brew install limadelic/tap/el

```
> el
el -v version
el ls list sessions
el <name> [-m <model>] [-a <agent>] start or status
el <name> <msg> send a msg
el <name|glob> log [n|all] view log (default: last 1)
el <name|glob> clear clear log
el <name|glob> exit exit session
el exit exit all sessions
el -v version
el ls list names
el <name> [-json] info
el <name> log [n|all] [-json] view log (default: last 1)

el <name> start [args] start session
args:
-m <model> model
-a <agent> agent

el <name> <msg> send a msg

el <name|glob> <cmd> apply command to one or many
el <cmd> apply command to all
cmds:
clear start new session
exit exit session
restart restart session
```
63 changes: 63 additions & 0 deletions credo_checks/folder_size.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
defmodule El.Credo.FolderSize do
use Credo.Check, category: :refactor, base_priority: :normal, run_on_all: true

alias Credo.SourceFile
alias Credo.Check.Params
alias Credo.IssueMeta

def param_defaults do
[max: 13, min: 3, exempt: [], exempt_names: []]
end

@check_desc "Folder structure is maintainable."

def explanations do
[
check: @check_desc,
params: [
max: "Maximum .ex files allowed in a folder (default 13)",
min: "Minimum .ex files required in a folder (default 3)",
exempt: "List of folder paths to skip (default [])",
exempt_names: "List of folder names to skip (default [])"
]
]
end

def run(%SourceFile{} = _source_file, _params) do
[]
end

def run_on_all_source_files(exec, source_files, params) do
max = Params.get(params, :max, __MODULE__)
min = Params.get(params, :min, __MODULE__)
exempt = Params.get(params, :exempt, __MODULE__)
exempt_names = Params.get(params, :exempt_names, __MODULE__)
source_files
|> Enum.group_by(fn sf -> Path.dirname(sf.filename) end)
|> Enum.each(fn {folder, files} ->
skip? = folder in exempt or Enum.any?(Path.split(folder), &(&1 in exempt_names))
unless skip? do
sf = hd(files)
issue_meta = IssueMeta.for(sf, params)
file_count = length(files)

if file_count > max do
issue = format_issue(issue_meta,
message: "Folder #{folder} has #{file_count} .ex files (max: #{max})",
line_no: 1
)
Credo.Execution.ExecutionIssues.append(exec, sf, issue)
end

if file_count < min do
issue = format_issue(issue_meta,
message: "Folder #{folder} has #{file_count} .ex files (min: #{min})",
line_no: 1
)
Credo.Execution.ExecutionIssues.append(exec, sf, issue)
end
end
end)
:ok
end
end
4 changes: 2 additions & 2 deletions features/agent.feature
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Feature: Agent support
* > el kenny exit

Scenario: Implicit agent detection from name
* > el kent:
* > el kent start:
| agent | kent |
| model | opus |
* > el kent exit
Expand All @@ -19,7 +19,7 @@ Feature: Agent support
* > el kent exit

Scenario: Lisa agent with sonnet model
* > el lisa:
* > el lisa start:
| agent | lisa |
| model | sonnet |
* > el lisa exit
10 changes: 5 additions & 5 deletions features/card.feature
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ Feature: Agent card
* > el <name> exit

Examples:
| args | name |
| kent | kent |
| kento -a kent | kento |
| kent@el | kent@el |
| args | name |
| kent start | kent |
| kento start -a kent | kento |
| kent@el start | kent@el |

@el_kent
Scenario: Used
Expand All @@ -41,7 +41,7 @@ Feature: Agent card
* > el kent exit

Scenario: Anom
* > el anom:
* > el anom start:
"""
╭────────────────────────────────────────────────╮
│ name: anom id: … │
Expand Down
37 changes: 24 additions & 13 deletions features/help.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,30 @@ Feature: Help
Scenario Outline: <scenario>
* > el <args>:
"""
el v0.1.
el -v version
el ls list sessions
el <name> [-m <model>] [-a <agent>] start or status
el <name> <msg> send a msg
el <name|glob> log [n|all] view log (default: last 1)
el <name|glob> clear clear log
el <name|glob> exit exit session
el exit exit all sessions
el restart restart daemon
el v0.1.*

el -v version
el ls list names
el <name> [-json] info
el <name> log [n|all] [-json] view log (default: last 1)

el <name> start [args] start session
args:
-m <model> model
-a <agent> agent

el <name> <msg> send a msg

el <name|glob> <cmd> apply command to one or many
el <cmd> apply command to all
cmds:
clear start new session
exit exit session
restart restart session
"""

Examples:
| scenario | args |
| Help | |
| Usage | --nonsense |
| scenario | args |
| Help | |
| Usage not a flag | --noflag |
| Usage not an agent | nobody |
8 changes: 7 additions & 1 deletion features/support/el_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ def verify_docstring(args, expected)
next if words.empty?

words.each do |word|
unless output.downcase.include?(word.downcase)
matched = output.downcase.include?(word.downcase) ||
begin
Regexp.new(word, Regexp::IGNORECASE).match?(output)
rescue RegexpError
false
end
unless matched
missing_words << { word: word, source_line: expected_line }
end
end
Expand Down
2 changes: 1 addition & 1 deletion features/support/hooks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
end

Before do |scenario|
scenario.tags.map(&:name).grep(/^@el_(.+)$/) { el($1) }
scenario.tags.map(&:name).grep(/^@el_(.+)$/) { el("#{$1} start") }
end

After do |scenario|
Expand Down
41 changes: 18 additions & 23 deletions lib/el.ex
Original file line number Diff line number Diff line change
Expand Up @@ -62,37 +62,32 @@ defmodule El do
end

@impl true
def exit_pattern(pattern, opts \\ []) do
ls(opts)
|> Enum.filter(&match_pattern?(&1, pattern))
|> Enum.each(&El.exit(&1, opts))
def restart(name, opts \\ []) do
El.Lifecycle.exit(name, :restart, opts)
resume_session(name, opts)
end

@impl true
def clear_pattern(pattern, opts \\ []) do
ls(opts) |> Enum.filter(&match_pattern?(&1, pattern)) |> Enum.each(&El.clear(&1, opts))
defp resume_session(name, opts) do
session_meta = Keyword.get(opts, :session_meta, El.Session.Meta)
resume_with_session_meta(name, opts, session_meta.lookup(name))
end

@impl true
def log_pattern(pattern, count, opts \\ []),
do:
ls(opts) |> Enum.filter(&match_pattern?(&1, pattern)) |> Enum.flat_map(&log_entries(&1, count, opts))

defp log_entries(name, count, opts) do
name |> session_api(opts).log(count) |> filter_found()
defp resume_with_session_meta(name, opts, {:ok, session_id, agent, model}) do
resume_opts = [resume: session_id, agent: agent, model: model] ++ opts
start(name, resume_opts)
end

defp filter_found(:not_found), do: []
defp filter_found(entries), do: entries
@impl true
defdelegate restart_pattern(pattern, opts \\ []), to: El.Pattern, as: :restart

defp match_pattern?(name, pattern) do
name_str = Atom.to_string(name)
regex_pattern = pattern_to_regex(pattern)
Regex.match?(~r/^#{regex_pattern}$/, name_str)
end
@impl true
defdelegate exit_pattern(pattern, opts \\ []), to: El.Pattern, as: :exit

@impl true
defdelegate clear_pattern(pattern, opts \\ []), to: El.Pattern, as: :clear

defp pattern_to_regex(pattern),
do: pattern |> String.replace("*", ".*") |> String.replace("?", ".")
@impl true
defdelegate log_pattern(pattern, count, opts \\ []), to: El.Pattern, as: :log

@impl true
def ls(opts \\ []) do
Expand Down
4 changes: 2 additions & 2 deletions lib/el/agent/detector.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ defmodule El.Agent.Detector do
check_paths(fs, paths(name))
end

def cwd(fs \\ El.Infra.FileSystem) do
fs.cwd()
def cwd!(fs \\ El.Infra.FileSystem) do
fs.cwd!()
end

def mkdir_p!(path, fs \\ El.Infra.FileSystem) do
Expand Down
Loading