yatfa 1.0.65 → 1.0.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,7 +7,7 @@ Run YATFA agents in any Docker container with Ruby.
7
7
  The easiest way to get started is with the interactive setup wizard:
8
8
 
9
9
  ```bash
10
- npx yatfa-agent setup
10
+ npx yatfa setup
11
11
  ```
12
12
 
13
13
  The wizard will guide you through:
@@ -30,7 +30,7 @@ Agents are configured via a `yatfa.env.rb` file in your project root. This Ruby
30
30
 
31
31
  **Do not commit `yatfa.env.rb` to git!** Add it to your `.gitignore`.
32
32
 
33
- **Pro tip:** Use `npx yatfa-agent setup` to generate this file interactively with validation.
33
+ **Pro tip:** Use `npx yatfa setup` to generate this file interactively with validation.
34
34
 
35
35
  Example `yatfa.env.rb`:
36
36
 
@@ -97,39 +97,52 @@ Example `yatfa.env.rb`:
97
97
  ### Setup
98
98
 
99
99
  ```bash
100
- npx yatfa-agent setup
100
+ npx yatfa setup
101
101
  ```
102
102
 
103
103
  Launches interactive setup wizard to configure `yatfa.env.rb`.
104
104
 
105
105
  ### Run Agents
106
106
 
107
+ When you run an agent, the CLI automatically:
108
+ 1. **Checks if the agent is already running** - prompts to Attach, Restart, or Cancel
109
+ 2. **Starts the agent** - creates the Docker container
110
+ 3. **Prompts to attach** - after starting, asks if you want to attach to the session
111
+
107
112
  ```bash
108
- # Start an agent
109
- npx yatfa-agent worker
110
- npx yatfa-agent planner
111
- npx yatfa-agent reviewer
112
- npx yatfa-agent researcher
113
+ npx yatfa worker
114
+ npx yatfa planner
115
+ npx yatfa reviewer
116
+ npx yatfa researcher
113
117
  ```
114
118
 
115
- ### Attach to Running Agents
119
+ #### Non-interactive Mode (for CI/scripts)
116
120
 
117
121
  ```bash
118
- npx yatfa-agent attach worker
122
+ # Run without prompts
123
+ npx yatfa worker --no-prompt
124
+ # or
125
+ YATFA_NO_PROMPT=1 npx yatfa worker
119
126
  ```
120
127
 
121
- Opens tmux session to view agent activity.
128
+ ### Attach to Running Agents (deprecated)
129
+
130
+ The `attach` command still works but is deprecated. Running `npx yatfa <agent-type>` now prompts to attach automatically.
131
+
132
+ ```bash
133
+ npx yatfa attach worker # Deprecated - use 'npx yatfa worker' instead
134
+ ```
122
135
 
123
136
  ### List Running Agents
124
137
 
125
138
  ```bash
126
- npx yatfa-agent list
139
+ npx yatfa list
127
140
  ```
128
141
 
129
142
  ### Stop All Agents
130
143
 
131
144
  ```bash
132
- npx yatfa-agent stop-all
145
+ npx yatfa stop-all
133
146
  ```
134
147
 
135
148
  ## What the Script Does
@@ -241,8 +254,10 @@ See hook files for implementation details and customization options.
241
254
 
242
255
  ## Attaching to Running Agent
243
256
 
257
+ After starting an agent, the CLI will prompt you to attach. You can also attach directly:
258
+
244
259
  ```bash
245
- docker exec -it <container> tmux attach -t agent-wrapper
260
+ docker exec -it <container> tmux attach -t agent
246
261
  ```
247
262
 
248
263
  Press `Ctrl+B` then `D` to detach.
@@ -2,9 +2,8 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # YATFA Agent Runner (Yet Another Tool For Agents)
5
- # Usage: npx yatfa-agent [worker|planner|reviewer|researcher]
6
- # npx yatfa-agent attach [agent-type]
7
- # npx yatfa-agent setup
5
+ # Usage: npx yatfa [worker|planner|reviewer|researcher]
6
+ # npx yatfa setup
8
7
  #
9
8
  # Requirements:
10
9
  # - Docker
@@ -22,18 +21,21 @@ def show_usage
22
21
  puts "YATFA Agent Runner (Yet Another Tool For Agents)"
