mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
add documentation for ai agents (#3115)
* add documentation for ai agents * docs: consolidate CLAUDE.md and CONTRIBUTING.md, add style guidelines - Refactor CLAUDE.md to reference CONTRIBUTING.md for build/test/format info - Expand CONTRIBUTING.md with comprehensive build targets, env vars, and testing - Add code style guidelines section with conventional commits - Standardize SDK prettier config to use single quotes (matching web) - Add project-level Claude Code settings to disable co-author attribution * style(sdk): apply prettier with single quotes Run prettier across sdk/base and sdk/package to apply the standardized quote style (single quotes matching web). * docs: add USER.md for per-developer TODO filtering - Add agents/USER.md to .gitignore (contains user identifier) - Document session startup flow in CLAUDE.md: - Create USER.md if missing, prompting for identifier - Filter TODOs by @username tags - Offer relevant TODOs on session start * docs: add i18n documentation task to agent TODOs * docs: document i18n ID patterns in core/ Add agents/i18n-patterns.md covering rust-i18n setup, translation file format, t!() macro usage, key naming conventions, and locale selection. Remove completed TODO item and add reference in CLAUDE.md. * chore: clarify that all builds work on any OS with Docker
This commit is contained in:
249
agents/core-rust-patterns.md
Normal file
249
agents/core-rust-patterns.md
Normal file
@@ -0,0 +1,249 @@
|
||||
# Utilities & Patterns
|
||||
|
||||
This document covers common utilities and patterns used throughout the StartOS codebase.
|
||||
|
||||
## Util Module (`core/src/util/`)
|
||||
|
||||
The `util` module contains reusable utilities. Key submodules:
|
||||
|
||||
| Module | Purpose |
|
||||
|--------|---------|
|
||||
| `actor/` | Actor pattern implementation for concurrent state management |
|
||||
| `collections/` | Custom collection types |
|
||||
| `crypto.rs` | Cryptographic utilities (encryption, hashing) |
|
||||
| `future.rs` | Future/async utilities |
|
||||
| `io.rs` | File I/O helpers (create_file, canonicalize, etc.) |
|
||||
| `iter.rs` | Iterator extensions |
|
||||
| `net.rs` | Network utilities |
|
||||
| `rpc.rs` | RPC helpers |
|
||||
| `rpc_client.rs` | RPC client utilities |
|
||||
| `serde.rs` | Serialization helpers (Base64, display/fromstr, etc.) |
|
||||
| `sync.rs` | Synchronization primitives (SyncMutex, etc.) |
|
||||
|
||||
## Command Invocation (`Invoke` trait)
|
||||
|
||||
The `Invoke` trait provides a clean way to run external commands with error handling:
|
||||
|
||||
```rust
|
||||
use crate::util::Invoke;
|
||||
|
||||
// Simple invocation
|
||||
tokio::process::Command::new("ls")
|
||||
.arg("-la")
|
||||
.invoke(ErrorKind::Filesystem)
|
||||
.await?;
|
||||
|
||||
// With timeout
|
||||
tokio::process::Command::new("slow-command")
|
||||
.timeout(Some(Duration::from_secs(30)))
|
||||
.invoke(ErrorKind::Timeout)
|
||||
.await?;
|
||||
|
||||
// With input
|
||||
let mut input = Cursor::new(b"input data");
|
||||
tokio::process::Command::new("cat")
|
||||
.input(Some(&mut input))
|
||||
.invoke(ErrorKind::Filesystem)
|
||||
.await?;
|
||||
|
||||
// Piped commands
|
||||
tokio::process::Command::new("cat")
|
||||
.arg("file.txt")
|
||||
.pipe(&mut tokio::process::Command::new("grep").arg("pattern"))
|
||||
.invoke(ErrorKind::Filesystem)
|
||||
.await?;
|
||||
```
|
||||
|
||||
## Guard Pattern
|
||||
|
||||
Guards ensure cleanup happens when they go out of scope.
|
||||
|
||||
### `GeneralGuard` / `GeneralBoxedGuard`
|
||||
|
||||
For arbitrary cleanup actions:
|
||||
|
||||
```rust
|
||||
use crate::util::GeneralGuard;
|
||||
|
||||
let guard = GeneralGuard::new(|| {
|
||||
println!("Cleanup runs on drop");
|
||||
});
|
||||
|
||||
// Do work...
|
||||
|
||||
// Explicit drop with action
|
||||
guard.drop();
|
||||
|
||||
// Or skip the action
|
||||
// guard.drop_without_action();
|
||||
```
|
||||
|
||||
### `FileLock`
|
||||
|
||||
File-based locking with automatic unlock:
|
||||
|
||||
```rust
|
||||
use crate::util::FileLock;
|
||||
|
||||
let lock = FileLock::new("/path/to/lockfile", true).await?; // blocking=true
|
||||
// Lock held until dropped or explicitly unlocked
|
||||
lock.unlock().await?;
|
||||
```
|
||||
|
||||
## Mount Guard Pattern (`core/src/disk/mount/guard.rs`)
|
||||
|
||||
RAII guards for filesystem mounts. Ensures filesystems are unmounted when guards are dropped.
|
||||
|
||||
### `MountGuard`
|
||||
|
||||
Basic mount guard:
|
||||
|
||||
```rust
|
||||
use crate::disk::mount::guard::MountGuard;
|
||||
use crate::disk::mount::filesystem::{MountType, ReadOnly};
|
||||
|
||||
let guard = MountGuard::mount(&filesystem, "/mnt/target", ReadOnly).await?;
|
||||
|
||||
// Use the mounted filesystem at guard.path()
|
||||
do_something(guard.path()).await?;
|
||||
|
||||
// Explicit unmount (or auto-unmounts on drop)
|
||||
guard.unmount(false).await?; // false = don't delete mountpoint
|
||||
```
|
||||
|
||||
### `TmpMountGuard`
|
||||
|
||||
Reference-counted temporary mount (mounts to `/media/startos/tmp/`):
|
||||
|
||||
```rust
|
||||
use crate::disk::mount::guard::TmpMountGuard;
|
||||
use crate::disk::mount::filesystem::ReadOnly;
|
||||
|
||||
// Multiple clones share the same mount
|
||||
let guard1 = TmpMountGuard::mount(&filesystem, ReadOnly).await?;
|
||||
let guard2 = guard1.clone();
|
||||
|
||||
// Mount stays alive while any guard exists
|
||||
// Auto-unmounts when last guard is dropped
|
||||
```
|
||||
|
||||
### `GenericMountGuard` trait
|
||||
|
||||
All mount guards implement this trait:
|
||||
|
||||
```rust
|
||||
pub trait GenericMountGuard: std::fmt::Debug + Send + Sync + 'static {
|
||||
fn path(&self) -> &Path;
|
||||
fn unmount(self) -> impl Future<Output = Result<(), Error>> + Send;
|
||||
}
|
||||
```
|
||||
|
||||
### `SubPath`
|
||||
|
||||
Wraps a mount guard to point to a subdirectory:
|
||||
|
||||
```rust
|
||||
use crate::disk::mount::guard::SubPath;
|
||||
|
||||
let mount = TmpMountGuard::mount(&filesystem, ReadOnly).await?;
|
||||
let subdir = SubPath::new(mount, "data/subdir");
|
||||
|
||||
// subdir.path() returns the full path including subdirectory
|
||||
```
|
||||
|
||||
## FileSystem Implementations (`core/src/disk/mount/filesystem/`)
|
||||
|
||||
Various filesystem types that can be mounted:
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `bind.rs` | Bind mounts |
|
||||
| `block_dev.rs` | Block device mounts |
|
||||
| `cifs.rs` | CIFS/SMB network shares |
|
||||
| `ecryptfs.rs` | Encrypted filesystem |
|
||||
| `efivarfs.rs` | EFI variables |
|
||||
| `httpdirfs.rs` | HTTP directory as filesystem |
|
||||
| `idmapped.rs` | ID-mapped mounts |
|
||||
| `label.rs` | Mount by label |
|
||||
| `loop_dev.rs` | Loop device mounts |
|
||||
| `overlayfs.rs` | Overlay filesystem |
|
||||
|
||||
## Other Useful Utilities
|
||||
|
||||
### `Apply` / `ApplyRef` traits
|
||||
|
||||
Fluent method chaining:
|
||||
|
||||
```rust
|
||||
use crate::util::Apply;
|
||||
|
||||
let result = some_value
|
||||
.apply(|v| transform(v))
|
||||
.apply(|v| another_transform(v));
|
||||
```
|
||||
|
||||
### `Container<T>`
|
||||
|
||||
Async-safe optional container:
|
||||
|
||||
```rust
|
||||
use crate::util::Container;
|
||||
|
||||
let container = Container::new(None);
|
||||
container.set(value).await;
|
||||
let taken = container.take().await;
|
||||
```
|
||||
|
||||
### `HashWriter<H, W>`
|
||||
|
||||
Write data while computing hash:
|
||||
|
||||
```rust
|
||||
use crate::util::HashWriter;
|
||||
use sha2::Sha256;
|
||||
|
||||
let writer = HashWriter::new(Sha256::new(), file);
|
||||
// Write data...
|
||||
let (hasher, file) = writer.finish();
|
||||
let hash = hasher.finalize();
|
||||
```
|
||||
|
||||
### `Never` type
|
||||
|
||||
Uninhabited type for impossible cases:
|
||||
|
||||
```rust
|
||||
use crate::util::Never;
|
||||
|
||||
fn impossible() -> Never {
|
||||
// This function can never return
|
||||
}
|
||||
|
||||
let never: Never = impossible();
|
||||
never.absurd::<String>() // Can convert to any type
|
||||
```
|
||||
|
||||
### `MaybeOwned<'a, T>`
|
||||
|
||||
Either borrowed or owned data:
|
||||
|
||||
```rust
|
||||
use crate::util::MaybeOwned;
|
||||
|
||||
fn accept_either(data: MaybeOwned<'_, String>) {
|
||||
// Use &*data to access the value
|
||||
}
|
||||
|
||||
accept_either(MaybeOwned::from(&existing_string));
|
||||
accept_either(MaybeOwned::from(owned_string));
|
||||
```
|
||||
|
||||
### `new_guid()`
|
||||
|
||||
Generate a random GUID:
|
||||
|
||||
```rust
|
||||
use crate::util::new_guid;
|
||||
|
||||
let guid = new_guid(); // Returns InternedString
|
||||
```
|
||||
Reference in New Issue
Block a user