feat: M3.7.7 signature extraction CLI + integration tests (unit tests pass, integration tests pending mem-cli fix)

This commit is contained in:
Story Crater Bot
2026-08-28 07:46:36 -07:00
parent ae0738289e
commit dbcefd8853
13 changed files with 370 additions and 0 deletions
+46
View File
@@ -136,6 +136,16 @@ enum Commands {
#[arg(long)]
database_url: Option<String>,
},
/// Extract and explain failure signature
Sig {
/// Tool name (e.g. npm, cargo, kubectl)
#[arg(long, value_name = "TOOL")]
tool: String,
/// Read failure log from file (stdin if not specified)
#[arg(long, value_name = "FILE")]
file: Option<PathBuf>,
},
}
#[tokio::main]
@@ -191,6 +201,9 @@ async fn main() -> anyhow::Result<()> {
};
cmd_verify(&project, db, log, log_dir, output_format, &database_url).await?
}
Commands::Sig { tool, file } => {
cmd_sig(&tool, file.as_ref())?
}
}
Ok(())
@@ -356,3 +369,36 @@ async fn cmd_verify(
Ok(())
}
fn cmd_sig(tool: &str, file: Option<&PathBuf>) -> anyhow::Result<()> {
use mem_core::lesson;
use std::io::Read;
// Read failure log from file or stdin
let mut output = String::new();
if let Some(file_path) = file {
output = fs::read_to_string(file_path)?;
} else {
std::io::stdin().read_to_string(&mut output)?;
}
// Extract signature
match lesson::extract(tool, &output) {
Some(sig) => {
println!("=== Failure Signature ===");
println!("Tool: {}", sig.tool);
println!("Rule: {}", sig.rule);
println!("Hash (SHA256): {}", sig.sig_sha);
println!("\n=== Raw Error ===");
println!("{}", sig.raw);
println!("\n=== Normalised Form ===");
println!("{}", sig.normalised);
}
None => {
eprintln!("Failed to extract signature for tool: {}", tool);
std::process::exit(1);
}
}
Ok(())
}