AgentStack
SKILL verified MIT Self-run

Revit External Events

skill-nice3point-revit-skills-revit-external-events · by Nice3point

>

No reviews yet
0 installs
0 views
view→install

Install

$ agentstack add skill-nice3point-revit-skills-revit-external-events

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Revit External Events? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Revit External Events

The Revit API may only be touched inside its API context. Nice3point.Revit.Toolkit external events auto-initialize, so you can construct and Raise them from any thread without creating them inside the API context first.

When to use

  • Invoking the API from a modeless WPF window, a background task, or another non-API-context callback.
  • Reviewing that code outside the API context does not touch the API directly.

When not to use

  • The caller already runs in the API context (an external command, a Revit event handler, a Revit-invoked callback) — call the API directly.
  • The window is shown modally with ShowDialog: it runs on the API thread, where the API is reachable directly.

Workflow

Step 1: Choose the event shape

  • ExternalEvent / ExternalEvent — fire-and-forget with zero or one argument.
  • AsyncExternalEvent / AsyncExternalEvent — await completion.
  • AsyncRequestExternalEvent / `` — await a returned result.

Step 2: Prefer the [ExternalEvent] generator

Mark every containing type partial and annotate a handler method with [ExternalEvent]. The generator emits the event member from the method signature and names it after the method — Event for the synchronous form and AsyncEvent for the awaitable one — and you raise the work through that generated property. Keep the transaction inside the method.

public sealed partial class WindowCleaner
{
    [ExternalEvent]
    private void DeleteWindows(UIApplication application)
    {
        var document = application.ActiveUIDocument.Document;
        using var transaction = new Transaction(document, "Delete windows");
        transaction.Start();
        document.Delete(document.GetInstanceIds(BuiltInCategory.OST_Windows));
        transaction.Commit();
    }

    // from any thread or context
    private void OnDeleteClick() => DeleteWindowsEvent.Raise();
}

A value-returning method generates only the awaitable request form (AsyncEvent, raised with await); a method with two or more extra parameters also gets a generated Raise(...)/RaiseAsync(...) taking the individual arguments.

Step 3: Construct an event manually when the generator does not fit

Use the public constructors for a lambda or field, a non-partial type, a DI-created instance, or a signature the generator rejects. Each event type offers two handler families — one that takes the argument(s) only, and one that also receives the current UIApplication — and every family has an optional ExternalEventOptions overload. (There is no ExternalEvent.Create(...) factory; the constructor is the manual entry point.)

private readonly ExternalEvent _deleteElement = new((application, elementId) =>
{
    var document = application.ActiveUIDocument.Document;
    using var transaction = new Transaction(document, "Delete element");
    transaction.Start();
    document.Delete(elementId);
    transaction.Commit();
});

// from any thread or context
_deleteElement.Raise(elementId);

Omit UIApplication when you do not need it (new ExternalEvent(() => …), new ExternalEvent(arg => …)) and read ambient state through RevitContext instead. Await completion with AsyncExternalEvent/AsyncExternalEvent (await …RaiseAsync()), and return a value with AsyncRequestExternalEvent/` (var result = await …RaiseAsync(arg)`).

Step 4: Allow direct invocation only for dual-context operations

Set [ExternalEvent(AllowDirectInvocation = true)] only when one operation must serve callers inside and outside the API context; it runs inline when already in API mode. Never use it to bypass ownership or concurrency rules.

Step 5: Verify

Await async event results before consuming generated Revit objects, and test the observable model result.

Validation

  • [ ] Code outside the API context reaches the API through an external event.
  • [ ] Transactions stay inside the event callback or annotated method.
  • [ ] An [ExternalEvent] method's containing type is partial.
  • [ ] AllowDirectInvocation is set only for a real dual-context need.

Common Pitfalls

| Pitfall | Correct approach | |-----------------------------------------------------------------------------|-----------------------------------------------------------------| | [ExternalEvent] on a method whose type is not partial (RVTTK0005) | Mark the containing type partial. | | [ExternalEvent] method returns Task or is async void (RVTTK0001/0002) | Use a synchronous signature; use Async… event types to await. | | Generic [ExternalEvent] method (RVTTK0003) | Use concrete parameter types. | | Using an external event while already in API context | Call the API directly. | | ExternalEvent or [ExternalEvent] not found | The Nice3point.Revit.Toolkit package is not referenced. |

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.