---
title: CLI Workflows
description: Use the SAIF CLI for tokens, publishing, environment repair, service discovery, and documentation retrieval.
---

# CLI Workflows

Use the [generated command reference](dotnet/SAIF.Platform.CLI/commands.md) for arguments and options. This page covers operational behavior that the command manifest cannot express. For installation, use [Install the SAIF CLI](../learn/install-saif-cli.md); for the difference between resource lookups, services, and cross-domain search, see the [CLI command model](dotnet/SAIF.Platform.CLI/index.md#command-model).

Commands that accept `--format` default to human-readable `table` output; pass `--format json` when a script needs to parse the result.

## Tokens for API testing

### `saif auth generate`

Follow [JWT Test Tokens](../build/identity/testing/create-jwt-for-testing-apis.md) for the token-generation workflow, provider-specific scopes, and pipeline alternative. Entra ID is the default provider; Okta requires an explicit `--identity-provider okta`.

`--plain` prints only the token on success, but it does not make authentication non-interactive. Supply `--name` because plain mode cannot prompt for the application name; for Entra, that name must also identify a single application. Okta still opens a browser and never reuses a cached token. The [Okta browser and loopback troubleshooting](../build/identity/testing/create-jwt-for-testing-apis.md#browser-closes-or-sign-in-doesnt-complete) covers the two-minute wait, registered ports `8406`-`8408`, and headless limitations.

Use [Request Specific Scopes](../build/identity/testing/create-jwt-for-testing-apis.md#4-request-specific-scopes) for scope qualification and the `--no-default-scopes` negative-test case. Use the API pipeline's auth-server output rather than guessing an issuer; a full issuer URL must use HTTPS. These checks live in [`AuthGenerateCommand`](https://github.com/saif-corp/forge/blob/main/src/dotnet/SAIF.Platform.CLI/Commands/Auth/AuthGenerateCommand.cs).

### `saif auth clear`

Clear the Entra ID MSAL cache when you need the next Entra token request to sign in again. This does not clear an Okta session or token cache: the CLI's Okta flow is interactive-only and stores no token cache. The command asks for confirmation unless you explicitly bypass it; see the [command options](dotnet/SAIF.Platform.CLI/commands.md#saif-auth-clear).

### `saif auth validate`

Use this command to decode claims, inspect expiration, and resolve the audience to an Entra application registration. It is an inspection tool, not proof that an API will accept the token. Run without a token argument and paste when prompted to avoid leaving the token in shell history. For long or wrapped tokens, use the [word-wrapping troubleshooting guidance](../build/identity/testing/create-jwt-for-testing-apis.md#word-wrapped-tokens).

## Create and publish an application

### `saif new`

[Create projects with Forge templates](../learn/project-templates.md) owns template discovery and generation. `saif new` normally publishes after scaffolding; use `--no-publish` when you want only local generation. That leaves repository creation and pipeline registration for a later `saif publish`, not for the local AppHost run.

### `saif publish`

Run from the directory containing the project's SAIF manifest, or select that directory with `--directory`. Publishing applies the manifest to create or reuse the remote repository, push the project, and register its pipelines. It does not itself deploy the application: the main pipeline still needs to run, whether triggered by the push or started manually.

After a partial failure, correct the reported error and retry. [`ApplicationService.PublishAsync()`](https://github.com/saif-corp/forge/blob/main/src/dotnet/SAIF.Platform.CLI/Services/Application/ApplicationService.cs) reuses an existing repository rather than duplicating it; review the returned pipeline errors and warnings rather than treating repository creation alone as success.

Use [Aspire Publish](../build/deploy/aspire-publish.md) to generate pipeline YAML and infrastructure configuration from an AppHost. That generation step and `saif publish` registration are different operations. For registration failures, see [saif publish does not create pipelines](../troubleshoot/saif-publish-pipelines-not-created.md).

## Check and repair the developer environment

### `saif doctor`

Run diagnostics before repairing an installation. Doctor reports health and whether it can fix a problem automatically. Version-based checks show installed and latest versions separately; after a fix, the row reports the installed or updated version. A healthy row with no latest-version information is not evidence that an online update check succeeded.

### `saif doctor fix`

[Updating the SAIF CLI](../learn/install-saif-cli.md#updating-saif-cli) owns the update commands, opt-in CLI self-update, and Aspire compatibility fallback. With no group filters, doctor repairs tools, templates, and plugins but excludes the SAIF CLI binary; use `--self` deliberately for that binary. `--tools` covers dotnet global tools, npm global tools, and the Aspire CLI. Use the global `--dry-run` flag to inspect proposed changes.

If doctor reports that Aspire requires a package-manager update, follow the remedy in that warning rather than repeatedly retrying the same repair. Forge's [`AspireCliCheck`](https://github.com/saif-corp/forge/blob/main/src/dotnet/SAIF.Platform.CLI/Commands/Doctor/Checks/AspireCliCheck.cs) attempts the current self-update form first and retries without `--yes` only when the installed CLI rejects that argument, not for every update failure. See the [Aspire update reference](https://aspire.dev/reference/cli/commands/aspire-update/) for upstream installation-method behavior.

Plugin registration, refresh, and recovery have one home in [Forge Agent Plugins](../learn/forge-plugin.md#keeping-packages-up-to-date).

## Find a service and interpret the result

### `saif app search`

Use `app` for an individual Entra registration, especially when investigating a JWT audience or a vendor application that does not follow Forge's service naming convention, and `service` for the consolidated, repository-aware view; the [command model](dotnet/SAIF.Platform.CLI/index.md#command-model) explains the layering.

Choose one lookup mode: audience, application ID, or listing. The generated [app-search options](dotnet/SAIF.Platform.CLI/commands.md#saif-app-search) list the accepted aliases. A GUID audience can identify an application ID; `--appid` makes that intent explicit.

### `saif service search`

Service search matches application registration display names by case-insensitive substring, then groups recognized environment suffixes under one service key. For example, `payments-test` and `payments-prod` become `payments`.

```powershell
saif service search policyportal
saif service search payments --verbose
saif service search payments --format json
```

Interpret these results with the catalog's limits:

- Queries need at least two characters.
- Only registrations ending in `platformdev`, `test`, `qa`, `uat`, or `prod` qualify as service facets. Use `app search` for registrations without one of those suffixes.
- The Entra provider scans at most 2,000 registrations for a filtered query, then applies the name filter. A truncation notice means the result is incomplete; narrowing the term does not remove that scan cap. Do not treat an empty truncated result as proof that a service does not exist.
- Repository correlation probes exact names in order: the full service name, a name assembled from its first and last hyphen-separated tokens (normally `{domain}-{app name}`), then its last token (normally the app name). The first candidate with an exact match wins. A `web` or `func` component whose code lives in a sibling repository can therefore show no repository. Correlation is a naming lookup, not a scan of repository contents.

[`ServiceCatalog`](https://github.com/saif-corp/forge/blob/main/src/dotnet/SAIF.Platform.Sdk/Catalog/ServiceCatalog.cs), [`ServiceGrouper`](https://github.com/saif-corp/forge/blob/main/src/dotnet/SAIF.Platform.Sdk/Catalog/ServiceGrouper.cs), and the [Entra catalog provider](https://github.com/saif-corp/forge/blob/main/src/dotnet/SAIF.Platform.Sdk/Catalog/Providers/EntraServiceCatalogProvider.cs) own this behavior. The `search_services` MCP tool and the service portion of `saif search` use the same consolidation.

### `saif service describe`

Use the full service key from search when you want a single topology view:

```powershell
saif service describe payments-prod
saif service describe payments --environment prod
```

Describe strips a recognized environment suffix before requiring an exact, case-insensitive service-key match. `payments-prod` selects the `payments` service, not only its production environment; use `--environment prod` to narrow the view. It errors when the requested service or environment does not resolve.

Permissions describe what each registration **requests** through Graph `requiredResourceAccess`, not a proof of effective access. The command resolves scope and role IDs against target service principals on a best-effort basis. A failed environment lookup renders permissions as `unavailable` (`null` in JSON) without discarding the rest of the topology; unresolved targets or permission IDs retain their raw IDs. Use `--verbose` when you need pipeline identifiers and low-level permission identifiers: with `--format json`, it populates the `pipelines` array (otherwise `null`) alongside `pipelineCount` and sets `resourceAppId` and `id` on each permission entry. Without `--verbose`, JSON output still sets `id` for a permission whose value did not resolve.

For deployed resource naming and runtime lookups, use the [Service and Azure Resource Model](service-azure-resource-model.md). [`ServiceDescribeCommand`](https://github.com/saif-corp/forge/blob/main/src/dotnet/SAIF.Platform.CLI/Commands/Service/ServiceDescribeCommand.cs) owns exact matching and partial-result handling.

## Find and read documentation

### `saif docs list`

Use this as a sample of available pages, not a complete inventory: it returns at most 25 results. The [MkDocs fallback configuration](documentation-retrieval.md#mkdocs-fallback-configuration) owns how wildcard listings are built, capped, and interleaved across sites.

### `saif docs search`

Search first when you do not know the location, then pass the returned location unchanged to `docs get`. Non-default sites carry a site prefix; removing it can select the wrong page. Search defaults to 10 results, and the shared service clamps requested limits to 1–25. A search hit does not guarantee that the page is still available or matches the indexed revision; see [Documentation Retrieval](documentation-retrieval.md#location-resolution).

```powershell
saif docs search "documentation retrieval" --limit 5
saif docs get reference/documentation-retrieval --section "Search result locations"
```

### `saif docs get`

`--section` matches the exact heading text case-insensitively and returns that heading through the next heading of equal or higher level. Use the heading text, not its URL fragment. Human output prints Markdown directly, without Spectre markup parsing or width-wrapping.

For scripts, use `--format json` and handle `not_found` and `section_not_found` error records as well as successful content. The [retrieval contract](documentation-retrieval.md#limits) owns path normalization, cross-site ambiguity, and freshness limitations; [`DocsGetCommand`](https://github.com/saif-corp/forge/blob/main/src/dotnet/SAIF.Platform.CLI/Commands/Docs/DocsGetCommand.cs) owns section extraction and output behavior.

## Agent integration

### `saif agent init`

Use [Forge Agent Plugins](../learn/forge-plugin.md) for package selection, installation, scope, permission opt-in, verification, and recovery. Do not maintain a second host configuration from command-reference examples.

### `saif agent mcp`

The `forge` plugin launches this stdio MCP entrypoint. Use the [plugin setup guide](../learn/forge-plugin.md#verify-installation) to verify the tools, the [generated options](dotnet/SAIF.Platform.CLI/commands.md#saif-agent-mcp) for command syntax, and [Smithy](smithy.md) for Smithy's separate MCP contract.
