cto-agent-system 1.1.0 → 1.2.0
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.
|
|
12
|
+
"version": "1.2.0",
|
|
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.
|
|
4
|
+
"version": "1.2.0",
|
|
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.
|
|
3
|
+
"version": "1.2.0",
|
|
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.
|
|
5
|
+
"version": "1.2.0",
|
|
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.
|
|
4
|
+
"version": "1.2.0",
|
|
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/).
|
|
@@ -100,6 +101,174 @@ async function askYesNo(rl, question, defaultYes = true) {
|
|
|
100
101
|
return answer === "y" || answer === "yes";
|
|
101
102
|
}
|
|
102
103
|
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// Interactive multi-select (arrow keys + space to toggle + enter to confirm)
|
|
106
|
+
// Zero-dependency: drives raw TTY mode directly. Falls back to a numbered
|
|
107
|
+
// prompt when stdin is not a TTY (piped/CI).
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
const ESC = "\x1b[";
|
|
111
|
+
|
|
112
|
+
function hideCursor() { stdout.write(`${ESC}?25l`); }
|
|
113
|
+
function showCursor() { stdout.write(`${ESC}?25h`); }
|
|
114
|
+
function clearMenu(lines) { stdout.write(`${ESC}${lines}A${ESC}J`); } // move up, clear down
|
|
115
|
+
function writeLine(s) { stdout.write(`${s}\r\n`); }
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Render a multi-select menu.
|
|
119
|
+
* @param {string} title Prompt header.
|
|
120
|
+
* @param {{label:string, hint?:string, checked:boolean}[]} items
|
|
121
|
+
* @param {boolean} allowToggle If false, behaves like a single-select.
|
|
122
|
+
* @returns {Promise<number[]>} Indices of checked items (on confirm).
|
|
123
|
+
*/
|
|
124
|
+
function checkboxMenu(title, items, { allowToggle = true } = {}) {
|
|
125
|
+
return new Promise((resolvePromise) => {
|
|
126
|
+
// Non-TTY fallback: numbered prompt.
|
|
127
|
+
const isTTY = stdin.isTTY && stdin instanceof ReadStream;
|
|
128
|
+
if (!isTTY) {
|
|
129
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
130
|
+
const fallback = async () => {
|
|
131
|
+
console.log(title);
|
|
132
|
+
items.forEach((it, i) => {
|
|
133
|
+
const mark = it.checked ? "[x]" : "[ ]";
|
|
134
|
+
console.log(` ${i + 1}. ${mark} ${it.label}${it.hint ? ` — ${it.hint}` : ""}`);
|
|
135
|
+
});
|
|
136
|
+
const ans = (await rl.question(
|
|
137
|
+
`Enter numbers comma-separated (e.g. 1,3) or 'all': `
|
|
138
|
+
)).trim().toLowerCase();
|
|
139
|
+
rl.close();
|
|
140
|
+
if (ans === "all" || ans === "") return items.map((_, i) => i);
|
|
141
|
+
const picked = ans.split(/[,\s]+/).map((n) => parseInt(n, 10) - 1)
|
|
142
|
+
.filter((n) => n >= 0 && n < items.length);
|
|
143
|
+
return picked;
|
|
144
|
+
};
|
|
145
|
+
fallback().then(resolvePromise);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let cursor = 0;
|
|
150
|
+
const state = items.map((it) => !!it.checked);
|
|
151
|
+
const headerLines = title.split("\n").length + 1; // title + blank
|
|
152
|
+
|
|
153
|
+
const render = () => {
|
|
154
|
+
writeLine(title);
|
|
155
|
+
writeLine(" (↑/↓ move, space toggle, a all, enter confirm)");
|
|
156
|
+
items.forEach((it, i) => {
|
|
157
|
+
const arrow = i === cursor ? "❯" : " ";
|
|
158
|
+
const box = state[i] ? "◉" : "◯";
|
|
159
|
+
const hint = it.hint ? ` ${it.hint}` : "";
|
|
160
|
+
writeLine(` ${arrow} ${box} ${it.label}${hint}`);
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// Render once, then capture how many lines we wrote so we can clear on re-render.
|
|
165
|
+
let lastLines = 0;
|
|
166
|
+
const paint = () => {
|
|
167
|
+
if (lastLines > 0) clearMenu(lastLines);
|
|
168
|
+
const before = stdout.rows; // unused; we count lines instead
|
|
169
|
+
const lineCount = headerLines + 1 + items.length; // title(=headerLines) + hint + items
|
|
170
|
+
render();
|
|
171
|
+
lastLines = lineCount;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
stdin.setRawMode(true);
|
|
175
|
+
stdin.resume();
|
|
176
|
+
stdin.setEncoding("utf8");
|
|
177
|
+
hideCursor();
|
|
178
|
+
paint();
|
|
179
|
+
|
|
180
|
+
const onData = (ch) => {
|
|
181
|
+
// Ctrl-C
|
|
182
|
+
if (ch === "\x03") { cleanup(); process.exit(0); }
|
|
183
|
+
// Enter
|
|
184
|
+
if (ch === "\r" || ch === "\n") {
|
|
185
|
+
cleanup();
|
|
186
|
+
const result = items.map((_, i) => i).filter((i) => state[i]);
|
|
187
|
+
resolvePromise(result);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
// 'a' = toggle all
|
|
191
|
+
if (ch === "a" || ch === "A") {
|
|
192
|
+
const allOn = state.every(Boolean);
|
|
193
|
+
for (let i = 0; i < state.length; i++) state[i] = !allOn;
|
|
194
|
+
paint();
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
// space = toggle current
|
|
198
|
+
if (ch === " ") {
|
|
199
|
+
if (allowToggle) { state[cursor] = !state[cursor]; paint(); }
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
// arrows
|
|
203
|
+
if (ch === `${ESC}A`) { cursor = (cursor - 1 + items.length) % items.length; paint(); return; } // up
|
|
204
|
+
if (ch === `${ESC}B`) { cursor = (cursor + 1) % items.length; paint(); return; } // down
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const cleanup = () => {
|
|
208
|
+
stdin.removeListener("data", onData);
|
|
209
|
+
stdin.setRawMode(false);
|
|
210
|
+
stdin.pause();
|
|
211
|
+
showCursor();
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
stdin.on("data", onData);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Single-select menu (arrows + enter). Returns chosen index.
|
|
220
|
+
*/
|
|
221
|
+
function selectMenu(title, items) {
|
|
222
|
+
return new Promise((resolvePromise) => {
|
|
223
|
+
const isTTY = stdin.isTTY && stdin instanceof ReadStream;
|
|
224
|
+
if (!isTTY) {
|
|
225
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
226
|
+
const fallback = async () => {
|
|
227
|
+
console.log(title);
|
|
228
|
+
items.forEach((it, i) => console.log(` ${i + 1}. ${it.label}`));
|
|
229
|
+
const ans = parseInt((await rl.question("Choice (number): ")).trim(), 10);
|
|
230
|
+
rl.close();
|
|
231
|
+
return Number.isNaN(ans) ? 0 : Math.max(0, Math.min(items.length - 1, ans - 1));
|
|
232
|
+
};
|
|
233
|
+
fallback().then(resolvePromise);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
let cursor = 0;
|
|
238
|
+
let lastLines = 0;
|
|
239
|
+
const paint = () => {
|
|
240
|
+
if (lastLines > 0) clearMenu(lastLines);
|
|
241
|
+
writeLine(title);
|
|
242
|
+
writeLine(" (↑/↓ move, enter select)");
|
|
243
|
+
items.forEach((it, i) => {
|
|
244
|
+
const arrow = i === cursor ? "❯" : " ";
|
|
245
|
+
writeLine(` ${arrow} ${it.label}`);
|
|
246
|
+
});
|
|
247
|
+
lastLines = 1 + 1 + items.length;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
stdin.setRawMode(true);
|
|
251
|
+
stdin.resume();
|
|
252
|
+
stdin.setEncoding("utf8");
|
|
253
|
+
hideCursor();
|
|
254
|
+
paint();
|
|
255
|
+
|
|
256
|
+
const onData = (ch) => {
|
|
257
|
+
if (ch === "\x03") { cleanup(); process.exit(0); }
|
|
258
|
+
if (ch === "\r" || ch === "\n") { cleanup(); resolvePromise(cursor); return; }
|
|
259
|
+
if (ch === `${ESC}A`) { cursor = (cursor - 1 + items.length) % items.length; paint(); return; }
|
|
260
|
+
if (ch === `${ESC}B`) { cursor = (cursor + 1) % items.length; paint(); return; }
|
|
261
|
+
};
|
|
262
|
+
const cleanup = () => {
|
|
263
|
+
stdin.removeListener("data", onData);
|
|
264
|
+
stdin.setRawMode(false);
|
|
265
|
+
stdin.pause();
|
|
266
|
+
showCursor();
|
|
267
|
+
};
|
|
268
|
+
stdin.on("data", onData);
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
103
272
|
// ---------------------------------------------------------------------------
|
|
104
273
|
// File helpers
|
|
105
274
|
// ---------------------------------------------------------------------------
|
|
@@ -236,20 +405,25 @@ Examples:
|
|
|
236
405
|
console.log("");
|
|
237
406
|
|
|
238
407
|
const interactive = !yes && stdin.isTTY;
|
|
239
|
-
const rl = interactive ? createInterface({ input: stdin, output: stdout }) : null;
|
|
240
408
|
|
|
241
|
-
// 2. Scope
|
|
409
|
+
// 2. Scope — arrow-key single select (interactive) or flags/default.
|
|
242
410
|
let scope;
|
|
243
411
|
if (goGlobal) scope = "global";
|
|
244
412
|
else if (goProject) scope = "project";
|
|
245
413
|
else if (interactive) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
414
|
+
const scopeItems = installed.length > 0
|
|
415
|
+
? [
|
|
416
|
+
{ label: "Global — install into the user config of detected CLIs (user-wide)" },
|
|
417
|
+
{ label: "Project — install into a specific project directory" },
|
|
418
|
+
]
|
|
419
|
+
: [
|
|
420
|
+
{ label: "Project — install into a specific project directory" },
|
|
421
|
+
{ label: "Global — install into the user config of detected CLIs" },
|
|
422
|
+
];
|
|
423
|
+
const idx = await selectMenu("Where do you want to install?", scopeItems);
|
|
424
|
+
scope = scopeItems[idx].label.startsWith("Global") ? "global" : "project";
|
|
250
425
|
} else {
|
|
251
|
-
//
|
|
252
|
-
scope = "project";
|
|
426
|
+
scope = "project"; // non-interactive default
|
|
253
427
|
}
|
|
254
428
|
|
|
255
429
|
// 3. Target
|
|
@@ -269,7 +443,7 @@ Examples:
|
|
|
269
443
|
log("→", `Target: ${target}`);
|
|
270
444
|
console.log("");
|
|
271
445
|
|
|
272
|
-
// 4. Which adapters
|
|
446
|
+
// 4. Which adapters — checkbox multi-select (interactive) or flags/default.
|
|
273
447
|
let chosen;
|
|
274
448
|
if (toolList) {
|
|
275
449
|
const want = toolList.split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -277,22 +451,16 @@ Examples:
|
|
|
277
451
|
} else if (installAll) {
|
|
278
452
|
chosen = scope === "global" && installed.length ? installed : ADAPTERS;
|
|
279
453
|
} 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
|
-
}
|
|
454
|
+
// Build checkbox items. Detected CLIs are pre-checked; others unchecked.
|
|
455
|
+
const detectedKeys = new Set(installed.map((a) => a.key));
|
|
456
|
+
const items = ADAPTERS.map((a) => ({
|
|
457
|
+
label: a.name,
|
|
458
|
+
hint: detectedKeys.has(a.key) ? "detected" : (existsSync(join(PKG_ROOT, a.dir)) ? "available" : "not shipped yet"),
|
|
459
|
+
checked: detectedKeys.has(a.key),
|
|
460
|
+
}));
|
|
461
|
+
const pickedIdx = await checkboxMenu("Select which CLI adapters to install:", items);
|
|
462
|
+
chosen = pickedIdx.map((i) => ADAPTERS[i]);
|
|
294
463
|
} else {
|
|
295
|
-
// Non-interactive default: all detected CLIs (global) or all adapters (project).
|
|
296
464
|
chosen = scope === "global" && installed.length ? installed : ADAPTERS;
|
|
297
465
|
}
|
|
298
466
|
|
|
@@ -301,12 +469,14 @@ Examples:
|
|
|
301
469
|
if (scope !== "project") {
|
|
302
470
|
initCto = false;
|
|
303
471
|
} else if (interactive) {
|
|
304
|
-
|
|
472
|
+
const idx = await selectMenu("Initialize .cto/ state files in the project?", [
|
|
473
|
+
{ label: "Yes — create .cto/ state files" },
|
|
474
|
+
{ label: "No — skip state init" },
|
|
475
|
+
]);
|
|
476
|
+
initCto = idx === 0;
|
|
305
477
|
} else {
|
|
306
|
-
initCto = true;
|
|
478
|
+
initCto = true;
|
|
307
479
|
}
|
|
308
|
-
|
|
309
|
-
if (rl) rl.close();
|
|
310
480
|
console.log("");
|
|
311
481
|
console.log(" ── Installing ──────────────────────────────────────────────");
|
|
312
482
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cto-agent-system",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
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": {
|