@orkestrel/scaffold 0.0.63 → 0.0.65

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 (57) hide show
  1. package/README.md +29 -104
  2. package/dist/bin/main.js +95 -27
  3. package/dist/bin/main.js.map +1 -1
  4. package/dist/host/AGENTS.md +2 -2
  5. package/dist/host/CLAUDE.md +6 -0
  6. package/dist/host/agents/orchestration.md +23 -15
  7. package/dist/host/agents/skills/enterprise-bootstrap/SKILL.md +184 -177
  8. package/dist/host/agents/skills/enterprise-bootstrap/references/bootstrap-reference.md +314 -91
  9. package/dist/host/agents/skills/enterprise-bootstrap/references/color-modes.md +241 -0
  10. package/dist/host/agents/skills/enterprise-bootstrap/references/components.md +83 -36
  11. package/dist/host/agents/skills/enterprise-bootstrap/references/frontend-design.md +297 -98
  12. package/dist/host/agents/skills/enterprise-bootstrap/references/inputs.md +25 -14
  13. package/dist/host/agents/skills/enterprise-bootstrap/references/inspection.md +216 -128
  14. package/dist/host/agents/skills/enterprise-bootstrap/references/responsive-layout.md +187 -0
  15. package/dist/host/agents/skills/enterprise-bootstrap/references/utilities.md +109 -20
  16. package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +5 -5
  17. package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +1 -1
  18. package/dist/host/agents/skills/orkestrel-publish/SKILL.md +15 -15
  19. package/dist/host/agents/skills/orkestrel-publish/references/wave.md +43 -17
  20. package/dist/host/agents/skills/orkestrel-publish/references/window.md +41 -16
  21. package/dist/host/claude/agents/orkestrel.md +56 -56
  22. package/dist/host/claude/agents/reviewer.md +13 -0
  23. package/dist/host/claude/rules/architecture.md +51 -45
  24. package/dist/host/claude/rules/documentation.md +18 -1
  25. package/dist/host/claude/rules/portability.md +2 -0
  26. package/dist/host/claude/rules/quality.md +1 -1
  27. package/dist/host/claude/rules/tests.md +12 -11
  28. package/dist/host/claude/rules/typescript.md +5 -0
  29. package/dist/host/claude/rules/workspace.md +25 -20
  30. package/dist/host/claude/rules/writing.md +4 -0
  31. package/dist/host/claude/settings.json +1 -1
  32. package/dist/host/claude/skills/enterprise-bootstrap/SKILL.md +10 -9
  33. package/dist/host/codex/agents/orkestrel.toml +3 -3
  34. package/dist/host/codex/agents/reviewer.toml +4 -2
  35. package/dist/host/configs/helpers.ts +311 -2
  36. package/dist/host/configs/policy.ts +1100 -51
  37. package/dist/host/dotfiles/oxlintrc.json +72 -1
  38. package/dist/host/guides/guide.md +749 -222
  39. package/dist/host/guides/scaffold.md +529 -394
  40. package/dist/host/manifest.json +53 -40
  41. package/dist/host/scripts/ollama.sh +322 -13
  42. package/dist/host/tests/config.test.ts +1200 -16
  43. package/dist/host/tests/policy.test.ts +157 -173
  44. package/dist/host/tests/setupPolicy.ts +522 -1007
  45. package/dist/src/core/index.cjs +402 -287
  46. package/dist/src/core/index.cjs.map +1 -1
  47. package/dist/src/core/index.d.cts +160 -128
  48. package/dist/src/core/index.d.ts +160 -128
  49. package/dist/src/core/index.js +400 -286
  50. package/dist/src/core/index.js.map +1 -1
  51. package/dist/src/server/index.cjs +28 -21
  52. package/dist/src/server/index.cjs.map +1 -1
  53. package/dist/src/server/index.d.cts +38 -33
  54. package/dist/src/server/index.d.ts +38 -33
  55. package/dist/src/server/index.js +28 -21
  56. package/dist/src/server/index.js.map +1 -1
  57. package/package.json +18 -19
