@orkestrel/scaffold 0.0.63 → 0.0.65
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 +29 -104
- package/dist/bin/main.js +95 -27
- package/dist/bin/main.js.map +1 -1
- package/dist/host/AGENTS.md +2 -2
- package/dist/host/CLAUDE.md +6 -0
- package/dist/host/agents/orchestration.md +23 -15
- package/dist/host/agents/skills/enterprise-bootstrap/SKILL.md +184 -177
- package/dist/host/agents/skills/enterprise-bootstrap/references/bootstrap-reference.md +314 -91
- package/dist/host/agents/skills/enterprise-bootstrap/references/color-modes.md +241 -0
- package/dist/host/agents/skills/enterprise-bootstrap/references/components.md +83 -36
- package/dist/host/agents/skills/enterprise-bootstrap/references/frontend-design.md +297 -98
- package/dist/host/agents/skills/enterprise-bootstrap/references/inputs.md +25 -14
- package/dist/host/agents/skills/enterprise-bootstrap/references/inspection.md +216 -128
- package/dist/host/agents/skills/enterprise-bootstrap/references/responsive-layout.md +187 -0
- package/dist/host/agents/skills/enterprise-bootstrap/references/utilities.md +109 -20
- package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +5 -5
- package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +1 -1
- package/dist/host/agents/skills/orkestrel-publish/SKILL.md +15 -15
- package/dist/host/agents/skills/orkestrel-publish/references/wave.md +43 -17
- package/dist/host/agents/skills/orkestrel-publish/references/window.md +41 -16
- package/dist/host/claude/agents/orkestrel.md +56 -56
- package/dist/host/claude/agents/reviewer.md +13 -0
- package/dist/host/claude/rules/architecture.md +51 -45
- package/dist/host/claude/rules/documentation.md +18 -1
- package/dist/host/claude/rules/portability.md +2 -0
- package/dist/host/claude/rules/quality.md +1 -1
- package/dist/host/claude/rules/tests.md +12 -11
- package/dist/host/claude/rules/typescript.md +5 -0
- package/dist/host/claude/rules/workspace.md +25 -20
- package/dist/host/claude/rules/writing.md +4 -0
- package/dist/host/claude/settings.json +1 -1
- package/dist/host/claude/skills/enterprise-bootstrap/SKILL.md +10 -9
- package/dist/host/codex/agents/orkestrel.toml +3 -3
- package/dist/host/codex/agents/reviewer.toml +4 -2
- package/dist/host/configs/helpers.ts +311 -2
- package/dist/host/configs/policy.ts +1100 -51
- package/dist/host/dotfiles/oxlintrc.json +72 -1
- package/dist/host/guides/guide.md +749 -222
- package/dist/host/guides/scaffold.md +529 -394
- package/dist/host/manifest.json +53 -40
- package/dist/host/scripts/ollama.sh +322 -13
- package/dist/host/tests/config.test.ts +1200 -16
- package/dist/host/tests/policy.test.ts +157 -173
- package/dist/host/tests/setupPolicy.ts +522 -1007
- package/dist/src/core/index.cjs +402 -287
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +160 -128
- package/dist/src/core/index.d.ts +160 -128
- package/dist/src/core/index.js +400 -286
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +28 -21
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +38 -33
- package/dist/src/server/index.d.ts +38 -33
- package/dist/src/server/index.js +28 -21
- package/dist/src/server/index.js.map +1 -1
- package/package.json +18 -19
package/README.md
CHANGED
|
@@ -1,26 +1,7 @@
|
|
|
1
1
|
# @orkestrel/scaffold
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
directory, and
|
|
5
|
-
|
|
6
|
-
Every `@orkestrel` repository shares one toolchain, one set of agent instructions, and one set of
|
|
7
|
-
root dotfiles. Scaffold ships that shared set as data inside the package and gives it verbs: create
|
|
8
|
-
a workspace from it, report how a workspace differs from it, and write the difference back.
|
|
9
|
-
|
|
10
|
-
The set splits by how a repository meets it. Each target carries its own copy of the paths it
|
|
11
|
-
selects from the vendored set — its licence, its harness permission file, its session-start hooks,
|
|
12
|
-
its policy register, its policy proof, its policy plugin, its configuration leaf and its proof, its
|
|
13
|
-
root dotfiles, and the guide mirrors it starts from, never its own guide — and the verbs write
|
|
14
|
-
them and compare them. A bench probe hook reports whether a bench CLI resolves, and the dependency
|
|
15
|
-
hook installs the lockfile's closure in a remote session; what wires a bench stays in the canon,
|
|
16
|
-
and a session reads it at its primary root. The instruction canon — the coding and
|
|
17
|
-
orchestration contracts, the rules, the skills, the templates, the transport contracts, the agent
|
|
18
|
-
roles, the bench configuration, and the MCP registrations — is published for reading instead, from a
|
|
19
|
-
scaffold checkout sitting beside the repository, or from
|
|
20
|
-
`node_modules/@orkestrel/scaffold/dist/host/` in the installed package. Every target carries the
|
|
21
|
-
`AGENTS.md` and `CLAUDE.md` pointers that name where to read it, and the
|
|
22
|
-
`.claude/agents/orkestrel.md` catalog file the `catalog` verb rewrites. Anything else a target holds
|
|
23
|
-
at a canon path is a superseded copy, and `overwrite` deletes it.
|
|
3
|
+
> A compiler that turns a workspace specification into an ordered list of files, compares that list
|
|
4
|
+
> to a real directory, and writes the difference.
|
|
24
5
|
|
|
25
6
|
## Install
|
|
26
7
|
|
|
@@ -28,7 +9,7 @@ at a canon path is a superseded copy, and `overwrite` deletes it.
|
|
|
28
9
|
npm install --save-dev @orkestrel/scaffold
|
|
29
10
|
```
|
|
30
11
|
|
|
31
|
-
The executable needs Node 22.
|
|
12
|
+
The executable needs Node 22.18.0 or later. Run it without installing anything:
|
|
32
13
|
|
|
33
14
|
```sh
|
|
34
15
|
npx @orkestrel/scaffold --help
|
|
@@ -36,73 +17,25 @@ npx @orkestrel/scaffold --help
|
|
|
36
17
|
|
|
37
18
|
## Verbs
|
|
38
19
|
|
|
39
|
-
Authority is the verb's: every verb except `audit` writes when it is typed, and no
|
|
40
|
-
|
|
20
|
+
Authority is the verb's: every verb except `audit` writes when it is typed, and no option grants a
|
|
21
|
+
write. The guide's [Command line](guides/scaffold.md#command-line) section specifies each verb, its
|
|
22
|
+
options, its defaults, and the exit codes.
|
|
41
23
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
Writes a complete workspace into `./router`: its manifest, its build configuration, empty barrels
|
|
52
|
-
for each selected environment, its tests, its documentation, the `AGENTS.md` and `CLAUDE.md`
|
|
53
|
-
pointers, and every vendored file. `--app` selects private application environments on an
|
|
54
|
-
independent axis, and `--deps` names `@orkestrel/*` runtime dependencies, each pinned to the
|
|
55
|
-
registry's latest release. `--bin` adds the command-line entry, its test, and its scoped build
|
|
56
|
-
configuration.
|
|
57
|
-
|
|
58
|
-
### `audit` — report how a target compares to its plan
|
|
59
|
-
|
|
60
|
-
```sh
|
|
61
|
-
npx scaffold audit --groups configs,orchestration
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Writes nothing. Reports one row per path that differs, and exits `1` when anything does. Omit
|
|
65
|
-
`--groups` to cover every group.
|
|
66
|
-
|
|
67
|
-
### `repair` — write back what drifted
|
|
68
|
-
|
|
69
|
-
```sh
|
|
70
|
-
npx scaffold repair
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Restores each planned path the target is missing or has let drift, then re-audits. A file the
|
|
74
|
-
workspace owns — its source, its own proofs, its README — is written once at creation and is never
|
|
75
|
-
rewritten here. Not everything is owned that way: `tests/distribution.test.ts` is restored when it
|
|
76
|
-
is absent and left alone when the workspace has replaced it, and the manifest's script region is
|
|
77
|
-
rewritten when its chain is the one scaffold generated and refused without a write when it is not.
|
|
78
|
-
|
|
79
|
-
### `catalog` — refresh the package table and the guide mirrors
|
|
80
|
-
|
|
81
|
-
```sh
|
|
82
|
-
npx scaffold catalog --all
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Reads the organization's published package list, rewrites the marker-bounded table in
|
|
86
|
-
`.claude/agents/orkestrel.md`, and fetches each package's guide into its local mirror. Without
|
|
87
|
-
`--all` it fetches only the guides the target declares as dependencies.
|
|
88
|
-
|
|
89
|
-
### `overwrite` — repair, catalog, delete, and re-pin
|
|
90
|
-
|
|
91
|
-
```sh
|
|
92
|
-
npx scaffold overwrite --dirty
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
Everything `repair` and `catalog` do, plus the steps only this verb carries: it deletes tracked
|
|
96
|
-
files the plan does not own, and it rewrites the `@orkestrel/*` ranges in the manifest to the
|
|
97
|
-
registry's latest releases. The deletion covers a stray beneath a vendored directory and a superseded
|
|
98
|
-
instruction copy alike, so one run repairs the pointers and sweeps the canon paths a release moved.
|
|
99
|
-
It needs a git repository, and it refuses a tree carrying uncommitted changes unless `--dirty` waives
|
|
100
|
-
that refusal.
|
|
24
|
+
- `new` writes a whole workspace into a target that holds nothing the plan would collide with.
|
|
25
|
+
- `audit` writes nothing, and reports one row per path that differs.
|
|
26
|
+
- `repair` writes each planned path the target is missing or has let drift, and the manifest's range
|
|
27
|
+
and script regions.
|
|
28
|
+
- `catalog` rewrites the package table in the target's catalog agent file, and refetches the guide
|
|
29
|
+
mirrors.
|
|
30
|
+
- `overwrite` does everything `repair` and `catalog` do, then deletes what the plan does not own and
|
|
31
|
+
re-declares the dependency ranges.
|
|
101
32
|
|
|
102
33
|
## Library
|
|
103
34
|
|
|
104
35
|
The entry points split by host. `@orkestrel/scaffold` is host-independent: it compiles, gates, and
|
|
105
|
-
compares.
|
|
36
|
+
compares. `@orkestrel/scaffold/server` is Node-only and holds everything that touches the filesystem
|
|
37
|
+
or the network: `Materializer` writes a plan into a target, `Upstream` reads the registry and the
|
|
38
|
+
guide host, and `WriteTransaction` stages and swaps a set of files with rollback.
|
|
106
39
|
|
|
107
40
|
```ts
|
|
108
41
|
import { Compiler, createBlueprint } from '@orkestrel/scaffold'
|
|
@@ -115,26 +48,8 @@ scaffolding.questions // the advice the compile could not settle
|
|
|
115
48
|
compiler.destroy()
|
|
116
49
|
```
|
|
117
50
|
|
|
118
|
-
A plan says the workspace can be built. It does not decide whether to create it: a caller
|
|
119
|
-
|
|
120
|
-
`new` does. [`guides/scaffold.md`](guides/scaffold.md) states that rule and what it covers.
|
|
121
|
-
|
|
122
|
-
`@orkestrel/scaffold/server` is Node-only and holds everything that touches the filesystem or the
|
|
123
|
-
network: `Materializer` writes a plan into a target, `Upstream` reads the registry and
|
|
124
|
-
the guide host, and `WriteTransaction` stages and swaps a set of files with rollback.
|
|
125
|
-
|
|
126
|
-
```ts
|
|
127
|
-
import type { Plan } from '@orkestrel/scaffold'
|
|
128
|
-
import { Materializer } from '@orkestrel/scaffold/server'
|
|
129
|
-
|
|
130
|
-
declare const plan: Plan
|
|
131
|
-
|
|
132
|
-
const materializer = new Materializer()
|
|
133
|
-
const result = materializer.materialize(plan, './packages/router')
|
|
134
|
-
|
|
135
|
-
result.written // every path created
|
|
136
|
-
materializer.destroy()
|
|
137
|
-
```
|
|
51
|
+
A plan says the workspace can be built. It does not decide whether to create it: a caller creating a
|
|
52
|
+
fresh workspace refuses on any question beside the plan, blocking or not, exactly as `new` does.
|
|
138
53
|
|
|
139
54
|
## Guide
|
|
140
55
|
|
|
@@ -143,6 +58,16 @@ vendored data root, and the generated file set.
|
|
|
143
58
|
|
|
144
59
|
## Notes
|
|
145
60
|
|
|
61
|
+
The `scaffold new` command generates a workspace that declares an npm floor of 11.6.0 in its
|
|
62
|
+
`devEngines` record. An npm at 10.9.0 or later and earlier than 11.6.0 refuses the `npm install`
|
|
63
|
+
command there with the `EBADDEVENGINES` code, before resolving the dependency graph.
|
|
64
|
+
npm 10.5.0 and npm 10.8.3, the releases measured earlier than 10.9.0, ignore the record and fail
|
|
65
|
+
inside dependency resolution instead.
|
|
66
|
+
No Node release the executable supports bundles an npm earlier than 10.9.0. Read the ambient
|
|
67
|
+
version with the `npm --version` command. Raise it with the `npm install --global npm@11.6.0`
|
|
68
|
+
command before the first install.
|
|
69
|
+
These readings come from a Linux host, on 2026-09-13.
|
|
70
|
+
|
|
146
71
|
On Windows, run the executable as `npx scaffold …` or `node ./dist/bin/main.js …`. PowerShell
|
|
147
72
|
mangles npm's `--` passthrough, so avoid `npm run scaffold -- …` there.
|
|
148
73
|
|
package/dist/bin/main.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { align, renderTable, strip, stripControls, width } from "@orkestrel/console";
|
|
3
3
|
import { attempt, isError, isRecord, isString, parseJSON } from "@orkestrel/contract";
|
|
4
|
-
import { BIN_ENTRY_PATH, CATALOG_AGENT_PATH, CONFORMANCE_TEST_PATH, Compiler, DEPENDENCY_NAME_PATTERN, ENVIRONMENTS, GLOBAL_SETUP_PATH, GROUPS, GUIDES_TEST_PATH, HOST_PATHS, INTEGRATION_TEST_PATH, MAX_MANIFEST_BYTES, SERVICE_SETUP_PATH, SHOWCASE_CONFIG_PATH, ScaffoldError, blueprintToDevDependencies, blueprintToRootVite, blueprintToScripts, blueprintToTestArtifacts, blueprintToWritableScripts, compareVersions, createBlueprint, extractRangeMajor, extractVersion, isFloorPath, isScaffoldError, manifestToDependencies, manifestToName, nameToGuide, replaceManifestScripts, replacePlanRanges } from "../src/core/index.js";
|
|
5
|
-
import { Materializer, Upstream, filesToHost, isExactCaseFile, isPhysicalDirectory, isWorktree, listFiles, readFileText, readHostFloor, readSnapshot, resolveContainedPath } from "../src/server/index.js";
|
|
4
|
+
import { BIN_ENTRY_PATH, CATALOG_AGENT_PATH, CONFORMANCE_TEST_PATH, Compiler, DEPENDENCY_NAME_PATTERN, ENVIRONMENTS, GLOBAL_SETUP_PATH, GROUPS, GUIDES_TEST_PATH, HOST_PATHS, INTEGRATION_TEST_PATH, MAX_MANIFEST_BYTES, MAX_PATH_LENGTH, SERVICE_SETUP_PATH, SHOWCASE_CONFIG_PATH, ScaffoldError, blueprintToDevDependencies, blueprintToRootVite, blueprintToScripts, blueprintToTestArtifacts, blueprintToWritableScripts, compareVersions, createBlueprint, extractRangeMajor, extractVersion, isFloorPath, isScaffoldError, manifestToDependencies, manifestToName, nameToGuide, replaceManifestScripts, replacePlanRanges } from "../src/core/index.js";
|
|
5
|
+
import { MAX_INVENTORY_PATHS, Materializer, Upstream, filesToHost, isExactCaseFile, isPhysicalDirectory, isWorktree, listFiles, readFileText, readHostFloor, readSnapshot, resolveContainedPath } from "../src/server/index.js";
|
|
6
6
|
import { createMarkdown, flattenText, isTableNode } from "@orkestrel/markdown";
|
|
7
|
-
import {
|
|
7
|
+
import { createSession } from "@orkestrel/process/server";
|
|
8
8
|
import { parseArgs } from "node:util";
|
|
9
9
|
//#region src/bin/constants.ts
|
|
10
10
|
/**
|
|
@@ -1033,40 +1033,108 @@ function catalogToNames(target) {
|
|
|
1033
1033
|
return names;
|
|
1034
1034
|
}
|
|
1035
1035
|
/**
|
|
1036
|
+
* Collects the complete NUL-delimited records from one raw git output chunk.
|
|
1037
|
+
*
|
|
1038
|
+
* @param records - The complete records retained in arrival order.
|
|
1039
|
+
* @param pending - The mutable cell carrying the unfinished record between chunks.
|
|
1040
|
+
* @param decoder - The streaming UTF-8 decoder shared by the query.
|
|
1041
|
+
* @param controller - The query controller aborted on a bound refusal.
|
|
1042
|
+
* @param target - The repository the query addresses.
|
|
1043
|
+
* @param chunk - The next owned output bytes.
|
|
1044
|
+
* @returns Nothing.
|
|
1045
|
+
*/
|
|
1046
|
+
function collectGitRecords(records, pending, decoder, controller, target, chunk) {
|
|
1047
|
+
if (controller.signal.aborted) return;
|
|
1048
|
+
const parts = `${pending[0] ?? ""}${decoder.decode(chunk, { stream: true })}`.split("\0");
|
|
1049
|
+
pending[0] = parts.pop() ?? "";
|
|
1050
|
+
for (const record of parts) {
|
|
1051
|
+
if (record.length === 0) continue;
|
|
1052
|
+
if (records.length >= MAX_INVENTORY_PATHS) {
|
|
1053
|
+
refuseGitRecords(controller, target, `The git query for target at ${target} exceeded the working-tree inventory limit.`);
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
records.push(record);
|
|
1057
|
+
}
|
|
1058
|
+
if ((pending[0] ?? "").length > MAX_PATH_LENGTH + 3) refuseGitRecords(controller, target, `The git query for target at ${target} returned an unfinished record beyond the supported path limit.`);
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* Aborts a git query with its first target refusal.
|
|
1062
|
+
*
|
|
1063
|
+
* @param controller - The query controller whose reason retains the refusal.
|
|
1064
|
+
* @param target - The repository the query addresses.
|
|
1065
|
+
* @param message - The refusal message.
|
|
1066
|
+
* @returns Nothing.
|
|
1067
|
+
*/
|
|
1068
|
+
function refuseGitRecords(controller, target, message) {
|
|
1069
|
+
if (!controller.signal.aborted) controller.abort(new ScaffoldError("TARGET", message, { target }));
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1036
1072
|
* Reads one git query as its NUL-separated records.
|
|
1037
1073
|
*
|
|
1038
1074
|
* @param target - The repository the query runs in.
|
|
1039
1075
|
* @param args - The git arguments, without the program name.
|
|
1040
1076
|
* @returns The non-empty records git wrote, in git's own order.
|
|
1041
|
-
* @throws `ScaffoldError('TARGET', …)` when the query fails
|
|
1042
|
-
*
|
|
1077
|
+
* @throws `ScaffoldError('TARGET', …)` when the query fails or its output is
|
|
1078
|
+
* incomplete or outside the working-tree inventory bounds.
|
|
1043
1079
|
*
|
|
1044
1080
|
* @remarks
|
|
1045
1081
|
* Git is asked rather than reimplemented, because the tracked set and the dirty
|
|
1046
|
-
* set are git's own answers and nothing else can give them.
|
|
1047
|
-
*
|
|
1048
|
-
*
|
|
1049
|
-
*
|
|
1050
|
-
* the result rather than written onto a stream nobody chose.
|
|
1082
|
+
* set are git's own answers and nothing else can give them. The raw session
|
|
1083
|
+
* streams complete NUL-delimited records without retaining the whole query as a
|
|
1084
|
+
* manifest-sized text buffer. Its initial hooks are installed before the eager
|
|
1085
|
+
* spawn, so no output or child fault can precede its observer.
|
|
1051
1086
|
*
|
|
1052
1087
|
* @example
|
|
1053
1088
|
* ```ts
|
|
1054
1089
|
* import { readGitRecords } from './helpers.js'
|
|
1055
1090
|
*
|
|
1056
|
-
* readGitRecords('./packages/router', ['ls-files', '-z']) // ['package.json', 'src/core/index.ts']
|
|
1091
|
+
* await readGitRecords('./packages/router', ['ls-files', '-z']) // ['package.json', 'src/core/index.ts']
|
|
1057
1092
|
* ```
|
|
1058
1093
|
*/
|
|
1059
|
-
function readGitRecords(target, args) {
|
|
1060
|
-
const
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1094
|
+
async function readGitRecords(target, args) {
|
|
1095
|
+
const records = [];
|
|
1096
|
+
const pending = [""];
|
|
1097
|
+
const decoder = new TextDecoder("utf-8", { ignoreBOM: true });
|
|
1098
|
+
const controller = new AbortController();
|
|
1099
|
+
let session;
|
|
1100
|
+
try {
|
|
1101
|
+
session = createSession({
|
|
1102
|
+
command: {
|
|
1103
|
+
file: "git",
|
|
1104
|
+
arguments: [
|
|
1105
|
+
"-C",
|
|
1106
|
+
target,
|
|
1107
|
+
...args
|
|
1108
|
+
]
|
|
1109
|
+
},
|
|
1110
|
+
workspace: ".",
|
|
1111
|
+
signal: controller.signal,
|
|
1112
|
+
on: {
|
|
1113
|
+
stdout: collectGitRecords.bind(void 0, records, pending, decoder, controller, target),
|
|
1114
|
+
error: refuseGitRecords.bind(void 0, controller, target, `The git query for target at ${target} failed before it completed.`)
|
|
1115
|
+
},
|
|
1116
|
+
error: refuseGitRecords.bind(void 0, controller, target, `The git query for target at ${target} failed while reading its output.`)
|
|
1117
|
+
});
|
|
1118
|
+
const exit = await session.exit;
|
|
1119
|
+
pending[0] = `${pending[0] ?? ""}${decoder.decode()}`;
|
|
1120
|
+
if (controller.signal.aborted) throw controller.signal.reason;
|
|
1121
|
+
if (exit.signal !== null) throw new ScaffoldError("TARGET", `The git query for target at ${target} ended from signal ${exit.signal}.`, {
|
|
1122
|
+
target,
|
|
1123
|
+
signal: exit.signal
|
|
1124
|
+
});
|
|
1125
|
+
if (exit.code !== 0) throw new ScaffoldError("TARGET", `The git query for target at ${target} failed with exit code ${String(exit.code)}.`, {
|
|
1126
|
+
target,
|
|
1127
|
+
code: exit.code
|
|
1128
|
+
});
|
|
1129
|
+
if (!exit.drained) throw new ScaffoldError("TARGET", `The git query for target at ${target} ended before its output drained.`, { target });
|
|
1130
|
+
if ((pending[0] ?? "").length > 0) throw new ScaffoldError("TARGET", `The git query for target at ${target} returned an incomplete record.`, { target });
|
|
1131
|
+
return records;
|
|
1132
|
+
} catch (error) {
|
|
1133
|
+
if (isScaffoldError(error)) throw error;
|
|
1134
|
+
throw new ScaffoldError("TARGET", `The git query for target at ${target} could not start.`, { target });
|
|
1135
|
+
} finally {
|
|
1136
|
+
if (session !== void 0) await session.destroy();
|
|
1137
|
+
}
|
|
1070
1138
|
}
|
|
1071
1139
|
/**
|
|
1072
1140
|
* Reads the environments a comma-separated selection names.
|
|
@@ -1446,7 +1514,7 @@ var CLI = class {
|
|
|
1446
1514
|
const groups = selectionToGroups(command.groups);
|
|
1447
1515
|
const blueprint = this.#derive(target);
|
|
1448
1516
|
this.#assertTarget(target, blueprint, groups);
|
|
1449
|
-
const worktree = this.#worktree(target);
|
|
1517
|
+
const worktree = await this.#worktree(target);
|
|
1450
1518
|
if (worktree.dirty.length > 0 && command.dirty !== true) throw new ScaffoldError("TARGET", `The target at ${target} carries ${String(worktree.dirty.length)} uncommitted change${worktree.dirty.length === 1 ? "" : "s"}. Commit them, or pass --dirty to waive the refusal.`, {
|
|
1451
1519
|
target,
|
|
1452
1520
|
dirty: worktree.dirty.length
|
|
@@ -1971,14 +2039,14 @@ var CLI = class {
|
|
|
1971
2039
|
if (manifest === void 0) throw new ScaffoldError("TARGET", `The target at ${target} carries no readable manifest.`, { target });
|
|
1972
2040
|
return manifest;
|
|
1973
2041
|
}
|
|
1974
|
-
#worktree(target) {
|
|
1975
|
-
const tracked = readGitRecords(target, ["ls-files", "-z"]);
|
|
1976
|
-
const dirty = readGitRecords(target, [
|
|
2042
|
+
async #worktree(target) {
|
|
2043
|
+
const tracked = await readGitRecords(target, ["ls-files", "-z"]);
|
|
2044
|
+
const dirty = (await readGitRecords(target, [
|
|
1977
2045
|
"status",
|
|
1978
2046
|
"--porcelain=v1",
|
|
1979
2047
|
"--untracked-files=all",
|
|
1980
2048
|
"-z"
|
|
1981
|
-
]).map((record) => record.length > 3 && record[2] === " " ? record.slice(3) : record);
|
|
2049
|
+
])).map((record) => record.length > 3 && record[2] === " " ? record.slice(3) : record);
|
|
1982
2050
|
const state = {
|
|
1983
2051
|
tracked,
|
|
1984
2052
|
dirty
|