Skip to content

Commit 66997b5

Browse files
committed
v0.3.1: full Mac Office coverage + CI + comprehensive docs sweep
Adds Word.Mac, PowerPoint.Mac, Outlook.Mac plugins (AppleScript via osascript). Extracts shared Osascript helper into ComBridge.Mac.Common so the four Mac plugins don't triplicate subprocess plumbing. All Mac plugins use the same Name as their Windows counterparts (word/ powerpoint/outlook) — PluginLoader's SupportedPlatforms filter picks the right one per OS. GitHub Actions CI (.github/workflows/build.yml): macOS runner builds Core + CLI + Mac.Common + 4 Mac plugins and smoke-tests list-plugins. Windows runner builds the same cross-platform parts (proves they compile on Windows too). Windows plugins requiring installed Office / SOLIDWORKS are NOT in CI — runners don't have those, and the COMBRIDGE001 validation catches missing interop at developer build time. LLM docs sweep: - LLM/plugins.md gets a full "macOS plugins" section - LLM/authoring.md gets a "macOS plugin pattern" worked-example with drop-in skeleton + other AppleScript-friendly apps to consider - LLM/troubleshooting.md gets a "macOS / AppleScript" section covering TCC permission prompts, slow loops, "App isn't running" diagnosis, New Outlook for Mac caveats - LLM/build.md gets Mac build commands + per-OS plugin availability - LLM/workflow.md task router has a Mac-plugin entry - LLM/symbols.md indexes the new plugins + shared library - LLM/README.md defaults table lists all 4 Mac plugins Windows regression clean — list-plugins still shows exactly the 5 Windows plugins; 4 Mac plugins deployed but silently filtered out by OS check. Both TFMs build with zero warnings. The Mac side still hasn't been live-tested — needs an actual Mac. The LLM/troubleshooting.md Mac section captures the failure modes I expect based on AppleScript / TCC / Office for Mac empirical knowledge; first real Mac test will likely surface things to add.
1 parent 5e1c2c0 commit 66997b5

23 files changed

Lines changed: 1345 additions & 19 deletions

