@orkestrel/scaffold 0.0.2 → 0.0.4

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 (82) hide show
  1. package/README.md +29 -15
  2. package/dist/bin/scaffold.js +1162 -1200
  3. package/dist/bin/scaffold.js.map +1 -1
  4. package/dist/host/AGENTS.md +124 -925
  5. package/dist/host/CLAUDE.md +335 -495
  6. package/dist/host/agents/skills/orkestrel-align-packages/SKILL.md +53 -0
  7. package/dist/host/agents/skills/orkestrel-align-packages/agents/openai.yaml +4 -0
  8. package/dist/host/agents/skills/orkestrel-align-packages/references/fleet.md +50 -0
  9. package/dist/host/agents/skills/orkestrel-align-packages/references/integration.md +56 -0
  10. package/dist/host/agents/skills/orkestrel-build-application/SKILL.md +64 -0
  11. package/dist/host/agents/skills/orkestrel-build-application/agents/openai.yaml +4 -0
  12. package/dist/host/agents/skills/orkestrel-build-application/references/application.md +129 -0
  13. package/dist/host/agents/skills/orkestrel-harden-package/SKILL.md +64 -0
  14. package/dist/host/agents/skills/orkestrel-harden-package/agents/openai.yaml +4 -0
  15. package/dist/host/agents/skills/orkestrel-harden-package/references/centralization.md +85 -0
  16. package/dist/host/agents/skills/orkestrel-harden-package/references/contract.md +57 -0
  17. package/dist/host/agents/skills/orkestrel-harden-package/references/hardening.md +93 -0
  18. package/dist/host/agents/skills/orkestrel-harden-package/references/research.md +61 -0
  19. package/dist/host/claude/agents/application.md +30 -0
  20. package/dist/host/claude/agents/builder.md +10 -7
  21. package/dist/host/claude/agents/checker.md +14 -8
  22. package/dist/host/claude/agents/codex.md +94 -0
  23. package/dist/host/claude/agents/grok.md +37 -34
  24. package/dist/host/claude/agents/implementer.md +40 -0
  25. package/dist/host/claude/agents/orkestrel.md +88 -223
  26. package/dist/host/claude/agents/planner.md +13 -34
  27. package/dist/host/claude/agents/reviewer.md +38 -24
  28. package/dist/host/claude/agents/verifier.md +14 -10
  29. package/dist/host/claude/rules/application.md +57 -0
  30. package/dist/host/claude/rules/architecture.md +180 -0
  31. package/dist/host/claude/rules/browser.md +28 -0
  32. package/dist/host/claude/rules/documentation.md +64 -0
  33. package/dist/host/claude/rules/names.md +209 -0
  34. package/dist/host/claude/rules/patterns.md +130 -0
  35. package/dist/host/claude/rules/quality.md +45 -0
  36. package/dist/host/claude/rules/styles.md +64 -0
  37. package/dist/host/claude/rules/tests.md +123 -0
  38. package/dist/host/claude/rules/typescript.md +78 -0
  39. package/dist/host/claude/rules/workspace.md +180 -0
  40. package/dist/host/claude/settings.json +112 -0
  41. package/dist/host/claude/skills/orkestrel-align-packages/SKILL.md +10 -0
  42. package/dist/host/claude/skills/orkestrel-build-application/SKILL.md +12 -0
  43. package/dist/host/claude/skills/orkestrel-harden-package/SKILL.md +10 -0
  44. package/dist/host/codex/agents/analyst.toml +15 -0
  45. package/dist/host/codex/agents/application.toml +25 -0
  46. package/dist/host/codex/agents/builder.toml +24 -0
  47. package/dist/host/codex/agents/checker.toml +15 -0
  48. package/dist/host/codex/agents/grok.toml +17 -0
  49. package/dist/host/codex/agents/implementer.toml +17 -0
  50. package/dist/host/codex/agents/opus.toml +25 -0
  51. package/dist/host/codex/agents/orkestrel.toml +16 -0
  52. package/dist/host/codex/agents/planner.toml +20 -0
  53. package/dist/host/codex/agents/reviewer.toml +18 -0
  54. package/dist/host/codex/agents/verifier.toml +14 -0
  55. package/dist/host/codex/config.toml +66 -0
  56. package/dist/host/cursor/mcp.json +12 -0
  57. package/dist/host/dotfiles/mcp.json +8 -0
  58. package/dist/host/dotfiles/oxlintrc.json +307 -1
  59. package/dist/host/guides/src/scaffold.md +2059 -2007
  60. package/dist/host/manifest.json +376 -137
  61. package/dist/host/scripts/codex.sh +49 -0
  62. package/dist/host/scripts/cursor.sh +33 -47
  63. package/dist/host/scripts/deps.sh +34 -16
  64. package/dist/host/scripts/ollama.sh +6 -149
  65. package/dist/host/tests/setupPolicy.ts +390 -0
  66. package/dist/src/core/index.cjs +6603 -1200
  67. package/dist/src/core/index.cjs.map +1 -1
  68. package/dist/src/core/index.d.cts +802 -267
  69. package/dist/src/core/index.d.ts +802 -267
  70. package/dist/src/core/index.js +6521 -1192
  71. package/dist/src/core/index.js.map +1 -1
  72. package/dist/src/server/index.cjs +3132 -593
  73. package/dist/src/server/index.cjs.map +1 -1
  74. package/dist/src/server/index.d.cts +643 -95
  75. package/dist/src/server/index.d.ts +643 -95
  76. package/dist/src/server/index.js +3054 -594
  77. package/dist/src/server/index.js.map +1 -1
  78. package/package.json +16 -12
  79. package/dist/host/claude/agents/composer.md +0 -64
  80. package/dist/host/claude/agents/researcher.md +0 -38
  81. package/dist/host/claude/agents/scout.md +0 -35
  82. package/dist/host/github/workflows/ci.yml +0 -64
