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
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,26 @@ The format is based on Keep a Changelog, and this project follows semantic versi

## [Unreleased]

## [0.1.25] - 2026-06-26

### Added

- Added first-class grouped-header summary answers so agents can inspect row-1 header spans, labels, and merged/unmerged status without chasing broad workbook overviews or stored result handles.
- Added explicit row and column structure operations, including insert/delete row support and richer delete-column guidance, so authorized destructive requests route to real structural changes instead of style or row-height workarounds.
- Added merge-aware multi-range previews for grouped headers so merge operations and center alignment can be batched and applied together.

### Fixed

- Fixed grouped-header color routing so row 1 grouped headers stay visually distinct from row 2 column headers, including exact `target.address` handling and darker grouped-header defaults.
- Fixed freeze-pane workflows so agents can apply, unfreeze, and answer frozen row/column status through live workbook state instead of cached style summaries.
- Fixed batched column width updates and column reorder/swap operations so widths, formulas, values, and formatting move with the affected columns.
- Fixed merged-header alignment normalization so center/middle alignment requests are translated to Office.js-compatible alignment values.

### Changed

- Improved `excel.agent.run` guidance, capability metadata, packaged skill docs, and generated MCP surface docs for batched updates, merge operations, freeze panes, grouped headers, and structural worksheet edits.
- Improved preview/apply regression coverage for OpenCode Excel workflows, including header styling, width preservation, merge batching, row/column deletion, freeze panes, and grouped-header summaries.

## [0.1.24] - 2026-06-25

### Fixed
Expand Down
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,41 @@ Agents call `excel.agent.run` with natural language plus optional structured fie

The backend keeps verbose workbook context local and returns compact proof, resource links, telemetry, warnings, and next actions. Caller LLMs may provide canonical `intent.action`, `intent.targetHints`, explicit `target`, and structured `values`, but the backend still owns ambiguity checks, stale-context checks, permissions, locks, backups, validation, and rollback metadata.

For styling review, agents should use `intent.action: "style_overview"` or `detailLevel: "style_overview"` with `mode: "answer"` to get current style context, column groups, grouped-header suggestions, and workflow hints without full data reads. For workbook design review, such as deciding which columns should be free text, dates, money, ID/text codes, dropdowns, or lookups/references from related sheets, agents should use `intent.action: "workbook_design_overview"` with `mode: "answer"` once before reading related sheets manually. It returns column-by-column recommendations, related-sheet hints, and next workflows without broad-reading empty data rows. For broad styling/readability work, agents should use `intent.action: "improve_visual_readability"` with `mode: "preview_update"` rather than issuing many primitive style calls. Options live under `values.visualReadability`; standard mode compiles safe column-first layout/formatting/highlight rules, comprehensive mode can include preview-only validation/formula suggestions, `stylePreservationMode` defaults to `protected_regions` so summary/template areas and grouped header bands stay guarded while ordinary table body styling, widths, alignment, and date/money formats can still be intentionally improved, `strict` preserves every detected existing style, `none` allows an explicit redesign, `referenceStyle` can preview adaptation from another sheet, and `presentationMode` can preview print/export suggestions. Apply still requires `apply_update` with the returned operation token, `nextAction: "call_apply_update"`, and `operationCount > 0`; if a preview reports `operationCount: 0` or `nextAction: "answer_now"`, agents should explain the skipped reasons instead of applying or decomposing the work into primitive style calls. Use `intent.action: "grouped_header"` for the separate structural preview that inserts a visual group row, merges group labels, and restyles the shifted table header. Grouped-header groups should use `{ "label": "...", "startColumn": "A", "endColumn": "B" }`; `{ "columns": ["A", "B"] }` and `{ "range": "A:B" }` are also accepted. Do not reuse an `operationId` from visual readability when creating a grouped-header preview.

Grouped headers are structure-level styling. If apply is blocked by `DESTRUCTIVE_ACTION_BLOCKED` or `PERMISSION_DENIED`, the public agent path can enable the required policy with `intent.action: "set_permissions"` and `values.permissions` such as `{ "allowWrites": true, "allowDestructiveActions": true, "scopeToWorkbook": true, "requireConfirmationFor": [] }`; after that, create and apply a fresh grouped-header preview.

Example OpenCode prompts:

```text
Use open-workbook. Inspect the active sheet with a style overview first, without reading every data cell. Suggest visual readability improvements including grouped headers, one consistent palette, safe widths, alignment, filters, number formats, and highlights. Do not apply yet.
```

```text
Preview a grouped_header workflow for this sheet. Add a higher-level grouped header row above the existing column headers, merge group labels, and use matching group colors. Wait for approval before apply_update.
```

```json
{
"mode": "preview_update",
"intent": { "action": "grouped_header" },
"target": { "sheetName": "Invoices", "tableName": "InvoicesTable" },
"values": {
"stylePreservationMode": "none",
"groupedHeader": {
"groups": [
{ "label": "สถานะ", "startColumn": "A", "endColumn": "B" },
{ "label": "ข้อมูลงาน", "startColumn": "C", "endColumn": "E" }
]
}
}
}
```

```text
Apply the safe visual readability preview in one apply_update. Include opt-in buckets layout, validation, and freeze_panes only if they were present in the preview.
```

