up
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-11-24 07:52:25 +02:00
parent 5970f0d9bd
commit 150b3730ef
215 changed files with 8119 additions and 740 deletions

View File

@@ -1236,8 +1236,78 @@ internal static class CommandFactory
cancellationToken);
});
var explainOptions = CreateAdvisoryOptions();
var explain = new Command("explain", "Explain an advisory conflict set with narrative and rationale.");
AddAdvisoryOptions(explain, explainOptions);
explain.SetAction((parseResult, _) =>
{
var advisoryKey = parseResult.GetValue(explainOptions.AdvisoryKey) ?? string.Empty;
var artifactId = parseResult.GetValue(explainOptions.ArtifactId);
var artifactPurl = parseResult.GetValue(explainOptions.ArtifactPurl);
var policyVersion = parseResult.GetValue(explainOptions.PolicyVersion);
var profile = parseResult.GetValue(explainOptions.Profile) ?? "default";
var sections = parseResult.GetValue(explainOptions.Sections) ?? Array.Empty<string>();
var forceRefresh = parseResult.GetValue(explainOptions.ForceRefresh);
var timeoutSeconds = parseResult.GetValue(explainOptions.TimeoutSeconds) ?? 120;
var outputFormat = ParseAdvisoryOutputFormat(parseResult.GetValue(explainOptions.Format));
var outputPath = parseResult.GetValue(explainOptions.Output);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleAdviseRunAsync(
services,
AdvisoryAiTaskType.Conflict,
advisoryKey,
artifactId,
artifactPurl,
policyVersion,
profile,
sections,
forceRefresh,
timeoutSeconds,
outputFormat,
outputPath,
verbose,
cancellationToken);
});
var remediateOptions = CreateAdvisoryOptions();
var remediate = new Command("remediate", "Generate remediation guidance for an advisory.");
AddAdvisoryOptions(remediate, remediateOptions);
remediate.SetAction((parseResult, _) =>
{
var advisoryKey = parseResult.GetValue(remediateOptions.AdvisoryKey) ?? string.Empty;
var artifactId = parseResult.GetValue(remediateOptions.ArtifactId);
var artifactPurl = parseResult.GetValue(remediateOptions.ArtifactPurl);
var policyVersion = parseResult.GetValue(remediateOptions.PolicyVersion);
var profile = parseResult.GetValue(remediateOptions.Profile) ?? "default";
var sections = parseResult.GetValue(remediateOptions.Sections) ?? Array.Empty<string>();
var forceRefresh = parseResult.GetValue(remediateOptions.ForceRefresh);
var timeoutSeconds = parseResult.GetValue(remediateOptions.TimeoutSeconds) ?? 120;
var outputFormat = ParseAdvisoryOutputFormat(parseResult.GetValue(remediateOptions.Format));
var outputPath = parseResult.GetValue(remediateOptions.Output);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleAdviseRunAsync(
services,
AdvisoryAiTaskType.Remediation,
advisoryKey,
artifactId,
artifactPurl,
policyVersion,
profile,
sections,
forceRefresh,
timeoutSeconds,
outputFormat,
outputPath,
verbose,
cancellationToken);
});
advise.Add(run);
advise.Add(summarize);
advise.Add(explain);
advise.Add(remediate);
return advise;
}

View File

@@ -553,6 +553,12 @@ internal static class CommandHandlers
logger.LogInformation("Advisory output written to {Path}.", fullPath);
}
if (rendered is not null)
{
// Surface the rendered advisory to the active console so users (and tests) can see it even when also writing to disk.
AnsiConsole.Console.WriteLine(rendered);
}
if (output.Guardrail.Blocked)
{
logger.LogError("Guardrail blocked advisory output (cache key {CacheKey}).", output.CacheKey);
@@ -3075,7 +3081,7 @@ internal static class CommandHandlers
};
var json = JsonSerializer.Serialize(payload, new JsonSerializerOptions(JsonSerializerDefaults.Web) { WriteIndented = true });
Console.WriteLine(json);
AnsiConsole.Console.WriteLine(json);
}
else
{
@@ -6359,9 +6365,9 @@ internal static class CommandHandlers
builder.AppendLine($"# Advisory {output.TaskType} ({output.Profile})");
builder.AppendLine();
builder.AppendLine($"- Cache Key: `{output.CacheKey}`");
builder.AppendLine($"- Generated: {output.GeneratedAtUtc.ToString(\"O\", CultureInfo.InvariantCulture)}");
builder.AppendLine($"- Plan From Cache: {(output.PlanFromCache ? \"yes\" : \"no\")}");
builder.AppendLine($"- Guardrail Blocked: {(output.Guardrail.Blocked ? \"yes\" : \"no\")}");
builder.AppendLine($"- Generated: {output.GeneratedAtUtc.ToString("O", CultureInfo.InvariantCulture)}");
builder.AppendLine($"- Plan From Cache: {(output.PlanFromCache ? "yes" : "no")}");
builder.AppendLine($"- Guardrail Blocked: {(output.Guardrail.Blocked ? "yes" : "no")}");
builder.AppendLine();
if (!string.IsNullOrWhiteSpace(output.Response))