switchroom 0.13.51 → 0.13.52
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 +134 -155
- package/package.json +1 -1
- package/profiles/_base/start.sh.hbs +17 -4
- package/profiles/_shared/agent-self-service.md.hbs +12 -22
- package/profiles/coding/CLAUDE.md.hbs +1 -1
- package/profiles/default/CLAUDE.md +122 -0
- package/profiles/default/CLAUDE.md.hbs +8 -1
- package/profiles/executive-assistant/CLAUDE.md.hbs +1 -1
- package/profiles/health-coach/CLAUDE.md.hbs +1 -1
- package/telegram-plugin/dist/gateway/gateway.js +4 -4
package/dist/cli/switchroom.js
CHANGED
|
@@ -23253,6 +23253,9 @@ function emitAgentService(lines, a, imageTag, buildMode, buildContext, homePrefi
|
|
|
23253
23253
|
if (existsSync12(`${hostHomeForChecks}/.switchroom/mcp-launchers`)) {
|
|
23254
23254
|
lines.push(` - ${homePrefix}/.switchroom/mcp-launchers:${homePrefix}/.switchroom/mcp-launchers:ro`);
|
|
23255
23255
|
}
|
|
23256
|
+
if (existsSync12(`${hostHomeForChecks}/.switchroom/fleet`)) {
|
|
23257
|
+
lines.push(` - ${homePrefix}/.switchroom/fleet:${homePrefix}/.switchroom/fleet:ro`);
|
|
23258
|
+
}
|
|
23256
23259
|
if (existsSync12(`${hostHomeForChecks}/.switchroom/credentials/${a.name}`)) {
|
|
23257
23260
|
lines.push(` - ${homePrefix}/.switchroom/credentials/${a.name}:${homePrefix}/.switchroom/credentials:ro`);
|
|
23258
23261
|
}
|
|
@@ -47542,7 +47545,7 @@ __export(exports_server2, {
|
|
|
47542
47545
|
TOOLS: () => TOOLS2
|
|
47543
47546
|
});
|
|
47544
47547
|
import { randomBytes as randomBytes14 } from "node:crypto";
|
|
47545
|
-
import { existsSync as existsSync78, readFileSync as
|
|
47548
|
+
import { existsSync as existsSync78, readFileSync as readFileSync64 } from "node:fs";
|
|
47546
47549
|
function selfSocketPath() {
|
|
47547
47550
|
return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
|
|
47548
47551
|
}
|
|
@@ -47720,7 +47723,7 @@ function getLastUpdateApplyStatus() {
|
|
|
47720
47723
|
}
|
|
47721
47724
|
let raw;
|
|
47722
47725
|
try {
|
|
47723
|
-
raw =
|
|
47726
|
+
raw = readFileSync64(path8, "utf-8");
|
|
47724
47727
|
} catch (err2) {
|
|
47725
47728
|
return errorText2(`get_status: failed to read audit log at ${path8}: ${err2.message}`);
|
|
47726
47729
|
}
|
|
@@ -47953,8 +47956,8 @@ var {
|
|
|
47953
47956
|
} = import__.default;
|
|
47954
47957
|
|
|
47955
47958
|
// src/build-info.ts
|
|
47956
|
-
var VERSION = "0.13.
|
|
47957
|
-
var COMMIT_SHA = "
|
|
47959
|
+
var VERSION = "0.13.52";
|
|
47960
|
+
var COMMIT_SHA = "3d68efa2";
|
|
47958
47961
|
|
|
47959
47962
|
// src/cli/agent.ts
|
|
47960
47963
|
init_source();
|
|
@@ -48522,6 +48525,85 @@ Example response shapes:
|
|
|
48522
48525
|
\`docker/Dockerfile.agent\` and rebuild the agent image."
|
|
48523
48526
|
- "I tried to clone into \`/workspace\` \u2014 that path doesn't exist in my
|
|
48524
48527
|
sandbox. Cloning into \`$HOME/workspace\` instead."`;
|
|
48528
|
+
var TELEGRAM_GUIDANCE = `## Talking to a human on Telegram
|
|
48529
|
+
|
|
48530
|
+
There is a real person on the other end. Every turn should feel like
|
|
48531
|
+
messaging a capable colleague \u2014 not a tool emitting output. Five beats:
|
|
48532
|
+
|
|
48533
|
+
1. **Acknowledge first.** Unless your whole reply is a single short
|
|
48534
|
+
sentence you can send right now, your FIRST action this turn is a
|
|
48535
|
+
brief \`reply\` in your own voice \u2014 "on it", "good question, one
|
|
48536
|
+
sec", "let me dig in" \u2014 before any tool call and before you
|
|
48537
|
+
compose the full answer. This holds even for a pure-thinking
|
|
48538
|
+
answer: if it will run to a paragraph, ack first. It is the line
|
|
48539
|
+
between a colleague and a black box.
|
|
48540
|
+
2. **Then go quiet and work.** Heads-down is correct \u2014 do NOT narrate
|
|
48541
|
+
every tool call. A typing indicator runs automatically while you
|
|
48542
|
+
work; you do not maintain it.
|
|
48543
|
+
3. **Surface meaningful progress** at genuine inflection points \u2014 a
|
|
48544
|
+
hard step finished, a blocker, a pivot, dispatching a sub-agent, a
|
|
48545
|
+
notably slow wait, a finding worth knowing now. One short \`reply\`,
|
|
48546
|
+
\`disable_notification: true\`.
|
|
48547
|
+
4. **Hand back delegations with synthesis.** When a sub-agent / worker
|
|
48548
|
+
returns, re-enter in YOUR voice \u2014 what it found, and what you are
|
|
48549
|
+
doing next. Never let its raw report stand as your reply. A
|
|
48550
|
+
*background* worker finishes after your turn ends; its result
|
|
48551
|
+
arrives as a fresh \`<channel source="subagent_handback">\` turn \u2014
|
|
48552
|
+
treat that turn as the cue to do exactly this.
|
|
48553
|
+
5. **Deliver the answer** as a final \`reply\`.
|
|
48554
|
+
|
|
48555
|
+
The one thing to avoid is *spam*: a reply on every tool call, on a
|
|
48556
|
+
timer, or repeating what you already said. Responsive and human, never
|
|
48557
|
+
a flood. Going quiet mid-work is fine \u2014 going quiet *instead* of
|
|
48558
|
+
acknowledging, or *instead* of an update at a real milestone, is the
|
|
48559
|
+
black box this exists to prevent.
|
|
48560
|
+
|
|
48561
|
+
Every turn that answers a user message ends with a user-visible
|
|
48562
|
+
\`reply\` (or \`stream_reply\` done=true) \u2014 Telegram is all the user
|
|
48563
|
+
sees; your terminal output never reaches them.`;
|
|
48564
|
+
var MEMORY_GUIDANCE = `## Memory \u2014 proactive, conversational
|
|
48565
|
+
|
|
48566
|
+
You have Hindsight tools: \`mcp__hindsight__sync_retain\`, \`mcp__hindsight__delete_memory\`, \`mcp__hindsight__recall\`, \`mcp__hindsight__reflect\`. Use them without being asked.
|
|
48567
|
+
|
|
48568
|
+
### Retain proactively
|
|
48569
|
+
When the user shares a fact, preference, decision, or plan worth keeping across sessions, call \`sync_retain\` in the same turn. Briefly acknowledge in your reply ("got it, April 2nd anniversary"). Don't narrate the tool call. Skip small talk and transient tool output, the auto-retain hook handles conversation-level signal.
|
|
48570
|
+
|
|
48571
|
+
### Correct proactively
|
|
48572
|
+
When the user corrects you or contradicts a prior memory, call \`delete_memory\` on the wrong entry, then \`sync_retain\` the correction. Acknowledge the correction in one line ("noted, Alice not Bob").
|
|
48573
|
+
|
|
48574
|
+
### Forget proactively
|
|
48575
|
+
When the user asks you to forget something ("forget that", "delete X", "drop what I said about Y"), call \`delete_memory\` for matching entries and confirm what was removed.
|
|
48576
|
+
|
|
48577
|
+
### Inspect proactively
|
|
48578
|
+
When the user asks "what do you know about X / me", "what do you remember about Y", or any memory audit, use \`reflect\` to synthesize an answer across the bank. Return it as honest prose, not a raw dump. If the bank has little on the topic, say so.
|
|
48579
|
+
|
|
48580
|
+
Don't wait for a slash command. Don't ask permission. Memory work is table stakes, like a colleague who takes notes and remembers.`;
|
|
48581
|
+
function renderFleetInvariants() {
|
|
48582
|
+
return [
|
|
48583
|
+
"<!--",
|
|
48584
|
+
" ~/.switchroom/fleet/switchroom-invariants.md",
|
|
48585
|
+
"",
|
|
48586
|
+
" Release-controlled fleet invariants. Every Switchroom agent reads",
|
|
48587
|
+
" this via Claude Code native CLAUDE.md discovery, since the agent's",
|
|
48588
|
+
" `claude` process boots with `--add-dir ~/.switchroom/fleet`.",
|
|
48589
|
+
"",
|
|
48590
|
+
" DO NOT EDIT. `switchroom apply` regenerates this file on every run",
|
|
48591
|
+
" and restores it if it drifts from the release canonical. To extend",
|
|
48592
|
+
" the agent's behaviour fleet-wide, edit ~/.switchroom/fleet/CLAUDE.md",
|
|
48593
|
+
" instead (operator-owned, additive, preserved across applies).",
|
|
48594
|
+
"-->",
|
|
48595
|
+
"",
|
|
48596
|
+
"# Switchroom fleet invariants",
|
|
48597
|
+
"",
|
|
48598
|
+
SANDBOX_GUIDANCE,
|
|
48599
|
+
"",
|
|
48600
|
+
TELEGRAM_GUIDANCE,
|
|
48601
|
+
"",
|
|
48602
|
+
MEMORY_GUIDANCE,
|
|
48603
|
+
""
|
|
48604
|
+
].join(`
|
|
48605
|
+
`);
|
|
48606
|
+
}
|
|
48525
48607
|
function alignAgentUid(name, agentDir, uid, opts = {}) {
|
|
48526
48608
|
const writeOut = opts.writeOut ?? ((s) => process.stdout.write(s));
|
|
48527
48609
|
const logsDir = join8(homedir4(), ".switchroom", "logs", name);
|
|
@@ -49297,70 +49379,7 @@ function buildWorkspaceContext(args) {
|
|
|
49297
49379
|
return out;
|
|
49298
49380
|
})(),
|
|
49299
49381
|
systemPromptAppendShellQuoted: (() => {
|
|
49300
|
-
const useSwitchroomPlugin = usesSwitchroomTelegramPlugin(agentConfig);
|
|
49301
49382
|
const baseAppend = agentConfig.system_prompt_append ?? "";
|
|
49302
|
-
const telegramGuidance = `## Talking to a human on Telegram
|
|
49303
|
-
|
|
49304
|
-
There is a real person on the other end. Every turn should feel like
|
|
49305
|
-
messaging a capable colleague \u2014 not a tool emitting output. Five beats:
|
|
49306
|
-
|
|
49307
|
-
1. **Acknowledge first.** Unless your whole reply is a single short
|
|
49308
|
-
sentence you can send right now, your FIRST action this turn is a
|
|
49309
|
-
brief \`reply\` in your own voice \u2014 "on it", "good question, one
|
|
49310
|
-
sec", "let me dig in" \u2014 before any tool call and before you
|
|
49311
|
-
compose the full answer. This holds even for a pure-thinking
|
|
49312
|
-
answer: if it will run to a paragraph, ack first. It is the line
|
|
49313
|
-
between a colleague and a black box.
|
|
49314
|
-
2. **Then go quiet and work.** Heads-down is correct \u2014 do NOT narrate
|
|
49315
|
-
every tool call. A typing indicator runs automatically while you
|
|
49316
|
-
work; you do not maintain it.
|
|
49317
|
-
3. **Surface meaningful progress** at genuine inflection points \u2014 a
|
|
49318
|
-
hard step finished, a blocker, a pivot, dispatching a sub-agent, a
|
|
49319
|
-
notably slow wait, a finding worth knowing now. One short \`reply\`,
|
|
49320
|
-
\`disable_notification: true\`.
|
|
49321
|
-
4. **Hand back delegations with synthesis.** When a sub-agent / worker
|
|
49322
|
-
returns, re-enter in YOUR voice \u2014 what it found, and what you are
|
|
49323
|
-
doing next. Never let its raw report stand as your reply. A
|
|
49324
|
-
*background* worker finishes after your turn ends; its result
|
|
49325
|
-
arrives as a fresh \`<channel source="subagent_handback">\` turn \u2014
|
|
49326
|
-
treat that turn as the cue to do exactly this.
|
|
49327
|
-
5. **Deliver the answer** as a final \`reply\`.
|
|
49328
|
-
|
|
49329
|
-
The one thing to avoid is *spam*: a reply on every tool call, on a
|
|
49330
|
-
timer, or repeating what you already said. Responsive and human, never
|
|
49331
|
-
a flood. Going quiet mid-work is fine \u2014 going quiet *instead* of
|
|
49332
|
-
acknowledging, or *instead* of an update at a real milestone, is the
|
|
49333
|
-
black box this exists to prevent.
|
|
49334
|
-
|
|
49335
|
-
Every turn that answers a user message ends with a user-visible
|
|
49336
|
-
\`reply\` (or \`stream_reply\` done=true) \u2014 Telegram is all the user
|
|
49337
|
-
sees; your terminal output never reaches them.`;
|
|
49338
|
-
const memoryGuidance = `## Memory \u2014 proactive, conversational
|
|
49339
|
-
|
|
49340
|
-
You have Hindsight tools: \`mcp__hindsight__sync_retain\`, \`mcp__hindsight__delete_memory\`, \`mcp__hindsight__recall\`, \`mcp__hindsight__reflect\`. Use them without being asked.
|
|
49341
|
-
|
|
49342
|
-
### Retain proactively
|
|
49343
|
-
When the user shares a fact, preference, decision, or plan worth keeping across sessions, call \`sync_retain\` in the same turn. Briefly acknowledge in your reply ("got it, April 2nd anniversary"). Don't narrate the tool call. Skip small talk and transient tool output, the auto-retain hook handles conversation-level signal.
|
|
49344
|
-
|
|
49345
|
-
### Correct proactively
|
|
49346
|
-
When the user corrects you or contradicts a prior memory, call \`delete_memory\` on the wrong entry, then \`sync_retain\` the correction. Acknowledge the correction in one line ("noted, Alice not Bob").
|
|
49347
|
-
|
|
49348
|
-
### Forget proactively
|
|
49349
|
-
When the user asks you to forget something ("forget that", "delete X", "drop what I said about Y"), call \`delete_memory\` for matching entries and confirm what was removed.
|
|
49350
|
-
|
|
49351
|
-
### Inspect proactively
|
|
49352
|
-
When the user asks "what do you know about X / me", "what do you remember about Y", or any memory audit, use \`reflect\` to synthesize an answer across the bank. Return it as honest prose, not a raw dump. If the bank has little on the topic, say so.
|
|
49353
|
-
|
|
49354
|
-
Don't wait for a slash command. Don't ask permission. Memory work is table stakes, like a colleague who takes notes and remembers.`;
|
|
49355
|
-
if (useSwitchroomPlugin) {
|
|
49356
|
-
const parts = [baseAppend, telegramGuidance, memoryGuidance, SANDBOX_GUIDANCE].filter((s) => s.length > 0);
|
|
49357
|
-
const combined = parts.join(`
|
|
49358
|
-
|
|
49359
|
-
---
|
|
49360
|
-
|
|
49361
|
-
`);
|
|
49362
|
-
return shellSingleQuote(combined);
|
|
49363
|
-
}
|
|
49364
49383
|
return baseAppend.length > 0 ? shellSingleQuote(baseAppend) : undefined;
|
|
49365
49384
|
})(),
|
|
49366
49385
|
extraCliArgs: (() => {
|
|
@@ -50261,70 +50280,7 @@ function reconcileAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchr
|
|
|
50261
50280
|
})(),
|
|
50262
50281
|
model: agentConfig.model,
|
|
50263
50282
|
systemPromptAppendShellQuoted: (() => {
|
|
50264
|
-
const useSwitchroomPlugin = usesSwitchroomTelegramPlugin(agentConfig);
|
|
50265
50283
|
const baseAppend = agentConfig.system_prompt_append ?? "";
|
|
50266
|
-
const telegramGuidance = `## Talking to a human on Telegram
|
|
50267
|
-
|
|
50268
|
-
There is a real person on the other end. Every turn should feel like
|
|
50269
|
-
messaging a capable colleague \u2014 not a tool emitting output. Five beats:
|
|
50270
|
-
|
|
50271
|
-
1. **Acknowledge first.** Unless your whole reply is a single short
|
|
50272
|
-
sentence you can send right now, your FIRST action this turn is a
|
|
50273
|
-
brief \`reply\` in your own voice \u2014 "on it", "good question, one
|
|
50274
|
-
sec", "let me dig in" \u2014 before any tool call and before you
|
|
50275
|
-
compose the full answer. This holds even for a pure-thinking
|
|
50276
|
-
answer: if it will run to a paragraph, ack first. It is the line
|
|
50277
|
-
between a colleague and a black box.
|
|
50278
|
-
2. **Then go quiet and work.** Heads-down is correct \u2014 do NOT narrate
|
|
50279
|
-
every tool call. A typing indicator runs automatically while you
|
|
50280
|
-
work; you do not maintain it.
|
|
50281
|
-
3. **Surface meaningful progress** at genuine inflection points \u2014 a
|
|
50282
|
-
hard step finished, a blocker, a pivot, dispatching a sub-agent, a
|
|
50283
|
-
notably slow wait, a finding worth knowing now. One short \`reply\`,
|
|
50284
|
-
\`disable_notification: true\`.
|
|
50285
|
-
4. **Hand back delegations with synthesis.** When a sub-agent / worker
|
|
50286
|
-
returns, re-enter in YOUR voice \u2014 what it found, and what you are
|
|
50287
|
-
doing next. Never let its raw report stand as your reply. A
|
|
50288
|
-
*background* worker finishes after your turn ends; its result
|
|
50289
|
-
arrives as a fresh \`<channel source="subagent_handback">\` turn \u2014
|
|
50290
|
-
treat that turn as the cue to do exactly this.
|
|
50291
|
-
5. **Deliver the answer** as a final \`reply\`.
|
|
50292
|
-
|
|
50293
|
-
The one thing to avoid is *spam*: a reply on every tool call, on a
|
|
50294
|
-
timer, or repeating what you already said. Responsive and human, never
|
|
50295
|
-
a flood. Going quiet mid-work is fine \u2014 going quiet *instead* of
|
|
50296
|
-
acknowledging, or *instead* of an update at a real milestone, is the
|
|
50297
|
-
black box this exists to prevent.
|
|
50298
|
-
|
|
50299
|
-
Every turn that answers a user message ends with a user-visible
|
|
50300
|
-
\`reply\` (or \`stream_reply\` done=true) \u2014 Telegram is all the user
|
|
50301
|
-
sees; your terminal output never reaches them.`;
|
|
50302
|
-
const memoryGuidance = `## Memory \u2014 proactive, conversational
|
|
50303
|
-
|
|
50304
|
-
You have Hindsight tools: \`mcp__hindsight__sync_retain\`, \`mcp__hindsight__delete_memory\`, \`mcp__hindsight__recall\`, \`mcp__hindsight__reflect\`. Use them without being asked.
|
|
50305
|
-
|
|
50306
|
-
### Retain proactively
|
|
50307
|
-
When the user shares a fact, preference, decision, or plan worth keeping across sessions, call \`sync_retain\` in the same turn. Briefly acknowledge in your reply ("got it, April 2nd anniversary"). Don't narrate the tool call. Skip small talk and transient tool output, the auto-retain hook handles conversation-level signal.
|
|
50308
|
-
|
|
50309
|
-
### Correct proactively
|
|
50310
|
-
When the user corrects you or contradicts a prior memory, call \`delete_memory\` on the wrong entry, then \`sync_retain\` the correction. Acknowledge the correction in one line ("noted, Alice not Bob").
|
|
50311
|
-
|
|
50312
|
-
### Forget proactively
|
|
50313
|
-
When the user asks you to forget something ("forget that", "delete X", "drop what I said about Y"), call \`delete_memory\` for matching entries and confirm what was removed.
|
|
50314
|
-
|
|
50315
|
-
### Inspect proactively
|
|
50316
|
-
When the user asks "what do you know about X / me", "what do you remember about Y", or any memory audit, use \`reflect\` to synthesize an answer across the bank. Return it as honest prose, not a raw dump. If the bank has little on the topic, say so.
|
|
50317
|
-
|
|
50318
|
-
Don't wait for a slash command. Don't ask permission. Memory work is table stakes, like a colleague who takes notes and remembers.`;
|
|
50319
|
-
if (useSwitchroomPlugin) {
|
|
50320
|
-
const parts = [baseAppend, telegramGuidance, memoryGuidance, SANDBOX_GUIDANCE].filter((s) => s.length > 0);
|
|
50321
|
-
const combined = parts.join(`
|
|
50322
|
-
|
|
50323
|
-
---
|
|
50324
|
-
|
|
50325
|
-
`);
|
|
50326
|
-
return shellSingleQuote(combined);
|
|
50327
|
-
}
|
|
50328
50284
|
return baseAppend.length > 0 ? shellSingleQuote(baseAppend) : undefined;
|
|
50329
50285
|
})(),
|
|
50330
50286
|
extraCliArgs: (() => {
|
|
@@ -74057,7 +74013,7 @@ function registerDriveMcpLauncherCommand(program3) {
|
|
|
74057
74013
|
|
|
74058
74014
|
// src/cli/apply.ts
|
|
74059
74015
|
init_source();
|
|
74060
|
-
import { accessSync as accessSync3, chownSync as chownSync4, constants as fsConstants6, copyFileSync as copyFileSync11, existsSync as existsSync68, mkdirSync as mkdirSync36, readdirSync as readdirSync25, renameSync as renameSync13, writeFileSync as writeFileSync32 } from "node:fs";
|
|
74016
|
+
import { accessSync as accessSync3, chownSync as chownSync4, constants as fsConstants6, copyFileSync as copyFileSync11, existsSync as existsSync68, mkdirSync as mkdirSync36, readFileSync as readFileSync55, readdirSync as readdirSync25, renameSync as renameSync13, writeFileSync as writeFileSync32 } from "node:fs";
|
|
74061
74017
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
74062
74018
|
import { spawnSync as childSpawnSync } from "node:child_process";
|
|
74063
74019
|
import readline from "node:readline";
|
|
@@ -74804,6 +74760,29 @@ async function ensureHostMountSources(config) {
|
|
|
74804
74760
|
chownSync4(tokenPath, uid, uid);
|
|
74805
74761
|
} catch {}
|
|
74806
74762
|
}
|
|
74763
|
+
const fleetDir = join62(home2, ".switchroom", "fleet");
|
|
74764
|
+
await mkdir(fleetDir, { recursive: true });
|
|
74765
|
+
const invariantsPath = join62(fleetDir, "switchroom-invariants.md");
|
|
74766
|
+
const invariantsCanonical = renderFleetInvariants();
|
|
74767
|
+
const invariantsCurrent = existsSync68(invariantsPath) ? readFileSync55(invariantsPath, "utf-8") : null;
|
|
74768
|
+
if (invariantsCurrent !== invariantsCanonical) {
|
|
74769
|
+
writeFileSync32(invariantsPath, invariantsCanonical, { mode: 420 });
|
|
74770
|
+
}
|
|
74771
|
+
const fleetClaudePath = join62(fleetDir, "CLAUDE.md");
|
|
74772
|
+
if (!existsSync68(fleetClaudePath)) {
|
|
74773
|
+
writeFileSync32(fleetClaudePath, [
|
|
74774
|
+
"# Switchroom fleet defaults",
|
|
74775
|
+
"",
|
|
74776
|
+
"Operator-owned fleet brain. Every agent reads this via",
|
|
74777
|
+
"`--add-dir ~/.switchroom/fleet` (Claude Code native CLAUDE.md",
|
|
74778
|
+
"discovery). Additions stack across the fleet; `switchroom apply`",
|
|
74779
|
+
"never clobbers your edits here.",
|
|
74780
|
+
"",
|
|
74781
|
+
"<!-- L2 fleet defaults content lands in switchroom #1855 -->",
|
|
74782
|
+
""
|
|
74783
|
+
].join(`
|
|
74784
|
+
`), { mode: 420 });
|
|
74785
|
+
}
|
|
74807
74786
|
}
|
|
74808
74787
|
function detectComposeV2() {
|
|
74809
74788
|
try {
|
|
@@ -75322,7 +75301,7 @@ function runRedactStdin() {
|
|
|
75322
75301
|
}
|
|
75323
75302
|
|
|
75324
75303
|
// src/cli/status-ask.ts
|
|
75325
|
-
import { readFileSync as
|
|
75304
|
+
import { readFileSync as readFileSync56, existsSync as existsSync69, readdirSync as readdirSync26 } from "node:fs";
|
|
75326
75305
|
import { join as join63 } from "node:path";
|
|
75327
75306
|
import { homedir as homedir36 } from "node:os";
|
|
75328
75307
|
|
|
@@ -75598,7 +75577,7 @@ function runReport(opts) {
|
|
|
75598
75577
|
for (const src of sources) {
|
|
75599
75578
|
let content;
|
|
75600
75579
|
try {
|
|
75601
|
-
content =
|
|
75580
|
+
content = readFileSync56(src.path, "utf-8");
|
|
75602
75581
|
} catch (err) {
|
|
75603
75582
|
process.stderr.write(`status-ask report: cannot read ${src.path}: ${err instanceof Error ? err.message : String(err)}
|
|
75604
75583
|
`);
|
|
@@ -75699,7 +75678,7 @@ import {
|
|
|
75699
75678
|
existsSync as existsSync70,
|
|
75700
75679
|
mkdirSync as mkdirSync37,
|
|
75701
75680
|
appendFileSync as appendFileSync4,
|
|
75702
|
-
readFileSync as
|
|
75681
|
+
readFileSync as readFileSync57
|
|
75703
75682
|
} from "node:fs";
|
|
75704
75683
|
var AUDIT_ROOT = join64(homedir37(), ".switchroom", "audit");
|
|
75705
75684
|
function auditPathFor(agent) {
|
|
@@ -75794,7 +75773,7 @@ function readAuditTail(agent, limit, opts = {}) {
|
|
|
75794
75773
|
return [];
|
|
75795
75774
|
let raw;
|
|
75796
75775
|
try {
|
|
75797
|
-
raw =
|
|
75776
|
+
raw = readFileSync57(path8, "utf-8");
|
|
75798
75777
|
} catch {
|
|
75799
75778
|
return [];
|
|
75800
75779
|
}
|
|
@@ -75955,7 +75934,7 @@ import {
|
|
|
75955
75934
|
mkdirSync as mkdirSync38,
|
|
75956
75935
|
openSync as openSync13,
|
|
75957
75936
|
readdirSync as readdirSync27,
|
|
75958
|
-
readFileSync as
|
|
75937
|
+
readFileSync as readFileSync58,
|
|
75959
75938
|
renameSync as renameSync14,
|
|
75960
75939
|
statSync as statSync28,
|
|
75961
75940
|
unlinkSync as unlinkSync14,
|
|
@@ -76079,7 +76058,7 @@ function listSkillsOverlayEntries(agent, opts = {}) {
|
|
|
76079
76058
|
continue;
|
|
76080
76059
|
const full = join65(paths.skillsDir, name);
|
|
76081
76060
|
try {
|
|
76082
|
-
const raw =
|
|
76061
|
+
const raw = readFileSync58(full, "utf-8");
|
|
76083
76062
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
76084
76063
|
out.push({ slug, path: full, raw });
|
|
76085
76064
|
} catch {}
|
|
@@ -76106,7 +76085,7 @@ function listOverlayEntries(agent, opts = {}) {
|
|
|
76106
76085
|
continue;
|
|
76107
76086
|
const full = join65(paths.scheduleDir, name);
|
|
76108
76087
|
try {
|
|
76109
|
-
const raw =
|
|
76088
|
+
const raw = readFileSync58(full, "utf-8");
|
|
76110
76089
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
76111
76090
|
out.push({ slug, path: full, raw });
|
|
76112
76091
|
} catch {}
|
|
@@ -76254,7 +76233,7 @@ import {
|
|
|
76254
76233
|
mkdirSync as mkdirSync39,
|
|
76255
76234
|
openSync as openSync14,
|
|
76256
76235
|
readdirSync as readdirSync28,
|
|
76257
|
-
readFileSync as
|
|
76236
|
+
readFileSync as readFileSync59,
|
|
76258
76237
|
renameSync as renameSync15,
|
|
76259
76238
|
unlinkSync as unlinkSync15,
|
|
76260
76239
|
writeFileSync as writeFileSync33,
|
|
@@ -76318,7 +76297,7 @@ function listPendingScheduleEntries(agent, opts = {}) {
|
|
|
76318
76297
|
if (!existsSync72(yamlPath))
|
|
76319
76298
|
continue;
|
|
76320
76299
|
try {
|
|
76321
|
-
const meta = JSON.parse(
|
|
76300
|
+
const meta = JSON.parse(readFileSync59(metaPath, "utf-8"));
|
|
76322
76301
|
if (meta?.v !== 1 || typeof meta.stage_id !== "string")
|
|
76323
76302
|
continue;
|
|
76324
76303
|
out.push({ stageId: meta.stage_id, agent: meta.agent, yamlPath, metaPath, meta });
|
|
@@ -76357,7 +76336,7 @@ function denyPendingScheduleEntry(opts) {
|
|
|
76357
76336
|
|
|
76358
76337
|
// src/cli/agent-config-write.ts
|
|
76359
76338
|
init_protocol3();
|
|
76360
|
-
import { existsSync as existsSync73, readFileSync as
|
|
76339
|
+
import { existsSync as existsSync73, readFileSync as readFileSync60 } from "node:fs";
|
|
76361
76340
|
var MAX_ENTRIES_PER_AGENT = 20;
|
|
76362
76341
|
var MIN_CRON_INTERVAL_MIN = 5;
|
|
76363
76342
|
function extractCronSmallestGapMin(expr) {
|
|
@@ -76644,7 +76623,7 @@ function scheduleRemove(opts) {
|
|
|
76644
76623
|
let priorContent = null;
|
|
76645
76624
|
try {
|
|
76646
76625
|
if (existsSync73(match.path))
|
|
76647
|
-
priorContent =
|
|
76626
|
+
priorContent = readFileSync60(match.path, "utf-8");
|
|
76648
76627
|
} catch {}
|
|
76649
76628
|
deleteOverlayEntry(agent, match.slug, { root: opts.root });
|
|
76650
76629
|
const reconcileFn = opts.reconcile === undefined ? opts.root ? null : reconcileAgentCronOnly : opts.reconcile;
|
|
@@ -77084,7 +77063,7 @@ import {
|
|
|
77084
77063
|
mkdirSync as mkdirSync40,
|
|
77085
77064
|
mkdtempSync as mkdtempSync5,
|
|
77086
77065
|
openSync as openSync15,
|
|
77087
|
-
readFileSync as
|
|
77066
|
+
readFileSync as readFileSync61,
|
|
77088
77067
|
readdirSync as readdirSync29,
|
|
77089
77068
|
realpathSync as realpathSync7,
|
|
77090
77069
|
renameSync as renameSync16,
|
|
@@ -77336,7 +77315,7 @@ function loadFromDir(dir) {
|
|
|
77336
77315
|
continue;
|
|
77337
77316
|
}
|
|
77338
77317
|
if (ent.isFile()) {
|
|
77339
|
-
const buf =
|
|
77318
|
+
const buf = readFileSync61(full);
|
|
77340
77319
|
files[rel.replace(/\\/g, "/")] = buf.toString("utf-8");
|
|
77341
77320
|
}
|
|
77342
77321
|
}
|
|
@@ -77385,7 +77364,7 @@ function loadFromTarball(tarPath) {
|
|
|
77385
77364
|
}
|
|
77386
77365
|
}
|
|
77387
77366
|
function loadSingleFile(filePath) {
|
|
77388
|
-
const content =
|
|
77367
|
+
const content = readFileSync61(filePath, "utf-8");
|
|
77389
77368
|
return { "SKILL.md": content };
|
|
77390
77369
|
}
|
|
77391
77370
|
function loadFromStdin() {
|
|
@@ -77476,7 +77455,7 @@ function diffSummary(currentDir, files) {
|
|
|
77476
77455
|
if (ent.isDirectory()) {
|
|
77477
77456
|
walk2(full);
|
|
77478
77457
|
} else if (ent.isFile()) {
|
|
77479
|
-
currentFiles[rel.replace(/\\/g, "/")] =
|
|
77458
|
+
currentFiles[rel.replace(/\\/g, "/")] = readFileSync61(full, "utf-8");
|
|
77480
77459
|
}
|
|
77481
77460
|
}
|
|
77482
77461
|
};
|
|
@@ -77636,7 +77615,7 @@ import {
|
|
|
77636
77615
|
mkdirSync as mkdirSync41,
|
|
77637
77616
|
mkdtempSync as mkdtempSync6,
|
|
77638
77617
|
openSync as openSync16,
|
|
77639
|
-
readFileSync as
|
|
77618
|
+
readFileSync as readFileSync62,
|
|
77640
77619
|
readdirSync as readdirSync30,
|
|
77641
77620
|
renameSync as renameSync17,
|
|
77642
77621
|
rmSync as rmSync17,
|
|
@@ -77718,7 +77697,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
77718
77697
|
if (ent.isDirectory())
|
|
77719
77698
|
walk2(s, d);
|
|
77720
77699
|
else if (ent.isFile()) {
|
|
77721
|
-
writeFileSync35(d,
|
|
77700
|
+
writeFileSync35(d, readFileSync62(s));
|
|
77722
77701
|
}
|
|
77723
77702
|
}
|
|
77724
77703
|
};
|
|
@@ -77797,7 +77776,7 @@ function loadFromDir2(dir) {
|
|
|
77797
77776
|
}
|
|
77798
77777
|
if (ent.isFile()) {
|
|
77799
77778
|
const rel = relative3(abs, full).replace(/\\/g, "/");
|
|
77800
|
-
files[rel] =
|
|
77779
|
+
files[rel] = readFileSync62(full, "utf-8");
|
|
77801
77780
|
}
|
|
77802
77781
|
}
|
|
77803
77782
|
};
|
|
@@ -77983,7 +77962,7 @@ function loadFiles(opts) {
|
|
|
77983
77962
|
return loadFromDir2(p);
|
|
77984
77963
|
}
|
|
77985
77964
|
if (p.endsWith(".md")) {
|
|
77986
|
-
return { "SKILL.md":
|
|
77965
|
+
return { "SKILL.md": readFileSync62(p, "utf-8") };
|
|
77987
77966
|
}
|
|
77988
77967
|
fail3(`--from must be a directory or a .md file. Got: ${opts.from}`);
|
|
77989
77968
|
}
|
|
@@ -78072,7 +78051,7 @@ function readSourceFiles(dir) {
|
|
|
78072
78051
|
fail3(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
|
|
78073
78052
|
}
|
|
78074
78053
|
} catch {}
|
|
78075
|
-
files[rel] =
|
|
78054
|
+
files[rel] = readFileSync62(full, "utf-8");
|
|
78076
78055
|
}
|
|
78077
78056
|
}
|
|
78078
78057
|
};
|
|
@@ -78241,7 +78220,7 @@ function registerSkillPersonalCommands(program3) {
|
|
|
78241
78220
|
// src/cli/skill-search.ts
|
|
78242
78221
|
init_helpers();
|
|
78243
78222
|
var import_yaml18 = __toESM(require_dist(), 1);
|
|
78244
|
-
import { existsSync as existsSync77, readdirSync as readdirSync31, readFileSync as
|
|
78223
|
+
import { existsSync as existsSync77, readdirSync as readdirSync31, readFileSync as readFileSync63, statSync as statSync31 } from "node:fs";
|
|
78245
78224
|
import { homedir as homedir40 } from "node:os";
|
|
78246
78225
|
import { join as join70, resolve as resolve46 } from "node:path";
|
|
78247
78226
|
var PERSONAL_PREFIX2 = "personal-";
|
|
@@ -78262,7 +78241,7 @@ function readSkillFrontmatter(skillDir) {
|
|
|
78262
78241
|
return null;
|
|
78263
78242
|
let content;
|
|
78264
78243
|
try {
|
|
78265
|
-
content =
|
|
78244
|
+
content = readFileSync63(mdPath, "utf-8");
|
|
78266
78245
|
} catch {
|
|
78267
78246
|
return null;
|
|
78268
78247
|
}
|
|
@@ -78534,7 +78513,7 @@ function registerHostdMcpCommand(program3) {
|
|
|
78534
78513
|
// src/cli/hostd.ts
|
|
78535
78514
|
init_source();
|
|
78536
78515
|
init_helpers();
|
|
78537
|
-
import { existsSync as existsSync79, mkdirSync as mkdirSync42, readdirSync as readdirSync32, readFileSync as
|
|
78516
|
+
import { existsSync as existsSync79, mkdirSync as mkdirSync42, readdirSync as readdirSync32, readFileSync as readFileSync65, writeFileSync as writeFileSync36, statSync as statSync32, copyFileSync as copyFileSync12 } from "node:fs";
|
|
78538
78517
|
import { homedir as homedir41 } from "node:os";
|
|
78539
78518
|
import { join as join71 } from "node:path";
|
|
78540
78519
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
@@ -78787,7 +78766,7 @@ function registerHostdCommand(program3) {
|
|
|
78787
78766
|
The log is created when hostd handles its first privileged-verb request.`));
|
|
78788
78767
|
return;
|
|
78789
78768
|
}
|
|
78790
|
-
const raw =
|
|
78769
|
+
const raw = readFileSync65(logPath, "utf-8");
|
|
78791
78770
|
const limit = Math.max(1, parseInt(opts.tail ?? "50", 10) || 50);
|
|
78792
78771
|
const filters = {
|
|
78793
78772
|
agent: opts.agent,
|
package/package.json
CHANGED
|
@@ -568,16 +568,29 @@ for f in \
|
|
|
568
568
|
fi
|
|
569
569
|
done
|
|
570
570
|
|
|
571
|
+
# Fleet directory — epic #1850 / issue #1852. Extends Claude Code's
|
|
572
|
+
# native CLAUDE.md auto-discovery to `~/.switchroom/fleet/` so every
|
|
573
|
+
# agent reads the release-pinned `switchroom-invariants.md` (lane 1)
|
|
574
|
+
# and operator-owned `CLAUDE.md` (lane 2) from there. Guarded on the
|
|
575
|
+
# directory existing so a fresh-install or a half-applied state
|
|
576
|
+
# doesn't fail boot — `switchroom apply`'s ensureHostMountSources
|
|
577
|
+
# creates the dir + seeds the files.
|
|
578
|
+
SR_FLEET_DIR="$HOME/.switchroom/fleet"
|
|
579
|
+
SR_FLEET_ARG=""
|
|
580
|
+
if [ -d "$SR_FLEET_DIR" ]; then
|
|
581
|
+
SR_FLEET_ARG="--add-dir $SR_FLEET_DIR"
|
|
582
|
+
fi
|
|
583
|
+
|
|
571
584
|
{{#if useSwitchroomPlugin}}
|
|
572
585
|
if [ -n "$APPEND_PROMPT" ]; then
|
|
573
|
-
exec claude $CONTINUE_FLAG --dangerously-load-development-channels server:switchroom-telegram --plugin-dir "{{securityPluginDir}}"{{#if hindsightEnabled}} --plugin-dir "{{agentDir}}/.claude/plugins/hindsight-memory"{{/if}}{{#if modelQ}} --model {{{modelQ}}}{{/if}}{{#if thinkingEffort}} --effort {{thinkingEffort}}{{/if}}{{#if permissionMode}} --permission-mode {{permissionMode}}{{/if}}{{#if fallbackModelQ}} --fallback-model {{{fallbackModelQ}}}{{/if}} --append-system-prompt "$APPEND_PROMPT"{{#if dangerousMode}} --dangerously-skip-permissions{{/if}}{{#if extraCliArgs}}{{{extraCliArgs}}}{{/if}}
|
|
586
|
+
exec claude $CONTINUE_FLAG --dangerously-load-development-channels server:switchroom-telegram --plugin-dir "{{securityPluginDir}}"{{#if hindsightEnabled}} --plugin-dir "{{agentDir}}/.claude/plugins/hindsight-memory"{{/if}} $SR_FLEET_ARG{{#if modelQ}} --model {{{modelQ}}}{{/if}}{{#if thinkingEffort}} --effort {{thinkingEffort}}{{/if}}{{#if permissionMode}} --permission-mode {{permissionMode}}{{/if}}{{#if fallbackModelQ}} --fallback-model {{{fallbackModelQ}}}{{/if}} --append-system-prompt "$APPEND_PROMPT"{{#if dangerousMode}} --dangerously-skip-permissions{{/if}}{{#if extraCliArgs}}{{{extraCliArgs}}}{{/if}}
|
|
574
587
|
else
|
|
575
|
-
exec claude $CONTINUE_FLAG --dangerously-load-development-channels server:switchroom-telegram --plugin-dir "{{securityPluginDir}}"{{#if hindsightEnabled}} --plugin-dir "{{agentDir}}/.claude/plugins/hindsight-memory"{{/if}}{{#if modelQ}} --model {{{modelQ}}}{{/if}}{{#if thinkingEffort}} --effort {{thinkingEffort}}{{/if}}{{#if permissionMode}} --permission-mode {{permissionMode}}{{/if}}{{#if fallbackModelQ}} --fallback-model {{{fallbackModelQ}}}{{/if}}{{#if dangerousMode}} --dangerously-skip-permissions{{/if}}{{#if extraCliArgs}}{{{extraCliArgs}}}{{/if}}
|
|
588
|
+
exec claude $CONTINUE_FLAG --dangerously-load-development-channels server:switchroom-telegram --plugin-dir "{{securityPluginDir}}"{{#if hindsightEnabled}} --plugin-dir "{{agentDir}}/.claude/plugins/hindsight-memory"{{/if}} $SR_FLEET_ARG{{#if modelQ}} --model {{{modelQ}}}{{/if}}{{#if thinkingEffort}} --effort {{thinkingEffort}}{{/if}}{{#if permissionMode}} --permission-mode {{permissionMode}}{{/if}}{{#if fallbackModelQ}} --fallback-model {{{fallbackModelQ}}}{{/if}}{{#if dangerousMode}} --dangerously-skip-permissions{{/if}}{{#if extraCliArgs}}{{{extraCliArgs}}}{{/if}}
|
|
576
589
|
fi
|
|
577
590
|
{{else}}
|
|
578
591
|
if [ -n "$APPEND_PROMPT" ]; then
|
|
579
|
-
exec claude $CONTINUE_FLAG --channels plugin:telegram@claude-plugins-official --plugin-dir "{{securityPluginDir}}"{{#if hindsightEnabled}} --plugin-dir "{{agentDir}}/.claude/plugins/hindsight-memory"{{/if}}{{#if modelQ}} --model {{{modelQ}}}{{/if}}{{#if thinkingEffort}} --effort {{thinkingEffort}}{{/if}}{{#if permissionMode}} --permission-mode {{permissionMode}}{{/if}}{{#if fallbackModelQ}} --fallback-model {{{fallbackModelQ}}}{{/if}} --append-system-prompt "$APPEND_PROMPT"{{#if dangerousMode}} --dangerously-skip-permissions{{/if}}{{#if extraCliArgs}}{{{extraCliArgs}}}{{/if}}
|
|
592
|
+
exec claude $CONTINUE_FLAG --channels plugin:telegram@claude-plugins-official --plugin-dir "{{securityPluginDir}}"{{#if hindsightEnabled}} --plugin-dir "{{agentDir}}/.claude/plugins/hindsight-memory"{{/if}} $SR_FLEET_ARG{{#if modelQ}} --model {{{modelQ}}}{{/if}}{{#if thinkingEffort}} --effort {{thinkingEffort}}{{/if}}{{#if permissionMode}} --permission-mode {{permissionMode}}{{/if}}{{#if fallbackModelQ}} --fallback-model {{{fallbackModelQ}}}{{/if}} --append-system-prompt "$APPEND_PROMPT"{{#if dangerousMode}} --dangerously-skip-permissions{{/if}}{{#if extraCliArgs}}{{{extraCliArgs}}}{{/if}}
|
|
580
593
|
else
|
|
581
|
-
exec claude $CONTINUE_FLAG --channels plugin:telegram@claude-plugins-official --plugin-dir "{{securityPluginDir}}"{{#if hindsightEnabled}} --plugin-dir "{{agentDir}}/.claude/plugins/hindsight-memory"{{/if}}{{#if modelQ}} --model {{{modelQ}}}{{/if}}{{#if thinkingEffort}} --effort {{thinkingEffort}}{{/if}}{{#if permissionMode}} --permission-mode {{permissionMode}}{{/if}}{{#if fallbackModelQ}} --fallback-model {{{fallbackModelQ}}}{{/if}}{{#if dangerousMode}} --dangerously-skip-permissions{{/if}}{{#if extraCliArgs}}{{{extraCliArgs}}}{{/if}}
|
|
594
|
+
exec claude $CONTINUE_FLAG --channels plugin:telegram@claude-plugins-official --plugin-dir "{{securityPluginDir}}"{{#if hindsightEnabled}} --plugin-dir "{{agentDir}}/.claude/plugins/hindsight-memory"{{/if}} $SR_FLEET_ARG{{#if modelQ}} --model {{{modelQ}}}{{/if}}{{#if thinkingEffort}} --effort {{thinkingEffort}}{{/if}}{{#if permissionMode}} --permission-mode {{permissionMode}}{{/if}}{{#if fallbackModelQ}} --fallback-model {{{fallbackModelQ}}}{{/if}}{{#if dangerousMode}} --dangerously-skip-permissions{{/if}}{{#if extraCliArgs}}{{{extraCliArgs}}}{{/if}}
|
|
582
595
|
fi
|
|
583
596
|
{{/if}}
|
|
@@ -22,6 +22,9 @@ tools is to let you do the edit yourself.
|
|
|
22
22
|
| "what other agents are running here?" / "is there an agent that does X?" / "who handles Y?" | `peers_list` |
|
|
23
23
|
| "install the foo skill" / "give yourself the foo skill" | `skill_install` with `source: "bundled:foo"` |
|
|
24
24
|
| "drop the foo skill" / "remove the foo skill" | `skill_remove` with `name: "foo"` |
|
|
25
|
+
| "is there a skill for X?" | `skill_search` with `query: "X"` |
|
|
26
|
+
| **YOU find a bug in a skill you're using** | `skill_clone_to_personal` then `skill_edit_personal` — fork-and-fix yourself |
|
|
27
|
+
| "write me a custom skill that does X" | `skill_init_personal` |
|
|
25
28
|
|
|
26
29
|
### Tools
|
|
27
30
|
|
|
@@ -66,6 +69,8 @@ tools is to let you do the edit yourself.
|
|
|
66
69
|
Does NOT remove skills the operator wrote directly into
|
|
67
70
|
`switchroom.yaml` — those are removed by the operator only.
|
|
68
71
|
|
|
72
|
+
- **`skill_search` / `skill_init_personal` / `skill_edit_personal` / `skill_remove_personal` / `skill_list_personal` / `skill_clone_to_personal`** — author/fork your own. `files` is `{path: content}` JSON; allowlist `SKILL.md`, `scripts/*.{sh,py}`, `references/*.md`.
|
|
73
|
+
|
|
69
74
|
### Safety rails — what gets rejected
|
|
70
75
|
|
|
71
76
|
The broker hard-rejects writes that would violate these limits. Anticipate
|
|
@@ -79,28 +84,13 @@ the rails will block:
|
|
|
79
84
|
- **20 entries per agent maximum.** `E_QUOTA_EXCEEDED`. If you're near the
|
|
80
85
|
cap, `cron_list` first; if full, prompt the user to remove an old one
|
|
81
86
|
before adding the new one.
|
|
82
|
-
- **No `secrets:` on agent-authored entries
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
- **
|
|
88
|
-
|
|
89
|
-
gateway sets when spawning your CLI — calls passing
|
|
90
|
-
`agent: "<other-agent>"` that doesn't match the pin are rejected. If
|
|
91
|
-
the user wants to set something up on a different agent, tell them
|
|
92
|
-
which agent to ask.
|
|
93
|
-
|
|
94
|
-
### Skills — self-service is live (#1163 Phase 2)
|
|
95
|
-
|
|
96
|
-
You can `skill_list` to inventory, `skill_install` to add, and
|
|
97
|
-
`skill_remove` to drop. v1 source format is `bundled:<name>` only — the
|
|
98
|
-
skill must exist in the host's bundled-skills pool (run `skill_list` on
|
|
99
|
-
the host to see what's available, or pass an obvious slug like
|
|
100
|
-
`webapp-testing`, `pdf`, `mcp-builder`). git+https sources are designed
|
|
101
|
-
but not yet shipped; if the user asks for an arbitrary URL, tell them
|
|
102
|
-
the operator needs to drop it under `~/.switchroom/skills/<name>/` and
|
|
103
|
-
run `switchroom apply`.
|
|
87
|
+
- **No `secrets:` on agent-authored entries** (`E_OVERLAY_SECRETS_REQUIRES_APPROVAL`). Cron fires the prompt; you go through `vault_request_access` at runtime.
|
|
88
|
+
- **Cross-agent writes rejected** — you manage only your own schedule. The broker pins identity via `$SWITCHROOM_AGENT_NAME`; if the user wants something on a different agent, tell them which agent to ask.
|
|
89
|
+
|
|
90
|
+
### Skills — self-service
|
|
91
|
+
|
|
92
|
+
- **Install** — `skill_install` (`bundled:<name>`) / `skill_remove`. git+https deferred.
|
|
93
|
+
- **Fix a skill yourself** — `skill_clone_to_personal` → `skill_edit_personal` → use `personal-<name>`. Fork is durable + auto-mirrors to `~/.switchroom-config/` when present.
|
|
104
94
|
|
|
105
95
|
### Honest confirmation pattern
|
|
106
96
|
|
|
@@ -34,7 +34,7 @@ You are a senior software engineering agent. You write, review, debug, and archi
|
|
|
34
34
|
- Clear commit messages in imperative mood explaining the why.
|
|
35
35
|
- Atomic commits. PR descriptions explain what, why, and how to test.
|
|
36
36
|
|
|
37
|
-
{{
|
|
37
|
+
{{!-- telegram-style now in ~/.switchroom/fleet/switchroom-invariants.md --}}
|
|
38
38
|
|
|
39
39
|
## Memory — Hindsight
|
|
40
40
|
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Agent:
|
|
2
|
+
|
|
3
|
+
## What you are
|
|
4
|
+
|
|
5
|
+
You are a **switchroom agent** — an instance of **Claude Code** (Anthropic's official `claude` CLI, unmodified) running in a Linux container, managed by switchroom. Your `$SWITCHROOM_AGENT_NAME` is ``. Be honest about this when asked ("what are you" / "what's running here"): switchroom agent `` running Claude Code under the official `claude` CLI. Not a custom model, not a wrapper, not "an AI assistant" in the abstract.
|
|
6
|
+
|
|
7
|
+
You are one of several agents here. To see the others, call `peers_list` on the `agent-config` MCP server — returns `[{name, purpose, admin}]` live from `switchroom.yaml`. **Never memorize peers into Hindsight or hard-code them into replies** — drift kills trust. On "who else is here" / "is there an agent that does X" / "who handles Y" / "who can do <admin op>", call `peers_list` first and answer from its result; if no peer matches, say so.
|
|
8
|
+
|
|
9
|
+
## Who you are
|
|
10
|
+
|
|
11
|
+
See `SOUL.md` (in this directory) for your identity, vibe, communication style, and expertise. That file is your persona source of truth.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## Core Behavior
|
|
15
|
+
- Respond helpfully, concisely, and conversationally.
|
|
16
|
+
- Use your available tools when they add clear value — don't force tool use when a plain answer suffices.
|
|
17
|
+
- Save important facts, preferences, and decisions to memory so you can recall them later.
|
|
18
|
+
- When asked to do something ambiguous, ask one clarifying question rather than guessing.
|
|
19
|
+
- If a task has multiple steps, outline your plan before executing.
|
|
20
|
+
|
|
21
|
+
## Safety
|
|
22
|
+
- Don't exfiltrate private data. Ever.
|
|
23
|
+
- Don't run destructive commands without asking.
|
|
24
|
+
- Prefer `trash` over `rm` when available (recoverable beats gone forever).
|
|
25
|
+
- Safe to do freely: read files, explore, organize, search the web, check calendars, work within this workspace.
|
|
26
|
+
- Ask first: sending emails, tweets, public posts, anything that leaves the machine, anything you're uncertain about.
|
|
27
|
+
|
|
28
|
+
## Execution Bias
|
|
29
|
+
|
|
30
|
+
How you should decide what to do next. These are procedural rules, not vibe.
|
|
31
|
+
|
|
32
|
+
- **Act in-turn.** If the request is actionable, do it this turn. Don't finish with a plan or promise when tools can move it forward.
|
|
33
|
+
- **Verify mutable facts before claiming them.** Files, git state, clocks, versions, services, processes, package state, the contents of an `Edit` target: read live. Memory and prior context are not verification sources. "I think the function is at line 200" is not an answer; `Grep`/`Read` is.
|
|
34
|
+
- **Final answer needs evidence.** Test/build/lint output, screenshot, inspection, tool output, or a named blocker. "It should work" is not a finalization.
|
|
35
|
+
- **Weak or empty tool result is not a conclusion.** Vary the query, path, command, or source before deciding the thing isn't there.
|
|
36
|
+
- **Non-final turn:** use tools to advance, or ask the one clarifying question that unblocks safe progress. One question, not five.
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
## Memory — Hindsight is your single backend
|
|
40
|
+
|
|
41
|
+
**Claude Code's built-in file-based auto-memory is disabled for this agent.** Don't try to write `.md` files under `.claude/projects/.../memory/` or maintain a `MEMORY.md` index — that whole system is off. There's exactly one memory backend: **Hindsight**.
|
|
42
|
+
|
|
43
|
+
Hindsight is a memory bank with semantic search, knowledge graph, entity resolution, mental models, and directives. You talk to it through MCP tools (all pre-approved):
|
|
44
|
+
|
|
45
|
+
### Day-to-day tools
|
|
46
|
+
- `mcp__hindsight__recall` — semantic-search the bank for relevant past memories. Auto-fires on every inbound user message via the plugin's UserPromptSubmit hook (you'll see "Relevant memories from past conversations" in your context). Call manually when you need a more specific query than the auto-fired one.
|
|
47
|
+
- `mcp__hindsight__retain` — store a new memory. The plugin automatically retains the conversation transcript every ~10 turns via the Stop hook, so you usually don't need this. Call manually for significant decisions, corrections, or facts you want immediately searchable.
|
|
48
|
+
- `mcp__hindsight__reflect` — Hindsight's LLM-powered "answer this query using the bank's content + directives". Use when the user asks a question that requires synthesis across multiple past memories.
|
|
49
|
+
|
|
50
|
+
### Mental Models (replaces hand-curated user profile)
|
|
51
|
+
A mental model is a pre-computed semantic summary backed by reflection over the bank. It's the proper way to maintain things like "what do we know about this user" — semantically populated, automatically refreshed.
|
|
52
|
+
|
|
53
|
+
- `mcp__hindsight__create_mental_model(name, source_query)` — create one. When the user shares a fact about themselves (preferences, background, goals), don't write a file — instead, retain the fact and (if no User Profile mental model exists yet) create one with `source_query: "what do we know about this user?"`. Hindsight will populate it from the retained memories.
|
|
54
|
+
|
|
55
|
+
### Directives (replaces feedback rules)
|
|
56
|
+
Hard rules the agent must follow during reflect — guardrails that are always applied.
|
|
57
|
+
|
|
58
|
+
- `mcp__hindsight__create_directive(text)` — e.g., `create_directive("Always prefer TypeScript over JavaScript for this user's projects")`. When the user gives you a correction or "always do X" rule, create a directive instead of writing a feedback `.md` file.
|
|
59
|
+
|
|
60
|
+
(Inspection tools like `list_memories`, `list_mental_models`, `update_mental_model`, `refresh_mental_model`, `list_directives`, `delete_directive` are available under the `mcp__hindsight__*` namespace if you ever need them, but you rarely should — Hindsight's own auto-recall surfaces what matters and the operator handles bank curation out-of-band.)
|
|
61
|
+
|
|
62
|
+
### What to retain — and what NOT to retain
|
|
63
|
+
|
|
64
|
+
Retain proactively when:
|
|
65
|
+
- The user shares a preference or fact about themselves
|
|
66
|
+
- The user gives you a correction or rule (these go to directives, not retain)
|
|
67
|
+
- A significant decision was made and the rationale matters for next time
|
|
68
|
+
- You did real work and the result + the path you took would be useful next session
|
|
69
|
+
|
|
70
|
+
Don't retain:
|
|
71
|
+
- Routine pleasantries, "thanks", "got it"
|
|
72
|
+
- Conversation chatter that doesn't carry forward
|
|
73
|
+
- Sensitive content the user explicitly asked you to not remember
|
|
74
|
+
- Things already in a mental model — they'll be re-derived from underlying memories
|
|
75
|
+
|
|
76
|
+
The plugin's auto-retain (Stop hook) handles transcript-level storage on a 10-turn cadence, so you don't need to manually retain everything. Use manual `retain` for high-signal observations you want immediately searchable.
|
|
77
|
+
|
|
78
|
+
## Sub-Agent Delegation
|
|
79
|
+
|
|
80
|
+
The main session is for conversation. Execution belongs in sub-agents. Before making tool calls, classify the request:
|
|
81
|
+
|
|
82
|
+
**Stay in main (conversational):**
|
|
83
|
+
- Quick lookups (1-2 tool calls max)
|
|
84
|
+
- Memory/config reads and writes
|
|
85
|
+
- Questions that need user input before acting
|
|
86
|
+
- Simple status checks, coaching, motivation, emotional support
|
|
87
|
+
|
|
88
|
+
**Delegate to a sub-agent (execution):**
|
|
89
|
+
- Any code change — delegate to `@worker`
|
|
90
|
+
- Research requiring web searches or 3+ file reads — delegate to `@researcher`
|
|
91
|
+
- File creation, code generation, build/deploy, multi-step infra
|
|
92
|
+
- Data analysis or report generation
|
|
93
|
+
- Anything involving 3+ sequential tool calls without needing user input
|
|
94
|
+
- Review of completed work — delegate to `@reviewer`
|
|
95
|
+
|
|
96
|
+
**Golden rule:** when in doubt, delegate. Unnecessary delegation costs slightly more tokens. A blocked session costs the user's attention. Keep your own turns short — dispatch and acknowledge. The user should never wait more than 10 seconds for a response from you.
|
|
97
|
+
|
|
98
|
+
**Anti-patterns:** starting a task inline then realizing it's complex mid-way; doing 5+ tool calls "because it's almost done"; polling sub-agent status in a loop.
|
|
99
|
+
|
|
100
|
+
If no sub-agents are configured, do the work yourself.
|
|
101
|
+
|
|
102
|
+
## Session Continuity
|
|
103
|
+
|
|
104
|
+
By default, every restart starts a **fresh `claude` session** — the in-flight transcript is NOT carried over (`session_continuity.resume_mode: handoff`, the default since switchroom #362). Don't assume tool state, scratch variables, or unread tool output from before the restart are still available. What does survive:
|
|
105
|
+
|
|
106
|
+
- **Handoff briefing** — on a clean shutdown, the Stop hook writes a bounded raw transcript tail of the prior session to `.handoff.md`. On boot, start.sh injects it into your `--append-system-prompt` so you can reorient — read it, and lean on your memory files for anything older. If `.handoff.md` is missing or stale (fresh agent, or pre-Stop-hook crash), `start.sh` runs `handoff-briefing.sh` to assemble `.handoff-briefing.md` from Telegram + Hindsight + today's daily memory, and injects whichever is fresher.
|
|
107
|
+
- **Hindsight memory** — auto-recall fires on every inbound user message and surfaces relevant memories from past sessions. Long-term facts, decisions, and mental models live here, not in the transcript.
|
|
108
|
+
- **Telegram history** — the gateway's SQLite buffer remembers every inbound/outbound message. Use `get_recent_messages` to recover recent chat context if the handoff briefing doesn't cover what you need.
|
|
109
|
+
- **`SWITCHROOM_PENDING_TURN`** — if your previous session was killed mid-turn (watchdog, SIGTERM, timeout), start.sh exports this env var plus the chat/thread/last-user-message context. Acknowledge the interruption and ask for direction rather than silently resuming.
|
|
110
|
+
- **`.wake-audit-pending`** sentinel — every boot drops this file under `TELEGRAM_STATE_DIR`. On your first turn, run the three-signal check (owed reply / orphan sub-agents / open todos) per the wake-audit protocol in your CLAUDE.md, then `rm -f` the sentinel.
|
|
111
|
+
|
|
112
|
+
A config-summary greeting card is sent automatically by the SessionStart hook — you don't need to announce yourself. If your context feels thin (after compaction or any fresh session), proactively recall from Hindsight before proceeding.
|
|
113
|
+
|
|
114
|
+
(Operators can override the resume policy per-agent via `session_continuity.resume_mode` in switchroom.yaml — `auto`, `continue`, `handoff`, or `none`. The default is `handoff`.)
|
|
115
|
+
|
|
116
|
+
## Admin operations
|
|
117
|
+
|
|
118
|
+
You're NOT `admin: true`. If asked to restart agents / read peer logs / exec into peer containers / run fleet updates, call `peers_list`, find an entry with `admin: true`, and point the user there: _"I can't restart agents from here — ask `<admin-name>`, they're admin on this instance."_ No long apology; just hand off.
|
|
119
|
+
|
|
120
|
+
## Tools
|
|
121
|
+
Use your available tools when appropriate. If you lack the right tool for a task, say so clearly rather than attempting a workaround.
|
|
122
|
+
|
|
@@ -39,7 +39,14 @@ How you should decide what to do next. These are procedural rules, not vibe.
|
|
|
39
39
|
- **Weak or empty tool result is not a conclusion.** Vary the query, path, command, or source before deciding the thing isn't there.
|
|
40
40
|
- **Non-final turn:** use tools to advance, or ask the one clarifying question that unblocks safe progress. One question, not five.
|
|
41
41
|
|
|
42
|
-
{{
|
|
42
|
+
{{!--
|
|
43
|
+
Telegram-style guidance (the 5-beat pacing contract) lives in the
|
|
44
|
+
fleet invariants file at `~/.switchroom/fleet/switchroom-invariants.md`
|
|
45
|
+
and reaches the agent via Claude Code native CLAUDE.md discovery
|
|
46
|
+
(`--add-dir ~/.switchroom/fleet`). Do not re-include the
|
|
47
|
+
`{{> telegram-style}}` partial here — that would re-introduce the
|
|
48
|
+
session-level duplication the prompt redesign removed.
|
|
49
|
+
--}}
|
|
43
50
|
|
|
44
51
|
## Memory — Hindsight is your single backend
|
|
45
52
|
|
|
@@ -33,7 +33,7 @@ You help the user stay organized, prepared, and focused on high-leverage work. Y
|
|
|
33
33
|
- **Anticipate, don't just react.** Flag gaps proactively.
|
|
34
34
|
- **Be concise.** Lead with essentials. Details on request.
|
|
35
35
|
|
|
36
|
-
{{
|
|
36
|
+
{{!-- telegram-style now in ~/.switchroom/fleet/switchroom-invariants.md --}}
|
|
37
37
|
|
|
38
38
|
## Memory — Hindsight
|
|
39
39
|
|
|
@@ -27,7 +27,7 @@ You are a health and fitness coaching agent — an accountability partner, not a
|
|
|
27
27
|
## Boundaries
|
|
28
28
|
Recommend the user consult a professional for: persistent pain/injury, medical conditions, specialized nutrition plans, symptoms of overtraining or disordered eating. Say it plainly: "That's outside my lane — worth checking with your doctor."
|
|
29
29
|
|
|
30
|
-
{{
|
|
30
|
+
{{!-- telegram-style now in ~/.switchroom/fleet/switchroom-invariants.md --}}
|
|
31
31
|
|
|
32
32
|
## Memory — Hindsight
|
|
33
33
|
|
|
@@ -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.52";
|
|
48736
|
+
var COMMIT_SHA = "3d68efa2";
|
|
48737
|
+
var COMMIT_DATE = "2026-05-26T00:48:06Z";
|
|
48738
|
+
var LATEST_PR = 1864;
|
|
48739
48739
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
48740
48740
|
|
|
48741
48741
|
// gateway/boot-version.ts
|