23
22
  puts ""
24
23
  puts "Usage:"
25
- puts " npx yatfa-agent setup # Interactive setup wizard"
26
- puts " npx yatfa-agent [worker|planner|reviewer|researcher] # Run agent"
27
- puts " npx yatfa-agent attach [agent-type] # Attach to running agent"
24
+ puts " npx yatfa setup # Interactive setup wizard"
25
+ puts " npx yatfa [worker|planner|reviewer|researcher] # Run agent (prompts to attach)"
26
+ puts ""
27
+ puts "Options:"
28
+ puts " --no-prompt, -d Run without prompts (for CI/scripts)"
29
+ puts " YATFA_NO_PROMPT=1 Environment variable alternative"
28
30
  puts ""
29
31
  puts "Setup:"
30
- puts " Run 'npx yatfa-agent setup' for interactive configuration"
32
+ puts " Run 'npx yatfa setup' for interactive configuration"
31
33
  puts ""
32
34
  puts "Manual setup:"
33
35
  puts " 1. Create Dockerfile.sandbox (see https://github.com/RoM4iK/yatfa-public/blob/main/README.md)"
34
36
  puts " 2. Create yatfa.env.rb (see https://github.com/RoM4iK/yatfa-public/blob/main/README.md)"
35
37
  puts " 3. echo 'yatfa.env.rb' >> .gitignore"
36
- puts " 4. npx yatfa-agent worker"
38
+ puts " 4. npx yatfa worker"
37
39
  exit 1
38
40
  end
39
41
 
@@ -49,10 +51,14 @@ if command == "setup"
49
51
  exit 0
50
52
  end
51
53
 
52
- # Handle attach command
54
+ # Handle attach command (deprecated - will still work)
53
55
  if command == "attach"
56
+ puts ""
57
+ puts "⚠️ Deprecation: The 'attach' command is deprecated"
58
+ puts " Running `npx yatfa <agent-type>` now prompts to attach automatically."
59
+ puts ""
54
60
  agent_type = ARGV[1]&.downcase
55
- abort "Usage: npx yatfa-agent attach [agent-type]" unless agent_type
61
+ abort "Usage: npx yatfa attach [agent-type]" unless agent_type
56
62
  config = YatfaAgent::Config.load
57
63
  YatfaAgent::Config.fetch_repositories!(config)
58
64
  YatfaAgent::Agent.attach(agent_type, config, AGENT_CONFIGS)
@@ -3,6 +3,72 @@ require 'fileutils'
3
3
 
4
4
  module YatfaAgent
5
5
  module Agent
6
+ # Check if container is currently running
7
+ def self.container_running?(container_name)
8
+ # Use IO.popen with array arguments to avoid shell injection
9
+ result = IO.popen(["docker", "ps", "--filter", "name=^#{container_name}$", "--format", "{{.Names}}"], err: File::NULL, &:read).strip
10
+ !result.empty?
11
+ end
12
+
13
+ # Prompt user for input with a default value
14
+ def self.prompt(message, default: nil, options: nil)
15
+ loop do
16
+ print message
17
+ input = $stdin.gets&.strip&.downcase || ""
18
+
19
+ # Handle default on empty input
20
+ if input.empty? && default
21
+ return default
22
+ end
23
+
24
+ # If options specified, validate input
25
+ if options
26
+ valid = options.any? { |opt| opt.downcase == input || opt.downcase.start_with?(input) }
27
+ if valid
28
+ # Return the full option that matches
29
+ return options.find { |opt| opt.downcase.start_with?(input) }
30
+ end
31
+ puts " Invalid option. Please choose: #{options.join('/')}"
32
+ else
33
+ return input
34
+ end
35
+ end
36
+ end
37
+
38
+ # Check if running in non-interactive mode
39
+ def self.non_interactive?
40
+ !$stdin.tty? || ENV['YATFA_NO_PROMPT'] == '1' || ARGV.include?('--no-prompt') || ARGV.include?('-d')
41
+ end
42
+
43
+ # Handle running container: prompt user for action
44
+ def self.handle_running_container(agent_type, container_name, config, agent_configs)
45
+ puts "⚠️ #{agent_type} agent is already running (#{container_name})"
46
+ puts ""
47
+
48
+ if non_interactive?
49
+ puts " Running in non-interactive mode. Run 'npx yatfa #{agent_type}' to attach, or stop the container first."
50
+ puts " docker stop #{container_name}"
51
+ exit 0
52
+ end
53
+
54
+ choice = prompt(" [A]ttach / [R]estart / [C]ancel? [A/r/c]: ", default: "A", options: %w[A R C])
55
+
56
+ case choice
57
+ when "A"
58
+ puts ""
59
+ attach(agent_type, config, agent_configs)
60
+ when "R"
61
+ puts ""
62
+ puts "🔄 Stopping existing container..."
63
+ system("docker", "stop", container_name, err: File::NULL, out: File::NULL)
64
+ system("docker", "rm", "-f", container_name, err: File::NULL, out: File::NULL)
65
+ return true # Continue with start
66
+ when "C"
67
+ puts " Cancelled."
68
+ exit 0
69
+ end
70
+ end
71
+
6
72
  def self.run(agent_type, config, agent_configs)
