@orkestrel/scaffold 0.0.101 → 0.0.103

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.
Files changed (59) hide show
  1. package/dist/agents/skills/orkestrel-dispatch/scripts/brief.js +6 -14
  2. package/dist/agents/skills/orkestrel-dispatch/scripts/cite.js +41 -28
  3. package/dist/agents/skills/orkestrel-dispatch/scripts/helpers.js +5 -12
  4. package/dist/agents/skills/orkestrel-dispatch/scripts/launch.js +25 -13
  5. package/dist/agents/skills/orkestrel-dispatch/scripts/login.js +18 -25
  6. package/dist/agents/skills/orkestrel-dispatch/scripts/result.js +11 -9
  7. package/dist/agents/skills/orkestrel-harden/scripts/discovery.js +233 -232
  8. package/dist/agents/skills/orkestrel-publish/scripts/compare.js +95 -89
  9. package/dist/agents/skills/orkestrel-publish/scripts/pins.js +5 -7
  10. package/dist/agents/skills/orkestrel-publish/scripts/swap.js +1273 -0
  11. package/dist/agents/skills/orkestrel-publish/scripts/wave.js +99 -89
  12. package/dist/agents/skills/orkestrel-publish/scripts/window.js +54 -52
  13. package/dist/agents/skills/orkestrel-scout/scripts/map.js +160 -161
  14. package/dist/bin/main.js +483 -444
  15. package/dist/bin/main.js.map +1 -1
  16. package/dist/host/AGENTS.md +1 -0
  17. package/dist/host/agents/skills/orkestrel-dispatch/SKILL.md +10 -10
  18. package/dist/host/agents/skills/orkestrel-dispatch/scripts/brief.ts +8 -11
  19. package/dist/host/agents/skills/orkestrel-dispatch/scripts/cite.ts +58 -28
  20. package/dist/host/agents/skills/orkestrel-dispatch/scripts/helpers.ts +5 -10
  21. package/dist/host/agents/skills/orkestrel-dispatch/scripts/launch.ts +31 -12
  22. package/dist/host/agents/skills/orkestrel-dispatch/scripts/login.ts +28 -23
  23. package/dist/host/agents/skills/orkestrel-dispatch/scripts/result.ts +12 -10
  24. package/dist/host/agents/skills/orkestrel-harden/scripts/discovery.ts +318 -237
  25. package/dist/host/agents/skills/orkestrel-journey/references/statechart.md +2 -5
  26. package/dist/host/agents/skills/orkestrel-publish/SKILL.md +7 -6
  27. package/dist/host/agents/skills/orkestrel-publish/references/release.md +12 -10
  28. package/dist/host/agents/skills/orkestrel-publish/references/wave.md +12 -5
  29. package/dist/host/agents/skills/orkestrel-publish/scripts/compare.ts +124 -82
  30. package/dist/host/agents/skills/orkestrel-publish/scripts/pins.ts +5 -7
  31. package/dist/host/agents/skills/orkestrel-publish/scripts/swap.ts +1655 -0
  32. package/dist/host/agents/skills/orkestrel-publish/scripts/wave.ts +128 -97
  33. package/dist/host/agents/skills/orkestrel-publish/scripts/window.ts +70 -55
  34. package/dist/host/agents/skills/orkestrel-scout/scripts/map.ts +215 -155
  35. package/dist/host/claude/rules/architecture.md +2 -2
  36. package/dist/host/claude/rules/tests.md +8 -2
  37. package/dist/host/claude/rules/typescript.md +130 -0
  38. package/dist/host/claude/rules/workspace.md +33 -18
  39. package/dist/host/configs/helpers.ts +209 -148
  40. package/dist/host/configs/policy.ts +110 -105
  41. package/dist/host/dotfiles/oxlintrc.json +1 -11
  42. package/dist/host/guides/scaffold.md +244 -170
  43. package/dist/host/manifest.json +36 -30
  44. package/dist/host/tests/config.test.ts +1263 -687
  45. package/dist/host/tests/policy.test.ts +19 -0
  46. package/dist/host/tests/setupPolicy.ts +1699 -1026
  47. package/dist/src/core/index.cjs +1595 -1686
  48. package/dist/src/core/index.cjs.map +1 -1
  49. package/dist/src/core/index.d.cts +202 -18
  50. package/dist/src/core/index.d.ts +202 -18
  51. package/dist/src/core/index.js +1590 -1687
  52. package/dist/src/core/index.js.map +1 -1
  53. package/dist/src/server/index.cjs +448 -392
  54. package/dist/src/server/index.cjs.map +1 -1
  55. package/dist/src/server/index.d.cts +1182 -1052
  56. package/dist/src/server/index.d.ts +1182 -1052
  57. package/dist/src/server/index.js +447 -395
  58. package/dist/src/server/index.js.map +1 -1
  59. 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
- parsed = JSON.parse(readFileSync('package.json', 'utf8'));
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
- let offset = 0;
44
- while (offset < data.length) {
45
- const space = data.indexOf(0x20, offset);
46
- if (space === -1)
47
- return undefined;
48
- const length = Number(data.subarray(offset, space).toString('utf8'));
49
- if (!Number.isFinite(length) || length <= 0)
50
- return undefined;
51
- const record = data.subarray(space + 1, offset + length - 1).toString('utf8');
52
- if (record.startsWith('path='))
53
- return record.slice('path='.length);
54
- offset += length;
55
- }
56
- return undefined;
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 files = new Map();
60
- let offset = 0;
61
- let overrideName;
62
- while (offset + BLOCK <= archive.length) {
63
- const header = archive.subarray(offset, offset + BLOCK);
64
- if (header.every((byte) => byte === 0))
65
- break;
66
- const size = Number.parseInt(readHeaderText(header, 124, 12).trim() || '0', 8);
67
- const type = String.fromCharCode(header[156] ?? 0);
68
- const magic = readHeaderText(header, 257, 6);
69
- const prefix = magic.startsWith('ustar') ? readHeaderText(header, 345, 155) : '';
70
- const shortName = readHeaderText(header, 0, 100);
71
- const name = overrideName ?? (prefix === '' ? shortName : `${prefix}/${shortName}`);
72
- const data = archive.subarray(offset + BLOCK, offset + BLOCK + size);
73
- overrideName = undefined;
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
- return files;
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
- let version = readOption(argv, '--version');
115
- let archive;
116
- if (tarball !== undefined) {
117
- if (!existsSync(tarball)) {
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
- for (const [path, content] of entries) {
149
- const prefixed = `${PACKAGE_PREFIX}${dist}/`;
150
- if (path.startsWith(prefixed) && isMaterial(path))
151
- published.set(path.slice(prefixed.length), content);
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 local = new Map();
154
- for (const { path } of listFiles(dist, NOTHING_SKIPPED)) {
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
- if (isMaterial(key))
157
- local.set(key, readFileSync(path));
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
- const hits = [];
39
- content
38
+ return content
40
39
  .toString('utf8')
41
40
  .split(/\r\n|\n/)
42
- .forEach((text, index) => {
41
+ .flatMap((text, index) => {
43
42
  const match = literals.find((entry) => entry.pattern.test(text));
44
- if (match !== undefined) {
45
- hits.push({ path, line: index + 1, literal: match.literal, text: text.trim() });
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']);