kaizen-agent 0.5.1 → 0.7.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 +8 -3
- package/cli/install.ts +108 -25
- package/cli/release.sh +34 -0
- package/commands/kaizen-init.md +1 -1
- package/package.json +3 -2
- package/skills/kaizen/SKILL.md +11 -8
- package/skills/kaizen/adapters.md +3 -2
- package/skills/kaizen/config.default.yml +17 -0
- package/skills/kaizen/spec-build.md +3 -0
- package/skills/kaizen-help/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -239,9 +239,14 @@ Agent Skills standard, so the same workflow runs under any of them:
|
|
|
239
239
|
| Antigravity | Skills | Parallel agents where available, else sequentially |
|
|
240
240
|
| OpenCode, Cursor, Gemini CLI | Skills | Sequentially, one session |
|
|
241
241
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
agent and it picks
|
|
242
|
+
An agent counts as present if it has a config directory or a command on your PATH.
|
|
243
|
+
Only what is already there is touched — installing does not create `~/.opencode` for
|
|
244
|
+
someone who does not use OpenCode. Install again after adding an agent and it picks
|
|
245
|
+
the new one up.
|
|
246
|
+
|
|
247
|
+
**No agent yet?** kaizen does nothing on its own — it is a workflow an agent runs.
|
|
248
|
+
If none is found, the installer says so and offers the list above, opening the
|
|
249
|
+
install page for whichever you pick. Install it, run `kaizen` again, and carry on.
|
|
245
250
|
|
|
246
251
|
The stage agents and slash commands are Claude Code's own formats and are not copied
|
|
247
252
|
elsewhere; under the others you invoke the skill by name. `.kaizen/` is plain files
|
package/cli/install.ts
CHANGED
|
@@ -27,18 +27,75 @@ const c = {
|
|
|
27
27
|
// Skills standard, so every one of these can run the workflow; agents/ and
|
|
28
28
|
// commands/ are Claude Code's own formats and go nowhere else.
|
|
29
29
|
const AGENTS = [
|
|
30
|
-
{ name: "Claude Code", dir: ".claude", full: true
|
|
31
|
-
|
|
32
|
-
{ name: "
|
|
33
|
-
|
|
34
|
-
{ name: "
|
|
35
|
-
|
|
30
|
+
{ name: "Claude Code", dir: ".claude", cmd: "claude", full: true,
|
|
31
|
+
docs: "https://docs.claude.com/en/docs/claude-code" },
|
|
32
|
+
{ name: "Codex", dir: ".codex", cmd: "codex", full: false,
|
|
33
|
+
docs: "https://github.com/openai/codex" },
|
|
34
|
+
{ name: "Antigravity", dir: ".agents", cmd: "agy", full: false,
|
|
35
|
+
docs: "https://antigravity.google" },
|
|
36
|
+
{ name: "OpenCode", dir: ".opencode", cmd: "opencode", full: false,
|
|
37
|
+
docs: "https://opencode.ai" },
|
|
38
|
+
{ name: "Cursor", dir: ".cursor", cmd: "cursor", full: false,
|
|
39
|
+
docs: "https://cursor.com" },
|
|
40
|
+
{ name: "Gemini CLI", dir: ".gemini", cmd: "gemini", full: false,
|
|
41
|
+
docs: "https://github.com/google-gemini/gemini-cli" },
|
|
36
42
|
];
|
|
37
43
|
|
|
38
|
-
//
|
|
39
|
-
// who
|
|
40
|
-
//
|
|
41
|
-
const
|
|
44
|
+
// An agent counts as present if it has a directory or a command. The directory
|
|
45
|
+
// alone misses someone who installed the agent and has not run it yet; the command
|
|
46
|
+
// alone misses one that is not on this shell's PATH.
|
|
47
|
+
const onPath = new Set(
|
|
48
|
+
AGENTS.filter((a) => Bun.which(a.cmd)).map((a) => a.dir),
|
|
49
|
+
);
|
|
50
|
+
const found = (r: string) =>
|
|
51
|
+
AGENTS.filter((a) => existsSync(join(r, a.dir)) || onPath.has(a.dir));
|
|
52
|
+
|
|
53
|
+
// Nothing to install into. kaizen is a workflow an agent runs; on its own it does
|
|
54
|
+
// nothing, so send them to the agent first rather than laying out files no reader
|
|
55
|
+
// will ever load.
|
|
56
|
+
if (!found(home).length && !args.has("--force")) {
|
|
57
|
+
await noAgent();
|
|
58
|
+
process.exit(0);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function noAgent() {
|
|
62
|
+
console.log(` ${c.bold("No AI coding agent found on this machine.")}\n`);
|
|
63
|
+
console.log(` ${c.dim("kaizen is a workflow that an agent runs — it plans, you approve, it builds,")}`);
|
|
64
|
+
console.log(` ${c.dim("and a second agent reviews. So an agent has to be installed first.")}\n`);
|
|
65
|
+
|
|
66
|
+
if (!interactive) {
|
|
67
|
+
for (const a of AGENTS) console.log(` ${a.name.padEnd(14)} ${a.docs}`);
|
|
68
|
+
console.log(`\n ${c.dim("Install one, then run this again.")}`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const pick = await select("Which one would you like to install?", [
|
|
73
|
+
...AGENTS.map((a) => ({ label: a.name, hint: a.docs, value: a as (typeof AGENTS)[number] | null })),
|
|
74
|
+
{ label: "None of these — just show me the list", hint: "", value: null },
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
if (!pick) {
|
|
78
|
+
for (const a of AGENTS) console.log(` ${a.name.padEnd(14)} ${a.docs}`);
|
|
79
|
+
console.log(`\n ${c.dim("Install one, then run this again.")}`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log(` ${c.bold(pick.name)}`);
|
|
84
|
+
console.log(` ${c.cyan(pick.docs)}\n`);
|
|
85
|
+
const opened = await openUrl(pick.docs);
|
|
86
|
+
console.log(opened
|
|
87
|
+
? ` ${c.dim("Opened in your browser. Install it, then run")} ${c.cyan("kaizen")} ${c.dim("again.")}`
|
|
88
|
+
: ` ${c.dim("Open that page to install it, then run")} ${c.cyan("kaizen")} ${c.dim("again.")}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Best effort: a headless box or a locked-down desktop has no opener, and the URL
|
|
92
|
+
// is printed either way.
|
|
93
|
+
async function openUrl(url: string) {
|
|
94
|
+
const opener = process.platform === "darwin" ? "open"
|
|
95
|
+
: process.platform === "win32" ? "start" : "xdg-open";
|
|
96
|
+
if (!Bun.which(opener)) return false;
|
|
97
|
+
try { await Bun.$`${opener} ${url}`.quiet(); return true; } catch { return false; }
|
|
98
|
+
}
|
|
42
99
|
|
|
43
100
|
if (wantSettings) {
|
|
44
101
|
const { settings } = await import("./settings.ts");
|
|
@@ -52,7 +109,7 @@ if (wantSettings) {
|
|
|
52
109
|
if (interactive && !upgrade && Bun.argv.length === 2) {
|
|
53
110
|
const repoDir = await resolveRepoQuietly();
|
|
54
111
|
const here = found(home);
|
|
55
|
-
const linked = here.every((a) =>
|
|
112
|
+
const linked = here.length > 0 && here.every((a) =>
|
|
56
113
|
["kaizen", "kaizen-help"].every((n) => existsSync(join(home, a.dir, "skills", n))));
|
|
57
114
|
if (linked) {
|
|
58
115
|
const { dashboard } = await import("./dashboard.ts");
|
|
@@ -230,10 +287,10 @@ async function chooseRoot() {
|
|
|
230
287
|
if (args.has("--project")) return process.cwd();
|
|
231
288
|
if (args.has("--global") || !interactive) return home;
|
|
232
289
|
// A project install only means something in a project. Standing in the home
|
|
233
|
-
// directory it is the global install under another name
|
|
234
|
-
//
|
|
290
|
+
// directory it is the global install under another name; any other folder is
|
|
291
|
+
// a project worth installing into, git repository or not.
|
|
235
292
|
const cwd = process.cwd();
|
|
236
|
-
if (cwd === home
|
|
293
|
+
if (cwd === home) return home;
|
|
237
294
|
|
|
238
295
|
const names = (r: string) => found(r).map((a) => a.name).join(", ");
|
|
239
296
|
return select("Install kaizen for", [
|
|
@@ -333,22 +390,43 @@ function installLauncher() {
|
|
|
333
390
|
}
|
|
334
391
|
}
|
|
335
392
|
|
|
336
|
-
const
|
|
393
|
+
const inProject = process.cwd() !== home;
|
|
337
394
|
const kaizenDir = join(process.cwd(), ".kaizen");
|
|
338
395
|
|
|
339
|
-
|
|
396
|
+
// A .kaizen/ in a parent already owns this folder's state, and setting up a child
|
|
397
|
+
// would shadow it, hiding the parent's runs and leaving the child out of the root
|
|
398
|
+
// .gitignore. Deliberately not shared with cli/dashboard.ts locate(): that one
|
|
399
|
+
// resolves which state directory to read and so falls back to a global ~/.kaizen,
|
|
400
|
+
// while this one decides whether a folder is already owned and must stop *before*
|
|
401
|
+
// $HOME — a global ~/.kaizen would otherwise suppress the setup offer for every
|
|
402
|
+
// non-repo folder under the home directory. Do not re-sync the two walks.
|
|
403
|
+
function kaizenAbove() {
|
|
404
|
+
let dir = process.cwd();
|
|
405
|
+
for (;;) {
|
|
406
|
+
if (dir === home) return false;
|
|
407
|
+
if (existsSync(join(dir, ".kaizen"))) return true;
|
|
408
|
+
if (existsSync(join(dir, ".git"))) return false;
|
|
409
|
+
const up = dirname(dir);
|
|
410
|
+
if (up === dir) return false;
|
|
411
|
+
dir = up;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (inProject && !kaizenAbove()) {
|
|
340
416
|
const now = interactive
|
|
341
417
|
? await select(`Set up ${basename(process.cwd())}/ for kaizen now`, [
|
|
342
|
-
|
|
418
|
+
// "Not now" leads: the offer now appears in any folder outside home, so the
|
|
419
|
+
// safe answer is the one under the cursor.
|
|
343
420
|
{ label: "Not now", hint: "the first /kaizen run will do it", value: false },
|
|
421
|
+
{ label: "Yes", hint: "writes .kaizen/ and a CLAUDE.md line so it runs by default", value: true },
|
|
344
422
|
])
|
|
345
423
|
: false;
|
|
346
|
-
if (now)
|
|
424
|
+
if (now) initProject();
|
|
347
425
|
} else if (existsSync(kaizenDir)) {
|
|
348
426
|
step(`${basename(process.cwd())}/.kaizen already set up`);
|
|
349
427
|
}
|
|
350
428
|
|
|
351
|
-
function
|
|
429
|
+
function initProject() {
|
|
352
430
|
mkdirSync(kaizenDir, { recursive: true });
|
|
353
431
|
for (const f of readdirSync(join(repo, "skills/kaizen")).filter((f) => f.startsWith("spec"))) {
|
|
354
432
|
copyFileSync(join(repo, "skills/kaizen", f), join(kaizenDir, f));
|
|
@@ -356,13 +434,18 @@ function initRepo() {
|
|
|
356
434
|
copyFileSync(join(repo, "skills/kaizen/config.default.yml"), join(kaizenDir, "config.yml"));
|
|
357
435
|
|
|
358
436
|
// Run state is local; the workflow itself is shared, so those files stay tracked.
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
437
|
+
// Nothing to ignore where there is no git, so a plain folder gets no .gitignore.
|
|
438
|
+
let ignored = false;
|
|
439
|
+
if (existsSync(join(process.cwd(), ".git"))) {
|
|
440
|
+
const rules = ".kaizen/*\n!.kaizen/spec*.md\n!.kaizen/config.yml\n";
|
|
441
|
+
const gi = join(process.cwd(), ".gitignore");
|
|
442
|
+
const current = existsSync(gi) ? readFileSync(gi, "utf8") : "";
|
|
443
|
+
if (!current.includes(".kaizen/*")) {
|
|
444
|
+
appendFileSync(gi, (current && !current.endsWith("\n") ? "\n" : "") + "\n# kaizen run state\n" + rules);
|
|
445
|
+
ignored = true;
|
|
446
|
+
}
|
|
364
447
|
}
|
|
365
|
-
step("wrote .kaizen/ and the gitignore entry");
|
|
448
|
+
step(ignored ? "wrote .kaizen/ and the gitignore entry" : "wrote .kaizen/");
|
|
366
449
|
writePointer();
|
|
367
450
|
}
|
|
368
451
|
|
package/cli/release.sh
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Runs after `npm publish`: tag the published version and open a GitHub release
|
|
3
|
+
# for it, so a version can never exist on npm with nothing to read on GitHub.
|
|
4
|
+
#
|
|
5
|
+
# Never fails the publish. The package is already public by the time this runs;
|
|
6
|
+
# exiting non-zero here would report a failure that did not happen.
|
|
7
|
+
set -u
|
|
8
|
+
v="${npm_package_version:-$(node -p "require('./package.json').version" 2>/dev/null)}"
|
|
9
|
+
[ -n "$v" ] || { echo "release: no version, skipping"; exit 0; }
|
|
10
|
+
tag="v$v"
|
|
11
|
+
|
|
12
|
+
git rev-parse --git-dir >/dev/null 2>&1 || { echo "release: not a git checkout, skipping"; exit 0; }
|
|
13
|
+
|
|
14
|
+
if git rev-parse "$tag" >/dev/null 2>&1; then
|
|
15
|
+
echo "release: $tag already tagged"
|
|
16
|
+
else
|
|
17
|
+
git tag -a "$tag" -m "kaizen-agent $tag" || { echo "release: could not tag"; exit 0; }
|
|
18
|
+
git push origin "$tag" >/dev/null 2>&1 || echo "release: tag made but not pushed"
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
command -v gh >/dev/null 2>&1 || { echo "release: gh not installed — tag pushed, write the release by hand"; exit 0; }
|
|
22
|
+
gh auth status >/dev/null 2>&1 || { echo "release: gh not logged in — tag pushed, write the release by hand"; exit 0; }
|
|
23
|
+
|
|
24
|
+
if gh release view "$tag" >/dev/null 2>&1; then
|
|
25
|
+
echo "release: $tag already released"
|
|
26
|
+
exit 0
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# --generate-notes lists the commits since the last tag. Good enough to never be
|
|
30
|
+
# empty; rewrite it with what actually changed when the release matters.
|
|
31
|
+
gh release create "$tag" --title "$tag" --generate-notes >/dev/null 2>&1 \
|
|
32
|
+
&& echo "release: published $tag https://github.com/hfadhlullah/kaizen/releases/tag/$tag" \
|
|
33
|
+
|| echo "release: tag pushed, release not created"
|
|
34
|
+
exit 0
|
package/commands/kaizen-init.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kaizen-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Staged workflow for AI agents \u2014 code, writing, research, operations: plan, human approval, build, independent review, bounded fix loop.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
],
|
|
18
18
|
"scripts": {
|
|
19
19
|
"install-links": "bun run cli/install.ts",
|
|
20
|
-
"check": "bun run cli/install.ts --check"
|
|
20
|
+
"check": "bun run cli/install.ts --check",
|
|
21
|
+
"postpublish": "sh cli/release.sh"
|
|
21
22
|
},
|
|
22
23
|
"repository": {
|
|
23
24
|
"type": "git",
|
package/skills/kaizen/SKILL.md
CHANGED
|
@@ -47,7 +47,7 @@ Porting the workflow to Codex or Antigravity is covered in [`adapters.md`](adapt
|
|
|
47
47
|
| `/kaizen reject <reason>` | Reject it; the reason is fed back to the stage that produced it |
|
|
48
48
|
| `/kaizen abort` | Mark the current run abandoned |
|
|
49
49
|
| `/kaizen config` | Show this project's settings and change them, one picker per setting |
|
|
50
|
-
| `/kaizen init` |
|
|
50
|
+
| `/kaizen init` | Set up this project folder: `.kaizen/` with the `spec*.md` files, config, and — in a git repo — the gitignore entry |
|
|
51
51
|
| `/kaizen install` | Install the workflow itself globally or per-project (see below) |
|
|
52
52
|
|
|
53
53
|
If the user typed `/kaizen` with no argument and a run is in progress, treat it as
|
|
@@ -55,10 +55,11 @@ If the user typed `/kaizen` with no argument and a run is in progress, treat it
|
|
|
55
55
|
|
|
56
56
|
## Before doing anything
|
|
57
57
|
|
|
58
|
-
1. Locate the state directory: `.kaizen/` in the
|
|
59
|
-
keyed by working directory. If neither exists and the user is starting
|
|
60
|
-
`/kaizen init` first: it creates `.kaizen/` with a copy of the `spec*.md`
|
|
61
|
-
the gitignore entry
|
|
58
|
+
1. Locate the state directory: `.kaizen/` in the project folder's root, else
|
|
59
|
+
`~/.kaizen/` keyed by working directory. If neither exists and the user is starting
|
|
60
|
+
work, run `/kaizen init` first: it creates `.kaizen/` with a copy of the `spec*.md`
|
|
61
|
+
files, a config, and — where the folder is a git repository — the gitignore entry
|
|
62
|
+
(see [`adapters.md`](adapters.md)).
|
|
62
63
|
2. Read `.kaizen/config.yml`, falling back to `config.default.yml` in this skill for
|
|
63
64
|
any key it does not set.
|
|
64
65
|
3. Read `.kaizen/runs/<current>/state.json` if a run is active. Never assume the
|
|
@@ -293,9 +294,11 @@ shown automatically.
|
|
|
293
294
|
}
|
|
294
295
|
```
|
|
295
296
|
|
|
296
|
-
`init` adds a gitignore entry that ignores run state but
|
|
297
|
-
ignores `.kaizen/*` and un-ignores `.kaizen/spec*.md` and
|
|
298
|
-
are meant to be committed (see
|
|
297
|
+
In a git repository, `init` also adds a gitignore entry that ignores run state but
|
|
298
|
+
keeps the workflow: it ignores `.kaizen/*` and un-ignores `.kaizen/spec*.md` and
|
|
299
|
+
`.kaizen/config.yml`, which are meant to be committed (see
|
|
300
|
+
[`adapters.md`](adapters.md)). A project folder that is not a repository gets no
|
|
301
|
+
gitignore.
|
|
299
302
|
|
|
300
303
|
## Install
|
|
301
304
|
|
|
@@ -64,8 +64,9 @@ completes. A stale `state.json` is the one way this breaks.
|
|
|
64
64
|
|
|
65
65
|
## Installing the adapter files
|
|
66
66
|
|
|
67
|
-
`/kaizen init` writes `.kaizen/` with the `spec*.md` files
|
|
68
|
-
|
|
67
|
+
`/kaizen init` writes `.kaizen/` with the `spec*.md` files and `config.yml` into the
|
|
68
|
+
project folder, plus the gitignore entry when that folder is a git repository.
|
|
69
|
+
`/kaizen install` additionally offers to append the `AGENTS.md` block above.
|
|
69
70
|
`.kaizen/spec*.md` and `.kaizen/config.yml` should be committed even though run state
|
|
70
71
|
is ignored — the workflow is shared, the run data is not:
|
|
71
72
|
|
|
@@ -55,12 +55,27 @@ review:
|
|
|
55
55
|
# Additional checks per track, chosen by what the run actually is. The planner may
|
|
56
56
|
# name a track not listed here; the reviewer then derives its checks from the
|
|
57
57
|
# track's "what would make it wrong".
|
|
58
|
+
# Each check below is one question the reviewer asks; the line beside it says which.
|
|
58
59
|
track_checks:
|
|
60
|
+
# security - no new way in, no secret or user data exposed
|
|
61
|
+
# reuse_simplification - nothing rebuilt that the codebase already had
|
|
62
|
+
# test_coverage - the changed behaviour has a check that fails when it breaks
|
|
59
63
|
software: [security, reuse_simplification, test_coverage]
|
|
60
64
|
# blast_radius and regression are universal — see spec-review.md — not listed here.
|
|
65
|
+
# completeness - every part the piece promises is actually present
|
|
66
|
+
# claim_accuracy - every checkable claim traced to a source
|
|
67
|
+
# reader_usability - its intended reader can act on it without asking
|
|
61
68
|
writing: [completeness, claim_accuracy, reader_usability]
|
|
69
|
+
# audience_fit - written for the people who will actually read it
|
|
70
|
+
# voice_consistency - reads as the same voice as the surrounding material
|
|
62
71
|
communication: [claim_accuracy, audience_fit, voice_consistency]
|
|
72
|
+
# prerequisites - everything needed before step one is stated
|
|
73
|
+
# rollback - a way back exists for every step that changes something
|
|
74
|
+
# executability - someone else can run it start to finish unaided
|
|
63
75
|
operations: [prerequisites, rollback, executability]
|
|
76
|
+
# evidence_support - the conclusion follows from evidence shown, not asserted
|
|
77
|
+
# selection_bias - the evidence was not picked to fit the answer
|
|
78
|
+
# alternatives - the readings that were rejected are named and why
|
|
64
79
|
research: [evidence_support, selection_bias, alternatives]
|
|
65
80
|
# Let the reviewer append durable lessons to .kaizen/memory.md.
|
|
66
81
|
write_memory: true
|
|
@@ -90,6 +105,8 @@ state:
|
|
|
90
105
|
|
|
91
106
|
git:
|
|
92
107
|
# Never commit, push, or open a PR unless the user explicitly asks.
|
|
108
|
+
# Ignored when the project is not a git repository.
|
|
93
109
|
auto_commit: false
|
|
94
110
|
# Create a branch before the implement stage when on the default branch.
|
|
111
|
+
# Ignored when the project is not a git repository.
|
|
95
112
|
branch_before_implement: true
|
|
@@ -35,6 +35,9 @@ Rules:
|
|
|
35
35
|
abstraction, a new file, a new section — is allowed, and costs one written line
|
|
36
36
|
saying what it serves. The line is the price of the technique, not a request for
|
|
37
37
|
permission. No line means the reviewer records a finding.
|
|
38
|
+
- The `git.*` settings apply only where the work sits in a git repository. In a plain
|
|
39
|
+
folder there is no branch to make and no commit to skip, and that absence is not a
|
|
40
|
+
finding.
|
|
38
41
|
|
|
39
42
|
**Baseline first, in any track with an existing suite of checks.** Before changing
|
|
40
43
|
anything, run whatever already proves the existing material correct — the test suite,
|
|
@@ -31,7 +31,7 @@ anything.
|
|
|
31
31
|
| `/kaizen reject <reason>` | Reject it; the reason decides revise (back to planner) vs kill (abandon) |
|
|
32
32
|
| `/kaizen abort` | Mark the current run abandoned |
|
|
33
33
|
| `/kaizen config` | Show this project's settings and change them, one picker per setting |
|
|
34
|
-
| `/kaizen init` |
|
|
34
|
+
| `/kaizen init` | Set up this project folder: `.kaizen/` with `spec.md`, config, and — in a git repo — the gitignore entry |
|
|
35
35
|
| `/kaizen install` | Install the workflow itself globally or per-project |
|
|
36
36
|
|
|
37
37
|
`/kaizen` with no argument: `status` if a run is in progress, else ask what to build.
|