recursive-agent 0.6.0

A minimal, orthogonal, self-improving coding agent kernel in Rust
Documentation
name: "REPL Mode (multi-turn interactive)"
description: |
  Verify recursive repl accepts goals from piped stdin, executes each turn,
  and exits cleanly when stdin closes (EOF). Each scenario gets its OWN
  workspace to eliminate cross-case pollution (the previous version reused
  /workspace/test-repl across cases 1, 5, 7 — files from case 1 would leak
  into the :clear and empty-line scenarios).

  Scenarios:
    A. Single-turn REPL (workspace test-repl-single)
    B. :q quit command (workspace test-repl-quit, no goal)
    C. Multi-turn context (workspace test-repl-multi)
    D. :clear command output (workspace test-repl-clear)
    E. Empty-line tolerance (workspace test-repl-empty)
sequential: true

setup:
  - name: "Prepare per-scenario workspaces (clean slate each)"
    exec:
      container: recursive-e2e
      command: |
        rm -rf /workspace/test-repl-{single,quit,multi,clear,empty}
        mkdir -p /workspace/test-repl-{single,quit,multi,clear,empty}
        echo prepared

  - name: "Scenario A (act): single goal via piped stdin"
    exec:
      container: recursive-e2e
      command: >
        printf 'Write file repl-a.txt with content turn-a\n'
        | recursive --workspace /workspace/test-repl-single
        --api-base http://aimock:4010/v1
        --api-key mock-key -m mock-chat
        --max-steps 5
        repl

  - name: "Scenario C (act): multi-turn — write repl-a.txt then repl-b.txt"
    exec:
      container: recursive-e2e
      command: >
        printf 'Write file repl-a.txt with content turn-a\nWrite file repl-b.txt with content turn-b\n:q\n'
        | recursive --workspace /workspace/test-repl-multi
        --api-base http://aimock:4010/v1
        --api-key mock-key -m mock-chat
        --max-steps 5
        repl

cases:
  # Scenario A asserts
  - name: "A: repl-a.txt created with turn-a"
    file:
      container: recursive-e2e
      path: /workspace/test-repl-single/repl-a.txt
      exists: true
      contains: "turn-a"

  # Scenario B: :q quit (act inline — no fixture to assert separately)
  - name: "B: REPL exits 0 on :q command"
    exec:
      container: recursive-e2e
      command: >
        printf ':q\n'
        | recursive --workspace /workspace/test-repl-quit
        --api-base http://aimock:4010/v1
        --api-key mock-key -m mock-chat
        repl
    expect:
      exitCode: 0

  # Scenario C asserts
  - name: "C: repl-a.txt created in turn 1"
    file:
      container: recursive-e2e
      path: /workspace/test-repl-multi/repl-a.txt
      exists: true
      contains: "turn-a"

  - name: "C: repl-b.txt created in turn 2"
    file:
      container: recursive-e2e
      path: /workspace/test-repl-multi/repl-b.txt
      exists: true
      contains: "turn-b"

  # Scenario D: :clear (act inline — REPL writes meta-info like
  # "(conversation cleared)" to stderr by design, while stdout carries
  # the agent's actual replies. Redirect stderr→stdout so ArgusAI's
  # `output:` matcher (which only captures stdout) can see it.
  - name: "D: :clear command prints 'cleared' (stderr merged via 2>&1)"
    exec:
      container: recursive-e2e
      command: >
        printf ':clear\n:q\n'
        | recursive --workspace /workspace/test-repl-clear
        --api-base http://aimock:4010/v1
        --api-key mock-key -m mock-chat
        repl 2>&1
    expect:
      exitCode: 0
      output:
        contains: "cleared"

  # Scenario E: empty-line tolerance (act inline)
  - name: "E: empty lines do not cause REPL errors"
    exec:
      container: recursive-e2e
      command: >
        printf '\n\n:q\n'
        | recursive --workspace /workspace/test-repl-empty
        --api-base http://aimock:4010/v1
        --api-key mock-key -m mock-chat
        repl
    expect:
      exitCode: 0

teardown:
  - name: "Clean per-scenario workspaces"
    ignoreError: true
    exec:
      container: recursive-e2e
      command: rm -rf /workspace/test-repl-{single,quit,multi,clear,empty}