fix(commands): inject forced skill content for /use (#4044)

This commit is contained in:
Rod Boev
2026-06-12 08:34:27 -04:00
parent df6cfd0a15
commit 80dab6f3eb
3 changed files with 32 additions and 5 deletions

View File

@@ -949,8 +949,11 @@ async function cmdUse(args){
}
return;
}
const detail = await api(`/api/skills/content?name=${encodeURIComponent(match.name)}`);
const skillContent = detail&&typeof detail.content==='string' ? detail.content.trim() : '';
if(!skillContent) throw new Error(`Skill \`${match.name}\` has no readable content.`);
const directive = `[USER OVERRIDE] You MUST consult skill '${match.name}' via skill_view before responding to the next message.`;
resolve(directive);
resolve({name:match.name,directive,content:skillContent});
if(isCurrentSession()){
S.messages.push({role:'assistant', content:`Next turn: skill \`${match.name}\` will be forced.`});
renderMessages();

View File

@@ -1063,10 +1063,22 @@ async function send(){
if(_forcedSkillDirectivePending){
const _pending=_forcedSkillDirectivePending;
if(!_pending.sessionId||_pending.sessionId===activeSid){
const _directive = await _pending.promise;
const _directivePayload = await _pending.promise;
if(_forcedSkillDirectivePending===_pending)_forcedSkillDirectivePending = null;
if(typeof _directive==='string'&&_directive){
msgText=`${_directive}\n\n${msgText||''}`.trim();
if(_directivePayload){
const _directive = typeof _directivePayload==='string'
? _directivePayload
: String(_directivePayload.directive||'').trim();
const _forcedSkillName = typeof _directivePayload==='string'
? ''
: String(_directivePayload.name||'').trim();
const _forcedSkillContent = typeof _directivePayload==='string'
? ''
: String(_directivePayload.content||'').trim();
const _forcedSkillBlock = _forcedSkillName&&_forcedSkillContent
? `[FORCED SKILL CONTEXT: ${_forcedSkillName}]\n${_forcedSkillContent}\n[/FORCED SKILL CONTEXT]`
: '';
msgText=`${_directive}${_forcedSkillBlock?`\n\n${_forcedSkillBlock}`:''}\n\n${msgText||''}`.trim();
}
}
}

View File

@@ -34,6 +34,8 @@ def test_forced_skill_directive_set_in_cmdUse():
src = read("static/commands.js")
assert "pending.promise = new Promise" in src, "cmdUse must create a pending Promise"
assert "_forcedSkillDirectivePending = pending;" in src, "cmdUse must publish the pending directive before awaiting"
assert "resolve({name:match.name,directive,content:skillContent});" in src, \
"cmdUse must resolve the pending payload with skill name, directive, and fetched content"
def test_use_entry_has_noEcho():
@@ -59,7 +61,7 @@ def test_directive_consumed_at_injection_site():
finally_part = src.split("finally")[1] if "finally" in src else ""
assert "_forcedSkillDirectivePending = null;" not in finally_part, \
"_forcedSkillDirectivePending must NOT be cleared in the finally block"
assert "const _directive = await _pending.promise;" in src, \
assert "const _directivePayload = await _pending.promise;" in src, \
"consume site must await the pending promise"
assert "_forcedSkillDirectivePending = null;" in src, \
"_forcedSkillDirectivePending must be cleared somewhere in messages.js"
@@ -78,6 +80,14 @@ def test_directive_text_uses_match_name():
assert "[USER OVERRIDE] You MUST consult skill '" in src, "directive text must match the specified format"
def test_use_fetches_canonical_skill_content():
src = read("static/commands.js")
assert "api(`/api/skills/content?name=${encodeURIComponent(match.name)}`)" in src, \
"cmdUse must fetch the canonical skill content after resolving the canonical skill name"
assert "typeof detail.content==='string' ? detail.content.trim() : ''" in src, \
"cmdUse must reject missing or non-string skill content"
def test_pending_promise_set_synchronously():
"""_forcedSkillDirectivePending must be set before the first await in cmdUse."""
src = read("static/commands.js")
@@ -114,3 +124,5 @@ def test_directive_only_consumed_by_matching_session():
"send() must only consume /use directives issued for the active session"
assert "if(_forcedSkillDirectivePending===_pending)_forcedSkillDirectivePending = null;" in src, \
"send() must not clear a newer pending directive created while awaiting"
assert "[FORCED SKILL CONTEXT: ${_forcedSkillName}]" in src, \
"send() must prepend deterministic forced-skill content before the user message"