@skanl/brambo-environment 0.1.1
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/LICENSE +21 -0
- package/README.md +29 -0
- package/dist/doctor.d.ts +261 -0
- package/dist/doctor.js +551 -0
- package/dist/executors.d.ts +88 -0
- package/dist/executors.js +109 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +60 -0
- package/dist/ingest.d.ts +60 -0
- package/dist/ingest.js +85 -0
- package/dist/init.d.ts +325 -0
- package/dist/init.js +641 -0
- package/dist/remediate.d.ts +51 -0
- package/dist/remediate.js +140 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SKANL
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# @skanl/brambo-environment
|
|
2
|
+
|
|
3
|
+
The verbs that change a machine: `init`, `doctor` and `remediate`, as a library.
|
|
4
|
+
`@skanl/brambo-cli` is a thin binding over this package and holds no capability of its
|
|
5
|
+
own — that is FR-29, and the consumer-install proof enforces it.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm i @skanl/brambo-environment
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## What it gives you
|
|
12
|
+
|
|
13
|
+
- **`initMachine` / `initProject`** — project the registry into every executor's
|
|
14
|
+
NATIVE configuration, at the locations those executors actually read, and
|
|
15
|
+
record what brambo wrote so it can be taken back exactly.
|
|
16
|
+
- **`diagnose`** — every state brambo can see, as a closed union of finding kinds.
|
|
17
|
+
`FINDING_EXITS` is a `Record` over that union, so **a finding kind without a
|
|
18
|
+
way out does not compile.**
|
|
19
|
+
- **`remediate`** — `adopt`, `release`, `repair` and `discard`, each the exit for
|
|
20
|
+
a finding rather than a general-purpose editor.
|
|
21
|
+
- **`detectExecutors`** — which executors this machine has, with the evidence
|
|
22
|
+
paths that decided it, so an absence is reportable rather than assumed.
|
|
23
|
+
|
|
24
|
+
## The rule it exists to keep
|
|
25
|
+
|
|
26
|
+
Brambo writes NATIVE vocabulary at NATIVE locations and never invents a location a
|
|
27
|
+
vendor does not read. A concept no target can express is REPORTED as
|
|
28
|
+
`unprojectable` — never approximated into a namespace that would look right and
|
|
29
|
+
do nothing.
|
package/dist/doctor.d.ts
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import type { DriftEntry, DriftKind, ProjectionWarning, RemediationKind } from '@skanl/brambo-contracts';
|
|
2
|
+
import type { ExecutorDetection } from './executors.ts';
|
|
3
|
+
import type { LegacyBlock, SkippedExecutor, TargetFailure, UnprojectableEntry } from './init.ts';
|
|
4
|
+
/**
|
|
5
|
+
* What one finding is about. The three drift kinds are the CONTRACT's
|
|
6
|
+
* (`DriftKind`), not a second vocabulary: a doctor that classified drift itself
|
|
7
|
+
* would be the divergence this command exists to not have, and widening
|
|
8
|
+
* `DriftKind` upstream turns the two total records below red until this file
|
|
9
|
+
* answers for the new kind.
|
|
10
|
+
*/
|
|
11
|
+
export type DiagnosisFindingKind = DriftKind
|
|
12
|
+
/** Brambo has no registry document for this scope; nothing was initialised. */
|
|
13
|
+
| 'not-initialised'
|
|
14
|
+
/** Brambo knows executors, and this machine has a configuration for none. */
|
|
15
|
+
| 'no-executor'
|
|
16
|
+
/** Brambo's own registry document exists and cannot be read. */
|
|
17
|
+
| 'registry-unreadable'
|
|
18
|
+
/**
|
|
19
|
+
* The registry document was written by a build NEWER than this one.
|
|
20
|
+
*
|
|
21
|
+
* Not `registry-unreadable`, and the split is the whole of spec M31.A: that
|
|
22
|
+
* kind's exit says *"Repair or remove that document"*, and this document is
|
|
23
|
+
* healthy — following the instruction destroys intact data. Brambo knows both
|
|
24
|
+
* version numbers, so it can name the one action that works. Routed on the
|
|
25
|
+
* store's CODE, never on its message text (AD-7).
|
|
26
|
+
*/
|
|
27
|
+
| 'registry-version-ahead'
|
|
28
|
+
/**
|
|
29
|
+
* A stored entry whose TYPE brambo has retired (story M4.E).
|
|
30
|
+
*
|
|
31
|
+
* Not `unprojectable`: that one is about an entry brambo still declares which
|
|
32
|
+
* no target happens to express, and it is reported per target because a target
|
|
33
|
+
* is what refused it. This one is about the REGISTRY holding a word brambo no
|
|
34
|
+
* longer has — no target ever saw it, so no target can report it, and the file
|
|
35
|
+
* it is about is brambo's own registry document.
|
|
36
|
+
*/
|
|
37
|
+
| 'retired-type'
|
|
38
|
+
/** Brambo's own ownership ledger cannot be read, or has lost records. */
|
|
39
|
+
| 'ledger-damaged'
|
|
40
|
+
/** A projection warning with no more specific reading than its own code. */
|
|
41
|
+
| 'projection-warning'
|
|
42
|
+
/**
|
|
43
|
+
* The bytes on disk differ from what projecting would produce.
|
|
44
|
+
*
|
|
45
|
+
* ponytail: FILE-level, not per entry. `ProjectionResult` reports THAT the
|
|
46
|
+
* merged text differs, never which entries account for the difference, so a
|
|
47
|
+
* per-entry answer here would have to be a second computation — the exact
|
|
48
|
+
* divergence this command exists to not have. Upgrade path: the engine
|
|
49
|
+
* surfaces the planned entries alongside `written` (deferred-work.md).
|
|
50
|
+
*/
|
|
51
|
+
| 'out-of-date'
|
|
52
|
+
/** Brambo would write here and the location refused a writability check. */
|
|
53
|
+
| 'not-writable'
|
|
54
|
+
/** A registry entry this target cannot express (correction-01 C5). */
|
|
55
|
+
| 'unprojectable'
|
|
56
|
+
/**
|
|
57
|
+
* Brambo's OWN prior output, still in a vendor file at a location no executor
|
|
58
|
+
* reads (correction-01 C6).
|
|
59
|
+
*
|
|
60
|
+
* Not drift: no ledger record claims it and no corrected build can produce it.
|
|
61
|
+
* It is litter a PREVIOUS build left, and until `brambo remediate discard`
|
|
62
|
+
* existed nothing in the product could take it back — which is why it was not
|
|
63
|
+
* reported before this story. A state brambo reports and cannot leave is
|
|
64
|
+
* exactly what M4.C exists to abolish, so the report and the exit ship
|
|
65
|
+
* together.
|
|
66
|
+
*/
|
|
67
|
+
| 'legacy-block'
|
|
68
|
+
/** This target could not be diagnosed at all; the others still were. */
|
|
69
|
+
| 'target-failed'
|
|
70
|
+
/**
|
|
71
|
+
* A worktree removal that was interrupted between recording its intent and
|
|
72
|
+
* finishing it (spec M16.A, D3/D4).
|
|
73
|
+
*
|
|
74
|
+
* It is REPORTED here and resolved by a verb, never swept at startup: a sweep
|
|
75
|
+
* that removed on every process start would make brambo destructive on a run
|
|
76
|
+
* the user did not ask to be destructive — the same reasoning `remediate`
|
|
77
|
+
* rests on, which is why the exit below is a command rather than something
|
|
78
|
+
* this command performs.
|
|
79
|
+
*
|
|
80
|
+
* The leftovers arrive through {@link DiagnoseOptions.worktreeLeftovers}
|
|
81
|
+
* rather than being discovered here. `@skanl/brambo-environment` may not import a
|
|
82
|
+
* workspace implementation (`test/guard.test.ts`), and doctor may not open a
|
|
83
|
+
* file of its own; the caller that already holds the worktree capability
|
|
84
|
+
* hands the facts in, and this file phrases them — the same shape every other
|
|
85
|
+
* row here has, where `runScope` supplies and doctor words.
|
|
86
|
+
*/
|
|
87
|
+
| 'worktree-leftover';
|
|
88
|
+
/**
|
|
89
|
+
* Whether a finding is something WRONG or something merely true.
|
|
90
|
+
*
|
|
91
|
+
* The distinction exists because the exit code is a promise: a non-zero exit a
|
|
92
|
+
* user cannot ever get back to zero is not a diagnosis, it is a stuck light. A
|
|
93
|
+
* `tool` entry in the registry is an ordinary thing to register and is
|
|
94
|
+
* unprojectable by every executor permanently — reporting it is required
|
|
95
|
+
* (correction-01 C5), failing on it forever is not.
|
|
96
|
+
*/
|
|
97
|
+
export type DiagnosisFindingSeverity = 'problem' | 'info';
|
|
98
|
+
/**
|
|
99
|
+
* One diagnosed problem, named so a user can act on it.
|
|
100
|
+
*
|
|
101
|
+
* The four locating fields are present EXACTLY when the finding is about them:
|
|
102
|
+
* an entry-level finding carries all four, a target-level one carries the
|
|
103
|
+
* executor and the file, a finding about brambo's own state carries the file
|
|
104
|
+
* alone, and a machine-level one carries none — which is why they are optional
|
|
105
|
+
* rather than filled with a placeholder that reads as a fact.
|
|
106
|
+
*/
|
|
107
|
+
export interface DiagnosisFinding {
|
|
108
|
+
readonly kind: DiagnosisFindingKind;
|
|
109
|
+
readonly severity: DiagnosisFindingSeverity;
|
|
110
|
+
/** The executor whose configuration this is about. */
|
|
111
|
+
readonly executorId?: string;
|
|
112
|
+
/** The file this is about — a vendor's own, or one of brambo's two. */
|
|
113
|
+
readonly filePath?: string;
|
|
114
|
+
/** Vendor-native location, e.g. `mcpServers.context7`. */
|
|
115
|
+
readonly location?: string;
|
|
116
|
+
readonly entryId?: string;
|
|
117
|
+
readonly detail: string;
|
|
118
|
+
/** What `brambo init` / `brambo project init` would do about it. */
|
|
119
|
+
readonly resolution: string;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* What `brambo init` WOULD do about each kind — and only ever what brambo can
|
|
123
|
+
* itself perform. A `Record` over the closed kind union, so a kind added without
|
|
124
|
+
* an answer here does not compile: "each finding carries what brambo would do
|
|
125
|
+
* about it" is a type error away from being false, rather than a promise.
|
|
126
|
+
*/
|
|
127
|
+
export declare const RESOLUTION: Record<DiagnosisFindingKind, string>;
|
|
128
|
+
/**
|
|
129
|
+
* How one reported state is LEFT. Three shapes, because they are three different
|
|
130
|
+
* promises and collapsing them is how a diagnosis starts lying:
|
|
131
|
+
*
|
|
132
|
+
* `remediation` — brambo performs it, named by the user, one at a time. These
|
|
133
|
+
* are the states whose only previous exit was hand-editing
|
|
134
|
+
* `~/.brambo/projection-ledger.json`.
|
|
135
|
+
* `command` — an existing brambo command already leaves this state.
|
|
136
|
+
* `outside-brambo`— brambo cannot leave it and says what does. Naming a
|
|
137
|
+
* remediation here would be the same false promise the
|
|
138
|
+
* `out-of-date`/`not-writable` split was written to remove.
|
|
139
|
+
*/
|
|
140
|
+
export type FindingExit = {
|
|
141
|
+
readonly by: 'remediation';
|
|
142
|
+
readonly remediations: readonly RemediationKind[];
|
|
143
|
+
readonly detail: string;
|
|
144
|
+
} | {
|
|
145
|
+
readonly by: 'command';
|
|
146
|
+
readonly command: string;
|
|
147
|
+
readonly detail: string;
|
|
148
|
+
} | {
|
|
149
|
+
readonly by: 'outside-brambo';
|
|
150
|
+
readonly detail: string;
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* The exit for every state brambo reports — TOTAL over {@link DiagnosisFindingKind},
|
|
154
|
+
* so a finding kind added without one does not compile.
|
|
155
|
+
*
|
|
156
|
+
* IT LIVES IN DOCTOR, beside the kinds, and not beside `remediate` — because the
|
|
157
|
+
* first version put it beside the capability, nothing outside the tests consumed
|
|
158
|
+
* it, and `brambo doctor` went on printing *"brambo never overwrites an entry that
|
|
159
|
+
* changed since it wrote it; projecting again leaves your edit exactly as it is"*
|
|
160
|
+
* for four of the five states this story gave an exit to. The trap was closed in
|
|
161
|
+
* the code and left open on the only surface a user reads. Every `resolution`
|
|
162
|
+
* below is now composed from this record, so the product cannot know an exit the
|
|
163
|
+
* report does not print.
|
|
164
|
+
*/
|
|
165
|
+
export declare const FINDING_EXITS: Record<DiagnosisFindingKind, FindingExit>;
|
|
166
|
+
/** Every kind this remediation is the named exit for. Derived, never listed. */
|
|
167
|
+
export declare function findingKindsFor(remediation: RemediationKind): DiagnosisFindingKind[];
|
|
168
|
+
/**
|
|
169
|
+
* Every kind, derived from a record TypeScript proves total. Exported for the
|
|
170
|
+
* tests that partition the kinds by what a finding of that kind must name — a
|
|
171
|
+
* hand-written list there would fall behind the union silently.
|
|
172
|
+
*/
|
|
173
|
+
export declare const DIAGNOSIS_FINDING_KINDS: readonly DiagnosisFindingKind[];
|
|
174
|
+
/** What happened to ONE executor's configuration, in the read-only reading. */
|
|
175
|
+
export interface DiagnosisTarget {
|
|
176
|
+
readonly executorId: string;
|
|
177
|
+
readonly targetId: string;
|
|
178
|
+
/** The vendor's own file, at the location that vendor reads. */
|
|
179
|
+
readonly filePath: string;
|
|
180
|
+
/** True when projecting WOULD change this file. Doctor changed nothing. */
|
|
181
|
+
readonly wouldWrite: boolean;
|
|
182
|
+
readonly drift: readonly DriftEntry[];
|
|
183
|
+
readonly unprojectable: readonly UnprojectableEntry[];
|
|
184
|
+
readonly error?: TargetFailure;
|
|
185
|
+
}
|
|
186
|
+
export interface Diagnosis {
|
|
187
|
+
readonly scope: 'machine' | 'project';
|
|
188
|
+
/** Brambo's own state directory for this scope. Doctor never creates it. */
|
|
189
|
+
readonly bramboDir: string;
|
|
190
|
+
/** This scope's registry document. Its absence is `not-initialised`. */
|
|
191
|
+
readonly registryPath: string;
|
|
192
|
+
readonly ledgerPath: string;
|
|
193
|
+
/** Registry entries the diagnosis read from, across every scope it can see. */
|
|
194
|
+
readonly entryCount: number;
|
|
195
|
+
/** EVERY executor brambo knows, found or not, with the paths consulted. */
|
|
196
|
+
readonly detected: readonly ExecutorDetection[];
|
|
197
|
+
readonly targets: readonly DiagnosisTarget[];
|
|
198
|
+
/**
|
|
199
|
+
* The same reading for each VERIFIED skills root. Separate from `targets`
|
|
200
|
+
* because `filePath` there names a file and here names a directory tree, and
|
|
201
|
+
* because a skills root is the one location where "projecting would change
|
|
202
|
+
* this" can mean brambo REMOVING something.
|
|
203
|
+
*/
|
|
204
|
+
readonly skills: readonly DiagnosisTarget[];
|
|
205
|
+
/**
|
|
206
|
+
* Brambo's own prior output found in a vendor file (correction-01 C6). Every
|
|
207
|
+
* row here was produced by the `discard` remediation under INSPECTION, so the
|
|
208
|
+
* sentence reported is the sentence that remediation acts on.
|
|
209
|
+
*/
|
|
210
|
+
readonly legacy: readonly LegacyBlock[];
|
|
211
|
+
readonly skipped: readonly SkippedExecutor[];
|
|
212
|
+
readonly warnings: readonly ProjectionWarning[];
|
|
213
|
+
/** Empty means clean. `severity: 'problem'` is what a non-zero exit answers for. */
|
|
214
|
+
readonly findings: readonly DiagnosisFinding[];
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* One interrupted worktree removal, as the caller who found it describes it.
|
|
218
|
+
*
|
|
219
|
+
* A STRUCTURAL shape, deliberately: `@skanl/brambo-environment` may not import the
|
|
220
|
+
* worktree implementation, and the one type both sides would otherwise share
|
|
221
|
+
* would have to live in `@skanl/brambo-contracts` — a third-party port surface, for a
|
|
222
|
+
* detail of one provider's own store. The caller holds the capability that
|
|
223
|
+
* discovers these; this file only phrases them.
|
|
224
|
+
*/
|
|
225
|
+
export interface WorktreeLeftover {
|
|
226
|
+
/** The workspace id, e.g. `w-3`. It is what the exit command takes. */
|
|
227
|
+
readonly id: string;
|
|
228
|
+
/** The tree the interrupted removal was working on. */
|
|
229
|
+
readonly path: string;
|
|
230
|
+
/** What the capability found, in its own words. */
|
|
231
|
+
readonly detail: string;
|
|
232
|
+
}
|
|
233
|
+
export interface DiagnoseOptions {
|
|
234
|
+
/** Defaults to the OS home directory. */
|
|
235
|
+
readonly homeDir?: string;
|
|
236
|
+
/** Read only for the project scope, where it defaults to `process.cwd()`. */
|
|
237
|
+
readonly projectDir?: string;
|
|
238
|
+
/** Defaults to `'machine'`, mirroring `brambo init`. */
|
|
239
|
+
readonly scope?: 'machine' | 'project';
|
|
240
|
+
/**
|
|
241
|
+
* Interrupted worktree removals the caller already found (spec M16.A, D4).
|
|
242
|
+
*
|
|
243
|
+
* Supplied rather than discovered, for the reason `WorktreeLeftover` gives.
|
|
244
|
+
* Absent means the caller did not look — which is NOT the same as "there are
|
|
245
|
+
* none", so nothing here reports an empty list as a clean bill of health; a
|
|
246
|
+
* caller that did look and found nothing simply produces no findings, exactly
|
|
247
|
+
* as it would for any other row.
|
|
248
|
+
*/
|
|
249
|
+
readonly worktreeLeftovers?: readonly WorktreeLeftover[];
|
|
250
|
+
}
|
|
251
|
+
/** True when at least one finding is something wrong — the non-zero condition. */
|
|
252
|
+
export declare function hasProblem(diagnosis: Diagnosis): boolean;
|
|
253
|
+
/**
|
|
254
|
+
* Diagnoses one scope and writes nothing at all.
|
|
255
|
+
*
|
|
256
|
+
* A clean environment yields no findings; anything wrong yields at least one
|
|
257
|
+
* with `severity: 'problem'`, which is what lets a script branch on it.
|
|
258
|
+
* Reporting stops at the scope the caller named: doctor never goes looking for
|
|
259
|
+
* other projects brambo has bound.
|
|
260
|
+
*/
|
|
261
|
+
export declare function diagnose(options?: DiagnoseOptions): Promise<Diagnosis>;
|