@hagicode/cli 0.1.0-dev.42.1.b13f8d2 → 0.1.0-dev.47.1.f8624d4

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,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/` or `**/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.