Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 37 additions & 36 deletions ProjGraph.slnx
Original file line number Diff line number Diff line change
@@ -1,51 +1,52 @@
<Solution>
<Folder Name="/Solution Items/">
<File Path=".editorconfig" />
<File Path=".gitattributes" />
<File Path=".gitignore" />
<File Path=".markdownlint.json" />
<File Path=".runsettings" />
<File Path="ARCHITECTURE.md" />
<File Path="CONTRIBUTING.md" />
<File Path="Directory.Build.props" />
<File Path="Directory.Packages.props" />
<File Path="global.json" />
<File Path="icon.png" />
<File Path="LICENSE" />
<File Path="Nuget.config" />
<File Path="README.md" />
<File Path="SECURITY.md" />
<File Path=".editorconfig"/>
<File Path=".gitattributes"/>
<File Path=".gitignore"/>
<File Path=".markdownlint.json"/>
<File Path=".runsettings"/>
<File Path="ARCHITECTURE.md"/>
<File Path="CONTRIBUTING.md"/>
<File Path="Directory.Build.props"/>
<File Path="Directory.Packages.props"/>
<File Path="global.json"/>
<File Path="icon.png"/>
<File Path="LICENSE"/>
<File Path="NuGet.config"/>
<File Path="README.md"/>
<File Path="SECURITY.md"/>
</Folder>
<Folder Name="/Solution Items/.githooks/">
<File Path=".githooks/pre-commit" />
<File Path=".githooks/pre-commit"/>
</Folder>
<Folder Name="/Solution Items/.github/">
<File Path=".github/dependabot.yml" />
<File Path=".github/dependabot.yml"/>
</Folder>
<Folder Name="/Solution Items/.github/workflows/">
<File Path=".github/workflows/ci.yml" />
<File Path=".github/workflows/ci.yml"/>
<File Path=".github/workflows/codeql.yml"/>
<File Path=".github/workflows/publish.yml" />
<File Path=".github/workflows/sonarqube.yml" />
<File Path=".github/workflows/docs-publish.yml"/>
<File Path=".github/workflows/publish.yml"/>
<File Path=".github/workflows/sonarqube.yml"/>
</Folder>
<Folder Name="/src/">
<Project Path="src/ProjGraph.Core/ProjGraph.Core.csproj" />
<Project Path="src/ProjGraph.Lib.ClassDiagram/ProjGraph.Lib.ClassDiagram.csproj" />
<Project Path="src/ProjGraph.Lib.Core/ProjGraph.Lib.Core.csproj" />
<Project Path="src/ProjGraph.Lib.EntityFramework/ProjGraph.Lib.EntityFramework.csproj" />
<Project Path="src/ProjGraph.Lib.ProjectGraph/ProjGraph.Lib.ProjectGraph.csproj" />
<Project Path="src/ProjGraph.Lib/ProjGraph.Lib.csproj" />
<Project Path="src/ProjGraph.Cli/ProjGraph.Cli.csproj" />
<Project Path="src/ProjGraph.Mcp/ProjGraph.Mcp.csproj" />
<Project Path="src/ProjGraph.Core/ProjGraph.Core.csproj"/>
<Project Path="src/ProjGraph.Lib.ClassDiagram/ProjGraph.Lib.ClassDiagram.csproj"/>
<Project Path="src/ProjGraph.Lib.Core/ProjGraph.Lib.Core.csproj"/>
<Project Path="src/ProjGraph.Lib.EntityFramework/ProjGraph.Lib.EntityFramework.csproj"/>
<Project Path="src/ProjGraph.Lib.ProjectGraph/ProjGraph.Lib.ProjectGraph.csproj"/>
<Project Path="src/ProjGraph.Lib/ProjGraph.Lib.csproj"/>
<Project Path="src/ProjGraph.Cli/ProjGraph.Cli.csproj"/>
<Project Path="src/ProjGraph.Mcp/ProjGraph.Mcp.csproj"/>
</Folder>
<Folder Name="/tests/">
<Project Path="tests/ProjGraph.Tests.Contract/ProjGraph.Tests.Contract.csproj" />
<Project Path="tests/ProjGraph.Tests.Integration.Cli/ProjGraph.Tests.Integration.Cli.csproj" />
<Project Path="tests/ProjGraph.Tests.Integration.Mcp/ProjGraph.Tests.Integration.Mcp.csproj" />
<Project Path="tests/ProjGraph.Tests.Shared/ProjGraph.Tests.Shared.csproj" />
<Project Path="tests/ProjGraph.Tests.Unit.ClassDiagram/ProjGraph.Tests.Unit.ClassDiagram.csproj" />
<Project Path="tests/ProjGraph.Tests.Unit.Core/ProjGraph.Tests.Unit.Core.csproj" />
<Project Path="tests/ProjGraph.Tests.Unit.EntityFramework/ProjGraph.Tests.Unit.EntityFramework.csproj" />
<Project Path="tests/ProjGraph.Tests.Unit.ProjectGraph/ProjGraph.Tests.Unit.ProjectGraph.csproj" />
<Project Path="tests/ProjGraph.Tests.Contract/ProjGraph.Tests.Contract.csproj"/>
<Project Path="tests/ProjGraph.Tests.Integration.Cli/ProjGraph.Tests.Integration.Cli.csproj"/>
<Project Path="tests/ProjGraph.Tests.Integration.Mcp/ProjGraph.Tests.Integration.Mcp.csproj"/>
<Project Path="tests/ProjGraph.Tests.Shared/ProjGraph.Tests.Shared.csproj"/>
<Project Path="tests/ProjGraph.Tests.Unit.ClassDiagram/ProjGraph.Tests.Unit.ClassDiagram.csproj"/>
<Project Path="tests/ProjGraph.Tests.Unit.Core/ProjGraph.Tests.Unit.Core.csproj"/>
<Project Path="tests/ProjGraph.Tests.Unit.EntityFramework/ProjGraph.Tests.Unit.EntityFramework.csproj"/>
<Project Path="tests/ProjGraph.Tests.Unit.ProjectGraph/ProjGraph.Tests.Unit.ProjectGraph.csproj"/>
</Folder>
</Solution>
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ dotnet run --project src/ProjGraph.Mcp
projgraph visualize ./MySolution.sln

