From qingyi
This skill should be used when the user wants to VIEW, ANALYZE, or EXTRACT data from Excel files. FILE-BASED TRIGGERS (highest priority): - Any mention of ".xlsx" or ".xls" files WITH read intent - "the excel file", "this excel", "my spreadsheet" + viewing context ACTION TRIGGERS: - "read excel", "open xlsx", "view spreadsheet" - "show excel data", "what's in the excel", "display spreadsheet" - "check the excel", "look at the spreadsheet" - "analyze spreadsheet", "extract data from excel" - "count rows", "find values", "list worksheets" - "summarize the excel", "what sheets are there" - "parse excel", "import from excel", "convert excel to" NATURAL LANGUAGE PATTERNS: - "show me" + excel/spreadsheet - "what does the excel contain" - "read" + file path ending in .xlsx or .xls
How this skill is triggered — by the user, by Claude, or both
Slash command
/qingyi:excel-readThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Read, analyze, and extract data from Excel files using Python openpyxl.
Read, analyze, and extract data from Excel files using Python openpyxl.
Before running any script, determine the Python runner (checked in order, first match wins):
~/.claude/qingyi.local.md, parse YAML frontmatter for excel_runner.claude/qingyi.local.md from the project root, parse YAML frontmatter for excel_runner (
overrides global)pythonRun command: <excel_runner> <script_path>
scripts/excel/ from project root<excel_runner> scripts/excel/<script_name>.pyWhen displaying Excel data to the user, format as markdown tables:
| Column A | Column B | Column C |
|----------|----------|----------|
| value1 | value2 | value3 |
For sparse data with many empty cells, show only populated rows/columns.
from openpyxl import load_workbook
from openpyxl.utils import get_column_letter
from pathlib import Path
wb = load_workbook(path, data_only=True) # data_only=True for formula results
ws = wb.active # Active sheet
ws = wb['SheetName'] # Named sheet
value = ws['A1'].value
value = ws.cell(row=1, column=1).value
# Specific range
for row in ws['A1:C10']:
for cell in row:
print(cell.value)
# All data as 2D list
data = [[cell.value for cell in row] for row in ws.iter_rows()]
# With bounds
data = [[cell.value for cell in row]
for row in ws.iter_rows(min_row=1, max_row=10, min_col=1, max_col=5)]
col_values = [cell.value for cell in ws['A']] # Column A
row_values = [cell.value for cell in ws[1]] # Row 1
for row in ws.iter_rows(values_only=True):
print(row) # tuple of values
print(f"Rows: {ws.max_row}, Columns: {ws.max_column}")
print(f"Data range: {ws.dimensions}")
for sheet_name in wb.sheetnames:
print(sheet_name)
for row in ws.iter_rows():
for cell in row:
if cell.value == "SearchTerm":
print(f"Found at {cell.coordinate}")
for merged_range in ws.merged_cells.ranges:
print(merged_range)
# Count non-empty cells
count = sum(1 for row in ws.iter_rows() for cell in row if cell.value is not None)
# Unique values in column
unique = set(cell.value for cell in ws['A'] if cell.value is not None)
# Summary statistics
headers = [cell.value for cell in ws[1]]
print(f"Columns: {len(headers)}, Data rows: {ws.max_row - 1}, Headers: {headers}")
from openpyxl import load_workbook
from pathlib import Path
def main():
path = Path(r"C:\path\to\file.xlsx")
wb = load_workbook(path, data_only=True)
print(f"Sheets: {wb.sheetnames}")
for sheet_name in wb.sheetnames:
ws = wb[sheet_name]
print(f"\n=== {sheet_name} ===")
print(f"Dimensions: {ws.dimensions}")
for i, row in enumerate(ws.iter_rows(values_only=True)):
if i >= 10:
print("...")
break
print(row)
if __name__ == "__main__":
main()
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub fly-onlyone/qingyi --plugin qingyi