@orkestrel/scaffold 0.0.48 → 0.0.50
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 +4 -2
- package/dist/bin/main.js +427 -97
- package/dist/bin/main.js.map +1 -1
- package/dist/host/agents/orchestration.md +35 -3
- package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +4 -0
- package/dist/host/claude/agents/orkestrel.md +44 -44
- package/dist/host/claude/rules/quality.md +4 -1
- package/dist/host/claude/rules/tests.md +6 -2
- package/dist/host/guides/scaffold.md +478 -161
- package/dist/host/manifest.json +217 -109
- 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 +129 -6
- package/dist/src/core/index.cjs +1315 -82
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +265 -44
- package/dist/src/core/index.d.ts +265 -44
- package/dist/src/core/index.js +1308 -83
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +816 -152
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +723 -391
- package/dist/src/server/index.d.ts +723 -391
- package/dist/src/server/index.js +812 -156
- package/dist/src/server/index.js.map +1 -1
- package/package.json +10 -9
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
import { createRequire } from 'node:module'
|
|
16
16
|
import { dirname, join, resolve } from 'node:path'
|
|
17
17
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
18
|
-
import { build, loadConfigFromFile } from 'vite'
|
|
18
|
+
import { build, createServer, loadConfigFromFile } from 'vite'
|
|
19
19
|
import { RuleTester } from 'oxlint/plugins-dev'
|
|
20
20
|
import * as configHelpers from '../configs/helpers.js'
|
|
21
21
|
import { MOCKING_RULE, NESTED_RULE, PRIVACY_RULE } from '../configs/policy.js'
|
|
@@ -152,11 +152,10 @@ describe('root configuration', () => {
|
|
|
152
152
|
pool: 'threads',
|
|
153
153
|
setup: ['./tests/setup.ts'],
|
|
154
154
|
})
|
|
155
|
-
// A row that is a configuration rather than a factory.
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
// wherever it runs instead of only where the shape happens to occur.
|
|
155
|
+
// A row that is a configuration rather than a factory. Every generated
|
|
156
|
+
// workspace registers the factory itself, so this shape is required here
|
|
157
|
+
// rather than observed: the proof exercises that resolution wherever it runs
|
|
158
|
+
// instead of only where a hand-written configuration happens to produce it.
|
|
160
159
|
expected.set('concrete', { include: 'tests/concrete.test.ts', setup: ['./tests/setup.ts'] })
|
|
161
160
|
|
|
162
161
|
const projects = configuration.test?.projects
|
|
@@ -297,6 +296,28 @@ describe('root configuration', () => {
|
|
|
297
296
|
}).toThrow(/strictly equal/u)
|
|
298
297
|
})
|
|
299
298
|
|
|
299
|
+
it('emits every project as a factory so the release mode reaches its proof', () => {
|
|
300
|
+
const projects = configuration.test?.projects
|
|
301
|
+
if (!Array.isArray(projects)) throw new Error('The root configuration carries no projects')
|
|
302
|
+
if (projects.length === 0) throw new Error('The root configuration registers no project')
|
|
303
|
+
// Measured: with `--mode release` on the command line, `import.meta.env.MODE` reads
|
|
304
|
+
// `release` inside a project Vitest calls and `test` inside an inline project
|
|
305
|
+
// configuration. `prepublishOnly` runs the distribution proof with `--mode release`, and
|
|
306
|
+
// that proof fails rather than skips only when it reads `release`, so converting these
|
|
307
|
+
// entries to inline configurations turns the publish gate into a skip while every suite
|
|
308
|
+
// stays green. The control is that conversion applied to one entry.
|
|
309
|
+
const inline = {
|
|
310
|
+
test: {
|
|
311
|
+
name: { label: 'inline' },
|
|
312
|
+
include: ['tests/inline.test.ts'],
|
|
313
|
+
setupFiles: ['./tests/setup.ts'],
|
|
314
|
+
},
|
|
315
|
+
}
|
|
316
|
+
const callable = projects.concat(inline).filter((entry) => typeof entry === 'function')
|
|
317
|
+
for (const entry of projects) expect(callable).toContain(entry)
|
|
318
|
+
expect(callable).not.toContain(inline)
|
|
319
|
+
})
|
|
320
|
+
|
|
300
321
|
it('requires and validates every selected target wrapper', async () => {
|
|
301
322
|
const required: string[] = []
|
|
302
323
|
for (const axis of ['src', 'app']) {
|
|
@@ -513,6 +534,108 @@ describe('root configuration', () => {
|
|
|
513
534
|
)
|
|
514
535
|
})
|
|
515
536
|
|
|
537
|
+
it('keeps the committed host inventory aligned with the vendored checkout bytes', async () => {
|
|
538
|
+
// Run this gate against a quiescent checkout. It compares two reads and cannot
|
|
539
|
+
// distinguish stale committed data from a source edit made while it runs.
|
|
540
|
+
const packageValue: unknown = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8'))
|
|
541
|
+
if (typeof packageValue !== 'object' || packageValue === null) {
|
|
542
|
+
throw new Error('The package manifest is not a record')
|
|
543
|
+
}
|
|
544
|
+
const scripts: unknown = Object.getOwnPropertyDescriptor(packageValue, 'scripts')?.value
|
|
545
|
+
if (typeof scripts !== 'object' || scripts === null) {
|
|
546
|
+
throw new Error('The package manifest carries no scripts')
|
|
547
|
+
}
|
|
548
|
+
const generator: unknown = Object.getOwnPropertyDescriptor(scripts, 'build:inventory')?.value
|
|
549
|
+
expect(generator === undefined || typeof generator === 'string').toBe(true)
|
|
550
|
+
if (generator === undefined) {
|
|
551
|
+
if (existsSync(resolve(root, 'host.json'))) {
|
|
552
|
+
throw new Error('A committed host inventory exists without a generator')
|
|
553
|
+
}
|
|
554
|
+
return
|
|
555
|
+
}
|
|
556
|
+
if (typeof generator !== 'string') {
|
|
557
|
+
throw new Error('The host inventory generator is not a script')
|
|
558
|
+
}
|
|
559
|
+
const committed = resolve(root, 'host.json')
|
|
560
|
+
if (!existsSync(committed)) {
|
|
561
|
+
throw new Error('The committed host inventory is absent at host.json')
|
|
562
|
+
}
|
|
563
|
+
const helper = resolve(root, 'src/server/helpers.ts')
|
|
564
|
+
if (!existsSync(helper)) {
|
|
565
|
+
throw new Error('The committed host inventory has no server stager')
|
|
566
|
+
}
|
|
567
|
+
const server = await createServer({
|
|
568
|
+
configFile: false,
|
|
569
|
+
root,
|
|
570
|
+
...(configuration.resolve === undefined ? {} : { resolve: configuration.resolve }),
|
|
571
|
+
server: { middlewareMode: true },
|
|
572
|
+
})
|
|
573
|
+
try {
|
|
574
|
+
const loaded: unknown = await server.ssrLoadModule(helper)
|
|
575
|
+
if (typeof loaded !== 'object' || loaded === null) {
|
|
576
|
+
throw new Error('The server helper module did not load')
|
|
577
|
+
}
|
|
578
|
+
const stage: unknown = Reflect.get(loaded, 'stageInventory')
|
|
579
|
+
if (typeof stage !== 'function') {
|
|
580
|
+
throw new Error('The server helper module exports no stageInventory function')
|
|
581
|
+
}
|
|
582
|
+
const workspace = mkdtempSync(join(root, 'host-inventory-'))
|
|
583
|
+
try {
|
|
584
|
+
const generated = join(workspace, 'host.json')
|
|
585
|
+
Reflect.apply(stage, undefined, [root, generated])
|
|
586
|
+
const generatedText = readFileSync(generated, 'utf8')
|
|
587
|
+
const committedText = readFileSync(committed, 'utf8')
|
|
588
|
+
const values: readonly unknown[] = [JSON.parse(generatedText), JSON.parse(committedText)]
|
|
589
|
+
const indexes: Array<Map<string, string>> = []
|
|
590
|
+
for (const value of values) {
|
|
591
|
+
if (typeof value !== 'object' || value === null) {
|
|
592
|
+
throw new Error('A host inventory is not a record')
|
|
593
|
+
}
|
|
594
|
+
const entries: unknown = Object.getOwnPropertyDescriptor(value, 'entries')?.value
|
|
595
|
+
if (!Array.isArray(entries)) throw new Error('A host inventory carries no entry list')
|
|
596
|
+
const index = new Map<string, string>()
|
|
597
|
+
for (const entry of entries) {
|
|
598
|
+
if (typeof entry !== 'object' || entry === null) {
|
|
599
|
+
throw new Error('A host inventory carries a malformed entry')
|
|
600
|
+
}
|
|
601
|
+
const destination: unknown = Object.getOwnPropertyDescriptor(
|
|
602
|
+
entry,
|
|
603
|
+
'destination',
|
|
604
|
+
)?.value
|
|
605
|
+
const digest: unknown = Object.getOwnPropertyDescriptor(entry, 'digest')?.value
|
|
606
|
+
if (typeof destination !== 'string' || typeof digest !== 'string') {
|
|
607
|
+
throw new Error('A host inventory entry carries no destination digest')
|
|
608
|
+
}
|
|
609
|
+
index.set(destination, digest)
|
|
610
|
+
}
|
|
611
|
+
indexes.push(index)
|
|
612
|
+
}
|
|
613
|
+
const generatedIndex = indexes[0]
|
|
614
|
+
const committedIndex = indexes[1]
|
|
615
|
+
if (generatedIndex === undefined || committedIndex === undefined) {
|
|
616
|
+
throw new Error('The host inventories were not indexed')
|
|
617
|
+
}
|
|
618
|
+
if (generatedText !== committedText) {
|
|
619
|
+
const stale: string[] = []
|
|
620
|
+
for (const [destination, digest] of generatedIndex) {
|
|
621
|
+
if (committedIndex.get(destination) !== digest) stale.push(destination)
|
|
622
|
+
}
|
|
623
|
+
for (const destination of committedIndex.keys()) {
|
|
624
|
+
if (!generatedIndex.has(destination)) stale.push(destination)
|
|
625
|
+
}
|
|
626
|
+
throw new Error(
|
|
627
|
+
`The committed host inventory is stale at ${stale.length > 0 ? stale.sort().join(', ') : 'host.json'}`,
|
|
628
|
+
)
|
|
629
|
+
}
|
|
630
|
+
console.info(`host-inventory: entries=${generatedIndex.size}`)
|
|
631
|
+
} finally {
|
|
632
|
+
rmSync(workspace, { recursive: true, force: true })
|
|
633
|
+
}
|
|
634
|
+
} finally {
|
|
635
|
+
await server.close()
|
|
636
|
+
}
|
|
637
|
+
})
|
|
638
|
+
|
|
516
639
|
it('keeps policy rules active across every linted workspace path', () => {
|
|
517
640
|
const parsed: unknown = JSON.parse(readFileSync(resolve(root, '.oxlintrc.json'), 'utf8'))
|
|
518
641
|
expect(inspectPolicyConfiguration(parsed)).toEqual([])
|