viberoom 0.5.7 → 0.5.9
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 +19 -1
- package/dist/context.js +6 -0
- package/dist/hub.js +4 -2
- package/dist/main.js +3 -2
- package/dist/mcp-skills-server.js +119 -0
- package/dist/persona.js +98 -32
- package/dist/room-design.js +205 -0
- package/dist/room.js +253 -118
- package/dist/server.js +54 -1
- package/dist/shortcuts.js +10 -10
- package/dist/skills.js +28 -2
- package/dist/templates.js +17 -0
- package/dist/update.js +3 -3
- package/package.json +1 -1
- package/scripts/install.mjs +6 -1
- package/scripts/update.mjs +6 -1
- package/templates/maken-and-elaine/template.json +30 -0
- package/ui/app.css +13 -1
- package/ui/app.js +164 -92
- package/ui/avatars.js +125 -26
- package/ui/icons.js +2 -0
- package/ui/theme.css +12 -2
- package/templates/wren-and-quinn/template.json +0 -30
package/dist/templates.js
CHANGED
|
@@ -41,6 +41,12 @@ export function cleanTemplate(raw, id) {
|
|
|
41
41
|
out.created = t.created.trim();
|
|
42
42
|
return out;
|
|
43
43
|
}
|
|
44
|
+
export function roomSettingsFromTemplate(template) {
|
|
45
|
+
const settings = { ...template.settings };
|
|
46
|
+
if (!settings.emoji && template.emoji)
|
|
47
|
+
settings.emoji = template.emoji;
|
|
48
|
+
return settings;
|
|
49
|
+
}
|
|
44
50
|
export function templateId(name) {
|
|
45
51
|
const id = name.toLowerCase().normalize("NFKD").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
46
52
|
return ID_PATTERN.test(id) ? id : "template";
|
|
@@ -97,4 +103,15 @@ export class TemplateLibrary {
|
|
|
97
103
|
this.log.info(`saved template "${clean.name}" (${id})`);
|
|
98
104
|
return clean;
|
|
99
105
|
}
|
|
106
|
+
overwrite(id, template) {
|
|
107
|
+
if (!ID_PATTERN.test(id))
|
|
108
|
+
throw new Error(`bad template id "${id}"`);
|
|
109
|
+
const own = readTemplates(this.dir, this.log, false).find((t) => t.id === id);
|
|
110
|
+
if (!own)
|
|
111
|
+
throw new Error(`no own template "${id}" to overwrite`);
|
|
112
|
+
const clean = cleanTemplate({ ...template, created: own.created ?? new Date().toISOString() }, id);
|
|
113
|
+
writeFileAtomic(join(this.dir, id, "template.json"), `${JSON.stringify(clean, null, 2)}\n`);
|
|
114
|
+
this.log.info(`overwrote template "${clean.name}" (${id})`);
|
|
115
|
+
return clean;
|
|
116
|
+
}
|
|
100
117
|
}
|
package/dist/update.js
CHANGED
|
@@ -74,10 +74,10 @@ export function installCommandLine(version) {
|
|
|
74
74
|
throw new Error(`not a version: ${version}`);
|
|
75
75
|
return `npm install -g viberoom@${version} --no-audit --no-fund`;
|
|
76
76
|
}
|
|
77
|
-
export function installUpdate(version) {
|
|
77
|
+
export function installUpdate(version, cwd) {
|
|
78
78
|
const line = installCommandLine(version);
|
|
79
79
|
return new Promise((resolve) => {
|
|
80
|
-
const child = process.platform === "win32" ? spawn(line, { shell: true, windowsHide: true }) : spawn("npm", line.split(" ").slice(1));
|
|
80
|
+
const child = process.platform === "win32" ? spawn(line, { cwd, shell: true, windowsHide: true }) : spawn("npm", line.split(" ").slice(1), { cwd });
|
|
81
81
|
let output = "";
|
|
82
82
|
const collect = (chunk) => {
|
|
83
83
|
output = (output + chunk.toString()).slice(-4000);
|
|
@@ -90,6 +90,6 @@ export function installUpdate(version) {
|
|
|
90
90
|
}
|
|
91
91
|
export function restartWithNewBuild(mainModuleUrl, port, dataDir) {
|
|
92
92
|
const main = fileURLToPath(mainModuleUrl);
|
|
93
|
-
const child = spawn(process.execPath, [main, "start", "--port", String(port), "--data-dir", dataDir, "--no-open"], { detached: true, stdio: "ignore", windowsHide: true });
|
|
93
|
+
const child = spawn(process.execPath, [main, "start", "--port", String(port), "--data-dir", dataDir, "--no-open"], { cwd: dataDir, detached: true, stdio: "ignore", windowsHide: true });
|
|
94
94
|
child.unref();
|
|
95
95
|
}
|
package/package.json
CHANGED
package/scripts/install.mjs
CHANGED
|
@@ -11,9 +11,14 @@ const args = new Set(process.argv.slice(2));
|
|
|
11
11
|
const dataDir = process.env.VIBEROOM_DATA_DIR ? resolve(process.env.VIBEROOM_DATA_DIR) : join(homedir(), ".viberoom");
|
|
12
12
|
const isWin = process.platform === "win32";
|
|
13
13
|
|
|
14
|
+
function npmEnv() {
|
|
15
|
+
return Object.fromEntries(Object.entries(process.env).filter(([name]) => !/^npm_config_/i.test(name)));
|
|
16
|
+
}
|
|
17
|
+
|
|
14
18
|
function run(cmd, cmdArgs, opts = {}) {
|
|
15
19
|
console.log(`> ${cmd} ${cmdArgs.join(" ")}`);
|
|
16
|
-
const
|
|
20
|
+
const env = cmd === "npm" ? npmEnv() : process.env;
|
|
21
|
+
const r = isWin && cmd === "npm" ? spawnSync(`npm ${cmdArgs.join(" ")}`, { cwd: root, stdio: "inherit", env, shell: true, ...opts }) : spawnSync(cmd, cmdArgs, { cwd: root, stdio: "inherit", env, ...opts });
|
|
17
22
|
if (r.status !== 0) throw new Error(`${cmd} ${cmdArgs.join(" ")} failed with exit code ${r.status}`);
|
|
18
23
|
}
|
|
19
24
|
|
package/scripts/update.mjs
CHANGED
|
@@ -10,9 +10,14 @@ const args = new Set(process.argv.slice(2));
|
|
|
10
10
|
const isWin = process.platform === "win32";
|
|
11
11
|
const main = join(root, "dist", "main.js");
|
|
12
12
|
|
|
13
|
+
function npmEnv() {
|
|
14
|
+
return Object.fromEntries(Object.entries(process.env).filter(([name]) => !/^npm_config_/i.test(name)));
|
|
15
|
+
}
|
|
16
|
+
|
|
13
17
|
function run(cmd, cmdArgs, opts = {}) {
|
|
14
18
|
console.log(`> ${cmd} ${cmdArgs.join(" ")}`);
|
|
15
|
-
const
|
|
19
|
+
const env = cmd === "npm" ? npmEnv() : process.env;
|
|
20
|
+
const r = isWin && cmd === "npm" ? spawnSync(`npm ${cmdArgs.join(" ")}`, { cwd: root, stdio: "inherit", env, shell: true, ...opts }) : spawnSync(cmd, cmdArgs, { cwd: root, stdio: "inherit", env, ...opts });
|
|
16
21
|
if (r.status !== 0) throw new Error(`${cmd} ${cmdArgs.join(" ")} failed with exit code ${r.status}`);
|
|
17
22
|
}
|
|
18
23
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Maken & Elaine",
|
|
3
|
+
"order": 1,
|
|
4
|
+
"emoji": "⚒️",
|
|
5
|
+
"recommended": true,
|
|
6
|
+
"description": "Two equals around one piece of work. Maken makes by default, Elaine explains by default; either does either when asked, and either reviews the other's work when you ask for it. The way viberoom itself was made.",
|
|
7
|
+
"settings": {
|
|
8
|
+
"language": {
|
|
9
|
+
"mode": "follow-human"
|
|
10
|
+
},
|
|
11
|
+
"turnTaking": "parallel",
|
|
12
|
+
"agentsWakeEachOther": true,
|
|
13
|
+
"hopLimit": 24,
|
|
14
|
+
"customRules": "Both make, both explain, both review; @ decides who does what. Without @, a task goes to the one who has worked on that part the most, or most recently; if neither has touched it, to Maken. A question without @ goes to Elaine.\nThe human decides when work is reviewed: ask the other one to review it. Unasked, the other answers questions and otherwise stays silent; it does not review, comment on or extend work it was not asked about. When asked to review, first say in one line what you will check, then check it by that criterion: a number, a live check, the risky case. Whoever made it reports to the human. Address the other vibemate too only when the report changes something it works on or relies on: a shared file, an interface, a convention, a measurement that overturns its claim; then say in one line what you want from it (\"nothing, for your context\" counts). Otherwise it reads the report later, unaddressed. A report is never a request for review; only the human asks for one. Finished work is reported as: what changed (the files, and the commit if the project uses version control), how to verify it (a command, a script, a screenshot), what was not verified, what is left.\nMeasure before diagnosing; say no with a reason and a cheaper alternative; keep replies short.\nWork only on your own task, never on the other's; do not touch what the other is working on.\nReply only when addressed. On a message to everyone, only the one it concerns answers; the other stays silent. Never add to, correct or comment on the other's reply unless the human asks.\nFollow the conventions of the project you work in (its instructions, notes, tests). Where it keeps decision notes, write the decision there before reporting it here.\nRead every proposal and analyse it; agree or reject only with a real argument. Never concede to be agreeable, never object to seem rigorous.\nReply in the human's language. Code identifiers, commands, file paths, protocol and tool names stay exactly as in the code; established technical terms (kernel, thread, commit, pull request, layout) stay in English."
|
|
15
|
+
},
|
|
16
|
+
"vibemates": [
|
|
17
|
+
{
|
|
18
|
+
"name": "Maken",
|
|
19
|
+
"tagline": "makes by default; explains and reviews when asked",
|
|
20
|
+
"role": "You are Maken, one of two equal vibemates, Maken and Elaine. You lean to making: a task without an address is yours when it touches what you have made, or when neither of you has; before you change anything, read what is already there, the notes, the docs and the files, and keep each change small. Everything else is in the room rules.",
|
|
21
|
+
"avatar": "🔨"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"name": "Elaine",
|
|
25
|
+
"tagline": "explains by default; makes and reviews when asked",
|
|
26
|
+
"role": "You are Elaine, one of two equal vibemates, Maken and Elaine. You lean to explaining: a question without an address is yours; when you review, it is by a criterion, not an impression. Everything else is in the room rules.",
|
|
27
|
+
"avatar": "💡"
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
package/ui/app.css
CHANGED
|
@@ -330,7 +330,7 @@
|
|
|
330
330
|
.done-x:hover { opacity: 1; background: var(--done-hover); }
|
|
331
331
|
.jump-latest .i { width: 16px; height: 16px; }
|
|
332
332
|
.bubble { max-width: 100%; background: #fff; border-radius: 6px 18px 18px 18px; padding: 12px 16px; min-width: 0; font-size: 14.5px; font-weight: 600; line-height: 1.55; color: var(--ink-2); box-shadow: 0 2px 0 rgba(28, 27, 51, 0.06), 0 1px 3px rgba(28, 27, 51, 0.04); }
|
|
333
|
-
.msg.mine .bubble { background: var(--grad-primary); color: #fff; border-radius: 18px
|
|
333
|
+
.msg.mine .bubble { background: var(--grad-primary); color: #fff; border-radius: 18px 6px 18px 18px; box-shadow: 0 8px 18px -10px rgba(91, 91, 240, 0.7); padding: 11px 16px; }
|
|
334
334
|
.msg.mine .bubble .text { color: #fff; }
|
|
335
335
|
.msg.mine.waiting .bubble { background: var(--attention-grad); box-shadow: var(--attention-shadow); }
|
|
336
336
|
.bubble .waiting { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 10px; padding-top: 8px; border-top: 1px solid rgba(255, 255, 255, 0.35); font-size: 13px; font-weight: 600; color: rgba(255, 255, 255, 0.92); }
|
|
@@ -486,6 +486,18 @@ table.csv tbody tr:nth-child(even) td { background: rgba(91, 91, 240, 0.03); }
|
|
|
486
486
|
.perm-btn.kind-allow_once, .perm-btn.kind-allow_always { background: var(--mint); color: var(--mint-ink); }
|
|
487
487
|
.perm-btn.kind-reject_once, .perm-btn.kind-reject_always { background: var(--rose); color: var(--rose-ink); }
|
|
488
488
|
.perm-result { color: var(--muted); font-size: 12px; }
|
|
489
|
+
.proposal { max-width: 760px; }
|
|
490
|
+
.prop-why { font-weight: 500; margin: 4px 0 6px; }
|
|
491
|
+
.prop-diff { display: flex; flex-direction: column; gap: 4px; margin: 6px 0; font-size: 12px; font-weight: 500; }
|
|
492
|
+
.prop-row { background: rgba(255, 255, 255, 0.6); border-radius: 8px; padding: 6px 8px; }
|
|
493
|
+
.prop-row > b { font-weight: 800; margin-right: 6px; }
|
|
494
|
+
.prop-line { margin-top: 2px; white-space: pre-wrap; }
|
|
495
|
+
.prop-line.add { color: var(--mint-ink); }
|
|
496
|
+
.prop-line.del { color: var(--rose-ink); text-decoration: line-through; opacity: 0.8; }
|
|
497
|
+
.prop-from { color: var(--muted); }
|
|
498
|
+
.prop-to { font-weight: 800; }
|
|
499
|
+
.prop-warn { margin: 6px 0 0 16px; padding: 0; font-size: 12px; font-weight: 500; color: var(--warm-ink, inherit); }
|
|
500
|
+
.prop-own { display: flex; gap: 6px; align-items: center; font-size: 12px; font-weight: 600; margin-top: 6px; }
|
|
489
501
|
.visibility-divider { display: flex; align-items: center; gap: 10px; margin: 4px 0; }
|
|
490
502
|
.visibility-divider .vd-line { flex: 1; height: 8px; background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='8' viewBox='0 0 16 8'><path d='M0 4 Q4 0 8 4 T16 4' fill='none' stroke='%23f472b6' stroke-width='1.6'/></svg>") repeat-x center / 16px 8px; opacity: 0.7; }
|
|
491
503
|
.visibility-divider .vd-label { color: var(--unseen-ink); background: var(--unseen-bg); border-radius: var(--r-pill); padding: 4px 12px; font-size: 11px; font-weight: 800; white-space: nowrap; max-width: 70%; overflow: hidden; text-overflow: ellipsis; }
|