@crewhaus/tool-lifecycle 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +274 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.js +1233 -0
- package/dist/lib/knowledge.d.ts +131 -0
- package/dist/lib/knowledge.js +283 -0
- package/dist/lib/migrate.d.ts +106 -0
- package/dist/lib/migrate.js +264 -0
- package/dist/lib/result.d.ts +72 -0
- package/dist/lib/result.js +139 -0
- package/dist/lib/retention.d.ts +87 -0
- package/dist/lib/retention.js +96 -0
- package/dist/lib/retire.d.ts +92 -0
- package/dist/lib/retire.js +269 -0
- package/dist/lib/tree.d.ts +74 -0
- package/dist/lib/tree.js +284 -0
- package/dist/paths.d.ts +42 -0
- package/dist/paths.js +158 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# @crewhaus/tool-lifecycle
|
|
2
|
+
|
|
3
|
+
The back half of a harness's life, as deterministic tools: retirement, store
|
|
4
|
+
migration, retention enforcement and knowledge sync.
|
|
5
|
+
|
|
6
|
+
These four are the operations that are irreversible when they are wrong. A
|
|
7
|
+
retirement moves a harness's durable state out and removes the live copy. A
|
|
8
|
+
retention sweep deletes sessions on an age rule. A store migration writes one
|
|
9
|
+
store's contents into another root. A knowledge push copies a harness's
|
|
10
|
+
memories into a store other harnesses read. None of them can be undone by
|
|
11
|
+
running them again with a better flag — so all four default to a dry run, and
|
|
12
|
+
all four refuse a selection wider than the caller plainly meant.
|
|
13
|
+
|
|
14
|
+
```yaml
|
|
15
|
+
tools:
|
|
16
|
+
- harnessRetire
|
|
17
|
+
- storeMigrate
|
|
18
|
+
- retentionEnforce
|
|
19
|
+
- knowledgeSync
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
| Tool | What it does |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `HarnessRetire` | Fingerprint a harness's `.crewhaus`, archive it, remove the live copy, and verify the archive against the fingerprint |
|
|
25
|
+
| `StoreMigrate` | Copy a harness's sessions and audit files into another store root, verify every byte, and write a receipt |
|
|
26
|
+
| `RetentionEnforce` | Run the TTL sweep or an explicit purge under `.crewhaus/retention.json`, and evidence it in the audit chain |
|
|
27
|
+
| `KnowledgeSync` | Push a harness's memories and fragments to a shared store, or pull the store's into the harness |
|
|
28
|
+
|
|
29
|
+
## The real library, not a second implementation
|
|
30
|
+
|
|
31
|
+
Every rule these tools enforce belongs to `@crewhaus/harness-lifecycle`, which
|
|
32
|
+
is where `apps/cli` kept it before a tool could reach it:
|
|
33
|
+
|
|
34
|
+
| Rule | Where it lives |
|
|
35
|
+
|---|---|
|
|
36
|
+
| Retirement plan, step order, active-pin refusal, abort-before-destruction, `retirement.json` | `buildRetirementPlan` / `runRetirement` |
|
|
37
|
+
| Age rule, pins, audit windows, the audit-chain deletion exclusion, the evidence record | `runRetentionSweep` / `runRetentionPurge` |
|
|
38
|
+
| Store enumeration and the verbatim copy | `openHarnessRecordStore` / `runRetentionExport` |
|
|
39
|
+
| Shared-record validation (including the re-hash that defeats dedupe poisoning) | `validateSharedMemory` / `readSharedFragments` |
|
|
40
|
+
| Credential masking and the drop-if-still-secret verdict | `buildKnowledgeRedactor` |
|
|
41
|
+
| Push/pull planning and application | `planPush` / `applyPush` / `planPull` / `applyPull` |
|
|
42
|
+
|
|
43
|
+
This package contributes the schema, the containment, the refusals and the
|
|
44
|
+
result shape. It contains no second copy of any of those rules — the whole
|
|
45
|
+
point of lifting them out of the CLI was that a second copy drifts.
|
|
46
|
+
|
|
47
|
+
## Four properties
|
|
48
|
+
|
|
49
|
+
**Dry run is the default, and it is the same code.** `dryRun` defaults to
|
|
50
|
+
`true` everywhere here; acting takes an explicit `dryRun: false`. The preview
|
|
51
|
+
is produced by calling the same library function with its own dry-run flag, so
|
|
52
|
+
the selection being previewed is the selection the real call acts on. Where the
|
|
53
|
+
two can still disagree — the library re-enumerates, and a store can change
|
|
54
|
+
between the calls — the difference is computed and reported as
|
|
55
|
+
`divergedFromPreview` instead of being hidden. `RetentionEnforce` reads the
|
|
56
|
+
clock once and hands the same instant to both passes, so at least the age rule
|
|
57
|
+
cannot move underneath them.
|
|
58
|
+
|
|
59
|
+
**Could not determine is not no.** A directory that could not be listed is
|
|
60
|
+
never reported as empty. An unreadable registry manifest is never reported as
|
|
61
|
+
"no pins" — that is the one that retires a harness an environment is still
|
|
62
|
+
pointed at. An archive that could not be located is never reported as verified.
|
|
63
|
+
A step this build did not perform reports that it did not perform it, rather
|
|
64
|
+
than returning success.
|
|
65
|
+
|
|
66
|
+
**Containment.** Every caller-supplied path goes through `resolveSafe` (copied
|
|
67
|
+
verbatim from `@crewhaus/tool-pkg`), which refuses anything resolving outside
|
|
68
|
+
`process.cwd()`, including through a symlink inside the workspace. A path
|
|
69
|
+
carrying a NUL is refused before any syscall sees it, because a NUL truncates a
|
|
70
|
+
path at the syscall boundary and the check would then be looking at a different
|
|
71
|
+
path from the `open`.
|
|
72
|
+
|
|
73
|
+
Containing the *directory* is not enough, so every path these tools actually
|
|
74
|
+
write to — or delete through — goes through the same check: `<dest>/sessions/<id>.json`,
|
|
75
|
+
`<shared>/memories.jsonl`, `<harness>/.crewhaus/prompts/…`, `.crewhaus/sessions`
|
|
76
|
+
itself. A symlink at one of those names sends the write, or the unlink, to the
|
|
77
|
+
link's target, and a *dangling* one does not even look like a conflict — `stat`
|
|
78
|
+
calls it absent while `open(…, "w")` through it creates the target. There is no
|
|
79
|
+
flag past this: `overwrite` replaces files, it is not consent to leave the
|
|
80
|
+
workspace.
|
|
81
|
+
|
|
82
|
+
Flags are parsed first and acted on as the parsed value:
|
|
83
|
+
a `before` or `since` becomes epoch ms via the library's own
|
|
84
|
+
`parseRetentionDate`, and the result echoes the timestamp that was actually
|
|
85
|
+
used, never the caller's string as though it were the cutoff.
|
|
86
|
+
|
|
87
|
+
**Verification, not optimism.** `HarnessRetire` fingerprints every file under
|
|
88
|
+
`.crewhaus` with sha256 before the move and re-hashes the archived tree
|
|
89
|
+
afterwards. `StoreMigrate` hashes each source file before the copy (after,
|
|
90
|
+
the library has already appended its own evidence record to the source) and
|
|
91
|
+
re-hashes the destination. Both report per-file mismatches, and both report an
|
|
92
|
+
entry they could not hash as *unverifiable* rather than counting it as
|
|
93
|
+
verified.
|
|
94
|
+
|
|
95
|
+
## What each tool refuses
|
|
96
|
+
|
|
97
|
+
**`HarnessRetire`**
|
|
98
|
+
|
|
99
|
+
- an environment still pinned to a registered version (the library's own rule);
|
|
100
|
+
- a registry manifest that cannot be read or parsed — refused as `unreadable`,
|
|
101
|
+
never read as "unpinned";
|
|
102
|
+
- a registry entry with versions this build cannot delete: the step fails, the
|
|
103
|
+
library aborts before the destructive move, and the live state is untouched.
|
|
104
|
+
There is deliberately no `force`: forcing past the pin refusal would leave
|
|
105
|
+
environments pointing at state that no longer exists, and this build cannot
|
|
106
|
+
tombstone them;
|
|
107
|
+
- an archive directory that is not empty, because `runRetirement` *replaces* an
|
|
108
|
+
existing `crewhaus-state` inside it — retiring twice into one archive would
|
|
109
|
+
destroy the first harness's copy (pass `overwriteArchive` if that is really
|
|
110
|
+
the intent);
|
|
111
|
+
- an archive directory overlapping the state directory it archives;
|
|
112
|
+
- an archive directory where the retirement log or the fingerprint manifest
|
|
113
|
+
would be written through a symlink leading out of the workspace;
|
|
114
|
+
- a `.crewhaus` that is a *symlink*: `renameSync` moves the link, so the data
|
|
115
|
+
would stay exactly where it is while the harness lost its pointer to it, and
|
|
116
|
+
a verification that followed the link into the archive would pass;
|
|
117
|
+
- a `.crewhaus` that exists but is **not a directory** — an entry that could
|
|
118
|
+
not be identified is not an absent one, and the archive step would move it
|
|
119
|
+
anyway;
|
|
120
|
+
- a state tree over `maxStateFiles` / `maxStateBytes` (50,000 files / 512 MiB by
|
|
121
|
+
default), because the fingerprint is the slow part and an unbounded one is
|
|
122
|
+
not a budget;
|
|
123
|
+
- a real run without `acceptUnverified`, see below.
|
|
124
|
+
|
|
125
|
+
**`StoreMigrate`**
|
|
126
|
+
|
|
127
|
+
- a destination overlapping a live store (the library's own refusal);
|
|
128
|
+
- a destination holding a `store-migration.json` from a *different* source —
|
|
129
|
+
two harnesses' stores merged into one root cannot be separated afterwards,
|
|
130
|
+
and their audit chains cannot both verify;
|
|
131
|
+
- a destination whose previous receipt cannot be read, unless `overwrite`;
|
|
132
|
+
- files at the destination that would be written over, unless `overwrite` —
|
|
133
|
+
counted by NAME, and counting everything the run *writes* rather than only
|
|
134
|
+
what it copies: the export path drops its own `manifest.json` beside the
|
|
135
|
+
copies and this tool adds `store-migration.json`, and both are in
|
|
136
|
+
`wouldWrite`;
|
|
137
|
+
- a destination path that is a symlink leading out of the workspace, with no
|
|
138
|
+
override;
|
|
139
|
+
- a `targetVersion` different from the source's, and any `targetVersion` at all
|
|
140
|
+
against a store with no version stamp. See below.
|
|
141
|
+
|
|
142
|
+
**`RetentionEnforce`**
|
|
143
|
+
|
|
144
|
+
- a selection covering *every* session in the store, unless `allowDeleteAll`
|
|
145
|
+
says that is the intent;
|
|
146
|
+
- a selected record whose timestamp is at or before 1971 — **with no override at
|
|
147
|
+
all**. A timestamp that low is a broken clock, a failed copy or a restored
|
|
148
|
+
archive, and every age rule reads it as infinitely old and takes it first.
|
|
149
|
+
The fix is to repair or pin the record, not to confirm a number nobody
|
|
150
|
+
believes;
|
|
151
|
+
- a purge cutoff in the future, which means "everything older than the future";
|
|
152
|
+
- a selection over `maxDeletions`;
|
|
153
|
+
- a malformed `.crewhaus/retention.json` — the library throws rather than
|
|
154
|
+
falling back to a default, and the tool surfaces that: an enforcer that half
|
|
155
|
+
understands its policy must not guess;
|
|
156
|
+
- a store that cannot be enumerated;
|
|
157
|
+
- a `.crewhaus/sessions` or `.crewhaus/audit` that is a symlink leading out of
|
|
158
|
+
the workspace. `readdir` follows it and session-store unlinks inside the
|
|
159
|
+
target, so a sweep of a harness in the workspace would delete files that were
|
|
160
|
+
never in it. `allowDeleteAll` does not reach this.
|
|
161
|
+
|
|
162
|
+
**`KnowledgeSync`**
|
|
163
|
+
|
|
164
|
+
- a push from a harness that has not opted in (`.crewhaus/knowledge.json`);
|
|
165
|
+
- a push without `allowWithoutPiiRedaction`, see below;
|
|
166
|
+
- a shared store overlapping the harness's `.crewhaus`, which would re-ingest
|
|
167
|
+
its own pushes;
|
|
168
|
+
- a sync moving more than `maxArtifacts` (500 by default) — a poisoned shared
|
|
169
|
+
store is untrusted input, and so is a runaway push;
|
|
170
|
+
- a push or a pull whose write path leaves the workspace through a symlink. The
|
|
171
|
+
shared store is untrusted by this tool's own description, so a link planted at
|
|
172
|
+
`memories.jsonl` would otherwise have appended the harness's memories to
|
|
173
|
+
whatever it pointed at.
|
|
174
|
+
|
|
175
|
+
## What this build does not do
|
|
176
|
+
|
|
177
|
+
Three things the CLI does around these operations need packages
|
|
178
|
+
`@crewhaus/tool-lifecycle` does not depend on. Each is named where it would
|
|
179
|
+
have happened, and the operations that depend on it refuse rather than
|
|
180
|
+
approximate.
|
|
181
|
+
|
|
182
|
+
**Audit-chain verification and the compliance-evidence bundle.**
|
|
183
|
+
`@crewhaus/audit-log` and `@crewhaus/compliance-controls` are not dependencies
|
|
184
|
+
here, so `HarnessRetire` supplies those two steps as `ok: false` with the
|
|
185
|
+
reason. The library's own gate then aborts the retirement, which is the correct
|
|
186
|
+
default: nothing should be moved on the strength of a chain nobody checked. Run
|
|
187
|
+
the `AuditVerify` tool (`@crewhaus/tool-crewhaus`) first, then pass
|
|
188
|
+
`acceptUnverified: true`. The tool never claims either check happened — a real
|
|
189
|
+
run reports both under `notPerformed`.
|
|
190
|
+
|
|
191
|
+
**Record-shape store migration.** `StoreMigrate` moves a store between roots at
|
|
192
|
+
the version it already has. Rewriting records into a new shape is
|
|
193
|
+
`migrateMemories` in `apps/cli`, over `@crewhaus/memory-store` and
|
|
194
|
+
`@crewhaus/migration-engine`; a package may not depend on an app, and those two
|
|
195
|
+
are not dependencies here. A version-changing migration is therefore refused by
|
|
196
|
+
name rather than silently performed as a copy, which would leave a caller
|
|
197
|
+
believing their store had been upgraded.
|
|
198
|
+
|
|
199
|
+
**PII redaction on push.** Production wires the knowledge redactor around
|
|
200
|
+
`@crewhaus/pii-redactor`. Without it this build runs the credential half —
|
|
201
|
+
masking, plus the strict post-mask rescan that *drops* an artifact still
|
|
202
|
+
looking like a secret — with an identity PII pass. Names, emails and phone
|
|
203
|
+
numbers are not removed. A push says so and requires
|
|
204
|
+
`allowWithoutPiiRedaction`. (The library's `IDENTITY_REDACTOR` is deliberately
|
|
205
|
+
not used: it never flags anything, so a memory holding an API key would be
|
|
206
|
+
pushed verbatim.)
|
|
207
|
+
|
|
208
|
+
**What a store migration leaves behind.** `runRetentionExport` carries
|
|
209
|
+
`.crewhaus/sessions` and `.crewhaus/audit`. Memories, prompts, graders, the
|
|
210
|
+
registry and the policy files are not part of it, so every other entry under
|
|
211
|
+
`.crewhaus` is listed in `notMigrated` — a migration that reports success while
|
|
212
|
+
leaving them behind is exactly the half-migration that puts a store in neither
|
|
213
|
+
shape. The source is never deleted, so it remains the rollback, and a re-run is
|
|
214
|
+
safe: the receipt says what was already there.
|
|
215
|
+
|
|
216
|
+
## Two things this package does differently from the CLI
|
|
217
|
+
|
|
218
|
+
**Dropped secrets are counted, never echoed.** `formatPushReport` prints a
|
|
219
|
+
60-character preview of every memory dropped for still carrying a credential —
|
|
220
|
+
those previews are the secret. This tool reports `droppedForSecrets` as a count
|
|
221
|
+
only. A test asserts the raw result string does not contain the fixture secret.
|
|
222
|
+
|
|
223
|
+
**A pulled memory is recognised on the next pull.** `applyPull` lands a memory
|
|
224
|
+
with an extra `shared:<harness>` tag, and the content hash covers text *and*
|
|
225
|
+
tags — so hashing what is on disk does not reproduce the shared record's hash.
|
|
226
|
+
`crewhaus knowledge pull` compares exactly that way, which means every pull
|
|
227
|
+
re-imports everything it pulled before and `shared.jsonl` grows on each run.
|
|
228
|
+
`KnowledgeSync` hashes a landed memory both ways (still with the library's
|
|
229
|
+
`memoryContentHash`), so a second pull brings in nothing. The undo is exactly
|
|
230
|
+
`applyPull`'s do: **one** trailing `shared:` tag comes back off, because one
|
|
231
|
+
went on. Stripping every `shared:`-prefixed tag is a different function, and it
|
|
232
|
+
is wrong for the record that already carried provenance of its own — one
|
|
233
|
+
harness pulled it and pushed it on — for which neither form reproduces the
|
|
234
|
+
shared hash. `index.test.ts` pins both: the plain case still holds one line
|
|
235
|
+
after two pulls, and the round-tripped record is neither reported missing after
|
|
236
|
+
the pull that landed it nor re-imported by the next one.
|
|
237
|
+
|
|
238
|
+
**A pulled grader fragment is recognised too.** `applyPull` writes one to
|
|
239
|
+
`graders.shared-<hash>.yaml`, which `readHarnessGraders` deliberately does not
|
|
240
|
+
look at — it reads the harness's *own* `graders.yaml`. Fragments a previous
|
|
241
|
+
pull landed are counted separately (matched by that landing name, hashed with
|
|
242
|
+
the library's `fragmentContentHash`), so a second pull does not report pulling
|
|
243
|
+
them again. Fragments are also verified after a pull, as a push verifies its
|
|
244
|
+
own: checking only the memories and calling the whole pull `verified` would be
|
|
245
|
+
a partial check reported as a total one.
|
|
246
|
+
|
|
247
|
+
## Results
|
|
248
|
+
|
|
249
|
+
Every tool returns compact JSON with a `status` of `preview`, `applied`,
|
|
250
|
+
`refused` or `failed`, plus a `code` and a `reason` on the two unhappy ones. A
|
|
251
|
+
preview carries `nothingWasTouched: true`. Sizeable lists are reported as
|
|
252
|
+
`{ shown, total }` so a bounded result never implies a short one.
|
|
253
|
+
|
|
254
|
+
## Limits
|
|
255
|
+
|
|
256
|
+
- **The opt-in marker gates a push, not a pull.** `crewhaus knowledge sync`
|
|
257
|
+
filters a whole fleet by `.crewhaus/knowledge.json` in both directions,
|
|
258
|
+
because it walks harnesses nobody named. A tool call names one directory, and
|
|
259
|
+
the risk there runs the other way — what leaves the harness — so the marker
|
|
260
|
+
is required to push and not to pull. A pulled record is still validated by
|
|
261
|
+
the library and counted if it is refused.
|
|
262
|
+
- **Single tenant.** The on-disk stores carry no tenant id, so everything here
|
|
263
|
+
operates on the local tenant, exactly as `@crewhaus/harness-lifecycle` does.
|
|
264
|
+
- **The archived-state directory name is mirrored, not imported.** The library
|
|
265
|
+
moves the live state to `<archive>/crewhaus-state` and does not export that
|
|
266
|
+
name. If it ever changes, `HarnessRetire` reports that it could not verify
|
|
267
|
+
the archive (it never reports a verified one it did not find), and a test
|
|
268
|
+
here fails on the rename.
|
|
269
|
+
- **A retirement cannot be resumed.** The library aborts before the destructive
|
|
270
|
+
step, so a failed retirement leaves the harness intact and is re-runnable,
|
|
271
|
+
but there is no half-state to pick up from — by design.
|
|
272
|
+
- **Verification is of bytes, not of meaning.** A verified archive is one whose
|
|
273
|
+
files hash the same as before the move. Whether the harness should have been
|
|
274
|
+
retired at all is not a question a hash can answer.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @crewhaus/tool-lifecycle — the back half of a harness's life, as
|
|
3
|
+
* deterministic tools: retirement, store migration, retention enforcement and
|
|
4
|
+
* knowledge sync.
|
|
5
|
+
*
|
|
6
|
+
* These four are the operations that are irreversible when they are wrong. A
|
|
7
|
+
* retirement moves a harness's durable state out and removes the live copy. A
|
|
8
|
+
* retention sweep deletes sessions on an age rule. A store migration writes
|
|
9
|
+
* one store's contents into another root. A knowledge push copies a harness's
|
|
10
|
+
* memories into a store other harnesses read. None of them can be undone by
|
|
11
|
+
* running them again with a better flag.
|
|
12
|
+
*
|
|
13
|
+
* Five properties hold across the package.
|
|
14
|
+
*
|
|
15
|
+
* 1. THE REAL LIBRARY. Every rule these tools enforce belongs to
|
|
16
|
+
* `@crewhaus/harness-lifecycle` — the retirement plan and its
|
|
17
|
+
* active-pin refusal, the retention age rule and its pins, windows and
|
|
18
|
+
* audit-chain exclusion, the shared-knowledge validation and the
|
|
19
|
+
* credential redaction. This package contributes the schema, the
|
|
20
|
+
* containment, the refusals and the result shape. It does not contain a
|
|
21
|
+
* second copy of any of those rules, which is the whole reason they were
|
|
22
|
+
* lifted out of `apps/cli` in the first place.
|
|
23
|
+
* 2. DRY RUN IS THE DEFAULT, AND IT IS THE SAME CODE. `dryRun` defaults to
|
|
24
|
+
* TRUE on all four tools: an irreversible operation should take an
|
|
25
|
+
* explicit `dryRun: false` to happen. The preview is produced by calling
|
|
26
|
+
* the same library function with its own dry-run flag, so the selection
|
|
27
|
+
* being previewed is the selection the real call acts on, not a parallel
|
|
28
|
+
* preview beside it. Where the two runs can still disagree — the library
|
|
29
|
+
* re-enumerates, and a store can change between the calls — the
|
|
30
|
+
* difference is computed and REPORTED rather than hidden.
|
|
31
|
+
* 3. A REFUSAL WHEN THE SELECTOR IS TOO WIDE. An age rule that selects
|
|
32
|
+
* every session in the store, a purge cutoff in the future, a record
|
|
33
|
+
* whose timestamp is epoch zero, a migration into a directory that
|
|
34
|
+
* already holds someone else's store, a retirement into an archive that
|
|
35
|
+
* already holds one: each is refused by name, with what it would have
|
|
36
|
+
* taken.
|
|
37
|
+
* 4. COULD NOT DETERMINE IS NOT NO. A directory that could not be listed is
|
|
38
|
+
* never reported as empty, an unreadable registry manifest is never
|
|
39
|
+
* reported as "no pins", an unverifiable archive is never reported as
|
|
40
|
+
* verified, and a step this build cannot perform reports that it did not
|
|
41
|
+
* perform it rather than returning success.
|
|
42
|
+
* 5. WHAT THIS BUILD CANNOT DO, IT SAYS. Three things the CLI does around
|
|
43
|
+
* these operations need packages this one does not depend on: verifying
|
|
44
|
+
* the audit chain, collecting a compliance-evidence bundle, and
|
|
45
|
+
* rewriting records into a new store version. Each is named where it
|
|
46
|
+
* would have happened, with the tool or command that does it, and the
|
|
47
|
+
* operations that depend on it are refused rather than approximated.
|
|
48
|
+
*/
|
|
49
|
+
import type { RegisteredTool } from "@crewhaus/tool-catalog";
|
|
50
|
+
export declare const harnessRetire: RegisteredTool;
|
|
51
|
+
export declare const storeMigrate: RegisteredTool;
|
|
52
|
+
export declare const retentionEnforce: RegisteredTool;
|
|
53
|
+
export declare const knowledgeSync: RegisteredTool;
|
|
54
|
+
/** Every tool this package registers, in the order a catalog should list them. */
|
|
55
|
+
export declare const LIFECYCLE_TOOLS: ReadonlyArray<RegisteredTool>;
|