@bunshin-cc/bunshin 0.4.0 → 0.4.2
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/dist/bin.js +48 -3
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -879,6 +879,8 @@ function firstHeading(text2) {
|
|
|
879
879
|
function applyTextOp(text2, op, legacyAnchor = "") {
|
|
880
880
|
const anchor = "anchor" in op && op.anchor || legacyAnchor;
|
|
881
881
|
switch (op.op) {
|
|
882
|
+
case "situation":
|
|
883
|
+
return { ok: true, text: text2 };
|
|
882
884
|
case "rewrite":
|
|
883
885
|
return { ok: true, text: op.text };
|
|
884
886
|
case "create":
|
|
@@ -1744,6 +1746,11 @@ ${robot ? `- Values that belong to THIS robot or cell (calibration, pixel column
|
|
|
1744
1746
|
- Code that should change (a hard-coded constant, a helper) \u2192 describe it as an append to the
|
|
1745
1747
|
skill's section, stating the change; reconciliation turns it into a harness patch.` : `- Machine specifics and personal preferences \u2192 your own user layer.`}
|
|
1746
1748
|
- Never include secrets, credentials or customer data.
|
|
1749
|
+
- A retrieval miss: if a document in the list above would have helped, but the agent did not use
|
|
1750
|
+
it (the user had to point at it, asked why it was not used, or the agent found it only late), add
|
|
1751
|
+
op "situation" to that document: the request as the user actually phrased it, made general (names
|
|
1752
|
+
and ticket ids replaced by what they stand for), e.g. "run me through an e2e test and what it
|
|
1753
|
+
does". Recall learns from it, so the next such request finds the document.
|
|
1747
1754
|
|
|
1748
1755
|
## Output
|
|
1749
1756
|
|
|
@@ -1755,7 +1762,8 @@ At most ${max} suggestions. Schema:
|
|
|
1755
1762
|
| {"op": "create", "section": {"kind": "skill"|"procedure"|"spec"|"verification"|"rule"|"guardrail"|"reference",
|
|
1756
1763
|
"title": string, "body": string, "when": string,
|
|
1757
1764
|
"paths"?: string[], "entities"?: string[], "skills"?: string[]}}
|
|
1758
|
-
| {"op": "param", "key": string, "value": string|number|boolean}
|
|
1765
|
+
| {"op": "param", "key": string, "value": string|number|boolean}
|
|
1766
|
+
| {"op": "situation", "text": string},
|
|
1759
1767
|
"rationale": string, "urgency": "normal"|"urgent"}]}`;
|
|
1760
1768
|
}
|
|
1761
1769
|
function extractJson(text2) {
|
|
@@ -1797,6 +1805,10 @@ function parseOp(raw) {
|
|
|
1797
1805
|
section: { kind, title, body, when: when || title, paths: strList(s.paths), entities: strList(s.entities).map((e) => e.toLowerCase()), skills: strList(s.skills) }
|
|
1798
1806
|
};
|
|
1799
1807
|
}
|
|
1808
|
+
case "situation": {
|
|
1809
|
+
const text2 = clean(o.text);
|
|
1810
|
+
return text2 ? { op: "situation", text: text2.slice(0, 500) } : null;
|
|
1811
|
+
}
|
|
1800
1812
|
case "param": {
|
|
1801
1813
|
const key = typeof o.key === "string" ? o.key.trim() : "";
|
|
1802
1814
|
const v = o.value;
|
|
@@ -3599,6 +3611,8 @@ function summarize(op) {
|
|
|
3599
3611
|
return clip(op.content === null ? `delete ${op.path}` : `file ${op.path}`);
|
|
3600
3612
|
case "retire":
|
|
3601
3613
|
return clip(`retire: ${op.reason}`);
|
|
3614
|
+
case "situation":
|
|
3615
|
+
return clip(`also applies to: ${op.text}`);
|
|
3602
3616
|
}
|
|
3603
3617
|
}
|
|
3604
3618
|
function describeTarget(s, view) {
|
|
@@ -8037,7 +8051,7 @@ async function handleV2(event, ctx) {
|
|
|
8037
8051
|
case "session-start":
|
|
8038
8052
|
return onSessionStart(ctx);
|
|
8039
8053
|
case "user-prompt-submit":
|
|
8040
|
-
return onPrompt(ctx);
|
|
8054
|
+
return await onPrompt(ctx);
|
|
8041
8055
|
case "pre-tool-use":
|
|
8042
8056
|
return onPreToolUse(ctx);
|
|
8043
8057
|
case "stop":
|
|
@@ -8117,12 +8131,28 @@ function liveUpdate(v2, ctx, session) {
|
|
|
8117
8131
|
return "";
|
|
8118
8132
|
}
|
|
8119
8133
|
}
|
|
8134
|
+
function skillHint(sec, others) {
|
|
8135
|
+
const also = others.length ? ` Also possibly relevant: ${others.map((t) => `"${t}"`).join(", ")}.` : "";
|
|
8136
|
+
return `Bunshin: the \`${sec.skill.name}\` skill may apply: ${sec.skill.description.replace(/\s+/g, " ").trim()} If it does, invoke it (Skill tool, or \`/${sec.skill.name}\`) before doing this by hand.${also}`;
|
|
8137
|
+
}
|
|
8138
|
+
async function remoteAuto(ctx, prompt, exclude) {
|
|
8139
|
+
const config = ctx.store.config();
|
|
8140
|
+
if (!canCall(config) || !config.recall.useMothership) return null;
|
|
8141
|
+
const budget = Math.min(2500, ctx.deadline - Date.now() - 600);
|
|
8142
|
+
if (budget < 300) return null;
|
|
8143
|
+
try {
|
|
8144
|
+
const res = await v2api(config, "POST", "/recall/auto", { prompt: prompt.slice(0, 8e3), exclude }, { timeoutMs: budget });
|
|
8145
|
+
return res.available ? res : null;
|
|
8146
|
+
} catch {
|
|
8147
|
+
return null;
|
|
8148
|
+
}
|
|
8149
|
+
}
|
|
8120
8150
|
function hintLine(titles, harness) {
|
|
8121
8151
|
const quoted = titles.map((t) => `"${t}"`).join(", ");
|
|
8122
8152
|
const how = harness === "claude-code" ? "If one could change your approach, call the bunshin_recall tool (describe the task abstractly) and load only what you need." : 'If one could change your approach, run `bunshin recall "<the task, abstractly>" --hypothetical "<the lesson you would want>"` and `bunshin load <id>`.';
|
|
8123
8153
|
return `Bunshin: possibly relevant library sections: ${quoted}. ${how}`;
|
|
8124
8154
|
}
|
|
8125
|
-
function onPrompt(ctx) {
|
|
8155
|
+
async function onPrompt(ctx) {
|
|
8126
8156
|
const prompt = ctx.input.prompt ?? "";
|
|
8127
8157
|
if (prompt.trimStart().startsWith(REFLECT_MARKER)) return {};
|
|
8128
8158
|
const v2 = new V2Store(ctx.store);
|
|
@@ -8143,6 +8173,19 @@ function onPrompt(ctx) {
|
|
|
8143
8173
|
const view = localView(v2, config, ctx.cwd);
|
|
8144
8174
|
const injected = [...session.loaded, ...session.pushed];
|
|
8145
8175
|
const res = auto(view.sections, view.reader, prompt, { exclude: injected });
|
|
8176
|
+
const remote = await remoteAuto(ctx, prompt, injected);
|
|
8177
|
+
if (remote) {
|
|
8178
|
+
const local = (id) => view.sections.find((s) => s.id === id);
|
|
8179
|
+
if (remote.section) {
|
|
8180
|
+
res.section = local(remote.section.id) ?? remote.section;
|
|
8181
|
+
res.anchor = void 0;
|
|
8182
|
+
res.reason = `dense ${remote.reason ?? ""}`.trim();
|
|
8183
|
+
} else {
|
|
8184
|
+
res.section = void 0;
|
|
8185
|
+
res.hint = (remote.hint ?? []).map((h) => ({ id: h.id, title: local(h.id)?.title ?? h.title }));
|
|
8186
|
+
res.reason = `dense ${remote.reason ?? ""}`.trim();
|
|
8187
|
+
}
|
|
8188
|
+
}
|
|
8146
8189
|
if (res.section) {
|
|
8147
8190
|
const sec = res.section;
|
|
8148
8191
|
v2.updateSession(session.sessionId, ctx.cwd, (s) => {
|
|
@@ -8159,6 +8202,8 @@ function onPrompt(ctx) {
|
|
|
8159
8202
|
addUnique(s.hinted, ...fresh.map((h) => h.id));
|
|
8160
8203
|
addUnique(s.served, ...fresh.map((h) => h.id));
|
|
8161
8204
|
});
|
|
8205
|
+
const topSkill = view.sections.find((s) => s.id === fresh[0].id && s.skill);
|
|
8206
|
+
if (topSkill && deployEnabled(config, view.ctx)) return withLive(additional("UserPromptSubmit", skillHint(topSkill, fresh.slice(1).map((h) => h.title))));
|
|
8162
8207
|
return withLive(additional("UserPromptSubmit", hintLine(fresh.map((h) => h.title), config.harness)));
|
|
8163
8208
|
}
|
|
8164
8209
|
function toolPaths(input) {
|