---
name: stratonext
description: >
  Work with the StratoNext platform (projects, tasks, environments, assets,
  agents, approvals, dossiers) as a logged-in user. Use whenever the user asks
  to look up, create, or update a StratoNext resource, upload or download an
  asset, or check what work is assigned to them.
---

# StratoNext platform CLI

## Working model: everything is a task

**All work on StratoNext must be tracked as a task.** Never start working
directly — a task is the unit of record for every piece of work. A task is a
**long-lived session**: it can be stopped, started, and resumed, and it lives
across many interactions. Do **not** create a new task for each new input.

**Resuming an INPUT_REQUIRED task:** when a task is paused in `INPUT_REQUIRED`
and the user supplies the requested input, **resume that same task** — never
spin up a new one. Feed the input in (`task update` for details if needed) and
`stratonext task start <task_id>` to move it back to WORKING. One task = one
unit of work, however many rounds of input it takes.

Follow this lifecycle end to end:

1. **Claim or create the task.**
   - Working an *existing* task? Fetch it first (`stratonext task get <task_id>`).
     If it isn't assigned to you, claim it in the next step.
   - *No task yet?* Create one in the relevant project, assigned to the logged-in
     user with the `--mine` shorthand:
     ```bash
     stratonext task create --project-id <id> --name NAME --description DESC --mine
     ```
     `--mine` sets `--assignee-type USER` and fills in your user id automatically.
     If the target project is ambiguous, ask the user which project first.
2. **Move it to WORKING** (and claim it in the same call if it isn't yours yet):
   ```bash
   stratonext task start <task_id>          # mark in-progress
   stratonext task start <task_id> --mine   # claim (assign to you) + mark in-progress
   ```
   This signals that the task is being actively worked. Do this *before* you begin.
3. **Do the work**, following the task's `instructions` if it has any, and keeping
   the task as the unit of record. Honour any approval gates (see the Approvals
   section below).
4. **Report the outcome — always with a result, never silently:**
   ```bash
   stratonext task complete <task_id> --result "what was done / where the output is"
   stratonext task fail <task_id> --reason "what failed and why"
   ```
   `complete` / `fail` are no-ops if the task is already in a terminal status.

### Scheduled jobs run as agents, not users

A scheduled job (`stratonext job create`) runs unattended on a cron `--schedule`,
so it **cannot** be assigned to a human — it requires an **agent** assignee:

```bash
stratonext job create --project-id <id> --name NAME --agent-id <agent_id> \
  --schedule "<cron>" --description DESC [--instructions TEXT]
```

`--agent-id` is required. Each run is dispatched to that agent, the tasks it
spawns are agent-assigned, and the agent reports every outcome the same way
(`task complete` / `task fail`). If the user hasn't said which agent to assign,
ask — you cannot point a scheduled job at a user.

## Authentication

The CLI authenticates as a **human user** via OAuth2 Authorization Code + PKCE:
`stratonext login` opens a browser tab and listens on `http://localhost:19191`
for the redirect (Cognito does not support the Device Authorization Grant).
Tokens are cached in `~/.stratonext/credentials`. You act across every workspace
you belong to; pick one per call with `--workspace-id` (authorization is enforced
per workspace). See ADR-035.

> Headless / CI use (a non-interactive machine identity for the CLI) is not
> supported yet — it is deferred to a later phase. Deployed agents authenticate
> with their own runtime M2M credential, which is unrelated to the CLI.

No environment variables are required — the CLI targets **prod** by default.
Override these only to log in against a non-prod environment:

```
STRATONEXT_AUTH_AUTHORITY     # Cognito OIDC issuer URL (endpoints discovered from it)
STRATONEXT_AUTH_CLIENT_ID     # public CLI client — different from the agent client
STRATONEXT_AUTH_AUDIENCE      # platform resource-server id; scopes are prefixed with it
STRATONEXT_WORKSPACE_ID       # optional; can be overridden with --workspace-id
STRATONEXT_API_URL            # GraphQL endpoint; defaults to prod
```

To avoid exporting these every session, persist them once — they're saved to
`~/.stratonext/settings` and used whenever the matching env var isn't set (a set
env var always wins):

```bash
stratonext config set STRATONEXT_API_URL https://api.gamma.stratonext.com/v1/graphql
stratonext config list          # effective value + source (env / settings / default) per key
stratonext config unset STRATONEXT_API_URL
```

