@link-assistant/hive-mind 2.8.5 → 2.8.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @link-assistant/hive-mind
2
2
 
3
+ ## 2.8.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 0140e87: Verify required Codex Agent Skills against the catalog the model actually receives instead of trusting plugin enablement, so a plugin reported as installed but whose skills are invisible is reported with an actionable diagnostic rather than passing the preflight.
8
+
3
9
  ## 2.8.5
4
10
 
5
11
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "2.8.5",
3
+ "version": "2.8.6",
4
4
  "description": "AI-powered issue solver and hive mind for collaborative problem solving",
5
5
  "main": "src/hive.mjs",
6
6
  "type": "module",
@@ -155,6 +155,61 @@ const parseJsonCommand = (result, label) => {
155
155
 
156
156
  const catalogEntries = catalog => [...(catalog?.installed || []), ...(catalog?.available || [])];
157
157
 
158
+ // Issue #2084: `codex plugin list` reports enablement, not model visibility.
159
+ //
160
+ // Codex renders the skills the model can actually see into a
161
+ // `<skills_instructions>` block in the prompt. `codex debug prompt-input`
162
+ // prints that prompt, so parsing the block is the only client-side signal that
163
+ // matches what the model receives. Entries are rendered as
164
+ // `- <name>: <description> (file: <path>)`, where `<name>` is bare for skills
165
+ // under `$CODEX_HOME/skills` and `<plugin>:<skill>` for plugin-provided ones.
166
+ const SKILLS_INSTRUCTIONS_BLOCK = /<skills_instructions>([\s\S]*?)<\/skills_instructions>/u;
167
+ const SKILL_CATALOG_ENTRY = /(?:^|\\n)\s*-\s+([a-z0-9][a-z0-9_-]*(?::[a-z0-9][a-z0-9_-]*)?)\s*:/giu;
168
+
169
+ export const parseModelVisibleSkills = (promptInput = '') => {
170
+ // `codex debug prompt-input` emits JSON, so newlines inside the prompt arrive
171
+ // as the two-character escape `\n`. Match both that and real newlines.
172
+ const block = SKILLS_INSTRUCTIONS_BLOCK.exec(String(promptInput).replace(/\r\n/gu, '\n').replace(/\n/gu, '\\n'));
173
+ if (!block) return null;
174
+ const skills = new Set();
175
+ for (const match of block[1].matchAll(SKILL_CATALOG_ENTRY)) skills.add(match[1].toLowerCase());
176
+ return skills;
177
+ };
178
+
179
+ // Verification is advisory when the probe itself cannot run: an older Codex
180
+ // without `debug prompt-input`, or a sandbox that blocks it, must not fail a
181
+ // run that the previous verification would have allowed.
182
+ const readModelVisibleSkills = async ({ command, env, runCommand, log }) => {
183
+ const result = await runCommand({ command, args: ['debug', 'prompt-input', 'hive-mind capability probe'], env });
184
+ if (result.code !== 0) {
185
+ await log(
186
+ ` ⚠️ Could not read the model-visible skill catalog: ${String(result.stderr || result.stdout)
187
+ .trim()
188
+ .slice(0, 200)}`,
189
+ { verbose: true }
190
+ );
191
+ return null;
192
+ }
193
+ const skills = parseModelVisibleSkills(result.stdout);
194
+ if (!skills) await log(' ⚠️ Codex prompt did not contain a <skills_instructions> block; skipping skill visibility verification', { verbose: true });
195
+ return skills;
196
+ };
197
+
198
+ const verifyModelVisibleSkills = async ({ command, env, runCommand, log, requiredSkills }) => {
199
+ if (!requiredSkills || requiredSkills.length === 0) return;
200
+ const visible = await readModelVisibleSkills({ command, env, runCommand, log });
201
+ if (!visible) return;
202
+
203
+ await log(` 🔎 Model-visible skills (${visible.size}): ${[...visible].sort().join(', ') || 'none'}`, { verbose: true });
204
+ const invisible = requiredSkills.filter(skill => !visible.has(skill.toLowerCase()));
205
+ if (invisible.length === 0) {
206
+ await log(` ✅ Verified ${requiredSkills.length} required skill(s) are visible to the model`);
207
+ return;
208
+ }
209
+
210
+ throw new CodexCapabilityPreflightError(`Codex reports the required plugins as installed, but the model cannot see: ${invisible.join(', ')}. ` + `Codex exposes a plugin's skills only while its payload is materialized under ` + `CODEX_HOME/plugins/cache/<marketplace>/<plugin>/<version>/skills. ` + `Visible skills were: ${[...visible].sort().join(', ') || 'none'}.`, { missing: invisible });
211
+ };
212
+
158
213
  const skillParts = skill => {
159
214
  const separator = skill.indexOf(':');
160
215
  return separator === -1 ? { namespace: null, name: skill } : { namespace: skill.slice(0, separator), name: skill.slice(separator + 1) };
@@ -341,6 +396,7 @@ async function provisionCodexCapabilities({ owner, repo, issueNumber, projectDir
341
396
  }
342
397
  if (plugins.length === 0) {
343
398
  await log(' ✅ Required Agent Skills are already available from standard skill directories');
399
+ await verifyModelVisibleSkills({ command, env: baseEnv, runCommand, log, requiredSkills: requirements.skills });
344
400
  return { required: true, plugins, skills: requirements.skills, codexHome: null, baseCodexHome };
345
401
  }
346
402
 
@@ -365,6 +421,13 @@ async function provisionCodexCapabilities({ owner, repo, issueNumber, projectDir
365
421
  const unverified = plugins.filter(plugin => !verified.has(plugin));
366
422
  if (unverified.length > 0) throw new CodexCapabilityPreflightError(`Codex capability installation did not verify successfully: ${unverified.join(', ')}`, { missing: unverified });
367
423
 
424
+ // Issue #2084: enablement is not exposure. The failing run reached this point
425
+ // with `superpowers@openai-curated` reported as "installed, enabled" while
426
+ // the model saw zero `superpowers:*` skills, so the run proceeded and then
427
+ // stalled on the repository's mandatory preflight. Confirm the requirement
428
+ // against the catalog the model actually receives.
429
+ await verifyModelVisibleSkills({ command, env: scopedEnv, runCommand, log, requiredSkills: requirements.skills });
430
+
368
431
  await log(` Codex capability state: ${codexHome}`, { verbose: true });
369
432
  return { required: true, plugins, skills: requirements.skills, codexHome, baseCodexHome };
370
433
  }