Skip to content
Open
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
6 changes: 4 additions & 2 deletions coverage.txt
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@
ℹ graph.js | 18.52 | 50.00 | 0.00 | 12-25 34-60 67-75 81-85 94-101 110-145 152-182 189-215 222-277 284-326 333-355 362-395 402-424 432-464 471-490 497-592 600-620
ℹ imap.js | 24.43 | 50.00 | 0.00 | 13-16 29-53 60-64 70-74 83-91 99-112 119-153 160-203 210-245 252-284 291-324 332-339 346-368 375-422 431-441
ℹ tools.js | 28.57 | 100.00 | 0.00 | 15-199
ℹ fileCreate | | | |
ℹ pptx.js | 39.86 | 100.00 | 0.00 | 25-28 142-180 190-202 210-228 242-259 269-283 296-463 475-478 495-558
ℹ fileExtract | | | |
ℹ docx.js | 48.68 | 100.00 | 0.00 | 28-66
ℹ docxParser.js | 18.22 | 100.00 | 0.00 | 15-74 81-119 126-145 152-158 166-214
Expand Down Expand Up @@ -136,13 +138,13 @@
ℹ inputPanel.js | 100.00 | 100.00 | 100.00 |
ℹ markdownText.js | 72.95 | 78.82 | 83.02 | 16-18 40-118 158 182-184 262-263 274-275 304-310 325-333 336-338 348-354 369-390 401-402 453-454 457-458 464
ℹ messageBubble.js | 85.30 | 53.13 | 71.43 | 139-144 163-166 181-190 195-202 207-214 255-259
ℹ messageList.js | 79.76 | 72.73 | 50.00 | 64 81-84 109-133 143-166 175 182-187 230 238 246 255-261 270 278-283 305-307 348-349 392
ℹ messageList.js | 80.00 | 72.73 | 50.00 | 69 86-89 114-138 148-171 180 187-192 235 243 251 260-266 275 283-288 310-312 353-354 397
ℹ messages.js | 100.00 | 94.44 | 100.00 |
ℹ panels.js | 100.00 | 100.00 | 100.00 |
ℹ statusBar.js | 90.82 | 81.25 | 100.00 | 22-23 34-40
ℹ workspace | | | |
ℹ loadAgents.js | 100.00 | 87.50 | 100.00 |
ℹ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ℹ all files | 66.68 | 83.26 | 51.57 |
ℹ all files | 65.88 | 83.27 | 51.02 |
ℹ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ℹ end of coverage report
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-08-24
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
## Context

The existing PPTX tool at `src/tools/fileExtract/pptx.js` uses `pptx-parser` to extract content from presentations. This tool is read-only. The agent harness needs a complementary write tool that creates presentations from structured content. The tool must follow the established pattern: zod schema, implementation function, registration in `src/tools/index.js`, and appropriate capability permissions.

## Goals / Non-Goals

**Goals:**
- Create a `createPptx` tool that generates .pptx files from structured input
- Support slide layouts: title, content, two-column, comparison, quote, image-only
- Support text formatting: font family, size, color, bold, italic, alignment
- Support image embedding from file paths with MIME validation
- Support table rendering on slides
- Support template loading from existing .pptx files
- Validate output paths against the allowed write directory
- Validate image files via extension whitelist and magic byte checks

**Non-Goals:**
- Chart generation (deferred to follow-up PR)
- Custom font file upload
- Slide transitions and animations
- Slide master customization beyond template support
- PPTX-to-PDF conversion
- Cloud storage integration

## Decisions

**Decision 1: Use pptxgenjs over alternatives**
- Rationale: Pure JavaScript, no native dependencies, 2M+ weekly downloads, actively maintained, supports all required features (layouts, images, tables, templates). `node-pptx` is less maintained and lacks chart support.
- Alternative: `python-pptx` via child process — rejected because it requires Python installation, adds system dependency, and breaks cross-platform consistency.

**Decision 2: Separate file from read tool**
- Rationale: The existing `src/tools/fileExtract/pptx.js` handles reading. The new tool goes in `src/tools/fileCreate/pptx.js` to maintain the read/write separation pattern used elsewhere (e.g., `fileExtract/docx.js` vs `fileCreate/docx.js`).
- This keeps each tool focused and testable.

