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
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-08-12
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
## Context

Madz currently supports vision-based file processing (`src/tools/vision.js`) and web content extraction (`src/tools/web.js`), but has no tools for extracting content from common office and personal file formats. Users working with `.docx`, `.pptx`, `.xlsx`, PDF, and similar formats cannot query their content through the agent.

Most office formats share a common architecture: ZIP archives containing XML files. This presents an opportunity for a shared extraction layer. PDF is architecturally distinct and requires a separate code path.

## Goals / Non-Goals

**Goals:**
- Provide a shared ZIP/XML extraction utility for archive-based formats
- Implement format-specific extraction tools for docx, pptx, xlsx, and PDF
- Output structured markdown (or JSON for tabular data) suitable for LLM consumption
- Register all tools in the central registry with appropriate permissions
- Include comprehensive unit tests with sample file fixtures

**Non-Goals:**
- Legacy binary formats (`.ppt`, `.xls`, `.doc`) — require OLE compound file parsing
- OCR for scanned PDFs — documented as future enhancement
- Embedded media extraction (images, audio, video within documents)
- Charts, macros, pivot tables, or complex spreadsheet formulas
- Writing/creating documents — extraction only

## Decisions

### Decision 1: Shared ZIP/XML Utility
**Choice**: Create `extractZipXml(filePath)` returning `Map<string, string>` (filename → XML content).
**Rationale**: Eliminates code duplication across 5+ format parsers. Centralizes error handling for corrupted archives. Each format-specific parser receives the same consistent input.
**Alternatives considered**:
- Inline ZIP handling in each tool → rejected (code duplication, inconsistent error handling)
- External library abstraction → rejected (unnecessary indirection for a single function)

### Decision 2: PDF as Separate Code Path
**Choice**: Use `pdf-parse` directly; do not force PDF into ZIP/XML pattern.
**Rationale**: PDF is a fundamentally different format with its own binary structure. The ZIP/XML pattern does not apply. `pdf-parse` is lightweight, well-maintained, and sufficient for text extraction.
**Alternatives considered**:
- `pdfjs-dist` (Mozilla) → rejected (larger bundle, more complex API)
- Custom PDF parser → rejected (unnecessary complexity)

### Decision 3: Markdown as Primary Output
**Choice**: All tools output markdown; xlsx additionally supports JSON output.
**Rationale**: Markdown is the natural input format for LLMs. The project already depends on `marked`. JSON output for xlsx enables programmatic consumption of tabular data.
**Alternatives considered**:
- Plain text → rejected (loses structure: headings, lists, tables)
- HTML → rejected (not LLM-friendly, requires stripping)

### Decision 4: Tool Registration Pattern
**Choice**: Follow the existing pattern from `src/tools/vision.js` — async function with zod input schema, registered in `src/tools/index.js` with permission tier and classification.
**Rationale**: Consistency with existing codebase. Minimal learning curve for future contributors.

### Decision 5: Sandbox Permissions
**Choice**: All tools require `filesystem:read` permission. File paths are validated against the sandbox path resolver.
**Rationale**: Tools read files from the filesystem. No write operations needed. Sandbox constraints from `src/tools/shell.js` apply.

## Risks / Trade-offs

[Risk] ZIP archives may be corrupted or password-protected → Mitigation: Graceful error handling with descriptive messages. Tools return structured error output, not crashes.

[Risk] Large documents may cause memory issues → Mitigation: Stream processing where possible. Document size limits in tool descriptions.

[Risk] XML namespaces vary across formats → Mitigation: Each format parser handles its own namespace resolution. Shared utility provides namespace-agnostic filename lookup.

[Risk] PDF text extraction may produce garbled output for complex layouts → Mitigation: Document limitations in tool description. OCR path documented for future enhancement.

[Risk] New dependencies increase attack surface → Mitigation: Only `pdf-parse` added (minimal, well-audited). ZIP handling uses existing Node.js `zlib` or `archiver` dependencies.

## Migration Plan

No migration required. This is a greenfield feature addition. Existing tools and workflows are unaffected.

## Open Questions

1. Should xlsx output include cell formatting (bold, color, formulas) or just values? → Default to values only; formatting can be added later.
2. Should pptx extraction include slide notes by default? → Yes, included in markdown output.
3. Should we support `.odt`/`.ods`/`.odp` (OpenDocument) in v1? → Yes, they follow the same ZIP/XML pattern as Office formats.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## Why

