How to Use IronWord with GitHub Copilot

Curtis Chau
Curtis Chau
Updated: October 19, 2026

A .NET service can generate monthly account statements as Word files, each with a styled title, two summary lines and a table built from data. In this guide, the service resides in a Visual Studio solution alongside the jobs that supply its data, so any assistant that modifies it must work within the same solution.

GitHub Copilot can work on that solution from Visual Studio or VS Code in chat or agent mode, from a terminal with Copilot CLI, and from GitHub, where its cloud agent opens a pull request for an issue assigned to it. IronWord builds the DOCX in code, with styled runs of text and tables that have merged cells and shaded rows. The IronWord rules are kept in instruction files in the repository, and each of those surfaces reads them.

The table guide and the text style guide cover the two parts of the API that this guide relies on.

Quickstart
NuGetInstall with NuGet

PM > Install-Package IronWord

Install IronWord by running the command above in the NuGet Package Manager Console, or search for the package in the NuGet Package Manager.

Start Copilot CLI from the solution root and paste in the prompt below. Copilot Chat in Visual Studio references a file with #, while the CLI uses @.

Fill in @src/Statements/Documents/StatementBuilder.cs with a public static Build method.
Its signature is Build(Account account, IReadOnlyList<Transaction> transactions, string outputPath).
Add Account and Transaction as small records in the same folder.

Write the DOCX with IronWord, from the IronWord NuGet package.
The types are in the IronWord and IronWord.Models namespaces.

Lay out the statement exactly as @docs/statement-layout.md describes. Three parts need care:
- the bold heading row
- the merged closing-balance cell
- the alternate row shading

Save the file to outputPath, then compile the solution and clear every build error.
Tell me which files you added or changed.
Text

The prompt names no styling member. The path-specific instructions file below specifies the members to use.

The solution's Program.cs reads IRONWORD_LICENSE_KEY from the environment and assigns it to IronWord.License.LicenseKey, so set the variable before starting Copilot. The license key guide shows the same assignment and the configuration files that can hold the key.



How do you set IronWord conventions for Copilot?

Rules for the whole repository go in .github/copilot-instructions.md. Copilot CLI, Copilot code review, the cloud agent and Copilot Chat in JetBrains IDEs, Visual Studio and VS Code all read that file, according to GitHub's support matrix. Visual Studio loads it only after the custom instructions checkbox is selected in its Copilot Chat options.

Statement code in this repository follows these rules:

- Statements are DOCX files written with IronWord, from the NuGet package IronWord.
- Import both namespaces, IronWord and IronWord.Models.
- Use no other Word library and no OpenXML SDK code.
- Keep the IronWord version set in Directory.Packages.props, unless the task is upgrading it.
- IRONWORD_LICENSE_KEY holds the license key, and Program.cs reads it there.
- Do not put the key in source, settings files, tests or logs.
- Never print its value, and don't list environment variables.
- docs/statement-layout.md is the layout contract. Do not edit it.
- Propose changes to that layout in your reply instead.
- IronWord's documentation for agents is at https://ironsoftware.com/word/csharp/llms.txt
- An IronWord member is real only when it builds and a saved file shows its effect.
- The documentation, its samples and IronWord.xml list some that do not compile or are not saved.

Statement tests follow these rules:

- Build statements in a temp directory and reopen the saved file with IronWord.
- Compare cell text, values and run styles. Never compare DOCX bytes.
Markdown

How do you give the document code its own rules?

Rules for some folders only go in a path-specific file such as .github/instructions/documents.instructions.md, whose applyTo patterns here cover the document code and its tests. GitHub's Copilot CLI documentation says the CLI adds the file to a request only when a file it is handling matches a listed pattern. In our run, the CLI listed the file's patterns and path at the start of each session, and Copilot read the file before its first edit.

---
applyTo: "src/Statements/Documents/**,tests/Statements.Tests/**"
---

The document code and its tests follow these IronWord rules:

- Style text on a Run, with new Run(new TextStyle { ... }) or Run.Style.
- TextContent.IsBold, TextContent.FontSize and Run.FontSize are read-only.
- SaveAs drops a TextStyle assigned to TextContent.Style.
- SaveAs writes a TextStyle's FontSize only when its TextFont is set too.
- Add each body line as its own paragraph with AddParagraph.
- WordDocument.AddText joins its text onto whichever paragraph came last.
- Table.ApplyCellStyle is listed in IronWord.xml but does not compile.
- Style each cell's runs in place of ApplyCellStyle.
- new Table(rows, columns) creates every cell, with zero-based indexes.
- Fill a cell with AddChild or AddText on table[row, column].
- Table.Rows, TableRow.Cells and a TableRow indexer return ITableRow and ITableCell.
- ITableRow and ITableCell have no AddText, AddChild or ExtractText.
- Cast to TableRow or TableCell first.
- SaveAs writes no merge made with Table.MergeCells.
- For a cell that spans two columns, add a new TableRow of two TableCells with AddRow.
- Give the first of those two cells GridSpan = 2.
- Shade alternate rows with Table.Zebra = new ZebraColor(first, second).
- SaveAs writes no fill set through TableCell.Shading.
- With no key set, every save throws LicensingException.
- An in-memory build does not throw it, so it proves nothing about the saved file.
Markdown

