test(02-02): add failing Kimi global skill install tests
- Expect Kimi global layout to stage skills/gsd-*/SKILL.md - Assert --kimi --global writes gsd-new-project SKILL.md - Preserve explicit --kimi --local no-op guard
This commit is contained in:
@@ -94,11 +94,15 @@ describe('Kimi runtime homes', () => {
|
||||
});
|
||||
|
||||
describe('Kimi runtime artifact layout', () => {
|
||||
test('known runtime with empty Phase 1 layout placeholder', () => {
|
||||
test('global layout stages Kimi skills while local layout remains guarded', () => {
|
||||
const globalLayout = resolveRuntimeArtifactLayout('kimi', '/tmp/kimi-config', 'global');
|
||||
assert.strictEqual(globalLayout.runtime, 'kimi');
|
||||
assert.strictEqual(globalLayout.configDir, '/tmp/kimi-config');
|
||||
assert.deepStrictEqual(globalLayout.kinds, []);
|
||||
assert.strictEqual(globalLayout.kinds.length, 1);
|
||||
assert.strictEqual(globalLayout.kinds[0].kind, 'skills');
|
||||
assert.strictEqual(globalLayout.kinds[0].destSubpath, 'skills');
|
||||
assert.strictEqual(globalLayout.kinds[0].prefix, 'gsd-');
|
||||
assert.strictEqual(typeof globalLayout.kinds[0].stage, 'function');
|
||||
|
||||
const localLayout = resolveRuntimeArtifactLayout('kimi', '/tmp/kimi-config', 'local');
|
||||
assert.strictEqual(localLayout.runtime, 'kimi');
|
||||
@@ -139,7 +143,7 @@ describe('Kimi local install guard', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('--kimi --global exits successfully without writing unconverted Kimi artifacts', () => {
|
||||
test('--kimi --global writes converted Kimi skills only', () => {
|
||||
const tmpProject = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-kimi-global-project-'));
|
||||
const tmpConfig = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-kimi-global-config-'));
|
||||
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-kimi-global-home-'));
|
||||
@@ -157,14 +161,20 @@ describe('Kimi local install guard', () => {
|
||||
assert.strictEqual(
|
||||
result.status,
|
||||
0,
|
||||
`expected --kimi --global skeleton guard to no-op successfully\nstdout: ${result.stdout}\nstderr: ${result.stderr}`,
|
||||
`expected --kimi --global to install Kimi skills successfully\nstdout: ${result.stdout}\nstderr: ${result.stderr}`,
|
||||
);
|
||||
const combined = `${result.stdout}\n${result.stderr}`;
|
||||
assert.match(combined, /Kimi global install/i);
|
||||
assert.match(combined, /deferred/i);
|
||||
assert.match(combined, /Installing for .*Kimi/i);
|
||||
assert.match(combined, /Installed \d+ skills to skills\//i);
|
||||
|
||||
assert.ok(!fs.existsSync(path.join(tmpConfig, 'skills')), 'must not write unconverted Kimi skills');
|
||||
assert.ok(!fs.existsSync(path.join(tmpConfig, 'agents')), 'must not write unconverted Kimi agents');
|
||||
const skillFile = path.join(tmpConfig, 'skills', 'gsd-new-project', 'SKILL.md');
|
||||
assert.ok(fs.existsSync(skillFile), 'must write gsd-new-project/SKILL.md');
|
||||
const skillContent = fs.readFileSync(skillFile, 'utf8');
|
||||
assert.match(skillContent, /^name: gsd-new-project$/m);
|
||||
assert.match(skillContent, /\/skill:gsd-new-project/);
|
||||
assert.doesNotMatch(skillContent, /kimi_cli\.tools|system_prompt_path|^version: 1$/m);
|
||||
|
||||
assert.ok(!fs.existsSync(path.join(tmpConfig, 'agents')), 'must not write Kimi agents');
|
||||
assert.ok(!fs.existsSync(path.join(tmpConfig, 'gsd-core')), 'must not write workflow payloads as Kimi artifacts');
|
||||
assert.ok(!fs.existsSync(path.join(tmpConfig, 'hooks')), 'must not write hooks under the Kimi root');
|
||||
} finally {
|
||||
|
||||
@@ -49,13 +49,13 @@ const RESOLVED_CORE = resolveProfile({ modes: ['core'], manifest: MANIFEST });
|
||||
|
||||
const SKILLS_RUNTIMES_LAYOUT = [
|
||||
'claude', 'cursor', 'codex', 'copilot', 'antigravity',
|
||||
'windsurf', 'augment', 'trae', 'qwen', 'codebuddy',
|
||||
'windsurf', 'augment', 'trae', 'qwen', 'kimi', 'codebuddy',
|
||||
];
|
||||
|
||||
const ALL_RUNTIMES_LAYOUT = [
|
||||
'claude', 'cursor', 'gemini', 'codex', 'copilot', 'antigravity',
|
||||
'windsurf', 'augment', 'trae', 'qwen', 'hermes', 'codebuddy',
|
||||
'cline', 'opencode', 'kilo',
|
||||
'cline', 'kimi', 'opencode', 'kilo',
|
||||
];
|
||||
|
||||
function countPrefixedEntries(destDir, prefix) {
|
||||
@@ -94,6 +94,15 @@ describe('installRuntimeArtifacts — skills runtimes write gsd-prefixed skill d
|
||||
`${runtime}: ${skillsKind.prefix}help/SKILL.md must exist`
|
||||
);
|
||||
|
||||
if (runtime === 'kimi') {
|
||||
const newProjectSkill = path.join(destDir, 'gsd-new-project', 'SKILL.md');
|
||||
assert.ok(fs.existsSync(newProjectSkill), 'kimi: gsd-new-project/SKILL.md must exist');
|
||||
const content = fs.readFileSync(newProjectSkill, 'utf8');
|
||||
assert.match(content, /^name: gsd-new-project$/m);
|
||||
assert.match(content, /\/skill:gsd-new-project/);
|
||||
assert.doesNotMatch(content, /kimi_cli\.tools|system_prompt_path|^version: 1$/m);
|
||||
}
|
||||
|
||||
if (RESOLVED_CORE.skills !== '*') {
|
||||
const prefixedCount = countPrefixedEntries(destDir, skillsKind.prefix || 'gsd-');
|
||||
assert.strictEqual(prefixedCount, RESOLVED_CORE.skills.size,
|
||||
|
||||
@@ -175,6 +175,24 @@ describe('resolveRuntimeArtifactLayout — qwen', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveRuntimeArtifactLayout — kimi', () => {
|
||||
test('returns global skills layout and guarded empty local layout for kimi', () => {
|
||||
const globalLayout = resolveRuntimeArtifactLayout('kimi', FAKE_DIR, 'global');
|
||||
assert.strictEqual(globalLayout.runtime, 'kimi');
|
||||
assert.strictEqual(globalLayout.configDir, FAKE_DIR);
|
||||
assert.strictEqual(globalLayout.kinds.length, 1);
|
||||
assert.strictEqual(globalLayout.kinds[0].kind, 'skills');
|
||||
assert.strictEqual(globalLayout.kinds[0].destSubpath, 'skills');
|
||||
assert.strictEqual(globalLayout.kinds[0].prefix, 'gsd-');
|
||||
assert.strictEqual(typeof globalLayout.kinds[0].stage, 'function');
|
||||
|
||||
const localLayout = resolveRuntimeArtifactLayout('kimi', FAKE_DIR, 'local');
|
||||
assert.strictEqual(localLayout.runtime, 'kimi');
|
||||
assert.strictEqual(localLayout.configDir, FAKE_DIR);
|
||||
assert.deepStrictEqual(localLayout.kinds, []);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveRuntimeArtifactLayout — hermes', () => {
|
||||
test('returns correct layout for hermes', () => {
|
||||
const layout = resolveRuntimeArtifactLayout('hermes', FAKE_DIR);
|
||||
@@ -379,6 +397,23 @@ describe('stage — skills kind (claude global)', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('stage — skills kind (kimi global)', () => {
|
||||
test('stage returns Kimi SKILL.md dirs with /skill:gsd-* invocations', () => {
|
||||
const layout = resolveRuntimeArtifactLayout('kimi', FAKE_STAGE_DIR, 'global');
|
||||
const skillsKind = layout.kinds.find(k => k.kind === 'skills');
|
||||
assert.ok(skillsKind, 'should have a skills kind');
|
||||
|
||||
const stagedDir = skillsKind.stage(PROFILE_CORE);
|
||||
assert.ok(fs.existsSync(stagedDir), 'stagedDir must exist');
|
||||
const skillMd = path.join(stagedDir, 'gsd-new-project', 'SKILL.md');
|
||||
assert.ok(fs.existsSync(skillMd), 'gsd-new-project/SKILL.md must exist');
|
||||
const content = fs.readFileSync(skillMd, 'utf8');
|
||||
assert.match(content, /^name: gsd-new-project$/m);
|
||||
assert.match(content, /\/skill:gsd-new-project/);
|
||||
assert.doesNotMatch(content, /kimi_cli\.tools|system_prompt_path|^version: 1$/m);
|
||||
});
|
||||
});
|
||||
|
||||
describe('stage — opencode commands kind', () => {
|
||||
test('opencode stage returns directory with .md files for selected skills', () => {
|
||||
const layout = resolveRuntimeArtifactLayout('opencode', FAKE_STAGE_DIR);
|
||||
|
||||
Reference in New Issue
Block a user