@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.
- package/README.md +81 -8
- package/dist/chunks/main-CAH7GrRu.js +251 -0
- package/dist/cli.js +1 -1
- package/dist/main.js +1 -1
- package/package.json +4 -3
- package/resources/saga/.gitignore +1 -0
- package/resources/saga/manifest.json +12 -0
- package/resources/saga/prompts/acceptance.md +15 -0
- package/resources/saga/prompts/amend.md +10 -0
- package/resources/saga/prompts/goal.md +23 -0
- package/resources/saga/prompts/plan.md +16 -0
- package/resources/saga/prompts/research.md +16 -0
- package/resources/saga/prompts/run.md +18 -0
- package/skills/README.md +1 -0
- package/skills/hagi/SKILL.md +23 -2
- package/skills/hagi/agents/openai.yaml +2 -2
- package/skills/hagi/references/cli-usage.md +63 -6
- package/skills/hagi/references/local-development.md +10 -0
- package/skills/hagi/references/resource-lock.md +135 -0
- package/skills/hagi/references/saga.md +189 -0
- package/skills/hagi/references/session-data.md +14 -0
- package/skills/hagi/references/validation.md +6 -2
- package/dist/chunks/main-CGAPe6tb.js +0 -179
|
@@ -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
|
|
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
|
|
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.
|