Log in once before running other commands:

```bash
stratonext login             # opens browser, caches token with full read+write scopes
stratonext login --readonly  # caches token with read-only scope (organization:read only)
stratonext logout            # clears the cached credentials
```

## Installing

```bash
# prod
uv tool install stratonext
```

## Global flags

```bash
stratonext [--workspace-id <id>] <command>
```

| Flag | Effect |
|------|--------|
| `--workspace-id <id>` | Override `STRATONEXT_WORKSPACE_ID` for a single call |

## Commands

> **`list` returns a summary, not the full object.** Every `list` command returns
> only a subset of each record's fields (enough to identify and scan). To get an
> object's complete metadata — every field, nested links, notes, approvals, graph,
> etc. — fetch it individually with the matching `get <id>` command.

### Identity

```bash
stratonext whoami
```

Output includes `authMode` (always `"user"`), active `scopes`, `apiUrl`, and
`workspaceId`.

```bash
stratonext login [--readonly]   # user mode: authenticate via browser
stratonext logout               # user mode: clear cached credentials
```

### Projects

```bash
stratonext project list
stratonext project get <project_id>
stratonext project create --name NAME --description DESC [--type SOFTWARE_APPLICATION|OTHER]
stratonext project update <project_id> [--name NAME] [--description DESC] [--status ACTIVE|ARCHIVED] [--instructions TEXT] [--type SOFTWARE_APPLICATION|OTHER]
stratonext project delete <project_id>
stratonext project add-link <project_id> --url URL --name NAME [--description DESC] [--type GENERIC|SOURCE_CODE|DOCUMENTATION|TOOL]
stratonext project add-note <project_id> "note text"
```

`add-link` and `add-note` fetch the existing list first and append — they never overwrite previous entries.

To attach a file to a project use `asset upload --parent-type PROJECT --parent-id <project_id>`.

### Tasks

```bash
stratonext task list [--mine] [--assignee <id>] [--ready] [--after <cursor>]
stratonext task get <task_id> [--no-approvals]
stratonext task create --project-id <id> --name NAME --description DESC \
  [--instructions TEXT] [--mine] [--assignee-id <id>] [--assignee-type AGENT|USER] \
  [--verification-criteria TEXT] [--verification-status PENDING|PASSED|FAILED] [--verification-notes TEXT]
stratonext task update <task_id> [--name NAME] [--description DESC] [--instructions TEXT] \
  [--assignee-id <id>] [--assignee-type AGENT|USER] \
  [--verification-criteria TEXT] [--verification-status PENDING|PASSED|FAILED] [--verification-notes TEXT]
stratonext task start <task_id> [--mine]
stratonext task complete <task_id> [--result TEXT]
stratonext task fail <task_id> [--reason TEXT]
stratonext task cancel <task_id>
stratonext task dispatch <task_id>
stratonext task add-link <task_id> --url URL --name NAME [--description DESC] [--type GENERIC|SOURCE_CODE|DOCUMENTATION|TOOL]
stratonext task remove-link <task_id> --url URL
```

`task add-link` fetches the task's current links and appends — it never overwrites
existing ones. `task remove-link` drops the link with the matching `--url`. Both are a
single whole-array update to the task's links (the platform replaces the array).

`task start` sets the task to **WORKING**; add `--mine` to also assign it to the
logged-in user in the same call (claim + start). Call it before you begin work.

`task list` returns 50 tasks per page, latest first. Pagination:
```bash
stratonext task list                  # first page
stratonext task list --after <cursor> # next page — cursor from previous call's nextCursor
```

Filters (server-side, combinable):
- `--mine` — tasks assigned to the logged-in user (platform ID cached at login time).
- `--assignee <id>` — tasks assigned to a specific agent or user ID
- `--ready` — only tasks in `CREATED`, `WAITING`, or `INPUT_REQUIRED` status

Common pattern — your ready tasks:
```bash
stratonext task list --mine --ready
```

**Assignee types:**
- `--assignee-type AGENT` (default when `--assignee-id` is given): task is assigned to an agent. Dispatch it to actually run (via A2A) with `stratonext task dispatch <task_id>` — valid only for AGENT-assigned tasks in a dispatchable status.
- `--assignee-type USER`: task is assigned to a human; dispatch is not allowed. The user completes it via the UI or `task complete` in user mode.
- Omitting `--assignee-id` creates an unassigned task.

