-
-
Notifications
You must be signed in to change notification settings - Fork 0
11 AI Agents
AI agents (Claude Code, GitHub Copilot Workspace, CI scripts) can drive Flowline without parsing free-form output. Two contracts make that work: structured exit codes, and error messages that include the corrective action. There's also an agent plugin for Claude Code and Codex that makes Flowline commands available without extra prompting.
Install Flowline as an agent plugin so Claude Code or Codex already knows how to drive it before you've even cloned a project:
# Claude Code
/plugin marketplace add RemyDuijkeren/Flowline
/plugin install flowline@flowline
# Codex
codex plugin marketplace add RemyDuijkeren/Flowline
codex plugin add flowline@flowlineShips five skills. Each carries its own trigger conditions, so the agent loads the relevant one without being told which. Edit a plugin class and the plugin-authoring skill applies itself.
-
flowlinecovers the core loop this page documents: detect a project, edit code with attributes/annotations,push --dry-run→push→sync→deploy, branch on exit codes. Triggers on a.flowlinefile at the repo root, or on any mention of Dataverse plugins, web resources, Custom APIs, or solution deploy. -
flowline-pluginscovers authoring plugin steps and Custom APIs: the class-naming convention that determines message, stage, and processing mode, the[Step]/[Filter]/[PreImage]/[PostImage]/[CustomApi]attributes, and how registrations are verified. Triggers when writing, renaming, splitting, or reviewing a C# plugin class in a repo referencingFlowline.Attributes, when converting a Power Automate flow or workflow into a plugin, or whenpushreports a step it could not parse or that landed on the wrong message or stage. See Push Plugins and Custom APIs. -
flowline-webresourcescovers authoring web resources: how a file underdist/becomes a Dataverse web resource name, the IIFE global a form event handler is registered against, and the// flowline:onload/onsave/onchange/tabstatechange/onreadystatecomplete/dependsannotations that wire forms and dependencies without a Maker Portal visit. Triggers when writing or reviewing a script, image, or HTML page underWebResources/, when a handler is not firing on a form, or whenpushreports a malformed annotation, a form it cannot find, or a name collision. See Push WebResources. -
flowline-generatecovers early-bound types: whether they are needed at all, where the generated models land, which settings persist to.flowline, and the three generators (pac,xrmcontext,xrmcontext3). Triggers when a plugin references a table or column that will not compile, whenModels/is missing or stale after a schema change, when choosing between late-bound and early-bound, or when output lands in an unexpected folder or namespace. See Generate Early-Bound Types. -
flowline-migrationdetects an existing spkl, Daxif, PACX, or ALM Accelerator project unprompted (aspkl.json, a Daxif_Config.fsxor*.daxif, a.pacxproj, or an Azure DevOps pipeline using Power Platform Build Tools, with no.flowlineyet), walks you through the matching migration guide (spkl, Daxif, ALM Accelerator, PACX), then verifies the result by actually runningflowline push --dry-run/syncinstead of trusting that the steps were followed.
Exit codes are a stable public API and don't change across versions. Renumbering would be a breaking change and requires a release note.
Codes 3, 4, and 130 follow de facto CLI conventions (curl, git, rsync). Codes 10 to 21 are Flowline-specific.
| Code | Name | Meaning | Corrective action |
|---|---|---|---|
| 0 | Success | Command completed | None |
| 1 | GeneralError | Unexpected/unhandled error | Check error output |
| 3 | NotFound | Solution not in Dataverse or repo | Verify solution name matches .flowline
|
| 4 | NotAuthenticated | No PAC auth profile | Run: pac auth create --environment <url>
|
| 10 | ConnectionFailed | Dataverse environment unreachable | Check environment URL in .flowline
|
| 11 | ConfigInvalid |
.flowline missing or malformed |
Verify .flowline exists and is valid |
| 12 | DirtyWorkingDirectory | Uncommitted git changes block the operation |
git commit or git stash first |
| 13 | BuildFailed |
dotnet build or PAC pack failed |
Fix errors in Plugins/ and retry |
| 14 | VersionConflict | Target environment has a newer solution version | Add the --force <specifier> the error names |
| 15 | ValidationFailed | Drift detected, missing dependencies, invalid --force value, or schema mismatch |
Run flowline sync first; check error output. An invalid --force value lists the ones that are valid for that command |
| 16 | Timeout | A Dataverse request got no response, or the PAC CLI 60-minute limit was exceeded | Re-run the same command. The write may already have landed, and the reconciling commands converge on a re-run; check environment health if it repeats |
| 17 | ForceRequired | Destructive operation requires explicit confirmation | Add the --force <specifier> the message names, e.g. --force config, --force recreate-assembly
|
| 18 | PartialSuccess | Deploy completed but orphan cleanup failed for some components | Remove the named items manually via maker portal |
| 19 | Inconclusive | A check couldn't run to completion: drift's empty-input guard skipped the comparison, or a deploy verification step couldn't finish (e.g. a locked directory or a Dataverse query fault) |
Not a pass/fail signal. Investigate the printed reason before trusting the result |
| 20 | WriteTargetOccupied | A file already occupies a path the command would write to: scaffold meeting an existing template file, or --output pointing at a file rather than a folder |
Distinct from 11: nothing is missing or malformed, something valid is in the way. Move the named file aside, or run the command somewhere else |
| 21 | AssemblyNotRegistered | Deploy completed but a plug-in package holds an assembly with no registration in the target, or one registered with no plugin types | Create the pluginassembly record under that package (sandbox isolation, matching version/culture/public key token), then deploy again so the content write populates its plugin types. This repeats every deploy until that record exists |
| 130 | Cancelled | Ctrl+C / SIGINT, or deploy's first-import confirmation declined interactively |
For the confirmation case: re-run with --force first-import to proceed non-interactively |
Codes 2 and 5 are intentionally unused.
Every error message states what happened and what to do next. For the four most actionable codes, the corrective command is included verbatim in the message, so agents can extract the fix directly without consulting a lookup table:
| Code | Message includes |
|---|---|
| 4 (NotAuthenticated) | run: pac auth create --environment <url> |
| 12 (DirtyWorkingDirectory) | Commit or stash changes first |
| 14 (VersionConflict) | Add --force <specifier> to overwrite |
| 17 (ForceRequired) |
Use --force <specifier> to proceed, and the specifier named is always valid for the command that raised it |
Each command description follows the what + when + what-changes-after pattern:
| Command | What it does | When to run | What changes after |
|---|---|---|---|
clone |
Initialize project from an existing Dataverse solution | One-time setup per solution that already exists | Folder structure, unpacked XML, Plugins and WebResources projects, AGENTS.md, CLAUDE.md, DATAVERSE_CONTEXT.md
|
init |
Create publisher + empty unmanaged solution in DEV, then scaffold | One-time setup when the solution does not exist in Dataverse yet | Publisher and solution created in DEV; then everything clone produces; DEV URL saved to .flowline
|
push |
Register plugins and web resources to DEV | After plugin or web resource changes | DEV reflects current source; no pack/import involved |
sync |
Export from DEV, bump version, unpack to XML | After testing changes in DEV |
Solution/src/ updated; build version bumped; DATAVERSE_CONTEXT.md regenerated |
deploy |
Pack and import into target environment | After sync + commit |
Target environment updated with current solution version |
provision |
Create environment by copying from PROD | One-time setup for new environments | Environment URL saved to .flowline
|
generate |
Generate early-bound C# types from solution | After adding or modifying entities or custom APIs |
Plugins/Models/ overwritten with generated .cs files |
drift |
Report what deploy's orphan cleanup would delete |
Before deploy, or as a standalone check on prod/test
|
Read-only, nothing changes |
scaffold |
Write a project template (webresources) into the current folder |
Adding web resources to a repo that has none |
WebResources/ project written where you are standing, and added to the nearest solution file (searched upward to the .flowline/.git root) when there is one. --output moves the folder, --name names it. No Dataverse call |
status |
Show environment, auth, and git state | Before running commands; when troubleshooting | Read-only, nothing changes |
sln add |
Add a .cdsproj to the solution file |
A repo assembled by hand or migrated off another tool | Solution file updated, found by searching upward to the .flowline/.git root, same rule as scaffold. No Dataverse call |
dotnet build # build plugin assembly
flowline push # register DLL + web resources in DEV
flowline push --dry-run # preview changes before touching Dataverse
# make changes in the maker portal if needed
flowline sync # pull solution state from DEV, unpack to repo
git add . && git commit -m "..." # commit the unpacked XML diff
flowline deploy test # promote to TEST
flowline deploy prod # promote to PROD-
Use Flowline, not
pac solutiondirectly. Flowline wraps PAC commands with correct sequencing, version bumping, and cleanup. -
Pick
initvscloneby whether the solution already exists in Dataverse.initcreates one and refuses a unique name that's already taken;cloneadopts an existing one and never creates. Guessing wrong costs a round-trip, not data. -
initruns against Sandbox and Developer environments only. It's an allow-list, so Production, Trial, Default and unknown types are all refused with exit15. It also never creates apac authprofile; if none matches the target it errors namingpac auth create. -
initnever prompts in a non-interactive run. Pass<name>,--dev <url>, and--publisher-prefix <prefix>. Each missing one fails naming the flag rather than hanging on a prompt. -
syncrequires a clean working directory. Exits12if there are uncommitted changes inSolution/src/. Commit or stash first. -
deploypacks from source, not from a previous export. If local changes exist that haven't been synced to DEV, deploy would overwrite them. Flowline detects this drift and exits15. Runsyncfirst. -
--force <specifier>is required for destructive operations in non-interactive mode.--forcealways takes a value naming the exact hazard being approved (e.g.--force dirty,--force config). A bare--forceis a parse error, and an unrecognized value fails listing the ones valid for that command. Without it, Flowline exits17rather than asking for confirmation, which is safe for scripted use. See 04-Command-Reference for the per-command--forcevocabulary. -
statusis always safe to run. Read-only; use it to verify connectivity and auth before running other commands.
.flowline ← environment URLs + solution config
docs/DATAVERSE_CONTEXT.md ← schema snapshot for AI agents (auto-generated)
Solution/<SolutionName>.cdsproj ← solution metadata
Solution/src/ ← unpacked solution XML (git-diffable)
Plugins/<SolutionName>.Plugins.csproj ← plugin source, decorated with [Step] attributes
WebResources/<SolutionName>.WebResources.csproj ← web resource build project
WebResources/dist/ ← web resource files synced to Dataverse by push
AGENTS.md ← scaffolded by flowline clone / init (this file)
CLAUDE.md ← scaffolded by flowline clone / init (`@AGENTS.md` import)
Need a genuine second solution in one repo? Two documented patterns: a separate repo per solution (default), or a nested solutions/<Name>/ folder inside one repo containing multiple independent, self-contained Flowline projects (each with its own .flowline, Solution/, Plugins/, WebResources/). This works via Flowline's existing upward .flowline discovery, with no code changes required.
flowline clone writes an AGENTS.md file at the repo root with the solution name substituted throughout. It includes:
- The daily dev loop for this specific solution
- Project structure with actual paths
- Exit code reference table
- Rules for this project
If AGENTS.md already exists, Flowline skips the write and logs an info message. It will not overwrite a customized file.
GitHub Copilot and Codex load AGENTS.md automatically on project open. Claude Code does not read AGENTS.md directly, it only auto-loads CLAUDE.md, so flowline clone also scaffolds a one-line CLAUDE.md containing @AGENTS.md, which Claude Code resolves as an import. Same skip-if-exists behavior applies. Keep AGENTS.md accurate as the project evolves.
After every successful flowline clone or flowline sync, Flowline writes docs/DATAVERSE_CONTEXT.md, a curated markdown snapshot of the solution schema optimised for AI token efficiency.
| Section | Content |
|---|---|
| Solution | Display name, unique name, version, publisher prefix |
| Entities | Logical name, display name, EntitySetName, ownership type |
| Attributes | Logical name, type, required level, description, custom flag |
| Option sets | Inline value → label mapping for picklist and boolean fields |
| Forms | Condensed tab → section → field list (hidden controls excluded) |
| Views | Column list and filter summary (no verbatim FetchXML) |
| Workflows | Display name, activation state, trigger entity |
| Plugin steps | Message, entity, stage (pre/post-operation), mode (sync/async), class name |
| Connection references | Display name, connector ID |
Sections with no content are omitted. No GUIDs appear in the output.
flowline clone seeds AGENTS.md with a "Dataverse schema context" section at the end:
## Dataverse schema context
- [ContosoSales](docs/DATAVERSE_CONTEXT.md)
@docs/DATAVERSE_CONTEXT.mdThe @ import causes Codex and GitHub Copilot to load the schema automatically on every request from AGENTS.md, and Claude Code to load it via the CLAUDE.md → AGENTS.md import chain.
After every flowline sync, Flowline checks AGENTS.md and appends the import and link if missing (self-healing). The entry is never duplicated on repeated runs.
If AGENTS.md does not exist when sync runs, Flowline emits a warning and skips the mutation. Run flowline clone to scaffold it.