Users frequently work with proprietary or binary file formats (`.docx`, `.pptx`, `.xlsx`, PDF, etc.) that are opaque to LLMs and text-based tooling. Converting these formats into structured markdown or JSON enables content extraction, analysis, and integration into the agent workflow. Most common office formats are ZIP archives containing XML, making extraction feasible with shared parsing utilities.

## What Changes

- Add a shared `extract-zip-xml` utility for ZIP-based format decompression and XML parsing
- Implement format-specific extraction tools: `docx-to-markdown`, `pptx-to-markdown`, `xlsx-to-markdown`, `xlsx-to-json`, `pdf-to-markdown`
- Register all new tools in the central tool registry (`src/tools/index.js`) with appropriate permissions and classifications
- Add `pdf-parse` dependency for PDF text extraction
- Create comprehensive unit tests with sample file fixtures

## Capabilities

### New Capabilities
- `file-extraction`: Shared ZIP/XML extraction utility for decompressing and parsing ZIP-based document formats (docx, pptx, xlsx, odt, ods, odp, epub, pages, numbers, key)
- `docx-extraction`: Convert `.docx` files to structured markdown with headings, paragraphs, lists, tables, and inline formatting
- `pptx-extraction`: Convert `.pptx` files to markdown with slide titles, bullet points, and speaker notes
- `xlsx-extraction`: Convert `.xlsx` files to markdown tables or JSON with cell values, types, and references
- `pdf-extraction`: Extract text from PDF files to markdown; OCR path documented for future enhancement

### Modified Capabilities
<!-- None — no existing spec-level requirements are changing -->

## Impact

- **Affected code**: `src/tools/index.js` (tool registration), `src/tools/vision.js` (pattern reference), `src/tools/shell.js` (sandbox constraints)
- **New dependencies**: `pdf-parse` for PDF extraction; `adm-zip` or `jszip` for ZIP handling (likely already available via existing dependencies)
- **New files**: `src/tools/fileExtract/` directory with utility and format-specific parsers
- **Test fixtures**: Sample docx, pptx, xlsx, and PDF files in `tests/fixtures/`
- **Non-goals**: Legacy binary formats (`.ppt`, `.xls`), OCR for scanned PDFs, embedded media extraction, charts/macros/pivot tables
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
## ADDED Requirements

### Requirement: DOCX to markdown conversion
The system SHALL convert `.docx` files to structured markdown, preserving headings, paragraphs, lists, tables, and inline formatting (bold, italic, code).

#### Scenario: Extract headings from docx
- **WHEN** a docx file with heading styles is provided
- **THEN** the system outputs markdown headings (`#`, `##`, `###`) matching the document hierarchy

#### Scenario: Extract paragraphs from docx
- **WHEN** a docx file with body text is provided
- **THEN** the system outputs markdown paragraphs with proper line breaks

#### Scenario: Extract lists from docx
- **WHEN** a docx file with ordered or unordered lists is provided
- **THEN** the system outputs markdown list items (`-` for unordered, `1.` for ordered)

#### Scenario: Extract tables from docx
- **WHEN** a docx file with tables is provided
- **THEN** the system outputs markdown tables with proper column alignment

#### Scenario: Handle empty docx file
- **WHEN** an empty docx file is provided
- **THEN** the system returns an empty string

#### Scenario: Handle missing document.xml
- **WHEN** a docx file without word/document.xml is provided
- **THEN** the system throws a descriptive error
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
## ADDED Requirements

### Requirement: ZIP archive extraction
The system SHALL decompress ZIP-based document archives and extract their XML content files into a structured map of filename to content.

#### Scenario: Successful ZIP extraction
- **WHEN** a valid ZIP archive file path is provided
- **THEN** the system returns a map of filename strings to their XML content strings

#### Scenario: Corrupted ZIP archive
- **WHEN** a corrupted or invalid ZIP file is provided
- **THEN** the system throws a descriptive error without crashing

#### Scenario: Password-protected ZIP archive
- **WHEN** a password-protected ZIP file is provided
- **THEN** the system throws an error indicating the archive is password-protected

### Requirement: XML content retrieval by filename
The system SHALL locate specific XML files within a ZIP archive by their known filenames regardless of internal directory structure.

#### Scenario: Locate document.xml in docx
- **WHEN** the utility is asked for "word/document.xml" in a docx file
- **THEN** the system returns the XML content of that file

