@orkestrel/scaffold 0.0.107 → 0.0.109

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 (164) hide show
  1. package/dist/agents/skills/orkestrel-dispatch/scripts/bench.js +326 -91
  2. package/dist/agents/skills/orkestrel-dispatch/scripts/brief.js +110 -14
  3. package/dist/agents/skills/orkestrel-dispatch/scripts/chain.js +280 -0
  4. package/dist/agents/skills/orkestrel-dispatch/scripts/cite.js +188 -26
  5. package/dist/agents/skills/orkestrel-dispatch/scripts/launch.js +142 -43
  6. package/dist/agents/skills/orkestrel-dispatch/scripts/login.js +240 -62
  7. package/dist/agents/skills/orkestrel-dispatch/scripts/patch.js +323 -0
  8. package/dist/agents/skills/orkestrel-dispatch/scripts/result.js +169 -32
  9. package/dist/agents/skills/orkestrel-dispatch/scripts/sweep.js +368 -27
  10. package/dist/agents/skills/orkestrel-falsify/scripts/mutate.js +560 -0
  11. package/dist/agents/skills/orkestrel-harden/scripts/discovery.js +1052 -205
  12. package/dist/agents/skills/orkestrel-harden/scripts/measure.js +361 -0
  13. package/dist/agents/skills/orkestrel-harden/scripts/titles.js +455 -80
  14. package/dist/agents/skills/orkestrel-parallel/scripts/cases.js +327 -0
  15. package/dist/agents/skills/orkestrel-publish/scripts/compare.js +298 -51
  16. package/dist/agents/skills/orkestrel-publish/scripts/pins.js +170 -13
  17. package/dist/agents/skills/orkestrel-publish/scripts/swap.js +956 -191
  18. package/dist/agents/skills/orkestrel-publish/scripts/wave.js +707 -112
  19. package/dist/agents/skills/orkestrel-publish/scripts/window.js +235 -41
  20. package/dist/agents/skills/orkestrel-scout/scripts/map.js +341 -54
  21. package/dist/agents/templates/brief.md +1 -1
  22. package/dist/bin/main.js +51 -46
  23. package/dist/bin/main.js.map +1 -1
  24. package/dist/host/AGENTS.md +26 -20
  25. package/dist/host/agents/orchestration.md +98 -58
  26. package/dist/host/agents/platforms/posix.md +15 -0
  27. package/dist/host/agents/platforms/windows.md +37 -0
  28. package/dist/host/agents/skills/orkestrel-align/SKILL.md +10 -13
  29. package/dist/host/agents/skills/orkestrel-align/references/fleet.md +4 -10
  30. package/dist/host/agents/skills/orkestrel-align/references/integration.md +1 -1
  31. package/dist/host/agents/skills/orkestrel-build/SKILL.md +16 -48
  32. package/dist/host/agents/skills/orkestrel-build/agents/openai.yaml +1 -1
  33. package/dist/host/agents/skills/orkestrel-debrief/SKILL.md +19 -16
  34. package/dist/host/agents/skills/orkestrel-debrief/agents/openai.yaml +1 -1
  35. package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +15 -28
  36. package/dist/host/agents/skills/orkestrel-debrief/references/instruction-audit.md +5 -6
  37. package/dist/host/agents/skills/orkestrel-debrief/references/retention.md +12 -14
  38. package/dist/host/agents/skills/orkestrel-dispatch/SKILL.md +36 -24
  39. package/dist/host/agents/skills/orkestrel-dispatch/references/launch.md +14 -11
  40. package/dist/host/agents/skills/orkestrel-dispatch/scripts/bench.ts +366 -75
  41. package/dist/host/agents/skills/orkestrel-dispatch/scripts/brief.ts +126 -16
  42. package/dist/host/agents/skills/orkestrel-dispatch/scripts/chain.ts +323 -0
  43. package/dist/host/agents/skills/orkestrel-dispatch/scripts/cite.ts +220 -31
  44. package/dist/host/agents/skills/orkestrel-dispatch/scripts/launch.ts +176 -33
  45. package/dist/host/agents/skills/orkestrel-dispatch/scripts/login.ts +294 -59
  46. package/dist/host/agents/skills/orkestrel-dispatch/scripts/patch.ts +401 -0
  47. package/dist/host/agents/skills/orkestrel-dispatch/scripts/result.ts +199 -34
  48. package/dist/host/agents/skills/orkestrel-dispatch/scripts/sweep.ts +428 -27
  49. package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +10 -8
  50. package/dist/host/agents/skills/orkestrel-falsify/references/brief.md +1 -5
  51. package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +1 -1
  52. package/dist/host/agents/skills/orkestrel-falsify/scripts/mutate.ts +701 -0
  53. package/dist/host/agents/skills/orkestrel-harden/SKILL.md +16 -12
  54. package/dist/host/agents/skills/orkestrel-harden/references/bindings.md +23 -81
  55. package/dist/host/agents/skills/orkestrel-harden/references/centralization.md +4 -5
  56. package/dist/host/agents/skills/orkestrel-harden/references/contract.md +2 -0
  57. package/dist/host/agents/skills/orkestrel-harden/references/document.md +33 -0
  58. package/dist/host/agents/skills/orkestrel-harden/references/equivalence.md +6 -3
  59. package/dist/host/agents/skills/orkestrel-harden/references/hardening.md +8 -4
  60. package/dist/host/agents/skills/orkestrel-harden/references/research.md +2 -0
  61. package/dist/host/agents/skills/orkestrel-harden/scripts/discovery.ts +1272 -279
  62. package/dist/host/agents/skills/orkestrel-harden/scripts/measure.ts +425 -0
  63. package/dist/host/agents/skills/orkestrel-harden/scripts/titles.ts +603 -98
  64. package/dist/host/agents/skills/orkestrel-journey/SKILL.md +75 -98
  65. package/dist/host/agents/skills/orkestrel-journey/references/captures.md +54 -17
  66. package/dist/host/agents/skills/orkestrel-journey/references/decide.md +1 -20
  67. package/dist/host/agents/skills/orkestrel-journey/references/layer.md +21 -31
  68. package/dist/host/agents/skills/orkestrel-journey/references/styles.md +1 -1
  69. package/dist/host/agents/skills/orkestrel-parallel/SKILL.md +91 -30
  70. package/dist/host/agents/skills/orkestrel-parallel/agents/openai.yaml +2 -2
  71. package/dist/host/agents/skills/orkestrel-parallel/references/claude.md +34 -0
  72. package/dist/host/agents/skills/orkestrel-parallel/references/codex.md +29 -0
  73. package/dist/host/agents/skills/orkestrel-parallel/references/cursor.md +29 -0
  74. package/dist/host/agents/skills/orkestrel-parallel/references/t3.md +31 -0
  75. package/dist/host/agents/skills/orkestrel-parallel/scripts/cases.ts +389 -0
  76. package/dist/host/agents/skills/orkestrel-polish/SKILL.md +16 -20
  77. package/dist/host/agents/skills/orkestrel-polish/references/capture-harness.md +16 -26
  78. package/dist/host/agents/skills/orkestrel-publish/SKILL.md +12 -26
  79. package/dist/host/agents/skills/orkestrel-publish/agents/openai.yaml +2 -2
  80. package/dist/host/agents/skills/orkestrel-publish/references/adopt.md +23 -0
  81. package/dist/host/agents/skills/orkestrel-publish/references/release.md +4 -4
  82. package/dist/host/agents/skills/orkestrel-publish/references/wave.md +12 -10
  83. package/dist/host/agents/skills/orkestrel-publish/references/window.md +20 -25
  84. package/dist/host/agents/skills/orkestrel-publish/scripts/compare.ts +348 -59
  85. package/dist/host/agents/skills/orkestrel-publish/scripts/pins.ts +202 -22
  86. package/dist/host/agents/skills/orkestrel-publish/scripts/swap.ts +1061 -191
  87. package/dist/host/agents/skills/orkestrel-publish/scripts/wave.ts +812 -140
  88. package/dist/host/agents/skills/orkestrel-publish/scripts/window.ts +280 -38
  89. package/dist/host/agents/skills/orkestrel-scout/SKILL.md +3 -3
  90. package/dist/host/agents/skills/orkestrel-scout/scripts/map.ts +428 -70
  91. package/dist/host/agents/templates/brief.md +1 -1
  92. package/dist/host/agents/transports/claude-cli.md +3 -3
  93. package/dist/host/agents/transports/codex.md +3 -8
  94. package/dist/host/agents/transports/cursor.md +2 -3
  95. package/dist/host/claude/AGENTS.md +7 -10
  96. package/dist/host/claude/agents/analyst.md +3 -3
  97. package/dist/host/claude/agents/astra.md +3 -3
  98. package/dist/host/claude/agents/builder.md +1 -1
  99. package/dist/host/claude/agents/checker.md +1 -1
  100. package/dist/host/claude/agents/distiller.md +1 -1
  101. package/dist/host/claude/agents/grok.md +1 -1
  102. package/dist/host/claude/agents/opus.md +2 -2
  103. package/dist/host/claude/agents/orkestrel.md +57 -90
  104. package/dist/host/claude/agents/researcher.md +1 -1
  105. package/dist/host/claude/agents/reviewer.md +4 -0
  106. package/dist/host/claude/agents/scout.md +1 -1
  107. package/dist/host/claude/agents/verifier.md +1 -1
  108. package/dist/host/claude/rules/application.md +14 -21
  109. package/dist/host/claude/rules/architecture.md +34 -44
  110. package/dist/host/claude/rules/browser.md +3 -4
  111. package/dist/host/claude/rules/campaign.md +55 -0
  112. package/dist/host/claude/rules/documentation.md +26 -11
  113. package/dist/host/claude/rules/names.md +9 -4
  114. package/dist/host/claude/rules/patterns.md +6 -7
  115. package/dist/host/claude/rules/portability.md +2 -3
  116. package/dist/host/claude/rules/quality.md +4 -5
  117. package/dist/host/claude/rules/styles.md +2 -2
  118. package/dist/host/claude/rules/tests.md +57 -114
  119. package/dist/host/claude/rules/typescript.md +8 -10
  120. package/dist/host/claude/rules/workspace.md +21 -19
  121. package/dist/host/claude/rules/writing.md +3 -3
  122. package/dist/host/claude/skills/orkestrel-dispatch/SKILL.md +1 -1
  123. package/dist/host/claude/skills/orkestrel-harden/SKILL.md +1 -1
  124. package/dist/host/claude/skills/orkestrel-parallel/SKILL.md +1 -1
  125. package/dist/host/claude/skills/orkestrel-publish/SKILL.md +1 -1
  126. package/dist/host/claude/skills/orkestrel-scout/SKILL.md +1 -1
  127. package/dist/host/codex/agents/analyst.toml +9 -3
  128. package/dist/host/codex/agents/astra.toml +3 -4
  129. package/dist/host/codex/agents/builder.toml +1 -1
  130. package/dist/host/codex/agents/opus.toml +3 -4
  131. package/dist/host/codex/agents/orkestrel.toml +1 -1
  132. package/dist/host/codex/agents/researcher.toml +2 -2
  133. package/dist/host/codex/agents/verifier.toml +1 -1
  134. package/dist/host/codex/config.toml +3 -3
  135. package/dist/host/configs/helpers.ts +21 -1
  136. package/dist/host/configs/policy.ts +405 -16
  137. package/dist/host/cursor/rules/orchestration.mdc +1 -0
  138. package/dist/host/dotfiles/oxlintrc.json +141 -5
  139. package/dist/host/guides/agent.md +111 -134
  140. package/dist/host/guides/capture.md +801 -0
  141. package/dist/host/guides/probe.md +27 -8
  142. package/dist/host/guides/scaffold.md +152 -19
  143. package/dist/host/guides/study.md +534 -440
  144. package/dist/host/guides/test.md +111 -44
  145. package/dist/host/manifest.json +199 -242
  146. package/dist/host/tests/config.test.ts +1558 -219
  147. package/dist/host/tests/policy.test.ts +127 -1
  148. package/dist/host/tests/setupPolicy.ts +1389 -39
  149. package/dist/src/core/index.cjs +239 -87
  150. package/dist/src/core/index.cjs.map +1 -1
  151. package/dist/src/core/index.d.cts +123 -39
  152. package/dist/src/core/index.d.ts +123 -39
  153. package/dist/src/core/index.js +234 -86
  154. package/dist/src/core/index.js.map +1 -1
  155. package/dist/src/server/index.cjs +20 -27
  156. package/dist/src/server/index.cjs.map +1 -1
  157. package/dist/src/server/index.js +21 -28
  158. package/dist/src/server/index.js.map +1 -1
  159. package/package.json +11 -11
  160. package/dist/agents/skills/orkestrel-dispatch/scripts/helpers.js +0 -283
  161. package/dist/host/agents/skills/orkestrel-dispatch/references/bench.md +0 -25
  162. package/dist/host/agents/skills/orkestrel-dispatch/scripts/helpers.ts +0 -333
  163. package/dist/host/agents/skills/orkestrel-parallel/references/harnesses.md +0 -19
  164. package/dist/host/guides/supervisor.md +0 -4890
