cargo fmt
This commit is contained in:
@@ -61,13 +61,17 @@ pub enum LintError {
|
||||
#[error("Decisions `status` must be one of open|resolved|replaced (got `{0}`)")]
|
||||
InvalidStatus(String),
|
||||
|
||||
#[error("Knowledge with model_invokation: true cannot have description longer than {limit} chars (got {actual})")]
|
||||
#[error(
|
||||
"Knowledge with model_invokation: true cannot have description longer than {limit} chars (got {actual})"
|
||||
)]
|
||||
DescriptionTooLong { actual: usize, limit: usize },
|
||||
|
||||
#[error("body exceeds the size limit for this record kind: {actual} chars > {limit}")]
|
||||
BodyTooLong { actual: usize, limit: usize },
|
||||
|
||||
#[error("write to `memory/workflow/` is forbidden via the memory tool — Workflows are human-edited")]
|
||||
#[error(
|
||||
"write to `memory/workflow/` is forbidden via the memory tool — Workflows are human-edited"
|
||||
)]
|
||||
WorkflowWriteForbidden,
|
||||
|
||||
#[error("slug `{0}` already exists; use the edit tool instead of creating a new record")]
|
||||
|
||||
@@ -208,23 +208,13 @@ impl Linter {
|
||||
report.push_error(LintError::ReplacedBySelf);
|
||||
}
|
||||
}
|
||||
references::check_replaced_by(
|
||||
cp.slug.as_ref(),
|
||||
target,
|
||||
existing,
|
||||
report,
|
||||
);
|
||||
references::check_replaced_by(cp.slug.as_ref(), target, existing, report);
|
||||
}
|
||||
|
||||
warnings::check_warnings_with_sources(parsed.body, fm.sources.len(), report);
|
||||
}
|
||||
|
||||
fn check_knowledge(
|
||||
&self,
|
||||
content: &str,
|
||||
cp: &ClassifiedPath,
|
||||
report: &mut LintReport,
|
||||
) {
|
||||
fn check_knowledge(&self, content: &str, cp: &ClassifiedPath, report: &mut LintReport) {
|
||||
let parsed = match parse_frontmatter::<KnowledgeFrontmatter>(content) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
@@ -236,8 +226,7 @@ impl Linter {
|
||||
size::check_body::<KnowledgeFrontmatter>(parsed.body, report);
|
||||
|
||||
if fm.model_invokation
|
||||
&& fm.description.chars().count()
|
||||
> crate::schema::KNOWLEDGE_DESCRIPTION_HARD_CAP
|
||||
&& fm.description.chars().count() > crate::schema::KNOWLEDGE_DESCRIPTION_HARD_CAP
|
||||
{
|
||||
report.push_error(LintError::DescriptionTooLong {
|
||||
actual: fm.description.chars().count(),
|
||||
@@ -339,7 +328,12 @@ mod tests {
|
||||
now = iso_now()
|
||||
);
|
||||
let report = linter.lint(&path, &content, WriteMode::Create);
|
||||
assert!(report.errors.iter().any(|e| matches!(e, LintError::WorkflowWriteForbidden)));
|
||||
assert!(
|
||||
report
|
||||
.errors
|
||||
.iter()
|
||||
.any(|e| matches!(e, LintError::WorkflowWriteForbidden))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -347,7 +341,12 @@ mod tests {
|
||||
let (dir, linter) = workspace();
|
||||
let path = dir.path().join("src/main.rs");
|
||||
let report = linter.lint(&path, "ignored", WriteMode::Create);
|
||||
assert!(report.errors.iter().any(|e| matches!(e, LintError::InvalidPath(_))));
|
||||
assert!(
|
||||
report
|
||||
.errors
|
||||
.iter()
|
||||
.any(|e| matches!(e, LintError::InvalidPath(_)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -359,10 +358,12 @@ mod tests {
|
||||
now = iso_now()
|
||||
);
|
||||
let report = linter.lint(&path, &content, WriteMode::Create);
|
||||
assert!(report.errors.iter().any(|e| matches!(
|
||||
e,
|
||||
LintError::UnknownReference { .. }
|
||||
)));
|
||||
assert!(
|
||||
report
|
||||
.errors
|
||||
.iter()
|
||||
.any(|e| matches!(e, LintError::UnknownReference { .. }))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -374,7 +375,12 @@ mod tests {
|
||||
now = iso_now()
|
||||
);
|
||||
let report = linter.lint(&path, &content, WriteMode::Update);
|
||||
assert!(report.errors.iter().any(|e| matches!(e, LintError::ReplacedBySelf)));
|
||||
assert!(
|
||||
report
|
||||
.errors
|
||||
.iter()
|
||||
.any(|e| matches!(e, LintError::ReplacedBySelf))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -424,7 +430,12 @@ mod tests {
|
||||
now = iso_now()
|
||||
);
|
||||
let report = linter.lint(&path, &content, WriteMode::Create);
|
||||
assert!(report.errors.iter().any(|e| matches!(e, LintError::DescriptionTooLong { .. })));
|
||||
assert!(
|
||||
report
|
||||
.errors
|
||||
.iter()
|
||||
.any(|e| matches!(e, LintError::DescriptionTooLong { .. }))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -468,7 +479,12 @@ mod tests {
|
||||
now = iso_now()
|
||||
);
|
||||
let report = linter.lint(&path, &content, WriteMode::Create);
|
||||
assert!(report.errors.iter().any(|e| matches!(e, LintError::SlugAlreadyExists(_))));
|
||||
assert!(
|
||||
report
|
||||
.errors
|
||||
.iter()
|
||||
.any(|e| matches!(e, LintError::SlugAlreadyExists(_)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -549,7 +565,11 @@ mod tests {
|
||||
.warnings
|
||||
.iter()
|
||||
.any(|w| matches!(w, LintWarning::SimilarSlugs(slugs) if slugs.len() >= 3));
|
||||
assert!(warned, "expected SimilarSlugs warning, got {:?}", report.warnings);
|
||||
assert!(
|
||||
warned,
|
||||
"expected SimilarSlugs warning, got {:?}",
|
||||
report.warnings
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -591,7 +611,12 @@ mod tests {
|
||||
body = big_body
|
||||
);
|
||||
let report = linter.lint(&path, &content, WriteMode::Create);
|
||||
assert!(report.errors.iter().any(|e| matches!(e, LintError::BodyTooLong { .. })));
|
||||
assert!(
|
||||
report
|
||||
.errors
|
||||
.iter()
|
||||
.any(|e| matches!(e, LintError::BodyTooLong { .. }))
|
||||
);
|
||||
// Sanity: ensure path was treated as PathBuf consistently.
|
||||
let _ = PathBuf::from(path);
|
||||
}
|
||||
|
||||
@@ -49,9 +49,7 @@ pub fn check_replaced_by(
|
||||
return;
|
||||
}
|
||||
chain.push(node.to_string());
|
||||
cursor = existing
|
||||
.decision(&node)
|
||||
.and_then(|m| m.replaced_by.clone());
|
||||
cursor = existing.decision(&node).and_then(|m| m.replaced_by.clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,9 +82,7 @@ fn levenshtein(a: &str, b: &str) -> usize {
|
||||
curr[0] = i + 1;
|
||||
for (j, cb) in b.iter().enumerate() {
|
||||
let cost = if ca == cb { 0 } else { 1 };
|
||||
curr[j + 1] = (curr[j] + 1)
|
||||
.min(prev[j + 1] + 1)
|
||||
.min(prev[j] + cost);
|
||||
curr[j + 1] = (curr[j] + 1).min(prev[j + 1] + 1).min(prev[j] + cost);
|
||||
}
|
||||
std::mem::swap(&mut prev, &mut curr);
|
||||
}
|
||||
|
||||
@@ -50,9 +50,8 @@ pub fn split_frontmatter(content: &str) -> Result<(&str, &str), LintError> {
|
||||
byte_offset += line.len();
|
||||
}
|
||||
|
||||
let (yaml_end_excl, body_start) = yaml_end.ok_or_else(|| {
|
||||
LintError::MalformedFrontmatter("missing closing `---` line".to_string())
|
||||
})?;
|
||||
let (yaml_end_excl, body_start) = yaml_end
|
||||
.ok_or_else(|| LintError::MalformedFrontmatter("missing closing `---` line".to_string()))?;
|
||||
|
||||
let yaml = &after_open[..yaml_end_excl];
|
||||
let body = &after_open[body_start..];
|
||||
|
||||
@@ -118,16 +118,7 @@ mod tests {
|
||||
#[test]
|
||||
fn rejects_bad_slugs() {
|
||||
for s in [
|
||||
"",
|
||||
"-",
|
||||
"-foo",
|
||||
"foo-",
|
||||
"Foo",
|
||||
"foo_bar",
|
||||
"foo bar",
|
||||
"foo--bar",
|
||||
"foo.bar",
|
||||
"ä",
|
||||
"", "-", "-foo", "foo-", "Foo", "foo_bar", "foo bar", "foo--bar", "foo.bar", "ä",
|
||||
] {
|
||||
assert!(!is_valid_slug(s), "expected `{s}` invalid");
|
||||
assert!(Slug::parse(s).is_err());
|
||||
|
||||
@@ -45,9 +45,8 @@ struct EditTool {
|
||||
#[async_trait]
|
||||
impl Tool for EditTool {
|
||||
async fn execute(&self, input_json: &str) -> Result<ToolOutput, ToolError> {
|
||||
let params: EditParams = serde_json::from_str(input_json).map_err(|e| {
|
||||
ToolError::InvalidArgument(format!("invalid MemoryEdit input: {e}"))
|
||||
})?;
|
||||
let params: EditParams = serde_json::from_str(input_json)
|
||||
.map_err(|e| ToolError::InvalidArgument(format!("invalid MemoryEdit input: {e}")))?;
|
||||
|
||||
if params.old_string.is_empty() {
|
||||
return Err(ToolError::InvalidArgument(
|
||||
@@ -60,7 +59,9 @@ impl Tool for EditTool {
|
||||
));
|
||||
}
|
||||
|
||||
let path = params.kind.resolve_path(&self.layout, params.slug.as_deref())?;
|
||||
let path = params
|
||||
.kind
|
||||
.resolve_path(&self.layout, params.slug.as_deref())?;
|
||||
|
||||
let current_bytes = std::fs::read(&path).map_err(|e| match e.kind() {
|
||||
std::io::ErrorKind::NotFound => ToolError::ExecutionFailed(format!(
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::workspace::{RecordKind, WorkspaceLayout};
|
||||
|
||||
pub use edit::edit_tool;
|
||||
pub use read::read_tool;
|
||||
pub use search::{knowledge_search_tool, memory_search_tool, SearchConfig};
|
||||
pub use search::{SearchConfig, knowledge_search_tool, memory_search_tool};
|
||||
pub use write::write_tool;
|
||||
|
||||
/// Kinds the memory tools accept as input. `Workflow` is intentionally
|
||||
@@ -71,13 +71,10 @@ impl MemoryToolKind {
|
||||
}
|
||||
other => {
|
||||
let raw = slug.ok_or_else(|| {
|
||||
ToolError::InvalidArgument(format!(
|
||||
"kind={} requires `slug`",
|
||||
other.as_str()
|
||||
))
|
||||
ToolError::InvalidArgument(format!("kind={} requires `slug`", other.as_str()))
|
||||
})?;
|
||||
let parsed = Slug::parse(raw)
|
||||
.map_err(|e| ToolError::InvalidArgument(e.to_string()))?;
|
||||
let parsed =
|
||||
Slug::parse(raw).map_err(|e| ToolError::InvalidArgument(e.to_string()))?;
|
||||
Ok(match other {
|
||||
Self::Decision => layout.decision_path(&parsed),
|
||||
Self::Request => layout.request_path(&parsed),
|
||||
|
||||
@@ -43,11 +43,12 @@ struct ReadTool {
|
||||
#[async_trait]
|
||||
impl Tool for ReadTool {
|
||||
async fn execute(&self, input_json: &str) -> Result<ToolOutput, ToolError> {
|
||||
let params: ReadParams = serde_json::from_str(input_json).map_err(|e| {
|
||||
ToolError::InvalidArgument(format!("invalid MemoryRead input: {e}"))
|
||||
})?;
|
||||
let params: ReadParams = serde_json::from_str(input_json)
|
||||
.map_err(|e| ToolError::InvalidArgument(format!("invalid MemoryRead input: {e}")))?;
|
||||
|
||||
let path = params.kind.resolve_path(&self.layout, params.slug.as_deref())?;
|
||||
let path = params
|
||||
.kind
|
||||
.resolve_path(&self.layout, params.slug.as_deref())?;
|
||||
|
||||
let bytes = std::fs::read(&path).map_err(|e| match e.kind() {
|
||||
std::io::ErrorKind::NotFound => {
|
||||
|
||||
@@ -116,9 +116,8 @@ struct KnowledgeSearchTool {
|
||||
#[async_trait]
|
||||
impl Tool for MemorySearchTool {
|
||||
async fn execute(&self, input_json: &str) -> Result<ToolOutput, ToolError> {
|
||||
let params: MemorySearchParams = serde_json::from_str(input_json).map_err(|e| {
|
||||
ToolError::InvalidArgument(format!("invalid MemorySearch input: {e}"))
|
||||
})?;
|
||||
let params: MemorySearchParams = serde_json::from_str(input_json)
|
||||
.map_err(|e| ToolError::InvalidArgument(format!("invalid MemorySearch input: {e}")))?;
|
||||
let needle = validate_query(¶ms.query)?;
|
||||
|
||||
let mut hits: Vec<MemoryHit> = Vec::new();
|
||||
@@ -241,9 +240,7 @@ impl Tool for KnowledgeSearchTool {
|
||||
|
||||
fn validate_query(query: &str) -> Result<String, ToolError> {
|
||||
if query.trim().is_empty() {
|
||||
return Err(ToolError::InvalidArgument(
|
||||
"query must not be empty".into(),
|
||||
));
|
||||
return Err(ToolError::InvalidArgument("query must not be empty".into()));
|
||||
}
|
||||
Ok(query.to_lowercase())
|
||||
}
|
||||
|
||||
@@ -40,11 +40,12 @@ struct WriteTool {
|
||||
#[async_trait]
|
||||
impl Tool for WriteTool {
|
||||
async fn execute(&self, input_json: &str) -> Result<ToolOutput, ToolError> {
|
||||
let params: WriteParams = serde_json::from_str(input_json).map_err(|e| {
|
||||
ToolError::InvalidArgument(format!("invalid MemoryWrite input: {e}"))
|
||||
})?;
|
||||
let params: WriteParams = serde_json::from_str(input_json)
|
||||
.map_err(|e| ToolError::InvalidArgument(format!("invalid MemoryWrite input: {e}")))?;
|
||||
|
||||
let path = params.kind.resolve_path(&self.layout, params.slug.as_deref())?;
|
||||
let path = params
|
||||
.kind
|
||||
.resolve_path(&self.layout, params.slug.as_deref())?;
|
||||
|
||||
let already_exists = path.exists();
|
||||
let mode = if already_exists {
|
||||
@@ -72,7 +73,11 @@ impl Tool for WriteTool {
|
||||
|
||||
let summary = format!(
|
||||
"{} {}{}",
|
||||
if already_exists { "Overwrote" } else { "Created" },
|
||||
if already_exists {
|
||||
"Overwrote"
|
||||
} else {
|
||||
"Created"
|
||||
},
|
||||
path.display(),
|
||||
warning_tail(&report),
|
||||
);
|
||||
|
||||
@@ -138,11 +138,7 @@ impl WorkspaceLayout {
|
||||
let knowledge = self.knowledge_dir();
|
||||
|
||||
if let Ok(rel) = path.strip_prefix(&knowledge) {
|
||||
return Ok(Some(classify_kinded_md(
|
||||
rel,
|
||||
RecordKind::Knowledge,
|
||||
path,
|
||||
)?));
|
||||
return Ok(Some(classify_kinded_md(rel, RecordKind::Knowledge, path)?));
|
||||
}
|
||||
let rel = match path.strip_prefix(&memory) {
|
||||
Ok(r) => r,
|
||||
|
||||
Reference in New Issue
Block a user