@hlix/cli 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ var s="@hlix/cli",e="0.8.0",n="SEE LICENSE IN LICENSE",a="Hlix Code and the hlix control-plane CLI.",r=["hlix","cli","agents"],l="https://hlix.ai",c="module",p="./dist/index.js",o={hlix:"./bin/hlix"},b=["bin","install.cjs","dist","skills","LICENSE","THIRD_PARTY_NOTICES"],u={node:">=22.19.0"},d={access:"public"},h={build:"rm -rf dist && bun build src/index.ts --outdir dist --target node --format esm --splitting --packages external --minify --banner '#!/usr/bin/env node'",test:'bun test src --path-ignore-patterns "**/*.integration.test.ts" --timeout 20000',typecheck:"tsc --noEmit",prepublishOnly:"bun run typecheck && bun run build && bun run test","test:integration":"NODE_ENV=test bun run ../../apps/backend/scripts/with-aimock.ts -- bun run ../../apps/backend/scripts/with-test-db.ts -- bun test --isolate --preload ../../apps/backend/test/helpers/pool-lifecycle.ts --timeout 60000 .integration.test.ts","build:binary":"bun run scripts/build-binary.ts","test:binary":"bun run build:binary && HLIX_TEST_BINARY=dist-native/hlix bun test src/__tests__/binary.test.ts --timeout 60000 && bun run scripts/smoke-binary.ts","test:integration:binary":"bun run build:binary && HLIX_TEST_BINARY=dist-native/hlix NODE_ENV=test bun run ../../apps/backend/scripts/with-aimock.ts -- bun run ../../apps/backend/scripts/with-test-db.ts -- bun test --isolate --preload ../../apps/backend/test/helpers/pool-lifecycle.ts --timeout 60000 src/__tests__/auth.integration.test.ts",postinstall:"node install.cjs"},m={"@agentclientprotocol/sdk":"0.21.1","@earendil-works/pi-tui":"0.85.1","@mastra/acp":"^0.4.1","@mastra/core":"1.72.0","@mastra/docker":"^0.9.0","@mastra/libsql":"1.23.0","@mastra/mcp":"2.1.0","@mastra/memory":"1.33.0",ignore:"7.0.8","jsonc-parser":"3.3.1","openapi-fetch":"^0.17.0","smol-toml":"1.8.0",yaml:"2.9.0",zod:"4.6.5"},x={"@hlix/sdk":"^0.3.0","@hlix/shared":"*","@types/bun":"latest",typescript:"^7.0.2"},y={"@hlix/cli-darwin-arm64":"0.8.0","@hlix/cli-darwin-x64":"0.8.0","@hlix/cli-linux-x64":"0.8.0","@hlix/cli-linux-arm64":"0.8.0"},k={name:s,version:e,license:n,description:a,keywords:r,homepage:l,type:c,main:p,bin:o,files:b,engines:u,publishConfig:d,scripts:h,dependencies:m,devDependencies:x,optionalDependencies:y};export{o as bin,k as default,m as dependencies,a as description,x as devDependencies,u as engines,b as files,l as homepage,r as keywords,n as license,p as main,s as name,y as optionalDependencies,d as publishConfig,h as scripts,c as type,e as version};
package/install.cjs ADDED
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ // @hlix/cli's postinstall: put the standalone hlix binary where `hlix` points.
3
+ //
4
+ // npm installs exactly one of the optional @hlix/cli-<platform> packages (their
5
+ // os/cpu/libc fields choose it). This links or copies its binary over
6
+ // bin/hlix, so the command runs the binary directly with no Node process in
7
+ // between. Without that package (--omit=optional, an unsupported platform) or
8
+ // without this script (--ignore-scripts, pnpm), bin/hlix stays the small Node
9
+ // wrapper, which finds the binary itself or runs the Node build.
10
+ //
11
+ // bin/hlix loads this file for `nativeBinary`, so the platform rules live once.
12
+ "use strict"
13
+ const { execFileSync } = require("node:child_process")
14
+ const { copyFileSync, existsSync, linkSync, renameSync, rmSync } = require("node:fs")
15
+ const { dirname, join } = require("node:path")
16
+
17
+ const PLATFORMS = ["darwin-arm64", "darwin-x64", "linux-x64", "linux-arm64"]
18
+
19
+ /** The platform package's binary, or the reason there is none. */
20
+ function nativeBinary() {
21
+ const platform = `${process.platform}-${process.arch}`
22
+ if (!PLATFORMS.includes(platform)) return { reason: `no native build for ${platform}` }
23
+ if (process.platform === "linux" && !process.report?.getReport().header.glibcVersionRuntime) {
24
+ return { reason: "no native build for musl-based Linux" }
25
+ }
26
+ const pkg = `@hlix/cli-${platform}`
27
+ try {
28
+ const binary = join(dirname(require.resolve(`${pkg}/package.json`)), "hlix")
29
+ if (existsSync(binary)) return { binary }
30
+ } catch {
31
+ // Not installed: reported below.
32
+ }
33
+ return { reason: `${pkg} is not installed (npm skips it with --omit=optional)` }
34
+ }
35
+
36
+ function main() {
37
+ const found = nativeBinary()
38
+ if (!found.binary) {
39
+ process.stderr.write(`@hlix/cli: ${found.reason}; hlix will run its Node build.\n`)
40
+ return
41
+ }
42
+ const target = join(__dirname, "bin", "hlix")
43
+ const staged = `${target}.${process.pid}`
44
+ try {
45
+ try {
46
+ linkSync(found.binary, staged)
47
+ } catch {
48
+ copyFileSync(found.binary, staged)
49
+ }
50
+ // A binary that cannot run here must never replace the working wrapper.
51
+ const { version } = require("./package.json")
52
+ const reported = execFileSync(staged, ["--version"], { stdio: ["ignore", "pipe", "ignore"], timeout: 30_000 })
53
+ if (reported.toString().trim() !== version) throw new Error(`the ${version} binary reported ${reported.toString().trim()}`)
54
+ renameSync(staged, target)
55
+ } catch (error) {
56
+ rmSync(staged, { force: true })
57
+ process.stderr.write(`@hlix/cli: kept the Node wrapper (${error instanceof Error ? error.message : error})\n`)
58
+ }
59
+ }
60
+
61
+ module.exports = { nativeBinary }
62
+ if (require.main === module) main()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hlix/cli",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "license": "SEE LICENSE IN LICENSE",
5
5
  "description": "Hlix Code and the hlix control-plane CLI.",