**Decision 3: Zod v4 schema with explicit types**
- Rationale: The project uses Zod v4 for all tool input validation. The schema must be exported alongside the implementation for the tool registration system.
- The schema will use `z.object()` with nested arrays for slides, and optional fields for layout-specific content.

**Decision 4: Image validation via extension + magic bytes**
- Rationale: No external MIME detection library is needed. We validate by file extension (.png, .jpg, .jpeg, .gif, .bmp) and check magic bytes for the first three formats (PNG: 89 50 4E 47, JPEG: FF D8 FF, GIF: 47 49 46 38). This is consistent with the project's security rules for file uploads.

**Decision 5: Template loading via pptxgenjs API**
- Rationale: pptxgenjs has built-in template loading that preserves master slides and layouts. This is more reliable than manually reconstructing template structure. The template path is validated against the write directory before loading.

**Decision 6: Error hierarchy with PptxError**
- Rationale: The project requires domain-specific error classes extending `Error`. A `PptxError` class will extend the project's `AppError` class with a `code` property for structured error handling.

## Risks / Trade-offs

**Risk:** pptxgenjs v3.x API surface is large — some features may require trial and error.
→ Mitigation: Start with the most common layouts and text formatting. Add advanced features only if tests fail.

**Risk:** Image embedding requires file reads which could be slow for large images.
→ Mitigation: Limit image dimensions in validation. Use pptxgenjs's built-in compression.

**Risk:** Template loading from user-provided paths could be a security concern.
→ Mitigation: Validate template path against the write directory using the same path resolver used for output paths. Check that the file is a valid ZIP (PPTX structure).

**Risk:** pptxgenjs creates files synchronously internally.
→ Mitigation: Wrap the save operation in a timeout. The library is fast for typical presentations (< 50 slides).

## Migration Plan

No migration needed. This is a new tool that coexists with the existing read tool. No breaking changes to any existing APIs.

## Open Questions

- Should the tool accept base64-encoded images inline, or only file paths? Decision: file paths only for v1. Base64 can be added later if needed.
- Should the tool return the file path on success, or the file buffer? Decision: return the file path (consistent with other file-write tools in the codebase).
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## Why

The existing PPTX tool (`src/tools/fileExtract/pptx.js`) is read-only — it extracts content from existing presentations. Marketing teams need to create slide decks: pitch decks, status reports, training materials, and client presentations. Currently the agent can only read PPTX files, not create them. Users must fall back to shell commands (python-pptx, libreoffice) or manual creation, which is fragile and loses formatting fidelity.

## What Changes

- Add a new tool `src/tools/fileCreate/pptx.js` that creates PowerPoint presentations using `pptxgenjs`
- Accept structured content: slides with layouts, text, formatting, images, and tables
- Support slide layouts: title, content, two-column, comparison, quote, image-only
- Support text formatting: font family, size, color, bold, italic, alignment
- Support image embedding from file paths with MIME validation
- Support table rendering on slides
- Support template loading from existing .pptx files
- Register tool in `src/tools/index.js` with `filesystem:write` capability
- Add `pptxgenjs` as a new npm dependency
- Create new OpenSpec spec for pptx-creation capability

## Capabilities

### New Capabilities
- `pptx-creation`: PowerPoint presentation creation with slides, layouts, formatting, images, tables, and template support

### Modified Capabilities
- None

## Impact

- **New dependency**: `pptxgenjs` (v3.x+)
- **New file**: `src/tools/fileCreate/pptx.js`
- **Modified file**: `src/tools/index.js` (tool registration)
- **Modified file**: `package.json` (dependency)
- **New spec**: `openspec/specs/pptx-creation/spec.md`
- **New tests**: `tests/unit/tools/pptx.test.js`
- **Non-goals**: Chart generation, custom font file upload, slide transitions/animations
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
## ADDED Requirements

### Requirement: Create presentation from structured content
The system SHALL create a PowerPoint presentation (.pptx) from structured input containing slides with titles, content, and formatting.

#### Scenario: Create presentation with title slide
- **WHEN** a presentation input with a title slide is provided
- **THEN** the system generates a .pptx file with a title slide containing the specified title and subtitle

#### Scenario: Create presentation with content slide
- **WHEN** a presentation input with a content slide is provided
- **THEN** the system generates a .pptx file with a content slide containing bullet points