Every rule in that file is derived from a build, an in-memory check or a saved file. The shipped XML documentation lists ApplyCellStyle on the table, but the compiler rejects the call with CS1061, so the XML alone does not show whether a member compiles. A probe showed that a fill set through a cell's Shading property was never written to the saved file, while Zebra wrote a fill to every cell.

How do you build a statement table from CSV data?

Statement code can fail at this stage without producing an error. In the saved files, a closing row merged with MergeCells still contained three plain cells, although the table recorded the merge in memory. A title set to 18 point with no font was saved with no size. Only an inspection of the saved file detects either problem.

Rework @src/Statements/Pipeline/StatementJob.cs to write one statement per account, in this order:

1. Read Data/accounts.csv and Data/transactions.csv.
2. Group the transactions by account.
3. Call StatementBuilder.Build once per account.
4. Write <AccountId>.docx into the output folder that StatementJob.Run receives.

That folder is out/statements under the solution root.

The data needs care in a few places:
- Descriptions such as "Rent, September" hold commas.
- Parse quoted fields with a few lines of your own code, and add no CSV package.
- Parse amounts as decimal with the invariant culture.
- An account with no transactions still gets a statement, with its heading and closing rows.

Run the job.
For every statement, print its path, its transaction row count and its closing balance.
Text

The fixture contains four accounts, with five, two, three and zero transactions. In the CSV file, the savings account's rows are not in date order, and the layout requires the oldest first.

How do you test a saved IronWord statement?

ExtractText on the document returned its paragraphs but none of the table text, so a test based on it passes even when every cell is empty.

Switch Copilot CLI to plan mode with Shift+Tab before sending this prompt.

Plan an xUnit suite in tests/Statements.Tests, and wait for my approval before you write any code.

Every test loads IRONWORD_LICENSE_KEY into the License.LicenseKey property first.
ExtractText leaves out the text inside tables.
Read the statement table through Tables, Rows and Cells instead.
A reopened file reads GridSpan and MergedCells back empty.
The merged label shows up there as a closing row of two cells.

The suite has to prove these points:
- Each account in Data/accounts.csv gets a table of its transactions.
- The table also holds a heading row and that closing row.
- Cell values and the closing balance match the CSV files.
- Every heading run that holds text is bold.
- The title is bold and 18 point.
- CHK-40902 has the heading and closing rows and no others.

Once I approve, write the tests, run dotnet test and tell me the pass and fail counts.
Text

With a plan first, you can check how the suite reads a merged cell and a run's style before any assertion is written.

How do you fix IronWord styling code that does not compile?

IronWord's table guide styles a cell's text with an object initializer on TextContent. That sample fails to build with CS0200 because those properties are read-only.

A teammate adapted @src/Statements/Documents/OverdraftNotice.cs from IronWord's add-table how-to.
Now the solution no longer builds.

Before you edit anything, work through these steps:
1. Build and quote each compiler error.
2. Open the IronWord.xml file inside the IronWord package in the NuGet cache.
3. List the styling members it gives for TextStyle and Run.

Then rewrite just the styling in that file.
The saved notice must keep its bold 14 point heading and its bold amount.
Rebuild and confirm the error count is zero. Don't touch any other file.
Text

The compiler reports errors on the heading and the amount, and the XML lookup lists the TextStyle members that the fix uses instead.

What makes an issue the cloud agent can act on?

The cloud agent works from the issue's title, description and existing comments, plus any instructions provided at assignment. According to GitHub, comments added later are never passed to the agent, so the completion criteria belong in the issue body.

Title: Show debit amounts in red on statements

The change covers these files and versions:
- Files: src/Statements/Documents/StatementBuilder.cs, tests/Statements.Tests
- IronWord version: the one Directory.Packages.props sets
- Framework: .NET 9

The expected result is this:
- In the Amount column, every negative amount is red (C00000).
- Positive amounts, the heading row and the closing row keep their current color.
- docs/statement-layout.md gains the rule in the pull request text only.

The actual result is that every amount is black.

The constraints are these:
- Keep the Build signature.
- Style the runs as .github/instructions/documents.instructions.md says.
- Add no packages.

The acceptance test is this:
- The current tests still pass.
- A new test reopens CHK-40211.docx and finds red runs in exactly the four debit cells.
Markdown