# Mermaid format for documentation
projgraph visualize ./MySolution.slnx --format mermaid > docs/dependencies.mmd
projgraph visualize ./MySolution.slnx --format mermaid --output docs/dependencies.mmd
```

### Generate Database Diagrams
Expand All @@ -100,7 +100,7 @@ projgraph erd ./Data/MyDbContext.cs
projgraph erd ./Migrations/MyDbContextModelSnapshot.cs

# Output to Markdown for documentation
projgraph erd ./Data/MyDbContext.cs > docs/database-schema.md
projgraph erd ./Data/MyDbContext.cs --output docs/database-schema.md
```

### Visualize Class Hierarchies
Expand Down
34 changes: 34 additions & 0 deletions specs/007-cli-file-output/checklists/requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Specification Quality Checklist: File Output (`--output` flag)

**Purpose**: Validate specification completeness and quality before proceeding to planning
**Created**: 2026-02-24
**Feature**: [spec.md](../spec.md)

## Content Quality

- [x] No implementation details (languages, frameworks, APIs)
- [x] Focused on user value and business needs
- [x] Written for non-technical stakeholders
- [x] All mandatory sections completed

## Requirement Completeness

- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Requirements are testable and unambiguous
- [x] Success criteria are measurable
- [x] Success criteria are technology-agnostic (no implementation details)
- [x] All acceptance scenarios are defined
- [x] Edge cases are identified
- [x] Scope is clearly bounded
- [x] Dependencies and assumptions identified

## Feature Readiness

