kadai 0.1.0 → 0.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.
- package/README.md +105 -5
- package/dist/cli.js +104 -352
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -2,16 +2,116 @@
|
|
|
2
2
|
|
|
3
3
|
# kadai
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
A terminal UI for discovering and running project scripts. Drop scripts into `.kadai/actions/`, and kadai gives you a fuzzy-searchable menu to run them.
|
|
6
|
+
|
|
7
|
+
## Getting Started
|
|
8
|
+
|
|
9
|
+
<img width="950" height="205" alt="image" src="https://github.com/user-attachments/assets/b694bfaa-146b-41c7-a44c-d197c7cea08e" />
|
|
6
10
|
|
|
7
11
|
```bash
|
|
8
|
-
|
|
12
|
+
bunx kadai
|
|
13
|
+
# OR
|
|
14
|
+
npx kadai
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
On first run, kadai creates a `.kadai/` directory with a sample action and config file. Run it again to open the interactive menu.
|
|
18
|
+
|
|
19
|
+
### Directory Structure
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
.kadai/
|
|
23
|
+
├── config.ts # Optional configuration (env vars, actions dir)
|
|
24
|
+
└── actions/ # Your scripts live here
|
|
25
|
+
├── hello.sh
|
|
26
|
+
├── deploy.ts
|
|
27
|
+
└── database/ # Subdirectories become categories
|
|
28
|
+
├── reset.sh
|
|
29
|
+
└── seed.py
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
### Supported Runtimes
|
|
35
|
+
|
|
36
|
+
| Extension | Runtime |
|
|
37
|
+
|--------------------|----------|
|
|
38
|
+
| `.sh`, `.bash` | bash |
|
|
39
|
+
| `.ts`, `.js`, `.mjs` | bun |
|
|
40
|
+
| `.py` | python |
|
|
41
|
+
|
|
42
|
+
Shebangs are respected — if your script has `#!/usr/bin/env python3`, kadai uses that directly. Otherwise it finds the best available interpreter automatically (e.g. `uv run` before `python3` for `.py` files).
|
|
43
|
+
|
|
44
|
+
### Frontmatter
|
|
45
|
+
|
|
46
|
+
Add metadata as comments in the first 20 lines of any script:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
#!/bin/bash
|
|
50
|
+
# kadai:name Deploy Staging
|
|
51
|
+
# kadai:emoji 🚀
|
|
52
|
+
# kadai:description Deploy the app to staging
|
|
53
|
+
# kadai:confirm true
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
For JS/TS, use `//` comments:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// kadai:name Reset Database
|
|
60
|
+
// kadai:emoji 🗑️
|
|
61
|
+
// kadai:confirm true
|
|
9
62
|
```
|
|
10
63
|
|
|
11
|
-
|
|
64
|
+
| Key | Type | Description |
|
|
65
|
+
|---------------|---------|--------------------------------------------|
|
|
66
|
+
| `name` | string | Display name (inferred from filename if omitted) |
|
|
67
|
+
| `emoji` | string | Emoji prefix in menus |
|
|
68
|
+
| `description` | string | Short description |
|
|
69
|
+
| `confirm` | boolean | Require confirmation before running |
|
|
70
|
+
| `hidden` | boolean | Hide from menu (still runnable via CLI) |
|
|
71
|
+
| `interactive` | boolean | Hand over the full terminal to the script |
|
|
72
|
+
|
|
73
|
+
### Interactive Scripts
|
|
74
|
+
|
|
75
|
+
Scripts marked `interactive` get full terminal control — kadai exits its UI, runs the script with inherited stdio, then returns to the menu. Use this for scripts that need user input (readline prompts, password entry, etc.).
|
|
76
|
+
|
|
77
|
+
### Ink UI Actions (Planned)
|
|
78
|
+
|
|
79
|
+
`.tsx` files will be able to export an Ink component that renders directly inside kadai's UI, enabling rich interactive interfaces (forms, progress bars, tables) without spawning a subprocess.
|
|
80
|
+
|
|
81
|
+
### Config
|
|
82
|
+
|
|
83
|
+
`.kadai/config.ts` lets you set environment variables injected into all actions:
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
export default {
|
|
87
|
+
env: {
|
|
88
|
+
DATABASE_URL: "postgres://localhost:5432/myapp",
|
|
89
|
+
APP_ENV: "development",
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## CLI
|
|
12
95
|
|
|
13
96
|
```bash
|
|
14
|
-
|
|
97
|
+
kadai # Interactive menu
|
|
98
|
+
kadai list --json # List actions as JSON
|
|
99
|
+
kadai list --json --all # Include hidden actions
|
|
100
|
+
kadai run <action-id> # Run an action directly
|
|
15
101
|
```
|
|
16
102
|
|
|
17
|
-
|
|
103
|
+
## AI
|
|
104
|
+
|
|
105
|
+
kadai is designed to work well with AI coding agents like Claude Code.
|
|
106
|
+
|
|
107
|
+
### How It Works
|
|
108
|
+
|
|
109
|
+
- `kadai list --json` gives agents a machine-readable list of available project actions
|
|
110
|
+
- `kadai run <action-id>` runs actions non-interactively (confirmation prompts auto-skip in non-TTY)
|
|
111
|
+
- Agents can discover what's available, then run the right action — no hardcoded commands
|
|
112
|
+
|
|
113
|
+
### Skill Installation
|
|
114
|
+
|
|
115
|
+
If your project uses Claude Code (has a `.claude/` directory or `CLAUDE.md`), kadai automatically creates a skill file at `.claude/skills/kadai/SKILL.md` on first run. This teaches Claude Code how to discover and run your project's actions.
|
|
116
|
+
|
|
117
|
+
The skill is non-user-invocable — Claude Code reads it automatically and uses kadai when relevant, without needing explicit prompts.
|
package/dist/cli.js
CHANGED
|
@@ -86,9 +86,6 @@ function parseMetadataFromContent(content) {
|
|
|
86
86
|
case "hidden":
|
|
87
87
|
meta.hidden = value.trim() === "true";
|
|
88
88
|
break;
|
|
89
|
-
case "interactive":
|
|
90
|
-
meta.interactive = value.trim() === "true";
|
|
91
|
-
break;
|
|
92
89
|
}
|
|
93
90
|
}
|
|
94
91
|
return meta;
|
|
@@ -108,15 +105,13 @@ async function extractMetadata(filePath) {
|
|
|
108
105
|
emoji: frontmatter.emoji,
|
|
109
106
|
description: frontmatter.description,
|
|
110
107
|
confirm: frontmatter.confirm ?? false,
|
|
111
|
-
hidden: frontmatter.hidden ?? false
|
|
112
|
-
interactive: frontmatter.interactive ?? false
|
|
108
|
+
hidden: frontmatter.hidden ?? false
|
|
113
109
|
};
|
|
114
110
|
}
|
|
115
111
|
return {
|
|
116
112
|
name: inferNameFromFilename(filename),
|
|
117
113
|
confirm: false,
|
|
118
|
-
hidden: false
|
|
119
|
-
interactive: false
|
|
114
|
+
hidden: false
|
|
120
115
|
};
|
|
121
116
|
}
|
|
122
117
|
var META_PATTERN, MAX_SCAN_LINES = 20;
|
|
@@ -251,31 +246,9 @@ var init_loader = __esm(() => {
|
|
|
251
246
|
// src/core/runner.ts
|
|
252
247
|
var exports_runner = {};
|
|
253
248
|
__export(exports_runner, {
|
|
254
|
-
runAction: () => runAction,
|
|
255
249
|
resolveCommand: () => resolveCommand,
|
|
256
250
|
parseShebangCommand: () => parseShebangCommand
|
|
257
251
|
});
|
|
258
|
-
function runAction(action, options) {
|
|
259
|
-
const { cwd, config } = options;
|
|
260
|
-
const cmd = resolveCommand(action);
|
|
261
|
-
const env = {
|
|
262
|
-
...process.env,
|
|
263
|
-
...config?.env ?? {}
|
|
264
|
-
};
|
|
265
|
-
const proc = Bun.spawn(cmd, {
|
|
266
|
-
cwd,
|
|
267
|
-
stdin: "pipe",
|
|
268
|
-
stdout: "pipe",
|
|
269
|
-
stderr: "pipe",
|
|
270
|
-
env
|
|
271
|
-
});
|
|
272
|
-
return {
|
|
273
|
-
proc,
|
|
274
|
-
stdout: proc.stdout,
|
|
275
|
-
stderr: proc.stderr,
|
|
276
|
-
stdin: proc.stdin
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
252
|
function cachedWhich(bin) {
|
|
280
253
|
if (whichCache.has(bin))
|
|
281
254
|
return whichCache.get(bin) ?? null;
|
|
@@ -20759,118 +20732,6 @@ var init_build3 = __esm(async () => {
|
|
|
20759
20732
|
init_types();
|
|
20760
20733
|
});
|
|
20761
20734
|
|
|
20762
|
-
// src/hooks/useActionRunner.ts
|
|
20763
|
-
function useActionRunner({
|
|
20764
|
-
action,
|
|
20765
|
-
cwd: cwd2,
|
|
20766
|
-
config,
|
|
20767
|
-
enabled = true,
|
|
20768
|
-
onRunningChange
|
|
20769
|
-
}) {
|
|
20770
|
-
const [lines, setLines] = import_react60.useState([]);
|
|
20771
|
-
const [exitCode, setExitCode] = import_react60.useState(null);
|
|
20772
|
-
const [running, setRunning] = import_react60.useState(false);
|
|
20773
|
-
const doneRef = import_react60.useRef(false);
|
|
20774
|
-
const onRunningChangeRef = import_react60.useRef(onRunningChange);
|
|
20775
|
-
onRunningChangeRef.current = onRunningChange;
|
|
20776
|
-
const stdinRef = import_react60.useRef(null);
|
|
20777
|
-
use_input_default((input, key) => {
|
|
20778
|
-
const stdin = stdinRef.current;
|
|
20779
|
-
if (!stdin)
|
|
20780
|
-
return;
|
|
20781
|
-
try {
|
|
20782
|
-
if (key.return) {
|
|
20783
|
-
stdin.write(`
|
|
20784
|
-
`);
|
|
20785
|
-
} else if (key.backspace || key.delete) {
|
|
20786
|
-
stdin.write("\x7F");
|
|
20787
|
-
} else if (key.tab) {
|
|
20788
|
-
stdin.write("\t");
|
|
20789
|
-
} else if (key.escape) {
|
|
20790
|
-
stdin.write("\x1B");
|
|
20791
|
-
} else if (key.ctrl && input) {
|
|
20792
|
-
const code = input.toUpperCase().charCodeAt(0) - 64;
|
|
20793
|
-
if (code > 0 && code < 32) {
|
|
20794
|
-
stdin.write(String.fromCharCode(code));
|
|
20795
|
-
}
|
|
20796
|
-
} else if (input) {
|
|
20797
|
-
stdin.write(input);
|
|
20798
|
-
}
|
|
20799
|
-
stdin.flush();
|
|
20800
|
-
} catch {}
|
|
20801
|
-
}, { isActive: running });
|
|
20802
|
-
import_react60.useEffect(() => {
|
|
20803
|
-
if (!action || !enabled)
|
|
20804
|
-
return;
|
|
20805
|
-
let aborted = false;
|
|
20806
|
-
setLines([]);
|
|
20807
|
-
setExitCode(null);
|
|
20808
|
-
setRunning(true);
|
|
20809
|
-
onRunningChangeRef.current?.(true);
|
|
20810
|
-
doneRef.current = false;
|
|
20811
|
-
let handle;
|
|
20812
|
-
try {
|
|
20813
|
-
handle = runAction(action, { cwd: cwd2, config });
|
|
20814
|
-
} catch {
|
|
20815
|
-
setRunning(false);
|
|
20816
|
-
onRunningChangeRef.current?.(false);
|
|
20817
|
-
setExitCode(-1);
|
|
20818
|
-
doneRef.current = true;
|
|
20819
|
-
return;
|
|
20820
|
-
}
|
|
20821
|
-
stdinRef.current = handle.stdin;
|
|
20822
|
-
const readStream = async (stream) => {
|
|
20823
|
-
const reader = stream.getReader();
|
|
20824
|
-
const decoder = new TextDecoder;
|
|
20825
|
-
try {
|
|
20826
|
-
while (true) {
|
|
20827
|
-
const { done, value } = await reader.read();
|
|
20828
|
-
if (done || aborted)
|
|
20829
|
-
break;
|
|
20830
|
-
const text = decoder.decode(value);
|
|
20831
|
-
const newLines = text.split(`
|
|
20832
|
-
`).filter((l) => l.length > 0);
|
|
20833
|
-
if (newLines.length > 0) {
|
|
20834
|
-
setLines((prev) => [...prev, ...newLines]);
|
|
20835
|
-
}
|
|
20836
|
-
}
|
|
20837
|
-
} catch {}
|
|
20838
|
-
};
|
|
20839
|
-
readStream(handle.stdout);
|
|
20840
|
-
readStream(handle.stderr);
|
|
20841
|
-
handle.proc.exited.then((code) => {
|
|
20842
|
-
stdinRef.current = null;
|
|
20843
|
-
try {
|
|
20844
|
-
handle.stdin.end();
|
|
20845
|
-
} catch {}
|
|
20846
|
-
if (aborted)
|
|
20847
|
-
return;
|
|
20848
|
-
setExitCode(code);
|
|
20849
|
-
setRunning(false);
|
|
20850
|
-
onRunningChangeRef.current?.(false);
|
|
20851
|
-
doneRef.current = true;
|
|
20852
|
-
});
|
|
20853
|
-
return () => {
|
|
20854
|
-
aborted = true;
|
|
20855
|
-
stdinRef.current = null;
|
|
20856
|
-
try {
|
|
20857
|
-
handle.stdin.end();
|
|
20858
|
-
} catch {}
|
|
20859
|
-
onRunningChangeRef.current?.(false);
|
|
20860
|
-
try {
|
|
20861
|
-
handle.proc.kill();
|
|
20862
|
-
} catch {}
|
|
20863
|
-
};
|
|
20864
|
-
}, [action?.id, cwd2, config, enabled]);
|
|
20865
|
-
return { lines, exitCode, running, doneRef };
|
|
20866
|
-
}
|
|
20867
|
-
var import_react60;
|
|
20868
|
-
var init_useActionRunner = __esm(async () => {
|
|
20869
|
-
init_runner();
|
|
20870
|
-
await init_build2();
|
|
20871
|
-
import_react60 = __toESM(require_react(), 1);
|
|
20872
|
-
});
|
|
20873
|
-
|
|
20874
20735
|
// node_modules/react/cjs/react-jsx-dev-runtime.development.js
|
|
20875
20736
|
var require_react_jsx_dev_runtime_development = __commonJS((exports) => {
|
|
20876
20737
|
var React29 = __toESM(require_react());
|
|
@@ -21094,106 +20955,50 @@ var require_jsx_dev_runtime = __commonJS((exports, module) => {
|
|
|
21094
20955
|
}
|
|
21095
20956
|
});
|
|
21096
20957
|
|
|
21097
|
-
// src/components/ActionOutput.tsx
|
|
21098
|
-
function ActionOutput({
|
|
21099
|
-
action,
|
|
21100
|
-
cwd: cwd2,
|
|
21101
|
-
config,
|
|
21102
|
-
onRunningChange
|
|
21103
|
-
}) {
|
|
21104
|
-
const { lines, exitCode, running } = useActionRunner({
|
|
21105
|
-
action,
|
|
21106
|
-
cwd: cwd2,
|
|
21107
|
-
config,
|
|
21108
|
-
onRunningChange
|
|
21109
|
-
});
|
|
21110
|
-
return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
21111
|
-
flexDirection: "column",
|
|
21112
|
-
children: [
|
|
21113
|
-
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
21114
|
-
marginBottom: 1,
|
|
21115
|
-
children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21116
|
-
bold: true,
|
|
21117
|
-
children: [
|
|
21118
|
-
action.meta.emoji ? `${action.meta.emoji} ` : "",
|
|
21119
|
-
action.meta.name
|
|
21120
|
-
]
|
|
21121
|
-
}, undefined, true, undefined, this)
|
|
21122
|
-
}, undefined, false, undefined, this),
|
|
21123
|
-
lines.map((line, i) => /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21124
|
-
children: line
|
|
21125
|
-
}, i, false, undefined, this)),
|
|
21126
|
-
running && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21127
|
-
dimColor: true,
|
|
21128
|
-
children: "Running..."
|
|
21129
|
-
}, undefined, false, undefined, this),
|
|
21130
|
-
!running && exitCode !== null && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
21131
|
-
marginTop: 1,
|
|
21132
|
-
children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21133
|
-
color: exitCode === 0 ? "green" : "red",
|
|
21134
|
-
children: [
|
|
21135
|
-
exitCode === 0 ? "\u2713" : "\u2717",
|
|
21136
|
-
" exit code ",
|
|
21137
|
-
exitCode
|
|
21138
|
-
]
|
|
21139
|
-
}, undefined, true, undefined, this)
|
|
21140
|
-
}, undefined, false, undefined, this)
|
|
21141
|
-
]
|
|
21142
|
-
}, undefined, true, undefined, this);
|
|
21143
|
-
}
|
|
21144
|
-
var jsx_dev_runtime;
|
|
21145
|
-
var init_ActionOutput = __esm(async () => {
|
|
21146
|
-
await __promiseAll([
|
|
21147
|
-
init_build2(),
|
|
21148
|
-
init_useActionRunner()
|
|
21149
|
-
]);
|
|
21150
|
-
jsx_dev_runtime = __toESM(require_jsx_dev_runtime(), 1);
|
|
21151
|
-
});
|
|
21152
|
-
|
|
21153
20958
|
// src/components/Breadcrumbs.tsx
|
|
21154
20959
|
function Breadcrumbs({ path }) {
|
|
21155
20960
|
const parts = ["kadai", ...path];
|
|
21156
|
-
return /* @__PURE__ */
|
|
20961
|
+
return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
21157
20962
|
marginBottom: 1,
|
|
21158
|
-
children: /* @__PURE__ */
|
|
20963
|
+
children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21159
20964
|
dimColor: true,
|
|
21160
20965
|
children: parts.join(" > ")
|
|
21161
20966
|
}, undefined, false, undefined, this)
|
|
21162
20967
|
}, undefined, false, undefined, this);
|
|
21163
20968
|
}
|
|
21164
|
-
var
|
|
20969
|
+
var jsx_dev_runtime;
|
|
21165
20970
|
var init_Breadcrumbs = __esm(async () => {
|
|
21166
20971
|
await init_build2();
|
|
21167
|
-
|
|
20972
|
+
jsx_dev_runtime = __toESM(require_jsx_dev_runtime(), 1);
|
|
21168
20973
|
});
|
|
21169
20974
|
|
|
21170
20975
|
// src/components/StatusBar.tsx
|
|
21171
20976
|
function StatusBar() {
|
|
21172
20977
|
const hints = "\u2191\u2193/j/k navigate / search esc back q quit";
|
|
21173
|
-
return /* @__PURE__ */
|
|
20978
|
+
return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
|
|
21174
20979
|
marginTop: 1,
|
|
21175
20980
|
flexDirection: "column",
|
|
21176
|
-
children: /* @__PURE__ */
|
|
20981
|
+
children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
|
|
21177
20982
|
dimColor: true,
|
|
21178
20983
|
children: hints
|
|
21179
20984
|
}, undefined, false, undefined, this)
|
|
21180
20985
|
}, undefined, false, undefined, this);
|
|
21181
20986
|
}
|
|
21182
|
-
var
|
|
20987
|
+
var jsx_dev_runtime2;
|
|
21183
20988
|
var init_StatusBar = __esm(async () => {
|
|
21184
20989
|
await init_build2();
|
|
21185
|
-
|
|
20990
|
+
jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
|
|
21186
20991
|
});
|
|
21187
20992
|
|
|
21188
20993
|
// src/hooks/useActions.ts
|
|
21189
20994
|
import { join as join4 } from "path";
|
|
21190
20995
|
function useActions({ kadaiDir }) {
|
|
21191
|
-
const [actions, setActions] =
|
|
21192
|
-
const [config, setConfig] =
|
|
21193
|
-
const [loading, setLoading] =
|
|
21194
|
-
const actionsRef =
|
|
20996
|
+
const [actions, setActions] = import_react60.useState([]);
|
|
20997
|
+
const [config, setConfig] = import_react60.useState({});
|
|
20998
|
+
const [loading, setLoading] = import_react60.useState(true);
|
|
20999
|
+
const actionsRef = import_react60.useRef(actions);
|
|
21195
21000
|
actionsRef.current = actions;
|
|
21196
|
-
|
|
21001
|
+
import_react60.useEffect(() => {
|
|
21197
21002
|
(async () => {
|
|
21198
21003
|
const cfg = await loadConfig(kadaiDir);
|
|
21199
21004
|
setConfig(cfg);
|
|
@@ -21205,11 +21010,11 @@ function useActions({ kadaiDir }) {
|
|
|
21205
21010
|
}, [kadaiDir]);
|
|
21206
21011
|
return { actions, actionsRef, config, loading };
|
|
21207
21012
|
}
|
|
21208
|
-
var
|
|
21013
|
+
var import_react60;
|
|
21209
21014
|
var init_useActions = __esm(() => {
|
|
21210
21015
|
init_config();
|
|
21211
21016
|
init_loader();
|
|
21212
|
-
|
|
21017
|
+
import_react60 = __toESM(require_react(), 1);
|
|
21213
21018
|
});
|
|
21214
21019
|
|
|
21215
21020
|
// src/hooks/useKeyboard.ts
|
|
@@ -21242,11 +21047,6 @@ function useKeyboard({
|
|
|
21242
21047
|
}) {
|
|
21243
21048
|
use_input_default((input, key) => {
|
|
21244
21049
|
const screen = stackRef.current.at(-1);
|
|
21245
|
-
if (screen.type === "output") {
|
|
21246
|
-
if (key.escape || key.return)
|
|
21247
|
-
popScreen();
|
|
21248
|
-
return;
|
|
21249
|
-
}
|
|
21250
21050
|
if (screen.type !== "menu")
|
|
21251
21051
|
return;
|
|
21252
21052
|
if (searchActiveRef.current) {
|
|
@@ -21309,7 +21109,7 @@ function useKeyboard({
|
|
|
21309
21109
|
return;
|
|
21310
21110
|
}
|
|
21311
21111
|
if (key.return) {
|
|
21312
|
-
selectCurrentItem(screen, actionsRef, searchQueryRef, selectedIndexRef, getMenuItems, computeFiltered, pushScreen);
|
|
21112
|
+
selectCurrentItem(screen, actionsRef, searchQueryRef, selectedIndexRef, getMenuItems, computeFiltered, pushScreen, onRunInteractive);
|
|
21313
21113
|
return;
|
|
21314
21114
|
}
|
|
21315
21115
|
if (key.upArrow || input === "k") {
|
|
@@ -21341,10 +21141,8 @@ function selectCurrentItem(screen, actionsRef, searchQueryRef, selectedIndexRef,
|
|
|
21341
21141
|
const action = actionsRef.current.find((a) => a.id === item.value);
|
|
21342
21142
|
if (action?.meta.confirm) {
|
|
21343
21143
|
pushScreen({ type: "confirm", actionId: item.value });
|
|
21344
|
-
} else if (action
|
|
21144
|
+
} else if (action) {
|
|
21345
21145
|
onRunInteractive(action);
|
|
21346
|
-
} else {
|
|
21347
|
-
pushScreen({ type: "output", actionId: item.value });
|
|
21348
21146
|
}
|
|
21349
21147
|
}
|
|
21350
21148
|
}
|
|
@@ -21354,8 +21152,8 @@ var init_useKeyboard = __esm(async () => {
|
|
|
21354
21152
|
|
|
21355
21153
|
// src/hooks/useNavigation.ts
|
|
21356
21154
|
function useNavigation({ onExit, onNavigate }) {
|
|
21357
|
-
const [stack, setStack] =
|
|
21358
|
-
const stackRef =
|
|
21155
|
+
const [stack, setStack] = import_react61.useState([{ type: "menu", path: [] }]);
|
|
21156
|
+
const stackRef = import_react61.useRef(stack);
|
|
21359
21157
|
stackRef.current = stack;
|
|
21360
21158
|
const currentScreen = stack.at(-1);
|
|
21361
21159
|
const pushScreen = (screen) => {
|
|
@@ -21387,9 +21185,9 @@ function useNavigation({ onExit, onNavigate }) {
|
|
|
21387
21185
|
stackRef
|
|
21388
21186
|
};
|
|
21389
21187
|
}
|
|
21390
|
-
var
|
|
21188
|
+
var import_react61;
|
|
21391
21189
|
var init_useNavigation = __esm(() => {
|
|
21392
|
-
|
|
21190
|
+
import_react61 = __toESM(require_react(), 1);
|
|
21393
21191
|
});
|
|
21394
21192
|
|
|
21395
21193
|
// node_modules/fuzzysort/fuzzysort.js
|
|
@@ -22124,17 +21922,17 @@ var require_fuzzysort = __commonJS((exports, module) => {
|
|
|
22124
21922
|
|
|
22125
21923
|
// src/hooks/useRefState.ts
|
|
22126
21924
|
function useRefState(initial) {
|
|
22127
|
-
const [state, setState] =
|
|
22128
|
-
const ref =
|
|
21925
|
+
const [state, setState] = import_react62.useState(initial);
|
|
21926
|
+
const ref = import_react62.useRef(initial);
|
|
22129
21927
|
const set = (value) => {
|
|
22130
21928
|
ref.current = value;
|
|
22131
21929
|
setState(value);
|
|
22132
21930
|
};
|
|
22133
21931
|
return [state, ref, set];
|
|
22134
21932
|
}
|
|
22135
|
-
var
|
|
21933
|
+
var import_react62;
|
|
22136
21934
|
var init_useRefState = __esm(() => {
|
|
22137
|
-
|
|
21935
|
+
import_react62 = __toESM(require_react(), 1);
|
|
22138
21936
|
});
|
|
22139
21937
|
|
|
22140
21938
|
// src/hooks/useSearch.ts
|
|
@@ -22193,12 +21991,12 @@ function MenuList({
|
|
|
22193
21991
|
selectedIndex
|
|
22194
21992
|
}) {
|
|
22195
21993
|
const hasAnyNew = items.some((item) => item.isNew);
|
|
22196
|
-
return /* @__PURE__ */
|
|
21994
|
+
return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(jsx_dev_runtime3.Fragment, {
|
|
22197
21995
|
children: items.map((item, i) => {
|
|
22198
21996
|
if (item.type === "separator") {
|
|
22199
|
-
return /* @__PURE__ */
|
|
21997
|
+
return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
22200
21998
|
marginTop: i > 0 ? 1 : 0,
|
|
22201
|
-
children: /* @__PURE__ */
|
|
21999
|
+
children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
22202
22000
|
dimColor: true,
|
|
22203
22001
|
bold: true,
|
|
22204
22002
|
children: item.label
|
|
@@ -22206,16 +22004,16 @@ function MenuList({
|
|
|
22206
22004
|
}, `sep-${item.value}`, false, undefined, this);
|
|
22207
22005
|
}
|
|
22208
22006
|
const selected = i === selectedIndex;
|
|
22209
|
-
return /* @__PURE__ */
|
|
22007
|
+
return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
22210
22008
|
children: [
|
|
22211
|
-
/* @__PURE__ */
|
|
22009
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
22212
22010
|
color: selected ? "cyan" : undefined,
|
|
22213
22011
|
children: selected ? "\u276F " : " "
|
|
22214
22012
|
}, undefined, false, undefined, this),
|
|
22215
|
-
hasAnyNew && /* @__PURE__ */
|
|
22013
|
+
hasAnyNew && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
22216
22014
|
children: item.isNew ? "\u2728 " : " "
|
|
22217
22015
|
}, undefined, false, undefined, this),
|
|
22218
|
-
/* @__PURE__ */
|
|
22016
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
22219
22017
|
color: selected ? "cyan" : undefined,
|
|
22220
22018
|
children: [
|
|
22221
22019
|
item.type === "category" ? "\uD83D\uDCC1 " : "",
|
|
@@ -22224,7 +22022,7 @@ function MenuList({
|
|
|
22224
22022
|
item.type === "category" ? " \u25B8" : ""
|
|
22225
22023
|
]
|
|
22226
22024
|
}, undefined, true, undefined, this),
|
|
22227
|
-
item.description && /* @__PURE__ */
|
|
22025
|
+
item.description && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
22228
22026
|
dimColor: true,
|
|
22229
22027
|
children: [
|
|
22230
22028
|
" (",
|
|
@@ -22237,16 +22035,15 @@ function MenuList({
|
|
|
22237
22035
|
})
|
|
22238
22036
|
}, undefined, false, undefined, this);
|
|
22239
22037
|
}
|
|
22240
|
-
function App2({
|
|
22038
|
+
function App2({ kadaiDir, onRunAction }) {
|
|
22241
22039
|
const { exit } = use_app_default();
|
|
22242
|
-
const
|
|
22243
|
-
|
|
22244
|
-
onRunInteractive(action);
|
|
22040
|
+
const handleRunAction = (action) => {
|
|
22041
|
+
onRunAction(action);
|
|
22245
22042
|
exit();
|
|
22246
|
-
}
|
|
22043
|
+
};
|
|
22247
22044
|
const search = useSearch();
|
|
22248
22045
|
const nav = useNavigation({ onExit: exit, onNavigate: search.resetSearch });
|
|
22249
|
-
const { actions, actionsRef,
|
|
22046
|
+
const { actions, actionsRef, loading } = useActions({
|
|
22250
22047
|
kadaiDir
|
|
22251
22048
|
});
|
|
22252
22049
|
useKeyboard({
|
|
@@ -22264,11 +22061,11 @@ function App2({ cwd: cwd2, kadaiDir, onRunInteractive }) {
|
|
|
22264
22061
|
exit,
|
|
22265
22062
|
getMenuItems: buildMenuItems,
|
|
22266
22063
|
computeFiltered: search.computeFiltered,
|
|
22267
|
-
isActive: nav.currentScreen.type !== "confirm"
|
|
22268
|
-
onRunInteractive:
|
|
22064
|
+
isActive: nav.currentScreen.type !== "confirm",
|
|
22065
|
+
onRunInteractive: handleRunAction
|
|
22269
22066
|
});
|
|
22270
22067
|
if (loading) {
|
|
22271
|
-
return /* @__PURE__ */
|
|
22068
|
+
return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
22272
22069
|
dimColor: true,
|
|
22273
22070
|
children: "Loading actions..."
|
|
22274
22071
|
}, undefined, false, undefined, this);
|
|
@@ -22281,38 +22078,38 @@ function App2({ cwd: cwd2, kadaiDir, onRunInteractive }) {
|
|
|
22281
22078
|
search.setSelectedIndex(1);
|
|
22282
22079
|
search.selectedIndexRef.current = 1;
|
|
22283
22080
|
}
|
|
22284
|
-
return /* @__PURE__ */
|
|
22081
|
+
return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
22285
22082
|
flexDirection: "column",
|
|
22286
22083
|
children: [
|
|
22287
|
-
/* @__PURE__ */
|
|
22084
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Breadcrumbs, {
|
|
22288
22085
|
path: menuPath
|
|
22289
22086
|
}, undefined, false, undefined, this),
|
|
22290
|
-
search.searchActive && /* @__PURE__ */
|
|
22087
|
+
search.searchActive && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
22291
22088
|
marginBottom: 1,
|
|
22292
22089
|
children: [
|
|
22293
|
-
/* @__PURE__ */
|
|
22090
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
22294
22091
|
children: [
|
|
22295
22092
|
"/ ",
|
|
22296
22093
|
search.searchQuery
|
|
22297
22094
|
]
|
|
22298
22095
|
}, undefined, true, undefined, this),
|
|
22299
|
-
/* @__PURE__ */
|
|
22096
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
22300
22097
|
dimColor: true,
|
|
22301
22098
|
children: "\u2588"
|
|
22302
22099
|
}, undefined, false, undefined, this)
|
|
22303
22100
|
]
|
|
22304
22101
|
}, undefined, true, undefined, this),
|
|
22305
|
-
filteredItems.length === 0 && menuItems.length === 0 ? /* @__PURE__ */
|
|
22102
|
+
filteredItems.length === 0 && menuItems.length === 0 ? /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
22306
22103
|
dimColor: true,
|
|
22307
22104
|
children: "No actions found"
|
|
22308
|
-
}, undefined, false, undefined, this) : filteredItems.length === 0 ? /* @__PURE__ */
|
|
22105
|
+
}, undefined, false, undefined, this) : filteredItems.length === 0 ? /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
22309
22106
|
dimColor: true,
|
|
22310
22107
|
children: "No matching items"
|
|
22311
|
-
}, undefined, false, undefined, this) : /* @__PURE__ */
|
|
22108
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(MenuList, {
|
|
22312
22109
|
items: filteredItems,
|
|
22313
22110
|
selectedIndex: search.selectedIndex
|
|
22314
22111
|
}, undefined, false, undefined, this),
|
|
22315
|
-
/* @__PURE__ */
|
|
22112
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(StatusBar, {}, undefined, false, undefined, this)
|
|
22316
22113
|
]
|
|
22317
22114
|
}, undefined, true, undefined, this);
|
|
22318
22115
|
}
|
|
@@ -22320,29 +22117,21 @@ function App2({ cwd: cwd2, kadaiDir, onRunInteractive }) {
|
|
|
22320
22117
|
const { actionId } = nav.currentScreen;
|
|
22321
22118
|
const action = actions.find((a) => a.id === actionId);
|
|
22322
22119
|
if (!action)
|
|
22323
|
-
return /* @__PURE__ */
|
|
22120
|
+
return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
22324
22121
|
color: "red",
|
|
22325
22122
|
children: "Action not found"
|
|
22326
22123
|
}, undefined, false, undefined, this);
|
|
22327
22124
|
const handleConfirm = () => {
|
|
22328
|
-
|
|
22329
|
-
handleRunInteractive(action);
|
|
22330
|
-
return;
|
|
22331
|
-
}
|
|
22332
|
-
nav.setStack((s) => {
|
|
22333
|
-
const next = [...s.slice(0, -1), { type: "output", actionId }];
|
|
22334
|
-
nav.stackRef.current = next;
|
|
22335
|
-
return next;
|
|
22336
|
-
});
|
|
22125
|
+
handleRunAction(action);
|
|
22337
22126
|
};
|
|
22338
|
-
return /* @__PURE__ */
|
|
22127
|
+
return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
22339
22128
|
flexDirection: "column",
|
|
22340
|
-
children: /* @__PURE__ */
|
|
22129
|
+
children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
22341
22130
|
children: [
|
|
22342
|
-
/* @__PURE__ */
|
|
22131
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
22343
22132
|
children: [
|
|
22344
22133
|
"Run ",
|
|
22345
|
-
/* @__PURE__ */
|
|
22134
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
22346
22135
|
bold: true,
|
|
22347
22136
|
children: action.meta.name
|
|
22348
22137
|
}, undefined, false, undefined, this),
|
|
@@ -22350,7 +22139,7 @@ function App2({ cwd: cwd2, kadaiDir, onRunInteractive }) {
|
|
|
22350
22139
|
" "
|
|
22351
22140
|
]
|
|
22352
22141
|
}, undefined, true, undefined, this),
|
|
22353
|
-
/* @__PURE__ */
|
|
22142
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(ConfirmInput, {
|
|
22354
22143
|
onConfirm: handleConfirm,
|
|
22355
22144
|
onCancel: () => nav.popScreen()
|
|
22356
22145
|
}, undefined, false, undefined, this)
|
|
@@ -22358,33 +22147,6 @@ function App2({ cwd: cwd2, kadaiDir, onRunInteractive }) {
|
|
|
22358
22147
|
}, undefined, true, undefined, this)
|
|
22359
22148
|
}, undefined, false, undefined, this);
|
|
22360
22149
|
}
|
|
22361
|
-
if (nav.currentScreen.type === "output") {
|
|
22362
|
-
const { actionId } = nav.currentScreen;
|
|
22363
|
-
const action = actions.find((a) => a.id === actionId);
|
|
22364
|
-
if (!action)
|
|
22365
|
-
return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
22366
|
-
color: "red",
|
|
22367
|
-
children: "Action not found"
|
|
22368
|
-
}, undefined, false, undefined, this);
|
|
22369
|
-
return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
|
|
22370
|
-
flexDirection: "column",
|
|
22371
|
-
children: [
|
|
22372
|
-
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(ActionOutput, {
|
|
22373
|
-
action,
|
|
22374
|
-
cwd: cwd2,
|
|
22375
|
-
config,
|
|
22376
|
-
onRunningChange: setIsProcessRunning
|
|
22377
|
-
}, undefined, false, undefined, this),
|
|
22378
|
-
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
|
|
22379
|
-
marginTop: 1,
|
|
22380
|
-
children: /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
22381
|
-
dimColor: true,
|
|
22382
|
-
children: "Press enter or esc to go back"
|
|
22383
|
-
}, undefined, false, undefined, this)
|
|
22384
|
-
}, undefined, false, undefined, this)
|
|
22385
|
-
]
|
|
22386
|
-
}, undefined, true, undefined, this);
|
|
22387
|
-
}
|
|
22388
22150
|
return null;
|
|
22389
22151
|
}
|
|
22390
22152
|
function isRecentlyAdded(action) {
|
|
@@ -22458,7 +22220,7 @@ function buildMenuItems(actions, path) {
|
|
|
22458
22220
|
});
|
|
22459
22221
|
return items;
|
|
22460
22222
|
}
|
|
22461
|
-
var
|
|
22223
|
+
var jsx_dev_runtime3, SEVEN_DAYS_MS;
|
|
22462
22224
|
var init_app = __esm(async () => {
|
|
22463
22225
|
init_useActions();
|
|
22464
22226
|
init_useNavigation();
|
|
@@ -22466,13 +22228,11 @@ var init_app = __esm(async () => {
|
|
|
22466
22228
|
await __promiseAll([
|
|
22467
22229
|
init_build3(),
|
|
22468
22230
|
init_build2(),
|
|
22469
|
-
init_ActionOutput(),
|
|
22470
22231
|
init_Breadcrumbs(),
|
|
22471
22232
|
init_StatusBar(),
|
|
22472
22233
|
init_useKeyboard()
|
|
22473
22234
|
]);
|
|
22474
|
-
|
|
22475
|
-
jsx_dev_runtime4 = __toESM(require_jsx_dev_runtime(), 1);
|
|
22235
|
+
jsx_dev_runtime3 = __toESM(require_jsx_dev_runtime(), 1);
|
|
22476
22236
|
SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;
|
|
22477
22237
|
});
|
|
22478
22238
|
|
|
@@ -22819,55 +22579,47 @@ function createStdinStream() {
|
|
|
22819
22579
|
});
|
|
22820
22580
|
return charStream;
|
|
22821
22581
|
}
|
|
22822
|
-
var
|
|
22823
|
-
|
|
22824
|
-
|
|
22825
|
-
|
|
22826
|
-
|
|
22827
|
-
|
|
22828
|
-
|
|
22829
|
-
|
|
22830
|
-
|
|
22831
|
-
|
|
22832
|
-
|
|
22833
|
-
|
|
22834
|
-
|
|
22835
|
-
|
|
22836
|
-
|
|
22837
|
-
|
|
22838
|
-
|
|
22839
|
-
|
|
22840
|
-
|
|
22841
|
-
|
|
22842
|
-
|
|
22843
|
-
|
|
22844
|
-
|
|
22845
|
-
...config.env ?? {}
|
|
22846
|
-
};
|
|
22847
|
-
console.log(`${action.meta.emoji ? `${action.meta.emoji} ` : ""}${action.meta.name}
|
|
22582
|
+
var selectedAction = null;
|
|
22583
|
+
var stdinStream = createStdinStream();
|
|
22584
|
+
var instance = render2(React29.createElement(App3, {
|
|
22585
|
+
kadaiDir,
|
|
22586
|
+
onRunAction: (action) => {
|
|
22587
|
+
selectedAction = action;
|
|
22588
|
+
}
|
|
22589
|
+
}), {
|
|
22590
|
+
stdin: stdinStream,
|
|
22591
|
+
stdout: process.stdout,
|
|
22592
|
+
stderr: process.stderr
|
|
22593
|
+
});
|
|
22594
|
+
await instance.waitUntilExit();
|
|
22595
|
+
if (!selectedAction)
|
|
22596
|
+
process.exit(0);
|
|
22597
|
+
var action = selectedAction;
|
|
22598
|
+
var config = await loadConfig2(kadaiDir);
|
|
22599
|
+
var cmd = resolveCommand2(action);
|
|
22600
|
+
var env3 = {
|
|
22601
|
+
...process.env,
|
|
22602
|
+
...config.env ?? {}
|
|
22603
|
+
};
|
|
22604
|
+
console.log(`${action.meta.emoji ? `${action.meta.emoji} ` : ""}${action.meta.name}
|
|
22848
22605
|
`);
|
|
22849
|
-
|
|
22850
|
-
|
|
22851
|
-
|
|
22852
|
-
|
|
22853
|
-
|
|
22854
|
-
|
|
22855
|
-
|
|
22856
|
-
|
|
22857
|
-
|
|
22858
|
-
|
|
22859
|
-
|
|
22860
|
-
|
|
22861
|
-
|
|
22862
|
-
|
|
22863
|
-
|
|
22864
|
-
|
|
22865
|
-
|
|
22866
|
-
|
|
22867
|
-
|
|
22868
|
-
|
|
22869
|
-
};
|
|
22870
|
-
process.stdin.on("data", onData);
|
|
22871
|
-
});
|
|
22872
|
-
}
|
|
22873
|
-
process.exit(0);
|
|
22606
|
+
process.stdin.removeAllListeners("data");
|
|
22607
|
+
var proc = Bun.spawn(cmd, {
|
|
22608
|
+
cwd: cwd2,
|
|
22609
|
+
stdout: "inherit",
|
|
22610
|
+
stderr: "inherit",
|
|
22611
|
+
stdin: "pipe",
|
|
22612
|
+
env: env3
|
|
22613
|
+
});
|
|
22614
|
+
var forwardStdin = (data) => {
|
|
22615
|
+
try {
|
|
22616
|
+
const converted = Buffer.from(data.toString().replace(/\r/g, `
|
|
22617
|
+
`));
|
|
22618
|
+
proc.stdin.write(converted);
|
|
22619
|
+
proc.stdin.flush();
|
|
22620
|
+
} catch {}
|
|
22621
|
+
};
|
|
22622
|
+
process.stdin.on("data", forwardStdin);
|
|
22623
|
+
process.stdin.resume();
|
|
22624
|
+
var exitCode = await proc.exited;
|
|
22625
|
+
process.exit(exitCode);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kadai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"kadai": "./dist/cli.js"
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
"lint": "biome check ./src ./test",
|
|
12
12
|
"lint:fix": "biome check --write ./src ./test"
|
|
13
13
|
},
|
|
14
|
-
"files": [
|
|
14
|
+
"files": [
|
|
15
|
+
"dist/"
|
|
16
|
+
],
|
|
15
17
|
"devDependencies": {
|
|
16
18
|
"@biomejs/biome": "^2.3.14",
|
|
17
19
|
"@types/bun": "latest",
|