@orkestrel/scaffold 0.0.88 → 0.0.90
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-harden/scripts/discovery.js +481 -80
- package/dist/agents/skills/orkestrel-publish/scripts/window.js +32 -5
- package/dist/bin/main.js +4 -3
- package/dist/bin/main.js.map +1 -1
- package/dist/host/agents/skills/orkestrel-harden/SKILL.md +3 -3
- package/dist/host/agents/skills/orkestrel-harden/scripts/discovery.ts +594 -83
- package/dist/host/agents/skills/orkestrel-publish/SKILL.md +1 -1
- package/dist/host/agents/skills/orkestrel-publish/references/wave.md +2 -0
- package/dist/host/agents/skills/orkestrel-publish/references/window.md +2 -0
- package/dist/host/agents/skills/orkestrel-publish/scripts/window.ts +34 -5
- package/dist/host/claude/rules/tests.md +2 -0
- package/dist/host/claude/rules/workspace.md +1 -1
- package/dist/host/guides/scaffold.md +13 -3
- package/dist/host/manifest.json +11 -11
- package/dist/host/scripts/codex.sh +0 -0
- package/dist/host/scripts/cursor.sh +0 -0
- package/dist/host/scripts/deps.sh +0 -0
- package/dist/host/scripts/ollama.sh +0 -0
- package/dist/host/tests/config.test.ts +17 -1
- package/dist/src/core/index.cjs +131 -105
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +7 -7
- package/dist/src/core/index.d.ts +7 -7
- package/dist/src/core/index.js +131 -105
- package/dist/src/core/index.js.map +1 -1
- package/package.json +4 -3
package/dist/src/core/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { fillTemplate } from "@orkestrel/template";
|
|
|
3
3
|
import { Emitter } from "@orkestrel/emitter";
|
|
4
4
|
var package_default = {
|
|
5
5
|
name: "@orkestrel/scaffold",
|
|
6
|
-
version: "0.0.
|
|
6
|
+
version: "0.0.90",
|
|
7
7
|
description: "Scaffold workspaces with commands: new, audit, repair, catalog, and overwrite.",
|
|
8
8
|
keywords: [
|
|
9
9
|
"audit",
|
|
@@ -77,6 +77,7 @@ var package_default = {
|
|
|
77
77
|
"test:config": "vitest run --config vite.config.ts --no-cache --reporter=dot --project config",
|
|
78
78
|
"test:guides": "node --experimental-strip-types tests/guides.test.ts",
|
|
79
79
|
"test:skills": "vitest run --config vite.config.ts --no-cache --reporter=dot --project skills",
|
|
80
|
+
"test:templates": "vitest run --config vite.config.ts --no-cache --reporter=dot --project templates",
|
|
80
81
|
"test:distribution": "vitest run --config vite.config.ts --no-cache --reporter=dot --project distribution",
|
|
81
82
|
"test:probe": "vitest run --config vite.config.ts --no-cache --reporter=verbose --project probe",
|
|
82
83
|
"test:bench": "vitest bench --config vite.config.ts --no-cache --project probe",
|
|
@@ -90,7 +91,7 @@ var package_default = {
|
|
|
90
91
|
"build:inventory": "node -e \"import('./dist/src/server/index.js').then((m)=>{const p=process.argv[1]??'host.json',n=m.stageInventory(process.cwd(),p).entries.length;console.log('build-inventory: staged '+n+' file(s) into '+p)})\"",
|
|
91
92
|
"build:skills": "tsc -p configs/agents/tsconfig.skills.json --noEmit false --allowImportingTsExtensions false --rewriteRelativeImportExtensions --declaration false --sourceMap false --rootDir .agents/skills --outDir dist/agents/skills && npm run copy .agents/templates/brief.md dist/agents/templates/brief.md",
|
|
92
93
|
"prepack": "npm run build",
|
|
93
|
-
"prepublishOnly": "npm run format:check && npm run lint:check && npm run check && npm run build && npm test && npm run test:distribution -- --mode release"
|
|
94
|
+
"prepublishOnly": "npm run format:check && npm run lint:check && npm run check && npm run build && npm test && npm run test:templates && npm run test:distribution -- --mode release"
|
|
94
95
|
},
|
|
95
96
|
dependencies: {
|
|
96
97
|
"@orkestrel/console": "^0.0.15",
|
|
@@ -102,7 +103,7 @@ var package_default = {
|
|
|
102
103
|
},
|
|
103
104
|
devDependencies: {
|
|
104
105
|
"@microsoft/api-extractor": "^7.59.3",
|
|
105
|
-
"@orkestrel/browser": "^0.0.
|
|
106
|
+
"@orkestrel/browser": "^0.0.22",
|
|
106
107
|
"@orkestrel/guide": "^0.0.24",
|
|
107
108
|
"@orkestrel/html": "^0.0.12",
|
|
108
109
|
"@orkestrel/probe": "^0.0.19",
|
|
@@ -828,15 +829,29 @@ const resolve = {
|
|
|
828
829
|
// merges it, so an override's arrays elsewhere concatenate with the base's rather than
|
|
829
830
|
// replacing them.
|
|
830
831
|
export function mergeOverride(base: UserConfig, override?: UserConfig): UserConfig {
|
|
831
|
-
if (override
|
|
832
|
-
if ('command' in override && 'mode' in override) {
|
|
832
|
+
if (override !== undefined && 'command' in override && 'mode' in override) {
|
|
833
833
|
if (typeof override.mode !== 'string') {
|
|
834
834
|
throw new Error('The project invocation carries no string mode')
|
|
835
835
|
}
|
|
836
|
-
|
|
836
|
+
override = { mode: override.mode }
|
|
837
|
+
}
|
|
838
|
+
const merged: UserConfig = override === undefined ? { ...base } : mergeConfig(base, override)
|
|
839
|
+
const name = merged.test?.name
|
|
840
|
+
const label = typeof name === 'string' ? name : name?.label
|
|
841
|
+
const browser = merged.test?.browser
|
|
842
|
+
if (label !== undefined && browser?.instances !== undefined) {
|
|
843
|
+
merged.test = {
|
|
844
|
+
...merged.test,
|
|
845
|
+
browser: {
|
|
846
|
+
...browser,
|
|
847
|
+
instances: browser.instances.map((instance) => ({
|
|
848
|
+
...instance,
|
|
849
|
+
name: instance.name ?? \`\${label} (\${instance.browser})\`,
|
|
850
|
+
})),
|
|
851
|
+
},
|
|
852
|
+
}
|
|
837
853
|
}
|
|
838
|
-
|
|
839
|
-
if (merged.plugins === undefined) return merged
|
|
854
|
+
if (merged.plugins === undefined || override === undefined) return merged
|
|
840
855
|
const candidates = override.plugins ?? []
|
|
841
856
|
const taken = new Set<number>()
|
|
842
857
|
const selected: PluginOption[] = []
|
|
@@ -1162,6 +1177,12 @@ export function srcBin(override?: UserConfig): UserConfig {
|
|
|
1162
1177
|
test: {
|
|
1163
1178
|
...browser.test,
|
|
1164
1179
|
name: { label: 'app:vue', color: 'magenta' },
|
|
1180
|
+
browser: {
|
|
1181
|
+
...browser.test?.browser,
|
|
1182
|
+
instances: (browser.test?.browser?.instances ?? []).map(
|
|
1183
|
+
({ name: _name, ...instance }) => instance,
|
|
1184
|
+
),
|
|
1185
|
+
},
|
|
1165
1186
|
include: ['tests/app/vue/**/*.test.ts'],
|
|
1166
1187
|
{{journeyExclude}} },
|
|
1167
1188
|
}
|
|
@@ -1178,7 +1199,7 @@ export function appJourney(
|
|
|
1178
1199
|
): UserConfig {
|
|
1179
1200
|
const application = resolveApplication(mode, applications)
|
|
1180
1201
|
const browser = applications[application]()
|
|
1181
|
-
|
|
1202
|
+
const project: UserConfig = {
|
|
1182
1203
|
...browser,
|
|
1183
1204
|
test: {
|
|
1184
1205
|
...browser.test,
|
|
@@ -1190,9 +1211,13 @@ export function appJourney(
|
|
|
1190
1211
|
...browser.test?.browser,
|
|
1191
1212
|
enabled: true,
|
|
1192
1213
|
viewport: { width: variant.width, height: variant.height },
|
|
1214
|
+
instances: (browser.test?.browser?.instances ?? []).map(
|
|
1215
|
+
({ name: _name, ...instance }) => instance,
|
|
1216
|
+
),
|
|
1193
1217
|
},
|
|
1194
1218
|
},
|
|
1195
1219
|
}
|
|
1220
|
+
return mergeOverride(project)
|
|
1196
1221
|
}
|
|
1197
1222
|
`,
|
|
1198
1223
|
showcase: `
|
|
@@ -1479,12 +1504,12 @@ export function probe(override?: UserConfig): UserConfig {
|
|
|
1479
1504
|
`,
|
|
1480
1505
|
integration: Object.freeze({
|
|
1481
1506
|
sheet: `export function integration(override?: UserConfig): UserConfig {
|
|
1482
|
-
const project =
|
|
1507
|
+
const project: UserConfig = {
|
|
1483
1508
|
test: {
|
|
1484
1509
|
include: ['tests/integration.test.ts'],
|
|
1485
1510
|
{{global}} },
|
|
1486
|
-
}
|
|
1487
|
-
return mergeOverride(project, override)
|
|
1511
|
+
}
|
|
1512
|
+
return sheetProject('integration', mergeOverride(project, override))
|
|
1488
1513
|
}
|
|
1489
1514
|
`,
|
|
1490
1515
|
module: `export function integration(override?: UserConfig): UserConfig {
|
|
@@ -2554,7 +2579,7 @@ import {
|
|
|
2554
2579
|
import { tmpdir } from 'node:os'
|
|
2555
2580
|
import { dirname, join, resolve } from 'node:path'
|
|
2556
2581
|
import { fileURLToPath } from 'node:url'
|
|
2557
|
-
{{launcher}}import { afterAll, describe, expect, it } from 'vitest'
|
|
2582
|
+
{{launcher}}import { afterAll, beforeAll, describe, expect, it } from 'vitest'
|
|
2558
2583
|
|
|
2559
2584
|
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
|
2560
2585
|
const NPM = process.platform === 'win32' ? 'npm.cmd' : 'npm'
|
|
@@ -2770,7 +2795,7 @@ function runNpm(args: readonly string[], cwd: string): SpawnSyncReturns<string>
|
|
|
2770
2795
|
return spawnSync(NPM, [...args], {
|
|
2771
2796
|
cwd,
|
|
2772
2797
|
encoding: 'utf8',
|
|
2773
|
-
env: { ...process.env, npm_config_cache:
|
|
2798
|
+
env: { ...process.env, npm_config_cache: join(readScratch(), 'cache') },
|
|
2774
2799
|
shell: SHELL,
|
|
2775
2800
|
windowsHide: true,
|
|
2776
2801
|
})
|
|
@@ -3127,8 +3152,8 @@ function driveRuntime(stage: Stage, specifier: string, driver: string): readonly
|
|
|
3127
3152
|
// published surface back off the installed tree. Every later claim reads this
|
|
3128
3153
|
// result, so a failure here is raised where it happens rather than once per entry.
|
|
3129
3154
|
function buildStage(): Stage {
|
|
3130
|
-
const packed = join(
|
|
3131
|
-
const consumer = join(
|
|
3155
|
+
const packed = join(readScratch(), 'packed')
|
|
3156
|
+
const consumer = join(readScratch(), 'consumer')
|
|
3132
3157
|
mkdirSync(packed, { recursive: true })
|
|
3133
3158
|
const pack = runNpm(['pack', '--ignore-scripts', '--pack-destination', packed], ROOT)
|
|
3134
3159
|
if (pack.status !== 0) throw new Error(\`npm pack refused this workspace: \${readOutput(pack)}\`)
|
|
@@ -3202,21 +3227,31 @@ function buildStage(): Stage {
|
|
|
3202
3227
|
return { consumer, installed, archives, entries, subpaths, undeclared, excluded, targets }
|
|
3203
3228
|
}
|
|
3204
3229
|
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3230
|
+
let scratch: string | undefined
|
|
3231
|
+
let staged: Stage | undefined
|
|
3232
|
+
|
|
3233
|
+
function readScratch(): string {
|
|
3234
|
+
if (scratch === undefined) {
|
|
3235
|
+
throw new Error('The distribution setup has not allocated its scratch directory')
|
|
3236
|
+
}
|
|
3237
|
+
return scratch
|
|
3238
|
+
}
|
|
3239
|
+
|
|
3240
|
+
beforeAll(() => {
|
|
3241
|
+
scratch = mkdtempSync(join(tmpdir(), 'distribution-'))
|
|
3242
|
+
mkdirSync(join(scratch, 'cache'), { recursive: true })
|
|
3243
|
+
staged = openStage()
|
|
3244
|
+
})
|
|
3245
|
+
|
|
3210
3246
|
afterAll(() => {
|
|
3211
|
-
|
|
3247
|
+
if (scratch !== undefined) {
|
|
3248
|
+
rmSync(scratch, { force: true, recursive: true, maxRetries: 3, retryDelay: 100 })
|
|
3249
|
+
}
|
|
3212
3250
|
})
|
|
3213
3251
|
|
|
3214
3252
|
// Installing the packed archive resolves its own runtime dependencies, so an
|
|
3215
3253
|
// unreachable registry leaves nothing to measure. Under release that is the gate
|
|
3216
3254
|
// failing; anywhere else the suite skips and names the mechanism it wanted.
|
|
3217
|
-
//
|
|
3218
|
-
// A module that throws while loading never reaches the \`afterAll\` it registered,
|
|
3219
|
-
// so every throw here removes the scratch tree on its way out.
|
|
3220
3255
|
function openStage(): Stage | undefined {
|
|
3221
3256
|
try {
|
|
3222
3257
|
if (runNpm(PING, ROOT).status !== 0) {
|
|
@@ -3227,17 +3262,14 @@ function openStage(): Stage | undefined {
|
|
|
3227
3262
|
}
|
|
3228
3263
|
return buildStage()
|
|
3229
3264
|
} catch (error) {
|
|
3230
|
-
rmSync(
|
|
3265
|
+
rmSync(readScratch(), { force: true, recursive: true, maxRetries: 3, retryDelay: 100 })
|
|
3231
3266
|
throw error
|
|
3232
3267
|
}
|
|
3233
3268
|
}
|
|
3234
3269
|
|
|
3235
|
-
const STAGE = openStage()
|
|
3236
|
-
const STAGED = STAGE !== undefined
|
|
3237
|
-
|
|
3238
3270
|
describe('distribution classifiers', () => {
|
|
3239
3271
|
it('classifies synthetic export mappings without a registry stage', () => {
|
|
3240
|
-
const root = join(
|
|
3272
|
+
const root = join(readScratch(), 'classifiers')
|
|
3241
3273
|
writeFile(
|
|
3242
3274
|
join(root, 'package.json'),
|
|
3243
3275
|
JSON.stringify({
|
|
@@ -3306,10 +3338,10 @@ describe('distribution classifiers', () => {
|
|
|
3306
3338
|
// The staged consumer, or a skip naming what the run could not reach. \`it.skipIf\`
|
|
3307
3339
|
// carries no reason, so the gate sits here where the test context can state one.
|
|
3308
3340
|
function requireStage(context: TestContext): Stage {
|
|
3309
|
-
if (
|
|
3341
|
+
if (staged === undefined) {
|
|
3310
3342
|
return context.skip('\`npm ping\` did not answer, so nothing was packed or installed')
|
|
3311
3343
|
}
|
|
3312
|
-
return
|
|
3344
|
+
return staged
|
|
3313
3345
|
}
|
|
3314
3346
|
|
|
3315
3347
|
describe('installed package consumer', () => {
|
|
@@ -3395,50 +3427,43 @@ describe('installed package consumer', () => {
|
|
|
3395
3427
|
})
|
|
3396
3428
|
{{guard}}})
|
|
3397
3429
|
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
expect(reported).toStrictEqual([])
|
|
3417
|
-
},
|
|
3418
|
-
)
|
|
3430
|
+
describe('installed entries', () => {
|
|
3431
|
+
it('publishes what it declares to a Node import, and no more', (context) => {
|
|
3432
|
+
const stage = requireStage(context)
|
|
3433
|
+
const entries = stage.entries.filter((entry) => entry.importable)
|
|
3434
|
+
if (entries.length === 0) return context.skip('No installed entry supports Node import')
|
|
3435
|
+
for (const entry of entries) {
|
|
3436
|
+
if (!entry.declaration.importable) {
|
|
3437
|
+
throw new Error(\`\${entry.subpath} publishes no import declaration\`)
|
|
3438
|
+
}
|
|
3439
|
+
const published = driveRuntime(stage, entry.specifier, ESM_DRIVER)
|
|
3440
|
+
const drivers = selectDrivers(entry, 'module')
|
|
3441
|
+
expect(drivers).not.toStrictEqual([])
|
|
3442
|
+
const reported = drivers.flatMap((driver) =>
|
|
3443
|
+
checkSurface(stage, { entry, extension: 'ts', published, driver }),
|
|
3444
|
+
)
|
|
3445
|
+
expect(reported).toStrictEqual([])
|
|
3446
|
+
}
|
|
3447
|
+
})
|
|
3419
3448
|
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
}
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
},
|
|
3439
|
-
)
|
|
3440
|
-
{{drive}} })
|
|
3441
|
-
}
|
|
3449
|
+
it('publishes what it declares to a Node require, and no more', (context) => {
|
|
3450
|
+
const stage = requireStage(context)
|
|
3451
|
+
const entries = stage.entries.filter((entry) => entry.requirable)
|
|
3452
|
+
if (entries.length === 0) return context.skip('No installed entry supports Node require')
|
|
3453
|
+
for (const entry of entries) {
|
|
3454
|
+
if (!entry.declaration.requirable) {
|
|
3455
|
+
throw new Error(\`\${entry.subpath} publishes no require declaration\`)
|
|
3456
|
+
}
|
|
3457
|
+
const published = driveRuntime(stage, entry.specifier, CJS_DRIVER)
|
|
3458
|
+
const drivers = selectDrivers(entry, 'commonjs')
|
|
3459
|
+
expect(drivers).not.toStrictEqual([])
|
|
3460
|
+
const reported = drivers.flatMap((driver) =>
|
|
3461
|
+
checkSurface(stage, { entry, extension: 'cts', published, driver }),
|
|
3462
|
+
)
|
|
3463
|
+
expect(reported).toStrictEqual([])
|
|
3464
|
+
}
|
|
3465
|
+
})
|
|
3466
|
+
{{drive}}})
|
|
3442
3467
|
`,
|
|
3443
3468
|
transport: `import { createServer } from 'node:http'
|
|
3444
3469
|
`,
|
|
@@ -3547,36 +3572,37 @@ async function readBrowserExports(browser: Browser, bundle: string): Promise<rea
|
|
|
3547
3572
|
}
|
|
3548
3573
|
`,
|
|
3549
3574
|
drive: `
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
}
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3575
|
+
it('publishes what it declares to a real browser [requires a browser]', async (context) => {
|
|
3576
|
+
const stage = requireStage(context)
|
|
3577
|
+
const entries = stage.entries.filter((entry) => entry.browsable)
|
|
3578
|
+
if (entries.length === 0) return context.skip('No installed entry supports a browser')
|
|
3579
|
+
for (const entry of entries) {
|
|
3580
|
+
if (!entry.declaration.browsable) {
|
|
3581
|
+
throw new Error(\`\${entry.subpath} publishes no browser declaration\`)
|
|
3582
|
+
}
|
|
3583
|
+
const options = resolveBrowser(resolvePinnedBrowser(), process.platform, process.env)
|
|
3584
|
+
const browser = await launchBrowser(options).catch((error: unknown) => {
|
|
3585
|
+
const cause = \`\${describeBrowser(options)} was rejected: \${String(error)}\`
|
|
3586
|
+
if (RELEASE) throw new Error(\`The release gate requires a browser, and \${cause}\`)
|
|
3587
|
+
return context.skip(\`No browser launched. \${cause}\`)
|
|
3588
|
+
})
|
|
3589
|
+
try {
|
|
3590
|
+
const bundle = await bundleEntry(stage, entry)
|
|
3591
|
+
const published = await readBrowserExports(browser, bundle)
|
|
3592
|
+
// A browser consumer reads the installed declarations through a bundler, so
|
|
3593
|
+
// that is the one driver this face answers under.
|
|
3594
|
+
const reported = checkSurface(stage, {
|
|
3595
|
+
entry,
|
|
3596
|
+
extension: 'ts',
|
|
3597
|
+
published,
|
|
3598
|
+
driver: BROWSER_DRIVER,
|
|
3562
3599
|
})
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
entry,
|
|
3570
|
-
extension: 'ts',
|
|
3571
|
-
published,
|
|
3572
|
-
driver: BROWSER_DRIVER,
|
|
3573
|
-
})
|
|
3574
|
-
expect(reported).toStrictEqual([])
|
|
3575
|
-
} finally {
|
|
3576
|
-
await browser.close()
|
|
3577
|
-
}
|
|
3578
|
-
},
|
|
3579
|
-
)
|
|
3600
|
+
expect(reported).toStrictEqual([])
|
|
3601
|
+
} finally {
|
|
3602
|
+
await browser.close()
|
|
3603
|
+
}
|
|
3604
|
+
}
|
|
3605
|
+
})
|
|
3580
3606
|
`,
|
|
3581
3607
|
guard: `
|
|
3582
3608
|
// This proof drives a Node import and a Node require and carries no browser
|
|
@@ -3588,7 +3614,7 @@ async function readBrowserExports(browser: Browser, bundle: string): Promise<rea
|
|
|
3588
3614
|
// a published browser face. \`vite\` selects nothing either, though the branch
|
|
3589
3615
|
// imports it: scaffold puts \`vite\` in every workspace's base development
|
|
3590
3616
|
// dependencies, whatever that workspace publishes. The later Node
|
|
3591
|
-
//
|
|
3617
|
+
// entry filters retire each matching Node drive for a face published
|
|
3592
3618
|
// later, which leaves nothing measuring it. So it reddens here and names the
|
|
3593
3619
|
// subpath a browser branch is owed for. A workspace that gains one deletes this
|
|
3594
3620
|
// file and runs the \`repair\` verb, which writes the variant carrying that branch.
|