.github/workflows/build.yml

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
name: Cross-platform build
2+
3+
# Validates the multi-target architecture: Core, CLI, and Mac plugins MUST
4+
# build on macOS; Core, CLI, and Mac plugins SHOULD build on Windows
5+
# (Windows plugins need installed apps that GitHub-hosted runners don't have,
6+
# so we skip them — they're verified manually on developer machines).
7+
8+
on:
9+
push:
10+
branches: [main]
11+
tags: ['v*']
12+
pull_request:
13+
branches: [main]
14+
workflow_dispatch:
15+
16+
jobs:
17+
macos:
18+
# Validates the cross-platform parts: ComBridge.Core (net10.0 TFM),
19+
# ComBridge.Cli (net10.0 TFM), ComBridge.Mac.Common, and the four Mac
20+
# plugins. This is the build that proves the architecture isn't
21+
# Windows-only.
22+
runs-on: macos-latest
23+
steps:
24+
- uses: actions/checkout@v4
25+
26+
- name: Set up .NET 10
27+
uses: actions/setup-dotnet@v4
28+
with:
29+
dotnet-version: '10.0.x'
30+
31+
- name: Build Core (net10.0)
32+
run: dotnet build src/ComBridge.Core/ComBridge.Core.csproj -c Release -f net10.0
33+
34+
- name: Build CLI (net10.0)
35+
run: dotnet build src/ComBridge.Cli/ComBridge.Cli.csproj -c Release -f net10.0
36+
37+
- name: Build Mac.Common
38+
run: dotnet build src/ComBridge.Mac.Common/ComBridge.Mac.Common.csproj -c Release
39+
40+
- name: Build Mac plugins
41+
run: |
42+
dotnet build src/plugins/ComBridge.Plugins.Excel.Mac/ComBridge.Plugins.Excel.Mac.csproj -c Release
43+
dotnet build src/plugins/ComBridge.Plugins.Word.Mac/ComBridge.Plugins.Word.Mac.csproj -c Release
44+
dotnet build src/plugins/ComBridge.Plugins.PowerPoint.Mac/ComBridge.Plugins.PowerPoint.Mac.csproj -c Release
45+
dotnet build src/plugins/ComBridge.Plugins.Outlook.Mac/ComBridge.Plugins.Outlook.Mac.csproj -c Release
46+
47+
- name: Smoke-test combridge binary (no apps running)
48+
run: |
49+
BIN=src/ComBridge.Cli/bin/Release/net10.0
50+
# The Mac plugins were deployed by their CopyToPluginsRoot targets to ./plugins/
51+
# — but the binary expects them next to itself. Stage them there.
52+
mkdir -p "$BIN/plugins"
53+
cp -R plugins/Excel.Mac plugins/Word.Mac plugins/PowerPoint.Mac plugins/Outlook.Mac "$BIN/plugins/"
54+
chmod +x "$BIN/combridge"
55+
"$BIN/combridge" list-plugins
56+
# Should list excel, outlook, powerpoint, word — all 4 Mac plugins.
57+
# Windows plugins compiled too but their net10.0-windows DLLs aren't
58+
# built on macOS, so plugins/Excel/ etc. don't exist here.
59+
60+
windows:
61+
# Validates that the Windows side still builds end-to-end. The Windows
62+
# plugins need Office/SOLIDWORKS installed to fully build (Common.Paths.props
63+
# validation will fail for SolidWorks/Word/PowerPoint/Outlook on a vanilla
64+
# runner). We build just Core + CLI + Excel (NuGet PIA) + the Mac plugins
65+
# (which work cross-platform).
66+
runs-on: windows-latest
67+
steps:
68+
- uses: actions/checkout@v4
69+
70+
- name: Set up .NET 10
71+
uses: actions/setup-dotnet@v4
72+
with:
73+
dotnet-version: '10.0.x'
74+
75+
- name: Build Core (both TFMs)
76+
run: dotnet build src/ComBridge.Core/ComBridge.Core.csproj -c Release
77+
78+
- name: Build CLI (both TFMs)
79+
run: dotnet build src/ComBridge.Cli/ComBridge.Cli.csproj -c Release
80+
81+
- name: Build Mac.Common + Mac plugins (cross-platform — should build on Windows too)
82+
run: |
83+
dotnet build src/ComBridge.Mac.Common/ComBridge.Mac.Common.csproj -c Release
84+
dotnet build src/plugins/ComBridge.Plugins.Excel.Mac/ComBridge.Plugins.Excel.Mac.csproj -c Release
85+
dotnet build src/plugins/ComBridge.Plugins.Word.Mac/ComBridge.Plugins.Word.Mac.csproj -c Release
86+
dotnet build src/plugins/ComBridge.Plugins.PowerPoint.Mac/ComBridge.Plugins.PowerPoint.Mac.csproj -c Release
87+
dotnet build src/plugins/ComBridge.Plugins.Outlook.Mac/ComBridge.Plugins.Outlook.Mac.csproj -c Release
88+
89+
- name: Smoke-test combridge.exe (list-plugins on a runner with no Office/SW)
90+
run: |
91+
$bin = "src/ComBridge.Cli/bin/Release/net10.0-windows"
92+
# Stage the Mac plugins so PluginLoader can attempt to load them
93+
# (they'll be filtered out by SupportedPlatforms — verifies the filter works)
94+
New-Item -ItemType Directory -Force "$bin/plugins" | Out-Null
95+
Copy-Item -Recurse plugins/Excel.Mac, plugins/Word.Mac, plugins/PowerPoint.Mac, plugins/Outlook.Mac "$bin/plugins/" -ErrorAction SilentlyContinue
96+
& "$bin/combridge.exe" list-plugins
97+
# Expected: empty list (no Windows plugin DLLs without their COM apps)
98+
# OR: Excel.Mac etc. filtered out (showing the filter works correctly)
99+
shell: pwsh
100+
101+
# Note on coverage: the SolidWorks, Excel (Windows), Word (Windows),
102+
# PowerPoint (Windows), and Outlook (Windows) plugins all need their target
103+
# apps installed (or in SolidWorks' case the API SDK installed) — neither is
104+
# available on GitHub-hosted runners. Their builds are verified manually on
105+
# developer machines per the LLM/build.md "Build prerequisites per plugin"
106+
# section. A self-hosted runner with Office/SOLIDWORKS would close that gap
107+
# if someone wants to pay for the licenses and machine.

