Skip to content

fix(init): only advertise slash commands the profile installs#1410

Merged
clay-good merged 1 commit into
mainfrom
fix/profile-aware-onboarding
Jul 22, 2026
Merged

fix(init): only advertise slash commands the profile installs#1410
clay-good merged 1 commit into
mainfrom
fix/profile-aware-onboarding

Conversation

@clay-good

@clay-good clay-good commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Status: LGTM — ready for review.

Fixes #1409

Stacked on #1404 (approved, mergeable). Base is fix/adapterless-skill-references; merge that first, then this retargets to main. The two are complementary: #1404 decides how a hint is spelled for each tool, this PR decides which hints appear.

What was wrong

Two onboarding surfaces told users to run /opsx:new and /opsx:continue. Neither is in the default core profile (propose/explore/apply/update/sync/archive), so OpenSpec never generates them. Users followed the instructions, the command wasn't there, and it looked like the install had failed.

openspec update before this change — not one of the three listed commands matches what was installed:

Getting started:
  /opsx:new       Start a new change
  /opsx:continue  Create the next artifact
  /opsx:apply     Implement tasks
$ ls .claude/commands/opsx/
apply.md  archive.md  explore.md  propose.md  sync.md  update.md

The same three appear on the init welcome screen. Note #1404 rewrites this same block to fix the reference form but keeps the three hardcoded workflows, so the bug survives it.

How it was fixed

getOnboardingCommands() holds the hints in lifecycle order and returns only those whose workflow is installed. Both surfaces print its result, then hand each one to #1404's referenceFor/printStartHints for spelling. After, on core:

Getting started:
  /opsx:propose  Start a change
  /opsx:apply    Implement tasks

A custom profile still sees its own:

Getting started:
  /opsx:new       Scaffold a change
  /opsx:continue  Next artifact
  /opsx:apply     Implement tasks

Two subtleties:

  • update filters by what was installed, not by the profile. A legacy upgrade infers Codex's workflows from its prompt filenames, so that tool can receive a narrower set. The menu lists the union of what the newly configured tools actually got.
  • The welcome screen is width-constrained. It renders beside a 24-column art column and only animates at 60 columns or wider, and the animation moves the cursor up a fixed count of logical lines — a wrapped line desyncs it and the frame walks down the screen. Descriptions are capped at DESCRIPTION_BUDGET so the widest rendered line stays at 59, the budget the screen has always kept.

