Compare commits
6
Commits
c2bf5888d0
...
0108257290
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0108257290 | ||
|
|
e28a4171ea | ||
|
|
d7304e2b64 | ||
|
|
70414def48 | ||
|
|
cb6cebb3d4 | ||
|
|
9e74b9be4a |
No files matched your search
@@ -172,6 +172,7 @@ jobs:
|
||||
- name: Run retrieval tests with real models
|
||||
env:
|
||||
QWEN_TEST_MODEL_CACHE: ${{ github.workspace }}/.cache/huggingface
|
||||
HF_HUB_DISABLE_XET: '1'
|
||||
run: python scripts/tests/test-retrieval-server.py
|
||||
|
||||
security:
|
||||
|
||||
@@ -172,6 +172,7 @@ jobs:
|
||||
- name: Run retrieval tests with real models
|
||||
env:
|
||||
QWEN_TEST_MODEL_CACHE: ${{ github.workspace }}/.cache/huggingface
|
||||
HF_HUB_DISABLE_XET: '1'
|
||||
run: python scripts/tests/test-retrieval-server.py
|
||||
|
||||
security:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
name claude_marketplace claude_plugin codex_marketplace codex_plugin codex_method codex_source codex_skill
|
||||
name claude_marketplace claude_plugin codex_marketplace codex_plugin codex_method codex_source codex_skill mcporter_name mcporter_url
|
||||
Ponytail DietrichGebert/ponytail ponytail@ponytail DietrichGebert/ponytail ponytail@ponytail plugin - -
|
||||
i-have-adhd ayghri/i-have-adhd i-have-adhd@i-have-adhd ayghri/i-have-adhd i-have-adhd@i-have-adhd plugin - -
|
||||
Superpowers - superpowers@claude-plugins-official openai-curated-remote superpowers@openai-curated-remote plugin - -
|
||||
Context7 upstash/context7 context7@context7-marketplace upstash/context7 context7@context7-marketplace plugin - -
|
||||
Context7 upstash/context7 context7@context7-marketplace upstash/context7 context7@context7-marketplace plugin - - context7 https://mcp.context7.com/mcp
|
||||
Caveman JuliusBrussee/caveman caveman@caveman thinkhome-org/caveman-codex caveman@thinkhome-caveman plugin - -
|
||||
QMD tobi/qmd qmd@qmd - - qmd @tobilu/qmd qmd
|
||||
MCPorter - mcporter - mcporter cli mcporter -
|
||||
|
||||
|
@@ -13,8 +13,9 @@ Require-Command jq
|
||||
$pythonVersion = $null
|
||||
foreach ($python in @('python', 'python3')) {
|
||||
if (-not (Get-Command $python -ErrorAction SilentlyContinue)) { continue }
|
||||
$candidate = (& $python --version 2>$null | Select-Object -First 1) -replace '^Python\s+', ''
|
||||
if ($LASTEXITCODE -eq 0) { $pythonVersion = $candidate; break }
|
||||
$candidate = & $python --version 2>$null
|
||||
$pythonExitCode = $LASTEXITCODE
|
||||
if ($pythonExitCode -eq 0) { $pythonVersion = $candidate -replace '^Python\s+', ''; break }
|
||||
}
|
||||
if ($null -ne $pythonVersion) { Check-Version 'Python' $pythonVersion ([version]'3.11') } else { Fail 'Python 3.11 or newer is required' }
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ class Manager:
|
||||
raise ValueError('Invalid extension ownership ledger.')
|
||||
identities = set()
|
||||
for resource in self.state['resources']:
|
||||
if not isinstance(resource, dict) or resource.get('client') not in ('codex', 'claude') or resource.get('kind') not in ('plugin', 'skill', 'qmd', 'cli'):
|
||||
if not isinstance(resource, dict) or resource.get('client') not in ('codex', 'claude', 'shared') or resource.get('kind') not in ('plugin', 'skill', 'qmd', 'cli', 'mcp'):
|
||||
raise ValueError('Invalid extension ownership record.')
|
||||
if not isinstance(resource.get('name'), str) or not resource['name']:
|
||||
raise ValueError('Invalid extension ownership name.')
|
||||
@@ -196,6 +196,8 @@ class Manager:
|
||||
raise ValueError('Invalid owned QMD record.')
|
||||
elif resource['kind'] == 'cli' and not re.fullmatch(r'[a-z0-9@/_.-]+', resource.get('package', '')):
|
||||
raise ValueError('Invalid owned CLI record.')
|
||||
elif resource['kind'] == 'mcp' and (resource['client'] != 'shared' or not re.fullmatch(r'[a-z0-9-]+', resource.get('server', '')) or not re.fullmatch(r'https://[^\s]+', resource.get('url', ''))):
|
||||
raise ValueError('Invalid owned MCP record.')
|
||||
|
||||
def save(self):
|
||||
if not self.dry_run:
|
||||
@@ -258,6 +260,8 @@ class Manager:
|
||||
pass
|
||||
if record['files']:
|
||||
return
|
||||
elif record['kind'] == 'mcp':
|
||||
self.command(['mcporter', '--config', str(self.home / '.mcporter/mcporter.json'), 'config', 'remove', record['server']])
|
||||
else:
|
||||
package = record['package']
|
||||
self.command(['npm', 'uninstall', '--global', package])
|
||||
@@ -361,6 +365,20 @@ class Manager:
|
||||
self.state['resources'].append(dict(client='codex', name=entry['name'], kind='qmd', package=package))
|
||||
self.save()
|
||||
|
||||
def ensure_mcporter(self, entry):
|
||||
server, url = entry['mcporter_name'], entry['mcporter_url']
|
||||
if not re.fullmatch(r'[a-z0-9-]+', server) or not re.fullmatch(r'https://[^\s]+', url):
|
||||
raise ValueError('Invalid MCPorter server definition.')
|
||||
record = next((r for r in self.state['resources'] if r['client'] == 'shared' and r['name'] == entry['name']), None)
|
||||
if record is not None and not self.update:
|
||||
return
|
||||
self.command(['mcporter', '--config', str(self.home / '.mcporter/mcporter.json'), 'config', 'add', server, url])
|
||||
if record is None:
|
||||
self.state['resources'].append(dict(client='shared', name=entry['name'], kind='mcp', server=server, url=url))
|
||||
else:
|
||||
record.update(server=server, url=url)
|
||||
self.save()
|
||||
|
||||
def ensure_skill(self, entry, record):
|
||||
skill = entry['codex_skill']
|
||||
if not re.fullmatch(r'[A-Za-z0-9_-]+', skill):
|
||||
@@ -417,15 +435,21 @@ class Manager:
|
||||
|
||||
def sync(self, entries, clients, selected, action='sync'):
|
||||
desired = {entry['name']: entry for entry in entries if entry['name'] in selected}
|
||||
use_mcporter = 'MCPorter' in desired
|
||||
mcporter_entries = {name: entry for name, entry in desired.items() if use_mcporter and entry.get('mcporter_name') and entry.get('mcporter_url') not in ('', '-')}
|
||||
direct_entries = {name: entry for name, entry in desired.items() if name not in mcporter_entries}
|
||||
for record in list(self.state['resources']):
|
||||
if record['client'] not in clients:
|
||||
if record['client'] != 'shared' and record['client'] not in clients:
|
||||
continue
|
||||
if (action == 'sync' and record['name'] not in desired) or (action == 'remove' and record['name'] in selected):
|
||||
selected_entries = mcporter_entries if record['client'] == 'shared' else direct_entries
|
||||
if (action == 'sync' and record['name'] not in selected_entries) or (action == 'remove' and record['name'] in selected):
|
||||
self.remove(record)
|
||||
if action != 'remove':
|
||||
for client in clients:
|
||||
for entry in desired.values():
|
||||
for entry in direct_entries.values():
|
||||
self.ensure(client, entry)
|
||||
for entry in mcporter_entries.values():
|
||||
self.ensure_mcporter(entry)
|
||||
print('EXTENSIONS: reconciliation complete' + (' (dry run)' if self.dry_run else ''))
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
[CmdletBinding()]
|
||||
param([switch]$Summary)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$root = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
|
||||
$content = Get-Content (Join-Path $root 'scripts/commands/check-capabilities.ps1') -Raw
|
||||
|
||||
if ($content -match '(?s)\(& \$python --version.*?Select-Object.*?\).*?\$LASTEXITCODE') {
|
||||
throw 'Python capability detection must not read $LASTEXITCODE after a pipeline.'
|
||||
}
|
||||
|
||||
if ($Summary) { Write-Output 'Tests: PASS | capabilities' } else { Write-Output 'PASS capabilities: Python exit code is read without a pipeline' }
|
||||
@@ -23,6 +23,16 @@ def skill():
|
||||
return dict(name='Humanizer', codex_method='skill', codex_source='blader/humanizer', codex_skill='humanizer', codex_plugin='-')
|
||||
|
||||
|
||||
def mcporter():
|
||||
return dict(name='MCPorter', codex_method='cli', codex_source='mcporter', codex_plugin='-', codex_marketplace='-', claude_plugin='-', claude_marketplace='-')
|
||||
|
||||
|
||||
def mcp():
|
||||
return dict(name='Context7', codex_method='plugin', codex_plugin='context7@test', codex_marketplace='-',
|
||||
claude_plugin='context7@test', claude_marketplace='-', mcporter_name='context7',
|
||||
mcporter_url='https://mcp.context7.com/mcp')
|
||||
|
||||
|
||||
class ManagedExtensionTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.temporary = tempfile.TemporaryDirectory()
|
||||
@@ -186,6 +196,12 @@ class ManagedExtensionTests(unittest.TestCase):
|
||||
self.assertIn(['claude', 'plugin', 'install', 'qmd@qmd', '--scope', 'user'], self.commands)
|
||||
self.assertNotIn(['codex', 'mcp', 'add', 'qmd', '--', 'qmd', 'mcp'], self.commands)
|
||||
|
||||
def test_mcporter_replaces_direct_context7_plugin(self):
|
||||
self.manager().sync([mcp(), mcporter()], ['codex'], {'Context7', 'MCPorter'})
|
||||
config = str(self.home / '.mcporter/mcporter.json')
|
||||
self.assertIn(['mcporter', '--config', config, 'config', 'add', 'context7', 'https://mcp.context7.com/mcp'], self.commands)
|
||||
self.assertNotIn(['codex', 'plugin', 'add', 'context7@test'], self.commands)
|
||||
|
||||
def test_foreign_empty_directory_survives_skill_removal(self):
|
||||
self.manager().sync([skill()], ['codex'], {'Humanizer'})
|
||||
foreign = self.home / '.agents/skills/humanizer/foreign-empty'
|
||||
|
||||
@@ -27,6 +27,11 @@ def embedding():
|
||||
|
||||
|
||||
class RetrievalServerTests(unittest.TestCase):
|
||||
def test_ci_disables_xet_for_model_downloads(self):
|
||||
for workflow in ('.github/workflows/ci.yml', '.gitea/workflows/ci.yml'):
|
||||
content = (ROOT / workflow).read_text(encoding='utf-8')
|
||||
self.assertIn("HF_HUB_DISABLE_XET: '1'", content)
|
||||
|
||||
def test_runtime_python_uses_adjacent_virtualenv(self):
|
||||
with TemporaryDirectory() as temporary:
|
||||
script = Path(temporary) / 'semantic-retrieval' / 'server.py'
|
||||
|
||||
@@ -55,6 +55,7 @@ foreach ($shell in @('powershell','bash')) {
|
||||
}
|
||||
$docFile = if ($client -eq 'codex') { 'AGENTS.md' } else { 'CLAUDE.md' }
|
||||
$docContent = Get-Content -LiteralPath (Join-Path $package $docFile) -Raw
|
||||
if ($docContent -notmatch [regex]::Escape('Use MCP services only through the `mcporter` CLI.')) { throw "$package must require MCPorter for MCP services" }
|
||||
if (([regex]::Matches($docContent, [regex]::Escape('Apply instructions in this order'))).Count -ne 1) { throw "$client-$shell must embed the priority rule exactly once" }
|
||||
if ($client -eq 'codex' -and $docContent -match 'Always load and apply `rules/general\.md`') { throw "$package must not still instruct loading general.md by path" }
|
||||
if ($client -eq 'claude' -and (Test-Path (Join-Path $package 'rules/general.md'))) { throw 'Claude must not automatically load a second copy of general rules.' }
|
||||
|
||||
@@ -75,6 +75,7 @@ for shell in powershell bash; do
|
||||
doc_file=AGENTS.md
|
||||
[ "$client" != claude ] || doc_file=CLAUDE.md
|
||||
doc_content=$(cat "$package/$doc_file")
|
||||
printf '%s' "$doc_content" | grep -Fq 'Use MCP services only through the `mcporter` CLI.'
|
||||
occurrences=$(printf '%s' "$doc_content" | grep -o 'Apply instructions in this order' | wc -l)
|
||||
[ "$occurrences" -eq 1 ] || { printf '%s-%s must embed the priority rule exactly once\n' "$client" "$shell" >&2; exit 1; }
|
||||
if [ "$client" = codex ] && printf '%s' "$doc_content" | grep -q 'Always load and apply `rules/general\.md`'; then
|
||||
|
||||
@@ -4,4 +4,6 @@ This file is generated into client-specific instruction files. Do not edit gener
|
||||
|
||||
__RULE_LOADING__
|
||||
|
||||
Use MCP services only through the `mcporter` CLI. Do not invoke direct MCP tools or create, edit, or use native MCP connections, including `/mcp`. If MCPorter or its required server is unavailable, report that limitation instead of bypassing it.
|
||||
|
||||
Main agent owns state and decisions. Delegate only for independent work where benefit exceeds context/tool cost. Use one implementer for contained work; subagents never spawn agents. Pass minimal context and verify handoffs.
|
||||
Reference in new issue
Block a user