#!/usr/bin/env node
/**
* Validates this DSH bundle before it can be installed:
* manifest shape, declared patch resolution, YAML legality (including the DSH
* `!!js` tag, which plain js-yaml rejects), the preset's structural invariants,
* and repo hygiene.
*
* Local run:
* npm install --no-save js-yaml@^4
* node scripts/validate.mjs # validates the repo this file lives in
* node scripts/validate.mjs
# validates another checkout
* CI run: see .github/workflows/validate.yml
*/
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import yaml from 'js-yaml';
const root = process.argv[2]
? path.resolve(process.argv[2])
: path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const read = (rel) => fs.readFileSync(path.join(root, rel), 'utf8');
/**
* DSH's loader registers `!!js` and evaluates those scalars when the owning
* plugin activates. Reproduce the tag so this file validates; do NOT evaluate
* anything here.
*/
const JsExpression = new yaml.Type('tag:yaml.org,2002:js', {
kind: 'scalar',
resolve: (data) => typeof data === 'string' && data.length > 0,
construct: (data) => data,
});
const SCHEMA = yaml.DEFAULT_SCHEMA.extend([JsExpression]);
const passed = [];
const failed = [];
const check = (condition, message) => (condition ? passed : failed).push(message);
/** Registry-accepted package names, mirroring the shipped install-spec rules. */
const PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*$/;
/** Global, model-visible tool names this preset is allowed to name in a filter. */
const KNOWN_TOOLS = new Set([
'pwsh', 'bash', 'read', 'write', 'edit', 'glob', 'grep',
'web_search', 'web_fetch', 'todo_write', 'ask_user_question', 'present',
'job_output', 'job_list', 'job_kill',
]);
const PERSONAL_PATH = /(?:[A-Za-z]:\\Users\\|\/Users\/[^/\s]+\/|\/home\/[^/\s]+\/)/;
// ------------------------------------------------------------------ manifest
console.log(`validating bundle at ${root}\n`);
const pkg = JSON.parse(read('package.json'));
check(PACKAGE_NAME.test(pkg.name), `package.json name is installable (${pkg.name})`);
check(/^\d+\.\d+\.\d+$/.test(pkg.version), `package.json version is semver (${pkg.version})`);
check(typeof pkg.description === 'string' && pkg.description.length > 0, 'package.json description is present');
check(pkg.type === 'module', 'package.json type is "module"');
check(typeof pkg.license === 'string' && pkg.license.length > 0, `package.json license is declared (${pkg.license})`);
// A bundle with no peerDependencies skips the runtime compatibility gate;
// declaring DSH peers without need only adds a way for installs to fail.
check(!Object.hasOwn(pkg, 'peerDependencies'), 'no peerDependencies declared (compat gate stays out of the way)');
const declared = pkg.dsh?.bundle?.patch;
check(declared !== undefined, 'dsh.bundle.patch is declared (required, else the installer reports not-a-bundle)');
const patchFiles = declared === undefined ? [] : Array.isArray(declared) ? declared : [declared];
check(
patchFiles.length > 0 && patchFiles.every((f) => typeof f === 'string' && f.length > 0),
'dsh.bundle.patch is a path or a list of paths',
);
// ----------------------------------------------------------- patch structure
const presetIds = new Set();
for (const rel of patchFiles) {
const abs = path.join(root, rel);
check(fs.existsSync(abs), `declared patch file exists: ${rel}`);
if (!fs.existsSync(abs)) continue;
const text = fs.readFileSync(abs, 'utf8');
check(!PERSONAL_PATH.test(text), `${rel} carries no personal absolute paths`);
let doc;
try {
doc = yaml.load(text, { schema: SCHEMA });
check(true, `${rel} parses as YAML (including the !!js tag)`);
} catch (error) {
check(false, `${rel} parses as YAML (${error.message})`);
continue;
}
check(Array.isArray(doc), `${rel} is a top-level YAML array`);
if (!Array.isArray(doc)) continue;
const presetRows = doc
.filter((entry) => entry !== null && typeof entry === 'object' && Array.isArray(entry.insert))
.flatMap((entry) => entry.insert)
.filter((row) => row !== null && typeof row === 'object' && row.name === '@deepseek-ai/dsh-agent-preset');
check(presetRows.length >= 1, `${rel} inserts at least one @deepseek-ai/dsh-agent-preset row`);
for (const row of presetRows) {
const config = row.config ?? {};
const id = config.id;
check(typeof id === 'string' && /^[a-z0-9][a-z0-9-]*$/.test(id), `${rel} preset config.id is a stable slug (${id})`);
if (typeof id === 'string') {
// The registry fails a duplicate preset id outright.
check(!presetIds.has(id), `preset id ${id} is unique across this bundle`);
presetIds.add(id);
}
check(Array.isArray(config.plugins) && config.plugins.length > 0, `preset ${id} declares a non-empty plugins list`);
if (!Array.isArray(config.plugins)) continue;
const rows = config.plugins.flatMap((p) => (p && p.group && Array.isArray(p.config) ? p.config : [p]));
const subagents = rows.filter((r) => r && r.name === '@deepseek-ai/dsh-tool-subagent');
check(subagents.length > 0, `preset ${id} mounts at least one tool-subagent instance`);
const toolNames = subagents.map((s) => s.config?.toolName).filter(Boolean);
check(new Set(toolNames).size === toolNames.length, `preset ${id} toolNames are unique (${toolNames.join(', ')})`);
for (const sub of subagents) {
const c = sub.config ?? {};
const label = c.toolName ?? '(unnamed subagent)';
const filter = c.toolFilter;
check(!!filter && (Array.isArray(filter.allow) || Array.isArray(filter.deny)), `${label}: toolFilter names allow or deny`);
for (const tool of (filter && filter.allow) || []) {
check(KNOWN_TOOLS.has(tool), `${label}: allow-listed tool "${tool}" is a real global tool name`);
}
// maxDepth 0 forbids the very delegation it is meant to permit: the child
// is at parent depth + 1, so every call fails with
// "subagent depth 1 exceeds maxDepth 0".
check(c.maxDepth !== 0, `${label}: maxDepth is not 0 (0 would forbid the delegation itself)`);
if (c.persona !== undefined) {
check(typeof c.persona === 'string' && c.persona.trim().length > 100, `${label}: persona is a non-trivial string`);
}
if (c.backgroundMode === 'continuable') {
check(c.enableRunInBackground !== false, `${label}: continuable mode must not disable background runs`);
}
}
}
}
// ----------------------------------------------------------------- repo files
for (const rel of ['README.md', 'LICENSE', 'lib/index.js']) {
check(fs.existsSync(path.join(root, rel)), `${rel} is present`);
}
// --------------------------------------------------------------------- report
for (const line of passed) console.log(`PASS ${line}`);
for (const line of failed) console.log(`FAIL ${line}`);
console.log(`\n${passed.length} passed, ${failed.length} failed`);
process.exit(failed.length === 0 ? 0 : 1);