Also: --profile is now validated before the welcome screen instead of being cast, so an invalid value fails before the user presses Enter rather than after. (getProfileWorkflows never throws on an unknown profile — anything that isn't literally 'custom' returns CORE_WORKFLOWS — so the old cast silently rendered a plausible core screen.)

Proof it works

Real output from the built CLI, three tool surfaces:

Setup Hint printed On disk
--tools claude (delivery both) /opsx:propose "your idea" .claude/commands/opsx/propose.md
--tools kimi (no adapter) /skill:openspec-propose "your idea" skills only, commands skipped
--tools claude, delivery skills /openspec-propose "your idea" no commands directory at all
  • test/core/onboarding-commands.test.ts — core yields propose/apply; a custom profile yields new/continue; order is stable; empty when nothing applies; descriptions stay within the width budget.
  • test/ui/welcome-screen.test.ts — the rendered screen contains the installed commands and not the others, drops the quick-start block entirely when none apply, and no rendered line exceeds 59 columns.
  • test/core/update.test.ts — the legacy-upgrade test asserted /opsx:new, i.e. it pinned the bug; it now asserts /opsx:propose and the absence of new/continue. Added a custom-profile case. fix(init): use skill references for tools without a command adapter #1404's Codex menu test now uses an opsx-new.md fixture so it still proves the skill-reference form under filtering, and additionally asserts the rest of the profile is not advertised.
  • test/core/init.test.ts — pins that showWelcomeScreen receives the profile's workflows, so the wiring can't silently regress.
  • Each new assertion was mutation-checked: reverting the matching source change makes it fail.
  • Full suite 2,056 passing; the 17 zsh-installer failures are the known local Oh My Zsh issue (test(completion): isolate ZshInstaller tests from a real Oh My Zsh install #1400), unaffected in CI. tsc clean, lint clean, madge --circular dist/ clean.

Notes / nits

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Onboarding and “Getting started” guidance now reflects the workflows installed in the active profile.
    • Welcome screens dynamically list available commands and provide tool-appropriate instructions.
    • Added clearer restart guidance for command-based and skills-based setups.
  • Bug Fixes

    • Removed misleading commands that were not available for certain profiles or tools.
    • Updated legacy upgrade messaging to show only applicable workflows.
  • Tests

    • Added coverage for profile-specific onboarding commands and welcome-screen output.

@clay-good
clay-good requested a review from TabishB as a code owner July 21, 2026 16:38
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Onboarding guidance is now derived from installed workflows and tool capabilities. Initialization, updates, and the welcome screen avoid advertising unavailable commands, render skill-specific references, and include profile-aware tests and changeset documentation.

Changes

Onboarding and skill surfaces

Layer / File(s) Summary
Workflow-aware onboarding contract
src/core/onboarding-commands.ts, test/core/onboarding-commands.test.ts
Adds ordered workflow-to-command mappings, description limits, filtering, and coverage for supported profiles.
Tool-specific skill reference resolution
src/core/init.ts
Passes resolved command-surface capabilities into skill-content transformation.
Init, update, and migration output
src/core/init.ts, src/core/update.ts, src/ui/welcome-screen.ts
Initialization, updates, and welcome rendering derive onboarding hints from active workflows and generated command or skill surfaces.
Skill guidance and release validation
.changeset/profile-aware-onboarding-commands.md, test/core/*, test/ui/welcome-screen.test.ts
Documents corrected guidance and validates profile-specific commands, skill references, output suppression, and rendering widths.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant InitCommand
  participant showWelcomeScreen
  participant getOnboardingCommands
  InitCommand->>InitCommand: resolve active profile workflows
  InitCommand->>showWelcomeScreen: pass active workflows
  showWelcomeScreen->>getOnboardingCommands: filter installed workflow hints
  getOnboardingCommands-->>showWelcomeScreen: return matching commands
Loading

Possibly related PRs

Suggested reviewers: tabishb, showms, alfred-openspec

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main behavioral change: profile-aware slash command advertising.
Linked Issues check ✅ Passed The PR updates init, update, and welcome-screen output to only advertise workflows installed by the active profile, matching #1409.
Out of Scope Changes check ✅ Passed The changes stay focused on onboarding messaging, workflow resolution, and related tests, with no unrelated functionality added.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/profile-aware-onboarding

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/core/onboarding-commands.ts (1)

11-15: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Export the OnboardingCommand type.

Since OnboardingCommand is used as the return type of the exported getOnboardingCommands function, it's best practice to export it. This prevents potential TS4023 compiler errors in strict configurations where declaration files are emitted.

💡 Proposed fix
-type OnboardingCommand = {
+export type OnboardingCommand = {
   workflow: WorkflowId;
   command: string;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/onboarding-commands.ts` around lines 11 - 15, Export the
OnboardingCommand type so it is publicly available alongside the exported
getOnboardingCommands function and can be referenced in emitted declarations.
Keep its existing fields and types unchanged.
src/core/init.ts (1)

817-820: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consolidate active workflow resolution.

This block recalculates the active workflows and repeats the profile cast. You can reuse this.getActiveWorkflows() to streamline the logic.

♻️ Proposed refactor
     if (successfulTools.length > 0) {
-      const globalConfig = getGlobalConfig();
-      const profile: Profile = (this.profileOverride as Profile) ?? globalConfig.profile ?? 'core';
-      const delivery: Delivery = globalConfig.delivery ?? 'both';
-      const workflows = getProfileWorkflows(profile, globalConfig.workflows);
+      const delivery: Delivery = getGlobalConfig().delivery ?? 'both';
+      const workflows = this.getActiveWorkflows();
       const toolDirs = [...new Set(successfulTools.map((t) => t.skillsDir))].join(', ');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/init.ts` around lines 817 - 820, Replace the local profile and
workflow resolution in the initialization block with this.getActiveWorkflows(),
reusing its established profile handling and active-workflow calculation. Keep
the globalConfig lookup for delivery unchanged and remove only the now-redundant
profile and workflows declarations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/core/init.ts`:
- Around line 255-259: Update getActiveWorkflows to obtain the profile override
through this.resolveProfileOverride() instead of casting this.profileOverride as
Profile, while preserving the existing global profile and 'core' fallbacks so
invalid interactive profile values are validated before workflow resolution and
prompting.

---

Nitpick comments:
In `@src/core/init.ts`:
- Around line 817-820: Replace the local profile and workflow resolution in the
initialization block with this.getActiveWorkflows(), reusing its established
profile handling and active-workflow calculation. Keep the globalConfig lookup
for delivery unchanged and remove only the now-redundant profile and workflows
declarations.

In `@src/core/onboarding-commands.ts`:
- Around line 11-15: Export the OnboardingCommand type so it is publicly
available alongside the exported getOnboardingCommands function and can be
referenced in emitted declarations. Keep its existing fields and types
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3bd2b25e-8304-4195-a83e-0124ab524381

📥 Commits

Reviewing files that changed from the base of the PR and between 60f720c and dc24b27.

📒 Files selected for processing (8)
  • .changeset/profile-aware-onboarding-commands.md
  • src/core/init.ts
  • src/core/onboarding-commands.ts
  • src/core/update.ts
  • src/ui/welcome-screen.ts
  • test/core/onboarding-commands.test.ts
  • test/core/update.test.ts
  • test/ui/welcome-screen.test.ts

Comment thread src/core/init.ts

@alfred-openspec alfred-openspec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The profile-aware hints and coverage look right, but please have getActiveWorkflows() call resolveProfileOverride() instead of casting the raw override. As written, an invalid interactive --profile renders and waits on the welcome screen before failing, so this should validate before prompting.

@clay-good

Copy link
Copy Markdown
Collaborator Author

Thanks — all three CodeRabbit findings are addressed in caf78c7, along with two further issues found by an independent adversarial review pass.

Addressed from this review

  • init.ts:257 — validate the profile override. Done, and slightly further than suggested: getActiveWorkflows() now calls this.resolveProfileOverride() instead of casting, and the explicit validation call was moved above the welcome screen. Previously --profile bogus fell through getProfileWorkflows to CORE_WORKFLOWS (it never throws on an unknown profile — see profiles.ts:44-51), painted a full welcome screen, and only errored after the user pressed Enter. Now it fails before anything is drawn.
  • init.ts:817-820 — consolidate. Done; displaySuccessMessage() reuses this.getActiveWorkflows() and only reads delivery from the global config.
  • Export OnboardingCommand. Done.

Found separately, also fixed here

  • The quick-start block overflowed the welcome screen (the one real defect). With /opsx:continue present, the widest rendered line was 66 columns against the 59 the screen has always kept. canAnimate() only bails below 60 columns, so at 60-65 columns the lines wrapped while the animation's \x1b[{frameHeight}A stayed on logical lines — every 120 ms frame redrew two rows lower until the screen scrolled. Descriptions are now capped at DESCRIPTION_BUDGET, with a test asserting no rendered line exceeds 59.
  • update.ts advertised the profile, not what was installed. A legacy upgrade sets legacyWorkflowOverrides for Codex from its inferred prompt filenames, so the tool can receive a narrower set than desiredWorkflows. It now lists the union of what the newly configured tools actually got.

Also added coverage for the initshowWelcomeScreen wiring, which was previously unpinned. Each new test was mutation-checked: reverting the matching source fix makes it fail.

Not changed, deliberately: the /opsx: prefix is not universal (delivery: 'skills' generates no slash commands; opencode/pi use /opsx-). That is a separate axis from the profile, is unchanged from main, and is exactly #962's design. Noted as a scope limit in the PR description.

@clay-good

Copy link
Copy Markdown
Collaborator Author

@alfred-openspec Addressed in caf78c7getActiveWorkflows() now calls this.resolveProfileOverride() instead of casting:

private getActiveWorkflows(): string[] {
  const globalCfg = getGlobalConfig();
  const activeProfile: Profile = this.resolveProfileOverride() ?? globalCfg.profile ?? 'core';
  return [...getProfileWorkflows(activeProfile, globalCfg.workflows)];
}

I also moved the explicit validation call above the welcome-screen block in execute(), so the failure is early on every path rather than only the one that happens to resolve workflows. Worth noting the old behavior was quieter than a crash: getProfileWorkflows never throws on an unknown profile — anything that is not the literal 'custom' returns CORE_WORKFLOWS (profiles.ts:44-51) — so --profile bogus rendered a plausible-looking core welcome screen and only errored after Enter.

The push also fixes two things an adversarial review pass turned up, both described in the updated PR body: the quick-start block overflowed the welcome screen's 59-column budget on profiles installing /opsx:continue (which desyncs the animation's fixed cursor-up count between 60 and 65 columns), and openspec update advertised the profile's workflows rather than the narrower set a legacy Codex upgrade actually installs. Both now have tests, and every new test was mutation-checked.

CI is green on linux, macOS, and Windows. Ready for another look.

@alfred-openspec alfred-openspec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The profile filtering, width guard, early profile validation, and 140 focused tests look good, but this head overlaps the approved #1404 fix in the same legacy-upgrade menu and currently pins a dead Codex instruction: the new test expects /opsx:apply even though Codex has no slash-command surface. Please merge or rebase onto #1404, combine getOnboardingCommands workflow filtering with its capability-aware reference selection, and assert that the Codex apply-only case names the openspec-apply-change skill rather than /opsx:apply.

@clay-good

Copy link
Copy Markdown
Collaborator Author

@alfred-openspec You are right on the substance and I have fixed it in a8f3489 — thank you, this was a real dead instruction.

Confirmed the claim before acting: resolveCommandSurfaceCapability('codex') returns skills-invocable (command-surface.ts:10-12), so shouldGenerateCommandsForTool('codex', …) is false for every delivery mode — Codex never receives an /opsx:* file. Skills-only delivery does the same for every tool. Both were still being told to run /opsx:<workflow>.

What changed. A new labelOnboardingCommand(command, toolIds, delivery) renders the hint the configured tools can actually invoke, falling back to the skill name through the existing transformToSkillReferences. Applied to the update legacy-upgrade summary and the init completion hint — the two surfaces that know their tools. The Codex test now asserts what you asked for:

expect(output).toContain('/openspec-apply-change');
expect(output).not.toContain('/opsx:apply');

Verified end-to-end against the built CLI, not just unit tests — init --tools claude under delivery: skills now prints Start your first change: /openspec-propose "your idea" and writes no .claude/commands/opsx/ at all; under delivery: both it still prints /opsx:propose and the command files are there.

Two things I did not do, and why — please push back if you disagree.

  1. No rebase onto fix(init): use skill references for tools without a command adapter #1404. I did not need one: this uses transformToSkillReferences, which is already on main. Rebasing would also not have delivered the assertion you asked for on its own — fix(init): use skill references for tools without a command adapter #1404's own comment says "skills-invocable tools (codex) are deliberately left untouched here to keep codex output stable while its reference rewriting is reworked separately." Keeping the PRs independent avoids coupling an approved PR to this one; they touch different regions of update.ts and init.ts.
  2. The welcome screen still says /opsx:*. It renders before tool selection, so only delivery is knowable there — and its two-column layout physically cannot fit skill names: /openspec-continue-change puts the line at 70 columns against the 59-column budget that keeps the animation from desyncing. Fixing that means redesigning the layout, which is squarely fix: clarify onboarding across tool syntax, workflows, and delivery #962's territory. Called out as a scope limit in the PR body rather than half-done.

Also unchanged and worth a follow-up: init still prints "Restart your IDE for slash commands to take effect" under skills-only delivery.

Full suite 2,043 passing (the 17 zsh-installer failures are the known local Oh My Zsh issue, #1400). tsc clean, lint clean, and madge --circular dist/ confirms the new core -> command-surface import introduced no cycle. Each new assertion was mutation-checked.

@alfred-openspec alfred-openspec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Codex case is fixed, but the core blocker remains for other skill-only tools. At this exact head, labelOnboardingCommand('/opsx:propose', ['kimi'], 'both') returns /openspec-propose; Kimi's documented invocation is /skill:openspec-propose. #1404 at f5c2de1 is already approved and implements the tool-specific transformer plus mixed-tool handling, so please rebase/merge it and reuse that path instead of landing a parallel generic labeler that is merge-order dependent.

@clay-good
clay-good force-pushed the fix/profile-aware-onboarding branch from a8f3489 to 6e084b0 Compare July 21, 2026 17:56
@clay-good
clay-good changed the base branch from main to fix/adapterless-skill-references July 21, 2026 17:56
@clay-good

Copy link
Copy Markdown
Collaborator Author

Rebased onto #1404 as you asked (6e084b0), and retargeted this PR's base to fix/adapterless-skill-references so the diff is just my 9 files.

I dropped my labelOnboardingCommand entirely — your point about it being a parallel, merge-order-dependent path was correct. Reference spelling now goes through #1404's referenceFor/printStartHints, so the division is clean: #1404 decides how a hint is spelled, this PR decides which hints appear.

Verified against the built CLI, all three surfaces:

Setup Hint printed On disk
--tools claude (delivery both) /opsx:propose "your idea" .claude/commands/opsx/propose.md
--tools kimi (no adapter) /skill:openspec-propose "your idea" skills only
--tools claude, delivery skills /openspec-propose "your idea" no commands directory

Kimi now gets /skill:openspec-propose — the case you flagged.

One thing to flag, since it means #1404 alone does not close #1409. #1404 rewrites the same update.ts legacy-upgrade block but keeps the three hardcoded workflows:

const entries: Array<[string, string]> = [
  [referenceFor('/opsx:new'), 'Start a new change'],
  [referenceFor('/opsx:continue'), 'Create the next artifact'],
  [referenceFor('/opsx:apply'), 'Implement tasks'],
];

On the default core profile none of new/continue exist, so #1404 would spell an unavailable workflow correctly. That is the gap this PR closes; the two are complementary rather than redundant.

I had to touch one of your testsshould print a skill-based getting-started menu when a legacy upgrade newly configures codex. Its fixture was opsx-explore.md, and explore is not an onboarding hint, so under filtering the menu correctly disappears and the test could not pass as written. I changed the fixture to opsx-new.md, which keeps it proving exactly what you wrote it to prove (the openspec-new-change skill, no /opsx:*), and added assertions that the rest of the profile is not advertised. Please sanity-check that I preserved your intent.

Full suite 2,056 passing (17 zsh-installer failures are the known local Oh My Zsh issue, #1400). tsc, lint, and madge --circular dist/ all clean.

Merge order: #1404 first, then this retargets to main.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
test/core/init.test.ts (1)

954-1162: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract repeated logCalls boilerplate into a helper.

The same (console.log as unknown as { mock: { calls: unknown[][] } }).mock.calls.flat().map(String) cast is repeated ~9 times across this new suite. A small helper (ideally using vi.mocked(console.log) instead of the unsafe as unknown as cast) would remove the duplication and the type-safety bypass.

♻️ Proposed helper
+function getLogCalls(): string[] {
+  return vi.mocked(console.log).mock.calls.flat().map(String);
+}

Then replace each occurrence, e.g.:

-    const logCalls = (console.log as unknown as { mock: { calls: unknown[][] } }).mock.calls.flat().map(String);
+    const logCalls = getLogCalls();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/core/init.test.ts` around lines 954 - 1162, Extract the repeated
console-log call collection into a shared test helper near the suite setup,
preferably using vi.mocked(console.log) to avoid the unsafe cast. Replace every
local logCalls declaration in these tests with the helper while preserving the
existing flattening and string conversion behavior.
src/core/update.ts (1)

334-348: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Consider extracting the shared "syntax-neutral onboarding reference" resolver. These two sites encode the same three-branch rule (command form → skills-invocable neutral skill → tool-specific skill reference, with a syntax-neutral fallback when tools disagree). Keeping two copies risks the fallback contract drifting between the update and migration surfaces.

  • src/core/update.ts#L334-L348: replace the inline referenceFor set-building with a call to a shared resolveOnboardingReference(command, tools, delivery).
  • src/core/migration.ts#L219-L232: derive proposeReference from the same shared helper (passing /opsx:propose) instead of the duplicated set-building.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/update.ts` around lines 334 - 348, The duplicated
onboarding-reference resolution logic must be centralized to preserve one
fallback contract. Add a shared resolveOnboardingReference(command, tools,
delivery) helper implementing the command-form, skills-invocable neutral form,
tool-specific transformer, and disagreement fallback rules; update
src/core/update.ts lines 334-348 to call it for referenceFor, and update
src/core/migration.ts lines 219-232 to derive proposeReference through the same
helper using /opsx:propose.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/core/update.ts`:
- Around line 334-348: The duplicated onboarding-reference resolution logic must
be centralized to preserve one fallback contract. Add a shared
resolveOnboardingReference(command, tools, delivery) helper implementing the
command-form, skills-invocable neutral form, tool-specific transformer, and
disagreement fallback rules; update src/core/update.ts lines 334-348 to call it
for referenceFor, and update src/core/migration.ts lines 219-232 to derive
proposeReference through the same helper using /opsx:propose.

In `@test/core/init.test.ts`:
- Around line 954-1162: Extract the repeated console-log call collection into a
shared test helper near the suite setup, preferably using vi.mocked(console.log)
to avoid the unsafe cast. Replace every local logCalls declaration in these
tests with the helper while preserving the existing flattening and string
conversion behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e988996d-6ca6-4db3-b84b-986131a19a8e

📥 Commits

Reviewing files that changed from the base of the PR and between a8f3489 and 6e084b0.

📒 Files selected for processing (23)
  • .changeset/adapterless-skill-references.md
  • .changeset/profile-aware-onboarding-commands.md
  • scripts/generate-skillssh.mjs
  • skills/openspec-apply-change/SKILL.md
  • skills/openspec-explore/SKILL.md
  • skills/openspec-ff-change/SKILL.md
  • skills/openspec-onboard/SKILL.md
  • skills/openspec-propose/SKILL.md
  • skills/openspec-update-change/SKILL.md
  • src/core/init.ts
  • src/core/migration.ts
  • src/core/onboarding-commands.ts
  • src/core/update.ts
  • src/ui/welcome-screen.ts
  • src/utils/command-references.ts
  • src/utils/index.ts
  • test/core/init.test.ts
  • test/core/migration.test.ts
  • test/core/onboarding-commands.test.ts
  • test/core/templates/skillssh-parity.test.ts
  • test/core/update.test.ts
  • test/ui/welcome-screen.test.ts
  • test/utils/command-references.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • .changeset/profile-aware-onboarding-commands.md
  • src/ui/welcome-screen.ts
  • test/ui/welcome-screen.test.ts

@clay-good

Copy link
Copy Markdown
Collaborator Author

Both CodeRabbit nitpicks on the latest pass land on #1404's code, not this PR's. Confirmed with git blame against the stacked range:

They are only visible here because this PR now stacks on fix/adapterless-skill-references. Leaving both for #1404 rather than editing its code from a downstream branch — the resolveOnboardingReference extraction in particular is a sensible cleanup, but it belongs in the PR that introduced the duplication.

No outstanding actionable items on this PR's own diff. CI green on linux, macOS, and Windows; mergeStateStatus is now CLEAN.

@alfred-openspec alfred-openspec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified at 6e084b0 on top of approved #1404 f5c2de1: profile filtering now composes with the tool-specific reference path, including Kimi, Codex, mixed-tool, legacy-upgrade, and width-bound cases. Build, clean skill regeneration, 218 focused tests, and exact-head CI pass.

Base automatically changed from fix/adapterless-skill-references to main July 22, 2026 13:59
@clay-good
clay-good dismissed alfred-openspec’s stale review July 22, 2026 13:59

The base branch was changed.

Fixes #1409. The `openspec init` welcome screen and the `openspec update`
legacy-upgrade menu hardcoded /opsx:new and /opsx:continue. The default core
profile is propose/explore/apply/update/sync/archive, so it never generates
them and users were told to run commands that did not exist.

getOnboardingCommands() holds the hints in lifecycle order and returns only
those whose workflow is installed; both surfaces print its result. In
`update` the set is what the newly configured tools actually received, since
a legacy upgrade installs an inferred subset for Codex.

Stacked on #1404, which decides how each hint is spelled per tool. This
commit decides which hints appear; #1404's referenceFor/printStartHints
decide the reference form, so Kimi still gets /skill:openspec-*.

The welcome screen's quick-start block is width-constrained: it renders
beside a 24-column art column and only animates at MIN_WIDTH (60) or wider,
and the animation moves the cursor up a fixed count of logical lines. A
wrapped line desyncs it, so descriptions are capped at DESCRIPTION_BUDGET
and a test asserts no rendered line exceeds 59.

Also validates --profile before the welcome screen rather than casting it,
so an invalid value fails before the user presses Enter.
@clay-good
clay-good force-pushed the fix/profile-aware-onboarding branch from 6e084b0 to 4e02766 Compare July 22, 2026 14:01

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/core/update.ts (1)

333-360: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Exclude tools that produced no invocation surface.

An adapterless legacy tool under delivery: 'commands' is added to newlyConfiguredTools even though it receives neither skills nor commands. It then contributes a /skill: reference and workflows here, advertising artifacts that do not exist. Filter the tools used by referenceFor and installedWorkflows to those for which shouldGenerateSkillsForTool(...) || shouldGenerateCommandsForTool(...) is true, and add an update regression case for this delivery mode.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/update.ts` around lines 333 - 360, Filter newlyConfiguredTools
before referenceFor and installedWorkflows to retain only tools where
shouldGenerateSkillsForTool(toolId, delivery) or
shouldGenerateCommandsForTool(toolId, delivery) is true, preventing adapterless
legacy tools from advertising nonexistent artifacts. Add a regression test
covering delivery: 'commands' with a tool that produces neither skills nor
commands.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/core/update.ts`:
- Around line 333-360: Filter newlyConfiguredTools before referenceFor and
installedWorkflows to retain only tools where
shouldGenerateSkillsForTool(toolId, delivery) or
shouldGenerateCommandsForTool(toolId, delivery) is true, preventing adapterless
legacy tools from advertising nonexistent artifacts. Add a regression test
covering delivery: 'commands' with a tool that produces neither skills nor
commands.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6ef521da-03f2-46b9-bb5e-3263d399481b

📥 Commits

Reviewing files that changed from the base of the PR and between 6e084b0 and 4e02766.

📒 Files selected for processing (9)
  • .changeset/profile-aware-onboarding-commands.md
  • src/core/init.ts
  • src/core/onboarding-commands.ts
  • src/core/update.ts
  • src/ui/welcome-screen.ts
  • test/core/init.test.ts
  • test/core/onboarding-commands.test.ts
  • test/core/update.test.ts
  • test/ui/welcome-screen.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • test/core/onboarding-commands.test.ts
  • src/core/onboarding-commands.ts
  • src/ui/welcome-screen.ts
  • src/core/init.ts

@alfred-openspec alfred-openspec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The patch is identical to the previously approved stack and now sits cleanly on merged #1404. Exact-head build and 209 focused tests pass; every currently eligible legacy-upgrade tool produces a valid surface under commands-only delivery, so the flagged zero-surface path is not reachable.

@clay-good
clay-good added this pull request to the merge queue Jul 22, 2026
Merged via the queue into main with commit b3b05e1 Jul 22, 2026
14 checks passed
@clay-good
clay-good deleted the fix/profile-aware-onboarding branch July 22, 2026 14:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Onboarding output advertises /opsx:new and /opsx:continue, which the default core profile never installs

2 participants