switchroom 0.13.49 → 0.13.51
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/cli/switchroom.js
CHANGED
|
@@ -23263,6 +23263,12 @@ function emitAgentService(lines, a, imageTag, buildMode, buildContext, homePrefi
|
|
|
23263
23263
|
mkdirSync8(`${hostHomeForChecks}/.switchroom/agents/${a.name}/schedule.d`, { recursive: true });
|
|
23264
23264
|
} catch {}
|
|
23265
23265
|
lines.push(` - ${homePrefix}/.switchroom/audit/${a.name}:${homePrefix}/.switchroom/audit/${a.name}:rw`);
|
|
23266
|
+
if (existsSync12(`${hostHomeForChecks}/.switchroom-config`)) {
|
|
23267
|
+
try {
|
|
23268
|
+
mkdirSync8(`${hostHomeForChecks}/.switchroom-config/agents/${a.name}/personal-skills`, { recursive: true });
|
|
23269
|
+
} catch {}
|
|
23270
|
+
lines.push(` - ${homePrefix}/.switchroom-config/agents/${a.name}/personal-skills:${homePrefix}/.switchroom-config/agents/${a.name}/personal-skills:rw`);
|
|
23271
|
+
}
|
|
23266
23272
|
if (bundledSkillsPoolDir && existsSync12(bundledSkillsPoolDir) && !bundledSkillsPoolDir.startsWith(`${hostHomeForChecks}/.switchroom/skills`)) {
|
|
23267
23273
|
lines.push(` - ${bundledSkillsPoolDir}:${bundledSkillsPoolDir}:ro`);
|
|
23268
23274
|
}
|
|
@@ -47249,6 +47255,20 @@ function dispatchTool(name, args) {
|
|
|
47249
47255
|
parseMode = "json";
|
|
47250
47256
|
break;
|
|
47251
47257
|
}
|
|
47258
|
+
case "skill_clone_to_personal": {
|
|
47259
|
+
const a = args;
|
|
47260
|
+
if (!a.source || typeof a.source !== "string") {
|
|
47261
|
+
return errorText("skill_clone_to_personal: source is required");
|
|
47262
|
+
}
|
|
47263
|
+
const base = ["skill", "clone-to-personal", a.source];
|
|
47264
|
+
if (a.agent)
|
|
47265
|
+
base.push("--agent", a.agent);
|
|
47266
|
+
if (a.name)
|
|
47267
|
+
base.push("--name", a.name);
|
|
47268
|
+
cliArgs = base;
|
|
47269
|
+
parseMode = "json";
|
|
47270
|
+
break;
|
|
47271
|
+
}
|
|
47252
47272
|
default:
|
|
47253
47273
|
return errorText(`unknown tool: ${name}`);
|
|
47254
47274
|
}
|
|
@@ -47488,6 +47508,26 @@ var init_server3 = __esm(() => {
|
|
|
47488
47508
|
type: "object",
|
|
47489
47509
|
properties: {}
|
|
47490
47510
|
}
|
|
47511
|
+
},
|
|
47512
|
+
{
|
|
47513
|
+
name: "skill_clone_to_personal",
|
|
47514
|
+
description: "Fork a shared or bundled skill into this agent's writable workspace. " + "Use when you find a defect or gap in a skill you depend on and want " + "to fix it yourself \u2014 the upstream source is untouched, your fork is " + "yours to edit via `skill_edit_personal`. Source format: " + "`shared:<name>` or `bundled:<name>`. Same validation gates as " + "init/edit. Pre-existing personal-<name> is refused (use edit or " + "remove first). No operator approval.",
|
|
47515
|
+
inputSchema: {
|
|
47516
|
+
type: "object",
|
|
47517
|
+
required: ["source"],
|
|
47518
|
+
properties: {
|
|
47519
|
+
source: {
|
|
47520
|
+
type: "string",
|
|
47521
|
+
pattern: "^(shared|bundled):[a-z0-9][a-z0-9_-]{0,62}$",
|
|
47522
|
+
description: "Source skill: `shared:<name>` (operator pool) or `bundled:<name>` (shipped)."
|
|
47523
|
+
},
|
|
47524
|
+
name: {
|
|
47525
|
+
type: "string",
|
|
47526
|
+
pattern: "^[a-z0-9][a-z0-9_-]{0,62}$",
|
|
47527
|
+
description: "Optional destination slug (defaults to the source slug)."
|
|
47528
|
+
}
|
|
47529
|
+
}
|
|
47530
|
+
}
|
|
47491
47531
|
}
|
|
47492
47532
|
];
|
|
47493
47533
|
});
|
|
@@ -47913,8 +47953,8 @@ var {
|
|
|
47913
47953
|
} = import__.default;
|
|
47914
47954
|
|
|
47915
47955
|
// src/build-info.ts
|
|
47916
|
-
var VERSION = "0.13.
|
|
47917
|
-
var COMMIT_SHA = "
|
|
47956
|
+
var VERSION = "0.13.51";
|
|
47957
|
+
var COMMIT_SHA = "9494f463";
|
|
47918
47958
|
|
|
47919
47959
|
// src/cli/agent.ts
|
|
47920
47960
|
init_source();
|
|
@@ -48486,6 +48526,7 @@ function alignAgentUid(name, agentDir, uid, opts = {}) {
|
|
|
48486
48526
|
const writeOut = opts.writeOut ?? ((s) => process.stdout.write(s));
|
|
48487
48527
|
const logsDir = join8(homedir4(), ".switchroom", "logs", name);
|
|
48488
48528
|
const auditDir = join8(homedir4(), ".switchroom", "audit", name);
|
|
48529
|
+
const configMirrorDir = join8(homedir4(), ".switchroom-config", "agents", name, "personal-skills");
|
|
48489
48530
|
const paths = [];
|
|
48490
48531
|
if (existsSync11(agentDir))
|
|
48491
48532
|
paths.push(agentDir);
|
|
@@ -48493,6 +48534,8 @@ function alignAgentUid(name, agentDir, uid, opts = {}) {
|
|
|
48493
48534
|
paths.push(logsDir);
|
|
48494
48535
|
if (existsSync11(auditDir))
|
|
48495
48536
|
paths.push(auditDir);
|
|
48537
|
+
if (existsSync11(configMirrorDir))
|
|
48538
|
+
paths.push(configMirrorDir);
|
|
48496
48539
|
if (paths.length === 0)
|
|
48497
48540
|
return { chowned: false, paths: [] };
|
|
48498
48541
|
const priors = [];
|
|
@@ -74728,6 +74771,9 @@ async function ensureHostMountSources(config) {
|
|
|
74728
74771
|
dirs.push(join62(home2, ".switchroom", "logs", name));
|
|
74729
74772
|
dirs.push(join62(home2, ".claude", "projects", name));
|
|
74730
74773
|
dirs.push(join62(home2, ".switchroom", "audit", name));
|
|
74774
|
+
if (existsSync68(join62(home2, ".switchroom-config"))) {
|
|
74775
|
+
dirs.push(join62(home2, ".switchroom-config", "agents", name, "personal-skills"));
|
|
74776
|
+
}
|
|
74731
74777
|
}
|
|
74732
74778
|
for (const dir of dirs) {
|
|
74733
74779
|
await mkdir(dir, { recursive: true });
|
|
@@ -77606,6 +77652,87 @@ init_source();
|
|
|
77606
77652
|
var PERSONAL_PREFIX = "personal-";
|
|
77607
77653
|
var TRASH_DIRNAME = "skills-trash";
|
|
77608
77654
|
var TRASH_TTL_MS = 24 * 60 * 60 * 1000;
|
|
77655
|
+
var PERSONAL_SKILLS_SUBPATH = "personal-skills";
|
|
77656
|
+
function resolveConfigSkillsDir(agent) {
|
|
77657
|
+
const override = process.env.SWITCHROOM_CONFIG_DIR;
|
|
77658
|
+
const candidate = override ? resolve45(override) : join69(homedir39(), ".switchroom-config");
|
|
77659
|
+
if (!existsSync76(candidate))
|
|
77660
|
+
return null;
|
|
77661
|
+
return join69(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
|
|
77662
|
+
}
|
|
77663
|
+
var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
|
|
77664
|
+
function sweepMirrorPriors(configSkillsRoot) {
|
|
77665
|
+
try {
|
|
77666
|
+
if (!existsSync76(configSkillsRoot))
|
|
77667
|
+
return;
|
|
77668
|
+
const now = Date.now();
|
|
77669
|
+
for (const ent of readdirSync30(configSkillsRoot)) {
|
|
77670
|
+
const m = /^\.(?:.+)-(?:prior|trash)-(\d+)$/.exec(ent);
|
|
77671
|
+
if (!m)
|
|
77672
|
+
continue;
|
|
77673
|
+
const ts = Number(m[1]);
|
|
77674
|
+
if (!Number.isFinite(ts))
|
|
77675
|
+
continue;
|
|
77676
|
+
if (now - ts < MIRROR_PRIOR_TTL_MS)
|
|
77677
|
+
continue;
|
|
77678
|
+
try {
|
|
77679
|
+
rmSync17(join69(configSkillsRoot, ent), { recursive: true, force: true });
|
|
77680
|
+
} catch {}
|
|
77681
|
+
}
|
|
77682
|
+
} catch {}
|
|
77683
|
+
}
|
|
77684
|
+
function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
77685
|
+
const configSkillsRoot = resolveConfigSkillsDir(agent);
|
|
77686
|
+
if (!configSkillsRoot)
|
|
77687
|
+
return;
|
|
77688
|
+
const dest = join69(configSkillsRoot, name);
|
|
77689
|
+
try {
|
|
77690
|
+
if (liveSkillDir !== null) {
|
|
77691
|
+
try {
|
|
77692
|
+
const st = lstatSync9(liveSkillDir);
|
|
77693
|
+
if (st.isSymbolicLink()) {
|
|
77694
|
+
process.stderr.write(source_default.yellow(`warning: refusing to mirror ${liveSkillDir} \u2014 source is a symlink
|
|
77695
|
+
`));
|
|
77696
|
+
return;
|
|
77697
|
+
}
|
|
77698
|
+
} catch {}
|
|
77699
|
+
}
|
|
77700
|
+
if (liveSkillDir === null) {
|
|
77701
|
+
sweepMirrorPriors(configSkillsRoot);
|
|
77702
|
+
if (existsSync76(dest)) {
|
|
77703
|
+
const trash = join69(configSkillsRoot, `.${name}-trash-${Date.now()}`);
|
|
77704
|
+
renameSync17(dest, trash);
|
|
77705
|
+
}
|
|
77706
|
+
return;
|
|
77707
|
+
}
|
|
77708
|
+
mkdirSync41(configSkillsRoot, { recursive: true, mode: 493 });
|
|
77709
|
+
sweepMirrorPriors(configSkillsRoot);
|
|
77710
|
+
const staging = mkdtempSync6(join69(configSkillsRoot, `.${name}-staging-`));
|
|
77711
|
+
const walk2 = (src, dst) => {
|
|
77712
|
+
mkdirSync41(dst, { recursive: true, mode: 493 });
|
|
77713
|
+
for (const ent of readdirSync30(src, { withFileTypes: true })) {
|
|
77714
|
+
const s = join69(src, ent.name);
|
|
77715
|
+
const d = join69(dst, ent.name);
|
|
77716
|
+
if (ent.isSymbolicLink())
|
|
77717
|
+
continue;
|
|
77718
|
+
if (ent.isDirectory())
|
|
77719
|
+
walk2(s, d);
|
|
77720
|
+
else if (ent.isFile()) {
|
|
77721
|
+
writeFileSync35(d, readFileSync61(s));
|
|
77722
|
+
}
|
|
77723
|
+
}
|
|
77724
|
+
};
|
|
77725
|
+
walk2(liveSkillDir, staging);
|
|
77726
|
+
if (existsSync76(dest)) {
|
|
77727
|
+
const prior = join69(configSkillsRoot, `.${name}-prior-${Date.now()}`);
|
|
77728
|
+
renameSync17(dest, prior);
|
|
77729
|
+
}
|
|
77730
|
+
renameSync17(staging, dest);
|
|
77731
|
+
} catch (err2) {
|
|
77732
|
+
process.stderr.write(source_default.yellow(`warning: mirror to ${dest} failed (${err2.message ?? err2}); ` + `live copy still works, but this skill is not version-controlled until next successful sync.
|
|
77733
|
+
`));
|
|
77734
|
+
}
|
|
77735
|
+
}
|
|
77609
77736
|
function fail3(msg, exit = 2) {
|
|
77610
77737
|
console.error(source_default.red(`error: ${msg}`));
|
|
77611
77738
|
process.exit(exit);
|
|
@@ -77842,14 +77969,6 @@ function loadValidateWrite(agentsRoot, agent, name, files, ensureNew) {
|
|
|
77842
77969
|
process.exit(3);
|
|
77843
77970
|
}
|
|
77844
77971
|
writePersonalSkill(target, files);
|
|
77845
|
-
console.log(JSON.stringify({
|
|
77846
|
-
ok: true,
|
|
77847
|
-
action: ensureNew ? "init" : "edit",
|
|
77848
|
-
agent,
|
|
77849
|
-
name,
|
|
77850
|
-
path: target,
|
|
77851
|
-
files: Object.keys(files).length
|
|
77852
|
-
}));
|
|
77853
77972
|
}
|
|
77854
77973
|
function loadFiles(opts) {
|
|
77855
77974
|
if (opts.from === undefined) {
|
|
@@ -77873,6 +77992,16 @@ function initPersonalAction(name, opts) {
|
|
|
77873
77992
|
const agentsRoot = resolveAgentsRoot(opts);
|
|
77874
77993
|
const files = loadFiles(opts);
|
|
77875
77994
|
loadValidateWrite(agentsRoot, agent, name, files, true);
|
|
77995
|
+
const skillDir = personalSkillDir(agentsRoot, agent, name);
|
|
77996
|
+
mirrorToConfigRepo(agent, name, skillDir);
|
|
77997
|
+
console.log(JSON.stringify({
|
|
77998
|
+
ok: true,
|
|
77999
|
+
action: "init",
|
|
78000
|
+
agent,
|
|
78001
|
+
name,
|
|
78002
|
+
path: skillDir,
|
|
78003
|
+
files: Object.keys(files).length
|
|
78004
|
+
}));
|
|
77876
78005
|
appendAudit(agent, "skill.init_personal", { name, files: Object.keys(files).length }, 0);
|
|
77877
78006
|
}
|
|
77878
78007
|
function editPersonalAction(name, opts) {
|
|
@@ -77880,8 +78009,137 @@ function editPersonalAction(name, opts) {
|
|
|
77880
78009
|
const agentsRoot = resolveAgentsRoot(opts);
|
|
77881
78010
|
const files = loadFiles(opts);
|
|
77882
78011
|
loadValidateWrite(agentsRoot, agent, name, files, false);
|
|
78012
|
+
const skillDir = personalSkillDir(agentsRoot, agent, name);
|
|
78013
|
+
mirrorToConfigRepo(agent, name, skillDir);
|
|
78014
|
+
console.log(JSON.stringify({
|
|
78015
|
+
ok: true,
|
|
78016
|
+
action: "edit",
|
|
78017
|
+
agent,
|
|
78018
|
+
name,
|
|
78019
|
+
path: skillDir,
|
|
78020
|
+
files: Object.keys(files).length
|
|
78021
|
+
}));
|
|
77883
78022
|
appendAudit(agent, "skill.edit_personal", { name, files: Object.keys(files).length }, 0);
|
|
77884
78023
|
}
|
|
78024
|
+
var CLONE_SOURCE_RE = /^(shared|bundled):([a-z0-9][a-z0-9_-]{0,62})$/;
|
|
78025
|
+
function defaultSharedRoot() {
|
|
78026
|
+
return join69(homedir39(), ".switchroom", "skills");
|
|
78027
|
+
}
|
|
78028
|
+
function defaultBundledRoot() {
|
|
78029
|
+
return join69(homedir39(), ".switchroom", "skills", "_bundled");
|
|
78030
|
+
}
|
|
78031
|
+
function resolveCloneSource(source, opts) {
|
|
78032
|
+
const m = CLONE_SOURCE_RE.exec(source);
|
|
78033
|
+
if (!m) {
|
|
78034
|
+
fail3(`source must be \`shared:<name>\` or \`bundled:<name>\` (got ${JSON.stringify(source)})`);
|
|
78035
|
+
}
|
|
78036
|
+
const tier = m[1];
|
|
78037
|
+
const slug = m[2];
|
|
78038
|
+
const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
|
|
78039
|
+
const dir = join69(root, slug);
|
|
78040
|
+
if (!existsSync76(dir)) {
|
|
78041
|
+
fail3(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
|
|
78042
|
+
}
|
|
78043
|
+
const st = lstatSync9(dir);
|
|
78044
|
+
if (st.isSymbolicLink()) {
|
|
78045
|
+
fail3(`clone source ${JSON.stringify(source)} is a symlink at ${dir}; ` + `point clone at the canonical pool path instead`);
|
|
78046
|
+
}
|
|
78047
|
+
return { tier, slug, dir };
|
|
78048
|
+
}
|
|
78049
|
+
var CLONE_MAX_FILE_BYTES = 1024 * 1024;
|
|
78050
|
+
function readSourceFiles(dir) {
|
|
78051
|
+
const files = {};
|
|
78052
|
+
const skipped = [];
|
|
78053
|
+
const walk2 = (sub) => {
|
|
78054
|
+
for (const ent of readdirSync30(sub, { withFileTypes: true })) {
|
|
78055
|
+
const full = join69(sub, ent.name);
|
|
78056
|
+
if (ent.isSymbolicLink()) {
|
|
78057
|
+
continue;
|
|
78058
|
+
}
|
|
78059
|
+
if (ent.isDirectory()) {
|
|
78060
|
+
walk2(full);
|
|
78061
|
+
continue;
|
|
78062
|
+
}
|
|
78063
|
+
if (ent.isFile()) {
|
|
78064
|
+
const rel = relative3(dir, full).replace(/\\/g, "/");
|
|
78065
|
+
if (!validateRelPath(rel)) {
|
|
78066
|
+
skipped.push(rel);
|
|
78067
|
+
continue;
|
|
78068
|
+
}
|
|
78069
|
+
try {
|
|
78070
|
+
const st = lstatSync9(full);
|
|
78071
|
+
if (st.size > CLONE_MAX_FILE_BYTES) {
|
|
78072
|
+
fail3(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
|
|
78073
|
+
}
|
|
78074
|
+
} catch {}
|
|
78075
|
+
files[rel] = readFileSync61(full, "utf-8");
|
|
78076
|
+
}
|
|
78077
|
+
}
|
|
78078
|
+
};
|
|
78079
|
+
walk2(dir);
|
|
78080
|
+
return { files, skipped };
|
|
78081
|
+
}
|
|
78082
|
+
function rewriteSkillMdName(content, newName) {
|
|
78083
|
+
if (!content.startsWith(`---
|
|
78084
|
+
`) && !content.startsWith(`---\r
|
|
78085
|
+
`)) {
|
|
78086
|
+
return content;
|
|
78087
|
+
}
|
|
78088
|
+
const rest = content.slice(content.indexOf(`
|
|
78089
|
+
`) + 1);
|
|
78090
|
+
const endIdx = rest.indexOf(`
|
|
78091
|
+
---`);
|
|
78092
|
+
if (endIdx < 0)
|
|
78093
|
+
return content;
|
|
78094
|
+
const fm = rest.slice(0, endIdx);
|
|
78095
|
+
const body = rest.slice(endIdx);
|
|
78096
|
+
const patched = fm.replace(/^(\s*name\s*:)[ \t]*\S.*$/m, `$1 ${newName}`);
|
|
78097
|
+
return `---
|
|
78098
|
+
` + patched + body;
|
|
78099
|
+
}
|
|
78100
|
+
function clonePersonalAction(source, opts) {
|
|
78101
|
+
const agent = resolveAgent(opts);
|
|
78102
|
+
const agentsRoot = resolveAgentsRoot(opts);
|
|
78103
|
+
const src = resolveCloneSource(source, opts);
|
|
78104
|
+
const newName = opts.name ?? src.slug;
|
|
78105
|
+
if (!SKILL_SLUG_RE.test(newName)) {
|
|
78106
|
+
fail3(`destination name must match ${SKILL_SLUG_RE.source}: got ${JSON.stringify(newName)}`);
|
|
78107
|
+
}
|
|
78108
|
+
const { files, skipped } = readSourceFiles(src.dir);
|
|
78109
|
+
if (!files["SKILL.md"]) {
|
|
78110
|
+
fail3(`source ${JSON.stringify(source)} has no SKILL.md at ${src.dir}`);
|
|
78111
|
+
}
|
|
78112
|
+
if (newName !== src.slug) {
|
|
78113
|
+
files["SKILL.md"] = rewriteSkillMdName(files["SKILL.md"], newName);
|
|
78114
|
+
}
|
|
78115
|
+
if (skipped.length > 0) {
|
|
78116
|
+
process.stderr.write(source_default.yellow(`note: skipped ${skipped.length} non-allowlisted path${skipped.length === 1 ? "" : "s"} from source: ${skipped.join(", ")}
|
|
78117
|
+
`));
|
|
78118
|
+
}
|
|
78119
|
+
loadValidateWrite(agentsRoot, agent, newName, files, true);
|
|
78120
|
+
const skillDir = personalSkillDir(agentsRoot, agent, newName);
|
|
78121
|
+
mirrorToConfigRepo(agent, newName, skillDir);
|
|
78122
|
+
console.log(JSON.stringify({
|
|
78123
|
+
ok: true,
|
|
78124
|
+
action: "clone_to_personal",
|
|
78125
|
+
agent,
|
|
78126
|
+
source,
|
|
78127
|
+
source_tier: src.tier,
|
|
78128
|
+
source_slug: src.slug,
|
|
78129
|
+
name: newName,
|
|
78130
|
+
path: skillDir,
|
|
78131
|
+
files: Object.keys(files).length,
|
|
78132
|
+
skipped
|
|
78133
|
+
}));
|
|
78134
|
+
appendAudit(agent, "skill.clone_to_personal", {
|
|
78135
|
+
source,
|
|
78136
|
+
source_tier: src.tier,
|
|
78137
|
+
source_slug: src.slug,
|
|
78138
|
+
name: newName,
|
|
78139
|
+
files: Object.keys(files).length,
|
|
78140
|
+
skipped_count: skipped.length
|
|
78141
|
+
}, 0);
|
|
78142
|
+
}
|
|
77885
78143
|
function removePersonalAction(name, opts) {
|
|
77886
78144
|
const agent = resolveAgent(opts);
|
|
77887
78145
|
const agentsRoot = resolveAgentsRoot(opts);
|
|
@@ -77909,6 +78167,7 @@ function removePersonalAction(name, opts) {
|
|
|
77909
78167
|
renameSync17(target, trashTarget);
|
|
77910
78168
|
const now = new Date(ts);
|
|
77911
78169
|
utimesSync(trashTarget, now, now);
|
|
78170
|
+
mirrorToConfigRepo(agent, name, null);
|
|
77912
78171
|
console.log(JSON.stringify({
|
|
77913
78172
|
ok: true,
|
|
77914
78173
|
action: "remove",
|
|
@@ -77974,6 +78233,9 @@ function registerSkillPersonalCommands(program3) {
|
|
|
77974
78233
|
parent.command("list-personal").description("List personal skills owned by this agent. JSON output by default.").option("--agent <name>", "Agent name (defaults to $SWITCHROOM_AGENT_NAME)").option("--root <path>", "Test-only override for agents-root dir").action(withConfigError(async (opts) => {
|
|
77975
78234
|
listPersonalAction(opts);
|
|
77976
78235
|
}));
|
|
78236
|
+
parent.command("clone-to-personal <source>").description("Fork a shared or bundled skill into this agent's writable workspace. " + "Source format: `shared:<name>` or `bundled:<name>`. The personal copy " + "becomes mutable via edit-personal; the upstream source is untouched. " + "Use --name to give the fork a different slug. No operator approval \u2014 " + "agent's own workspace.").option("--agent <name>", "Agent name (defaults to $SWITCHROOM_AGENT_NAME)").option("--name <slug>", "Override destination slug (default: source slug)").addOption(new Option("--root <path>").hideHelp()).addOption(new Option("--shared-root <path>").hideHelp()).addOption(new Option("--bundled-root <path>").hideHelp()).action(withConfigError(async (source, opts) => {
|
|
78237
|
+
clonePersonalAction(source, opts);
|
|
78238
|
+
}));
|
|
77977
78239
|
}
|
|
77978
78240
|
|
|
77979
78241
|
// src/cli/skill-search.ts
|
|
@@ -77988,10 +78250,10 @@ var AGENT_NAME_RE3 = /^[a-z][a-z0-9_-]{0,62}$/;
|
|
|
77988
78250
|
function defaultAgentsRoot() {
|
|
77989
78251
|
return resolve46(homedir40(), ".switchroom/agents");
|
|
77990
78252
|
}
|
|
77991
|
-
function
|
|
78253
|
+
function defaultSharedRoot2() {
|
|
77992
78254
|
return resolve46(homedir40(), ".switchroom/skills");
|
|
77993
78255
|
}
|
|
77994
|
-
function
|
|
78256
|
+
function defaultBundledRoot2() {
|
|
77995
78257
|
return resolve46(homedir40(), ".switchroom/skills/_bundled");
|
|
77996
78258
|
}
|
|
77997
78259
|
function readSkillFrontmatter(skillDir) {
|
|
@@ -78077,7 +78339,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
|
78077
78339
|
}
|
|
78078
78340
|
return out;
|
|
78079
78341
|
}
|
|
78080
|
-
function listSharedSkills(sharedRoot =
|
|
78342
|
+
function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
78081
78343
|
if (!existsSync77(sharedRoot))
|
|
78082
78344
|
return [];
|
|
78083
78345
|
const out = [];
|
|
@@ -78115,7 +78377,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot()) {
|
|
|
78115
78377
|
}
|
|
78116
78378
|
return out;
|
|
78117
78379
|
}
|
|
78118
|
-
function listBundledSkills(bundledRoot =
|
|
78380
|
+
function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
|
|
78119
78381
|
if (!existsSync77(bundledRoot))
|
|
78120
78382
|
return [];
|
|
78121
78383
|
const out = [];
|
package/package.json
CHANGED
|
@@ -159,6 +159,19 @@ fi
|
|
|
159
159
|
if [ ! -e "$HOME/.switchroom" ] || [ -L "$HOME/.switchroom" ]; then
|
|
160
160
|
ln -sfn {{{hostHomeQ}}}/.switchroom "$HOME/.switchroom" 2>/dev/null || true
|
|
161
161
|
fi
|
|
162
|
+
# Host ~/.switchroom-config symlink (#1846). Same shape as the
|
|
163
|
+
# ~/.switchroom link above, for the operator's git-tracked config repo.
|
|
164
|
+
# The compose generator bind-mounts a per-agent slice at
|
|
165
|
+
# <host-home>/.switchroom-config/agents/<self>/personal-skills/ (when
|
|
166
|
+
# the operator has opted in). Without this symlink, the CLI's
|
|
167
|
+
# resolveConfigSkillsDir would call existsSync("$HOME/.switchroom-config")
|
|
168
|
+
# and miss the bind-mounted path entirely → mirror silently no-ops.
|
|
169
|
+
# The link lands ONLY when the host slice is actually mounted (the dir
|
|
170
|
+
# the bind-mount targets) — leaving a dangling link when the operator
|
|
171
|
+
# hasn't opted in is fine, existsSync resolves false through it.
|
|
172
|
+
if [ ! -e "$HOME/.switchroom-config" ] || [ -L "$HOME/.switchroom-config" ]; then
|
|
173
|
+
ln -sfn {{{hostHomeQ}}}/.switchroom-config "$HOME/.switchroom-config" 2>/dev/null || true
|
|
174
|
+
fi
|
|
162
175
|
{{/if}}
|
|
163
176
|
|
|
164
177
|
export NVM_DIR="$HOME/.nvm"
|
|
@@ -48732,10 +48732,10 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
48732
48732
|
}
|
|
48733
48733
|
|
|
48734
48734
|
// ../src/build-info.ts
|
|
48735
|
-
var VERSION = "0.13.
|
|
48736
|
-
var COMMIT_SHA = "
|
|
48737
|
-
var COMMIT_DATE = "2026-05-
|
|
48738
|
-
var LATEST_PR =
|
|
48735
|
+
var VERSION = "0.13.51";
|
|
48736
|
+
var COMMIT_SHA = "9494f463";
|
|
48737
|
+
var COMMIT_DATE = "2026-05-25T23:39:25Z";
|
|
48738
|
+
var LATEST_PR = 1861;
|
|
48739
48739
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
48740
48740
|
|
|
48741
48741
|
// gateway/boot-version.ts
|