feat: add main features

Agents for developpement added + frontend add + backend added. Git viewer created + agent and template creator + layout and project creator
This commit is contained in:
2026-06-06 01:27:01 +02:00
parent 55b3bee2c8
commit 307ae71857
273 changed files with 48740 additions and 0 deletions

View File

@ -0,0 +1,147 @@
//! L6 integration tests for [`IdeaiContextStore`] against a real temp directory
//! and a real [`LocalFileSystem`], exercising the full `.ideai/` persistence path
//! (manifest JSON, context `.md` round-trip, tolerant reads, NotFound).
use std::path::PathBuf;
use std::sync::Arc;
use domain::agent::{Agent, AgentManifest, AgentOrigin, ManifestEntry};
use domain::ids::{AgentId, ProfileId};
use domain::markdown::MarkdownDoc;
use domain::ports::{AgentContextStore, FileSystem, RemotePath, StoreError};
use domain::project::{Project, ProjectPath};
use domain::remote::RemoteRef;
use infrastructure::{IdeaiContextStore, LocalFileSystem};
use uuid::Uuid;
/// A unique scratch directory under the OS temp dir, cleaned up on drop.
struct TempDir(PathBuf);
impl TempDir {
fn new() -> Self {
let p = std::env::temp_dir().join(format!("idea-l6-ctx-{}", Uuid::new_v4()));
std::fs::create_dir_all(&p).unwrap();
Self(p)
}
fn root(&self) -> String {
self.0.to_string_lossy().into_owned()
}
fn child(&self, rel: &str) -> RemotePath {
RemotePath::new(self.0.join(rel).to_string_lossy().into_owned())
}
}
impl Drop for TempDir {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.0);
}
}
fn store() -> IdeaiContextStore {
let fs: Arc<dyn FileSystem> = Arc::new(LocalFileSystem::new());
IdeaiContextStore::new(fs)
}
fn project(root: &str) -> Project {
Project::new(
domain::ids::ProjectId::new_random(),
"demo",
ProjectPath::new(root).unwrap(),
RemoteRef::local(),
1_700_000_000_000,
)
.unwrap()
}
fn aid(n: u128) -> AgentId {
AgentId::from_uuid(Uuid::from_u128(n))
}
fn pid(n: u128) -> ProfileId {
ProfileId::from_uuid(Uuid::from_u128(n))
}
fn agent(id: AgentId, name: &str, md: &str, profile: ProfileId) -> Agent {
Agent::new(id, name, md, profile, AgentOrigin::Scratch, false).unwrap()
}
#[tokio::test]
async fn missing_manifest_loads_empty() {
let tmp = TempDir::new();
let store = store();
let manifest = store.load_manifest(&project(&tmp.root())).await.unwrap();
assert!(manifest.entries.is_empty());
assert_eq!(manifest.version, 1);
}
#[tokio::test]
async fn manifest_save_then_load_roundtrips() {
let tmp = TempDir::new();
let store = store();
let p = project(&tmp.root());
let a = agent(aid(1), "Backend", "agents/backend.md", pid(9));
let manifest = AgentManifest::new(1, vec![ManifestEntry::from_agent(&a)]).unwrap();
store.save_manifest(&p, &manifest).await.unwrap();
let back = store.load_manifest(&p).await.unwrap();
assert_eq!(back, manifest);
}
#[tokio::test]
async fn context_write_then_read_roundtrips() {
let tmp = TempDir::new();
let store = store();
let p = project(&tmp.root());
// The manifest must know the agent before its context can be addressed.
let a = agent(aid(1), "Backend", "agents/backend.md", pid(9));
let manifest = AgentManifest::new(1, vec![ManifestEntry::from_agent(&a)]).unwrap();
store.save_manifest(&p, &manifest).await.unwrap();
let md = MarkdownDoc::new("# Backend\nYou are the backend agent.");
store.write_context(&p, &a.id, &md).await.unwrap();
let back = store.read_context(&p, &a.id).await.unwrap();
assert_eq!(back, md);
// The `.md` actually landed at `.ideai/agents/backend.md`.
let fs = LocalFileSystem::new();
let bytes = fs
.read(&tmp.child(".ideai/agents/backend.md"))
.await
.unwrap();
assert_eq!(String::from_utf8(bytes).unwrap(), md.as_str());
}
#[tokio::test]
async fn read_context_for_unknown_agent_is_not_found() {
let tmp = TempDir::new();
let store = store();
let p = project(&tmp.root());
let err = store.read_context(&p, &aid(404)).await.unwrap_err();
assert!(matches!(err, StoreError::NotFound), "got {err:?}");
}
#[tokio::test]
async fn manifest_file_is_camelcase_json_under_ideai() {
let tmp = TempDir::new();
let store = store();
let p = project(&tmp.root());
let a = agent(aid(1), "Backend", "agents/backend.md", pid(9));
let manifest = AgentManifest::new(1, vec![ManifestEntry::from_agent(&a)]).unwrap();
store.save_manifest(&p, &manifest).await.unwrap();
let fs = LocalFileSystem::new();
let bytes = fs.read(&tmp.child(".ideai/agents.json")).await.unwrap();
let json: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
let agents = json
.get("agents")
.and_then(|v| v.as_array())
.expect("top-level `agents` array");
assert_eq!(agents.len(), 1);
let entry = &agents[0];
assert_eq!(entry.get("mdPath").and_then(|v| v.as_str()), Some("agents/backend.md"));
assert_eq!(entry.get("name").and_then(|v| v.as_str()), Some("Backend"));
assert!(entry.get("profileId").is_some(), "camelCase profileId present");
assert!(entry.get("md_path").is_none(), "no snake_case leak");
}