6
6
  "keywords": [
@@ -12,9 +12,11 @@
12
12
  "type": "module",
13
13
  "main": "./dist/index.js",
14
14
  "bin": {
15
- "hlix": "./dist/index.js"
15
+ "hlix": "./bin/hlix"
16
16
  },
17
17
  "files": [
18
+ "bin",
19
+ "install.cjs",
18
20
  "dist",
19
21
  "skills",
20
22
  "LICENSE",
@@ -33,28 +35,36 @@
33
35
  "prepublishOnly": "bun run typecheck && bun run build && bun run test",
34
36
  "test:integration": "NODE_ENV=test bun run ../../apps/backend/scripts/with-aimock.ts -- bun run ../../apps/backend/scripts/with-test-db.ts -- bun test --isolate --preload ../../apps/backend/test/helpers/pool-lifecycle.ts --timeout 60000 .integration.test.ts",
35
37
  "build:binary": "bun run scripts/build-binary.ts",
36
- "test:binary": "bun run build:binary && HLIX_TEST_BINARY=dist-native/hlix bun test src/binary.test.ts --timeout 60000 && bun run scripts/smoke-binary.ts",
37
- "test:integration:binary": "bun run build:binary && HLIX_TEST_BINARY=dist-native/hlix NODE_ENV=test bun run ../../apps/backend/scripts/with-aimock.ts -- bun run ../../apps/backend/scripts/with-test-db.ts -- bun test --isolate --preload ../../apps/backend/test/helpers/pool-lifecycle.ts --timeout 60000 src/sync.integration.test.ts src/auth.integration.test.ts"
38
+ "test:binary": "bun run build:binary && HLIX_TEST_BINARY=dist-native/hlix bun test src/__tests__/binary.test.ts --timeout 60000 && bun run scripts/smoke-binary.ts",
39
+ "test:integration:binary": "bun run build:binary && HLIX_TEST_BINARY=dist-native/hlix NODE_ENV=test bun run ../../apps/backend/scripts/with-aimock.ts -- bun run ../../apps/backend/scripts/with-test-db.ts -- bun test --isolate --preload ../../apps/backend/test/helpers/pool-lifecycle.ts --timeout 60000 src/__tests__/auth.integration.test.ts",
40
+ "postinstall": "node install.cjs"
38
41
  },
39
42
  "dependencies": {
43
+ "@agentclientprotocol/sdk": "0.21.1",
40
44
  "@earendil-works/pi-tui": "0.85.1",
41
45
  "@mastra/acp": "^0.4.1",
42
- "@mastra/core": "1.71.0",
46
+ "@mastra/core": "1.72.0",
43
47
  "@mastra/docker": "^0.9.0",
44
48
  "@mastra/libsql": "1.23.0",
45
49
  "@mastra/mcp": "2.1.0",
46
- "@mastra/memory": "1.30.0",
50
+ "@mastra/memory": "1.33.0",
47
51
  "ignore": "7.0.8",
48
52
  "jsonc-parser": "3.3.1",
49
53
  "openapi-fetch": "^0.17.0",
50
54
  "smol-toml": "1.8.0",
51
55
  "yaml": "2.9.0",
52
- "zod": "4.5.4"
56
+ "zod": "4.6.5"
53
57
  },
54
58
  "devDependencies": {
55
59
  "@hlix/sdk": "^0.3.0",
56
60
  "@hlix/shared": "*",
57
61
  "@types/bun": "latest",
58
62
  "typescript": "^7.0.2"
63
+ },
64
+ "optionalDependencies": {
65
+ "@hlix/cli-darwin-arm64": "0.8.0",
66
+ "@hlix/cli-darwin-x64": "0.8.0",
67
+ "@hlix/cli-linux-x64": "0.8.0",
68
+ "@hlix/cli-linux-arm64": "0.8.0"
59
69
  }
60
70
  }
