@orkestrel/scaffold 0.0.76 → 0.0.78
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/bench.js +204 -0
- package/dist/agents/skills/orkestrel-dispatch/scripts/brief.js +102 -0
- package/dist/agents/skills/orkestrel-dispatch/scripts/cite.js +95 -0
- package/dist/agents/skills/orkestrel-dispatch/scripts/helpers.js +207 -0
- package/dist/agents/skills/orkestrel-dispatch/scripts/launch.js +108 -0
- package/dist/agents/skills/orkestrel-dispatch/scripts/login.js +114 -0
- package/dist/agents/skills/orkestrel-dispatch/scripts/result.js +108 -0
- package/dist/agents/skills/orkestrel-dispatch/scripts/sweep.js +156 -0
- package/dist/agents/skills/orkestrel-harden/scripts/discovery.js +196 -0
- package/dist/agents/skills/orkestrel-publish/scripts/compare.js +206 -0
- package/dist/agents/skills/orkestrel-publish/scripts/pins.js +93 -0
- package/dist/agents/skills/orkestrel-publish/scripts/wave.js +458 -0
- package/dist/agents/skills/orkestrel-publish/scripts/window.js +188 -0
- package/dist/agents/skills/orkestrel-scout/scripts/map.js +300 -0
- package/dist/agents/templates/brief.md +55 -0
- package/dist/bin/main.js +4 -2
- package/dist/bin/main.js.map +1 -1
- package/dist/host/AGENTS.md +77 -135
- package/dist/host/agents/orchestration.md +147 -927
- package/dist/host/agents/skills/enterprise-bootstrap/SKILL.md +2 -2
- package/dist/host/agents/skills/enterprise-bootstrap/references/inspection.md +1 -1
- package/dist/host/agents/skills/{orkestrel-align-packages → orkestrel-align}/SKILL.md +6 -13
- package/dist/host/agents/skills/{orkestrel-align-packages → orkestrel-align}/agents/openai.yaml +1 -1
- package/dist/host/agents/skills/{orkestrel-align-packages → orkestrel-align}/references/fleet.md +5 -7
- package/dist/host/agents/skills/{orkestrel-build-application → orkestrel-build}/SKILL.md +11 -22
- package/dist/host/agents/skills/{orkestrel-build-application → orkestrel-build}/agents/openai.yaml +1 -1
- package/dist/host/agents/skills/orkestrel-debrief/SKILL.md +8 -16
- package/dist/host/agents/skills/orkestrel-debrief/references/instruction-audit.md +3 -3
- package/dist/host/agents/skills/orkestrel-debrief/references/retention.md +13 -13
- package/dist/host/agents/skills/orkestrel-dispatch/SKILL.md +61 -0
- package/dist/host/agents/skills/orkestrel-dispatch/agents/openai.yaml +4 -0
- package/dist/host/agents/skills/orkestrel-dispatch/references/bench.md +25 -0
- package/dist/host/agents/skills/orkestrel-dispatch/references/launch.md +32 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/bench.ts +259 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/brief.ts +110 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/cite.ts +115 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/helpers.ts +239 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/launch.ts +124 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/login.ts +123 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/result.ts +129 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/sweep.ts +157 -0
- package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +42 -193
- package/dist/host/agents/skills/orkestrel-falsify/references/brief.md +38 -108
- package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +35 -134
- package/dist/host/agents/skills/{orkestrel-harden-package → orkestrel-harden}/SKILL.md +10 -14
- package/dist/host/agents/skills/{orkestrel-harden-package → orkestrel-harden}/agents/openai.yaml +1 -1
- package/dist/host/agents/skills/{orkestrel-harden-package → orkestrel-harden}/references/hardening.md +3 -4
- package/dist/host/agents/skills/orkestrel-harden/scripts/discovery.ts +228 -0
- package/dist/host/agents/skills/{orkestrel-prove-journey → orkestrel-journey}/SKILL.md +15 -23
- package/dist/host/agents/skills/orkestrel-journey/agents/openai.yaml +4 -0
- package/dist/host/agents/skills/{orkestrel-prove-journey → orkestrel-journey}/references/captures.md +1 -1
- package/dist/host/agents/skills/{orkestrel-polish-surface → orkestrel-polish}/SKILL.md +25 -33
- package/dist/host/agents/skills/{orkestrel-polish-surface → orkestrel-polish}/agents/openai.yaml +1 -1
- package/dist/host/agents/skills/{orkestrel-polish-surface → orkestrel-polish}/references/capture-harness.md +3 -3
- package/dist/host/agents/skills/orkestrel-publish/SKILL.md +33 -20
- package/dist/host/agents/skills/orkestrel-publish/references/release.md +39 -0
- package/dist/host/agents/skills/orkestrel-publish/references/wave.md +22 -21
- package/dist/host/agents/skills/orkestrel-publish/references/window.md +27 -14
- package/dist/host/agents/skills/orkestrel-publish/scripts/compare.ts +220 -0
- package/dist/host/agents/skills/orkestrel-publish/scripts/pins.ts +114 -0
- package/dist/host/agents/skills/orkestrel-publish/scripts/wave.ts +629 -0
- package/dist/host/agents/skills/orkestrel-publish/scripts/window.ts +242 -0
- package/dist/host/agents/skills/orkestrel-scout/SKILL.md +28 -0
- package/dist/host/agents/skills/orkestrel-scout/agents/openai.yaml +4 -0
- package/dist/host/agents/skills/orkestrel-scout/scripts/map.ts +352 -0
- package/dist/host/agents/templates/brief.md +21 -142
- package/dist/host/agents/transports/claude-cli.md +21 -0
- package/dist/host/agents/transports/codex.md +38 -159
- package/dist/host/agents/transports/cursor.md +16 -65
- package/dist/host/claude/AGENTS.md +38 -0
- package/dist/host/claude/agents/analyst.md +14 -53
- package/dist/host/claude/agents/astra.md +26 -0
- package/dist/host/claude/agents/builder.md +14 -30
- package/dist/host/claude/agents/checker.md +13 -57
- package/dist/host/claude/agents/distiller.md +11 -26
- package/dist/host/claude/agents/grok.md +12 -35
- package/dist/host/claude/agents/opus.md +14 -30
- package/dist/host/claude/agents/planner.md +10 -44
- package/dist/host/claude/agents/researcher.md +11 -30
- package/dist/host/claude/agents/reviewer.md +11 -95
- package/dist/host/claude/agents/scout.md +9 -23
- package/dist/host/claude/agents/verifier.md +15 -33
- package/dist/host/claude/rules/documentation.md +8 -2
- package/dist/host/claude/rules/portability.md +7 -1
- package/dist/host/claude/rules/quality.md +36 -96
- package/dist/host/claude/rules/styles.md +3 -0
- package/dist/host/claude/rules/tests.md +6 -3
- package/dist/host/claude/rules/workspace.md +19 -15
- package/dist/host/claude/rules/writing.md +57 -108
- package/dist/host/claude/settings.json +5 -3
- package/dist/host/claude/skills/enterprise-bootstrap/SKILL.md +1 -1
- package/dist/host/claude/skills/{orkestrel-align-packages → orkestrel-align}/SKILL.md +2 -2
- package/dist/host/claude/skills/{orkestrel-build-application → orkestrel-build}/SKILL.md +2 -2
- package/dist/host/claude/skills/orkestrel-dispatch/SKILL.md +11 -0
- package/dist/host/claude/skills/orkestrel-falsify/SKILL.md +2 -1
- package/dist/host/claude/skills/{orkestrel-harden-package → orkestrel-harden}/SKILL.md +2 -2
- package/dist/host/claude/skills/{orkestrel-prove-journey → orkestrel-journey}/SKILL.md +2 -2
- package/dist/host/claude/skills/orkestrel-polish/SKILL.md +12 -0
- package/dist/host/claude/skills/orkestrel-scout/SKILL.md +11 -0
- package/dist/host/codex/agents/analyst.toml +15 -32
- package/dist/host/codex/agents/astra.toml +25 -0
- package/dist/host/codex/agents/builder.toml +13 -20
- package/dist/host/codex/agents/checker.toml +13 -27
- package/dist/host/codex/agents/distiller.toml +9 -22
- package/dist/host/codex/agents/grok.toml +11 -30
- package/dist/host/codex/agents/opus.toml +14 -22
- package/dist/host/codex/agents/orkestrel.toml +1 -1
- package/dist/host/codex/agents/planner.toml +11 -28
- package/dist/host/codex/agents/researcher.toml +10 -22
- package/dist/host/codex/agents/reviewer.toml +11 -27
- package/dist/host/codex/agents/scout.toml +11 -17
- package/dist/host/codex/agents/verifier.toml +16 -12
- package/dist/host/codex/config.toml +20 -23
- package/dist/host/cursor/mcp.json +0 -4
- package/dist/host/cursor/rules/orchestration.mdc +12 -20
- package/dist/host/dotfiles/mcp.json +0 -4
- package/dist/host/dotfiles/oxlintrc.json +7 -0
- package/dist/host/guides/probe.md +9 -9
- package/dist/host/guides/scaffold.md +117 -71
- package/dist/host/guides/test.md +1 -1
- package/dist/host/manifest.json +322 -185
- package/dist/host/scripts/codex.sh +2 -2
- package/dist/host/tests/config.test.ts +68 -46
- package/dist/host/tests/policy.test.ts +1 -5
- package/dist/host/tests/setupPolicy.ts +179 -4
- package/dist/src/core/index.cjs +255 -84
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +94 -29
- package/dist/src/core/index.d.ts +94 -29
- package/dist/src/core/index.js +253 -85
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +55 -9
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +29 -4
- package/dist/src/server/index.d.ts +29 -4
- package/dist/src/server/index.js +56 -11
- package/dist/src/server/index.js.map +1 -1
- package/package.json +15 -11
- package/dist/host/CLAUDE.md +0 -61
- package/dist/host/agents/skills/orkestrel-prove-journey/agents/openai.yaml +0 -4
- package/dist/host/agents/transports/claude.md +0 -49
- package/dist/host/claude/agents/application.md +0 -36
- package/dist/host/claude/agents/sol.md +0 -61
- package/dist/host/claude/skills/orkestrel-polish-surface/SKILL.md +0 -12
- package/dist/host/codex/agents/application.toml +0 -25
- package/dist/host/codex/agents/sol.toml +0 -19
- /package/dist/host/agents/skills/{orkestrel-align-packages → orkestrel-align}/references/integration.md +0 -0
- /package/dist/host/agents/skills/{orkestrel-harden-package → orkestrel-harden}/references/centralization.md +0 -0
- /package/dist/host/agents/skills/{orkestrel-harden-package → orkestrel-harden}/references/contract.md +0 -0
- /package/dist/host/agents/skills/{orkestrel-harden-package → orkestrel-harden}/references/research.md +0 -0
- /package/dist/host/agents/skills/{orkestrel-prove-journey → orkestrel-journey}/references/decide.md +0 -0
- /package/dist/host/agents/skills/{orkestrel-prove-journey → orkestrel-journey}/references/layer.md +0 -0
- /package/dist/host/agents/skills/{orkestrel-prove-journey → orkestrel-journey}/references/statechart.md +0 -0
- /package/dist/host/agents/skills/{orkestrel-prove-journey → orkestrel-journey}/references/styles.md +0 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// Compare the rebuilt dist/ against the published tarball on material content. Run from the
|
|
2
|
+
// publishing package's root after its build:
|
|
3
|
+
// node .agents/skills/orkestrel-publish/scripts/compare.ts [--package NAME] [--version V] [--tarball PATH] [--dist dist] [--json]
|
|
4
|
+
// The package defaults to the manifest's name and the version to what the registry serves; --tarball
|
|
5
|
+
// compares against a local .tgz instead of fetching. Sourcemaps are excluded, a text file differs
|
|
6
|
+
// only when it differs with all whitespace removed, and a binary file differs on any byte. The
|
|
7
|
+
// summary lists the paths added, removed, and changed. Exit 0 when nothing material differs, 3 when
|
|
8
|
+
// something does, 2 when the tarball cannot be read, 64 on usage.
|
|
9
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
10
|
+
import { relative } from 'node:path'
|
|
11
|
+
import { gunzipSync } from 'node:zlib'
|
|
12
|
+
import {
|
|
13
|
+
listFiles,
|
|
14
|
+
readMissingFlags,
|
|
15
|
+
readOption,
|
|
16
|
+
runNpm,
|
|
17
|
+
} from '../../orkestrel-dispatch/scripts/helpers.ts'
|
|
18
|
+
|
|
19
|
+
const BLOCK = 512
|
|
20
|
+
const PACKAGE_PREFIX = 'package/'
|
|
21
|
+
|
|
22
|
+
interface Manifest {
|
|
23
|
+
readonly name: string | undefined
|
|
24
|
+
readonly version: string | undefined
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function readManifest(): Manifest {
|
|
28
|
+
if (!existsSync('package.json')) return { name: undefined, version: undefined }
|
|
29
|
+
let parsed: unknown
|
|
30
|
+
try {
|
|
31
|
+
parsed = JSON.parse(readFileSync('package.json', 'utf8'))
|
|
32
|
+
} catch {
|
|
33
|
+
return { name: undefined, version: undefined }
|
|
34
|
+
}
|
|
35
|
+
if (typeof parsed !== 'object' || parsed === null) return { name: undefined, version: undefined }
|
|
36
|
+
const record = Object.fromEntries(Object.entries(parsed))
|
|
37
|
+
return {
|
|
38
|
+
name: typeof record.name === 'string' ? record.name : undefined,
|
|
39
|
+
version: typeof record.version === 'string' ? record.version : undefined,
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function readField(text: string): string | undefined {
|
|
44
|
+
const stripped = text.replace(/^"|"$/gu, '')
|
|
45
|
+
return stripped === '' ? undefined : stripped
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function readHeaderText(header: Buffer, start: number, length: number): string {
|
|
49
|
+
const slice = header.subarray(start, start + length)
|
|
50
|
+
const end = slice.indexOf(0)
|
|
51
|
+
return slice.subarray(0, end === -1 ? slice.length : end).toString('utf8')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function readPaxPath(data: Buffer): string | undefined {
|
|
55
|
+
let offset = 0
|
|
56
|
+
while (offset < data.length) {
|
|
57
|
+
const space = data.indexOf(0x20, offset)
|
|
58
|
+
if (space === -1) return undefined
|
|
59
|
+
const length = Number(data.subarray(offset, space).toString('utf8'))
|
|
60
|
+
if (!Number.isFinite(length) || length <= 0) return undefined
|
|
61
|
+
const record = data.subarray(space + 1, offset + length - 1).toString('utf8')
|
|
62
|
+
if (record.startsWith('path=')) return record.slice('path='.length)
|
|
63
|
+
offset += length
|
|
64
|
+
}
|
|
65
|
+
return undefined
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function readTar(archive: Buffer): ReadonlyMap<string, Buffer> {
|
|
69
|
+
const files = new Map<string, Buffer>()
|
|
70
|
+
let offset = 0
|
|
71
|
+
let overrideName: string | undefined
|
|
72
|
+
while (offset + BLOCK <= archive.length) {
|
|
73
|
+
const header = archive.subarray(offset, offset + BLOCK)
|
|
74
|
+
if (header.every((byte) => byte === 0)) break
|
|
75
|
+
const size = Number.parseInt(readHeaderText(header, 124, 12).trim() || '0', 8)
|
|
76
|
+
const type = String.fromCharCode(header[156] ?? 0)
|
|
77
|
+
const magic = readHeaderText(header, 257, 6)
|
|
78
|
+
const prefix = magic.startsWith('ustar') ? readHeaderText(header, 345, 155) : ''
|
|
79
|
+
const shortName = readHeaderText(header, 0, 100)
|
|
80
|
+
const name = overrideName ?? (prefix === '' ? shortName : `${prefix}/${shortName}`)
|
|
81
|
+
const data = archive.subarray(offset + BLOCK, offset + BLOCK + size)
|
|
82
|
+
overrideName = undefined
|
|
83
|
+
if (type === 'x') overrideName = readPaxPath(data)
|
|
84
|
+
else if (type === 'L') overrideName = readHeaderText(data, 0, data.length)
|
|
85
|
+
else if (type === '0' || type === '\0') files.set(name, Buffer.from(data))
|
|
86
|
+
offset += BLOCK + Math.ceil(size / BLOCK) * BLOCK
|
|
87
|
+
}
|
|
88
|
+
return files
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// A dist tree holds nothing a walk skips, so every emitted file counts.
|
|
92
|
+
const NOTHING_SKIPPED: ReadonlySet<string> = new Set()
|
|
93
|
+
|
|
94
|
+
function readNpmValue(args: readonly string[]): string | undefined {
|
|
95
|
+
const result = runNpm(args)
|
|
96
|
+
return result.status === 0 ? readField(result.stdout.trim()) : undefined
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function isMaterial(path: string): boolean {
|
|
100
|
+
return !path.endsWith('.map')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function differs(left: Buffer, right: Buffer): boolean {
|
|
104
|
+
if (left.includes(0) || right.includes(0)) return !left.equals(right)
|
|
105
|
+
return left.toString('utf8').replace(/\s+/gu, '') !== right.toString('utf8').replace(/\s+/gu, '')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function main(argv: readonly string[]): Promise<number> {
|
|
109
|
+
const missing = readMissingFlags(argv, ['--package', '--version', '--tarball', '--dist'])
|
|
110
|
+
if (missing.length > 0) {
|
|
111
|
+
console.error(`compare: ${missing.join(', ')} given with no value`)
|
|
112
|
+
return 64
|
|
113
|
+
}
|
|
114
|
+
const manifest = readManifest()
|
|
115
|
+
const name = readOption(argv, '--package') ?? manifest.name
|
|
116
|
+
const dist = readOption(argv, '--dist') ?? 'dist'
|
|
117
|
+
const tarball = readOption(argv, '--tarball')
|
|
118
|
+
if (
|
|
119
|
+
name === undefined ||
|
|
120
|
+
!existsSync(dist) ||
|
|
121
|
+
(argv.includes('--tarball') && tarball === undefined)
|
|
122
|
+
) {
|
|
123
|
+
console.error(
|
|
124
|
+
'usage: compare.ts [--package NAME] [--version V] [--tarball PATH] [--dist dist] [--json]',
|
|
125
|
+
)
|
|
126
|
+
return 64
|
|
127
|
+
}
|
|
128
|
+
let version = readOption(argv, '--version')
|
|
129
|
+
let archive: Buffer
|
|
130
|
+
if (tarball !== undefined) {
|
|
131
|
+
if (!existsSync(tarball)) {
|
|
132
|
+
console.error(`compare: ${tarball} does not exist`)
|
|
133
|
+
return 2
|
|
134
|
+
}
|
|
135
|
+
archive = readFileSync(tarball)
|
|
136
|
+
} else {
|
|
137
|
+
version ??= readNpmValue(['view', name, 'version', '--json'])
|
|
138
|
+
const url =
|
|
139
|
+
version === undefined
|
|
140
|
+
? undefined
|
|
141
|
+
: readNpmValue(['view', `${name}@${version}`, 'dist.tarball', '--json'])
|
|
142
|
+
if (version === undefined || url === undefined) {
|
|
143
|
+
console.error(
|
|
144
|
+
`compare: the registry served no tarball for ${name}${version === undefined ? '' : `@${version}`}`,
|
|
145
|
+
)
|
|
146
|
+
return 2
|
|
147
|
+
}
|
|
148
|
+
const fetched = await fetchTarball(url)
|
|
149
|
+
if (fetched === undefined) {
|
|
150
|
+
console.error(`compare: fetching ${url} failed`)
|
|
151
|
+
return 2
|
|
152
|
+
}
|
|
153
|
+
archive = fetched
|
|
154
|
+
}
|
|
155
|
+
const published = new Map<string, Buffer>()
|
|
156
|
+
let entries: ReadonlyMap<string, Buffer>
|
|
157
|
+
try {
|
|
158
|
+
entries = readTar(gunzipSync(archive))
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.error(
|
|
161
|
+
`compare: the tarball could not be read: ${error instanceof Error ? error.message : String(error)}`,
|
|
162
|
+
)
|
|
163
|
+
return 2
|
|
164
|
+
}
|
|
165
|
+
for (const [path, content] of entries) {
|
|
166
|
+
const prefixed = `${PACKAGE_PREFIX}${dist}/`
|
|
167
|
+
if (path.startsWith(prefixed) && isMaterial(path))
|
|
168
|
+
published.set(path.slice(prefixed.length), content)
|
|
169
|
+
}
|
|
170
|
+
const local = new Map<string, Buffer>()
|
|
171
|
+
for (const { path } of listFiles(dist, NOTHING_SKIPPED)) {
|
|
172
|
+
const key = relative(dist, path).split('\\').join('/')
|
|
173
|
+
if (isMaterial(key)) local.set(key, readFileSync(path))
|
|
174
|
+
}
|
|
175
|
+
const added = [...local.keys()].filter((key) => !published.has(key)).sort()
|
|
176
|
+
const removed = [...published.keys()].filter((key) => !local.has(key)).sort()
|
|
177
|
+
const changed = [...local.keys()]
|
|
178
|
+
.filter((key) => {
|
|
179
|
+
const other = published.get(key)
|
|
180
|
+
const mine = local.get(key)
|
|
181
|
+
return other !== undefined && mine !== undefined && differs(mine, other)
|
|
182
|
+
})
|
|
183
|
+
.sort()
|
|
184
|
+
const material = added.length + removed.length + changed.length > 0
|
|
185
|
+
const summary = {
|
|
186
|
+
package: name,
|
|
187
|
+
version,
|
|
188
|
+
tarball,
|
|
189
|
+
dist,
|
|
190
|
+
compared: published.size,
|
|
191
|
+
added,
|
|
192
|
+
removed,
|
|
193
|
+
changed,
|
|
194
|
+
material,
|
|
195
|
+
}
|
|
196
|
+
if (argv.includes('--json')) console.log(JSON.stringify(summary))
|
|
197
|
+
else {
|
|
198
|
+
console.log(
|
|
199
|
+
`compare: ${name}${version === undefined ? '' : `@${version}`} — ${published.size} published file(s), ${added.length} added, ${removed.length} removed, ${changed.length} changed`,
|
|
200
|
+
)
|
|
201
|
+
for (const path of added) console.log(`compare: added ${path}`)
|
|
202
|
+
for (const path of removed) console.log(`compare: removed ${path}`)
|
|
203
|
+
for (const path of changed) console.log(`compare: changed ${path}`)
|
|
204
|
+
}
|
|
205
|
+
return material ? 3 : 0
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async function fetchTarball(url: string): Promise<Buffer | undefined> {
|
|
209
|
+
try {
|
|
210
|
+
const response = await fetch(url)
|
|
211
|
+
if (!response.ok) return undefined
|
|
212
|
+
return Buffer.from(await response.arrayBuffer())
|
|
213
|
+
} catch {
|
|
214
|
+
return undefined
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
main(process.argv.slice(2)).then((code) => {
|
|
219
|
+
process.exitCode = code
|
|
220
|
+
})
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// Sweep source and tests for a prior version or range literal after a bump or a re-pin. Run from
|
|
2
|
+
// the publishing package's root:
|
|
3
|
+
// node .agents/skills/orkestrel-publish/scripts/pins.ts --version 0.0.76 [--range ^0.0.16 ...] [--paths src,tests] [--json]
|
|
4
|
+
// `--range` repeats. A version hit is the literal bounded by characters other than digits and dots,
|
|
5
|
+
// so `~0.0.76` and `"0.0.76"` both hit and `0.0.760` does not. A range that is one comparator over
|
|
6
|
+
// one version (`^8.3.0`, `~8.3.0`, `>=8.3.0`, `v8.3.0`) hits its bare version in any form, bounded the
|
|
7
|
+
// same way, so `--range ^8.3.0` hits `~8.3.0` and `serves 8.3.0` and misses `^8.3.1`; any other
|
|
8
|
+
// range (`>=1.2.0 <2.0.0`, `8.x`) hits its literal bounded the same way and not preceded by another
|
|
9
|
+
// range operator. Each matching line prints once as path:line: text, naming the first requested
|
|
10
|
+
// literal that matched in the order --version then each --range, so the operator rules on it.
|
|
11
|
+
// Exit 0 with no hits, 3 with hits, 64 on usage.
|
|
12
|
+
import { readFileSync, statSync } from 'node:fs'
|
|
13
|
+
import {
|
|
14
|
+
listFiles,
|
|
15
|
+
readMissingFlags,
|
|
16
|
+
readOptions,
|
|
17
|
+
} from '../../orkestrel-dispatch/scripts/helpers.ts'
|
|
18
|
+
|
|
19
|
+
const BINARY = /\.(png|jpg|jpeg|gif|webp|ico|woff2?|ttf|otf|eot|zip|tgz|gz|pdf|wasm)$/u
|
|
20
|
+
const COMPARATOR =
|
|
21
|
+
/^(?:\^|~|>=|>|<=|<|=)?v?(\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?)$/u
|
|
22
|
+
|
|
23
|
+
interface Literal {
|
|
24
|
+
readonly literal: string
|
|
25
|
+
readonly pattern: RegExp
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface Hit {
|
|
29
|
+
readonly path: string
|
|
30
|
+
readonly line: number
|
|
31
|
+
readonly literal: string
|
|
32
|
+
readonly text: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function escapePattern(literal: string): string {
|
|
36
|
+
return literal.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function buildVersionPattern(version: string): RegExp {
|
|
40
|
+
return new RegExp(`(?<![0-9.])${escapePattern(version)}(?![0-9.])`, 'u')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function buildVersionLiteral(literal: string): Literal {
|
|
44
|
+
return { literal, pattern: buildVersionPattern(literal) }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function buildRangeLiteral(literal: string): Literal {
|
|
48
|
+
const version = COMPARATOR.exec(literal)?.[1]
|
|
49
|
+
if (version !== undefined) return { literal, pattern: buildVersionPattern(version) }
|
|
50
|
+
return {
|
|
51
|
+
literal,
|
|
52
|
+
pattern: new RegExp(`(?<![0-9.^~<>=])${escapePattern(literal)}(?![0-9.])`, 'u'),
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function sweepFile(path: string, literals: readonly Literal[]): readonly Hit[] {
|
|
57
|
+
const content = readFileSync(path)
|
|
58
|
+
if (content.includes(0)) return []
|
|
59
|
+
const hits: Hit[] = []
|
|
60
|
+
content
|
|
61
|
+
.toString('utf8')
|
|
62
|
+
.split(/\r\n|\n/)
|
|
63
|
+
.forEach((text, index) => {
|
|
64
|
+
const match = literals.find((entry) => entry.pattern.test(text))
|
|
65
|
+
if (match !== undefined) {
|
|
66
|
+
hits.push({ path, line: index + 1, literal: match.literal, text: text.trim() })
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
return hits
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function main(argv: readonly string[]): number {
|
|
73
|
+
const missing = readMissingFlags(argv, ['--version', '--range', '--paths'])
|
|
74
|
+
if (missing.length > 0) {
|
|
75
|
+
console.error(`pins: ${missing.join(', ')} given with no value`)
|
|
76
|
+
return 64
|
|
77
|
+
}
|
|
78
|
+
const version = readOptions(argv, '--version')[0]
|
|
79
|
+
const ranges = readOptions(argv, '--range')
|
|
80
|
+
const paths = (readOptions(argv, '--paths')[0] ?? 'src,tests').split(',')
|
|
81
|
+
if (version === undefined && ranges.length === 0) {
|
|
82
|
+
console.error(
|
|
83
|
+
'usage: pins.ts --version PRIOR [--range PRIOR_RANGE ...] [--paths src,tests] [--json]',
|
|
84
|
+
)
|
|
85
|
+
return 64
|
|
86
|
+
}
|
|
87
|
+
const literals = [
|
|
88
|
+
...(version === undefined ? [] : [buildVersionLiteral(version)]),
|
|
89
|
+
...ranges.map(buildRangeLiteral),
|
|
90
|
+
]
|
|
91
|
+
const present = paths.filter((path) => {
|
|
92
|
+
try {
|
|
93
|
+
return statSync(path).isDirectory()
|
|
94
|
+
} catch {
|
|
95
|
+
return false
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
const skipped = paths.filter((path) => !present.includes(path))
|
|
99
|
+
for (const path of skipped) console.error(`pins: ${path} is not a directory; skipped`)
|
|
100
|
+
const hits = present
|
|
101
|
+
.flatMap((directory) => listFiles(directory).map((file) => file.path))
|
|
102
|
+
.filter((file) => !BINARY.test(file))
|
|
103
|
+
.flatMap((file) => sweepFile(file, literals))
|
|
104
|
+
const names = literals.map((entry) => entry.literal)
|
|
105
|
+
if (argv.includes('--json')) {
|
|
106
|
+
console.log(JSON.stringify({ literals: names, paths: present, skipped, hits }))
|
|
107
|
+
} else {
|
|
108
|
+
for (const hit of hits) console.log(`${hit.path}:${hit.line}: ${hit.text}`)
|
|
109
|
+
console.log(`pins: ${hits.length} hit(s) for ${names.join(', ')} under ${present.join(', ')}`)
|
|
110
|
+
}
|
|
111
|
+
return hits.length === 0 ? 0 : 3
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
process.exitCode = main(process.argv.slice(2))
|