Because the test counts colored cells in one known statement, a change that colors the entire column fails it.

Why do the cloud agent's tests fail on an IronWord project?

By default, the cloud agent works in an Ubuntu Linux environment. IronWord 2026.10.4 ships a native library for linux-x64 inside the package, so a NuGet restore on the runner includes it. The runner has no license key. On a test machine without a key, the statement built in memory without error, and every save call in the probe threw a LicensingException whose message read "Production License Required". Every test that writes a file fails even when the code is correct.

Before Copilot starts work, GitHub Actions executes the copilot-setup-steps job from .github/workflows/copilot-setup-steps.yml. The workflow does not trigger until the default branch contains that file.

The cloud agent's Ubuntu runner needs .github/workflows/copilot-setup-steps.yml.
GitHub picks up a single job called copilot-setup-steps, so give the file exactly that job.

Set the job up like this:
- Give it read access to the repository contents.
- Trigger it on workflow_dispatch and on pull requests that touch the file itself.
- Fetch the code and install whichever .NET SDK the projects use.
- Run a restore, then build in Release.

Without a license key, every IronWord save throws a LicensingException.
Add a step that checks IRONWORD_LICENSE_KEY, which is an Agents secret.
GitHub documents Agents secrets for the cloud agent's environment.
That includes this workflow when it runs for the agent.
So the check works like this:
- In pull_request and workflow_dispatch runs, it prints a warning and passes.
- In any other run, it exits non-zero with a clear message when the key is empty.

Put that step last, because a failing setup step makes Copilot skip whatever follows it.
Write the file, then show me the finished YAML.
Text

With the check placed last, a missing secret still leaves a restored and compiled solution, plus one line that identifies the problem.

Store the key as an Agents secret: secrets saved for Actions, Codespaces or Dependabot are not passed to the cloud agent. Add IRONWORD_LICENSE_KEY in the repository's Settings under Security, Secrets and variables, Agents, and the agent receives it as an environment variable.

What should you check before merging?

Start the review with its own copilot -p call, so it runs in a different session from the one that wrote the code.

/review every change committed after the first commit on this branch.
Hold every IronWord call up against .github/instructions/documents.instructions.md.
Check it against the XML API documentation in the IronWord NuGet package as well.
List each place they conflict.

Also flag any of these:
- styling set on TextContent instead of on runs
- a table cell filled anywhere but on the cell the table already holds
- a closing row that spans its label with MergeCells instead of GridSpan
- a font size with no TextFont beside it
- a test that reads table data with ExtractText instead of reloading the saved file
- any line that prints IRONWORD_LICENSE_KEY

Tell me what you find, and don't edit any file.
Text

What did the prompts produce in a real run?

Copilot CLI ran all six prompts unattended on a statement solution built for this guide. We rebuilt each commit, reran the job and tests, and read the DOCX files with a script that does not use IronWord.

  • The teammate's notice file caused 3 CS0200 build errors before the styling prompt and 0 after it.
  • Only that file changed in the styling fix.
  • The notice kept its bold 14 point heading and bold amount.
  • All 4 statements passed the DOCX check from the CSV prompt onward.
  • All 4 closing balances matched the CSV data.
  • Each closing row had two cells, with the label spanning two columns.
  • The title was bold at 18 point.
  • The heading row was bold.
  • Rows alternated FFFFFF and F2F2F2.
  • Copilot's 3 tests passed.
  • Each test read the saved table through its rows and cells.
  • The setup workflow parsed as YAML, with one copilot-setup-steps job and the key check last.
  • The review call changed no file and flagged two places in the builder. One fills the closing row's cells before adding the row, as the path-specific file specifies. The other uses TableCell.SetContent instead of AddChild or AddText.
  • Some steps ran differently from the page. The session ran headless, so plan mode used the --plan flag and approval was a typed reply. The issue, cloud agent, Agents secret and setup workflow need a hosted repository and did not run.

The Cursor guide scopes its IronWord rule to file patterns with globs, as the path-specific instructions file does here with applyTo. The Google Antigravity guide publishes an IronWord page for linux-x64, the platform the cloud agent runs on.

The Copilot CLI version, the model that Auto picked, the test machine and the run's duration appear in the shared run record.


Troubleshooting


Questions?

If you have any questions, reach out to support@ironsoftware.com

Curtis Chau holds a Bachelor’s degree in Computer Science (Carleton University) and specializes in front-end development with expertise in Node.js, TypeScript, JavaScript, and React. Passionate about crafting intuitive and aesthetically pleasing user interfaces, Curtis enjoys working with modern frameworks and creating well-structured, visually appealing manuals.

Read More

Ready to Get Started?

Nuget Downloads 58,016Version:2026.10just released