| 8d803d85 Andy McCall ↶ |
1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
mod common; |
| 7 |
|
| 8 |
use common::{hermetic, run, setup, text, Serving}; |
| 9 |
use serde_json::{json, Value}; |
| 10 |
use std::io::{BufRead, BufReader, Write}; |
| 11 |
use std::path::Path; |
| 12 |
use std::process::Stdio; |
| 13 |
|
| 14 |
|
| 15 |
fn ask(d: &Path, cwd: &Path) -> (Value, Value) { |
| 16 |
let mut c = hermetic(d) |
| 17 |
.args(["mcp", "--auto"]) |
| 18 |
.current_dir(cwd) |
| 19 |
.env("SPECTRA_AGENTS", d.join("agents")) |
| 20 |
.stdin(Stdio::piped()) |
| 21 |
.stdout(Stdio::piped()) |
| 22 |
.stderr(Stdio::piped()) |
| 23 |
.spawn() |
| 24 |
.unwrap(); |
| 25 |
let mut stdin = c.stdin.take().unwrap(); |
| 26 |
for v in [ |
| 27 |
json!({ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": { "name": "t", "version": "1" } } }), |
| 28 |
json!({ "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "brief", "arguments": {} } }), |
| 29 |
] { |
| 30 |
writeln!(stdin, "{v}").unwrap(); |
| 31 |
} |
| 32 |
stdin.flush().unwrap(); |
| 33 |
let mut lines = BufReader::new(c.stdout.take().unwrap()).lines(); |
| 34 |
let mut got = std::collections::HashMap::new(); |
| 35 |
while got.len() < 2 { |
| 36 |
let v: Value = serde_json::from_str(&lines.next().unwrap().unwrap()).unwrap(); |
| 37 |
got.insert(v["id"].as_u64().unwrap(), v); |
| 38 |
} |
| 39 |
drop(stdin); |
| 40 |
let _ = c.kill(); |
| 41 |
let _ = c.wait(); |
| 42 |
(got.remove(&1).unwrap(), got.remove(&2).unwrap()) |
| 43 |
} |
| 44 |
|
| 45 |
#[test] |
| 46 |
fn one_entry_serves_every_folder() { |
| 47 |
let dir = setup(); |
| 48 |
let d = dir.path(); |
| 49 |
for p in ["book", "web"] { |
| 50 |
std::fs::create_dir_all(d.join(p).join("chapters")).unwrap(); |
| 51 |
assert!(run(d, &["project", "add", d.join(p).to_str().unwrap()]).status.success()); |
| 52 |
} |
| 53 |
for (n, p) in [("claude-book", "book"), ("claude-web", "web")] { |
| 54 |
let home = d.join("agents").join(n); |
| 55 |
let o = run(d, &["agent", "add", n, "--kind", "claude-code", "--project", p, "--home", home.to_str().unwrap(), "--yes"]); |
| 56 |
assert!(o.status.success(), "{}", text(&o)); |
| 57 |
assert!(home.join("agent.toml").exists(), "agent add labels the home"); |
| 58 |
} |
| 59 |
|
| 60 |
std::fs::remove_file(d.join("agents/claude-web/agent.toml")).unwrap(); |
| 61 |
let srv = Serving::start(d); |
| 62 |
|
| 63 |
let (_, brief) = ask(d, &d.join("book/chapters")); |
| 64 |
assert!(brief["result"]["content"][0]["text"].as_str().unwrap().contains("You are claude-book"), "{brief}"); |
| 65 |
let (_, brief) = ask(d, &d.join("web")); |
| 66 |
assert!(brief["result"]["content"][0]["text"].as_str().unwrap().contains("You are claude-web"), "{brief}"); |
| 67 |
assert!(d.join("agents/claude-web/agent.toml").exists(), "labelled on first use"); |
| 68 |
|
| 69 |
|
| 70 |
std::fs::create_dir_all(d.join("elsewhere")).unwrap(); |
| 71 |
let (init, call) = ask(d, &d.join("elsewhere")); |
| 72 |
assert!(init["result"]["instructions"].as_str().unwrap().contains("has no Spectra agent"), "{init}"); |
| 73 |
assert!(call["error"]["message"].as_str().unwrap().contains("Spectra: Add Agent"), "{call}"); |
| 74 |
srv.stop(); |
| 75 |
} |