#### Scenario: Create presentation with multiple slides
- **WHEN** a presentation input with multiple slides is provided
- **THEN** the system generates a .pptx file with all specified slides in order

#### Scenario: Create presentation with empty slides array
- **WHEN** a presentation input with an empty slides array is provided
- **THEN** the system generates a .pptx file with one default blank slide

### Requirement: Support slide layouts
The system SHALL support the following slide layouts: title, content, two-column, comparison, quote, and image-only.

#### Scenario: Create slide with title layout
- **WHEN** a slide with layout "title" is provided
- **THEN** the system creates a slide with a large title area and optional subtitle

#### Scenario: Create slide with content layout
- **WHEN** a slide with layout "content" is provided
- **THEN** the system creates a slide with a title and bullet point content area

#### Scenario: Create slide with two-column layout
- **WHEN** a slide with layout "two-column" is provided
- **THEN** the system creates a slide with two side-by-side content columns

#### Scenario: Create slide with comparison layout
- **WHEN** a slide with layout "comparison" is provided
- **THEN** the system creates a slide with two columns for comparing items

#### Scenario: Create slide with quote layout
- **WHEN** a slide with layout "quote" is provided
- **THEN** the system creates a slide with centered quote text and optional attribution

#### Scenario: Create slide with image-only layout
- **WHEN** a slide with layout "image-only" is provided
- **THEN** the system creates a slide with a full-slide image

#### Scenario: Create slide with unknown layout
- **WHEN** a slide with an unknown layout name is provided
- **THEN** the system defaults to the "content" layout

### Requirement: Apply text formatting
The system SHALL apply text formatting including font family, font size, font color, bold, italic, and alignment.

#### Scenario: Apply bold formatting
- **WHEN** a text element with bold=true is provided
- **THEN** the system renders the text in bold

#### Scenario: Apply italic formatting
- **WHEN** a text element with italic=true is provided
- **THEN** the system renders the text in italic

#### Scenario: Apply custom font color
- **WHEN** a text element with a hex color code is provided
- **THEN** the system renders the text in the specified color

#### Scenario: Apply custom font size
- **WHEN** a text element with a font size is provided
- **THEN** the system renders the text at the specified size

#### Scenario: Apply text alignment
- **WHEN** a text element with an alignment is provided
- **THEN** the system aligns the text as specified (left, center, right)

#### Scenario: Apply custom font family
- **WHEN** a text element with a font family is provided
- **THEN** the system renders the text using the specified font

### Requirement: Embed images in slides
The system SHALL embed images from file paths into slides with MIME validation.

#### Scenario: Embed PNG image
- **WHEN** a slide with a valid PNG image path is provided
- **THEN** the system embeds the image on the slide

#### Scenario: Embed JPEG image
- **WHEN** a slide with a valid JPEG image path is provided
- **THEN** the system embeds the image on the slide

#### Scenario: Reject unsupported image format
- **WHEN** a slide with an unsupported image file extension is provided
- **THEN** the system throws an error listing supported formats

#### Scenario: Reject non-image file with valid extension
- **WHEN** a slide with a file that has a valid image extension but invalid content is provided
- **THEN** the system throws an error indicating the file is not a valid image

#### Scenario: Embed image with custom position
- **WHEN** a slide with an image and custom x/y position is provided
- **THEN** the system places the image at the specified position

#### Scenario: Embed image with custom dimensions
- **WHEN** a slide with an image and custom width/height is provided
- **THEN** the system resizes the image to the specified dimensions

### Requirement: Render tables on slides
The system SHALL render tabular data on slides.

#### Scenario: Render simple table
- **WHEN** a slide with a table is provided
- **THEN** the system renders the table with rows and columns

#### Scenario: Render table with header row
- **WHEN** a slide with a table that has a header row is provided
- **THEN** the system renders the header row with bold formatting

#### Scenario: Render empty table
- **WHEN** a slide with an empty table is provided
- **THEN** the system renders an empty table structure

### Requirement: Load presentation from template
The system SHALL load an existing .pptx file as a template and apply new content to it.

#### Scenario: Load template and add slides
- **WHEN** a template path and new content are provided
- **THEN** the system loads the template and appends new slides

#### Scenario: Load invalid template file
- **WHEN** a template path pointing to a non-.pptx file is provided
- **THEN** the system throws an error indicating the file is not a valid PPTX