@@ -182,7 +182,8 @@ config key, which only picks the coder Hlix Code spawns on this machine.
182
182
  ## Commands
183
183
 
184
184
  ```bash
185
- hlix status [--json] # workspace, binding, local and cloud drift
185
+ hlix status [--json] # workspace, binding, local and cloud drift, and how this hlix was installed
186
+ hlix uninstall [--yes] [--json] # remove an hlix the one-line installer put here; keeps your login
186
187
  hlix standards [--json] # instruction files, skills, commands, MCP servers and subagents other agent tools left in this folder
187
188
  hlix rules [--edit] [--global] [--json] # the rules files the coding agent reads; --edit opens one in $EDITOR
188
189
  hlix projects list [--json] # projects in the workspace
@@ -190,11 +191,11 @@ hlix projects get [project-id] [--json]
190
191
  hlix projects delete <id|name> [--yes] [--json]
191
192
  hlix init [folder] [--project <id>] [--json] # create committed project configuration; --project binds an existing cloud project
192
193
  hlix terminal [project-id] [--print] [--web] [--json] # shell into the project's Coding Workspace (ssh here, or the browser terminal)
193
- hlix import [folder] [--dry-run] [--env-file <path>] [--history preserve|sanitize] [--include <path>] [--no-connect-origin] [--attach] [--yes] [--json]
194
+ hlix import [folder] [--dry-run] [--env-file <path>] [--history preserve|sanitize] [--include <path>] [--yes] [--json]
194
195
  hlix sync [--dry-run] [--json] # push or pull when only one side changed
195
- hlix pull [--force] [--yes] [--dry-run] [--json]
196
- hlix push [--dry-run] [--json] # compare-and-swap a new immutable revision
197
- hlix mirror [--once] [--json] # keep this folder a live mirror of the cloud files
196
+ hlix pull [--dry-run] [--json]
197
+ hlix push [--dry-run] [--json] # push with an expected Git head
198
+ hlix mirror [--json] # keep this folder a live mirror of the cloud files
198
199
  hlix skill import <SKILL.md|folder> [--name <slug>] [--json]
199
200
  hlix agent import <markdown-file> [--name <name>] [--runtime <runtime>] [--duty <duty>] [--json]
200
201
  hlix mcp import <.mcp.json> [--server <name>] [--allow-stdio] [--json]
@@ -219,96 +220,15 @@ rejected before `status` runs at all.
219
220
  `tasks list` and `projects get` default to the project this folder is bound to,
220
221
  so both are usable with no arguments inside an imported project.
221
222
 
222
- Run `hlix import --dry-run` first. The report names secret **keys**, quarantined
223
- agent/skill/MCP resources, and Git-history findings, but never secret values.
224
- Hlix collects every project-local dotenv and credential file. It applies the
225
- standard development dotenv precedence automatically and preserves every other
226
- profile encrypted; `--env-file` is an explicit override.
227
- Git history is preserved by default. Possible historical secrets are warnings,
228
- with at most five redacted commit-and-file findings. To leave old commits out,
229
- use `--history sanitize` to upload a current-tree snapshot without changing local Git history. `--history preserve`
230
- and the legacy `--history abort-on-findings` remain accepted; neither blocks on
231
- historical findings. Nested repositories and declared submodules import their
232
- working files without their own Git metadata or history. Ignored nested
233
- repositories are skipped silently.
234
-
235
- Ordinary files over the upload limit are skipped with a named hint. If needed, history is omitted, then the largest folders are skipped until the snapshot fits. Import, init, and push refresh a marked Hlix-managed block in `.hlixignore` from shared stack detection and verified local generated paths. Personal lines outside the block are preserved byte-for-byte; `!` overrides outside the block take precedence even above it. Automatic rules never cover tracked files or secrets. Declared tools identify untracked build output without generation comments: Vite literal build.outDir or dist, Next .next (plus out for a default static export), Nuxt .nuxt/.output, Astro dist, Turbo .turbo, TypeScript literal tsconfig outDir, Webpack/Parcel dist defaults, and Rollup configured output.dir. Python build/dist/egg-info needs a declared build backend. Resolve output paths per package and configured root, applying recognized build-command output flags first. Unresolved root/config/CLI overrides disable inference for that tool rather than reverting to a default; configuration code is never executed. Track authored files in declared output directories or use ! overrides to keep them visible. Composer vendor requires a valid lockfile; local path dependencies stay visible. Untracked agent scratch and external nested repos are eligible only when they are not project source. Unknown paths stay visible. A refresh removes broad rules for folders that acquired tracked files or secrets; source outside declared build outputs stays visible. Human output adds one line: `Hlix ignored 6 generated folders — see .hlixignore`. Dry runs do not refresh files, and linked ignore targets or malformed blocks are left untouched. Folders with a managed block use these rules instead of blanket generated-directory exclusions. `--include <path>` restores one ignored path and persists it for later push/sync scans, subject to upload limits; it never includes Git metadata or bypasses secret protection. Push leaves the cloud unchanged when skipping would remove an existing cloud file. Symlinked `.hlixignore` files are read from their targets; an unreadable target leaves uploads unchanged with a permission warning.
236
-
237
- Never delete .git folders or files to get past this; use .hlixignore instead.
238
-
239
- The human scan report is ordered: uploaded / skipped / secrets / warnings /
240
- blocked. The secrets line says files move encrypted. Protected working-tree files are named individually; their values are
241
- never printed. The JSON dry run retains the full environment and file report.
242
- The upload manifest summarizes skip lists longer than 5,000 entries; local
243
- reports and push protection retain every skipped path.
244
-
245
- Current environment values are sent only in the authenticated finalization
246
- request and stored encrypted; they are never placed in the Git bundle.
247
-
248
- When the scan finds cloud setup commands, protected files, or quarantined
249
- resources, interactive imports ask for one explicit trust decision before any
250
- project is created or uploaded. Non-interactive and `--json` callers must first
251
- review `--dry-run`, then pass `--yes`; it never bypasses a scan blocker. A
252
- declined prompt exits as `cancelled`, while a caller that cannot prompt exits
253
- as `approval_required`.
254
-
255
- When the folder's Git `origin` remote (or its sole remote) is a GitHub
256
- repository, `import` checks it against the workspace: unowned and reachable
257
- through a GitHub App installation here, it is connected to the new project
258
- automatically, no question asked. Already attached to another project in this
259
- workspace, an interactive import asks whether to bind this folder there
260
- instead of creating a duplicate; `--yes`, `--json`, and any non-interactive
261
- run never attach on their own — they import repo-less and print a line
262
- naming the owning project, and `--attach` is the explicit, promptless opt-in
263
- that does the binding in any mode. No match, an unparseable remote, or a
264
- non-GitHub host changes nothing and is never a blocker. `--no-connect-origin`
265
- skips this whole check.
266
-
267
- Agent, skill, and MCP files found during a project scan stay quarantined until
268
- one of the explicit import commands above is run. Skill content and agents are
269
- scoped to the bound project. MCP imports send environment key names only;
270
- values remain in encrypted project secrets and the generated cloud config uses
271
- `${KEY}` placeholders, including remote authentication headers such as
272
- `Authorization: Bearer ${TOKEN}`. Literal-only headers are refused. Remote MCP URLs must use public HTTPS. Stdio servers
273
- require `--allow-stdio` and run only through a validated direct executable,
274
- never a shell.
275
-
276
- `sync` never guesses through a two-sided conflict. If only local changed it
277
- pushes; if only cloud changed it pulls; if both changed it exits with
278
- `conflict`.
279
-
280
- A folder bound with `hlix init --project <id>` has no local base revision. If
281
- the project already has cloud files, `hlix pull` brings them down and becomes
282
- that base. If it has none — a project created in the dashboard — `hlix push`
283
- (or `hlix sync`) uploads the folder as revision 1 and builds the project's
284
- Coding Workspace from it. Only a project with no revision at all can be
285
- bootstrapped that way: if the cloud already has a revision but this folder has no base, push leaves both copies unchanged and reports that it needs a pull first. `hlix
286
- pull` on a project with no cloud files exits `not_found` and names the way
287
- forward.
288
-
289
- `push`, `pull` and `sync` accept `--dry-run`: it prints the divergence — each
290
- side's generation, and how many files are local-only, cloud-only or differing —
291
- takes no action, and reports the verdict the real command would reach. A dry run
292
- that would end in `conflict` exits 1, as `import --dry-run` does for a blocker.
293
-
294
- `pull --force` is the destructive override, and it now asks before replacing
295
- local files: `--force` states "replace my changes", `--yes` states "do not ask".
296
- A `--json` or non-interactive caller cannot answer, so it exits
297
- `approval_required` pointing at `--dry-run`; a declined prompt exits `cancelled`.
298
- `--force` without local changes replaces nothing and does not prompt.
299
-
300
- `mirror` makes the folder follow the project's cloud Coding Workspace: it
301
- catches up once, then pulls whenever the cloud's files move (a task merged,
302
- another machine pushed, a cloud Hlix Code session edited) and pushes whenever
303
- a file here is saved. Every change still lands as a revision. When both sides
304
- changed the same file, the cloud's version wins and the local one is kept
305
- beside it as `<file>.local-<timestamp>`, named in a `mirror:` line. `.env` and
306
- the other protected files never move: unlike `pull`, the mirror never writes
307
- them here, and unlike `push`, a mirror push keeps the cloud's exactly as they
308
- were — change secrets with `hlix secrets`, or an explicit `hlix push`. `--once` does the catch-up pass and exits;
309
- `--json` prints one envelope per line (JSONL): `{ caughtUp, generation }` once the
310
- catch-up is done, then one per pass that moved something. Interactive
311
- `hlix` in a bound folder runs the same mirror while its cloud session is open.
223
+ Run `hlix import --dry-run` first. The report includes file count, byte count and the number of protected files, without secret values. Review the upload, then use `--yes` to approve it. New projects receive a repository in **HLIX Repos**. Local Git history and origin stay intact.
224
+
225
+ `--history preserve` retains local history after a complete secret scan. If the scan finds potential secrets or cannot complete, review the findings or use `--history sanitize` to upload current files. Source files travel through Git. Protected files use the encrypted secrets channel and never enter the source upload.
226
+
227
+ `push`, `pull` and `sync` compare an immutable commit SHA and local file identities. They refuse to overwrite simultaneous local and remote changes. `--dry-run` reports the intended action. `mirror` checks for changes every 15 seconds and stops when cancelled; conflicts leave both copies intact and require manual resolution. Neither sync nor browsing committed files requires a Coding Workspace to wake up.
228
+
229
+ Import keeps the project binding after an interrupted push. Use `hlix push` to resume against that same repository. A folder bound with `init` can bootstrap an empty repository, or pull an existing repository into an empty local folder. A nonempty local folder with no known base cannot overwrite existing remote source.
230
+
231
+ Import skills, agents and MCP configuration explicitly with the resource commands listed above. They do not execute as a side effect of a source upload.
312
232
 
313
233
  ## Machine output
314
234
 
@@ -318,7 +238,7 @@ catch-up is done, then one per pass that moved something. Interactive
318
238
  {
319
239
  "schemaVersion": 1,
320
240
  "command": "projects list",
321
- "data": [ /* documented command result */ ]
241
+ "data": [/* documented command result */]
322
242
  }
323
243
  ```