@@ -1,7 +1,7 @@
1
1
  name = "verifier"
2
2
  description = "Independent gate runner that runs the exact commands the dispatch names, scoped first, and reports exit-code truth with exact failure excerpts without fixing anything."
3
3
  model = "gpt-6-luna"
4
- model_reasoning_effort = "medium"
4
+ model_reasoning_effort = "low"
5
5
  sandbox_mode = "workspace-write"
6
6
  developer_instructions = """
7
7
  Run the requested gates and report their results. Write only the evidence artifacts and the declared
@@ -15,9 +15,9 @@ files you touch; Codex does not load them for you. Follow a skill under .agents/
15
15
  trigger fires.
16
16
 
17
17
  Use the role mapping and continuation rules in .agents/orchestration.md. Before using native or
18
- T3 collaboration, read .agents/skills/orkestrel-parallel/references/harnesses.md for the channel
19
- mechanics and retained identifiers, and read the installed tool descriptions before relying on a
20
- capability that reference names.
18
+ T3 collaboration, read .agents/skills/orkestrel-parallel/references/codex.md and t3.md for the
19
+ channel mechanics, retained identifiers, and quirks, and read the installed tool descriptions
20
+ before relying on a capability those pages name. .agents/platforms/ owns the host shell.
21
21
 
22
22
  Hooks: .codex/hooks.json runs the orkestrel-dispatch sweep script (node
23
23
  .agents/skills/orkestrel-dispatch/scripts/sweep.ts --report) on SessionStart and git diff --check
@@ -756,6 +756,26 @@ export function readCompilerOutput(compiler: string, args: readonly string[]): s
756
756
  return result.stdout ?? ''
757
757
  }
758
758
 
759
+ /**
760
+ * Resolves the TypeScript command from its exported package manifest.
761
+ * @param path - The module or manifest path whose installation supplies TypeScript.
762
+ * @returns The absolute compiler command path on TypeScript 6 and 7.
763
+ * @throws An error when the package declares no `bin.tsc` command.
764
+ */
765
+ export function resolveCompiler(path: string): string {
766
+ const load = createRequire(path)
767
+ const manifest = load.resolve('typescript/package.json')
768
+ const declared: unknown = load(manifest)
769
+ if (typeof declared !== 'object' || declared === null || !('bin' in declared)) {
770
+ throw new Error('The TypeScript package declares no bin.tsc command')
771
+ }
772
+ const bin = declared.bin
773
+ if (typeof bin !== 'object' || bin === null || !('tsc' in bin) || typeof bin.tsc !== 'string') {
774
+ throw new Error('The TypeScript package declares no bin.tsc command')
775
+ }
776
+ return resolvePath(dirname(manifest), bin.tsc)
777
+ }
778
+
759
779
  /**
760
780
  * Parses a `tsc --showConfig` reading into the compiler scope a face's roll-up requires.
761
781
  *
@@ -886,7 +906,7 @@ export function declarationRollup(options: DeclarationRollupOptions): Plugin {
886
906
  )
887
907
  }
888
908
  const load = createRequire(import.meta.url)
889
- const compiler = load.resolve('typescript/bin/tsc')
909
+ const compiler = resolveCompiler(import.meta.url)
890
910
  const scope = parseProjectScope(
891
911
  readCompilerOutput(compiler, ['--showConfig', '-p', options.project]),
892
912
  options.project,
@@ -12,8 +12,20 @@ export interface PolicyExpression extends PolicyNode {
12
12
  readonly key?: PolicyExpression
13
13
  readonly id?: PolicyExpression | null
14
14
  readonly method?: boolean
15
- readonly kind?: 'const' | 'get' | 'init' | 'let' | 'set' | 'var'
15
+ readonly kind?:
16
+ | 'const'
17
+ | 'get'
18
+ | 'global'
19
+ | 'init'
20
+ | 'let'
21
+ | 'module'
22
+ | 'namespace'
23
+ | 'set'
24
+ | 'var'
16
25
  readonly expression?: boolean
26
+ readonly readonly?: boolean
27
+ readonly test?: PolicyExpression
28
+ readonly alternate?: PolicyExpression | null
17
29
  readonly object?: PolicyExpression
18
30
  readonly property?: PolicyExpression
19
31
  readonly computed?: boolean
@@ -25,10 +37,11 @@ export interface PolicyExpression extends PolicyNode {
25
37
  readonly quasis?: readonly PolicyExpression[]
26
38
  readonly expressions?: readonly PolicyExpression[]
27
39
  readonly accessibility?: 'private' | 'protected' | 'public' | null
40
+ readonly declare?: boolean
28
41
  readonly declaration?: PolicyExpression
29
42
  readonly declarations?: readonly PolicyExpression[]
30
43
  readonly init?: PolicyExpression | null
31
- readonly source?: PolicyExpression
44
+ readonly source?: PolicyExpression | null
32
45
  readonly specifiers?: readonly PolicyExpression[]
33
46
  readonly imported?: PolicyExpression
34
47
  readonly exported?: PolicyExpression
@@ -52,12 +65,30 @@ export interface PolicyComment extends PolicyNode {
52
65
  readonly value: string
53
66
  }
54
67
 
68
+ /** Describes one reference the scope manager records to a declared variable. */
69
+ export interface PolicyReference {
70
+ readonly identifier: PolicyNode
71
+ readonly init?: boolean
72
+ isWrite(): boolean
73
+ }
74
+
75
+ /** Describes one variable the scope manager records for a declaration. */
76
+ export interface PolicyVariable {
77
+ readonly identifiers: readonly PolicyNode[]
78
+ readonly references: readonly PolicyReference[]
79
+ }
80
+
55
81
  /** Lists the Oxlint source-text operations the comment rules read. */
56
82
  export interface PolicySourceCode {
57
83
  readonly text: string
58
84
  getAllComments(): readonly PolicyComment[]
59
85
  }
60
86
 
87
+ /** Adds the Oxlint scope operation the binding rule reads. */
88
+ export interface PolicyScopeCode extends PolicySourceCode {
89
+ getDeclaredVariables(node: PolicyNode): readonly PolicyVariable[]
90
+ }
91
+
61
92
  /** Pairs one doc block with the declared name its first sentence must not repeat. */
62
93
  export interface PolicyDoc {
63
94
  readonly comment: PolicyComment
@@ -89,7 +120,7 @@ export interface PolicyContext {
89
120
  readonly filename: string
90
121
  /** Names the directory Oxlint resolves `filename` against. */
91
122
  readonly cwd: string
92
- readonly sourceCode: PolicySourceCode
123
+ readonly sourceCode: PolicyScopeCode
93
124
  report(diagnostic: PolicyDiagnostic): void
94
125
  }
95
126
 
@@ -116,6 +147,9 @@ export interface PolicyVisitor {
116
147
  readonly FunctionExpression?: (node: PolicyNode) => void
117
148
  readonly ArrowFunctionExpression?: (node: PolicyNode) => void
118
149
  readonly ImportDeclaration?: (node: PolicyNode) => void
150
+ readonly ImportExpression?: (node: PolicyNode) => void
151
+ readonly ExportNamedDeclaration?: (node: PolicyNode) => void
152
+ readonly ExportAllDeclaration?: (node: PolicyNode) => void
119
153
  readonly MemberExpression?: (node: PolicyNode) => void
120
154
  readonly MethodDefinition?: (node: PolicyNode) => void
121
155
  readonly PropertyDefinition?: (node: PolicyNode) => void
@@ -127,6 +161,7 @@ export interface PolicyVisitor {
127
161
  readonly TSEnumDeclaration?: (node: PolicyNode) => void
128
162
  readonly TSInterfaceDeclaration?: (node: PolicyNode) => void
129
163
  readonly TSModuleDeclaration?: (node: PolicyNode) => void
164
+ readonly TSPropertySignature?: (node: PolicyNode) => void
130
165
  readonly TSTypeAliasDeclaration?: (node: PolicyNode) => void
131
166
  readonly VariableDeclaration?: (node: PolicyNode) => void
132
167
  }
@@ -205,9 +240,25 @@ export const DATA_EXEMPT_FILES: readonly string[] = Object.freeze(['helpers.ts']
205
240
  /** Lists the fleet-registered folders whose direct modules each contain one named function. */
206
241
  export const FUNCTION_DOMAIN_FOLDERS: readonly string[] = Object.freeze([
207
242
  'app/browser/composables',
243
+ 'app/vue/composables',
208
244
  'src/server/execution',
209
245
  ])
210
246
 
247
+ /** Lists the published destinations a Vue relative import may reach before independent restrictions. */
248
+ export const VUE_SOURCE_ROOTS: readonly string[] = Object.freeze([
249
+ 'src/core',
250
+ 'src/browser',
251
+ 'src/vue',
252
+ ])
253
+
254
+ /** Lists the additional private destinations an application Vue relative import may reach. */
255
+ export const VUE_APPLICATION_ROOTS: readonly string[] = Object.freeze([
256
+ ...VUE_SOURCE_ROOTS,
257
+ 'app/core',
258
+ 'app/browser',
259
+ 'app/vue',
260
+ ])
261
+
211
262
  /** Lists the registered function-domain names no source file may take as its stem. */
212
263
  export const FUNCTION_DOMAIN_NAMES: readonly string[] = Object.freeze(
213
264
  FUNCTION_DOMAIN_FOLDERS.map((folder) => folder.slice(folder.lastIndexOf('/') + 1)),
@@ -247,6 +298,68 @@ export const POLICY_ENDING_GLOBS: readonly string[] = Object.freeze([
247
298
  'src/**/*.ts',
248
299
  ])
249
300
 
301
+ /** Lists every source extension the nested-function rule reads. */
302
+ export const POLICY_CODE_EXTENSIONS: readonly string[] = Object.freeze([
303
+ 'cjs',
304
+ 'cts',
305
+ 'js',
306
+ 'jsx',
307
+ 'mjs',
308
+ 'mts',
309
+ 'ts',
310
+ 'tsx',
311
+ 'vue',
312
+ ])
313
+
314
+ /**
315
+ * Matches the lint population the nested-function rule runs over, as the Oxlint configuration
316
+ * declares it.
317
+ */
318
+ export const POLICY_NESTED_GLOBS: readonly string[] = Object.freeze(
319
+ ['src', 'app', 'tests', 'configs'].map(
320
+ (root) => `${root}/**/*.{${POLICY_CODE_EXTENSIONS.join(',')}}`,
321
+ ),
322
+ )
323
+
324
+ /**
325
+ * Matches the lint population the skill-script rules run over, as the Oxlint configuration declares
326
+ * it.
327
+ */
328
+ export const POLICY_SCRIPT_GLOBS: readonly string[] = Object.freeze([
329
+ '.agents/skills/*/scripts/*.ts',
330
+ ])
331
+
332
+ /** Matches the workspace-relative path of a skill script, the file shape the script rule reads. */
333
+ export const POLICY_SCRIPT_PATTERN = /^\.agents\/skills\/[^/]+\/scripts\/[^/]+\.ts$/u
334
+
335
+ /** Holds the one entry guard a skill script runs `main` under, as its source text reads. */
336
+ export const POLICY_ENTRY_GUARD =
337
+ 'process.argv[1] !== undefined && realpathSync(process.argv[1]) === import.meta.filename'
338
+
339
+ /** Lists every statement type that declares a name at module scope. */
340
+ export const POLICY_DECLARATION_TYPES: readonly string[] = Object.freeze([
341
+ 'ClassDeclaration',
342
+ 'FunctionDeclaration',
343
+ 'TSDeclareFunction',
344
+ 'TSEnumDeclaration',
345
+ 'TSInterfaceDeclaration',
346
+ 'TSModuleDeclaration',
347
+ 'TSTypeAliasDeclaration',
348
+ 'VariableDeclaration',
349
+ ])
350
+
351
+ /**
352
+ * Lists the type nodes a property signature's search for its owning declaration never crosses,
353
+ * because a parameter, call, or construct signature beyond one is an inline type.
354
+ */
355
+ export const POLICY_INLINE_TYPES: readonly string[] = Object.freeze([
356
+ 'TSCallSignatureDeclaration',
357
+ 'TSConstructSignatureDeclaration',
358
+ 'TSConstructorType',
359
+ 'TSFunctionType',
360
+ 'TSMethodSignature',
361
+ ])
362
+
250
363
  /**
251
364
  * Matches the file name shape an implementation file takes, holding the class that matches its
252
365
  * stem.
@@ -439,6 +552,69 @@ export function pathToPolicyRelative(filename: string, cwd: string): string {
439
552
  return normalizedFile.startsWith(prefix) ? normalizedFile.slice(prefix.length) : normalizedFile
440
553
  }
441
554
 
555
+ /**
556
+ * Computes a lexical workspace-relative destination from an absolute Vue importer and its cwd.
557
+ * Refuses unclassifiable locations, noncanonical relative segments, and traversal above the
558
+ * workspace. Query and fragment text is excluded from the path without decoding it; callers retain
559
+ * the original specifier for independent restrictions. No filesystem or module resolution occurs.
560
+ */
561
+ export function computePolicyTarget(
562
+ filename: string,
563
+ cwd: string,
564
+ specifier: string,
565
+ ): string | undefined {
566
+ const relative = pathToPolicyRelative(filename, cwd)
567
+ const location = relative.split('/')
568
+ if (
569
+ !/^(?:\/|[A-Za-z]:\/)/u.test(cwd.replaceAll('\\', '/')) ||
570
+ /(?:^|\/)\.{1,2}(?:\/|$)/u.test(cwd.replaceAll('\\', '/')) ||
571
+ relative === filename.replaceAll('\\', '/') ||
572
+ location.length < 3 ||
573
+ (location[0] !== 'src' && location[0] !== 'app') ||
574
+ location[1] !== 'vue' ||
575
+ location.some((part) => part === '' || part === '.' || part === '..')
576
+ ) {
577
+ return undefined
578
+ }
579
+ const path = specifier.split(/[?#]/u)[0] ?? ''
580
+ if (!/^\.{1,2}(?:\/|$)/u.test(path) || path.includes('\\') || path.includes('//')) {
581
+ return undefined
582
+ }
583
+ const parts = path.replace(/\/$/u, '').split('/')
584
+ const first = parts.findIndex((part) => part !== '.' && part !== '..')
585
+ const boundary = first === -1 ? parts.length : first
586
+ const leading = parts.slice(0, boundary)
587
+ const parents = leading.filter((part) => part === '..').length
588
+ const remaining = parts.slice(boundary)
589
+ const directory = pathToPolicyFolder(relative).split('/')
590
+ if (
591
+ (parents === 0 && leading.length !== 1) ||
592
+ (parents !== 0 && parents !== leading.length) ||
593
+ parents > directory.length ||
594
+ remaining.some((part) => part === '' || part === '.' || part === '..')
595
+ ) {
596
+ return undefined
597
+ }
598
+ return [...directory.slice(0, directory.length - parents), ...remaining].join('/')
599
+ }
600
+
601
+ /** Reports a literal relative import or re-export whose actual destination leaves its Vue boundary. */
602
+ export function reportVueImport(context: PolicyContext, node: PolicyExpression): void {
603
+ const source = node.source
604
+ if (source?.type !== 'Literal' || typeof source.value !== 'string') return
605
+ if (!/^\.{1,2}(?:[/?#]|$)/u.test(source.value)) return
606
+ const target = computePolicyTarget(context.filename, context.cwd, source.value)
607
+ const roots = pathToPolicyRelative(context.filename, context.cwd).startsWith('app/vue/')
608
+ ? VUE_APPLICATION_ROOTS
609
+ : VUE_SOURCE_ROOTS
610
+ if (
611
+ target !== undefined &&
612
+ roots.some((root) => target === root || target.startsWith(root + '/'))
613
+ )
614
+ return
615
+ context.report({ node, messageId: 'vue' })
616
+ }
617
+
442
618
  /** Returns the extensionless stem of one policy file name. */
443
619
  export function fileToPolicyStem(file: string): string {
444
620
  const boundary = file.lastIndexOf('.')
@@ -788,12 +964,37 @@ export function isPolicyVoiced(word: string): boolean {
788
964
  }
789
965
 
790
966
  /**
791
- * Pairs every exported top-level statement with the doc block written directly above it.
967
+ * Reads the doc block written directly above one top-level statement.
792
968
  *
793
969
  * @remarks
794
970
  * A statement takes the last comment that closes before it, and takes it only where that comment is
795
971
  * a doc block and nothing but whitespace separates the two. A blank line between them is still
796
- * whitespace, so the pairing survives one.
972
+ * whitespace, so the pairing survives one. Every comment is read where a `findLast` would stop at
973
+ * the nearest one.
974
+ *
975
+ * @param statement - The top-level statement to pair.
976
+ * @param comments - Every comment of the linted file, in source order.
977
+ * @param sourceCode - The source-text reader supplying the text between a comment and the statement.
978
+ * @returns The paired doc block, or `undefined` where the statement has none.
979
+ */
980
+ export function statementToPolicyDoc(
981
+ statement: PolicyExpression,
982
+ comments: readonly PolicyComment[],
983
+ sourceCode: PolicySourceCode,
984
+ ): PolicyComment | undefined {
985
+ const start = statement.range[0]
986
+ const previous = Iterator.from(comments)
987
+ .flatMap((comment) => (comment.range[1] <= start ? [comment] : []))
988
+ .toArray()
989
+ .at(-1)
990
+ if (previous === undefined || previous.type !== 'Block') return undefined
991
+ if (!previous.value.startsWith('*')) return undefined
992
+ if (sourceCode.text.slice(previous.range[1], start).trim() !== '') return undefined
993
+ return previous
994
+ }
995
+
996
+ /**
997
+ * Pairs every exported top-level statement with the doc block written directly above it.
797
998
  *
798
999
  * @param node - The program node whose top-level statements are read.
799
1000
  * @param sourceCode - The source-text reader supplying the comments and the text between them.
@@ -804,24 +1005,18 @@ export function programToPolicyDocs(
804
1005
  sourceCode: PolicySourceCode,
805
1006
  ): readonly PolicyDoc[] {
806
1007
  // The iterator consumes one entry at a time and reads a hole as an `undefined` entry, as a
807
- // `for…of` over the list would. The comments are read only for an export statement, once per
808
- // statement, and every comment is read where a `findLast` would stop at the nearest one.
1008
+ // `for…of` over the list would. The comments are read once, and a statement pairs with one only
1009
+ // where it is an export.
809
1010
  const comments = sourceCode.getAllComments()
810
1011
  return Iterator.from(programToPolicyStatements(node))
811
1012
  .flatMap((statement) => {
812
1013
  if (!statement.type.startsWith('Export')) return []
813
- const start = statement.range[0]
814
- const previous = Iterator.from(comments)
815
- .flatMap((comment) => (comment.range[1] <= start ? [comment] : []))
816
- .toArray()
817
- .at(-1)
818
- if (previous === undefined || previous.type !== 'Block') return []
819
- if (!previous.value.startsWith('*')) return []
820
- if (sourceCode.text.slice(previous.range[1], start).trim() !== '') return []
1014
+ const comment = statementToPolicyDoc(statement, comments, sourceCode)
1015
+ if (comment === undefined) return []
821
1016
  const declaration = statement.declaration
822
1017
  return [
823
1018
  {
824
- comment: previous,
1019
+ comment,
825
1020
  name:
826
1021
  identifierToPolicyName(declaration?.id) ??
827
1022
  identifierToPolicyName(declaration?.declarations?.[0]?.id),
@@ -941,6 +1136,54 @@ export function reportPrivacy(context: PolicyContext, node: PolicyExpression): v
941
1136
  }
942
1137
  }
943
1138
 
1139
+ /** Checks whether code writes a declared variable anywhere but in its own declaration. */
1140
+ export function isPolicyReassigned(variable: PolicyVariable): boolean {
1141
+ return variable.references.some(
1142
+ (reference) =>
1143
+ reference.isWrite() &&
1144
+ reference.init !== true &&
1145
+ !variable.identifiers.includes(reference.identifier),
1146
+ )
1147
+ }
1148
+
1149
+ /**
1150
+ * Checks whether a node sits inside a `declare` module, namespace, or global augmentation.
1151
+ *
1152
+ * @remarks
1153
+ * A declaration inside such a block carries no `declare` flag of its own and no value, because the
1154
+ * block is the ambient context. A `global` block is ambient wherever it stands.
1155
+ */
1156
+ export function isPolicyInDeclaredModule(node: PolicyExpression): boolean {
1157
+ const parent = node.parent
1158
+ if (parent === undefined || parent === null) return false
1159
+ return (
1160
+ (parent.type === 'TSModuleDeclaration' &&
1161
+ (parent.declare === true || parent.kind === 'global')) ||
1162
+ isPolicyInDeclaredModule(parent)
1163
+ )
1164
+ }
1165
+
1166
+ /** Reports a let declaration that prefer-const leaves: one reassigned or declared without a value. */
1167
+ export function reportLet(context: PolicyContext, node: PolicyExpression): void {
1168
+ if (
1169
+ node.kind !== 'let' ||
1170
+ node.declare === true ||
1171
+ isPolicyAmbient(context.filename) ||
1172
+ isPolicyInDeclaredModule(node)
1173
+ ) {
1174
+ return
1175
+ }
1176
+ const head = node.parent?.type === 'ForInStatement' || node.parent?.type === 'ForOfStatement'
1177
+ const bare =
1178
+ !head &&
1179
+ (node.declarations ?? []).some(
1180
+ (declarator) => declarator.init === undefined || declarator.init === null,
1181
+ )
1182
+ if (bare || context.sourceCode.getDeclaredVariables(node).some(isPolicyReassigned)) {
1183
+ context.report({ node, messageId: 'let' })
1184
+ }
1185
+ }
1186
+
944
1187
  /** Reports a centralized declaration that carries no export. */
945
1188
  export function reportHidden(context: PolicyContext, node: PolicyExpression): void {
946
1189
  if (isPolicyAmbient(context.filename)) return
@@ -1111,6 +1354,64 @@ export function reportEndingImport(context: PolicyContext, node: PolicyExpressio
1111
1354
  if (importsPolicyTerminator(node)) context.report({ node, messageId: 'terminator' })
1112
1355
  }
1113
1356
 
1357
+ /** Reports whether a path is a skill script, the one file shape the script rule reads. */
1358
+ export function isPolicyScript(filename: string, cwd: string): boolean {
1359
+ return POLICY_SCRIPT_PATTERN.test(pathToPolicyRelative(filename, cwd))
1360
+ }
1361
+
1362
+ /** Reports whether a statement is the one entry guard a skill script runs its main under. */
1363
+ export function isPolicyGuard(statement: PolicyExpression, sourceCode: PolicySourceCode): boolean {
1364
+ const test = statement.test
1365
+ if (statement.type !== 'IfStatement' || test === undefined) return false
1366
+ if (statement.alternate !== undefined && statement.alternate !== null) return false
1367
+ const text = sourceCode.text.slice(test.range[0], test.range[1]).replace(/\s+/gu, ' ')
1368
+ return text === POLICY_ENTRY_GUARD
1369
+ }
1370
+
1371
+ /** Reports a skill script's top-level statement that is hidden, undocumented, or runs at import. */
1372
+ export function reportScript(context: PolicyContext, node: PolicyExpression): void {
1373
+ if (isPolicyAmbient(context.filename) || !isPolicyScript(context.filename, context.cwd)) return
1374
+ const sourceCode = context.sourceCode
1375
+ const comments = sourceCode.getAllComments()
1376
+ const statements = programToPolicyStatements(node)
1377
+ const guard = statements.findIndex((statement) => isPolicyGuard(statement, sourceCode))
1378
+ for (const [index, statement] of statements.entries()) {
1379
+ if (statement.type === 'ImportDeclaration' || index === guard) continue
1380
+ const declaration = statement.declaration
1381
+ if (
1382
+ statement.type === 'ExportNamedDeclaration' &&
1383
+ declaration !== undefined &&
1384
+ declaration !== null
1385
+ ) {
1386
+ if (statementToPolicyDoc(statement, comments, sourceCode) === undefined) {
1387
+ context.report({ node: statement, messageId: 'doc' })
1388
+ }
1389
+ continue
1390
+ }
1391
+ context.report({
1392
+ node: statement,
1393
+ messageId: POLICY_DECLARATION_TYPES.includes(statement.type) ? 'hidden' : 'entry',
1394
+ })
1395
+ }
1396
+ }
1397
+
1398
+ /** Reports whether a property signature sits in an interface or in a type alias's object type. */
1399
+ export function isPolicyOwned(node: PolicyExpression): boolean {
1400
+ const parent = node.parent
1401
+ if (parent === undefined || parent === null) return false
1402
+ if (parent.type === 'TSInterfaceDeclaration' || parent.type === 'TSTypeAliasDeclaration') {
1403
+ return true
1404
+ }
1405
+ if (POLICY_INLINE_TYPES.includes(parent.type)) return false
1406
+ return isPolicyOwned(parent)
1407
+ }
1408
+
1409
+ /** Reports a property signature of an interface or type alias that is not readonly. */
1410
+ export function reportMutable(context: PolicyContext, node: PolicyExpression): void {
1411
+ if (isPolicyAmbient(context.filename) || node.readonly === true) return
1412
+ if (isPolicyOwned(node)) context.report({ node, messageId: 'mutable' })
1413
+ }
1414
+
1114
1415
  /** Bans function declarations and assignments inside another function body. */
1115
1416
  export const NESTED_RULE: PolicyRuleInterface = {
1116
1417
  meta: {
@@ -1180,6 +1481,25 @@ export const PRIVACY_RULE: PolicyRuleInterface = {
1180
1481
  },
1181
1482
  }
1182
1483
 
1484
+ /** Bans a let declaration that prefer-const does not already report. */
1485
+ export const LET_RULE: PolicyRuleInterface = {
1486
+ meta: {
1487
+ type: 'problem',
1488
+ docs: {
1489
+ description:
1490
+ 'Disallow a let declaration that is reassigned or declared without a value; prefer-const reports the rest.',
1491
+ },
1492
+ messages: {
1493
+ let: 'Declare this binding with const and derive its value: an expression, a helper that returns it, or a for…of over entries() in place of a counter.',
1494
+ },
1495
+ },
1496
+ create(context) {
1497
+ return {
1498
+ VariableDeclaration: (node) => reportLet(context, node),
1499
+ }
1500
+ },
1501
+ }
1502
+
1183
1503
  /** Bans a centralized declaration that no export reaches. */
1184
1504
  export const HIDDEN_RULE: PolicyRuleInterface = {
1185
1505
  meta: {
@@ -1399,6 +1719,28 @@ export const DOMAIN_RULE: PolicyRuleInterface = {
1399
1719
  },
1400
1720
  }
1401
1721
 
1722
+ /** Bans literal relative imports and re-exports outside the importing Vue face's permitted roots. */
1723
+ export const VUE_IMPORT_RULE: PolicyRuleInterface = {
1724
+ meta: {
1725
+ type: 'problem',
1726
+ docs: {
1727
+ description:
1728
+ 'Disallow Vue relative imports outside the faces their importing file may reach.',
1729
+ },
1730
+ messages: {
1731
+ vue: 'Keep this Vue relative import within the permitted source and application faces.',
1732
+ },
1733
+ },
1734
+ create(context) {
1735
+ return {
1736
+ ImportDeclaration: (node) => reportVueImport(context, node),
1737
+ ImportExpression: (node) => reportVueImport(context, node),
1738
+ ExportNamedDeclaration: (node) => reportVueImport(context, node),
1739
+ ExportAllDeclaration: (node) => reportVueImport(context, node),
1740
+ }
1741
+ },
1742
+ }
1743
+
1402
1744
  /** Bans host-specific line-ending handling. */
1403
1745
  export const ENDING_RULE: PolicyRuleInterface = {
1404
1746
  meta: {
@@ -1442,6 +1784,49 @@ export const VOICE_RULE: PolicyRuleInterface = {
1442
1784
  },
1443
1785
  }
1444
1786
 
1787
+ /** Bans a skill script top-level statement that is hidden, undocumented, or runs at import. */
1788
+ export const SCRIPT_RULE: PolicyRuleInterface = {
1789
+ meta: {
1790
+ type: 'problem',
1791
+ docs: {
1792
+ description:
1793
+ 'Disallow an unexported declaration, an undocumented export, and a statement beyond the one guarded entry in a skill script.',
1794
+ },
1795
+ messages: {
1796
+ hidden:
1797
+ 'Export this declaration; a skill script exports every declaration so its proof can test each one.',
1798
+ doc: 'Write a one-sentence TSDoc summary directly above this export.',
1799
+ entry:
1800
+ 'Move this statement into a function; a skill script holds imports, exported declarations, and the one process.argv[1] guard that runs main, and an import runs nothing.',
1801
+ },
1802
+ },
1803
+ create(context) {
1804
+ return {
1805
+ Program: (node) => reportScript(context, node),
1806
+ }
1807
+ },
1808
+ }
1809
+
1810
+ /** Bans a property signature in an interface or type alias that is not readonly. */
1811
+ export const MUTABLE_RULE: PolicyRuleInterface = {
1812
+ meta: {
1813
+ type: 'problem',
1814
+ docs: {
1815
+ description:
1816
+ 'Disallow a property signature without readonly in an interface or in the object type of a type alias.',
1817
+ },
1818
+ messages: {
1819
+ mutable:
1820
+ 'Declare this property readonly; a contract exposes its members and no holder rewrites them.',
1821
+ },
1822
+ },
1823
+ create(context) {
1824
+ return {
1825
+ TSPropertySignature: (node) => reportMutable(context, node),
1826
+ }
1827
+ },
1828
+ }
1829
+
1445
1830
  /** Bans a comment carrying a term the substitution table bans unconditionally. */
1446
1831
  export const TERM_RULE: PolicyRuleInterface = {
1447
1832
  meta: {
@@ -1467,6 +1852,7 @@ export default {
1467
1852
  rules: {
1468
1853
  'no-mocking': MOCKING_RULE,
1469
1854
  'no-keyword-privacy': PRIVACY_RULE,
1855
+ 'no-let': LET_RULE,
1470
1856
  'no-nested-functions': NESTED_RULE,
1471
1857
  'no-hidden-declaration': HIDDEN_RULE,
1472
1858
  'no-misplaced-type': TYPE_RULE,
@@ -1478,8 +1864,11 @@ export default {
1478
1864
  'no-misnamed-factory': FACTORY_RULE,
1479
1865
  'no-misnamed-plugin': PLUGIN_RULE,
1480
1866
  'no-malformed-domain': DOMAIN_RULE,
1867
+ 'no-cross-vue-face': VUE_IMPORT_RULE,
1481
1868
  'no-host-line-endings': ENDING_RULE,
1482
1869
  'no-malformed-summary': VOICE_RULE,
1483
1870
  'no-banned-term': TERM_RULE,
1871
+ 'no-malformed-script': SCRIPT_RULE,
1872
+ 'no-mutable-property': MUTABLE_RULE,
1484
1873
  },
1485
1874
  }
@@ -11,6 +11,7 @@ alwaysApply: true
11
11
 
12
12
  - Invoked through the `grok` bridge (`agent -p --mode=ask`): act as a read-only executor. Return distilled evidence with `file:line` pointers. Never edit, decide, or accept.
13
13
  - Driven by the user: act as the Orchestrator per `.agents/orchestration.md`, on Grok.
14
+ - `.agents/skills/orkestrel-parallel/references/cursor.md` owns Cursor's lane mechanics and quirks; `.agents/platforms/` owns the host shell.
14
15
 
15
16
  ## Reaching the other engines
16
17