`task create --mine` assigns the new task to the logged-in user in one step
(equivalent to `--assignee-type USER --assignee-id <you>`); it cannot be combined
with `--assignee-id`.

`task get` also returns the task's pending approvals under a `pendingApprovals`
key, so you see what is gating the task in a single call. The approvals are
filtered server-side by `taskId` + `status`, so the result is exact regardless of
workspace size. Pass `--no-approvals` to skip that extra lookup.

`task complete` / `task fail` are how the task's assignee reports its own outcome.
Callable by the logged-in user for user-assigned tasks. Both are no-ops if the
task is already in a terminal status. `task cancel` sets the task to
**CANCELLED** and stops any in-flight work.

### Environments

```bash
stratonext environment list
stratonext environment get <environment_id>
stratonext environment create --name <name> --provider AWS \
  [--classification PRODUCTION|TESTING|GENERIC] \
  [--automation-policy FULLY_GATED|APPROVAL_FOR_WRITES_AND_PRODUCTION|FULLY_AUTONOMOUS] \
  [--metadata KEY=VALUE ...]
stratonext environment update <environment_id> [--name ...] [--classification ...] [--automation-policy ...] [--metadata KEY=VALUE ...]
stratonext environment delete <environment_id>
```

`--provider` is one of AWS, AZURE, GOOGLE_CLOUD, VERCEL, CLOUDFLARE, OTHER.
`--metadata` is repeatable (e.g. `--metadata AccountId=123 --metadata DefaultRegion=us-east-1`);
on `update` it replaces the whole metadata array, so omit it to leave metadata untouched.

### Deployments

```bash
stratonext deployment list --project-id <id>
stratonext deployment refresh <deployment_id>
```

`deployment list` returns a project's deployments (the same records nested under
`project get`, with `config`, `environmentId`, and the discovery `graph`).
`deployment refresh` re-runs resource discovery on a deployment's graph — after
it returns, `project get` shows that deployment's `graph.isDiscovering: true`.

### Agents

```bash
stratonext agent list [--after <cursor>]
stratonext agent get <agent_id>
stratonext agent create --name NAME [--project-id <id>]
stratonext agent rename <agent_id> --name NAME
stratonext agent delete <agent_id>
```

`agent create` registers an agent in the workspace: pass `--project-id` for a
**Worker** (scoped to that project), omit it for a **Lead-Agent**. `agent rename`
changes the display name.

### Assets

```bash
stratonext asset upload <local_path> --parent-id <id> --parent-type PROJECT|TASK|DOSSIER [--name NAME] [--description DESC]
stratonext asset download <asset_id> <local_dest_path>
stratonext asset delete <asset_id>
```

### Approvals

```bash
stratonext approval list [--after <cursor>]
stratonext approval get <approval_id>
stratonext approval submit \
  --task-id <task_id> \
  --agent-id <agent_id> \
  --environment-id <environment_id> \
  --request-type CHANGE_REQUEST|CREDENTIAL_REQUEST \
  --name "Short human-readable name" \
  --action-description "Detailed description including resource IDs and mutation intent" \
  --payload-ref <opaque-ref>
```

`list` returns 50 approvals per page; paginate with `--after <cursor>`.
Risk level is evaluated automatically from `--action-description`.
`approve` and `reject` are CLI commands but **must only be called by humans**, never by agents.

### Dossiers

```bash
stratonext dossier list [--after <cursor>]
stratonext dossier get <dossier_id>
stratonext dossier create --title TITLE [--type INCIDENT|INVESTIGATION|GENERIC] [--body TEXT] [--owner-id <user_id>]
stratonext dossier update <dossier_id> [--title TITLE] [--body TEXT] [--owner-id <user_id>] [--status OPEN|SEALED]
stratonext dossier delete <dossier_id>
stratonext dossier seal <dossier_id>
stratonext dossier reopen <dossier_id>
stratonext dossier link-project <dossier_id> <project_id>
stratonext dossier unlink-project <dossier_id> <project_id>
```

To attach a file to a dossier use `asset upload --parent-type DOSSIER --parent-id <dossier_id>`.

---

## Approvals — permission gates for sensitive actions

