cto-agent-system 1.1.0 → 1.2.1
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.
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
{
|
|
10
10
|
"name": "cto-agent-system",
|
|
11
11
|
"description": "An autonomous software company: CEO (you) + CTO/CPO/CMO leading 15 specialist agents. Run /cto and the CTO takes over — digests the project, fixes fires, improves the product, reports back with a roadmap.",
|
|
12
|
-
"version": "1.1
|
|
12
|
+
"version": "1.2.1",
|
|
13
13
|
"source": "./",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "xenitV1",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cto-agent-system",
|
|
3
3
|
"description": "An autonomous software company: CEO (you) + CTO/CPO/CMO leading 15 specialist agents. Run /cto and the CTO takes over — digests the project, fixes fires, improves the product, reports back with a roadmap.",
|
|
4
|
-
"version": "1.1
|
|
4
|
+
"version": "1.2.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "xenitV1",
|
|
7
7
|
"url": "https://github.com/xenitV1"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cto-agent-system",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "An autonomous software company: CEO (you) + CTO/CPO/CMO leading 15 specialist agents. Run /cto and the CTO takes over — digests the project, fixes fires, improves the product, reports back with a roadmap.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "xenitV1",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "cto-agent-system",
|
|
3
3
|
"displayName": "Software Company Agent System",
|
|
4
4
|
"description": "An autonomous software company: CEO (you) + CTO/CPO/CMO leading 15 specialist agents. Run /cto and the CTO takes over.",
|
|
5
|
-
"version": "1.1
|
|
5
|
+
"version": "1.2.1",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "xenitV1",
|
|
8
8
|
"url": "https://github.com/xenitV1"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cto-agent-system",
|
|
3
3
|
"description": "An autonomous software company: CEO (you) + CTO/CPO/CMO leading 15 specialist agents. Run /cto and the CTO takes over — digests the project, fixes fires, improves the product, reports back with a roadmap.",
|
|
4
|
-
"version": "1.1
|
|
4
|
+
"version": "1.2.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "xenitV1",
|
|
7
7
|
"url": "https://github.com/xenitV1"
|
package/install.js
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
import { dirname, join, resolve, isAbsolute } from "node:path";
|
|
23
23
|
import { fileURLToPath } from "node:url";
|
|
24
24
|
import { homedir, platform } from "node:os";
|
|
25
|
+
import { ReadStream } from "node:tty";
|
|
25
26
|
|
|
26
27
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
27
28
|
// install.js lives at the package root (next to AGENTS.md, src/, .claude/).
|
|
@@ -60,22 +61,26 @@ const ADAPTERS = [
|
|
|
60
61
|
{
|
|
61
62
|
key: "claude", name: "Claude Code", dir: ".claude", cmds: ["claude"],
|
|
62
63
|
globalDir: () => join(homedir(), ".claude"),
|
|
63
|
-
|
|
64
|
+
pluginCapable: true,
|
|
65
|
+
pluginCmds: [
|
|
66
|
+
"/plugin marketplace add xenitV1/cto-agent-system",
|
|
67
|
+
"/plugin install cto-agent-system@cto-agent-marketplace",
|
|
68
|
+
],
|
|
64
69
|
},
|
|
65
70
|
{
|
|
66
71
|
key: "codex", name: "OpenAI Codex", dir: ".codex", cmds: ["codex"],
|
|
67
72
|
globalDir: () => join(homedir(), ".codex"),
|
|
68
|
-
|
|
73
|
+
pluginCapable: false, // .codex-plugin auto-detected from folder copy
|
|
69
74
|
},
|
|
70
75
|
{
|
|
71
76
|
key: "opencode", name: "OpenCode", dir: ".opencode", cmds: ["opencode"],
|
|
72
77
|
globalDir: () => join(homedir(), ".config", "opencode"),
|
|
73
|
-
|
|
78
|
+
pluginCapable: false, // OpenCode plugins are JS hooks, separate concept
|
|
74
79
|
},
|
|
75
80
|
{
|
|
76
81
|
key: "cursor", name: "Cursor", dir: ".cursor", cmds: ["cursor"],
|
|
77
82
|
globalDir: () => join(homedir(), ".cursor"),
|
|
78
|
-
|
|
83
|
+
pluginCapable: false, // .cursor-plugin auto-detected from folder copy
|
|
79
84
|
},
|
|
80
85
|
];
|
|
81
86
|
|
|
@@ -100,6 +105,178 @@ async function askYesNo(rl, question, defaultYes = true) {
|
|
|
100
105
|
return answer === "y" || answer === "yes";
|
|
101
106
|
}
|
|
102
107
|
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// Interactive menus (arrow keys + space toggle + enter).
|
|
110
|
+
// Zero-dependency: raw TTY via node:tty. Non-TTY falls back to a numbered text
|
|
111
|
+
// prompt. We redraw by clearing each line individually with \x1b[2K and moving
|
|
112
|
+
// up, which is more reliable than whole-block clears.
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
const ESC = "\x1b";
|
|
116
|
+
const HIDE = `${ESC}[?25l`;
|
|
117
|
+
const SHOW = `${ESC}[?25h`;
|
|
118
|
+
const UP = `${ESC}[1A`;
|
|
119
|
+
const CLR = `${ESC}[2K`; // erase entire current line
|
|
120
|
+
const DOWN = `${ESC}[1B`;
|
|
121
|
+
|
|
122
|
+
function isInteractiveTty() {
|
|
123
|
+
return Boolean(stdin.isTTY && stdin instanceof ReadStream);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Move up N lines and clear each, leaving the cursor at the top of the cleared block.
|
|
127
|
+
function rewind(lines) {
|
|
128
|
+
if (lines <= 0) return;
|
|
129
|
+
let out = "";
|
|
130
|
+
for (let i = 0; i < lines; i++) out += `${UP}${CLR}`;
|
|
131
|
+
// Drop back down to the first line of the block so we can re-render there.
|
|
132
|
+
for (let i = 0; i < lines; i++) out += DOWN;
|
|
133
|
+
out += UP.repeat(lines);
|
|
134
|
+
stdout.write(out);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Multi-select checkbox menu.
|
|
139
|
+
* @returns {Promise<number[]>} indices of checked items at confirm time.
|
|
140
|
+
*/
|
|
141
|
+
function checkboxMenu(title, items) {
|
|
142
|
+
return new Promise((resolvePromise) => {
|
|
143
|
+
if (!isInteractiveTty()) {
|
|
144
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
145
|
+
const fallback = async () => {
|
|
146
|
+
console.log(title);
|
|
147
|
+
items.forEach((it, i) => {
|
|
148
|
+
const mark = it.checked ? "[x]" : "[ ]";
|
|
149
|
+
console.log(` ${i + 1}. ${mark} ${it.label}${it.hint ? ` — ${it.hint}` : ""}`);
|
|
150
|
+
});
|
|
151
|
+
const ans = (await rl.question(
|
|
152
|
+
`Enter numbers comma-separated (e.g. 1,3) or 'all' (blank=all): `
|
|
153
|
+
)).trim().toLowerCase();
|
|
154
|
+
rl.close();
|
|
155
|
+
if (ans === "all" || ans === "") return items.map((_, i) => i);
|
|
156
|
+
return ans.split(/[,\s]+/).map((n) => parseInt(n, 10) - 1)
|
|
157
|
+
.filter((n) => n >= 0 && n < items.length);
|
|
158
|
+
};
|
|
159
|
+
fallback().then(resolvePromise);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
let cursor = 0;
|
|
164
|
+
const state = items.map((it) => !!it.checked);
|
|
165
|
+
const titleLines = title.split("\n");
|
|
166
|
+
const totalLines = titleLines.length + 1 + items.length; // title + hint + items
|
|
167
|
+
let drawn = 0;
|
|
168
|
+
|
|
169
|
+
const render = () => {
|
|
170
|
+
if (drawn > 0) rewind(drawn);
|
|
171
|
+
const lines = [];
|
|
172
|
+
lines.push(...titleLines);
|
|
173
|
+
lines.push(" (↑/↓ move · space toggle · a = all · enter = confirm)");
|
|
174
|
+
items.forEach((it, i) => {
|
|
175
|
+
const arrow = i === cursor ? "❯" : " ";
|
|
176
|
+
const box = state[i] ? "◉" : "◯";
|
|
177
|
+
const hint = it.hint ? ` ${it.hint}` : "";
|
|
178
|
+
lines.push(` ${arrow} ${box} ${it.label}${hint}`);
|
|
179
|
+
});
|
|
180
|
+
stdout.write(lines.join("\r\n") + "\r\n");
|
|
181
|
+
drawn = totalLines;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
stdin.setRawMode(true);
|
|
185
|
+
stdin.resume();
|
|
186
|
+
stdin.setEncoding("utf8");
|
|
187
|
+
stdin.setRawMode(true);
|
|
188
|
+
stdout.write(HIDE);
|
|
189
|
+
render();
|
|
190
|
+
|
|
191
|
+
const cleanup = () => {
|
|
192
|
+
stdin.removeListener("data", onData);
|
|
193
|
+
try { stdin.setRawMode(false); } catch {}
|
|
194
|
+
stdin.pause();
|
|
195
|
+
stdout.write(SHOW);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const onData = (ch) => {
|
|
199
|
+
if (ch === "\x03") { cleanup(); process.exit(0); }
|
|
200
|
+
if (ch === "\r" || ch === "\n") {
|
|
201
|
+
cleanup();
|
|
202
|
+
resolvePromise(items.map((_, i) => i).filter((i) => state[i]));
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (ch === "a" || ch === "A") {
|
|
206
|
+
const allOn = state.every(Boolean);
|
|
207
|
+
for (let i = 0; i < state.length; i++) state[i] = !allOn;
|
|
208
|
+
render();
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (ch === " ") { state[cursor] = !state[cursor]; render(); return; }
|
|
212
|
+
if (ch === `${ESC}[A`) { cursor = (cursor - 1 + items.length) % items.length; render(); return; }
|
|
213
|
+
if (ch === `${ESC}[B`) { cursor = (cursor + 1) % items.length; render(); return; }
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
stdin.on("data", onData);
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Single-select menu (arrows + enter). Returns chosen index.
|
|
222
|
+
*/
|
|
223
|
+
function selectMenu(title, items) {
|
|
224
|
+
return new Promise((resolvePromise) => {
|
|
225
|
+
if (!isInteractiveTty()) {
|
|
226
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
227
|
+
const fallback = async () => {
|
|
228
|
+
console.log(title);
|
|
229
|
+
items.forEach((it, i) => console.log(` ${i + 1}. ${it.label}`));
|
|
230
|
+
const ans = parseInt((await rl.question("Choice (number): ")).trim(), 10);
|
|
231
|
+
rl.close();
|
|
232
|
+
return Number.isNaN(ans) ? 0 : Math.max(0, Math.min(items.length - 1, ans - 1));
|
|
233
|
+
};
|
|
234
|
+
fallback().then(resolvePromise);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
let cursor = 0;
|
|
239
|
+
const titleLines = title.split("\n");
|
|
240
|
+
const totalLines = titleLines.length + 1 + items.length;
|
|
241
|
+
let drawn = 0;
|
|
242
|
+
|
|
243
|
+
const render = () => {
|
|
244
|
+
if (drawn > 0) rewind(drawn);
|
|
245
|
+
const lines = [];
|
|
246
|
+
lines.push(...titleLines);
|
|
247
|
+
lines.push(" (↑/↓ move · enter = select)");
|
|
248
|
+
items.forEach((it, i) => {
|
|
249
|
+
const arrow = i === cursor ? "❯" : " ";
|
|
250
|
+
lines.push(` ${arrow} ${it.label}`);
|
|
251
|
+
});
|
|
252
|
+
stdout.write(lines.join("\r\n") + "\r\n");
|
|
253
|
+
drawn = totalLines;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
stdin.setRawMode(true);
|
|
257
|
+
stdin.resume();
|
|
258
|
+
stdin.setEncoding("utf8");
|
|
259
|
+
stdout.write(HIDE);
|
|
260
|
+
render();
|
|
261
|
+
|
|
262
|
+
const cleanup = () => {
|
|
263
|
+
stdin.removeListener("data", onData);
|
|
264
|
+
try { stdin.setRawMode(false); } catch {}
|
|
265
|
+
stdin.pause();
|
|
266
|
+
stdout.write(SHOW);
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const onData = (ch) => {
|
|
270
|
+
if (ch === "\x03") { cleanup(); process.exit(0); }
|
|
271
|
+
if (ch === "\r" || ch === "\n") { cleanup(); resolvePromise(cursor); return; }
|
|
272
|
+
if (ch === `${ESC}[A`) { cursor = (cursor - 1 + items.length) % items.length; render(); return; }
|
|
273
|
+
if (ch === `${ESC}[B`) { cursor = (cursor + 1) % items.length; render(); return; }
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
stdin.on("data", onData);
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
103
280
|
// ---------------------------------------------------------------------------
|
|
104
281
|
// File helpers
|
|
105
282
|
// ---------------------------------------------------------------------------
|
|
@@ -107,6 +284,15 @@ async function askYesNo(rl, question, defaultYes = true) {
|
|
|
107
284
|
function log(icon, msg) { console.log(` ${icon} ${msg}`); }
|
|
108
285
|
|
|
109
286
|
function copyTree(src, dst, { overwrite = false } = {}) {
|
|
287
|
+
const stat = lstatSync(src);
|
|
288
|
+
if (stat.isFile()) {
|
|
289
|
+
// src is a single file — copy it (ensuring the parent dir exists).
|
|
290
|
+
if (!overwrite && existsSync(dst)) return;
|
|
291
|
+
mkdirSync(dirname(dst), { recursive: true });
|
|
292
|
+
cpSync(src, dst, { overwrite: true });
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
// src is a directory — recurse.
|
|
110
296
|
mkdirSync(dst, { recursive: true });
|
|
111
297
|
for (const name of readdirSync(src)) {
|
|
112
298
|
const s = join(src, name);
|
|
@@ -145,21 +331,32 @@ function installAdapter(adapter, scope, force) {
|
|
|
145
331
|
log("⚠", `${adapter.dir}/ not shipped (adapter not built yet) — skipping ${adapter.name}`);
|
|
146
332
|
return;
|
|
147
333
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
334
|
+
// Project scope: write the adapter dir (e.g. <project>/.claude) as-is.
|
|
335
|
+
// Global scope: merge the CONTENTS of the adapter dir into the tool's
|
|
336
|
+
// global config dir (e.g. ~/.claude/), NOT a nested ~/.claude/.claude/.
|
|
337
|
+
// That's how Claude Code / Codex / OpenCode actually discover them.
|
|
338
|
+
if (scope === "global") {
|
|
339
|
+
const globalBase = adapter.globalDir();
|
|
340
|
+
mkdirSync(globalBase, { recursive: true });
|
|
341
|
+
// Merge each child of srcDir into globalBase.
|
|
342
|
+
for (const name of readdirSync(srcDir)) {
|
|
343
|
+
copyTree(join(srcDir, name), join(globalBase, name), { overwrite: force });
|
|
344
|
+
}
|
|
345
|
+
log("✓", `${adapter.dir}/* → ${globalBase} (global) — ${adapter.name}`);
|
|
152
346
|
} else {
|
|
153
|
-
|
|
154
|
-
|
|
347
|
+
const dst = join(PROJECT_TARGET, adapter.dir);
|
|
348
|
+
if (existsSync(dst) && !force) {
|
|
349
|
+
copyTree(srcDir, dst, { overwrite: false });
|
|
350
|
+
log("✓", `${adapter.dir}/ (merged) — ${adapter.name}`);
|
|
351
|
+
} else {
|
|
352
|
+
copyTree(srcDir, dst, { overwrite: true });
|
|
353
|
+
log("✓", `${adapter.dir}/ — ${adapter.name}`);
|
|
354
|
+
}
|
|
155
355
|
}
|
|
156
356
|
}
|
|
157
357
|
|
|
158
|
-
//
|
|
358
|
+
// Project target (set in main()).
|
|
159
359
|
let PROJECT_TARGET = process.cwd();
|
|
160
|
-
function adapterBase(adapter, scope) {
|
|
161
|
-
return scope === "global" ? adapter.globalDir() : PROJECT_TARGET;
|
|
162
|
-
}
|
|
163
360
|
|
|
164
361
|
function initState(target, force) {
|
|
165
362
|
const cto = join(target, ".cto");
|
|
@@ -236,20 +433,25 @@ Examples:
|
|
|
236
433
|
console.log("");
|
|
237
434
|
|
|
238
435
|
const interactive = !yes && stdin.isTTY;
|
|
239
|
-
const rl = interactive ? createInterface({ input: stdin, output: stdout }) : null;
|
|
240
436
|
|
|
241
|
-
// 2. Scope
|
|
437
|
+
// 2. Scope — arrow-key single select (interactive) or flags/default.
|
|
242
438
|
let scope;
|
|
243
439
|
if (goGlobal) scope = "global";
|
|
244
440
|
else if (goProject) scope = "project";
|
|
245
441
|
else if (interactive) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
442
|
+
const scopeItems = installed.length > 0
|
|
443
|
+
? [
|
|
444
|
+
{ label: "Global — install into the user config of detected CLIs (user-wide)" },
|
|
445
|
+
{ label: "Project — install into a specific project directory" },
|
|
446
|
+
]
|
|
447
|
+
: [
|
|
448
|
+
{ label: "Project — install into a specific project directory" },
|
|
449
|
+
{ label: "Global — install into the user config of detected CLIs" },
|
|
450
|
+
];
|
|
451
|
+
const idx = await selectMenu("Where do you want to install?", scopeItems);
|
|
452
|
+
scope = scopeItems[idx].label.startsWith("Global") ? "global" : "project";
|
|
250
453
|
} else {
|
|
251
|
-
//
|
|
252
|
-
scope = "project";
|
|
454
|
+
scope = "project"; // non-interactive default
|
|
253
455
|
}
|
|
254
456
|
|
|
255
457
|
// 3. Target
|
|
@@ -269,7 +471,7 @@ Examples:
|
|
|
269
471
|
log("→", `Target: ${target}`);
|
|
270
472
|
console.log("");
|
|
271
473
|
|
|
272
|
-
// 4. Which adapters
|
|
474
|
+
// 4. Which adapters — checkbox multi-select (interactive) or flags/default.
|
|
273
475
|
let chosen;
|
|
274
476
|
if (toolList) {
|
|
275
477
|
const want = toolList.split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -277,52 +479,86 @@ Examples:
|
|
|
277
479
|
} else if (installAll) {
|
|
278
480
|
chosen = scope === "global" && installed.length ? installed : ADAPTERS;
|
|
279
481
|
} else if (interactive) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
chosen = [];
|
|
290
|
-
for (const a of ADAPTERS) {
|
|
291
|
-
if (await askYesNo(rl, ` Install ${a.name} adapter?`, false)) chosen.push(a);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
482
|
+
// Build checkbox items. Detected CLIs are pre-checked; others unchecked.
|
|
483
|
+
const detectedKeys = new Set(installed.map((a) => a.key));
|
|
484
|
+
const items = ADAPTERS.map((a) => ({
|
|
485
|
+
label: a.name,
|
|
486
|
+
hint: detectedKeys.has(a.key) ? "detected" : (existsSync(join(PKG_ROOT, a.dir)) ? "available" : "not shipped yet"),
|
|
487
|
+
checked: detectedKeys.has(a.key),
|
|
488
|
+
}));
|
|
489
|
+
const pickedIdx = await checkboxMenu("Select which CLI adapters to install:", items);
|
|
490
|
+
chosen = pickedIdx.map((i) => ADAPTERS[i]);
|
|
294
491
|
} else {
|
|
295
|
-
// Non-interactive default: all detected CLIs (global) or all adapters (project).
|
|
296
492
|
chosen = scope === "global" && installed.length ? installed : ADAPTERS;
|
|
297
493
|
}
|
|
298
494
|
|
|
495
|
+
// 4b. For plugin-capable adapters, ask: install as plugin (show commands) or copy files?
|
|
496
|
+
// npx cannot run a CLI's internal /plugin command, so "plugin" means: print the
|
|
497
|
+
// exact commands for the user to run inside their CLI, and skip file copy.
|
|
498
|
+
const installMethod = new Map(); // adapter.key -> "plugin" | "files"
|
|
499
|
+
const pluginCapable = chosen.filter((a) => a.pluginCapable);
|
|
500
|
+
if (interactive && pluginCapable.length > 0) {
|
|
501
|
+
console.log("");
|
|
502
|
+
for (const a of pluginCapable) {
|
|
503
|
+
const idx = await selectMenu(
|
|
504
|
+
`${a.name} supports plugins (auto-updates, namespaced). How to install?`,
|
|
505
|
+
[
|
|
506
|
+
{ label: `Plugin — I'll run the /plugin commands in ${a.name} (recommended)` },
|
|
507
|
+
{ label: "Files — copy the adapter files directly (no auto-updates)" },
|
|
508
|
+
],
|
|
509
|
+
);
|
|
510
|
+
installMethod.set(a.key, idx === 0 ? "plugin" : "files");
|
|
511
|
+
}
|
|
512
|
+
} else {
|
|
513
|
+
for (const a of chosen) installMethod.set(a.key, "files");
|
|
514
|
+
}
|
|
515
|
+
|
|
299
516
|
// 5. Init .cto/ state
|
|
300
517
|
let initCto;
|
|
301
518
|
if (scope !== "project") {
|
|
302
519
|
initCto = false;
|
|
303
520
|
} else if (interactive) {
|
|
304
|
-
|
|
521
|
+
const idx = await selectMenu("Initialize .cto/ state files in the project?", [
|
|
522
|
+
{ label: "Yes — create .cto/ state files" },
|
|
523
|
+
{ label: "No — skip state init" },
|
|
524
|
+
]);
|
|
525
|
+
initCto = idx === 0;
|
|
305
526
|
} else {
|
|
306
|
-
initCto = true;
|
|
527
|
+
initCto = true;
|
|
307
528
|
}
|
|
308
|
-
|
|
309
|
-
if (rl) rl.close();
|
|
310
529
|
console.log("");
|
|
311
530
|
console.log(" ── Installing ──────────────────────────────────────────────");
|
|
312
531
|
|
|
313
532
|
installConstitution(target, force);
|
|
314
533
|
if (scope === "project") installSrc(target, force);
|
|
315
|
-
for (const a of chosen)
|
|
534
|
+
for (const a of chosen) {
|
|
535
|
+
if (installMethod.get(a.key) === "plugin") {
|
|
536
|
+
log("⏭", `${a.name}: skipped file copy — install via plugin commands below`);
|
|
537
|
+
} else {
|
|
538
|
+
installAdapter(a, scope, force);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
316
541
|
if (initCto) initState(target, force);
|
|
317
542
|
|
|
318
543
|
console.log("");
|
|
319
544
|
console.log(" ✅ Done.");
|
|
320
545
|
console.log("");
|
|
321
546
|
// Per-CLI next steps: each tool has a different plugin model.
|
|
322
|
-
console.log(" Next steps per
|
|
547
|
+
console.log(" Next steps per selected CLI:");
|
|
323
548
|
for (const a of chosen) {
|
|
324
549
|
console.log(` ── ${a.name} ──`);
|
|
325
|
-
|
|
550
|
+
if (installMethod.get(a.key) === "plugin") {
|
|
551
|
+
console.log(` Install as a plugin (run inside ${a.name}):`);
|
|
552
|
+
for (const c of (a.pluginCmds || [])) console.log(` ${c}`);
|
|
553
|
+
} else if (a.key === "codex") {
|
|
554
|
+
console.log(` Codex plugin (.codex-plugin/) copied — Codex auto-detects it.`);
|
|
555
|
+
} else if (a.key === "opencode") {
|
|
556
|
+
console.log(` OpenCode agents/rules copied (OpenCode plugins are JS hooks, separate).`);
|
|
557
|
+
} else if (a.key === "cursor") {
|
|
558
|
+
console.log(` Cursor plugin (.cursor-plugin/) copied — Cursor auto-detects it.`);
|
|
559
|
+
} else {
|
|
560
|
+
console.log(` Adapter files copied into ${scope === "global" ? a.globalDir() : "the project"}.`);
|
|
561
|
+
}
|
|
326
562
|
console.log("");
|
|
327
563
|
}
|
|
328
564
|
console.log(" Then start the CTO's daily loop:");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cto-agent-system",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "An autonomous software company: CEO (you) + CTO/CPO/CMO leading 15 specialist agents. Run /cto and the CTO takes over — digests the project, fixes fires, improves the product, reports back with a roadmap.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|