@orkestrel/scaffold 0.0.21 → 0.0.23

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 (68) hide show
  1. package/dist/bin/scaffold.js +371 -140
  2. package/dist/bin/scaffold.js.map +1 -1
  3. package/dist/host/AGENTS.md +61 -42
  4. package/dist/host/CLAUDE.md +39 -408
  5. package/dist/host/agents/orchestration.md +454 -0
  6. package/dist/host/agents/skills/enterprise-bootstrap/SKILL.md +25 -7
  7. package/dist/host/agents/skills/orkestrel-align-packages/SKILL.md +2 -2
  8. package/dist/host/agents/skills/orkestrel-build-application/SKILL.md +42 -31
  9. package/dist/host/agents/skills/orkestrel-build-application/references/application.md +129 -0
  10. package/dist/host/agents/skills/orkestrel-debrief/SKILL.md +3 -3
  11. package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +2 -2
  12. package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +167 -0
  13. package/dist/host/agents/skills/orkestrel-falsify/references/brief.md +98 -0
  14. package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +148 -0
  15. package/dist/host/agents/skills/orkestrel-harden-package/SKILL.md +4 -2
  16. package/dist/host/agents/skills/orkestrel-harden-package/references/centralization.md +5 -5
  17. package/dist/host/agents/skills/orkestrel-harden-package/references/research.md +2 -0
  18. package/dist/host/agents/skills/orkestrel-polish-surface/SKILL.md +11 -6
  19. package/dist/host/claude/agents/analyst.md +59 -0
  20. package/dist/host/claude/agents/application.md +1 -1
  21. package/dist/host/claude/agents/builder.md +1 -1
  22. package/dist/host/claude/agents/checker.md +5 -1
  23. package/dist/host/claude/agents/codex.md +120 -94
  24. package/dist/host/claude/agents/grok.md +44 -32
  25. package/dist/host/claude/agents/implementer.md +1 -1
  26. package/dist/host/claude/agents/orkestrel.md +5 -5
  27. package/dist/host/claude/agents/planner.md +9 -3
  28. package/dist/host/claude/agents/researcher.md +6 -5
  29. package/dist/host/claude/agents/reviewer.md +12 -6
  30. package/dist/host/claude/agents/scout.md +7 -4
  31. package/dist/host/claude/agents/verifier.md +1 -1
  32. package/dist/host/claude/rules/application.md +17 -16
  33. package/dist/host/claude/rules/architecture.md +41 -22
  34. package/dist/host/claude/rules/documentation.md +2 -1
  35. package/dist/host/claude/rules/names.md +7 -6
  36. package/dist/host/claude/rules/quality.md +65 -18
  37. package/dist/host/claude/rules/styles.md +1 -1
  38. package/dist/host/claude/rules/tests.md +12 -6
  39. package/dist/host/claude/rules/typescript.md +1 -1
  40. package/dist/host/claude/rules/workspace.md +6 -3
  41. package/dist/host/claude/settings.json +2 -0
  42. package/dist/host/claude/skills/orkestrel-falsify/SKILL.md +10 -0
  43. package/dist/host/codex/agents/analyst.toml +13 -4
  44. package/dist/host/codex/agents/checker.toml +1 -1
  45. package/dist/host/codex/agents/grok.toml +2 -1
  46. package/dist/host/codex/agents/opus.toml +1 -1
  47. package/dist/host/codex/agents/planner.toml +1 -1
  48. package/dist/host/codex/agents/researcher.toml +2 -2
  49. package/dist/host/codex/agents/reviewer.toml +1 -1
  50. package/dist/host/codex/agents/scout.toml +2 -2
  51. package/dist/host/codex/config.toml +23 -70
  52. package/dist/host/cursor/rules/orchestration.mdc +33 -0
  53. package/dist/host/guides/src/scaffold.md +339 -125
  54. package/dist/host/manifest.json +47 -1
  55. package/dist/host/tests/setupPolicy.ts +210 -3
  56. package/dist/src/core/index.cjs +477 -69
  57. package/dist/src/core/index.cjs.map +1 -1
  58. package/dist/src/core/index.d.cts +132 -43
  59. package/dist/src/core/index.d.ts +132 -43
  60. package/dist/src/core/index.js +474 -70
  61. package/dist/src/core/index.js.map +1 -1
  62. package/dist/src/server/index.cjs +83 -33
  63. package/dist/src/server/index.cjs.map +1 -1
  64. package/dist/src/server/index.d.cts +43 -27
  65. package/dist/src/server/index.d.ts +43 -27
  66. package/dist/src/server/index.js +84 -35
  67. package/dist/src/server/index.js.map +1 -1
  68. package/package.json +4 -4
