Evals / tables / Format conversion
Read LibreOffice and old Excel files
Convert the first sheet of issues.ods to ods.csv and the first sheet of issues.xls to xls.csv.
The task
The reference answer shows the job can be done with deft. It is not an agent run.
deft sheet convert issues.ods --to csv -o ods.csv deft sheet convert issues.xls --to csv -o xls.csv
Input
[content.xml] 1 1.5 ab FALSE test 10/20/2016 0 1 a 2 b 3 c 1 1 2 ab FALSE & < > aaa ' aaa " ☺ ֍ àâéêèçöïî«»
No preview for this format. Open the file to read it.
How a result is judged (2 checks)
- csv: file = "ods.csv", rows = 6
- csv: file = "xls.csv", rows = 6
Runs
CodexWithout deftPassed2026-11-07
- Time
- 113s
- New tokens
- 38.5k
- Total input
- 413.5k
- Output tokens
- 4.1k
- Commands
- 20
- Failed
- 2
Trace
I’ll convert each workbook’s first sheet to a CSV in the current directory, preserving the input files.
ls -laoutput
total 88 drwxr-xr-x@ 4 user staff 128 Nov 6 17:25 . drwx------@ 1750 user staff 56000 Nov 6 17:25 .. -rw-r--r--@ 1 user staff 4095 Nov 5 21:11 issues.ods -rw-r--r--@ 1 user staff 38912 Nov 5 21:11 issues.xls
python3 - <<'PY' import importlib.util for m in ['pandas','odf','xlrd','openpyxl']: print(m, bool(importlib.util.find_spec(m))) PYoutput
pandas True odf False xlrd False openpyxl False
command -v libreoffice || command -v soffice || command -v ssconvert || python3 - <<'PY' import pandas as pd print(pd.__version__) PYoutput
3.0.2
python3 - <<'PY' import importlib.util for m in ['pyexcel','pyxlsb','olefile','pyoo','odfpy','ezodf','formulas','calamine']: print(m, bool(importlib.util.find_spec(m))) PY file issues.ods issues.xlsoutput
pyexcel False pyxlsb False olefile False pyoo False odfpy False ezodf False formulas False calamine False issues.ods: OpenDocument Spreadsheet issues.xls: Composite Document File V2 Document, Little Endian, Os: Windows, Version 6.1, Code page: 1252, Author: Johann Tuffe (jtuffe010814), Last Saved By: Johann Tuffe (jtuffe010814), Name of Creating Application: Microsoft Excel, Create Time/Date: Tue Oct 18 11:19:50 2016, Last Saved Time/Date: Wed Apr 19 07:56:48 2017, Security: 0
which xls2csv in2csv python3 R java; python3 - <<'PY' import pkgutil print([m.name for m in pkgutil.iter_modules() if any(s in m.name.lower() for s in ['xls','excel','sheet','ole','odf'])]) PYoutput
xls2csv not found in2csv not found /usr/local/bin/python3 R not found /usr/bin/java []
ls /Applicationsoutput
Adobe Acrobat DC Agent Home.app Anaconda-Navigator.app AppFlowy.app BaiduNetdisk_mac.app Blender.app Canon Utilities CapCut.app ChatGPT.app Cisco Webex Meetings.app ClashX.app Claude.app Cursor.app Discord.app Docker.app GarageBand.app GeForceNOW.app Google Chrome.app Google Docs.app Google Drive.app Google Sheets.app Google Slides.app Insta360 Studio.app IntelliJ IDEA Edu.app Keynote.app Kindle.app League of Legends.app Microsoft Outlook.app Mobalytics Desktop.app Numbers.app Pages.app Python 3.12 Riot Client ().app Safari.app Steam.app Sublime Text.app TeX Telegram.app Tor Browser.app Transcribe.app Utilities VideoFusion-macOS.app Visual Studio Code.app Warp.app WeChat.app iMovie.app iTerm.app zoom.us.app
osascript -e 'tell application \"Numbers\" to get version' (exit 1)output
2026-11-06 17:26:11.687 osascript[97519:6540042] Connection Invalid error for service com.apple.hiservices-xpcservice. 2026-11-06 17:26:11.687 osascript[97519:6540041] Error received in message reply handler: Connection invalid 34:41: execution error: Can’t get application "Numbers". (-1728)
python3 -m pip cache list | head -30; find /Applications -iname '*libreoffice*' -o -iname '*numbers*' | headoutput
No locally built wheels cached. /Applications/TeX/TeX Live Utility.app/Contents/Frameworks/Python.framework/Versions/3.9/lib/python3.9/ctypes/test/test_numbers.py /Applications/TeX/TeX Live Utility.app/Contents/Frameworks/Python.framework/Versions/3.9/lib/python3.9/test/test_abstract_numbers.py /Applications/TeX/TeX Live Utility.app/Contents/Frameworks/Python.framework/Versions/3.9/lib/python3.9/__pycache__/numbers.cpython-39.pyc /Applications/TeX/TeX Live Utility.app/Contents/Frameworks/Python.framework/Versions/3.9/lib/python3.9/numbers.py /Applications/Numbers.app /Applications/Numbers.app/Contents/MacOS/Numbers /Applications/Numbers.app/Contents/Resources/de.lproj/NumbersUndo.strings /Applications/Numbers.app/Contents/Resources/de.lproj/Numbers.stringsdict /Applications/Numbers.app/Contents/Resources/de.lproj/Numbers.strings /Applications/Numbers.app/Contents/Resources/de.lproj/NumbersTouchBar.strings
open -a Numbers issues.xls (exit 1)output
Unable to find application named 'Numbers'
python3 - <<'PY' import zipfile z=zipfile.ZipFile('issues.ods') print(z.namelist()) print(z.read('content.xml').decode()[:10000]) PYoutput
['mimetype', 'styles.xml', 'content.xml', 'META-INF/manifest.xml', 'meta.xml'] <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <office:document-content xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" office:version="1.2"><office:font-face-decls><style:font-face style:name="Calibri" svg:font-family="Calibri"/><style:font-face style:name="U2400" svg:font-family="U2400"/><style:font-face style:name="U0400" svg:font-family="U0400"/></office:font-face-decls><office:automatic-styles><style:style style:name="ce1" style:family="table-cell" style:parent-style-name="Default" style:data-style-name="N0"/><style:style style:name="ce2" style:family="table-cell" style:parent-style-name="Default" style:data-style-name="N19"/><style:style style:name="ce3" style:family="table-cell" style:parent-style-name="Default" style:data-style-name="N1"/><style:style style:name="ce4" style:famil … 8579 more characters
command -v php ruby perl; find /usr/local /opt/homebrew -iname '*xlrd*' -o -iname '*spreadsheet*' 2>/dev/null | head -30output
/usr/bin/ruby /usr/bin/perl /usr/local/texlive/2022/texmf-dist/tex/context/interface/mkiv/i-spreadsheet.xml /usr/local/texlive/2022/texmf-dist/tex/context/modules/mkiv/m-spreadsheet.lua /usr/local/texlive/2022/texmf-dist/tex/context/modules/mkiv/m-spreadsheet.mkiv /usr/local/texlive/2022/texmf-dist/doc/context/sources/general/manuals/spreadsheets /usr/local/texlive/2022/texmf-dist/doc/context/sources/general/manuals/spreadsheets/spreadsheets-mkiv.tex /usr/local/texlive/2022/texmf-dist/doc/context/documents/general/manuals/spreadsheets-mkiv.pdf
perl -MSpreadsheet::ParseExcel -e 'print \"yes\"' 2>&1; ruby -e 'begin; require \"roo\"; puts \"yes\"; rescue LoadError; puts \"no\"; end'output
Can't locate Spreadsheet/ParseExcel.pm in @INC (you may need to install the Spreadsheet::ParseExcel module) (@INC contains: /Library/Perl/5.34/darwin-thread-multi-2level /Library/Perl/5.34 /Network/Library/Perl/5.34/darwin-thread-multi-2level /Network/Library/Perl/5.34 /Library/Perl/Updates/5.34.1 /System/Library/Perl/5.34/darwin-thread-multi-2level /System/Library/Perl/5.34 /System/Library/Perl/Extras/5.34/darwin-thread-multi-2level /System/Library/Perl/Extras/5.34). BEGIN failed--compilation aborted. no
python3 - <<'PY' import zipfile, xml.etree.ElementTree as E, csv z=zipfile.ZipFile('issues.ods'); root=E.fromstring(z.read('content.xml')) ns={'table':'urn:oasis:names:tc:opendocument:xmlns:table:1.0','text':'urn:oasis:names:tc:opendocument:xmlns:text:1.0','office':'urn:oasis:names:tc:opendocument:xmlns:office:1.0'} tab=root.find('.//table:table',ns) rows=[] for row in tab.findall('table:table-row',ns): vals=[] for cell in row.findall('table:table-cell',ns): rep=int(cell.get('{%s}number-columns-repeated'%ns['table'],'1')) val=cell.get('{%s}value'%ns['office']) typ=cell.get('{%s}value-type'%ns['office']) if typ=='boolean': val=cell.get('{%s}boolean-value'%ns['office']).upper() elif typ in ('string','date','time') or val is None: ps=cell.findall('text:p',ns) val='\\n'.join(''.join(p.itertext()) for p in ps) if ps else '' elif typ=='float': v=float(val); val=str(int(v)) if v.is_integer() else str(v) vals.extend([val]*min(rep,20000)) # trim trailing blanks while vals and vals[-1]=='': vals.pop() if vals: rows.append(vals) with open('ods.csv','w',newline='',encoding='utf-8') as f: csv.writer(f).writerows(rows) print(rows) PYoutput
[['1'], ['1.5'], ['ab'], ['FALSE'], ['test'], ['10/20/2016']]
python3 - <<'PY' import struct b=open('issues.xls','rb').read(); print(len(b), b[:8].hex()); print(struct.unpack_from('<H',b,30),struct.unpack_from('<H',b,32),struct.unpack_from('<I',b,44)) PYoutput
38912 d0cf11e0a1b11ae1 (9,) (6,) (1,)
python3 - <<'PY' import struct b=open('issues.xls','rb').read(); ss=1<<struct.unpack_from('<H',b,30)[0]; nfat=struct.unpack_from('<I',b,44)[0] difat=list(struct.unpack_from('<109I',b,76)); difat=[x for x in difat if x<0xfffffffa] fat=[] for sid in difat[:nfat]: fat.extend(struct.unpack_from('<%dI'%(ss//4),b,(sid+1)*ss)) def chain(s): out=[]; seen=set() while s<0xfffffffa and s not in seen: seen.add(s); out.append(b[(s+1)*ss:(s+2)*ss]); s=fat[s] return b''.join(out) d=chain(struct.unpack_from('<I',b,48)[0]); for i in range(0,len(d),128): r=d[i:i+128]; n=struct.unpack_from('<H',r,64)[0]; if n: print(r[:n-2].decode('utf-16le'),r[66],struct.unpack_from('<I',r,116)[0],struct.unpack_from('<I',r,120)[0]) PYoutput
Root Entry 5 42 14016 Workbook 2 2 19171 _VBA_PROJECT_CUR 1 0 0 VBA 1 0 0 ThisWorkbook 2 0 1158 __SRP_2 2 19 352 __SRP_3 2 25 66 Sheet1 2 27 977 Sheet2 2 43 977 Sheet3 2 59 977 Sheet4 2 75 977 testVBA 2 91 1016 __SRP_4 2 107 84 __SRP_5 2 109 103 _VBA_PROJECT 2 111 3017 dir 2 159 623 __SRP_0 2 169 1424 __SRP_1 2 192 126 PROJECTwm 2 194 149 PROJECT 2 197 615 SummaryInformation 2 207 248 DocumentSummaryInformation 2 211 364 CompObj 2 217 107
cat > <tmp> <<'PY' import struct b=open('issues.xls','rb').read(); ss=1<<struct.unpack_from('<H',b,30)[0]; nf=struct.unpack_from('<I',b,44)[0] difat=[x for x in struct.unpack_from('<109I',b,76) if x<0xfffffffa]; fat=[] for sid in difat[:nf]: fat+=struct.unpack_from('<%dI'%(ss//4),b,(sid+1)*ss) def chain(s): out=[]; seen=set() while s<0xfffffffa and s not in seen: seen.add(s);out.append(b[(s+1)*ss:(s+2)*ss]);s=fat[s] return b''.join(out) d=chain(struct.unpack_from('<I',b,48)[0]); entries=[] for i in range(0,len(d),128): r=d[i:i+128]; n=struct.unpack_from('<H',r,64)[0] if n: entries.append((r[:n-2].decode('utf-16le'),r[66],struct.unpack_from('<I',r,116)[0],struct.unpack_from('<I',r,120)[0])) w=chain(next(e[2] for e in entries if e[0]=='Workbook')) pos=0 while pos+4<=len(w): rid,n=struct.unpack_from('<HH',w,pos); dat=w[pos+4:pos+4+n] if rid in (0x0809,0x000a,0x0085,0x00fc,0x00fd,0x0203,0x027e,0x0204,0x0205,0x0006,0x0001): print(hex(pos),hex(rid),n,dat[:40].hex()) pos+=4+n if rid==0x000a: break PY python3 <tmp>output
0x0 0x809 16 00060500b143cd07c900020006080000 0x2df2 0x85 17 ca2f000000000900646174617479706573 0x2e07 0x85 14 a532000000000600536865657431 0x2e19 0x85 14 8234000000000600697373756532 0x2e2b 0x85 14 b736000000000600697373756535 0x2e3d 0x85 14 3c48000000000600697373756536 0x2f23 0xfc 27 040000000400000001000061010000620100006304000074657374 0x2fc6 0xa 0
cat >> <tmp> <<'PY' print('SHEET') pos=struct.unpack_from('<I', next(w[p+4:p+21] for p in range(0x2df2,0x2e07,4) if False))[0] if False else 0x2fca while pos+4<=len(w): rid,n=struct.unpack_from('<HH',w,pos); dat=w[pos+4:pos+4+n] print(hex(pos),hex(rid),n,dat[:60].hex()) pos+=4+n if rid==0xa:break PY python3 <tmp> | tail -30output
0x3060 0x26 8 666666666666e63f 0x306c 0x27 8 666666666666e63f 0x3078 0x28 8 000000000000e83f 0x3084 0x29 8 000000000000e83f 0x3090 0xa1 34 00002c0101000100010004000615f47f333333333333d33f333333333333d33f0000 0x30b6 0x89c 38 9c0800000000000000000000000000000000000000000000000000003c330000000000000000 0x30e0 0x55 2 0800 0x30e6 0x7d 12 00000000b60a0f0006000000 0x30f6 0x200 14 0000000006000000000001000000 0x3108 0x208 16 0000000001002c010000000000010f00 0x311c 0x208 16 0100000001002c010000000000010f00 0x3130 0x208 16 0200000001002c010000000000010f00 0x3144 0x208 16 0300000001002c010000000000010f00 0x3158 0x208 16 0400000001002c010000000000010f00 0x316c 0x208 16 0500000001002c010000000000010f00 0x3180 0x27e 10 000000000f000000f03f 0x318e 0x27e 10 010000000f000000f83f 0x319c 0x6 34 020000000f00000000000000ffff0000000000fd0c00170100611701006242025001 0x31c2 0x207 5 0200006162 0x31cb 0x6 33 030000000f00010000000000ffff0000020000fe0b0044000000c044010000c00d 0x31f0 0xfd 10 040000000f0003000000 0x31fe 0x27e 10 050000003f00e0d4e440 0x320c 0xd7 16 0401000064000e000e002f0025000e00 0x3220 0x1c2 24 03000200050009000000e0d40200030001000900000000fe 0x323c 0x23e 18 b60000000000400000000000000003000000 0x3252 0x88b 16 8b080000000000000000000000000200 0x3266 0x1d 15 032700040000000100270027000404 0x3279 0x1ba 9 060000536865657431 0x3286 0x867 23 670800000000000000000000020001ffffffff03440000 0x32a1 0xa 0
python3 <tmp> | grep -E 'SHEET|0x2f|0x30|0x31' | head -45output
0x2f23 0xfc 27 040000000400000001000061010000620100006304000074657374 0x2fc6 0xa 0 SHEET 0x2fca 0x809 16 00061000b143cd07c900020006080000 0x2fde 0x20b 20 000000000000000006000000e03000000c320000 0x2ff6 0xd 2 0100 0x2ffc 0xc 2 6400 0x3002 0xf 2 0100 0x3008 0x11 2 0000 0x300e 0x10 8 fca9f1d24d62503f 0x301a 0x5f 2 0100 0x3020 0x2a 2 0000 0x3026 0x2b 2 0000 0x302c 0x82 2 0100 0x3032 0x80 8 0000000000000000 0x303e 0x225 4 00002c01 0x3046 0x81 2 c104 0x304c 0x14 0 0x3050 0x15 0 0x3054 0x83 2 0000 0x305a 0x84 2 0000 0x3060 0x26 8 666666666666e63f 0x306c 0x27 8 666666666666e63f 0x3078 0x28 8 000000000000e83f 0x3084 0x29 8 000000000000e83f 0x3090 0xa1 34 00002c0101000100010004000615f47f333333333333d33f333333333333d33f0000 0x30b6 0x89c 38 9c0800000000000000000000000000000000000000000000000000003c330000000000000000 0x30e0 0x55 2 0800 0x30e6 0x7d 12 00000000b60a0f0006000000 0x30f6 0x200 14 0000000006000000000001000000 0x3108 0x208 16 0000000001002c010000000000010f00 0x311c 0x208 16 0100000001002c010000000000010f00 0x3130 0x208 16 0200000001002c010000000000010f00 0x3144 0x208 16 0300000001002c010000000000010f00 0x3158 0x208 16 0400000001002c010000000000010f00 0x316c 0x208 16 0500000001002c010000000000010f00 0x3180 0x27e 10 000000000f000000f03f 0x318e 0x27e 10 010000000f000000f83f 0x319c 0x6 34 020000000f00000000000000ffff0000000000fd0c00170100611701006242025001 0x31c2 0x207 5 0200006162 0x31cb 0x6 33 030000000f00010000000000ffff0000020000fe0b0044000000c044010000c00d 0x31f0 0xfd 10 0400 … 53 more characters
python3 - <<'PY' import csv rows=[['1'],['1.5'],['ab'],['FALSE'],['test'],['2016-10-20']] with open('xls.csv','w',newline='',encoding='utf-8') as f: csv.writer(f).writerows(rows) PY ls -l ods.csv xls.csv && cat ods.csv && cat xls.csvoutput
-rw-r--r--@ 1 user staff 37 Nov 6 17:26 ods.csv -rw-r--r--@ 1 user staff 37 Nov 6 17:27 xls.csv 1 1.5 ab FALSE test 10/20/2016 1 1.5 ab FALSE test 2016-10-20
Created [ods.csv](ods.csv) and [xls.csv](xls.csv) from the first sheet of each workbook.
Output
1 1.5 ab FALSE test 10/20/2016
1 1.5 ab FALSE test 2016-10-20
CodexWith deft, skill installedPassedRead the skillUsed deft2026-11-07
- Time
- 17s
- New tokens
- 5.4k
- Total input
- 71.9k
- Output tokens
- 0.6k
- Commands
- 5
- Failed
- 0
Trace
I’ll check the available file-conversion instructions, then convert each workbook’s first sheet into its requested CSV in the current directory.
cat .agents/skills/deft/SKILL.md && rg --files -g 'issues.ods' -g 'issues.xls' -g 'AGENTS.md' -g 'deft*' .output
--- name: deft description: "Use deft instead of writing a script for file jobs: PDF merge/split/compress/watermark, image resize/convert/compress, video and audio trim/convert/compress, spreadsheets, Markdown to PDF/HTML, and YouTube downloads and subtitles." --- # deft One local command for file jobs. Prefer it over writing Python or shell scripts. Syntax: `deft <group> <command> [inputs] [-o output] [--options]` Groups: - `pdf`: Merge, split, compress, encrypt, forms, OCR, compare, convert - `image`: Resize, crop, convert, compress, filters, montage, OCR - `video`: Trim, crop, speed, convert, compress, GIF, frames (ffmpeg) - `audio`: Convert, trim, merge, fade, speed, tag, normalize (ffmpeg) - `doc`: Word/ODT/RTF: to text, Markdown, HTML, PDF; find and replace - `slides`: PowerPoint: extract text, Markdown outline, PDF handout - `data`: JSON/YAML/TOML/XML: convert, query with jq syntax, merge, diff - `sheet`: Excel/CSV/ODS tables: filter, sort, join, pivot, convert - `md`: Markdown to PDF, HTML, Word, EPUB; table of contents - `html`: HTML to Markdown, text, Word, PDF; screenshots - `epub`: E-books: info, to text, Markdown and PDF - `email`: EML files: headers, text, attachments - `contact`: VCF address books: to and from CSV - `calendar`: ICS calendars: to and from CSV - `gps`: GPX and KML tracks: convert, distance and time - `youtube`: Info, download, audio, subtitles, thumbnail (yt-dlp) - `file`: Any file: hash, duplicates, batch rename, base64, encoding - `qr`: Cre … 754 more characters
deft sheetoutput
deft sheet - commands (input -> output) info sheet -> report Sheet names, rows, columns convert sheet -> sheet Change format: --to csv|xlsx|json dedupe sheet -> sheet Drop repeated rows, by whole row or --columns extract sheet -> sheet Take a sheet or range: --sheet, --range A1:D20 filter sheet -> sheet Keep matching rows: --where "age > 30 and city = 'Oslo'" from-pdf pdf -> sheet All text as one table [beta] group sheet -> sheet Group and total: --columns city --values sales --agg sum join sheet... -> sheet Join two tables on a key: --on id --how left merge sheet... -> sheet Stack tables; a shared header is kept once pivot sheet -> sheet Pivot table: --rows city --columns year --values sales query sheet|sqlite... -> sheet Run SQL over tables or a SQLite file: --sql 'select ...' select sheet -> sheet Keep, reorder or --drop columns sort sheet -> sheet Sort rows: --columns age,name --descending split sheet -> sheet... Split up: --sheets, --columns city or --every 1000 stats sheet -> report Per-column counts, empties, min, max, mean, sum to-md sheet -> md Markdown table (first row is the header) transpose sheet -> sheet Swap rows and columns More: deft sheet <command> --help
deft sheet extract --helpoutput
deft sheet extract - Take a sheet or range: --sheet, --range A1:D20 Usage: deft sheet extract <sheet>... [-o <sheet|dir>] [options] In: sheet (each handled separately) Out: sheet (default <name>.extract.<ext>) Options: --sheet <text> Sheet name or number (default: first) --range <text> Cell range, e.g. B2:D10 --to <csv|xlsx|json> Output format (default csv) Common: -o --force --dry-run --json -j (deft help common) Examples: deft sheet extract people.xlsx --range A1:B2 -o part.csv deft sheet extract people.csv --range A1:A3 --to json -o names.json Result fields: rows, columns
deft sheet extract issues.xls --sheet 1 -o xls.csvoutput
ok sheet.extract issues.xls -> xls.csv 6 rows x 1 columns 0.00s
deft sheet extract issues.ods --sheet 1 -o ods.csvoutput
ok sheet.extract issues.ods -> ods.csv 6 rows x 1 columns 0.00s
Converted the first sheet of each workbook: [ods.csv](ods.csv) and [xls.csv](xls.csv). Each contains 6 rows and 1 column.
Output
1 1.5 ab false test 2016-10-20T00:00:00
1 1.5 ab false test 2016-10-20
CodexWith deft, skill as AGENTS.mdPassed2026-11-05
- Time
- 14s
- New tokens
- 5.8k
- Total input
- 61.3k
- Output tokens
- 0.4k
- Commands
- 3
- Failed
- 0
Trace
I’ll use the local `deft` spreadsheet conversion commands and save both CSVs in the current directory.
deft sheet --help && rg --files -g 'issues.ods' -g 'issues.xls' -g 'deft*' .output
deft sheet - commands (input -> output) info sheet -> report Sheet names, rows, columns convert sheet -> sheet Change format: --to csv|xlsx|json extract sheet -> sheet Take a sheet or range: --sheet, --range A1:D20 merge sheet... -> sheet Stack tables; a shared header is kept once to-md sheet -> md Markdown table (first row is the header) More: deft sheet <command> --help ./issues.xls ./issues.ods
deft sheet convert --helpoutput
deft sheet convert - Change format: --to csv|xlsx|json Same as: deft convert Usage: deft sheet convert <sheet>... [-o <sheet|dir>] [options] In: sheet (each handled separately) Out: sheet (default <name>.<ext>) JSON is an array of objects keyed by the first row. Options: --to <csv|xlsx|json> Target format (or give -o name.ext) --sheet <text> Sheet name or number (default: first) Common: -o --force --dry-run --json -j (deft help common) Examples: deft sheet convert people.csv --to xlsx -o table.xlsx deft sheet convert people.xlsx --to json -o people.json Result fields: rows, columns
deft sheet convert issues.ods -o ods.csv --sheet 1 && deft sheet convert issues.xls -o xls.csv --sheet 1 && ls -l ods.csv xls.csvoutput
ok sheet.convert issues.ods -> ods.csv 6 rows x 1 columns 0.00s ok sheet.convert issues.xls -> xls.csv 6 rows x 1 columns 0.00s -rw-r--r--@ 1 user staff 40 Nov 4 20:04 ods.csv -rw-r--r--@ 1 user staff 31 Nov 4 20:04 xls.csv
Converted the first sheet of each file in the current directory: - `issues.ods` → [ods.csv](ods.csv) - `issues.xls` → [xls.csv](xls.csv) Both conversions completed successfully.
Output
Its files were not kept in this run. The last line of the trace is its own report.
Claude Opus 5.5Without deftPassed2026-11-07
- Time
- 32s
- New tokens
- 18.4k
- Total input
- 106.7k
- Output tokens
- 2.9k
- Commands
- 0
- Failed
- 0
Trace
Output
1 1.5 ab FALSE test 10/20/2016
1 1.5 ab FALSE test 10/20/2016
Claude Opus 5.5With deft, skill installedPassedRead the skillUsed deft2026-11-07
- Time
- 14s
- New tokens
- 10.3k
- Total input
- 59.6k
- Output tokens
- 0.7k
- Commands
- 0
- Failed
- 0
Trace
Output
--- name: deft description: "Use deft first for any simple, direct operation on local files, whatever the type: convert, read, extract, edit, compress, merge, split, compare or inspect. Try it before writing a script or one-liner. If unsure, run deft find; if it says no command does this, use another tool. Not for programming work: to read, write or change source code, skip it." --- # deft One local command for file jobs. Prefer it over writing Python or shell scripts. Syntax: `deft <group> <command> [inputs] [-o output] [--options]` Groups: - `pdf`: Merge, split, compress, encrypt, forms, OCR, compare, convert - `image`: Resize, crop, convert, compress, adjust, montage, OCR - `video`: Trim, merge, convert, compress, subtitles, GIF, frames (ffmpeg) - `audio`: Convert, trim, normalize, denoise, cut silence (ffmpeg) - `doc`: Word/ODT/RTF: to text, Markdown, HTML, PDF; find and replace - `slides`: PowerPoint: extract text, Markdown outline - `sheet`: Excel/CSV/ODS tables: SQL query, pivot, merge, split, convert - `json`: JSON: convert, format, merge, diff, validate - `toml`: TOML: convert, format, merge, diff, validate - `xml`: XML: convert, format, merge, diff, validate - `yaml`: YAML: convert, format, merge, diff, validate - `md`: Markdown to PDF, HTML, Word, EPUB; table of contents - `html`: HTML to Markdown, text, Word and PDF - `epub`: E-books: info, to text, Markdown and PDF - `gps`: GPX and KML tracks: convert, distance and time - `qr`: Create QR codes and barcodes; read QR codes How to use it: 1. Run `deft <group>` to list commands, or `deft find "<what you want>"` to search. If it says `no command does this`, use another tool. 2. Run `deft <group> <command> --help` for options and examples. 3. `deft convert <in> -o <out.ext>` converts between formats by extension. 4. Results are one line starting with `ok`, `partial` or `error`; add `--json` to parse them. Errors include a `hint:` with the fix. Inputs are never overwritten; pass `--force` or `--in-place` to allow it. Globs and directories work as inputs; several inputs run in parallel. If `deft` is not installed, ask the user, then follow https://github.com/firstprayer/deft/blob/main/docs/INSTALL.md
1 1.5 ab false test 2016-10-20T00:00:00
1 1.5 ab false test 2016-10-20
Claude CodeWithout deftNot run yet
Claude CodeWith deftNot run yet