refactor: drop 12 runtimes, keep Claude, Codex, OpenCode, Cursor, ZCode, Antigravity
Removes kilo, kimi, kimi-code, copilot, windsurf, augment, trae, qwen, hermes, cline, codebuddy and pi end to end: capability descriptors, installer branches and converters (bin/install.js 14.9k -> 11.2k lines), TypeScript converters, hook surfaces and runtime homes, review lanes qwen/kimi-code, the two pi migrations, Kimi payload normalization in the hook guards, dead hostBehaviors vocabulary, launcher home probes, fixtures, runtime-specific tests and the prose that presented them as supported. Installer output for the six kept runtimes is byte-identical to before the prune. The Kimi tool-vocabulary tests in workflow-guard, read-guard and read-injection-scanner are left in place pending a decision.
This commit is contained in:
@@ -44,9 +44,6 @@ const HOOKS_TO_COPY = [
|
||||
'msd-cursor-stop.js',
|
||||
'msd-cursor-subagent-start.js',
|
||||
'msd-cursor-subagent-stop.js',
|
||||
// Windsurf/Cascade lifecycle hooks (ADR-1239/#2100 Stage 2): 2 blocking events
|
||||
'msd-windsurf-pre-write.js',
|
||||
'msd-windsurf-pre-command.js',
|
||||
// Claude Code FileChanged hook (#770) — hot-reloads msd config when
|
||||
// .planning/config.json changes mid-session. Must ship to dist so the
|
||||
// installer can copy it to the target hooks/ dir and register FileChanged.
|
||||
|
||||
@@ -316,7 +316,6 @@ const DOCS_GUARD_TESTS = {
|
||||
'tests/intel.test.cjs': ['*'],
|
||||
'tests/inventory-headings-countfree.test.cjs': ['docs/INVENTORY.md'],
|
||||
'tests/inventory-manifest-sync.test.cjs': ['docs/INVENTORY.md', 'docs/INVENTORY-MANIFEST.json'],
|
||||
'tests/kilo-upgrades.test.cjs': ['docs/how-to/connect-msd-mcp-server.md'],
|
||||
'tests/live-config-guard.test.cjs': ['docs/TESTING-SUITES.md'],
|
||||
// #3726: pins the `milestone complete` synopsis (English + four localized
|
||||
// mirrors), the `--confirm` flag row and the guard-override instructions —
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
* `--check`-guarded JSON manifest that is NOT runtime code. It previously
|
||||
* lived at msd-core/bin/lib/context-index.cjs — a shipped runtime module is
|
||||
* the wrong place for ~120 KB of arbitrary CONTEXT.md prose: it tripped both
|
||||
* tests/cline-install.test.cjs's leaked-`.claude`-path guard and
|
||||
* the leaked-`.claude`-path guard and
|
||||
* tests/package-name-single-source.test.cjs's hardcoded-package-name guard,
|
||||
* both true positives against runtime-code content scanning. Moving the
|
||||
* artifact to docs/ (never scanned as runtime code) fixes both without
|
||||
|
||||
@@ -407,7 +407,7 @@ function runMain(main) {
|
||||
* like `payload`. This exists because `hooks/msd-write-guard.js`'s
|
||||
* emitBlock does NOT write the same bytes to both streams today — it
|
||||
* writes the full JSON `output` to stdout but only the plain-text
|
||||
* `output.reason` STRING to stderr, because Kimi's native hook bus reads
|
||||
* `output.reason` STRING to stderr, because a native hook bus may read
|
||||
* stderr verbatim back to the model on exit 2. Migrating that call site
|
||||
* onto terminateNow requires a way to say "fd 2 gets this different,
|
||||
* plain-text value" — `stderrPayload` is that seam. Ignored entirely for
|
||||
|
||||
@@ -38,7 +38,6 @@ module.exports = {
|
||||
"tests/chunked-planning-parallel.test.cjs",
|
||||
"tests/ci-docs-guard-registry.test.cjs",
|
||||
"tests/ci-test-scope.test.cjs",
|
||||
"tests/cline-install.test.cjs",
|
||||
"tests/code-review-pipeline-regression.test.cjs",
|
||||
"tests/codex-config-agents.test.cjs",
|
||||
"tests/codex-config-hooks.test.cjs",
|
||||
@@ -98,7 +97,6 @@ module.exports = {
|
||||
"tests/install.test.cjs",
|
||||
"tests/installer-migration-antigravity-retire-confighome-artifacts.test.cjs",
|
||||
"tests/installer-migration-config-root-marker.test.cjs",
|
||||
"tests/installer-migration-pi-retire-hooks-dir.test.cjs",
|
||||
"tests/installer-migration-prune-stale-pristine.test.cjs",
|
||||
"tests/installer-migration-rename-msd-core.test.cjs",
|
||||
"tests/installer-migrations.test.cjs",
|
||||
@@ -106,7 +104,6 @@ module.exports = {
|
||||
"tests/inventory-nested-families.test.cjs",
|
||||
"tests/isolation-sentinel.test.cjs",
|
||||
"tests/issue-version-gate.test.cjs",
|
||||
"tests/kimi-upgrades.test.cjs",
|
||||
"tests/lint-docs-command-form.test.cjs",
|
||||
"tests/lint-retired-runtime-name.test.cjs",
|
||||
"tests/lint-workflow-shellcheck-fetch.test.cjs",
|
||||
|
||||
@@ -6,7 +6,6 @@ module.exports = {
|
||||
"tests/adr-index-gate.test.cjs",
|
||||
"tests/adr857-core-without-capabilities.test.cjs",
|
||||
"tests/agent-install-check.test.cjs",
|
||||
"tests/agent-install-validation.test.cjs",
|
||||
"tests/agent-skills.test.cjs",
|
||||
"tests/antigravity-upgrades.test.cjs",
|
||||
"tests/api-coverage-gate-e2e.test.cjs",
|
||||
@@ -14,7 +13,6 @@ module.exports = {
|
||||
"tests/assumption-delta-checkpoint-e2e.test.cjs",
|
||||
"tests/assumption-delta.test.cjs",
|
||||
"tests/audit-command-cutover.test.cjs",
|
||||
"tests/augment-upgrades.test.cjs",
|
||||
"tests/broken-windows.test.cjs",
|
||||
"tests/capability-cli.test.cjs",
|
||||
"tests/capability-command-dispatch.test.cjs",
|
||||
@@ -40,7 +38,6 @@ module.exports = {
|
||||
"tests/ci-test-scope.test.cjs",
|
||||
"tests/cjs-command-router-adapter.test.cjs",
|
||||
"tests/claude-md.test.cjs",
|
||||
"tests/cline-install.test.cjs",
|
||||
"tests/close-phase-todos-padded-resolves.test.cjs",
|
||||
"tests/code-review-fix-pipeline-regression.test.cjs",
|
||||
"tests/code-review-pipeline-regression.test.cjs",
|
||||
@@ -64,8 +61,6 @@ module.exports = {
|
||||
"tests/config-schema.property.test.cjs",
|
||||
"tests/config.test.cjs",
|
||||
"tests/configured-entrypoint-validation.test.cjs",
|
||||
"tests/copilot-install.test.cjs",
|
||||
"tests/copilot-upgrades.test.cjs",
|
||||
"tests/core-utils.test.cjs",
|
||||
"tests/cursor-hook-workspace-roots.test.cjs",
|
||||
"tests/cursor-hooks.test.cjs",
|
||||
@@ -108,7 +103,6 @@ module.exports = {
|
||||
"tests/health-diagnostic.test.cjs",
|
||||
"tests/helpers-cleanup.test.cjs",
|
||||
"tests/helpers-process-isolation.test.cjs",
|
||||
"tests/hermes-skills-migration.test.cjs",
|
||||
"tests/hooks-commonjs-marker.test.cjs",
|
||||
"tests/hooks-crash-policy.test.cjs",
|
||||
"tests/hooks-opt-in.test.cjs",
|
||||
@@ -124,7 +118,6 @@ module.exports = {
|
||||
"tests/install.test.cjs",
|
||||
"tests/installer-migration-antigravity-retire-confighome-artifacts.test.cjs",
|
||||
"tests/installer-migration-config-root-marker.test.cjs",
|
||||
"tests/installer-migration-pi-retire-hooks-dir.test.cjs",
|
||||
"tests/installer-migration-prune-stale-pristine.test.cjs",
|
||||
"tests/installer-migration-rename-msd-core.test.cjs",
|
||||
"tests/installer-migrations.test.cjs",
|
||||
@@ -132,10 +125,6 @@ module.exports = {
|
||||
"tests/inventory-nested-families.test.cjs",
|
||||
"tests/io.test.cjs",
|
||||
"tests/isolation-sentinel.test.cjs",
|
||||
"tests/kilo-upgrades.test.cjs",
|
||||
"tests/kimi-agent-converter.test.cjs",
|
||||
"tests/kimi-upgrades.test.cjs",
|
||||
"tests/kimi-variant-disambiguation.test.cjs",
|
||||
"tests/lint-workflow-shellcheck-fetch.test.cjs",
|
||||
"tests/live-config-guard.test.cjs",
|
||||
"tests/lockfile-cve-audit.test.cjs",
|
||||
@@ -185,7 +174,6 @@ module.exports = {
|
||||
"tests/phase-locator.test.cjs",
|
||||
"tests/phase.test.cjs",
|
||||
"tests/phase6-capstone-conformance.test.cjs",
|
||||
"tests/pi-config-dir-env-override.test.cjs",
|
||||
"tests/plan-count-single-owner.test.cjs",
|
||||
"tests/plan-phase-stall-detection.test.cjs",
|
||||
"tests/plan-pre-hook-e2e.test.cjs",
|
||||
@@ -216,7 +204,6 @@ module.exports = {
|
||||
"tests/quick-review-scope-tip-bound.test.cjs",
|
||||
"tests/read-guard.test.cjs",
|
||||
"tests/reapply-verify-hunks.test.cjs",
|
||||
"tests/repo-layout.test.cjs",
|
||||
"tests/representative-corpus.test.cjs",
|
||||
"tests/require-fs-op-fallback.rule.test.cjs",
|
||||
"tests/require-full-tmpdir-triad.rule.test.cjs",
|
||||
@@ -269,7 +256,6 @@ module.exports = {
|
||||
"tests/verify-archive-dirs-live-path.test.cjs",
|
||||
"tests/verify-command-grounding.test.cjs",
|
||||
"tests/verify.test.cjs",
|
||||
"tests/windsurf-hooks-bridge.test.cjs",
|
||||
"tests/workflow-guard.test.cjs",
|
||||
"tests/workflow-shell-pinning.test.cjs",
|
||||
"tests/workstream-inventory.test.cjs",
|
||||
|
||||
@@ -17,19 +17,15 @@
|
||||
"tests/changeset-cli.test.cjs :: source-text-is-the-product",
|
||||
"tests/claude-md.test.cjs :: source-text-is-the-product",
|
||||
"tests/cleanup-branch-pruning.test.cjs :: source-text-is-the-product",
|
||||
"tests/cline-install.test.cjs :: source-text-is-the-product",
|
||||
"tests/cline-support.test.cjs :: source-text-is-the-product",
|
||||
"tests/code-review-agent-skills.test.cjs :: source-text-is-the-product",
|
||||
"tests/code-review-command.test.cjs :: source-text-is-the-product",
|
||||
"tests/code-review-pipeline-regression.test.cjs :: source-text-is-the-product",
|
||||
"tests/code-review.test.cjs :: source-text-is-the-product",
|
||||
"tests/codebuddy-install.test.cjs :: source-text-is-the-product",
|
||||
"tests/command-contract.test.cjs :: source-text-is-the-product",
|
||||
"tests/commands.test.cjs :: source-text-is-the-product",
|
||||
"tests/config-field-docs.test.cjs :: docs-parity",
|
||||
"tests/context-enrichment.test.cjs :: source-text-is-the-product",
|
||||
"tests/contributor-standards.test.cjs :: source-text-is-the-product",
|
||||
"tests/copilot-install.test.cjs :: integration-test-input",
|
||||
"tests/cursor-hooks.test.cjs :: source-text-is-the-product",
|
||||
"tests/cursor-reviewer.test.cjs :: source-text-is-the-product",
|
||||
"tests/debug-session-management.test.cjs :: source-text-is-the-product",
|
||||
@@ -55,7 +51,6 @@
|
||||
"tests/msd-researcher-flow-diagram.test.cjs :: source-text-is-the-product",
|
||||
"tests/msd-settings-advanced.test.cjs :: source-text-is-the-product",
|
||||
"tests/helpers/live-command-registry.cjs :: source-text-is-the-product",
|
||||
"tests/hermes-skills-migration.test.cjs :: source-text-is-the-product",
|
||||
"tests/import-command.test.cjs :: source-text-is-the-product",
|
||||
"tests/ingest-docs.test.cjs :: source-text-is-the-product",
|
||||
"tests/inline-plan-threshold.test.cjs :: source-text-is-the-product",
|
||||
@@ -91,7 +86,6 @@
|
||||
"tests/profile-output.test.cjs :: source-text-is-the-product",
|
||||
"tests/progress-forensic.test.cjs :: source-text-is-the-product",
|
||||
"tests/prompt-thinning.test.cjs :: source-text-is-the-product",
|
||||
"tests/qwen-skills-migration.test.cjs :: source-text-is-the-product",
|
||||
"tests/read-guard.test.cjs :: source-text-is-the-product",
|
||||
"tests/reapply-patches.test.cjs :: source-text-is-the-product",
|
||||
"tests/reapply-verify-hunks.test.cjs :: source-text-is-the-product",
|
||||
|
||||
@@ -76,9 +76,9 @@
|
||||
*
|
||||
* `local/no-source-grep` is registered by `eslint.config.mjs` on several
|
||||
* glob blocks, not only `tests/** /*.cjs`: also `scripts/** /*.cjs`,
|
||||
* `eslint-rules/** /*.cjs`, `bin/lib/** /*.cjs`, `pi/** /*.cjs`,
|
||||
* `eslint-rules/** /*.cjs`, `bin/lib/** /*.cjs`,
|
||||
* `examples/** /*.cjs`, `msd-core/bin/** /*.cjs`, `vscode/*.js`,
|
||||
* `.kilo/plugins/*.js`, and `.opencode/plugins/*.js`. A prior version of
|
||||
* and `.opencode/plugins/*.js`. A prior version of
|
||||
* this script only walked `tests/** /*.test.cjs`, which silently dropped
|
||||
* every non-`.test.cjs` file under `tests/` AND every file under every one
|
||||
* of those other blocks from BOTH reported numbers — a marker there would
|
||||
@@ -168,8 +168,8 @@ const ROOT = path.join(__dirname, '..');
|
||||
const ESLINT_CONFIG_PATH = path.join(ROOT, 'eslint.config.mjs');
|
||||
const TESTS_DIR = process.env.MSD_LINT_ALLOW_TEST_RULE_TESTS_DIR || path.join(ROOT, 'tests');
|
||||
// Overrides the ROOT that non-`tests/` glob base directories (scripts/,
|
||||
// eslint-rules/, bin/lib/, pi/, examples/, msd-core/bin/, vscode/,
|
||||
// .kilo/plugins/, .opencode/plugins/ — see deriveNoSourceGrepGlobs) are
|
||||
// eslint-rules/, bin/lib/, examples/, msd-core/bin/, vscode/,
|
||||
// .opencode/plugins/ — see deriveNoSourceGrepGlobs) are
|
||||
// resolved under. TESTS_DIR already has its own override (above) for the
|
||||
// `tests/**/*.cjs` block; this is the equivalent knob for every OTHER block
|
||||
// that registers `local/no-source-grep`, so sandbox fixtures can exercise
|
||||
@@ -246,9 +246,9 @@ function extractGenuineMarkerReasons(allComments) {
|
||||
* set, which silently missed every non-`.test.cjs` file under `tests/`
|
||||
* (`tests/helpers/**`, `tests/qa/**`, `tests/fixtures/**`, ...) AND every one
|
||||
* of the other config blocks the rule is registered on (`scripts/** /*.cjs`,
|
||||
* `eslint-rules/** /*.cjs`, `bin/lib/** /*.cjs`, `pi/** /*.cjs`,
|
||||
* `eslint-rules/** /*.cjs`, `bin/lib/** /*.cjs`,
|
||||
* `examples/** /*.cjs`, `msd-core/bin/** /*.cjs`, `vscode/*.js`,
|
||||
* `.kilo/plugins/*.js`, `.opencode/plugins/*.js`). A marker in any of those
|
||||
* `.opencode/plugins/*.js`). A marker in any of those
|
||||
* would vanish from BOTH reported numbers instead of tripping anything.
|
||||
*
|
||||
* Rather than hand-maintain a second copy of that glob list (the exact
|
||||
@@ -294,7 +294,7 @@ async function deriveNoSourceGrepGlobs() {
|
||||
/**
|
||||
* The literal (non-wildcard) leading path segments of a glob — the
|
||||
* directory that must actually be walked on disk to find candidate files.
|
||||
* e.g. `tests/** /*.cjs` -> `tests`, `.kilo/plugins/*.js` -> `.kilo/plugins`.
|
||||
* e.g. `tests/** /*.cjs` -> `tests`, `.opencode/plugins/*.js` -> `.opencode/plugins`.
|
||||
*
|
||||
* The LAST segment is always excluded from consideration even when it has
|
||||
* no wildcard of its own (e.g. the single-file fixture globs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"maxSites": 84,
|
||||
"maxSites": 81,
|
||||
"grace": 2
|
||||
}
|
||||
|
||||
@@ -40,10 +40,8 @@ const DOCS_GUARD_EXEMPT_BASELINE = [
|
||||
'capability-validator-task-content-resolver.test.cjs',
|
||||
'ci-docs-guard-registry.test.cjs',
|
||||
'ci-test-scope.test.cjs',
|
||||
'cline-install.test.cjs',
|
||||
'code-review-depth.test.cjs',
|
||||
'code-review-pipeline-regression.test.cjs',
|
||||
'codebuddy-upgrades.test.cjs',
|
||||
'commands.test.cjs',
|
||||
'commit-docs-bypass.test.cjs',
|
||||
'compact-content-partition-guard.test.cjs',
|
||||
@@ -58,13 +56,10 @@ const DOCS_GUARD_EXEMPT_BASELINE = [
|
||||
'gen-context-index.test.cjs',
|
||||
'gen-registry.test.cjs',
|
||||
'msd-agent-isolation-guard.test.cjs',
|
||||
'hermes-dispatch-upgrade.test.cjs',
|
||||
'install-minimal-hooks.test.cjs',
|
||||
'install-runtime-artifacts.test.cjs',
|
||||
'installer-migration-config-root-marker.test.cjs',
|
||||
'installer-migration-pi-extension-ext.test.cjs',
|
||||
'installer-migrations.test.cjs',
|
||||
'kimi-upgrades.test.cjs',
|
||||
'lint-allow-test-rule-refs.test.cjs',
|
||||
'lint-docs-command-form.test.cjs',
|
||||
'lint-docs-required.test.cjs',
|
||||
@@ -84,7 +79,6 @@ const DOCS_GUARD_EXEMPT_BASELINE = [
|
||||
'reviewer-manifest-body.test.cjs',
|
||||
'reviewer-step-dispatch.test.cjs',
|
||||
'run-tests-harness.test.cjs',
|
||||
'runtime-name-policy.test.cjs',
|
||||
'security-prompt-injection.security.test.cjs',
|
||||
'shipped-reference-cites.test.cjs',
|
||||
'state.test.cjs',
|
||||
@@ -134,10 +128,8 @@ const DOCS_GUARD_EXEMPT_DOCS_PATHS = {
|
||||
'docs/adr/4641-windows-selector-consolidation.md', 'docs/how-to/configure-model-profiles.md',
|
||||
'docs/installer-migrations.md', 'docs/ja-JP', 'docs/ja-JP/USAGE.md', 'docs/usage.md', 'docs/x.md',
|
||||
],
|
||||
'cline-install.test.cjs': ['docs/guide.md'],
|
||||
'code-review-depth.test.cjs': ['docs/src/auth/x.ts'],
|
||||
'code-review-pipeline-regression.test.cjs': ['docs/DEVELOPMENT.md'],
|
||||
'codebuddy-upgrades.test.cjs': ['docs/cli/sub-agents'],
|
||||
'commands.test.cjs': ['docs/x.md'],
|
||||
'commit-docs-bypass.test.cjs': [
|
||||
'docs/40-design.md', 'docs/CONFIGURATION.md', 'docs/readme.md', 'docs/tracked-var-mentioning',
|
||||
@@ -166,13 +158,10 @@ const DOCS_GUARD_EXEMPT_DOCS_PATHS = {
|
||||
'gen-context-index.test.cjs': ['docs/CONTEXT-INDEX.json', 'docs/INVENTORY-MANIFEST.json'],
|
||||
'gen-registry.test.cjs': ['docs/registries', 'docs/registries/reviewers.json'],
|
||||
'msd-agent-isolation-guard.test.cjs': ['docs/adr/1239-...md', 'docs/adr/1239-msd-embeddable-orchestration-engine.md'],
|
||||
'hermes-dispatch-upgrade.test.cjs': ['docs/guides/delegation-patterns.md'],
|
||||
'install-minimal-hooks.test.cjs': ['docs/en/hooks', 'docs/en/users/features/hooks'],
|
||||
'install-runtime-artifacts.test.cjs': ['docs/CONFIGURATION.md', 'docs/adr/58-...md', 'docs/adr/58-runtime-install-policy-module.md', 'docs/cli/slash-commands'],
|
||||
'install-runtime-artifacts.test.cjs': ['docs/CONFIGURATION.md', 'docs/adr/58-...md', 'docs/adr/58-runtime-install-policy-module.md'],
|
||||
'installer-migration-config-root-marker.test.cjs': ['docs/installer-migrations.md'],
|
||||
'installer-migration-pi-extension-ext.test.cjs': ['docs/installer-migrations.md'],
|
||||
'installer-migrations.test.cjs': ['docs/installer-migrations.md'],
|
||||
'kimi-upgrades.test.cjs': ['docs/reference/host-integration-capability-matrix.md'],
|
||||
'lint-allow-test-rule-refs.test.cjs': ['docs/readme.md'],
|
||||
'lint-docs-command-form.test.cjs': ['docs/adr', 'docs/adr/999-example.md', 'docs/how-to/example.md'],
|
||||
'lint-docs-required.test.cjs': [
|
||||
@@ -230,7 +219,6 @@ const DOCS_GUARD_EXEMPT_DOCS_PATHS = {
|
||||
// review). Neither is a real filesystem read — the file never reads any docs/ file.
|
||||
'reviewer-step-dispatch.test.cjs': ['docs/adr/456-test-rigor-architecture.md', 'docs/spec.md'],
|
||||
'run-tests-harness.test.cjs': ['docs/TESTING-SUITES.md'],
|
||||
'runtime-name-policy.test.cjs': ['docs/customize/skills'],
|
||||
'security-prompt-injection.security.test.cjs': ['docs/notes.md'],
|
||||
'shipped-reference-cites.test.cjs': [],
|
||||
// #4186: the record-session usage-contract test cites the documented
|
||||
|
||||
@@ -42,20 +42,18 @@
|
||||
* entries and MISSED a real leak into `<live>/skills/msd-dev-preferences/`.
|
||||
*
|
||||
* KNOWN GAP — a leak into a file MSD does not own (e.g. mutating the host's own
|
||||
* `.claude.json`, `settings.json`, `hooks.json`, `kilo.json`, `opencode.json`) is
|
||||
* `.claude.json`, `settings.json`, `hooks.json`, `opencode.json`) is
|
||||
* outside this guard by construction. Closing it would require watching shared
|
||||
* files, which is the false-positive trap above.
|
||||
*
|
||||
* NAMED RESIDUALS — stated rather than implied, because two successive rounds
|
||||
* asserted this list was complete and both were refuted. Still NOT watched:
|
||||
* - `agents/subagents/**` (kimi stages `subagents/msd-executor.yaml` under an
|
||||
* UNPREFIXED intermediate dir, so no prefix scan of `agents/` reaches it);
|
||||
* - the loose capability generators copied to `<root>/scripts/*.cjs`
|
||||
* (`fix-slash-commands.cjs` is watched by name; the generators are not);
|
||||
* - `extensions/package.json` and `plugins/package.json` — CommonJS markers in
|
||||
* dirs MSD fills but does not own, so they fall under the shared-ground rule
|
||||
* below rather than being watched;
|
||||
* - the shared-hooks bundle in a NON-registry root's `<root>/hooks/` (kimi) —
|
||||
* - the shared-hooks bundle in a NON-registry root's `<root>/hooks/` —
|
||||
* see resolveExtraWatchTargets; closing it is a layout decision.
|
||||
* DELIBERATELY not watched, which is a different thing from missed: `hooks/lib`,
|
||||
* `hooks/package.json`, `scripts/lib` and `scripts/changeset`. The installer
|
||||
@@ -125,7 +123,7 @@ const NON_REGISTRY_ARTIFACT_PARENTS = ['hooks', 'plugins', 'scripts', 'extension
|
||||
/**
|
||||
* Prefixes used for a parent with no registry-declared one. BOTH forms are the
|
||||
* point: MSD writes `msd-`-hyphen artifacts (`hooks/msd-check-update.js`) AND
|
||||
* bare `msd.`-dotted ones (pi's `extensions/msd.js`), and a lone `msd-` sees
|
||||
* bare `msd.`-dotted ones (`plugins/msd.js`), and a lone `msd-` sees
|
||||
* only the first.
|
||||
*/
|
||||
const DEFAULT_ARTIFACT_PREFIXES = ['msd-', 'msd.'];
|
||||
@@ -176,13 +174,13 @@ function deriveArtifactTargets(runtimes) {
|
||||
const dest = layout?.destSubpath;
|
||||
if (typeof dest !== 'string' || !dest) continue;
|
||||
const last = dest.split('/').pop() || '';
|
||||
// A destination whose own final segment is MSD's (hermes' `skills/msd`) is
|
||||
// A destination whose own final segment is MSD's (`skills/msd`) is
|
||||
// owned wholesale — that directory is ours, not shared.
|
||||
if (last.startsWith('msd')) { owned.add(dest); continue; }
|
||||
// The layout declares its OWN prefix, and it varies: kimi's `kimi-agents`
|
||||
// layout declares `msd` (no hyphen) and writes `agents/msd.yaml` +
|
||||
// `agents/msd.md`, invisible to a fixed `msd-` scan. The same destSubpath
|
||||
// also carries different prefixes across runtimes, so a parent maps to a SET.
|
||||
// The layout declares its OWN prefix, and it may vary: a layout declaring
|
||||
// `msd` (no hyphen) would be invisible to a fixed `msd-` scan. The same
|
||||
// destSubpath also carries different prefixes across runtimes, so a
|
||||
// parent maps to a SET.
|
||||
const declared = typeof layout?.prefix === 'string' && layout.prefix
|
||||
? [layout.prefix]
|
||||
: DEFAULT_ARTIFACT_PREFIXES;
|
||||
@@ -214,12 +212,10 @@ function artifactTargets(deps = {}) {
|
||||
/**
|
||||
* The file MSD writes into a NON-REGISTRY config home.
|
||||
*
|
||||
* Both current descriptors are Kimi's — Kimi CLI's `~/.kimi` (KIMI_SHARE_DIR) and
|
||||
* Kimi Code's `~/.kimi-code` (KIMI_CODE_HOME) — and MSD writes its native
|
||||
* `[[hooks]]` block into `config.toml` in each, so the single filename below holds
|
||||
* for both. NAMED RESIDUAL: this assumes every non-registry descriptor is written
|
||||
* the same way. That assumption is now load-bearing rather than vacuous — it is
|
||||
* carrying two descriptors, not one — and a future descriptor whose owned file
|
||||
* NON_REGISTRY_CONFIG_HOME_DESCRIPTORS is empty today (its former entries were
|
||||
* native `config.toml` hook homes of since-retired runtimes), so this filename
|
||||
* is currently unused. NAMED RESIDUAL: this assumes every non-registry
|
||||
* descriptor is written the same way; a future descriptor whose owned file
|
||||
* differs needs a per-descriptor mapping here. The consequence of getting it wrong
|
||||
* is under-watching (a missed leak), not a false positive, so it fails in the quiet
|
||||
* direction and is called out rather than left to be discovered.
|
||||
@@ -339,8 +335,8 @@ function resolveLiveConfigRoots(deps = {}) {
|
||||
* #2665 round 3: resolveLiveConfigRoots enumerates getGlobalConfigDir per registry
|
||||
* runtime plus grok. A live write surface that is not a config ROOT is invisible to
|
||||
* that shape, so a leak on one passed through this guard — the PR's own safety net —
|
||||
* silently. There are THREE today ($MSD_HOME/.msd, plus one config.toml per entry in
|
||||
* NON_REGISTRY_CONFIG_HOME_DESCRIPTORS, which #2755 took from one entry to two):
|
||||
* silently. Today: $MSD_HOME/.msd, plus one config.toml per entry in
|
||||
* NON_REGISTRY_CONFIG_HOME_DESCRIPTORS (currently none):
|
||||
*
|
||||
* $MSD_HOME/.msd — MSD's user-owned store (consent.json, defaults.json, capability
|
||||
* overlays). Watched WHOLESALE: unlike ~/.claude this root is
|
||||
@@ -348,9 +344,8 @@ function resolveLiveConfigRoots(deps = {}) {
|
||||
* SCOPE above does not apply and an ownership filter would only
|
||||
* narrow the guard for nothing.
|
||||
* <non-registry home>/config.toml — the file MSD writes its native [[hooks]] block
|
||||
* into, one per NON_REGISTRY_CONFIG_HOME_DESCRIPTORS entry: Kimi
|
||||
* CLI's ~/.kimi (KIMI_SHARE_DIR) and, since #2755, Kimi Code's
|
||||
* ~/.kimi-code (KIMI_CODE_HOME). The INVERSE case: those roots
|
||||
* into, one per NON_REGISTRY_CONFIG_HOME_DESCRIPTORS entry
|
||||
* (none today). The INVERSE case: those roots
|
||||
* belong to their products, so the root is never watched
|
||||
* wholesale. This is the KNOWN GAP above accepted deliberately
|
||||
* in one direction — MSD demonstrably writes these files
|
||||
@@ -384,19 +379,18 @@ function resolveExtraWatchTargets(deps = {}) {
|
||||
resolveConfigHomeFromDescriptor,
|
||||
} = require(path.join(libDir, 'runtime-homes.cjs'));
|
||||
|
||||
// ITERATE the descriptor array rather than naming one resolver. Calling
|
||||
// resolveKimiHooksTomlDir directly would cover one of today's two entries and
|
||||
// silently miss tomorrow's — the same partial-enumeration defect that put
|
||||
// KIMI_SHARE_DIR outside the scrub set in the first place, reintroduced one
|
||||
// layer over. TEST_ENV_BASE derives its keys from this array; deriving the
|
||||
// guard's paths from it keeps the two halves from drifting apart.
|
||||
// ITERATE the descriptor array rather than naming one resolver — a
|
||||
// partial enumeration would silently miss a future entry. TEST_ENV_BASE
|
||||
// derives its keys from this array; deriving the guard's paths from it
|
||||
// keeps the two halves from drifting apart.
|
||||
//
|
||||
// Thread the SAME injected env/home used above: resolving bare would read
|
||||
// process.env and os.homedir() regardless of `deps`, leaving the seam
|
||||
// untestable and the targets resolved against different worlds.
|
||||
for (const descriptor of NON_REGISTRY_CONFIG_HOME_DESCRIPTORS) {
|
||||
// The fallback leg, per the note above: a child that blanks KIMI_SHARE_DIR /
|
||||
// KIMI_CODE_HOME writes to the HOME-derived root instead of the ambient one.
|
||||
// The fallback leg, per the note above: a child that blanks the
|
||||
// descriptor's env var writes to the HOME-derived root instead of the
|
||||
// ambient one.
|
||||
const fallbackDir = resolveConfigHomeFromDescriptor(descriptor, { env: {}, home: homedir() });
|
||||
if (typeof fallbackDir === 'string' && fallbackDir.length > 0) {
|
||||
targets.push(path.resolve(path.join(fallbackDir, NON_REGISTRY_OWNED_FILE)));
|
||||
@@ -405,12 +399,11 @@ function resolveExtraWatchTargets(deps = {}) {
|
||||
// The root belongs to the runtime, so it is never watched wholesale — only
|
||||
// the named file below.
|
||||
//
|
||||
// NAMED RESIDUAL (#2665, found pre-push while rebasing): config.toml is NOT
|
||||
// the only thing MSD writes here. bin/install.js also calls
|
||||
// installSharedHooksBundle(kimiHooksRoot), which populates <root>/hooks/
|
||||
// with MSD's hook scripts and a CommonJS marker. That subtree is UNWATCHED,
|
||||
// so a suite-produced leak of a hook bundle into a developer's real ~/.kimi
|
||||
// or ~/.kimi-code passes this guard silently. Closing it needs a layout
|
||||
// NAMED RESIDUAL (#2665, found pre-push while rebasing): config.toml may
|
||||
// NOT be the only thing MSD writes here — an installer may also populate
|
||||
// <root>/hooks/ with MSD's hook scripts and a CommonJS marker. That
|
||||
// subtree is UNWATCHED, so a suite-produced leak of a hook bundle into a
|
||||
// developer's real non-registry root passes this guard silently. Closing it needs a layout
|
||||
// decision, not one more path: the same reason getGlobalSkillsBase is a
|
||||
// deliberate non-target above. Under-watching fails quiet, like the
|
||||
// NON_REGISTRY_OWNED_FILE residual it sits beside.
|
||||
@@ -507,8 +500,8 @@ function snapshotLiveConfig(roots, extraTargets = [], limits = {}) {
|
||||
|
||||
for (const root of roots) {
|
||||
for (const entry of MSD_OWNED_ENTRIES) record(path.join(root, entry));
|
||||
// Paths whose own name is MSD's, nested inside a shared root (hermes'
|
||||
// `skills/msd`, `hooks/lib`, `scripts/lib`, …) — no prefix rule sees these.
|
||||
// Paths whose own name is MSD's, nested inside a shared root
|
||||
// (`skills/msd`, `hooks/lib`, `scripts/lib`, …) — no prefix rule sees these.
|
||||
for (const entry of watchOwned) record(path.join(root, ...entry.split('/')));
|
||||
|
||||
// Shared dirs: enumerate only msd-prefixed children. A child that appears
|
||||
|
||||
@@ -137,10 +137,6 @@ ALLOWLIST=(
|
||||
# ("ignore previous instructions", "you are now…") as examples agents must
|
||||
# NOT comply with — it is the defense, not an attack vector.
|
||||
'references/untrusted-input-boundary.md'
|
||||
# Security regression tests for input validators — fixtures must contain
|
||||
# real injection payloads to prove the validator rejects them. See
|
||||
# DEFECT.PROMPT-INJECTION-SCAN-COLLISION in CONTEXT.md.
|
||||
'tests/windsurf-conversion.test.cjs'
|
||||
# RuleTester fixtures for the local/no-unguarded-nonportable-exec ESLint rule
|
||||
# contain shell-exec command strings (exec("sh -c …"), execFileSync('bash',['-c',…]))
|
||||
# as test DATA the rule must lint — not attack vectors. ADR-1703 Phase 3 (#1720).
|
||||
@@ -149,12 +145,6 @@ ALLOWLIST=(
|
||||
# exec command strings (execFileSync('npm', ['install'])) as test DATA the rule
|
||||
# must lint — not attack vectors. ADR-1703 Phase 4 (#1726).
|
||||
'tests/no-bare-npm-exec.rule.test.cjs'
|
||||
# #2547 — the Kimi field-shadowing regression proves msd-prompt-guard still
|
||||
# SCANS the reconstructed edit[].new content when a model-supplied new_string
|
||||
# tries to shadow it. The fixture must be a real injection phrase or the test
|
||||
# asserts nothing: it is the payload the guard is required to catch, carried
|
||||
# as test DATA. Same class as the read-injection-scanner suites above.
|
||||
'tests/kimi-payload-field-shadowing.security.test.cjs'
|
||||
# Phase-ID grammar regression tests exercise `RegExp.prototype.exec` via
|
||||
# `re.exec('<phase-id>')` against fixtures like 'MANIFOLD-64-auth' / 'CK-64-auth'.
|
||||
# The scanner's `exec('` code-execution pattern matches that benign method call,
|
||||
|
||||
@@ -295,13 +295,9 @@ function scanWorkflowColonLeak(filePath, cmdNames) {
|
||||
* settings.json, codex into hooks.json and config.toml.
|
||||
*
|
||||
* This is NOT an exhaustive map of where MSD writes launch paths across all
|
||||
* runtimes, and the scan is top-level only by design. Two known surfaces sit
|
||||
* outside it: Cline registers its hook at `.clinerules/hooks/PreToolUse` (a
|
||||
* subdirectory, and not one of these names — see writeClineArtifacts in
|
||||
* src/runtime-hooks-surface.cts), and Kimi's native `[[hooks]]` config.toml
|
||||
* lives under `resolveKimiHooksTomlDir()` (`~/.kimi`), a directory separate
|
||||
* from Kimi's own MSD configDir. Adding either runtime to entrypointRuntimes
|
||||
* requires teaching scanConfiguredEntrypoints about its surface first,
|
||||
* runtimes, and the scan is top-level only by design. A runtime whose hook
|
||||
* surface lives in a subdirectory or outside its MSD configDir must first be
|
||||
* taught to scanConfiguredEntrypoints before being added to entrypointRuntimes,
|
||||
* otherwise the scan reports zero entrypoints and silently proves nothing.
|
||||
*/
|
||||
const RUNTIME_CONFIG_FILES = Object.freeze(['settings.json', 'hooks.json', 'config.toml']);
|
||||
|
||||
Reference in New Issue
Block a user