Some actions require human approval before an agent may proceed. Approvals are
**human-only decisions**: an agent must never approve or reject an approval
request on behalf of another agent or itself.

### When to request an approval

Raise an approval request whenever:

- A task description or instructions indicate that an action requires approval
  before it can be executed.
- Any AWS, platform, or external API call returns an **access denied /
  permission error** (e.g. `AccessDeniedException`, `UnauthorizedException`,
  HTTP 403).
- A required permission or IAM right is missing — do not attempt workarounds
  or alternative paths; stop and request approval instead.

### Behavior rules

1. **Stop immediately.** Do not attempt the action, retry with different credentials, or find a side-channel that bypasses the missing permission.
2. **Submit an approval request** using `stratonext approval submit` with a precise `--action-description` (include resource ARNs, mutation intent, and scope).
3. **Fail the task** with a clear reason:
   ```bash
   stratonext task fail <task_id> --reason "Access denied on <action>: <error message>. Approval submitted (<approval_id>), awaiting human decision."
   ```
4. **Wait.** A human will approve or reject in the StratoNext UI. Do not poll or retry autonomously.

### What never to do

- Do not approve or reject approvals yourself — only humans can do this.
- Do not escalate privileges, switch roles, or use alternative credentials to work around a denied permission.
- Do not proceed with a partial version of the action that avoids the blocked permission.

---

## Onboarding a new cloud account (environment setup)

Use this when the user wants to connect a **new cloud account** (e.g. a fresh
AWS account) to StratoNext as an environment.

1. **Create the environment** in the workspace:
   ```bash
   stratonext environment create --name NAME --provider AWS \
     [--classification PRODUCTION|TESTING|GENERIC] \
     --metadata AccountId=<account_id> --metadata DefaultRegion=<region>
   ```
2. **Download the provider onboarding template** (CloudFormation for AWS):
   ```bash
   stratonext environment setup-template --provider AWS --output stratonext-setup.yaml
   ```
