@orkestrel/scaffold 0.0.108 → 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.
- package/dist/agents/skills/orkestrel-dispatch/scripts/bench.js +326 -91
- package/dist/agents/skills/orkestrel-dispatch/scripts/brief.js +110 -14
- package/dist/agents/skills/orkestrel-dispatch/scripts/chain.js +280 -0
- package/dist/agents/skills/orkestrel-dispatch/scripts/cite.js +188 -26
- package/dist/agents/skills/orkestrel-dispatch/scripts/launch.js +142 -43
- package/dist/agents/skills/orkestrel-dispatch/scripts/login.js +240 -62
- package/dist/agents/skills/orkestrel-dispatch/scripts/patch.js +323 -0
- package/dist/agents/skills/orkestrel-dispatch/scripts/result.js +169 -32
- package/dist/agents/skills/orkestrel-dispatch/scripts/sweep.js +290 -69
- package/dist/agents/skills/orkestrel-falsify/scripts/mutate.js +560 -0
- package/dist/agents/skills/orkestrel-harden/scripts/discovery.js +1052 -205
- package/dist/agents/skills/orkestrel-harden/scripts/measure.js +361 -0
- package/dist/agents/skills/orkestrel-harden/scripts/titles.js +455 -80
- package/dist/agents/skills/orkestrel-parallel/scripts/cases.js +327 -0
- package/dist/agents/skills/orkestrel-publish/scripts/compare.js +298 -51
- package/dist/agents/skills/orkestrel-publish/scripts/pins.js +170 -13
- package/dist/agents/skills/orkestrel-publish/scripts/swap.js +956 -191
- package/dist/agents/skills/orkestrel-publish/scripts/wave.js +707 -112
- package/dist/agents/skills/orkestrel-publish/scripts/window.js +235 -41
- package/dist/agents/skills/orkestrel-scout/scripts/map.js +341 -54
- package/dist/agents/templates/brief.md +1 -1
- package/dist/bin/main.js +51 -46
- package/dist/bin/main.js.map +1 -1
- package/dist/host/AGENTS.md +23 -21
- package/dist/host/agents/orchestration.md +43 -18
- package/dist/host/agents/platforms/windows.md +3 -3
- package/dist/host/agents/skills/orkestrel-align/SKILL.md +10 -13
- package/dist/host/agents/skills/orkestrel-align/references/fleet.md +4 -10
- package/dist/host/agents/skills/orkestrel-align/references/integration.md +1 -1
- package/dist/host/agents/skills/orkestrel-build/SKILL.md +16 -48
- package/dist/host/agents/skills/orkestrel-build/agents/openai.yaml +1 -1
- package/dist/host/agents/skills/orkestrel-debrief/SKILL.md +19 -16
- package/dist/host/agents/skills/orkestrel-debrief/agents/openai.yaml +1 -1
- package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +15 -28
- package/dist/host/agents/skills/orkestrel-debrief/references/instruction-audit.md +5 -6
- package/dist/host/agents/skills/orkestrel-debrief/references/retention.md +4 -6
- package/dist/host/agents/skills/orkestrel-dispatch/SKILL.md +31 -20
- package/dist/host/agents/skills/orkestrel-dispatch/references/launch.md +3 -4
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/bench.ts +366 -75
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/brief.ts +126 -16
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/chain.ts +323 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/cite.ts +220 -31
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/launch.ts +176 -33
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/login.ts +294 -59
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/patch.ts +401 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/result.ts +199 -34
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/sweep.ts +330 -69
- package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +10 -8
- package/dist/host/agents/skills/orkestrel-falsify/references/brief.md +1 -5
- package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +1 -1
- package/dist/host/agents/skills/orkestrel-falsify/scripts/mutate.ts +701 -0
- package/dist/host/agents/skills/orkestrel-harden/SKILL.md +16 -12
- package/dist/host/agents/skills/orkestrel-harden/references/bindings.md +23 -81
- package/dist/host/agents/skills/orkestrel-harden/references/centralization.md +4 -5
- package/dist/host/agents/skills/orkestrel-harden/references/contract.md +2 -0
- package/dist/host/agents/skills/orkestrel-harden/references/document.md +33 -0
- package/dist/host/agents/skills/orkestrel-harden/references/equivalence.md +6 -3
- package/dist/host/agents/skills/orkestrel-harden/references/hardening.md +8 -4
- package/dist/host/agents/skills/orkestrel-harden/references/research.md +2 -0
- package/dist/host/agents/skills/orkestrel-harden/scripts/discovery.ts +1272 -279
- package/dist/host/agents/skills/orkestrel-harden/scripts/measure.ts +425 -0
- package/dist/host/agents/skills/orkestrel-harden/scripts/titles.ts +603 -98
- package/dist/host/agents/skills/orkestrel-journey/SKILL.md +75 -98
- package/dist/host/agents/skills/orkestrel-journey/references/captures.md +54 -17
- package/dist/host/agents/skills/orkestrel-journey/references/decide.md +1 -20
- package/dist/host/agents/skills/orkestrel-journey/references/layer.md +21 -31
- package/dist/host/agents/skills/orkestrel-journey/references/styles.md +1 -1
- package/dist/host/agents/skills/orkestrel-parallel/SKILL.md +28 -24
- package/dist/host/agents/skills/orkestrel-parallel/references/claude.md +15 -13
- package/dist/host/agents/skills/orkestrel-parallel/references/codex.md +1 -0
- package/dist/host/agents/skills/orkestrel-parallel/references/cursor.md +13 -13
- package/dist/host/agents/skills/orkestrel-parallel/scripts/cases.ts +389 -0
- package/dist/host/agents/skills/orkestrel-polish/SKILL.md +16 -20
- package/dist/host/agents/skills/orkestrel-polish/references/capture-harness.md +16 -26
- package/dist/host/agents/skills/orkestrel-publish/SKILL.md +12 -26
- package/dist/host/agents/skills/orkestrel-publish/agents/openai.yaml +2 -2
- package/dist/host/agents/skills/orkestrel-publish/references/adopt.md +23 -0
- package/dist/host/agents/skills/orkestrel-publish/references/release.md +4 -4
- package/dist/host/agents/skills/orkestrel-publish/references/wave.md +10 -9
- package/dist/host/agents/skills/orkestrel-publish/references/window.md +20 -25
- package/dist/host/agents/skills/orkestrel-publish/scripts/compare.ts +348 -59
- package/dist/host/agents/skills/orkestrel-publish/scripts/pins.ts +202 -22
- package/dist/host/agents/skills/orkestrel-publish/scripts/swap.ts +1061 -191
- package/dist/host/agents/skills/orkestrel-publish/scripts/wave.ts +812 -140
- package/dist/host/agents/skills/orkestrel-publish/scripts/window.ts +280 -38
- package/dist/host/agents/skills/orkestrel-scout/SKILL.md +3 -3
- package/dist/host/agents/skills/orkestrel-scout/scripts/map.ts +428 -70
- package/dist/host/agents/templates/brief.md +1 -1
- package/dist/host/agents/transports/claude-cli.md +3 -3
- package/dist/host/agents/transports/codex.md +2 -7
- package/dist/host/agents/transports/cursor.md +2 -3
- package/dist/host/claude/AGENTS.md +2 -2
- package/dist/host/claude/agents/analyst.md +3 -3
- package/dist/host/claude/agents/astra.md +3 -3
- package/dist/host/claude/agents/builder.md +1 -1
- package/dist/host/claude/agents/checker.md +1 -1
- package/dist/host/claude/agents/distiller.md +1 -1
- package/dist/host/claude/agents/grok.md +1 -1
- package/dist/host/claude/agents/opus.md +2 -2
- package/dist/host/claude/agents/orkestrel.md +57 -90
- package/dist/host/claude/agents/researcher.md +1 -1
- package/dist/host/claude/agents/reviewer.md +4 -0
- package/dist/host/claude/agents/scout.md +1 -1
- package/dist/host/claude/agents/verifier.md +1 -1
- package/dist/host/claude/rules/application.md +14 -21
- package/dist/host/claude/rules/architecture.md +34 -44
- package/dist/host/claude/rules/browser.md +3 -4
- package/dist/host/claude/rules/campaign.md +2 -2
- package/dist/host/claude/rules/documentation.md +25 -12
- package/dist/host/claude/rules/names.md +9 -4
- package/dist/host/claude/rules/patterns.md +6 -7
- package/dist/host/claude/rules/portability.md +1 -1
- package/dist/host/claude/rules/quality.md +4 -5
- package/dist/host/claude/rules/styles.md +2 -2
- package/dist/host/claude/rules/tests.md +57 -114
- package/dist/host/claude/rules/typescript.md +7 -9
- package/dist/host/claude/rules/workspace.md +21 -19
- package/dist/host/claude/rules/writing.md +2 -5
- package/dist/host/claude/skills/orkestrel-dispatch/SKILL.md +1 -1
- package/dist/host/claude/skills/orkestrel-harden/SKILL.md +1 -1
- package/dist/host/claude/skills/orkestrel-publish/SKILL.md +1 -1
- package/dist/host/claude/skills/orkestrel-scout/SKILL.md +1 -1
- package/dist/host/codex/agents/analyst.toml +9 -3
- package/dist/host/codex/agents/astra.toml +3 -4
- package/dist/host/codex/agents/builder.toml +1 -1
- package/dist/host/codex/agents/opus.toml +3 -4
- package/dist/host/codex/agents/orkestrel.toml +1 -1
- package/dist/host/codex/agents/researcher.toml +2 -2
- package/dist/host/codex/agents/verifier.toml +1 -1
- package/dist/host/configs/helpers.ts +21 -1
- package/dist/host/configs/policy.ts +299 -15
- package/dist/host/dotfiles/oxlintrc.json +137 -3
- package/dist/host/guides/agent.md +111 -134
- package/dist/host/guides/capture.md +801 -0
- package/dist/host/guides/probe.md +27 -8
- package/dist/host/guides/scaffold.md +96 -18
- package/dist/host/guides/study.md +534 -440
- package/dist/host/guides/test.md +111 -44
- package/dist/host/manifest.json +160 -240
- package/dist/host/tests/config.test.ts +971 -187
- package/dist/host/tests/policy.test.ts +127 -1
- package/dist/host/tests/setupPolicy.ts +893 -33
- package/dist/src/core/index.cjs +214 -86
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +122 -38
- package/dist/src/core/index.d.ts +122 -38
- package/dist/src/core/index.js +209 -85
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +20 -27
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.js +21 -28
- package/dist/src/server/index.js.map +1 -1
- package/package.json +11 -11
- package/dist/agents/skills/orkestrel-dispatch/scripts/helpers.js +0 -283
- package/dist/host/agents/skills/orkestrel-dispatch/references/bench.md +0 -25
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/helpers.ts +0 -333
- package/dist/host/guides/supervisor.md +0 -4890
|
@@ -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 =
|
|
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?:
|
|
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,6 +37,7 @@ 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
|
|
@@ -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:
|
|
123
|
+
readonly sourceCode: PolicyScopeCode
|
|
93
124
|
report(diagnostic: PolicyDiagnostic): void
|
|
94
125
|
}
|
|
95
126
|
|
|
@@ -130,6 +161,7 @@ export interface PolicyVisitor {
|
|
|
130
161
|
readonly TSEnumDeclaration?: (node: PolicyNode) => void
|
|
131
162
|
readonly TSInterfaceDeclaration?: (node: PolicyNode) => void
|
|
132
163
|
readonly TSModuleDeclaration?: (node: PolicyNode) => void
|
|
164
|
+
readonly TSPropertySignature?: (node: PolicyNode) => void
|
|
133
165
|
readonly TSTypeAliasDeclaration?: (node: PolicyNode) => void
|
|
134
166
|
readonly VariableDeclaration?: (node: PolicyNode) => void
|
|
135
167
|
}
|
|
@@ -266,6 +298,68 @@ export const POLICY_ENDING_GLOBS: readonly string[] = Object.freeze([
|
|
|
266
298
|
'src/**/*.ts',
|
|
267
299
|
])
|
|
268
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
|
+
|
|
269
363
|
/**
|
|
270
364
|
* Matches the file name shape an implementation file takes, holding the class that matches its
|
|
271
365
|
* stem.
|
|
@@ -870,12 +964,37 @@ export function isPolicyVoiced(word: string): boolean {
|
|
|
870
964
|
}
|
|
871
965
|
|
|
872
966
|
/**
|
|
873
|
-
*
|
|
967
|
+
* Reads the doc block written directly above one top-level statement.
|
|
874
968
|
*
|
|
875
969
|
* @remarks
|
|
876
970
|
* A statement takes the last comment that closes before it, and takes it only where that comment is
|
|
877
971
|
* a doc block and nothing but whitespace separates the two. A blank line between them is still
|
|
878
|
-
* 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.
|
|
879
998
|
*
|
|
880
999
|
* @param node - The program node whose top-level statements are read.
|
|
881
1000
|
* @param sourceCode - The source-text reader supplying the comments and the text between them.
|
|
@@ -886,24 +1005,18 @@ export function programToPolicyDocs(
|
|
|
886
1005
|
sourceCode: PolicySourceCode,
|
|
887
1006
|
): readonly PolicyDoc[] {
|
|
888
1007
|
// The iterator consumes one entry at a time and reads a hole as an `undefined` entry, as a
|
|
889
|
-
// `for…of` over the list would. The comments are read
|
|
890
|
-
//
|
|
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.
|
|
891
1010
|
const comments = sourceCode.getAllComments()
|
|
892
1011
|
return Iterator.from(programToPolicyStatements(node))
|
|
893
1012
|
.flatMap((statement) => {
|
|
894
1013
|
if (!statement.type.startsWith('Export')) return []
|
|
895
|
-
const
|
|
896
|
-
|
|
897
|
-
.flatMap((comment) => (comment.range[1] <= start ? [comment] : []))
|
|
898
|
-
.toArray()
|
|
899
|
-
.at(-1)
|
|
900
|
-
if (previous === undefined || previous.type !== 'Block') return []
|
|
901
|
-
if (!previous.value.startsWith('*')) return []
|
|
902
|
-
if (sourceCode.text.slice(previous.range[1], start).trim() !== '') return []
|
|
1014
|
+
const comment = statementToPolicyDoc(statement, comments, sourceCode)
|
|
1015
|
+
if (comment === undefined) return []
|
|
903
1016
|
const declaration = statement.declaration
|
|
904
1017
|
return [
|
|
905
1018
|
{
|
|
906
|
-
comment
|
|
1019
|
+
comment,
|
|
907
1020
|
name:
|
|
908
1021
|
identifierToPolicyName(declaration?.id) ??
|
|
909
1022
|
identifierToPolicyName(declaration?.declarations?.[0]?.id),
|
|
@@ -1023,6 +1136,54 @@ export function reportPrivacy(context: PolicyContext, node: PolicyExpression): v
|
|
|
1023
1136
|
}
|
|
1024
1137
|
}
|
|
1025
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
|
+
|
|
1026
1187
|
/** Reports a centralized declaration that carries no export. */
|
|
1027
1188
|
export function reportHidden(context: PolicyContext, node: PolicyExpression): void {
|
|
1028
1189
|
if (isPolicyAmbient(context.filename)) return
|
|
@@ -1193,6 +1354,64 @@ export function reportEndingImport(context: PolicyContext, node: PolicyExpressio
|
|
|
1193
1354
|
if (importsPolicyTerminator(node)) context.report({ node, messageId: 'terminator' })
|
|
1194
1355
|
}
|
|
1195
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
|
+
|
|
1196
1415
|
/** Bans function declarations and assignments inside another function body. */
|
|
1197
1416
|
export const NESTED_RULE: PolicyRuleInterface = {
|
|
1198
1417
|
meta: {
|
|
@@ -1262,6 +1481,25 @@ export const PRIVACY_RULE: PolicyRuleInterface = {
|
|
|
1262
1481
|
},
|
|
1263
1482
|
}
|
|
1264
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
|
+
|
|
1265
1503
|
/** Bans a centralized declaration that no export reaches. */
|
|
1266
1504
|
export const HIDDEN_RULE: PolicyRuleInterface = {
|
|
1267
1505
|
meta: {
|
|
@@ -1546,6 +1784,49 @@ export const VOICE_RULE: PolicyRuleInterface = {
|
|
|
1546
1784
|
},
|
|
1547
1785
|
}
|
|
1548
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
|
+
|
|
1549
1830
|
/** Bans a comment carrying a term the substitution table bans unconditionally. */
|
|
1550
1831
|
export const TERM_RULE: PolicyRuleInterface = {
|
|
1551
1832
|
meta: {
|
|
@@ -1571,6 +1852,7 @@ export default {
|
|
|
1571
1852
|
rules: {
|
|
1572
1853
|
'no-mocking': MOCKING_RULE,
|
|
1573
1854
|
'no-keyword-privacy': PRIVACY_RULE,
|
|
1855
|
+
'no-let': LET_RULE,
|
|
1574
1856
|
'no-nested-functions': NESTED_RULE,
|
|
1575
1857
|
'no-hidden-declaration': HIDDEN_RULE,
|
|
1576
1858
|
'no-misplaced-type': TYPE_RULE,
|
|
@@ -1586,5 +1868,7 @@ export default {
|
|
|
1586
1868
|
'no-host-line-endings': ENDING_RULE,
|
|
1587
1869
|
'no-malformed-summary': VOICE_RULE,
|
|
1588
1870
|
'no-banned-term': TERM_RULE,
|
|
1871
|
+
'no-malformed-script': SCRIPT_RULE,
|
|
1872
|
+
'no-mutable-property': MUTABLE_RULE,
|
|
1589
1873
|
},
|
|
1590
1874
|
}
|
|
@@ -50,14 +50,20 @@
|
|
|
50
50
|
"no-unused-private-class-members": "error",
|
|
51
51
|
"no-var": "error",
|
|
52
52
|
"prefer-const": "error",
|
|
53
|
+
"no-param-reassign": "error",
|
|
54
|
+
"no-ex-assign": "error",
|
|
53
55
|
"no-debugger": "error",
|
|
54
56
|
"no-underscore-dangle": "warn",
|
|
55
57
|
|
|
56
58
|
"vitest/no-alias-methods": "error",
|
|
59
|
+
"vitest/no-identical-title": "error",
|
|
60
|
+
"vitest/no-focused-tests": "error",
|
|
61
|
+
"vitest/no-disabled-tests": "error",
|
|
57
62
|
"vitest/warn-todo": "off",
|
|
58
63
|
|
|
59
64
|
"policy/no-mocking": "error",
|
|
60
65
|
"policy/no-keyword-privacy": "error",
|
|
66
|
+
"policy/no-let": "error",
|
|
61
67
|
"policy/no-malformed-summary": "error",
|
|
62
68
|
"policy/no-banned-term": "error"
|
|
63
69
|
},
|
|
@@ -86,13 +92,27 @@
|
|
|
86
92
|
"files": [".agents/skills/*/scripts/*.ts"],
|
|
87
93
|
"rules": {
|
|
88
94
|
"policy/no-nested-functions": "error",
|
|
89
|
-
"policy/no-host-line-endings": "error"
|
|
95
|
+
"policy/no-host-line-endings": "error",
|
|
96
|
+
"policy/no-malformed-script": "error",
|
|
97
|
+
"no-restricted-imports": [
|
|
98
|
+
"error",
|
|
99
|
+
{
|
|
100
|
+
"patterns": [
|
|
101
|
+
{
|
|
102
|
+
"regex": "^(?:[^n]|n[^o]|no[^d]|nod[^e]|node[^:]|n$|no$|nod$|node$|$)",
|
|
103
|
+
"message": "a skill script is self-contained: import node: modules only and declare every helper in the script"
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
]
|
|
90
108
|
}
|
|
91
109
|
},
|
|
92
110
|
{
|
|
93
111
|
"files": [
|
|
94
112
|
"src/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx,vue}",
|
|
95
|
-
"app/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx,vue}"
|
|
113
|
+
"app/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx,vue}",
|
|
114
|
+
"tests/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx,vue}",
|
|
115
|
+
"configs/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx,vue}"
|
|
96
116
|
],
|
|
97
117
|
"rules": {
|
|
98
118
|
"policy/no-nested-functions": "error"
|
|
@@ -110,7 +130,8 @@
|
|
|
110
130
|
"policy/no-misnamed-parser": "error",
|
|
111
131
|
"policy/no-misnamed-factory": "error",
|
|
112
132
|
"policy/no-misnamed-plugin": "error",
|
|
113
|
-
"policy/no-malformed-domain": "error"
|
|
133
|
+
"policy/no-malformed-domain": "error",
|
|
134
|
+
"policy/no-mutable-property": "error"
|
|
114
135
|
}
|
|
115
136
|
},
|
|
116
137
|
{
|
|
@@ -772,6 +793,119 @@
|
|
|
772
793
|
}
|
|
773
794
|
]
|
|
774
795
|
}
|
|
796
|
+
},
|
|
797
|
+
{
|
|
798
|
+
"files": ["tests/setup*.ts"],
|
|
799
|
+
"rules": {
|
|
800
|
+
"no-restricted-imports": [
|
|
801
|
+
"error",
|
|
802
|
+
{
|
|
803
|
+
"paths": [
|
|
804
|
+
{
|
|
805
|
+
"name": "vitest",
|
|
806
|
+
"importNames": ["describe", "it", "test", "expect"],
|
|
807
|
+
"message": "a setup module owns everything an assertion needs and nothing an assertion is: register and assert in a test file"
|
|
808
|
+
}
|
|
809
|
+
],
|
|
810
|
+
"patterns": [
|
|
811
|
+
{
|
|
812
|
+
"regex": "^typescript(?:[/?#]|$)",
|
|
813
|
+
"message": "the in-process compiler API is not a surface the fleet uses"
|
|
814
|
+
}
|
|
815
|
+
]
|
|
816
|
+
}
|
|
817
|
+
]
|
|
818
|
+
}
|
|
819
|
+
},
|
|
820
|
+
{
|
|
821
|
+
"files": ["tests/setup*.test.ts"],
|
|
822
|
+
"rules": {
|
|
823
|
+
"no-restricted-imports": [
|
|
824
|
+
"error",
|
|
825
|
+
{
|
|
826
|
+
"patterns": [
|
|
827
|
+
{
|
|
828
|
+
"regex": "^typescript(?:[/?#]|$)",
|
|
829
|
+
"message": "the in-process compiler API is not a surface the fleet uses"
|
|
830
|
+
}
|
|
831
|
+
]
|
|
832
|
+
}
|
|
833
|
+
]
|
|
834
|
+
}
|
|
835
|
+
},
|
|
836
|
+
{
|
|
837
|
+
"files": ["tests/setup.ts"],
|
|
838
|
+
"rules": {
|
|
839
|
+
"no-restricted-imports": [
|
|
840
|
+
"error",
|
|
841
|
+
{
|
|
842
|
+
"paths": [
|
|
843
|
+
{
|
|
844
|
+
"name": "vitest",
|
|
845
|
+
"importNames": ["describe", "it", "test", "expect"],
|
|
846
|
+
"message": "a setup module owns everything an assertion needs and nothing an assertion is: register and assert in a test file"
|
|
847
|
+
}
|
|
848
|
+
],
|
|
849
|
+
"patterns": [
|
|
850
|
+
{
|
|
851
|
+
"regex": "^typescript(?:[/?#]|$)",
|
|
852
|
+
"message": "the in-process compiler API is not a surface the fleet uses"
|
|
853
|
+
},
|
|
854
|
+
{
|
|
855
|
+
"regex": "^node:",
|
|
856
|
+
"message": "tests/setup.ts must stay host-independent: import no node: module"
|
|
857
|
+
},
|
|
858
|
+
{
|
|
859
|
+
"regex": "^(?:vue(?:[/?#]|$)|@vue/)",
|
|
860
|
+
"message": "tests/setup.ts must stay host-independent: import no Vue module"
|
|
861
|
+
}
|
|
862
|
+
]
|
|
863
|
+
}
|
|
864
|
+
]
|
|
865
|
+
}
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
"files": ["tests/setupBrowser.ts", "tests/setupStyles.ts"],
|
|
869
|
+
"rules": {
|
|
870
|
+
"no-restricted-imports": [
|
|
871
|
+
"error",
|
|
872
|
+
{
|
|
873
|
+
"paths": [
|
|
874
|
+
{
|
|
875
|
+
"name": "vitest",
|
|
876
|
+
"importNames": ["describe", "it", "test", "expect"],
|
|
877
|
+
"message": "a setup module owns everything an assertion needs and nothing an assertion is: register and assert in a test file"
|
|
878
|
+
}
|
|
879
|
+
],
|
|
880
|
+
"patterns": [
|
|
881
|
+
{
|
|
882
|
+
"regex": "^typescript(?:[/?#]|$)",
|
|
883
|
+
"message": "the in-process compiler API is not a surface the fleet uses"
|
|
884
|
+
},
|
|
885
|
+
{
|
|
886
|
+
"regex": "^node:",
|
|
887
|
+
"message": "the browser and styles setup modules hold no Node: import no node: module"
|
|
888
|
+
}
|
|
889
|
+
]
|
|
890
|
+
}
|
|
891
|
+
]
|
|
892
|
+
}
|
|
893
|
+
},
|
|
894
|
+
{
|
|
895
|
+
"files": ["configs/policy.ts"],
|
|
896
|
+
"rules": {
|
|
897
|
+
"no-restricted-imports": [
|
|
898
|
+
"error",
|
|
899
|
+
{
|
|
900
|
+
"patterns": [
|
|
901
|
+
{
|
|
902
|
+
"regex": ".",
|
|
903
|
+
"message": "configs/policy.ts imports nothing, so it resolves in every workspace"
|
|
904
|
+
}
|
|
905
|
+
]
|
|
906
|
+
}
|
|
907
|
+
]
|
|
908
|
+
}
|
|
775
909
|
}
|
|
776
910
|
]
|
|
777
911
|
}
|