@@ -1,5 +1,10 @@
1
1
  {
2
2
  "entries": [
3
+ {
4
+ "storage": "agents/orchestration.md",
5
+ "destination": ".agents/orchestration.md",
6
+ "executable": false
7
+ },
3
8
  {
4
9
  "storage": "agents/skills/enterprise-bootstrap/SKILL.md",
5
10
  "destination": ".agents/skills/enterprise-bootstrap/SKILL.md",
@@ -60,6 +65,11 @@
60
65
  "destination": ".agents/skills/orkestrel-build-application/agents/openai.yaml",
61
66
  "executable": false
62
67
  },
68
+ {
69
+ "storage": "agents/skills/orkestrel-build-application/references/application.md",
70
+ "destination": ".agents/skills/orkestrel-build-application/references/application.md",
71
+ "executable": false
72
+ },
63
73
  {
64
74
  "storage": "agents/skills/orkestrel-debrief/SKILL.md",
65
75
  "destination": ".agents/skills/orkestrel-debrief/SKILL.md",
@@ -80,6 +90,21 @@
80
90
  "destination": ".agents/skills/orkestrel-debrief/references/instruction-audit.md",
81
91
  "executable": false
82
92
  },
93
+ {
94
+ "storage": "agents/skills/orkestrel-falsify/SKILL.md",
95
+ "destination": ".agents/skills/orkestrel-falsify/SKILL.md",
96
+ "executable": false
97
+ },
98
+ {
99
+ "storage": "agents/skills/orkestrel-falsify/references/brief.md",
100
+ "destination": ".agents/skills/orkestrel-falsify/references/brief.md",
101
+ "executable": false
102
+ },
103
+ {
104
+ "storage": "agents/skills/orkestrel-falsify/references/reconcile.md",
105
+ "destination": ".agents/skills/orkestrel-falsify/references/reconcile.md",
106
+ "executable": false
107
+ },
83
108
  {
84
109
  "storage": "agents/skills/orkestrel-harden-package/SKILL.md",
85
110
  "destination": ".agents/skills/orkestrel-harden-package/SKILL.md",
@@ -125,6 +150,11 @@
125
150
  "destination": ".agents/skills/orkestrel-polish-surface/references/capture-harness.md",
126
151
  "executable": false
127
152
  },
153
+ {
154
+ "storage": "claude/agents/analyst.md",
155
+ "destination": ".claude/agents/analyst.md",
156
+ "executable": false
157
+ },
128
158
  {
129
159
  "storage": "claude/agents/application.md",
130
160
  "destination": ".claude/agents/application.md",
@@ -265,6 +295,11 @@
265
295
  "destination": ".claude/skills/orkestrel-debrief/SKILL.md",
266
296
  "executable": false
267
297
  },
298
+ {
299
+ "storage": "claude/skills/orkestrel-falsify/SKILL.md",
300
+ "destination": ".claude/skills/orkestrel-falsify/SKILL.md",
301
+ "executable": false
302
+ },
268
303
  {
269
304
  "storage": "claude/skills/orkestrel-harden-package/SKILL.md",
270
305
  "destination": ".claude/skills/orkestrel-harden-package/SKILL.md",
@@ -350,6 +385,11 @@
350
385
  "destination": ".cursor/mcp.json",
351
386
  "executable": false
352
387
  },
388
+ {
389
+ "storage": "cursor/rules/orchestration.mdc",
390
+ "destination": ".cursor/rules/orchestration.mdc",
391
+ "executable": false
392
+ },
353
393
  {
354
394
  "storage": "dotfiles/editorconfig",
355
395
  "destination": ".editorconfig",
@@ -452,9 +492,12 @@
452
492
  ".agents/skills/orkestrel-align-packages/references",
453
493
  ".agents/skills/orkestrel-build-application",
454
494
  ".agents/skills/orkestrel-build-application/agents",
495
+ ".agents/skills/orkestrel-build-application/references",
455
496
  ".agents/skills/orkestrel-debrief",
456
497
  ".agents/skills/orkestrel-debrief/agents",
457
498
  ".agents/skills/orkestrel-debrief/references",
499
+ ".agents/skills/orkestrel-falsify",
500
+ ".agents/skills/orkestrel-falsify/references",
458
501
  ".agents/skills/orkestrel-harden-package",
459
502
  ".agents/skills/orkestrel-harden-package/agents",
460
503
  ".agents/skills/orkestrel-harden-package/references",
@@ -469,14 +512,17 @@
469
512
  ".claude/skills/orkestrel-align-packages",
470
513
  ".claude/skills/orkestrel-build-application",
471
514
  ".claude/skills/orkestrel-debrief",
515
+ ".claude/skills/orkestrel-falsify",
472
516
  ".claude/skills/orkestrel-harden-package",
473
517
  ".claude/skills/orkestrel-polish-surface",
474
518
  ".codex",
475
519
  ".codex/agents",
476
520
  ".cursor",
521
+ ".cursor/rules",
477
522
  "guides",
478
523
  "guides/src",
479
524
  "scripts",
480
525
  "tests"
481
- ]
526
+ ],
527
+ "digest": "e4351919e4d34e3696ee3ce77e2cce555c20acc58a29649cd9f3c489f3d0bf2b"
482
528
  }
