* fix: migrate Qwen Code CLI from TOML to Markdown format (#1589)
Qwen Code CLI v0.10.0 deprecated TOML format and fully switched to
Markdown as the core format for configuration and interaction files.
- Update create-release-packages.sh: generate .md files with $ARGUMENTS
instead of .toml files with {{args}} for qwen agent
- Update create-release-packages.ps1: same change for PowerShell script
- Update AGENTS.md: reflect Qwen's new Markdown format in docs and
remove Qwen from TOML format section
- Update tests/test_ai_skills.py: add commands_dir_qwen fixture and
tests covering Markdown-format skills installation for Qwen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: update CommandRegistrar qwen config to Markdown format
extensions.py CommandRegistrar.AGENT_CONFIGS['qwen'] was still set to
TOML format, causing `specify extension` to write .toml files into
.qwen/commands, conflicting with Qwen Code CLI v0.10.0+ expectations.
- Change qwen format from toml to markdown
- Change qwen args from {{args}} to $ARGUMENTS
- Change qwen extension from .toml to .md
- Add test to assert qwen config is Markdown format
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(extensions): implement automatic updates with atomic backup/restore
- Implement automatic extension updates with download from catalog
- Add comprehensive backup/restore mechanism for failed updates:
- Backup registry entry before update
- Backup extension directory
- Backup command files for all AI agents
- Backup hooks from extensions.yml
- Add extension ID verification after install
- Add KeyboardInterrupt handling to allow clean cancellation
- Fix enable/disable to preserve installed_at timestamp by using
direct registry manipulation instead of registry.add()
- Add rollback on any update failure with command file,
hook, and registry restoration
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(extensions): comprehensive name resolution and error handling improvements
- Add shared _resolve_installed_extension helper for ID/display name resolution
with proper ambiguous name handling (shows table of matches)
- Add _resolve_catalog_extension helper for catalog lookups by ID or display name
- Update enable/disable/update/remove commands to use name resolution helpers
- Fix extension_info to handle catalog errors gracefully:
- Fallback to local installed info when catalog unavailable
- Distinguish "catalog unavailable" from "not found in catalog"
- Support display name lookup for both installed and catalog extensions
- Use resolved display names in all status messages for consistency
- Extract _print_extension_info helper for DRY catalog info printing
Addresses reviewer feedback:
- Ambiguous name handling in enable/disable/update
- Catalog error fallback for installed extensions
- UX message clarity (catalog unavailable vs not found)
- Resolved ID in status messages
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(extensions): properly detect ambiguous names in extension_info
The extension_info command was breaking on the first name match without
checking for ambiguity. This fix separates ID matching from name matching
and checks for ambiguity before selecting a match, consistent with the
_resolve_installed_extension() helper used by other commands.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor(extensions): add public update() method to ExtensionRegistry
Add a proper public API for updating registry metadata while preserving
installed_at timestamp, instead of directly mutating internal registry
data and calling private _save() method.
Changes:
- Add ExtensionRegistry.update() method that preserves installed_at
- Update enable/disable commands to use registry.update()
- Update rollback logic to use registry.update()
This decouples the CLI from registry internals and maintains proper
encapsulation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(extensions): safely access optional author field in extension_info
ExtensionManifest doesn't expose an author property - the author field
is optional in extension.yml and stored in data["extension"]["author"].
Use safe dict access to avoid AttributeError.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(extensions): address multiple reviewer comments
- ExtensionRegistry.update() now preserves original installed_at timestamp
- Add ExtensionRegistry.restore() for rollback (entry was removed)
- Clean up wrongly installed extension on ID mismatch before rollback
- Remove unused catalog_error parameter from _print_extension_info()
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(extensions): check _install_allowed for updates, preserve backup on failed rollback
- Skip automatic updates for extensions from catalogs with install_allowed=false
- Only delete backup directory on successful rollback, preserve it on failure
for manual recovery
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(extensions): address reviewer feedback on update/rollback logic
- Hook rollback: handle empty backup_hooks by checking `is not None`
instead of truthiness (falsy empty dict would skip hook cleanup)
- extension_info: use resolved_installed_id for catalog lookup when
extension was found by display name (prevents wrong catalog match)
- Rollback: always remove extension dir first, then restore if backup
exists (handles case when no original dir existed before update)
- Validate extension ID from ZIP before installing, not after
(avoids side effects of installing wrong extension before rollback)
- Preserve enabled state during updates: re-apply disabled state and
hook enabled flags after successful update
- Optimize _resolve_catalog_extension: pass query to catalog.search()
instead of fetching all extensions
- update() now merges metadata with existing entry instead of replacing
(preserves fields like registered_commands when only updating enabled)
- Add tests for ExtensionRegistry.update() and restore() methods:
- test_update_preserves_installed_at
- test_update_merges_with_existing
- test_update_raises_for_missing_extension
- test_restore_overwrites_completely
- test_restore_can_recreate_removed_entry
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs(extensions): update RFC to reflect implemented status
- Change status from "Draft" to "Implemented"
- Update all Implementation Phases to show completed items
- Add new features implemented beyond original RFC:
- Display name resolution for all commands
- Ambiguous name handling with tables
- Atomic update with rollback
- Pre-install ID validation
- Enabled state preservation
- Registry update/restore methods
- Catalog error fallback
- _install_allowed flag
- Cache invalidation
- Convert Open Questions to Resolved Questions with decisions
- Add remaining Open Questions (sandboxing, signatures) as future work
- Fix table of contents links
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(extensions): address third round of PR review comments
- Refactor extension_info to use _resolve_installed_extension() helper
with new allow_not_found parameter instead of duplicating resolution logic
- Fix rollback hook restoration to not create empty hooks: {} in config
when original config had no hooks section
- Fix ZIP pre-validation to handle nested extension.yml files (GitHub
auto-generated ZIPs have structure like repo-name-branch/extension.yml)
- Replace unused installed_manifest variable with _ placeholder
- Add display name to update status messages for better UX
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(extensions): address fourth round of PR review comments
Rollback fixes:
- Preserve installed_at timestamp after successful update (was reset by
install_from_zip calling registry.add)
- Fix rollback to only delete extension_dir if backup exists (avoids
destroying valid installation when failure happens before modification)
- Fix rollback to remove NEW command files created by failed install
(files that weren't in original backup are now cleaned up)
- Fix rollback to delete hooks key entirely when backup_hooks is None
(original config had no hooks key, so restore should remove it)
Cross-command consistency fix:
- Add display name resolution to `extension add` command using
_resolve_catalog_extension() helper (was only in `extension info`)
- Use resolved extension ID for download_extension() call, not original
argument which may be a display name
Security fix (fail-closed):
- Malformed catalog config (empty/missing URLs) now raises ValidationError
instead of silently falling back to built-in catalogs
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(lint): address ruff linting errors and registry.update() semantics
- Remove unused import ExtensionError in extension_info
- Remove extraneous f-prefix from strings without placeholders
- Use registry.restore() instead of registry.update() for installed_at
preservation (update() always preserves existing installed_at, ignoring
our override)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: iamaeroplane <michal.bachorik@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add Kimi Code (kimi) CLI agent support
- Register kimi in AGENT_CONFIG with folder `.kimi/`, markdown format, requires_cli=True
- Register kimi in CommandRegistrar.AGENT_CONFIGS
- Add kimi to supported agents table in AGENTS.md and README.md
- Add kimi to release packaging scripts (bash and PowerShell)
- Add kimi CLI installation to devcontainer post-create script
- Add kimi support to update-agent-context scripts (bash and PowerShell)
- Add 4 consistency tests covering all kimi integration surfaces
- Bump version to 0.1.14 and update CHANGELOG
* fix: include .specify/templates/ and real command files in release ZIPs
- Copy real command files from templates/commands/ (with speckit. prefix)
instead of generating stubs, so slash commands have actual content
- Add .specify/templates/ to every ZIP so ensure_constitution_from_template
can find constitution-template.md on init
- Add .vscode/settings.json to every ZIP
- Having 3 top-level dirs prevents the extraction flatten heuristic from
incorrectly stripping the agent config folder (.kimi/, .claude/, etc.)
- Bump version to 0.1.14.1
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(kimi): use .kimi/skills/<name>/SKILL.md structure for Kimi Code CLI
Kimi Code CLI uses a skills system, not flat command files:
- Skills live in .kimi/skills/<name>/SKILL.md (project-level)
- Invoked with /skill:<name> (e.g. /skill:speckit.specify)
- Each skill is a directory containing SKILL.md with YAML frontmatter
Changes:
- AGENT_CONFIG["kimi"]["commands_subdir"] = "skills" (was "commands")
- create-release-packages.sh: new create_kimi_skills() function creates
skill directories with SKILL.md from real template content
- Bump version to 0.1.14.2
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(test): align kimi commands_subdir assertion with skills structure
* fix: use forward slashes for tabnine path in create-release-packages.ps1
* fix: align kimi to .kimi/skills convention and fix ARGUMENTS unbound variable
* fix: address PR review comments for kimi agent support
- Fix VERSION_NO_V undefined variable in create-github-release.sh
- Restore version $1 argument handling in create-release-packages.sh
- Fix tabnine/vibe/generic cases calling undefined generate_commands
- Align roo path .roo/rules -> .roo/commands with AGENT_CONFIG
- Fix kimi extension to use per-skill SKILL.md directory structure
- Add parent mkdir before dest_file.write_text for nested paths
- Restore devcontainer tools removed by regression + add Kimi CLI
- Strengthen test_kimi_in_powershell_validate_set assertion
* fix: restore release scripts and address all PR review comments
- Restore create-release-packages.sh to original with full generate_commands/
rewrite_paths logic; add kimi case using create_kimi_skills function
- Restore create-release-packages.ps1 to original with full Generate-Commands/
Rewrite-Paths logic; add kimi case using New-KimiSkills function
- Restore create-github-release.sh to original with proper $1 argument
handling and VERSION_NO_V; add kimi zip entries
- Add test_ai_help_includes_kimi for consistency with other agents
- Strengthen test_kimi_in_powershell_validate_set to check ValidateSet
* fix: address second round of PR review comments
- Add __AGENT__ and {AGENT_SCRIPT} substitutions in create_kimi_skills (bash)
- Add __AGENT__ and {AGENT_SCRIPT} substitutions in New-KimiSkills (PowerShell)
- Replace curl|bash Kimi installer with pipx install kimi-cli in post-create.sh
* fix: align kimi skill naming and add extension registrar test
- Fix install_ai_skills() to use speckit.<cmd> naming for kimi (dot
separator) instead of speckit-<cmd>, matching /skill:speckit.<cmd>
invocation convention and packaging scripts
- Add test_kimi_in_extension_registrar to verify CommandRegistrar.AGENT_CONFIGS
includes kimi with correct dir and SKILL.md extension
* fix(test): align kimi skill name assertion with dot-separator convention
test_skills_install_for_all_agents now expects speckit.specify (dot) for
kimi and speckit-specify (hyphen) for all other agents, matching the
install_ai_skills() implementation added in the previous commit.
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix init --preset error masking: distinguish "not found" from real errors
- Fix bash resolve_template: skip hidden dirs in extensions (match Python/PS)
- Fix temp dir leaks in tests: use temp_dir fixture instead of mkdtemp
- Fix self-test catalog entry: add note that it's local-only (no download_url)
- Fix Windows path issue in resolve_with_source: use Path.relative_to()
- Fix skill restore path: use project's .specify/templates/commands/ not source tree
- Add encoding="utf-8" to all file read/write in agents.py
- Update test to set up core command templates for skill restoration
- Remove extraneous f-prefix from two f-strings without placeholders
- Replace substring URL check in test with startswith/endswith assertions
to satisfy CodeQL incomplete URL substring sanitization rule
- Add save_init_options() / load_init_options() helpers that persist
CLI flags from 'specify init' to .specify/init-options.json
- PresetManager._register_skills() overwrites SKILL.md files when
--ai-skills was used during init and corresponding skill dirs exist
- PresetManager._unregister_skills() restores core template content
on preset removal
- registered_skills stored in preset registry metadata
- 8 new tests covering skill override, skip conditions, and restore
* feat(extensions): support .extensionignore to exclude files during install
Add .extensionignore support so extension authors can exclude files and
folders from being copied when users run 'specify extension add'.
The file uses glob-style patterns (one per line), supports comments (#),
blank lines, trailing-slash directory patterns, and relative path matching.
The .extensionignore file itself is always excluded from the copy.
- Add _load_extensionignore() to ExtensionManager
- Integrate ignore function into shutil.copytree in install_from_directory
- Document .extensionignore in EXTENSION-DEVELOPMENT-GUIDE.md
- Add 6 tests covering all pattern matching scenarios
- Bump version to 0.1.14
* fix(extensions): use pathspec for gitignore-compatible .extensionignore matching
Replace fnmatch with pathspec.GitIgnoreSpec to get proper .gitignore
semantics where * does not cross directory boundaries. This addresses
review feedback on #1781.
Changes:
- Switch from fnmatch to pathspec>=0.12.0 (GitIgnoreSpec.from_lines)
- Normalize backslashes in patterns for cross-platform compatibility
- Distinguish directories from files for trailing-slash patterns
- Update docs to accurately describe supported pattern semantics
- Add edge-case tests: .., absolute paths, empty file, backslashes,
* vs ** boundary behavior, and ! negation
- Move changelog entry to [Unreleased] section
* Initial plan
* feat(extensions): implement multi-catalog stack support
- Add CatalogEntry dataclass to represent catalog entries
- Add get_active_catalogs() reading SPECKIT_CATALOG_URL, project config,
user config, or built-in default stack (org-approved + community)
- Add _load_catalog_config() to parse .specify/extension-catalogs.yml
- Add _validate_catalog_url() HTTPS validation helper
- Add _fetch_single_catalog() with per-URL caching, backward-compat for DEFAULT_CATALOG_URL
- Add _get_merged_extensions() that merges all catalogs (priority wins on conflict)
- Update search() and get_extension_info() to use merged results
annotated with _catalog_name and _install_allowed
- Update clear_cache() to also remove per-URL hash cache files
- Add extension_catalogs CLI command to list active catalogs
- Add catalog add/remove sub-commands for .specify/extension-catalogs.yml
- Update extension_add to enforce install_allowed=false policy
- Update extension_search to show source catalog per result
- Update extension_info to show source catalog with install_allowed status
- Add 13 new tests covering catalog stack, merge conflict resolution,
install_allowed enforcement, and catalog metadata
Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
* docs: update RFC, user guide, and API reference for multi-catalog support
- RFC: replace FUTURE FEATURE section with full implementation docs,
add catalog stack resolution order, config file examples, merge
conflict resolution, and install_allowed behavior
- EXTENSION-USER-GUIDE.md: add multi-catalog section with CLI examples
for catalogs/catalog-add/catalog-remove, update catalog config docs
- EXTENSION-API-REFERENCE.md: add CatalogEntry class docs, update
ExtensionCatalog docs with new methods and result annotations,
add catalog CLI commands (catalogs, catalog add, catalog remove)
Also fix extension_catalogs command to correctly show "Using built-in
default catalog stack" when config file exists but has empty catalogs
Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
* Potential fix for pull request finding 'Empty except'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* fix: remove extraneous f-string prefixes (ruff F541)
Remove f-prefix from strings with no placeholders in catalog_remove
and extension_search commands.
Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
* fix: address PR review feedback for multi-catalog support
- Rename 'org-approved' catalog to 'default'
- Move 'catalogs' command to 'catalog list' for consistency
- Add 'description' field to CatalogEntry dataclass
- Add --description option to 'catalog add' CLI command
- Align install_allowed default to False in _load_catalog_config
- Add user-level config detection in catalog list footer
- Fix _load_catalog_config docstring (document ValidationError)
- Fix test isolation for test_search_by_tag, test_search_by_query,
test_search_verified_only, test_get_extension_info
- Update version to 0.1.14 and CHANGELOG
- Update all docs (RFC, User Guide, API Reference)
* fix: wrap _load_catalog_config() calls in catalog_list with try/except
- Check SPECKIT_CATALOG_URL first (matching get_active_catalogs() resolution order)
- Wrap both _load_catalog_config() calls in try/except ValidationError so a
malformed config file cannot crash `specify extension catalog list` after
the active catalogs have already been printed successfully
Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* fix: wire after_tasks and after_implement hook events into command templates (#1701)
The HookExecutor backend in extensions.py was fully implemented but
check_hooks_for_event() was never called by anything — the core command
templates had no instructions to check .specify/extensions.yml.
Add a final step to templates/commands/tasks.md (step 6, after_tasks) and
templates/commands/implement.md (step 10, after_implement) that instructs
the AI agent to:
- Read .specify/extensions.yml if it exists
- Filter hooks.{event} to enabled: true entries
- Evaluate any condition fields and skip non-matching hooks
- Output the RFC-specified hook message format, including
EXECUTE_COMMAND: markers for mandatory (optional: false) hooks
Bumps version to 0.1.7.
* fix: clarify hook condition handling and add YAML error guidance in templates
- Replace ambiguous "evaluate any condition value" instruction with explicit
guidance to skip hooks with non-empty conditions, deferring evaluation to
HookExecutor
- Add instruction to skip hook checking silently if extensions.yml cannot
be parsed or is invalid
* Fix/extension hooks not triggered (#1)
* feat(templates): implement before-hooks check as pre-execution phase
* test(hooks): create scenario for LLMs/Agents on hooks
---------
Co-authored-by: Dhilip <s.dhilipkumar@gmail.com>
* feat: add kiro-cli and AGENT_CONFIG consistency coverage
* fix: address PR feedback for kiro-cli migration
* test: assert init invocation result in --here mode test
* test: capture init result in here-mode command test
* chore: save local unapproved work in progress
* fix: resolve remaining PR1690 review threads
* Update src/specify_cli/__init__.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* test: reduce brittleness in ai help alias assertion
* fix: resolve PR1690 ruff syntax regression
---------
Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* fix: correct Copilot extension command registration (#copilot)
- Use .agent.md extension for commands in .github/agents/
- Generate companion .prompt.md files in .github/prompts/
- Clean up .prompt.md files on extension removal
- Add tests for Copilot-specific registration behavior
Bumps version to 0.1.7.
* fix: test copilot prompt cleanup via ExtensionManager.remove() instead of manual unlink
---------
Co-authored-by: Ismael <ismael.jimenez-martinez@bmw.de>
* fix: prepend YAML frontmatter to Cursor .mdc files for auto-inclusion
Cursor IDE requires YAML frontmatter with `alwaysApply: true` in .mdc
rule files for them to be automatically loaded. Without this frontmatter,
users must manually configure glob patterns for the rules to take effect.
This fix adds frontmatter generation to both the bash and PowerShell
update-agent-context scripts, handling three scenarios:
- New .mdc file creation (frontmatter prepended after template processing)
- Existing .mdc file update without frontmatter (frontmatter added)
- Existing .mdc file with frontmatter (no duplication)
Closes#669🤖 Generated with [Claude Code](https://claude.com/code)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: address Copilot review suggestions
- Handle CRLF line endings in frontmatter detection (grep '^---' instead
of '^---$')
- Fix double blank line after frontmatter in PowerShell New-AgentFile
- Remove unused tempfile import from tests
- Add encoding="utf-8" to all open() calls for cross-platform safety
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* chore: bump version to v0.0.6 [skip ci]
* Fix parameter ordering issues in CLI (#1641)
- Add validation to detect when option flags are consumed as values
- Provide clear error messages with helpful hints and examples
- Add 5 comprehensive tests to prevent regressions
- Update CODEOWNERS to @mnriem
- Bump version to 0.1.6 with changelog entry
Fixes: #1641
* Fix ruff linting errors: remove f-string prefix from strings without placeholders
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
- Add --ai generic option for unsupported AI agents (bring your own agent)
- Require --ai-commands-dir to specify where agent reads commands from
- Generate Markdown commands with $ARGUMENTS format (compatible with most agents)
- Rebuild CHANGELOG from GitHub releases (last 10 releases)
- Align version to 0.1.3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add modular extension system for Spec Kit
Implement a complete extension system that allows third-party developers
to extend Spec Kit functionality through plugins.
## Core Features
- Extension discovery and loading from local and global directories
- YAML-based extension manifest (extension.yml) with metadata and capabilities
- Command extensions: custom slash commands with markdown templates
- Hook system: pre/post hooks for generate, task, and sync operations
- Extension catalog for discovering and installing community extensions
- SPECKIT_CATALOG_URL environment variable for catalog URL override
## Installation Methods
- Catalog install: `specify extension add <name>`
- URL install: `specify extension add <name> --from <url>`
- Dev install: `specify extension add --dev <path>`
## Implementation
- ExtensionManager class for lifecycle management (load, enable, disable)
- Support for extension dependencies and version constraints
- Configuration layering (global → project → extension)
- Hook conditions for conditional execution
## Documentation
- RFC with design rationale and architecture decisions
- API reference for extension developers
- Development guide with examples
- User guide for installing and managing extensions
- Publishing guide for the extension catalog
## Included
- Extension template for bootstrapping new extensions
- Comprehensive test suite
- Example catalog.json structure
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update Jira extension to v2.1.0 in catalog
Adds 2-level mode support (Epic → Stories only).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Address PR review feedback
- Fix Zip Slip vulnerability in ZIP extraction with path validation
- Fix keep_config option to actually preserve config files on removal
- Add URL validation for SPECKIT_CATALOG_URL (HTTPS required, localhost exception)
- Add security warning when installing from custom URLs (--from flag)
- Empty catalog.json so organizations can ship their own catalogs
- Fix markdown linter errors (MD040: add language to code blocks)
- Remove redundant import and fix unused variables in tests
- Add comment explaining empty except clause for backwards compatibility
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add comprehensive organization catalog customization docs
- Explain why default catalog is empty (org control)
- Document how to create and host custom catalogs
- Add catalog JSON schema reference
- Include use cases: private extensions, curated catalogs, air-gapped environments
- Add examples for combining catalog with direct installation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix test assertions for extension system data structures
- Update test_config_backup_on_remove to use new subdirectory structure
(.backup/test-ext/file.yml instead of .backup/test-ext-file.yml)
- Update test_full_install_and_remove_workflow to handle registered_commands
being a dict keyed by agent name instead of a flat list
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Address Copilot review feedback
- Fix localhost URL check to use parsed.hostname instead of netloc.startswith()
This correctly handles URLs with ports like localhost:8080
- Fix YAML indentation error in config-template.yml (line 57)
- Fix double space typo in example.md (line 172)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add catalog.example.json as reference for organizations
The main catalog.json is intentionally empty so organizations can ship
their own curated catalogs. This example file shows the expected schema
and structure for creating organization-specific catalogs.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Address remaining Copilot security and logic review feedback
- Fix Zip Slip vulnerability by using relative_to() for safe path validation
- Add HTTPS validation for extension download URLs
- Backup both *-config.yml and *-config.local.yml files on remove
- Normalize boolean values to lowercase for hook condition comparisons
- Show non-default catalog warning only once per instance
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Ignoring linter for extensions directory
---------
Co-authored-by: iamaeroplane <michal.bachorik@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Manfred Riem <manfred.riem@microsoft.com>