@dylanrussell/agent-router 1.0.6 → 1.0.7
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/README.md +26 -9
- package/dist/cli.js +376 -16
- package/dist/cli.js.map +1 -1
- package/dist/plugin.js +376 -16
- package/dist/plugin.js.map +1 -1
- package/dist/tui.js +367 -13
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
package/dist/plugin.js
CHANGED
|
@@ -14705,8 +14705,300 @@ async function resolvePathsWithConfig(options = {}) {
|
|
|
14705
14705
|
import { existsSync as existsSync2 } from "fs";
|
|
14706
14706
|
import { readFile as readFile2, readdir } from "fs/promises";
|
|
14707
14707
|
import path3 from "path";
|
|
14708
|
+
|
|
14709
|
+
// src/core/yaml-lite.ts
|
|
14710
|
+
var RESERVED_WORDS = /* @__PURE__ */ new Set([
|
|
14711
|
+
"null",
|
|
14712
|
+
"true",
|
|
14713
|
+
"false",
|
|
14714
|
+
"yes",
|
|
14715
|
+
"no",
|
|
14716
|
+
"on",
|
|
14717
|
+
"off",
|
|
14718
|
+
"~",
|
|
14719
|
+
// YAML 1.1 nulls
|
|
14720
|
+
"Null",
|
|
14721
|
+
"NULL",
|
|
14722
|
+
"True",
|
|
14723
|
+
"False",
|
|
14724
|
+
"Yes",
|
|
14725
|
+
"No",
|
|
14726
|
+
"On",
|
|
14727
|
+
"Off"
|
|
14728
|
+
]);
|
|
14729
|
+
var BARE_STRING_RE = /^[A-Za-z0-9._\-\/+@$%^&()~]+$/;
|
|
14730
|
+
function needsQuoting(s) {
|
|
14731
|
+
if (s === "") return true;
|
|
14732
|
+
if (RESERVED_WORDS.has(s)) return true;
|
|
14733
|
+
if (/^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?$/.test(s)) return true;
|
|
14734
|
+
if (/^0x[0-9A-Fa-f]+$/.test(s)) return true;
|
|
14735
|
+
if (/^[+-]?\.(inf|Inf|INF|nan|NaN|NAN)$/.test(s)) return true;
|
|
14736
|
+
if (!BARE_STRING_RE.test(s)) return true;
|
|
14737
|
+
return false;
|
|
14738
|
+
}
|
|
14739
|
+
function serializeOptionValue(value) {
|
|
14740
|
+
if (value === null || value === void 0) return "null";
|
|
14741
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
14742
|
+
if (typeof value === "number") {
|
|
14743
|
+
if (!Number.isFinite(value)) return String(value);
|
|
14744
|
+
return JSON.stringify(value);
|
|
14745
|
+
}
|
|
14746
|
+
if (typeof value === "bigint") return String(value);
|
|
14747
|
+
if (typeof value === "string") return needsQuoting(value) ? JSON.stringify(value) : value;
|
|
14748
|
+
return JSON.stringify(value);
|
|
14749
|
+
}
|
|
14750
|
+
function stripComment(line) {
|
|
14751
|
+
let inSingle = false;
|
|
14752
|
+
let inDouble = false;
|
|
14753
|
+
for (let i = 0; i < line.length; i++) {
|
|
14754
|
+
const ch = line[i];
|
|
14755
|
+
if (ch === "'" && !inDouble) inSingle = !inSingle;
|
|
14756
|
+
else if (ch === '"' && !inSingle) inDouble = !inDouble;
|
|
14757
|
+
else if (ch === "#" && !inSingle && !inDouble) {
|
|
14758
|
+
if (i === 0 || /\s/.test(line[i - 1] ?? "")) return line.slice(0, i);
|
|
14759
|
+
}
|
|
14760
|
+
}
|
|
14761
|
+
return line;
|
|
14762
|
+
}
|
|
14763
|
+
function tokenize(block) {
|
|
14764
|
+
const out = [];
|
|
14765
|
+
for (const raw of block.split(/\r?\n/)) {
|
|
14766
|
+
if (raw.trim() === "") continue;
|
|
14767
|
+
const indentMatch = /^( *)/.exec(raw);
|
|
14768
|
+
const indent = indentMatch?.[1]?.length ?? 0;
|
|
14769
|
+
const content = stripComment(raw.slice(indent));
|
|
14770
|
+
if (content.trim() === "") continue;
|
|
14771
|
+
out.push({ indent, text: content.trimEnd(), raw });
|
|
14772
|
+
}
|
|
14773
|
+
return out;
|
|
14774
|
+
}
|
|
14775
|
+
function coerceScalar(raw) {
|
|
14776
|
+
const v = raw.trim();
|
|
14777
|
+
if (v === "" || v === "null" || v === "~" || v === "Null" || v === "NULL") return null;
|
|
14778
|
+
if (v === "true" || v === "True" || v === "TRUE") return true;
|
|
14779
|
+
if (v === "false" || v === "False" || v === "FALSE") return false;
|
|
14780
|
+
if (v === "yes" || v === "Yes" || v === "YES") return true;
|
|
14781
|
+
if (v === "no" || v === "No" || v === "NO") return false;
|
|
14782
|
+
if (v === "on" || v === "On" || v === "ON") return true;
|
|
14783
|
+
if (v === "off" || v === "Off" || v === "OFF") return false;
|
|
14784
|
+
if (/^[+-]?\d+$/.test(v)) return Number.parseInt(v, 10);
|
|
14785
|
+
if (/^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?$/.test(v)) return Number.parseFloat(v);
|
|
14786
|
+
if (/^0x[0-9A-Fa-f]+$/.test(v)) return Number.parseInt(v, 16);
|
|
14787
|
+
return v;
|
|
14788
|
+
}
|
|
14789
|
+
function parseQuoted(s) {
|
|
14790
|
+
const quote = s[0] ?? "";
|
|
14791
|
+
if (quote !== '"' && quote !== "'") return null;
|
|
14792
|
+
if (quote === '"') {
|
|
14793
|
+
let out2 = "";
|
|
14794
|
+
for (let i = 1; i < s.length; i++) {
|
|
14795
|
+
const ch = s[i] ?? "";
|
|
14796
|
+
if (ch === "\\" && i + 1 < s.length) {
|
|
14797
|
+
const next = s[i + 1] ?? "";
|
|
14798
|
+
const map2 = {
|
|
14799
|
+
n: "\n",
|
|
14800
|
+
t: " ",
|
|
14801
|
+
r: "\r",
|
|
14802
|
+
'"': '"',
|
|
14803
|
+
"\\": "\\",
|
|
14804
|
+
"/": "/",
|
|
14805
|
+
b: "\b",
|
|
14806
|
+
f: "\f"
|
|
14807
|
+
};
|
|
14808
|
+
out2 += map2[next] ?? next;
|
|
14809
|
+
i++;
|
|
14810
|
+
} else if (ch === '"') {
|
|
14811
|
+
return { value: out2, rest: s.slice(i + 1) };
|
|
14812
|
+
} else {
|
|
14813
|
+
out2 += ch;
|
|
14814
|
+
}
|
|
14815
|
+
}
|
|
14816
|
+
return null;
|
|
14817
|
+
}
|
|
14818
|
+
let out = "";
|
|
14819
|
+
for (let i = 1; i < s.length; i++) {
|
|
14820
|
+
const ch = s[i] ?? "";
|
|
14821
|
+
if (ch === "'") {
|
|
14822
|
+
if (s[i + 1] === "'") {
|
|
14823
|
+
out += "'";
|
|
14824
|
+
i++;
|
|
14825
|
+
} else {
|
|
14826
|
+
return { value: out, rest: s.slice(i + 1) };
|
|
14827
|
+
}
|
|
14828
|
+
} else {
|
|
14829
|
+
out += ch;
|
|
14830
|
+
}
|
|
14831
|
+
}
|
|
14832
|
+
return null;
|
|
14833
|
+
}
|
|
14834
|
+
function splitFlow(raw) {
|
|
14835
|
+
const parts = [];
|
|
14836
|
+
let depth = 0;
|
|
14837
|
+
let inSingle = false;
|
|
14838
|
+
let inDouble = false;
|
|
14839
|
+
let start = 0;
|
|
14840
|
+
for (let i = 0; i < raw.length; i++) {
|
|
14841
|
+
const ch = raw[i];
|
|
14842
|
+
if (ch === "'" && !inDouble) inSingle = !inSingle;
|
|
14843
|
+
else if (ch === '"' && !inSingle) inDouble = !inDouble;
|
|
14844
|
+
else if (!inSingle && !inDouble) {
|
|
14845
|
+
if (ch === "{" || ch === "[") depth++;
|
|
14846
|
+
else if (ch === "}" || ch === "]") depth--;
|
|
14847
|
+
else if (ch === "," && depth === 0) {
|
|
14848
|
+
parts.push(raw.slice(start, i));
|
|
14849
|
+
start = i + 1;
|
|
14850
|
+
}
|
|
14851
|
+
}
|
|
14852
|
+
}
|
|
14853
|
+
parts.push(raw.slice(start));
|
|
14854
|
+
return parts;
|
|
14855
|
+
}
|
|
14856
|
+
function parseFlowValue(raw) {
|
|
14857
|
+
const s = raw.trim();
|
|
14858
|
+
if (s === "") return null;
|
|
14859
|
+
const first = s[0] ?? "";
|
|
14860
|
+
if (first === "{") {
|
|
14861
|
+
const inner = s.slice(1, s.length - 1);
|
|
14862
|
+
const obj = {};
|
|
14863
|
+
for (const part of splitFlow(inner)) {
|
|
14864
|
+
const t = part.trim();
|
|
14865
|
+
if (t === "") continue;
|
|
14866
|
+
const colon = findColon(t);
|
|
14867
|
+
if (colon < 0) {
|
|
14868
|
+
obj[t] = null;
|
|
14869
|
+
} else {
|
|
14870
|
+
const k = t.slice(0, colon).trim();
|
|
14871
|
+
const v = t.slice(colon + 1).trim();
|
|
14872
|
+
obj[stripKeyQuotes(k)] = parseFlowValue(v);
|
|
14873
|
+
}
|
|
14874
|
+
}
|
|
14875
|
+
return obj;
|
|
14876
|
+
}
|
|
14877
|
+
if (first === "[") {
|
|
14878
|
+
const inner = s.slice(1, s.length - 1);
|
|
14879
|
+
return splitFlow(inner).filter((p) => p.trim() !== "").map((p) => parseFlowValue(p));
|
|
14880
|
+
}
|
|
14881
|
+
if (first === '"' || first === "'") {
|
|
14882
|
+
const q = parseQuoted(s);
|
|
14883
|
+
return q ? q.value : s;
|
|
14884
|
+
}
|
|
14885
|
+
return coerceScalar(s);
|
|
14886
|
+
}
|
|
14887
|
+
function findColon(s) {
|
|
14888
|
+
let inSingle = false;
|
|
14889
|
+
let inDouble = false;
|
|
14890
|
+
let depth = 0;
|
|
14891
|
+
for (let i = 0; i < s.length; i++) {
|
|
14892
|
+
const ch = s[i];
|
|
14893
|
+
if (ch === "'" && !inDouble) inSingle = !inSingle;
|
|
14894
|
+
else if (ch === '"' && !inSingle) inDouble = !inDouble;
|
|
14895
|
+
else if (!inSingle && !inDouble) {
|
|
14896
|
+
if (ch === "{" || ch === "[") depth++;
|
|
14897
|
+
else if (ch === "}" || ch === "]") depth--;
|
|
14898
|
+
else if (ch === ":" && depth === 0) return i;
|
|
14899
|
+
}
|
|
14900
|
+
}
|
|
14901
|
+
return -1;
|
|
14902
|
+
}
|
|
14903
|
+
function stripKeyQuotes(k) {
|
|
14904
|
+
if (k.length >= 2 && (k[0] === '"' && k.at(-1) === '"' || k[0] === "'" && k.at(-1) === "'")) {
|
|
14905
|
+
return k.slice(1, -1);
|
|
14906
|
+
}
|
|
14907
|
+
return k;
|
|
14908
|
+
}
|
|
14909
|
+
function parseMapping(lines, startIdx, indent) {
|
|
14910
|
+
const out = {};
|
|
14911
|
+
let i = startIdx;
|
|
14912
|
+
while (i < lines.length) {
|
|
14913
|
+
const line = lines[i];
|
|
14914
|
+
if (!line) break;
|
|
14915
|
+
if (line.indent < indent) break;
|
|
14916
|
+
if (line.indent > indent) {
|
|
14917
|
+
i++;
|
|
14918
|
+
continue;
|
|
14919
|
+
}
|
|
14920
|
+
const colon = findColon(line.text);
|
|
14921
|
+
if (colon < 0) {
|
|
14922
|
+
i++;
|
|
14923
|
+
continue;
|
|
14924
|
+
}
|
|
14925
|
+
const key = stripKeyQuotes(line.text.slice(0, colon).trim());
|
|
14926
|
+
if (key === "") {
|
|
14927
|
+
i++;
|
|
14928
|
+
continue;
|
|
14929
|
+
}
|
|
14930
|
+
const rest = line.text.slice(colon + 1).trim();
|
|
14931
|
+
if (rest !== "") {
|
|
14932
|
+
out[key] = parseFlowValue(rest);
|
|
14933
|
+
i++;
|
|
14934
|
+
} else {
|
|
14935
|
+
const childIndent = lines[i + 1]?.indent ?? line.indent + 1;
|
|
14936
|
+
let j = i + 1;
|
|
14937
|
+
while (j < lines.length && (lines[j]?.indent ?? -1) >= childIndent) j++;
|
|
14938
|
+
if (j === i + 1) {
|
|
14939
|
+
out[key] = null;
|
|
14940
|
+
i++;
|
|
14941
|
+
} else {
|
|
14942
|
+
const childBlock = lines.slice(i + 1, j);
|
|
14943
|
+
const firstChild = childBlock[0];
|
|
14944
|
+
if (firstChild?.text.trimStart().startsWith("- ")) {
|
|
14945
|
+
out[key] = parseSequence(childBlock, childIndent);
|
|
14946
|
+
} else {
|
|
14947
|
+
out[key] = parseMapping(childBlock, 0, childIndent).value;
|
|
14948
|
+
}
|
|
14949
|
+
i = j;
|
|
14950
|
+
}
|
|
14951
|
+
}
|
|
14952
|
+
}
|
|
14953
|
+
return { value: out, next: i };
|
|
14954
|
+
}
|
|
14955
|
+
function parseSequence(lines, indent) {
|
|
14956
|
+
const out = [];
|
|
14957
|
+
let i = 0;
|
|
14958
|
+
while (i < lines.length) {
|
|
14959
|
+
const line = lines[i];
|
|
14960
|
+
if (!line) break;
|
|
14961
|
+
if (line.indent < indent) break;
|
|
14962
|
+
const t = line.text.trimStart();
|
|
14963
|
+
if (!t.startsWith("- ")) {
|
|
14964
|
+
i++;
|
|
14965
|
+
continue;
|
|
14966
|
+
}
|
|
14967
|
+
const item = t.slice(2).trim();
|
|
14968
|
+
if (item === "") {
|
|
14969
|
+
const childIndent = lines[i + 1]?.indent ?? line.indent + 1;
|
|
14970
|
+
let j = i + 1;
|
|
14971
|
+
while (j < lines.length && (lines[j]?.indent ?? -1) >= childIndent) j++;
|
|
14972
|
+
out.push(parseMapping(lines.slice(i + 1, j), 0, childIndent).value);
|
|
14973
|
+
i = j;
|
|
14974
|
+
} else {
|
|
14975
|
+
out.push(parseFlowValue(item));
|
|
14976
|
+
i++;
|
|
14977
|
+
}
|
|
14978
|
+
}
|
|
14979
|
+
return out;
|
|
14980
|
+
}
|
|
14981
|
+
function parseFrontmatterBlock(block) {
|
|
14982
|
+
const lines = tokenize(block);
|
|
14983
|
+
if (lines.length === 0) return null;
|
|
14984
|
+
const { value } = parseMapping(lines, 0, 0);
|
|
14985
|
+
return value;
|
|
14986
|
+
}
|
|
14987
|
+
|
|
14988
|
+
// src/core/frontmatter.ts
|
|
14708
14989
|
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
|
|
14709
14990
|
var MODEL_LINE_RE = /^model:[ \t]*(.*)$/m;
|
|
14991
|
+
var RESERVED_AGENT_KEYS = /* @__PURE__ */ new Set([
|
|
14992
|
+
"name",
|
|
14993
|
+
"mode",
|
|
14994
|
+
"description",
|
|
14995
|
+
"permission",
|
|
14996
|
+
"color",
|
|
14997
|
+
"tools",
|
|
14998
|
+
"prompt",
|
|
14999
|
+
"steps",
|
|
15000
|
+
"maxSteps"
|
|
15001
|
+
]);
|
|
14710
15002
|
function cleanModelValue(raw) {
|
|
14711
15003
|
let v = raw;
|
|
14712
15004
|
const hash2 = v.search(/[ \t]#/);
|
|
@@ -14735,6 +15027,55 @@ function setFrontmatterModel(content, model) {
|
|
|
14735
15027
|
const nextFm = fm[0].slice(0, blockStart) + nextBlock + fm[0].slice(blockStart + block.length);
|
|
14736
15028
|
return nextFm + content.slice(fm[0].length);
|
|
14737
15029
|
}
|
|
15030
|
+
function getFrontmatterOptions(content) {
|
|
15031
|
+
const fm = FRONTMATTER_RE.exec(content);
|
|
15032
|
+
if (!fm?.[1]) return {};
|
|
15033
|
+
const parsed = parseFrontmatterBlock(fm[1]);
|
|
15034
|
+
if (!parsed) return {};
|
|
15035
|
+
const out = {};
|
|
15036
|
+
for (const [k, v] of Object.entries(parsed)) {
|
|
15037
|
+
if (k === "model" || RESERVED_AGENT_KEYS.has(k)) continue;
|
|
15038
|
+
out[k] = v;
|
|
15039
|
+
}
|
|
15040
|
+
return out;
|
|
15041
|
+
}
|
|
15042
|
+
function setFrontmatterOptions(content, options) {
|
|
15043
|
+
const fm = FRONTMATTER_RE.exec(content);
|
|
15044
|
+
if (!fm?.[1]) throw new Error("no frontmatter block");
|
|
15045
|
+
const block = fm[1];
|
|
15046
|
+
const keys = Object.keys(options);
|
|
15047
|
+
if (keys.length === 0) return content;
|
|
15048
|
+
const eol = block.includes("\r\n") ? "\r\n" : "\n";
|
|
15049
|
+
const lines = block.split(/\r?\n/);
|
|
15050
|
+
const handled = /* @__PURE__ */ new Set();
|
|
15051
|
+
for (let i = 0; i < lines.length; i++) {
|
|
15052
|
+
const m = /^([A-Za-z0-9_-]+):[ \t]*(.*)$/.exec(lines[i] ?? "");
|
|
15053
|
+
if (!m) continue;
|
|
15054
|
+
const key = m[1] ?? "";
|
|
15055
|
+
if (!(key in options)) continue;
|
|
15056
|
+
handled.add(key);
|
|
15057
|
+
let end = i + 1;
|
|
15058
|
+
while (end < lines.length && /^[ \t]+/.test(lines[end] ?? "")) end++;
|
|
15059
|
+
const value = options[key];
|
|
15060
|
+
if (value === null || value === void 0) {
|
|
15061
|
+
lines.splice(i, end - i);
|
|
15062
|
+
i--;
|
|
15063
|
+
} else {
|
|
15064
|
+
lines.splice(i, end - i, `${key}: ${serializeOptionValue(value)}`);
|
|
15065
|
+
}
|
|
15066
|
+
}
|
|
15067
|
+
const appended = [];
|
|
15068
|
+
for (const [key, value] of Object.entries(options)) {
|
|
15069
|
+
if (handled.has(key)) continue;
|
|
15070
|
+
if (value === null || value === void 0) continue;
|
|
15071
|
+
appended.push(`${key}: ${serializeOptionValue(value)}`);
|
|
15072
|
+
}
|
|
15073
|
+
const nextBlock = appended.length > 0 ? lines.join(eol) + eol + appended.join(eol) : lines.join(eol);
|
|
15074
|
+
if (nextBlock === block) return content;
|
|
15075
|
+
const blockStart = fm[0].indexOf(block);
|
|
15076
|
+
const nextFm = fm[0].slice(0, blockStart) + nextBlock + fm[0].slice(blockStart + block.length);
|
|
15077
|
+
return nextFm + content.slice(fm[0].length);
|
|
15078
|
+
}
|
|
14738
15079
|
function agentFilePath(agentsDir, name) {
|
|
14739
15080
|
return path3.join(agentsDir, `${name}.md`);
|
|
14740
15081
|
}
|
|
@@ -14748,7 +15089,7 @@ async function listAgentFiles(agentsDir) {
|
|
|
14748
15089
|
}
|
|
14749
15090
|
return names.filter((n) => n.endsWith(".md")).map((n) => n.slice(0, -".md".length)).sort();
|
|
14750
15091
|
}
|
|
14751
|
-
async function
|
|
15092
|
+
async function readAgentEntries(agentsDir) {
|
|
14752
15093
|
const out = {};
|
|
14753
15094
|
for (const name of await listAgentFiles(agentsDir)) {
|
|
14754
15095
|
const filePath = agentFilePath(agentsDir, name);
|
|
@@ -14759,10 +15100,17 @@ async function readAgentModels(agentsDir) {
|
|
|
14759
15100
|
throw new IOError(`Failed to read ${filePath}: ${cause.message}`, cause);
|
|
14760
15101
|
}
|
|
14761
15102
|
const model = getFrontmatterModel(content);
|
|
14762
|
-
if (model
|
|
15103
|
+
if (model === null) continue;
|
|
15104
|
+
out[name] = { model, options: getFrontmatterOptions(content) };
|
|
14763
15105
|
}
|
|
14764
15106
|
return out;
|
|
14765
15107
|
}
|
|
15108
|
+
async function readAgentModels(agentsDir) {
|
|
15109
|
+
const entries = await readAgentEntries(agentsDir);
|
|
15110
|
+
const out = {};
|
|
15111
|
+
for (const [name, entry] of Object.entries(entries)) out[name] = entry.model;
|
|
15112
|
+
return out;
|
|
15113
|
+
}
|
|
14766
15114
|
async function readAgentFileStrict(agentsDir, name) {
|
|
14767
15115
|
const filePath = agentFilePath(agentsDir, name);
|
|
14768
15116
|
if (!existsSync2(filePath)) {
|
|
@@ -14778,7 +15126,7 @@ async function readAgentFileStrict(agentsDir, name) {
|
|
|
14778
15126
|
if (model === null) {
|
|
14779
15127
|
throw new AgentFileError(name, filePath, "no frontmatter `model:` line to rewrite");
|
|
14780
15128
|
}
|
|
14781
|
-
return { filePath, content, model };
|
|
15129
|
+
return { filePath, content, model, options: getFrontmatterOptions(content) };
|
|
14782
15130
|
}
|
|
14783
15131
|
|
|
14784
15132
|
// src/core/stack-manager.ts
|
|
@@ -15036,15 +15384,14 @@ async function applyStack(paths, name, options = {}) {
|
|
|
15036
15384
|
const pending = [];
|
|
15037
15385
|
for (const [agent, entry] of Object.entries(target.agents)) {
|
|
15038
15386
|
const { filePath, content, model } = await readAgentFileStrict(paths.agentsDir, agent);
|
|
15039
|
-
|
|
15040
|
-
|
|
15041
|
-
|
|
15042
|
-
|
|
15043
|
-
});
|
|
15387
|
+
const wantOptions = entryOptions(entry);
|
|
15388
|
+
const afterOptions = setFrontmatterOptions(content, wantOptions);
|
|
15389
|
+
const nextContent = entry.model === model ? afterOptions : setFrontmatterModel(afterOptions, entry.model);
|
|
15390
|
+
pending.push({ agent, filePath, next: nextContent === content ? null : nextContent });
|
|
15044
15391
|
}
|
|
15045
15392
|
const prevState = await readState(paths.statePath);
|
|
15046
15393
|
const prevActive = prevState?.active ?? null;
|
|
15047
|
-
const displaced = { agents:
|
|
15394
|
+
const displaced = { agents: entriesToStackAgents(await readAgentEntries(paths.agentsDir)) };
|
|
15048
15395
|
const historyId = await appendHistory(
|
|
15049
15396
|
paths.historyDir,
|
|
15050
15397
|
prevActive ?? "(none)",
|
|
@@ -15074,11 +15421,24 @@ async function applyStack(paths, name, options = {}) {
|
|
|
15074
15421
|
restartRequired: true
|
|
15075
15422
|
};
|
|
15076
15423
|
}
|
|
15077
|
-
function
|
|
15424
|
+
function entryOptions(entry) {
|
|
15425
|
+
const rec = entry;
|
|
15426
|
+
const out = {};
|
|
15427
|
+
for (const [k, v] of Object.entries(rec)) {
|
|
15428
|
+
if (k === "model" || RESERVED_AGENT_KEYS.has(k)) continue;
|
|
15429
|
+
out[k] = v;
|
|
15430
|
+
}
|
|
15431
|
+
return out;
|
|
15432
|
+
}
|
|
15433
|
+
function entriesToStackAgents(entries) {
|
|
15078
15434
|
const out = {};
|
|
15079
|
-
for (const
|
|
15080
|
-
const
|
|
15081
|
-
if (
|
|
15435
|
+
for (const name of Object.keys(entries).sort()) {
|
|
15436
|
+
const entry = entries[name];
|
|
15437
|
+
if (!entry) continue;
|
|
15438
|
+
const { model, options } = entry;
|
|
15439
|
+
const stackEntry = { model };
|
|
15440
|
+
for (const [k, v] of Object.entries(options)) stackEntry[k] = v;
|
|
15441
|
+
out[name] = stackEntry;
|
|
15082
15442
|
}
|
|
15083
15443
|
return out;
|
|
15084
15444
|
}
|
|
@@ -15115,8 +15475,8 @@ async function captureStack(paths, name, options = {}) {
|
|
|
15115
15475
|
if (existsSync5(dest) && !options.force) {
|
|
15116
15476
|
throw new UserError(`Stack "${name}" already exists. Use --force to overwrite.`);
|
|
15117
15477
|
}
|
|
15118
|
-
const
|
|
15119
|
-
const agents =
|
|
15478
|
+
const entries = await readAgentEntries(paths.agentsDir);
|
|
15479
|
+
const agents = entriesToStackAgents(entries);
|
|
15120
15480
|
if (Object.keys(agents).length === 0) {
|
|
15121
15481
|
throw new UserError(
|
|
15122
15482
|
`No agent .md files with a frontmatter \`model:\` line found in ${paths.agentsDir}.`
|
|
@@ -15128,7 +15488,7 @@ async function captureStack(paths, name, options = {}) {
|
|
|
15128
15488
|
}
|
|
15129
15489
|
|
|
15130
15490
|
// src/version.ts
|
|
15131
|
-
var VERSION = "1.0.
|
|
15491
|
+
var VERSION = "1.0.7";
|
|
15132
15492
|
|
|
15133
15493
|
// src/plugin.ts
|
|
15134
15494
|
async function safeToast(client, message, variant = "success") {
|