CHANGELOG.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,51 @@ All notable changes to this project will be documented in this file.
44
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
55
versions follow [SemVer](https://semver.org/spec/v2.0.0.html).
66

7+
## [0.3.1] — full Mac Office coverage + CI + comprehensive docs sweep
8+
9+
### Added
10+
- **`ComBridge.Mac.Common`** library — shared `Osascript` helper used by
11+
all Mac plugins. Extracted from Excel.Mac so Word.Mac / PowerPoint.Mac /
12+
Outlook.Mac aren't triplicating the same subprocess plumbing.
13+
- **`ComBridge.Plugins.Word.Mac`** — AppleScript-backed Word for Mac
14+
plugin. Commands: `info`, `extract-text`, `doc-stats`. Same CLI name
15+
(`word`) as the Windows Word plugin.
16+
- **`ComBridge.Plugins.PowerPoint.Mac`** — AppleScript-backed PowerPoint
17+
for Mac plugin. Commands: `info`, `list-slides`. Same CLI name
18+
(`powerpoint`).
19+
- **`ComBridge.Plugins.Outlook.Mac`** — AppleScript-backed Outlook for
20+
Mac plugin (with documented limitations vs the Windows MAPI plugin —
21+
no Stores collection, thinner dictionary, "New Outlook for Mac"
22+
restrictions noted). Commands: `info`, `list-accounts`.
23+
- **GitHub Actions CI** (`.github/workflows/build.yml`) — two jobs:
24+
- macOS runner: builds Core, CLI, Mac.Common, all 4 Mac plugins,
25+
smoke-tests `combridge list-plugins`.
26+
- Windows runner: builds Core (both TFMs), CLI (both TFMs), Mac
27+
plugins (proves they compile cross-platform). Windows plugins
28+
needing installed Office/SOLIDWORKS are NOT built (no app
29+
available on hosted runners; documented in workflow comments).
30+
- **LLM docs full cross-platform sweep**:
31+
- `LLM/plugins.md` § "macOS plugins" with per-plugin specifics, AppleScript app names, what differs vs Windows, implementation notes
32+
- `LLM/authoring.md` § "macOS plugin pattern" — prescriptive template + reference layout + drop-in skeleton + other AppleScript-friendly apps
33+
- `LLM/troubleshooting.md` § "macOS / AppleScript issues" — TCC permission prompts, `osascript` slowness in loops, "Application isn't running" cause, New Outlook for Mac caveats, plugin-doesn't-load diagnostics
34+
- `LLM/build.md` — Mac build commands + per-OS plugin availability table
35+
- `LLM/workflow.md` task router — "Add a plugin for macOS" entry
36+
- `LLM/symbols.md` — Mac plugin deployment paths + Mac.Common library + symbol index
37+
- `LLM/README.md` defaults table — all 4 Mac plugins listed
38+
39+
### Architecture
40+
- Plugin tree now categorized by platform:
41+
- 5 Windows plugins (SW + Office)
42+
- 4 Mac plugins (Office only — SolidWorks doesn't exist on macOS)
43+
- 1 shared library (Mac.Common)
44+
- A single combridge bundle ships all 9 plugin folders side-by-side; the
45+
OS-supported ones load per machine.
46+
47+
### Migration
48+
- No source changes needed for existing plugins.
49+
- Existing v0.3.0 release tag remains valid; v0.3.1 adds Mac coverage
50+
+ CI without breaking anything.
51+
752
## [0.3.0] — cross-platform foundation (Windows + macOS)
853

954
### Added

LLM/README.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,16 @@ PLUGIN_LOAD_DIR: `<exeDir>/plugins/<Name>/<assembly>.dll`
6363
| `powerpoint` | Windows | `["PowerPoint.Application"]` | `true` | `PptGlobals` | `Microsoft.Office.Interop.PowerPoint` |
6464
| `outlook` | Windows | `["Outlook.Application"]` | `true` | `OlGlobals` | `Microsoft.Office.Interop.Outlook` |
6565
| `excel` (Mac plugin) | macOS | `["Microsoft Excel"]` (AppleScript app name) | `true` | `XlMacGlobals` | `ComBridge.Plugins.Excel.Mac` |
66+
| `word` (Mac plugin) | macOS | `["Microsoft Word"]` | `true` | `WdMacGlobals` | `ComBridge.Plugins.Word.Mac` |
67+
| `powerpoint` (Mac plugin) | macOS | `["Microsoft PowerPoint"]` | `true` | `PptMacGlobals` | `ComBridge.Plugins.PowerPoint.Mac` |
68+
| `outlook` (Mac plugin) | macOS | `["Microsoft Outlook"]` | `true` | `OlMacGlobals` | `ComBridge.Plugins.Outlook.Mac` |
6669

67-
Two plugins share `Name = "excel"` — the one targeting the current OS
68-
loads, the other is silently filtered out by `PluginLoader` per its
69-
`SupportedPlatforms`. So `combridge excel <command>` works the same on
70-
Windows and macOS (different backend, same CLI contract).
70+
Pairs share their CLI `Name` (`"excel"`, `"word"`, `"powerpoint"`,
71+
`"outlook"`) — the one targeting the current OS loads, the other is
72+
silently filtered out by `PluginLoader` per its `SupportedPlatforms`.
73+
`combridge <app> <command>` works the same on Windows and macOS
74+
(different backend, same CLI contract). SolidWorks has no Mac plugin —
75+
SOLIDWORKS doesn't exist on macOS.
7176

7277
## When verifying SolidWorks API calls
7378

LLM/authoring.md

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,130 @@ Console.WriteLine($"version: {d.Version}");
364364

365365
If any of these fail, the plugin isn't done. See troubleshooting.
366366

367+
## macOS plugin pattern (AppleScript via osascript)
368+
369+
Real, shipped: see `ComBridge.Plugins.{Excel,Word,PowerPoint,Outlook}.Mac`
370+
in this repo for live examples. All four follow the same template;
371+
extracting a new one for any AppleScript-driven Mac app takes ~30 min.
372+
373+
### What changes vs. a Windows plugin
374+
375+
| Aspect | Windows plugin | Mac plugin |
376+
|---|---|---|
377+
| `TargetFramework` | `net10.0-windows` | `net10.0` |
378+
| `SupportedPlatforms` | inherit default `[Windows]` | override → `[OSPlatform.OSX]` |
379+
| Interop reference | NuGet PIA or GAC HintPath | none (shells to `osascript`) |
380+
| `RotMonikerPatterns` | Used for ROT walk | Empty / unused |
381+
| `TryExtractRoot` | Used to ascend Workbook→App | Unused (no COM object) |
382+
| `DescribeInstance` | PID via HWND + Win32 | Stubbed; FindSessions does the work directly |
383+
| `FindSessions()` | Default impl uses SessionPicker | **Must override** with native discovery |
384+
| `CreateGlobals(comRoot)` | Casts comRoot to `_Application` | Ignores comRoot; instantiates wrapper |
385+
| Globals type | Strongly-typed COM interop | Custom thin wrapper (~10 members) |
386+
| Build prerequisites | Target app's COM install | Nothing beyond .NET 10 SDK |
387+
388+
### Reference layout (copy + rename for a new Mac plugin)
389+
390+
```
391+
src/plugins/ComBridge.Plugins.<App>.Mac/
392+
├── ComBridge.Plugins.<App>.Mac.csproj
393+
├── <Xx>MacApp.cs ← AppleScript wrapper (Version, ActiveDoc, ...)
394+
└── <App>MacPlugin.cs ← IComBridgePlugin impl + commands
395+
```
396+
397+
References:
398+
- `..\..\ComBridge.Core\ComBridge.Core.csproj` (`<Private>false</Private>`)
399+
- `..\..\ComBridge.Mac.Common\ComBridge.Mac.Common.csproj` (the shared `Osascript` helper)
400+
401+
### Skeleton (drop-in starting point)
402+
403+
```csharp
404+
// MyAppMacPlugin.cs
405+
using System.Runtime.InteropServices;
406+
using ComBridge.Core;
407+
using ComBridge.Mac.Common;
408+
using Microsoft.CodeAnalysis;
409+
410+
namespace ComBridge.Plugins.MyApp.Mac;
411+
412+
public sealed class MyAppMacPlugin : IComBridgePlugin
413+
{
414+
public string Name => "myapp"; // same CLI name as the Windows counterpart, if any
415+
public string Description => "MyApp for macOS (AppleScript backend). Globals: myApp.";
416+
public string[] ProgIds => new[] { "Exact AppleScript Application Name" }; // e.g. "Adobe Photoshop 2024"
417+
public bool AllowCreateNew => true;
418+
public Type GlobalsType => typeof(MyAppMacGlobals);
419+
420+
public IReadOnlyCollection<OSPlatform> SupportedPlatforms => new[] { OSPlatform.OSX };
421+
422+
public object CreateGlobals(object comRoot) => new MyAppMacGlobals();
423+
424+
public IEnumerable<MetadataReference> ScriptReferences
425+
{
426+
get
427+
{
428+
yield return MetadataReference.CreateFromFile(typeof(MyAppMacPlugin).Assembly.Location);
429+
yield return MetadataReference.CreateFromFile(typeof(Osascript).Assembly.Location);
430+
}
431+
}
432+
433+
public IEnumerable<string> ScriptUsings => new[] { "ComBridge.Plugins.MyApp.Mac" };
434+
435+
public IEnumerable<IBridgeCommand> Commands => new IBridgeCommand[] { /* yours */ };
436+
437+
public List<(object Root, SessionInfo Info)> FindSessions()
438+
{
439+
var sessions = new List<(object, SessionInfo)>();
440+
if (!Osascript.IsAvailable()) return sessions;
441+
442+
var running = Osascript.TryRun(
443+
$"tell application \"System Events\" to (name of processes) contains \"{ProgIds[0]}\"");
444+
if (running != "true") return sessions;
445+
446+
int? pid = null;
447+
var pidRaw = Osascript.TryRun(
448+
$"tell application \"System Events\" to unix id of (first process whose name is \"{ProgIds[0]}\")");
449+
if (int.TryParse(pidRaw, out var n)) pid = n;
450+
451+
string? title = /* app-specific: ActiveDocumentName equivalent */ null;
452+
var desc = (pid, title) switch
453+
{
454+
(int pp, string t) when !string.IsNullOrEmpty(t) => $"pid={pp} title={t}",
455+
(int pp, _) => $"pid={pp}",
456+
_ => "(no info)",
457+
};
458+
459+
sessions.Add((new object(), new SessionInfo(1, pid, title, desc)));
460+
return sessions;
461+
}
462+
463+
public (int? Pid, string? Title) DescribeInstance(object comRoot) => (null, null);
464+
}
465+
```
466+
467+
### Worked examples that ship in this repo
468+
469+
| App | Source | Notable |
470+
|---|---|---|
471+
| Excel | `src/plugins/ComBridge.Plugins.Excel.Mac/` | `DumpUsedRange` batches via one big AppleScript loop — avoid per-cell shell-outs |
472+
| Word | `src/plugins/ComBridge.Plugins.Word.Mac/` | `Content` via `content of text object of active document` |
473+
| PowerPoint | `src/plugins/ComBridge.Plugins.PowerPoint.Mac/` | `SlideTitles` loops in AppleScript; tab-joined return |
474+
| Outlook | `src/plugins/ComBridge.Plugins.Outlook.Mac/` | Limited dictionary documented; "New Outlook for Mac" caveat |
475+
476+
### Other AppleScript-friendly apps you could write plugins for
477+
478+
| App | AppleScript app name | Notes |
479+
|---|---|---|
480+
| Adobe Photoshop | `"Adobe Photoshop 2024"` (year suffix per version) | Rich dictionary; per-version ProgID-equivalent |
481+
| Adobe Illustrator | `"Adobe Illustrator"` | Same shape as Photoshop |
482+
| Adobe Acrobat Pro | `"Adobe Acrobat"` | Limited but workable for PDF manipulation |
483+
| OmniGraffle | `"OmniGraffle"` | Strong AppleScript support |
484+
| Sketch | `"Sketch"` | AppleScript support varies by version |
485+
| BBEdit | `"BBEdit"` | Excellent AppleScript dictionary |
486+
| Logic Pro / Final Cut Pro | varies | Apple's pro apps have rich dictionaries |
487+
488+
For any of these, copy the Excel.Mac pattern, swap the application
489+
name, and define the wrapper API surface you actually need.
490+
367491
## Worked examples for shippable-by-LLM plugins
368492

369493
These are the apps you'd plausibly want a plugin for, with the exact

0 commit comments

Comments
 (0)