@hagicode/cli 0.1.0-dev.43.1.03d9a6a → 0.1.0-dev.48.1.ce630a8

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/dist/cli.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("./chunks/main-mvnmEVmb.js");s.runCli().then(e=>{e!==0&&(process.exitCode=e)}).catch(e=>{const n=e instanceof Error?e.stack??e.message:String(e);process.stderr.write(`${n}
2
+ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("./chunks/main-_kYHYZ-C.js");s.runCli().then(e=>{e!==0&&(process.exitCode=e)}).catch(e=>{const n=e instanceof Error?e.stack??e.message:String(e);process.stderr.write(`${n}
3
3
  `),process.exitCode=s.EXIT_CODES.UNKNOWN});
package/dist/main.js CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./chunks/main-mvnmEVmb.js");exports.createCliApp=e.createCliApp;exports.runCli=e.runCli;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./chunks/main-_kYHYZ-C.js");exports.createCliApp=e.createCliApp;exports.runCli=e.runCli;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hagicode/cli",
3
- "version": "0.1.0-dev.43.1.03d9a6a",
3
+ "version": "0.1.0-dev.48.1.ce630a8",
4
4
  "description": "HagiCode CLI for project, proposal, vault, chat, and AutoTask management.",
5
5
  "homepage": "https://github.com/HagiCode-org/cli#readme",
6
6
  "bugs": {
@@ -17,7 +17,8 @@
17
17
  "files": [
18
18
  "dist",
19
19
  "README.md",
20
- "skills"
20
+ "skills",
21
+ "resources/saga"
21
22
  ],
22
23
  "packageManager": "pnpm@12.16.1",
23
24
  "engines": {
@@ -25,7 +26,7 @@
25
26
  },
26
27
  "scripts": {
27
28
  "clean": "rimraf dist",
28
- "build": "export NODE_OPTIONS=\"${NODE_OPTIONS:-} --max-old-space-size=8192\"; pnpm run clean && vite build",
29
+ "build": "export NODE_OPTIONS=\"${NODE_OPTIONS:-} --max-old-space-size=8192\"; pnpm run clean && vite build && node -e \"require('node:fs').chmodSync('dist/cli.js', 0o755)\"",
29
30
  "dev": "tsx ./src/cli.ts",
30
31
  "generate:grpc": "node ./buildTools/generate-grpc.mjs",
31
32
  "generate:grpc:check": "node ./buildTools/generate-grpc.mjs --check",
@@ -0,0 +1 @@
1
+ **/runtime/
@@ -0,0 +1,12 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "files": {
4
+ ".gitignore": "b43c8b58f191b9e0a0b54b6938c0aa4685d70b8f4ae2294c83a9ad6e728b7c0a",
5
+ "prompts/acceptance.md": "11ca06794d0ec19e87d1a4e883a67014563e9750b9fa62a8bedd43a69d52ca65",
6
+ "prompts/amend.md": "be621d1221a85656851548aee1e9e9d78480288bd856baf1bdbf50cb3317f49b",
7
+ "prompts/goal.md": "a545b01c9f6010df661f02cf62ef9bcae527ccf329f304ea82ffdda239b118df",
8
+ "prompts/plan.md": "bcc4c0509f2971083775db2b3678f41c55f16e69deba2d994d3ed636f314d917",
9
+ "prompts/research.md": "2504283ea845440c7909ff1da20bfb70f846d55ca16565740168848cfb8060b1",
10
+ "prompts/run.md": "1193fbc45645b513bbd6e1017513b62a53286d1d60fd00df479cff15de7c785b"
11
+ }
12
+ }
@@ -0,0 +1,15 @@
1
+ # acceptance.md
2
+
3
+ ## Instruction
4
+ Give every plan item one acceptance command. A separate verification worker runs it, so it must decide pass or fail by itself.
5
+
6
+ - One heading per item: `## <ID> <name>`, followed by one line `cmd: <command>`.
7
+ - The command runs from the project root (the folder that holds `.hagicode`), not from the saga root. Write `cd <dir> && ...` when it needs a subfolder. Exit code 0 means the item is done.
8
+ - The command must be able to fail. A command that always passes proves nothing.
9
+ - Every plan item has exactly one heading here, and every heading here is a plan item. If you change the command of an item that is already ticked, the next freeze reopens that item.
10
+
11
+ ## Template
12
+ ```
13
+ ## <ID> <name>
14
+ cmd: <command to run from the project root>
15
+ ```
@@ -0,0 +1,10 @@
1
+ # Folding an amendment into a saga
2
+
3
+ An amendment is a file under `<saga>/amendments/`. It is open while its `merged-in:` line is empty. Folding it in changes the planning artifacts only.
4
+
5
+ 1. `hagi saga status <saga> --path <root> --json` reports the current `revision` N (0 means the saga was never frozen). The merge target is `r<N+1>`.
6
+ 2. Read the amendment. Update `goal.md`, `research.md`, `acceptance.md` and `progress.md` so they agree with each other and with the amendment.
7
+ 3. Items may be added or superseded (`- ~ <ID> <name> | superseded in r<N+1>: <reason>`), never deleted. Do not touch the ticks: when an item's acceptance command changes, the next freeze reopens it.
8
+ 4. Do not decide new open questions yourself. List them under `## Open questions`.
9
+ 5. In the amendment file set `merged-in: r<N+1>`.
10
+ 6. Run `hagi saga check <saga> --phase plan --path <root>` and fix what it reports. Do not run `freeze`.
@@ -0,0 +1,23 @@
1
+ # goal.md
2
+
3
+ ## Instruction
4
+ Write the goal contract of the saga: what it must achieve, in what scope, with which safety rules.
5
+
6
+ - Every work item the saga will have must be checkable by a machine (a command that exits 0 when the item is done). Say so in the goal.
7
+ - Write the non-goals and the block conditions (when an item is blocked instead of forced).
8
+ - Do not write implementation details.
9
+ - Write in the language the user used for the objective.
10
+ - If `amendments/` holds files whose `merged-in:` line is empty, fold them in and set `merged-in: r<next revision>` in each file (see `prompts/amend.md`).
11
+
12
+ ## Template
13
+ ```
14
+ ## Goal
15
+
16
+ ## Scope
17
+
18
+ ## Non-goals
19
+
20
+ ## Safety rules
21
+
22
+ ## Block conditions
23
+ ```
@@ -0,0 +1,16 @@
1
+ # progress.md (the plan)
2
+
3
+ ## Instruction
4
+ List the work items, one line each: `- [ ] <ID> <name> | `. The evidence path is added when an item is ticked.
5
+
6
+ - Items are small enough to verify with one command, mostly independent, and ordered so that earlier items do not depend on later ones.
7
+ - Keep the list short enough to review (roughly 3 to 15 items). Use the user's work items as a start; add or split items only when needed.
8
+ - Do not add items for committing, pushing, opening a pull request, releasing or archiving: they are outside the contract.
9
+ - IDs match the headings of `acceptance.md`.
10
+ - In a revision an item may be added or superseded, never deleted. A superseded item is written without brackets: `- ~ <ID> <name> | superseded in r<N>: <reason>`.
11
+ - Planning ends here. Do not run `freeze`, do not start the run, do not edit project code.
12
+
13
+ ## Template
14
+ ```
15
+ - [ ] <ID> <name> |
16
+ ```
@@ -0,0 +1,16 @@
1
+ # research.md
2
+
3
+ ## Instruction
4
+ Read the project and the references the user gave. Read only: change no project file.
5
+
6
+ - Record what you found: the layout, the existing conventions, the commands that run the tests, anything the plan depends on.
7
+ - List every decision you cannot make yourself under the heading `## Open questions` (the Chinese heading `## 待决问题` also works). One line per question, starting with `- `. Leave them undecided: a human answers them before the run by rewriting a line as `- [decided: <answer>] <question>` (or `- [已决定: <结论>] <问题>`).
8
+ - If there is nothing to decide, write `- none` under that heading.
9
+ - If an item has no honest verification command, say so here as an open question. Do not invent a check.
10
+
11
+ ## Template
12
+ ```
13
+ ## Findings
14
+
15
+ ## Open questions
16
+ ```
@@ -0,0 +1,18 @@
1
+ # Running a saga (the orchestrator)
2
+
3
+ You are the orchestrator. You dispatch work and keep the record. You do not plan, you do not edit the contract, and you do not implement items yourself. A human started this run; starting it is the approval.
4
+
5
+ `<root>` is the registered project path given in the run's input. Every command is `hagi saga <command> <saga> --path <root>` and works from any directory. Every command that changes a Saga also takes `--token <token>`: the token that `claim` printed.
6
+
7
+ 0. **Claim first.** `hagi saga claim <saga> --session <your session id> --path <root> --json`. Keep the printed token for every later command. If it refuses because another owner holds the Saga, stop and report it. Never take over by age, and never write a paused snapshot over another owner's progress.
8
+ 1. **Freeze.** `hagi saga freeze <saga> --path <root> --token <token>`. If it refuses (open questions, open amendments, a changed contract), run `hagi saga set-status <saga> --state paused --note "<reason>" --path <root> --token <token>`, stop and report the reasons. Do not edit contract files and do not decide open questions. A first freeze writes revision 1. After an amendment was merged it writes the next revision, chained to the previous one, and reopens the items whose acceptance command changed.
9
+ 2. **Check before every item and at every heartbeat.** `hagi saga assert-owner <saga> --path <root> --token <token>` before each dispatch, then `hagi saga check <saga> --phase run --path <root>`. If a check fails, pause the same way and report. An open amendment also means: pause.
10
+ 3. **Work one item at a time** (`hagi saga items <saga> --path <root>` lists them with their commands), unless the goal allows parallel items. For each item:
11
+ - An implementation worker writes code and tests only. Give it the goal, the item, the scope and the safety rules, the project root and its report path.
12
+ - A separate verification worker runs the item's command from `acceptance.md` in the project root, writes the output to `<root>/.hagicode/sagas/<saga>/runtime/evidence/<ITEM>.log`, and writes `<root>/.hagicode/sagas/<saga>/runtime/reports/<ITEM>.md` with the line `Status: PASS` (or `状态: PASS`) only if the exit code is 0. It changes no files.
13
+ - Only `hagi saga tick <saga> <ITEM> --path <root> --token <token>` ticks an item. It refuses without a PASS report and an evidence file.
14
+ 4. **On a failed verification, classify it.** An environment fault (missing module, port in use, quota) does not count. A real failure counts: give a tuning worker the failure log path, then verify again. After 3 real failures run `hagi saga block <saga> <ITEM> "<reason>" --path <root> --token <token>` and move on.
15
+ 5. **After a revision reopened items,** also re-verify any item whose command is the full regression: its own command did not change, so it was not reopened.
16
+ 6. **Keep the record.** Append a line to `<root>/.hagicode/sagas/<saga>/runtime/orchestrator-log.md` after every dispatch, verification and tick. Append every session you create to `<root>/.hagicode/sagas/<saga>/runtime/sessions.md`. Run `hagi saga set-status <saga> --state running --note "<heartbeat>" --path <root> --token <token>` at every heartbeat, and `hagi saga status <saga> --path <root> --json` to read the projection at the end.
17
+ 7. **Never leave a command hanging.** Every shell command must end on its own: never read from standard input, and give waits a limit. If something does not answer, report it instead of waiting forever.
18
+ 8. **Stop** when every item is ticked or blocked, when a pause condition appears, or when the human's run options say so. Write the terminal snapshot first (`set-status --state done` only when every active item is verified, otherwise `paused` with the reason), then, once every worker has stopped, `hagi saga release <saga> --path <root> --token <token> --workers-settled`. Do not archive and do not commit. Report the revision, every item with its state and evidence path, the blocked items with reasons, the sessions you created, and the next step for a human.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: hagi
3
- description: Use when the user wants to run, verify, or update the HagiCode CLI against the Hagi API, or asks about their own projects, proposals, chat sessions, vaults, scheduled plans, or installed skills, including user skills versus built-in skills, and enabling or disabling a built-in skill. Also covers the OpenSpec workflow behind proposal commands and Super Commit, which commits a code repository through a HagiTask session. Keep package-first command guidance primary, then follow the proposal flow, local development, and validation references as needed. Inside a HagiCode session, read the session-data reference first for questions about the user's own data.
3
+ description: Use when the user wants to run, verify, or update the HagiCode CLI against the Hagi API, use local Saga initialization, creation, checks, writes, or read-only queries, or asks about their own projects, proposals, chat sessions, vaults, scheduled plans, or installed skills, including user skills versus built-in skills and enabling or disabling a built-in skill. Also covers the OpenSpec workflow behind proposal commands and Super Commit, which commits a code repository through a HagiTask session. Keep package-first command guidance primary, then follow the Saga, proposal, local development, and validation references as needed. Inside a HagiCode session, read the session-data reference first for questions about the user's own data.
4
4
  ---
5
5
 
6
6
  # Hagi
@@ -11,7 +11,9 @@ Use this skill when the user asks to:
11
11
  - list which installed skills are user skills and which are built-in skills, or which built-in skills are enabled
12
12
  - enable or disable a built-in skill
13
13
  - run or verify the `hagi` CLI
14
- - explain or execute `project`, `proposal`, `chat`, `hagitask`, `vault`, `plan`, or `skills` commands
14
+ - run, explain, or verify local `saga init`, `saga new`, `saga list`, `saga status`, or `saga items` operations
15
+ - run, explain, or verify local Saga ownership, checks, and writes: `claim`, `assert-owner`, `release`, `freeze`, `check`, `tick`, `block`, `set-status`, `amend`, `archive`
16
+ - explain or execute `project`, `proposal`, `chat`, `hagitask`, `vault`, `plan`, `skills`, or `saga` commands
15
17
  - explain the OpenSpec workflow behind `proposal` commands, which stage each command starts, or how to follow a run until it settles
16
18
  - take turns on a shared resource (a game server, a test environment) with other agents through `hagi agent lock`
17
19
  - run, explain or check a Super Commit, which commits the uncommitted changes of a code repository through a HagiTask session
@@ -38,7 +40,8 @@ Use this skill when the user asks to:
38
40
  5. Source-tree development in `repos/cli` -> read [`references/local-development.md`](references/local-development.md)
39
41
  6. Post-change validation -> read [`references/validation.md`](references/validation.md)
40
42
  7. The user's own data, inside a HagiCode session -> read [`references/session-data.md`](references/session-data.md)
41
- 8. A shared resource that other agents also use, or any `hagi agent lock` command -> read [`references/resource-lock.md`](references/resource-lock.md)
43
+ 8. Local Saga initialization, creation, list, status, item queries, ownership, checks, freeze, ticks, status writes, amendments, or archive -> read [`references/saga.md`](references/saga.md)
44
+ 9. A shared resource that other agents also use, or any `hagi agent lock` command -> read [`references/resource-lock.md`](references/resource-lock.md)
42
45
 
43
46
  When `hagi` is loaded as a built-in skill inside a HagiCode session, read item 7 first for any question about the user's own data or skills, before the other references.
44
47
 
@@ -84,6 +87,9 @@ The current top-level command families are:
84
87
  - `plan`
85
88
  - `skills`
86
89
  - `agent`
90
+ - `saga`
91
+
92
+ `hagi saga` provides `init`, `new`, `list`, `status`, and `items`, plus the write and check commands `claim`, `assert-owner`, `release`, `freeze`, `check`, `tick`, `block`, `set-status`, `amend`, and `archive`; mutating commands take `--token` or `SAGA_EXECUTION_TOKEN`. With `--path`, these operate on host-local files without reading backend configuration or calling the API; `--project-id` resolves a project through the backend. See [`references/saga.md`](references/saga.md) for initialization, creation, offline behavior, and read-only query guarantees.
87
93
 
88
94
  `project repositories --id <project-id>` is read-only. It lists one project's repositories with each path relative to the project root (for example `repos/cli`). Those paths are the values a proposal's `Repository scope` names; see [`references/proposal-flow.md`](references/proposal-flow.md#repository-scope).
89
95
 
@@ -116,5 +122,6 @@ Each family also reads one session by id. `proposal get` and `hagitask get` read
116
122
  - [`references/openspec-workflow.md`](references/openspec-workflow.md) - the OpenSpec stage each proposal command starts, stage boundaries, who drives each step, and the monitoring rule
117
123
  - [`references/super-commit.md`](references/super-commit.md) - Super Commit: starting it by prompt id, its options, result reading and the explicit-request rule
118
124
  - [`references/resource-lock.md`](references/resource-lock.md) - the shared resource lock: numbers, owner, exit codes, the watcher protocol, the seven agent rules and requisition
125
+ - [`references/saga.md`](references/saga.md) - local initialization, creation, ownership, checks, Saga writes, and read-only queries
119
126
  - [`references/local-development.md`](references/local-development.md) - maintainer-only development, build, and repository internals
120
127
  - [`references/validation.md`](references/validation.md) - targeted verification after documentation or code changes
@@ -1,7 +1,7 @@
1
1
  interface:
2
2
  display_name: "Hagi CLI"
3
- short_description: "Answer questions about your HagiCode data and skills, operate the hagi CLI, follow the OpenSpec workflow, and run a Super Commit."
4
- default_prompt: "Use $hagi to look up your projects, proposals, sessions, vaults, plans, or skills, to follow the OpenSpec workflow of a proposal, to run a Super Commit, to enable or disable a built-in skill, or to inspect, run, or update the HagiCode CLI in repos/cli."
3
+ short_description: "Operate the hagi CLI, including local Saga commands, and work with HagiCode data, skills, proposals, and Super Commit."
4
+ default_prompt: "Use $hagi to operate local Saga init, new, list, status, or items commands and the Saga ownership, check, freeze, tick, set-status, amend, and archive commands; look up HagiCode projects, proposals, sessions, vaults, plans, or skills; follow a proposal's OpenSpec workflow; run a Super Commit; enable or disable a built-in skill; or inspect, run, or update the CLI in repos/cli."
5
5
 
6
6
  policy:
7
7
  allow_implicit_invocation: true
@@ -35,6 +35,7 @@ For one-off execution, replace `hagi` with `npx @hagicode/cli`.
35
35
  hagi --help
36
36
  hagi project list --json
37
37
  hagi proposal list --json
38
+ hagi saga list --path .
38
39
  hagi chat list --json
39
40
  hagi hagitask list --json
40
41
  hagi hagitask create --title "Auto commit" --project-id <project-id> --prompt-id auto-compose-commit.en-US
@@ -55,9 +56,11 @@ Example:
55
56
  hagi --base-url https://api.example.com --token "$HAGI_TOKEN" project list --json
56
57
  ```
57
58
 
59
+ Saga commands using `--path` accept these flags but do not read backend configuration or contact the backend.
60
+
58
61
  ## Environment variables
59
62
 
60
- The CLI resolves runtime settings centrally before every command.
63
+ Backend operations resolve runtime settings centrally before accessing the API.
61
64
  Explicit flags always override environment variables.
62
65
 
63
66
  | Variable | Purpose |
@@ -78,7 +81,9 @@ The base URL can also be stored in a local config file:
78
81
  The file is `$XDG_CONFIG_HOME/hagi/config.json`, or `~/.config/hagi/config.json` when `XDG_CONFIG_HOME` is unset. The `steer` key must be `true` or `false`; any other value exits with code `2` and names the file.
79
82
 
80
83
  Base URL precedence, highest first: `--base-url`, then `HAGI_API_BASE_URL`, then `baseUrl` in the config file.
81
- There is no default. When none of the three is set, the CLI exits with code `2` and names all three sources.
84
+ There is no default. When none of the three is set, a backend operation exits with code `2` and names all three sources.
85
+ The local Saga selector `--path` is the exception: it does not read this file, so malformed, unreachable, or absent backend
86
+ settings do not affect that local operation.
82
87
 
83
88
  ## Command overview
84
89
 
@@ -87,6 +92,7 @@ hagi
87
92
  |-- project list | repositories | create | update | delete
88
93
  | `-- monospecs show | init | set | validate | reload | clone | remove-checkout
89
94
  |-- proposal list | create | optimize-description | generate | annotate | execute | archive | status | complete | send | get | messages
95
+ |-- saga init | new | list | status | items | claim | assert-owner | release | freeze | check | tick | block | set-status | amend | archive
90
96
  |-- vault list | create | update | delete
91
97
  | `-- files list | preview
92
98
  |-- chat list | create | archive | delete | send
@@ -98,6 +104,33 @@ hagi
98
104
  `-- agent lock acquire | status | renew | heartbeat | release | requisition | list
99
105
  ```
100
106
 
107
+ ## Saga commands
108
+
109
+ Each command requires exactly one nonempty project selector:
110
+
111
+ - `--path <directory>` resolves a local directory on the CLI host. Relative paths use the current working directory. This
112
+ selector does not read backend configuration or make API calls, and shared backend flags do not change its behavior.
113
+ - `--project-id <id>` reads the project list through the configured backend. Its project path must be a directory available
114
+ on the machine running the CLI; the CLI does not execute filesystem operations remotely.
115
+
116
+ Missing, empty, or conflicting selectors exit `2` before backend access or file writes. Paths with spaces should be quoted.
117
+ Shared `--base-url`, `--token`, `--lang`, and `--json` options are accepted.
118
+
119
+ ```bash
120
+ hagi saga init --path "./My Project"
121
+ hagi saga new demo --path "./My Project" --json
122
+ hagi saga list --path "./My Project"
123
+ hagi saga status demo --project-id <project-id> --json
124
+ hagi saga items demo --path "./My Project" --json
125
+ hagi saga claim demo --path "./My Project" --session orchestrator-1 --json
126
+ hagi saga check demo --path "./My Project" --phase run --json
127
+ hagi saga tick demo T1 --path "./My Project" --token "$SAGA_EXECUTION_TOKEN" --json
128
+ ```
129
+
130
+ For offline use, invoke an installed package or a package already present in the `npx` cache. An uncached
131
+ `npx @hagicode/cli` command may need network access to download the package; the local `--path` operation itself does not
132
+ need backend access.
133
+
101
134
  ## Project repositories
102
135
 
103
136
  `project repositories --id <project-id>` is read-only. It reads the same aggregation snapshot as `project list` and prints one project's repositories: name, path relative to the project root (for example `repos/cli`), display name, type and branch, with the project's MonoSpecs flag. The `path` values are the ones a proposal's `Repository scope` names.
@@ -63,6 +63,16 @@ The DTO types in `src/generated/api/models` are committed and maintained by hand
63
63
  generated: the backend's OpenAPI document now lists only the retained REST routes, so regenerating from it would delete the
64
64
  types of every removed route. When a proto DTO changes, edit or add the matching type there.
65
65
 
66
+ ## Saga asset bundle
67
+
68
+ The Saga resources are owned by this repository. `resources/saga/` contains the six method prompt files, `.gitignore`,
69
+ and `manifest.json`; there is no standalone Saga tool and no sync step. Keep the bytes raw: do not render templates or
70
+ normalize line endings. The manifest uses schema version `1` and sorted POSIX relative paths mapped to lowercase SHA-256
71
+ digests; it does not hash itself.
72
+
73
+ After editing a prompt, update its digest in `manifest.json`. `tests/runtime/saga/assets.test.ts` fails on any mismatch
74
+ between the files and the manifest, and `pnpm run pack:check` verifies the published contents.
75
+
66
76
  ## Release automation
67
77
 
68
78
  GitHub workflows under `.github/workflows/` handle release drafting and npm publishing.
@@ -0,0 +1,189 @@
1
+ # Saga commands
2
+
3
+ Saga files are project-local. Select exactly one project with `--path <directory>` or `--project-id <id>` for every
4
+ Saga command. A local `--path` is resolved on the CLI host and does not read backend configuration or make API calls.
5
+
6
+ ## Initialize managed resources
7
+
8
+ Run `init` before creating a Saga:
9
+
10
+ ```bash
11
+ hagi saga init --path "./My Project"
12
+ hagi saga init --path "./My Project" --force --json
13
+ ```
14
+
15
+ Initialization installs the six bundled method prompts and the ignore template under `.hagicode/sagas`. It does not repair
16
+ or overwrite existing Saga contracts, metadata, or runtime records.
17
+
18
+ By default, differing managed resources are preserved as conflicts while missing nonconflicting resources are still
19
+ installed. The command prints each file's `created`, `unchanged`, `merged`, `replaced`, or `conflict` outcome and exits
20
+ with code `4` if any conflicts remain. Use `--force` to replace differing managed resources with the bundled versions;
21
+ identical resources remain untouched. Forced initialization never discards custom `.gitignore` entries.
22
+
23
+ The `.gitignore` rule `**/runtime/` is merged as a separate exact line. Existing bytes, line endings, and entries are
24
+ preserved; LF and CRLF files are supported, and repeated initialization does not append duplicate rules.
25
+
26
+ Before writing, initialization checks that managed directories and files are not redirected, that resource targets are
27
+ regular files, and that writes will not replace unsafe hard-linked resources. Refused targets are reported as operation
28
+ failures rather than followed outside the selected project. Exact resources are not rewritten on repeat runs, preserving
29
+ their modes and modification times.
30
+
31
+ With `--json`, stdout contains the installation root, `force` setting, per-file `path`/`outcome` entries, and any
32
+ conflicts. A conflict report is still emitted before the command reports the code-4 error on stderr.
33
+
34
+ ## Create and inspect Sagas
35
+
36
+ `new <saga-id>` creates the Saga natively: it reserves `<saga-id>/` with an exclusive directory creation and writes
37
+ `saga.json` with `id`, `createdAt`, and `method`. It writes no runtime file. IDs must
38
+ match `^[a-z0-9][a-z0-9-]{0,62}$`: one to 63 lowercase letters, digits, or hyphens, beginning with a letter or digit. The
39
+ reserved names are `prompts`, `tools`, `archive`, `runtime`, `amendments`, and `revisions`. Invalid IDs are usage errors
40
+ (code `2`). An existing active path or an archive entry whose name ends in `-<saga-id>` is a collision; creation is
41
+ refused with code `30` and neither Saga is changed. Concurrent creations of one ID leave exactly one Saga.
42
+
43
+ The `--json` result identifies the Saga directory and includes the metadata written to `saga.json`:
44
+
45
+ ```json
46
+ {
47
+ "id": "demo",
48
+ "directory": "/path/to/My Project/.hagicode/sagas/demo",
49
+ "metadata": {
50
+ "id": "demo",
51
+ "createdAt": "2026-01-01T00:00:00Z",
52
+ "method": 1
53
+ }
54
+ }
55
+ ```
56
+
57
+ The human result names the installed goal prompt and the four planning files to write. From the project root, the prompt is
58
+ `.hagicode/sagas/prompts/goal.md`; the planning files are `goal.md`, `research.md`, `acceptance.md`, and `progress.md`.
59
+
60
+ Then inspect the Saga with the read-only commands:
61
+
62
+ ```bash
63
+ hagi saga new demo --path "./My Project" --json
64
+ hagi saga status demo --path "./My Project" --json
65
+ hagi saga list --path "./My Project"
66
+ hagi saga items demo --path "./My Project" --json
67
+ ```
68
+
69
+ ### Query payloads and read-only behavior
70
+
71
+ `list --json` returns `{ "sagas": [...] }`; each row contains `sagaId`, `state`, `revision`, `counts`, `openAmendments`,
72
+ and `updatedAt`. `items <saga-id> --json` returns `{ "sagaId": "...", "items": [...] }`, with each item carrying its
73
+ canonical `id`, `title`, `state`, `cmd`, and `note`.
74
+
75
+ `status <saga-id> --json` returns the `saga-status/2` projection directly, with `schema`, `sagaId`, `revision`, `state`,
76
+ `updatedAt`, `note`, `artifacts`, `nextArtifact`, `counts`, `openAmendments`, and `items`. It derives this view from the
77
+ Saga files; `updatedAt` is the time of this observation, not the execution heartbeat in `runtime/status.json`. The query
78
+ does not refresh or rewrite that stored status, and its default `note` is empty rather than a cached execution note.
79
+
80
+ All three queries are strictly read-only: they do not initialize or repair resources, create runtime files, run the Saga
81
+ tool, or execute acceptance commands. Missing optional draft files use canonical empty-data behavior; malformed or
82
+ unreadable existing data is reported instead of being repaired. ## Execution ownership and the token
83
+
84
+ `claim`, `assert-owner`, and `release` manage `runtime/execute.lock`. Every mutating command (`freeze`, `tick`, `block`,
85
+ `set-status`, `amend`, `archive`, `release`, `assert-owner`) reads the execution token from `--token <token>` or, when
86
+ that flag is absent, from `SAGA_EXECUTION_TOKEN`.
87
+
88
+ ```bash
89
+ hagi saga claim demo --path "./My Project" --session orchestrator-1
90
+ hagi saga assert-owner demo --path "./My Project" --token "$SAGA_EXECUTION_TOKEN"
91
+ hagi saga release demo --path "./My Project" --token "$SAGA_EXECUTION_TOKEN" --workers-settled --json
92
+ ```
93
+
94
+ - `claim --session <id>` creates the lock exclusively and returns the token once, in the human output and as `token` in
95
+ JSON. A second claim is refused with code `1` and names the owning session. A lock is never taken over because of its
96
+ age, and a refused claim changes no file.
97
+ - While a lock exists, a mutating command needs the holder's token. A missing or different token is refused with exit
98
+ code `1` and changes no file. Without a lock, mutating commands run under their own gates, so a Saga created before ownership stays runnable.
99
+ - `assert-owner` exits `0` when no lock exists or the token matches, and `1` otherwise.
100
+ - `release` removes the lock only for the holder, with `--workers-settled`, and only when `runtime/status.json` records
101
+ `paused` or `done`. Any other case exits `1` and keeps the lock.
102
+ - `status`, `items`, `list`, and `check` need no token and never write.
103
+ - With `--project-id`, the root `--token` is the backend bearer token, so `--token` is rejected with `--project-id` as a
104
+ usage error (code `2`) that explains the rule. Pass the execution token through `SAGA_EXECUTION_TOKEN` instead. An execution token is never sent to the backend.
105
+
106
+ ## Plan check and freeze
107
+
108
+ ```bash
109
+ hagi saga check demo --path "./My Project" --phase plan
110
+ hagi saga check demo --path "./My Project" --phase run --json
111
+ hagi saga freeze demo --path "./My Project" --token "$SAGA_EXECUTION_TOKEN" --json
112
+ ```
113
+
114
+ `check --phase plan` passes when `goal.md`, `research.md`, `acceptance.md`, and `progress.md` exist, `progress.md` has at
115
+ least one active item, every active item has an acceptance `cmd:`, and every acceptance entry names an active item.
116
+ `check --phase run` is the default; it adds the frozen-contract hash comparison, the amendment and open-question gates,
117
+ and the proof of every ticked item (an evidence file and a `Status: PASS` or `状态: PASS` report). Any other `--phase`
118
+ value is a usage error (code `2`). Checks write nothing.
119
+
120
+ `freeze` writes `contract.lock` revision 1 for a valid first contract. A repeat on an unchanged contract exits `0`, reports
121
+ the existing revision, and leaves the lock bytes intact. After a contract change, freeze writes revision N+1 only when an
122
+ amendment is merged into `rN+1` and no frozen item disappeared without a superseded row. The previous lock moves to
123
+ `revisions/rN.lock`, each ticked item whose acceptance command changed is reopened, and its evidence and report move to
124
+ `runtime/history/rN/`. Every refusal (open question, unmerged amendment, plan mismatch, tampered contract, removed item)
125
+ lists each reason, exits `1`, and writes nothing.
126
+
127
+ ## Items, amendments, and archive
128
+
129
+ ```bash
130
+ hagi saga tick demo T1 --path "./My Project" --token "$SAGA_EXECUTION_TOKEN" --json
131
+ hagi saga block demo T2 "tests keep failing" --path "./My Project" --token "$SAGA_EXECUTION_TOKEN"
132
+ hagi saga amend demo "Add retry" "Retry twice on timeout" --path "./My Project" --token "$SAGA_EXECUTION_TOKEN"
133
+ hagi saga archive demo --path "./My Project" --token "$SAGA_EXECUTION_TOKEN" --json
134
+ ```
135
+
136
+ - `tick` marks an active item done in `progress.md` as `- [x] <id> <title> | evidence/<id>.log`. It needs a PASS report at
137
+ `runtime/reports/<id>.md`, the evidence file, and a passing run check; otherwise it exits `1` and keeps `progress.md`.
138
+ A successful tick clears that item's blocker. Item IDs containing a path separator or `.`/`..` are refused before any
139
+ file access.
140
+ - `block` records a reason for an active item in `runtime/blocked.json`. An unknown item is refused. An empty reason is a
141
+ usage error (code `2`).
142
+ - `amend` appends the next numbered file, `amendments/<NNN>-<slug>.md`, with an empty `merged-in:` header; the new
143
+ amendment is open until the contract merges it. An empty title is a usage error (code `2`).
144
+ - `archive` moves a finished Saga (every active item done, no open amendment) to `archive/<date>-<saga-id>`. Use
145
+ `--force` to archive unfinished work. An existing destination is refused even with `--force`.
146
+
147
+ ## Status snapshot
148
+
149
+ ```bash
150
+ hagi saga set-status demo --state paused --note "freeze refused" --path "./My Project" --token "$SAGA_EXECUTION_TOKEN" --json
151
+ ```
152
+
153
+ `set-status --state` accepts `draft`, `planned`, `running`, `paused`, or `done`; any other value (or a missing value) is a
154
+ usage error (code `2`) and writes no file. The snapshot is the same `saga-status/2` projection that `status` reports, with
155
+ only `state` and `note` replaced. `planned` and `running` require the plan check to pass. `done` requires every active
156
+ item to be done with its evidence and PASS report, a frozen and unchanged contract, no open amendment, and no undecided
157
+ research question. A refusal exits `1`, lists its reasons, and leaves the stored `runtime/status.json` unchanged. The file
158
+ is replaced atomically, so a concurrent reader always sees a complete document.
159
+
160
+ ## Exit codes and JSON
161
+
162
+ | Code | Meaning |
163
+ |------|---------|
164
+ | `0` | Success. |
165
+ | `1` | Refusal or failed check. Every reason is printed; nothing was changed. |
166
+ | `2` | Usage error: bad selector, missing or invalid argument, or `--token` with `--project-id`. |
167
+ | `4` | Init resource conflict (see above). |
168
+ | `24` | Saga or project not found. |
169
+ | `30` | Operation failed, including a `new` collision. |
170
+
171
+ Write and check commands accept `--json`. Success and refusal write one object to stdout:
172
+
173
+ ```json
174
+ { "sagaId": "demo", "command": "tick", "ok": true, "itemId": "T1", "evidence": "evidence/T1.log" }
175
+ { "sagaId": "demo", "command": "tick", "ok": false, "errors": ["no PASS report for T1: runtime/reports/T1.md must contain \"Status: PASS\""] }
176
+ ```
177
+
178
+ Human output prints a one-line outcome and one line per refusal reason.
179
+
180
+ ## Runtime ignore precondition
181
+
182
+ Before any command writes a file under a Saga's `runtime/` directory (`claim`, `set-status`, `tick`, `block`, and a
183
+ `freeze` that moves history), it makes sure `.hagicode/sagas/.gitignore` ignores runtime directories. An existing
184
+ `**/runtime/` line is kept byte for byte; otherwise `**/runtime/` is appended with the file's line endings.
185
+ If the ignore file cannot be written, the command stops before `runtime/` is created.
186
+
187
+ ## Read-only projection and ownership
188
+
189
+ Every write and check command is implemented by the CLI and never executes a separate tool. The CLI installs only the method prompts and the ignore rule; there is no installed tool to run.
@@ -98,6 +98,19 @@ hagi plan list --json
98
98
  hagi plan get --id <plan-id> --json
99
99
  ```
100
100
 
101
+ ### Sagas
102
+
103
+ ```bash
104
+ hagi saga list --project-id <project-id> --json
105
+ hagi saga status <saga-id> --project-id <project-id> --json
106
+ hagi saga items <saga-id> --project-id <project-id> --json
107
+ ```
108
+
109
+ These queries read the selected project's Saga files without initializing or repairing them, refreshing runtime status,
110
+ or executing acceptance commands. Status is a fresh `saga-status/2` observation; its `updatedAt` is not the stored
111
+ execution heartbeat in `runtime/status.json`. `init` installs resources and `new` creates a Saga, so run either only
112
+ when the user explicitly asks for that change. Contract and lifecycle operations run through `hagi saga` with the execution token rules in `references/saga.md`.
113
+
101
114
  ### Skills
102
115
 
103
116
  ```bash
@@ -128,6 +141,7 @@ A question such as "what is in my plan" is not a request to run, enable, disable
128
141
  | `chat` | `create`, `archive`, `delete`, `send` |
129
142
  | `hagitask` | `create`, `send` |
130
143
  | `plan` | `create`, `update`, `enable`, `disable`, `delete`, `run` |
144
+ | `saga` | `init`, `new` |
131
145
  | `skills` | `local enable`, `local disable`, `local uninstall`, `gallery install`, `trusted upsert`, `trusted delete`, `trusted reset` |
132
146
 
133
147
  A `send` or `run` starts agent work, and `execute` and `generate` start it too. Treat those as changes of the same kind.
@@ -135,11 +135,15 @@ If only a specific command changed, also verify that command's help directly.
135
135
 
136
136
  ## Package publish-surface check
137
137
 
138
- Run this only when `package.json`, publish contents, or release metadata changed:
138
+ Run this when `package.json`, publish contents, or release metadata changed. The Saga asset check and the package check
139
+ are offline:
139
140
 
140
141
  ```bash
141
142
  cd repos/cli
143
+ pnpm exec vitest run tests/runtime/saga/assets.test.ts
142
144
  pnpm run pack:check
143
145
  ```
144
146
 
145
- The npm tarball ships `dist` and `README.md` only (`package.json` `files`), so `skills/**` is not in the package. The skill reaches users through the repository and the server's built-in skill copy, not through npm. Change `files` only if that decision changes.
147
+ The npm tarball ships `dist`, `README.md`, `skills/**`, and only
148
+ the allowlisted Saga resources, including the hidden `.gitignore` template. `pack:check` verifies the actual package
149
+ contents and rejects source maps or unexpected resource files.