symbiot 0.40.4 → 0.40.5
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/agents.mjs +13 -3
- package/index.mjs +1 -0
- package/package.json +1 -1
- package/ui.mjs +6 -1
package/agents.mjs
CHANGED
|
@@ -37,12 +37,22 @@ function setHandoffCmd(cmd) {
|
|
|
37
37
|
// `tool` (a command like "python3" → Bash(python3:*), or a full "Tool(spec)")
|
|
38
38
|
// joins --allowedTools; `dir` joins --add-dir. Claude only (that's where these
|
|
39
39
|
// flags live); for another agent we say to edit the command directly.
|
|
40
|
+
// The --allowedTools rule for what was typed: a rule already in Tool(spec) form
|
|
41
|
+
// is kept as it is, even pasted with its quotes as it reads in the command;
|
|
42
|
+
// only a bare command is wrapped, once (npm install -> Bash(npm install:*)).
|
|
43
|
+
// Stripping quotes first is the point: a quoted "Bash(npm install:*)" once
|
|
44
|
+
// looked bare and became Bash(Bashnpm install:*:*).
|
|
45
|
+
function grantRule(tool) {
|
|
46
|
+
let t = String(tool || "").trim().replace(/^["'`]+|["'`]+$/g, "").trim();
|
|
47
|
+
if (/^[A-Za-z]+\(.*\)$/.test(t)) return t;
|
|
48
|
+
t = t.replace(/[()"'`]/g, "").replace(/:\*$/, "").trim();
|
|
49
|
+
return t ? `Bash(${t}:*)` : "";
|
|
50
|
+
}
|
|
40
51
|
function grantAgent({ tool, dir } = {}) {
|
|
41
52
|
const cfg = loadConfig(); let cmd = (cfg.agentCmd || CLAUDE_CMD).trim();
|
|
42
53
|
if (!/^\s*claude\b/.test(cmd)) return { error: "Grants apply to the Claude agent command. Pick a Claude preset first, or edit the command directly." };
|
|
43
54
|
if (tool) {
|
|
44
|
-
|
|
45
|
-
if (t && !/^[A-Za-z]+\(/.test(t)) t = `Bash(${t.replace(/[()"]/g, "")}:*)`; // bare word -> Bash(word:*)
|
|
55
|
+
const t = grantRule(tool);
|
|
46
56
|
if (t && !cmd.includes(`"${t}"`)) {
|
|
47
57
|
if (/--allowedTools\b/.test(cmd)) cmd = cmd.replace(/(--allowedTools\s+(?:"[^"]*"\s*)+)/, (m) => m.trimEnd() + ` "${t}" `);
|
|
48
58
|
else cmd += ` --allowedTools "${t}"`;
|
|
@@ -343,4 +353,4 @@ function agentsList() {
|
|
|
343
353
|
});
|
|
344
354
|
}
|
|
345
355
|
|
|
346
|
-
export { HANDOFFS, HANDOFF_PROMPT, QUESTIONS_MAX, shSingle, CLAUDE_CMD, ORCA_CLAUDE_CMD, handoffCmd, setHandoffCmd, grantAgent, fillHandoff, runHandoff, runningHandoff, writeTasks, releaseHeldTasks, startHeldTasks, detectHandoffs, orcaHandoffCmd, migrateOrcaCmd, migrateClaudeCmd, track, agentChanges, parseQuestions, agentQuestions, answerQuestions, agentsList };
|
|
356
|
+
export { HANDOFFS, HANDOFF_PROMPT, QUESTIONS_MAX, shSingle, CLAUDE_CMD, ORCA_CLAUDE_CMD, handoffCmd, setHandoffCmd, grantAgent, grantRule, fillHandoff, runHandoff, runningHandoff, writeTasks, releaseHeldTasks, startHeldTasks, detectHandoffs, orcaHandoffCmd, migrateOrcaCmd, migrateClaudeCmd, track, agentChanges, parseQuestions, agentQuestions, answerQuestions, agentsList };
|
package/index.mjs
CHANGED
|
@@ -485,6 +485,7 @@ function buildTasksMd(name, ctx, list) {
|
|
|
485
485
|
L.push("## When you finish an item", "- Tick it here (`- [x]`) as soon as it's done — that's how it reaches review. Ticking doesn't archive it: the user approves it in Symbiot, which commits it on a branch and opens a PR.", "- Leave your changes **uncommitted**, and don't tick anything you didn't finish or couldn't verify.", "");
|
|
486
486
|
L.push("## If you need a decision, or have ideas", "You may be running unattended, so you can't ask in chat. Write `.symbiot/QUESTIONS.md` instead: Symbiot shows it to the user on your block in its Agents tab, and their answers come back in `.symbiot/ANSWERS.md` (read that first if it exists).",
|
|
487
487
|
`- At most ${QUESTIONS_MAX} questions, under a \`## Questions\` heading. Each is a \`### \` heading, then an optional line of context, then 2–4 options as \`- \` bullets (put the one you recommend first).`,
|
|
488
|
+
"- Start every option with who acts, because the user reads it as something they'd be agreeing to do: `👤 You:` when they have to do something (a setting, a click, a command), `🤖 Agent:` when picking it is enough and the next run does the work. If it takes both, put their step first: `👤 You: allow npm install in Settings. 🤖 Agent: the next run adds ESLint`.",
|
|
488
489
|
"- If a step needs a release that isn't out yet, name its version in the question (\"Once 0.41.0 is installed: …\"). Symbiot shows it next to the installed and npm versions, and holds back an answer that starts \"Done\" until that release is out and installed.",
|
|
489
490
|
"- Ideas, options or follow-ups outside these tasks go under `## Suggestions` as `- ` bullets — the user can add them to their tasks in one click.",
|
|
490
491
|
"- Carry on with everything that doesn't depend on an answer, and don't tick an item that does. Ask there rather than doing anything destructive.", "");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "symbiot",
|
|
3
|
-
"version": "0.40.
|
|
3
|
+
"version": "0.40.5",
|
|
4
4
|
"description": "Your week, written from your real work. Reads your local git and writes it up with the AI of your choice — Claude, OpenAI, Gemini, or a local model.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/ui.mjs
CHANGED
|
@@ -112,6 +112,8 @@ footer{padding:10px 18px;border-top:1px solid var(--line);display:flex}
|
|
|
112
112
|
.aq .qc{color:var(--faint);font-size:12.5px;margin-top:3px;line-height:1.5}
|
|
113
113
|
.aq label.opt{display:flex;gap:8px;align-items:flex-start;margin:6px 0 0;font-size:13px;color:var(--text);cursor:pointer}
|
|
114
114
|
.aq .opt input{width:auto;margin-top:3px;flex:none}
|
|
115
|
+
.who{display:inline-block;font-size:11px;font-weight:700;border-radius:999px;padding:0 7px;margin-right:2px;white-space:nowrap}
|
|
116
|
+
.who.you{color:var(--amber);border:1px solid var(--amber)}.who.agent{color:var(--green);border:1px solid var(--green)}
|
|
115
117
|
.aq .qrel{font-size:12px;margin-top:4px;color:var(--green)}.aq .qrel.wait{color:var(--amber)}
|
|
116
118
|
.aq label.opt.off{color:var(--faint);cursor:default}.aq .opt.off i{font-size:12px}
|
|
117
119
|
.aq .qother{margin-top:7px;padding:7px 10px;font-size:13px}
|
|
@@ -317,6 +319,9 @@ api('/api/connect',cfg).then(function(r){$('saveMsg').textContent=r.message||(r.
|
|
|
317
319
|
$('quit').addEventListener('click',function(){api('/api/quit');document.body.innerHTML='<div style=\\'padding:40px;color:#7E9690;font-family:sans-serif\\'>Symbiot stopped. You can close this window.</div>';});
|
|
318
320
|
var GRAPH=null,sel=null,view={k:1,x:0,y:0},GW=960,GH=620;
|
|
319
321
|
function esc(s){return String(s).replace(/[&<>]/g,function(ch){return ch==='&'?'&':ch==='<'?'<':'>';});}
|
|
322
|
+
// An agent's option, with its who-acts markers as badges: 👤 You (you do this
|
|
323
|
+
// step) and 🤖 Agent (picking it is enough; the next run does it). Escaped first.
|
|
324
|
+
function whoHtml(o){return esc(o).replace(/👤[ ]*(You:|You(?=[ ]))?/g,"<span class='who you'>👤 You</span> ").replace(/🤖[ ]*(Agent:|Agent(?=[ ]))?/g,"<span class='who agent'>🤖 Agent</span> ");}
|
|
320
325
|
function layout(nodes,edges){var idx={};nodes.forEach(function(n){n.x=GW/2+(Math.random()-0.5)*GW*0.8;n.y=GH/2+(Math.random()-0.5)*GH*0.8;n.vx=0;n.vy=0;idx[n.id]=n;});
|
|
321
326
|
for(var it=0;it<340;it++){for(var i=0;i<nodes.length;i++)for(var j=i+1;j<nodes.length;j++){var a=nodes[i],b=nodes[j];var dx=a.x-b.x,dy=a.y-b.y;var d2=dx*dx+dy*dy+0.01;var d=Math.sqrt(d2);var f=4600/d2;a.vx+=f*dx/d;a.vy+=f*dy/d;b.vx-=f*dx/d;b.vy-=f*dy/d;}
|
|
322
327
|
edges.forEach(function(e){var a=idx[e.source],b=idx[e.target];if(!a||!b)return;var dx=b.x-a.x,dy=b.y-a.y;var d=Math.sqrt(dx*dx+dy*dy)+0.01;var f=(d-115)*0.03;a.vx+=f*dx/d;a.vy+=f*dy/d;b.vx-=f*dx/d;b.vy-=f*dy/d;});
|
|
@@ -485,7 +490,7 @@ function askHtml(a){var k=a.ask;if(!k)return '';var qs=k.questions||[],ss=k.sugg
|
|
|
485
490
|
var h="<div class='aq' data-id='"+esc(a.id)+"'>";
|
|
486
491
|
if(qs.length){h+="<h4>❓ "+qs.length+" question"+(qs.length>1?"s":"")+" for you</h4>";
|
|
487
492
|
qs.forEach(function(q,i){var rl=q.release;h+="<div class='q' data-i='"+i+"'><div class='qt'>"+esc(q.q)+"</div>"+(q.context?"<div class='qc'>"+esc(q.context)+"</div>":"")+(rl?relHtml(rl):"");
|
|
488
|
-
(q.options||[]).forEach(function(o,j){var off=rl&&rl.waiting&&DONEOPT.test(o);h+="<label class='opt"+(off?" off":"")+"'><input type='radio' name='q_"+esc(a.id)+"_"+i+"' value='"+j+"'"+(off?" disabled":"")+"><span>"+
|
|
493
|
+
(q.options||[]).forEach(function(o,j){var off=rl&&rl.waiting&&DONEOPT.test(o);h+="<label class='opt"+(off?" off":"")+"'><input type='radio' name='q_"+esc(a.id)+"_"+i+"' value='"+j+"'"+(off?" disabled":"")+"><span>"+whoHtml(o)+(off?" <i>(once "+esc(rl.name+" "+rl.needs)+" is installed)</i>":"")+"</span></label>";});
|
|
489
494
|
h+="<input class='qother' placeholder='"+((q.options&&q.options.length)?"or answer in your own words":"your answer")+"'></div>";});
|
|
490
495
|
h+="<div class='row'><button class='act qsend' title='save the answers and hand the repo back to your agent'>Send answers & continue</button><button class='ghost qsave' title='save the answers for the next run'>Save only</button></div>";}
|
|
491
496
|
if(ss.length){h+="<h4>💡 Ideas from the agent</h4>";ss.forEach(function(s,i){h+="<div class='idea'><span style='flex:1'>"+esc(s.text)+"</span>"+(s.added?"<span class='tag'>in Tasks</span>":"<button class='ghost qidea' data-i='"+i+"' style='padding:3px 9px;font-size:12px'>+ task</button>")+"</div>";});}
|