# hlix import

**`hlix import` creates a cloud project from a local folder** and records its first immutable revision. It is the one command that uploads your source, its Git history, and its protected configuration, so it is also the one command with a trust gate in front of it.

For what moves in which lane and how to resolve each blocker, read [Import a project](/cli/import/). This page is the flag surface.

## Prerequisites

- A credential that names a workspace (see [`hlix auth login`](/reference/cli/auth/))
- Git installed, even when the source folder is not already a repository
- No existing `.hlix/config.json` binding the folder to a different API or workspace

## Usage

```bash
hlix import [folder] \
  [--dry-run] \
  [--env-file <path>] \
  [--history <abort-on-findings|preserve>] \
  [--name <name>] \
  [--stack <stack>] \
  [--yes] \
  [--json]
```

### Arguments

- **`folder`** (`string`, optional, default `.`): the directory to import, resolved against `--cwd`. At most one; a second exits `invalid_usage`.

### Flags

- **`--dry-run`** (`boolean`, default `false`): scan and report; upload nothing, create nothing, and write no `.hlix/config.json`. The report names the stack, file and byte counts, environment evidence, cloud setup commands, required and collected secret **key names**, protected and quarantined paths, environment warnings, and blockers. Exits `1` when the scan found a blocker — the report is still the deliverable.
- **`--env-file <path>`** (`string`, default: automatic dotenv precedence): choose which dotenv profile is the active cloud environment when the scan finds several. Every other profile is still preserved encrypted. Precedence when this is omitted is `.env`, `.env.development`, `.env.local`, `.env.development.local`, later overriding earlier.
- **`--history <mode>`** (`abort-on-findings` | `preserve`, default `abort-on-findings`): the default aborts on possible historical secrets. `preserve` keeps the full Git history **after showing the risk** — it means "upload the history despite findings", not "remove the findings". Any other value exits with ``--history must be `abort-on-findings` or `preserve`.``
- **`--name <name>`** (`string`, default the folder's base name): the cloud project name.
- **`--stack <stack>`** (`string`, default the detected stack): override stack detection.
- **`--yes`** (`boolean`, default `false`): record an explicit approval for the reviewed upload and cloud setup in non-interactive use. It does **not** override history, path, integrity, or size blockers.
- **`--json`** (`boolean`, default `false`): the dry-run report, or on a real import `{ projectId, revisionId, generation, commitSha, files, bytes }`. Never contains secret values.

Expected result:

```text
Imported acme-invoices
Project ID: 6f1c2a9e-8f0b-4a7d-9d33-2f0b1c7e5a41
Revision: 1
```

## The trust gate

When the scan finds cloud setup commands, protected files, quarantined resources, or collected secret keys, the CLI shows them — commands verbatim, paths, and secret **key names** only — and asks for one explicit trust decision before any project is created or uploaded.

| Caller | Behaviour |
| --- | --- |
| Interactive terminal, no `--yes` | prompts once; declining exits `cancelled` |
| Interactive terminal, `--yes` | proceeds without prompting |
| `--json` or non-interactive, no `--yes` | exits `approval_required` pointing at `--dry-run` |
| Any caller, blocker present | exits `scan_blocked` regardless of `--yes` |
**A dry run is evidence, not an approval token:** A real import performs its **own** fresh authoritative scan and binds that exact result to its prompt, manifest, and upload. It does not reuse the dry run and does not rescan after approval. So `--yes` approves what the new scan found, and it cannot bypass missing secrets, unsafe paths, history findings, integrity failures, or size limits.

### Conflicts and validation

- The target is resolved **at the folder being imported**. A contradicting override exits `workspace_mismatch` before a single byte is uploaded.
- Both a workspace and a credential are required, or the command exits `workspace_required` — this is where a dry run learns it could not have finished.
- A folder already carrying a `projectId` exits `already_imported`: ``This folder is already bound to an Hlix project. Use `hlix push`.``
- Outside `--dry-run`, `.hlix/config.json` is written **before** the authoritative scan, so `.hlixignore` is reviewed, hashed, and uploaded with the project. A blocked or cancelled import therefore leaves the folder initialized but unbound.
- Repeating the same import uses an idempotency key derived from the manifest and bundle hashes, so an interrupted retry does not create a duplicate project.
- If the source changes between scan and snapshot, import stops rather than uploading a mixed state.

## If import fails

- `workspace_required` — ``No workspace selected. Run `hlix auth login --workspace <id>` first.``
- `already_imported` — the folder is bound. Use [`hlix push`](/reference/cli/sync/).
- `scan_blocked` — the message lists every blocker. [If the scan blocks the import](/cli/import/#if-the-scan-blocks-the-import) maps each to its fix.
- `approval_required` — ``Import needs approval for protected files or cloud setup. Review with `hlix import --dry-run`, then repeat with `--yes`.``
- `cancelled` — the prompt was declined. Nothing was uploaded.
- ``--history must be `abort-on-findings` or `preserve`.`` — the flag takes those two values only.
- `Import completed without project revision metadata.` — the upload finished but the API returned no revision. Retry; the idempotency key makes that safe.

## Next steps

[Import a project](/cli/import/)
  [hlix push, pull & sync](/reference/cli/sync/)
  [Import trust model](/security/trust-model/)
  [Environment discovery](/cli/environment-discovery/)