@orkestrel/scaffold 0.0.101 → 0.0.103

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 (59) hide show
  1. package/dist/agents/skills/orkestrel-dispatch/scripts/brief.js +6 -14
  2. package/dist/agents/skills/orkestrel-dispatch/scripts/cite.js +41 -28
  3. package/dist/agents/skills/orkestrel-dispatch/scripts/helpers.js +5 -12
  4. package/dist/agents/skills/orkestrel-dispatch/scripts/launch.js +25 -13
  5. package/dist/agents/skills/orkestrel-dispatch/scripts/login.js +18 -25
  6. package/dist/agents/skills/orkestrel-dispatch/scripts/result.js +11 -9
  7. package/dist/agents/skills/orkestrel-harden/scripts/discovery.js +233 -232
  8. package/dist/agents/skills/orkestrel-publish/scripts/compare.js +95 -89
  9. package/dist/agents/skills/orkestrel-publish/scripts/pins.js +5 -7
  10. package/dist/agents/skills/orkestrel-publish/scripts/swap.js +1273 -0
  11. package/dist/agents/skills/orkestrel-publish/scripts/wave.js +99 -89
  12. package/dist/agents/skills/orkestrel-publish/scripts/window.js +54 -52
  13. package/dist/agents/skills/orkestrel-scout/scripts/map.js +160 -161
  14. package/dist/bin/main.js +483 -444
  15. package/dist/bin/main.js.map +1 -1
  16. package/dist/host/AGENTS.md +1 -0
  17. package/dist/host/agents/skills/orkestrel-dispatch/SKILL.md +10 -10
  18. package/dist/host/agents/skills/orkestrel-dispatch/scripts/brief.ts +8 -11
  19. package/dist/host/agents/skills/orkestrel-dispatch/scripts/cite.ts +58 -28
  20. package/dist/host/agents/skills/orkestrel-dispatch/scripts/helpers.ts +5 -10
  21. package/dist/host/agents/skills/orkestrel-dispatch/scripts/launch.ts +31 -12
  22. package/dist/host/agents/skills/orkestrel-dispatch/scripts/login.ts +28 -23
  23. package/dist/host/agents/skills/orkestrel-dispatch/scripts/result.ts +12 -10
  24. package/dist/host/agents/skills/orkestrel-harden/scripts/discovery.ts +318 -237
  25. package/dist/host/agents/skills/orkestrel-journey/references/statechart.md +2 -5
  26. package/dist/host/agents/skills/orkestrel-publish/SKILL.md +7 -6
  27. package/dist/host/agents/skills/orkestrel-publish/references/release.md +12 -10
  28. package/dist/host/agents/skills/orkestrel-publish/references/wave.md +12 -5
  29. package/dist/host/agents/skills/orkestrel-publish/scripts/compare.ts +124 -82
  30. package/dist/host/agents/skills/orkestrel-publish/scripts/pins.ts +5 -7
  31. package/dist/host/agents/skills/orkestrel-publish/scripts/swap.ts +1655 -0
  32. package/dist/host/agents/skills/orkestrel-publish/scripts/wave.ts +128 -97
  33. package/dist/host/agents/skills/orkestrel-publish/scripts/window.ts +70 -55
  34. package/dist/host/agents/skills/orkestrel-scout/scripts/map.ts +215 -155
  35. package/dist/host/claude/rules/architecture.md +2 -2
  36. package/dist/host/claude/rules/tests.md +8 -2
  37. package/dist/host/claude/rules/typescript.md +130 -0
  38. package/dist/host/claude/rules/workspace.md +33 -18
  39. package/dist/host/configs/helpers.ts +209 -148
  40. package/dist/host/configs/policy.ts +110 -105
  41. package/dist/host/dotfiles/oxlintrc.json +1 -11
  42. package/dist/host/guides/scaffold.md +244 -170
  43. package/dist/host/manifest.json +36 -30
  44. package/dist/host/tests/config.test.ts +1263 -687
  45. package/dist/host/tests/policy.test.ts +19 -0
  46. package/dist/host/tests/setupPolicy.ts +1699 -1026
  47. package/dist/src/core/index.cjs +1595 -1686
  48. package/dist/src/core/index.cjs.map +1 -1
  49. package/dist/src/core/index.d.cts +202 -18
  50. package/dist/src/core/index.d.ts +202 -18
  51. package/dist/src/core/index.js +1590 -1687
  52. package/dist/src/core/index.js.map +1 -1
  53. package/dist/src/server/index.cjs +448 -392
  54. package/dist/src/server/index.cjs.map +1 -1
  55. package/dist/src/server/index.d.cts +1182 -1052
  56. package/dist/src/server/index.d.ts +1182 -1052
  57. package/dist/src/server/index.js +447 -395
  58. package/dist/src/server/index.js.map +1 -1
  59. package/package.json +2 -2
