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,99 @@
//! [`ProjectMeta`] — the on-disk shape of `.ideai/project.json` (ARCHITECTURE §9.1).
//!
//! This is the *project-local* metadata that travels with the code (it lives
//! inside the project root, is versionable, and is independent of the machine).
//! The known-projects **registry** is a separate, machine-local concern owned by
//! the [`domain::ports::ProjectStore`] adapter (ARCHITECTURE §9.2).
use serde::{Deserialize, Serialize};
use domain::{Project, ProjectId, ProjectPath, RemoteRef};
use crate::error::AppError;
/// The `.ideai/` directory name inside a project root.
pub(crate) const IDEAI_DIR: &str = ".ideai";
/// The project-meta file name inside `.ideai/`.
pub(crate) const PROJECT_FILE: &str = "project.json";
/// The agent manifest file name inside `.ideai/`.
pub(crate) const AGENTS_FILE: &str = "agents.json";
/// Current schema version of `project.json`.
pub(crate) const PROJECT_META_VERSION: u32 = 1;
/// Serialised contents of `.ideai/project.json`.
///
/// Carries the project's identity and the metadata needed to reopen it: its
/// stable id, display name, the default agent profile, the remote reference and
/// the creation timestamp. The `root` itself is *not* stored here — the file
/// already lives at `<root>/.ideai/project.json`, so the root is implied by the
/// file's location (and authoritatively held by the registry).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProjectMeta {
/// Schema version of this file.
pub version: u32,
/// Stable project id (matches the registry entry).
pub id: ProjectId,
/// Display name.
pub name: String,
/// Default agent profile id, if one has been chosen (`null` until first-run
/// / profile selection in later lots).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default_profile_id: Option<String>,
/// Where the project physically lives.
pub remote: RemoteRef,
/// Creation timestamp, epoch milliseconds.
pub created_at: i64,
}
impl ProjectMeta {
/// Builds the meta image from a [`Project`].
#[must_use]
pub fn from_project(project: &Project, default_profile_id: Option<String>) -> Self {
Self {
version: PROJECT_META_VERSION,
id: project.id,
name: project.name.clone(),
default_profile_id,
remote: project.remote.clone(),
created_at: project.created_at,
}
}
/// Reconstructs a validated [`Project`] from this meta and its (registry-known)
/// root.
///
/// # Errors
/// Returns [`AppError::Invalid`] if the stored fields violate a domain
/// invariant (e.g. empty name).
pub fn into_project(self, root: ProjectPath) -> Result<Project, AppError> {
Project::new(self.id, self.name, root, self.remote, self.created_at)
.map_err(|e| AppError::Invalid(e.to_string()))
}
}
/// Serialises a value to pretty JSON bytes, mapping failures to [`AppError`].
pub(crate) fn to_json_bytes<T: Serialize>(value: &T) -> Result<Vec<u8>, AppError> {
serde_json::to_vec_pretty(value)
.map(|mut v| {
v.push(b'\n');
v
})
.map_err(|e| AppError::Store(format!("serialize failed: {e}")))
}
/// Deserialises JSON bytes, mapping failures to [`AppError`].
pub(crate) fn from_json_bytes<T: for<'de> Deserialize<'de>>(bytes: &[u8]) -> Result<T, AppError> {
serde_json::from_slice(bytes).map_err(|e| AppError::Store(format!("deserialize failed: {e}")))
}
/// Joins a project root with a relative path segment using a POSIX-style
/// separator. Paths inside `.ideai/` are always written with `/`, which is valid
/// on every platform we target (Windows `tokio::fs` accepts `/`).
pub(crate) fn join_root(root: &ProjectPath, rel: &str) -> String {
let base = root.as_str().trim_end_matches(['/', '\\']);
format!("{base}/{rel}")
}