fix: close Kimi runtime review gaps

This commit is contained in:
Viktorplus
2026-06-07 23:40:18 +02:00
parent bb29adce4d
commit 7f868dcc6b
8 changed files with 111 additions and 5 deletions

View File

@@ -10,12 +10,17 @@
const { test, describe, beforeEach, afterEach } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const os = require('os');
const path = require('path');
const { spawnSync } = require('node:child_process');
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
const { installerEnv } = require('./helpers/install-shared.cjs');
const AGENTS_DIR_NAME = 'agents';
const MODEL_PROFILES = require('../gsd-core/bin/lib/model-profiles.cjs').MODEL_PROFILES;
const EXPECTED_AGENTS = Object.keys(MODEL_PROFILES);
const ROOT = path.join(__dirname, '..');
const INSTALL_SCRIPT = path.join(ROOT, 'bin', 'install.js');
/**
* Create a fake GSD install directory structure that mirrors what the installer
@@ -276,6 +281,76 @@ describe('checkAgentsInstalled: Copilot .agent.md format (#1512)', () => {
});
});
// ─── Kimi agents/subagents detection (#743 review) ─────────────────────────
describe('checkAgentsInstalled: Kimi agents/subagents layout', () => {
test('Kimi install is detected by init, validate agents, and health checks', () => {
const tmpDir = createTempProject('gsd-kimi-agent-status-project-');
const tmpConfig = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-kimi-agent-status-config-'));
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-kimi-agent-status-home-'));
const env = installerEnv({
HOME: tmpHome,
USERPROFILE: tmpHome,
KIMI_CONFIG_DIR: tmpConfig,
});
try {
const installResult = spawnSync(
process.execPath,
[INSTALL_SCRIPT, '--kimi', '--global', '--config-dir', tmpConfig, '--no-sdk'],
{
cwd: tmpDir,
encoding: 'utf8',
env,
},
);
assert.strictEqual(
installResult.status,
0,
`Kimi install failed\nstdout: ${installResult.stdout}\nstderr: ${installResult.stderr}`,
);
fs.writeFileSync(
path.join(tmpDir, '.planning', 'config.json'),
JSON.stringify({ runtime: 'kimi', model_profile: 'balanced' }, null, 2),
);
const initResult = runGsdTools('init new-workspace --raw', tmpDir, env);
assert.ok(initResult.success, `init failed: ${initResult.error}`);
const initOutput = JSON.parse(initResult.output);
assert.strictEqual(initOutput.agent_runtime, 'kimi');
assert.strictEqual(initOutput.agents_dir, path.join(tmpConfig, 'agents'));
assert.strictEqual(initOutput.agents_installed, true);
assert.deepStrictEqual(initOutput.missing_agents, []);
const validateResult = runGsdTools('validate agents --raw', tmpDir, {
...env,
GSD_RUNTIME: 'kimi',
});
assert.ok(validateResult.success, `validate agents failed: ${validateResult.error}`);
const validateOutput = JSON.parse(validateResult.output);
assert.strictEqual(validateOutput.agents_dir, path.join(tmpConfig, 'agents'));
assert.strictEqual(validateOutput.agents_found, true);
assert.deepStrictEqual(validateOutput.missing, []);
const healthResult = runGsdTools('validate health --raw', tmpDir, {
...env,
GSD_RUNTIME: 'kimi',
});
assert.ok(healthResult.success, `validate health failed: ${healthResult.error}`);
const healthOutput = JSON.parse(healthResult.output);
const agentWarnings = (healthOutput.warnings || []).filter(
(warning) => warning.code === 'W010' || /GSD agents/i.test(warning.message || ''),
);
assert.deepStrictEqual(agentWarnings, []);
} finally {
cleanup(tmpDir);
cleanup(tmpConfig);
cleanup(tmpHome);
}
});
});
// ─── validate agents subcommand ─────────────────────────────────────────────
describe('validate agents subcommand (#1371)', () => {

View File

@@ -334,7 +334,10 @@ describe('Kimi local install guard', () => {
0,
`second install failed\nstdout: ${second.stdout}\nstderr: ${second.stderr}`,
);
assert.match(`${second.stdout}\n${second.stderr}`, /locally modified GSD file/i);
const secondOutput = `${second.stdout}\n${second.stderr}`;
assert.match(secondOutput, /locally modified GSD file/i);
assert.match(secondOutput, /\/skill:gsd-update --reapply/);
assert.doesNotMatch(secondOutput, /Run\s+\/gsd-update --reapply/);
const skillBackup = path.join(tmpConfig, 'gsd-local-patches', 'skills', 'gsd-new-project', 'SKILL.md');
const agentBackup = path.join(tmpConfig, 'gsd-local-patches', 'agents', 'subagents', 'gsd-executor.md');

View File

@@ -9,6 +9,7 @@ const fs = require('node:fs');
const path = require('node:path');
const { catalog, KNOWN_RUNTIMES } = require('../gsd-core/bin/lib/model-catalog.cjs');
const { allRuntimes } = require('../bin/install.js');
const ROOT = path.join(__dirname, '..');
const SETTINGS_ADVANCED = fs.readFileSync(path.join(ROOT, 'gsd-core', 'workflows', 'settings-advanced.md'), 'utf8');
@@ -17,9 +18,14 @@ const CONFIG_DOC = fs.readFileSync(path.join(ROOT, 'docs', 'CONFIGURATION.md'),
describe('model catalog runtime defaults parity (#3229)', () => {
test('known runtimes include hermes and match catalog keys', () => {
assert.ok(KNOWN_RUNTIMES.has('hermes'));
assert.ok(KNOWN_RUNTIMES.has('kimi'));
assert.deepStrictEqual([...KNOWN_RUNTIMES].sort(), Object.keys(catalog.runtimeTierDefaults).sort());
});
test('installer-supported runtimes are all known to the model catalog', () => {
assert.deepStrictEqual([...allRuntimes].sort(), [...KNOWN_RUNTIMES].sort());
});
test('settings-advanced runtime defaults table matches catalog for concrete runtimes', () => {
for (const [runtime, tiers] of Object.entries(catalog.runtimeTierDefaults)) {
if (!tiers.opus) continue; // Group B runtimes intentionally have no built-ins