Add PHP Analyzer Plugin and Composer Lock Data Handling
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Implemented the PhpAnalyzerPlugin to analyze PHP projects. - Created ComposerLockData class to represent data from composer.lock files. - Developed ComposerLockReader to load and parse composer.lock files asynchronously. - Introduced ComposerPackage class to encapsulate package details. - Added PhpPackage class to represent PHP packages with metadata and evidence. - Implemented PhpPackageCollector to gather packages from ComposerLockData. - Created PhpLanguageAnalyzer to perform analysis and emit results. - Added capability signals for known PHP frameworks and CMS. - Developed unit tests for the PHP language analyzer and its components. - Included sample composer.lock and expected output for testing. - Updated project files for the new PHP analyzer library and tests.
This commit is contained in:
@@ -448,6 +448,8 @@ internal static class CommandHandlers
|
||||
IReadOnlyList<string> preferredSections,
|
||||
bool forceRefresh,
|
||||
int timeoutSeconds,
|
||||
AdvisoryOutputFormat outputFormat,
|
||||
string? outputPath,
|
||||
bool verbose,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -542,7 +544,14 @@ internal static class CommandHandlers
|
||||
activity?.SetTag("stellaops.cli.advisory.cache_hit", output.PlanFromCache);
|
||||
logger.LogInformation("Advisory output ready (cache key {CacheKey}).", output.CacheKey);
|
||||
|
||||
RenderAdvisoryOutput(output);
|
||||
var rendered = RenderAdvisoryOutput(output, outputFormat);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(outputPath) && rendered is not null)
|
||||
{
|
||||
var fullPath = Path.GetFullPath(outputPath!);
|
||||
await File.WriteAllTextAsync(fullPath, rendered, cancellationToken).ConfigureAwait(false);
|
||||
logger.LogInformation("Advisory output written to {Path}.", fullPath);
|
||||
}
|
||||
|
||||
if (output.Guardrail.Blocked)
|
||||
{
|
||||
@@ -6326,7 +6335,113 @@ internal static class CommandHandlers
|
||||
}
|
||||
}
|
||||
|
||||
private static void RenderAdvisoryOutput(AdvisoryPipelineOutputModel output)
|
||||
private static string? RenderAdvisoryOutput(AdvisoryPipelineOutputModel output, AdvisoryOutputFormat format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
AdvisoryOutputFormat.Json => RenderAdvisoryOutputJson(output),
|
||||
AdvisoryOutputFormat.Markdown => RenderAdvisoryOutputMarkdown(output),
|
||||
_ => RenderAdvisoryOutputTable(output)
|
||||
};
|
||||
}
|
||||
|
||||
private static string RenderAdvisoryOutputJson(AdvisoryPipelineOutputModel output)
|
||||
{
|
||||
return JsonSerializer.Serialize(output, new JsonSerializerOptions(JsonSerializerDefaults.Web)
|
||||
{
|
||||
WriteIndented = true
|
||||
});
|
||||
}
|
||||
|
||||
private static string RenderAdvisoryOutputMarkdown(AdvisoryPipelineOutputModel output)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
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();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(output.Response))
|
||||
{
|
||||
builder.AppendLine("## Response");
|
||||
builder.AppendLine(output.Response.Trim());
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(output.Prompt))
|
||||
{
|
||||
builder.AppendLine("## Prompt (sanitized)");
|
||||
builder.AppendLine(output.Prompt.Trim());
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
if (output.Citations.Count > 0)
|
||||
{
|
||||
builder.AppendLine("## Citations");
|
||||
foreach (var citation in output.Citations.OrderBy(c => c.Index))
|
||||
{
|
||||
builder.AppendLine($"- [{citation.Index}] {citation.DocumentId} :: {citation.ChunkId}");
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
if (output.Metadata.Count > 0)
|
||||
{
|
||||
builder.AppendLine("## Output Metadata");
|
||||
foreach (var entry in output.Metadata.OrderBy(kvp => kvp.Key, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
builder.AppendLine($"- **{entry.Key}**: {entry.Value}");
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
if (output.Guardrail.Metadata.Count > 0)
|
||||
{
|
||||
builder.AppendLine("## Guardrail Metadata");
|
||||
foreach (var entry in output.Guardrail.Metadata.OrderBy(kvp => kvp.Key, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
builder.AppendLine($"- **{entry.Key}**: {entry.Value}");
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
if (output.Guardrail.Violations.Count > 0)
|
||||
{
|
||||
builder.AppendLine("## Guardrail Violations");
|
||||
foreach (var violation in output.Guardrail.Violations)
|
||||
{
|
||||
builder.AppendLine($"- `{violation.Code}`: {violation.Message}");
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
builder.AppendLine("## Provenance");
|
||||
builder.AppendLine($"- Input Digest: `{output.Provenance.InputDigest}`");
|
||||
builder.AppendLine($"- Output Hash: `{output.Provenance.OutputHash}`");
|
||||
|
||||
if (output.Provenance.Signatures.Count > 0)
|
||||
{
|
||||
foreach (var signature in output.Provenance.Signatures)
|
||||
{
|
||||
builder.AppendLine($"- Signature: `{signature}`");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine("- Signature: none");
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static string? RenderAdvisoryOutputTable(AdvisoryPipelineOutputModel output)
|
||||
{
|
||||
var console = AnsiConsole.Console;
|
||||
|
||||
@@ -6428,6 +6543,8 @@ internal static class CommandHandlers
|
||||
provenance.AddRow("Signatures", signatures);
|
||||
|
||||
console.Write(provenance);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Table CreateKeyValueTable(string title, IReadOnlyDictionary<string, string> entries)
|
||||
|
||||
Reference in New Issue
Block a user