mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-20 10:06:54 +03:00
Fix: install missing explicitly selected providers without --force (#536)
* Fix: install missing explicitly selected providers without --force (#500) An explicit --providers list now treats "already installed" per selected target: providers with an existing install take the update path, providers with none get a fresh install (skills + hooks) in the same run. Previously any existing install (e.g. .claude) made `install --providers=grok` exit 0 without writing .grok, leaving Grok Build on the Claude-variant fallback. Written with AI assistance (Cursor agent), reviewed and tested by maintainer. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix: copy provider agents for freshly installed mixed-install targets Bugbot caught that the mixed explicit-providers path installed skills and hooks for missing targets but skipped copyProviderAgents, which both the update and fresh-install paths run. Written with AI assistance (Cursor agent). Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1446,6 +1446,57 @@ describe('skills install/update: local universal bundle e2e', () => {
|
||||
rmSync(tmp, { recursive: true, force: true });
|
||||
}, 15000);
|
||||
|
||||
test('explicit --providers installs a missing provider without --force (#500)', () => {
|
||||
const tmp = mkdtempSync(join(tmpdir(), 'imp-test-explicit-missing-'));
|
||||
execSync('git init', { cwd: tmp });
|
||||
const bundleRoot = createFakeUniversalBundle(tmp, ['.claude', '.cursor']);
|
||||
|
||||
// Seed an existing .claude install; .cursor has nothing yet.
|
||||
const skillDir = join(tmp, '.claude', 'skills', 'impeccable');
|
||||
mkdirSync(skillDir, { recursive: true });
|
||||
writeFileSync(join(skillDir, 'SKILL.md'), '---\nname: impeccable\nversion: 9.9.9-local\n---\nSeeded install.\n');
|
||||
|
||||
const output = run('skills install -y --providers=cursor --no-hooks', {
|
||||
cwd: tmp,
|
||||
env: { ...process.env, IMPECCABLE_BUNDLE_PATH: bundleRoot },
|
||||
});
|
||||
|
||||
expect(output).toContain('Installed impeccable into: .cursor');
|
||||
expect(readFileSync(join(tmp, '.cursor', 'skills', 'impeccable', 'SKILL.md'), 'utf8')).toContain('version: 9.9.9-local');
|
||||
// The unselected .claude install is left alone.
|
||||
expect(readFileSync(join(skillDir, 'SKILL.md'), 'utf8')).toContain('Seeded install.');
|
||||
|
||||
rmSync(tmp, { recursive: true, force: true });
|
||||
}, 15000);
|
||||
|
||||
test('explicit --providers mixes per-target updates and fresh installs (#500)', () => {
|
||||
const tmp = mkdtempSync(join(tmpdir(), 'imp-test-explicit-mixed-'));
|
||||
execSync('git init', { cwd: tmp });
|
||||
const bundleRoot = createFakeUniversalBundle(tmp, ['.claude', '.cursor']);
|
||||
|
||||
// Stale .claude install; .cursor has nothing yet.
|
||||
const skillDir = join(tmp, '.claude', 'skills', 'impeccable');
|
||||
mkdirSync(join(skillDir, 'scripts'), { recursive: true });
|
||||
writeFileSync(join(skillDir, 'SKILL.md'), '---\nname: impeccable\nstale: .claude\n---\nOld content.\n');
|
||||
writeFileSync(join(skillDir, 'scripts', 'context.mjs'), 'console.log("old script");\n');
|
||||
|
||||
const output = run('skills install -y --providers=claude,cursor', {
|
||||
cwd: tmp,
|
||||
env: { ...process.env, IMPECCABLE_BUNDLE_PATH: bundleRoot },
|
||||
});
|
||||
|
||||
expect(output).toContain('already installed');
|
||||
expect(output).toContain('Updated');
|
||||
expect(output).toContain('Installed impeccable into: .cursor');
|
||||
expect(readFileSync(join(skillDir, 'SKILL.md'), 'utf8')).toContain('version: 9.9.9-local');
|
||||
expect(readFileSync(join(tmp, '.cursor', 'skills', 'impeccable', 'SKILL.md'), 'utf8')).toContain('version: 9.9.9-local');
|
||||
// The freshly installed provider gets its hooks and agents too.
|
||||
expect(existsSync(join(tmp, '.cursor', 'hooks.json'))).toBe(true);
|
||||
expect(existsSync(join(tmp, '.cursor', 'agents', 'impeccable-finish-reviewer.md'))).toBe(true);
|
||||
|
||||
rmSync(tmp, { recursive: true, force: true });
|
||||
}, 15000);
|
||||
|
||||
test('skills update --no-hooks refreshes skills without touching malformed hook manifests', () => {
|
||||
const tmp = mkdtempSync(join(tmpdir(), 'imp-test-update-no-hooks-'));
|
||||
execSync('git init', { cwd: tmp });
|
||||
|
||||
Reference in New Issue
Block a user