7
73
  unless agent_configs.keys.include?(agent_type)
8
74
  puts "❌ Unknown agent type: #{agent_type}"
@@ -14,13 +80,18 @@ module YatfaAgent
14
80
  agent_config = config.dig("agents", agent_type) || {}
15
81
  container_name = agent_config["container_name"] || agent_def[:name]
16
82
 
17
- puts "🚀 Starting #{agent_type} agent..."
83
+ # Check if container is already running and handle interactively
84
+ if container_running?(container_name)
85
+ should_continue = handle_running_container(agent_type, container_name, config, agent_configs)
86
+ # If user chose Attach or Cancel, the method calls exec/exit, so we never reach here
87
+ # If user chose Restart, should_continue is true and we proceed to start a new container
88
+ return unless should_continue
89
+ end
18
90
 
19
- # Stop existing container if running
20
- system("docker", "rm", "-f", container_name, err: File::NULL, out: File::NULL)
91
+ puts "🚀 Starting #{agent_type} agent..."
21
92
 
22
93
  # Write banner to a persistent temp file (not auto-deleted)
23
- banner_path = "/tmp/yatfa-agent-banner-#{agent_type}.txt"
94
+ banner_path = "/tmp/yatfa-banner-#{agent_type}.txt"
24
95
  FileUtils.rm_rf(banner_path)
25
96
 
26
97
  # Append repository context to banner
@@ -47,15 +118,46 @@ module YatfaAgent
47
118
  if success
48
119
  puts "✅ Agent started in background"
49
120
  puts ""
50
- puts " Attach: npx yatfa-agent attach #{agent_type}"
51
- puts " Logs: docker logs -f #{container_name}"
52
- puts " Stop: docker stop #{container_name}"
121
+
122
+ # Prompt to attach (interactive mode only)
123
+ prompt_to_attach(agent_type, container_name, config, agent_configs)
53
124
  else
54
125
  puts "❌ Failed to start agent"
55
126
  exit 1
56
127
  end
57
128
  end
58
129
 
130
+ # Prompt user to attach after starting a new agent
131
+ def self.prompt_to_attach(agent_type, container_name, config, agent_configs)
132
+ if non_interactive?
133
+ # Non-interactive: just print helpful info
134
+ puts " Logs: docker logs -f #{container_name}"
135
+ puts " Stop: docker stop #{container_name}"
136
+ puts " Attach: docker exec -it #{container_name} tmux attach -t agent"
137
+ return
138
+ end
139
+
140
+ choice = prompt(" Agent started. Attach to session? [Y/n]: ", default: "Y", options: %w[Y N])
141
+
142
+ if choice == "Y"
143
+ puts ""
144
+ # Wait briefly for container to initialize
145
+ print " Waiting for agent to initialize"
146
+ 5.times do
147
+ print "."
148
+ sleep 1
149
+ end
150
+ puts ""
151
+ attach(agent_type, config, agent_configs)
152
+ else
153
+ puts ""
154
+ puts " Agent is running in background."
155
+ puts " Logs: docker logs -f #{container_name}"
156
+ puts " Stop: docker stop #{container_name}"
157
+ puts " Attach: docker exec -it #{container_name} tmux attach -t agent"
158
+ end
159
+ end
160
+
59
161
  def self.attach(agent_type, config, agent_configs)