- [x] All functional requirements have clear acceptance criteria
- [x] User scenarios cover primary flows
- [x] Feature meets measurable outcomes defined in Success Criteria
- [x] No implementation details leak into specification

## Notes

- Items marked incomplete require spec updates before `/speckit.clarify` or `/speckit.plan`
17 changes: 17 additions & 0 deletions specs/007-cli-file-output/data-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Data Model Summary: File Output (`--output` flag)

## Entity Update: Command Settings

Adding a new field to CLI command settings to handle the output file path.

### Settings Entity (Used by `VisualizeCommand`, `ErdCommand`, `ClassDiagramCommand`)

- **Field**: `OutputPath`
Comment thread
HandyS11 marked this conversation as resolved.
- **Type**: `string?`
- **CLI Flag**: `-o|--output`
- **Description**: The path to save the diagram output to disk.

### DiagramOptions (No changes needed, existing fields)

- `bool ShowTitle`
- `bool WrapInMarkdownFence`
69 changes: 69 additions & 0 deletions specs/007-cli-file-output/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Implementation Plan: File Output (`--output` flag)

**Branch**: `007-cli-file-output` | **Date**: 2026-02-24 | **Spec**: [007-cli-file-output/spec.md](spec.md)
**Input**: Feature specification from `/specs/007-cli-file-output/spec.md`

## Summary

This feature adds a `-o|--output <file>` option to the `visualize`, `class-diagram`, and `erd` CLI commands. This allows users to reliably save diagrams to a file, avoiding shell redirection issues on Windows PowerShell. Each command will read the `OutputPath` from its settings and use `IFileSystem` to write the rendered diagram to disk. If the output file has a `.md` extension (or others except `.mmd`), the diagram will be automatically wrapped in a Mermaid markdown code fence.

## Technical Context