@@ -1,6 +1,6 @@
1
1
  import { globSync, readFileSync } from 'node:fs'
2
2
  import { isBuiltin } from 'node:module'
3
- import { basename, join } from 'node:path'
3
+ import { basename, extname, join } from 'node:path'
4
4
  import * as ts from 'typescript'
5
5
 
6
6
  /** Centralized source modules whose top-level declarations must all be exported. */
@@ -60,6 +60,9 @@ export const DATA_SOURCE_FILES: readonly string[] = Object.freeze([
60
60
  'validators.ts',
61
61
  ])
62
62
 
63
+ /** Domain folders whose modules each export one named function rather than one class. */
64
+ export const FUNCTION_DOMAIN_FOLDERS: readonly string[] = Object.freeze(['app/browser/composables'])
65
+
63
66
  /** Worker-only value globals that WebWorker typing must not expose to core implementations. */
64
67
  export const WORKER_SCOPE_VALUE_GLOBALS: readonly string[] = Object.freeze([
65
68
  'name',
@@ -109,6 +112,32 @@ export const CODING_SOURCE_EXTENSIONS: readonly string[] = Object.freeze([
109
112
  /** Production-source glob derived from the complete inspected extension vocabulary. */
110
113
  export const CODING_SOURCE_GLOB = `{app,src}/**/*.{${CODING_SOURCE_EXTENSIONS.join(',')}}`
111
114
 
115
+ /** Fleet-owned policy files that must stay free of any one package's architecture. */
116
+ export const POLICY_INFRASTRUCTURE_FILES: readonly string[] = Object.freeze([
117
+ 'tests/policy.test.ts',
118
+ 'tests/setupPolicy.ts',
119
+ ])
120
+
121
+ /** Source environments whose prefixed paths fleet policy must not name in a literal. */
122
+ export const POLICY_SOURCE_ENVIRONMENTS: readonly string[] = Object.freeze([
123
+ 'browser',
124
+ 'core',
125
+ 'server',
126
+ ])
127
+
128
+ /**
129
+ * The prefixed source-environment path fleet policy must not name in a literal.
130
+ *
131
+ * @remarks
132
+ * Built rather than written, so this file states no matching literal of its own
133
+ * and can never report itself. Policy names an environment without the source
134
+ * prefix when it must name one at all, which is why the prefix is the rule.
135
+ */
136
+ export const POLICY_ENVIRONMENT_PATTERN: RegExp = new RegExp(
137
+ `src/(?:${POLICY_SOURCE_ENVIRONMENTS.join('|')})/`,
138
+ 'u',
139
+ )
140
+
112
141
  /** Virtual source text used while binding one policy-inspected module. */
113
142
  export const POLICY_SOURCE_TEXTS: Map<string, string> = new Map()
114
143
 
@@ -139,6 +168,28 @@ export function isCodingSourcePath(path: string): boolean {
139
168
  )
140
169
  }
141
170
 
171
+ /**
172
+ * Whether a path is an eligible function-domain module.
173
+ *
174
+ * @param path - The workspace-relative source path to inspect
175
+ * @returns `true` when the path is a direct module of a registered function domain
176
+ */
177
+ export function isFunctionDomainPath(path: string): boolean {
178
+ const normalized = normalizePolicyPath(path)
179
+ const file = basename(normalized)
180
+ const separator = normalized.lastIndexOf('/')
181
+ const parent = separator < 0 ? '' : normalized.slice(0, separator)
182
+ return (
183
+ FUNCTION_DOMAIN_FOLDERS.includes(parent) &&
184
+ /^[a-z][A-Za-z0-9]*\.ts$/u.test(file) &&
185
+ file !== 'index.ts' &&
186
+ file !== 'main.ts' &&
187
+ !CENTRAL_SOURCE_FILES.includes(file) &&
188
+ !FUNCTION_SOURCE_FILES.includes(file) &&
189
+ !DATA_SOURCE_FILES.includes(file)
190
+ )
191
+ }
192
+
142
193
  /** Whether a declaration carries an explicit export modifier. */
143
194
  export function hasExportModifier(node: ts.Node): boolean {
144
195
  return (
@@ -458,7 +509,16 @@ export function inspectCodingSource(
458
509
  if (!normalizedPath.startsWith('app/browser/')) {
459
510
  violations.push(`${normalizedPath} Vue components belong in app/browser`)
460
511
  }
461
- violations.push(...inspectVueCodingLaw(normalizedPath, vueScripts?.(normalizedPath, content)))
512
+ // A missing extractor is reported, never absorbed. Returning [] here would make
513
+ // every SFC's script block silently unchecked while the surrounding sweep still
514
+ // reported success — an instrument claiming a coverage it does not have.
515
+ if (vueScripts === undefined) {
516
+ violations.push(
517
+ `${normalizedPath} requires a Vue script extractor; its script blocks were not inspected`,
518
+ )
519
+ return violations
520
+ }
521
+ violations.push(...inspectVueCodingLaw(normalizedPath, vueScripts(normalizedPath, content)))
462
522
  return violations
463
523
  }
464
524
 
@@ -536,6 +596,34 @@ export function inspectCodingNode(
536
596
  ts.forEachChild(node, (child) => inspectCodingNode(path, child, violations, checker))
537
597
  }
538
598
 
599
+ /**
600
+ * Inspect one eligible function-domain module for its required declaration shape.
601
+ *
602
+ * @param path - The source path used in diagnostics
603
+ * @param source - The parsed source file to inspect
604
+ * @returns A shape violation when the module is not imports plus one matching named export
605
+ */
606
+ export function inspectFunctionModule(path: string, source: ts.SourceFile): readonly string[] {
607
+ const file = basename(normalizePolicyPath(path))
608
+ const declarations = source.statements.filter(ts.isFunctionDeclaration)
609
+ const functions = declarations.filter((declaration) => declaration.body !== undefined)
610
+ const invalid = source.statements.filter(
611
+ (statement) => !ts.isImportDeclaration(statement) && !ts.isFunctionDeclaration(statement),
612
+ )
613
+ const declaration = functions[0]
614
+ if (
615
+ functions.length === 1 &&
616
+ invalid.length === 0 &&
617
+ declarations.every((candidate) => candidate.name?.text === file.slice(0, -3)) &&
618
+ declaration !== undefined &&
619
+ hasExportModifier(declaration) &&
620
+ !hasModifier(declaration, ts.SyntaxKind.DefaultKeyword)
621
+ ) {
622
+ return []
623
+ }
624
+ return [`${path} declarations do not form one matching exported function implementation`]
625
+ }
626
+
539
627
  /** Inspect one TypeScript source module for repository coding-law violations. */
540
628
  export function inspectCodingLaw(path: string, content: string): readonly string[] {
541
629
  const violations: string[] = []
@@ -544,6 +632,8 @@ export function inspectCodingLaw(path: string, content: string): readonly string
544
632
  if (source === undefined) throw new Error(`Policy source was not bound at ${path}`)
545
633
  const checker = program.getTypeChecker()
546
634
  const file = basename(path)
635
+ const stem = basename(file, extname(file))
636
+ const functionModule = isFunctionDomainPath(path)
547
637
  const placementExempt =
548
638
  !CENTRAL_SOURCE_FILES.includes(file) &&
549
639
  !FUNCTION_SOURCE_FILES.includes(file) &&
@@ -553,6 +643,9 @@ export function inspectCodingLaw(path: string, content: string): readonly string
553
643
  if (/\.[cm]?jsx?$/u.test(path)) {
554
644
  violations.push(`${path} production modules use TypeScript source extensions`)
555
645
  }
646
+ if (FUNCTION_DOMAIN_FOLDERS.some((folder) => basename(folder) === stem)) {
647
+ violations.push(`${path} names a function domain, which belongs in a folder rather than a file`)
648
+ }
556
649
  if (/@ts-(?:expect-error|ignore|nocheck)|eslint-disable|oxlint-disable/u.test(content)) {
557
650
  violations.push(`${path} forbids suppression directives`)
558
651
  }
@@ -598,6 +691,7 @@ export function inspectCodingLaw(path: string, content: string): readonly string
598
691
  }
599
692
  if (
600
693
  !placementExempt &&
694
+ !functionModule &&
601
695
  ts.isFunctionDeclaration(statement) &&
602
696
  !FUNCTION_SOURCE_FILES.includes(file)
603
697
  ) {
@@ -605,6 +699,7 @@ export function inspectCodingLaw(path: string, content: string): readonly string
605
699
  }
606
700
  if (
607
701
  !placementExempt &&
702
+ !functionModule &&
608
703
  ts.isVariableStatement(statement) &&
609
704
  !DATA_SOURCE_FILES.includes(file)
610
705
  ) {
@@ -646,6 +741,10 @@ export function inspectCodingLaw(path: string, content: string): readonly string
646
741
  }
647
742
  }
648
743
 
744
+ if (functionModule) {
745
+ violations.push(...inspectFunctionModule(path, source))
746
+ }
747
+
649
748
  if (/^[A-Z][A-Za-z0-9]*\.ts$/u.test(file)) {
650
749
  const classes = source.statements.filter(ts.isClassDeclaration)
651
750
  const invalid = source.statements.filter(
@@ -673,7 +772,14 @@ export function inspectCodingLaw(path: string, content: string): readonly string
673
772
  return violations
674
773
  }
675
774
 
676
- /** Inspect every production source under one workspace. */
775
+ /**
776
+ * Inspect every production source under one workspace.
777
+ *
778
+ * @remarks
779
+ * `vueScripts` is required whenever the workspace contains a `.vue` file: script
780
+ * blocks cannot be read without it, and omitting it is reported as a violation
781
+ * against each SFC rather than passing silently.
782
+ */
677
783
  export function inspectCodingWorkspace(
678
784
  root: string,
679
785
  vueScripts?: VueScriptExtractorInterface,
@@ -687,3 +793,104 @@ export function inspectCodingWorkspace(
687
793
  }
688
794
  return violations
689
795
  }
796
+
797
+ /**
798
+ * Read one workspace's declared package name.
799
+ *
800
+ * @param root - The workspace root holding the package manifest
801
+ * @returns The declared package name
802
+ */
803
+ export function readPackageName(root: string): string {
804
+ const manifest: unknown = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'))
805
+ const name =
806
+ typeof manifest === 'object' && manifest !== null && 'name' in manifest
807
+ ? manifest.name
808
+ : undefined
809
+ if (typeof name !== 'string') throw new Error(`Package manifest at ${root} declares no name`)
810
+ return name
811
+ }
812
+
813
+ /**
814
+ * Derive the identifier prefixes fleet policy must not use from a package name.
815
+ *
816
+ * @param name - The declared package name, scoped or bare
817
+ * @returns The short name's upper-snake and Pascal spellings, deduped
818
+ *
819
+ * @example
820
+ * ```ts
821
+ * derivePolicyTokens('@orkestrel/my-router') // ['MY_ROUTER', 'MyRouter']
822
+ * ```
823
+ */
824
+ export function derivePolicyTokens(name: string): readonly string[] {
825
+ const short = name.slice(name.lastIndexOf('/') + 1)
826
+ const words = short.split(/[^A-Za-z0-9]+/u).filter((word) => word.length > 0)
827
+ const upper = words.map((word) => word.toUpperCase()).join('_')
828
+ const pascal = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join('')
829
+ return [...new Set([upper, pascal])].filter((token) => token.length > 0)
830
+ }
831
+
832
+ /**
833
+ * Add package-architecture violations while traversing one fleet policy source tree.
834
+ *
835
+ * @remarks
836
+ * An identifier carries a package's architecture when it is named for that package,
837
+ * so it must begin with the token. A word that merely holds the token somewhere
838
+ * inside it belongs to the fleet's own vocabulary and is left alone.
839
+ */
840
+ export function inspectPolicyNode(
841
+ path: string,
842
+ node: ts.Node,
843
+ violations: string[],
844
+ tokens: readonly string[],
845
+ ): void {
846
+ if (ts.isIdentifier(node)) {
847
+ const token = tokens.find((candidate) => node.text.startsWith(candidate))
848
+ if (token !== undefined) {
849
+ violations.push(`${path}:${formatPolicyPosition(node)} forbids the ${token} package token`)
850
+ }
851
+ }
852
+ if (
853
+ (ts.isStringLiteral(node) || ts.isTemplateLiteralToken(node)) &&
854
+ POLICY_ENVIRONMENT_PATTERN.test(node.text)
855
+ ) {
856
+ violations.push(
857
+ `${path}:${formatPolicyPosition(node)} forbids a source-environment path literal`,
858
+ )
859
+ }
860
+ ts.forEachChild(node, (child) => inspectPolicyNode(path, child, violations, tokens))
861
+ }
862
+
863
+ /**
864
+ * Inspect one fleet policy module for a single package's architecture.
865
+ *
866
+ * @param path - The workspace-relative source path used in diagnostics
867
+ * @param content - The TypeScript source text to inspect
868
+ * @param tokens - The package identifier tokens no identifier may begin with
869
+ * @returns Every violation, in source-position order
870
+ */
871
+ export function inspectPolicyPurity(
872
+ path: string,
873
+ content: string,
874
+ tokens: readonly string[],
875
+ ): readonly string[] {
876
+ const violations: string[] = []
877
+ const source = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true)
878
+ inspectPolicyNode(path, source, violations, tokens)
879
+ return violations
880
+ }
881
+
882
+ /**
883
+ * Inspect every fleet policy module under one workspace.
884
+ *
885
+ * @param root - The workspace root whose manifest names the consuming package
886
+ * @returns Every violation across the fleet policy files, in file and position order
887
+ */
888
+ export function inspectPolicyWorkspace(root: string): readonly string[] {
889
+ const tokens = derivePolicyTokens(readPackageName(root))
890
+ const violations: string[] = []
891
+ for (const path of POLICY_INFRASTRUCTURE_FILES) {
892
+ const content = readFileSync(join(root, path), 'utf8')
893
+ violations.push(...inspectPolicyPurity(path, content, tokens))
894
+ }
895
+ return violations
896
+ }