With the shared daemon, multiple MCP sessions get distinct trusted agent identities. `status` and `prepare` include compact collaboration summaries for active agents, open tasks, locks, queued/applying transactions, conflicts, and recent events.

## Common Commands
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@components-kit/open-workbook-backend",
"version": "0.1.24",
"version": "0.1.25",
"description": "Local backend broker for Open Workbook add-in sessions, backups, snapshots, plans, and permissions.",
"license": "MIT",
"type": "module",
Expand Down
44 changes: 44 additions & 0 deletions apps/backend/src/agent-action-handlers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ describe("agent action handlers", () => {
expect(findAgentActionHandler(natural, undefined, true)?.id).toBe("format_range");
});

it("routes header row color-only changes to formatting instead of table row updates", () => {
const input: AgentRunInput = {
request: "Change row 1 header grouping fill to a darker color",
target: { sheetName: "Invoices", range: "A1:O1" }
};

expect(findAgentActionHandler(input, undefined, true)?.id).toBe("format_range");
});

it("matches promoted range-core actions by caller intent", () => {
const target: AgentRunInput["target"] = { sheetName: "Data", range: "A1:B2" };

Expand Down Expand Up @@ -75,6 +84,21 @@ describe("agent action handlers", () => {
expect(findAgentActionHandler({ request: "Do it", intent: { action: "copy_table_structure" }, target }, "copy_table_structure", true)?.id).toBe("copy_table_structure");
});

it("routes freeze and unfreeze pane requests to the sheet freeze panes operation", () => {
const target: AgentRunInput["target"] = { sheetName: "Invoices", range: "A1:O1002" };

expect(findAgentActionHandler({ request: "unfreeze all panes on Invoices", target }, undefined, false)?.id).toBe("freeze_panes");
expect(findAgentActionHandler({ request: "freeze top row and first column", target }, undefined, false)?.id).toBe("freeze_panes");
expect(findAgentActionHandler({ request: "Do it", intent: { action: "freeze_panes" }, target }, "freeze_panes", false)?.id).toBe("freeze_panes");
});

it("does not route read-only freeze pane questions to the mutation handler", () => {
const target: AgentRunInput["target"] = { sheetName: "Invoices", range: "A1:O1002" };

expect(findAgentActionHandler({ request: "Which column is frozen on Invoices?", target }, undefined, false)).toBeUndefined();
expect(findAgentActionHandler({ request: "Check current freeze panes column", target }, undefined, false)).toBeUndefined();
});

it("matches promoted sheet-core actions by caller intent", () => {
const target: AgentRunInput["target"] = { sheetName: "Report" };

Expand All @@ -97,6 +121,17 @@ describe("agent action handlers", () => {
expect(findAgentActionHandler({ request: "Show cols B:C", target: { sheetName: "Data", range: "B:C" } }, undefined, true)?.id).toBe("unhide_columns");
});

it("routes explicit deletion wording by row, column, or cell scope", () => {
expect(findAgentActionHandler({ request: "Delete this row", target: { sheetName: "Data", range: "5:5" } }, undefined, true)?.id).toBe("delete_rows");
expect(findAgentActionHandler({ request: "Delete col B", target: { sheetName: "Data", range: "B:B" } }, undefined, true)?.id).toBe("delete_columns");
expect(findAgentActionHandler({ request: "Remove this cell", target: { sheetName: "Data", range: "B5" } }, undefined, true)?.id).toBe("clear_values");
});

it("routes column swap and move wording to column reorder", () => {
expect(findAgentActionHandler({ request: "Swap cols A and B", target: { sheetName: "Data", range: "A:B" } }, undefined, true)?.id).toBe("reorder_range_columns");
expect(findAgentActionHandler({ request: "Move column B before column A", target: { sheetName: "Data", range: "A:B" } }, undefined, true)?.id).toBe("reorder_range_columns");
});

it("matches promoted workbook mutation actions by caller intent", () => {
expect(findAgentActionHandler({ request: "Do it", intent: { action: "restore_workbook_backup" } }, "restore_workbook_backup", false)?.id).toBe("restore_workbook_backup");
expect(findAgentActionHandler({ request: "Do it", intent: { action: "import_local_config" } }, "import_local_config", false)?.id).toBe("import_local_config");
Expand All @@ -119,6 +154,15 @@ describe("agent action handlers", () => {
expect(findAgentActionHandler({ request: "Do it", intent: { action: "delete_name" } }, "delete_name", false)?.id).toBe("delete_name");
});

it("does not route grouped header creation wording to named-range creation", () => {
expect(findAgentActionHandler({
request: "Create merged group header cells for the Invoices sheet with labels and colors."
}, undefined, false)?.id).not.toBe("create_name");
expect(findAgentActionHandler({
request: "Create a named range for the input cells."
}, undefined, false)?.id).toBe("create_name");
});

it("matches promoted region mutation actions by caller intent", () => {
expect(findAgentActionHandler({ request: "Do it", intent: { action: "register_region" } }, "register_region", false)?.id).toBe("register_region");
expect(findAgentActionHandler({ request: "Do it", intent: { action: "clear_region_values" } }, "clear_region_values", false)?.id).toBe("clear_region_values");
Expand Down
Loading
Loading