[GH-ISSUE #6840] [Bug] Interactive setup omits the CLI skill choice when a detected personal copy is current #4183

Closed
opened 2026-10-31 00:47:31 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @coygeek on GitHub (Oct 23, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/6840

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Setup, packaging, or upgrade

Observed behavior

In an executed production-workflow probe, an already-current Claude Code personal skill prevents interactive setup from offering the shared CLI skill, even when that shared copy needs installation and the operator selects no clients.

The probe injects the repository's recording setup host: the personal copy is current, the shared copy is absent, and the callback selects no clients but accepts the skill if offered. With Claude Code detected, selection receives no skill action and installation is never called. With no client detected, the same shared-skill state produces the skill action and installation is called once.

This is an observed runSetup result with injected host facts, not a terminal or real-filesystem reproduction. The missing terminal prompt follows from source: selectSetupActions asks “Install the guided REA skill for CLI use?” only when its action list contains install_skill.

Reproduction

From the source checkout with locked dependencies installed, run:

node --import tsx --input-type=module <<'JS'
import { runSetup } from './src/application/Setup.ts';
import { FakeSetupHost } from './src/application/Setup.fixture.ts';
for (const detectedPersonal of [true, false]) {
  const host = new FakeSetupHost('darwin');
  host.hopper = '/tmp/rea-example/Hopper';
  host.doctorHealthy = true;
  if (detectedPersonal) host.clients = [{
    name: 'claude_code', format: 'claude_code',
    configPath: '/tmp/rea-example/home/.claude.json',
  }];
  const checkedScopes = [];
  // A current personal copy and an absent shared copy.
  host.skillNeedsInstall = async ids => {
    checkedScopes.push([...ids]);
    return ids.length === 0 || ids.includes('shared');
  };
  let offeredSkill = false;
  const stages = [];
  const result = await runSetup({
    approved: false, installHopper: false, structured: false,
  }, host, async (actions, context) => {
    stages.push({ stage: context?.stage, actions: actions.map(a => a.id) });
    if (context?.stage === 'select') {
      const skill = actions.find(a => a.kind === 'install_skill');
      offeredSkill = skill !== undefined;
      return {
        approved: false,
        selectedActionIds: skill === undefined ? [] : [skill.id],
      };
    }
    return true;
  });
  console.log(JSON.stringify({
    detectedPersonal, checkedScopes, offeredSkill, stages,
    status: result.status, skillInstalls: host.skillInstalls,
  }));
}
JS

One execution produced:

{"detectedPersonal":true,"checkedScopes":[[],["claude_code"],[]],"offeredSkill":false,"stages":[{"stage":"select","actions":[]}],"status":"ready","skillInstalls":0}
{"detectedPersonal":false,"checkedScopes":[[],[],[]],"offeredSkill":true,"stages":[{"stage":"select","actions":["install_skill"]},{"stage":"confirm","actions":["install_skill"]}],"status":"ready","skillInstalls":1}

All effects use the recording host; no actual home, client, provider, or skill files are changed.

Expected behavior

When the operator chooses no clients, offer the shared CLI skill whenever that destination needs installation, independently of the state of detected clients' personal copies. The documented destination rule says selecting the skill without a client uses the shared directory. The interactive prompt explicitly supports that choice.

Impact and scope

A CLI-only onboarding choice disappears for users who have an up-to-date detected personal skill but lack the shared skill. The setup workflow completes without offering or installing the shared copy. The observed scope is the injected Claude Code case and no-detected-client control. Other clients with personal skill roots are source-derived possibilities, not tested cases. No data loss was observed.

Explicitly selecting the shared skill bypasses the omission. In a separate executed recording-host control, clientIds: [], skillClientIds: ['shared'], and installSkill: true produced an install_skill plan and one installation call. The corresponding public selection is rea setup --skill-client shared --yes; that full CLI command was not executed.

REA version

Package 6.7.0; source revision 169512a35bb015ff209168fb8bc1c9b31a2c678b.

Runtime and platform

Node.js v26.12.1; Linux 7.2.8-9-omarchy, x86_64. The injected setup host reports darwin; this does not establish actual macOS behavior. No real analysis provider involved.

Target and artifact context

Synthetic setup state with one current personal skill, one absent shared skill, and no clients selected by the confirmation callback. No target artifact.

Interface

Package or setup

Earliest failure stage

Setup or provider admission

Relevant output or evidence

The reduced output above was obtained by executing runSetup with the repository's FakeSetupHost. The explicit-selection control returned:

{"status":"ready","plannedActions":["install_skill"],"skillInstalls":1}

Setup decides whether to offer the skill using the union of default and detected client IDs. A current personal copy makes this check false even though the earlier shared-scope check returned true. After client selection, installation with no clients requires an already-offered skill action to have been selected. An empty client selection maps to the shared skill root, so the two checks concern different destinations.

NOT_ENOUGH_INFO: no real terminal interaction, packaged runtime, or current personal skill files were exercised. The prompt consequence is source-derived from the traced production branches, while the offered action list and installation-call counts were observed.

Typed outcome or recovery expected

Present the shared skill choice after the operator selects no clients. Keep current personal copies untouched and retain the normal approval step before installation.

Analysis or suspected cause

Source-derived: setup decides whether the skill may be offered before it knows the final destination. The preselection check examines detected clients' personal destinations; the no-client choice later changes the destination to shared without reevaluating whether the action must be offered.

Additional context

The initial open-issue inventory was searched in titles and bodies for interactive selection and shared/personal skill terms. Related #6451 concerns doctor recovery selecting the wrong stale copy; this case concerns omission of the CLI-only interactive choice. #6434 concerns an unresolved Hermes destination. Closed #5856 concerns dropping a skill action after the user accepts its prompt. This report is the preceding offer decision: when the personal copy is current, setup never offers the missing shared copy at all.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: The production setup workflow omits a supported installation choice for a bounded mixed-destination state and never calls the requested skill installation path.
  • Workaround status: available
  • Workaround basis: Explicit shared-destination selection produced an install plan and one installation call in the recording-host control; real-filesystem execution of the public CLI workaround remains untested.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6840 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Setup, packaging, or upgrade ### Observed behavior In an executed production-workflow probe, an already-current Claude Code personal skill prevents interactive setup from offering the shared CLI skill, even when that shared copy needs installation and the operator selects no clients. The probe injects the repository's recording setup host: the personal copy is current, the shared copy is absent, and the callback selects no clients but accepts the skill if offered. With Claude Code detected, selection receives no skill action and installation is never called. With no client detected, the same shared-skill state produces the skill action and installation is called once. This is an observed `runSetup` result with injected host facts, not a terminal or real-filesystem reproduction. The missing terminal prompt follows from source: `selectSetupActions` asks “Install the guided REA skill for CLI use?” only when its action list contains `install_skill`. ### Reproduction From the source checkout with locked dependencies installed, run: ```sh node --import tsx --input-type=module <<'JS' import { runSetup } from './src/application/Setup.ts'; import { FakeSetupHost } from './src/application/Setup.fixture.ts'; for (const detectedPersonal of [true, false]) { const host = new FakeSetupHost('darwin'); host.hopper = '/tmp/rea-example/Hopper'; host.doctorHealthy = true; if (detectedPersonal) host.clients = [{ name: 'claude_code', format: 'claude_code', configPath: '/tmp/rea-example/home/.claude.json', }]; const checkedScopes = []; // A current personal copy and an absent shared copy. host.skillNeedsInstall = async ids => { checkedScopes.push([...ids]); return ids.length === 0 || ids.includes('shared'); }; let offeredSkill = false; const stages = []; const result = await runSetup({ approved: false, installHopper: false, structured: false, }, host, async (actions, context) => { stages.push({ stage: context?.stage, actions: actions.map(a => a.id) }); if (context?.stage === 'select') { const skill = actions.find(a => a.kind === 'install_skill'); offeredSkill = skill !== undefined; return { approved: false, selectedActionIds: skill === undefined ? [] : [skill.id], }; } return true; }); console.log(JSON.stringify({ detectedPersonal, checkedScopes, offeredSkill, stages, status: result.status, skillInstalls: host.skillInstalls, })); } JS ``` One execution produced: ```json {"detectedPersonal":true,"checkedScopes":[[],["claude_code"],[]],"offeredSkill":false,"stages":[{"stage":"select","actions":[]}],"status":"ready","skillInstalls":0} {"detectedPersonal":false,"checkedScopes":[[],[],[]],"offeredSkill":true,"stages":[{"stage":"select","actions":["install_skill"]},{"stage":"confirm","actions":["install_skill"]}],"status":"ready","skillInstalls":1} ``` All effects use the recording host; no actual home, client, provider, or skill files are changed. ### Expected behavior When the operator chooses no clients, offer the shared CLI skill whenever that destination needs installation, independently of the state of detected clients' personal copies. [The documented destination rule](https://github.com/morluto/rea/blob/169512a35bb015ff209168fb8bc1c9b31a2c678b/docs/installation.md#L149-L158) says selecting the skill without a client uses the shared directory. [The interactive prompt](https://github.com/morluto/rea/blob/169512a35bb015ff209168fb8bc1c9b31a2c678b/src/cli/interactiveSetup.ts#L135-L146) explicitly supports that choice. ### Impact and scope A CLI-only onboarding choice disappears for users who have an up-to-date detected personal skill but lack the shared skill. The setup workflow completes without offering or installing the shared copy. The observed scope is the injected Claude Code case and no-detected-client control. Other clients with personal skill roots are source-derived possibilities, not tested cases. No data loss was observed. Explicitly selecting the shared skill bypasses the omission. In a separate executed recording-host control, `clientIds: []`, `skillClientIds: ['shared']`, and `installSkill: true` produced an `install_skill` plan and one installation call. The corresponding public selection is `rea setup --skill-client shared --yes`; that full CLI command was not executed. ### REA version Package 6.7.0; source revision `169512a35bb015ff209168fb8bc1c9b31a2c678b`. ### Runtime and platform Node.js v26.12.1; Linux 7.2.8-9-omarchy, x86_64. The injected setup host reports `darwin`; this does not establish actual macOS behavior. No real analysis provider involved. ### Target and artifact context Synthetic setup state with one current personal skill, one absent shared skill, and no clients selected by the confirmation callback. No target artifact. ### Interface Package or setup ### Earliest failure stage Setup or provider admission ### Relevant output or evidence The reduced output above was obtained by executing `runSetup` with the repository's `FakeSetupHost`. The explicit-selection control returned: ```json {"status":"ready","plannedActions":["install_skill"],"skillInstalls":1} ``` [Setup decides whether to offer the skill using the union of default and detected client IDs](https://github.com/morluto/rea/blob/169512a35bb015ff209168fb8bc1c9b31a2c678b/src/application/Setup.ts#L156-L181). A current personal copy makes this check false even though the earlier shared-scope check returned true. [After client selection, installation with no clients requires an already-offered skill action to have been selected](https://github.com/morluto/rea/blob/169512a35bb015ff209168fb8bc1c9b31a2c678b/src/application/Setup.ts#L202-L226). [An empty client selection maps to the shared skill root](https://github.com/morluto/rea/blob/169512a35bb015ff209168fb8bc1c9b31a2c678b/src/application/SupportedClients.ts#L820-L830), so the two checks concern different destinations. NOT_ENOUGH_INFO: no real terminal interaction, packaged runtime, or current personal skill files were exercised. The prompt consequence is source-derived from the traced production branches, while the offered action list and installation-call counts were observed. ### Typed outcome or recovery expected Present the shared skill choice after the operator selects no clients. Keep current personal copies untouched and retain the normal approval step before installation. ### Analysis or suspected cause Source-derived: setup decides whether the skill may be offered before it knows the final destination. The preselection check examines detected clients' personal destinations; the no-client choice later changes the destination to shared without reevaluating whether the action must be offered. ### Additional context The initial open-issue inventory was searched in titles and bodies for interactive selection and shared/personal skill terms. Related [#6451](https://github.com/morluto/rea/issues/6451) concerns doctor recovery selecting the wrong stale copy; this case concerns omission of the CLI-only interactive choice. [#6434](https://github.com/morluto/rea/issues/6434) concerns an unresolved Hermes destination. Closed [#5856](https://github.com/morluto/rea/issues/5856) concerns dropping a skill action after the user accepts its prompt. This report is the preceding offer decision: when the personal copy is current, setup never offers the missing shared copy at all. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: The production setup workflow omits a supported installation choice for a bounded mixed-destination state and never calls the requested skill installation path. - Workaround status: available - Workaround basis: Explicit shared-destination selection produced an install plan and one installation call in the recording-host control; real-filesystem execution of the public CLI workaround remains untested.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4183
No description provided.