@orkestrel/scaffold 0.0.3 → 0.0.5

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.
@@ -37,12 +37,14 @@ Work directly for trivial tasks. For non-trivial work: send the reading to grok;
37
37
  planner (Opus 5, subjective) and analyst (Sol, objective) on the
38
38
  SAME design brief in parallel without showing either the other's answer, then
39
39
  reconcile them into one plan; plan bounded units with disjoint ownership; route each
40
- nontrivial unit to implementer (Sol), reserving builder/application for fully
40
+ nontrivial unit to implementer (Sol) or the opus implementation bridge (Opus 5) —
41
+ whichever engine fits the unit — reserving builder/application for fully
41
42
  specified taste-free mechanics; integrate
42
43
  shared files serially; obtain an independent reviewer (Opus 5) design audit and an
43
44
  independent analyst (Sol) correctness audit plus a checker conformance pass; then
44
- run one verifier. Parallelize only independent units. Writing agents use worktrees or
45
- disjoint owned files and never run tree-wide mutating commands concurrently.
45
+ run one verifier. Parallelize only independent units. Writing agents run serially in the
46
+ main checkout with disjoint owned files and never run tree-wide mutating commands
47
+ concurrently.
46
48
 
47
49
  Every dispatch includes objective, context, AGENTS.md, applicable rules, governing
48
50
  skill name/path and required references (or explicit none), guide/spec, owned and
