@orkestrel/scaffold 0.0.101 → 0.0.102
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/dist/agents/skills/orkestrel-dispatch/scripts/brief.js +6 -14
- package/dist/agents/skills/orkestrel-dispatch/scripts/cite.js +41 -28
- package/dist/agents/skills/orkestrel-dispatch/scripts/helpers.js +5 -12
- package/dist/agents/skills/orkestrel-dispatch/scripts/launch.js +25 -13
- package/dist/agents/skills/orkestrel-dispatch/scripts/login.js +18 -25
- package/dist/agents/skills/orkestrel-dispatch/scripts/result.js +11 -9
- package/dist/agents/skills/orkestrel-harden/scripts/discovery.js +233 -232
- package/dist/agents/skills/orkestrel-publish/scripts/compare.js +95 -89
- package/dist/agents/skills/orkestrel-publish/scripts/pins.js +5 -7
- package/dist/agents/skills/orkestrel-publish/scripts/swap.js +1273 -0
- package/dist/agents/skills/orkestrel-publish/scripts/wave.js +99 -89
- package/dist/agents/skills/orkestrel-publish/scripts/window.js +54 -52
- package/dist/agents/skills/orkestrel-scout/scripts/map.js +160 -161
- package/dist/bin/main.js +483 -444
- package/dist/bin/main.js.map +1 -1
- package/dist/host/AGENTS.md +1 -0
- package/dist/host/agents/skills/orkestrel-dispatch/SKILL.md +10 -10
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/brief.ts +8 -11
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/cite.ts +58 -28
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/helpers.ts +5 -10
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/launch.ts +31 -12
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/login.ts +28 -23
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/result.ts +12 -10
- package/dist/host/agents/skills/orkestrel-harden/scripts/discovery.ts +318 -237
- package/dist/host/agents/skills/orkestrel-journey/references/statechart.md +2 -5
- package/dist/host/agents/skills/orkestrel-publish/SKILL.md +7 -6
- package/dist/host/agents/skills/orkestrel-publish/references/release.md +12 -10
- package/dist/host/agents/skills/orkestrel-publish/references/wave.md +12 -5
- package/dist/host/agents/skills/orkestrel-publish/scripts/compare.ts +124 -82
- package/dist/host/agents/skills/orkestrel-publish/scripts/pins.ts +5 -7
- package/dist/host/agents/skills/orkestrel-publish/scripts/swap.ts +1655 -0
- package/dist/host/agents/skills/orkestrel-publish/scripts/wave.ts +128 -97
- package/dist/host/agents/skills/orkestrel-publish/scripts/window.ts +70 -55
- package/dist/host/agents/skills/orkestrel-scout/scripts/map.ts +215 -155
- package/dist/host/claude/rules/architecture.md +2 -2
- package/dist/host/claude/rules/tests.md +8 -2
- package/dist/host/claude/rules/typescript.md +130 -0
- package/dist/host/claude/rules/workspace.md +30 -12
- package/dist/host/configs/helpers.ts +206 -133
- package/dist/host/configs/policy.ts +110 -105
- package/dist/host/guides/scaffold.md +244 -170
- package/dist/host/manifest.json +35 -29
- package/dist/host/tests/config.test.ts +1220 -633
- package/dist/host/tests/policy.test.ts +19 -0
- package/dist/host/tests/setupPolicy.ts +1669 -985
- package/dist/src/core/index.cjs +1595 -1686
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +202 -18
- package/dist/src/core/index.d.ts +202 -18
- package/dist/src/core/index.js +1590 -1687
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +448 -392
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +1182 -1052
- package/dist/src/server/index.d.ts +1182 -1052
- package/dist/src/server/index.js +447 -395
- package/dist/src/server/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -9,26 +9,19 @@
|
|
|
9
9
|
import { existsSync, readFileSync } from 'node:fs';
|
|
10
10
|
import { relative } from 'node:path';
|
|
11
11
|
import { gunzipSync } from 'node:zlib';
|
|
12
|
-
import { listFiles, readMissingFlags, readOption, runNpm, } from "../../orkestrel-dispatch/scripts/helpers.js";
|
|
12
|
+
import { listFiles, readMissingFlags, readOption, parseJsonRecord, readString, runNpm, } from "../../orkestrel-dispatch/scripts/helpers.js";
|
|
13
13
|
const BLOCK = 512;
|
|
14
14
|
const PACKAGE_PREFIX = 'package/';
|
|
15
15
|
function readManifest() {
|
|
16
16
|
if (!existsSync('package.json'))
|
|
17
17
|
return { name: undefined, version: undefined };
|
|
18
|
-
let parsed;
|
|
19
18
|
try {
|
|
20
|
-
|
|
19
|
+
const record = parseJsonRecord(readFileSync('package.json', 'utf8'));
|
|
20
|
+
return { name: readString(record, 'name'), version: readString(record, 'version') };
|
|
21
21
|
}
|
|
22
22
|
catch {
|
|
23
23
|
return { name: undefined, version: undefined };
|
|
24
24
|
}
|
|
25
|
-
if (typeof parsed !== 'object' || parsed === null)
|
|
26
|
-
return { name: undefined, version: undefined };
|
|
27
|
-
const record = Object.fromEntries(Object.entries(parsed));
|
|
28
|
-
return {
|
|
29
|
-
name: typeof record.name === 'string' ? record.name : undefined,
|
|
30
|
-
version: typeof record.version === 'string' ? record.version : undefined,
|
|
31
|
-
};
|
|
32
25
|
}
|
|
33
26
|
function readField(text) {
|
|
34
27
|
const stripped = text.replace(/^"|"$/gu, '');
|
|
@@ -39,47 +32,86 @@ function readHeaderText(header, start, length) {
|
|
|
39
32
|
const end = slice.indexOf(0);
|
|
40
33
|
return slice.subarray(0, end === -1 ? slice.length : end).toString('utf8');
|
|
41
34
|
}
|
|
42
|
-
function readPaxPath(data) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
offset
|
|
55
|
-
|
|
56
|
-
|
|
35
|
+
function readPaxPath(data, offset = 0) {
|
|
36
|
+
if (offset >= data.length)
|
|
37
|
+
return undefined;
|
|
38
|
+
const space = data.indexOf(0x20, offset);
|
|
39
|
+
if (space === -1)
|
|
40
|
+
return undefined;
|
|
41
|
+
const length = Number(data.subarray(offset, space).toString('utf8'));
|
|
42
|
+
if (!Number.isFinite(length) || length <= 0)
|
|
43
|
+
return undefined;
|
|
44
|
+
const record = data.subarray(space + 1, offset + length - 1).toString('utf8');
|
|
45
|
+
return record.startsWith('path=')
|
|
46
|
+
? record.slice('path='.length)
|
|
47
|
+
: readPaxPath(data, offset + length);
|
|
48
|
+
}
|
|
49
|
+
// A tar entry occupies a header block and the data blocks its size names, so a step ignores every
|
|
50
|
+
// block offset an earlier entry already consumed.
|
|
51
|
+
function stepTar(archive, state, offset) {
|
|
52
|
+
if (state.done || offset < state.next)
|
|
53
|
+
return state;
|
|
54
|
+
const header = archive.subarray(offset, offset + BLOCK);
|
|
55
|
+
if (header.every((byte) => byte === 0))
|
|
56
|
+
return { ...state, done: true };
|
|
57
|
+
const size = Number.parseInt(readHeaderText(header, 124, 12).trim() || '0', 8);
|
|
58
|
+
const type = String.fromCharCode(header[156] ?? 0);
|
|
59
|
+
const magic = readHeaderText(header, 257, 6);
|
|
60
|
+
const prefix = magic.startsWith('ustar') ? readHeaderText(header, 345, 155) : '';
|
|
61
|
+
const shortName = readHeaderText(header, 0, 100);
|
|
62
|
+
const name = state.name ?? (prefix === '' ? shortName : `${prefix}/${shortName}`);
|
|
63
|
+
const data = archive.subarray(offset + BLOCK, offset + BLOCK + size);
|
|
64
|
+
const next = offset + BLOCK + Math.ceil(size / BLOCK) * BLOCK;
|
|
65
|
+
return {
|
|
66
|
+
next,
|
|
67
|
+
name: type === 'x'
|
|
68
|
+
? readPaxPath(data)
|
|
69
|
+
: type === 'L'
|
|
70
|
+
? readHeaderText(data, 0, data.length)
|
|
71
|
+
: undefined,
|
|
72
|
+
files: type === '0' || type === '\0' ? [...state.files, [name, Buffer.from(data)]] : state.files,
|
|
73
|
+
done: !Number.isFinite(next),
|
|
74
|
+
};
|
|
57
75
|
}
|
|
58
76
|
function readTar(archive) {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (type === 'x')
|
|
75
|
-
overrideName = readPaxPath(data);
|
|
76
|
-
else if (type === 'L')
|
|
77
|
-
overrideName = readHeaderText(data, 0, data.length);
|
|
78
|
-
else if (type === '0' || type === '\0')
|
|
79
|
-
files.set(name, Buffer.from(data));
|
|
80
|
-
offset += BLOCK + Math.ceil(size / BLOCK) * BLOCK;
|
|
77
|
+
const offsets = Array.from({ length: Math.floor(archive.length / BLOCK) }, (_, index) => index * BLOCK);
|
|
78
|
+
// A later entry of the same name replaces an earlier one at the earlier position, as a map write does.
|
|
79
|
+
return new Map(offsets.reduce((state, offset) => stepTar(archive, state, offset), {
|
|
80
|
+
next: 0,
|
|
81
|
+
name: undefined,
|
|
82
|
+
files: [],
|
|
83
|
+
done: false,
|
|
84
|
+
}).files);
|
|
85
|
+
}
|
|
86
|
+
function unpackTar(archive) {
|
|
87
|
+
try {
|
|
88
|
+
return readTar(gunzipSync(archive));
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
81
92
|
}
|
|
82
|
-
|
|
93
|
+
}
|
|
94
|
+
async function readSource(name, requested, tarball) {
|
|
95
|
+
if (tarball !== undefined) {
|
|
96
|
+
return existsSync(tarball)
|
|
97
|
+
? { archive: readFileSync(tarball), version: requested, failure: undefined }
|
|
98
|
+
: { archive: undefined, version: requested, failure: `${tarball} does not exist` };
|
|
99
|
+
}
|
|
100
|
+
const version = requested ?? readNpmValue(['view', name, 'version', '--json']);
|
|
101
|
+
const url = version === undefined
|
|
102
|
+
? undefined
|
|
103
|
+
: readNpmValue(['view', `${name}@${version}`, 'dist.tarball', '--json']);
|
|
104
|
+
if (version === undefined || url === undefined) {
|
|
105
|
+
return {
|
|
106
|
+
archive: undefined,
|
|
107
|
+
version,
|
|
108
|
+
failure: `the registry served no tarball for ${name}${version === undefined ? '' : `@${version}`}`,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const fetched = await fetchTarball(url);
|
|
112
|
+
return fetched === undefined
|
|
113
|
+
? { archive: undefined, version, failure: `fetching ${url} failed` }
|
|
114
|
+
: { archive: fetched, version, failure: undefined };
|
|
83
115
|
}
|
|
84
116
|
// A dist tree holds nothing a walk skips, so every emitted file counts.
|
|
85
117
|
const NOTHING_SKIPPED = new Set();
|
|
@@ -111,51 +143,25 @@ async function main(argv) {
|
|
|
111
143
|
console.error('usage: compare.ts [--package NAME] [--version V] [--tarball PATH] [--dist dist] [--json]');
|
|
112
144
|
return 64;
|
|
113
145
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
console.error(`compare: ${tarball} does not exist`);
|
|
119
|
-
return 2;
|
|
120
|
-
}
|
|
121
|
-
archive = readFileSync(tarball);
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
version ??= readNpmValue(['view', name, 'version', '--json']);
|
|
125
|
-
const url = version === undefined
|
|
126
|
-
? undefined
|
|
127
|
-
: readNpmValue(['view', `${name}@${version}`, 'dist.tarball', '--json']);
|
|
128
|
-
if (version === undefined || url === undefined) {
|
|
129
|
-
console.error(`compare: the registry served no tarball for ${name}${version === undefined ? '' : `@${version}`}`);
|
|
130
|
-
return 2;
|
|
131
|
-
}
|
|
132
|
-
const fetched = await fetchTarball(url);
|
|
133
|
-
if (fetched === undefined) {
|
|
134
|
-
console.error(`compare: fetching ${url} failed`);
|
|
135
|
-
return 2;
|
|
136
|
-
}
|
|
137
|
-
archive = fetched;
|
|
138
|
-
}
|
|
139
|
-
const published = new Map();
|
|
140
|
-
let entries;
|
|
141
|
-
try {
|
|
142
|
-
entries = readTar(gunzipSync(archive));
|
|
143
|
-
}
|
|
144
|
-
catch (error) {
|
|
145
|
-
console.error(`compare: the tarball could not be read: ${error instanceof Error ? error.message : String(error)}`);
|
|
146
|
+
const source = await readSource(name, readOption(argv, '--version'), tarball);
|
|
147
|
+
const { archive, version } = source;
|
|
148
|
+
if (archive === undefined) {
|
|
149
|
+
console.error(`compare: ${source.failure}`);
|
|
146
150
|
return 2;
|
|
147
151
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
+
const entries = unpackTar(archive);
|
|
153
|
+
if (entries instanceof Error) {
|
|
154
|
+
console.error(`compare: the tarball could not be read: ${entries.message}`);
|
|
155
|
+
return 2;
|
|
152
156
|
}
|
|
153
|
-
const
|
|
154
|
-
|
|
157
|
+
const prefixed = `${PACKAGE_PREFIX}${dist}/`;
|
|
158
|
+
const published = new Map([...entries].flatMap(([path, content]) => path.startsWith(prefixed) && isMaterial(path)
|
|
159
|
+
? [[path.slice(prefixed.length), content]]
|
|
160
|
+
: []));
|
|
161
|
+
const local = new Map(listFiles(dist, NOTHING_SKIPPED).flatMap(({ path }) => {
|
|
155
162
|
const key = relative(dist, path).split('\\').join('/');
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
163
|
+
return isMaterial(key) ? [[key, readFileSync(path)]] : [];
|
|
164
|
+
}));
|
|
159
165
|
const added = [...local.keys()].filter((key) => !published.has(key)).sort();
|
|
160
166
|
const removed = [...published.keys()].filter((key) => !local.has(key)).sort();
|
|
161
167
|
const changed = [...local.keys()]
|
|
@@ -35,17 +35,15 @@ function sweepFile(path, literals) {
|
|
|
35
35
|
const content = readFileSync(path);
|
|
36
36
|
if (content.includes(0))
|
|
37
37
|
return [];
|
|
38
|
-
|
|
39
|
-
content
|
|
38
|
+
return content
|
|
40
39
|
.toString('utf8')
|
|
41
40
|
.split(/\r\n|\n/)
|
|
42
|
-
.
|
|
41
|
+
.flatMap((text, index) => {
|
|
43
42
|
const match = literals.find((entry) => entry.pattern.test(text));
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
return match === undefined
|
|
44
|
+
? []
|
|
45
|
+
: [{ path, line: index + 1, literal: match.literal, text: text.trim() }];
|
|
47
46
|
});
|
|
48
|
-
return hits;
|
|
49
47
|
}
|
|
50
48
|
function main(argv) {
|
|
51
49
|
const missing = readMissingFlags(argv, ['--version', '--range', '--paths']);
|