60
162
  unless agent_configs.keys.include?(agent_type)
61
163
  puts "❌ Unknown agent type: #{agent_type}"
@@ -66,7 +168,7 @@ module YatfaAgent
66
168
  agent_config = config.dig("agents", agent_type) || {}
67
169
  container_name = agent_config["container_name"] || agent_def[:name]
68
170
 
69
- running = `docker ps --filter name=^#{container_name}$ --format '{{.Names}}'`.strip
171
+ running = IO.popen(["docker", "ps", "--filter", "name=^#{container_name}$", "--format", "{{.Names}}"], err: File::NULL, &:read).strip
70
172
 
71
173
  if running.empty?
72
174
  puts "⚠️ #{agent_type} agent is not running. Auto-starting..."
@@ -208,52 +310,27 @@ module YatfaAgent
208
310
 
209
311
  def self.add_development_mounts!(docker_cmd)
210
312
  # Check for local setup-agent.rb (for development)
211
- local_setup_script = File.join(File.dirname(__FILE__), "..", "..", "setup-agent.rb")
212
-
213
- # Check for local agent-bridge binaries (for development)
214
- arch = `uname -m`.strip
215
- linux_arch = (arch == "x86_64") ? "amd64" : "arm64"
216
-
217
- # Try multiple possible paths for the binary (built Go binaries in dist/)
218
- possible_paths = [
219
- File.join(Dir.pwd, "overrides", "agent-bridge-linux-#{linux_arch}"), # YATFA_SANDBOX/overrides
220
- File.join(Dir.pwd, "yatfa-public", "dist", "agent-bridge-linux-#{linux_arch}"), # Running from metafolder
221
- File.join(Dir.pwd, "dist", "agent-bridge-linux-#{linux_arch}"), # Running from yatfa-public
313
+ # Try multiple paths: relative to gem, relative to cwd
314
+ setup_script_paths = [
315
+ File.join(File.dirname(__FILE__), "..", "..", "setup-agent.rb"), # Relative to gem
316
+ File.join(Dir.pwd, "tinker-public", "setup-agent.rb"), # Running from metafolder
317
+ File.join(Dir.pwd, "setup-agent.rb"), # Running from yatfa-public
222
318
  ]
223
319
 
224
- linux_bridge = nil
225
- possible_paths.each do |path|
320
+ local_setup_script = nil
321
+ setup_script_paths.each do |path|
226
322
  if File.exist?(path)
227
- linux_bridge = path
323
+ local_setup_script = path
228
324
  break
229
325
  end
230
326
  end
231
327
 
232
- local_bridge_default = File.join(Dir.pwd, "bin", "agent-bridge")
233
- local_tmux = File.join(File.dirname(__FILE__), "..", "..", "bin", "agent-bridge-tmux")
234
-
235
- if File.exist?(local_setup_script)
328
+ # Mount local setup-agent.rb for development if present
329
+ # Binaries are always fetched from bucket by setup-agent.rb
330
+ if local_setup_script
236
331
  puts "🔧 Using local setup-agent.rb for development"
237
332
  docker_cmd.concat(["-v", "#{File.expand_path(local_setup_script)}:/tmp/setup-agent.rb:ro"])
238
333
  end
239
-
240
- if linux_bridge && File.exist?(linux_bridge)
241
- puts "🔧 Using local linux binary: #{linux_bridge}"
242
- docker_cmd.concat(["-v", "#{File.expand_path(linux_bridge)}:/tmp/agent-bridge:ro"])
243
- elsif File.exist?(local_bridge_default)
244
- # Check if it's a binary or script
245
- is_script = File.read(local_bridge_default, 4) == "#!/b"
246
- if is_script
247
- puts "⚠️ bin/agent-bridge is a host wrapper script. Please run 'bin/build-bridge' to generate linux binaries."
248
- else
249
- puts "🔧 Using local agent-bridge binary"
250
- docker_cmd.concat(["-v", "#{local_bridge_default}:/tmp/agent-bridge:ro"])
251
- end
252
- end
253
-
254
- if File.exist?(local_tmux)
255
- docker_cmd.concat(["-v", "#{File.expand_path(local_tmux)}:/tmp/agent-bridge-tmux:ro"])
256
- end
257
334
  end
258
335
 
259
336
  def self.detect_agent_user(container_name)
@@ -5,16 +5,14 @@ require "json"
5
5
  module YatfaAgent
6
6
  module Config
7
7
  def self.load
8
- rb_config_file = File.join(Dir.pwd, "yatfa.env.rb")
8
+ config_file = File.join(Dir.pwd, "yatfa.env.rb")
9
9
 
10
- unless File.exist?(rb_config_file)
10
+ unless File.exist?(config_file)
11
11
  puts "❌ Error: yatfa.env.rb not found in current directory"
12
12
  puts ""
13
13
  puts "Create yatfa.env.rb:"
14
14
  puts " {"
15
15
  puts " yatfa_url: 'https://yatfa.example.com', # Required"
16
- puts " # OR legacy name:"
17
- puts " rails_api_url: 'https://yatfa.example.com/api/v1',"
18
16
  puts " # ..."
19
17
  puts " # Paste your stripped .env content here:"
20
18
  puts " dot_env: <<~ENV"
@@ -23,11 +21,11 @@ module YatfaAgent
23
21
  puts " ENV"
24
22
  puts " }"
25
23
  puts " echo 'yatfa.env.rb' >> .gitignore"
26
- puts " exit 1"
24
+ exit 1
27
25
  end
28
26
 
29
27
  puts "⚙️ Loading configuration from yatfa.env.rb"
30
- config = eval(File.read(rb_config_file), binding, rb_config_file)
28
+ config = eval(File.read(config_file), binding, config_file)
31
29
 
32
30
  # Convert symbols to strings for easier handling before JSON normalization
33
31
  config = config.transform_keys(&:to_s)
@@ -69,8 +67,7 @@ module YatfaAgent
69
67
  end
70
68
 
71
69
  def self.yatfa_url(config)
72
- # Support both new name and legacy name for backwards compatibility
73
- config["yatfa_url"] || config["yafta_url"] || config["rails_api_url"]
70
+ config["yatfa_url"] || config["rails_api_url"]
74
71
  end
75
72
 
76
73
  def self.api_url(config)
@@ -58,7 +58,7 @@ module YatfaAgent
58
58
  puts "║ YATFA Agent Setup Wizard ║"
59
59
  puts "║ (Yet Another Tool For Agents) ║"
60
60
  puts "║ ║"
61
- puts "║ This wizard will guide you through configuring yatfa-agent ║"
61
+ puts "║ This wizard will guide you through configuring yatfa ║"
62
62
  puts "║ ║"
63
63
  puts "╚═══════════════════════════════════════════════════════════════╝"
64
64
  puts ""
@@ -157,7 +157,7 @@ module YatfaAgent
157
157
 
158
158
  # Set defaults from parent directory if available
159
159
  default_name = "YATFA Agent"
160
- default_email = "yatfa-agent@yatfa.ai"
160
+ default_email = "yatfa@yatfa.ai"
161
161
 
162
162
  parent_config = File.join(Dir.pwd, "..", "yatfa.env.rb")
163
163
  if File.exist?(parent_config)
@@ -512,12 +512,12 @@ module YatfaAgent
512
512
  puts ""
513
513
  puts " 3. Start an agent:"
514
514
  if @config[:agents].key?(:worker)
515
- puts " npx yatfa-agent worker"
515
+ puts " npx yatfa worker"
516
516
  elsif @config[:agents].key?(:planner)
517
- puts " npx yatfa-agent planner"
517
+ puts " npx yatfa planner"
518
518
  else
519
519
  agent = @config[:agents].keys.first
520
- puts " npx yatfa-agent #{agent}"
520
+ puts " npx yatfa #{agent}"
521
521
  end
522
522
  puts ""
523
523
  puts "For more information, see: https://github.com/RoM4iK/yatfa-public"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yatfa",
3
- "version": "1.0.65",
3
+ "version": "1.0.67",
4
4
  "description": "YATFA - Yet Another Tool For Agents",
5
5
  "bin": "./bin/run-yatfa-agent.rb",
6
6
  "files": [