Skip to content

Commit 4ba2690

Browse files
authored
feat: simplify skill installation with profiles and smart defaults (Fission-AI#726)
* feat: implement simplified skill installation with profiles and smart defaults Introduces a profile system (core/custom) to reduce the default workflow count from 10 to 4, auto-detects AI tools during init, adds a new `propose` workflow combining new+ff, fixes multi-select keybindings, and adds backwards-compatible migration for existing users. * feat: harden update config drift, command-only detection, and init profile validation Address post-implementation review findings: update now detects profile/delivery drift even when template versions are current, recognizes command-only installs as configured tools, init validates --profile values and applies delivery cleanup on re-init. Specs and design docs updated with new scenarios and rationale. * fix: address AI reviewer feedback on config, detection, and test cleanup - Add error handling for execSync in config profile apply and use static import - Fix config list showing "(explicit)" for core profile workflows misleadingly - Add missing 'openspec-onboard' to SKILL_NAMES for parity with COMMAND_IDS - Remove unused fsSync import in init tests - Fix configTempDir leak in test afterEach cleanup * docs: add qa smoke harness change proposal
1 parent 5fd8e9d commit 4ba2690

45 files changed

Lines changed: 3094 additions & 613 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
schema: spec-driven
2+
created: 2026-02-20
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
## Why
2+
3+
We need a faster, more reliable way to manually validate CLI behavior changes like profile/delivery sync, migration behavior, and tool-detection UX.
4+
5+
Today, manual review is mostly ad hoc: each developer sets up state differently, runs a different command order, and checks outputs informally. This makes regressions easy to miss and slows iteration on CLI UX work.
6+
7+
An 80/20 solution is to add a lightweight smoke harness for deterministic non-interactive flows, plus a short manual checklist for interactive prompt behavior.
8+
9+
## What Changes
10+
11+
- Add a lightweight QA smoke harness for OpenSpec CLI behavior with isolated per-run sandbox state
12+
- Use `Makefile` targets as the primary entrypoint:
13+
- `make qa` (default local QA entrypoint)
14+
- `make qa-smoke` (deterministic non-interactive suite)
15+
- `make qa-interactive` (prints/opens manual interactive checklist)
16+
- Implement smoke logic in a script (invoked by Make targets), not in Make itself
17+
- Ensure each scenario runs in an isolated sandbox with temporary `HOME`, `XDG_CONFIG_HOME`, `XDG_DATA_HOME`, and `CODEX_HOME`
18+
- Capture scenario artifacts for inspection (command output, exit code, and before/after filesystem state)
19+
- Add a focused scenario set for high-risk behavior:
20+
- init core output generation
21+
- non-interactive detected-tool behavior
22+
- migration when profile is unset
23+
- delivery cleanup (`both -> skills`, `both -> commands`)
24+
- commands-only update detection
25+
- new tool directory detection messaging
26+
- invalid profile override validation
27+
- Add a short interactive checklist for keypress/prompt UX verification (Space toggle, Enter confirm, detected pre-selection)
28+
- Wire CI to run the smoke suite on Linux as a fast regression gate
29+
30+
## Capabilities
31+
32+
### New Capabilities
33+
34+
- `qa-smoke-harness`: Deterministic, sandboxed CLI smoke validation with a single developer entrypoint
35+
36+
### Modified Capabilities
37+
38+
- `developer-qa-workflow`: Standardized local/CI QA flow for CLI behavior and migration-sensitive scenarios
39+
40+
## Impact
41+
42+
- `Makefile` - Add `qa`, `qa-smoke`, and `qa-interactive` targets
43+
- `scripts/qa-smoke.sh` (or equivalent) - Implement sandbox setup, scenario execution, and assertions
44+
- `docs/` - Add/update contributor-facing QA instructions and interactive checklist usage
45+
- CI workflow - Add smoke target execution as a lightweight regression gate
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
## ADDED Requirements
2+
3+
### Requirement: Makefile QA Entry Point
4+
5+
The repository SHALL provide Makefile targets as the primary developer entrypoint for CLI QA flows.
6+
7+
#### Scenario: Default QA target runs smoke suite
8+
9+
- **WHEN** a developer runs `make qa`
10+
- **THEN** the command SHALL execute the non-interactive smoke suite
11+
- **AND** exit with status code 0 only when all smoke scenarios pass
12+
13+
#### Scenario: Smoke suite target is directly invokable
14+
15+
- **WHEN** a developer runs `make qa-smoke`
16+
- **THEN** the command SHALL execute the same smoke suite used by `make qa`
17+
- **AND** return a non-zero exit code on assertion failure
18+
19+
#### Scenario: Interactive checklist target exists
20+
21+
- **WHEN** a developer runs `make qa-interactive`
22+
- **THEN** the command SHALL provide the manual interactive verification checklist
23+
- **AND** SHALL NOT run interactive prompt automation by default
24+
25+
### Requirement: Sandboxed Smoke Scenario Runner
26+
27+
The smoke suite SHALL run CLI scenarios in isolated sandboxes so tests are repeatable and do not depend on machine-global state.
28+
29+
#### Scenario: Scenario execution is environment-isolated
30+
31+
- **WHEN** a smoke scenario runs
32+
- **THEN** it SHALL use temporary values for `HOME`, `XDG_CONFIG_HOME`, `XDG_DATA_HOME`, and `CODEX_HOME`
33+
- **AND** global config from the host machine SHALL NOT affect scenario outcomes
34+
35+
#### Scenario: Scenario artifacts are captured for review
36+
37+
- **WHEN** a smoke scenario completes
38+
- **THEN** the runner SHALL capture command output and exit status
39+
- **AND** SHALL capture enough filesystem state to inspect before/after behavior
40+
41+
#### Scenario: High-risk workflow coverage exists
42+
43+
- **WHEN** the smoke suite executes
44+
- **THEN** it SHALL include scenarios covering profile/delivery behavior and migration-sensitive flows
45+
- **AND** include at least:
46+
- non-interactive tool detection
47+
- migration when profile is unset
48+
- delivery cleanup (`both -> skills`, `both -> commands`)
49+
- commands-only update detection

openspec/changes/simplify-skill-installation/design.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,76 @@ What's installed in `.claude/skills/` (etc.) is the source of truth, not config.
132132
- Extra workflows (not in profile) are preserved
133133
- Delivery changes are applied: switching to `skills` removes commands, switching to `commands` removes skills
134134

135+
**Why not a separate tool manifest?**
136+
137+
Tool selection (which assistants a project uses) is per-user AND per-project, but the two config locations are per-user-only (global config) or per-project-shared (checked-in project config). A separate manifest was explored and rejected:
138+
139+
- *Path-keyed global config* (`projects: { "/path": { tools: [...] } }`): Fragile on directory move/rename/delete, symlink ambiguity, and project behavior depends on invisible external state.
140+
- *Gitignored local file* (`.openspec.local`): Lost on fresh clone, adds file management overhead.
141+
- *Checked-in project config* (`openspec/config.yaml` with `tools` field): Forces tool choices on the whole team — Alice uses Claude Code, Bob uses Cursor, neither wants the other's tools mandated.
142+
143+
The filesystem approach avoids all three problems. For teams, it's actually beneficial: checked-in skill files mean `openspec update` from any team member refreshes skills for all tools the project supports. The generated files serve as both the deliverable and the implicit tool manifest.
144+
145+
Known gap: a tool that stores config outside the project tree (no local directory to scan) would need tool-specific handling, since there's nothing in the project to scan. Address if/when such a tool is supported.
146+
135147
**When to use init vs update:**
136148
- `init`: First time setup, or when you want to change which tools are configured
137149
- `update`: After changing config, or to refresh templates to latest version
138150

151+
### 8. Existing User Migration
152+
153+
When `openspec init` or `openspec update` encounters a project with existing workflows but no `profile` field in global config, it performs a one-time migration to preserve the user's current setup.
154+
155+
**Rationale:** Without migration, existing users would default to `core` profile, causing `propose` to be added on top of their 10 workflows — making things worse, not better. Migration ensures existing users keep exactly what they have.
156+
157+
**Triggered by:** Both `init` (re-init on existing project) and `update`. The migration check is a shared function called early in both commands, before profile resolution.
158+
159+
**Detection logic:**
160+
```typescript
161+
// Shared migration check, called by both init and update:
162+
function migrateIfNeeded(projectPath: string, tools: AiTool[]): void {
163+
const globalConfig = readGlobalConfig();
164+
if (globalConfig.profile) return; // already migrated or explicitly set
165+
166+
const installedWorkflows = scanInstalledWorkflows(projectPath, tools);
167+
if (installedWorkflows.length === 0) return; // new user, use core defaults
168+
169+
// Existing user — migrate to custom profile
170+
writeGlobalConfig({
171+
...globalConfig,
172+
profile: 'custom',
173+
delivery: 'both',
174+
workflows: installedWorkflows,
175+
});
176+
}
177+
```
178+
179+
**Scanning logic:**
180+
- Scan all tool directories (`.claude/skills/`, `.cursor/skills/`, etc.) for workflow directories/files
181+
- Match only against `ALL_WORKFLOWS` constant — ignore user-created custom skills/commands
182+
- Map directory names back to workflow IDs (e.g., `openspec-explore/``explore`, `opsx-explore.md``explore`)
183+
- Take the union of detected workflow names across all tools
184+
185+
**Edge cases:**
186+
- **User manually deleted some workflows:** Migration scans what's actually installed, respecting their choices
187+
- **Multiple projects with different workflow sets:** First project to trigger migration sets global config; subsequent projects use it
188+
- **User has custom (non-OpenSpec) skills in the directory:** Ignored — scanner only matches known workflow IDs from `ALL_WORKFLOWS`
189+
- **Migration is idempotent:** If `profile` is already set in config, no re-migration occurs
190+
- **Non-interactive (CI):** Same migration logic, no confirmation needed — it's preserving existing state
191+
192+
**Alternatives considered:**
193+
- Migrate during `init` instead of `update`: Init already has its own flow (tool selection, etc.). Mixing migration with init creates confusing UX
194+
- Don't migrate, just default to core: Breaks existing users by adding `propose` and showing "extra workflows" warnings
195+
- Migrate at global config read time: Too implicit, hard to show feedback to user
196+
197+
### 9. Generic Next-Step Guidance in Templates
198+
199+
Workflow templates use generic, concept-based next-step guidance rather than referencing specific workflow commands. For example, instead of "run `/opsx:propose`", templates say "create a change proposal".
200+
201+
**Rationale:** Conditional cross-referencing (where each template checks which other workflows are installed and renders different command names) adds significant complexity to template generation, testing, and maintenance. Generic guidance avoids this entirely while still being useful — users already know their installed workflows.
202+
203+
**Note:** If we find that users consistently struggle to map concepts to commands, we can revisit this with conditional cross-references. For now, simplicity wins.
204+
139205
### 7. Fix Multi-Select Keybindings
140206

141207
Change from tab-to-confirm to industry-standard space/enter.
@@ -144,6 +210,35 @@ Change from tab-to-confirm to industry-standard space/enter.
144210

145211
**Implementation:** Modify `src/prompts/searchable-multi-select.ts` keybinding configuration.
146212

213+
### 10. Update Sync Must Consider Config Drift, Not Just Version Drift
214+
215+
`openspec update` cannot rely only on `generatedBy` version checks for deciding whether work is needed.
216+
217+
**Rationale:** profile and delivery changes can require file add/remove operations even when existing skill templates are current. If we only check template versions, update may incorrectly return "up to date" and skip required sync.
218+
219+
**Implementation:**
220+
- Keep version checks for template refresh decisions
221+
- Add file-state drift checks for profile/delivery (missing expected files or stale files from removed delivery mode)
222+
- Treat either version drift OR config drift as update-required
223+
224+
### 11. Tool Configuration Detection Includes Commands-Only Installs
225+
226+
Configured-tool detection for update must include command files, not only skill files.
227+
228+
**Rationale:** with `delivery: commands`, a project can be fully configured without skill files. Skill-only detection incorrectly reports "No configured tools found."
229+
230+
**Implementation:**
231+
- For update flows, treat a tool as configured if it has either generated skills or generated commands
232+
- Keep migration workflow scanning behavior unchanged (skills remain the migration source of truth)
233+
234+
### 12. Init Profile Override Is Strictly Validated
235+
236+
`openspec init --profile` must validate allowed values before proceeding.
237+
238+
**Rationale:** silently accepting unknown profile values hides user errors and produces implicit fallback behavior.
239+
240+
**Implementation:** accept only `core` and `custom`; throw a clear CLI error for invalid values.
241+
147242
## Risks / Trade-offs
148243

149244
**Risk: Breaking existing user workflows**

openspec/changes/simplify-skill-installation/proposal.md

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -127,30 +127,43 @@ Workflows: (space to toggle, enter to save)
127127
[ ] onboard
128128
```
129129

130-
### 8. Backwards Compatibility
131-
132-
- Existing users with all workflows keep them (extra workflows not in profile are preserved)
133-
- `openspec init` sets up new projects using current profile config
134-
- `openspec update` applies config changes to existing projects (adds missing workflows, refreshes templates)
130+
### 8. Backwards Compatibility & Migration
131+
132+
**Existing users keep their current setup.** When `openspec update` runs on a project with existing workflows and no `profile` in global config, it performs a one-time migration:
133+
134+
1. Scans installed workflow files across all tool directories in the project
135+
2. Writes `profile: "custom"`, `delivery: "both"`, `workflows: [<detected>]` to global config
136+
3. Refreshes templates but does NOT add or remove any workflows
137+
4. Displays: "Migrated: custom profile with N existing workflows"
138+
139+
After migration, subsequent `init` and `update` commands respect the migrated config.
140+
141+
**Key behaviors:**
142+
- Existing users' workflows are preserved exactly as-is (no `propose` added automatically)
143+
- Both `init` (re-init) and `update` trigger migration on existing projects if no profile is set
144+
- `openspec init` on a **new** project (no existing workflows) uses global config, defaulting to `core`
145+
- `init` with a custom profile shows what will be installed and prompts to proceed or reconfigure
146+
- `init` validates `--profile` values (`core` or `custom`) and errors on invalid input
147+
- Migration message mentions `propose` and suggests `openspec config profile core` to opt in
148+
- After migration, users can opt into `core` profile via `openspec config profile core`
149+
- Workflow templates conditionally reference only installed workflows in "next steps" guidance
135150
- Delivery changes are applied: switching to `skills` removes command files, switching to `commands` removes skill files
151+
- Re-running `init` applies delivery cleanup on existing projects (removes files that no longer match delivery)
152+
- `update` treats profile/delivery drift as update-required even when template versions are already current
153+
- `update` treats command-only installs as configured tools
136154
- All workflows remain available via custom profile
137155

138156
## Capabilities
139157

140158
### New Capabilities
141159

142-
- `profiles`: Support for workflow profiles (core, custom) with interactive configuration
143-
- `delivery-config`: User preference for delivery method (skills, commands, both)
160+
- `profiles`: Workflow profiles (core, custom), delivery preferences, global config storage, interactive picker
144161
- `propose-workflow`: Combined workflow that creates change + generates all artifacts
145-
- `user-config`: Extend existing global config with profile/delivery settings
146-
- `available-tools`: Detect what AI tools the user has from existing directories
147162

148163
### Modified Capabilities
149164

150-
- `cli-init`: Smart defaults with auto-detection and confirmation
151-
- `tool-selection-ux`: Space to select, Enter to confirm
152-
- `skill-generation`: Conditional based on profile and delivery settings
153-
- `command-generation`: Conditional based on profile and delivery settings
165+
- `cli-init`: Smart defaults with tool auto-detection, profile-based skill/command generation
166+
- `cli-update`: Profile support, delivery changes, one-time migration for existing users
154167

155168
## Impact
156169

@@ -166,7 +179,7 @@ Workflows: (space to toggle, enter to save)
166179
- `src/core/shared/skill-generation.ts` - Filter by profile, respect delivery
167180
- `src/core/shared/tool-detection.ts` - Update SKILL_NAMES and COMMAND_IDS to include propose
168181
- `src/commands/config.ts` - Add `profile` subcommand with interactive picker
169-
- `src/commands/update.ts` - Add profile/delivery support, file deletion for delivery changes
182+
- `src/core/update.ts` - Add profile/delivery support, file deletion for delivery changes
170183
- `src/prompts/searchable-multi-select.ts` - Fix keybindings (space/enter)
171184

172185
### Global Config Schema Extension

openspec/changes/simplify-skill-installation/specs/available-tools/spec.md

Lines changed: 0 additions & 48 deletions
This file was deleted.

0 commit comments

Comments
 (0)