diff --git a/skills/drupalorg-cli/SKILL.md b/skills/drupalorg-cli/SKILL.md index a8b28c8..deeb8fb 100644 --- a/skills/drupalorg-cli/SKILL.md +++ b/skills/drupalorg-cli/SKILL.md @@ -26,8 +26,8 @@ Commands that fetch data accept `--format` / `-f`: |--------|-------------|---------| | `text` | Human-readable plain text (default) | All commands | | `json` | Machine-readable JSON | Most commands | -| `md` | Markdown suitable for display or copy-paste | `issue:show`, `issue:get-fork`, `mr:list`, `mr:status`, `project:issues`, `project:releases`, `project:release-notes`, `maintainer:issues` | -| `llm` | Structured XML optimised for agent consumption | `issue:show` (add `--with-comments` to include comment thread), `issue:get-fork`, `mr:list`, `mr:status`, `project:issues`, `project:releases`, `project:release-notes`, `maintainer:issues` | +| `md` | Markdown suitable for display or copy-paste | `issue:show`, `issue:get-fork`, `mr:list`, `mr:status`, `mr:files`, `mr:diff`, `project:issues`, `project:releases`, `project:release-notes`, `maintainer:issues` | +| `llm` | Structured XML optimised for agent consumption | `issue:show` (add `--with-comments` to include comment thread), `issue:get-fork`, `mr:list`, `mr:status`, `mr:files`, `mr:diff`, `project:issues`, `project:releases`, `project:release-notes`, `maintainer:issues` | **Agents should always pass `--format=llm`** to get rich, structured output with clearly labelled fields, contributor lists, and change records. @@ -92,14 +92,14 @@ drupalorg mr:list [nid] [--state=opened] --format=llm drupalorg mr:list project/drupal --format=llm # Show the unified diff for a merge request -# Supports --format=text (default), json -drupalorg mr:diff -drupalorg mr:diff 'project/drupal!708' +# Supports --format=text (default), json, md, llm +drupalorg mr:diff --format=llm +drupalorg mr:diff 'project/drupal!708' --format=llm # List changed files in a merge request -# Supports --format=text (default), json -drupalorg mr:files -drupalorg mr:files 'project/drupal!708' +# Supports --format=text (default), json, md, llm +drupalorg mr:files --format=llm +drupalorg mr:files 'project/drupal!708' --format=llm # Show the pipeline status for a merge request drupalorg mr:status --format=llm diff --git a/src/Cli/Command/MergeRequest/GetDiff.php b/src/Cli/Command/MergeRequest/GetDiff.php index 00c868f..0eed447 100644 --- a/src/Cli/Command/MergeRequest/GetDiff.php +++ b/src/Cli/Command/MergeRequest/GetDiff.php @@ -19,7 +19,7 @@ protected function configure(): void 'format', 'f', InputOption::VALUE_OPTIONAL, - 'Output format: text, json. Defaults to text.', + 'Output format: text, json, md, llm. Defaults to text.', 'text' ); $this->configureNidAndMrIid(); @@ -32,8 +32,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $action = new GetMergeRequestDiffAction($this->client, new GitLabClient()); $result = $action($this->nid ?? '', $this->mrIid, $this->mrRef); - if ($format === 'json') { - $this->stdOut->writeln((string) json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + if ($this->writeFormatted($result, $format)) { return 0; } diff --git a/src/Cli/Command/MergeRequest/GetFiles.php b/src/Cli/Command/MergeRequest/GetFiles.php index 6f73e1f..a912130 100644 --- a/src/Cli/Command/MergeRequest/GetFiles.php +++ b/src/Cli/Command/MergeRequest/GetFiles.php @@ -20,7 +20,7 @@ protected function configure(): void 'format', 'f', InputOption::VALUE_OPTIONAL, - 'Output format: text, json. Defaults to text.', + 'Output format: text, json, md, llm. Defaults to text.', 'text' ); $this->configureNidAndMrIid(); @@ -33,8 +33,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $action = new GetMergeRequestFilesAction($this->client, new GitLabClient()); $result = $action($this->nid ?? '', $this->mrIid, $this->mrRef); - if ($format === 'json') { - $this->stdOut->writeln((string) json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + if ($this->writeFormatted($result, $format)) { return 0; } diff --git a/src/Cli/Formatter/AbstractFormatter.php b/src/Cli/Formatter/AbstractFormatter.php index dbde8fb..9b6387d 100644 --- a/src/Cli/Formatter/AbstractFormatter.php +++ b/src/Cli/Formatter/AbstractFormatter.php @@ -5,6 +5,8 @@ use mglaman\DrupalOrg\Result\Issue\IssueForkResult; use mglaman\DrupalOrg\Result\Issue\IssueResult; use mglaman\DrupalOrg\Result\Maintainer\MaintainerIssuesResult; +use mglaman\DrupalOrg\Result\MergeRequest\MergeRequestDiffResult; +use mglaman\DrupalOrg\Result\MergeRequest\MergeRequestFilesResult; use mglaman\DrupalOrg\Result\MergeRequest\MergeRequestListResult; use mglaman\DrupalOrg\Result\MergeRequest\MergeRequestStatusResult; use mglaman\DrupalOrg\Result\Project\ProjectIssuesResult; @@ -23,6 +25,8 @@ final public function format(ResultInterface $result): string $result instanceof ProjectReleasesResult => $this->formatProjectReleases($result), $result instanceof MergeRequestListResult => $this->formatMergeRequestList($result), $result instanceof MergeRequestStatusResult => $this->formatMergeRequestStatus($result), + $result instanceof MergeRequestFilesResult => $this->formatMergeRequestFiles($result), + $result instanceof MergeRequestDiffResult => $this->formatMergeRequestDiff($result), default => throw new \InvalidArgumentException( sprintf('Unsupported result type: %s', get_class($result)) ), @@ -36,4 +40,6 @@ abstract protected function formatMaintainerIssues(MaintainerIssuesResult $resul abstract protected function formatProjectReleases(ProjectReleasesResult $result): string; abstract protected function formatMergeRequestList(MergeRequestListResult $result): string; abstract protected function formatMergeRequestStatus(MergeRequestStatusResult $result): string; + abstract protected function formatMergeRequestFiles(MergeRequestFilesResult $result): string; + abstract protected function formatMergeRequestDiff(MergeRequestDiffResult $result): string; } diff --git a/src/Cli/Formatter/LlmFormatter.php b/src/Cli/Formatter/LlmFormatter.php index 7f14846..ff9fcf7 100644 --- a/src/Cli/Formatter/LlmFormatter.php +++ b/src/Cli/Formatter/LlmFormatter.php @@ -6,6 +6,8 @@ use mglaman\DrupalOrg\Result\Issue\IssueForkResult; use mglaman\DrupalOrg\Result\Issue\IssueResult; use mglaman\DrupalOrg\Result\Maintainer\MaintainerIssuesResult; +use mglaman\DrupalOrg\Result\MergeRequest\MergeRequestDiffResult; +use mglaman\DrupalOrg\Result\MergeRequest\MergeRequestFilesResult; use mglaman\DrupalOrg\Result\MergeRequest\MergeRequestListResult; use mglaman\DrupalOrg\Result\MergeRequest\MergeRequestStatusResult; use mglaman\DrupalOrg\Result\Project\ProjectIssuesResult; @@ -182,6 +184,40 @@ protected function formatMergeRequestStatus(MergeRequestStatusResult $result): s XML; } + protected function formatMergeRequestFiles(MergeRequestFilesResult $result): string + { + $filesXml = ''; + foreach ($result->files as $file) { + $path = $this->xmlEscape((string) $file['path']); + $newFile = (bool) $file['new_file'] ? 'true' : 'false'; + $deletedFile = (bool) $file['deleted_file'] ? 'true' : 'false'; + $renamedFile = (bool) $file['renamed_file'] ? 'true' : 'false'; + $filesXml .= " \n"; + $filesXml .= " {$path}\n"; + $filesXml .= " {$newFile}\n"; + $filesXml .= " {$deletedFile}\n"; + $filesXml .= " {$renamedFile}\n"; + $filesXml .= " \n"; + } + return "\n {$result->iid}\n \n{$filesXml} \n"; + } + + protected function formatMergeRequestDiff(MergeRequestDiffResult $result): string + { + $sourceBranch = $this->xmlEscape($result->sourceBranch); + $targetBranch = $this->xmlEscape($result->targetBranch); + $diff = $this->cdataWrap($result->diff); + + return << + {$result->iid} + {$sourceBranch} + {$targetBranch} + {$diff} + +XML; + } + private function toIso8601(int $timestamp): string { return (new \DateTimeImmutable())->setTimestamp($timestamp)->format(\DateTimeInterface::ATOM); diff --git a/src/Cli/Formatter/MarkdownFormatter.php b/src/Cli/Formatter/MarkdownFormatter.php index 07bc17b..4f57dbb 100644 --- a/src/Cli/Formatter/MarkdownFormatter.php +++ b/src/Cli/Formatter/MarkdownFormatter.php @@ -6,6 +6,8 @@ use mglaman\DrupalOrg\Result\Issue\IssueForkResult; use mglaman\DrupalOrg\Result\Issue\IssueResult; use mglaman\DrupalOrg\Result\Maintainer\MaintainerIssuesResult; +use mglaman\DrupalOrg\Result\MergeRequest\MergeRequestDiffResult; +use mglaman\DrupalOrg\Result\MergeRequest\MergeRequestFilesResult; use mglaman\DrupalOrg\Result\MergeRequest\MergeRequestListResult; use mglaman\DrupalOrg\Result\MergeRequest\MergeRequestStatusResult; use mglaman\DrupalOrg\Result\Project\ProjectIssuesResult; @@ -136,4 +138,38 @@ protected function formatMergeRequestStatus(MergeRequestStatusResult $result): s } return implode("\n", $lines); } + + protected function formatMergeRequestFiles(MergeRequestFilesResult $result): string + { + $lines = []; + $lines[] = "# MR !{$result->iid} Changed Files"; + $lines[] = ''; + foreach ($result->files as $file) { + $path = (string) $file['path']; + $annotations = []; + if ((bool) $file['new_file']) { + $annotations[] = '*(new)*'; + } + if ((bool) $file['deleted_file']) { + $annotations[] = '*(deleted)*'; + } + if ((bool) $file['renamed_file']) { + $annotations[] = '*(renamed)*'; + } + $suffix = $annotations !== [] ? ' ' . implode(' ', $annotations) : ''; + $lines[] = "- {$path}{$suffix}"; + } + return implode("\n", $lines); + } + + protected function formatMergeRequestDiff(MergeRequestDiffResult $result): string + { + $lines = []; + $lines[] = "# MR !{$result->iid} Diff (`{$result->sourceBranch}` → `{$result->targetBranch}`)"; + $lines[] = ''; + $lines[] = '```diff'; + $lines[] = $result->diff; + $lines[] = '```'; + return implode("\n", $lines); + } }