#### Scenario: Locate slide files in pptx
- **WHEN** the utility is asked for "ppt/slides/slide1.xml" in a pptx file
- **THEN** the system returns the XML content of that slide file

### Requirement: File format validation
The system SHALL validate that a file is a supported ZIP-based format before attempting extraction.

#### Scenario: Validate docx extension
- **WHEN** a file with `.docx` extension is provided
- **THEN** the system confirms it is a supported format

#### Scenario: Reject unsupported format
- **WHEN** a file with an unsupported extension is provided
- **THEN** the system throws an error listing supported formats
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## ADDED Requirements

### Requirement: PDF text extraction
The system SHALL extract text content from PDF files and output it as structured markdown.

#### Scenario: Extract text from simple PDF
- **WHEN** a PDF file with plain text content is provided
- **THEN** the system outputs the extracted text as markdown paragraphs

#### Scenario: Extract text from multi-page PDF
- **WHEN** a multi-page PDF file is provided
- **THEN** the system outputs text from all pages in sequential order

#### Scenario: Handle PDF with no extractable text
- **WHEN** a PDF file contains only images (no text layer)
- **THEN** the system returns an error indicating no text could be extracted

#### Scenario: Handle empty PDF file
- **WHEN** an empty or minimal PDF file is provided
- **THEN** the system returns an empty string

#### Scenario: Handle PDF with special characters
- **WHEN** a PDF file contains Unicode characters
- **THEN** the system preserves Unicode characters in the output

### Requirement: PDF extraction error handling
The system SHALL provide descriptive error messages for PDF extraction failures.

#### Scenario: Handle corrupted PDF
- **WHEN** a corrupted PDF file is provided
- **THEN** the system throws a descriptive error without crashing

#### Scenario: Handle password-protected PDF
- **WHEN** a password-protected PDF file is provided
- **THEN** the system throws an error indicating the file is password-protected
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
## ADDED Requirements

### Requirement: PPTX to markdown conversion
The system SHALL convert `.pptx` files to structured markdown, preserving slide titles, bullet points, speaker notes, and basic text content.

#### Scenario: Extract slide titles from pptx
- **WHEN** a pptx file with slide titles is provided
- **THEN** the system outputs markdown headings (`#`) for each slide title

#### Scenario: Extract bullet points from pptx
- **WHEN** a pptx file with bullet points is provided
- **THEN** the system outputs markdown unordered list items (`-`) for each bullet

#### Scenario: Extract speaker notes from pptx
- **WHEN** a pptx file with speaker notes is provided
- **THEN** the system outputs markdown text prefixed with "Speaker Notes:"

#### Scenario: Handle slides without titles
- **WHEN** a pptx slide has no title text
- **THEN** the system outputs a numbered slide separator (e.g., `---`)

#### Scenario: Handle empty pptx file
- **WHEN** an empty pptx file is provided
- **THEN** the system returns an empty string

#### Scenario: Handle missing slide files
- **WHEN** a pptx file with missing slide XML is provided
- **THEN** the system skips the missing slide and continues processing remaining slides
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
## ADDED Requirements

### Requirement: XLSX to markdown table conversion
The system SHALL convert `.xlsx` spreadsheets to markdown tables, preserving cell values, column headers, and row structure.

#### Scenario: Extract single-sheet xlsx to markdown
- **WHEN** a single-sheet xlsx file is provided
- **THEN** the system outputs a markdown table with headers from the first row and data from subsequent rows

#### Scenario: Extract multi-sheet xlsx to markdown
- **WHEN** a multi-sheet xlsx file is provided
- **THEN** the system outputs separate markdown tables for each sheet, separated by sheet name headers

#### Scenario: Handle empty xlsx file
- **WHEN** an empty xlsx file is provided
- **THEN** the system returns an empty string

#### Scenario: Handle numeric cell values
- **WHEN** a cell contains a numeric value
- **THEN** the system outputs the numeric value as a string in the markdown table

#### Scenario: Handle merged cells
- **WHEN** a cell is merged with another cell
- **THEN** the system outputs the value in the top-left cell position and empty strings for merged positions

### Requirement: XLSX to JSON conversion
The system SHALL convert `.xlsx` spreadsheets to JSON, preserving cell values, types, and sheet structure.

#### Scenario: Extract xlsx to JSON with sheet names
- **WHEN** a multi-sheet xlsx file is provided
- **THEN** the system returns a JSON object with sheet names as keys and arrays of row objects as values