#### Scenario: Load template from outside write directory
- **WHEN** a template path outside the allowed write directory is provided
- **THEN** the system throws an error indicating the path is not allowed

### Requirement: Validate output path
The system SHALL validate that the output path is within the allowed write directory.

#### Scenario: Valid output path
- **WHEN** an output path within the allowed directory is provided
- **THEN** the system proceeds with file creation

#### Scenario: Output path outside write directory
- **WHEN** an output path outside the allowed directory is provided
- **THEN** the system throws an error indicating the path is not allowed

#### Scenario: Output path with directory traversal
- **WHEN** an output path containing "../" is provided
- **THEN** the system throws an error indicating the path is not allowed

### Requirement: Generate valid PPTX file
The system SHALL generate a valid .pptx file that can be opened by standard presentation software.

#### Scenario: Generate valid PPTX file
- **WHEN** a valid presentation input is provided
- **THEN** the system generates a .pptx file that is a valid ZIP archive with correct PPTX structure

#### Scenario: Generated file has correct extension
- **WHEN** a presentation is generated with a .pptx extension
- **THEN** the generated file has the .pptx extension

### Requirement: Handle text overflow
The system SHALL handle text that exceeds slide boundaries by shrinking text to fit.

#### Scenario: Shrink text to fit slide
- **WHEN** a text element exceeds the available slide space
- **THEN** the system shrinks the font size to fit the text within the slide boundaries

#### Scenario: Default font fallback
- **WHEN** a specified font family is not available in pptxgenjs
- **THEN** the system falls back to a default font (Arial)
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
## 1. Setup

- [ ] 1.1 Add pptxgenjs dependency to package.json
- [ ] 1.2 Create src/tools/fileCreate/ directory structure
- [ ] 1.3 Create PptxError class extending AppError

## 2. Zod Schema

- [ ] 2.1 Define PptxInputSchema with zod v4 (outputPath, templatePath, slides array)
- [ ] 2.2 Define SlideSchema with layout, title, content, images, tables
- [ ] 2.3 Define ImageSchema with path, x, y, width, height
- [ ] 2.4 Define TableSchema with rows, headers, styling
- [ ] 2.5 Define TextStyleSchema with font, size, color, bold, italic, alignment

## 3. Core Implementation

- [ ] 3.1 Implement validateImagePath helper (extension whitelist + magic bytes)
- [ ] 3.2 Implement validateOutputPath helper (write directory check + traversal prevention)
- [ ] 3.3 Implement validateTemplatePath helper (validates PPTX structure)
- [ ] 3.4 Implement createTextRun helper for pptxgenjs text runs
- [ ] 3.5 Implement createSlide helper that maps slide schema to pptxgenjs slide
- [ ] 3.6 Implement createPptx main function (creates presentation, adds slides, saves)
- [ ] 3.7 Implement template loading via pptxgenjs API
- [ ] 3.8 Implement text overflow handling (shrink-to-fit)
- [ ] 3.9 Implement font fallback (Arial default)

## 4. Tool Registration

- [ ] 4.1 Register createPptx tool in src/tools/index.js with filesystem:write capability
- [ ] 4.2 Export schema alongside implementation

## 5. Tests

- [ ] 5.1 Create tests/unit/tools/pptx.test.js
- [ ] 5.2 Test: create presentation with title slide
- [ ] 5.3 Test: create presentation with content slide
- [ ] 5.4 Test: create presentation with multiple slides
- [ ] 5.5 Test: create presentation with empty slides array
- [ ] 5.6 Test: all slide layouts (title, content, two-column, comparison, quote, image-only)
- [ ] 5.7 Test: text formatting (bold, italic, color, size, alignment, font family)
- [ ] 5.8 Test: embed PNG image
- [ ] 5.9 Test: embed JPEG image
- [ ] 5.10 Test: reject unsupported image format
- [ ] 5.11 Test: reject non-image file with valid extension
- [ ] 5.12 Test: render table with header row
- [ ] 5.13 Test: load template and add slides
- [ ] 5.14 Test: load invalid template file
- [ ] 5.15 Test: validate output path (valid, outside directory, traversal)
- [ ] 5.16 Test: generate valid PPTX file (ZIP structure validation)

## 6. Verification

- [ ] 6.1 Run npm run test
- [ ] 6.2 Run npm run lint
- [ ] 6.3 Run npm run coverage
Loading