mirror of
https://github.com/affaan-m/ECC.git
synced 2026-08-17 21:15:40 +02:00
* feat(install): add guided Claude plugin setup * fix: support Claude command shims on Windows * feat: support safe Claude plugin scope migration * fix(install): preserve interactive setup terminal * fix(install): auto-migrate setup scope changes * feat(install): add guided multi-harness installer * fix(install): sync Yarn binary metadata * fix(install): handle wizard EOF on Node 18 * ci: allow installer matrix tests to finish * test(install): allow slower PowerShell delegation * fix(install): harden guided provider reconciliation * test(install): harden packaged and local compatibility * chore: prepare guided installer release 2.2.0 * fix(install): report refreshed Codex marketplace state * fix(install): verify managed content provenance * test(install): allow empty Yarn smoke fixture * test(install): invoke Windows package shims safely * fix(install): close cross-platform release gaps * fix(install): require trusted GitHub origins * fix(install): preserve hook profile precedence * refactor(install): centralize trusted GitHub origins * ci: retrigger workflow run after merge of main Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
175 lines
6.6 KiB
JavaScript
175 lines
6.6 KiB
JavaScript
'use strict';
|
|
|
|
const assert = require('assert');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const repoRoot = path.resolve(__dirname, '..', '..');
|
|
|
|
const configureEccDocs = [
|
|
'skills/configure-ecc/SKILL.md',
|
|
'docs/zh-CN/skills/configure-ecc/SKILL.md',
|
|
'docs/ja-JP/skills/configure-ecc/SKILL.md',
|
|
];
|
|
|
|
const localizedWizardContract = {
|
|
'skills/configure-ecc/SKILL.md': [
|
|
'Ask exactly one scope question',
|
|
'Ask exactly one hook-mode question',
|
|
'Show exactly one confirmation summary',
|
|
],
|
|
'docs/zh-CN/skills/configure-ecc/SKILL.md': [
|
|
'只询问一次安装范围',
|
|
'只询问一次 Hook 模式',
|
|
'只显示一次确认摘要',
|
|
],
|
|
'docs/ja-JP/skills/configure-ecc/SKILL.md': [
|
|
'スコープについて 1 回だけ質問',
|
|
'フックモードについて 1 回だけ質問',
|
|
'確認サマリーは 1 回だけ表示',
|
|
],
|
|
};
|
|
|
|
let passed = 0;
|
|
let failed = 0;
|
|
|
|
function test(name, fn) {
|
|
try {
|
|
fn();
|
|
console.log(` ✓ ${name}`);
|
|
passed++;
|
|
} catch (error) {
|
|
console.log(` ✗ ${name}`);
|
|
console.log(` Error: ${error.message}`);
|
|
failed++;
|
|
}
|
|
}
|
|
|
|
function readConfigureEccDoc(relativePath) {
|
|
return fs.readFileSync(path.join(repoRoot, relativePath), 'utf8');
|
|
}
|
|
|
|
function countEntries(relativePath, predicate) {
|
|
return fs.readdirSync(path.join(repoRoot, relativePath), { withFileTypes: true })
|
|
.filter(predicate)
|
|
.length;
|
|
}
|
|
|
|
console.log('\n=== Testing configure-ecc install path guidance ===\n');
|
|
|
|
for (const relativePath of configureEccDocs) {
|
|
test(`${relativePath} delegates to guided plugin setup`, () => {
|
|
const content = readConfigureEccDoc(relativePath);
|
|
|
|
assert.ok(content.includes('ecc setup'));
|
|
assert.ok(content.includes('npx ecc-universal setup'));
|
|
assert.ok(content.includes('--mode claude-plugin'));
|
|
assert.ok(content.includes('--scope <scope>'));
|
|
assert.ok(content.includes('--hooks <hooks>'));
|
|
assert.ok(content.includes('--move-scope'));
|
|
assert.ok(!content.includes('rm -rf /tmp/everything-claude-code'));
|
|
assert.ok(!content.includes('cp -R "$ECC_ROOT'));
|
|
});
|
|
|
|
test(`${relativePath} defines the Claude in-harness wizard contract`, () => {
|
|
const content = readConfigureEccDoc(relativePath);
|
|
|
|
for (const instruction of localizedWizardContract[relativePath]) {
|
|
assert.ok(content.includes(instruction), `missing: ${instruction}`);
|
|
}
|
|
assert.ok(content.includes('claude plugin list --json'));
|
|
assert.ok(content.includes('user | project | local'));
|
|
assert.ok(content.includes('off | minimal | standard | strict'));
|
|
assert.ok(content.includes('$CLAUDE_PLUGIN_ROOT'));
|
|
assert.ok(content.includes('scripts/setup.js'));
|
|
assert.ok(content.includes('--yes --json'));
|
|
assert.ok(content.includes('<installed-version>'));
|
|
assert.ok(content.includes('ECC_VERSION_PATTERN'));
|
|
assert.ok(content.includes('argument array'));
|
|
});
|
|
|
|
test(`${relativePath} verifies before showing the welcome`, () => {
|
|
const content = readConfigureEccDoc(relativePath);
|
|
const applyIndex = content.indexOf('--yes --json');
|
|
const verificationIndex = content.indexOf('claude plugin list --json', applyIndex);
|
|
const welcomeIndex = content.indexOf('renderTerminalWelcome');
|
|
|
|
assert.ok(applyIndex > -1, 'missing non-interactive apply command');
|
|
assert.ok(verificationIndex > -1, 'missing post-setup plugin verification');
|
|
assert.ok(welcomeIndex > verificationIndex, 'welcome must follow verification');
|
|
});
|
|
|
|
test(`${relativePath} keeps provider capabilities truthful`, () => {
|
|
const content = readConfigureEccDoc(relativePath);
|
|
|
|
assert.ok(content.includes('codex plugin add ecc@ecc --json'));
|
|
assert.ok(content.includes('Codex'));
|
|
assert.ok(content.includes('.kimi-code'));
|
|
assert.ok(content.includes('--target kimi'));
|
|
assert.ok(content.includes('hooks=unsupported'));
|
|
});
|
|
|
|
test(`${relativePath} verifies Codex and Kimi before their concrete welcomes`, () => {
|
|
const content = readConfigureEccDoc(relativePath);
|
|
const codexVerifyIndex = content.indexOf('codex plugin list --json');
|
|
const codexWelcomeIndex = content.indexOf(
|
|
'["<installedPath>/scripts/welcome.js", "--action", "configured", "--version", "<installed-version>"]'
|
|
);
|
|
const kimiVerifyIndex = content.indexOf('ecc doctor --target kimi');
|
|
const kimiWelcomeIndex = content.indexOf('ecc welcome --action configured');
|
|
|
|
assert.ok(codexVerifyIndex > -1, 'missing Codex verification');
|
|
assert.ok(codexWelcomeIndex > codexVerifyIndex, 'Codex welcome must follow verification');
|
|
assert.ok(
|
|
content.includes('argument array'),
|
|
'Codex welcome must use an executable plus argument array'
|
|
);
|
|
assert.ok(
|
|
!content.includes('node "<installedPath>/scripts/welcome.js"'),
|
|
'Codex JSON values must not be shown in a shell command'
|
|
);
|
|
assert.ok(kimiVerifyIndex > -1, 'missing Kimi verification');
|
|
assert.ok(kimiWelcomeIndex > kimiVerifyIndex, 'Kimi welcome must follow verification');
|
|
});
|
|
}
|
|
|
|
test('Codex legacy sync docs do not require an unrelated package install', () => {
|
|
const content = readConfigureEccDoc('.codex-plugin/README.md');
|
|
|
|
assert.ok(content.includes('bash scripts/sync-ecc-to-codex.sh'));
|
|
assert.ok(!content.includes('npm install && bash scripts/sync-ecc-to-codex.sh'));
|
|
});
|
|
|
|
test('Kimi docs scope hooks and compatibility to the verified adapter', () => {
|
|
const content = readConfigureEccDoc('.kimi/README.md');
|
|
|
|
assert.ok(content.includes('verified against Kimi Code 0.31.x'));
|
|
assert.ok(content.includes("newer provider releases are outside this adapter's verified range"));
|
|
assert.ok(content.includes('does not configure or map provider lifecycle hooks'));
|
|
assert.ok(!content.includes('Kimi Code 0.31.x does not expose'));
|
|
});
|
|
|
|
test('Turkish agent instructions report the live catalog counts', () => {
|
|
const content = readConfigureEccDoc('docs/tr/AGENTS.md');
|
|
const agentCount = countEntries('agents', entry => entry.isFile() && entry.name.endsWith('.md'));
|
|
const skillCount = countEntries('skills', entry => entry.isDirectory());
|
|
const commandCount = countEntries(
|
|
'commands',
|
|
entry => entry.isFile() && entry.name.endsWith('.md')
|
|
);
|
|
|
|
assert.ok(content.includes(`${agentCount} özel agent`));
|
|
assert.ok(content.includes(`${skillCount} skill`));
|
|
assert.ok(content.includes(`${commandCount} command`));
|
|
assert.ok(content.includes(`agents/ — ${agentCount} özel subagent`));
|
|
assert.ok(content.includes(`skills/ — ${skillCount} iş akışı`));
|
|
assert.ok(content.includes(`commands/ — ${commandCount} slash command`));
|
|
});
|
|
|
|
if (failed > 0) {
|
|
console.log(`\nFailed: ${failed}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log(`\nPassed: ${passed}`);
|