**Language/Version**: .NET 10.0 (C# 14+)
**Primary Dependencies**: `Spectre.Console`, `ProjGraph.Lib.Core` (for `IFileSystem`, `IOutputConsole`)
**Storage**: Files (rendered diagrams)
**Testing**: xUnit, FluentAssertions, Moq
**Target Platform**: .NET Core (Windows, Linux, macOS)
**Project Type**: CLI (`ProjGraph.Cli`)
**Performance Goals**: N/A (minimal overhead)
**Constraints**: MCP 1.0 Compliance (functionality remains in Lib), Zero Warnings, Strict SemVer
**Scale/Scope**: Thin change across 3 commands.

## Constitution Check

*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*

- [x] **I. Modern .NET 10 Baseline**: Targeting .NET 10.0+?
- [x] **II. MCP Native Interoperability**: Tool functionality already in Lib/MCP. CLI-specific file writing doesn't affect MCP.
- [x] **III. Library-First Core**: File writing logic will use `IFileSystem` from `Lib.Core`.
- [x] **IV. Absolute Testing Requirement**: Integration tests planned for CLI output.

## Project Structure

### Documentation (this feature)

```text
specs/[###-feature]/
├── plan.md # This file (/speckit.plan command output)
├── research.md # Phase 0 output (/speckit.plan command)
├── data-model.md # Phase 1 output (/speckit.plan command)
├── quickstart.md # Phase 1 output (/speckit.plan command)
├── contracts/ # Phase 1 output (/speckit.plan command - MUST include MCP schema)
└── tasks.md # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)
```

### Source Code (repository root)

```text
src/
├── ProjGraph.Core/ # Shared Domain Models & Constants
├── ProjGraph.Lib/ # Principles III: Core Business Logic Libraries
├── ProjGraph.Cli/ # Thin CLI tool wrapper
└── ProjGraph.Mcp/ # Principles II: MCP Server interface
```

tests/
├── contract/ # MCP Contact Tests
├── integration/ # CLI & MCP Integration
└── unit/ # Library Logic Unit Tests

**Structure Decision**: [Document the selected structure]

## Complexity Tracking

> **Fill ONLY if Constitution Check has violations that must be justified**

| Violation | Why Needed | Simpler Alternative Rejected Because |
| ----------- | ------------ | ------------------------------------- |
| [e.g., 4th project] | [current need] | [why 3 projects insufficient] |
| [e.g., Repository pattern] | [specific problem] | [why direct DB access insufficient] |
30 changes: 30 additions & 0 deletions specs/007-cli-file-output/quickstart.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Quickstart: File Output (`--output` flag)

## Usage: CLI Commands

Save diagrams directly to disk using the `-o|--output` flag.

### Save Mermaid Diagram to `.mmd` (No code fence)

```bash
projgraph visualize ./MySolution.sln -o diagram.mmd
```

### Save Mermaid Diagram to `.md` (Wrapped in code fence)

```bash
projgraph visualize ./MySolution.sln -o README.md
```

### ERD and Class Diagrams

```bash
projgraph erd ./Data/AppDbContext.cs -o erd.mmd
projgraph classdiagram ./Models/User.cs -o class.mmd
```

## Troubleshooting

If the output file cannot be written (permission denied or invalid path), the CLI will report a clear error message and return a non-zero exit code.
No diagrams will be written to `stdout` when the `--output` flag is used.
Success is confirmed with a "Saved to <path>" message in the informational output (stderr-like).
33 changes: 33 additions & 0 deletions specs/007-cli-file-output/research.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Research: File Output (`--output` flag)

## Decision: File Writing Orchestration

- **Chosen**: Logic stays in `Cli` commands but uses `IFileSystem` for execution.
- **Rationale**: Writing to a file is a transport-level concern. The commands already coordinate between analysis services and renderers. The `IFileSystem` provides abstraction for testing.
- **Alternatives Considered**:
- Creating a separate `IOutputFileService`. Rejected as it would be too much abstraction for a simple `WriteAllTextAsync` call.

## Decision: Markdown Fencing Logic

- **Chosen**: Automatically wrap in a code fence if the output file extension is `.md` or anything other than `.mmd`.
- **Rationale**: `.mmd` is a Mermaid-specific file format which should not contain markdown fences. `.md` files are intended for embedding in markdown environments where fences are required.
- **Implementation**:
- `visualize`: Check `settings.Output` extension.
- Set `DiagramOptions.WrapInMarkdownFence = !outputPath.EndsWith(".mmd", StringComparison.OrdinalIgnoreCase)`.

## Best Practices: CLI File Output

- Follow standard `-o|--output` flag naming.
- Use `System.IO.File` through `IFileSystem`.
- Default to UTF-8 without BOM (modern .NET default).
- Ensure output directory exists before writing (optional but good). I'll use `IFileSystem.GetDirectoryName` and `Directory.CreateDirectory`. Wait, `IFileSystem` doesn't have `CreateDirectory`.
- Let's check `IFileSystem` again.
- I might need to add `CreateDirectory` or `GetDirectoryName` is already there.

## Dependency Check

The `IFileSystem` should be used for testing.
I'll check `ProjGraph.Tests.Integration.Cli` to see how it's tested.

- It might use a real file system or a mock.
- `ProjGraph.Cli` usually uses `PhysicalFileSystem`.
84 changes: 84 additions & 0 deletions specs/007-cli-file-output/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Feature Specification: File Output (`--output` flag)

**Feature Branch**: `007-cli-file-output`
**Created**: 2026-02-24
**Status**: Draft
**Input**: User description: "Add -o|--output <file> to all CLI commands to write the diagram directly to disk instead of stdout. The current workflow requires `projgraph visualize ... > diagram.mmd` which is fragile on Windows PowerShell (BOM issues, encoding). A built-in flag is more reliable and enables easier CI integration. Each `Command` reads `OutputPath` from settings and calls `File.WriteAllTextAsync` after rendering."

## User Scenarios & Testing *(mandatory)*

### User Story 1 - Save diagram to file (Priority: P1)

As a user, I want to save the generated diagram directly to a file using a CLI flag so that I can avoid shell redirection issues like BOM or encoding errors on Windows PowerShell.

**Why this priority**: Core value of the feature. Shell redirection is fragile on some platforms, and a built-in flag is the standard way to handle file output in CLI tools.

**Independent Test**: Run `projgraph visualize <path> --output diagram.mmd` and verify that `diagram.mmd` is created with the correct content and no unwanted encoding issues.

**Acceptance Scenarios**:

1. **Given** a valid project path, **When** I run the visualize command with `-o output.mmd`, **Then** the diagram is saved to `output.mmd` and not printed to stdout.
2. **Given** an invalid output path (e.g., read-only directory), **When** I run the command with `--output`, **Then** the CLI should report a clear error message.

---

### User Story 2 - CI/CD Integration (Priority: P2)

As a developer, I want to use the `--output` flag in my build scripts or CI pipelines to generate documentation artifacts reliably.

**Why this priority**: Enables automation and improves the developer experience for documentation generation.

**Independent Test**: Running the command in a github action or script and verifying the exit code is 0 and the file exists.

**Acceptance Scenarios**:

1. **Given** a CI script, **When** I execute `projgraph erd <path> --output docs/erd.mmd`, **Then** the file is created in the specified directory.

---

### User Story 3 - Feedback on success (Priority: P2)

As a user, I want to receive confirmation that the file has been written successfully.

**Why this priority**: Good UX; users should know exactly what happened and where the file is.

**Independent Test**: Verify that a "Saved to <path>" message appears in the console output (stderr/info) when using the flag.

**Acceptance Scenarios**:

1. **Given** the `--output` flag is used, **When** the operation completes, **Then** an informational message stating "Saved to <file>" is displayed.

### Edge Cases

- **File already exists**: Should the CLI overwrite without asking (common for CLI tools) or should it warn? I'll assume it overwrites by default.
- **Directory doesn't exist**: Should the CLI create the directory or fail? I'll assume it should try to create it.
- **Permission denied**: The CLI should report it clearly.
- **Very large output**: Ensure the file writing is handled correctly without running out of memory (not expected to be an issue for Mermaid diagrams).

## Requirements *(mandatory)*

### Functional Requirements

- **FR-001**: System MUST provide a `-o|--output <file>` option for `visualize`, `classdiagram`, and `erd` commands.
- **FR-002**: System MUST write the rendered diagram to the specified file path if the output option is provided.
- **FR-003**: System MUST NOT write the diagram to stdout when the output option is provided.
- **FR-004**: System MUST ensure the output file is written using consistent encoding (UTF-8 without BOM).
- **FR-005**: System MUST report an error if the specified output file cannot be written (e.g., permission denied, invalid path).
- **FR-006**: System MUST display a success confirmation message upon successfully writing the file.
- **FR-007**: System MUST automatically wrap diagram output in a Markdown code fence if the output file extension indicates a Markdown environment (e.g., `.md`), unless the extension is `.mmd`.
- **FR-008**: System MUST attempt to create the parent directory if it does not exist before writing the output file.

### Success Criteria *(mandatory)*

### Measurable Outcomes

- **SC-001**: 100% of CLI commands (`visualize`, `classdiagram`, `erd`) correctly handle the `--output` flag.
- **SC-002**: Diagrams saved via `--output` contain the same diagram logic as stdout, with optional Markdown fencing as per `FR-007`.
- **SC-003**: Output files are consistently encoded as UTF-8 without BOM.
- **SC-004**: Command returns a non-zero exit code if file writing fails.

## Assumptions

- We will use `IFileSystem` for file writing to maintain testability.
- If the directory for the output file doesn't exist, we might or might not want to create it. (I'll assume we should try to create it or at least report it).
- We will use UTF-8 without BOM as it is the standard for most modern tools, especially for Mermaid diagrams.
Loading
Loading