@@ -1,19 +1,20 @@
1
1
  ---
2
2
  name: enterprise-bootstrap
3
3
  description: >-
4
- Design and build distinctive, production-grade user interfaces with Bootstrap
5
- 5.3 and intentional frontend craft, in any host project and on any stack. Use
6
- for Bootstrap user-interface work — creating, restyling, or
4
+ Design and build distinctive, production-grade UI with Bootstrap 5.3 on any
5
+ stack. Use for any Bootstrap interface work — creating, restyling, or
7
6
  extending pages, screens, components, layouts, app shells, dashboards, admin
8
7
  panels, SaaS tools, data tables, filter bars, forms, wizards, navigation,
9
8
  modals, empty/loading/error states, dark mode, marketing surfaces — whenever
10
9
  the task touches HTML/CSS/visual design, mentions Bootstrap or its components,
11
- or must look professional and avoid templated defaults. Covers aesthetics,
12
- typography, color modes, design tokens, accessibility (WCAG 2.2 AA),
13
- responsive layout, and enterprise app patterns. The `orkestrel-polish-surface`
14
- skill owns a requested verdict, round, or campaign over a surface that already
15
- renders, including a review that changes nothing. In that campaign's fix
16
- units, use this skill for Bootstrap craft.
10
+ asks for visual hierarchy, polish, a design system, or spacing/type/color
11
+ scales, or must look professional rather than like stock Bootstrap. Covers
12
+ aesthetics, typography, color modes, design tokens, elevation, finishing
13
+ details, accessibility (WCAG 2.2 AA), responsive layout, and enterprise app
14
+ patterns. The `orkestrel-polish-surface` skill owns a requested verdict,
15
+ round, or campaign over a surface that already renders, including a review
16
+ that changes nothing; in that campaign's fix units, use this skill for
17
+ Bootstrap craft.
17
18
  ---
18
19
 
19
20
  # Load the canonical workflow
@@ -1,5 +1,5 @@
1
1
  name = "orkestrel"
2
- description = "Read-only @orkestrel ecosystem reconciler: turns the evidence the dispatch supplies — manifests, lockfiles, installed declarations, guides, and registry readings — into package maps, drift audits, and dependency sequencing. Collects no live state itself."
2
+ description = "Read-only Orkestrel ecosystem reconciler: turns the evidence the dispatch supplies — manifests, lockfiles, installed declarations, guides, and registry readings — into package maps, dependency sequencing, blast radius, and drift findings. Collects no live state itself, and never treats the embedded catalog as live state."
3
3
  model = "gpt-5.6-terra"
4
4
  model_reasoning_effort = "medium"
5
5
  sandbox_mode = "read-only"
@@ -17,6 +17,6 @@ readings the dispatch carries, never against memory. Where the dispatch omits th
17
17
  evidence a question needs, report that question as unknown, name the reading that would
18
18
  settle it, and stop; live collection belongs to a tool-capable lane or to the
19
19
  Orchestrator before this role is dispatched. Never edit, install, push, or spawn.
