@orkestrel/scaffold 0.0.77 → 0.0.78
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 +204 -0
- package/dist/agents/skills/orkestrel-dispatch/scripts/brief.js +102 -0
- package/dist/agents/skills/orkestrel-dispatch/scripts/cite.js +95 -0
- package/dist/agents/skills/orkestrel-dispatch/scripts/helpers.js +207 -0
- package/dist/agents/skills/orkestrel-dispatch/scripts/launch.js +108 -0
- package/dist/agents/skills/orkestrel-dispatch/scripts/login.js +114 -0
- package/dist/agents/skills/orkestrel-dispatch/scripts/result.js +108 -0
- package/dist/agents/skills/orkestrel-dispatch/scripts/sweep.js +156 -0
- package/dist/agents/skills/orkestrel-harden/scripts/discovery.js +196 -0
- package/dist/agents/skills/orkestrel-publish/scripts/compare.js +206 -0
- package/dist/agents/skills/orkestrel-publish/scripts/pins.js +93 -0
- package/dist/agents/skills/orkestrel-publish/scripts/wave.js +458 -0
- package/dist/agents/skills/orkestrel-publish/scripts/window.js +188 -0
- package/dist/agents/skills/orkestrel-scout/scripts/map.js +300 -0
- package/dist/agents/templates/brief.md +55 -0
- package/dist/bin/main.js +4 -2
- package/dist/bin/main.js.map +1 -1
- package/dist/host/AGENTS.md +77 -135
- package/dist/host/agents/orchestration.md +147 -998
- package/dist/host/agents/skills/enterprise-bootstrap/SKILL.md +2 -2
- package/dist/host/agents/skills/enterprise-bootstrap/references/inspection.md +1 -1
- package/dist/host/agents/skills/{orkestrel-align-packages → orkestrel-align}/SKILL.md +6 -13
- package/dist/host/agents/skills/{orkestrel-align-packages → orkestrel-align}/agents/openai.yaml +1 -1
- package/dist/host/agents/skills/{orkestrel-align-packages → orkestrel-align}/references/fleet.md +5 -7
- package/dist/host/agents/skills/{orkestrel-build-application → orkestrel-build}/SKILL.md +11 -22
- package/dist/host/agents/skills/{orkestrel-build-application → orkestrel-build}/agents/openai.yaml +1 -1
- package/dist/host/agents/skills/orkestrel-debrief/SKILL.md +8 -16
- package/dist/host/agents/skills/orkestrel-debrief/references/instruction-audit.md +3 -3
- package/dist/host/agents/skills/orkestrel-debrief/references/retention.md +13 -13
- package/dist/host/agents/skills/orkestrel-dispatch/SKILL.md +61 -0
- package/dist/host/agents/skills/orkestrel-dispatch/agents/openai.yaml +4 -0
- package/dist/host/agents/skills/orkestrel-dispatch/references/bench.md +25 -0
- package/dist/host/agents/skills/orkestrel-dispatch/references/launch.md +32 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/bench.ts +259 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/brief.ts +110 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/cite.ts +115 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/helpers.ts +239 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/launch.ts +124 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/login.ts +123 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/result.ts +129 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/sweep.ts +157 -0
- package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +42 -193
- package/dist/host/agents/skills/orkestrel-falsify/references/brief.md +38 -108
- package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +35 -134
- package/dist/host/agents/skills/{orkestrel-harden-package → orkestrel-harden}/SKILL.md +10 -14
- package/dist/host/agents/skills/{orkestrel-harden-package → orkestrel-harden}/agents/openai.yaml +1 -1
- package/dist/host/agents/skills/{orkestrel-harden-package → orkestrel-harden}/references/hardening.md +3 -4
- package/dist/host/agents/skills/orkestrel-harden/scripts/discovery.ts +228 -0
- package/dist/host/agents/skills/{orkestrel-prove-journey → orkestrel-journey}/SKILL.md +15 -23
- package/dist/host/agents/skills/orkestrel-journey/agents/openai.yaml +4 -0
- package/dist/host/agents/skills/{orkestrel-prove-journey → orkestrel-journey}/references/captures.md +1 -1
- package/dist/host/agents/skills/{orkestrel-polish-surface → orkestrel-polish}/SKILL.md +25 -33
- package/dist/host/agents/skills/{orkestrel-polish-surface → orkestrel-polish}/agents/openai.yaml +1 -1
- package/dist/host/agents/skills/{orkestrel-polish-surface → orkestrel-polish}/references/capture-harness.md +3 -3
- package/dist/host/agents/skills/orkestrel-publish/SKILL.md +33 -20
- package/dist/host/agents/skills/orkestrel-publish/references/release.md +39 -0
- package/dist/host/agents/skills/orkestrel-publish/references/wave.md +22 -21
- package/dist/host/agents/skills/orkestrel-publish/references/window.md +27 -14
- package/dist/host/agents/skills/orkestrel-publish/scripts/compare.ts +220 -0
- package/dist/host/agents/skills/orkestrel-publish/scripts/pins.ts +114 -0
- package/dist/host/agents/skills/orkestrel-publish/scripts/wave.ts +629 -0
- package/dist/host/agents/skills/orkestrel-publish/scripts/window.ts +242 -0
- package/dist/host/agents/skills/orkestrel-scout/SKILL.md +28 -0
- package/dist/host/agents/skills/orkestrel-scout/agents/openai.yaml +4 -0
- package/dist/host/agents/skills/orkestrel-scout/scripts/map.ts +352 -0
- package/dist/host/agents/templates/brief.md +21 -142
- package/dist/host/agents/transports/claude-cli.md +21 -0
- package/dist/host/agents/transports/codex.md +38 -159
- package/dist/host/agents/transports/cursor.md +16 -65
- package/dist/host/claude/AGENTS.md +38 -0
- package/dist/host/claude/agents/analyst.md +14 -53
- package/dist/host/claude/agents/astra.md +26 -0
- package/dist/host/claude/agents/builder.md +14 -30
- package/dist/host/claude/agents/checker.md +13 -57
- package/dist/host/claude/agents/distiller.md +11 -26
- package/dist/host/claude/agents/grok.md +12 -35
- package/dist/host/claude/agents/opus.md +14 -30
- package/dist/host/claude/agents/planner.md +10 -44
- package/dist/host/claude/agents/researcher.md +11 -30
- package/dist/host/claude/agents/reviewer.md +11 -95
- package/dist/host/claude/agents/scout.md +9 -23
- package/dist/host/claude/agents/verifier.md +15 -33
- package/dist/host/claude/rules/documentation.md +8 -2
- package/dist/host/claude/rules/portability.md +7 -1
- package/dist/host/claude/rules/quality.md +36 -96
- package/dist/host/claude/rules/styles.md +3 -0
- package/dist/host/claude/rules/tests.md +6 -3
- package/dist/host/claude/rules/workspace.md +19 -15
- package/dist/host/claude/rules/writing.md +57 -108
- package/dist/host/claude/settings.json +5 -3
- package/dist/host/claude/skills/enterprise-bootstrap/SKILL.md +1 -1
- package/dist/host/claude/skills/{orkestrel-align-packages → orkestrel-align}/SKILL.md +2 -2
- package/dist/host/claude/skills/{orkestrel-build-application → orkestrel-build}/SKILL.md +2 -2
- package/dist/host/claude/skills/orkestrel-dispatch/SKILL.md +11 -0
- package/dist/host/claude/skills/orkestrel-falsify/SKILL.md +2 -1
- package/dist/host/claude/skills/{orkestrel-harden-package → orkestrel-harden}/SKILL.md +2 -2
- package/dist/host/claude/skills/{orkestrel-prove-journey → orkestrel-journey}/SKILL.md +2 -2
- package/dist/host/claude/skills/orkestrel-polish/SKILL.md +12 -0
- package/dist/host/claude/skills/orkestrel-scout/SKILL.md +11 -0
- package/dist/host/codex/agents/analyst.toml +14 -31
- package/dist/host/codex/agents/astra.toml +25 -0
- package/dist/host/codex/agents/builder.toml +13 -20
- package/dist/host/codex/agents/checker.toml +13 -27
- package/dist/host/codex/agents/distiller.toml +9 -22
- package/dist/host/codex/agents/grok.toml +11 -30
- package/dist/host/codex/agents/opus.toml +14 -22
- package/dist/host/codex/agents/orkestrel.toml +1 -1
- package/dist/host/codex/agents/planner.toml +11 -28
- package/dist/host/codex/agents/researcher.toml +10 -22
- package/dist/host/codex/agents/reviewer.toml +11 -27
- package/dist/host/codex/agents/scout.toml +11 -17
- package/dist/host/codex/agents/verifier.toml +16 -12
- package/dist/host/codex/config.toml +18 -21
- package/dist/host/cursor/mcp.json +0 -4
- package/dist/host/cursor/rules/orchestration.mdc +12 -20
- package/dist/host/dotfiles/mcp.json +0 -4
- package/dist/host/dotfiles/oxlintrc.json +7 -0
- package/dist/host/guides/probe.md +9 -9
- package/dist/host/guides/scaffold.md +117 -71
- package/dist/host/guides/test.md +1 -1
- package/dist/host/manifest.json +321 -184
- package/dist/host/scripts/codex.sh +0 -0
- package/dist/host/scripts/cursor.sh +0 -0
- package/dist/host/scripts/deps.sh +0 -0
- package/dist/host/scripts/ollama.sh +0 -0
- package/dist/host/tests/config.test.ts +68 -46
- package/dist/host/tests/policy.test.ts +1 -5
- package/dist/host/tests/setupPolicy.ts +179 -4
- package/dist/src/core/index.cjs +255 -84
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +94 -29
- package/dist/src/core/index.d.ts +94 -29
- package/dist/src/core/index.js +253 -85
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +55 -9
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +29 -4
- package/dist/src/server/index.d.ts +29 -4
- package/dist/src/server/index.js +56 -11
- package/dist/src/server/index.js.map +1 -1
- package/package.json +15 -11
- package/dist/host/CLAUDE.md +0 -61
- package/dist/host/agents/skills/orkestrel-prove-journey/agents/openai.yaml +0 -4
- package/dist/host/agents/transports/claude.md +0 -49
- package/dist/host/claude/agents/application.md +0 -36
- package/dist/host/claude/agents/sol.md +0 -61
- package/dist/host/claude/skills/orkestrel-polish-surface/SKILL.md +0 -12
- package/dist/host/codex/agents/application.toml +0 -25
- package/dist/host/codex/agents/sol.toml +0 -19
- /package/dist/host/agents/skills/{orkestrel-align-packages → orkestrel-align}/references/integration.md +0 -0
- /package/dist/host/agents/skills/{orkestrel-harden-package → orkestrel-harden}/references/centralization.md +0 -0
- /package/dist/host/agents/skills/{orkestrel-harden-package → orkestrel-harden}/references/contract.md +0 -0
- /package/dist/host/agents/skills/{orkestrel-harden-package → orkestrel-harden}/references/research.md +0 -0
- /package/dist/host/agents/skills/{orkestrel-prove-journey → orkestrel-journey}/references/decide.md +0 -0
- /package/dist/host/agents/skills/{orkestrel-prove-journey → orkestrel-journey}/references/layer.md +0 -0
- /package/dist/host/agents/skills/{orkestrel-prove-journey → orkestrel-journey}/references/statechart.md +0 -0
- /package/dist/host/agents/skills/{orkestrel-prove-journey → orkestrel-journey}/references/styles.md +0 -0
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -186,6 +186,14 @@ describe('root configuration', () => {
|
|
|
186
186
|
setup: ['./tests/setup.ts', './tests/setupServer.ts'],
|
|
187
187
|
})
|
|
188
188
|
}
|
|
189
|
+
// The `configs/agents/tsconfig.skills.json` wrapper selects the `skills` blueprint fact, and the
|
|
190
|
+
// project it registers runs the mirrored proofs under `tests/agents/`.
|
|
191
|
+
if (existsSync(resolve(root, 'configs/agents/tsconfig.skills.json'))) {
|
|
192
|
+
expected.set('skills', {
|
|
193
|
+
include: 'tests/agents/**/*.test.ts',
|
|
194
|
+
setup: ['./tests/setup.ts'],
|
|
195
|
+
})
|
|
196
|
+
}
|
|
189
197
|
for (const label of [
|
|
190
198
|
'policy',
|
|
191
199
|
'config',
|
|
@@ -220,8 +228,8 @@ describe('root configuration', () => {
|
|
|
220
228
|
})
|
|
221
229
|
}
|
|
222
230
|
expected.set('probe', {
|
|
223
|
-
benchmark: ['tmp/
|
|
224
|
-
include: 'tmp/
|
|
231
|
+
benchmark: ['tmp/probes/**/*.test.ts', 'tests/**/*.test.ts'],
|
|
232
|
+
include: 'tmp/probes/**/*.test.ts',
|
|
225
233
|
parallel: false,
|
|
226
234
|
pool: 'threads',
|
|
227
235
|
setup: ['./tests/setup.ts'],
|
|
@@ -370,63 +378,77 @@ describe('root configuration', () => {
|
|
|
370
378
|
}).toThrow(/strictly equal/u)
|
|
371
379
|
})
|
|
372
380
|
|
|
373
|
-
it('
|
|
381
|
+
it('returns the invocation mode and no other invocation field from every registered project factory', () => {
|
|
374
382
|
const projects = configuration.test?.projects
|
|
375
383
|
if (!Array.isArray(projects)) throw new Error('The root configuration carries no projects')
|
|
376
384
|
if (projects.length === 0) throw new Error('The root configuration registers no project')
|
|
377
|
-
//
|
|
378
|
-
// `
|
|
379
|
-
//
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
//
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
setupFiles: ['./tests/setup.ts'],
|
|
388
|
-
},
|
|
389
|
-
}
|
|
390
|
-
const callable = projects.concat(inline).filter((entry) => typeof entry === 'function')
|
|
391
|
-
for (const entry of projects) expect(callable).toContain(entry)
|
|
392
|
-
expect(callable).not.toContain(inline)
|
|
393
|
-
})
|
|
394
|
-
|
|
395
|
-
it('keeps Vitest invocation fields out of project configurations', () => {
|
|
396
|
-
const projects = configuration.test?.projects
|
|
397
|
-
if (!Array.isArray(projects)) throw new Error('The root configuration carries no projects')
|
|
398
|
-
const factories = projects.filter((row) => typeof row === 'function')
|
|
399
|
-
if (factories.length === 0)
|
|
400
|
-
throw new Error('The root configuration registers no project factory')
|
|
401
|
-
const sentinel = {
|
|
402
|
-
command: 'sentinel-command',
|
|
385
|
+
// Vitest calls each registered factory with its invocation record, whose `mode` is the
|
|
386
|
+
// `--mode` value, and runs a project that declares no `mode` in its own run mode, `test`.
|
|
387
|
+
// `prepublishOnly` runs the distribution proof with `--mode release`, and that proof fails
|
|
388
|
+
// rather than skips only when it reads `release`, so every project returns the record's
|
|
389
|
+
// `mode` and none of the record's other fields. The sentinel mode is a value no factory
|
|
390
|
+
// declares, so only a forwarded mode reads it. The controls are the ways a row misses
|
|
391
|
+
// that: a factory that ignores the record, one that spreads it whole, and an inline
|
|
392
|
+
// entry, which Vitest never calls.
|
|
393
|
+
const invocation = {
|
|
394
|
+
command: 'serve',
|
|
403
395
|
isPreview: true,
|
|
404
396
|
isSsrBuild: true,
|
|
405
397
|
mode: 'sentinel-mode',
|
|
406
398
|
sentinel: true,
|
|
407
399
|
}
|
|
408
|
-
|
|
409
|
-
|
|
400
|
+
const test = { name: { label: 'control' }, include: ['tests/control.test.ts'] }
|
|
401
|
+
const ignoring = Object.defineProperty(() => ({ test }), 'name', { value: 'ignoring' })
|
|
402
|
+
const spreading = Object.defineProperty((record: object) => ({ ...record, test }), 'name', {
|
|
403
|
+
value: 'spreading',
|
|
404
|
+
})
|
|
405
|
+
const entries: readonly unknown[] = [...projects, ignoring, spreading, { test }]
|
|
406
|
+
const readings = entries.map((entry) => {
|
|
407
|
+
if (typeof entry !== 'function') {
|
|
408
|
+
return { name: undefined, callable: false, mode: undefined, leaked: [] }
|
|
409
|
+
}
|
|
410
|
+
const project: unknown = Reflect.apply(entry, undefined, [invocation])
|
|
410
411
|
if (typeof project !== 'object' || project === null) {
|
|
411
|
-
throw new Error(
|
|
412
|
+
throw new Error(`The project factory ${entry.name} returned no configuration`)
|
|
412
413
|
}
|
|
413
|
-
|
|
414
|
-
|
|
414
|
+
return {
|
|
415
|
+
name: entry.name,
|
|
416
|
+
callable: true,
|
|
417
|
+
mode: Object.getOwnPropertyDescriptor(project, 'mode')?.value,
|
|
418
|
+
leaked: Object.keys(invocation).filter(
|
|
419
|
+
(field) => field !== 'mode' && Object.hasOwn(project, field),
|
|
420
|
+
),
|
|
415
421
|
}
|
|
422
|
+
})
|
|
423
|
+
const forwarded = { callable: true, mode: 'sentinel-mode', leaked: [] }
|
|
424
|
+
expect(readings.slice(0, projects.length)).toStrictEqual(
|
|
425
|
+
projects.map((entry) => ({
|
|
426
|
+
name: typeof entry === 'function' ? entry.name : undefined,
|
|
427
|
+
...forwarded,
|
|
428
|
+
})),
|
|
429
|
+
)
|
|
430
|
+
expect(readings.slice(projects.length)).toStrictEqual([
|
|
431
|
+
{ name: 'ignoring', callable: true, mode: undefined, leaked: [] },
|
|
432
|
+
{
|
|
433
|
+
name: 'spreading',
|
|
434
|
+
callable: true,
|
|
435
|
+
mode: 'sentinel-mode',
|
|
436
|
+
leaked: ['command', 'isPreview', 'isSsrBuild', 'sentinel'],
|
|
437
|
+
},
|
|
438
|
+
{ name: undefined, callable: false, mode: undefined, leaked: [] },
|
|
439
|
+
])
|
|
440
|
+
for (const reading of readings.slice(projects.length)) {
|
|
441
|
+
expect({ ...reading, name: undefined }).not.toStrictEqual({ name: undefined, ...forwarded })
|
|
416
442
|
}
|
|
417
443
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
expect(Object.getOwnPropertyDescriptor(project, field)?.value).toBeUndefined()
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
}).toThrow(/expected/u)
|
|
444
|
+
// A record that carries no string `mode` is refused rather than forwarded as a project
|
|
445
|
+
// with no mode, which would run in `test` again.
|
|
446
|
+
for (const entry of projects) {
|
|
447
|
+
if (typeof entry !== 'function') continue
|
|
448
|
+
expect(() => Reflect.apply(entry, undefined, [{ ...invocation, mode: undefined }])).toThrow(
|
|
449
|
+
'The project invocation carries no string mode',
|
|
450
|
+
)
|
|
451
|
+
}
|
|
430
452
|
})
|
|
431
453
|
|
|
432
454
|
it('requires and validates every selected target wrapper', async () => {
|
|
@@ -762,7 +762,7 @@ describe('repository policy', () => {
|
|
|
762
762
|
// pointer pair and no `.agents/` directory, no rule map, and no skill bridges.
|
|
763
763
|
// This vendored suite runs there, and every inspector it routes through has to
|
|
764
764
|
// stay silent on that shape.
|
|
765
|
-
it('accepts a target holding the pointer
|
|
765
|
+
it('accepts a target holding the pointer and no canon tree', () => {
|
|
766
766
|
const scratch = createPolicyScratch({ prefix: 'orkestrel-policy-pointer-' })
|
|
767
767
|
try {
|
|
768
768
|
writePolicySurfaceHost(scratch)
|
|
@@ -770,10 +770,6 @@ describe('repository policy', () => {
|
|
|
770
770
|
'AGENTS.md',
|
|
771
771
|
'# AGENTS.md\n\nRead `node_modules/@orkestrel/scaffold/dist/host/AGENTS.md` for the canon.\n',
|
|
772
772
|
)
|
|
773
|
-
scratch.write(
|
|
774
|
-
'CLAUDE.md',
|
|
775
|
-
'# Claude Code bridge\n\nRead the `AGENTS.md` file beside this one first.\n',
|
|
776
|
-
)
|
|
777
773
|
scratch.write('.claude/settings.json', '{\n\t"permissions": {\n\t\t"allow": []\n\t}\n}\n')
|
|
778
774
|
scratch.write(
|
|
779
775
|
'.claude/agents/orkestrel.md',
|
|
@@ -220,6 +220,9 @@ export const SKILL_FAMILY_ROOT = '.agents/skills'
|
|
|
220
220
|
/** Names the directory whose immediate child directories form the Claude skill bridge family. */
|
|
221
221
|
export const SKILL_BRIDGE_ROOT = '.claude/skills'
|
|
222
222
|
|
|
223
|
+
/** Names the directory whose tree mirrors `.agents/skills/<skill>/scripts/*.ts` as `*.test.ts` proofs. */
|
|
224
|
+
export const SKILL_PROOF_ROOT = 'tests/agents/skills'
|
|
225
|
+
|
|
223
226
|
/** Holds the minimal valid skill text for physical family controls. */
|
|
224
227
|
export const SKILL_POLICY_TEXT =
|
|
225
228
|
'---\nname: sample\ndescription: Use this skill for a policy fixture.\n---\n\n# Skill\n'
|
|
@@ -831,6 +834,65 @@ export function readSkillReferences(root: string, name: string): readonly string
|
|
|
831
834
|
.sort()
|
|
832
835
|
}
|
|
833
836
|
|
|
837
|
+
/**
|
|
838
|
+
* Lists the TypeScript scripts a canonical skill directory holds.
|
|
839
|
+
*
|
|
840
|
+
* @param root - The workspace root to inspect.
|
|
841
|
+
* @param name - The skill directory name.
|
|
842
|
+
* @returns Each `scripts/*.ts` path relative to the skill directory, sorted.
|
|
843
|
+
*/
|
|
844
|
+
export function readSkillScripts(root: string, name: string): readonly string[] {
|
|
845
|
+
const directory = resolvePolicyDirectory(root, `${SKILL_FAMILY_ROOT}/${name}/scripts`)
|
|
846
|
+
if (directory === undefined) return []
|
|
847
|
+
return readdirSync(directory, { withFileTypes: true })
|
|
848
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith('.ts'))
|
|
849
|
+
.map((entry) => `scripts/${entry.name}`)
|
|
850
|
+
.sort()
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
/**
|
|
854
|
+
* Extracts every `scripts/*.ts` path a skill document names as its own.
|
|
855
|
+
*
|
|
856
|
+
* @remarks
|
|
857
|
+
* A path written under another skill's directory, such as
|
|
858
|
+
* `.agents/skills/orkestrel-dispatch/scripts/cite.ts` inside a different skill's document, names
|
|
859
|
+
* that skill's script and is skipped here; the owning skill's own sweep requires it.
|
|
860
|
+
*
|
|
861
|
+
* @param content - The raw SKILL.md text.
|
|
862
|
+
* @param name - The directory name of the skill whose document `content` is.
|
|
863
|
+
* @returns The distinct script paths, relative to the skill directory, in first-mention order.
|
|
864
|
+
*/
|
|
865
|
+
export function extractSkillScripts(content: string, name: string): readonly string[] {
|
|
866
|
+
const found: string[] = []
|
|
867
|
+
for (const match of content.matchAll(
|
|
868
|
+
/(?:\.agents\/skills\/([A-Za-z0-9-]+)\/)?(scripts\/[A-Za-z0-9][A-Za-z0-9._-]*\.ts)/gu,
|
|
869
|
+
)) {
|
|
870
|
+
const owner = match[1]
|
|
871
|
+
const script = match[2]
|
|
872
|
+
if (script === undefined || (owner !== undefined && owner !== name)) continue
|
|
873
|
+
if (!found.includes(script)) found.push(script)
|
|
874
|
+
}
|
|
875
|
+
return found
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* Extracts every script path a skill document names under another skill's directory.
|
|
880
|
+
*
|
|
881
|
+
* @param content - The raw SKILL.md text.
|
|
882
|
+
* @param name - The directory name of the skill whose document `content` is.
|
|
883
|
+
* @returns The distinct `.agents/skills/<other>/scripts/*.ts` paths, in first-mention order.
|
|
884
|
+
*/
|
|
885
|
+
export function extractForeignSkillScripts(content: string, name: string): readonly string[] {
|
|
886
|
+
const found: string[] = []
|
|
887
|
+
for (const match of content.matchAll(
|
|
888
|
+
/\.agents\/skills\/([A-Za-z0-9-]+)\/scripts\/[A-Za-z0-9][A-Za-z0-9._-]*\.ts/gu,
|
|
889
|
+
)) {
|
|
890
|
+
if (match[1] === name || found.includes(match[0])) continue
|
|
891
|
+
found.push(match[0])
|
|
892
|
+
}
|
|
893
|
+
return found
|
|
894
|
+
}
|
|
895
|
+
|
|
834
896
|
/**
|
|
835
897
|
* Creates metadata in the canonical skill interface shape.
|
|
836
898
|
*
|
|
@@ -1493,6 +1555,46 @@ export function inspectSkill(
|
|
|
1493
1555
|
)
|
|
1494
1556
|
}
|
|
1495
1557
|
}
|
|
1558
|
+
const scripts = content === undefined ? [] : extractSkillScripts(content, name)
|
|
1559
|
+
for (const script of scripts) {
|
|
1560
|
+
if (!isPolicyFile(root, `${base}/${script}`)) {
|
|
1561
|
+
violations.push(
|
|
1562
|
+
createPolicyViolation(
|
|
1563
|
+
'skill',
|
|
1564
|
+
`${base}/${script}`,
|
|
1565
|
+
`SKILL.md script resolves to an exact-case regular file: ${script}`,
|
|
1566
|
+
),
|
|
1567
|
+
)
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
for (const script of readSkillScripts(root, name)) {
|
|
1571
|
+
if (!scripts.includes(script)) {
|
|
1572
|
+
violations.push(
|
|
1573
|
+
createPolicyViolation(
|
|
1574
|
+
'skill',
|
|
1575
|
+
`${base}/${script}`,
|
|
1576
|
+
`scripts TypeScript file is named by SKILL.md: ${script}`,
|
|
1577
|
+
),
|
|
1578
|
+
)
|
|
1579
|
+
}
|
|
1580
|
+
const proof = `${SKILL_PROOF_ROOT}/${name}/${script.replace(/\.ts$/u, '.test.ts')}`
|
|
1581
|
+
if (!isPolicyFile(root, proof)) {
|
|
1582
|
+
violations.push(
|
|
1583
|
+
createPolicyViolation('skill', proof, `skill script has a mirrored proof: ${proof}`),
|
|
1584
|
+
)
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
for (const script of content === undefined ? [] : extractForeignSkillScripts(content, name)) {
|
|
1588
|
+
if (!isPolicyFile(root, script)) {
|
|
1589
|
+
violations.push(
|
|
1590
|
+
createPolicyViolation(
|
|
1591
|
+
'skill',
|
|
1592
|
+
script,
|
|
1593
|
+
`SKILL.md names another skill's script that resolves to an exact-case regular file: ${script}`,
|
|
1594
|
+
),
|
|
1595
|
+
)
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1496
1598
|
const references = resolvePolicyDirectory(root, `${base}/references`)
|
|
1497
1599
|
if (references !== undefined) {
|
|
1498
1600
|
for (const entry of readdirSync(references, { withFileTypes: true })) {
|
|
@@ -1514,6 +1616,7 @@ export function inspectSkill(
|
|
|
1514
1616
|
if (
|
|
1515
1617
|
path === 'agents' ||
|
|
1516
1618
|
path === 'references' ||
|
|
1619
|
+
path === 'scripts' ||
|
|
1517
1620
|
path.startsWith('references/') ||
|
|
1518
1621
|
(!hasSkill && path.toLowerCase() === 'skill.md') ||
|
|
1519
1622
|
(!hasMetadata && path.toLowerCase() === 'agents/openai.yaml')
|
|
@@ -1524,7 +1627,7 @@ export function inspectSkill(
|
|
|
1524
1627
|
createPolicyViolation(
|
|
1525
1628
|
'skill',
|
|
1526
1629
|
`${base}/${path}`,
|
|
1527
|
-
'skill directory contains only agents
|
|
1630
|
+
'skill directory contains only agents/, references/, and scripts/ directories',
|
|
1528
1631
|
),
|
|
1529
1632
|
)
|
|
1530
1633
|
continue
|
|
@@ -1545,6 +1648,7 @@ export function inspectSkill(
|
|
|
1545
1648
|
path === 'SKILL.md' ||
|
|
1546
1649
|
path === 'agents/openai.yaml' ||
|
|
1547
1650
|
/^references\/[^/]+\.md$/u.test(path) ||
|
|
1651
|
+
/^scripts\/[^/]+\.ts$/u.test(path) ||
|
|
1548
1652
|
(!hasSkill &&
|
|
1549
1653
|
(path.toLowerCase() === 'skill.md' || path.toLowerCase().startsWith('skill.md/'))) ||
|
|
1550
1654
|
(!hasMetadata && path.toLowerCase() === 'agents/openai.yaml') ||
|
|
@@ -1556,7 +1660,7 @@ export function inspectSkill(
|
|
|
1556
1660
|
createPolicyViolation(
|
|
1557
1661
|
'skill',
|
|
1558
1662
|
`${base}/${path}`,
|
|
1559
|
-
'skill directory contains only SKILL.md, agents/openai.yaml, and
|
|
1663
|
+
'skill directory contains only SKILL.md, agents/openai.yaml, references/*.md, and scripts/*.ts',
|
|
1560
1664
|
),
|
|
1561
1665
|
)
|
|
1562
1666
|
}
|
|
@@ -2574,6 +2678,76 @@ export const POLICY_CONTROLS: readonly PolicyControl[] = Object.freeze([
|
|
|
2574
2678
|
|
|
2575
2679
|
/** Lists the physical in-family controls for every skill-family assertion class. */
|
|
2576
2680
|
export const SKILL_POLICY_CONTROLS: readonly PolicyControl[] = Object.freeze([
|
|
2681
|
+
{
|
|
2682
|
+
label: 'rejects a named script of another skill that does not exist',
|
|
2683
|
+
membership: 'script paths qualified with another skill directory in canonical skill documents',
|
|
2684
|
+
rule: 'skill',
|
|
2685
|
+
files: [
|
|
2686
|
+
{
|
|
2687
|
+
path: '.agents/skills/sample/SKILL.md',
|
|
2688
|
+
content: SKILL_POLICY_TEXT + '\nRun `node .agents/skills/other/scripts/gone.ts` first.\n',
|
|
2689
|
+
},
|
|
2690
|
+
{ path: '.agents/skills/sample/agents/openai.yaml', content: createSkillMetadata('sample') },
|
|
2691
|
+
],
|
|
2692
|
+
violations: [
|
|
2693
|
+
{
|
|
2694
|
+
rule: 'skill',
|
|
2695
|
+
path: '.agents/skills/other/scripts/gone.ts',
|
|
2696
|
+
message:
|
|
2697
|
+
"SKILL.md names another skill's script that resolves to an exact-case regular file: .agents/skills/other/scripts/gone.ts",
|
|
2698
|
+
},
|
|
2699
|
+
],
|
|
2700
|
+
},
|
|
2701
|
+
{
|
|
2702
|
+
label: 'accepts a named script of another skill that exists',
|
|
2703
|
+
membership: 'script paths qualified with another skill directory in canonical skill documents',
|
|
2704
|
+
rule: 'skill',
|
|
2705
|
+
files: [
|
|
2706
|
+
{
|
|
2707
|
+
path: '.agents/skills/sample/SKILL.md',
|
|
2708
|
+
content:
|
|
2709
|
+
SKILL_POLICY_TEXT + '\nRun `node .agents/skills/other/scripts/present.ts` first.\n',
|
|
2710
|
+
},
|
|
2711
|
+
{ path: '.agents/skills/sample/agents/openai.yaml', content: createSkillMetadata('sample') },
|
|
2712
|
+
{
|
|
2713
|
+
path: '.agents/skills/other/SKILL.md',
|
|
2714
|
+
content:
|
|
2715
|
+
SKILL_POLICY_TEXT.replace('name: sample', 'name: other') +
|
|
2716
|
+
'\nRun `scripts/present.ts`.\n',
|
|
2717
|
+
},
|
|
2718
|
+
{ path: '.agents/skills/other/agents/openai.yaml', content: createSkillMetadata('other') },
|
|
2719
|
+
{
|
|
2720
|
+
path: '.agents/skills/other/scripts/present.ts',
|
|
2721
|
+
content: '// Usage: node .agents/skills/other/scripts/present.ts\n',
|
|
2722
|
+
},
|
|
2723
|
+
{ path: 'tests/agents/skills/other/scripts/present.test.ts', content: '' },
|
|
2724
|
+
],
|
|
2725
|
+
violations: [],
|
|
2726
|
+
},
|
|
2727
|
+
{
|
|
2728
|
+
label: 'rejects a skill script with no mirrored proof',
|
|
2729
|
+
membership: 'skill scripts against tests/agents/skills/<skill>/scripts/*.test.ts',
|
|
2730
|
+
rule: 'skill',
|
|
2731
|
+
files: [
|
|
2732
|
+
{
|
|
2733
|
+
path: '.agents/skills/sample/SKILL.md',
|
|
2734
|
+
content: SKILL_POLICY_TEXT + '\nRun `scripts/lone.ts`.\n',
|
|
2735
|
+
},
|
|
2736
|
+
{ path: '.agents/skills/sample/agents/openai.yaml', content: createSkillMetadata('sample') },
|
|
2737
|
+
{
|
|
2738
|
+
path: '.agents/skills/sample/scripts/lone.ts',
|
|
2739
|
+
content: '// Usage: node .agents/skills/sample/scripts/lone.ts\n',
|
|
2740
|
+
},
|
|
2741
|
+
],
|
|
2742
|
+
violations: [
|
|
2743
|
+
{
|
|
2744
|
+
rule: 'skill',
|
|
2745
|
+
path: 'tests/agents/skills/sample/scripts/lone.test.ts',
|
|
2746
|
+
message:
|
|
2747
|
+
'skill script has a mirrored proof: tests/agents/skills/sample/scripts/lone.test.ts',
|
|
2748
|
+
},
|
|
2749
|
+
],
|
|
2750
|
+
},
|
|
2577
2751
|
{
|
|
2578
2752
|
label: 'rejects an unexported value binding in a skill fence',
|
|
2579
2753
|
membership: 'named value imports in canonical skill fences',
|
|
@@ -2911,7 +3085,8 @@ export const SKILL_POLICY_CONTROLS: readonly PolicyControl[] = Object.freeze([
|
|
|
2911
3085
|
label: 'rejects a non-contract file in a skill directory',
|
|
2912
3086
|
membership: 'regular files at any depth inside a discovered skill directory',
|
|
2913
3087
|
rule: 'skill',
|
|
2914
|
-
message:
|
|
3088
|
+
message:
|
|
3089
|
+
'skill directory contains only SKILL.md, agents/openai.yaml, references/*.md, and scripts/*.ts',
|
|
2915
3090
|
files: [
|
|
2916
3091
|
{ path: '.agents/skills/sample/SKILL.md', content: SKILL_POLICY_TEXT },
|
|
2917
3092
|
{ path: '.agents/skills/sample/agents/openai.yaml', content: createSkillMetadata('sample') },
|
|
@@ -2922,7 +3097,7 @@ export const SKILL_POLICY_CONTROLS: readonly PolicyControl[] = Object.freeze([
|
|
|
2922
3097
|
label: 'rejects an empty non-contract directory in a skill directory',
|
|
2923
3098
|
membership: 'directories at any depth inside a discovered skill directory',
|
|
2924
3099
|
rule: 'skill',
|
|
2925
|
-
message: 'skill directory contains only agents
|
|
3100
|
+
message: 'skill directory contains only agents/, references/, and scripts/ directories',
|
|
2926
3101
|
files: [
|
|
2927
3102
|
{ path: '.agents/skills/sample/SKILL.md', content: SKILL_POLICY_TEXT },
|
|
2928
3103
|
{ path: '.agents/skills/sample/agents/openai.yaml', content: createSkillMetadata('sample') },
|