@hagicode/cli 0.1.0-dev.3.1.074a0f9 → 0.1.0-dev.32.1.c8f71ee

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.
package/README.md CHANGED
@@ -1,203 +1,599 @@
1
1
  # HagiCode CLI
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/%40hagicode%2Fcli?logo=npm)](https://www.npmjs.com/package/@hagicode/cli)
3
+ [![npm version](https://img.shields.io/npm/v/%40hagicode%2Fcli?logo=npm&color=cb3837)](https://www.npmjs.com/package/@hagicode/cli)
4
+ [![npm downloads](https://img.shields.io/npm/dm/%40hagicode%2Fcli?logo=npm&color=2d8cf0)](https://www.npmjs.com/package/@hagicode/cli)
5
+ [![license](https://img.shields.io/npm/l/%40hagicode%2Fcli?color=ffd43b)](https://www.npmjs.com/package/@hagicode/cli)
6
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D24.0.0-339933?logo=node.js&logoColor=white)](https://nodejs.org/)
4
7
 
5
- `repos/cli` contains the npm-based `hagi` command for proposal session management.
8
+ `@hagicode/cli` provides the published `hagi` command, which lets you drive a HagiCode backend from a terminal, scripts, or an AI agent without opening the app. Manage projects, proposals (generate, execute, archive, and send), vaults and their files, chat sessions, HagiTasks, scheduled plans, and skills (local list, enable, disable and uninstall, gallery search and install, and trusted providers). Every command supports a shared `--json` output for scripting, and an embedded Hagi skill under [`skills/`](skills/) guides agent workflows.
6
9
 
7
10
  ## Requirements
8
11
 
9
- - Node.js 20+
10
- - npm compatible with `10.9.2`
12
+ - Node.js 24+
11
13
  - Access to a HagiCode backend API
12
14
 
13
15
  ## Installation
14
16
 
17
+ Use the published npm package for normal CLI usage. You do not need to clone this repository to run `hagi`.
18
+
19
+ ### Zero-install usage
20
+
15
21
  ```bash
16
- cd repos/cli
17
- npm install
18
- npm run build
22
+ npx @hagicode/cli --help
23
+ npx @hagicode/cli project list --json
19
24
  ```
20
25
 
21
- `npm run build` now performs a Vite 8 production bundle for Node 20+ and emits the publish contract at `dist/cli.js`, `dist/main.js`, plus any runtime-required shared chunks under `dist/chunks/`.
22
- Release builds are minified, tree-shaken, and exclude `.map` files.
26
+ ### Project-local install
27
+
28
+ ```bash
29
+ npm install @hagicode/cli
30
+ npx @hagicode/cli proposal list --json
31
+ ```
23
32
 
24
- Run the built CLI with:
33
+ ### Optional global install
25
34
 
26
35
  ```bash
27
- npx hagi --help
28
- node ./dist/cli.js --help
36
+ npm install -g @hagicode/cli
37
+ hagi --help
29
38
  ```
30
39
 
31
- For local development without building:
40
+ ## Quick Start
41
+
42
+ The examples below use `hagi ...` after the package is available on your `PATH`. For one-off execution, replace `hagi` with `npx @hagicode/cli`.
32
43
 
33
44
  ```bash
34
- cd repos/cli
35
- npm run dev -- proposal list --json
45
+ hagi --help
46
+ hagi project list --json
47
+ hagi proposal list --json
48
+ hagi vault list --json
49
+ hagi chat list --json
50
+ hagi hagitask list
51
+ hagi hagitask create --title "Auto commit" --project-id <project-id> --prompt-id auto-compose-commit.en-US
36
52
  ```
37
53
 
38
- ## Release automation
54
+ ## Embedded Skills
55
+
56
+ If you are integrating Hagi CLI into an agent workflow, start from the embedded skill package in this repository:
57
+
58
+ - [`skills/README.md`](skills/README.md) - repository-level discovery entrypoint
59
+ - [`skills/hagi/SKILL.md`](skills/hagi/SKILL.md) - canonical Hagi skill contract
60
+ - [`skills/hagi/references/`](skills/hagi/references/) - focused package usage, session data, proposal flow, maintainer, and validation guidance
61
+
62
+ The same folder is also shipped as the HagiCode server's built-in skill `hagi`, so an agent running inside a HagiCode session can answer questions about the user's own projects, proposals, sessions, vaults and plans without setup. Edit `skills/hagi/` here; the built-in copy is recopied from it.
63
+
64
+ ## Shared Runtime Flags
65
+
66
+ Every command family supports the same global runtime flags:
67
+
68
+ - `--base-url <url>`
69
+ - `--token <token>`
70
+ - `--lang <lang>`
71
+ - `--json`
72
+
73
+ Example:
39
74
 
40
- `repos/cli/.github/workflows/release-drafter.yml` maintains the next GitHub draft release for `@hagicode/cli`.
41
- `repos/cli/.github/workflows/npm-publish.yml` now publishes two channels with GitHub Actions OIDC plus provenance:
75
+ ```bash
76
+ hagi --base-url https://api.example.com --token "$HAGI_TOKEN" project list --json
77
+ ```
42
78
 
43
- - pushes to `main` publish a unique development prerelease under the npm `dev` dist-tag
44
- - only published GitHub Releases for strict `vX.Y.Z` tags publish the stable package under the npm `latest` dist-tag
79
+ ## Connection Setup
45
80
 
46
- ### Draft release flow
81
+ Every operation command resolves the backend base URL on each invocation, in this order:
47
82
 
48
- 1. Label each PR with one semantic version label: `major`, `minor`, or `patch`.
49
- 2. Add at least one release note category label:
50
- - `breaking-change` for breaking release note callouts
51
- - `feature` or `enhancement` for the `Features` section
52
- - `bug` for the `Fixes` section
53
- - `docs` or `documentation` for the `Documentation` section
54
- - `chore` or `dependencies` for the `Maintenance` section
55
- 3. Add `skip-changelog` only when the PR should stay out of the draft notes entirely, such as pure CI noise or internal housekeeping.
56
- 4. Merge the labeled PR into `main`, or update the PR while it is open.
57
- 5. Let Release Drafter refresh the GitHub draft release name, tag, and categorized notes.
58
- 6. Review the draft release in GitHub before creating the next stable tag.
83
+ 1. `--base-url <url>` flag
84
+ 2. `HAGI_API_BASE_URL` environment variable
85
+ 3. `baseUrl` in the local config file: `$XDG_CONFIG_HOME/hagi/config.json`, or `~/.config/hagi/config.json` when `XDG_CONFIG_HOME` is unset
59
86
 
60
- Release Drafter manages draft release metadata only. Draft review and publish remain native GitHub Release operations.
87
+ ```json
88
+ {
89
+ "baseUrl": "https://hagicode.example.com"
90
+ }
91
+ ```
61
92
 
62
- ### Current label coverage
93
+ If none of the three is set, the command exits with code `2` and sends no backend request.
63
94
 
64
- At implementation time, the public `HagiCode-org/cli` label set already included `bug`, `documentation`, and `enhancement`.
65
- The following planned release labels were still missing and should be created before maintainers rely on the full contract:
95
+ Operation commands send requests without a credential by default. Pass `--token` (or set `HAGI_API_TOKEN`) only when your backend requires one.
66
96
 
67
- - `major`
68
- - `minor`
69
- - `patch`
70
- - `breaking-change`
71
- - `feature`
72
- - `docs`
73
- - `chore`
74
- - `dependencies`
75
- - `skip-changelog`
97
+ When the backend cannot be reached, the CLI names the base URL and exits with code `60`. A backend failure is reported differently: it prints the HTTP-equivalent status of the gRPC status and the backend error message, and exits with a status-mapped code.
76
98
 
77
- ### Trusted Publisher prerequisites
99
+ ```text
100
+ $ hagi --base-url http://127.0.0.1:1 project list
101
+ Cannot reach HagiCode backend at http://127.0.0.1:1. Check that the backend is running and the base URL is correct.
78
102
 
79
- Before the first automated release, an npm package owner must bind the package to the repository workflow:
103
+ $ echo $?
104
+ 60
105
+ ```
80
106
 
81
- 1. Open the npm package settings for `@hagicode/cli`.
82
- 2. Add a Trusted Publisher for the GitHub repository `HagiCode-org/cli`.
83
- 3. Set the workflow file to `.github/workflows/npm-publish.yml`.
84
- 4. Keep the package access level public so `npm publish --access public` can succeed.
107
+ ## Command Overview
108
+
109
+ ```text
110
+ hagi
111
+ |-- project list | repositories | create | update | delete
112
+ | `-- monospecs show | init | set | validate | reload | clone | remove-checkout
113
+ |-- proposal list | create | optimize-description | generate | annotate | execute | archive | status | complete | send
114
+ |-- vault list | create | update | delete
115
+ | `-- files list | preview
116
+ |-- chat list | create | archive | delete | send
117
+ |-- hagitask list | schema | create | send
118
+ |-- plan list | get | create | update | enable | disable | delete | run
119
+ `-- skills local list | enable | disable | uninstall
120
+ gallery search | detail | install
121
+ trusted list | upsert | delete | reset
122
+ ```
85
123
 
86
- The workflow requests `contents: read` and `id-token: write`. It does not use a long-lived `NPM_TOKEN`.
124
+ ## Example Setup
87
125
 
88
- ### Local release verification
126
+ The examples in the family sections run in order against a local backend. They assume:
89
127
 
90
- Run the same stable release gates locally before publishing a GitHub Release:
128
+ - the backend listens on `http://127.0.0.1:35167`
129
+ - `hagi` is on `PATH`
130
+ - `json_field` reads one field from `--json` output
91
131
 
92
132
  ```bash
93
- cd repos/cli
94
- version="$(npm run --silent publish:verify-release -- v0.1.0)"
95
- npm version --no-git-tag-version "$version"
96
- npm run build
97
- npm run typecheck
98
- npm test
99
- npm run pack:check
133
+ export HAGI_API_BASE_URL=http://127.0.0.1:35167
134
+ json_field() { node -pe "JSON.parse(require('node:fs').readFileSync(0, 'utf8'))$1"; }
135
+
136
+ mkdir -p /tmp/cli-examples/repos/cli
137
+ PROJECT_ID=$(hagi project create --name "CLI Examples" --path /tmp/cli-examples --json | json_field '.project.id')
100
138
  ```
101
139
 
102
- `publish:verify-release` validates the strict stable tag format and resolves the stable package version that CI will stamp into `package.json` immediately before build and publish.
103
- `pack:check` runs `npm pack --dry-run --json` and rejects missing bundled entrypoints, `.map` files, and legacy source-mirrored output such as `dist/runtime/**` or `dist/generated/api/models/**`.
104
- `publish:resolve-dev-version` derives the CI-only prerelease version used for `main` branch publishes, for example `0.1.0-dev.<run>.<attempt>.<sha>`.
140
+ ## Project Commands
105
141
 
106
- ### Stable release flow
142
+ List projects through the aggregation endpoint:
107
143
 
108
- 1. Make sure the draft release maintained by Release Drafter already reflects the intended tag such as `v0.1.0`.
109
- 2. Run the local verification commands above.
110
- 3. Commit and push the release-ready source tree.
111
- 4. Publish the matching GitHub draft release for the stable tag.
112
- 5. Confirm the GitHub Actions summary shows build, typecheck, test, package inspection, version stamping, and `npm publish --tag latest --provenance --access public`.
144
+ ```bash
145
+ hagi project list
146
+ hagi project list --json
147
+ ```
113
148
 
114
- ## Environment Variables
149
+ List the repositories of one project. The read-only command prints each repository's name, path relative to the project root, display name, type and branch. These paths are the values a proposal's repository scope names:
115
150
 
116
- The CLI resolves runtime settings centrally before every command. Explicit flags always override environment variables.
151
+ ```bash
152
+ hagi project repositories --id "$PROJECT_ID"
153
+ hagi project repositories --id "$PROJECT_ID" --json
154
+ ```
117
155
 
118
- | Variable | Purpose |
119
- | --- | --- |
120
- | `HAGI_API_BASE_URL` | Backend base URL. Defaults to `http://127.0.0.1:35168` when unset. |
121
- | `HAGI_API_TOKEN` | Bearer token attached as `Authorization: Bearer <token>`. |
122
- | `HAGI_API_LANG` | Shared `Accept-Language` header value. |
156
+ Create a project. The backend creates the directory if it does not exist. The new id is read from `project.id`:
123
157
 
124
- Global flags:
158
+ ```bash
159
+ DEMO_PROJECT_ID=$(hagi project create \
160
+ --name "CLI Demo" \
161
+ --path /tmp/cli-demo \
162
+ --description "Automation sandbox" \
163
+ --json | json_field '.project.id')
164
+ ```
125
165
 
126
- - `--base-url <url>`
127
- - `--token <token>`
128
- - `--lang <lang>`
129
- - `--json`
166
+ Update only part of a project record. The CLI resolves current project data first so omitted fields are preserved:
130
167
 
131
- ## Generate the API Client
168
+ ```bash
169
+ hagi project update --id "$DEMO_PROJECT_ID" --description "Updated description"
170
+ hagi project update --id "$DEMO_PROJECT_ID" --name "CLI Demo renamed"
171
+ ```
132
172
 
133
- The CLI owns its own OpenAPI configuration in `openapi-ts.config.json`.
173
+ Delete the demo project:
134
174
 
135
175
  ```bash
136
- cd repos/cli
137
- npm run generate:api
138
- npm run generate:api:once
176
+ hagi project delete --id "$DEMO_PROJECT_ID"
139
177
  ```
140
178
 
141
- Default Swagger source:
179
+ ## MonoSpecs Commands
180
+
181
+ `project monospecs` manages a project's MonoSpecs configuration (`monospecs.yaml`) and the local checkouts of its repositories. The CLI only sends requests: the backend reads and writes the files and runs Git on its own host, so `--base-url` may point at a remote backend.
142
182
 
143
- - `http://127.0.0.1:35168/swagger/v1/swagger.json`
183
+ | Command | Purpose |
184
+ | --- | --- |
185
+ | `show --id <id>` | Print the version, repo directory, config path, commit-when-archive flag and one row per repository with its local Git state. `--yaml` adds the YAML preview. |
186
+ | `init --id <id>` | Initialize the configuration. An initialized project returns its existing document. |
187
+ | `set --id <id>` | Change metadata or repositories. Options: `--version`, `--repo-directory`, `--commit-when-archive <true\|false>`, repeatable `--add-repository <path>` and `--remove-repository <path>`, and `--clone`. `--url`, `--name`, `--display-name` and `--type` apply to a single `--add-repository`. |
188
+ | `validate --id <id>` | Validate the configuration. Exits `30` and still prints every error when it is invalid. |
189
+ | `reload --id <id>` | Reread `monospecs.yaml` on the backend host and refresh the Git state. |
190
+ | `clone --id <id>` | Clone configured repositories that are missing. Repeatable `--path <path>` selects repositories; `--include-optional` adds optional ones when no `--path` is given. |
191
+ | `remove-checkout --id <id> --path <path>` | Delete the local checkout of one configured repository. `--force` allows a checkout with uncommitted changes, unpushed commits or no upstream branch. |
192
+
193
+ ```bash
194
+ hagi project monospecs show --id "$PROJECT_ID"
195
+ hagi project monospecs set --id "$PROJECT_ID" --commit-when-archive true
196
+ hagi project monospecs set --id "$PROJECT_ID" --add-repository repos/api --url https://github.com/example/api.git --clone
197
+ hagi project monospecs set --id "$PROJECT_ID" --remove-repository repos/api
198
+ hagi project monospecs validate --id "$PROJECT_ID" --json
199
+ hagi project monospecs clone --id "$PROJECT_ID" --path repos/web
200
+ hagi project monospecs remove-checkout --id "$PROJECT_ID" --path repos/web
201
+ ```
202
+
203
+ Safety rules:
144
204
 
145
- When the backend cannot export Swagger in the current environment, keep `src/generated/api/**` aligned with the same controller contract used by `repos/web`, then regenerate when Swagger becomes available again.
205
+ - `set` reads the current document, applies only the options given and saves the complete document. Local Git state (branch, sync state and similar) is never written back; `code` and `metadata` are kept.
206
+ - `set` rejects a duplicate `--add-repository`, a `--remove-repository` that is not configured, and companion options with more than one addition, before anything is saved. With `--clone`, the configuration is saved first; a failed clone leaves it saved and exits `30`.
207
+ - `clone` and `remove-checkout` act only on repositories listed in the configuration. The backend confines paths to the project root and clones only over `https`, `http`, `ssh` and `git`, so a `file://` URL or a plain path is reported as `failed`.
208
+ - `remove-checkout` refuses a checkout with uncommitted changes, unpushed commits or no upstream unless `--force` is given, and it never removes a path that is not a standalone repository root. It deletes a directory on the backend host and leaves `monospecs.yaml` unchanged.
146
209
 
147
- `generate:api:once` matches the frontend workflow: it reuses an existing Swagger host when one is already up, otherwise it starts `repos/hagicode-core/src/PCode.SwaggerHost/PCode.SwaggerHost.csproj`, waits for Swagger readiness, runs the normal generator once, then stops the temporary host.
210
+ Exit codes for these commands: `2` usage error (no request was sent), `20` backend validation error (every field error is listed), `24` project not found, `29` another clone or removal is running or a checkout is blocked, `30` invalid configuration or a failed clone.
148
211
 
149
212
  ## Proposal Commands
150
213
 
151
- `proposal list` queries proposal sessions through `GET /api/Sessions` with `type=Proposal`.
214
+ List proposal sessions for the example project:
152
215
 
153
216
  ```bash
154
- hagi proposal list --project-id <project-id>
217
+ hagi proposal list --project-id "$PROJECT_ID"
155
218
  hagi proposal list --include-archived --json
156
219
  ```
157
220
 
158
- `proposal create` only creates the proposal session. It does not trigger any later lifecycle API.
221
+ Every proposal names its writable repositories and its read-only repositories. Take the paths from `hagi project repositories --id "$PROJECT_ID" --json`, and begin the chief complaint with a `Repository scope` section: a `Writable repositories:` list and a `Read-only repositories:` list, one `- <path>` entry per line, with `- none` for an empty list. The scope is a text convention in the chief complaint; `proposal create` has no scope flags. The format and a worked example are in [`skills/hagi/references/proposal-flow.md`](skills/hagi/references/proposal-flow.md#repository-scope).
222
+
223
+ Create a proposal session only. The new id is read from `session.sessionId`:
159
224
 
160
225
  ```bash
161
- hagi proposal create \
162
- --project-id <project-id> \
163
- --chief-complaint "Add npm commands for proposal APIs" \
164
- --default-title
226
+ PROPOSAL_ID=$(hagi proposal create \
227
+ --project-id "$PROJECT_ID" \
228
+ --chief-complaint "Repository scope
229
+ Writable repositories:
230
+ - repos/cli
231
+ Read-only repositories:
232
+ - none
233
+
234
+ Add CLI completion for session workflows" \
235
+ --default-title \
236
+ --json | json_field '.session.sessionId')
165
237
  ```
166
238
 
167
- `proposal generate-name` only generates the proposal name for an existing session.
239
+ Update status and send a follow-up message:
168
240
 
169
241
  ```bash
170
- hagi proposal generate-name --session-id <session-id>
242
+ hagi proposal status --session-id "$PROPOSAL_ID" --status Reviewing
243
+ hagi proposal send --session-id "$PROPOSAL_ID" --content "Please continue"
171
244
  ```
172
245
 
173
- `proposal generate` only starts proposal generation.
246
+ The next steps are AI lifecycle actions and need a backend with an AI executor. `optimize-description` writes the optimized title, description, and proposal name to the session in one backend step, and `generate` needs that name. Without the AI executor, or before the name exists, the backend answers with a 400 that names the missing state:
174
247
 
175
248
  ```bash
176
- hagi proposal generate --session-id <session-id> --enable-explore-mode
177
- hagi proposal generate --session-id <session-id> --hero-id <hero-id>
249
+ hagi proposal optimize-description --session-id "$PROPOSAL_ID" --description "Add CLI completion for session workflows" --default-title
250
+ hagi proposal generate --session-id "$PROPOSAL_ID" --proposal-content-language en-US --enable-explore-mode
251
+
252
+ cat > annotations.json <<'EOF'
253
+ {"files":[{"fileName":"proposal.md","filePath":"proposal.md"}]}
254
+ EOF
255
+ hagi proposal annotate --session-id "$PROPOSAL_ID" --input annotations.json
256
+ cat annotations.json | hagi proposal annotate --session-id "$PROPOSAL_ID" --input - --json
257
+
258
+ hagi proposal execute --session-id "$PROPOSAL_ID"
259
+ hagi proposal complete --session-id "$PROPOSAL_ID"
260
+ hagi proposal archive --session-id "$PROPOSAL_ID"
178
261
  ```
179
262
 
180
- `proposal execute` only starts proposal execution.
263
+ ### Explicit Lifecycle Sequence
264
+
265
+ The CLI keeps proposal lifecycle actions explicit and non-interactive. Each command maps to one backend action:
266
+
267
+ 1. `hagi proposal list`
268
+ 2. `hagi proposal create`
269
+ 3. `hagi proposal optimize-description --session-id <session-id> --description <text>` (sets the title, description, and proposal name)
270
+ 4. `hagi proposal generate --session-id <session-id> --proposal-content-language <language>` (optionally `--enable-explore-mode`)
271
+ 5. `hagi proposal annotate --session-id <session-id> --input <file>` (optional review loop)
272
+ 6. `hagi proposal execute --session-id <session-id>`
273
+ 7. `hagi proposal complete --session-id <session-id>`
274
+ 8. `hagi proposal archive --session-id <session-id>`
275
+
276
+ The CLI may suggest a next command, but it never chains later lifecycle APIs automatically.
277
+
278
+ Each command maps to one backend action. The CLI may suggest a next command, but it never chains later lifecycle APIs automatically.
279
+
280
+ ## Vault Commands
281
+
282
+ Create a vault with a local folder to browse. The new id is read from `vault.id`:
181
283
 
182
284
  ```bash
183
- hagi proposal execute --session-id <session-id>
285
+ mkdir -p /tmp/cli-vault-notes/docs
286
+ printf '# Notes\n' > /tmp/cli-vault-notes/docs/readme.md
287
+
288
+ VAULT_ID=$(hagi vault create \
289
+ --name "Main Notes" \
290
+ --type folder \
291
+ --physical-path /tmp/cli-vault-notes \
292
+ --json | json_field '.vault.id')
184
293
  ```
185
294
 
186
- ## Explicit Lifecycle Sequence
295
+ List registered vaults:
187
296
 
188
- The CLI keeps proposal lifecycle actions explicit and non-interactive:
297
+ ```bash
298
+ hagi vault list
299
+ hagi vault list --json
300
+ ```
189
301
 
190
- 1. `hagi proposal list`
191
- 2. `hagi proposal create`
192
- 3. `hagi proposal generate-name`
193
- 4. `hagi proposal generate`
194
- 5. `hagi proposal execute`
302
+ Update only part of a vault record. The CLI resolves the current vault snapshot first so omitted fields are preserved:
303
+
304
+ ```bash
305
+ hagi vault update --id "$VAULT_ID" --name "Renamed Vault"
306
+ ```
307
+
308
+ Browse vault files and preview supported content:
309
+
310
+ ```bash
311
+ hagi vault files list --id "$VAULT_ID"
312
+ hagi vault files list --id "$VAULT_ID" --path docs --json
313
+ hagi vault files preview --id "$VAULT_ID" --path docs/readme.md
314
+ ```
315
+
316
+ Delete a vault and request backend cleanup of its local files:
317
+
318
+ ```bash
319
+ hagi vault delete --id "$VAULT_ID" --delete-local-files --json
320
+ ```
321
+
322
+ ## Chat Commands
323
+
324
+ Create a chat session. The new id is read from `session.sessionId`:
325
+
326
+ ```bash
327
+ CHAT_ID=$(hagi chat create \
328
+ --title "Repository Q&A" \
329
+ --project-id "$PROJECT_ID" \
330
+ --json | json_field '.session.sessionId')
331
+ ```
332
+
333
+ List chat sessions:
334
+
335
+ ```bash
336
+ hagi chat list --active-only
337
+ hagi chat list --include-archived --json
338
+ ```
339
+
340
+ Send a message, archive, and delete:
341
+
342
+ ```bash
343
+ hagi chat send --session-id "$CHAT_ID" --content "Summarize the current diff"
344
+ hagi chat archive --session-id "$CHAT_ID"
345
+ hagi chat delete --session-id "$CHAT_ID"
346
+ ```
347
+
348
+ ## HagiTask Commands
349
+
350
+ A HagiTask is a preset task with a structured input. Read its schema first, write the input from the schema, then create the session. `hagitask list` shows the active tasks:
351
+
352
+ ```bash
353
+ hagi hagitask list
354
+ hagi hagitask list --json
355
+ ```
356
+
357
+ `hagitask schema` shows one task's target scope, commands, parameters, requirements and scenes. Each required parameter is marked `yes`. The `--json` output adds the input JSON Schema and an example request body:
358
+
359
+ ```bash
360
+ hagi hagitask schema --task-id compose-commit
361
+ hagi hagitask schema --task-id compose-commit --json
362
+ ```
363
+
364
+ Write the input file. Its keys are the parameter keys the schema lists under `inputs`, plus the `targets` and `targetScopes` keys when the schema's scope lists targets. For example, `input.json` for a task with one required input:
365
+
366
+ ```json
367
+ {
368
+ "inputs": {
369
+ "commitScope": "cli"
370
+ }
371
+ }
372
+ ```
373
+
374
+ Create the session from that task and input. The new id is read from `session.sessionId`:
375
+
376
+ ```bash
377
+ HAGITASK_ID=$(hagi hagitask create \
378
+ --title "Auto compose commit" \
379
+ --project-id "$PROJECT_ID" \
380
+ --task-id compose-commit \
381
+ --input input.json \
382
+ --repository-path repos/cli \
383
+ --need-push \
384
+ --target-branch-mode new-custom \
385
+ --target-branch-name feature/cli-completion \
386
+ --json | json_field '.session.sessionId')
387
+ ```
388
+
389
+ Use `--prompt-id` instead of `--task-id` for a prompt template with no structured input. `--input` needs `--task-id`. Input is read from a file, or from stdin with `--input -`. The CLI only checks that the input is a JSON object. The backend checks the keys and values. It rejects a key that the schema does not list, and a missing required value. Its error is printed unchanged, and no session is reported as created. `--hero-selection-mode` takes `first` (the default) or `random`.
390
+
391
+ The backend must have a hero available for the task's script. Without one, `hagitask create` fails with a 400 whose error is `no-executable-hero`.
392
+
393
+ Send a follow-up message to the HagiTask session:
394
+
395
+ ```bash
396
+ hagi hagitask send --session-id "$HAGITASK_ID" --content "Retry with a smaller diff"
397
+ ```
398
+
399
+ **Migration from AutoTask:** `hagi hagitask create` and `hagi hagitask send` replace `hagi autotask create` and `hagi autotask send`. There is no `autotask` alias, so an `autotask` command now fails as an unknown command. Read the task schema with `hagitask schema` to find the input keys that replace the old flags.
400
+
401
+ ## Plan Commands
402
+
403
+ A scheduled plan saves a proposal or HagiTask input with a schedule and an agent pool. The backend runs it on that schedule. `plan list` and `plan get` show the plans that exist:
404
+
405
+ ```bash
406
+ hagi plan list
407
+ hagi plan list --json
408
+ hagi plan get --id "$PLAN_ID"
409
+ hagi plan get --id "$PLAN_ID" --json | jq .plan
410
+ ```
411
+
412
+ `plan list` prints one row per plan with its schedule summary, and an explicit message when there are none. `--json` prints `{ "plans": [...] }`, where each entry carries `id`, `type`, `name`, `enabled`, `state`, `nextRunAt`, `schedule`, `agentPool` and `payload` exactly as the backend returns them. `plan get --json` prints `{ "plan": {...} }` with the same fields.
413
+
414
+ ### Create a plan
415
+
416
+ `plan create` reads a plan document with `--input <file>` or `--input -` for stdin. A recurring HagiTask plan:
417
+
418
+ ```json
419
+ {
420
+ "type": "hagitask",
421
+ "name": "Nightly review",
422
+ "enabled": true,
423
+ "schedule": {
424
+ "mode": "recurring",
425
+ "timeZone": "Asia/Shanghai",
426
+ "startDate": "2026-05-01",
427
+ "endDate": "2026-05-31",
428
+ "timeOfDay": "02:00",
429
+ "repeat": { "rule": "daily", "weekdays": [] }
430
+ },
431
+ "agentPool": { "strategy": "random", "agents": [ { "heroId": "5de02573-0000-4000-8000-000000000001" } ] },
432
+ "payload": {
433
+ "projectId": "11111111-2222-3333-4444-555555555555",
434
+ "presetTaskId": "nightly-review"
435
+ }
436
+ }
437
+ ```
438
+
439
+ A one-time proposal plan runs once at `runAt` and has no time zone or repeat fields:
440
+
441
+ ```json
442
+ {
443
+ "type": "proposal",
444
+ "name": "Weekly triage",
445
+ "schedule": { "mode": "once", "runAt": "2026-06-12T10:30:00Z" },
446
+ "payload": {
447
+ "title": "Weekly triage",
448
+ "chiefComplaint": "Triage the open issues and propose the next sprint",
449
+ "projectId": "11111111-2222-3333-4444-555555555555"
450
+ }
451
+ }
452
+ ```
453
+
454
+ ```bash
455
+ hagi plan create --input nightly-review.json
456
+ hagi plan create --input weekly-triage.json --disabled
457
+ PLAN_ID=$(hagi plan create --input nightly-review.json --json | json_field '.plan.id')
458
+ ```
459
+
460
+ A plan is **enabled by default**. `create` sends `enabled: true` unless the document sets `enabled` or you pass `--disabled`. `--name` and `--hero-id` replace the document's `name` and agent pool, and `--hero-id` builds the single-agent `random` pool. Server-owned fields (`id`, `createdAt`, `updatedAt`, `state`, `nextRunAt`) in the document are ignored.
461
+
462
+ ### Update, enable, disable and delete
463
+
464
+ `plan update` reads the current plan, applies your changes and sends the full plan back, so fields you do not mention keep their values:
465
+
466
+ ```bash
467
+ hagi plan update --id "$PLAN_ID" --name "Nightly review (EU)"
468
+ hagi plan update --id "$PLAN_ID" --input changes.json
469
+ hagi plan update --id "$PLAN_ID" --hero-id 5de02573-0000-4000-8000-000000000002 --enabled
470
+ ```
471
+
472
+ `--input` on update is a **shallow merge** over the top-level keys of the plan (`type`, `name`, `enabled`, `schedule`, `agentPool`, `payload`). Each key in the file replaces the current value as a whole, and keys missing from the file are kept. A `schedule` in the file replaces the whole current schedule, so write the complete object, without the fields you want removed (for example `endDate`). Passing `--enabled` and `--disabled` together is a usage error, and so is an update with no change.
473
+
474
+ `enable` and `disable` change only the enabled flag. When the plan already has that flag, nothing is sent and the output says so:
475
+
476
+ ```bash
477
+ hagi plan disable --id "$PLAN_ID"
478
+ hagi plan enable --id "$PLAN_ID" --json
479
+ ```
480
+
481
+ Delete a plan. It never runs again:
482
+
483
+ ```bash
484
+ hagi plan delete --id "$PLAN_ID"
485
+ hagi plan delete --id "$PLAN_ID" --json
486
+ ```
487
+
488
+ To change a plan with a text editor, take the current document, edit it and feed it back. Server-owned fields are dropped automatically:
489
+
490
+ ```bash
491
+ hagi plan get --id "$PLAN_ID" --json | jq .plan > plan.json
492
+ # edit plan.json, then:
493
+ hagi plan update --id "$PLAN_ID" --input plan.json
494
+ ```
495
+
496
+ When the backend rejects a plan with 400 (exit code `20`), the CLI prints the summary and then one line per problem, naming its field:
497
+
498
+ ```text
499
+ Scheduled plan is invalid. (status=400)
500
+ - agentPool: A HagiTask plan needs an agent pool with one agent.
501
+ ```
502
+
503
+ Use `--json` to get the full backend body under `error.details`.
504
+
505
+ ### Run a plan now
506
+
507
+ `plan run` creates a session from the plan's stored inputs immediately, without waiting for the schedule. It waits for the session and prints its summary, or `{ "session": {...} }` with `--json`:
508
+
509
+ ```bash
510
+ hagi plan run --id "$PLAN_ID"
511
+ ```
512
+
513
+ `plan run --background` starts the same run and returns as soon as the backend accepts it, before the session exists. It prints the plan id and `accepted: true`. The session shows up in the normal session list:
514
+
515
+ ```bash
516
+ hagi plan run --id "$PLAN_ID" --background --json
517
+ ```
518
+
519
+ A manual run does not change the plan's state or next run time. It works for disabled and completed plans, and the next automatic run still happens on the original schedule.
520
+
521
+ ## Skills Commands
522
+
523
+ List installed local skills. The table shows each skill's origin (`user` or `built-in`), whether a built-in skill is enabled, its version, scope, agents and path. A user skill shows `-` in the Enabled column, because availability does not apply to it:
524
+
525
+ ```bash
526
+ hagi skills local list
527
+ hagi skills local list --json
528
+ ```
529
+
530
+ Turn a built-in skill on or off by name. The change applies to agent runs that start afterwards; a run already in progress keeps its skills. A name that is not a built-in skill exits with code `30`, and the output carries the backend message:
531
+
532
+ ```bash
533
+ hagi skills local enable --skill-name hagi
534
+ hagi skills local disable --skill-name hagi --json
535
+ ```
536
+
537
+ Built-in skills cannot be uninstalled. Use `skills local disable` to stop one from loading. Remove a user skill by name:
538
+
539
+ ```bash
540
+ SKILL_NAME=demo-skill
541
+ hagi skills local uninstall --skill-name "$SKILL_NAME"
542
+ ```
543
+
544
+ The uninstall result reports a status such as `Succeeded` or `PrerequisiteMissing`, with the backend message. Passing a built-in skill's name returns the backend's refusal unchanged.
545
+
546
+ Skill recommendation and install progress streaming are not part of the CLI. `skills gallery search`, `detail` and `install` are the discovery and install path.
547
+
548
+ Search the gallery and read one skill:
549
+
550
+ ```bash
551
+ hagi skills gallery search --query react
552
+ hagi skills gallery search --query react --json
553
+ hagi skills gallery detail --id vercel-react-best-practices --source vercel-labs/agent-skills
554
+ ```
555
+
556
+ Start a managed install of a gallery skill. This writes the skill into your local skill directories, so run it only when you intend to install it:
557
+
558
+ ```bash
559
+ hagi skills gallery install \
560
+ --id vercel-react-best-practices \
561
+ --source vercel-labs/agent-skills \
562
+ --skill-slug vercel-react-best-practices
563
+ ```
564
+
565
+ Manage the trusted provider directory. `upsert` reads a JSON document from `--input <file>` or `--input -`:
566
+
567
+ ```bash
568
+ hagi skills trusted list
569
+ cat > provider.json <<'EOF'
570
+ {"providerId":"example-provider","displayName":"Example","isEnabled":true,"sortOrder":50,"matchRules":[{"matchType":"owner","value":"example"}]}
571
+ EOF
572
+ hagi skills trusted upsert --input provider.json
573
+ cat provider.json | hagi skills trusted upsert --input - --json
574
+ hagi skills trusted delete --provider-id example-provider
575
+ hagi skills trusted reset
576
+ ```
577
+
578
+ ## Environment Variables
579
+
580
+ The CLI resolves runtime settings centrally before every command. Explicit flags always override environment variables.
581
+
582
+ | Variable | Purpose |
583
+ | --- | --- |
584
+ | `HAGI_API_BASE_URL` | Backend base URL, used when `--base-url` is not given. Overrides the config file. Required when neither is set. |
585
+ | `HAGI_API_TOKEN` | Bearer token attached as `Authorization: Bearer <token>`. |
586
+ | `HAGI_API_LANG` | Shared `Accept-Language` header value. |
587
+
588
+ ## Backend Transport
589
+
590
+ The CLI talks to the backend over gRPC-Web only. It appends `/grpc` to the base URL (for example `http://127.0.0.1:35167/grpc`) and calls the `hagicode.*.v1` services; no REST route is used. gRPC-Web runs over HTTP/1.1, so it works against the plain-HTTP local backend and through reverse proxies. The bearer token, `Accept-Language` and the `X-Hagi-Client` marker travel as request headers. The backend therefore has to expose its `/grpc` endpoints.
195
591
 
196
- Each command maps to one backend action. The CLI prints the suggested next command, but it never chains later lifecycle APIs automatically.
592
+ gRPC status codes keep the exit codes below: `InvalidArgument` is `20`, `Unauthenticated` `21`, `PermissionDenied` `23`, `NotFound` `24`, `FailedPrecondition`, `AlreadyExists` and `Aborted` `29`, other client-side statuses `40`, and `Internal`, `Unavailable`, `DeadlineExceeded` and the rest `50`. The status line in error output (`status=404`) shows the HTTP-equivalent number.
197
593
 
198
594
  ## Exit Codes
199
595
 
200
- Stable non-zero exits used by the CLI:
596
+ Stable non-zero exits used by the CLI. The HTTP numbers are the equivalents of the gRPC status the backend answered with:
201
597
 
202
598
  | Exit code | Meaning |
203
599
  | --- | --- |
@@ -207,6 +603,7 @@ Stable non-zero exits used by the CLI:
207
603
  | `23` | HTTP 403 |
208
604
  | `24` | HTTP 404 |
209
605
  | `29` | HTTP 409 |
606
+ | `30` | Operation failed: the backend returned a refusal, for example `skills local enable` or `disable` on a name that is not a built-in skill, an invalid MonoSpecs configuration from `project monospecs validate`, or a failed clone from `project monospecs clone` or `set --clone` |
210
607
  | `40` | Other HTTP 4xx |
211
608
  | `50` | HTTP 5xx |
212
609
  | `60` | Transport or connection failure |