convene-cli 1.0.3 → 1.0.5
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/commands/init.js +75 -5
- package/dist/index.js +1 -0
- package/package.json +1 -1
package/dist/commands/init.js
CHANGED
|
@@ -34,13 +34,15 @@ function upsertMarkerBlock(content, block) {
|
|
|
34
34
|
const sep = content.length === 0 ? '' : content.endsWith('\n') ? '\n' : '\n\n';
|
|
35
35
|
return content + sep + block + '\n';
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
// Every file this writes lives inside the git repo, so git history IS the backup —
|
|
38
|
+
// dropping a sibling `.bak` just litters the working tree (and shows up as untracked
|
|
39
|
+
// noise / risks being committed). The only `.bak` we keep is for the user's GLOBAL
|
|
40
|
+
// ~/.claude/settings.json, which is outside any repo (see registerHook).
|
|
41
|
+
function writeIfChanged(file, content) {
|
|
38
42
|
const exists = node_fs_1.default.existsSync(file);
|
|
39
43
|
const old = exists ? node_fs_1.default.readFileSync(file, 'utf8') : null;
|
|
40
44
|
if (old === content)
|
|
41
45
|
return 'unchanged';
|
|
42
|
-
if (exists && backup)
|
|
43
|
-
node_fs_1.default.writeFileSync(file + '.bak', old);
|
|
44
46
|
node_fs_1.default.writeFileSync(file, content);
|
|
45
47
|
return exists ? 'updated' : 'created';
|
|
46
48
|
}
|
|
@@ -81,8 +83,7 @@ function ensureGitignoreGuard(top) {
|
|
|
81
83
|
next = next + sep + guard;
|
|
82
84
|
}
|
|
83
85
|
if (next !== old) {
|
|
84
|
-
|
|
85
|
-
node_fs_1.default.writeFileSync(file + '.bak', old);
|
|
86
|
+
// .gitignore is git-tracked — no .bak (git history is the backup).
|
|
86
87
|
node_fs_1.default.writeFileSync(file, next);
|
|
87
88
|
}
|
|
88
89
|
// Belt-and-suspenders: confirm the join-token-bearing file is actually trackable.
|
|
@@ -174,6 +175,66 @@ function seedMemory(top, slug, baseUrl) {
|
|
|
174
175
|
/* memory seed is best-effort */
|
|
175
176
|
}
|
|
176
177
|
}
|
|
178
|
+
// ── Cross-agent rule files ───────────────────────────────────────────────────
|
|
179
|
+
// AGENTS.md (written above) is the keystone — Codex, Windsurf, Cursor, and Cline
|
|
180
|
+
// auto-read it. These add each agent's NATIVE rule file so they reliably pick
|
|
181
|
+
// Convene up and call the `convene` CLI themselves. No per-prompt FRESH injection
|
|
182
|
+
// outside Claude Code (and, later, Codex) — see CONVENE_PROTOCOL.md.
|
|
183
|
+
function writeCursorRule(top, block) {
|
|
184
|
+
const file = node_path_1.default.join(top, '.cursor', 'rules', 'convene.mdc');
|
|
185
|
+
const frontmatter = '---\ndescription: Convene AI coordination bus — read channel context and post updates\nalwaysApply: true\n---\n\n';
|
|
186
|
+
const old = node_fs_1.default.existsSync(file) ? node_fs_1.default.readFileSync(file, 'utf8') : null;
|
|
187
|
+
const content = old !== null ? upsertMarkerBlock(old, block) : frontmatter + block + '\n';
|
|
188
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(file), { recursive: true });
|
|
189
|
+
const r = writeIfChanged(file, content);
|
|
190
|
+
log(`${r === 'unchanged' ? '·' : '✓'} .cursor/rules/convene.mdc (${r}) — Cursor auto-applies it every chat`);
|
|
191
|
+
}
|
|
192
|
+
function writeClineRule(top, block) {
|
|
193
|
+
const file = node_path_1.default.join(top, '.clinerules', 'convene.md');
|
|
194
|
+
const old = node_fs_1.default.existsSync(file) ? node_fs_1.default.readFileSync(file, 'utf8') : '';
|
|
195
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(file), { recursive: true });
|
|
196
|
+
const r = writeIfChanged(file, upsertMarkerBlock(old, block));
|
|
197
|
+
log(`${r === 'unchanged' ? '·' : '✓'} .clinerules/convene.md (${r}) — Cline workspace rule`);
|
|
198
|
+
}
|
|
199
|
+
function writeGeminiSettings(top) {
|
|
200
|
+
const file = node_path_1.default.join(top, '.gemini', 'settings.json');
|
|
201
|
+
let obj = {};
|
|
202
|
+
if (node_fs_1.default.existsSync(file)) {
|
|
203
|
+
try {
|
|
204
|
+
obj = JSON.parse(node_fs_1.default.readFileSync(file, 'utf8')) || {};
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
log('· .gemini/settings.json (left as-is — unparseable JSON)');
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
obj.context = obj.context && typeof obj.context === 'object' ? obj.context : {};
|
|
212
|
+
const cur = obj.context.fileName;
|
|
213
|
+
let names = Array.isArray(cur) ? cur : typeof cur === 'string' ? [cur] : [];
|
|
214
|
+
if (!names.includes('AGENTS.md'))
|
|
215
|
+
names = [...names, 'AGENTS.md'];
|
|
216
|
+
obj.context.fileName = names;
|
|
217
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(file), { recursive: true });
|
|
218
|
+
const r = writeIfChanged(file, JSON.stringify(obj, null, 2) + '\n');
|
|
219
|
+
log(`${r === 'unchanged' ? '·' : '✓'} .gemini/settings.json (${r}) — Gemini CLI reads AGENTS.md each prompt`);
|
|
220
|
+
}
|
|
221
|
+
function writeAiderConf(top) {
|
|
222
|
+
// No YAML parser bundled, so write-if-absent (don't risk clobbering an existing config).
|
|
223
|
+
const file = node_path_1.default.join(top, '.aider.conf.yml');
|
|
224
|
+
if (node_fs_1.default.existsSync(file)) {
|
|
225
|
+
log('· .aider.conf.yml (exists) — add `read: [AGENTS.md, CONVENE_PROTOCOL.md]` so Aider loads Convene');
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
node_fs_1.default.writeFileSync(file, 'read:\n - AGENTS.md\n - CONVENE_PROTOCOL.md\n');
|
|
229
|
+
log('✓ .aider.conf.yml (created) — Aider loads the Convene instructions at startup');
|
|
230
|
+
}
|
|
231
|
+
function writeAgentRules(top, slug, member, baseUrl) {
|
|
232
|
+
const block = (0, protocol_1.conveneBlock)(slug, member, baseUrl);
|
|
233
|
+
writeCursorRule(top, block);
|
|
234
|
+
writeClineRule(top, block);
|
|
235
|
+
writeGeminiSettings(top);
|
|
236
|
+
writeAiderConf(top);
|
|
237
|
+
}
|
|
177
238
|
async function init(opts) {
|
|
178
239
|
const top = (0, git_1.gitToplevel)();
|
|
179
240
|
if (!top)
|
|
@@ -185,6 +246,7 @@ async function init(opts) {
|
|
|
185
246
|
const skipHook = opts.noHook === true || opts.hook === false;
|
|
186
247
|
const skipGithook = opts.noGithook === true || opts.githook === false;
|
|
187
248
|
const skipJoinToken = opts.noJoinToken === true || opts.joinToken === false;
|
|
249
|
+
const skipAgentRules = opts.noAgentRules === true || opts.agentRules === false;
|
|
188
250
|
let slug = opts.slug || existing?.slug;
|
|
189
251
|
let displayName = opts.name || existing?.displayName;
|
|
190
252
|
let joinToken = existing?.joinToken; // reuse if present (keeps init idempotent)
|
|
@@ -321,6 +383,14 @@ async function init(opts) {
|
|
|
321
383
|
// 7b. committed git pre-push hook — the tool-agnostic backstop that auto-posts a
|
|
322
384
|
// [STATUS] when work is pushed (fires for Codex/Cowork/humans, not just Claude).
|
|
323
385
|
installGitHookStep(top, skipGithook);
|
|
386
|
+
// 7c. Cross-agent rule files so Codex/Cursor/Cline/Gemini/Aider pick Convene up too
|
|
387
|
+
// (AGENTS.md already covers the auto-readers; these add each agent's native file).
|
|
388
|
+
if (skipAgentRules) {
|
|
389
|
+
log('Skipped cross-agent rule files (--no-agent-rules).');
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
writeAgentRules(top, slug, member, baseUrl);
|
|
393
|
+
}
|
|
324
394
|
// 8. memory seed (best-effort, outside the repo)
|
|
325
395
|
seedMemory(top, slug, baseUrl);
|
|
326
396
|
// 9. teammate one-liner
|
package/dist/index.js
CHANGED
|
@@ -124,6 +124,7 @@ program
|
|
|
124
124
|
.option('--no-hook', 'do not register the UserPromptSubmit hook')
|
|
125
125
|
.option('--no-githook', 'do not install the git pre-push auto-status hook')
|
|
126
126
|
.option('--no-join-token', 'do not mint/commit a self-serve join token (use for public repos)')
|
|
127
|
+
.option('--no-agent-rules', 'do not write Cursor/Cline/Gemini/Aider rule files (Claude/Codex via AGENTS.md still work)')
|
|
127
128
|
.option('--force', 'commit a join token even if the repo looks public (overrides the guard)')
|
|
128
129
|
.option('--yes', 'non-interactive')
|
|
129
130
|
.option('--offline', 'write local files only (no API calls)')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "convene-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Convene CLI — AI development coordination bus client + UserPromptSubmit hook. Install: npm i -g convene-cli; then `convene setup`.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://dev.convene.live",
|