#### Scenario: Preserve cell data types in JSON
- **WHEN** a cell contains a number, string, or boolean value
- **THEN** the system preserves the original data type in the JSON output

#### Scenario: Handle empty rows in JSON output
- **WHEN** an xlsx file has empty rows
- **THEN** the system includes empty row objects in the JSON output
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
## 1. Setup and Dependencies

- [ ] 1.1 Add pdf-parse dependency to package.json
- [ ] 1.2 Create src/tools/fileExtract/ directory structure
- [ ] 1.3 Create extractZipXml utility function in src/tools/fileExtract/zipExtractor.js
- [ ] 1.4 Create format validation utility in src/tools/fileExtract/formatValidator.js

## 2. DOCX Extraction Tool

- [ ] 2.1 Implement docxToMarkdown parser in src/tools/fileExtract/docxParser.js
- [ ] 2.2 Handle headings, paragraphs, lists, tables, and inline formatting
- [ ] 2.3 Create docx tool wrapper function in src/tools/fileExtract/docx.js
- [ ] 2.4 Write unit tests for docx extraction with sample fixtures

## 3. PPTX Extraction Tool

- [ ] 3.1 Implement pptxToMarkdown parser in src/tools/fileExtract/pptxParser.js
- [ ] 3.2 Handle slide titles, bullet points, and speaker notes
- [ ] 3.3 Create pptx tool wrapper function in src/tools/fileExtract/pptx.js
- [ ] 3.4 Write unit tests for pptx extraction with sample fixtures

## 4. XLSX Extraction Tool

- [ ] 4.1 Implement xlsxToMarkdown parser in src/tools/fileExtract/xlsxParser.js
- [ ] 4.2 Handle single and multi-sheet conversion to markdown tables
- [ ] 4.3 Implement xlsxToJSON converter in src/tools/fileExtract/xlsxJson.js
- [ ] 4.4 Create xlsx tool wrapper function in src/tools/fileExtract/xlsx.js
- [ ] 4.5 Write unit tests for xlsx extraction with sample fixtures

## 5. PDF Extraction Tool

- [ ] 5.1 Implement pdfToMarkdown parser in src/tools/fileExtract/pdfParser.js
- [ ] 5.2 Handle text extraction, multi-page documents, and Unicode
- [ ] 5.3 Create pdf tool wrapper function in src/tools/fileExtract/pdf.js
- [ ] 5.4 Write unit tests for PDF extraction with sample fixtures

## 6. Tool Registration and Integration

- [ ] 6.1 Register all extraction tools in src/tools/index.js
- [ ] 6.2 Add TOOL_PERMISSIONS entries for all new tools
- [ ] 6.3 Add TOOL_CLASSIFICATIONS entries for all new tools
- [ ] 6.4 Verify tools are discoverable by the skills registry

## 7. Testing and Verification

- [ ] 7.1 Run npm test and verify all tests pass
- [ ] 7.2 Run npm run lint and fix any lint errors
- [ ] 7.3 Run npm start and verify application starts without crashing
- [ ] 7.4 Create sample fixture files for manual testing
32 changes: 32 additions & 0 deletions openspec/specs/docx-extraction/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# docx-extraction Specification

## Purpose
TBD - created by archiving change file-format-extraction-tools. Update Purpose after archive.
## Requirements
### Requirement: DOCX to markdown conversion
The system SHALL convert `.docx` files to structured markdown, preserving headings, paragraphs, lists, tables, and inline formatting (bold, italic, code).

#### Scenario: Extract headings from docx
- **WHEN** a docx file with heading styles is provided
- **THEN** the system outputs markdown headings (`#`, `##`, `###`) matching the document hierarchy

#### Scenario: Extract paragraphs from docx
- **WHEN** a docx file with body text is provided
- **THEN** the system outputs markdown paragraphs with proper line breaks

#### Scenario: Extract lists from docx
- **WHEN** a docx file with ordered or unordered lists is provided
- **THEN** the system outputs markdown list items (`-` for unordered, `1.` for ordered)

#### Scenario: Extract tables from docx
- **WHEN** a docx file with tables is provided
- **THEN** the system outputs markdown tables with proper column alignment

#### Scenario: Handle empty docx file
- **WHEN** an empty docx file is provided
- **THEN** the system returns an empty string

#### Scenario: Handle missing document.xml
- **WHEN** a docx file without word/document.xml is provided
- **THEN** the system throws a descriptive error

Loading