@orkestrel/scaffold 0.0.67 → 0.0.69
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.
- package/dist/bin/main.js +67 -44
- package/dist/bin/main.js.map +1 -1
- package/dist/host/agents/templates/brief.md +9 -0
- package/dist/host/claude/agents/orkestrel.md +4 -4
- package/dist/host/claude/rules/names.md +15 -0
- package/dist/host/claude/rules/tests.md +33 -4
- package/dist/host/claude/rules/workspace.md +14 -2
- package/dist/host/dotfiles/prettierignore +3 -0
- package/dist/host/guides/README.md +65 -0
- package/dist/host/guides/abort.md +169 -0
- package/dist/host/guides/agent.md +1567 -0
- package/dist/host/guides/brief.md +1266 -0
- package/dist/host/guides/browser.md +2200 -0
- package/dist/host/guides/budget.md +196 -0
- package/dist/host/guides/codec.md +519 -0
- package/dist/host/guides/console.md +785 -0
- package/dist/host/guides/contract.md +1193 -0
- package/dist/host/guides/csv.md +541 -0
- package/dist/host/guides/database.md +2518 -0
- package/dist/host/guides/emitter.md +233 -0
- package/dist/host/guides/form.md +1791 -0
- package/dist/host/guides/html.md +717 -0
- package/dist/host/guides/indexeddb.md +505 -0
- package/dist/host/guides/interpret.md +1029 -0
- package/dist/host/guides/lsp.md +515 -0
- package/dist/host/guides/markdown.md +964 -0
- package/dist/host/guides/mcp.md +5554 -0
- package/dist/host/guides/middleware.md +927 -0
- package/dist/host/guides/msg.md +440 -0
- package/dist/host/guides/ndjson.md +120 -0
- package/dist/host/guides/ollama.md +380 -0
- package/dist/host/guides/pool.md +280 -0
- package/dist/host/guides/probe.md +1210 -0
- package/dist/host/guides/process.md +1620 -0
- package/dist/host/guides/program.md +1110 -0
- package/dist/host/guides/qualifier.md +854 -0
- package/dist/host/guides/queue.md +370 -0
- package/dist/host/guides/rater.md +330 -0
- package/dist/host/guides/reason.md +1122 -0
- package/dist/host/guides/relation.md +373 -0
- package/dist/host/guides/router.md +753 -0
- package/dist/host/guides/scaffold.md +192 -31
- package/dist/host/guides/sea.md +383 -0
- package/dist/host/guides/server.md +752 -0
- package/dist/host/guides/sqlite.md +330 -0
- package/dist/host/guides/sse.md +187 -0
- package/dist/host/guides/supervisor.md +4890 -0
- package/dist/host/guides/table.md +1556 -0
- package/dist/host/guides/template.md +280 -0
- package/dist/host/guides/terminal.md +1145 -0
- package/dist/host/guides/test.md +2969 -0
- package/dist/host/guides/timeout.md +252 -0
- package/dist/host/guides/tool.md +507 -0
- package/dist/host/guides/toolbox.md +1038 -0
- package/dist/host/guides/websocket.md +282 -0
- package/dist/host/guides/worker.md +615 -0
- package/dist/host/guides/workflow.md +1507 -0
- package/dist/host/guides/workspace.md +595 -0
- package/dist/host/manifest.json +1218 -10
- package/dist/host/tests/policy.test.ts +279 -2
- package/dist/host/tests/setupPolicy.ts +445 -6
- package/dist/src/core/index.cjs +38 -16
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +33 -9
- package/dist/src/core/index.d.ts +33 -9
- package/dist/src/core/index.js +37 -17
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +1750 -1567
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +106 -24
- package/dist/src/server/index.d.ts +106 -24
- package/dist/src/server/index.js +1751 -1570
- package/dist/src/server/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
+
createGuide,
|
|
3
|
+
extractSourceLines,
|
|
4
|
+
hasCanonicalSegments,
|
|
5
|
+
resolveLink,
|
|
6
|
+
} from '@orkestrel/guide'
|
|
7
|
+
import { HOST_PATHS } from '@orkestrel/scaffold'
|
|
8
|
+
import {
|
|
9
|
+
existsSync,
|
|
2
10
|
globSync,
|
|
3
11
|
mkdirSync,
|
|
4
12
|
mkdtempSync,
|
|
@@ -10,6 +18,7 @@ import {
|
|
|
10
18
|
import { tmpdir } from 'node:os'
|
|
11
19
|
import { basename, dirname, join, matchesGlob, relative as relativePath, resolve } from 'node:path'
|
|
12
20
|
import { fileURLToPath } from 'node:url'
|
|
21
|
+
import { parseSync } from 'vite'
|
|
13
22
|
import { stripPolicyCode, textToPolicyHits } from '../configs/policy.js'
|
|
14
23
|
|
|
15
24
|
/** Names a rule the fleet sweep decides from workspace text and paths. */
|
|
@@ -20,6 +29,7 @@ export type PolicyRule =
|
|
|
20
29
|
| 'prose'
|
|
21
30
|
| 'rules'
|
|
22
31
|
| 'skill'
|
|
32
|
+
| 'surface'
|
|
23
33
|
| 'suppression'
|
|
24
34
|
|
|
25
35
|
/** Describes one workspace file a physical control writes. */
|
|
@@ -36,6 +46,107 @@ export interface PolicyViolation {
|
|
|
36
46
|
readonly message: string
|
|
37
47
|
}
|
|
38
48
|
|
|
49
|
+
/** Describes one reachable declaration and its physical source location. */
|
|
50
|
+
export interface PolicySurfaceDeclaration {
|
|
51
|
+
readonly name: string
|
|
52
|
+
readonly path: string
|
|
53
|
+
readonly line: number
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Groups reachable declarations with refusals of incomplete barrel evidence. */
|
|
57
|
+
export interface PolicySurfacePopulation {
|
|
58
|
+
readonly declarations: readonly PolicySurfaceDeclaration[]
|
|
59
|
+
readonly violations: readonly PolicyViolation[]
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Reports one file's reachable declarations, or the violation reading it raised. */
|
|
63
|
+
export interface PolicyDeclarationRead {
|
|
64
|
+
readonly declarations: readonly PolicySurfaceDeclaration[]
|
|
65
|
+
readonly violation?: PolicyViolation
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Names the installed host root containing the fleet's reference guides. */
|
|
69
|
+
export const POLICY_SURFACE_HOST = 'node_modules/@orkestrel/scaffold/dist/host'
|
|
70
|
+
|
|
71
|
+
/** Names the staged catalog's storage path beneath the installed host. */
|
|
72
|
+
export const POLICY_SURFACE_CATALOG = 'claude/agents/orkestrel.md'
|
|
73
|
+
|
|
74
|
+
/** Supplies export forms that must participate in the fleet comparison. */
|
|
75
|
+
export const POLICY_SURFACE_EXPORT_CASES = Object.freeze([
|
|
76
|
+
{ label: 'namespace', text: 'export namespace waitForCondition { export const value = 1 }' },
|
|
77
|
+
{ label: 'const declarators', text: 'export const other = 0, waitForCondition = 1' },
|
|
78
|
+
{ label: 'let declarators', text: 'export let other = 0, waitForCondition = 1' },
|
|
79
|
+
{ label: 'enum', text: 'export enum waitForCondition { Ready }' },
|
|
80
|
+
{ label: 'class', text: ' export class waitForCondition {}' },
|
|
81
|
+
{ label: 'interface', text: ' export interface waitForCondition {}' },
|
|
82
|
+
{ label: 'type', text: ' export type waitForCondition = string' },
|
|
83
|
+
{ label: 'function', text: ' export function waitForCondition() {}' },
|
|
84
|
+
{
|
|
85
|
+
label: 'function overload',
|
|
86
|
+
text:
|
|
87
|
+
'export function waitForCondition(value: string): void\n' +
|
|
88
|
+
'export function waitForCondition(value: number): void\n' +
|
|
89
|
+
'export function waitForCondition() {}',
|
|
90
|
+
},
|
|
91
|
+
{ label: 're-export list', text: "export { other as waitForCondition } from './helpers.js'" },
|
|
92
|
+
{ label: 'local export list', text: 'const other = 1; export { other as waitForCondition }' },
|
|
93
|
+
])
|
|
94
|
+
|
|
95
|
+
/** Matches the complete physical barrel rows the parser accepts. */
|
|
96
|
+
export const POLICY_SURFACE_BARREL_PATTERN =
|
|
97
|
+
/^\s*export\s+\*\s+from\s+(?:'(\.\.?\/[^']+\.js)'|"(\.\.?\/[^"]+\.js)")\s*;?\s*$/u
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Creates a guide whose surface claims the supplied fixture names.
|
|
101
|
+
*
|
|
102
|
+
* @param names - The bare names the guide claims.
|
|
103
|
+
* @returns The fixture guide text.
|
|
104
|
+
*/
|
|
105
|
+
export function createPolicySurfaceGuide(names: readonly string[]): string {
|
|
106
|
+
return [
|
|
107
|
+
'# Fixture',
|
|
108
|
+
'',
|
|
109
|
+
'## Surface',
|
|
110
|
+
'',
|
|
111
|
+
'| Name | Kind | Summary |',
|
|
112
|
+
'| ---- | ---- | ------- |',
|
|
113
|
+
...names.map((name) => `| \`${name}\` | function | Declares a fixture name. |`),
|
|
114
|
+
'',
|
|
115
|
+
].join('\n')
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Writes a complete hosted reference population for a physical policy control.
|
|
120
|
+
*
|
|
121
|
+
* @param scratch - The owned workspace receiving the hosted references.
|
|
122
|
+
* @returns Nothing.
|
|
123
|
+
*/
|
|
124
|
+
export function writePolicySurfaceHost(scratch: PolicyScratchInterface): void {
|
|
125
|
+
scratch.write(
|
|
126
|
+
`${POLICY_SURFACE_HOST}/${POLICY_SURFACE_CATALOG}`,
|
|
127
|
+
createPolicyCatalog(['other', 'sample']),
|
|
128
|
+
)
|
|
129
|
+
scratch.write(`${POLICY_SURFACE_HOST}/guides/other.md`, createPolicySurfaceGuide(['readShared']))
|
|
130
|
+
scratch.write(`${POLICY_SURFACE_HOST}/guides/sample.md`, createPolicySurfaceGuide([]))
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Creates a scratch target with a complete installed guide population.
|
|
135
|
+
*
|
|
136
|
+
* @returns The owned scratch workspace.
|
|
137
|
+
*/
|
|
138
|
+
export function createPolicySurfaceFixture(): PolicyScratchInterface {
|
|
139
|
+
const scratch = createPolicyScratch({ prefix: 'orkestrel-policy-surface-' })
|
|
140
|
+
try {
|
|
141
|
+
writePolicySurfaceHost(scratch)
|
|
142
|
+
scratch.write('package.json', '{"name":"@orkestrel/sample"}\n')
|
|
143
|
+
return scratch
|
|
144
|
+
} catch (error) {
|
|
145
|
+
scratch.destroy()
|
|
146
|
+
throw error
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
39
150
|
/** Describes one physical negative control, including the population boundary it attacks. */
|
|
40
151
|
export interface PolicyControl {
|
|
41
152
|
readonly label: string
|
|
@@ -1410,13 +1521,15 @@ export function readPolicyPackage(root: string): string | undefined {
|
|
|
1410
1521
|
* there rather than failing.
|
|
1411
1522
|
*
|
|
1412
1523
|
* @param root - The workspace root to read.
|
|
1524
|
+
* @param path - The catalog's path within that root. Default: the target catalog path.
|
|
1413
1525
|
* @returns Each catalog row's package short name, its scope removed, in table order.
|
|
1414
1526
|
*/
|
|
1415
|
-
export function readPolicyCatalog(
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1527
|
+
export function readPolicyCatalog(
|
|
1528
|
+
root: string,
|
|
1529
|
+
path: string = POLICY_CATALOG_FILE,
|
|
1530
|
+
): readonly string[] {
|
|
1531
|
+
if (!isPolicyFile(root, path)) return []
|
|
1532
|
+
const lines = readFileSync(join(root, path), 'utf8').replaceAll('\r\n', '\n').split('\n')
|
|
1420
1533
|
const heading = lines.indexOf(POLICY_CATALOG_HEADING)
|
|
1421
1534
|
if (heading === -1) return []
|
|
1422
1535
|
const names: string[] = []
|
|
@@ -1565,11 +1678,335 @@ export function inspectPolicyPortability(root: string): readonly PolicyViolation
|
|
|
1565
1678
|
]
|
|
1566
1679
|
}
|
|
1567
1680
|
|
|
1681
|
+
/**
|
|
1682
|
+
* Locates parsed exports at their physical declaration lines.
|
|
1683
|
+
*
|
|
1684
|
+
* @param path - The declaring workspace-relative path.
|
|
1685
|
+
* @param text - The declaration source text.
|
|
1686
|
+
* @param root - The workspace root used to resolve relative star exports, when supplied.
|
|
1687
|
+
* @param ancestors - The source paths already visited along this star-export branch.
|
|
1688
|
+
* @returns The exported names with normalized paths and declaration lines.
|
|
1689
|
+
* @throws An `Error` when syntax, an export form, or a star target cannot be read.
|
|
1690
|
+
*/
|
|
1691
|
+
export function readPolicyDeclarations(
|
|
1692
|
+
path: string,
|
|
1693
|
+
text: string,
|
|
1694
|
+
root?: string,
|
|
1695
|
+
ancestors: readonly string[] = [],
|
|
1696
|
+
): readonly PolicySurfaceDeclaration[] {
|
|
1697
|
+
path = normalizePolicyPath(path)
|
|
1698
|
+
if (ancestors.includes(path)) return []
|
|
1699
|
+
const source = parseSync(path, text)
|
|
1700
|
+
if (source.errors.length > 0) throw new Error(`export syntax is unreadable at ${path}`)
|
|
1701
|
+
const declarations: PolicySurfaceDeclaration[] = []
|
|
1702
|
+
for (const statement of source.program.body) {
|
|
1703
|
+
const line = text.slice(0, statement.start).split(/\r\n|\n/u).length
|
|
1704
|
+
if (statement.type === 'ExportAllDeclaration') {
|
|
1705
|
+
if (statement.exported !== null) {
|
|
1706
|
+
const name =
|
|
1707
|
+
statement.exported.type === 'Identifier'
|
|
1708
|
+
? statement.exported.name
|
|
1709
|
+
: statement.exported.value
|
|
1710
|
+
if (name === 'default') throw new Error(`default export is unsupported at ${path}:${line}`)
|
|
1711
|
+
declarations.push({ name, path, line })
|
|
1712
|
+
continue
|
|
1713
|
+
}
|
|
1714
|
+
const target = statement.source.value
|
|
1715
|
+
const resolved = resolveLink(
|
|
1716
|
+
path,
|
|
1717
|
+
target.endsWith('.js') ? `${target.slice(0, -3)}.ts` : target,
|
|
1718
|
+
)
|
|
1719
|
+
if (
|
|
1720
|
+
root === undefined ||
|
|
1721
|
+
!target.startsWith('.') ||
|
|
1722
|
+
!hasCanonicalSegments(resolved) ||
|
|
1723
|
+
!isPolicyFile(root, resolved)
|
|
1724
|
+
) {
|
|
1725
|
+
throw new Error(`star export target is unreadable at ${path}:${line}: ${target}`)
|
|
1726
|
+
}
|
|
1727
|
+
declarations.push(
|
|
1728
|
+
...readPolicyDeclarations(resolved, readFileSync(join(root, resolved), 'utf8'), root, [
|
|
1729
|
+
...ancestors,
|
|
1730
|
+
path,
|
|
1731
|
+
]),
|
|
1732
|
+
)
|
|
1733
|
+
continue
|
|
1734
|
+
}
|
|
1735
|
+
if (statement.type === 'ExportNamedDeclaration') {
|
|
1736
|
+
for (const specifier of statement.specifiers) {
|
|
1737
|
+
const name =
|
|
1738
|
+
specifier.exported.type === 'Identifier'
|
|
1739
|
+
? specifier.exported.name
|
|
1740
|
+
: specifier.exported.value
|
|
1741
|
+
if (name === 'default') throw new Error(`default export is unsupported at ${path}:${line}`)
|
|
1742
|
+
declarations.push({
|
|
1743
|
+
name,
|
|
1744
|
+
path,
|
|
1745
|
+
line: text.slice(0, specifier.start).split(/\r\n|\n/u).length,
|
|
1746
|
+
})
|
|
1747
|
+
}
|
|
1748
|
+
const declaration = statement.declaration
|
|
1749
|
+
if (declaration === null) continue
|
|
1750
|
+
if (declaration.type === 'VariableDeclaration') {
|
|
1751
|
+
for (const variable of declaration.declarations) {
|
|
1752
|
+
if (variable.id.type !== 'Identifier')
|
|
1753
|
+
throw new Error(`export binding is unsupported at ${path}:${line}`)
|
|
1754
|
+
declarations.push({
|
|
1755
|
+
name: variable.id.name,
|
|
1756
|
+
path,
|
|
1757
|
+
line: text.slice(0, variable.id.start).split(/\r\n|\n/u).length,
|
|
1758
|
+
})
|
|
1759
|
+
}
|
|
1760
|
+
continue
|
|
1761
|
+
}
|
|
1762
|
+
if (
|
|
1763
|
+
declaration.type === 'FunctionDeclaration' ||
|
|
1764
|
+
declaration.type === 'TSDeclareFunction' ||
|
|
1765
|
+
declaration.type === 'ClassDeclaration' ||
|
|
1766
|
+
declaration.type === 'TSInterfaceDeclaration' ||
|
|
1767
|
+
declaration.type === 'TSTypeAliasDeclaration' ||
|
|
1768
|
+
declaration.type === 'TSEnumDeclaration' ||
|
|
1769
|
+
declaration.type === 'TSModuleDeclaration'
|
|
1770
|
+
) {
|
|
1771
|
+
if (declaration.id?.type !== 'Identifier')
|
|
1772
|
+
throw new Error(`export name is unreadable at ${path}:${line}`)
|
|
1773
|
+
declarations.push({ name: declaration.id.name, path, line })
|
|
1774
|
+
continue
|
|
1775
|
+
}
|
|
1776
|
+
throw new Error(`export declaration is unsupported at ${path}:${line}: ${declaration.type}`)
|
|
1777
|
+
}
|
|
1778
|
+
if (
|
|
1779
|
+
statement.type === 'ExportDefaultDeclaration' ||
|
|
1780
|
+
statement.type === 'TSExportAssignment' ||
|
|
1781
|
+
statement.type === 'TSNamespaceExportDeclaration'
|
|
1782
|
+
) {
|
|
1783
|
+
throw new Error(`export statement is unsupported at ${path}:${line}: ${statement.type}`)
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
return declarations
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
/**
|
|
1790
|
+
* Reads one file's declarations, or converts the reader's throw into a surface violation.
|
|
1791
|
+
*
|
|
1792
|
+
* @param root - The workspace root used to resolve relative star exports.
|
|
1793
|
+
* @param path - The workspace-relative path being read.
|
|
1794
|
+
* @param text - The declaration source text.
|
|
1795
|
+
* @returns The read declarations, or the violation the reader raised.
|
|
1796
|
+
*/
|
|
1797
|
+
export function collectPolicyDeclarations(
|
|
1798
|
+
root: string,
|
|
1799
|
+
path: string,
|
|
1800
|
+
text: string,
|
|
1801
|
+
): PolicyDeclarationRead {
|
|
1802
|
+
try {
|
|
1803
|
+
return { declarations: readPolicyDeclarations(path, text, root) }
|
|
1804
|
+
} catch (error) {
|
|
1805
|
+
return {
|
|
1806
|
+
declarations: [],
|
|
1807
|
+
violation: createPolicyViolation(
|
|
1808
|
+
'surface',
|
|
1809
|
+
path,
|
|
1810
|
+
`surface population incomplete: ${error instanceof Error ? error.message : String(error)}`,
|
|
1811
|
+
),
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
/**
|
|
1817
|
+
* Reads reachable source declarations and refuses unread barrel statements or targets.
|
|
1818
|
+
*
|
|
1819
|
+
* @param root - The workspace root to inspect.
|
|
1820
|
+
* @returns The parsed declarations and incomplete-population violations.
|
|
1821
|
+
*/
|
|
1822
|
+
export function readPolicySurface(root: string): PolicySurfacePopulation {
|
|
1823
|
+
const files: Record<string, string> = {}
|
|
1824
|
+
for (const path of globSync('src/**/*.ts', { cwd: root }).map(normalizePolicyPath).sort()) {
|
|
1825
|
+
files[path] = readFileSync(join(root, path), 'utf8')
|
|
1826
|
+
}
|
|
1827
|
+
const barrels = Object.keys(files).filter((path) => path.endsWith('/index.ts'))
|
|
1828
|
+
const targets = new Set<string>()
|
|
1829
|
+
const violations: PolicyViolation[] = []
|
|
1830
|
+
for (const path of barrels) {
|
|
1831
|
+
const text = files[path]
|
|
1832
|
+
if (text === undefined) continue
|
|
1833
|
+
const source = parseSync(path, text)
|
|
1834
|
+
const lines = extractSourceLines(text)
|
|
1835
|
+
if (source.errors.length > 0) {
|
|
1836
|
+
violations.push(
|
|
1837
|
+
createPolicyViolation(
|
|
1838
|
+
'surface',
|
|
1839
|
+
path,
|
|
1840
|
+
'surface population incomplete: barrel syntax is unreadable',
|
|
1841
|
+
),
|
|
1842
|
+
)
|
|
1843
|
+
continue
|
|
1844
|
+
}
|
|
1845
|
+
for (const statement of source.program.body) {
|
|
1846
|
+
const start = text.slice(0, statement.start).split(/\r\n|\n/u).length - 1
|
|
1847
|
+
const end = text.slice(0, statement.end).split(/\r\n|\n/u).length - 1
|
|
1848
|
+
const row = lines[start]?.code.match(POLICY_SURFACE_BARREL_PATTERN)
|
|
1849
|
+
const target = row?.[1] ?? row?.[2]
|
|
1850
|
+
if (
|
|
1851
|
+
statement.type !== 'ExportAllDeclaration' ||
|
|
1852
|
+
statement.exportKind !== 'value' ||
|
|
1853
|
+
statement.exported !== null ||
|
|
1854
|
+
start !== end ||
|
|
1855
|
+
target === undefined
|
|
1856
|
+
) {
|
|
1857
|
+
violations.push(
|
|
1858
|
+
createPolicyViolation(
|
|
1859
|
+
'surface',
|
|
1860
|
+
path,
|
|
1861
|
+
'surface population incomplete: barrel requires a relative .js star export on one line',
|
|
1862
|
+
start + 1,
|
|
1863
|
+
),
|
|
1864
|
+
)
|
|
1865
|
+
continue
|
|
1866
|
+
}
|
|
1867
|
+
const resolved = resolveLink(path, `${target.slice(0, -3)}.ts`)
|
|
1868
|
+
if (
|
|
1869
|
+
!hasCanonicalSegments(resolved) ||
|
|
1870
|
+
!resolved.startsWith('src/') ||
|
|
1871
|
+
files[resolved] === undefined
|
|
1872
|
+
) {
|
|
1873
|
+
violations.push(
|
|
1874
|
+
createPolicyViolation(
|
|
1875
|
+
'surface',
|
|
1876
|
+
path,
|
|
1877
|
+
`surface population incomplete: barrel target is unreadable: ${target}`,
|
|
1878
|
+
start + 1,
|
|
1879
|
+
),
|
|
1880
|
+
)
|
|
1881
|
+
continue
|
|
1882
|
+
}
|
|
1883
|
+
if (!resolved.endsWith('/index.ts')) targets.add(resolved)
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
const declarations: PolicySurfaceDeclaration[] = []
|
|
1887
|
+
for (const path of targets) {
|
|
1888
|
+
const text = files[path]
|
|
1889
|
+
if (text === undefined) continue
|
|
1890
|
+
const read = collectPolicyDeclarations(root, path, text)
|
|
1891
|
+
declarations.push(...read.declarations)
|
|
1892
|
+
if (read.violation !== undefined) violations.push(read.violation)
|
|
1893
|
+
}
|
|
1894
|
+
return { declarations, violations }
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
/**
|
|
1898
|
+
* Inspects source and target-owned setup names against the hosted fleet guides.
|
|
1899
|
+
*
|
|
1900
|
+
* @remarks
|
|
1901
|
+
* Source names claimed by the target's own hosted guide are grandfathered. Setup names have no
|
|
1902
|
+
* grandfather. An absent installed host falls back to checkout guides only with catalog coverage.
|
|
1903
|
+
* Missing comparison evidence and unread barrel statements produce surface violations.
|
|
1904
|
+
*
|
|
1905
|
+
* @param root - The workspace root to inspect.
|
|
1906
|
+
* @returns Surface violations sorted by path, declaration line, name, and owner.
|
|
1907
|
+
*/
|
|
1908
|
+
export function inspectPolicySurface(root: string): readonly PolicyViolation[] {
|
|
1909
|
+
const installed = join(root, POLICY_SURFACE_HOST)
|
|
1910
|
+
const own = readPolicyPackage(root)
|
|
1911
|
+
const hosted = own !== 'scaffold' && existsSync(installed)
|
|
1912
|
+
const host = hosted ? installed : root
|
|
1913
|
+
const catalog = hosted ? POLICY_SURFACE_CATALOG : POLICY_CATALOG_FILE
|
|
1914
|
+
const directory = hosted ? `${POLICY_SURFACE_HOST}/guides` : 'guides'
|
|
1915
|
+
const names = new Set([...readPolicyCatalog(host, catalog), ...readPolicyCatalog(root)])
|
|
1916
|
+
if (!existsSync(join(host, 'guides')) || names.size === 0) {
|
|
1917
|
+
return [
|
|
1918
|
+
createPolicyViolation(
|
|
1919
|
+
'surface',
|
|
1920
|
+
directory,
|
|
1921
|
+
'surface evidence missing: hosted guides and a populated catalog are required',
|
|
1922
|
+
),
|
|
1923
|
+
]
|
|
1924
|
+
}
|
|
1925
|
+
const violations: PolicyViolation[] = []
|
|
1926
|
+
for (const name of names) {
|
|
1927
|
+
if (!isPolicyFile(host, `guides/${name}.md`)) {
|
|
1928
|
+
violations.push(
|
|
1929
|
+
createPolicyViolation(
|
|
1930
|
+
'surface',
|
|
1931
|
+
`${directory}/${name}.md`,
|
|
1932
|
+
`surface evidence missing: catalog package ${name} has no hosted guide`,
|
|
1933
|
+
),
|
|
1934
|
+
)
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
const grandfather = new Set<string>()
|
|
1938
|
+
const owners = new Map<string, Set<string>>()
|
|
1939
|
+
for (const path of globSync('guides/*.md', { cwd: host }).map(normalizePolicyPath).sort()) {
|
|
1940
|
+
const owner = basename(path, '.md')
|
|
1941
|
+
if (owner === POLICY_GUIDE_MAP) continue
|
|
1942
|
+
const guide = createGuide(readFileSync(join(host, path), 'utf8'))
|
|
1943
|
+
if (!guide.sections().includes('Surface')) {
|
|
1944
|
+
violations.push(
|
|
1945
|
+
createPolicyViolation(
|
|
1946
|
+
'surface',
|
|
1947
|
+
`${directory}/${owner}.md`,
|
|
1948
|
+
'surface evidence missing: hosted guide has no Surface section',
|
|
1949
|
+
),
|
|
1950
|
+
)
|
|
1951
|
+
continue
|
|
1952
|
+
}
|
|
1953
|
+
for (const symbol of guide.surface()) {
|
|
1954
|
+
if (owner === own) {
|
|
1955
|
+
grandfather.add(symbol.name)
|
|
1956
|
+
continue
|
|
1957
|
+
}
|
|
1958
|
+
const claimed = owners.get(symbol.name) ?? new Set<string>()
|
|
1959
|
+
claimed.add(owner)
|
|
1960
|
+
owners.set(symbol.name, claimed)
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
const population = readPolicySurface(root)
|
|
1964
|
+
violations.push(...population.violations)
|
|
1965
|
+
const declarations = population.declarations.filter(
|
|
1966
|
+
(declaration) => !grandfather.has(declaration.name),
|
|
1967
|
+
)
|
|
1968
|
+
for (const path of globSync('tests/setup*.ts', { cwd: root }).map(normalizePolicyPath).sort()) {
|
|
1969
|
+
if (
|
|
1970
|
+
path.endsWith('.test.ts') ||
|
|
1971
|
+
HOST_PATHS.some(
|
|
1972
|
+
(vendored) =>
|
|
1973
|
+
path === normalizePolicyPath(vendored) ||
|
|
1974
|
+
path.startsWith(`${normalizePolicyPath(vendored)}/`),
|
|
1975
|
+
)
|
|
1976
|
+
)
|
|
1977
|
+
continue
|
|
1978
|
+
const read = collectPolicyDeclarations(root, path, readFileSync(join(root, path), 'utf8'))
|
|
1979
|
+
declarations.push(...read.declarations)
|
|
1980
|
+
if (read.violation !== undefined) violations.push(read.violation)
|
|
1981
|
+
}
|
|
1982
|
+
const seen = new Set<string>()
|
|
1983
|
+
for (const declaration of declarations) {
|
|
1984
|
+
for (const owner of owners.get(declaration.name) ?? []) {
|
|
1985
|
+
const key = `${declaration.path}\n${declaration.name}\n${owner}`
|
|
1986
|
+
if (seen.has(key)) continue
|
|
1987
|
+
seen.add(key)
|
|
1988
|
+
violations.push(
|
|
1989
|
+
createPolicyViolation(
|
|
1990
|
+
'surface',
|
|
1991
|
+
declaration.path,
|
|
1992
|
+
`surface name belongs to one package: ${declaration.name} (${owner})`,
|
|
1993
|
+
declaration.line,
|
|
1994
|
+
),
|
|
1995
|
+
)
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
return violations.sort((first, second) => {
|
|
1999
|
+
if (first.path !== second.path) return first.path < second.path ? -1 : 1
|
|
2000
|
+
if (first.line !== second.line) return (first.line ?? 0) - (second.line ?? 0)
|
|
2001
|
+
return first.message === second.message ? 0 : first.message < second.message ? -1 : 1
|
|
2002
|
+
})
|
|
2003
|
+
}
|
|
2004
|
+
|
|
1568
2005
|
/**
|
|
1569
2006
|
* Inspects every policy rule across one workspace.
|
|
1570
2007
|
*
|
|
1571
2008
|
* @param root - The workspace root to inspect.
|
|
1572
|
-
* @returns Every mirror, suppression, skill, bridge, portability, and
|
|
2009
|
+
* @returns Every mirror, suppression, skill, bridge, portability, prose, and surface violation.
|
|
1573
2010
|
*/
|
|
1574
2011
|
export function inspectPolicyWorkspace(root: string): readonly PolicyViolation[] {
|
|
1575
2012
|
return [
|
|
@@ -1579,6 +2016,7 @@ export function inspectPolicyWorkspace(root: string): readonly PolicyViolation[]
|
|
|
1579
2016
|
...inspectSkillBridges(root),
|
|
1580
2017
|
...inspectPolicyPortability(root),
|
|
1581
2018
|
...inspectPolicyProse(root),
|
|
2019
|
+
...inspectPolicySurface(root),
|
|
1582
2020
|
]
|
|
1583
2021
|
}
|
|
1584
2022
|
|
|
@@ -1594,6 +2032,7 @@ export function inspectPolicyWorkspace(root: string): readonly PolicyViolation[]
|
|
|
1594
2032
|
export function inspectPolicyControl(control: PolicyControl): readonly PolicyViolation[] {
|
|
1595
2033
|
const scratch = createPolicyScratch({ prefix: 'orkestrel-policy-' })
|
|
1596
2034
|
try {
|
|
2035
|
+
writePolicySurfaceHost(scratch)
|
|
1597
2036
|
for (const file of control.files) {
|
|
1598
2037
|
scratch.write(file.path, file.content)
|
|
1599
2038
|
}
|
package/dist/src/core/index.cjs
CHANGED
|
@@ -4,7 +4,7 @@ let _orkestrel_template = require("@orkestrel/template");
|
|
|
4
4
|
let _orkestrel_emitter = require("@orkestrel/emitter");
|
|
5
5
|
var package_default = {
|
|
6
6
|
name: "@orkestrel/scaffold",
|
|
7
|
-
version: "0.0.
|
|
7
|
+
version: "0.0.69",
|
|
8
8
|
description: "Scaffold workspaces with commands: new, audit, repair, catalog, and overwrite.",
|
|
9
9
|
keywords: [
|
|
10
10
|
"audit",
|
|
@@ -84,7 +84,7 @@ var package_default = {
|
|
|
84
84
|
"build:src:core": "vite build --config configs/src/vite.core.config.ts && npm run copy dist/src/core/index.d.ts dist/src/core/index.d.cts",
|
|
85
85
|
"build:src:server": "vite build --config configs/src/vite.server.config.ts && npm run copy dist/src/server/index.d.ts dist/src/server/index.d.cts",
|
|
86
86
|
"build:src:bin": "vite build --config configs/src/vite.bin.config.ts",
|
|
87
|
-
"build:host": "node -e \"import('./dist/src/server/index.js').then((m)=>{const n=m.stageHost(process.cwd(),'dist/host').length;console.log('build-host: staged '+n+' file(s) into dist/host')})\"",
|
|
87
|
+
"build:host": "node -e \"import('./dist/src/server/index.js').then((m)=>{const n=m.stageHost(process.cwd(),'dist/host',{report: (message) => console.error(message)}).length;console.log('build-host: staged '+n+' file(s) into dist/host')})\"",
|
|
88
88
|
"build:inventory": "node -e \"import('./dist/src/server/index.js').then((m)=>{const p=process.argv[1]??'host.json',n=m.stageInventory(process.cwd(),p).entries.length;console.log('build-inventory: staged '+n+' file(s) into '+p)})\"",
|
|
89
89
|
"prepack": "npm run build",
|
|
90
90
|
"prepublishOnly": "npm run format:check && npm run lint:check && npm run check && npm run build && npm test && npm run test:distribution -- --mode release"
|
|
@@ -99,7 +99,7 @@ var package_default = {
|
|
|
99
99
|
},
|
|
100
100
|
devDependencies: {
|
|
101
101
|
"@microsoft/api-extractor": "^7.59.1",
|
|
102
|
-
"@orkestrel/guide": "^0.0.
|
|
102
|
+
"@orkestrel/guide": "^0.0.19",
|
|
103
103
|
"@orkestrel/html": "^0.0.9",
|
|
104
104
|
"@orkestrel/probe": "^0.0.14",
|
|
105
105
|
"@orkestrel/test": "^0.0.14",
|
|
@@ -214,12 +214,11 @@ var BIN_ENTRY_PATH = "src/bin/main.ts";
|
|
|
214
214
|
* of the paths it selects: the licence, the harness permission file, the
|
|
215
215
|
* session-start hooks, the shared policy
|
|
216
216
|
* register, the shared policy proof, the shared policy plugin, the shared
|
|
217
|
-
* configuration leaf and its proof, the byte-identical root dotfiles
|
|
218
|
-
*
|
|
219
|
-
* everything beneath it.
|
|
217
|
+
* configuration leaf and its proof, and the byte-identical root dotfiles.
|
|
218
|
+
* A directory entry vendors everything beneath it.
|
|
220
219
|
*
|
|
221
|
-
* A plan carries the subset its target selects
|
|
222
|
-
*
|
|
220
|
+
* A plan carries the subset its target selects. The seed guide mirrors are
|
|
221
|
+
* claimed separately by `blueprintToHostArtifacts` through {@link SEED_GUIDE_PATHS}.
|
|
223
222
|
*
|
|
224
223
|
* Neither the instruction canon nor the bench and MCP wiring is here. A target reads
|
|
225
224
|
* its rules, its skills, its agent roles, its bench configuration, and its MCP
|
|
@@ -243,9 +242,7 @@ var HOST_PATHS = Object.freeze([
|
|
|
243
242
|
".oxfmtrc.json",
|
|
244
243
|
".oxlintrc.json",
|
|
245
244
|
".oxlintignore",
|
|
246
|
-
".prettierignore"
|
|
247
|
-
"guides/guide.md",
|
|
248
|
-
"guides/scaffold.md"
|
|
245
|
+
".prettierignore"
|
|
249
246
|
]);
|
|
250
247
|
/**
|
|
251
248
|
* Lists the instruction-canon paths staged for reading rather than for a target, frozen.
|
|
@@ -263,8 +260,9 @@ var HOST_PATHS = Object.freeze([
|
|
|
263
260
|
* the installed package. The `AGENTS.md` and `CLAUDE.md` pointers scaffold plans
|
|
264
261
|
* are what name each location.
|
|
265
262
|
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
263
|
+
* This list, {@link HOST_PATHS}, and {@link REFERENCE_PATHS} are disjoint by
|
|
264
|
+
* prefix in every direction: no member equals or sits beneath another list's
|
|
265
|
+
* member. Staging depends on that, because
|
|
268
266
|
* the walk covers the union and a path it discovers twice claims one storage
|
|
269
267
|
* name twice, which refuses the stage.
|
|
270
268
|
*
|
|
@@ -294,6 +292,24 @@ var CANON_PATHS = Object.freeze([
|
|
|
294
292
|
".cursor/mcp.json",
|
|
295
293
|
".cursor/rules"
|
|
296
294
|
]);
|
|
295
|
+
/**
|
|
296
|
+
* Lists the reference paths staged for offline reading, frozen.
|
|
297
|
+
*
|
|
298
|
+
* @remarks
|
|
299
|
+
* A directory entry covers everything beneath it. Reference membership grants
|
|
300
|
+
* no target ownership and no instruction-canon membership. Keep this list,
|
|
301
|
+
* {@link HOST_PATHS}, and {@link CANON_PATHS} disjoint by prefix in every
|
|
302
|
+
* direction; duplicate discovery refuses the stage.
|
|
303
|
+
*/
|
|
304
|
+
var REFERENCE_PATHS = Object.freeze(["guides"]);
|
|
305
|
+
/**
|
|
306
|
+
* Lists the guide paths a generated workspace starts with, frozen.
|
|
307
|
+
*
|
|
308
|
+
* @remarks
|
|
309
|
+
* `blueprintToHostArtifacts` claims these mirrors from {@link REFERENCE_PATHS}.
|
|
310
|
+
* `selectHostPaths` excludes the workspace's own guide from that claim.
|
|
311
|
+
*/
|
|
312
|
+
var SEED_GUIDE_PATHS = Object.freeze(["guides/guide.md", "guides/scaffold.md"]);
|
|
297
313
|
/** Names the repository-relative path where the committed vendored-file inventory is served. */
|
|
298
314
|
var HOST_INVENTORY_PATH = "host.json";
|
|
299
315
|
/**
|
|
@@ -3842,7 +3858,7 @@ function srcToRoot(src) {
|
|
|
3842
3858
|
* @returns Every candidate except the workspace's own guide, in input order.
|
|
3843
3859
|
*
|
|
3844
3860
|
* @remarks
|
|
3845
|
-
*
|
|
3861
|
+
* The candidates include the seed guide mirrors beside `HOST_PATHS`. A workspace never
|
|
3846
3862
|
* mirrors its own guide: that file is the workspace's own product, and vendoring
|
|
3847
3863
|
* it would have the target overwrite its guide with the copy it published.
|
|
3848
3864
|
*
|
|
@@ -5625,7 +5641,7 @@ function blueprintToOrchestrationArtifacts(blueprint) {
|
|
|
5625
5641
|
*
|
|
5626
5642
|
* @param blueprint - The workspace specification.
|
|
5627
5643
|
* @returns One artifact per selected vendored path in `HOST_PATHS` order, then
|
|
5628
|
-
* the catalog file.
|
|
5644
|
+
* the seed guide mirrors and the catalog file.
|
|
5629
5645
|
*
|
|
5630
5646
|
* @remarks
|
|
5631
5647
|
* Every artifact is claimed by presence, which is the strongest claim a pure
|
|
@@ -5648,6 +5664,10 @@ function blueprintToOrchestrationArtifacts(blueprint) {
|
|
|
5648
5664
|
* from the installed package, at the locations the `AGENTS.md` and `CLAUDE.md`
|
|
5649
5665
|
* pointers {@link blueprintToDocumentArtifacts} emits name.
|
|
5650
5666
|
*
|
|
5667
|
+
* The {@link SEED_GUIDE_PATHS} mirrors are claimed explicitly
|
|
5668
|
+
* from `REFERENCE_PATHS`. The target's own guide is excluded by
|
|
5669
|
+
* {@link selectHostPaths}; the remaining reference guides grant no target claim.
|
|
5670
|
+
*
|
|
5651
5671
|
* @example
|
|
5652
5672
|
* ```ts
|
|
5653
5673
|
* import { blueprintToHostArtifacts, createBlueprint } from '@orkestrel/scaffold'
|
|
@@ -5659,7 +5679,7 @@ function blueprintToOrchestrationArtifacts(blueprint) {
|
|
|
5659
5679
|
* ```
|
|
5660
5680
|
*/
|
|
5661
5681
|
function blueprintToHostArtifacts(blueprint) {
|
|
5662
|
-
return [...selectHostPaths(HOST_PATHS, blueprint.name), CATALOG_AGENT_PATH].map((path) => ({
|
|
5682
|
+
return [...selectHostPaths([...HOST_PATHS, ...SEED_GUIDE_PATHS], blueprint.name), CATALOG_AGENT_PATH].map((path) => ({
|
|
5663
5683
|
path,
|
|
5664
5684
|
group: inferGroup(path),
|
|
5665
5685
|
ownership: "presence",
|
|
@@ -6989,7 +7009,9 @@ exports.ORCHESTRATION_PATH_NAMES = ORCHESTRATION_PATH_NAMES;
|
|
|
6989
7009
|
exports.ORCHESTRATION_PATH_PREFIXES = ORCHESTRATION_PATH_PREFIXES;
|
|
6990
7010
|
exports.ORKESTREL_RANGE_PATTERN = ORKESTREL_RANGE_PATTERN;
|
|
6991
7011
|
exports.PRINT_WIDTH = PRINT_WIDTH;
|
|
7012
|
+
exports.REFERENCE_PATHS = REFERENCE_PATHS;
|
|
6992
7013
|
exports.RELEASE_PROOF_COMMAND = RELEASE_PROOF_COMMAND;
|
|
7014
|
+
exports.SEED_GUIDE_PATHS = SEED_GUIDE_PATHS;
|
|
6993
7015
|
exports.SERVICE_SCRIPT_PATH = SERVICE_SCRIPT_PATH;
|
|
6994
7016
|
exports.SERVICE_SETUP_PATH = SERVICE_SETUP_PATH;
|
|
6995
7017
|
exports.SERVICE_TEST_INCLUDE = SERVICE_TEST_INCLUDE;
|