kaizen-agent 0.1.1 → 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 CHANGED
@@ -29,9 +29,13 @@ 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, as an arrow-key
33
+ choice; piped or scripted runs take the global install without asking.
34
+
32
35
  | Flag | Effect |
33
36
  |---|---|
34
- | *(none)* | Install for every project, into `~/.claude/` |
37
+ | *(none)* | Ask, defaulting to every project (`~/.claude/`) |
38
+ | `--global` | Every project, no question asked |
35
39
  | `--project` | Install into `.claude/` in the current directory only |
36
40
  | `--check` | Report what is linked and exit non-zero if anything is missing |
37
41
  | `--force` | Replace a real file sitting where a link belongs |
package/cli/install.ts CHANGED
@@ -60,8 +60,77 @@ function linkState(src: string, dest: string) {
60
60
  const args = new Set(Bun.argv.slice(2));
61
61
  const check = args.has("--check");
62
62
  const force = args.has("--force");
63
- const root = args.has("--project") ? process.cwd() : home;
64
- const base = join(root, ".claude");
63
+ const base = join(await chooseRoot(), ".claude");
64
+
65
+ async function chooseRoot() {
66
+ if (args.has("--project")) return process.cwd();
67
+ if (args.has("--global") || check) return home;
68
+
69
+ // Only ask when there is someone to answer. Piped, scripted, or CI runs take the
70
+ // global install silently rather than hanging on a prompt nobody sees.
71
+ if (!process.stdin.isTTY) return home;
72
+
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;
133
+ }
65
134
 
66
135
  let bad = 0;
67
136
  for (const [src, sub] of links) {
@@ -91,4 +160,5 @@ if (check) {
91
160
  process.exit(bad ? 1 : 0);
92
161
  }
93
162
  console.log(`\nInstalled into ${base}. Restart Claude Code: skills load live, slash commands only on session start.`);
163
+ console.log(`Then, in a repository you want to use it on: /kaizen-init`);
94
164
  if (bad) process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kaizen-agent",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "Staged AI development workflow: plan, human approval, implement, independent review, bounded fix loop.",
5
5
  "type": "module",
6
6
  "bin": {