20
- Return only the requested primed map, evidence-first health audit, or
21
- dependency blast-radius and topological work-order proposal.
20
+ Return exactly one requested shape: `Map`, `Health`, or `Work order`, as the Claude
21
+ charter defines them. The embedded catalog is discovery data, never live state.
22
22
  """
@@ -1,5 +1,5 @@
1
1
  name = "reviewer"
2
- description = "Codex-side driver for the Claude `reviewer` route — design-fit review of the actual diff. Prepares a journaled launch: returns the brief content, the target path, and the resolved CLI command for the Orchestrator to write and launch, and returns the Opus verdict labeled untrusted. Reviews nothing itself and endorses nothing."
2
+ description = "Codex-side driver for the Claude `reviewer` route — design-fit review of the actual diff, holding the subjective lane by default and the objective lane when the dispatch assigns it. Prepares a journaled launch: returns the brief content, the target path, and the resolved CLI command for the Orchestrator to write and launch, and returns the Opus verdict labeled untrusted. Reviews nothing itself and endorses nothing."
3
3
  model = "gpt-5.6-terra"
4
4
  model_reasoning_effort = "low"
5
5
  sandbox_mode = "read-only"
@@ -11,7 +11,9 @@ in full; read it and follow it.
11
11
  This route pins `--permission-mode plan`.
12
12
 
13
13
  The brief names the lane the route holds, and requires the returned verdict to state
14
- which lane it held. It requires the `orkestrel-falsify` verdict shape and its single
14
+ which lane it held. The Claude charter enumerates the lenses of each lane, and a
15
+ verdict returned under the objective lane holds those lenses in full. The brief
16
+ requires the `orkestrel-falsify` verdict shape and its single
15
17
  terminal line, unless the dispatch names a different skill that fixes one; file:line
16
18
  evidence on every required change; out-of-lane questions returned as referrals rather
17
19
  than verdicts; `UNRESOLVED` rather than `CONFIRMED` on a claim whose only evidence is
@@ -1,18 +1,24 @@
1
1
  import type { Plugin, Rolldown } from 'vite'
2
2
  import { parseSync, transformWithOxc, Visitor } from 'vite'
3
3
  import { fileURLToPath } from 'node:url'
4
- import { isBuiltin } from 'node:module'
4
+ import { spawnSync } from 'node:child_process'
5
+ import { createRequire, isBuiltin } from 'node:module'
6
+ import { tmpdir } from 'node:os'
5
7
  import {
6
8
  closeSync,
7
9
  constants as FS_CONSTANTS,
8
10
  existsSync,
9
11
  fstatSync,
10
12
  lstatSync,
13
+ mkdtempSync,
11
14
  openSync,
15
+ readFileSync,
12
16
  readSync,
13
17
  realpathSync,
18
+ rmSync,
19
+ writeFileSync,
14
20
  } from 'node:fs'
15
- import { dirname, isAbsolute, relative, resolve as resolvePath, sep } from 'node:path'
21
+ import { dirname, isAbsolute, join, relative, resolve as resolvePath, sep } from 'node:path'
16
22
 
17
23
  export function hasAsciiUrlControl(value: string): boolean {
18
24
  for (const character of value) {
@@ -435,6 +441,309 @@ export function outputBoundary(output: string): Plugin {
435
441
  }
436
442
  }
437
443
 
444
+ /**
445
+ * Describes the compiler scope a face resolves to, as its declaration emit and its roll-up both
446
+ * read it.
447
+ */
448
+ export interface ProjectScope {
449
+ readonly lib: readonly string[]
450
+ readonly types: readonly string[]
451
+ readonly root: string
452
+ }
453
+
454
+ /**
455
+ * Describes the `overrideTsconfig` a declaration roll-up hands the extractor, and every option it
456
+ * may read.
457
+ */
458
+ export interface ExtractorOverride {
459
+ readonly compilerOptions: {
460
+ readonly types: readonly string[]
461
+ readonly lib: readonly string[]
462
+ readonly target: string
463
+ readonly module: string
464
+ readonly moduleResolution: string
465
+ readonly skipLibCheck: boolean
466
+ readonly strict: boolean
467
+ }
468
+ readonly files: readonly string[]
469
+ }
470
+
471
+ /**
472
+ * Lists the extractor exports a declaration roll-up dereferences, named as that package publishes
473
+ * them.
474
+ */
475
+ export interface ExtractorModule {
476
+ readonly Extractor: { readonly invoke: (config: unknown, options: unknown) => unknown }
477
+ readonly ExtractorConfig: { readonly prepare: (options: unknown) => unknown }
478
+ }
479
+
480
+ /**
481
+ * Configures one published face's declaration roll-up.
482
+ *
483
+ * @remarks
484
+ * `project` is the absolute path of that face's TypeScript project file. `types` overrides the
485
+ * `types` the extractor's own program reads, and defaults to the face's resolved `types`. `rewrite`
486
+ * receives the finished roll-up and returns what the face ships; a face that omits it ships the
487
+ * roll-up as the extractor wrote it.
488
+ */
489
+ export interface DeclarationRollupOptions {
490
+ readonly project: string
491
+ readonly types?: readonly string[]
492
+ readonly rewrite?: (content: string) => string
493
+ }
494
+
495
+ /**
496
+ * Checks whether a value is a list of strings.
497
+ *
498
+ * @param value - The value to check.
499
+ * @returns True if the value is an array whose every member is a string; false otherwise.
500
+ */
501
+ export function isStringList(value: unknown): value is readonly string[] {
502
+ return Array.isArray(value) && value.every((entry: unknown) => typeof entry === 'string')
503
+ }
504
+
505
+ /**
506
+ * Checks whether a value exposes the extractor entry a declaration roll-up calls.
507
+ *
508
+ * @param value - The loaded extractor module.
509
+ * @returns True if the value carries the `Extractor.invoke` and `ExtractorConfig.prepare` entry
510
+ * points; false otherwise.
511
+ *
512
+ * @remarks
513
+ * The guard reads each member through `Reflect.get`. A loader can hand back a module object whose
514
+ * members are reachable only through its prototype, so an own-descriptor read misses them and
515
+ * refuses a module that carries `Extractor.invoke` and `ExtractorConfig.prepare`.
516
+ */
517
+ export function isExtractorModule(value: unknown): value is ExtractorModule {
518
+ if (typeof value !== 'object' || value === null) return false
519
+ const extractor: unknown = Reflect.get(value, 'Extractor')
520
+ const configuration: unknown = Reflect.get(value, 'ExtractorConfig')
521
+ if (typeof extractor !== 'function' || typeof configuration !== 'function') return false
522
+ return (
523
+ typeof Reflect.get(extractor, 'invoke') === 'function' &&
524
+ typeof Reflect.get(configuration, 'prepare') === 'function'
525
+ )
526
+ }
527
+
528
+ /**
529
+ * Returns the standard output of the workspace TypeScript compiler run as a process.
530
+ *
531
+ * @param compiler - The absolute path of the compiler's JavaScript entry.
532
+ * @param args - The compiler arguments that follow that entry.
533
+ * @returns The compiler's standard output.
534
+ * @throws An error carrying the compiler's own output when it fails or exits non-zero.
535
+ */
536
+ export function readCompilerOutput(compiler: string, args: readonly string[]): string {
537
+ const result = spawnSync(process.execPath, [compiler, ...args], {
538
+ cwd: WORKSPACE_ROOT,
539
+ encoding: 'utf8',
540
+ })
541
+ if (result.error !== undefined || result.status !== 0) {
542
+ throw new Error(
543
+ `[orkestrel-declaration-rollup] The declaration compiler failed:\n${result.stdout ?? ''}${result.stderr ?? ''}`,
544
+ )
545
+ }
546
+ return result.stdout ?? ''
547
+ }
548
+
549
+ /**
550
+ * Parses a `tsc --showConfig` reading into the compiler scope a face's roll-up requires.
551
+ *
552
+ * @param text - The compiler's `--showConfig` output.
553
+ * @param project - The absolute path of the project file that produced that output.
554
+ * @returns That scope with `root` resolved against the project file, or `undefined` when the
555
+ * project resolves no `lib`, no `types`, or no `rootDir`.
556
+ */
557
+ export function parseProjectScope(text: string, project: string): ProjectScope | undefined {
558
+ try {
559
+ const parsed: unknown = JSON.parse(text)
560
+ if (typeof parsed !== 'object' || parsed === null) return undefined
561
+ const options: unknown = Object.getOwnPropertyDescriptor(parsed, 'compilerOptions')?.value
562
+ if (typeof options !== 'object' || options === null) return undefined
563
+ const lib: unknown = Object.getOwnPropertyDescriptor(options, 'lib')?.value
564
+ const types: unknown = Object.getOwnPropertyDescriptor(options, 'types')?.value
565
+ const root: unknown = Object.getOwnPropertyDescriptor(options, 'rootDir')?.value
566
+ if (!isStringList(lib) || !isStringList(types) || typeof root !== 'string') return undefined
567
+ return { lib, types, root: resolvePath(dirname(project), root) }
568
+ } catch {
569
+ return undefined
570
+ }
571
+ }
572
+
573
+ /**
574
+ * Builds the `overrideTsconfig` the extractor analyses one emitted face entry under.
575
+ *
576
+ * @param entry - The absolute path of the emitted declaration entry.
577
+ * @param lib - The face's own resolved `lib`.
578
+ * @param types - The `types` the extractor's program reads.
579
+ * @returns That override.
580
+ *
581
+ * @remarks
582
+ * The extractor runs its own bundled compiler engine, so the override carries the face's resolved
583
+ * `lib` and `types` and nothing else the face resolved. Passing the face's full options, a `paths`
584
+ * table, or a `typescriptCompilerFolder` leaves that engine unable to follow a symbol.
585
+ */
586
+ export function buildExtractorOverride(
587
+ entry: string,
588
+ lib: readonly string[],
589
+ types: readonly string[],
590
+ ): ExtractorOverride {
591
+ return {
592
+ compilerOptions: {
593
+ types,
594
+ lib,
595
+ target: 'ESNext',
596
+ module: 'ESNext',
597
+ moduleResolution: 'bundler',
598
+ skipLibCheck: true,
599
+ strict: true,
600
+ },
601
+ files: [entry],
602
+ }
603
+ }
604
+
605
+ /**
606
+ * Rewrites every core specifier in a roll-up to the workspace package's published name.
607
+ *
608
+ * @param content - The finished roll-up.
609
+ * @returns That roll-up with each core specifier replaced.
610
+ * @throws An error when the workspace manifest names no package.
611
+ *
612
+ * @remarks
613
+ * A browser or server face reaches core through an `@src/core` alias or a relative core path, and
614
+ * the extractor keeps either external and writes it through unchanged. Neither spelling exists in
615
+ * the published tarball, so both become the package's own root export. A core face passes no
616
+ * `rewrite` at all, because that face's declarations quote both spellings as documentation.
617
+ */
618
+ export function rewriteCoreSpecifier(content: string): string {
619
+ const name = packageManifestName(WORKSPACE_ROOT)
620
+ if (name === undefined) {
621
+ throw new Error('[orkestrel-declaration-rollup] The workspace manifest names no package')
622
+ }
623
+ return content.replaceAll(/(?:\.\.\/)+core\/index\.[jt]s/g, name).replaceAll('@src/core', name)
624
+ }
625
+
626
+ /**
627
+ * Rolls one published face's declarations into the single file that face ships.
628
+ *
629
+ * @param options - The face's roll-up options.
630
+ * @returns The Vite plugin that performs that roll-up.
631
+ *
632
+ * @remarks
633
+ * At `closeBundle`, after Vite has written every format of the face, the plugin emits the face's
634
+ * declarations with the workspace compiler run as a process into a scratch directory made fresh
635
+ * under the host temporary directory, hands the emitted entry to the extractor's own engine,
636
+ * applies `rewrite`, and removes the scratch directory. Keeping the scratch outside the face's own
637
+ * output means its unconditional removal can never take a sibling the face already published, such
638
+ * as its own `declarations` folder. The extractor is a publishing workspace's development
639
+ * dependency, so this vendored leaf defers loading it to that hook: a workspace that publishes no
640
+ * library installs no extractor, never reaches the hook, and never resolves the package at build
641
+ * or at check.
642
+ */
643
+ export function declarationRollup(options: DeclarationRollupOptions): Plugin {
644
+ let output = WORKSPACE_ROOT
645
+ let source = ''
646
+ let build = false
647
+ return {
648
+ name: 'orkestrel-declaration-rollup',
649
+ configResolved(config) {
650
+ build = config.command === 'build'
651
+ output = resolvePath(config.root, config.build.outDir)
652
+ source =
653
+ config.build.lib !== false && typeof config.build.lib.entry === 'string'
654
+ ? config.build.lib.entry
655
+ : ''
656
+ },
657
+ closeBundle() {
658
+ if (!build) return
659
+ const declaration = source.replace(/\.tsx?$/, '.d.ts')
660
+ if (declaration === source) {
661
+ throw new Error(
662
+ '[orkestrel-declaration-rollup] The face must build one TypeScript library entry',
663
+ )
664
+ }
665
+ const load = createRequire(import.meta.url)
666
+ const compiler = load.resolve('typescript/bin/tsc')
667
+ const scope = parseProjectScope(
668
+ readCompilerOutput(compiler, ['--showConfig', '-p', options.project]),
669
+ options.project,
670
+ )
671
+ if (scope === undefined) {
672
+ throw new Error(
673
+ '[orkestrel-declaration-rollup] The face project must resolve its lib, types, and root',
674
+ )
675
+ }
676
+ const scratch = mkdtempSync(join(tmpdir(), 'orkestrel-declarations-'))
677
+ const rollup = resolvePath(output, 'index.d.ts')
678
+ try {
679
+ readCompilerOutput(compiler, [
680
+ '-p',
681
+ options.project,
682
+ '--declaration',
683
+ '--emitDeclarationOnly',
684
+ '--noEmit',
685
+ 'false',
686
+ '--outDir',
687
+ scratch,
688
+ ])
689
+ const entry = resolvePath(scratch, relative(scope.root, declaration))
690
+ // A literal `import()` of the extractor reddens `tsc` in a workspace that does not
691
+ // install it, and a variable specifier reddens `import/no-dynamic-require`, so the
692
+ // literal `createRequire` call is the form that clears every gate in an app-only
693
+ // workspace.
694
+ const loaded: unknown = load('@microsoft/api-extractor')
695
+ if (!isExtractorModule(loaded)) {
696
+ throw new Error(
697
+ '[orkestrel-declaration-rollup] The declaration extractor exposes no entry point',
698
+ )
699
+ }
700
+ const prepared: unknown = loaded.ExtractorConfig.prepare({
701
+ configObject: {
702
+ projectFolder: WORKSPACE_ROOT,
703
+ mainEntryPointFilePath: entry,
704
+ bundledPackages: [],
705
+ compiler: {
706
+ overrideTsconfig: buildExtractorOverride(
707
+ entry,
708
+ scope.lib,
709
+ options.types ?? scope.types,
710
+ ),
711
+ },
712
+ apiReport: { enabled: false },
713
+ docModel: { enabled: false },
714
+ tsdocMetadata: { enabled: false },
715
+ dtsRollup: { enabled: true, untrimmedFilePath: rollup },
716
+ messages: {
717
+ compilerMessageReporting: { default: { logLevel: 'none' } },
718
+ extractorMessageReporting: { default: { logLevel: 'none' } },
719
+ tsdocMessageReporting: { default: { logLevel: 'none' } },
720
+ },
721
+ },
722
+ configObjectFullPath: resolvePath(WORKSPACE_ROOT, 'api-extractor.json'),
723
+ packageJsonFullPath: resolvePath(WORKSPACE_ROOT, 'package.json'),
724
+ })
725
+ const outcome: unknown = loaded.Extractor.invoke(prepared, {
726
+ localBuild: true,
727
+ showVerboseMessages: false,
728
+ showDiagnostics: false,
729
+ })
730
+ const succeeded: unknown =
731
+ typeof outcome === 'object' && outcome !== null
732
+ ? Reflect.get(outcome, 'succeeded')
733
+ : undefined
734
+ if (succeeded !== true) {
735
+ throw new Error('[orkestrel-declaration-rollup] The declaration roll-up failed')
736
+ }
737
+ if (options.rewrite !== undefined) {
738
+ writeFileSync(rollup, options.rewrite(readFileSync(rollup, 'utf8')), 'utf8')
739
+ }
740
+ } finally {
741
+ rmSync(scratch, { recursive: true, force: true })
742
+ }
743
+ },
744
+ }
745
+ }
746
+
438
747
  export function decodeAssetSource(source: string): string | undefined {
439
748
  try {
440
749
  return decodeURI(source)