@@ -0,0 +1,12 @@
1
+ {
2
+ "mcpServers": {
3
+ "codex": {
4
+ "command": "codex",
5
+ "args": ["mcp-server"]
6
+ },
7
+ "claude": {
8
+ "command": "claude",
9
+ "args": ["mcp", "serve"]
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "codex": {
4
+ "command": "codex",
5
+ "args": ["mcp-server"]
6
+ }
7
+ }
8
+ }
@@ -133,6 +133,7 @@ From [`types.ts`](../../src/core/types.ts).
133
133
  | `BuildFormat` | type |
134
134
  | `SrcDefinition` | interface |
135
135
  | `AppDefinition` | interface |
136
+ | `ViteMachinery` | interface |
136
137
  | `Origin` | type |
137
138
  | `Group` | type |
138
139
  | `Category` | type |
@@ -183,6 +184,12 @@ is `'INVALID' | 'BLOCKED' | 'DESTROYED' | 'TARGET' | 'WRITE' | 'FETCH'`.
183
184
  environment contributes, its test-project label, and — on the `src` axis — its `exports` subpath
184
185
  and build formats, or — on the `app` axis — its optional runtime entry.
185
186
 
187
+ `ViteMachinery` names the three host-specific pipelines a workspace's generated `vite.config.ts` may
188
+ carry: `browser` for the CSS pipeline and the Playwright-backed browser test project, `vue` for the
189
+ single-file-component, HTML, and development-server machinery an application browser environment
190
+ needs, and `output` for build-output containment. It never selects a boundary guarantee — those ship
191
+ in every shape, as the compilers section sets out.
192
+
186
193
  `Blueprint` is the closed input spec:
187
194
 
188
195
  ```ts
@@ -946,6 +953,7 @@ From [`compilers.ts`](../../src/core/compilers.ts).
946
953
  | `devDependenciesFor` | function |
947
954
  | `packageManifest` | function |
948
955
  | `rootTsconfig` | function |
956
+ | `viteMachinery` | function |
949
957
  | `viteHeader` | function |
950
958
  | `policyViteProject` | function |
951
959
  | `singleSrcViteConfig` | function |
@@ -987,10 +995,33 @@ optional metadata, and engines.
987
995
  `rootTsconfig` emits the root compiler options and one path alias per declared environment;
988
996
  `coreTsconfig`, `srcTsconfig`, and `appTsconfig` emit the scoped configurations that remove the
989
997
  wrong host's globals from each environment. `viteHeader` renders the shared header — the alias block
990
- derived from the tsconfig paths, plus the environment-boundary plugin. `coreViteConfig`,
991
- `srcViteConfig`, and `appViteConfig` emit the thin per-target wrappers; `rootViteConfig`,
992
- `singleSrcViteConfig`, and `applicationViteConfig` emit the root configuration for a
993
- library-only, single non-core `src` environment, and application-bearing workspace respectively;
998
+ derived from the tsconfig paths, plus the environment-boundary plugin — and `viteMachinery` is the
999
+ one place the header's axes are derived, read by `rootViteConfig`, `singleSrcViteConfig`,
1000
+ `applicationViteConfig`, and `configArtifacts` alike so no caller can invent a fourth answer.
1001
+
1002
+ **The boundary guarantees do not vary by blueprint.** Every generated `vite.config.ts` — a
1003
+ `core`-only library, an application of `app/core` alone, or the full six-environment workspace —
1004
+ emits `environmentBoundary`, its `resolveId` / `load` / `buildEnd` walks, the module-graph AST audit
1005
+ (`environmentAssetSources`, `parseSync`, `Visitor`), and stylesheet rejection (`isStylesheetPath`
1006
+ plus its `environmentPathError` / `environmentSourceError` clauses). Those enforce owner-independent
1007
+ laws: core stays host-independent whatever else the workspace declares, a server module never
1008
+ imports a stylesheet, and a `@vite-ignore` dynamic import — which `resolveId` never sees and the
1009
+ module graph never records — has no other enforcement point. Only host-specific pipelines vary,
1010
+ along the three `ViteMachinery` axes:
1011
+
1012
+ | Machinery | Emitted when |
1013
+ | ----------------------------------------------------------------- | ------------------------------------ |
1014
+ | CSS pipeline (`ENVIRONMENT_CSS`, `preprocessCSS`, `isCSSRequest`) | a `src` or `app` browser environment |
1015
+ | Playwright provider and `hasChromium` | a `src` or `app` browser environment |
1016
+ | Vue plugin, HTML boundary, browser development server | an `app` browser environment |
1017
+ | Output containment (`outputBoundary`, `enforceOutputPath`) | anything the workspace builds |
1018
+
1019
+ An application of `app/core` alone is the sole shape that builds nothing, so it is the sole shape
1020
+ without output containment — and it still carries every boundary guarantee above.
1021
+
1022
+ `coreViteConfig`, `srcViteConfig`, and `appViteConfig` emit the thin per-target wrappers;
1023
+ `rootViteConfig`, `singleSrcViteConfig`, and `applicationViteConfig` emit the root configuration for
1024
+ a library-only, single non-core `src` environment, and application-bearing workspace respectively;
994
1025
  `policyViteProject` emits the dedicated Node-only repository-policy test project.
995
1026
 
996
1027
  `configArtifacts`, `sourceArtifacts`, `applicationArtifacts`, `testArtifacts`, and `guideArtifacts`
@@ -1924,6 +1955,7 @@ import {
1924
1955
  srcTsconfig,
1925
1956
  srcViteConfig,
1926
1957
  viteHeader,
1958
+ viteMachinery,
1927
1959
  } from '@orkestrel/scaffold'
1928
1960
 
1929
1961
  rootTsconfig(['core'], ['core', 'server'])
@@ -1931,7 +1963,9 @@ coreTsconfig()
1931
1963
  srcTsconfig('server')
1932
1964
  appTsconfig('browser', true)
1933
1965
 
1934
- viteHeader(true, true) // the shared header, with browser and Vue support
1966
+ viteMachinery(['core']) // { browser: false, vue: false, output: true }
1967
+ viteMachinery([], ['core', 'browser']) // { browser: true, vue: true, output: true }
1968
+ viteHeader(viteMachinery([], ['core', 'browser'])) // the shared header, with browser and Vue support
1935
1969
  coreViteConfig()
1936
1970
  srcViteConfig('browser')
1937
1971
  appViteConfig('server')
@@ -90,6 +90,11 @@
90
90
  "destination": ".claude/agents/grok.md",
91
91
  "executable": false
92
92
  },
93
+ {
94
+ "storage": "claude/agents/implementer.md",
95
+ "destination": ".claude/agents/implementer.md",
96
+ "executable": false
97
+ },
93
98
  {
94
99
  "storage": "claude/agents/orkestrel.md",
95
100
  "destination": ".claude/agents/orkestrel.md",
@@ -215,6 +220,11 @@
215
220
  "destination": ".codex/agents/implementer.toml",
216
221
  "executable": false
217
222
  },
223
+ {
224
+ "storage": "codex/agents/opus.toml",
225
+ "destination": ".codex/agents/opus.toml",
226
+ "executable": false
227
+ },
218
228
  {
219
229
  "storage": "codex/agents/orkestrel.toml",
220
230
  "destination": ".codex/agents/orkestrel.toml",
@@ -240,6 +250,11 @@
240
250
  "destination": ".codex/config.toml",
241
251
  "executable": false
242
252
  },
253
+ {
254
+ "storage": "cursor/mcp.json",
255
+ "destination": ".cursor/mcp.json",
256
+ "executable": false
257
+ },
243
258
  {
244
259
  "storage": "dotfiles/editorconfig",
245
260
  "destination": ".editorconfig",
@@ -255,6 +270,11 @@
255
270
  "destination": ".gitignore",
256
271
  "executable": false
257
272
  },
273
+ {
274
+ "storage": "dotfiles/mcp.json",
275
+ "destination": ".mcp.json",
276
+ "executable": false
277
+ },
258
278
  {
259
279
  "storage": "dotfiles/oxfmtrc.json",
260
280
  "destination": ".oxfmtrc.json",
@@ -347,6 +367,7 @@
347
367
  ".claude/skills/orkestrel-harden-package",
348
368
  ".codex",
349
369
  ".codex/agents",
370
+ ".cursor",
350
371
  "guides",
351
372
  "guides/src",
352
373
  "scripts",
@@ -13,6 +13,7 @@ export const CENTRAL_SOURCE_FILES: readonly string[] = Object.freeze([
13
13
  'factories.ts',
14
14
  'helpers.ts',
15
15
  'handlers.ts',
16
+ 'inferers.ts',
16
17
  'middlewares.ts',
17
18
  'parsers.ts',
18
19
  'relations.ts',
@@ -33,6 +34,7 @@ export const FUNCTION_SOURCE_FILES: readonly string[] = Object.freeze([
33
34
  'factories.ts',
34
35
  'handlers.ts',
35
36
  'helpers.ts',
37
+ 'inferers.ts',
36
38
  'middlewares.ts',
37
39
  'parsers.ts',
38
40
  'relations.ts',
@@ -100,6 +102,23 @@ export function isDirectCallback(node: ts.ArrowFunction | ts.FunctionExpression)
100
102
  )
101
103
  }
102
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
+
103
122
  /** Whether a function expression is assigned by one module-scope variable declaration. */
104
123
  export function isModuleFunction(node: ts.ArrowFunction | ts.FunctionExpression): boolean {
105
124
  const declaration = node.parent
@@ -113,6 +132,18 @@ export function isModuleFunction(node: ts.ArrowFunction | ts.FunctionExpression)
113
132
  )
114
133
  }
115
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
+
116
147
  /** Whether a property signature belongs to a centralized interface or type alias contract. */
117
148
  export function isContractProperty(node: ts.PropertySignature): boolean {
118
149
  let parent: ts.Node = node.parent
@@ -162,10 +193,10 @@ export function inspectCodingNode(path: string, node: ts.Node, violations: strin
162
193
  ts.isTypeAssertionExpression(node) ||
163
194
  ts.isNonNullExpression(node)
164
195
  ) {
165
- violations.push(`${path}:${node.getStart()} forbids type/non-null assertions`)
196
+ violations.push(`${path}:${formatPolicyPosition(node)} forbids type/non-null assertions`)
166
197
  }
167
198
  if (node.kind === ts.SyntaxKind.AnyKeyword) {
168
- violations.push(`${path}:${node.getStart()} forbids any`)
199
+ violations.push(`${path}:${formatPolicyPosition(node)} forbids any`)
169
200
  }
170
201
  if (
171
202
  (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) &&
@@ -173,14 +204,14 @@ export function inspectCodingNode(path: string, node: ts.Node, violations: strin
173
204
  ts.isStringLiteral(node.moduleSpecifier) &&
174
205
  isUnsupportedModuleSpecifier(node.moduleSpecifier.text)
175
206
  ) {
176
- violations.push(`${path}:${node.getStart()} forbids non-Node URL module specifiers`)
207
+ violations.push(`${path}:${formatPolicyPosition(node)} forbids non-Node URL module specifiers`)
177
208
  }
178
209
  if (
179
210
  ts.isPropertySignature(node) &&
180
211
  isContractProperty(node) &&
181
212
  !hasModifier(node, ts.SyntaxKind.ReadonlyKeyword)
182
213
  ) {
183
- violations.push(`${path}:${node.getStart()} requires readonly contract properties`)
214
+ violations.push(`${path}:${formatPolicyPosition(node)} requires readonly contract properties`)
184
215
  }
185
216
  if (
186
217
  ts.isCallExpression(node) &&
@@ -188,7 +219,7 @@ export function inspectCodingNode(path: string, node: ts.Node, violations: strin
188
219
  (node.arguments.length !== 1 || !node.arguments.every(ts.isStringLiteral))
189
220
  ) {
190
221
  violations.push(
191
- `${path}:${node.getStart()} requires dynamic imports to use string literals so import policy remains enforceable`,
222
+ `${path}:${formatPolicyPosition(node)} requires dynamic imports to use string literals so import policy remains enforceable`,
192
223
  )
193
224
  }
194
225
  if (
@@ -198,17 +229,18 @@ export function inspectCodingNode(path: string, node: ts.Node, violations: strin
198
229
  node.arguments.every(ts.isStringLiteral) &&
199
230
  isUnsupportedModuleSpecifier(node.arguments[0]?.text ?? '')
200
231
  ) {
201
- violations.push(`${path}:${node.getStart()} forbids non-Node URL module specifiers`)
232
+ violations.push(`${path}:${formatPolicyPosition(node)} forbids non-Node URL module specifiers`)
202
233
  }
203
234
  if (ts.isFunctionDeclaration(node) && !ts.isSourceFile(node.parent)) {
204
- violations.push(`${path}:${node.getStart()} forbids nested function declarations`)
235
+ violations.push(`${path}:${formatPolicyPosition(node)} forbids nested function declarations`)
205
236
  }
206
237
  if (
207
238
  (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) &&
208
239
  !isDirectCallback(node) &&
240
+ !isDirectReturn(node) &&
209
241
  !isModuleFunction(node)
210
242
  ) {
211
- violations.push(`${path}:${node.getStart()} forbids hidden function assignments`)
243
+ violations.push(`${path}:${formatPolicyPosition(node)} forbids hidden function assignments`)
212
244
  }
213
245
  ts.forEachChild(node, (child) => inspectCodingNode(path, child, violations))
214
246
  }
@@ -290,14 +322,18 @@ export function inspectCodingLaw(path: string, content: string): readonly string
290
322
  !ts.isIdentifier(declaration.name) ||
291
323
  !/^[A-Z][A-Z0-9_]*$/u.test(declaration.name.text)
292
324
  ) {
293
- violations.push(`${path}:${declaration.getStart()} requires UPPER_SNAKE_CASE constants`)
325
+ violations.push(
326
+ `${path}:${formatPolicyPosition(declaration)} requires UPPER_SNAKE_CASE constants`,
327
+ )
294
328
  }
295
329
  if (
296
330
  declaration.initializer !== undefined &&
297
331
  (ts.isArrayLiteralExpression(declaration.initializer) ||
298
332
  ts.isObjectLiteralExpression(declaration.initializer))
299
333
  ) {
300
- violations.push(`${path}:${declaration.getStart()} freezes collection constants`)
334
+ violations.push(
335
+ `${path}:${formatPolicyPosition(declaration)} freezes collection constants`,
336
+ )
301
337
  }
302
338
  }
303
339
  }
@@ -321,7 +357,7 @@ export function inspectCodingLaw(path: string, content: string): readonly string
321
357
  }
322
358
  for (const member of classes[0]?.members ?? []) {
323
359
  if (hasModifier(member, ts.SyntaxKind.PrivateKeyword)) {
324
- violations.push(`${path}:${member.getStart()} uses runtime # privacy`)
360
+ violations.push(`${path}:${formatPolicyPosition(member)} uses runtime # privacy`)
325
361
  }
326
362
  }
327
363
  }