324
244
 
@@ -353,12 +273,12 @@ stream cannot be a single JSON document. Read them line by line.
353
273
 
354
274
  ## Exit codes
355
275
 
356
- | Code | Meaning |
357
- | --- | --- |
358
- | 0 | Success |
359
- | 1 | The command ran and failed |
360
- | 2 | Usage error — unknown command, bad flags, missing argument |
361
- | 3 | Authentication or permission failure |
276
+ | Code | Meaning |
277
+ | ---- | ---------------------------------------------------------- |
278
+ | 0 | Success |
279
+ | 1 | The command ran and failed |
280
+ | 2 | Usage error — unknown command, bad flags, missing argument |
281
+ | 3 | Authentication or permission failure |
362
282
 
363
283
  ## Hlix Code, non-interactively
364
284
 
@@ -396,28 +316,12 @@ of yours.
396
316
 
397
317
  - **No execution commands** beyond `cycles create`. Starting a single task is
398
318
  not offered because the API does not implement it: `POST
399
- /v1/api/tasks/{id}/execute` publishes no success response and answers `501`
319
+ /v1/api/tasks/{id}/execute` publishes no success response and answers `501`
400
320
  by design. Work is dispatched by its cycle.
401
321
  - **No `tasks logs`.** The log endpoint is not part of the published API
402
322
  contract, and there is no log stream to follow. `tasks watch` streams task
403
- *status*, which is a different thing and is named accordingly.
323
+ _status_, which is a different thing and is named accordingly.
404
324
 
405
325
  ### Import recovery and project deletion
406
326
 
407
- An interrupted import resumes the saved session in `.hlix/import.json` on the
408
- next `hlix import` in that folder. Keep that file and `.hlix/import.bundle`
409
- until it succeeds. A finalize timeout polls the same import for up to ten
410
- minutes; rerun the command if the wait ends. Secret values are re-read from
411
- local files only if finalization has not started. Restore changed files before
412
- resuming an upload; completed imports bind without rescanning. If the server
413
- confirms an unfinished session has expired, the CLI discards its saved recovery
414
- files, reports it, and starts a fresh import. A completed or still-finalizing
415
- session is never replaced just because its upload deadline passed.
416
-
417
- `hlix projects delete <id|name>` prints the selected project's name
418
- before deleting it. Names must match exactly and be unique; otherwise use the
419
- ID. Interactive deletion asks for confirmation. Non-interactive deletion,
420
- `--json`, and deletion of this folder's bound project require `--yes`.
421
- Deletion removes the cloud project; local files are kept.
422
-
423
- `push --dry-run` reports conflicts with exit 0 and makes no changes. The nonzero conflict verdict described above applies to `pull` and `sync`. Missing local push bindings and unreadable import recovery records likewise produce explicit no-op reports; no replacement project or upload is created.
327
+ An interrupted import keeps its project binding. Retry with `hlix push`; source is stored in HLIX Repos rather than a resumable bundle session. Deleting a project removes its managed repository and encrypted project files.