Commit Graph

13 Commits

Author SHA1 Message Date
lselvar
d79a514b30 fix: remove unsupported mode: frontmatter from Copilot skills mode (fixes #2799) (#2819)
VS Code Copilot Agent Skills do not support the `mode:` frontmatter field.
The generated SKILL.md files included `mode: speckit.<stem>` injected by
CopilotIntegration.post_process_skill_content(), which had no effect in
VS Code and could cause confusion. Simplify post_process_skill_content to
delegate directly to _CopilotSkillsHelper without injecting mode:.

Update tests to assert mode: is absent from generated skill frontmatter.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 17:14:08 -05:00
darion-yaphet
ee17b04784 refactor(integrations): co-locate integration commands in integrations/ domain dir (PR-5/8) (#2720)
* refactor(integrations): co-locate integration commands in integrations/ domain dir

- Remove commands/ stubs (handlers will live in domain dirs)
- Move all integration CLI handlers out of __init__.py into integrations/
- Split into focused modules under integrations/:
    _helpers.py           (340 lines) — domain helpers
    _install_commands.py  (306 lines) — install / uninstall
    _migrate_commands.py  (487 lines) — switch / upgrade
    _query_commands.py    (442 lines) — list / use / search / info / catalog
    _commands.py           (34 lines) — app objects + register()
- __init__.py reduced by ~1400 lines; integration block replaced with register() call
- Fix patch paths in tests to new module locations

* fix(integrations): restore original integration list output in refactor

Preserve the CLI Required column, post-table default/installed summary,
and no-installed guidance that were dropped during the no-behavior-change
refactor of integration list into _query_commands.py.

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix(integrations): restore _clear/_update_init_options public imports

The refactor that split integration commands moved
_clear_init_options_for_integration and _update_init_options_for_integration
into integrations/_helpers.py, but tests still import them from the top-level
specify_cli package, causing ImportError. Re-export them with explicit aliases
at the end of __init__.py to preserve the public import surface.

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-02 12:21:19 -05:00
Pedro Barbosa
44aac9f6e4 feat: add native Cline integration (#2508)
* test: strip ansi to make asserts work

* feat: add native Cline integration
2026-06-01 11:20:48 -05:00
Copilot
50da3a0f77 Extract agent context updates into bundled agent-context extension (#2546)
* Initial plan

* Extract agent context updates into bundled agent-context extension

* Potential fix for pull request finding 'Unused import'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>

* Potential fix for pull request finding 'Unused import'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>

* fix: address review comments on agent-context extension

- bash: parse init-options.json with a single python3 invocation instead
  of three separate read_json_field calls, for parity with the PowerShell
  ConvertFrom-Json approach and to avoid divergent error semantics
- bash: use parameter expansion to strip PROJECT_ROOT prefix from plan
  path instead of sed interpolation, avoiding special-character fragility
- powershell: limit Get-ChildItem to -Depth 1 so plan.md discovery matches
  the bash glob specs/*/plan.md (one level deep) — fixes cross-platform
  inconsistency with nested plan.md files
- powershell: replace Substring+Length relative-path with
  [System.IO.Path]::GetRelativePath for robustness across case/PSDrive
  differences
- __init__.py: move agent-context extension install to after
  save_init_options so init-options.json is present when hooks run
- __init__.py: seed context_markers in init-options only when
  context_file is truthy; avoids noise for integrations without a context
  file
- integrations/base.py: narrow blanket except Exception in
  _resolve_context_markers to ImportError / (OSError, ValueError) so
  unexpected bugs surface instead of being silently swallowed

* fix: gate context_markers in _update_init_options_for_integration on context_file

Apply the same gating logic used during `specify init`: only write
context_markers to init-options.json when the integration actually has a
context_file set.  When switching to an integration without a context file
the stale markers are removed, keeping the two init paths consistent.

* fix: move context_file/context_markers from init-options.json to agent-context extension config

* Potential fix for pull request finding 'Unused global variable'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>

* fix: clarify local import comment in agents.py

* Fix remaining agent-context review findings

* Fix follow-up agent-context review issues

* Address review feedback: narrow except, improve PyYAML messaging, surface config-written note

* Fix double-space in PyYAML install hint message

* 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>

* 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>

* Address latest agent-context review feedback

* Harden bash config parse output handling

* Clarify ImportError-only fallback comment

* Apply review feedback: drop dead try/except, guard ext-config creation, explicit ConvertFrom-Yaml check

* Remove redundant $Options = $null in PS1 catch block

* Add constitution directives, deprecation warning, agent-context auto-install, and init flow fix

- Add constitution-loading directive to specify, clarify, tasks, checklist, taskstoissues commands
- Add deprecation warning (v0.12.0) in upsert_context_section()
- Auto-install agent-context extension during specify init
- Move context_file from init-options.json to agent-context extension config
- Add tests: deprecation warning, corrupt config, constitution directives
- Update file inventories across all integration tests

* Address review: fix init ordering, test coverage, and hermes inventory

- Move agent-context extension install after init-options.json is saved
  so skill registration can read ai_skills + integration key
- Write extension config after install (avoids template overwriting context_file)
- Fix test_defaults_when_markers_field_missing to truly test missing markers key
- Update hermes tests to allow extension-installed agent-context skill

* Address review: chmod ordering, preserve markers, PS1 Python check, YAML key order

- Move ensure_executable_scripts after agent-context extension install
  so extension scripts get execute bits set
- Use preserve_markers=True on reinit to keep user-customized markers
- Add Python 3 version check in PowerShell fallback (matching bash behavior)
- Add sort_keys=False to yaml.safe_dump for stable config output

* Address review: path traversal guards and docstring fix

- Reject absolute paths and '..' segments in context_file in both bash and
  PowerShell scripts to prevent writes outside the project root
- Fix docstring in _update_init_options_for_integration to accurately
  describe marker preservation behavior

* Address review: strict enabled check, docstring, segment-level path traversal

- Use 'is not False' for enabled check so only literal False disables
- Update upsert_context_section docstring to mention disabled-extension return
- Fix path traversal guards to check actual path segments, not substrings
  (allows filenames like 'notes..md' while rejecting '../' traversal)

* Address review: UnicodeError handling, missing extension warning

- Add UnicodeError to exception tuples in _load_agent_context_config and
  _resolve_context_markers so garbled UTF-8 config files fall back to defaults
- Emit error (with reinstall command) instead of silent skip when bundled
  agent-context extension is not found during init

* Address review: bash backslash traversal guard, wheel packaging

- Reject backslash separators and Windows drive-letter paths in bash
  context_file validation (prevents traversal on Git-Bash/Windows)
- Add extensions/agent-context to pyproject.toml force-include so the
  bundled extension is included in wheel builds

* Address review: write extension config before init-options.json

- Reorder writes in _update_init_options_for_integration so the
  agent-context extension config is updated first; if it fails,
  init-options.json remains consistent with the previous state

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-05-30 06:37:18 -05:00
WOLIKIMCHENG
57a518a583 Fix shared script command hints for integration separators (#2627)
* fix shared script command refs for integration separators

* Fix integration use shared infra refresh hint

* Clarify shared infrastructure force wording

---------

Co-authored-by: root <1647273252@qq.com>
Co-authored-by: root <kinsonnee@gmail.com>
2026-05-28 10:02:27 -05:00
Copilot
c7e0cacaff fix: PS 5.1 compat — replace non-ASCII chars in shipped PowerShell scripts (#2709)
* Initial plan

* fix: replace non-ASCII chars in PS1 files, add encoding regression tests, fix ANSI stripping in tests, update docs

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-05-26 13:18:34 -05:00
Pascal THUET
dca81b90de fix init-options speckit version refresh (#2647) 2026-05-26 06:51:06 -05:00
Pascal THUET
c87081a50a fix(integration): clarify multi-install guidance (#2549) 2026-05-14 13:46:48 -05:00
Marcus Burghardt
9732a4d092 fix(opencode): use commands/ directory (plural) to match OpenCode docs (#2453)
* fix(opencode): use commands/ directory (plural) to match OpenCode docs

OpenCode documentation (https://opencode.ai/docs/commands/) uses
.opencode/commands/ (plural) as the canonical command directory.
The OpenCode runtime supports both .opencode/command/ and
.opencode/commands/ via a {command,commands} glob, but the
singular form was the original convention and is now outdated.

Update the OpenCode integration to write to .opencode/commands/
instead of .opencode/command/, aligning with the documented
standard and the OpenSpec fix (Fission-AI/OpenSpec#748).

Signed-off-by: Marcus Burghardt <maburgha@redhat.com>
Assisted-by: OpenCode (claude-opus-4-6)

* feat(registrar): add legacy_dir fallback for backward-compatible directory migration

Add _resolve_agent_dir() to CommandRegistrar that checks a
legacy_dir fallback when the canonical directory does not exist.
When legacy_dir is found, a deprecation warning directs users to
run "specify integration upgrade" to migrate.

The OpenCode integration declares legacy_dir: ".opencode/command"
so that extension and preset registration, as well as command
cleanup, continue working for projects that have not yet migrated
to .opencode/commands/.

The legacy_dir mechanism is opt-in: integrations that do not
declare it get no fallback and no behavioral change.

Add end-to-end test verifying that "specify integration upgrade
opencode" migrates commands from legacy .opencode/command/ to
canonical .opencode/commands/ and removes stale files.

Signed-off-by: Marcus Burghardt <maburgha@redhat.com>
Assisted-by: OpenCode (claude-opus-4-6)

* fix(registrar): address PR review feedback on legacy_dir handling

- Fix deprecation warning formatting: quote paths and remove trailing
  '/.' that produced confusing '.opencode/commands/.' output
- Eliminate duplicate warnings: pass pre-resolved directory to
  register_commands() via _resolved_dir parameter so
  _resolve_agent_dir() is only called once per agent
- Fix unregister_commands() to clean both canonical and legacy dirs
  when both exist, preventing orphaned command files after upgrade
- Add test_unregister_cleans_legacy_when_both_dirs_exist regression
  test and tighten warning count assertion to exactly 1

Assisted-by: OpenCode (claude-opus-4-6)
Signed-off-by: Marcus Burghardt <maburgha@redhat.com>

---------

Signed-off-by: Marcus Burghardt <maburgha@redhat.com>
2026-05-13 09:55:56 -05:00
Quratulain-bilal
947b4398c7 fix(integration): refresh shared infra on integration switch (#2375)
* fix(integration): refresh shared infra on integration switch

* fix(integration): address Copilot review on switch shared-infra refresh

- Clarify install_shared_infra docstring: force overwrites regular files
  but always preserves symlinks (safe-destination check refuses to follow).
- Print refresh_hint only for preserved_user_files; skipped_files keeps
  the generic remediation. Avoids misleading guidance when files were
  merely skipped (not detected as customized).
- Catch ValueError from the safe-destination check and bucket the path
  under a new symlinked_files warning instead of aborting the switch.
- Restore templates/constitution-template.md to upstream (drop accidental
  leading blank lines).

* fix(integration): narrow symlink bucketing to dedicated exception

Address Copilot feedback on shared_infra.py:305 — _safe_dest_or_bucket
caught any ValueError as 'symlinked', which masked genuine safety errors
(path escape, parent-not-a-directory).

- Introduce SymlinkedSharedPathError(ValueError) raised only by the
  symlink-specific branches in _ensure_safe_shared_*().
- _safe_dest_or_bucket() now catches only SymlinkedSharedPathError;
  other ValueErrors propagate so the operation aborts with the real
  cause instead of being silently bucketed.
- Wrap top-level dest_scripts/dest_variant/dest_templates mkdir calls
  in the same bucket helper so a symlinked .specify/scripts or
  .specify/templates is preserved with a warning rather than aborting
  the switch (matches the documented 'preserve customizations' behavior).
- Update tests to expect the new bucket+warn behavior for leaf-level
  symlinked destinations.

* fix(integration): tailor shared-infra warnings and rename preflight test

Address Copilot review on PR #2375:

- skipped_files hint now uses refresh_hint when refresh_managed=True
  so integration switch suggests --refresh-shared-infra instead of the
  generic init/upgrade flags.
- symlinked-files warning header says "path(s)" rather than "file(s)"
  since symlinked directories (e.g. .specify/scripts/bash) are also
  bucketed there.
- Rename test_shared_infra_install_preflights_before_writing to
  test_shared_infra_install_buckets_unsafe_destinations_and_continues
  to match the new bucket-and-continue semantics.

* test: rename symlink bucketing tests to reflect bucket-and-continue behavior

The two file-bucketing tests at line 300/320 were named *_refuses_*, but
the new behavior buckets symlinked file destinations with a warning while
safe destinations in the same install still complete. Rename to
*_buckets_* and update docstrings to match.

The remaining *_refuses_* tests (line 342/362/381) genuinely raise on
symlinked dirs/manifests and keep their names.

---------

Co-authored-by: Quratulain-bilal <quratulain.bilal@users.noreply.github.com>
2026-05-11 10:49:48 -05:00
Pascal THUET
38fd1f6cc2 Support controlled multi-install for safe AI agent integrations (#2389)
* support controlled multi-install integrations

* fix: harden multi-install integration state

* refactor: isolate integration runtime helpers

* fix: address copilot review feedback

* fix: address follow-up copilot feedback

* fix: tighten integration switch semantics

* fix: address final copilot review feedback

* fix: harden integration manifest read errors

* fix: refuse symlinked shared infra paths

* test: filter expected self-test preset warning

* test: address copilot review nits

* refactor: centralize safe shared infra writes

* fix: use no-follow writes for shared infra

* fix: keep default integration atomic on template refresh

* fix: harden shared infra error paths

* fix: preflight shared infra and future state schemas

* fix: support nested shared scripts during preflight

* test: tolerate wrapped schema error output

* fix: use safe default mode for shared text writes

* fix: use posix paths in shared skip output

* fix: share project guard for integration use

* fix: centralize spec-kit project guards

* fix: use posix project paths in cli output

* fix: harden shared manifest and upgrade refresh
2026-05-01 11:54:41 -05:00
Chengyou Liu
5edc9a5358 fix: migrate extension commands on integration switch (#2404)
* fix: migrate extension commands on integration switch

When switching integrations (e.g. kimi → opencode), extension commands
were not re-registered for the new agent, leaving the new agent without
extension support and orphaning files in the old agent's directory.

Changes:
- Add ExtensionManager.unregister_agent_artifacts() to clean up old
  agent extension files and registry entries during switch
- Add ExtensionManager.register_enabled_extensions_for_agent() to
  re-register all enabled extensions for the new agent
- Wire both into integration_switch() after uninstall/install phases
- Handle skills mode (Copilot --skills) correctly
- Add tests for kimi→opencode→claude migration, Copilot skills mode,
  and disabled extension handling

Fixes extension commands not appearing after integration switch.

* Update src/specify_cli/extensions.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-01 07:41:38 -05:00
Copilot
94ba857b78 Add specify integration subcommand for post-init integration management (#2083)
* Initial plan

* Add specify integration subcommand (list, install, uninstall, switch)

Implements the `specify integration` subcommand group for managing
integrations in existing projects after initial setup:

- `specify integration list` — shows available integrations and installed status
- `specify integration install <key>` — installs an integration into existing project
- `specify integration uninstall [key]` — hash-safe removal preserving modified files
- `specify integration switch <target>` — uninstalls current, installs target

Follows the established `specify <noun> <verb>` CLI pattern used by
extensions and presets. Shared infrastructure (scripts, templates) is
preserved during uninstall and switch operations.

Agent-Logs-Url: https://github.com/github/spec-kit/sessions/1cca6c84-3e12-465d-88b8-a646d3504f63

Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>

* Address review feedback: extract helper, fix return type annotation

- Extract _update_init_options_for_integration() to deduplicate init-options
  update logic between install and switch commands
- Fix _parse_integration_options return type to dict[str, Any] | None

Agent-Logs-Url: https://github.com/github/spec-kit/sessions/1cca6c84-3e12-465d-88b8-a646d3504f63

Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>

* Potential fix for pull request finding 'Unused import'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>

* Address review feedback: validate script type, handle --flag=value, fix metadata cleanup

- Add _normalize_script_type() to validate script type against SCRIPT_TYPE_CHOICES
- Handle --name=value syntax in _parse_integration_options()
- Clear init-options.json keys in no-manifest uninstall early-return path
- Clear stale metadata between switch teardown and install phases
- Add 5 tests covering the new edge cases

* Block --force with different integration, persist script type in init-options

- --force on install now rejects overwriting a different integration; users must
  use 'specify integration switch' instead
- _update_init_options_for_integration() now accepts and persists script_type
- Fix misleading test docstring for switch metadata test
- Add test_force_blocked_with_different_integration

* Remove --force from integration install, ensure shared infra on install/switch

- Remove --force parameter entirely from integration install; users must
  uninstall before reinstalling to prevent orphaned files
- Auto-install shared infrastructure (.specify/scripts/, .specify/templates/)
  when missing during install or switch
- Add test for shared infra creation on bare project install

* Remove redundant installed_key != key check

The == key case already returns above, so the != key guard is always true
at this point. Simplify to just 'if installed_key:'.

* Run shared infra unconditionally, defer metadata removal in switch

- Call _install_shared_infra() unconditionally on install and switch since it
  merges without overwriting existing files
- Remove premature metadata cleanup between switch phases; metadata is now
  only updated after successful Phase 2 install

* Add install rollback, graceful manifest errors, clear switch metadata

- Attempt teardown rollback on install/switch failure to avoid orphaned files
- Catch ValueError/FileNotFoundError on IntegrationManifest.load() in uninstall
  with user-friendly recovery guidance
- Clear metadata immediately after switch teardown so failed Phase 2 doesn't
  leave stale references to the removed integration

* Log rollback failures instead of silently suppressing them

* Handle corrupt manifest in switch, distinguish unknown vs missing manifest

- Wrap IntegrationManifest.load() in switch with ValueError/FileNotFoundError
  handling, matching the pattern used in uninstall
- Split else branch to report 'unknown integration' vs 'no manifest' separately

* Clean up metadata on rollback, broaden init-options match in uninstall

- Remove integration.json in install/switch rollback paths so failed installs
  don't leave stale metadata
- Match on both 'integration' and 'ai' keys when clearing init-options.json
  during uninstall to handle partially-written metadata

* Fix recovery guidance for unreadable manifests, fix type annotations

- Recovery instructions now guide users through delete manifest → uninstall →
  reinstall workflow that actually works
- Type annotations for optional CLI parameters changed from str to str | None

* Allow manifest-only uninstall for unknown/removed integrations

- Uninstall no longer requires the integration to be in the registry; falls back
  to manifest.uninstall() directly when get_integration() returns None
- Switch Phase 1 similarly uses manifest-only uninstall for unknown integrations
  instead of skipping teardown, preventing orphaned files

* Fail fast on corrupt integration.json, validate integration options

- _read_integration_json() now exits with an actionable error when
  integration.json exists but is corrupt/unreadable
- _parse_integration_options() rejects unknown options, validates flag usage,
  and requires values for non-flag options

* Validate integration.json is a dict, fail fast on missing manifest in switch

- _read_integration_json() validates parsed JSON is a dict, not a list/string
- Switch fails fast with recovery guidance when manifest is missing instead
  of silently skipping teardown and risking co-existing integration files

---------

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>
2026-04-03 17:35:04 -05:00