opencode-agent-skill 7.7.0
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/CHANGELOG.md +163 -0
- package/LICENSE +9 -0
- package/README.md +581 -0
- package/bin/ocskill.mjs +975 -0
- package/docs/DETERMINISTIC-TOOLS.md +88 -0
- package/docs/ENGINEERING-DESIGN.md +176 -0
- package/docs/EVALS.md +136 -0
- package/docs/NPM-PUBLISH.md +102 -0
- package/docs/OPENCODE-COMPAT.md +109 -0
- package/docs/RESEARCH-SOURCES.md +37 -0
- package/docs/TRACE-SCHEMA.md +109 -0
- package/docs/V7-INTELLIGENCE-RUNTIME.md +166 -0
- package/evals/live/fixtures/engineering-bench/package.json +1 -0
- package/evals/live/fixtures/engineering-bench/src/api-errors.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/authz.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/cache-tags.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/config.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/contract-consumer.mjs +6 -0
- package/evals/live/fixtures/engineering-bench/src/contract-producer.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/dedupe.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/dependency.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/discount.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/inventory.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/migration.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/money.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/pagination.mjs +5 -0
- package/evals/live/fixtures/engineering-bench/src/path-safe.mjs +5 -0
- package/evals/live/fixtures/engineering-bench/src/payment.mjs +5 -0
- package/evals/live/fixtures/engineering-bench/src/query-sort.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/react-state.mjs +8 -0
- package/evals/live/fixtures/engineering-bench/src/retry.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/rn-platform.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/upload.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/webhook.mjs +3 -0
- package/evals/live/graders/engineering-bench.mjs +239 -0
- package/evals/live/tasks.json +126 -0
- package/evals/long/fixtures/long-horizon/package.json +1 -0
- package/evals/long/fixtures/long-horizon/src/auth.mjs +5 -0
- package/evals/long/fixtures/long-horizon/src/checkout.mjs +9 -0
- package/evals/long/fixtures/long-horizon/src/inventory.mjs +5 -0
- package/evals/long/fixtures/long-horizon/src/money.mjs +3 -0
- package/evals/long/fixtures/long-horizon/src/payment.mjs +7 -0
- package/evals/long/fixtures/long-horizon/src/product-api.mjs +10 -0
- package/evals/long/fixtures/long-horizon/src/product-cache.mjs +3 -0
- package/evals/long/fixtures/long-horizon/src/product-service.mjs +6 -0
- package/evals/long/fixtures/long-horizon/src/product-state.mjs +8 -0
- package/evals/long/fixtures/long-horizon/src/project-api.mjs +9 -0
- package/evals/long/fixtures/long-horizon/src/project-service.mjs +7 -0
- package/evals/long/fixtures/long-horizon/src/user-consumer.mjs +3 -0
- package/evals/long/fixtures/long-horizon/src/user-migration.mjs +7 -0
- package/evals/long/fixtures/long-horizon/src/user-serializer.mjs +3 -0
- package/evals/long/fixtures/long-horizon/src/user-validation.mjs +3 -0
- package/evals/long/graders/long-horizon.mjs +209 -0
- package/evals/long/tasks.json +36 -0
- package/evals/router-triggers.json +1238 -0
- package/evals/routing.json +321 -0
- package/global-config/AGENTS.md +212 -0
- package/global-config/agents/architect.md +38 -0
- package/global-config/agents/codebase-mapper.md +41 -0
- package/global-config/agents/critic.md +37 -0
- package/global-config/agents/debugger.md +36 -0
- package/global-config/agents/executor.md +48 -0
- package/global-config/agents/integration-verifier.md +43 -0
- package/global-config/agents/plan-checker.md +43 -0
- package/global-config/agents/researcher.md +30 -0
- package/global-config/agents/reviewer.md +30 -0
- package/global-config/agents/verifier.md +33 -0
- package/global-config/commands/audit.md +8 -0
- package/global-config/commands/critique.md +8 -0
- package/global-config/commands/debug.md +8 -0
- package/global-config/commands/feature.md +8 -0
- package/global-config/commands/fix.md +8 -0
- package/global-config/commands/plan.md +8 -0
- package/global-config/commands/research.md +8 -0
- package/global-config/commands/resume.md +22 -0
- package/global-config/commands/review.md +8 -0
- package/global-config/commands/run.md +29 -0
- package/global-config/commands/verify.md +8 -0
- package/global-config/plugins/ues-router/capabilities.js +20 -0
- package/global-config/plugins/ues-router/index.js +277 -0
- package/global-config/plugins/ues-router/router.js +74 -0
- package/global-config/plugins/ues-router/safety.js +16 -0
- package/global-config/skills/accessibility/SKILL.md +10 -0
- package/global-config/skills/accessibility/references/workflow.md +17 -0
- package/global-config/skills/api-contract/SKILL.md +12 -0
- package/global-config/skills/api-contract/references/workflow.md +17 -0
- package/global-config/skills/auth-security/SKILL.md +12 -0
- package/global-config/skills/auth-security/references/workflow.md +15 -0
- package/global-config/skills/bug-diagnosis/SKILL.md +28 -0
- package/global-config/skills/change-impact-analysis/SKILL.md +23 -0
- package/global-config/skills/code-review/SKILL.md +19 -0
- package/global-config/skills/context-engineering/SKILL.md +18 -0
- package/global-config/skills/context-engineering/references/large-repo.md +16 -0
- package/global-config/skills/database-engineering/SKILL.md +12 -0
- package/global-config/skills/database-engineering/references/workflow.md +15 -0
- package/global-config/skills/dependency-management/SKILL.md +12 -0
- package/global-config/skills/dependency-management/references/workflow.md +14 -0
- package/global-config/skills/devops-engineering/SKILL.md +10 -0
- package/global-config/skills/devops-engineering/references/workflow.md +11 -0
- package/global-config/skills/django-engineering/SKILL.md +10 -0
- package/global-config/skills/django-engineering/references/workflow.md +11 -0
- package/global-config/skills/documentation-engineering/SKILL.md +10 -0
- package/global-config/skills/documentation-engineering/references/workflow.md +15 -0
- package/global-config/skills/dotnet-engineering/SKILL.md +10 -0
- package/global-config/skills/dotnet-engineering/references/workflow.md +11 -0
- package/global-config/skills/ecommerce-engineering/SKILL.md +10 -0
- package/global-config/skills/ecommerce-engineering/references/workflow.md +17 -0
- package/global-config/skills/engineering-orchestrator/SKILL.md +29 -0
- package/global-config/skills/engineering-orchestrator/references/delegation.md +22 -0
- package/global-config/skills/engineering-orchestrator/references/evaluator-loop.md +18 -0
- package/global-config/skills/engineering-orchestrator/references/long-horizon.md +57 -0
- package/global-config/skills/engineering-orchestrator/references/model-escalation.md +19 -0
- package/global-config/skills/engineering-orchestrator/references/retry-policy.md +12 -0
- package/global-config/skills/engineering-orchestrator/references/routing.md +39 -0
- package/global-config/skills/engineering-orchestrator/references/verification-matrix.md +18 -0
- package/global-config/skills/fastapi-engineering/SKILL.md +10 -0
- package/global-config/skills/fastapi-engineering/references/workflow.md +13 -0
- package/global-config/skills/file-upload-engineering/SKILL.md +10 -0
- package/global-config/skills/file-upload-engineering/references/workflow.md +17 -0
- package/global-config/skills/flutter-engineering/SKILL.md +10 -0
- package/global-config/skills/flutter-engineering/references/workflow.md +13 -0
- package/global-config/skills/git-safety/SKILL.md +10 -0
- package/global-config/skills/git-safety/references/workflow.md +15 -0
- package/global-config/skills/implementation-engineer/SKILL.md +10 -0
- package/global-config/skills/implementation-engineer/references/workflow.md +14 -0
- package/global-config/skills/java-spring-engineering/SKILL.md +10 -0
- package/global-config/skills/java-spring-engineering/references/workflow.md +13 -0
- package/global-config/skills/long-task-state/SKILL.md +28 -0
- package/global-config/skills/long-task-state/references/context-ledger.md +27 -0
- package/global-config/skills/long-task-state/templates/STATE.md +48 -0
- package/global-config/skills/nestjs-engineering/SKILL.md +10 -0
- package/global-config/skills/nestjs-engineering/references/workflow.md +11 -0
- package/global-config/skills/nextjs-engineering/SKILL.md +12 -0
- package/global-config/skills/nextjs-engineering/references/workflow.md +15 -0
- package/global-config/skills/nodejs-engineering/SKILL.md +12 -0
- package/global-config/skills/nodejs-engineering/references/workflow.md +11 -0
- package/global-config/skills/payment-engineering/SKILL.md +12 -0
- package/global-config/skills/payment-engineering/references/workflow.md +19 -0
- package/global-config/skills/performance-engineering/SKILL.md +10 -0
- package/global-config/skills/performance-engineering/references/workflow.md +17 -0
- package/global-config/skills/python-engineering/SKILL.md +10 -0
- package/global-config/skills/python-engineering/references/workflow.md +11 -0
- package/global-config/skills/react-engineering/SKILL.md +14 -0
- package/global-config/skills/react-engineering/references/workflow.md +16 -0
- package/global-config/skills/react-native-engineering/SKILL.md +14 -0
- package/global-config/skills/react-native-engineering/references/workflow.md +16 -0
- package/global-config/skills/repo-explorer/SKILL.md +17 -0
- package/global-config/skills/research-verification/SKILL.md +18 -0
- package/global-config/skills/research-verification/references/source-hierarchy.md +12 -0
- package/global-config/skills/rest-api-design/SKILL.md +10 -0
- package/global-config/skills/rest-api-design/references/workflow.md +18 -0
- package/global-config/skills/software-architect/SKILL.md +10 -0
- package/global-config/skills/software-architect/references/workflow.md +18 -0
- package/global-config/skills/task-planner/SKILL.md +21 -0
- package/global-config/skills/task-planner/references/plan-schema.md +56 -0
- package/global-config/skills/test-driven-development/SKILL.md +22 -0
- package/global-config/skills/test-driven-development/references/writing-good-tests.md +21 -0
- package/global-config/skills/test-verification/SKILL.md +20 -0
- package/global-config/skills/ui-ux-engineering/SKILL.md +10 -0
- package/global-config/skills/ui-ux-engineering/references/workflow.md +17 -0
- package/global-config/skills/web-security-review/SKILL.md +10 -0
- package/global-config/skills/web-security-review/references/workflow.md +22 -0
- package/lib/context-manifest.mjs +101 -0
- package/lib/control-center.mjs +148 -0
- package/lib/eval-auth.mjs +21 -0
- package/lib/eval-report.mjs +72 -0
- package/lib/eval-telemetry.mjs +155 -0
- package/lib/evidence-receipt.mjs +50 -0
- package/lib/hermes-bridge.mjs +28 -0
- package/lib/ids.mjs +21 -0
- package/lib/installer.mjs +636 -0
- package/lib/learning-engine.mjs +161 -0
- package/lib/model-config.mjs +88 -0
- package/lib/model-policy.mjs +71 -0
- package/lib/opencode-compat.mjs +86 -0
- package/lib/orchestrator-policy.mjs +35 -0
- package/lib/process-runner.mjs +117 -0
- package/lib/repo-graph.mjs +135 -0
- package/lib/repo-inspect.mjs +264 -0
- package/lib/review-scope.mjs +98 -0
- package/lib/router-config.mjs +40 -0
- package/lib/task-engine.mjs +777 -0
- package/lib/task-graph.mjs +262 -0
- package/lib/update-resolver.mjs +43 -0
- package/lib/verification-plan.mjs +52 -0
- package/lib/version.mjs +47 -0
- package/lib/workspace-snapshot.mjs +45 -0
- package/lib/worktree-sandbox.mjs +59 -0
- package/package.json +69 -0
- package/scripts/check-working-tree.mjs +2 -0
- package/scripts/collect-evidence.mjs +2 -0
- package/scripts/control-center.mjs +37 -0
- package/scripts/detect-stack.mjs +2 -0
- package/scripts/detect-test-commands.mjs +2 -0
- package/scripts/eval-live.mjs +437 -0
- package/scripts/eval-report.mjs +49 -0
- package/scripts/eval-router.mjs +45 -0
- package/scripts/eval-skills.mjs +71 -0
- package/scripts/impact-map.mjs +4 -0
- package/scripts/install.mjs +61 -0
- package/scripts/repo-map.mjs +2 -0
- package/scripts/smoke-packed-install.mjs +326 -0
- package/scripts/syntax-check.mjs +35 -0
- package/scripts/uninstall.mjs +21 -0
- package/scripts/validate-live-suite.mjs +65 -0
- package/scripts/validate.mjs +133 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import assert from "node:assert/strict"
|
|
2
|
+
import { existsSync } from "node:fs"
|
|
3
|
+
import { mkdir, mkdtemp, readFile, readdir, realpath, rm, writeFile } from "node:fs/promises"
|
|
4
|
+
import os from "node:os"
|
|
5
|
+
import path from "node:path"
|
|
6
|
+
import { spawnSync } from "node:child_process"
|
|
7
|
+
import { fileURLToPath } from "node:url"
|
|
8
|
+
|
|
9
|
+
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
|
|
10
|
+
const sourcePackageJson = JSON.parse(await readFile(path.join(repoRoot, "package.json"), "utf8"))
|
|
11
|
+
const packageName = sourcePackageJson.name
|
|
12
|
+
|
|
13
|
+
function runNpm(args, options = {}) {
|
|
14
|
+
const common = {
|
|
15
|
+
cwd: options.cwd ?? repoRoot,
|
|
16
|
+
env: options.env ?? process.env,
|
|
17
|
+
encoding: "utf8",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const npmExecPath = process.env.npm_execpath
|
|
21
|
+
if (npmExecPath && existsSync(npmExecPath)) {
|
|
22
|
+
return spawnSync(process.execPath, [npmExecPath, ...args], common)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return spawnSync("npm", args, {
|
|
26
|
+
...common,
|
|
27
|
+
shell: process.platform === "win32",
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function requireSuccess(result, label) {
|
|
32
|
+
if (result.status === 0) return
|
|
33
|
+
const details = [result.stdout, result.stderr].filter(Boolean).join("\n")
|
|
34
|
+
throw new Error(`${label} failed with exit code ${result.status ?? "unknown"}\n${details}`)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const temp = await mkdtemp(path.join(os.tmpdir(), "ocskill-pack-smoke-"))
|
|
38
|
+
const packDir = path.join(temp, "pack")
|
|
39
|
+
const prefix = path.join(temp, "prefix")
|
|
40
|
+
const configDir = path.join(temp, "opencode")
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
await mkdir(packDir, { recursive: true })
|
|
44
|
+
|
|
45
|
+
const pack = runNpm(["pack", "--pack-destination", packDir])
|
|
46
|
+
requireSuccess(pack, "npm pack")
|
|
47
|
+
|
|
48
|
+
const tarballs = (await readdir(packDir)).filter((name) => name.endsWith(".tgz"))
|
|
49
|
+
assert.equal(tarballs.length, 1, "expected exactly one packed tarball")
|
|
50
|
+
const tarball = path.join(packDir, tarballs[0])
|
|
51
|
+
|
|
52
|
+
const npmVersion = runNpm(["--version"], { cwd: temp })
|
|
53
|
+
requireSuccess(npmVersion, "npm --version")
|
|
54
|
+
const npmMajor = Number.parseInt(npmVersion.stdout.trim().split(".")[0], 10)
|
|
55
|
+
const installArgs = ["install", "-g", tarball, "--prefix", prefix]
|
|
56
|
+
if (Number.isFinite(npmMajor) && npmMajor >= 11) {
|
|
57
|
+
installArgs.push(`--allow-scripts=${packageName}`)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const install = runNpm(installArgs, {
|
|
61
|
+
cwd: temp,
|
|
62
|
+
env: { ...process.env, OPENCODE_CONFIG_DIR: configDir, UES_OPENCODE_MAJOR: "2" },
|
|
63
|
+
})
|
|
64
|
+
requireSuccess(install, "packed global install with automatic OpenCode sync")
|
|
65
|
+
|
|
66
|
+
const root = runNpm(["root", "-g", "--prefix", prefix], { cwd: temp })
|
|
67
|
+
requireSuccess(root, "npm root -g")
|
|
68
|
+
const globalRoot = root.stdout.trim()
|
|
69
|
+
assert.ok(globalRoot, "npm root -g returned an empty path")
|
|
70
|
+
|
|
71
|
+
const packageDir = path.join(globalRoot, ...packageName.split("/"))
|
|
72
|
+
const packedReal = await realpath(packageDir)
|
|
73
|
+
const repoReal = await realpath(repoRoot)
|
|
74
|
+
assert.notEqual(
|
|
75
|
+
packedReal.toLowerCase(),
|
|
76
|
+
repoReal.toLowerCase(),
|
|
77
|
+
"packed global install unexpectedly points back to the source repository",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
const packageJson = JSON.parse(await readFile(path.join(packageDir, "package.json"), "utf8"))
|
|
81
|
+
assert.equal(packageJson.name, packageName)
|
|
82
|
+
|
|
83
|
+
const cli = path.join(packageDir, "bin", "ocskill.mjs")
|
|
84
|
+
const env = { ...process.env, OPENCODE_CONFIG_DIR: configDir }
|
|
85
|
+
|
|
86
|
+
const status = spawnSync(process.execPath, [cli, "status"], {
|
|
87
|
+
cwd: temp,
|
|
88
|
+
env,
|
|
89
|
+
encoding: "utf8",
|
|
90
|
+
})
|
|
91
|
+
requireSuccess(status, "ocskill status from packed copy")
|
|
92
|
+
|
|
93
|
+
const state = JSON.parse(
|
|
94
|
+
await readFile(path.join(configDir, ".ues", "state.json"), "utf8"),
|
|
95
|
+
)
|
|
96
|
+
assert.equal(state.package, packageName)
|
|
97
|
+
assert.equal(state.version, packageJson.version)
|
|
98
|
+
assert.ok(state.skills.length >= 39)
|
|
99
|
+
assert.ok(state.commands.length >= 11)
|
|
100
|
+
assert.ok(state.agents.length >= 10)
|
|
101
|
+
assert.equal(state.openCodeMajor, 2)
|
|
102
|
+
assert.deepEqual(state.plugins, ["ues-router/index.js"])
|
|
103
|
+
|
|
104
|
+
const routerPlugin = path.join(configDir, "plugins", "ues-router", "index.js")
|
|
105
|
+
assert.ok(existsSync(routerPlugin), "v2 router plugin was not installed from packed package")
|
|
106
|
+
assert.ok(existsSync(path.join(configDir, "plugins", "ues-router", "router.js")), "v2 router helper was not installed from packed package")
|
|
107
|
+
assert.ok(existsSync(path.join(configDir, "plugins", "ues-router", "safety.js")), "v2 router safety gate was not installed from packed package")
|
|
108
|
+
assert.ok(existsSync(path.join(configDir, "plugins", "ues-router", "capabilities.js")), "v7 router capability probe was not installed from packed package")
|
|
109
|
+
const routerSource = await readFile(routerPlugin, "utf8")
|
|
110
|
+
assert.match(routerSource, /name: "dispatch_task"/)
|
|
111
|
+
assert.match(routerSource, /ctx\.session\.create/)
|
|
112
|
+
assert.match(routerSource, /ctx\.session\.switchAgent/)
|
|
113
|
+
assert.match(routerSource, /ctx\.session\.switchModel/)
|
|
114
|
+
assert.match(routerSource, /name: "capabilities"/)
|
|
115
|
+
assert.match(routerSource, /name: "task_policy"/)
|
|
116
|
+
const reviewer = await readFile(path.join(configDir, "agents", "ues-reviewer.md"), "utf8")
|
|
117
|
+
assert.match(reviewer, /permissions:/)
|
|
118
|
+
assert.doesNotMatch(reviewer, /^permission:/m)
|
|
119
|
+
|
|
120
|
+
for (const name of ["ues-codebase-mapper.md", "ues-plan-checker.md", "ues-executor.md", "ues-integration-verifier.md"]) {
|
|
121
|
+
assert.ok(existsSync(path.join(configDir, "agents", name)), "missing packed V6 subagent " + name)
|
|
122
|
+
}
|
|
123
|
+
const executor = await readFile(path.join(configDir, "agents", "ues-executor.md"), "utf8")
|
|
124
|
+
assert.match(executor, /permissions:/)
|
|
125
|
+
assert.match(executor, /action: subagent/)
|
|
126
|
+
assert.match(executor, /effect: deny/)
|
|
127
|
+
assert.doesNotMatch(executor, /^permission:/m)
|
|
128
|
+
|
|
129
|
+
const inspect = spawnSync(process.execPath, [cli, "inspect", temp], {
|
|
130
|
+
cwd: temp,
|
|
131
|
+
env,
|
|
132
|
+
encoding: "utf8",
|
|
133
|
+
})
|
|
134
|
+
requireSuccess(inspect, "ocskill inspect from packed copy")
|
|
135
|
+
|
|
136
|
+
const planFile = path.join(temp, "PLAN.json")
|
|
137
|
+
await writeFile(planFile, JSON.stringify({
|
|
138
|
+
schemaVersion: 1,
|
|
139
|
+
goal: "Packed V6 smoke",
|
|
140
|
+
tasks: [{
|
|
141
|
+
id: "T1",
|
|
142
|
+
title: "Smoke",
|
|
143
|
+
summary: "Validate packed task graph",
|
|
144
|
+
files: { modify: ["README.md"] },
|
|
145
|
+
dependsOn: [],
|
|
146
|
+
acceptance: ["Task graph is valid"],
|
|
147
|
+
verification: ["node --version"],
|
|
148
|
+
risk: "low",
|
|
149
|
+
}],
|
|
150
|
+
}, null, 2) + "\n", "utf8")
|
|
151
|
+
|
|
152
|
+
const graph = spawnSync(process.execPath, [cli, "task-graph", planFile], {
|
|
153
|
+
cwd: temp,
|
|
154
|
+
env,
|
|
155
|
+
encoding: "utf8",
|
|
156
|
+
})
|
|
157
|
+
requireSuccess(graph, "ocskill task-graph from packed copy")
|
|
158
|
+
assert.match(graph.stdout, /"valid": true/)
|
|
159
|
+
|
|
160
|
+
const workInit = spawnSync(process.execPath, [cli, "work", "init", "packed-smoke", temp, "--goal", "Packed V6 work smoke"], {
|
|
161
|
+
cwd: temp,
|
|
162
|
+
env,
|
|
163
|
+
encoding: "utf8",
|
|
164
|
+
})
|
|
165
|
+
requireSuccess(workInit, "ocskill work init from packed copy")
|
|
166
|
+
|
|
167
|
+
const workPlan = spawnSync(process.execPath, [cli, "work", "plan", "packed-smoke", planFile, temp], {
|
|
168
|
+
cwd: temp,
|
|
169
|
+
env,
|
|
170
|
+
encoding: "utf8",
|
|
171
|
+
})
|
|
172
|
+
requireSuccess(workPlan, "ocskill work plan from packed copy")
|
|
173
|
+
|
|
174
|
+
const workStatusBeforeApproval = spawnSync(process.execPath, [cli, "work", "status", "packed-smoke", temp], {
|
|
175
|
+
cwd: temp,
|
|
176
|
+
env,
|
|
177
|
+
encoding: "utf8",
|
|
178
|
+
})
|
|
179
|
+
requireSuccess(workStatusBeforeApproval, "ocskill work status before approval from packed copy")
|
|
180
|
+
assert.match(workStatusBeforeApproval.stdout, /"status": "awaiting-plan-approval"/)
|
|
181
|
+
assert.match(workStatusBeforeApproval.stdout, /"ready": \[\]/)
|
|
182
|
+
|
|
183
|
+
const workStartBeforeApproval = spawnSync(process.execPath, [cli, "work", "start", "packed-smoke", "T1", temp], {
|
|
184
|
+
cwd: temp,
|
|
185
|
+
env,
|
|
186
|
+
encoding: "utf8",
|
|
187
|
+
})
|
|
188
|
+
assert.notEqual(workStartBeforeApproval.status, 0)
|
|
189
|
+
assert.match(workStartBeforeApproval.stderr, /plan is not approved/)
|
|
190
|
+
|
|
191
|
+
const workApprove = spawnSync(process.execPath, [cli, "work", "approve-plan", "packed-smoke", temp, "--evidence", "packed plan checker PASS"], {
|
|
192
|
+
cwd: temp,
|
|
193
|
+
env,
|
|
194
|
+
encoding: "utf8",
|
|
195
|
+
})
|
|
196
|
+
requireSuccess(workApprove, "ocskill work approve-plan from packed copy")
|
|
197
|
+
|
|
198
|
+
const workStatus = spawnSync(process.execPath, [cli, "work", "status", "packed-smoke", temp], {
|
|
199
|
+
cwd: temp,
|
|
200
|
+
env,
|
|
201
|
+
encoding: "utf8",
|
|
202
|
+
})
|
|
203
|
+
requireSuccess(workStatus, "ocskill work status after approval from packed copy")
|
|
204
|
+
assert.match(workStatus.stdout, /"T1"/)
|
|
205
|
+
|
|
206
|
+
const workStart = spawnSync(process.execPath, [cli, "work", "start", "packed-smoke", "T1", temp], {
|
|
207
|
+
cwd: temp,
|
|
208
|
+
env,
|
|
209
|
+
encoding: "utf8",
|
|
210
|
+
})
|
|
211
|
+
requireSuccess(workStart, "ocskill work start from packed copy")
|
|
212
|
+
const started = JSON.parse(workStart.stdout)
|
|
213
|
+
assert.ok(started.record.runId)
|
|
214
|
+
|
|
215
|
+
const heartbeat = spawnSync(process.execPath, [cli, "work", "heartbeat", "packed-smoke", "T1", temp, "--run-id", started.record.runId], {
|
|
216
|
+
cwd: temp,
|
|
217
|
+
env,
|
|
218
|
+
encoding: "utf8",
|
|
219
|
+
})
|
|
220
|
+
requireSuccess(heartbeat, "ocskill work heartbeat from packed copy")
|
|
221
|
+
|
|
222
|
+
const verifyCommand = spawnSync(process.execPath, [
|
|
223
|
+
cli, "work", "verify-command", "packed-smoke", "T1", temp,
|
|
224
|
+
"--run-id", started.record.runId,
|
|
225
|
+
"--", process.execPath, "--version",
|
|
226
|
+
], {
|
|
227
|
+
cwd: temp,
|
|
228
|
+
env,
|
|
229
|
+
encoding: "utf8",
|
|
230
|
+
})
|
|
231
|
+
requireSuccess(verifyCommand, "ocskill work verify-command from packed copy")
|
|
232
|
+
assert.match(verifyCommand.stdout, /"passed": true/)
|
|
233
|
+
|
|
234
|
+
const workComplete = spawnSync(process.execPath, [
|
|
235
|
+
cli, "work", "complete", "packed-smoke", "T1", temp,
|
|
236
|
+
"--evidence", "node --version => PASS",
|
|
237
|
+
"--run-id", started.record.runId,
|
|
238
|
+
], {
|
|
239
|
+
cwd: temp,
|
|
240
|
+
env,
|
|
241
|
+
encoding: "utf8",
|
|
242
|
+
})
|
|
243
|
+
requireSuccess(workComplete, "ocskill work complete from packed copy")
|
|
244
|
+
assert.match(workComplete.stdout, /"evidenceStrength": "receipt-backed"/)
|
|
245
|
+
|
|
246
|
+
const finalizeBeforeVerify = spawnSync(process.execPath, [cli, "work", "finalize", "packed-smoke", temp, "--evidence", "should fail"], {
|
|
247
|
+
cwd: temp,
|
|
248
|
+
env,
|
|
249
|
+
encoding: "utf8",
|
|
250
|
+
})
|
|
251
|
+
assert.notEqual(finalizeBeforeVerify.status, 0)
|
|
252
|
+
assert.match(finalizeBeforeVerify.stderr, /recorded integration PASS/)
|
|
253
|
+
|
|
254
|
+
const workVerify = spawnSync(process.execPath, [cli, "work", "verify-integration", "packed-smoke", temp, "--verdict", "PASS", "--evidence", "packed integration PASS"], {
|
|
255
|
+
cwd: temp,
|
|
256
|
+
env,
|
|
257
|
+
encoding: "utf8",
|
|
258
|
+
})
|
|
259
|
+
requireSuccess(workVerify, "ocskill work verify-integration from packed copy")
|
|
260
|
+
|
|
261
|
+
const workFinalize = spawnSync(process.execPath, [cli, "work", "finalize", "packed-smoke", temp, "--evidence", "packed final acceptance PASS"], {
|
|
262
|
+
cwd: temp,
|
|
263
|
+
env,
|
|
264
|
+
encoding: "utf8",
|
|
265
|
+
})
|
|
266
|
+
requireSuccess(workFinalize, "ocskill work finalize from packed copy")
|
|
267
|
+
assert.match(workFinalize.stdout, /"status": "completed"/)
|
|
268
|
+
|
|
269
|
+
const adaptivePolicy = spawnSync(process.execPath, [
|
|
270
|
+
cli, "task-policy",
|
|
271
|
+
"Refactor the entire repository authentication schema migration and public API contracts",
|
|
272
|
+
], {
|
|
273
|
+
cwd: temp,
|
|
274
|
+
env,
|
|
275
|
+
encoding: "utf8",
|
|
276
|
+
})
|
|
277
|
+
requireSuccess(adaptivePolicy, "ocskill task-policy from packed copy")
|
|
278
|
+
assert.match(adaptivePolicy.stdout, /"mode": "long-horizon"/)
|
|
279
|
+
assert.match(adaptivePolicy.stdout, /"modelTier": "heavy"/)
|
|
280
|
+
|
|
281
|
+
const dashboard = spawnSync(process.execPath, [cli, "dashboard", temp], {
|
|
282
|
+
cwd: temp,
|
|
283
|
+
env,
|
|
284
|
+
encoding: "utf8",
|
|
285
|
+
})
|
|
286
|
+
requireSuccess(dashboard, "ocskill dashboard from packed copy")
|
|
287
|
+
assert.ok(existsSync(path.join(temp, ".ues-dashboard", "index.html")))
|
|
288
|
+
|
|
289
|
+
const hermes = spawnSync(process.execPath, [cli, "hermes", "status"], {
|
|
290
|
+
cwd: temp,
|
|
291
|
+
env,
|
|
292
|
+
encoding: "utf8",
|
|
293
|
+
})
|
|
294
|
+
requireSuccess(hermes, "ocskill hermes status from packed copy")
|
|
295
|
+
assert.match(hermes.stdout, /"available":/)
|
|
296
|
+
|
|
297
|
+
const modelStatus = spawnSync(process.execPath, [cli, "models", "status"], {
|
|
298
|
+
cwd: temp,
|
|
299
|
+
env,
|
|
300
|
+
encoding: "utf8",
|
|
301
|
+
})
|
|
302
|
+
requireSuccess(modelStatus, "ocskill models status from packed copy")
|
|
303
|
+
assert.match(modelStatus.stdout, /"enabled": false/)
|
|
304
|
+
|
|
305
|
+
const modelSet = spawnSync(process.execPath, [cli, "models", "set", "standard", "provider/mid"], {
|
|
306
|
+
cwd: temp,
|
|
307
|
+
env,
|
|
308
|
+
encoding: "utf8",
|
|
309
|
+
})
|
|
310
|
+
requireSuccess(modelSet, "ocskill models set from packed copy")
|
|
311
|
+
|
|
312
|
+
const modelPolicy = spawnSync(process.execPath, [cli, "model-policy", "executor", "--attempt", "1"], {
|
|
313
|
+
cwd: temp,
|
|
314
|
+
env,
|
|
315
|
+
encoding: "utf8",
|
|
316
|
+
})
|
|
317
|
+
requireSuccess(modelPolicy, "ocskill model-policy from packed copy")
|
|
318
|
+
assert.match(modelPolicy.stdout, /"model": "provider\/mid"/)
|
|
319
|
+
|
|
320
|
+
console.log(
|
|
321
|
+
`One-command packed install smoke passed for ${packageName}@${packageJson.version}: ` +
|
|
322
|
+
`${state.skills.length} skills, ${state.commands.length} commands, ${state.agents.length} subagents, ${state.plugins.length} v2 router plugin.`,
|
|
323
|
+
)
|
|
324
|
+
} finally {
|
|
325
|
+
await rm(temp, { recursive: true, force: true })
|
|
326
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises"
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
import { spawnSync } from "node:child_process"
|
|
4
|
+
import { fileURLToPath } from "node:url"
|
|
5
|
+
|
|
6
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
|
|
7
|
+
const roots = ["bin", "lib", "scripts", "test", "global-config/plugins", "evals/live/graders", "evals/live/fixtures", "evals/long/graders", "evals/long/fixtures"]
|
|
8
|
+
const files = []
|
|
9
|
+
|
|
10
|
+
async function walk(dir) {
|
|
11
|
+
const entries = await readdir(dir, { withFileTypes: true }).catch(() => [])
|
|
12
|
+
for (const entry of entries) {
|
|
13
|
+
const full = path.join(dir, entry.name)
|
|
14
|
+
if (entry.isDirectory()) await walk(full)
|
|
15
|
+
else if (entry.isFile() && /\.(?:mjs|js|cjs)$/.test(entry.name)) files.push(full)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
for (const relative of roots) await walk(path.join(root, relative))
|
|
20
|
+
|
|
21
|
+
const errors = []
|
|
22
|
+
for (const file of files.sort()) {
|
|
23
|
+
const run = spawnSync(process.execPath, ["--check", file], { encoding: "utf8" })
|
|
24
|
+
if (run.status !== 0) {
|
|
25
|
+
errors.push(`${path.relative(root, file)}\n${run.stderr || run.stdout || "syntax check failed"}`)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (errors.length) {
|
|
30
|
+
console.error("JavaScript syntax validation failed:")
|
|
31
|
+
for (const error of errors) console.error("\n" + error)
|
|
32
|
+
process.exit(1)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log(`Syntax-checked ${files.length} JavaScript module files.`)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { removeResources } from "../lib/installer.mjs"
|
|
2
|
+
|
|
3
|
+
try {
|
|
4
|
+
const result = await removeResources()
|
|
5
|
+
for (const warning of result.warnings || []) {
|
|
6
|
+
console.warn(`[ocskill] WARNING: ${warning}`)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
if (result.stateError) {
|
|
10
|
+
console.error(`[ocskill] ERROR: ${result.stateError}`)
|
|
11
|
+
process.exit(1)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
console.log(
|
|
15
|
+
`[ocskill] Removed ${result.skills} managed skills, ${result.commands} managed commands and ${result.agents} managed subagents.`,
|
|
16
|
+
)
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.error("[ocskill] Cleanup failed.")
|
|
19
|
+
console.error(error instanceof Error ? error.stack : error)
|
|
20
|
+
process.exit(1)
|
|
21
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { existsSync } from "node:fs"
|
|
2
|
+
import { readFile } from "node:fs/promises"
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
import { spawnSync } from "node:child_process"
|
|
5
|
+
import { fileURLToPath } from "node:url"
|
|
6
|
+
|
|
7
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
|
|
8
|
+
const args = process.argv.slice(2)
|
|
9
|
+
const suiteIndex = args.indexOf("--suite")
|
|
10
|
+
const suiteName = suiteIndex >= 0 ? args[suiteIndex + 1] : "live"
|
|
11
|
+
if (!["live", "long"].includes(suiteName)) {
|
|
12
|
+
console.error("--suite must be live or long")
|
|
13
|
+
process.exit(2)
|
|
14
|
+
}
|
|
15
|
+
const suiteRoot = path.join(root, "evals", suiteName)
|
|
16
|
+
const suite = JSON.parse(await readFile(path.join(suiteRoot, "tasks.json"), "utf8"))
|
|
17
|
+
const errors = []
|
|
18
|
+
const ids = new Set()
|
|
19
|
+
|
|
20
|
+
const minimumTasks = suiteName === "long" ? 5 : 20
|
|
21
|
+
if (!Array.isArray(suite.tasks) || suite.tasks.length < minimumTasks) {
|
|
22
|
+
errors.push(suiteName + " eval suite must contain at least " + minimumTasks + " tasks")
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
for (const task of suite.tasks || []) {
|
|
26
|
+
if (!task.id || ids.has(task.id)) {
|
|
27
|
+
errors.push(`invalid or duplicate task id: ${task.id || "<missing>"}`)
|
|
28
|
+
continue
|
|
29
|
+
}
|
|
30
|
+
ids.add(task.id)
|
|
31
|
+
|
|
32
|
+
if (!task.prompt || typeof task.prompt !== "string" || task.prompt.length < 40) {
|
|
33
|
+
errors.push(`${task.id}: prompt must be a concrete non-trivial string`)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const fixture = path.join(suiteRoot, task.fixture || "")
|
|
37
|
+
const grader = path.join(suiteRoot, task.grader || "")
|
|
38
|
+
if (!task.fixture || !existsSync(fixture)) errors.push(`${task.id}: missing fixture ${task.fixture || "<missing>"}`)
|
|
39
|
+
if (!task.grader || !existsSync(grader)) errors.push(`${task.id}: missing grader ${task.grader || "<missing>"}`)
|
|
40
|
+
if (!task.fixture || !task.grader || !existsSync(fixture) || !existsSync(grader)) continue
|
|
41
|
+
|
|
42
|
+
const run = spawnSync(process.execPath, [grader], {
|
|
43
|
+
cwd: fixture,
|
|
44
|
+
env: { ...process.env, UES_EVAL_WORKSPACE: fixture, UES_EVAL_TASK: task.id },
|
|
45
|
+
encoding: "utf8",
|
|
46
|
+
maxBuffer: 1024 * 1024,
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
if (run.status === 0) {
|
|
50
|
+
errors.push(`${task.id}: hidden grader unexpectedly passes the intentionally broken fixture`)
|
|
51
|
+
} else {
|
|
52
|
+
const diagnostic = `${run.stderr || ""}\n${run.stdout || ""}`
|
|
53
|
+
if (!/AssertionError|ERR_ASSERTION|assert/i.test(diagnostic)) {
|
|
54
|
+
errors.push(`${task.id}: grader failed for a non-assertion reason; check syntax/runtime setup`)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (errors.length) {
|
|
60
|
+
console.error(suiteName + " eval suite validation failed:")
|
|
61
|
+
for (const error of errors) console.error(`- ${error}`)
|
|
62
|
+
process.exit(1)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log("Validated " + suite.tasks.length + " " + suiteName + " tasks; every hidden grader rejects its broken fixture.")
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { existsSync } from "node:fs"
|
|
2
|
+
import { readFile, readdir } from "node:fs/promises"
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
import { fileURLToPath } from "node:url"
|
|
5
|
+
import { validResourceID } from "../lib/ids.mjs"
|
|
6
|
+
|
|
7
|
+
const root = process.env.UES_BUNDLE_ROOT
|
|
8
|
+
? path.resolve(process.env.UES_BUNDLE_ROOT)
|
|
9
|
+
: path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
|
|
10
|
+
const skillsRoot = path.join(root, "global-config", "skills")
|
|
11
|
+
const commandsRoot = path.join(root, "global-config", "commands")
|
|
12
|
+
const agentsRoot = path.join(root, "global-config", "agents")
|
|
13
|
+
const pluginsRoot = path.join(root, "global-config", "plugins")
|
|
14
|
+
const errors = []
|
|
15
|
+
const ids = new Set()
|
|
16
|
+
|
|
17
|
+
function localMarkdownLinks(source) {
|
|
18
|
+
return [...source.matchAll(/\[[^\]]+\]\(([^)]+)\)/g)]
|
|
19
|
+
.map((match) => match[1].trim())
|
|
20
|
+
.filter((value) =>
|
|
21
|
+
value &&
|
|
22
|
+
!value.startsWith("#") &&
|
|
23
|
+
!value.startsWith("http://") &&
|
|
24
|
+
!value.startsWith("https://") &&
|
|
25
|
+
!value.startsWith("mailto:"),
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
for (const entry of await readdir(skillsRoot, { withFileTypes: true })) {
|
|
30
|
+
if (!entry.isDirectory()) continue
|
|
31
|
+
|
|
32
|
+
const dir = path.join(skillsRoot, entry.name)
|
|
33
|
+
const file = path.join(dir, "SKILL.md")
|
|
34
|
+
if (!existsSync(file)) {
|
|
35
|
+
errors.push(`${entry.name}: missing SKILL.md`)
|
|
36
|
+
continue
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const source = await readFile(file, "utf8")
|
|
40
|
+
const name = source.match(/^name:\s*([^\r\n]+)/m)?.[1]?.trim()
|
|
41
|
+
const description = source.match(/^description:\s*([^\r\n]+)/m)?.[1]?.trim()
|
|
42
|
+
|
|
43
|
+
if (!name) errors.push(`${entry.name}: missing name`)
|
|
44
|
+
if (!description) errors.push(`${entry.name}: missing description`)
|
|
45
|
+
if (name && name !== entry.name) errors.push(`${entry.name}: frontmatter name must match directory`)
|
|
46
|
+
if (!validResourceID(entry.name)) errors.push(`${entry.name}: invalid skill id`)
|
|
47
|
+
if (ids.has(entry.name)) errors.push(`${entry.name}: duplicate skill id`)
|
|
48
|
+
ids.add(entry.name)
|
|
49
|
+
|
|
50
|
+
for (const link of localMarkdownLinks(source)) {
|
|
51
|
+
const relative = link.split("#", 1)[0]
|
|
52
|
+
if (!relative) continue
|
|
53
|
+
if (!existsSync(path.resolve(dir, relative))) {
|
|
54
|
+
errors.push(`${entry.name}: broken local reference ${link}`)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const agentIDs = new Set()
|
|
60
|
+
for (const entry of await readdir(agentsRoot, { withFileTypes: true })) {
|
|
61
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) continue
|
|
62
|
+
agentIDs.add(`ues-${entry.name.slice(0, -3)}`)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let commands = 0
|
|
66
|
+
for (const entry of await readdir(commandsRoot, { withFileTypes: true })) {
|
|
67
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) continue
|
|
68
|
+
commands += 1
|
|
69
|
+
const id = entry.name.slice(0, -3)
|
|
70
|
+
if (!validResourceID(id)) errors.push(`${entry.name}: invalid command id`)
|
|
71
|
+
const source = await readFile(path.join(commandsRoot, entry.name), "utf8")
|
|
72
|
+
if (!source.includes("description:")) errors.push(`${entry.name}: missing command description`)
|
|
73
|
+
const agent = source.match(/^agent:\s*([^\r\n]+)/m)?.[1]?.trim()
|
|
74
|
+
if (agent?.startsWith("ues-") && !agentIDs.has(agent)) {
|
|
75
|
+
errors.push(`${entry.name}: references missing subagent ${agent}`)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let agents = 0
|
|
80
|
+
for (const entry of await readdir(agentsRoot, { withFileTypes: true })) {
|
|
81
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) continue
|
|
82
|
+
agents += 1
|
|
83
|
+
const id = entry.name.slice(0, -3)
|
|
84
|
+
if (!validResourceID(id)) errors.push(`${entry.name}: invalid agent id`)
|
|
85
|
+
const source = await readFile(path.join(agentsRoot, entry.name), "utf8")
|
|
86
|
+
if (!source.includes("description:")) errors.push(`${entry.name}: missing agent description`)
|
|
87
|
+
if (!/mode:\s*subagent/.test(source)) errors.push(`${entry.name}: agent must use mode: subagent`)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (ids.size < 39) errors.push(`expected at least 39 skills, found ${ids.size}`)
|
|
91
|
+
if (commands < 11) errors.push(`expected at least 11 commands, found ${commands}`)
|
|
92
|
+
if (agents < 10) errors.push(`expected at least 10 subagents, found ${agents}`)
|
|
93
|
+
|
|
94
|
+
const routerIndex = path.join(pluginsRoot, "ues-router", "index.js")
|
|
95
|
+
const routerCore = path.join(pluginsRoot, "ues-router", "router.js")
|
|
96
|
+
const routerSafety = path.join(pluginsRoot, "ues-router", "safety.js")
|
|
97
|
+
const routerCapabilities = path.join(pluginsRoot, "ues-router", "capabilities.js")
|
|
98
|
+
if (!existsSync(routerIndex)) errors.push("missing OpenCode v2 router plugin entrypoint")
|
|
99
|
+
if (!existsSync(routerCore)) errors.push("missing OpenCode v2 router core")
|
|
100
|
+
if (!existsSync(routerSafety)) errors.push("missing OpenCode v2 safety gate")
|
|
101
|
+
if (!existsSync(routerCapabilities)) errors.push("missing OpenCode v2 runtime capability probe")
|
|
102
|
+
if (existsSync(routerIndex)) {
|
|
103
|
+
const source = await readFile(routerIndex, "utf8")
|
|
104
|
+
if (!source.includes('id: "ues-router"')) errors.push("v2 router plugin must declare stable id ues-router")
|
|
105
|
+
if (!source.includes('ctx.session.hook("prompt"')) errors.push("v2 router plugin must register prompt admission hook")
|
|
106
|
+
if (!source.includes('ctx.session.hook("context"')) errors.push("v2 router plugin must register context guardrail hook")
|
|
107
|
+
if (!source.includes('ctx.permission.hook("evaluate"')) errors.push("v2 router plugin must register permission safety hook")
|
|
108
|
+
if (!source.includes("ctx.tool.transform")) errors.push("v2 router plugin must register UES helper tools")
|
|
109
|
+
if (!source.includes('name: "dispatch_task"')) errors.push("v2 router plugin must expose fresh-context task dispatch")
|
|
110
|
+
if (!source.includes("ctx.session.create")) errors.push("v2 task dispatch must create a fresh session")
|
|
111
|
+
if (!source.includes("ctx.session.switchAgent")) errors.push("v2 task dispatch must select ues-executor")
|
|
112
|
+
if (!source.includes("ctx.session.switchModel")) errors.push("v2 task dispatch must support configured model escalation")
|
|
113
|
+
if (!source.includes("ctx.session.wait")) errors.push("v2 task dispatch must wait for executor completion")
|
|
114
|
+
if (!source.includes('name: "capabilities"')) errors.push("v7 router plugin must expose runtime capability inspection")
|
|
115
|
+
if (!source.includes('name: "task_policy"')) errors.push("v7 router plugin must expose adaptive task policy")
|
|
116
|
+
if (!source.includes('"work", "heartbeat"')) errors.push("v7 task dispatch must refresh task leases")
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
for (const name of ["process-runner.mjs","evidence-receipt.mjs","context-manifest.mjs","orchestrator-policy.mjs","worktree-sandbox.mjs","learning-engine.mjs","hermes-bridge.mjs","control-center.mjs"]) {
|
|
120
|
+
if (!existsSync(path.join(root, "lib", name))) errors.push(`missing V7 core module ${name}`)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
for (const name of ["codebase-mapper.md","plan-checker.md","executor.md","integration-verifier.md"]) {
|
|
124
|
+
if (!existsSync(path.join(agentsRoot, name))) errors.push(`missing V6 subagent ${name}`)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (errors.length) {
|
|
128
|
+
console.error("Validation failed:")
|
|
129
|
+
for (const error of errors) console.error(`- ${error}`)
|
|
130
|
+
process.exit(1)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
console.log(`Validated ${ids.size} skills, ${commands} commands and ${agents} subagents.`)
|