@@ -0,0 +1,390 @@
1
+ import { globSync, readFileSync } from 'node:fs'
2
+ import { basename, join } from 'node:path'
3
+ import * as ts from 'typescript'
4
+
5
+ /** Centralized source modules whose top-level declarations must all be exported. */
6
+ export const CENTRAL_SOURCE_FILES: readonly string[] = Object.freeze([
7
+ 'combinators.ts',
8
+ 'cloners.ts',
9
+ 'compilers.ts',
10
+ 'constants.ts',
11
+ 'contracts.ts',
12
+ 'errors.ts',
13
+ 'factories.ts',
14
+ 'helpers.ts',
15
+ 'handlers.ts',
16
+ 'inferers.ts',
17
+ 'middlewares.ts',
18
+ 'parsers.ts',
19
+ 'relations.ts',
20
+ 'schemas.ts',
21
+ 'seeders.ts',
22
+ 'shapers.ts',
23
+ 'templates.ts',
24
+ 'types.ts',
25
+ 'validators.ts',
26
+ ])
27
+
28
+ /** Centralized files that own module-scope function declarations. */
29
+ export const FUNCTION_SOURCE_FILES: readonly string[] = Object.freeze([
30
+ 'combinators.ts',
31
+ 'cloners.ts',
32
+ 'compilers.ts',
33
+ 'errors.ts',
34
+ 'factories.ts',
35
+ 'handlers.ts',
36
+ 'helpers.ts',
37
+ 'inferers.ts',
38
+ 'middlewares.ts',
39
+ 'parsers.ts',
40
+ 'relations.ts',
41
+ 'schemas.ts',
42
+ 'seeders.ts',
43
+ 'shapers.ts',
44
+ 'validators.ts',
45
+ ])
46
+
47
+ /** Centralized files that own module-scope data declarations. */
48
+ export const DATA_SOURCE_FILES: readonly string[] = Object.freeze([
49
+ 'combinators.ts',
50
+ 'constants.ts',
51
+ 'contracts.ts',
52
+ 'relations.ts',
53
+ 'schemas.ts',
54
+ 'templates.ts',
55
+ 'validators.ts',
56
+ ])
57
+
58
+ /** One script block extracted from a Vue SFC by the official compiler. */
59
+ export interface VueScriptBlockInterface {
60
+ readonly content: string
61
+ readonly lang?: string
62
+ }
63
+
64
+ /** An injected official Vue SFC script-block extractor. */
65
+ export interface VueScriptExtractorInterface {
66
+ (path: string, content: string): readonly VueScriptBlockInterface[]
67
+ }
68
+
69
+ /** Normalize platform separators and duplicate glob segments for stable diagnostics. */
70
+ export function normalizePolicyPath(path: string): string {
71
+ return path.replaceAll('\\', '/').replace(/\/+/gu, '/')
72
+ }
73
+
74
+ /** Whether a declaration carries an explicit export modifier. */
75
+ export function hasExportModifier(node: ts.Node): boolean {
76
+ return (
77
+ ts.canHaveModifiers(node) &&
78
+ ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword) ===
79
+ true
80
+ )
81
+ }
82
+
83
+ /** Whether a declaration carries a specified modifier. */
84
+ export function hasModifier(node: ts.Node, modifier: ts.SyntaxKind): boolean {
85
+ return (
86
+ ts.canHaveModifiers(node) &&
87
+ ts.getModifiers(node)?.some((candidate) => candidate.kind === modifier) === true
88
+ )
89
+ }
90
+
91
+ /** Whether a module specifier uses a URL scheme other than Node's builtin namespace. */
92
+ export function isUnsupportedModuleSpecifier(specifier: string): boolean {
93
+ return /^[A-Za-z][A-Za-z0-9+.-]*:/u.test(specifier) && !specifier.startsWith('node:')
94
+ }
95
+
96
+ /** Whether an arrow/function expression is an anonymous callback passed directly as an argument. */
97
+ export function isDirectCallback(node: ts.ArrowFunction | ts.FunctionExpression): boolean {
98
+ const parent = node.parent
99
+ return (
100
+ (ts.isCallExpression(parent) || ts.isNewExpression(parent)) &&
101
+ parent.arguments?.some((argument) => argument === node) === true
102
+ )
103
+ }
104
+
105
+ /**
106
+ * Whether an arrow/function expression is returned directly as a factory or combinator result.
107
+ *
108
+ * @param node - The function expression to inspect
109
+ * @returns `true` when the node is a direct return value, including one parenthesized layer
110
+ */
111
+ export function isDirectReturn(node: ts.ArrowFunction | ts.FunctionExpression): boolean {
112
+ const parent = node.parent
113
+ if (ts.isReturnStatement(parent)) return true
114
+ if (ts.isArrowFunction(parent) && parent.body === node) return true
115
+ if (!ts.isParenthesizedExpression(parent)) return false
116
+ const container = parent.parent
117
+ return (
118
+ ts.isReturnStatement(container) || (ts.isArrowFunction(container) && container.body === parent)
119
+ )
120
+ }
121
+
122
+ /** Whether a function expression is assigned by one module-scope variable declaration. */
123
+ export function isModuleFunction(node: ts.ArrowFunction | ts.FunctionExpression): boolean {
124
+ const declaration = node.parent
125
+ const list = declaration.parent
126
+ const statement = list.parent
127
+ return (
128
+ ts.isVariableDeclaration(declaration) &&
129
+ ts.isVariableDeclarationList(list) &&
130
+ ts.isVariableStatement(statement) &&
131
+ ts.isSourceFile(statement.parent)
132
+ )
133
+ }
134
+
135
+ /**
136
+ * Format a syntax node's source position as a one-based line and character.
137
+ *
138
+ * @param node - The syntax node whose starting position to format
139
+ * @returns The node's one-based `line:character` position
140
+ */
141
+ export function formatPolicyPosition(node: ts.Node): string {
142
+ const source = node.getSourceFile()
143
+ const position = source.getLineAndCharacterOfPosition(node.getStart())
144
+ return `${String(position.line + 1)}:${String(position.character + 1)}`
145
+ }
146
+
147
+ /** Whether a property signature belongs to a centralized interface or type alias contract. */
148
+ export function isContractProperty(node: ts.PropertySignature): boolean {
149
+ let parent: ts.Node = node.parent
150
+ while (ts.isTypeLiteralNode(parent)) parent = parent.parent
151
+ return ts.isInterfaceDeclaration(parent) || ts.isTypeAliasDeclaration(parent)
152
+ }
153
+
154
+ /** Whether the sole production triple-slash reference is the generated browser Vite contract. */
155
+ export function hasAllowedTripleSlashReference(path: string, source: ts.SourceFile): boolean {
156
+ return (
157
+ path.replaceAll('\\', '/') === 'app/browser/env.d.ts' &&
158
+ source.referencedFiles.length === 0 &&
159
+ source.libReferenceDirectives.length === 0 &&
160
+ source.typeReferenceDirectives.length === 1 &&
161
+ source.typeReferenceDirectives[0]?.fileName === 'vite/client'
162
+ )
163
+ }
164
+
165
+ /** Inspect a Vue single-file component for syntax that can bypass declared import policy. */
166
+ export function inspectVueCodingLaw(
167
+ path: string,
168
+ scripts: readonly VueScriptBlockInterface[] = [],
169
+ ): readonly string[] {
170
+ const violations: string[] = []
171
+ for (const [index, script] of scripts.entries()) {
172
+ if (
173
+ script.lang !== 'ts' &&
174
+ script.lang !== 'tsx' &&
175
+ script.lang !== 'mts' &&
176
+ script.lang !== 'cts'
177
+ ) {
178
+ violations.push(`${path}.script-${String(index)} requires a TypeScript script language`)
179
+ }
180
+ const extension =
181
+ script.lang === 'tsx' || script.lang === 'mts' || script.lang === 'cts' ? script.lang : 'ts'
182
+ violations.push(
183
+ ...inspectCodingLaw(`${path}.script-${String(index)}.${extension}`, script.content),
184
+ )
185
+ }
186
+ return violations
187
+ }
188
+
189
+ /** Add syntax-wide coding-law violations while traversing one source tree. */
190
+ export function inspectCodingNode(path: string, node: ts.Node, violations: string[]): void {
191
+ if (
192
+ ts.isAsExpression(node) ||
193
+ ts.isTypeAssertionExpression(node) ||
194
+ ts.isNonNullExpression(node)
195
+ ) {
196
+ violations.push(`${path}:${formatPolicyPosition(node)} forbids type/non-null assertions`)
197
+ }
198
+ if (node.kind === ts.SyntaxKind.AnyKeyword) {
199
+ violations.push(`${path}:${formatPolicyPosition(node)} forbids any`)
200
+ }
201
+ if (
202
+ (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) &&
203
+ node.moduleSpecifier !== undefined &&
204
+ ts.isStringLiteral(node.moduleSpecifier) &&
205
+ isUnsupportedModuleSpecifier(node.moduleSpecifier.text)
206
+ ) {
207
+ violations.push(`${path}:${formatPolicyPosition(node)} forbids non-Node URL module specifiers`)
208
+ }
209
+ if (
210
+ ts.isPropertySignature(node) &&
211
+ isContractProperty(node) &&
212
+ !hasModifier(node, ts.SyntaxKind.ReadonlyKeyword)
213
+ ) {
214
+ violations.push(`${path}:${formatPolicyPosition(node)} requires readonly contract properties`)
215
+ }
216
+ if (
217
+ ts.isCallExpression(node) &&
218
+ node.expression.kind === ts.SyntaxKind.ImportKeyword &&
219
+ (node.arguments.length !== 1 || !node.arguments.every(ts.isStringLiteral))
220
+ ) {
221
+ violations.push(
222
+ `${path}:${formatPolicyPosition(node)} requires dynamic imports to use string literals so import policy remains enforceable`,
223
+ )
224
+ }
225
+ if (
226
+ ts.isCallExpression(node) &&
227
+ node.expression.kind === ts.SyntaxKind.ImportKeyword &&
228
+ node.arguments.length === 1 &&
229
+ node.arguments.every(ts.isStringLiteral) &&
230
+ isUnsupportedModuleSpecifier(node.arguments[0]?.text ?? '')
231
+ ) {
232
+ violations.push(`${path}:${formatPolicyPosition(node)} forbids non-Node URL module specifiers`)
233
+ }
234
+ if (ts.isFunctionDeclaration(node) && !ts.isSourceFile(node.parent)) {
235
+ violations.push(`${path}:${formatPolicyPosition(node)} forbids nested function declarations`)
236
+ }
237
+ if (
238
+ (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) &&
239
+ !isDirectCallback(node) &&
240
+ !isDirectReturn(node) &&
241
+ !isModuleFunction(node)
242
+ ) {
243
+ violations.push(`${path}:${formatPolicyPosition(node)} forbids hidden function assignments`)
244
+ }
245
+ ts.forEachChild(node, (child) => inspectCodingNode(path, child, violations))
246
+ }
247
+
248
+ /** Inspect one TypeScript source module for repository coding-law violations. */
249
+ export function inspectCodingLaw(path: string, content: string): readonly string[] {
250
+ const violations: string[] = []
251
+ const source = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true)
252
+ const file = basename(path)
253
+
254
+ if (/\.[cm]?jsx?$/u.test(path)) {
255
+ violations.push(`${path} production modules use TypeScript source extensions`)
256
+ }
257
+ if (/@ts-(?:expect-error|ignore|nocheck)|eslint-disable|oxlint-disable/u.test(content)) {
258
+ violations.push(`${path} forbids suppression directives`)
259
+ }
260
+ if (
261
+ (source.referencedFiles.length > 0 ||
262
+ source.libReferenceDirectives.length > 0 ||
263
+ source.typeReferenceDirectives.length > 0) &&
264
+ !hasAllowedTripleSlashReference(path, source)
265
+ ) {
266
+ violations.push(`${path} forbids triple-slash references outside app/browser/env.d.ts`)
267
+ }
268
+
269
+ if (file === 'index.ts') {
270
+ for (const statement of source.statements) {
271
+ if (
272
+ !ts.isExportDeclaration(statement) ||
273
+ statement.exportClause !== undefined ||
274
+ statement.isTypeOnly ||
275
+ statement.moduleSpecifier === undefined
276
+ ) {
277
+ violations.push(`${path} barrels contain only export * declarations`)
278
+ }
279
+ }
280
+ }
281
+
282
+ for (const statement of source.statements) {
283
+ if (
284
+ (ts.isInterfaceDeclaration(statement) || ts.isTypeAliasDeclaration(statement)) &&
285
+ file !== 'types.ts'
286
+ ) {
287
+ violations.push(`${path} centralizes interfaces and type aliases in types.ts`)
288
+ }
289
+ if (
290
+ CENTRAL_SOURCE_FILES.includes(file) &&
291
+ (ts.isClassDeclaration(statement) ||
292
+ ts.isFunctionDeclaration(statement) ||
293
+ ts.isInterfaceDeclaration(statement) ||
294
+ ts.isTypeAliasDeclaration(statement) ||
295
+ ts.isVariableStatement(statement)) &&
296
+ !hasExportModifier(statement)
297
+ ) {
298
+ violations.push(`${path} exports every centralized declaration`)
299
+ }
300
+ if (ts.isFunctionDeclaration(statement) && !FUNCTION_SOURCE_FILES.includes(file)) {
301
+ violations.push(`${path} places module functions in their centralized kind file`)
302
+ }
303
+ if (ts.isVariableStatement(statement) && !DATA_SOURCE_FILES.includes(file)) {
304
+ violations.push(`${path} places module data in its centralized kind file`)
305
+ }
306
+ if (
307
+ ts.isClassDeclaration(statement) &&
308
+ file !== 'errors.ts' &&
309
+ !/^[A-Z][A-Za-z0-9]*\.ts$/u.test(file)
310
+ ) {
311
+ violations.push(`${path} places each class in its matching implementation or errors file`)
312
+ }
313
+ if (ts.isEnumDeclaration(statement) && file !== 'types.ts') {
314
+ violations.push(`${path} centralizes enum contracts in types.ts`)
315
+ }
316
+ if (file === 'constants.ts' && ts.isVariableStatement(statement)) {
317
+ if ((statement.declarationList.flags & ts.NodeFlags.Const) === 0) {
318
+ violations.push(`${path} constants.ts permits only const declarations`)
319
+ }
320
+ for (const declaration of statement.declarationList.declarations) {
321
+ if (
322
+ !ts.isIdentifier(declaration.name) ||
323
+ !/^[A-Z][A-Z0-9_]*$/u.test(declaration.name.text)
324
+ ) {
325
+ violations.push(
326
+ `${path}:${formatPolicyPosition(declaration)} requires UPPER_SNAKE_CASE constants`,
327
+ )
328
+ }
329
+ if (
330
+ declaration.initializer !== undefined &&
331
+ (ts.isArrayLiteralExpression(declaration.initializer) ||
332
+ ts.isObjectLiteralExpression(declaration.initializer))
333
+ ) {
334
+ violations.push(
335
+ `${path}:${formatPolicyPosition(declaration)} freezes collection constants`,
336
+ )
337
+ }
338
+ }
339
+ }
340
+ }
341
+
342
+ if (/^[A-Z][A-Za-z0-9]*\.ts$/u.test(file)) {
343
+ const classes = source.statements.filter(ts.isClassDeclaration)
344
+ const invalid = source.statements.filter(
345
+ (statement) => !ts.isImportDeclaration(statement) && !ts.isClassDeclaration(statement),
346
+ )
347
+ if (
348
+ classes.length !== 1 ||
349
+ invalid.length !== 0 ||
350
+ classes[0]?.name?.text !== file.slice(0, -3) ||
351
+ classes[0] === undefined ||
352
+ !hasExportModifier(classes[0])
353
+ ) {
354
+ violations.push(
355
+ `${path} implementation modules contain imports and one matching exported class`,
356
+ )
357
+ }
358
+ for (const member of classes[0]?.members ?? []) {
359
+ if (hasModifier(member, ts.SyntaxKind.PrivateKeyword)) {
360
+ violations.push(`${path}:${formatPolicyPosition(member)} uses runtime # privacy`)
361
+ }
362
+ }
363
+ }
364
+
365
+ inspectCodingNode(path, source, violations)
366
+ return violations
367
+ }
368
+
369
+ /** Inspect every production TypeScript module under one workspace. */
370
+ export function inspectCodingWorkspace(
371
+ root: string,
372
+ vueScripts?: VueScriptExtractorInterface,
373
+ ): readonly string[] {
374
+ const violations: string[] = []
375
+ for (const path of globSync('{app,src}/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx,vue}', {
376
+ cwd: root,
377
+ })) {
378
+ const content = readFileSync(join(root, path), 'utf8')
379
+ const normalizedPath = normalizePolicyPath(path)
380
+ if (path.endsWith('.vue') && !normalizedPath.startsWith('app/browser/')) {
381
+ violations.push(`${normalizedPath} Vue components belong in app/browser`)
382
+ }
383
+ violations.push(
384
+ ...(path.endsWith('.vue')
385
+ ? inspectVueCodingLaw(normalizedPath, vueScripts?.(normalizedPath, content))
386
+ : inspectCodingLaw(normalizedPath, content)),
387
+ )
388
+ }
389
+ return violations
390
+ }