@@ -536,31 +536,29 @@ export function isPolicyDeclaredFunction(node: PolicyExpression): boolean {
536
536
 
537
537
  /** Returns a policy function's position through parentheses, object values, and array elements. */
538
538
  export function functionToPolicyPosition(node: PolicyExpression): PolicyExpression {
539
- let position = node
540
- while (position.parent !== undefined && position.parent !== null) {
541
- const parent = position.parent
542
- if (parent.type === 'ParenthesizedExpression') {
543
- position = parent
544
- continue
545
- }
546
- if (
547
- parent.type === 'Property' &&
548
- parent.kind === 'init' &&
549
- parent.method === false &&
550
- parent.computed !== true &&
551
- parent.value === position &&
552
- parent.parent?.type === 'ObjectExpression'
553
- ) {
554
- position = parent.parent
555
- continue
556
- }
557
- if (parent.type === 'ArrayExpression' && parent.elements?.includes(position) === true) {
558
- position = parent
559
- continue
560
- }
561
- break
539
+ const parent = node.parent
540
+ if (parent === undefined || parent === null) return node
541
+ if (parent.type === 'ParenthesizedExpression') return functionToPolicyPosition(parent)
542
+ if (
543
+ parent.type === 'Property' &&
544
+ parent.kind === 'init' &&
545
+ parent.method === false &&
546
+ parent.computed !== true &&
547
+ parent.value === node &&
548
+ parent.parent?.type === 'ObjectExpression'
549
+ ) {
550
+ return functionToPolicyPosition(parent.parent)
551
+ }
552
+ if (parent.type === 'ArrayExpression' && parent.elements?.includes(node) === true) {
553
+ return functionToPolicyPosition(parent)
562
554
  }
563
- return position
555
+ return node
556
+ }
557
+
558
+ /** Returns a policy node's position through its enclosing parentheses alone. */
559
+ export function nodeToPolicyOuter(node: PolicyExpression): PolicyExpression {
560
+ const parent = node.parent
561
+ return parent?.type === 'ParenthesizedExpression' ? nodeToPolicyOuter(parent) : node
564
562
  }
565
563
 
566
564
  /** Reports whether a literal position is admitted as an argument, return, or arrow body. */
@@ -611,20 +609,22 @@ export function isPolicyMethod(node: PolicyExpression): boolean {
611
609
  /**
612
610
  * Reports whether a policy function sits inside another function before any class-expression
613
611
  * boundary.
612
+ *
613
+ * @param node - The function node whose ancestors are read.
614
+ * @param method - If `true`, a method body was already crossed on the way up; if `false`, none was.
615
+ * Default: `false`.
616
+ * @returns True if a non-method function, or a method inside a class declaration, encloses the node
617
+ * before a class expression does; false otherwise.
614
618
  */
615
- export function hasPolicyFunctionAncestor(node: PolicyExpression): boolean {
616
- let parent = node.parent
617
- let method = false
618
- while (parent !== undefined && parent !== null) {
619
- if (parent.type === 'ClassExpression') return false
620
- if (parent.type === 'ClassDeclaration' && method) return true
621
- if (isPolicyFunction(parent)) {
622
- if (!isPolicyMethod(parent)) return true
623
- method = true
624
- }
625
- parent = parent.parent
619
+ export function hasPolicyFunctionAncestor(node: PolicyExpression, method = false): boolean {
620
+ const parent = node.parent
621
+ if (parent === undefined || parent === null) return method
622
+ if (parent.type === 'ClassExpression') return false
623
+ if (parent.type === 'ClassDeclaration' && method) return true
624
+ if (isPolicyFunction(parent)) {
625
+ return !isPolicyMethod(parent) || hasPolicyFunctionAncestor(parent, true)
626
626
  }
627
- return method
627
+ return hasPolicyFunctionAncestor(parent, method)
628
628
  }
629
629
 
630
630
  /**
@@ -634,15 +634,11 @@ export function hasPolicyFunctionAncestor(node: PolicyExpression): boolean {
634
634
  * reads module regions rather than class members.
635
635
  */
636
636
  export function functionToPolicyRegion(node: PolicyExpression): PolicyExpression | undefined {
637
- let current = node
638
- let parent = current.parent
639
- while (parent !== undefined && parent !== null) {
640
- if (parent.type === 'ClassDeclaration' || parent.type === 'ClassExpression') return undefined
641
- if (parent.type === 'Program') return statementToPolicyDeclaration(current)
642
- current = parent
643
- parent = parent.parent
644
- }
645
- return undefined
637
+ const parent = node.parent
638
+ if (parent === undefined || parent === null) return undefined
639
+ if (parent.type === 'ClassDeclaration' || parent.type === 'ClassExpression') return undefined
640
+ if (parent.type === 'Program') return statementToPolicyDeclaration(node)
641
+ return functionToPolicyRegion(parent)
646
642
  }
647
643
 
648
644
  /** Lists every module function a top-level statement declares, paired with its declared name. */
@@ -651,15 +647,16 @@ export function statementToPolicyBindings(node: PolicyExpression): readonly Poli
651
647
  return [{ node, name: identifierToPolicyName(node.id) }]
652
648
  }
653
649
  if (node.type !== 'VariableDeclaration') return []
654
- const bindings: PolicyBinding[] = []
655
- for (const declarator of node.declarations ?? []) {
656
- const init = declarator.init
657
- if (init === undefined || init === null || !isPolicyFunction(init)) continue
658
- const name = identifierToPolicyName(declarator.id)
659
- if (name === undefined) continue
660
- bindings.push({ node: declarator, name })
661
- }
662
- return bindings
650
+ // The iterator consumes one declarator at a time and reads a hole as an `undefined` declarator,
651
+ // as a `for…of` over the list would.
652
+ return Iterator.from(node.declarations ?? [])
653
+ .flatMap((declarator) => {
654
+ const init = declarator.init
655
+ if (init === undefined || init === null || !isPolicyFunction(init)) return []
656
+ const name = identifierToPolicyName(declarator.id)
657
+ return name === undefined ? [] : [{ node: declarator, name }]
658
+ })
659
+ .toArray()
663
660
  }
664
661
 
665
662
  /** Reports whether a call trims a whole payload before splitting it on a line feed. */
@@ -735,11 +732,9 @@ export function stripPolicyCode(text: string): string {
735
732
  * @returns One hit per match, each naming its row and the offset the match starts at.
736
733
  */
737
734
  export function textToPolicyHits(text: string): readonly PolicyHit[] {
738
- const hits: PolicyHit[] = []
739
- for (const term of POLICY_BANNED_TERMS) {
740
- for (const match of text.matchAll(term.pattern)) hits.push({ term, index: match.index })
741
- }
742
- return hits.sort((left, right) => left.index - right.index)
735
+ return POLICY_BANNED_TERMS.flatMap((term) =>
736
+ [...text.matchAll(term.pattern)].map((match) => ({ term, index: match.index })),
737
+ ).sort((left, right) => left.index - right.index)
743
738
  }
744
739
 
745
740
  /**
@@ -749,13 +744,15 @@ export function textToPolicyHits(text: string): readonly PolicyHit[] {
749
744
  * @returns The description with continuation markers removed and whitespace collapsed.
750
745
  */
751
746
  export function commentToPolicyParagraph(comment: PolicyComment): string {
752
- const description: string[] = []
753
- for (const line of comment.value.split(POLICY_BREAK_PATTERN)) {
754
- const text = line.replace(POLICY_MARKER_PATTERN, '')
755
- if (text.trimStart().startsWith('@')) break
756
- description.push(text)
757
- }
758
- return description.join(' ').replace(/\s+/gu, ' ').trim()
747
+ const lines = comment.value
748
+ .split(POLICY_BREAK_PATTERN)
749
+ .map((line) => line.replace(POLICY_MARKER_PATTERN, ''))
750
+ const end = lines.findIndex((text) => text.trimStart().startsWith('@'))
751
+ return lines
752
+ .slice(0, end === -1 ? lines.length : end)
753
+ .join(' ')
754
+ .replace(/\s+/gu, ' ')
755
+ .trim()
759
756
  }
760
757
 
761
758
  /**
@@ -795,27 +792,32 @@ export function programToPolicyDocs(
795
792
  node: PolicyExpression,
796
793
  sourceCode: PolicySourceCode,
797
794
  ): readonly PolicyDoc[] {
795
+ // The iterator consumes one entry at a time and reads a hole as an `undefined` entry, as a
796
+ // `for…of` over the list would. The comments are read only for an export statement, once per
797
+ // statement, and every comment is read where a `findLast` would stop at the nearest one.
798
798
  const comments = sourceCode.getAllComments()
799
- const docs: PolicyDoc[] = []
800
- for (const statement of programToPolicyStatements(node)) {
801
- if (!statement.type.startsWith('Export')) continue
802
- const start = statement.range[0]
803
- let previous: PolicyComment | undefined
804
- for (const comment of comments) {
805
- if (comment.range[1] <= start) previous = comment
806
- }
807
- if (previous === undefined || previous.type !== 'Block') continue
808
- if (!previous.value.startsWith('*')) continue
809
- if (sourceCode.text.slice(previous.range[1], start).trim() !== '') continue
810
- const declaration = statement.declaration
811
- docs.push({
812
- comment: previous,
813
- name:
814
- identifierToPolicyName(declaration?.id) ??
815
- identifierToPolicyName(declaration?.declarations?.[0]?.id),
799
+ return Iterator.from(programToPolicyStatements(node))
800
+ .flatMap((statement) => {
801
+ if (!statement.type.startsWith('Export')) return []
802
+ const start = statement.range[0]
803
+ const previous = Iterator.from(comments)
804
+ .flatMap((comment) => (comment.range[1] <= start ? [comment] : []))
805
+ .toArray()
806
+ .at(-1)
807
+ if (previous === undefined || previous.type !== 'Block') return []
808
+ if (!previous.value.startsWith('*')) return []
809
+ if (sourceCode.text.slice(previous.range[1], start).trim() !== '') return []
810
+ const declaration = statement.declaration
811
+ return [
812
+ {
813
+ comment: previous,
814
+ name:
815
+ identifierToPolicyName(declaration?.id) ??
816
+ identifierToPolicyName(declaration?.declarations?.[0]?.id),
817
+ },
818
+ ]
816
819
  })
817
- }
818
- return docs
820
+ .toArray()
819
821
  }
820
822
 
821
823
  /** Reports a doc block whose first sentence is not a third-person summary of its own symbol. */
@@ -981,8 +983,7 @@ export function reportFunction(context: PolicyContext, node: PolicyExpression):
981
983
  return
982
984
  }
983
985
  if (region.type !== 'VariableDeclaration') return
984
- let position = node
985
- while (position.parent?.type === 'ParenthesizedExpression') position = position.parent
986
+ const position = nodeToPolicyOuter(node)
986
987
  // Module placement admits direct callbacks; a literal member still belongs in a function file.
987
988
  if (
988
989
  (node.type === 'ArrowFunctionExpression' || node.id === null) &&
@@ -1062,24 +1063,28 @@ export function reportDomain(context: PolicyContext, node: PolicyExpression): vo
1062
1063
  }
1063
1064
  if (!isPolicyDomain(context.filename, context.cwd)) return
1064
1065
  const expected = fileToPolicyStem(file)
1065
- let implementations = 0
1066
- let malformed = 0
1067
- for (const statement of programToPolicyStatements(node)) {
1068
- if (statement.type === 'ImportDeclaration') continue
1069
- const declaration = statementToPolicyDeclaration(statement)
1070
- if (declaration === undefined || !isPolicyDeclaredFunction(declaration)) {
1071
- malformed += 1
1072
- continue
1073
- }
1074
- if (declaration.type === 'FunctionDeclaration') implementations += 1
1075
- if (
1076
- statement.type !== 'ExportNamedDeclaration' ||
1077
- identifierToPolicyName(declaration.id) !== expected
1078
- ) {
1079
- malformed += 1
1080
- }
1081
- }
1082
- if (implementations !== 1 || malformed > 0) context.report({ node, messageId: 'module' })
1066
+ // The iterator consumes one statement at a time and reads a hole as an `undefined` statement, as
1067
+ // a `for…of` over the body would. Each statement is judged whole before the next is read.
1068
+ const members = Iterator.from(programToPolicyStatements(node))
1069
+ .flatMap((statement) => {
1070
+ if (statement.type === 'ImportDeclaration') return []
1071
+ const declaration = statementToPolicyDeclaration(statement)
1072
+ if (declaration === undefined || !isPolicyDeclaredFunction(declaration)) {
1073
+ return [{ implementation: false, malformed: true }]
1074
+ }
1075
+ return [
1076
+ {
1077
+ implementation: declaration.type === 'FunctionDeclaration',
1078
+ malformed:
1079
+ statement.type !== 'ExportNamedDeclaration' ||
1080
+ identifierToPolicyName(declaration.id) !== expected,
1081
+ },
1082
+ ]
1083
+ })
1084
+ .toArray()
1085
+ const implementations = members.filter(({ implementation }) => implementation).length
1086
+ const malformed = members.some((member) => member.malformed)
1087
+ if (implementations !== 1 || malformed) context.report({ node, messageId: 'module' })
1083
1088
  }
1084
1089
 
1085
1090
  /** Reports source that reads the host line ending or splits arrived text before trimming it. */
@@ -38,7 +38,7 @@
38
38
  ],
39
39
 
40
40
  "import/no-default-export": "error",
41
- "import/no-dynamic-require": ["error", { "esmodule": true }],
41
+ "import/no-dynamic-require": ["error", { "esmodule": false }],
42
42
  "import/extensions": "off",
43
43
  "import/no-unassigned-import": [
44
44
  "error",
@@ -70,16 +70,6 @@
70
70
  "restriction": "off"
71
71
  },
72
72
  "overrides": [
73
- {
74
- "files": [
75
- "src/server/artifacts/**/*.{js,mjs,ts,mts}",
76
- "tools/artifacts/**/*.{js,mjs,ts,mts}",
77
- ".orkestrel/*/instruments/**/tools/artifacts/**/*.{js,mjs,ts,mts}"
78
- ],
79
- "rules": {
80
- "import/no-dynamic-require": ["error", { "esmodule": false }]
81
- }
82
- },
83
73
  {
84
74
  "files": ["*.config.ts", "*.config.js"],
85
75
  "rules": {