kaizen-agent 0.2.0 → 0.3.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.
- package/README.md +2 -2
- package/cli/install.ts +60 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,8 +29,8 @@ cd ~/kaizen && bun run cli/install.ts
|
|
|
29
29
|
|
|
30
30
|
Flags, either way:
|
|
31
31
|
|
|
32
|
-
Run interactively with no flags and it asks where to install
|
|
33
|
-
runs take the global install without asking.
|
|
32
|
+
Run interactively with no flags and it asks where to install, as an arrow-key
|
|
33
|
+
choice; piped or scripted runs take the global install without asking.
|
|
34
34
|
|
|
35
35
|
| Flag | Effect |
|
|
36
36
|
|---|---|
|
package/cli/install.ts
CHANGED
|
@@ -70,10 +70,66 @@ async function chooseRoot() {
|
|
|
70
70
|
// global install silently rather than hanging on a prompt nobody sees.
|
|
71
71
|
if (!process.stdin.isTTY) return home;
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
const choice = await select("Install where?", [
|
|
74
|
+
{ label: "Every project", hint: join(home, ".claude"), value: home },
|
|
75
|
+
{ label: "This one only", hint: join(process.cwd(), ".claude"), value: process.cwd() },
|
|
76
|
+
]);
|
|
77
|
+
return choice;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Arrow-key picker. Raw mode delivers keys unbuffered, one escape sequence at a
|
|
81
|
+
// time; the alternative is a dependency for forty lines of escape codes.
|
|
82
|
+
async function select<T>(question: string, options: { label: string; hint: string; value: T }[]) {
|
|
83
|
+
const { stdin, stdout } = process;
|
|
84
|
+
let active = 0;
|
|
85
|
+
|
|
86
|
+
const draw = (first: boolean) => {
|
|
87
|
+
if (!first) stdout.write(`\x1b[${options.length}A`); // back up over the list
|
|
88
|
+
for (const [i, o] of options.entries()) {
|
|
89
|
+
const on = i === active;
|
|
90
|
+
stdout.write(
|
|
91
|
+
`\x1b[2K ${on ? "\x1b[36m>\x1b[0m" : " "} ${on ? "\x1b[1m" : "\x1b[2m"}${o.label}\x1b[0m` +
|
|
92
|
+
` \x1b[2m${o.hint}\x1b[0m\n`,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
stdout.write(`\n\x1b[1m${question}\x1b[0m \x1b[2m(arrows, enter)\x1b[0m\n`);
|
|
98
|
+
stdout.write("\x1b[?25l"); // hide cursor
|
|
99
|
+
draw(true);
|
|
100
|
+
stdin.setRawMode(true);
|
|
101
|
+
stdin.resume();
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
outer: for await (const chunk of stdin) {
|
|
105
|
+
// A chunk can hold more than one keypress -- an arrow and the enter behind
|
|
106
|
+
// it arrive together when input is piped -- so walk it rather than
|
|
107
|
+
// comparing the whole thing to one key.
|
|
108
|
+
const keys = chunk.toString();
|
|
109
|
+
for (let i = 0; i < keys.length; i++) {
|
|
110
|
+
const rest = keys.slice(i);
|
|
111
|
+
if (rest.startsWith("\x03")) { // ctrl-c
|
|
112
|
+
stdout.write("\ncancelled\n");
|
|
113
|
+
process.exit(130);
|
|
114
|
+
}
|
|
115
|
+
if (rest.startsWith("\r") || rest.startsWith("\n")) break outer;
|
|
116
|
+
if (rest.startsWith("\x1b[A") || rest.startsWith("\x1b[B")) {
|
|
117
|
+
active = rest[2] === "A"
|
|
118
|
+
? (active - 1 + options.length) % options.length
|
|
119
|
+
: (active + 1) % options.length;
|
|
120
|
+
i += 2;
|
|
121
|
+
} else if (rest.startsWith("k")) active = (active - 1 + options.length) % options.length;
|
|
122
|
+
else if (rest.startsWith("j")) active = (active + 1) % options.length;
|
|
123
|
+
else continue;
|
|
124
|
+
draw(false);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
} finally {
|
|
128
|
+
stdin.setRawMode(false);
|
|
129
|
+
stdin.pause();
|
|
130
|
+
stdout.write("\x1b[?25h\n"); // show cursor again
|
|
131
|
+
}
|
|
132
|
+
return options[active]!.value;
|
|
77
133
|
}
|
|
78
134
|
|
|
79
135
|
let bad = 0;
|