rename: adopt yoi identity
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! - [`PodClient`]: 既存 pod の Unix ソケットへ接続して `Method` を送り、
|
||||
//! `Event` を受け取る低レベル接続。
|
||||
//! - [`spawn`]: pod バイナリをサブプロセスとして起動し、`INSOMNIA-READY`
|
||||
//! - [`spawn`]: pod バイナリをサブプロセスとして起動し、`YOI-READY`
|
||||
//! ハンドシェイクが終わるまで待つフロー。subprocess を立ち上げる必要が
|
||||
//! ない呼び出し側 (=既存 pod に attach する場合) は使わなくてよい。
|
||||
//!
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::fmt;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
const POD_RUNTIME_COMMAND_ENV: &str = "INSOMNIA_POD_RUNTIME_COMMAND";
|
||||
const POD_RUNTIME_COMMAND_ENV: &str = "YOI_POD_RUNTIME_COMMAND";
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct PodRuntimeCommand {
|
||||
@@ -29,9 +29,9 @@ impl PodRuntimeCommand {
|
||||
|
||||
/// Resolve the Pod runtime command used for subprocess launches.
|
||||
///
|
||||
/// The default launch path is always the current `insomnia` executable plus
|
||||
/// The default launch path is always the current `yoi` executable plus
|
||||
/// the unified `pod` prefix argument. During development, a non-empty
|
||||
/// `INSOMNIA_POD_RUNTIME_COMMAND` value replaces only the executable path;
|
||||
/// `YOI_POD_RUNTIME_COMMAND` value replaces only the executable path;
|
||||
/// the `pod` prefix is still added here and the env value is not parsed as a
|
||||
/// shell command.
|
||||
pub fn resolve() -> io::Result<Self> {
|
||||
@@ -89,10 +89,10 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn insomnia_binary_defaults_to_pod_prefix() {
|
||||
let command = PodRuntimeCommand::for_executable("/opt/insomnia/bin/insomnia");
|
||||
fn yoi_binary_defaults_to_pod_prefix() {
|
||||
let command = PodRuntimeCommand::for_executable("/opt/yoi/bin/yoi");
|
||||
|
||||
assert_eq!(command.program(), Path::new("/opt/insomnia/bin/insomnia"));
|
||||
assert_eq!(command.program(), Path::new("/opt/yoi/bin/yoi"));
|
||||
assert_eq!(command.prefix_args(), [OsString::from("pod")]);
|
||||
assert_eq!(
|
||||
command.argv_with(["--pod", "agent"]),
|
||||
@@ -105,12 +105,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn any_runtime_executable_gets_pod_prefix() {
|
||||
let command = PodRuntimeCommand::for_executable("/opt/insomnia/bin/custom-runtime");
|
||||
let command = PodRuntimeCommand::for_executable("/opt/yoi/bin/custom-runtime");
|
||||
|
||||
assert_eq!(
|
||||
command.program(),
|
||||
Path::new("/opt/insomnia/bin/custom-runtime")
|
||||
);
|
||||
assert_eq!(command.program(), Path::new("/opt/yoi/bin/custom-runtime"));
|
||||
assert_eq!(command.prefix_args(), [OsString::from("pod")]);
|
||||
assert_eq!(
|
||||
command.argv_with(["--pod", "agent"]),
|
||||
@@ -124,38 +121,38 @@ mod tests {
|
||||
#[test]
|
||||
fn resolve_uses_current_exe_when_override_is_unset() {
|
||||
let command = PodRuntimeCommand::resolve_from_env_value(None, || {
|
||||
Ok(PathBuf::from("/opt/insomnia/bin/insomnia"))
|
||||
Ok(PathBuf::from("/opt/yoi/bin/yoi"))
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
command,
|
||||
PodRuntimeCommand::for_executable("/opt/insomnia/bin/insomnia")
|
||||
PodRuntimeCommand::for_executable("/opt/yoi/bin/yoi")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_uses_current_exe_when_override_is_empty() {
|
||||
let command = PodRuntimeCommand::resolve_from_env_value(Some(OsString::new()), || {
|
||||
Ok(PathBuf::from("/opt/insomnia/bin/insomnia"))
|
||||
Ok(PathBuf::from("/opt/yoi/bin/yoi"))
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
command,
|
||||
PodRuntimeCommand::for_executable("/opt/insomnia/bin/insomnia")
|
||||
PodRuntimeCommand::for_executable("/opt/yoi/bin/yoi")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_override_replaces_only_program_and_keeps_pod_prefix() {
|
||||
let command = PodRuntimeCommand::resolve_from_env_value(
|
||||
Some(OsString::from("/tmp/rebuilt insomnia")),
|
||||
Some(OsString::from("/tmp/rebuilt yoi")),
|
||||
|| panic!("override must not inspect current_exe"),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(command.program(), Path::new("/tmp/rebuilt insomnia"));
|
||||
assert_eq!(command.program(), Path::new("/tmp/rebuilt yoi"));
|
||||
assert_eq!(command.prefix_args(), [OsString::from("pod")]);
|
||||
assert_eq!(
|
||||
command.argv_with(["--pod", "agent"]),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! Pod runtime command をサブプロセスとして立ち上げ、`INSOMNIA-READY` を待つ
|
||||
//! Pod runtime command をサブプロセスとして立ち上げ、`YOI-READY` を待つ
|
||||
//! ハンドシェイク。
|
||||
//!
|
||||
//! - 親プロセス (TUI / GUI / E2E) は profile/default/typed restore flags を
|
||||
//! 指定してこの関数に渡す。pod はそれを受けて socket を bind し、stderr に
|
||||
//! `INSOMNIA-READY\t<name>\t<socket>` を吐く。
|
||||
//! `YOI-READY\t<name>\t<socket>` を吐く。
|
||||
//! - 待機中の stderr 行は `progress` コールバック越しに呼び出し側へ流す。
|
||||
//! UI の進捗表示や E2E のログ収集はここで賄う。
|
||||
//! - `kill_on_drop = false` + `process_group(0)` により、親プロセス
|
||||
@@ -19,7 +19,7 @@ use crate::PodRuntimeCommand;
|
||||
use tokio::process::Command;
|
||||
use uuid::Uuid;
|
||||
|
||||
const READY_PREFIX: &str = "INSOMNIA-READY\t";
|
||||
const READY_PREFIX: &str = "YOI-READY\t";
|
||||
const READY_TIMEOUT: Duration = Duration::from_secs(20);
|
||||
|
||||
/// `spawn_pod` の入力。
|
||||
@@ -69,7 +69,7 @@ impl std::fmt::Display for SpawnError {
|
||||
Self::Io(e) => write!(f, "io error: {e}"),
|
||||
Self::RuntimeDirUnavailable => write!(
|
||||
f,
|
||||
"could not resolve runtime directory (set INSOMNIA_HOME, INSOMNIA_RUNTIME_DIR, XDG_RUNTIME_DIR, or HOME)"
|
||||
"could not resolve runtime directory (set YOI_HOME, YOI_RUNTIME_DIR, XDG_RUNTIME_DIR, or HOME)"
|
||||
),
|
||||
Self::PodLaunchFailed { command, source } => write!(
|
||||
f,
|
||||
@@ -106,7 +106,7 @@ impl From<io::Error> for SpawnError {
|
||||
}
|
||||
}
|
||||
|
||||
/// pod を spawn し、`INSOMNIA-READY` ハンドシェイクが終わるまで待つ。
|
||||
/// pod を spawn し、`YOI-READY` ハンドシェイクが終わるまで待つ。
|
||||
///
|
||||
/// `progress` は ready 行を見つけるまでに観測した stderr の各行で呼ばれる
|
||||
/// (ready 行自体は除外される)。UI の表示更新や E2E ログ取得に使う。
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! `response.*` 名前空間の SSE を共通の [`Event`](crate::llm_client::event::Event)
|
||||
//! に変換する。Responses の (output_index, content_index) 2 次元座標と
|
||||
//! insomnia 側 1 次元 `BlockStart/Delta/Stop::index` のマッピングは
|
||||
//! yoi 側 1 次元 `BlockStart/Delta/Stop::index` のマッピングは
|
||||
//! [`OpenAIResponsesState`] が保持する。
|
||||
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! LLM Client Common Types
|
||||
//!
|
||||
//! Core conversation types for insomnia's LLM interaction model.
|
||||
//! Core conversation types for yoi's LLM interaction model.
|
||||
//! The core abstraction is `Item` which represents different types of conversation elements:
|
||||
//! - Message items (user/assistant messages with content parts)
|
||||
//! - ToolCall items (tool invocations)
|
||||
|
||||
@@ -688,7 +688,7 @@ mod tests {
|
||||
use crate::{Permission, ReasoningEffort, ScopeRule};
|
||||
|
||||
fn abs(path: &str) -> PathBuf {
|
||||
PathBuf::from(format!("/tmp/insomnia-test{path}"))
|
||||
PathBuf::from(format!("/tmp/yoi-test{path}"))
|
||||
}
|
||||
|
||||
fn api_key_file_auth(path: PathBuf) -> AuthRef {
|
||||
@@ -791,14 +791,14 @@ mod tests {
|
||||
fn resolve_paths_joins_relative_auth_file() {
|
||||
let mut cfg = minimal_valid();
|
||||
cfg.model.auth = Some(api_key_file_auth(PathBuf::from("keys/anthropic")));
|
||||
let resolved = cfg.resolve_paths(Path::new("/home/user/.config/insomnia"));
|
||||
let resolved = cfg.resolve_paths(Path::new("/home/user/.config/yoi"));
|
||||
let file = match resolved.model.auth {
|
||||
Some(AuthRef::ApiKey { file, .. }) => file,
|
||||
_ => panic!("expected ApiKey"),
|
||||
};
|
||||
assert_eq!(
|
||||
file.as_deref(),
|
||||
Some(Path::new("/home/user/.config/insomnia/keys/anthropic"))
|
||||
Some(Path::new("/home/user/.config/yoi/keys/anthropic"))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,8 +44,8 @@ pub const COMPACT_OVERVIEW_DEADLINE_TOKENS: u64 = 40_000;
|
||||
|
||||
/// Default instruction asset reference used when `worker.instruction`
|
||||
/// is omitted. See the `PromptLoader` prefix addressing scheme for the
|
||||
/// `$insomnia/` / `$user/` / `$workspace/` namespaces.
|
||||
pub const DEFAULT_INSTRUCTION: &str = "$insomnia/default";
|
||||
/// `$yoi/` / `$user/` / `$workspace/` namespaces.
|
||||
pub const DEFAULT_INSTRUCTION: &str = "$yoi/default";
|
||||
|
||||
/// Default language policy used by the main worker for normal prose
|
||||
/// responses. See [`crate::WorkerManifest::language`].
|
||||
|
||||
@@ -256,7 +256,7 @@ pub struct PodMeta {
|
||||
pub struct WorkerManifest {
|
||||
/// Reference to the instruction prompt asset used as the body of
|
||||
/// the worker's system prompt. Uses the `PromptLoader` prefix
|
||||
/// addressing scheme (`$insomnia/...`, `$user/...`,
|
||||
/// addressing scheme (`$yoi/...`, `$user/...`,
|
||||
/// `$workspace/...`) and is always populated after resolution —
|
||||
/// unset manifests fall through to [`defaults::DEFAULT_INSTRUCTION`].
|
||||
#[serde(default = "default_instruction")]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Insomnia のホームディレクトリ配下のパス解決を一元化するモジュール。
|
||||
//! Yoi のホームディレクトリ配下のパス解決を一元化するモジュール。
|
||||
//!
|
||||
//! 用途別に三つの base directory を持つ:
|
||||
//!
|
||||
@@ -10,13 +10,13 @@
|
||||
//!
|
||||
//! ## 解決順 (優先順位高 → 低)
|
||||
//!
|
||||
//! | base | 1. `INSOMNIA_<KIND>_DIR` | 2. `INSOMNIA_HOME` | 3. `XDG_*` | 4. 既定 |
|
||||
//! | base | 1. `YOI_<KIND>_DIR` | 2. `YOI_HOME` | 3. `XDG_*` | 4. 既定 |
|
||||
//! |---|---|---|---|---|
|
||||
//! | config | `INSOMNIA_CONFIG_DIR` | `$INSOMNIA_HOME/config` | `$XDG_CONFIG_HOME/insomnia` | `$HOME/.config/insomnia` |
|
||||
//! | data | `INSOMNIA_DATA_DIR` | `$INSOMNIA_HOME` | — | `$HOME/.insomnia` |
|
||||
//! | runtime | `INSOMNIA_RUNTIME_DIR` | `$INSOMNIA_HOME/run` | `$XDG_RUNTIME_DIR/insomnia` | `$HOME/.insomnia/run` |
|
||||
//! | config | `YOI_CONFIG_DIR` | `$YOI_HOME/config` | `$XDG_CONFIG_HOME/yoi` | `$HOME/.config/yoi` |
|
||||
//! | data | `YOI_DATA_DIR` | `$YOI_HOME` | — | `$HOME/.yoi` |
|
||||
//! | runtime | `YOI_RUNTIME_DIR` | `$YOI_HOME/run` | `$XDG_RUNTIME_DIR/yoi` | `$HOME/.yoi/run` |
|
||||
//!
|
||||
//! `INSOMNIA_HOME=$X` のとき config は `$X/config`、data は `$X` 直下、
|
||||
//! `YOI_HOME=$X` のとき config は `$X/config`、data は `$X` 直下、
|
||||
//! runtime は `$X/run` に集約される。テストや sandbox 利用ではこれ一本
|
||||
//! で全部 tempdir に向けられる。
|
||||
//!
|
||||
@@ -29,8 +29,8 @@ use std::path::PathBuf;
|
||||
/// `prompts/` などが置かれる。
|
||||
pub fn config_dir() -> Option<PathBuf> {
|
||||
resolve_config_dir_from_parts(
|
||||
env_path("INSOMNIA_CONFIG_DIR"),
|
||||
env_path("INSOMNIA_HOME"),
|
||||
env_path("YOI_CONFIG_DIR"),
|
||||
env_path("YOI_HOME"),
|
||||
env_path("XDG_CONFIG_HOME"),
|
||||
env_path("HOME"),
|
||||
)
|
||||
@@ -40,8 +40,8 @@ pub fn config_dir() -> Option<PathBuf> {
|
||||
/// 置き場。
|
||||
pub fn data_dir() -> Option<PathBuf> {
|
||||
resolve_data_dir_from_parts(
|
||||
env_path("INSOMNIA_DATA_DIR"),
|
||||
env_path("INSOMNIA_HOME"),
|
||||
env_path("YOI_DATA_DIR"),
|
||||
env_path("YOI_HOME"),
|
||||
env_path("HOME"),
|
||||
)
|
||||
}
|
||||
@@ -50,8 +50,8 @@ pub fn data_dir() -> Option<PathBuf> {
|
||||
/// `status.json` 等が置かれる。再起動で消えて構わない。
|
||||
pub fn runtime_dir() -> Option<PathBuf> {
|
||||
resolve_runtime_dir_from_parts(
|
||||
env_path("INSOMNIA_RUNTIME_DIR"),
|
||||
env_path("INSOMNIA_HOME"),
|
||||
env_path("YOI_RUNTIME_DIR"),
|
||||
env_path("YOI_HOME"),
|
||||
env_path("XDG_RUNTIME_DIR"),
|
||||
env_path("HOME"),
|
||||
)
|
||||
@@ -111,53 +111,53 @@ pub fn pod_socket_path(pod_name: &str) -> Option<PathBuf> {
|
||||
// ---- internals --------------------------------------------------------------
|
||||
|
||||
fn resolve_config_dir_from_parts(
|
||||
insomnia_config_dir: Option<PathBuf>,
|
||||
insomnia_home: Option<PathBuf>,
|
||||
yoi_config_dir: Option<PathBuf>,
|
||||
yoi_home: Option<PathBuf>,
|
||||
xdg_config_home: Option<PathBuf>,
|
||||
home: Option<PathBuf>,
|
||||
) -> Option<PathBuf> {
|
||||
if let Some(p) = insomnia_config_dir {
|
||||
if let Some(p) = yoi_config_dir {
|
||||
return Some(p);
|
||||
}
|
||||
if let Some(p) = insomnia_home {
|
||||
if let Some(p) = yoi_home {
|
||||
return Some(p.join("config"));
|
||||
}
|
||||
if let Some(p) = xdg_config_home {
|
||||
return Some(p.join("insomnia"));
|
||||
return Some(p.join("yoi"));
|
||||
}
|
||||
Some(home?.join(".config").join("insomnia"))
|
||||
Some(home?.join(".config").join("yoi"))
|
||||
}
|
||||
|
||||
fn resolve_data_dir_from_parts(
|
||||
insomnia_data_dir: Option<PathBuf>,
|
||||
insomnia_home: Option<PathBuf>,
|
||||
yoi_data_dir: Option<PathBuf>,
|
||||
yoi_home: Option<PathBuf>,
|
||||
home: Option<PathBuf>,
|
||||
) -> Option<PathBuf> {
|
||||
if let Some(p) = insomnia_data_dir {
|
||||
if let Some(p) = yoi_data_dir {
|
||||
return Some(p);
|
||||
}
|
||||
if let Some(p) = insomnia_home {
|
||||
if let Some(p) = yoi_home {
|
||||
return Some(p);
|
||||
}
|
||||
Some(home?.join(".insomnia"))
|
||||
Some(home?.join(".yoi"))
|
||||
}
|
||||
|
||||
fn resolve_runtime_dir_from_parts(
|
||||
insomnia_runtime_dir: Option<PathBuf>,
|
||||
insomnia_home: Option<PathBuf>,
|
||||
yoi_runtime_dir: Option<PathBuf>,
|
||||
yoi_home: Option<PathBuf>,
|
||||
xdg_runtime_dir: Option<PathBuf>,
|
||||
home: Option<PathBuf>,
|
||||
) -> Option<PathBuf> {
|
||||
if let Some(p) = insomnia_runtime_dir {
|
||||
if let Some(p) = yoi_runtime_dir {
|
||||
return Some(p);
|
||||
}
|
||||
if let Some(p) = insomnia_home {
|
||||
if let Some(p) = yoi_home {
|
||||
return Some(p.join("run"));
|
||||
}
|
||||
if let Some(p) = xdg_runtime_dir {
|
||||
return Some(p.join("insomnia"));
|
||||
return Some(p.join("yoi"));
|
||||
}
|
||||
Some(home?.join(".insomnia").join("run"))
|
||||
Some(home?.join(".yoi").join("run"))
|
||||
}
|
||||
|
||||
fn user_profiles_path_from_config_dir(config_dir: Option<PathBuf>) -> Option<PathBuf> {
|
||||
@@ -221,7 +221,7 @@ mod tests {
|
||||
fn config_dir_falls_back_to_home_dot_config() {
|
||||
assert_eq!(
|
||||
resolve_config_dir_from_parts(None, None, None, Some(PathBuf::from("/h"))).unwrap(),
|
||||
PathBuf::from("/h/.config/insomnia")
|
||||
PathBuf::from("/h/.config/yoi")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -235,12 +235,12 @@ mod tests {
|
||||
Some(PathBuf::from("/h")),
|
||||
)
|
||||
.unwrap(),
|
||||
PathBuf::from("/x/insomnia")
|
||||
PathBuf::from("/x/yoi")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_dir_insomnia_home_outranks_xdg() {
|
||||
fn config_dir_yoi_home_outranks_xdg() {
|
||||
assert_eq!(
|
||||
resolve_config_dir_from_parts(
|
||||
None,
|
||||
@@ -254,7 +254,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_dir_explicit_wins_over_insomnia_home() {
|
||||
fn config_dir_explicit_wins_over_yoi_home() {
|
||||
assert_eq!(
|
||||
resolve_config_dir_from_parts(
|
||||
Some(PathBuf::from("/explicit-cfg")),
|
||||
@@ -268,15 +268,15 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn data_dir_default_is_dot_insomnia() {
|
||||
fn data_dir_default_is_dot_yoi() {
|
||||
assert_eq!(
|
||||
resolve_data_dir_from_parts(None, None, Some(PathBuf::from("/h"))).unwrap(),
|
||||
PathBuf::from("/h/.insomnia")
|
||||
PathBuf::from("/h/.yoi")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn data_dir_insomnia_home_is_data_dir_itself() {
|
||||
fn data_dir_yoi_home_is_data_dir_itself() {
|
||||
assert_eq!(
|
||||
resolve_data_dir_from_parts(
|
||||
None,
|
||||
@@ -289,7 +289,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn data_dir_explicit_wins_over_insomnia_home() {
|
||||
fn data_dir_explicit_wins_over_yoi_home() {
|
||||
assert_eq!(
|
||||
resolve_data_dir_from_parts(
|
||||
Some(PathBuf::from("/explicit-data")),
|
||||
@@ -311,20 +311,20 @@ mod tests {
|
||||
Some(PathBuf::from("/h")),
|
||||
)
|
||||
.unwrap(),
|
||||
PathBuf::from("/xdg-runtime/insomnia")
|
||||
PathBuf::from("/xdg-runtime/yoi")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_dir_falls_back_to_dot_insomnia_run() {
|
||||
fn runtime_dir_falls_back_to_dot_yoi_run() {
|
||||
assert_eq!(
|
||||
resolve_runtime_dir_from_parts(None, None, None, Some(PathBuf::from("/h"))).unwrap(),
|
||||
PathBuf::from("/h/.insomnia/run")
|
||||
PathBuf::from("/h/.yoi/run")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_dir_insomnia_home_is_run_subdir() {
|
||||
fn runtime_dir_yoi_home_is_run_subdir() {
|
||||
assert_eq!(
|
||||
resolve_runtime_dir_from_parts(
|
||||
None,
|
||||
@@ -338,7 +338,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_dir_explicit_wins_over_insomnia_home() {
|
||||
fn runtime_dir_explicit_wins_over_yoi_home() {
|
||||
assert_eq!(
|
||||
resolve_runtime_dir_from_parts(
|
||||
Some(PathBuf::from("/explicit-run")),
|
||||
@@ -358,7 +358,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
resolve_config_dir_from_parts(None, None, xdg_config_home, Some(PathBuf::from("/h")))
|
||||
.unwrap(),
|
||||
PathBuf::from("/h/.config/insomnia")
|
||||
PathBuf::from("/h/.config/yoi")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,11 +20,11 @@ use crate::{
|
||||
ScopeConfig, ScopeRule, SkillsConfig, WebConfig, WorkerManifestConfig, paths,
|
||||
};
|
||||
|
||||
const PROFILE_FORMAT_V1: &str = "insomnia.lua-profile.v1";
|
||||
const PROFILE_FORMAT_V1: &str = "yoi.lua-profile.v1";
|
||||
const BUILTIN_DEFAULT_PROFILE_NAME: &str = "default";
|
||||
const BUILTIN_DEFAULT_PROFILE: &str = include_str!("../../../resources/profiles/default.lua");
|
||||
const BUILTIN_MODEL_CATALOG: &str = include_str!("../../../resources/models/builtin.toml");
|
||||
const DEFAULT_POD_NAME: &str = "insomnia";
|
||||
const DEFAULT_POD_NAME: &str = "yoi";
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@@ -694,7 +694,7 @@ fn find_project_profiles_from(start: &Path) -> Option<PathBuf> {
|
||||
.unwrap_or_else(|| start.to_path_buf());
|
||||
let mut cur: Option<&Path> = Some(start.as_path());
|
||||
while let Some(dir) = cur {
|
||||
let candidate = dir.join(".insomnia").join("profiles.toml");
|
||||
let candidate = dir.join(".yoi").join("profiles.toml");
|
||||
if candidate.is_file() {
|
||||
return Some(candidate);
|
||||
}
|
||||
@@ -709,7 +709,7 @@ fn add_builtin_profiles(registry: &mut ProfileRegistry) {
|
||||
BUILTIN_DEFAULT_PROFILE_NAME,
|
||||
"builtin:default",
|
||||
BUILTIN_DEFAULT_PROFILE,
|
||||
Some("Bundled default Insomnia coding profile".into()),
|
||||
Some("Bundled default Yoi coding profile".into()),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -827,7 +827,7 @@ fn require_module(
|
||||
if let Some(value) = host_module(lua, name)? {
|
||||
return Ok(value);
|
||||
}
|
||||
if name.starts_with("insomnia.") || name == "insomnia" {
|
||||
if name.starts_with("yoi.") || name == "yoi" {
|
||||
return Err(mlua::Error::RuntimeError(format!(
|
||||
"unknown host module `{name}`"
|
||||
)));
|
||||
@@ -876,7 +876,7 @@ fn require_module(
|
||||
|
||||
fn host_module(lua: &Lua, name: &str) -> mlua::Result<Option<LuaValue>> {
|
||||
match name {
|
||||
"insomnia" => {
|
||||
"yoi" => {
|
||||
let t = lua.create_table()?;
|
||||
t.set("profile", profile_function(lua)?)?;
|
||||
t.set("models", models_module(lua)?)?;
|
||||
@@ -884,10 +884,10 @@ fn host_module(lua: &Lua, name: &str) -> mlua::Result<Option<LuaValue>> {
|
||||
t.set("scope", scope_module(lua)?)?;
|
||||
Ok(Some(LuaValue::Table(t)))
|
||||
}
|
||||
"insomnia.profile" => Ok(Some(LuaValue::Function(profile_function(lua)?))),
|
||||
"insomnia.models" => Ok(Some(LuaValue::Table(models_module(lua)?))),
|
||||
"insomnia.compact" => Ok(Some(LuaValue::Table(compact_module(lua)?))),
|
||||
"insomnia.scope" => Ok(Some(LuaValue::Table(scope_module(lua)?))),
|
||||
"yoi.profile" => Ok(Some(LuaValue::Function(profile_function(lua)?))),
|
||||
"yoi.models" => Ok(Some(LuaValue::Table(models_module(lua)?))),
|
||||
"yoi.compact" => Ok(Some(LuaValue::Table(compact_module(lua)?))),
|
||||
"yoi.scope" => Ok(Some(LuaValue::Table(scope_module(lua)?))),
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
@@ -997,7 +997,7 @@ fn reject_manifest_shaped_profile(value: &serde_json::Value) -> Result<(), Profi
|
||||
for key in ["allow", "deny"] {
|
||||
if scope.contains_key(key) {
|
||||
return Err(ProfileError::InvalidProfile(format!(
|
||||
"field `scope.{key}` grants concrete authority and is not allowed in reusable Profiles; use require(\"insomnia.scope\") intent helpers"
|
||||
"field `scope.{key}` grants concrete authority and is not allowed in reusable Profiles; use require(\"yoi.scope\") intent helpers"
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -1312,8 +1312,8 @@ mod tests {
|
||||
tmp.path(),
|
||||
"coder.lua",
|
||||
r#"
|
||||
local profile = require("insomnia.profile")
|
||||
local scope = require("insomnia.scope")
|
||||
local profile = require("yoi.profile")
|
||||
local scope = require("yoi.scope")
|
||||
return profile {
|
||||
slug = "coder",
|
||||
model = { scheme = "anthropic", model_id = "claude-sonnet-4-20250514" },
|
||||
@@ -1352,19 +1352,19 @@ return profile {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
std::fs::write(
|
||||
tmp.path().join("shared.lua"),
|
||||
r#"return { model = require("insomnia.models").catalog("codex-oauth/gpt-5.5") }"#,
|
||||
r#"return { model = require("yoi.models").catalog("codex-oauth/gpt-5.5") }"#,
|
||||
)
|
||||
.unwrap();
|
||||
let profile = write_profile(
|
||||
tmp.path(),
|
||||
"main.lua",
|
||||
r#"
|
||||
local insomnia = require("insomnia")
|
||||
local yoi = require("yoi")
|
||||
local shared = require("shared")
|
||||
return insomnia.profile {
|
||||
return yoi.profile {
|
||||
slug = "main",
|
||||
model = shared.model,
|
||||
scope = insomnia.scope.workspace_write(),
|
||||
scope = yoi.scope.workspace_write(),
|
||||
}
|
||||
"#,
|
||||
);
|
||||
@@ -1445,9 +1445,9 @@ return insomnia.profile {
|
||||
tmp.path(),
|
||||
"ratio.lua",
|
||||
r#"
|
||||
local profile = require("insomnia.profile")
|
||||
local models = require("insomnia.models")
|
||||
local compact = require("insomnia.compact")
|
||||
local profile = require("yoi.profile")
|
||||
local models = require("yoi.models")
|
||||
local compact = require("yoi.compact")
|
||||
return profile {
|
||||
model = models.catalog("codex-oauth/gpt-5.5"),
|
||||
compaction = compact.ratio { threshold = 0.5, request = 0.75, worker = 0.25 },
|
||||
@@ -1473,7 +1473,7 @@ return profile {
|
||||
.with_workspace_base(tmp.path())
|
||||
.resolve(&ProfileSelector::Default, ProfileResolveOptions::default())
|
||||
.unwrap();
|
||||
assert_eq!(resolved.manifest.pod.name, "insomnia");
|
||||
assert_eq!(resolved.manifest.pod.name, "yoi");
|
||||
assert_eq!(
|
||||
resolved.manifest.model.ref_.as_deref(),
|
||||
Some("codex-oauth/gpt-5.5")
|
||||
@@ -1516,7 +1516,7 @@ return profile {
|
||||
fn discovery_reads_user_and_project_registry_and_project_default_wins() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let user_config = tmp.path().join("profiles.toml");
|
||||
let project_dir = tmp.path().join("project/.insomnia");
|
||||
let project_dir = tmp.path().join("project/.yoi");
|
||||
std::fs::create_dir_all(&project_dir).unwrap();
|
||||
let project_config = project_dir.join("profiles.toml");
|
||||
std::fs::write(
|
||||
@@ -1542,7 +1542,7 @@ return profile {
|
||||
#[test]
|
||||
fn default_marks_direct_profile_entry() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let project_dir = tmp.path().join("project/.insomnia");
|
||||
let project_dir = tmp.path().join("project/.yoi");
|
||||
std::fs::create_dir_all(&project_dir).unwrap();
|
||||
let project_config = project_dir.join("profiles.toml");
|
||||
std::fs::write(
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
//! Append-only JSONL audit log for memory workers and tools.
|
||||
//!
|
||||
//! The log is evidence-only observability data under
|
||||
//! `.insomnia/memory/_logs/current.log`. It is intentionally separate from
|
||||
//! `.yoi/memory/_logs/current.log`. It is intentionally separate from
|
||||
//! `_staging` and `_usage`, and consolidation never consumes it. Operators can
|
||||
//! follow the latest stream with:
|
||||
//!
|
||||
//! ```text
|
||||
//! tail -f .insomnia/memory/_logs/current.log
|
||||
//! tail -f .yoi/memory/_logs/current.log
|
||||
//! ```
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
@@ -260,7 +260,7 @@ pub struct RecordSnapshot {
|
||||
pub hash: String,
|
||||
}
|
||||
|
||||
/// Append one audit event to `.insomnia/memory/_logs/current.log`.
|
||||
/// Append one audit event to `.yoi/memory/_logs/current.log`.
|
||||
pub fn append_audit_event(layout: &WorkspaceLayout, event: &AuditEvent) -> io::Result<()> {
|
||||
let path = layout.audit_current_log_path();
|
||||
if let Some(parent) = path.parent() {
|
||||
@@ -425,7 +425,7 @@ mod tests {
|
||||
#[test]
|
||||
fn counts_created_edited_deleted_records() {
|
||||
let (dir, layout) = setup();
|
||||
let decision_dir = dir.path().join(".insomnia/memory/decisions");
|
||||
let decision_dir = dir.path().join(".yoi/memory/decisions");
|
||||
fs::create_dir_all(&decision_dir).unwrap();
|
||||
fs::write(decision_dir.join("a.md"), "old").unwrap();
|
||||
fs::write(decision_dir.join("gone.md"), "old").unwrap();
|
||||
|
||||
@@ -74,7 +74,7 @@ pub fn render_staging_records(entries: &[StagingEntry]) -> String {
|
||||
out
|
||||
}
|
||||
|
||||
/// `<workspace>/.insomnia/memory/{summary.md,decisions/*,requests/*}` を
|
||||
/// `<workspace>/.yoi/memory/{summary.md,decisions/*,requests/*}` を
|
||||
/// 「`### <kind>:<slug>` ヘッダ + raw markdown ブロック」で全文渡す。
|
||||
pub fn render_existing_memory_records(layout: &WorkspaceLayout) -> String {
|
||||
let mut out = String::new();
|
||||
@@ -230,11 +230,11 @@ mod tests {
|
||||
let layout = WorkspaceLayout::new(dir.path().to_path_buf());
|
||||
|
||||
write(
|
||||
&dir.path().join(".insomnia/memory/summary.md"),
|
||||
&dir.path().join(".yoi/memory/summary.md"),
|
||||
&format!("---\nupdated_at: {n}\n---\nstate of the world\n", n = now()),
|
||||
);
|
||||
write(
|
||||
&dir.path().join(".insomnia/memory/decisions/dec.md"),
|
||||
&dir.path().join(".yoi/memory/decisions/dec.md"),
|
||||
&format!(
|
||||
"---\ncreated_at: {n}\nupdated_at: {n}\nsources: []\nstatus: open\n---\nbody\n",
|
||||
n = now()
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//! が追加した分は残す
|
||||
//!
|
||||
//! 占有判定は Linux/macOS の `kill(pid, 0)` 経由で行う(`ESRCH` で死亡判定)。
|
||||
//! Windows は対象外: INSOMNIA は POSIX 環境を前提にしている。
|
||||
//! Windows は対象外: Yoi は POSIX 環境を前提にしている。
|
||||
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@@ -133,8 +133,8 @@ pub fn collect_tidy_hints(layout: &WorkspaceLayout) -> TidyHints {
|
||||
hints
|
||||
}
|
||||
|
||||
/// `<root>/.insomnia/memory/<kind>/*.md` (Knowledge は
|
||||
/// `<root>/.insomnia/knowledge/*.md`) を slug ごとに `(slug, full content)`
|
||||
/// `<root>/.yoi/memory/<kind>/*.md` (Knowledge は
|
||||
/// `<root>/.yoi/knowledge/*.md`) を slug ごとに `(slug, full content)`
|
||||
/// 化して返す。
|
||||
fn read_kind_records(layout: &WorkspaceLayout, kind: RecordKind) -> BTreeMap<String, String> {
|
||||
let dir = match kind {
|
||||
@@ -280,14 +280,14 @@ mod tests {
|
||||
fn collects_replaced_chain() {
|
||||
let (dir, layout) = workspace();
|
||||
write(
|
||||
&dir.path().join(".insomnia/memory/decisions/replaced.md"),
|
||||
&dir.path().join(".yoi/memory/decisions/replaced.md"),
|
||||
&format!(
|
||||
"---\ncreated_at: {n}\nupdated_at: {n}\nsources: []\nstatus: replaced\nreplaced_by: winner\n---\n",
|
||||
n = now()
|
||||
),
|
||||
);
|
||||
write(
|
||||
&dir.path().join(".insomnia/memory/decisions/winner.md"),
|
||||
&dir.path().join(".yoi/memory/decisions/winner.md"),
|
||||
&format!(
|
||||
"---\ncreated_at: {n}\nupdated_at: {n}\nsources: []\nstatus: open\n---\n",
|
||||
n = now()
|
||||
@@ -308,7 +308,7 @@ mod tests {
|
||||
.map(|i| format!(" - segment_id: s{i}\n range: [{i}, {i}]\n"))
|
||||
.collect();
|
||||
write(
|
||||
&dir.path().join(".insomnia/memory/decisions/big.md"),
|
||||
&dir.path().join(".yoi/memory/decisions/big.md"),
|
||||
&format!(
|
||||
"---\ncreated_at: {n}\nupdated_at: {n}\nstatus: open\nsources:\n{m}---\n",
|
||||
n = now(),
|
||||
@@ -327,8 +327,7 @@ mod tests {
|
||||
let (dir, layout) = workspace();
|
||||
for slug in ["db-pool", "db-pol", "db-pools", "alpha"] {
|
||||
write(
|
||||
&dir.path()
|
||||
.join(format!(".insomnia/memory/decisions/{slug}.md")),
|
||||
&dir.path().join(format!(".yoi/memory/decisions/{slug}.md")),
|
||||
&format!(
|
||||
"---\ncreated_at: {n}\nupdated_at: {n}\nsources: []\nstatus: open\n---\n",
|
||||
n = now()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! extract: 活動抽出。
|
||||
//!
|
||||
//! 通常 Pod の post-run hook で発火する disposable Worker と、その
|
||||
//! 出力を `<workspace>/.insomnia/memory/_staging/<id>.json` に書き出す
|
||||
//! 出力を `<workspace>/.yoi/memory/_staging/<id>.json` に書き出す
|
||||
//! ヘルパーを提供する。Pod 側はこのモジュールから:
|
||||
//!
|
||||
//! - [`build_extract_input`] を sub-Worker の最初の user 入力に
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! `<workspace>/.insomnia/memory/_staging/<id>.json` への書き出しヘルパー。
|
||||
//! `<workspace>/.yoi/memory/_staging/<id>.json` への書き出しヘルパー。
|
||||
//!
|
||||
//! 1 件 1 ファイル、UUIDv7 命名(短命なので衝突回避と順序を兼ねる)。
|
||||
//! `source` を機械付与した [`StagingRecord`] 形式で保存する。
|
||||
|
||||
@@ -291,7 +291,7 @@ mod tests {
|
||||
#[test]
|
||||
fn decision_with_unknown_replaced_by_errors() {
|
||||
let (dir, linter) = workspace();
|
||||
let path = dir.path().join(".insomnia/memory/decisions/foo.md");
|
||||
let path = dir.path().join(".yoi/memory/decisions/foo.md");
|
||||
let content = format!(
|
||||
"---\ncreated_at: {now}\nupdated_at: {now}\nsources: []\nstatus: replaced\nreplaced_by: ghost\n---\nbody\n",
|
||||
now = iso_now()
|
||||
@@ -308,7 +308,7 @@ mod tests {
|
||||
#[test]
|
||||
fn decision_replaced_by_self_errors() {
|
||||
let (dir, linter) = workspace();
|
||||
let path = dir.path().join(".insomnia/memory/decisions/foo.md");
|
||||
let path = dir.path().join(".yoi/memory/decisions/foo.md");
|
||||
let content = format!(
|
||||
"---\ncreated_at: {now}\nupdated_at: {now}\nsources: []\nstatus: replaced\nreplaced_by: foo\n---\nbody\n",
|
||||
now = iso_now()
|
||||
@@ -326,7 +326,7 @@ mod tests {
|
||||
fn decision_replaced_by_existing_ok() {
|
||||
let (dir, linter) = workspace();
|
||||
// Pre-create the target.
|
||||
let target = dir.path().join(".insomnia/memory/decisions/bar.md");
|
||||
let target = dir.path().join(".yoi/memory/decisions/bar.md");
|
||||
write(
|
||||
&target,
|
||||
&format!(
|
||||
@@ -334,7 +334,7 @@ mod tests {
|
||||
now = iso_now()
|
||||
),
|
||||
);
|
||||
let path = dir.path().join(".insomnia/memory/decisions/foo.md");
|
||||
let path = dir.path().join(".yoi/memory/decisions/foo.md");
|
||||
let content = format!(
|
||||
"---\ncreated_at: {now}\nupdated_at: {now}\nsources: []\nstatus: replaced\nreplaced_by: bar\n---\nbody\n",
|
||||
now = iso_now()
|
||||
@@ -346,7 +346,7 @@ mod tests {
|
||||
#[test]
|
||||
fn missing_required_field_errors() {
|
||||
let (dir, linter) = workspace();
|
||||
let path = dir.path().join(".insomnia/memory/decisions/foo.md");
|
||||
let path = dir.path().join(".yoi/memory/decisions/foo.md");
|
||||
// Missing `status`.
|
||||
let content = format!(
|
||||
"---\ncreated_at: {now}\nupdated_at: {now}\nsources: []\n---\nbody\n",
|
||||
@@ -363,7 +363,7 @@ mod tests {
|
||||
#[test]
|
||||
fn knowledge_long_description_with_model_invokation_errors() {
|
||||
let (dir, linter) = workspace();
|
||||
let path = dir.path().join(".insomnia/knowledge/foo.md");
|
||||
let path = dir.path().join(".yoi/knowledge/foo.md");
|
||||
let big_desc = "x".repeat(2000);
|
||||
let content = format!(
|
||||
"---\ncreated_at: {now}\nupdated_at: {now}\nkind: rule\ndescription: {big_desc}\nmodel_invokation: true\nuser_invocable: true\nlast_sources: []\n---\nbody\n",
|
||||
@@ -381,7 +381,7 @@ mod tests {
|
||||
#[test]
|
||||
fn knowledge_long_description_without_model_invokation_ok() {
|
||||
let (dir, linter) = workspace();
|
||||
let path = dir.path().join(".insomnia/knowledge/foo.md");
|
||||
let path = dir.path().join(".yoi/knowledge/foo.md");
|
||||
let big_desc = "x".repeat(2000);
|
||||
let content = format!(
|
||||
"---\ncreated_at: {now}\nupdated_at: {now}\nkind: rule\ndescription: {big_desc}\nmodel_invokation: false\nuser_invocable: true\nlast_sources: []\n---\nbody\n",
|
||||
@@ -394,7 +394,7 @@ mod tests {
|
||||
#[test]
|
||||
fn summary_path_accepted() {
|
||||
let (dir, linter) = workspace();
|
||||
let path = dir.path().join(".insomnia/memory/summary.md");
|
||||
let path = dir.path().join(".yoi/memory/summary.md");
|
||||
let content = format!(
|
||||
"---\nupdated_at: {now}\n---\nsummary body\n",
|
||||
now = iso_now()
|
||||
@@ -406,7 +406,7 @@ mod tests {
|
||||
#[test]
|
||||
fn create_when_existing_errors() {
|
||||
let (dir, linter) = workspace();
|
||||
let path = dir.path().join(".insomnia/memory/decisions/foo.md");
|
||||
let path = dir.path().join(".yoi/memory/decisions/foo.md");
|
||||
write(
|
||||
&path,
|
||||
&format!(
|
||||
@@ -434,15 +434,14 @@ mod tests {
|
||||
// `db-pol` (1 deletion), `db-pools` (1 insertion).
|
||||
for slug in ["db-pol", "db-pools"] {
|
||||
write(
|
||||
&dir.path()
|
||||
.join(format!(".insomnia/memory/decisions/{slug}.md")),
|
||||
&dir.path().join(format!(".yoi/memory/decisions/{slug}.md")),
|
||||
&format!(
|
||||
"---\ncreated_at: {n}\nupdated_at: {n}\nsources: []\nstatus: open\n---\n",
|
||||
n = iso_now()
|
||||
),
|
||||
);
|
||||
}
|
||||
let path = dir.path().join(".insomnia/memory/decisions/db-pool.md");
|
||||
let path = dir.path().join(".yoi/memory/decisions/db-pool.md");
|
||||
let content = format!(
|
||||
"---\ncreated_at: {n}\nupdated_at: {n}\nsources: []\nstatus: open\n---\nbody\n",
|
||||
n = iso_now()
|
||||
@@ -464,15 +463,14 @@ mod tests {
|
||||
let (dir, linter) = workspace();
|
||||
for slug in ["alpha", "bravo"] {
|
||||
write(
|
||||
&dir.path()
|
||||
.join(format!(".insomnia/memory/decisions/{slug}.md")),
|
||||
&dir.path().join(format!(".yoi/memory/decisions/{slug}.md")),
|
||||
&format!(
|
||||
"---\ncreated_at: {n}\nupdated_at: {n}\nsources: []\nstatus: open\n---\n",
|
||||
n = iso_now()
|
||||
),
|
||||
);
|
||||
}
|
||||
let path = dir.path().join(".insomnia/memory/decisions/charlie.md");
|
||||
let path = dir.path().join(".yoi/memory/decisions/charlie.md");
|
||||
let content = format!(
|
||||
"---\ncreated_at: {n}\nupdated_at: {n}\nsources: []\nstatus: open\n---\n",
|
||||
n = iso_now()
|
||||
@@ -491,7 +489,7 @@ mod tests {
|
||||
#[test]
|
||||
fn body_size_limit_errors() {
|
||||
let (dir, linter) = workspace();
|
||||
let path = dir.path().join(".insomnia/memory/decisions/foo.md");
|
||||
let path = dir.path().join(".yoi/memory/decisions/foo.md");
|
||||
let big_body = "x".repeat(8001);
|
||||
let content = format!(
|
||||
"---\ncreated_at: {now}\nupdated_at: {now}\nsources: []\nstatus: open\n---\n{body}",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//! - [`collect_resident_knowledge`] — resident-injection candidates
|
||||
//! (`model_invokation: true`) returned as `(slug, description)` pairs.
|
||||
//! - [`collect_resident_summary`] — the body of
|
||||
//! `<workspace>/.insomnia/memory/summary.md` when it parses as a summary
|
||||
//! `<workspace>/.yoi/memory/summary.md` when it parses as a summary
|
||||
//! record and has non-empty body.
|
||||
//! - [`list_knowledge_slugs`] — every slug whose file parses, regardless
|
||||
//! of `model_invokation`. Used by the Pod IPC layer to answer TUI `#`
|
||||
@@ -25,7 +25,7 @@ pub struct ResidentKnowledgeEntry {
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
/// Walk `<workspace>/.insomnia/knowledge/*.md` and return entries whose
|
||||
/// Walk `<workspace>/.yoi/knowledge/*.md` and return entries whose
|
||||
/// frontmatter has `model_invokation: true`, sorted by slug. A missing
|
||||
/// directory yields an empty vec.
|
||||
pub fn collect_resident_knowledge(layout: &WorkspaceLayout) -> Vec<ResidentKnowledgeEntry> {
|
||||
@@ -42,7 +42,7 @@ pub fn collect_resident_knowledge(layout: &WorkspaceLayout) -> Vec<ResidentKnowl
|
||||
out
|
||||
}
|
||||
|
||||
/// Read `<workspace>/.insomnia/memory/summary.md` for resident prompt
|
||||
/// Read `<workspace>/.yoi/memory/summary.md` for resident prompt
|
||||
/// injection. Returns only the markdown body (frontmatter stripped), and
|
||||
/// degrades to `None` for missing, unreadable, malformed, or empty records.
|
||||
pub fn collect_resident_summary(layout: &WorkspaceLayout) -> Option<String> {
|
||||
@@ -115,7 +115,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn write_summary(dir: &Path, body: &str) {
|
||||
let path = dir.join(".insomnia/memory/summary.md");
|
||||
let path = dir.join(".yoi/memory/summary.md");
|
||||
let content = format!("---\nupdated_at: {n}\n---\n{body}", n = now());
|
||||
std::fs::write(path, content).unwrap();
|
||||
}
|
||||
@@ -127,7 +127,7 @@ mod tests {
|
||||
model_invokation: bool,
|
||||
body: &str,
|
||||
) {
|
||||
let path = dir.join(".insomnia/knowledge").join(format!("{slug}.md"));
|
||||
let path = dir.join(".yoi/knowledge").join(format!("{slug}.md"));
|
||||
let content = format!(
|
||||
"---\ncreated_at: {n}\nupdated_at: {n}\nkind: policy\ndescription: \"{description}\"\nmodel_invokation: {flag}\nuser_invocable: true\nlast_sources: []\n---\n{body}",
|
||||
n = now(),
|
||||
@@ -138,8 +138,8 @@ mod tests {
|
||||
|
||||
fn setup() -> (TempDir, WorkspaceLayout) {
|
||||
let dir = TempDir::new().unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".insomnia/knowledge")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".insomnia/memory")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".yoi/knowledge")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".yoi/memory")).unwrap();
|
||||
let layout = WorkspaceLayout::new(dir.path().to_path_buf());
|
||||
(dir, layout)
|
||||
}
|
||||
@@ -166,7 +166,7 @@ mod tests {
|
||||
fn malformed_summary_returns_none() {
|
||||
let (dir, layout) = setup();
|
||||
std::fs::write(
|
||||
dir.path().join(".insomnia/memory/summary.md"),
|
||||
dir.path().join(".yoi/memory/summary.md"),
|
||||
"---\nthis is not yaml: : :\n---\nbody\n",
|
||||
)
|
||||
.unwrap();
|
||||
@@ -222,7 +222,7 @@ mod tests {
|
||||
write_knowledge(dir.path(), "good", "ok", true, "");
|
||||
// Garbage in frontmatter — must be skipped, not panic.
|
||||
std::fs::write(
|
||||
dir.path().join(".insomnia/knowledge/bad.md"),
|
||||
dir.path().join(".yoi/knowledge/bad.md"),
|
||||
"---\nthis is not yaml: : :\n---\nbody\n",
|
||||
)
|
||||
.unwrap();
|
||||
@@ -236,11 +236,7 @@ mod tests {
|
||||
fn non_md_files_ignored() {
|
||||
let (dir, layout) = setup();
|
||||
write_knowledge(dir.path(), "good", "ok", true, "");
|
||||
std::fs::write(
|
||||
dir.path().join(".insomnia/knowledge/note.txt"),
|
||||
"not markdown\n",
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::write(dir.path().join(".yoi/knowledge/note.txt"), "not markdown\n").unwrap();
|
||||
|
||||
let got = collect_resident_knowledge(&layout);
|
||||
assert_eq!(got.len(), 1);
|
||||
@@ -269,15 +265,11 @@ mod tests {
|
||||
let (dir, layout) = setup();
|
||||
write_knowledge(dir.path(), "good", "ok", true, "");
|
||||
std::fs::write(
|
||||
dir.path().join(".insomnia/knowledge/bad.md"),
|
||||
dir.path().join(".yoi/knowledge/bad.md"),
|
||||
"---\nthis is not yaml: : :\n---\nbody\n",
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::write(
|
||||
dir.path().join(".insomnia/knowledge/note.txt"),
|
||||
"not markdown\n",
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::write(dir.path().join(".yoi/knowledge/note.txt"), "not markdown\n").unwrap();
|
||||
|
||||
let got = list_knowledge_slugs(&layout);
|
||||
assert_eq!(got, vec!["good"]);
|
||||
|
||||
@@ -41,9 +41,9 @@ mod tests {
|
||||
let layout = WorkspaceLayout::new(PathBuf::from("/ws"));
|
||||
let rules = deny_write_rules(&layout);
|
||||
assert_eq!(rules.len(), 2);
|
||||
assert_eq!(rules[0].target, PathBuf::from("/ws/.insomnia/memory"));
|
||||
assert_eq!(rules[0].target, PathBuf::from("/ws/.yoi/memory"));
|
||||
assert_eq!(rules[0].permission, Permission::Write);
|
||||
assert!(rules[0].recursive);
|
||||
assert_eq!(rules[1].target, PathBuf::from("/ws/.insomnia/knowledge"));
|
||||
assert_eq!(rules[1].target, PathBuf::from("/ws/.yoi/knowledge"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ mod tests {
|
||||
fn setup() -> (TempDir, WorkspaceLayout, PathBuf) {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let layout = WorkspaceLayout::new(dir.path().to_path_buf());
|
||||
let path = dir.path().join(".insomnia/memory/decisions/foo.md");
|
||||
let path = dir.path().join(".yoi/memory/decisions/foo.md");
|
||||
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
|
||||
let initial = format!(
|
||||
"---\ncreated_at: {n}\nupdated_at: {n}\nsources: []\nstatus: open\n---\nbody body\n",
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
//! omitted, returns one entry per file (no excerpt) so the agent can
|
||||
//! enumerate what records exist without knowing what's inside them.
|
||||
//!
|
||||
//! - `MemoryQuery` walks `.insomnia/memory/{summary.md,decisions/,
|
||||
//! requests/}`. `.insomnia/workflow/`, `.insomnia/memory/_staging/`,
|
||||
//! `.insomnia/memory/_usage/`, and `.insomnia/memory/_logs/` are excluded
|
||||
//! - `MemoryQuery` walks `.yoi/memory/{summary.md,decisions/,
|
||||
//! requests/}`. `.yoi/workflow/`, `.yoi/memory/_staging/`,
|
||||
//! `.yoi/memory/_usage/`, and `.yoi/memory/_logs/` are excluded
|
||||
//! by construction.
|
||||
//! - `KnowledgeQuery` walks `.insomnia/knowledge/*.md` and supports a
|
||||
//! - `KnowledgeQuery` walks `.yoi/knowledge/*.md` and supports a
|
||||
//! `kind` filter against the Knowledge frontmatter's `kind` field.
|
||||
//!
|
||||
//! No derived index — the file tree is the source of truth and is
|
||||
@@ -513,18 +513,16 @@ mod tests {
|
||||
fn setup() -> (TempDir, WorkspaceLayout) {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let layout = WorkspaceLayout::new(dir.path().to_path_buf());
|
||||
std::fs::create_dir_all(dir.path().join(".insomnia/memory/decisions")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".insomnia/memory/requests")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".insomnia/memory/_staging")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".insomnia/workflow")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".insomnia/knowledge")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".yoi/memory/decisions")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".yoi/memory/requests")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".yoi/memory/_staging")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".yoi/workflow")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".yoi/knowledge")).unwrap();
|
||||
(dir, layout)
|
||||
}
|
||||
|
||||
fn write_decision(dir: &Path, slug: &str, body: &str) {
|
||||
let path = dir
|
||||
.join(".insomnia/memory/decisions")
|
||||
.join(format!("{slug}.md"));
|
||||
let path = dir.join(".yoi/memory/decisions").join(format!("{slug}.md"));
|
||||
let content = format!(
|
||||
"---\ncreated_at: {n}\nupdated_at: {n}\nsources: []\nstatus: open\n---\n{body}",
|
||||
n = now()
|
||||
@@ -533,7 +531,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn write_knowledge(dir: &Path, slug: &str, kind: &str, description: &str, body: &str) {
|
||||
let path = dir.join(".insomnia/knowledge").join(format!("{slug}.md"));
|
||||
let path = dir.join(".yoi/knowledge").join(format!("{slug}.md"));
|
||||
let content = format!(
|
||||
"---\ncreated_at: {n}\nupdated_at: {n}\nkind: {kind}\ndescription: \"{description}\"\nmodel_invokation: false\nuser_invocable: true\nlast_sources: []\n---\n{body}",
|
||||
n = now()
|
||||
@@ -590,7 +588,7 @@ mod tests {
|
||||
let (dir, layout) = setup();
|
||||
write_decision(dir.path(), "alpha", "body\n");
|
||||
write_decision(dir.path(), "beta", "body\n");
|
||||
let summary_path = dir.path().join(".insomnia/memory/summary.md");
|
||||
let summary_path = dir.path().join(".yoi/memory/summary.md");
|
||||
std::fs::write(
|
||||
&summary_path,
|
||||
format!("---\nupdated_at: {n}\n---\nhello\n", n = now()),
|
||||
@@ -610,7 +608,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn memory_query_finds_summary() {
|
||||
let (dir, layout) = setup();
|
||||
let summary_path = dir.path().join(".insomnia/memory/summary.md");
|
||||
let summary_path = dir.path().join(".yoi/memory/summary.md");
|
||||
std::fs::write(
|
||||
&summary_path,
|
||||
format!("---\nupdated_at: {n}\n---\nthe needle is here\n", n = now()),
|
||||
@@ -628,9 +626,9 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn memory_query_excludes_workflow_and_staging() {
|
||||
let (dir, layout) = setup();
|
||||
let wf = dir.path().join(".insomnia/workflow/wf.md");
|
||||
let wf = dir.path().join(".yoi/workflow/wf.md");
|
||||
std::fs::write(&wf, "needle in workflow\n").unwrap();
|
||||
let stg = dir.path().join(".insomnia/memory/_staging/abc.json");
|
||||
let stg = dir.path().join(".yoi/memory/_staging/abc.json");
|
||||
std::fs::write(&stg, "needle in staging\n").unwrap();
|
||||
|
||||
let (_, tool) = memory_query_tool(layout, QueryConfig::default())();
|
||||
|
||||
@@ -219,7 +219,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn read_decision_by_slug() {
|
||||
let (dir, layout) = setup();
|
||||
let path = dir.path().join(".insomnia/memory/decisions/foo.md");
|
||||
let path = dir.path().join(".yoi/memory/decisions/foo.md");
|
||||
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
|
||||
std::fs::write(&path, "alpha\nbeta\n").unwrap();
|
||||
|
||||
@@ -234,7 +234,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn read_summary_without_slug() {
|
||||
let (dir, layout) = setup();
|
||||
let path = dir.path().join(".insomnia/memory/summary.md");
|
||||
let path = dir.path().join(".yoi/memory/summary.md");
|
||||
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
|
||||
std::fs::write(&path, "summary body\n").unwrap();
|
||||
|
||||
@@ -274,7 +274,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn knowledge_path_resolution() {
|
||||
let (dir, layout) = setup();
|
||||
let path = dir.path().join(".insomnia/knowledge/policy.md");
|
||||
let path = dir.path().join(".yoi/knowledge/policy.md");
|
||||
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
|
||||
std::fs::write(&path, "k\n").unwrap();
|
||||
|
||||
@@ -287,7 +287,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn read_logs_explicit_use_when_usage_session_is_set() {
|
||||
let (dir, layout) = setup();
|
||||
let path = dir.path().join(".insomnia/memory/decisions/foo.md");
|
||||
let path = dir.path().join(".yoi/memory/decisions/foo.md");
|
||||
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
|
||||
std::fs::write(&path, "alpha\n").unwrap();
|
||||
|
||||
|
||||
@@ -219,7 +219,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn write_creates_summary() {
|
||||
let (dir, layout) = setup();
|
||||
let path = dir.path().join(".insomnia/memory/summary.md");
|
||||
let path = dir.path().join(".yoi/memory/summary.md");
|
||||
let content = format!("---\nupdated_at: {n}\n---\nbody\n", n = now());
|
||||
|
||||
let (meta, tool) = write_tool(layout)();
|
||||
@@ -257,7 +257,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn write_update_existing() {
|
||||
let (dir, layout) = setup();
|
||||
let path = dir.path().join(".insomnia/memory/decisions/foo.md");
|
||||
let path = dir.path().join(".yoi/memory/decisions/foo.md");
|
||||
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
|
||||
let initial = format!(
|
||||
"---\ncreated_at: {n}\nupdated_at: {n}\nsources: []\nstatus: open\n---\nold\n",
|
||||
@@ -290,7 +290,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn write_does_not_persist_on_lint_failure() {
|
||||
let (dir, layout) = setup();
|
||||
let path = dir.path().join(".insomnia/memory/decisions/foo.md");
|
||||
let path = dir.path().join(".yoi/memory/decisions/foo.md");
|
||||
let bad = "no frontmatter at all";
|
||||
let (_, tool) = write_tool(layout)();
|
||||
let inp = serde_json::json!({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Workspace-local usage event log for memory / knowledge / workflow records.
|
||||
//!
|
||||
//! The log is append-only JSONL under the workspace's `.insomnia/` tree. It is
|
||||
//! The log is append-only JSONL under the workspace's `.yoi/` tree. It is
|
||||
//! intentionally evidence-only: aggregation reports explicit context reads and
|
||||
//! resident exposure cost telemetry, but it does not classify records as
|
||||
//! Knowledge candidates or tidy-protected records.
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
//! Workspace-level path layout for the memory subsystem.
|
||||
//!
|
||||
//! `WorkspaceLayout` carries the workspace root (typically the Pod's
|
||||
//! pwd). All insomnia-managed content lives under the conventional
|
||||
//! `<root>/.insomnia/` subdirectory — the same place that holds
|
||||
//! pwd). All yoi-managed content lives under the conventional
|
||||
//! `<root>/.yoi/` subdirectory — the same place that holds
|
||||
//! `profiles.toml`, `prompts/`, workflow, knowledge, and generated
|
||||
//! memory. The trees inside it:
|
||||
//!
|
||||
//! - `<root>/.insomnia/workflow/<slug>.md`
|
||||
//! - `<root>/.insomnia/knowledge/<slug>.md`
|
||||
//! - `<root>/.insomnia/memory/summary.md`
|
||||
//! - `<root>/.insomnia/memory/decisions/<slug>.md`
|
||||
//! - `<root>/.insomnia/memory/requests/<slug>.md`
|
||||
//! - `<root>/.insomnia/memory/_staging/<id>.json`
|
||||
//! - `<root>/.insomnia/memory/_logs/current.log` (append-only audit log)
|
||||
//! - `<root>/.yoi/workflow/<slug>.md`
|
||||
//! - `<root>/.yoi/knowledge/<slug>.md`
|
||||
//! - `<root>/.yoi/memory/summary.md`
|
||||
//! - `<root>/.yoi/memory/decisions/<slug>.md`
|
||||
//! - `<root>/.yoi/memory/requests/<slug>.md`
|
||||
//! - `<root>/.yoi/memory/_staging/<id>.json`
|
||||
//! - `<root>/.yoi/memory/_logs/current.log` (append-only audit log)
|
||||
//!
|
||||
//! `memory/` is reserved for session-derived / generated state;
|
||||
//! Workflows are human-managed and live one level up under
|
||||
//! `.insomnia/workflow/`.
|
||||
//! `.yoi/workflow/`.
|
||||
//!
|
||||
//! Configuring `[memory]` with an empty body is therefore sufficient
|
||||
//! for any workspace that already uses the `.insomnia/` convention; no
|
||||
//! for any workspace that already uses the `.yoi/` convention; no
|
||||
//! `workspace_root` override is needed.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -29,7 +29,7 @@ use crate::error::LintError;
|
||||
#[cfg(test)]
|
||||
use lint_common::RecordLintError;
|
||||
|
||||
const INSOMNIA_DIR: &str = ".insomnia";
|
||||
const YOI_DIR: &str = ".yoi";
|
||||
const MEMORY_DIR: &str = "memory";
|
||||
const KNOWLEDGE_DIR: &str = "knowledge";
|
||||
const WORKFLOW_DIR: &str = "workflow";
|
||||
@@ -100,17 +100,17 @@ impl WorkspaceLayout {
|
||||
&self.root
|
||||
}
|
||||
|
||||
/// `<root>/.insomnia/`. The base of every other memory path.
|
||||
pub fn insomnia_dir(&self) -> PathBuf {
|
||||
self.root.join(INSOMNIA_DIR)
|
||||
/// `<root>/.yoi/`. The base of every other memory path.
|
||||
pub fn yoi_dir(&self) -> PathBuf {
|
||||
self.root.join(YOI_DIR)
|
||||
}
|
||||
|
||||
pub fn memory_dir(&self) -> PathBuf {
|
||||
self.insomnia_dir().join(MEMORY_DIR)
|
||||
self.yoi_dir().join(MEMORY_DIR)
|
||||
}
|
||||
|
||||
pub fn knowledge_dir(&self) -> PathBuf {
|
||||
self.insomnia_dir().join(KNOWLEDGE_DIR)
|
||||
self.yoi_dir().join(KNOWLEDGE_DIR)
|
||||
}
|
||||
|
||||
pub fn summary_path(&self) -> PathBuf {
|
||||
@@ -125,9 +125,9 @@ impl WorkspaceLayout {
|
||||
self.memory_dir().join(REQUESTS_DIR)
|
||||
}
|
||||
|
||||
/// Workflow directory: `<root>/.insomnia/workflow/`.
|
||||
/// Workflow directory: `<root>/.yoi/workflow/`.
|
||||
pub fn workflow_dir(&self) -> PathBuf {
|
||||
self.insomnia_dir().join(WORKFLOW_DIR)
|
||||
self.yoi_dir().join(WORKFLOW_DIR)
|
||||
}
|
||||
|
||||
pub fn staging_dir(&self) -> PathBuf {
|
||||
@@ -149,7 +149,7 @@ impl WorkspaceLayout {
|
||||
/// Tail-friendly latest memory audit log path.
|
||||
///
|
||||
/// Operators can inspect live memory worker and tool events with:
|
||||
/// `tail -f .insomnia/memory/_logs/current.log`.
|
||||
/// `tail -f .yoi/memory/_logs/current.log`.
|
||||
pub fn audit_current_log_path(&self) -> PathBuf {
|
||||
self.audit_logs_dir().join(AUDIT_CURRENT_LOG_FILE)
|
||||
}
|
||||
@@ -171,12 +171,12 @@ impl WorkspaceLayout {
|
||||
}
|
||||
|
||||
/// Classify a path under the memory tree. Returns `None` if the
|
||||
/// path is not under `.insomnia/memory/` or `.insomnia/knowledge/`
|
||||
/// path is not under `.yoi/memory/` or `.yoi/knowledge/`
|
||||
/// of this workspace, or if it lives in
|
||||
/// `_staging/` / `_usage/` / `_logs/` (opaque subsystem-owned trees).
|
||||
///
|
||||
/// On a conventional path that's *almost* a record but malformed
|
||||
/// (e.g. `.insomnia/memory/decisions/Foo.md` with an invalid slug),
|
||||
/// (e.g. `.yoi/memory/decisions/Foo.md` with an invalid slug),
|
||||
/// returns `Err(LintError::Record(InvalidSlug) | InvalidPath)` so the caller
|
||||
/// can surface it as a write violation.
|
||||
pub fn classify(&self, path: &Path) -> Result<Option<ClassifiedPath>, LintError> {
|
||||
@@ -265,7 +265,7 @@ mod tests {
|
||||
#[test]
|
||||
fn classifies_summary() {
|
||||
let cp = layout()
|
||||
.classify(&PathBuf::from("/ws/.insomnia/memory/summary.md"))
|
||||
.classify(&PathBuf::from("/ws/.yoi/memory/summary.md"))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(cp.kind, RecordKind::Summary);
|
||||
@@ -275,7 +275,7 @@ mod tests {
|
||||
#[test]
|
||||
fn classifies_decision_with_slug() {
|
||||
let cp = layout()
|
||||
.classify(&PathBuf::from("/ws/.insomnia/memory/decisions/foo-bar.md"))
|
||||
.classify(&PathBuf::from("/ws/.yoi/memory/decisions/foo-bar.md"))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(cp.kind, RecordKind::Decision);
|
||||
@@ -285,7 +285,7 @@ mod tests {
|
||||
#[test]
|
||||
fn classifies_knowledge() {
|
||||
let cp = layout()
|
||||
.classify(&PathBuf::from("/ws/.insomnia/knowledge/x.md"))
|
||||
.classify(&PathBuf::from("/ws/.yoi/knowledge/x.md"))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(cp.kind, RecordKind::Knowledge);
|
||||
@@ -294,7 +294,7 @@ mod tests {
|
||||
#[test]
|
||||
fn workflow_under_memory_is_invalid_path() {
|
||||
let err = layout()
|
||||
.classify(&PathBuf::from("/ws/.insomnia/memory/workflow/wf.md"))
|
||||
.classify(&PathBuf::from("/ws/.yoi/memory/workflow/wf.md"))
|
||||
.unwrap_err();
|
||||
assert!(matches!(err, LintError::InvalidPath(_)));
|
||||
}
|
||||
@@ -303,7 +303,7 @@ mod tests {
|
||||
fn staging_returns_none() {
|
||||
assert!(
|
||||
layout()
|
||||
.classify(&PathBuf::from("/ws/.insomnia/memory/_staging/abc.json"))
|
||||
.classify(&PathBuf::from("/ws/.yoi/memory/_staging/abc.json"))
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
@@ -312,7 +312,7 @@ mod tests {
|
||||
#[test]
|
||||
fn usage_tree_is_opaque_to_classifier() {
|
||||
let cp = layout()
|
||||
.classify(&PathBuf::from("/ws/.insomnia/memory/_usage/events.jsonl"))
|
||||
.classify(&PathBuf::from("/ws/.yoi/memory/_usage/events.jsonl"))
|
||||
.unwrap();
|
||||
assert!(cp.is_none());
|
||||
}
|
||||
@@ -320,7 +320,7 @@ mod tests {
|
||||
#[test]
|
||||
fn logs_tree_is_opaque_to_classifier() {
|
||||
let cp = layout()
|
||||
.classify(&PathBuf::from("/ws/.insomnia/memory/_logs/current.log"))
|
||||
.classify(&PathBuf::from("/ws/.yoi/memory/_logs/current.log"))
|
||||
.unwrap();
|
||||
assert!(cp.is_none());
|
||||
}
|
||||
@@ -344,7 +344,7 @@ mod tests {
|
||||
#[test]
|
||||
fn invalid_slug_rejected() {
|
||||
let err = layout()
|
||||
.classify(&PathBuf::from("/ws/.insomnia/memory/decisions/Foo.md"))
|
||||
.classify(&PathBuf::from("/ws/.yoi/memory/decisions/Foo.md"))
|
||||
.unwrap_err();
|
||||
assert!(matches!(
|
||||
err,
|
||||
@@ -355,7 +355,7 @@ mod tests {
|
||||
#[test]
|
||||
fn nested_under_record_dir_rejected() {
|
||||
let err = layout()
|
||||
.classify(&PathBuf::from("/ws/.insomnia/memory/decisions/sub/foo.md"))
|
||||
.classify(&PathBuf::from("/ws/.yoi/memory/decisions/sub/foo.md"))
|
||||
.unwrap_err();
|
||||
assert!(matches!(err, LintError::InvalidPath(_)));
|
||||
}
|
||||
@@ -363,7 +363,7 @@ mod tests {
|
||||
#[test]
|
||||
fn unknown_top_level_dir_rejected() {
|
||||
let err = layout()
|
||||
.classify(&PathBuf::from("/ws/.insomnia/memory/something/foo.md"))
|
||||
.classify(&PathBuf::from("/ws/.yoi/memory/something/foo.md"))
|
||||
.unwrap_err();
|
||||
assert!(matches!(err, LintError::InvalidPath(_)));
|
||||
}
|
||||
|
||||
@@ -71,14 +71,14 @@ impl LockFile {
|
||||
|
||||
/// Default on-disk path: `<runtime_dir>/pods.json` resolved via
|
||||
/// [`manifest::paths::pod_registry_path`]. Tests should point this
|
||||
/// elsewhere by setting `INSOMNIA_HOME` or `INSOMNIA_RUNTIME_DIR` to a
|
||||
/// elsewhere by setting `YOI_HOME` or `YOI_RUNTIME_DIR` to a
|
||||
/// tempdir.
|
||||
pub fn default_registry_path() -> io::Result<PathBuf> {
|
||||
paths::pod_registry_path().ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
"could not resolve pods.json path (no INSOMNIA_HOME / \
|
||||
INSOMNIA_RUNTIME_DIR / XDG_RUNTIME_DIR / HOME)",
|
||||
"could not resolve pods.json path (no YOI_HOME / \
|
||||
YOI_RUNTIME_DIR / XDG_RUNTIME_DIR / HOME)",
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -190,7 +190,7 @@ mod tests {
|
||||
fn open_creates_file_with_owner_only_permissions() {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let dir = TempDir::new().unwrap();
|
||||
let parent = dir.path().join("insomnia");
|
||||
let parent = dir.path().join("yoi");
|
||||
let path = parent.join("pods.json");
|
||||
let _guard = LockFileGuard::open(&path).unwrap();
|
||||
let file_mode = std::fs::metadata(&path).unwrap().permissions().mode() & 0o777;
|
||||
|
||||
@@ -20,8 +20,8 @@ pub(crate) fn sid() -> SegmentId {
|
||||
/// parallel test's `default_registry_path()` lookup.
|
||||
pub(crate) static ENV_LOCK: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));
|
||||
|
||||
/// Sandbox `INSOMNIA_RUNTIME_DIR` to a tempdir for the duration of
|
||||
/// a test; restore the previous value (and any `INSOMNIA_HOME` /
|
||||
/// Sandbox `YOI_RUNTIME_DIR` to a tempdir for the duration of
|
||||
/// a test; restore the previous value (and any `YOI_HOME` /
|
||||
/// `XDG_RUNTIME_DIR` that would otherwise outrank it) on drop.
|
||||
pub(crate) struct RuntimeDirSandbox {
|
||||
prev_runtime: Option<String>,
|
||||
@@ -33,16 +33,16 @@ pub(crate) struct RuntimeDirSandbox {
|
||||
impl RuntimeDirSandbox {
|
||||
pub(crate) fn new(dir: &Path) -> Self {
|
||||
let guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let prev_runtime = std::env::var("INSOMNIA_RUNTIME_DIR").ok();
|
||||
let prev_home = std::env::var("INSOMNIA_HOME").ok();
|
||||
let prev_runtime = std::env::var("YOI_RUNTIME_DIR").ok();
|
||||
let prev_home = std::env::var("YOI_HOME").ok();
|
||||
let prev_xdg = std::env::var("XDG_RUNTIME_DIR").ok();
|
||||
// SAFETY: ENV_LOCK serialises env writes across this test
|
||||
// module; other modules that touch env vars rely on their
|
||||
// own lock or `serial_test`.
|
||||
unsafe {
|
||||
std::env::remove_var("INSOMNIA_HOME");
|
||||
std::env::remove_var("YOI_HOME");
|
||||
std::env::remove_var("XDG_RUNTIME_DIR");
|
||||
std::env::set_var("INSOMNIA_RUNTIME_DIR", dir);
|
||||
std::env::set_var("YOI_RUNTIME_DIR", dir);
|
||||
}
|
||||
Self {
|
||||
prev_runtime,
|
||||
@@ -57,12 +57,12 @@ impl Drop for RuntimeDirSandbox {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
match &self.prev_runtime {
|
||||
Some(v) => std::env::set_var("INSOMNIA_RUNTIME_DIR", v),
|
||||
None => std::env::remove_var("INSOMNIA_RUNTIME_DIR"),
|
||||
Some(v) => std::env::set_var("YOI_RUNTIME_DIR", v),
|
||||
None => std::env::remove_var("YOI_RUNTIME_DIR"),
|
||||
}
|
||||
match &self.prev_home {
|
||||
Some(v) => std::env::set_var("INSOMNIA_HOME", v),
|
||||
None => std::env::remove_var("INSOMNIA_HOME"),
|
||||
Some(v) => std::env::set_var("YOI_HOME", v),
|
||||
None => std::env::remove_var("YOI_HOME"),
|
||||
}
|
||||
match &self.prev_xdg {
|
||||
Some(v) => std::env::set_var("XDG_RUNTIME_DIR", v),
|
||||
|
||||
@@ -18,5 +18,5 @@
|
||||
|
||||
### ランタイム
|
||||
|
||||
- `RuntimeDir` — `$XDG_RUNTIME_DIR/insomnia/{pod_name}/` 配下のランタイムディレクトリ管理(ステータス・履歴のアトミック書き込み)
|
||||
- `RuntimeDir` — `$XDG_RUNTIME_DIR/yoi/{pod_name}/` 配下のランタイムディレクトリ管理(ステータス・履歴のアトミック書き込み)
|
||||
- `SocketServer` — Pod Protocol 用 Unix ソケットサーバー
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Minimal example: Pod running a single prompt with persistence.
|
||||
//!
|
||||
//! Demonstrates the core insomnia abstraction — a TOML manifest drives
|
||||
//! Demonstrates the core yoi abstraction — a TOML manifest drives
|
||||
//! provider selection, model config, and system prompt, while FsStore
|
||||
//! persists the session to disk automatically.
|
||||
//!
|
||||
|
||||
@@ -822,7 +822,7 @@ mod tests {
|
||||
let runtime_base = root.path().join("runtime");
|
||||
std::fs::create_dir_all(&runtime_base).unwrap();
|
||||
unsafe {
|
||||
std::env::set_var("INSOMNIA_RUNTIME_DIR", &runtime_base);
|
||||
std::env::set_var("YOI_RUNTIME_DIR", &runtime_base);
|
||||
}
|
||||
|
||||
let store = FsPodStore::new(&store_dir).unwrap();
|
||||
|
||||
@@ -191,7 +191,7 @@ fn load_single_manifest(
|
||||
}
|
||||
|
||||
pub async fn run_cli() -> ExitCode {
|
||||
run_cli_from("insomnia pod", std::env::args_os().skip(1)).await
|
||||
run_cli_from("yoi pod", std::env::args_os().skip(1)).await
|
||||
}
|
||||
|
||||
pub async fn run_cli_from<I, T>(bin_name: &'static str, args: I) -> ExitCode
|
||||
@@ -246,7 +246,7 @@ async fn run_cli_inner(cli: Cli) -> ExitCode {
|
||||
};
|
||||
|
||||
// Initialize persistent store. `paths::sessions_dir()` only
|
||||
// returns None when none of INSOMNIA_HOME / INSOMNIA_DATA_DIR /
|
||||
// returns None when none of YOI_HOME / YOI_DATA_DIR /
|
||||
// HOME is set — surface that as a hard error to match the
|
||||
// runtime-dir resolution below, rather than silently writing to a
|
||||
// relative path under cwd.
|
||||
@@ -257,7 +257,7 @@ async fn run_cli_inner(cli: Cli) -> ExitCode {
|
||||
None => {
|
||||
eprintln!(
|
||||
"error: could not resolve sessions directory \
|
||||
(set --store, INSOMNIA_HOME, INSOMNIA_DATA_DIR, or HOME)"
|
||||
(set --store, YOI_HOME, YOI_DATA_DIR, or HOME)"
|
||||
);
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
@@ -375,7 +375,7 @@ async fn run_cli_inner(cli: Cli) -> ExitCode {
|
||||
None => {
|
||||
eprintln!(
|
||||
"error: could not resolve runtime directory \
|
||||
(set INSOMNIA_HOME, INSOMNIA_RUNTIME_DIR, XDG_RUNTIME_DIR, or HOME)"
|
||||
(set YOI_HOME, YOI_RUNTIME_DIR, XDG_RUNTIME_DIR, or HOME)"
|
||||
);
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
@@ -393,7 +393,7 @@ async fn run_cli_inner(cli: Cli) -> ExitCode {
|
||||
// (e.g. the TUI's interactive `spawn` flow). Tab-separated so a
|
||||
// pod name with spaces still parses cleanly. Emit before the
|
||||
// human line so a stderr-watching parent sees it first.
|
||||
eprintln!("INSOMNIA-READY\t{pod_name}\t{}", socket_path.display());
|
||||
eprintln!("YOI-READY\t{pod_name}\t{}", socket_path.display());
|
||||
eprintln!("pod: {pod_name} listening on {:?}", socket_path);
|
||||
|
||||
tokio::select! {
|
||||
@@ -443,36 +443,30 @@ permission = "write"
|
||||
|
||||
#[test]
|
||||
fn user_manifest_flag_is_not_accepted() {
|
||||
let err =
|
||||
Cli::try_parse_from(["insomnia pod", "--user-manifest", "manifest.toml"]).unwrap_err();
|
||||
let err = Cli::try_parse_from(["yoi pod", "--user-manifest", "manifest.toml"]).unwrap_err();
|
||||
assert_eq!(err.kind(), clap::error::ErrorKind::UnknownArgument);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subcommand_help_uses_insomnia_pod_invocation() {
|
||||
let err = parse_cli_from("insomnia pod", ["--help"]).unwrap_err();
|
||||
fn subcommand_help_uses_yoi_pod_invocation() {
|
||||
let err = parse_cli_from("yoi pod", ["--help"]).unwrap_err();
|
||||
assert_eq!(err.kind(), clap::error::ErrorKind::DisplayHelp);
|
||||
let help = err.to_string();
|
||||
assert!(help.contains("Usage: insomnia pod"), "{help}");
|
||||
assert!(help.contains("Usage: yoi pod"), "{help}");
|
||||
assert!(help.contains("--pod <NAME>"), "{help}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manifest_conflicts_with_project() {
|
||||
let project_err = Cli::try_parse_from([
|
||||
"insomnia pod",
|
||||
"--manifest",
|
||||
"manifest.toml",
|
||||
"--project",
|
||||
".",
|
||||
])
|
||||
.unwrap_err();
|
||||
let project_err =
|
||||
Cli::try_parse_from(["yoi pod", "--manifest", "manifest.toml", "--project", "."])
|
||||
.unwrap_err();
|
||||
assert_eq!(project_err.kind(), clap::error::ErrorKind::ArgumentConflict);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlay_flag_is_not_accepted() {
|
||||
let err = Cli::try_parse_from(["insomnia pod", "--overlay", "pod.name = 'x'"]).unwrap_err();
|
||||
let err = Cli::try_parse_from(["yoi pod", "--overlay", "pod.name = 'x'"]).unwrap_err();
|
||||
assert_eq!(err.kind(), clap::error::ErrorKind::UnknownArgument);
|
||||
}
|
||||
|
||||
@@ -481,8 +475,8 @@ permission = "write"
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let manifest = tmp.path().join("manifest.toml");
|
||||
write(&manifest, &manifest_toml("single", tmp.path()));
|
||||
let cli = Cli::try_parse_from(["insomnia pod", "--manifest", manifest.to_str().unwrap()])
|
||||
.unwrap();
|
||||
let cli =
|
||||
Cli::try_parse_from(["yoi pod", "--manifest", manifest.to_str().unwrap()]).unwrap();
|
||||
|
||||
let (manifest, loader) = resolve_manifest(&cli).unwrap();
|
||||
|
||||
@@ -496,7 +490,7 @@ permission = "write"
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let profile = tmp.path().join("profile.lua");
|
||||
let cli = Cli::try_parse_from([
|
||||
"insomnia pod",
|
||||
"yoi pod",
|
||||
"--profile",
|
||||
profile.to_str().unwrap(),
|
||||
"--profile-pod-name",
|
||||
@@ -529,7 +523,7 @@ permission = "write"
|
||||
fn profile_accepts_source_qualified_discovered_name() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let cli = Cli::try_parse_from([
|
||||
"insomnia pod",
|
||||
"yoi pod",
|
||||
"--profile",
|
||||
"project:coder",
|
||||
"--profile-pod-name",
|
||||
@@ -564,7 +558,7 @@ permission = "write"
|
||||
#[test]
|
||||
fn normal_startup_uses_default_profile() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let cli = Cli::try_parse_from(["insomnia pod"]).unwrap();
|
||||
let cli = Cli::try_parse_from(["yoi pod"]).unwrap();
|
||||
let mut called = false;
|
||||
|
||||
let (manifest, _loader) =
|
||||
@@ -585,7 +579,7 @@ permission = "write"
|
||||
|
||||
#[test]
|
||||
fn project_flag_no_longer_enables_ambient_manifest_cascade() {
|
||||
let cli = Cli::try_parse_from(["insomnia pod", "--project", "."]).unwrap();
|
||||
let cli = Cli::try_parse_from(["yoi pod", "--project", "."]).unwrap();
|
||||
let err = resolve_manifest_with_profile_loader(&cli, |_, _| {
|
||||
panic!("default profile loader must not run when deprecated --project is present")
|
||||
})
|
||||
@@ -597,7 +591,7 @@ permission = "write"
|
||||
fn pod_flag_conflicts_with_session() {
|
||||
let segment_id = session_store::new_segment_id();
|
||||
let segment_id = segment_id.to_string();
|
||||
let err = Cli::try_parse_from(["insomnia pod", "--pod", "agent", "--session", &segment_id])
|
||||
let err = Cli::try_parse_from(["yoi pod", "--pod", "agent", "--session", &segment_id])
|
||||
.unwrap_err();
|
||||
assert_eq!(err.kind(), clap::error::ErrorKind::ArgumentConflict);
|
||||
}
|
||||
@@ -608,7 +602,7 @@ permission = "write"
|
||||
let manifest = tmp.path().join("manifest.toml");
|
||||
write(&manifest, &manifest_toml("from-file", tmp.path()));
|
||||
let cli = Cli::try_parse_from([
|
||||
"insomnia pod",
|
||||
"yoi pod",
|
||||
"--manifest",
|
||||
manifest.to_str().unwrap(),
|
||||
"--pod",
|
||||
@@ -640,7 +634,7 @@ permission = "write"
|
||||
"#,
|
||||
);
|
||||
let cli = Cli::try_parse_from([
|
||||
"insomnia pod",
|
||||
"yoi pod",
|
||||
"--manifest",
|
||||
manifest.to_str().unwrap(),
|
||||
"--pod",
|
||||
@@ -657,7 +651,7 @@ permission = "write"
|
||||
#[test]
|
||||
fn pod_flag_with_no_manifest_creates_from_default_profile_with_typed_name() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let cli = Cli::try_parse_from(["insomnia pod", "--pod", "agent"]).unwrap();
|
||||
let cli = Cli::try_parse_from(["yoi pod", "--pod", "agent"]).unwrap();
|
||||
let mut called = false;
|
||||
|
||||
let (manifest, _loader) =
|
||||
@@ -683,15 +677,9 @@ permission = "write"
|
||||
fn profile_conflicts_with_manifest_and_restore_modes() {
|
||||
let segment_id = session_store::new_segment_id().to_string();
|
||||
for args in [
|
||||
vec!["insomnia pod", "--profile", "p.lua", "--manifest", "m.toml"],
|
||||
vec!["insomnia pod", "--profile", "p.lua", "--pod", "agent"],
|
||||
vec![
|
||||
"insomnia pod",
|
||||
"--profile",
|
||||
"p.lua",
|
||||
"--session",
|
||||
&segment_id,
|
||||
],
|
||||
vec!["yoi pod", "--profile", "p.lua", "--manifest", "m.toml"],
|
||||
vec!["yoi pod", "--profile", "p.lua", "--pod", "agent"],
|
||||
vec!["yoi pod", "--profile", "p.lua", "--session", &segment_id],
|
||||
] {
|
||||
let err = Cli::try_parse_from(args).unwrap_err();
|
||||
assert_eq!(err.kind(), clap::error::ErrorKind::ArgumentConflict);
|
||||
@@ -700,14 +688,14 @@ permission = "write"
|
||||
|
||||
#[test]
|
||||
fn profile_pod_name_requires_profile() {
|
||||
let err = Cli::try_parse_from(["insomnia pod", "--profile-pod-name", "agent"]).unwrap_err();
|
||||
let err = Cli::try_parse_from(["yoi pod", "--profile-pod-name", "agent"]).unwrap_err();
|
||||
assert_eq!(err.kind(), clap::error::ErrorKind::MissingRequiredArgument);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn profile_pod_name_is_not_restore_pod_flag() {
|
||||
let cli = Cli::try_parse_from([
|
||||
"insomnia pod",
|
||||
"yoi pod",
|
||||
"--profile",
|
||||
"p.lua",
|
||||
"--profile-pod-name",
|
||||
@@ -724,13 +712,9 @@ permission = "write"
|
||||
let single_manifest = tmp.path().join("single.toml");
|
||||
write(&single_manifest, &manifest_toml("single-file", tmp.path()));
|
||||
std::fs::create_dir_all(tmp.path().join("prompts")).unwrap();
|
||||
std::fs::create_dir_all(tmp.path().join(".insomnia").join("prompts")).unwrap();
|
||||
let cli = Cli::try_parse_from([
|
||||
"insomnia pod",
|
||||
"--manifest",
|
||||
single_manifest.to_str().unwrap(),
|
||||
])
|
||||
.unwrap();
|
||||
std::fs::create_dir_all(tmp.path().join(".yoi").join("prompts")).unwrap();
|
||||
let cli = Cli::try_parse_from(["yoi pod", "--manifest", single_manifest.to_str().unwrap()])
|
||||
.unwrap();
|
||||
|
||||
let (manifest, loader) = resolve_manifest(&cli).unwrap();
|
||||
|
||||
|
||||
@@ -333,7 +333,7 @@ pub struct Pod<C: LlmClient, St: Store> {
|
||||
/// [`Self::from_manifest`], or defaults to the builtin pack when a
|
||||
/// Pod is constructed through lower-level paths that have no loader.
|
||||
prompts: Arc<PromptCatalog>,
|
||||
/// Registry loaded from `<workspace>/.insomnia/workflow/*.md` when
|
||||
/// Registry loaded from `<workspace>/.yoi/workflow/*.md` when
|
||||
/// memory is enabled. Missing memory config keeps this empty.
|
||||
workflow_registry: workflow_crate::WorkflowRegistry,
|
||||
/// Memory workspace layout used by the workflow resolver to load required
|
||||
@@ -5337,21 +5337,21 @@ mod build_summary_prompt_tests {
|
||||
let pwd = dir.path().join("workspace");
|
||||
std::fs::create_dir_all(&pwd).unwrap();
|
||||
if let Some(doc) = summary_doc {
|
||||
std::fs::create_dir_all(pwd.join(".insomnia/memory")).unwrap();
|
||||
std::fs::write(pwd.join(".insomnia/memory/summary.md"), doc).unwrap();
|
||||
std::fs::create_dir_all(pwd.join(".yoi/memory")).unwrap();
|
||||
std::fs::write(pwd.join(".yoi/memory/summary.md"), doc).unwrap();
|
||||
}
|
||||
if include_knowledge {
|
||||
std::fs::create_dir_all(pwd.join(".insomnia/knowledge")).unwrap();
|
||||
std::fs::create_dir_all(pwd.join(".yoi/knowledge")).unwrap();
|
||||
std::fs::write(
|
||||
pwd.join(".insomnia/knowledge/resident-policy.md"),
|
||||
pwd.join(".yoi/knowledge/resident-policy.md"),
|
||||
knowledge_doc("knowledge resident desc"),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
if include_workflow {
|
||||
std::fs::create_dir_all(pwd.join(".insomnia/workflow")).unwrap();
|
||||
std::fs::create_dir_all(pwd.join(".yoi/workflow")).unwrap();
|
||||
std::fs::write(
|
||||
pwd.join(".insomnia/workflow/resident-flow.md"),
|
||||
pwd.join(".yoi/workflow/resident-flow.md"),
|
||||
workflow_doc("workflow resident desc"),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -5379,7 +5379,7 @@ mod build_summary_prompt_tests {
|
||||
pod.set_resident_workflow_injection(gates.workflows);
|
||||
}
|
||||
let template = SystemPromptTemplate::parse(
|
||||
"$insomnia/default",
|
||||
"$yoi/default",
|
||||
crate::prompt::loader::PromptLoader::builtins_only(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
//! binary. Must cover every [`PodPrompt`] variant (build-time check).
|
||||
//! 2. **user** — `<config_dir>/prompts.toml`, when a caller supplies it.
|
||||
//! Optional.
|
||||
//! 3. **workspace** — `<project>/.insomnia/prompts.toml`, when a caller
|
||||
//! 3. **workspace** — `<project>/.yoi/prompts.toml`, when a caller
|
||||
//! supplies it. Optional.
|
||||
//! 4. **manifest pack** — `manifest.pod.prompt_pack`, an explicit path
|
||||
//! per-Pod. Optional.
|
||||
@@ -258,7 +258,7 @@ struct PackFile {
|
||||
/// Owns a `minijinja::Environment` with one template registered per
|
||||
/// [`PodPrompt`] key (after the 4-layer merge). Includes inside templates
|
||||
/// are resolved via a provided [`PromptLoader`], so values can pull from
|
||||
/// `$insomnia` / `$user` / `$workspace`.
|
||||
/// `$yoi` / `$user` / `$workspace`.
|
||||
pub struct PromptCatalog {
|
||||
env: Environment<'static>,
|
||||
}
|
||||
@@ -271,7 +271,7 @@ impl std::fmt::Debug for PromptCatalog {
|
||||
|
||||
impl PromptCatalog {
|
||||
/// Builtin-only catalog. All `{% include %}` references must resolve
|
||||
/// through `$insomnia` (user/workspace prefixes are unavailable).
|
||||
/// through `$yoi` (user/workspace prefixes are unavailable).
|
||||
pub fn builtins_only() -> Result<Arc<Self>, CatalogError> {
|
||||
Self::load(&PromptLoader::builtins_only(), None)
|
||||
}
|
||||
@@ -707,7 +707,7 @@ interrupt_system_note = "[FROM-MANIFEST-PACK]"
|
||||
#[test]
|
||||
fn value_can_pull_long_text_via_include() {
|
||||
// A runtime pack that overrides `compact_system` with an
|
||||
// `{% include %}` into the same `$insomnia` namespace — exercises
|
||||
// `{% include %}` into the same `$yoi` namespace — exercises
|
||||
// the template resolver path through all four layers.
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let pack = tmp.path().join("p.toml");
|
||||
@@ -715,7 +715,7 @@ interrupt_system_note = "[FROM-MANIFEST-PACK]"
|
||||
&pack,
|
||||
r#"
|
||||
[prompt]
|
||||
compact_system = "PREFIX\n{% include \"$insomnia/internal/compact_system\" %}"
|
||||
compact_system = "PREFIX\n{% include \"$yoi/internal/compact_system\" %}"
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
//!
|
||||
//! | prefix | location |
|
||||
//! |--------------|---------------------------------------------------------|
|
||||
//! | `$insomnia` | builtin, baked into the binary via `include_dir!` |
|
||||
//! | `$yoi` | builtin, baked into the binary via `include_dir!` |
|
||||
//! | `$user` | `<config_dir>/prompts/` (resolved by `manifest::paths`) |
|
||||
//! | `$workspace` | `<project>/.insomnia/prompts/` |
|
||||
//! | `$workspace` | `<project>/.yoi/prompts/` |
|
||||
//!
|
||||
//! A reference is `$<prefix>/<path>` where `<path>` is a `/`-separated
|
||||
//! name without the `.md` extension (e.g. `$insomnia/common/header`).
|
||||
//! name without the `.md` extension (e.g. `$yoi/common/header`).
|
||||
//! Unqualified names (no `$prefix/` at the front) are resolved relative
|
||||
//! to an optional current reference — typically the file that issued
|
||||
//! the `{% include %}` — so a prompt library can be authored as a
|
||||
@@ -25,13 +25,13 @@ use thiserror::Error;
|
||||
|
||||
static BUILTIN_PROMPTS: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/../../resources/prompts");
|
||||
|
||||
const PREFIX_INSOMNIA: &str = "$insomnia";
|
||||
const PREFIX_YOI: &str = "$yoi";
|
||||
const PREFIX_USER: &str = "$user";
|
||||
const PREFIX_WORKSPACE: &str = "$workspace";
|
||||
|
||||
/// Prefix-resolved reference to a prompt asset. Produced by
|
||||
/// [`PromptLoader::parse_ref`] from a user-supplied string such as
|
||||
/// `"$insomnia/default"`.
|
||||
/// `"$yoi/default"`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct PromptRef {
|
||||
prefix: Prefix,
|
||||
@@ -42,7 +42,7 @@ pub struct PromptRef {
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Prefix {
|
||||
Insomnia,
|
||||
Yoi,
|
||||
User,
|
||||
Workspace,
|
||||
}
|
||||
@@ -50,7 +50,7 @@ enum Prefix {
|
||||
impl Prefix {
|
||||
fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Insomnia => PREFIX_INSOMNIA,
|
||||
Self::Yoi => PREFIX_YOI,
|
||||
Self::User => PREFIX_USER,
|
||||
Self::Workspace => PREFIX_WORKSPACE,
|
||||
}
|
||||
@@ -116,7 +116,7 @@ pub struct PromptLoader {
|
||||
}
|
||||
|
||||
impl PromptLoader {
|
||||
/// Loader with only the builtin `$insomnia` library available.
|
||||
/// Loader with only the builtin `$yoi` library available.
|
||||
/// `$user` / `$workspace` references fail with
|
||||
/// [`LoaderError::PrefixNotConfigured`].
|
||||
pub fn builtins_only() -> Self {
|
||||
@@ -221,7 +221,7 @@ impl PromptLoader {
|
||||
/// when the prefix is not configured or the file does not exist.
|
||||
pub fn load(&self, reference: &PromptRef) -> Result<String, LoaderError> {
|
||||
match reference.prefix {
|
||||
Prefix::Insomnia => load_from_include_dir(&BUILTIN_PROMPTS, reference),
|
||||
Prefix::Yoi => load_from_include_dir(&BUILTIN_PROMPTS, reference),
|
||||
Prefix::User => match self.user_dir.as_deref() {
|
||||
Some(dir) => load_from_dir(dir, reference),
|
||||
None => Err(LoaderError::PrefixNotConfigured {
|
||||
@@ -252,7 +252,7 @@ impl PromptLoader {
|
||||
|
||||
fn parse_prefix(raw: &str, prefix_name: &str) -> Result<Prefix, LoaderError> {
|
||||
match prefix_name {
|
||||
"insomnia" => Ok(Prefix::Insomnia),
|
||||
"yoi" => Ok(Prefix::Yoi),
|
||||
"user" => Ok(Prefix::User),
|
||||
"workspace" => Ok(Prefix::Workspace),
|
||||
_ => Err(LoaderError::UnknownPrefix {
|
||||
@@ -311,15 +311,15 @@ mod tests {
|
||||
#[test]
|
||||
fn builtin_default_resolves() {
|
||||
let loader = PromptLoader::builtins_only();
|
||||
let (r, source) = loader.resolve("$insomnia/default", None).unwrap();
|
||||
assert_eq!(r.to_qualified_string(), "$insomnia/default");
|
||||
let (r, source) = loader.resolve("$yoi/default", None).unwrap();
|
||||
assert_eq!(r.to_qualified_string(), "$yoi/default");
|
||||
assert!(!source.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builtin_subdirectory_lookup() {
|
||||
let loader = PromptLoader::builtins_only();
|
||||
let (_, source) = loader.resolve("$insomnia/common/tool-usage", None).unwrap();
|
||||
let (_, source) = loader.resolve("$yoi/common/tool-usage", None).unwrap();
|
||||
assert!(source.contains("tool"));
|
||||
}
|
||||
|
||||
@@ -346,9 +346,7 @@ mod tests {
|
||||
#[test]
|
||||
fn missing_file_is_hard_error() {
|
||||
let loader = PromptLoader::builtins_only();
|
||||
let err = loader
|
||||
.resolve("$insomnia/definitely-missing", None)
|
||||
.unwrap_err();
|
||||
let err = loader.resolve("$yoi/definitely-missing", None).unwrap_err();
|
||||
assert!(matches!(err, LoaderError::NotFound { .. }));
|
||||
}
|
||||
|
||||
@@ -379,20 +377,18 @@ mod tests {
|
||||
#[test]
|
||||
fn unqualified_ref_resolves_relative_to_current() {
|
||||
let loader = PromptLoader::builtins_only();
|
||||
let current = loader
|
||||
.parse_ref("$insomnia/common/tool-usage", None)
|
||||
.unwrap();
|
||||
let current = loader.parse_ref("$yoi/common/tool-usage", None).unwrap();
|
||||
// Sibling lookup under the same prefix and directory.
|
||||
let sibling = loader.parse_ref("workspace", Some(¤t)).unwrap();
|
||||
assert_eq!(sibling.to_qualified_string(), "$insomnia/common/workspace");
|
||||
assert_eq!(sibling.to_qualified_string(), "$yoi/common/workspace");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unqualified_ref_from_root_file_has_empty_dir() {
|
||||
let loader = PromptLoader::builtins_only();
|
||||
let current = loader.parse_ref("$insomnia/default", None).unwrap();
|
||||
let current = loader.parse_ref("$yoi/default", None).unwrap();
|
||||
let sibling = loader.parse_ref("other", Some(¤t)).unwrap();
|
||||
assert_eq!(sibling.to_qualified_string(), "$insomnia/other");
|
||||
assert_eq!(sibling.to_qualified_string(), "$yoi/other");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -402,8 +398,8 @@ mod tests {
|
||||
std::fs::write(user_dir.join("custom.md"), "user-body").unwrap();
|
||||
let loader = PromptLoader::new(Some(user_dir), None);
|
||||
|
||||
let current = loader.parse_ref("$insomnia/default", None).unwrap();
|
||||
// Even with an $insomnia-rooted current, an explicit $user
|
||||
let current = loader.parse_ref("$yoi/default", None).unwrap();
|
||||
// Even with an $yoi-rooted current, an explicit $user
|
||||
// prefix must win.
|
||||
let (reference, source) = loader.resolve("$user/custom", Some(¤t)).unwrap();
|
||||
assert_eq!(reference.to_qualified_string(), "$user/custom");
|
||||
@@ -413,7 +409,7 @@ mod tests {
|
||||
#[test]
|
||||
fn traversal_segments_rejected() {
|
||||
let loader = PromptLoader::builtins_only();
|
||||
let err = loader.resolve("$insomnia/../etc/passwd", None).unwrap_err();
|
||||
let err = loader.resolve("$yoi/../etc/passwd", None).unwrap_err();
|
||||
assert!(matches!(err, LoaderError::InvalidRef { .. }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ pub struct SystemPromptContext<'a> {
|
||||
/// Not visible from the template; consumed by the trailing-section
|
||||
/// formatter in [`SystemPromptTemplate::render`].
|
||||
pub agents_md: Option<String>,
|
||||
/// The body of `<workspace>/.insomnia/memory/summary.md`, with
|
||||
/// The body of `<workspace>/.yoi/memory/summary.md`, with
|
||||
/// frontmatter stripped. `None` disables the resident summary section;
|
||||
/// empty strings are ignored by the trailing-section formatter.
|
||||
pub resident_summary: Option<&'a str>,
|
||||
@@ -164,7 +164,7 @@ pub struct SystemPromptContext<'a> {
|
||||
/// section entirely (memory disabled, or a consolidation worker that opts
|
||||
/// out); `Some(&[])` also yields no section.
|
||||
pub resident_knowledge: Option<&'a [ResidentKnowledgeEntry]>,
|
||||
/// Resident workflow descriptions from `<workspace>/.insomnia/workflow/*`
|
||||
/// Resident workflow descriptions from `<workspace>/.yoi/workflow/*`
|
||||
/// whose frontmatter has `model_invokation: true`. `None` disables the
|
||||
/// section; consolidation workers opt out together with resident Knowledge.
|
||||
pub resident_workflows: Option<&'a [ResidentWorkflowEntry]>,
|
||||
@@ -557,9 +557,9 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn instruction_default_resolves_to_insomnia_default() {
|
||||
fn instruction_default_resolves_to_yoi_default() {
|
||||
let loader = PromptLoader::builtins_only();
|
||||
let tmpl = SystemPromptTemplate::parse("$insomnia/default", loader).unwrap();
|
||||
let tmpl = SystemPromptTemplate::parse("$yoi/default", loader).unwrap();
|
||||
let dir = TempDir::new().unwrap();
|
||||
let scope = build_scope(dir.path());
|
||||
let rendered = tmpl
|
||||
@@ -582,7 +582,7 @@ mod tests {
|
||||
#[test]
|
||||
fn instruction_default_omits_memory_guidance_without_memory_tools() {
|
||||
let loader = PromptLoader::builtins_only();
|
||||
let tmpl = SystemPromptTemplate::parse("$insomnia/default", loader).unwrap();
|
||||
let tmpl = SystemPromptTemplate::parse("$yoi/default", loader).unwrap();
|
||||
let dir = TempDir::new().unwrap();
|
||||
let scope = build_scope(dir.path());
|
||||
let rendered = tmpl
|
||||
@@ -608,7 +608,7 @@ mod tests {
|
||||
#[test]
|
||||
fn memory_guidance_names_only_available_memory_tools() {
|
||||
let loader = PromptLoader::builtins_only();
|
||||
let tmpl = SystemPromptTemplate::parse("$insomnia/default", loader).unwrap();
|
||||
let tmpl = SystemPromptTemplate::parse("$yoi/default", loader).unwrap();
|
||||
let dir = TempDir::new().unwrap();
|
||||
let scope = build_scope(dir.path());
|
||||
let rendered = tmpl
|
||||
@@ -632,7 +632,7 @@ mod tests {
|
||||
#[test]
|
||||
fn pod_orchestration_guidance_is_included_for_pod_management_tools() {
|
||||
let loader = PromptLoader::builtins_only();
|
||||
let tmpl = SystemPromptTemplate::parse("$insomnia/default", loader).unwrap();
|
||||
let tmpl = SystemPromptTemplate::parse("$yoi/default", loader).unwrap();
|
||||
let dir = TempDir::new().unwrap();
|
||||
let scope = build_scope(dir.path());
|
||||
let rendered = tmpl
|
||||
@@ -651,7 +651,7 @@ mod tests {
|
||||
#[test]
|
||||
fn pod_orchestration_guidance_is_omitted_without_pod_management_tools() {
|
||||
let loader = PromptLoader::builtins_only();
|
||||
let tmpl = SystemPromptTemplate::parse("$insomnia/default", loader).unwrap();
|
||||
let tmpl = SystemPromptTemplate::parse("$yoi/default", loader).unwrap();
|
||||
let dir = TempDir::new().unwrap();
|
||||
let scope = build_scope(dir.path());
|
||||
let rendered = tmpl
|
||||
@@ -735,7 +735,7 @@ mod tests {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
std::fs::write(
|
||||
tmp.path().join("root.md"),
|
||||
"U-ROOT\n{% include \"$insomnia/common/tool-usage\" %}",
|
||||
"U-ROOT\n{% include \"$yoi/common/tool-usage\" %}",
|
||||
)
|
||||
.unwrap();
|
||||
let loader = PromptLoader::new(Some(tmp.path().to_path_buf()), None);
|
||||
@@ -758,7 +758,7 @@ mod tests {
|
||||
#[test]
|
||||
fn prefix_with_missing_file_is_hard_error() {
|
||||
let loader = PromptLoader::builtins_only();
|
||||
let err = SystemPromptTemplate::parse("$insomnia/definitely-missing", loader).unwrap_err();
|
||||
let err = SystemPromptTemplate::parse("$yoi/definitely-missing", loader).unwrap_err();
|
||||
assert!(matches!(err, SystemPromptError::LoaderResolve(_)));
|
||||
}
|
||||
|
||||
|
||||
@@ -116,8 +116,8 @@ pub fn default_base() -> Result<PathBuf, io::Error> {
|
||||
paths::runtime_dir().ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
"could not resolve runtime directory (no INSOMNIA_HOME / \
|
||||
INSOMNIA_RUNTIME_DIR / XDG_RUNTIME_DIR / HOME)",
|
||||
"could not resolve runtime directory (no YOI_HOME / \
|
||||
YOI_RUNTIME_DIR / XDG_RUNTIME_DIR / HOME)",
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -203,13 +203,13 @@ mod tests {
|
||||
|
||||
let records = vec![SpawnedPodRecord {
|
||||
pod_name: "child".into(),
|
||||
socket_path: "/run/insomnia/child/sock".into(),
|
||||
socket_path: "/run/yoi/child/sock".into(),
|
||||
scope_delegated: vec![ScopeRule {
|
||||
target: "/tmp/work".into(),
|
||||
permission: Permission::Write,
|
||||
recursive: true,
|
||||
}],
|
||||
callback_address: "/run/insomnia/my-pod/sock".into(),
|
||||
callback_address: "/run/yoi/my-pod/sock".into(),
|
||||
}];
|
||||
rt.write_spawned_pods(&records).await.unwrap();
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ struct SpawnPodInput {
|
||||
/// unambiguous profile slug. Raw/path selectors are rejected.
|
||||
#[serde(default)]
|
||||
profile: Option<String>,
|
||||
/// Instruction-file reference (e.g. `$insomnia/default`, `$user/my-agent`).
|
||||
/// Instruction-file reference (e.g. `$yoi/default`, `$user/my-agent`).
|
||||
#[serde(default)]
|
||||
instruction: Option<String>,
|
||||
/// First message sent to the spawned Pod via `Method::Run`.
|
||||
@@ -214,7 +214,7 @@ pub struct SpawnPodTool {
|
||||
/// Path to the spawner's Unix socket. Handed to the child via
|
||||
/// `--callback` so its `PodEvent` callbacks have somewhere to land.
|
||||
callback_socket: PathBuf,
|
||||
/// Root of the `$XDG_RUNTIME_DIR/insomnia/` tree, used to predict
|
||||
/// Root of the `$XDG_RUNTIME_DIR/yoi/` tree, used to predict
|
||||
/// the spawned Pod's socket path before the child has bound it.
|
||||
runtime_base: PathBuf,
|
||||
/// Directory the spawned Pod should run in when the LLM did not
|
||||
@@ -890,7 +890,7 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
worker: WorkerManifestConfig {
|
||||
instruction: Some("$insomnia/parent".into()),
|
||||
instruction: Some("$yoi/parent".into()),
|
||||
language: Some("Parentish".into()),
|
||||
max_tokens: Some(1234),
|
||||
stop_sequences: Some(vec!["STOP".into()]),
|
||||
@@ -916,8 +916,8 @@ mod tests {
|
||||
default: Option<&str>,
|
||||
profiles: &[(&str, &str, &str)],
|
||||
) -> AvailableProfiles {
|
||||
let insomnia = project.join(".insomnia");
|
||||
let profile_dir = insomnia.join("profiles");
|
||||
let yoi = project.join(".yoi");
|
||||
let profile_dir = yoi.join("profiles");
|
||||
std::fs::create_dir_all(&profile_dir).unwrap();
|
||||
let mut registry_toml = String::new();
|
||||
if let Some(default) = default {
|
||||
@@ -928,7 +928,7 @@ mod tests {
|
||||
std::fs::write(profile_dir.join(file), body).unwrap();
|
||||
registry_toml.push_str(&format!("{name} = \"profiles/{file}\"\n"));
|
||||
}
|
||||
let registry_path = insomnia.join("profiles.toml");
|
||||
let registry_path = yoi.join("profiles.toml");
|
||||
std::fs::write(®istry_path, registry_toml).unwrap();
|
||||
AvailableProfiles {
|
||||
registry: Some(
|
||||
@@ -963,23 +963,23 @@ mod tests {
|
||||
}
|
||||
|
||||
const CODER_PROFILE: &str = r#"
|
||||
local profile = require("insomnia.profile")
|
||||
local scope = require("insomnia.scope")
|
||||
local profile = require("yoi.profile")
|
||||
local scope = require("yoi.scope")
|
||||
return profile {
|
||||
slug = "coder",
|
||||
model = { scheme = "anthropic", model_id = "coder-model" },
|
||||
worker = { instruction = "$insomnia/coder", language = "Coderish", max_tokens = 2222 },
|
||||
worker = { instruction = "$yoi/coder", language = "Coderish", max_tokens = 2222 },
|
||||
scope = scope.workspace_write(),
|
||||
}
|
||||
"#;
|
||||
|
||||
const REVIEWER_PROFILE: &str = r#"
|
||||
local profile = require("insomnia.profile")
|
||||
local scope = require("insomnia.scope")
|
||||
local profile = require("yoi.profile")
|
||||
local scope = require("yoi.scope")
|
||||
return profile {
|
||||
slug = "reviewer",
|
||||
model = { scheme = "anthropic", model_id = "reviewer-model" },
|
||||
worker = { instruction = "$insomnia/reviewer", language = "Reviewerish", max_tokens = 3333 },
|
||||
worker = { instruction = "$yoi/reviewer", language = "Reviewerish", max_tokens = 3333 },
|
||||
scope = scope.workspace_write(),
|
||||
}
|
||||
"#;
|
||||
@@ -997,7 +997,7 @@ return profile {
|
||||
};
|
||||
|
||||
let config_json =
|
||||
build_spawn_config_json("child", "$insomnia/default", &[], &model, false).unwrap();
|
||||
build_spawn_config_json("child", "$yoi/default", &[], &model, false).unwrap();
|
||||
let parsed: PodManifestConfig = serde_json::from_str(&config_json).unwrap();
|
||||
|
||||
assert_eq!(parsed.model.scheme, Some(SchemeKind::Anthropic));
|
||||
@@ -1020,7 +1020,7 @@ return profile {
|
||||
..Default::default()
|
||||
};
|
||||
let config_json =
|
||||
build_spawn_config_json("child", "$insomnia/default", &[], &model, false).unwrap();
|
||||
build_spawn_config_json("child", "$yoi/default", &[], &model, false).unwrap();
|
||||
let parsed: PodManifestConfig = serde_json::from_str(&config_json).unwrap();
|
||||
assert_eq!(
|
||||
parsed.model.ref_.as_deref(),
|
||||
@@ -1041,7 +1041,7 @@ return profile {
|
||||
}];
|
||||
|
||||
let config_json =
|
||||
build_spawn_config_json("child", "$insomnia/default", &scope, &model, true).unwrap();
|
||||
build_spawn_config_json("child", "$yoi/default", &scope, &model, true).unwrap();
|
||||
let parsed: PodManifestConfig = serde_json::from_str(&config_json).unwrap();
|
||||
assert_eq!(
|
||||
parsed.session.as_ref().and_then(|s| s.record_event_trace),
|
||||
@@ -1062,7 +1062,7 @@ return profile {
|
||||
..Default::default()
|
||||
};
|
||||
let config_json =
|
||||
build_spawn_config_json("child", "$insomnia/default", &[], &model, false).unwrap();
|
||||
build_spawn_config_json("child", "$yoi/default", &[], &model, false).unwrap();
|
||||
let parsed: PodManifestConfig = serde_json::from_str(&config_json).unwrap();
|
||||
|
||||
assert!(parsed.session.is_none());
|
||||
@@ -1098,10 +1098,7 @@ return profile {
|
||||
|
||||
assert_eq!(config.pod.name.as_deref(), Some("child-default"));
|
||||
assert_eq!(config.model.model_id.as_deref(), Some("reviewer-model"));
|
||||
assert_eq!(
|
||||
config.worker.instruction.as_deref(),
|
||||
Some("$insomnia/reviewer")
|
||||
);
|
||||
assert_eq!(config.worker.instruction.as_deref(), Some("$yoi/reviewer"));
|
||||
assert_eq!(config.worker.language.as_deref(), Some("Reviewerish"));
|
||||
assert_eq!(config.scope.allow, scope);
|
||||
assert!(config.scope.deny.is_empty());
|
||||
@@ -1140,10 +1137,7 @@ return profile {
|
||||
|
||||
assert_eq!(config.pod.name.as_deref(), Some("review-child"));
|
||||
assert_eq!(config.model.model_id.as_deref(), Some("reviewer-model"));
|
||||
assert_eq!(
|
||||
config.worker.instruction.as_deref(),
|
||||
Some("$insomnia/reviewer")
|
||||
);
|
||||
assert_eq!(config.worker.instruction.as_deref(), Some("$yoi/reviewer"));
|
||||
assert_eq!(config.worker.language.as_deref(), Some("Reviewerish"));
|
||||
assert_eq!(config.worker.max_tokens, Some(3333));
|
||||
assert_eq!(config.scope.allow, scope);
|
||||
@@ -1177,10 +1171,7 @@ return profile {
|
||||
|
||||
assert_eq!(config.pod.name.as_deref(), Some("inherited-child"));
|
||||
assert_eq!(config.model.model_id.as_deref(), Some("parent-model"));
|
||||
assert_eq!(
|
||||
config.worker.instruction.as_deref(),
|
||||
Some("$insomnia/parent")
|
||||
);
|
||||
assert_eq!(config.worker.instruction.as_deref(), Some("$yoi/parent"));
|
||||
assert_eq!(config.worker.language.as_deref(), Some("Parentish"));
|
||||
assert_eq!(config.worker.max_tokens, Some(1234));
|
||||
assert_eq!(
|
||||
@@ -1313,7 +1304,7 @@ return profile {
|
||||
|
||||
let user_config = tmp.path().join("user-profiles.toml");
|
||||
std::fs::write(&user_config, "[profile]\ncoder = \"user-coder.lua\"\n").unwrap();
|
||||
let project_config = project.join(".insomnia/profiles.toml");
|
||||
let project_config = project.join(".yoi/profiles.toml");
|
||||
let ambiguous = AvailableProfiles {
|
||||
registry: Some(
|
||||
ProfileDiscovery::with_sources(Some(user_config), Some(project_config))
|
||||
|
||||
@@ -145,11 +145,11 @@ mod tests {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let layout = WorkspaceLayout::new(dir.path().to_path_buf());
|
||||
write(
|
||||
&dir.path().join(".insomnia/knowledge/policy.md"),
|
||||
&dir.path().join(".yoi/knowledge/policy.md"),
|
||||
"---\ncreated_at: 2026-01-01T00:00:00Z\nupdated_at: 2026-01-01T00:00:00Z\nkind: policy\ndescription: p\nmodel_invokation: false\nuser_invocable: true\nlast_sources: []\n---\npolicy body\n",
|
||||
);
|
||||
write(
|
||||
&dir.path().join(".insomnia/workflow/run-it.md"),
|
||||
&dir.path().join(".yoi/workflow/run-it.md"),
|
||||
"---\ndescription: run\nrequires: [policy]\n---\nworkflow body\n",
|
||||
);
|
||||
let registry = workflow_crate::load_workflows(&layout).unwrap();
|
||||
@@ -173,7 +173,7 @@ mod tests {
|
||||
fn user_invocable_false_errors() {
|
||||
let (dir, layout, _registry) = setup();
|
||||
write(
|
||||
&dir.path().join(".insomnia/workflow/hidden.md"),
|
||||
&dir.path().join(".yoi/workflow/hidden.md"),
|
||||
"---\ndescription: hidden\nuser_invocable: false\n---\nbody\n",
|
||||
);
|
||||
let registry = workflow_crate::load_workflows(&layout).unwrap();
|
||||
@@ -186,7 +186,7 @@ mod tests {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let layout = WorkspaceLayout::new(dir.path().to_path_buf());
|
||||
write(
|
||||
&dir.path().join(".insomnia/workflow/bad.md"),
|
||||
&dir.path().join(".yoi/workflow/bad.md"),
|
||||
"---\ndescription: bad\nrequires: [ghost]\n---\nbody\n",
|
||||
);
|
||||
let registry = workflow_crate::load_workflows(&layout).unwrap();
|
||||
|
||||
@@ -29,11 +29,11 @@ use tokio::sync::mpsc;
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
/// Serialises env-mutating tests. The test harness runs tasks across
|
||||
/// threads, and `INSOMNIA_RUNTIME_DIR` is a process-wide resource.
|
||||
/// threads, and `YOI_RUNTIME_DIR` is a process-wide resource.
|
||||
static ENV_LOCK: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));
|
||||
|
||||
/// Take `ENV_LOCK` and clear any env vars that would outrank
|
||||
/// `INSOMNIA_RUNTIME_DIR` in `paths::runtime_dir` resolution; restore
|
||||
/// `YOI_RUNTIME_DIR` in `paths::runtime_dir` resolution; restore
|
||||
/// previous values on drop.
|
||||
struct EnvGuard {
|
||||
prev_home: Option<String>,
|
||||
@@ -44,10 +44,10 @@ struct EnvGuard {
|
||||
impl EnvGuard {
|
||||
fn acquire() -> Self {
|
||||
let lock = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let prev_home = std::env::var("INSOMNIA_HOME").ok();
|
||||
let prev_home = std::env::var("YOI_HOME").ok();
|
||||
let prev_xdg = std::env::var("XDG_RUNTIME_DIR").ok();
|
||||
unsafe {
|
||||
std::env::remove_var("INSOMNIA_HOME");
|
||||
std::env::remove_var("YOI_HOME");
|
||||
std::env::remove_var("XDG_RUNTIME_DIR");
|
||||
}
|
||||
Self {
|
||||
@@ -62,14 +62,14 @@ impl Drop for EnvGuard {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
match &self.prev_home {
|
||||
Some(v) => std::env::set_var("INSOMNIA_HOME", v),
|
||||
None => std::env::remove_var("INSOMNIA_HOME"),
|
||||
Some(v) => std::env::set_var("YOI_HOME", v),
|
||||
None => std::env::remove_var("YOI_HOME"),
|
||||
}
|
||||
match &self.prev_xdg {
|
||||
Some(v) => std::env::set_var("XDG_RUNTIME_DIR", v),
|
||||
None => std::env::remove_var("XDG_RUNTIME_DIR"),
|
||||
}
|
||||
std::env::remove_var("INSOMNIA_RUNTIME_DIR");
|
||||
std::env::remove_var("YOI_RUNTIME_DIR");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -400,7 +400,7 @@ async fn stop_pod_sends_shutdown_and_releases_scope() {
|
||||
.unwrap(),
|
||||
);
|
||||
unsafe {
|
||||
std::env::set_var("INSOMNIA_RUNTIME_DIR", tmp.path());
|
||||
std::env::set_var("YOI_RUNTIME_DIR", tmp.path());
|
||||
}
|
||||
let lock_path = tmp.path().join("pods.json");
|
||||
|
||||
@@ -486,7 +486,7 @@ async fn stop_pod_succeeds_even_when_child_unreachable() {
|
||||
let _env = EnvGuard::acquire();
|
||||
let (tmp, registry, _rd) = setup_registry().await;
|
||||
unsafe {
|
||||
std::env::set_var("INSOMNIA_RUNTIME_DIR", tmp.path());
|
||||
std::env::set_var("YOI_RUNTIME_DIR", tmp.path());
|
||||
}
|
||||
|
||||
// No live listener — socket never bound. Registered record points
|
||||
@@ -518,7 +518,7 @@ async fn restored_registry_uses_pod_state_without_runtime_file() {
|
||||
FsPodStore::new(store_tmp.path().join("pods")).unwrap(),
|
||||
);
|
||||
unsafe {
|
||||
std::env::set_var("INSOMNIA_RUNTIME_DIR", runtime_tmp.path());
|
||||
std::env::set_var("YOI_RUNTIME_DIR", runtime_tmp.path());
|
||||
}
|
||||
|
||||
let rd = Arc::new(
|
||||
@@ -633,7 +633,7 @@ async fn load_from_pod_state_reclaims_missing_child_scope_and_records_history()
|
||||
FsPodStore::new(store_tmp.path().join("pods")).unwrap(),
|
||||
);
|
||||
unsafe {
|
||||
std::env::set_var("INSOMNIA_RUNTIME_DIR", runtime_tmp.path());
|
||||
std::env::set_var("YOI_RUNTIME_DIR", runtime_tmp.path());
|
||||
}
|
||||
let rd = Arc::new(
|
||||
RuntimeDir::create(runtime_tmp.path(), "spawner")
|
||||
|
||||
@@ -18,11 +18,11 @@ use protocol::{Event, Greeting, Method, Permission, PodEvent, PodStatus, ScopeRu
|
||||
use tempfile::TempDir;
|
||||
use tokio::net::UnixListener;
|
||||
|
||||
/// Serialises tests that mutate `INSOMNIA_RUNTIME_DIR`.
|
||||
/// Serialises tests that mutate `YOI_RUNTIME_DIR`.
|
||||
static ENV_LOCK: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));
|
||||
|
||||
/// Take `ENV_LOCK` and clear any env vars that would outrank
|
||||
/// `INSOMNIA_RUNTIME_DIR`; restore previous values on drop.
|
||||
/// `YOI_RUNTIME_DIR`; restore previous values on drop.
|
||||
struct EnvGuard {
|
||||
prev_home: Option<String>,
|
||||
prev_xdg: Option<String>,
|
||||
@@ -32,10 +32,10 @@ struct EnvGuard {
|
||||
impl EnvGuard {
|
||||
fn acquire() -> Self {
|
||||
let lock = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let prev_home = std::env::var("INSOMNIA_HOME").ok();
|
||||
let prev_home = std::env::var("YOI_HOME").ok();
|
||||
let prev_xdg = std::env::var("XDG_RUNTIME_DIR").ok();
|
||||
unsafe {
|
||||
std::env::remove_var("INSOMNIA_HOME");
|
||||
std::env::remove_var("YOI_HOME");
|
||||
std::env::remove_var("XDG_RUNTIME_DIR");
|
||||
}
|
||||
Self {
|
||||
@@ -50,29 +50,29 @@ impl Drop for EnvGuard {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
match &self.prev_home {
|
||||
Some(v) => std::env::set_var("INSOMNIA_HOME", v),
|
||||
None => std::env::remove_var("INSOMNIA_HOME"),
|
||||
Some(v) => std::env::set_var("YOI_HOME", v),
|
||||
None => std::env::remove_var("YOI_HOME"),
|
||||
}
|
||||
match &self.prev_xdg {
|
||||
Some(v) => std::env::set_var("XDG_RUNTIME_DIR", v),
|
||||
None => std::env::remove_var("XDG_RUNTIME_DIR"),
|
||||
}
|
||||
std::env::remove_var("INSOMNIA_RUNTIME_DIR");
|
||||
std::env::remove_var("YOI_RUNTIME_DIR");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Point `INSOMNIA_RUNTIME_DIR` at `dir`. The pod-registry then lives at
|
||||
/// Point `YOI_RUNTIME_DIR` at `dir`. The pod-registry then lives at
|
||||
/// `<dir>/pods.json` and Pod runtime sub-dirs at `<dir>/{pod_name}/`.
|
||||
fn set_runtime_dir(dir: &std::path::Path) {
|
||||
unsafe {
|
||||
std::env::set_var("INSOMNIA_RUNTIME_DIR", dir);
|
||||
std::env::set_var("YOI_RUNTIME_DIR", dir);
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_runtime_dir() {
|
||||
unsafe {
|
||||
std::env::remove_var("INSOMNIA_RUNTIME_DIR");
|
||||
std::env::remove_var("YOI_RUNTIME_DIR");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ use std::sync::Arc;
|
||||
use tempfile::TempDir;
|
||||
use tokio::net::UnixListener;
|
||||
|
||||
/// Serialises tests that mutate `INSOMNIA_RUNTIME_DIR` across the
|
||||
/// Serialises tests that mutate `YOI_RUNTIME_DIR` across the
|
||||
/// thread-pooled test harness.
|
||||
static ENV_LOCK: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));
|
||||
|
||||
@@ -42,7 +42,7 @@ impl EnvGuard {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set up a tempdir, point `INSOMNIA_RUNTIME_DIR` at it (so
|
||||
/// Set up a tempdir, point `YOI_RUNTIME_DIR` at it (so
|
||||
/// `pods.json` and per-Pod runtime subdirs both land in the
|
||||
/// sandbox), and install a live top-level "spawner" allocation so the
|
||||
/// tool has something to delegate from. Returns the tempdir (keeps it
|
||||
@@ -57,9 +57,9 @@ async fn setup_spawner(
|
||||
unsafe {
|
||||
// Outranking env vars must be cleared so `paths::runtime_dir`
|
||||
// resolves to our sandbox instead of the developer's real one.
|
||||
std::env::remove_var("INSOMNIA_HOME");
|
||||
std::env::remove_var("YOI_HOME");
|
||||
std::env::remove_var("XDG_RUNTIME_DIR");
|
||||
std::env::set_var("INSOMNIA_RUNTIME_DIR", &runtime_base);
|
||||
std::env::set_var("YOI_RUNTIME_DIR", &runtime_base);
|
||||
}
|
||||
|
||||
let spawner_rd = RuntimeDir::create(&runtime_base, spawner_name)
|
||||
@@ -209,7 +209,7 @@ fn shared_scope_for(allow_root: &Path) -> SharedScope {
|
||||
|
||||
fn clear_env() {
|
||||
unsafe {
|
||||
std::env::remove_var("INSOMNIA_RUNTIME_DIR");
|
||||
std::env::remove_var("YOI_RUNTIME_DIR");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1258,7 +1258,7 @@ mod tests {
|
||||
let method = Method::PodEvent(PodEvent::ScopeSubDelegated {
|
||||
parent_pod: "child".into(),
|
||||
sub_pod: "grandchild".into(),
|
||||
sub_socket: "/run/insomnia/grandchild/sock".into(),
|
||||
sub_socket: "/run/yoi/grandchild/sock".into(),
|
||||
scope: vec![ScopeRule {
|
||||
target: "/tmp/work".into(),
|
||||
permission: Permission::Write,
|
||||
@@ -1276,7 +1276,7 @@ mod tests {
|
||||
}) => {
|
||||
assert_eq!(parent_pod, "child");
|
||||
assert_eq!(sub_pod, "grandchild");
|
||||
assert_eq!(sub_socket, PathBuf::from("/run/insomnia/grandchild/sock"));
|
||||
assert_eq!(sub_socket, PathBuf::from("/run/yoi/grandchild/sock"));
|
||||
assert_eq!(scope.len(), 1);
|
||||
assert_eq!(scope[0].target, PathBuf::from("/tmp/work"));
|
||||
assert_eq!(scope[0].permission, Permission::Write);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
## 責務
|
||||
|
||||
- プロバイダ / モデルカタログの builtin (`resources/{providers,models}/builtin.toml`) と user override (`$XDG_CONFIG_HOME/insomnia/{providers,models}.toml`) の解決
|
||||
- プロバイダ / モデルカタログの builtin (`resources/{providers,models}/builtin.toml`) と user override (`$XDG_CONFIG_HOME/yoi/{providers,models}.toml`) の解決
|
||||
- `ModelManifest` の ref 形を `(provider, model_id)` に split し、`ModelConfig` へ展開
|
||||
- `AuthRef::SecretRef` / `AuthRef::ApiKey` を `ResolvedAuth::ApiKey` に解決(通常は local secret store、低レベル manifest では明示ファイルも可)
|
||||
- `AuthRef::None` / `AuthRef::CodexOAuth` の解決
|
||||
|
||||
@@ -636,9 +636,9 @@ auth_hint = { kind = "none" }
|
||||
assert!(matches!(err, CatalogError::Parse { .. }));
|
||||
}
|
||||
|
||||
/// `INSOMNIA_CONFIG_DIR` を tempdir に向けるテストガード。
|
||||
/// `paths::config_dir` は他の env (INSOMNIA_HOME / XDG_CONFIG_HOME)
|
||||
/// より高優先で `INSOMNIA_CONFIG_DIR` を尊重するため、これだけで
|
||||
/// `YOI_CONFIG_DIR` を tempdir に向けるテストガード。
|
||||
/// `paths::config_dir` は他の env (YOI_HOME / XDG_CONFIG_HOME)
|
||||
/// より高優先で `YOI_CONFIG_DIR` を尊重するため、これだけで
|
||||
/// 開発機の env 設定に左右されないテストになる。
|
||||
struct ConfigDirGuard {
|
||||
prev: Option<String>,
|
||||
@@ -646,10 +646,10 @@ auth_hint = { kind = "none" }
|
||||
|
||||
impl ConfigDirGuard {
|
||||
fn new(path: &Path) -> Self {
|
||||
let prev = std::env::var("INSOMNIA_CONFIG_DIR").ok();
|
||||
let prev = std::env::var("YOI_CONFIG_DIR").ok();
|
||||
// SAFETY: serial_test の `#[serial]` 属性で env を弄るテスト
|
||||
// 同士は直列化される。
|
||||
unsafe { std::env::set_var("INSOMNIA_CONFIG_DIR", path) };
|
||||
unsafe { std::env::set_var("YOI_CONFIG_DIR", path) };
|
||||
Self { prev }
|
||||
}
|
||||
}
|
||||
@@ -658,8 +658,8 @@ auth_hint = { kind = "none" }
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
match &self.prev {
|
||||
Some(v) => std::env::set_var("INSOMNIA_CONFIG_DIR", v),
|
||||
None => std::env::remove_var("INSOMNIA_CONFIG_DIR"),
|
||||
Some(v) => std::env::set_var("YOI_CONFIG_DIR", v),
|
||||
None => std::env::remove_var("YOI_CONFIG_DIR"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! `~/.codex/auth.json` の読み書き。
|
||||
//!
|
||||
//! Codex CLI と schema を共有するが、insomnia は知らないフィールドを
|
||||
//! Codex CLI と schema を共有するが、yoi は知らないフィールドを
|
||||
//! 失わないようファイル全体を `serde_json::Value` で保持し、必要箇所
|
||||
//! のみアクセスする。書込は `mode 0o600` を再設定(Codex CLI 同様)、
|
||||
//! ファイルロックは取らない(manager 側で guarded reload)。
|
||||
@@ -106,7 +106,7 @@ pub async fn load(path: &Path) -> Result<AuthSnapshot, CodexAuthError> {
|
||||
/// 既存ファイルを再読込し、`tokens.{id_token,access_token,refresh_token}` と
|
||||
/// `last_refresh` を更新して書き戻す。Codex CLI の `persist_tokens` 相当。
|
||||
///
|
||||
/// 並行する Codex CLI / 別 insomnia プロセスが先に refresh していた場合の
|
||||
/// 並行する Codex CLI / 別 yoi プロセスが先に refresh していた場合の
|
||||
/// fields を保護するため、書込前に再 load して merge する。
|
||||
pub async fn persist_refreshed(
|
||||
path: &Path,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
//! [`CodexAuthProvider`] はこのクレートに置く(feedback_llm_worker_scope)
|
||||
//! - access_token JWT の `exp` を読み、`now` 以下で proactive refresh
|
||||
//! (Codex CLI と同じバッファなし)
|
||||
//! - 並行する Codex CLI / 別 insomnia の refresh と取り違えないよう、
|
||||
//! - 並行する Codex CLI / 別 yoi の refresh と取り違えないよう、
|
||||
//! refresh 直前に再 load して account_id 一致を確認(guarded reload)
|
||||
//! - ファイルロックは取らず、書込前に再 load + diff merge で吸収
|
||||
//! - Codex の Keyring storage は対象外。auth.json 不在ならエラーで案内
|
||||
|
||||
@@ -78,7 +78,7 @@ impl SecretResolver for DefaultSecretResolver {
|
||||
path: std::path::PathBuf::from("<data_dir>"),
|
||||
source: std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"could not determine insomnia data directory",
|
||||
"could not determine yoi data directory",
|
||||
),
|
||||
})?;
|
||||
SecretStore::new(data_dir).get(id)
|
||||
|
||||
@@ -305,7 +305,7 @@ pub fn validate_id(id: &str) -> Result<()> {
|
||||
|
||||
fn derive_key(data_dir: &Path) -> [u8; KEY_LEN] {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(b"insomnia local secret store obfuscation key v1");
|
||||
hasher.update(b"yoi local secret store obfuscation key v1");
|
||||
hasher.update(data_dir.as_os_str().as_encoded_bytes());
|
||||
hasher.finalize().into()
|
||||
}
|
||||
@@ -339,7 +339,7 @@ fn make_nonce(id: &str, plaintext: &[u8]) -> Vec<u8> {
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_nanos();
|
||||
hasher.update(b"insomnia nonce v1");
|
||||
hasher.update(b"yoi nonce v1");
|
||||
hasher.update(now.to_le_bytes());
|
||||
hasher.update(std::process::id().to_le_bytes());
|
||||
hasher.update(NONCE_COUNTER.fetch_add(1, Ordering::Relaxed).to_le_bytes());
|
||||
@@ -353,7 +353,7 @@ fn xor_stream(key: &[u8; KEY_LEN], nonce: &[u8], input: &[u8]) -> Vec<u8> {
|
||||
let mut counter = 0u64;
|
||||
for chunk in input.chunks(KEY_LEN) {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(b"insomnia secret keystream v1");
|
||||
hasher.update(b"yoi secret keystream v1");
|
||||
hasher.update(key);
|
||||
hasher.update(nonce);
|
||||
hasher.update(counter.to_le_bytes());
|
||||
@@ -368,7 +368,7 @@ fn xor_stream(key: &[u8; KEY_LEN], nonce: &[u8], input: &[u8]) -> Vec<u8> {
|
||||
|
||||
fn tag(key: &[u8; KEY_LEN], id: &str, nonce: &[u8], ciphertext: &[u8]) -> [u8; TAG_LEN] {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(b"insomnia secret tag v1");
|
||||
hasher.update(b"yoi secret tag v1");
|
||||
hasher.update(key);
|
||||
hasher.update(id.as_bytes());
|
||||
hasher.update(nonce);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//!
|
||||
//! Migration: this layout is incompatible with the pre-`session-grouping`
|
||||
//! flat `{root}/{segment_id}.jsonl` form. Project policy is no
|
||||
//! backward compatibility — discard `~/.insomnia/sessions/` (or whatever
|
||||
//! backward compatibility — discard `~/.yoi/sessions/` (or whatever
|
||||
//! `root` resolved to) before running the new code. `list_sessions`
|
||||
//! ignores top-level files outside session directories, so leftover
|
||||
//! flat files do not corrupt new sessions, but they are no longer
|
||||
|
||||
@@ -141,7 +141,7 @@ impl Tool for BashTool {
|
||||
// close before bash itself exits.
|
||||
// exit $__exit propagate the user's exit
|
||||
let wrapped = format!(
|
||||
"exec >{out} 2>&1\n{{ {user_cmd}\n}}\n__insomnia_exit=$?\nwait 2>/dev/null\nexit $__insomnia_exit\n",
|
||||
"exec >{out} 2>&1\n{{ {user_cmd}\n}}\n__yoi_exit=$?\nwait 2>/dev/null\nexit $__yoi_exit\n",
|
||||
out = shell_single_quote(output_path_str),
|
||||
user_cmd = params.command,
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Built-in tools for the Insomnia LLM agent.
|
||||
//! Built-in tools for the Yoi LLM agent.
|
||||
//!
|
||||
//! Implements Read / Write / Edit / Glob / Grep / Bash on top of the
|
||||
//! `llm-worker` `Tool` infrastructure. Filesystem access is mediated by
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
//! let scope = Scope::writable("/workspace").unwrap();
|
||||
//! let fs = ScopedFs::new(scope, PathBuf::from("/workspace")); // pod lifetime
|
||||
//! let tracker = Tracker::new(); // session lifetime
|
||||
//! let bash_outputs = PathBuf::from("/run/insomnia/bash-output");
|
||||
//! let bash_outputs = PathBuf::from("/run/yoi/bash-output");
|
||||
//! let task_store = tools::TaskStore::new();
|
||||
//! let defs = builtin_tools(fs, tracker, task_store, bash_outputs, None);
|
||||
//! ```
|
||||
|
||||
@@ -44,7 +44,7 @@ impl WebTools {
|
||||
pub fn new(config: Option<WebConfig>) -> Self {
|
||||
let client = Client::builder()
|
||||
.redirect(reqwest::redirect::Policy::none())
|
||||
.user_agent("insomnia-web-tools/0.1")
|
||||
.user_agent("yoi-web-tools/0.1")
|
||||
.build()
|
||||
.expect("static reqwest client configuration is valid");
|
||||
let secret_store = manifest::paths::data_dir().map(SecretStore::new);
|
||||
@@ -249,7 +249,7 @@ async fn brave_search(
|
||||
let api_key_secret = cfg.api_key_secret.as_ref().ok_or_else(|| {
|
||||
disabled_error(
|
||||
"WebSearch",
|
||||
"set web.search.api_key_secret to the insomnia keys secret id for the Brave API key",
|
||||
"set web.search.api_key_secret to the yoi keys secret id for the Brave API key",
|
||||
)
|
||||
})?;
|
||||
let store = secret_store.ok_or_else(|| {
|
||||
@@ -1783,7 +1783,7 @@ mod tests {
|
||||
);
|
||||
let search_err = tools
|
||||
.run_search(WebSearchInput {
|
||||
query: "insomnia".into(),
|
||||
query: "yoi".into(),
|
||||
limit: None,
|
||||
offset: None,
|
||||
})
|
||||
@@ -2099,7 +2099,7 @@ mod tests {
|
||||
);
|
||||
let result = tools
|
||||
.run_search(WebSearchInput {
|
||||
query: "insomnia".into(),
|
||||
query: "yoi".into(),
|
||||
limit: Some(1),
|
||||
offset: Some(0),
|
||||
})
|
||||
@@ -2126,12 +2126,12 @@ mod tests {
|
||||
fetch: None,
|
||||
}));
|
||||
let cfg = brave_search_config(format!("http://{addr}/search"));
|
||||
let result = brave_search_with_api_key(&tools.client, &cfg, "test-key", "insomnia", 1, 0)
|
||||
let result = brave_search_with_api_key(&tools.client, &cfg, "test-key", "yoi", 1, 0)
|
||||
.await
|
||||
.unwrap();
|
||||
let value: Value = serde_json::from_str(result.content.as_deref().unwrap()).unwrap();
|
||||
let request = captured.lock().await.clone().unwrap();
|
||||
assert!(request.starts_with("GET /search?q=insomnia&count=1&offset=0 "));
|
||||
assert!(request.starts_with("GET /search?q=yoi&count=1&offset=0 "));
|
||||
assert!(
|
||||
request
|
||||
.to_ascii_lowercase()
|
||||
@@ -2158,7 +2158,7 @@ mod tests {
|
||||
fetch: None,
|
||||
}));
|
||||
let cfg = brave_search_config(format!("http://{addr}/search"));
|
||||
let err = brave_search_with_api_key(&tools.client, &cfg, "test-key", "insomnia", 1, 0)
|
||||
let err = brave_search_with_api_key(&tools.client, &cfg, "test-key", "yoi", 1, 0)
|
||||
.await
|
||||
.unwrap_err();
|
||||
assert!(err.to_string().contains("Content-Length"));
|
||||
|
||||
@@ -221,14 +221,14 @@ pub async fn launch() -> ExitCode {
|
||||
let data_dir = match manifest::paths::data_dir() {
|
||||
Some(path) => path,
|
||||
None => {
|
||||
eprintln!("insomnia keys: could not determine insomnia data directory");
|
||||
eprintln!("yoi keys: could not determine yoi data directory");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
};
|
||||
match run(SecretStore::new(data_dir)) {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(err) => {
|
||||
eprintln!("insomnia keys: {err}");
|
||||
eprintln!("yoi keys: {err}");
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
@@ -391,7 +391,7 @@ fn draw(frame: &mut Frame<'_>, app: &KeysApp) {
|
||||
fn title_line(app: &KeysApp) -> Line<'_> {
|
||||
Line::from(vec![
|
||||
Span::styled(
|
||||
"insomnia keys local secrets",
|
||||
"yoi keys local secrets",
|
||||
Style::default().add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::raw(" "),
|
||||
|
||||
@@ -38,20 +38,20 @@ pub enum LaunchMode {
|
||||
Spawn {
|
||||
profile: Option<String>,
|
||||
},
|
||||
/// `insomnia <name>` / `insomnia --pod <name>`: attach to a live Pod by name if
|
||||
/// `yoi <name>` / `yoi --pod <name>`: attach to a live Pod by name if
|
||||
/// possible; otherwise launch the Pod runtime command with `--pod <name>` so it
|
||||
/// resumes from name-keyed state or creates a fresh same-name Pod.
|
||||
PodName {
|
||||
pod_name: String,
|
||||
socket_override: Option<PathBuf>,
|
||||
},
|
||||
/// `insomnia -r` / `insomnia --resume`: open the Pod picker, then attach to the
|
||||
/// `yoi -r` / `yoi --resume`: open the Pod picker, then attach to the
|
||||
/// selected live Pod or restore the selected stopped Pod by name.
|
||||
Resume,
|
||||
/// `insomnia --session <UUID>`: skip the picker, go straight to the
|
||||
/// `yoi --session <UUID>`: skip the picker, go straight to the
|
||||
/// resume name dialog with `id` baked in.
|
||||
ResumeWithSession(SegmentId),
|
||||
/// `insomnia --multi`: open the multi-Pod dashboard. This is intentionally
|
||||
/// `yoi --multi`: open the multi-Pod dashboard. This is intentionally
|
||||
/// separate from `-r`/`--resume`, which keeps its single-Pod picker
|
||||
/// meaning.
|
||||
Multi,
|
||||
@@ -64,12 +64,12 @@ pub async fn launch(options: LaunchOptions) -> ExitCode {
|
||||
} = options;
|
||||
|
||||
if let Err(e) = enable_raw_mode() {
|
||||
eprintln!("insomnia: failed to enter raw mode: {e}");
|
||||
eprintln!("yoi: failed to enter raw mode: {e}");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
if let Err(e) = execute!(io::stdout(), EnableBracketedPaste) {
|
||||
let _ = disable_raw_mode();
|
||||
eprintln!("insomnia: {e}");
|
||||
eprintln!("yoi: {e}");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ pub async fn launch(options: LaunchOptions) -> ExitCode {
|
||||
// duplicate. Other errors (pod-name failures, terminal setup
|
||||
// hiccups, etc.) need surfacing here.
|
||||
if e.downcast_ref::<spawn::SpawnError>().is_none() {
|
||||
eprintln!("insomnia: {e}");
|
||||
eprintln!("yoi: {e}");
|
||||
}
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ impl std::fmt::Display for MultiPodError {
|
||||
Self::Store(e) => write!(f, "session store error: {e}"),
|
||||
Self::NoPods => write!(
|
||||
f,
|
||||
"no pods found — start a fresh pod with `insomnia` or restore one with `insomnia -r`"
|
||||
"no pods found — start a fresh pod with `yoi` or restore one with `yoi -r`"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ impl std::fmt::Display for PickerError {
|
||||
Self::Store(e) => write!(f, "session store error: {e}"),
|
||||
Self::NoPods => write!(
|
||||
f,
|
||||
"no pods found — start a fresh pod with `insomnia` and try again"
|
||||
"no pods found — start a fresh pod with `yoi` and try again"
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -169,7 +169,7 @@ fn default_store_dir() -> Result<PathBuf, PickerError> {
|
||||
PickerError::Io(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
"could not resolve sessions directory \
|
||||
(set INSOMNIA_HOME, INSOMNIA_DATA_DIR, or HOME)",
|
||||
(set YOI_HOME, YOI_DATA_DIR, or HOME)",
|
||||
))
|
||||
})
|
||||
}
|
||||
@@ -181,7 +181,7 @@ fn default_pod_store_dir() -> Result<PathBuf, PickerError> {
|
||||
PickerError::Io(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
"could not resolve pod state directory \
|
||||
(set INSOMNIA_HOME, INSOMNIA_DATA_DIR, or HOME)",
|
||||
(set YOI_HOME, YOI_DATA_DIR, or HOME)",
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ fn resolve_socket(pod_name: &str, override_path: Option<PathBuf>) -> PathBuf {
|
||||
}
|
||||
manifest::paths::pod_socket_path(pod_name).unwrap_or_else(|| {
|
||||
PathBuf::from("/tmp")
|
||||
.join("insomnia")
|
||||
.join("yoi")
|
||||
.join(pod_name)
|
||||
.join("sock")
|
||||
})
|
||||
@@ -307,7 +307,7 @@ impl TerminalEventReader {
|
||||
let stop = Arc::new(AtomicBool::new(false));
|
||||
let thread_stop = Arc::clone(&stop);
|
||||
let thread = thread::Builder::new()
|
||||
.name("insomnia-tui-terminal-reader".to_string())
|
||||
.name("yoi-tui-terminal-reader".to_string())
|
||||
.spawn(move || read_terminal_events(thread_stop, tx))?;
|
||||
|
||||
Ok((
|
||||
|
||||
+10
-10
@@ -1,11 +1,11 @@
|
||||
//! Inline-viewport "spawn Pod and attach" UX.
|
||||
//!
|
||||
//! Rendered at the user's current cursor position when `insomnia` is invoked
|
||||
//! with no positional argument. Discovers `.insomnia/profiles.toml` profile
|
||||
//! Rendered at the user's current cursor position when `yoi` is invoked
|
||||
//! with no positional argument. Discovers `.yoi/profiles.toml` profile
|
||||
//! choices plus bundled profiles, defaults to the builtin profile, prompts for
|
||||
//! the Pod's name, and on confirmation launches the Pod runtime command as an
|
||||
//! independent process. Once the process reports its socket via the
|
||||
//! `INSOMNIA-READY` stderr line, the dialog hands control back so main can
|
||||
//! `YOI-READY` stderr line, the dialog hands control back so main can
|
||||
//! switch the terminal to alternate-screen mode.
|
||||
//!
|
||||
//! The viewport's last frame stays in the terminal's scrollback so the
|
||||
@@ -654,10 +654,10 @@ mod tests {
|
||||
fn profile_choices_use_project_registry_default() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let project = temp.path().join("project");
|
||||
let insomnia = project.join(".insomnia");
|
||||
std::fs::create_dir_all(&insomnia).unwrap();
|
||||
let yoi = project.join(".yoi");
|
||||
std::fs::create_dir_all(&yoi).unwrap();
|
||||
std::fs::write(
|
||||
insomnia.join("profiles.toml"),
|
||||
yoi.join("profiles.toml"),
|
||||
r#"
|
||||
default = "coder"
|
||||
[profile]
|
||||
@@ -678,10 +678,10 @@ coder = "profiles/coder.lua"
|
||||
fn profile_choices_include_builtin_and_project_default_marker() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let project = temp.path().join("project");
|
||||
let insomnia = project.join(".insomnia");
|
||||
std::fs::create_dir_all(&insomnia).unwrap();
|
||||
let yoi = project.join(".yoi");
|
||||
std::fs::create_dir_all(&yoi).unwrap();
|
||||
std::fs::write(
|
||||
insomnia.join("profiles.toml"),
|
||||
yoi.join("profiles.toml"),
|
||||
r#"
|
||||
default = "coder"
|
||||
[profile.coder]
|
||||
@@ -695,7 +695,7 @@ description = "Project coder"
|
||||
assert_eq!(choices[0].selector.as_deref(), Some("builtin:default"));
|
||||
assert_eq!(
|
||||
choices[0].label,
|
||||
"builtin:default — Bundled default Insomnia coding profile"
|
||||
"builtin:default — Bundled default Yoi coding profile"
|
||||
);
|
||||
assert_eq!(default_index, 1);
|
||||
assert_eq!(choices[1].selector.as_deref(), Some("project:coder"));
|
||||
|
||||
@@ -176,7 +176,7 @@ mod tests {
|
||||
fn workflow_lint_accepts_valid_file() {
|
||||
let (dir, linter) = workspace();
|
||||
write(
|
||||
&dir.path().join(".insomnia/knowledge/policy.md"),
|
||||
&dir.path().join(".yoi/knowledge/policy.md"),
|
||||
"---\ndescription: p\n---\nbody",
|
||||
);
|
||||
let wf = "---\ndescription: run\nrequires: [policy]\n---\nbody";
|
||||
|
||||
@@ -6,7 +6,7 @@ use manifest::{Permission, ScopeRule};
|
||||
use memory::WorkspaceLayout;
|
||||
|
||||
/// Build deny rules that strip Write permission from
|
||||
/// `<workspace>/.insomnia/workflow/` for generic CRUD tools.
|
||||
/// `<workspace>/.yoi/workflow/` for generic CRUD tools.
|
||||
pub fn deny_write_rules(layout: &WorkspaceLayout) -> Vec<ScopeRule> {
|
||||
vec![deny_write(layout.workflow_dir().as_path())]
|
||||
}
|
||||
@@ -29,7 +29,7 @@ mod tests {
|
||||
let layout = WorkspaceLayout::new(PathBuf::from("/ws"));
|
||||
let rules = deny_write_rules(&layout);
|
||||
assert_eq!(rules.len(), 1);
|
||||
assert_eq!(rules[0].target, PathBuf::from("/ws/.insomnia/workflow"));
|
||||
assert_eq!(rules[0].target, PathBuf::from("/ws/.yoi/workflow"));
|
||||
assert_eq!(rules[0].permission, Permission::Write);
|
||||
assert!(rules[0].recursive);
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
//! Skills follow the [agentskills.io](https://agentskills.io/specification)
|
||||
//! spec: a directory `<root>/<name>/` containing `SKILL.md` (YAML frontmatter
|
||||
//! + Markdown body) and optional `scripts/` / `references/` / `assets/`
|
||||
//! subdirectories. The body is procedural agent guidance; insomnia ingests
|
||||
//! subdirectories. The body is procedural agent guidance; yoi ingests
|
||||
//! it as a Workflow so `/<name>` resolves to it just like an internal
|
||||
//! Workflow.
|
||||
//!
|
||||
//! Parsing is intentionally lenient at the directory-scan level — one
|
||||
//! malformed SKILL.md emits `tracing::warn!` and is skipped, leaving sibling
|
||||
//! skills loadable. Internal Workflows (`.insomnia/workflow/<slug>.md`) keep
|
||||
//! skills loadable. Internal Workflows (`.yoi/workflow/<slug>.md`) keep
|
||||
//! their hard-error semantics.
|
||||
|
||||
use std::io;
|
||||
@@ -30,7 +30,7 @@ pub const SKILL_FILENAME: &str = "SKILL.md";
|
||||
/// SKILL.md frontmatter as defined by the agent-skills spec.
|
||||
///
|
||||
/// Fields beyond `name` / `description` are accepted to be spec-compatible
|
||||
/// but not used by insomnia today: `license`, `compatibility`, and
|
||||
/// but not used by yoi today: `license`, `compatibility`, and
|
||||
/// `metadata` are documentary, while `allowed-tools` is recognised and
|
||||
/// emits a warning until [`permission-extension-point.md`] lands.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Workflow loader and registry.
|
||||
//!
|
||||
//! Workflows live under `<workspace>/.insomnia/workflow/<slug>.md`. They are
|
||||
//! Workflows live under `<workspace>/.yoi/workflow/<slug>.md`. They are
|
||||
//! human-authored Markdown documents with YAML frontmatter. The loader is
|
||||
//! intentionally strict about malformed records because Pod startup should
|
||||
//! fail rather than silently ignoring a broken procedural instruction.
|
||||
@@ -27,7 +27,7 @@ pub const WORKFLOW_DESCRIPTION_HARD_CAP: usize = 1024;
|
||||
/// win over external skills.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum WorkflowSource {
|
||||
/// `<workspace>/.insomnia/workflow/<slug>.md`. Authored in-tree by
|
||||
/// `<workspace>/.yoi/workflow/<slug>.md`. Authored in-tree by
|
||||
/// the project.
|
||||
WorkspaceWorkflow,
|
||||
/// SKILL.md ingested from a `[skills] directories` entry in the
|
||||
@@ -316,13 +316,13 @@ mod tests {
|
||||
|
||||
fn setup() -> (TempDir, WorkspaceLayout) {
|
||||
let dir = TempDir::new().unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".insomnia/workflow")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".yoi/workflow")).unwrap();
|
||||
let layout = WorkspaceLayout::new(dir.path().to_path_buf());
|
||||
(dir, layout)
|
||||
}
|
||||
|
||||
fn write_workflow(root: &Path, slug: &str, frontmatter: &str, body: &str) {
|
||||
let path = root.join(".insomnia/workflow").join(format!("{slug}.md"));
|
||||
let path = root.join(".yoi/workflow").join(format!("{slug}.md"));
|
||||
std::fs::write(path, format!("---\n{frontmatter}\n---\n{body}")).unwrap();
|
||||
}
|
||||
|
||||
@@ -380,12 +380,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn workflow_under_memory_is_ignored() {
|
||||
// The legacy `.insomnia/memory/workflow/` location is no longer
|
||||
// The legacy `.yoi/memory/workflow/` location is no longer
|
||||
// a Workflow source. Files placed there must be ignored (the
|
||||
// loader is rooted at `.insomnia/workflow/` only).
|
||||
// loader is rooted at `.yoi/workflow/` only).
|
||||
let dir = TempDir::new().unwrap();
|
||||
let layout = WorkspaceLayout::new(dir.path().to_path_buf());
|
||||
let legacy = dir.path().join(".insomnia/memory/workflow");
|
||||
let legacy = dir.path().join(".yoi/memory/workflow");
|
||||
std::fs::create_dir_all(&legacy).unwrap();
|
||||
std::fs::write(
|
||||
legacy.join("ghost.md"),
|
||||
@@ -509,7 +509,7 @@ mod tests {
|
||||
let s = ShadowedSkill {
|
||||
slug: Slug::parse("x").unwrap(),
|
||||
kept_source: WorkflowSource::WorkspaceWorkflow,
|
||||
kept_path: std::path::PathBuf::from("/ws/.insomnia/workflow/x.md"),
|
||||
kept_path: std::path::PathBuf::from("/ws/.yoi/workflow/x.md"),
|
||||
shadowed_source: WorkflowSource::Skill {
|
||||
dir: std::path::PathBuf::from("/skills"),
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "insomnia"
|
||||
name = "yoi"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
@@ -7,6 +7,7 @@ license.workspace = true
|
||||
[dependencies]
|
||||
client = { workspace = true }
|
||||
memory = { workspace = true }
|
||||
manifest = { workspace = true }
|
||||
pod = { workspace = true }
|
||||
session-store = { workspace = true }
|
||||
tui = { workspace = true }
|
||||
@@ -35,8 +35,8 @@ async fn main() -> ExitCode {
|
||||
let mode = match parse_args() {
|
||||
Ok(mode) => mode,
|
||||
Err(e) => {
|
||||
eprintln!("insomnia: {e}");
|
||||
eprintln!("try `insomnia --help` for usage.");
|
||||
eprintln!("yoi: {e}");
|
||||
eprintln!("try `yoi --help` for usage.");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
};
|
||||
@@ -54,17 +54,17 @@ async fn main() -> ExitCode {
|
||||
Ok(LintStatus::Clean) => ExitCode::SUCCESS,
|
||||
Ok(LintStatus::Failed) => ExitCode::FAILURE,
|
||||
Err(e) => {
|
||||
eprintln!("insomnia memory lint: {e}");
|
||||
eprintln!("yoi memory lint: {e}");
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
},
|
||||
Mode::PodRuntime(args) => pod::entrypoint::run_cli_from("insomnia pod", args).await,
|
||||
Mode::PodRuntime(args) => pod::entrypoint::run_cli_from("yoi pod", args).await,
|
||||
Mode::Keys => tui::keys::launch().await,
|
||||
Mode::Tui(mode) => {
|
||||
let runtime_command = match PodRuntimeCommand::resolve() {
|
||||
Ok(command) => command,
|
||||
Err(e) => {
|
||||
eprintln!("insomnia: failed to resolve Pod runtime command: {e}");
|
||||
eprintln!("yoi: failed to resolve Pod runtime command: {e}");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
};
|
||||
@@ -100,7 +100,7 @@ fn parse_args_slice(args: &[String]) -> Result<Mode, ParseError> {
|
||||
"pod" => return Ok(Mode::PodRuntime(args[1..].to_vec())),
|
||||
"keys" => {
|
||||
if args.len() != 1 {
|
||||
return Err(ParseError("insomnia keys does not accept arguments".into()));
|
||||
return Err(ParseError("yoi keys does not accept arguments".into()));
|
||||
}
|
||||
return Ok(Mode::Keys);
|
||||
}
|
||||
@@ -322,13 +322,13 @@ fn parse_session_id(value: &str) -> Result<SegmentId, ParseError> {
|
||||
|
||||
fn print_help() {
|
||||
println!(
|
||||
"insomnia\n\nUsage:\n insomnia [OPTIONS] [POD_NAME]\n insomnia keys\n insomnia pod [POD_OPTIONS]\n insomnia memory lint [OPTIONS]\n\nOptions:\n -r, --resume Open the Pod picker and resume/attach a Pod\n --multi Open the multi-Pod dashboard\n --pod <NAME> Attach/restore/create a Pod by name\n --socket <PATH> Attach to a specific Pod socket with --pod\n --session <UUID> Resume a specific session segment\n --profile <REF> Start a fresh Pod from a profile\n -h, --help Print help\n"
|
||||
"yoi\n\nUsage:\n yoi [OPTIONS] [POD_NAME]\n yoi keys\n yoi pod [POD_OPTIONS]\n yoi memory lint [OPTIONS]\n\nOptions:\n -r, --resume Open the Pod picker and resume/attach a Pod\n --multi Open the multi-Pod dashboard\n --pod <NAME> Attach/restore/create a Pod by name\n --socket <PATH> Attach to a specific Pod socket with --pod\n --session <UUID> Resume a specific session segment\n --profile <REF> Start a fresh Pod from a profile\n -h, --help Print help\n"
|
||||
);
|
||||
}
|
||||
|
||||
fn print_memory_lint_help() {
|
||||
println!(
|
||||
"insomnia memory lint\n\nUsage:\n insomnia memory lint [OPTIONS]\n\nOptions:\n --workspace <PATH> Workspace root to lint (defaults to cwd)\n --json Emit a JSON report\n --warnings-as-errors Return failure when warnings are present\n -h, --help Print help\n"
|
||||
"yoi memory lint\n\nUsage:\n yoi memory lint [OPTIONS]\n\nOptions:\n --workspace <PATH> Workspace root to lint (defaults to cwd)\n --json Emit a JSON report\n --warnings-as-errors Return failure when warnings are present\n -h, --help Print help\n"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -524,7 +524,7 @@ mod tests {
|
||||
"--profile".to_string(),
|
||||
"p.lua".to_string(),
|
||||
"--socket".to_string(),
|
||||
"/tmp/insomnia/sock".to_string(),
|
||||
"/tmp/yoi/sock".to_string(),
|
||||
],
|
||||
"--profile can only be used for fresh spawn",
|
||||
),
|
||||
@@ -416,19 +416,16 @@ mod tests {
|
||||
fn lints_only_workspace_memory_and_knowledge_records() {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let root = dir.path();
|
||||
write(&root.join(".insomnia/memory/summary.md"), valid_summary());
|
||||
write(&root.join(".yoi/memory/summary.md"), valid_summary());
|
||||
write(
|
||||
&root.join(".insomnia/memory/requests/request-one.md"),
|
||||
&root.join(".yoi/memory/requests/request-one.md"),
|
||||
valid_request(),
|
||||
);
|
||||
write(
|
||||
&root.join(".insomnia/memory/_logs/ignored.md"),
|
||||
"not frontmatter",
|
||||
);
|
||||
write(
|
||||
&root.join(".insomnia/workflow/ignored.md"),
|
||||
&root.join(".yoi/memory/_logs/ignored.md"),
|
||||
"not frontmatter",
|
||||
);
|
||||
write(&root.join(".yoi/workflow/ignored.md"), "not frontmatter");
|
||||
|
||||
let report = lint_workspace(root).unwrap();
|
||||
assert_eq!(
|
||||
@@ -438,8 +435,8 @@ mod tests {
|
||||
.map(|file| file.path.as_str())
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
".insomnia/memory/requests/request-one.md",
|
||||
".insomnia/memory/summary.md",
|
||||
".yoi/memory/requests/request-one.md",
|
||||
".yoi/memory/summary.md",
|
||||
]
|
||||
);
|
||||
assert_eq!(report.counts.files, 2);
|
||||
@@ -450,10 +447,7 @@ mod tests {
|
||||
fn invalid_records_count_as_lint_failures() {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let root = dir.path();
|
||||
write(
|
||||
&root.join(".insomnia/memory/summary.md"),
|
||||
"missing frontmatter",
|
||||
);
|
||||
write(&root.join(".yoi/memory/summary.md"), "missing frontmatter");
|
||||
|
||||
let report = lint_workspace(root).unwrap();
|
||||
assert_eq!(report.counts.files, 1);
|
||||
@@ -467,7 +461,7 @@ mod tests {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let root = dir.path();
|
||||
write(
|
||||
&root.join(".insomnia/memory/requests/large-record.md"),
|
||||
&root.join(".yoi/memory/requests/large-record.md"),
|
||||
&warning_request(),
|
||||
);
|
||||
|
||||
@@ -492,7 +486,7 @@ mod tests {
|
||||
fn json_output_is_machine_readable() {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let root = dir.path();
|
||||
write(&root.join(".insomnia/memory/summary.md"), valid_summary());
|
||||
write(&root.join(".yoi/memory/summary.md"), valid_summary());
|
||||
|
||||
let mut output = Vec::new();
|
||||
let status = run_with_writer(
|
||||
@@ -509,7 +503,7 @@ mod tests {
|
||||
let parsed: Value = serde_json::from_slice(&output).unwrap();
|
||||
assert_eq!(parsed["workspace"], root.display().to_string());
|
||||
assert_eq!(parsed["counts"]["files"], 1);
|
||||
assert_eq!(parsed["files"][0]["path"], ".insomnia/memory/summary.md");
|
||||
assert_eq!(parsed["files"][0]["path"], ".yoi/memory/summary.md");
|
||||
assert!(parsed["files"][0]["errors"].as_array().unwrap().is_empty());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user