@orkestrel/scaffold 0.0.48 → 0.0.49
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/bin/main.js +271 -53
- package/dist/bin/main.js.map +1 -1
- package/dist/host/agents/orchestration.md +7 -0
- package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +4 -0
- package/dist/host/claude/rules/quality.md +4 -1
- package/dist/host/guides/scaffold.md +150 -65
- package/dist/host/manifest.json +217 -109
- package/dist/host/tests/config.test.ts +103 -1
- package/dist/src/core/index.cjs +32 -4
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +65 -8
- package/dist/src/core/index.d.ts +65 -8
- package/dist/src/core/index.js +31 -5
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +769 -137
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +681 -376
- package/dist/src/server/index.d.ts +681 -376
- package/dist/src/server/index.js +766 -141
- package/dist/src/server/index.js.map +1 -1
- package/package.json +3 -2
|
@@ -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'
|
|
@@ -513,6 +513,108 @@ describe('root configuration', () => {
|
|
|
513
513
|
)
|
|
514
514
|
})
|
|
515
515
|
|
|
516
|
+
it('keeps the committed host inventory aligned with the vendored checkout bytes', async () => {
|
|
517
|
+
// Run this gate against a quiescent checkout. It compares two reads and cannot
|
|
518
|
+
// distinguish stale committed data from a source edit made while it runs.
|
|
519
|
+
const packageValue: unknown = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8'))
|
|
520
|
+
if (typeof packageValue !== 'object' || packageValue === null) {
|
|
521
|
+
throw new Error('The package manifest is not a record')
|
|
522
|
+
}
|
|
523
|
+
const scripts: unknown = Object.getOwnPropertyDescriptor(packageValue, 'scripts')?.value
|
|
524
|
+
if (typeof scripts !== 'object' || scripts === null) {
|
|
525
|
+
throw new Error('The package manifest carries no scripts')
|
|
526
|
+
}
|
|
527
|
+
const generator: unknown = Object.getOwnPropertyDescriptor(scripts, 'build:inventory')?.value
|
|
528
|
+
expect(generator === undefined || typeof generator === 'string').toBe(true)
|
|
529
|
+
if (generator === undefined) {
|
|
530
|
+
if (existsSync(resolve(root, 'host.json'))) {
|
|
531
|
+
throw new Error('A committed host inventory exists without a generator')
|
|
532
|
+
}
|
|
533
|
+
return
|
|
534
|
+
}
|
|
535
|
+
if (typeof generator !== 'string') {
|
|
536
|
+
throw new Error('The host inventory generator is not a script')
|
|
537
|
+
}
|
|
538
|
+
const committed = resolve(root, 'host.json')
|
|
539
|
+
if (!existsSync(committed)) {
|
|
540
|
+
throw new Error('The committed host inventory is absent at host.json')
|
|
541
|
+
}
|
|
542
|
+
const helper = resolve(root, 'src/server/helpers.ts')
|
|
543
|
+
if (!existsSync(helper)) {
|
|
544
|
+
throw new Error('The committed host inventory has no server stager')
|
|
545
|
+
}
|
|
546
|
+
const server = await createServer({
|
|
547
|
+
configFile: false,
|
|
548
|
+
root,
|
|
549
|
+
...(configuration.resolve === undefined ? {} : { resolve: configuration.resolve }),
|
|
550
|
+
server: { middlewareMode: true },
|
|
551
|
+
})
|
|
552
|
+
try {
|
|
553
|
+
const loaded: unknown = await server.ssrLoadModule(helper)
|
|
554
|
+
if (typeof loaded !== 'object' || loaded === null) {
|
|
555
|
+
throw new Error('The server helper module did not load')
|
|
556
|
+
}
|
|
557
|
+
const stage: unknown = Reflect.get(loaded, 'stageInventory')
|
|
558
|
+
if (typeof stage !== 'function') {
|
|
559
|
+
throw new Error('The server helper module exports no stageInventory function')
|
|
560
|
+
}
|
|
561
|
+
const workspace = mkdtempSync(join(root, 'host-inventory-'))
|
|
562
|
+
try {
|
|
563
|
+
const generated = join(workspace, 'host.json')
|
|
564
|
+
Reflect.apply(stage, undefined, [root, generated])
|
|
565
|
+
const generatedText = readFileSync(generated, 'utf8')
|
|
566
|
+
const committedText = readFileSync(committed, 'utf8')
|
|
567
|
+
const values: readonly unknown[] = [JSON.parse(generatedText), JSON.parse(committedText)]
|
|
568
|
+
const indexes: Array<Map<string, string>> = []
|
|
569
|
+
for (const value of values) {
|
|
570
|
+
if (typeof value !== 'object' || value === null) {
|
|
571
|
+
throw new Error('A host inventory is not a record')
|
|
572
|
+
}
|
|
573
|
+
const entries: unknown = Object.getOwnPropertyDescriptor(value, 'entries')?.value
|
|
574
|
+
if (!Array.isArray(entries)) throw new Error('A host inventory carries no entry list')
|
|
575
|
+
const index = new Map<string, string>()
|
|
576
|
+
for (const entry of entries) {
|
|
577
|
+
if (typeof entry !== 'object' || entry === null) {
|
|
578
|
+
throw new Error('A host inventory carries a malformed entry')
|
|
579
|
+
}
|
|
580
|
+
const destination: unknown = Object.getOwnPropertyDescriptor(
|
|
581
|
+
entry,
|
|
582
|
+
'destination',
|
|
583
|
+
)?.value
|
|
584
|
+
const digest: unknown = Object.getOwnPropertyDescriptor(entry, 'digest')?.value
|
|
585
|
+
if (typeof destination !== 'string' || typeof digest !== 'string') {
|
|
586
|
+
throw new Error('A host inventory entry carries no destination digest')
|
|
587
|
+
}
|
|
588
|
+
index.set(destination, digest)
|
|
589
|
+
}
|
|
590
|
+
indexes.push(index)
|
|
591
|
+
}
|
|
592
|
+
const generatedIndex = indexes[0]
|
|
593
|
+
const committedIndex = indexes[1]
|
|
594
|
+
if (generatedIndex === undefined || committedIndex === undefined) {
|
|
595
|
+
throw new Error('The host inventories were not indexed')
|
|
596
|
+
}
|
|
597
|
+
if (generatedText !== committedText) {
|
|
598
|
+
const stale: string[] = []
|
|
599
|
+
for (const [destination, digest] of generatedIndex) {
|
|
600
|
+
if (committedIndex.get(destination) !== digest) stale.push(destination)
|
|
601
|
+
}
|
|
602
|
+
for (const destination of committedIndex.keys()) {
|
|
603
|
+
if (!generatedIndex.has(destination)) stale.push(destination)
|
|
604
|
+
}
|
|
605
|
+
throw new Error(
|
|
606
|
+
`The committed host inventory is stale at ${stale.length > 0 ? stale.sort().join(', ') : 'host.json'}`,
|
|
607
|
+
)
|
|
608
|
+
}
|
|
609
|
+
console.info(`host-inventory: entries=${generatedIndex.size}`)
|
|
610
|
+
} finally {
|
|
611
|
+
rmSync(workspace, { recursive: true, force: true })
|
|
612
|
+
}
|
|
613
|
+
} finally {
|
|
614
|
+
await server.close()
|
|
615
|
+
}
|
|
616
|
+
})
|
|
617
|
+
|
|
516
618
|
it('keeps policy rules active across every linted workspace path', () => {
|
|
517
619
|
const parsed: unknown = JSON.parse(readFileSync(resolve(root, '.oxlintrc.json'), 'utf8'))
|
|
518
620
|
expect(inspectPolicyConfiguration(parsed)).toEqual([])
|
package/dist/src/core/index.cjs
CHANGED
|
@@ -4,7 +4,7 @@ let _orkestrel_template = require("@orkestrel/template");
|
|
|
4
4
|
let _orkestrel_emitter = require("@orkestrel/emitter");
|
|
5
5
|
var package_default = {
|
|
6
6
|
name: "@orkestrel/scaffold",
|
|
7
|
-
version: "0.0.
|
|
7
|
+
version: "0.0.49",
|
|
8
8
|
description: "Scaffold workspaces with five commands: new, audit, repair, catalog, and overwrite.",
|
|
9
9
|
keywords: [
|
|
10
10
|
"audit",
|
|
@@ -84,6 +84,7 @@ var package_default = {
|
|
|
84
84
|
"build:src:server": "vite build --config configs/src/vite.server.config.ts && npm run copy dist/src/server/index.d.ts dist/src/server/index.d.cts",
|
|
85
85
|
"build:src:bin": "vite build --config configs/src/vite.bin.config.ts",
|
|
86
86
|
"build:host": "node -e \"import('./dist/src/server/index.js').then((m)=>{const n=m.stageHost(process.cwd(),'dist/host').length;console.log('build-host: staged '+n+' file(s) into dist/host')})\"",
|
|
87
|
+
"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)})\"",
|
|
87
88
|
"prepack": "npm run build",
|
|
88
89
|
"prepublishOnly": "npm run format:check && npm run lint:check && npm run check && npm run build && npm test && npm run test:distribution -- --mode release"
|
|
89
90
|
},
|
|
@@ -100,7 +101,7 @@ var package_default = {
|
|
|
100
101
|
"@orkestrel/guide": "^0.0.12",
|
|
101
102
|
"@orkestrel/html": "^0.0.4",
|
|
102
103
|
"@orkestrel/probe": "^0.0.2",
|
|
103
|
-
"@orkestrel/test": "^0.0.
|
|
104
|
+
"@orkestrel/test": "^0.0.10",
|
|
104
105
|
"@types/node": "^26.2.0",
|
|
105
106
|
"@vitest/browser-playwright": "^4.1.11",
|
|
106
107
|
"oxfmt": "^0.64.0",
|
|
@@ -252,6 +253,8 @@ var HOST_PATHS = Object.freeze([
|
|
|
252
253
|
"guides/guide.md",
|
|
253
254
|
"guides/scaffold.md"
|
|
254
255
|
]);
|
|
256
|
+
/** The repository-relative path where the committed vendored-file inventory is served. */
|
|
257
|
+
var HOST_INVENTORY_PATH = "host.json";
|
|
255
258
|
/**
|
|
256
259
|
* The vendored paths whose present bytes belong to each workspace, frozen.
|
|
257
260
|
*
|
|
@@ -2062,7 +2065,7 @@ var isMirror = (0, _orkestrel_contract.unionOf)((0, _orkestrel_contract.recordOf
|
|
|
2062
2065
|
}, ["observed"]), (0, _orkestrel_contract.recordOf)({
|
|
2063
2066
|
name: isDependencyName,
|
|
2064
2067
|
path: isPath,
|
|
2065
|
-
lookup: (0, _orkestrel_contract.literalOf)("missing", "failed"),
|
|
2068
|
+
lookup: (0, _orkestrel_contract.literalOf)("missing", "unmatched", "failed"),
|
|
2066
2069
|
note: _orkestrel_contract.isString,
|
|
2067
2070
|
observed: isHex
|
|
2068
2071
|
}, ["observed"]));
|
|
@@ -2080,7 +2083,7 @@ var isCatalogEntry = (0, _orkestrel_contract.unionOf)((0, _orkestrel_contract.re
|
|
|
2080
2083
|
dependencies: (0, _orkestrel_contract.andOf)(isCollection, (0, _orkestrel_contract.arrayOf)(isDependency))
|
|
2081
2084
|
}), (0, _orkestrel_contract.recordOf)({
|
|
2082
2085
|
name: isDependencyName,
|
|
2083
|
-
lookup: (0, _orkestrel_contract.literalOf)("missing", "failed"),
|
|
2086
|
+
lookup: (0, _orkestrel_contract.literalOf)("missing", "unmatched", "failed"),
|
|
2084
2087
|
note: _orkestrel_contract.isString
|
|
2085
2088
|
}));
|
|
2086
2089
|
/**
|
|
@@ -2411,6 +2414,29 @@ function matchesOrchestrationPath(path) {
|
|
|
2411
2414
|
return ORCHESTRATION_PATH_PREFIXES.some((prefix) => path.startsWith(prefix));
|
|
2412
2415
|
}
|
|
2413
2416
|
/**
|
|
2417
|
+
* Checks whether another surface owns the vendored bytes at a path.
|
|
2418
|
+
*
|
|
2419
|
+
* @param path - The target-relative vendored path to test.
|
|
2420
|
+
* @returns `true` for the catalog agent file and for a Markdown guide mirror;
|
|
2421
|
+
* `false` otherwise.
|
|
2422
|
+
*
|
|
2423
|
+
* @remarks
|
|
2424
|
+
* The materializer keeps these paths presence-owned because the catalog or
|
|
2425
|
+
* mirror surface writes their bytes. A live host fill therefore keeps the
|
|
2426
|
+
* installed floor's bytes for them and requests only the host-owned paths.
|
|
2427
|
+
*
|
|
2428
|
+
* @example
|
|
2429
|
+
* ```ts
|
|
2430
|
+
* import { isDeferredPath } from '@orkestrel/scaffold'
|
|
2431
|
+
*
|
|
2432
|
+
* isDeferredPath('guides/router.md') // true
|
|
2433
|
+
* isDeferredPath('AGENTS.md') // false
|
|
2434
|
+
* ```
|
|
2435
|
+
*/
|
|
2436
|
+
function isDeferredPath(path) {
|
|
2437
|
+
return path === ".claude/agents/orkestrel.md" || path.startsWith("guides/") && path.endsWith(".md");
|
|
2438
|
+
}
|
|
2439
|
+
/**
|
|
2414
2440
|
* Infer the {@link Group} a path belongs to.
|
|
2415
2441
|
*
|
|
2416
2442
|
* @param path - The target-relative path to classify.
|
|
@@ -5263,6 +5289,7 @@ exports.GLOBAL_SETUP_PATH = GLOBAL_SETUP_PATH;
|
|
|
5263
5289
|
exports.GROUPS = GROUPS;
|
|
5264
5290
|
exports.GUIDES_TEST_PATH = GUIDES_TEST_PATH;
|
|
5265
5291
|
exports.HEX_PATTERN = HEX_PATTERN;
|
|
5292
|
+
exports.HOST_INVENTORY_PATH = HOST_INVENTORY_PATH;
|
|
5266
5293
|
exports.HOST_PATHS = HOST_PATHS;
|
|
5267
5294
|
exports.INTEGRATION_TEST_PATH = INTEGRATION_TEST_PATH;
|
|
5268
5295
|
exports.INVALID_PATH_CHARACTER_PATTERN = INVALID_PATH_CHARACTER_PATTERN;
|
|
@@ -5333,6 +5360,7 @@ exports.isCollection = isCollection;
|
|
|
5333
5360
|
exports.isCompilerHooks = isCompilerHooks;
|
|
5334
5361
|
exports.isCompilerOptions = isCompilerOptions;
|
|
5335
5362
|
exports.isContent = isContent;
|
|
5363
|
+
exports.isDeferredPath = isDeferredPath;
|
|
5336
5364
|
exports.isDependency = isDependency;
|
|
5337
5365
|
exports.isDependencyName = isDependencyName;
|
|
5338
5366
|
exports.isEnvironment = isEnvironment;
|