3. **Deploy it in the target account** (credentials for *that* account, e.g. an
   admin profile):
   ```bash
   aws cloudformation deploy \
     --template-file stratonext-setup.yaml \
     --stack-name stratonext-setup \
     --capabilities CAPABILITY_NAMED_IAM \
     --parameter-overrides EnableAgentAccess=true EnableAgentDeployment=true \
       ExternalId=<random-secret>
   ```
   - `EnableAgentAccess=true` creates the operations roles (agents read/operate
     on the account's resources); `EnableAgentDeployment=true` creates the
     deployment + runtime roles (StratoNext runs agents inside the account).
     Leave either `false` to opt out; the connection role is always created.
   - Role names default to the standard StratoNext names
     (`StratoNextConnectionRole`, `StratoNextOperationsRole`,
     `StratoNextOperationsMaxRole`, `StratoNextRuntimeRole`,
     `StratoNextDeploymentRole`). If the account has naming conventions,
     override any of them with the matching parameter, e.g.
     `ConnectionRoleName=acme-stratonext-connection`.
   - All created roles are tagged `stratonext=true` for easy identification.
4. **Read the stack outputs** — they are the role ARNs StratoNext needs:
   ```bash
   aws cloudformation describe-stacks --stack-name stratonext-setup \
     --query "Stacks[0].Outputs" --output table
   ```
5. **Record the ARNs on the environment** so the platform (and agents) can use
   them — each output's description names its StratoNext field:
   ```bash
   stratonext environment update <environment_id> \
     --metadata AccountId=<account_id> --metadata DefaultRegion=<region> \
     --metadata ConnectionRoleArn=<ConnectionRoleArn> \
     --metadata AgentOperationsRoleArn=<OperationsRoleArn> \
     --metadata AgentOperationsExternalId=<random-secret>
   ```
   (`--metadata` replaces the whole array — include every key, not just new ones.)

## Onboarding a new project (guided setup)

Use this when the user wants to stand up a **new project**. Assume an environment
already exists (`stratonext environment list` to confirm). The goal is a project
with enough context that agents and humans can work in it. **Guide the user — ask
for each piece; never invent details.** For anything they can't hand over directly,
ask *where to find it* and capture a link instead.

1. **Name + description.** Ask what the project is and does, then create it:
   ```bash
   stratonext project create --name NAME --description DESC [--type SOFTWARE_APPLICATION|OTHER]
   ```
2. **Standing instructions.** Ask for conventions/guardrails agents must always
   follow in this project:
   ```bash
   stratonext project update <project_id> --instructions "TEXT"
   ```
3. **Links.** Ask for the source repo, docs, dashboards, and tools. Add each
   (typed so they're easy to find later):
   ```bash
   stratonext project add-link <project_id> --url URL --name NAME \
     --type SOURCE_CODE|DOCUMENTATION|TOOL|GENERIC
   ```
4. **Documents.** Ask for specs, diagrams, runbooks. Upload each as a project asset:
   ```bash
   stratonext asset upload <path> --parent-type PROJECT --parent-id <project_id> [--name NAME]
   ```
   If the user can't share the file, ask where it lives (a repo path, a drive URL)
   and add a **link** (step 3) instead of leaving it out.
5. **Notes.** Capture anything else worth recording:
   ```bash
   stratonext project add-note <project_id> "TEXT"
   ```
6. **Confirm.** Read the assembled project back to the user:
   ```bash
   stratonext project get <project_id>
   ```

Work through the list one item at a time. For each: the user either provides it or
points you to the source. If neither, skip it and note the gap in step 5 — don't
guess. Once the project has a description, its key links, and any documents, it's
ready for the task workflow above.

## Working a task with deployments

When a task includes `deploymentIds`, follow this sequence before doing any work:

**1. Gather the full context**

```bash
stratonext task get <task_id>          # includes deploymentIds, projectId
stratonext project get <project_id>    # includes deployments with graph, config.region, environmentId
stratonext environment get <environment_id>   # for each deployment's environmentId
```

**2. Understand the deployment graph**

Each deployment has a `graph` with `nodes` and `relationships` — the live
resource map for that deployment. `config.region` is the AWS region;
`config.stackNames` are the CloudFormation stacks.

**3. Get credentials to operate — local first, never assume the operations role by default**

The interactive CLI runs as **you**, a human, under **your own local AWS credentials**. The
environment's `AgentOperationsRoleArn` is provisioned for **deployed agents**, not for you: its
trust policy by default trusts **account roots** (the StratoNext platform account, and the
environment's own account), not individual users — so an interactive user generally **cannot**
assume it, and must **not** try to by default.

The environment's `metadata` array carries these for reference — they describe the *agent's*
access and the region, not your path in:

| Key | Meaning |
|-----|---------|
| `AgentOperationsRoleArn` | IAM role the **deployed agent** assumes inside the account. Not the interactive CLI's path. |
| `AgentOperationsExternalId` | STS external ID for that role (confused-deputy guard). |
| `DefaultRegion` | Default AWS region for this environment. |

Work the environment in this order — **always try local first, then fall back**:

1. **StratoNext gives you context, not credentials.** `project get` / `environment get` /
   `deployment refresh` give you the deployment graph, `config.region`, and stack names — the map
   of what to operate on. They do not hand you AWS credentials.
2. **Try your own local AWS credentials (the default path).** Confirm who you are and that you can
   reach the account/region first:
   ```bash
   aws sts get-caller-identity
   ```
   If that succeeds and you have the access you need, do the work under your local identity in
   `config.region` (or `DefaultRegion`).
3. **No usable credentials for this environment? Don't force it.** Do **not** assume the operations
   role, switch identities, or reach for a side channel. Instead **ask the user** whether to hand
   the work to StratoNext. Only on their confirmation, create an **agent-assigned** task in this
   project/environment — the deployed agent runs *inside* the account and can assume the operations
   role — then read back its result:
   ```bash
   # only after the user confirms:
   stratonext task create --project-id <id> --name NAME --description DESC \
     --assignee-id <agent_id> --assignee-type AGENT
   stratonext task dispatch <task_id>     # hand it to the agent to run
   stratonext task get <task_id>          # check its status / result once the agent has worked it
   ```
4. **Assume the operations role only if it is explicitly configured for your principal** — a trust
   policy that names *you*, not just an account root — **and the user confirms**. It is not the
   default path. When you do, include `AgentOperationsExternalId` if present:
   ```bash
   aws sts assume-role --role-arn "<AgentOperationsRoleArn>" \
     --role-session-name "stratonext-cli-$(date +%s)" \
     $([ -n "<AgentOperationsExternalId>" ] && echo "--external-id <AgentOperationsExternalId>")
   ```
