kadai 0.5.0 → 0.7.1
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 +15 -2
- package/dist/cli.js +370 -227
- package/dist/exports/ink.js +3 -0
- package/dist/exports/jsx-dev-runtime.js +3 -0
- package/dist/exports/jsx-runtime.js +3 -0
- package/dist/exports/react.js +7 -0
- package/dist/exports/ui.js +3 -0
- package/dist/types.js +1 -0
- package/package.json +10 -1
package/README.md
CHANGED
|
@@ -7,12 +7,25 @@
|
|
|
7
7
|
3. Share them with your team in the repo.
|
|
8
8
|
4. Automatically make them discoverable by AI.
|
|
9
9
|
|
|
10
|
+
## Prerequisites
|
|
11
|
+
|
|
12
|
+
kadai requires [Bun](https://bun.sh) as its runtime.
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# macOS / Linux
|
|
16
|
+
curl -fsSL https://bun.sh/install | bash
|
|
17
|
+
|
|
18
|
+
# Homebrew
|
|
19
|
+
brew install oven-sh/bun/bun
|
|
20
|
+
|
|
21
|
+
# Windows
|
|
22
|
+
powershell -c "irm bun.sh/install.ps1 | iex"
|
|
23
|
+
```
|
|
24
|
+
|
|
10
25
|
## Getting Started
|
|
11
26
|
|
|
12
27
|
```bash
|
|
13
28
|
bunx kadai
|
|
14
|
-
# OR
|
|
15
|
-
npx kadai
|
|
16
29
|
```
|
|
17
30
|
|
|
18
31
|
On first run, kadai creates a `.kadai/` directory with a sample action and config file. Run it again to open the interactive menu.
|
package/dist/cli.js
CHANGED
|
@@ -29,14 +29,67 @@ var __export = (target, all) => {
|
|
|
29
29
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
30
30
|
var __require = import.meta.require;
|
|
31
31
|
|
|
32
|
+
// src/core/shared-deps.ts
|
|
33
|
+
import { existsSync, mkdirSync, symlinkSync, unlinkSync } from "fs";
|
|
34
|
+
import { dirname, join } from "path";
|
|
35
|
+
function registerSharedDeps() {
|
|
36
|
+
const escaped = SHARED_DEPS.map((d) => d.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
37
|
+
const filter = new RegExp(`^(${escaped.join("|")})(/.*)?$`);
|
|
38
|
+
Bun.plugin({
|
|
39
|
+
name: "kadai-shared-deps",
|
|
40
|
+
setup(build) {
|
|
41
|
+
build.onResolve({ filter }, (args) => {
|
|
42
|
+
try {
|
|
43
|
+
return { path: __require.resolve(args.path) };
|
|
44
|
+
} catch {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function ensureKadaiResolvable(projectNodeModules) {
|
|
52
|
+
const link = join(projectNodeModules, "kadai");
|
|
53
|
+
if (existsSync(link))
|
|
54
|
+
return null;
|
|
55
|
+
const kadaiRoot = dirname(import.meta.dir);
|
|
56
|
+
if (!existsSync(join(kadaiRoot, "package.json")))
|
|
57
|
+
return null;
|
|
58
|
+
try {
|
|
59
|
+
if (!existsSync(projectNodeModules)) {
|
|
60
|
+
mkdirSync(projectNodeModules, { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
symlinkSync(kadaiRoot, link);
|
|
63
|
+
} catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
let cleaned = false;
|
|
67
|
+
const cleanup = () => {
|
|
68
|
+
if (cleaned)
|
|
69
|
+
return;
|
|
70
|
+
cleaned = true;
|
|
71
|
+
try {
|
|
72
|
+
unlinkSync(link);
|
|
73
|
+
} catch {}
|
|
74
|
+
};
|
|
75
|
+
process.on("exit", cleanup);
|
|
76
|
+
process.on("SIGTERM", cleanup);
|
|
77
|
+
process.on("SIGINT", cleanup);
|
|
78
|
+
return cleanup;
|
|
79
|
+
}
|
|
80
|
+
var SHARED_DEPS;
|
|
81
|
+
var init_shared_deps = __esm(() => {
|
|
82
|
+
SHARED_DEPS = ["ink", "react", "@inkjs/ui"];
|
|
83
|
+
});
|
|
84
|
+
|
|
32
85
|
// src/core/config.ts
|
|
33
86
|
var exports_config = {};
|
|
34
87
|
__export(exports_config, {
|
|
35
88
|
loadConfig: () => loadConfig
|
|
36
89
|
});
|
|
37
|
-
import { join } from "path";
|
|
90
|
+
import { join as join2 } from "path";
|
|
38
91
|
async function loadConfig(kadaiDir) {
|
|
39
|
-
const configPath =
|
|
92
|
+
const configPath = join2(kadaiDir, "config.ts");
|
|
40
93
|
const configFile = Bun.file(configPath);
|
|
41
94
|
if (!await configFile.exists()) {
|
|
42
95
|
return { ...DEFAULT_CONFIG };
|
|
@@ -150,7 +203,7 @@ var init_metadata = __esm(() => {
|
|
|
150
203
|
|
|
151
204
|
// src/core/loader.ts
|
|
152
205
|
import { readdir } from "fs/promises";
|
|
153
|
-
import { join as
|
|
206
|
+
import { join as join3 } from "path";
|
|
154
207
|
async function readShebang(filePath) {
|
|
155
208
|
try {
|
|
156
209
|
const head = await Bun.file(filePath).slice(0, 256).text();
|
|
@@ -192,7 +245,7 @@ async function getGitAddedDates(dir) {
|
|
|
192
245
|
if (/^\d+$/.test(trimmed)) {
|
|
193
246
|
currentTimestamp = Number.parseInt(trimmed, 10) * 1000;
|
|
194
247
|
} else {
|
|
195
|
-
const absPath =
|
|
248
|
+
const absPath = join3(repoRoot, trimmed);
|
|
196
249
|
dates.set(absPath, currentTimestamp);
|
|
197
250
|
}
|
|
198
251
|
}
|
|
@@ -218,7 +271,7 @@ async function scanDirectory(baseDir, currentDir, category, actions, depth, gitD
|
|
|
218
271
|
for (const entry of entries) {
|
|
219
272
|
if (entry.name.startsWith("_") || entry.name.startsWith("."))
|
|
220
273
|
continue;
|
|
221
|
-
const fullPath =
|
|
274
|
+
const fullPath = join3(currentDir, entry.name);
|
|
222
275
|
if (entry.isDirectory()) {
|
|
223
276
|
await scanDirectory(baseDir, fullPath, [...category, entry.name], actions, depth + 1, gitDates, origin);
|
|
224
277
|
} else if (entry.isFile()) {
|
|
@@ -247,15 +300,15 @@ async function scanDirectory(baseDir, currentDir, category, actions, depth, gitD
|
|
|
247
300
|
function findZcliDir(cwd) {
|
|
248
301
|
let dir = cwd;
|
|
249
302
|
while (true) {
|
|
250
|
-
const candidate =
|
|
251
|
-
if (Bun.file(
|
|
303
|
+
const candidate = join3(dir, ".kadai");
|
|
304
|
+
if (Bun.file(join3(candidate, "actions")).name) {
|
|
252
305
|
try {
|
|
253
306
|
const stat = __require("fs").statSync(candidate);
|
|
254
307
|
if (stat.isDirectory())
|
|
255
308
|
return candidate;
|
|
256
309
|
} catch {}
|
|
257
310
|
}
|
|
258
|
-
const parent =
|
|
311
|
+
const parent = join3(dir, "..");
|
|
259
312
|
if (parent === dir)
|
|
260
313
|
break;
|
|
261
314
|
dir = parent;
|
|
@@ -276,9 +329,44 @@ var init_loader = __esm(() => {
|
|
|
276
329
|
]);
|
|
277
330
|
});
|
|
278
331
|
|
|
332
|
+
// src/core/last-action.ts
|
|
333
|
+
import { join as join4 } from "path";
|
|
334
|
+
async function saveLastAction(kadaiDir, actionId) {
|
|
335
|
+
await Bun.write(join4(kadaiDir, LAST_ACTION_FILE), actionId);
|
|
336
|
+
await ensureGitignore(kadaiDir);
|
|
337
|
+
}
|
|
338
|
+
async function ensureGitignore(kadaiDir) {
|
|
339
|
+
const gitignorePath = join4(kadaiDir, ".gitignore");
|
|
340
|
+
const file = Bun.file(gitignorePath);
|
|
341
|
+
if (await file.exists()) {
|
|
342
|
+
const content = await file.text();
|
|
343
|
+
const lines = content.split(`
|
|
344
|
+
`).map((l) => l.trim());
|
|
345
|
+
if (!lines.includes(LAST_ACTION_FILE)) {
|
|
346
|
+
const suffix = content.endsWith(`
|
|
347
|
+
`) ? "" : `
|
|
348
|
+
`;
|
|
349
|
+
await Bun.write(gitignorePath, `${content}${suffix}${LAST_ACTION_FILE}
|
|
350
|
+
`);
|
|
351
|
+
}
|
|
352
|
+
} else {
|
|
353
|
+
await Bun.write(gitignorePath, `${LAST_ACTION_FILE}
|
|
354
|
+
`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
async function loadLastAction(kadaiDir) {
|
|
358
|
+
const file = Bun.file(join4(kadaiDir, LAST_ACTION_FILE));
|
|
359
|
+
if (!await file.exists())
|
|
360
|
+
return null;
|
|
361
|
+
const content = (await file.text()).trim();
|
|
362
|
+
return content || null;
|
|
363
|
+
}
|
|
364
|
+
var LAST_ACTION_FILE = ".last-action";
|
|
365
|
+
var init_last_action = () => {};
|
|
366
|
+
|
|
279
367
|
// src/core/fetchers/github.ts
|
|
280
368
|
import { mkdir, rm } from "fs/promises";
|
|
281
|
-
import { join as
|
|
369
|
+
import { join as join5 } from "path";
|
|
282
370
|
async function fetchGithubPlugin(source, destDir) {
|
|
283
371
|
const ref = source.ref ?? "main";
|
|
284
372
|
const repoUrl = `https://github.com/${source.github}.git`;
|
|
@@ -296,7 +384,7 @@ async function fetchGithubPlugin(source, destDir) {
|
|
|
296
384
|
});
|
|
297
385
|
const sha = (await new Response(shaProc.stdout).text()).trim();
|
|
298
386
|
await shaProc.exited;
|
|
299
|
-
await rm(
|
|
387
|
+
await rm(join5(destDir, ".git"), { recursive: true, force: true });
|
|
300
388
|
return { resolvedVersion: sha };
|
|
301
389
|
}
|
|
302
390
|
async function checkGithubUpdate(source, currentSha) {
|
|
@@ -321,29 +409,25 @@ async function checkGithubUpdate(source, currentSha) {
|
|
|
321
409
|
}
|
|
322
410
|
var init_github = () => {};
|
|
323
411
|
|
|
324
|
-
// src/core/
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
const withoutPrerelease = v.replace(/^v/, "").split("-")[0] ?? "";
|
|
329
|
-
const clean = withoutPrerelease.split("+")[0] ?? "";
|
|
330
|
-
const parts = clean.split(".");
|
|
331
|
-
if (parts.length !== 3)
|
|
412
|
+
// src/core/semver.ts
|
|
413
|
+
function parseSemver(version) {
|
|
414
|
+
const match = version.match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
415
|
+
if (!match)
|
|
332
416
|
return null;
|
|
333
|
-
|
|
334
|
-
if (nums.some((n) => Number.isNaN(n)))
|
|
335
|
-
return null;
|
|
336
|
-
return nums;
|
|
417
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
337
418
|
}
|
|
338
419
|
function compareSemver(a, b) {
|
|
339
420
|
for (let i = 0;i < 3; i++) {
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
return av - bv;
|
|
421
|
+
const diff = a[i] - b[i];
|
|
422
|
+
if (diff !== 0)
|
|
423
|
+
return diff;
|
|
344
424
|
}
|
|
345
425
|
return 0;
|
|
346
426
|
}
|
|
427
|
+
|
|
428
|
+
// src/core/fetchers/npm.ts
|
|
429
|
+
import { mkdir as mkdir2, unlink } from "fs/promises";
|
|
430
|
+
import { join as join6 } from "path";
|
|
347
431
|
function satisfies(version, range) {
|
|
348
432
|
if (range === "*" || range === "x")
|
|
349
433
|
return true;
|
|
@@ -417,7 +501,7 @@ async function fetchNpmPlugin(source, destDir) {
|
|
|
417
501
|
}
|
|
418
502
|
await mkdir2(destDir, { recursive: true });
|
|
419
503
|
const tarball = await tarballRes.arrayBuffer();
|
|
420
|
-
const tarballPath =
|
|
504
|
+
const tarballPath = join6(destDir, ".plugin.tgz");
|
|
421
505
|
await Bun.write(tarballPath, tarball);
|
|
422
506
|
const proc = Bun.spawn(["tar", "xzf", tarballPath, "--strip-components=1"], {
|
|
423
507
|
cwd: destDir,
|
|
@@ -457,9 +541,9 @@ var init_which = __esm(() => {
|
|
|
457
541
|
});
|
|
458
542
|
|
|
459
543
|
// src/core/pm.ts
|
|
460
|
-
import { join as
|
|
544
|
+
import { join as join7 } from "path";
|
|
461
545
|
async function resolvePM(dir) {
|
|
462
|
-
const pkgJsonPath =
|
|
546
|
+
const pkgJsonPath = join7(dir, "package.json");
|
|
463
547
|
try {
|
|
464
548
|
const file = Bun.file(pkgJsonPath);
|
|
465
549
|
if (await file.exists()) {
|
|
@@ -489,13 +573,13 @@ var init_pm = __esm(() => {
|
|
|
489
573
|
});
|
|
490
574
|
|
|
491
575
|
// src/core/plugins.ts
|
|
492
|
-
import { existsSync } from "fs";
|
|
576
|
+
import { existsSync as existsSync2 } from "fs";
|
|
493
577
|
import { mkdir as mkdir3, rm as rm2 } from "fs/promises";
|
|
494
|
-
import { isAbsolute, join as
|
|
578
|
+
import { isAbsolute, join as join8, resolve } from "path";
|
|
495
579
|
async function ensurePluginCacheDir(kadaiDir) {
|
|
496
|
-
const cacheDir =
|
|
580
|
+
const cacheDir = join8(kadaiDir, ".cache", "plugins");
|
|
497
581
|
await mkdir3(cacheDir, { recursive: true });
|
|
498
|
-
const gitignorePath =
|
|
582
|
+
const gitignorePath = join8(kadaiDir, ".cache", ".gitignore");
|
|
499
583
|
const gitignoreFile = Bun.file(gitignorePath);
|
|
500
584
|
if (!await gitignoreFile.exists()) {
|
|
501
585
|
await Bun.write(gitignorePath, `*
|
|
@@ -522,7 +606,7 @@ function pluginDisplayName(source) {
|
|
|
522
606
|
}
|
|
523
607
|
async function readPluginMeta(cacheDir) {
|
|
524
608
|
try {
|
|
525
|
-
const file = Bun.file(
|
|
609
|
+
const file = Bun.file(join8(cacheDir, ".plugin-meta.json"));
|
|
526
610
|
if (!await file.exists())
|
|
527
611
|
return null;
|
|
528
612
|
return await file.json();
|
|
@@ -531,18 +615,18 @@ async function readPluginMeta(cacheDir) {
|
|
|
531
615
|
}
|
|
532
616
|
}
|
|
533
617
|
async function writePluginMeta(cacheDir, meta) {
|
|
534
|
-
await Bun.write(
|
|
618
|
+
await Bun.write(join8(cacheDir, ".plugin-meta.json"), JSON.stringify(meta, null, 2));
|
|
535
619
|
}
|
|
536
620
|
async function loadCachedPlugins(kadaiDir, plugins) {
|
|
537
621
|
const allActions = [];
|
|
538
|
-
const cacheBase =
|
|
622
|
+
const cacheBase = join8(kadaiDir, ".cache", "plugins");
|
|
539
623
|
for (const source of plugins) {
|
|
540
624
|
if ("path" in source)
|
|
541
625
|
continue;
|
|
542
626
|
const key = cacheKeyFor(source);
|
|
543
|
-
const pluginCacheDir =
|
|
544
|
-
const actionsDir =
|
|
545
|
-
if (!
|
|
627
|
+
const pluginCacheDir = join8(cacheBase, key);
|
|
628
|
+
const actionsDir = join8(pluginCacheDir, "actions");
|
|
629
|
+
if (!existsSync2(actionsDir))
|
|
546
630
|
continue;
|
|
547
631
|
const name = pluginDisplayName(source);
|
|
548
632
|
const origin = { type: "plugin", pluginName: name };
|
|
@@ -556,8 +640,8 @@ async function loadCachedPlugins(kadaiDir, plugins) {
|
|
|
556
640
|
return allActions;
|
|
557
641
|
}
|
|
558
642
|
async function installPluginDeps(pluginDir) {
|
|
559
|
-
const pkgJsonPath =
|
|
560
|
-
if (!
|
|
643
|
+
const pkgJsonPath = join8(pluginDir, "package.json");
|
|
644
|
+
if (!existsSync2(pkgJsonPath))
|
|
561
645
|
return;
|
|
562
646
|
const pm = await resolvePM(pluginDir);
|
|
563
647
|
const proc = Bun.spawn(pm.install, {
|
|
@@ -574,7 +658,7 @@ async function installPluginDeps(pluginDir) {
|
|
|
574
658
|
async function syncPlugin(kadaiDir, source) {
|
|
575
659
|
const cacheBase = await ensurePluginCacheDir(kadaiDir);
|
|
576
660
|
const key = cacheKeyFor(source);
|
|
577
|
-
const pluginCacheDir =
|
|
661
|
+
const pluginCacheDir = join8(cacheBase, key);
|
|
578
662
|
const meta = await readPluginMeta(pluginCacheDir);
|
|
579
663
|
if (meta) {
|
|
580
664
|
let needsUpdate = false;
|
|
@@ -624,8 +708,8 @@ async function syncPlugins(kadaiDir, plugins, callbacks) {
|
|
|
624
708
|
}
|
|
625
709
|
async function loadPathPlugin(kadaiDir, source) {
|
|
626
710
|
const pluginRoot = isAbsolute(source.path) ? source.path : resolve(kadaiDir, source.path);
|
|
627
|
-
const actionsDir =
|
|
628
|
-
if (!
|
|
711
|
+
const actionsDir = join8(pluginRoot, "actions");
|
|
712
|
+
if (!existsSync2(actionsDir))
|
|
629
713
|
return [];
|
|
630
714
|
const name = source.path;
|
|
631
715
|
const origin = { type: "plugin", pluginName: name };
|
|
@@ -640,8 +724,8 @@ async function loadUserGlobalActions() {
|
|
|
640
724
|
const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
641
725
|
if (!homeDir)
|
|
642
726
|
return [];
|
|
643
|
-
const actionsDir =
|
|
644
|
-
if (!
|
|
727
|
+
const actionsDir = join8(homeDir, ".kadai", "actions");
|
|
728
|
+
if (!existsSync2(actionsDir))
|
|
645
729
|
return [];
|
|
646
730
|
const origin = { type: "plugin", pluginName: "~" };
|
|
647
731
|
const actions = await loadActions(actionsDir, origin);
|
|
@@ -729,13 +813,14 @@ var exports_commands = {};
|
|
|
729
813
|
__export(exports_commands, {
|
|
730
814
|
handleSync: () => handleSync,
|
|
731
815
|
handleRun: () => handleRun,
|
|
816
|
+
handleRerun: () => handleRerun,
|
|
732
817
|
handleList: () => handleList
|
|
733
818
|
});
|
|
734
|
-
import { join as
|
|
819
|
+
import { join as join9 } from "path";
|
|
735
820
|
async function handleList(options) {
|
|
736
821
|
const { kadaiDir, all } = options;
|
|
737
822
|
const config = await loadConfig(kadaiDir);
|
|
738
|
-
const actionsDir =
|
|
823
|
+
const actionsDir = join9(kadaiDir, config.actionsDir ?? "actions");
|
|
739
824
|
let actions = await loadActions(actionsDir);
|
|
740
825
|
const globalActions = await loadUserGlobalActions();
|
|
741
826
|
actions = [...actions, ...globalActions];
|
|
@@ -768,7 +853,7 @@ async function handleList(options) {
|
|
|
768
853
|
async function handleRun(options) {
|
|
769
854
|
const { kadaiDir, actionId, cwd } = options;
|
|
770
855
|
const config = await loadConfig(kadaiDir);
|
|
771
|
-
const actionsDir =
|
|
856
|
+
const actionsDir = join9(kadaiDir, config.actionsDir ?? "actions");
|
|
772
857
|
let actions = await loadActions(actionsDir);
|
|
773
858
|
const globalActions = await loadUserGlobalActions();
|
|
774
859
|
actions = [...actions, ...globalActions];
|
|
@@ -788,7 +873,9 @@ async function handleRun(options) {
|
|
|
788
873
|
`);
|
|
789
874
|
process.exit(1);
|
|
790
875
|
}
|
|
876
|
+
await saveLastAction(kadaiDir, actionId);
|
|
791
877
|
if (action.runtime === "ink") {
|
|
878
|
+
const cleanupKadai = ensureKadaiResolvable(join9(cwd, "node_modules"));
|
|
792
879
|
const mod = await import(action.filePath);
|
|
793
880
|
if (typeof mod.default !== "function") {
|
|
794
881
|
process.stderr.write(`Error: "${action.filePath}" does not export a default function component
|
|
@@ -806,6 +893,7 @@ async function handleRun(options) {
|
|
|
806
893
|
}));
|
|
807
894
|
await instance.waitUntilExit();
|
|
808
895
|
cleanupFullscreen?.();
|
|
896
|
+
cleanupKadai?.();
|
|
809
897
|
process.exit(0);
|
|
810
898
|
}
|
|
811
899
|
const cmd = resolveCommand(action);
|
|
@@ -829,6 +917,16 @@ async function handleRun(options) {
|
|
|
829
917
|
const exitCode = await proc.exited;
|
|
830
918
|
process.exit(exitCode);
|
|
831
919
|
}
|
|
920
|
+
async function handleRerun(options) {
|
|
921
|
+
const { kadaiDir, cwd } = options;
|
|
922
|
+
const actionId = await loadLastAction(kadaiDir);
|
|
923
|
+
if (!actionId) {
|
|
924
|
+
process.stderr.write(`No last action found. Run an action first before using --rerun.
|
|
925
|
+
`);
|
|
926
|
+
process.exit(1);
|
|
927
|
+
}
|
|
928
|
+
return handleRun({ kadaiDir, actionId, cwd });
|
|
929
|
+
}
|
|
832
930
|
async function handleSync(options) {
|
|
833
931
|
const { kadaiDir } = options;
|
|
834
932
|
const config = await loadConfig(kadaiDir);
|
|
@@ -875,6 +973,8 @@ All plugins synced.
|
|
|
875
973
|
var init_commands = __esm(() => {
|
|
876
974
|
init_config();
|
|
877
975
|
init_loader();
|
|
976
|
+
init_last_action();
|
|
977
|
+
init_shared_deps();
|
|
878
978
|
init_plugins();
|
|
879
979
|
init_runner();
|
|
880
980
|
});
|
|
@@ -883,11 +983,20 @@ var init_commands = __esm(() => {
|
|
|
883
983
|
var require_package = __commonJS((exports, module) => {
|
|
884
984
|
module.exports = {
|
|
885
985
|
name: "kadai",
|
|
886
|
-
version: "0.
|
|
986
|
+
version: "0.7.1",
|
|
887
987
|
type: "module",
|
|
888
988
|
bin: {
|
|
889
989
|
kadai: "./dist/cli.js"
|
|
890
990
|
},
|
|
991
|
+
exports: {
|
|
992
|
+
".": "./dist/cli.js",
|
|
993
|
+
"./types": "./dist/types.js",
|
|
994
|
+
"./ink": "./dist/exports/ink.js",
|
|
995
|
+
"./ui": "./dist/exports/ui.js",
|
|
996
|
+
"./react": "./dist/exports/react.js",
|
|
997
|
+
"./react/jsx-runtime": "./dist/exports/jsx-runtime.js",
|
|
998
|
+
"./react/jsx-dev-runtime": "./dist/exports/jsx-dev-runtime.js"
|
|
999
|
+
},
|
|
891
1000
|
scripts: {
|
|
892
1001
|
build: "bun build.ts",
|
|
893
1002
|
check: "tsc --noEmit && biome check ./src ./test",
|
|
@@ -924,7 +1033,7 @@ __export(exports_mcp, {
|
|
|
924
1033
|
ensureMcpConfig: () => ensureMcpConfig,
|
|
925
1034
|
actionIdToToolName: () => actionIdToToolName
|
|
926
1035
|
});
|
|
927
|
-
import { join as
|
|
1036
|
+
import { join as join10, resolve as resolve2 } from "path";
|
|
928
1037
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
929
1038
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
930
1039
|
function resolveInvocationCommand() {
|
|
@@ -951,40 +1060,35 @@ function buildToolDescription(action) {
|
|
|
951
1060
|
return parts.join(" ");
|
|
952
1061
|
}
|
|
953
1062
|
async function ensureMcpConfig(projectRoot) {
|
|
954
|
-
const mcpJsonPath =
|
|
1063
|
+
const mcpJsonPath = join10(projectRoot, ".mcp.json");
|
|
955
1064
|
const mcpFile = Bun.file(mcpJsonPath);
|
|
956
1065
|
const kadaiEntry = resolveInvocationCommand();
|
|
957
1066
|
if (await mcpFile.exists()) {
|
|
958
1067
|
const existing = await mcpFile.json();
|
|
959
1068
|
if (existing.mcpServers?.kadai) {
|
|
960
|
-
|
|
961
|
-
`);
|
|
962
|
-
return;
|
|
1069
|
+
return false;
|
|
963
1070
|
}
|
|
964
1071
|
existing.mcpServers = existing.mcpServers ?? {};
|
|
965
1072
|
existing.mcpServers.kadai = kadaiEntry;
|
|
966
1073
|
await Bun.write(mcpJsonPath, `${JSON.stringify(existing, null, 2)}
|
|
967
1074
|
`);
|
|
968
|
-
|
|
969
|
-
`);
|
|
970
|
-
} else {
|
|
971
|
-
const config = {
|
|
972
|
-
mcpServers: {
|
|
973
|
-
kadai: kadaiEntry
|
|
974
|
-
}
|
|
975
|
-
};
|
|
976
|
-
await Bun.write(mcpJsonPath, `${JSON.stringify(config, null, 2)}
|
|
977
|
-
`);
|
|
978
|
-
process.stderr.write(`Created .mcp.json with kadai MCP server config
|
|
979
|
-
`);
|
|
1075
|
+
return true;
|
|
980
1076
|
}
|
|
1077
|
+
const config = {
|
|
1078
|
+
mcpServers: {
|
|
1079
|
+
kadai: kadaiEntry
|
|
1080
|
+
}
|
|
1081
|
+
};
|
|
1082
|
+
await Bun.write(mcpJsonPath, `${JSON.stringify(config, null, 2)}
|
|
1083
|
+
`);
|
|
1084
|
+
return true;
|
|
981
1085
|
}
|
|
982
1086
|
async function startMcpServer(kadaiDir, cwd) {
|
|
983
1087
|
let visibleActions = [];
|
|
984
1088
|
let config = {};
|
|
985
1089
|
if (kadaiDir) {
|
|
986
1090
|
config = await loadConfig(kadaiDir);
|
|
987
|
-
const actionsDir =
|
|
1091
|
+
const actionsDir = join10(kadaiDir, config.actionsDir ?? "actions");
|
|
988
1092
|
let allActions = await loadActions(actionsDir);
|
|
989
1093
|
const globalActions = await loadUserGlobalActions();
|
|
990
1094
|
allActions = [...allActions, ...globalActions];
|
|
@@ -1051,6 +1155,168 @@ var init_mcp = __esm(() => {
|
|
|
1051
1155
|
init_runner();
|
|
1052
1156
|
});
|
|
1053
1157
|
|
|
1158
|
+
// src/core/init-wizard.ts
|
|
1159
|
+
var exports_init_wizard = {};
|
|
1160
|
+
__export(exports_init_wizard, {
|
|
1161
|
+
writeInitFiles: () => writeInitFiles,
|
|
1162
|
+
generateConfigFile: () => generateConfigFile,
|
|
1163
|
+
ensureClaudeIntegration: () => ensureClaudeIntegration
|
|
1164
|
+
});
|
|
1165
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
1166
|
+
import { join as join11 } from "path";
|
|
1167
|
+
function generateConfigFile() {
|
|
1168
|
+
const lines = [' // actionsDir: "actions",', " // env: {},"];
|
|
1169
|
+
return `export default {
|
|
1170
|
+
${lines.join(`
|
|
1171
|
+
`)}
|
|
1172
|
+
};
|
|
1173
|
+
`;
|
|
1174
|
+
}
|
|
1175
|
+
async function writeInitFiles(cwd) {
|
|
1176
|
+
const kadaiDir = join11(cwd, ".kadai");
|
|
1177
|
+
const actionsDir = join11(kadaiDir, "actions");
|
|
1178
|
+
mkdirSync2(actionsDir, { recursive: true });
|
|
1179
|
+
const sampleAction = join11(actionsDir, "hello.sh");
|
|
1180
|
+
const sampleFile = Bun.file(sampleAction);
|
|
1181
|
+
let sampleCreated = false;
|
|
1182
|
+
if (!await sampleFile.exists()) {
|
|
1183
|
+
await Bun.write(sampleAction, `#!/bin/bash
|
|
1184
|
+
# kadai:name Hello World
|
|
1185
|
+
# kadai:emoji \uD83D\uDC4B
|
|
1186
|
+
# kadai:description A sample action \u2014 edit or delete this file
|
|
1187
|
+
|
|
1188
|
+
echo "Hello from kadai!"
|
|
1189
|
+
echo "Add your own scripts to .kadai/actions/ to get started."
|
|
1190
|
+
`);
|
|
1191
|
+
sampleCreated = true;
|
|
1192
|
+
}
|
|
1193
|
+
const configContent = generateConfigFile();
|
|
1194
|
+
const configPath = join11(kadaiDir, "config.ts");
|
|
1195
|
+
await Bun.write(configPath, configContent);
|
|
1196
|
+
const integration = await ensureClaudeIntegration(cwd);
|
|
1197
|
+
return { sampleCreated, skillCreated: integration.skillCreated };
|
|
1198
|
+
}
|
|
1199
|
+
async function ensureClaudeIntegration(projectRoot) {
|
|
1200
|
+
const hasClaudeDir = existsSync3(join11(projectRoot, ".claude"));
|
|
1201
|
+
const hasClaudeMd = existsSync3(join11(projectRoot, "CLAUDE.md"));
|
|
1202
|
+
if (!hasClaudeDir && !hasClaudeMd) {
|
|
1203
|
+
return { skillCreated: false, mcpConfigured: false };
|
|
1204
|
+
}
|
|
1205
|
+
const skillCreated = await ensureSkillFile(projectRoot);
|
|
1206
|
+
const mcpConfigured = await ensureMcpJsonEntry(projectRoot);
|
|
1207
|
+
return { skillCreated, mcpConfigured };
|
|
1208
|
+
}
|
|
1209
|
+
async function ensureSkillFile(projectRoot) {
|
|
1210
|
+
const skillDir = join11(projectRoot, ".claude", "skills", "kadai");
|
|
1211
|
+
const skillPath = join11(skillDir, "SKILL.md");
|
|
1212
|
+
if (await Bun.file(skillPath).exists()) {
|
|
1213
|
+
return false;
|
|
1214
|
+
}
|
|
1215
|
+
mkdirSync2(skillDir, { recursive: true });
|
|
1216
|
+
await Bun.write(skillPath, generateSkillFile());
|
|
1217
|
+
return true;
|
|
1218
|
+
}
|
|
1219
|
+
async function ensureMcpJsonEntry(projectRoot) {
|
|
1220
|
+
const { ensureMcpConfig: ensureMcpConfig2 } = await Promise.resolve().then(() => (init_mcp(), exports_mcp));
|
|
1221
|
+
return await ensureMcpConfig2(projectRoot);
|
|
1222
|
+
}
|
|
1223
|
+
function generateSkillFile() {
|
|
1224
|
+
return `---
|
|
1225
|
+
name: kadai
|
|
1226
|
+
description: >-
|
|
1227
|
+
kadai is a script runner for this project. Discover available actions with
|
|
1228
|
+
kadai list --json, and run them with kadai run <action-id>.
|
|
1229
|
+
user-invocable: false
|
|
1230
|
+
---
|
|
1231
|
+
|
|
1232
|
+
# kadai \u2014 Project Script Runner
|
|
1233
|
+
|
|
1234
|
+
kadai manages and runs project-specific shell scripts stored in \`.kadai/actions/\`.
|
|
1235
|
+
|
|
1236
|
+
## Discovering Actions
|
|
1237
|
+
|
|
1238
|
+
\`\`\`bash
|
|
1239
|
+
kadai list --json
|
|
1240
|
+
\`\`\`
|
|
1241
|
+
|
|
1242
|
+
Returns a JSON array of available actions:
|
|
1243
|
+
|
|
1244
|
+
\`\`\`json
|
|
1245
|
+
[
|
|
1246
|
+
{
|
|
1247
|
+
"id": "database/reset",
|
|
1248
|
+
"name": "Reset Database",
|
|
1249
|
+
"emoji": "\uD83D\uDDD1\uFE0F",
|
|
1250
|
+
"description": "Drop and recreate the dev database",
|
|
1251
|
+
"category": ["database"],
|
|
1252
|
+
"runtime": "bash",
|
|
1253
|
+
"confirm": true
|
|
1254
|
+
}
|
|
1255
|
+
]
|
|
1256
|
+
\`\`\`
|
|
1257
|
+
|
|
1258
|
+
Use \`--all\` to include hidden actions: \`kadai list --json --all\`
|
|
1259
|
+
|
|
1260
|
+
Always use \`kadai list --json\` for the current set of actions \u2014 do not hardcode action lists.
|
|
1261
|
+
|
|
1262
|
+
## Running Actions
|
|
1263
|
+
|
|
1264
|
+
\`\`\`bash
|
|
1265
|
+
kadai run <action-id>
|
|
1266
|
+
\`\`\`
|
|
1267
|
+
|
|
1268
|
+
Runs the action and streams stdout/stderr directly. The process exits with the action's exit code.
|
|
1269
|
+
Confirmation prompts are automatically skipped in non-TTY environments.
|
|
1270
|
+
|
|
1271
|
+
### Examples
|
|
1272
|
+
|
|
1273
|
+
\`\`\`bash
|
|
1274
|
+
kadai run hello
|
|
1275
|
+
kadai run database/reset
|
|
1276
|
+
\`\`\`
|
|
1277
|
+
|
|
1278
|
+
## Creating Actions
|
|
1279
|
+
|
|
1280
|
+
Create a script file in \`.kadai/actions/\`. Supported extensions: \`.sh\`, \`.bash\`, \`.ts\`, \`.js\`, \`.mjs\`, \`.py\`, \`.tsx\`.
|
|
1281
|
+
|
|
1282
|
+
Add metadata as comments in the first 20 lines using \`# kadai:<key> <value>\` (for shell/python) or \`// kadai:<key> <value>\` (for JS/TS):
|
|
1283
|
+
|
|
1284
|
+
\`\`\`bash
|
|
1285
|
+
#!/bin/bash
|
|
1286
|
+
# kadai:name Deploy Staging
|
|
1287
|
+
# kadai:emoji \uD83D\uDE80
|
|
1288
|
+
# kadai:description Deploy the app to the staging environment
|
|
1289
|
+
# kadai:confirm true
|
|
1290
|
+
|
|
1291
|
+
echo "Deploying..."
|
|
1292
|
+
\`\`\`
|
|
1293
|
+
|
|
1294
|
+
Available metadata keys:
|
|
1295
|
+
|
|
1296
|
+
| Key | Description |
|
|
1297
|
+
|---------------|---------------------------------------------|
|
|
1298
|
+
| \`name\` | Display name in menus |
|
|
1299
|
+
| \`emoji\` | Emoji prefix |
|
|
1300
|
+
| \`description\` | Short description |
|
|
1301
|
+
| \`confirm\` | Require confirmation before running (true/false) |
|
|
1302
|
+
| \`hidden\` | Hide from default listing (true/false) |
|
|
1303
|
+
| \`fullscreen\` | Use alternate screen buffer for ink actions (true/false) |
|
|
1304
|
+
|
|
1305
|
+
If \`name\` is omitted, it is inferred from the filename (e.g. \`deploy-staging.sh\` \u2192 "Deploy Staging").
|
|
1306
|
+
|
|
1307
|
+
Organize actions into categories using subdirectories:
|
|
1308
|
+
|
|
1309
|
+
\`\`\`
|
|
1310
|
+
.kadai/actions/
|
|
1311
|
+
hello.sh \u2192 id: "hello"
|
|
1312
|
+
database/
|
|
1313
|
+
migrate.sh \u2192 id: "database/migrate"
|
|
1314
|
+
reset.ts \u2192 id: "database/reset"
|
|
1315
|
+
\`\`\`
|
|
1316
|
+
`;
|
|
1317
|
+
}
|
|
1318
|
+
var init_init_wizard = () => {};
|
|
1319
|
+
|
|
1054
1320
|
// src/components/Breadcrumbs.tsx
|
|
1055
1321
|
import { Box, Text } from "ink";
|
|
1056
1322
|
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
@@ -1087,6 +1353,7 @@ var init_FullscreenProvider = () => {};
|
|
|
1087
1353
|
|
|
1088
1354
|
// src/components/InkActionRenderer.tsx
|
|
1089
1355
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
1356
|
+
import { join as join12 } from "path";
|
|
1090
1357
|
import React, { useEffect, useState } from "react";
|
|
1091
1358
|
import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
|
|
1092
1359
|
function InkActionRenderer({
|
|
@@ -1100,6 +1367,7 @@ function InkActionRenderer({
|
|
|
1100
1367
|
let cancelled = false;
|
|
1101
1368
|
(async () => {
|
|
1102
1369
|
try {
|
|
1370
|
+
ensureKadaiResolvable(join12(cwd, "node_modules"));
|
|
1103
1371
|
const mod = await import(action.filePath);
|
|
1104
1372
|
if (cancelled)
|
|
1105
1373
|
return;
|
|
@@ -1162,6 +1430,7 @@ function InkActionRenderer({
|
|
|
1162
1430
|
}
|
|
1163
1431
|
var InkActionErrorBoundary;
|
|
1164
1432
|
var init_InkActionRenderer = __esm(() => {
|
|
1433
|
+
init_shared_deps();
|
|
1165
1434
|
InkActionErrorBoundary = class InkActionErrorBoundary extends React.Component {
|
|
1166
1435
|
constructor(props) {
|
|
1167
1436
|
super(props);
|
|
@@ -1212,7 +1481,7 @@ function StatusBar() {
|
|
|
1212
1481
|
var init_StatusBar = () => {};
|
|
1213
1482
|
|
|
1214
1483
|
// src/hooks/useActions.ts
|
|
1215
|
-
import { join as
|
|
1484
|
+
import { join as join13 } from "path";
|
|
1216
1485
|
import { useEffect as useEffect2, useRef, useState as useState2 } from "react";
|
|
1217
1486
|
function useActions({ kadaiDir }) {
|
|
1218
1487
|
const [actions, setActions] = useState2([]);
|
|
@@ -1225,7 +1494,7 @@ function useActions({ kadaiDir }) {
|
|
|
1225
1494
|
(async () => {
|
|
1226
1495
|
const cfg = await loadConfig(kadaiDir);
|
|
1227
1496
|
setConfig(cfg);
|
|
1228
|
-
const actionsDir =
|
|
1497
|
+
const actionsDir = join13(kadaiDir, cfg.actionsDir ?? "actions");
|
|
1229
1498
|
const localActions = await loadActions(actionsDir);
|
|
1230
1499
|
let allActions = [...localActions];
|
|
1231
1500
|
const globalActions = await loadUserGlobalActions();
|
|
@@ -2267,6 +2536,7 @@ function MenuList({
|
|
|
2267
2536
|
}
|
|
2268
2537
|
const selected = i === selectedIndex;
|
|
2269
2538
|
return /* @__PURE__ */ jsxDEV5(Box4, {
|
|
2539
|
+
width: "100%",
|
|
2270
2540
|
children: [
|
|
2271
2541
|
/* @__PURE__ */ jsxDEV5(Text4, {
|
|
2272
2542
|
color: selected ? "cyan" : undefined,
|
|
@@ -2288,14 +2558,13 @@ function MenuList({
|
|
|
2288
2558
|
dimColor: true,
|
|
2289
2559
|
children: " \u27F3"
|
|
2290
2560
|
}, undefined, false, undefined, this),
|
|
2561
|
+
/* @__PURE__ */ jsxDEV5(Box4, {
|
|
2562
|
+
flexGrow: 1
|
|
2563
|
+
}, undefined, false, undefined, this),
|
|
2291
2564
|
item.description && /* @__PURE__ */ jsxDEV5(Text4, {
|
|
2292
2565
|
dimColor: true,
|
|
2293
|
-
children:
|
|
2294
|
-
|
|
2295
|
-
item.description,
|
|
2296
|
-
")"
|
|
2297
|
-
]
|
|
2298
|
-
}, undefined, true, undefined, this)
|
|
2566
|
+
children: item.description
|
|
2567
|
+
}, undefined, false, undefined, this)
|
|
2299
2568
|
]
|
|
2300
2569
|
}, `${i}-${item.value}`, true, undefined, this);
|
|
2301
2570
|
})
|
|
@@ -2548,153 +2817,8 @@ var init_app = __esm(() => {
|
|
|
2548
2817
|
SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;
|
|
2549
2818
|
});
|
|
2550
2819
|
|
|
2551
|
-
// src/
|
|
2552
|
-
|
|
2553
|
-
__export(exports_init_wizard, {
|
|
2554
|
-
writeInitFiles: () => writeInitFiles,
|
|
2555
|
-
generateConfigFile: () => generateConfigFile
|
|
2556
|
-
});
|
|
2557
|
-
import { existsSync as existsSync2, mkdirSync } from "fs";
|
|
2558
|
-
import { join as join10 } from "path";
|
|
2559
|
-
function generateConfigFile() {
|
|
2560
|
-
const lines = [' // actionsDir: "actions",', " // env: {},"];
|
|
2561
|
-
return `export default {
|
|
2562
|
-
${lines.join(`
|
|
2563
|
-
`)}
|
|
2564
|
-
};
|
|
2565
|
-
`;
|
|
2566
|
-
}
|
|
2567
|
-
async function writeInitFiles(cwd) {
|
|
2568
|
-
const kadaiDir = join10(cwd, ".kadai");
|
|
2569
|
-
const actionsDir = join10(kadaiDir, "actions");
|
|
2570
|
-
mkdirSync(actionsDir, { recursive: true });
|
|
2571
|
-
const sampleAction = join10(actionsDir, "hello.sh");
|
|
2572
|
-
const sampleFile = Bun.file(sampleAction);
|
|
2573
|
-
let sampleCreated = false;
|
|
2574
|
-
if (!await sampleFile.exists()) {
|
|
2575
|
-
await Bun.write(sampleAction, `#!/bin/bash
|
|
2576
|
-
# kadai:name Hello World
|
|
2577
|
-
# kadai:emoji \uD83D\uDC4B
|
|
2578
|
-
# kadai:description A sample action \u2014 edit or delete this file
|
|
2579
|
-
|
|
2580
|
-
echo "Hello from kadai!"
|
|
2581
|
-
echo "Add your own scripts to .kadai/actions/ to get started."
|
|
2582
|
-
`);
|
|
2583
|
-
sampleCreated = true;
|
|
2584
|
-
}
|
|
2585
|
-
const configContent = generateConfigFile();
|
|
2586
|
-
const configPath = join10(kadaiDir, "config.ts");
|
|
2587
|
-
await Bun.write(configPath, configContent);
|
|
2588
|
-
let skillCreated = false;
|
|
2589
|
-
const hasClaudeDir = existsSync2(join10(cwd, ".claude"));
|
|
2590
|
-
const hasClaudeMd = existsSync2(join10(cwd, "CLAUDE.md"));
|
|
2591
|
-
if (hasClaudeDir || hasClaudeMd) {
|
|
2592
|
-
const skillDir = join10(cwd, ".claude", "skills", "kadai");
|
|
2593
|
-
const skillPath = join10(skillDir, "SKILL.md");
|
|
2594
|
-
if (!await Bun.file(skillPath).exists()) {
|
|
2595
|
-
mkdirSync(skillDir, { recursive: true });
|
|
2596
|
-
await Bun.write(skillPath, generateSkillFile());
|
|
2597
|
-
skillCreated = true;
|
|
2598
|
-
}
|
|
2599
|
-
}
|
|
2600
|
-
return { sampleCreated, skillCreated };
|
|
2601
|
-
}
|
|
2602
|
-
function generateSkillFile() {
|
|
2603
|
-
return `---
|
|
2604
|
-
name: kadai
|
|
2605
|
-
description: >-
|
|
2606
|
-
kadai is a script runner for this project. Discover available actions with
|
|
2607
|
-
kadai list --json, and run them with kadai run <action-id>.
|
|
2608
|
-
user-invocable: false
|
|
2609
|
-
---
|
|
2610
|
-
|
|
2611
|
-
# kadai \u2014 Project Script Runner
|
|
2612
|
-
|
|
2613
|
-
kadai manages and runs project-specific shell scripts stored in \`.kadai/actions/\`.
|
|
2614
|
-
|
|
2615
|
-
## Discovering Actions
|
|
2616
|
-
|
|
2617
|
-
\`\`\`bash
|
|
2618
|
-
kadai list --json
|
|
2619
|
-
\`\`\`
|
|
2620
|
-
|
|
2621
|
-
Returns a JSON array of available actions:
|
|
2622
|
-
|
|
2623
|
-
\`\`\`json
|
|
2624
|
-
[
|
|
2625
|
-
{
|
|
2626
|
-
"id": "database/reset",
|
|
2627
|
-
"name": "Reset Database",
|
|
2628
|
-
"emoji": "\uD83D\uDDD1\uFE0F",
|
|
2629
|
-
"description": "Drop and recreate the dev database",
|
|
2630
|
-
"category": ["database"],
|
|
2631
|
-
"runtime": "bash",
|
|
2632
|
-
"confirm": true
|
|
2633
|
-
}
|
|
2634
|
-
]
|
|
2635
|
-
\`\`\`
|
|
2636
|
-
|
|
2637
|
-
Use \`--all\` to include hidden actions: \`kadai list --json --all\`
|
|
2638
|
-
|
|
2639
|
-
Always use \`kadai list --json\` for the current set of actions \u2014 do not hardcode action lists.
|
|
2640
|
-
|
|
2641
|
-
## Running Actions
|
|
2642
|
-
|
|
2643
|
-
\`\`\`bash
|
|
2644
|
-
kadai run <action-id>
|
|
2645
|
-
\`\`\`
|
|
2646
|
-
|
|
2647
|
-
Runs the action and streams stdout/stderr directly. The process exits with the action's exit code.
|
|
2648
|
-
Confirmation prompts are automatically skipped in non-TTY environments.
|
|
2649
|
-
|
|
2650
|
-
### Examples
|
|
2651
|
-
|
|
2652
|
-
\`\`\`bash
|
|
2653
|
-
kadai run hello
|
|
2654
|
-
kadai run database/reset
|
|
2655
|
-
\`\`\`
|
|
2656
|
-
|
|
2657
|
-
## Creating Actions
|
|
2658
|
-
|
|
2659
|
-
Create a script file in \`.kadai/actions/\`. Supported extensions: \`.sh\`, \`.bash\`, \`.ts\`, \`.js\`, \`.mjs\`, \`.py\`, \`.tsx\`.
|
|
2660
|
-
|
|
2661
|
-
Add metadata as comments in the first 20 lines using \`# kadai:<key> <value>\` (for shell/python) or \`// kadai:<key> <value>\` (for JS/TS):
|
|
2662
|
-
|
|
2663
|
-
\`\`\`bash
|
|
2664
|
-
#!/bin/bash
|
|
2665
|
-
# kadai:name Deploy Staging
|
|
2666
|
-
# kadai:emoji \uD83D\uDE80
|
|
2667
|
-
# kadai:description Deploy the app to the staging environment
|
|
2668
|
-
# kadai:confirm true
|
|
2669
|
-
|
|
2670
|
-
echo "Deploying..."
|
|
2671
|
-
\`\`\`
|
|
2672
|
-
|
|
2673
|
-
Available metadata keys:
|
|
2674
|
-
|
|
2675
|
-
| Key | Description |
|
|
2676
|
-
|---------------|---------------------------------------------|
|
|
2677
|
-
| \`name\` | Display name in menus |
|
|
2678
|
-
| \`emoji\` | Emoji prefix |
|
|
2679
|
-
| \`description\` | Short description |
|
|
2680
|
-
| \`confirm\` | Require confirmation before running (true/false) |
|
|
2681
|
-
| \`hidden\` | Hide from default listing (true/false) |
|
|
2682
|
-
| \`fullscreen\` | Use alternate screen buffer for ink actions (true/false) |
|
|
2683
|
-
|
|
2684
|
-
If \`name\` is omitted, it is inferred from the filename (e.g. \`deploy-staging.sh\` \u2192 "Deploy Staging").
|
|
2685
|
-
|
|
2686
|
-
Organize actions into categories using subdirectories:
|
|
2687
|
-
|
|
2688
|
-
\`\`\`
|
|
2689
|
-
.kadai/actions/
|
|
2690
|
-
hello.sh \u2192 id: "hello"
|
|
2691
|
-
database/
|
|
2692
|
-
migrate.sh \u2192 id: "database/migrate"
|
|
2693
|
-
reset.ts \u2192 id: "database/reset"
|
|
2694
|
-
\`\`\`
|
|
2695
|
-
`;
|
|
2696
|
-
}
|
|
2697
|
-
var init_init_wizard = () => {};
|
|
2820
|
+
// src/cli.tsx
|
|
2821
|
+
init_shared_deps();
|
|
2698
2822
|
|
|
2699
2823
|
// src/core/args.ts
|
|
2700
2824
|
function parseArgs(argv) {
|
|
@@ -2705,6 +2829,9 @@ function parseArgs(argv) {
|
|
|
2705
2829
|
if (command === "--version" || command === "-v") {
|
|
2706
2830
|
return { type: "version" };
|
|
2707
2831
|
}
|
|
2832
|
+
if (command === "--rerun" || command === "-r") {
|
|
2833
|
+
return { type: "rerun" };
|
|
2834
|
+
}
|
|
2708
2835
|
switch (command) {
|
|
2709
2836
|
case "list": {
|
|
2710
2837
|
if (!argv.includes("--json")) {
|
|
@@ -2730,7 +2857,7 @@ function parseArgs(argv) {
|
|
|
2730
2857
|
default:
|
|
2731
2858
|
return {
|
|
2732
2859
|
type: "error",
|
|
2733
|
-
message: `Unknown command: ${command}. Available commands: list, run, sync, mcp, --version`
|
|
2860
|
+
message: `Unknown command: ${command}. Available commands: list, run, sync, mcp, --version, --rerun`
|
|
2734
2861
|
};
|
|
2735
2862
|
}
|
|
2736
2863
|
}
|
|
@@ -2738,6 +2865,8 @@ function parseArgs(argv) {
|
|
|
2738
2865
|
// src/cli.tsx
|
|
2739
2866
|
init_commands();
|
|
2740
2867
|
init_loader();
|
|
2868
|
+
init_last_action();
|
|
2869
|
+
registerSharedDeps();
|
|
2741
2870
|
var cwd = process.cwd();
|
|
2742
2871
|
var parsed = parseArgs(process.argv.slice(2));
|
|
2743
2872
|
if (parsed.type === "error") {
|
|
@@ -2758,17 +2887,22 @@ if (parsed.type === "mcp") {
|
|
|
2758
2887
|
await startMcpServer2(kadaiDir, cwd);
|
|
2759
2888
|
await new Promise(() => {});
|
|
2760
2889
|
}
|
|
2761
|
-
if (parsed.type === "list" || parsed.type === "run" || parsed.type === "sync") {
|
|
2890
|
+
if (parsed.type === "list" || parsed.type === "run" || parsed.type === "sync" || parsed.type === "rerun") {
|
|
2762
2891
|
const kadaiDir = findZcliDir(cwd);
|
|
2763
2892
|
if (!kadaiDir) {
|
|
2764
2893
|
process.stderr.write(`Error: No .kadai directory found. Run kadai to initialize.
|
|
2765
2894
|
`);
|
|
2766
2895
|
process.exit(1);
|
|
2767
2896
|
}
|
|
2897
|
+
const { dirname: dirname2 } = await import("path");
|
|
2898
|
+
const { ensureClaudeIntegration: ensureClaudeIntegration2 } = await Promise.resolve().then(() => (init_init_wizard(), exports_init_wizard));
|
|
2899
|
+
await ensureClaudeIntegration2(dirname2(kadaiDir));
|
|
2768
2900
|
if (parsed.type === "list") {
|
|
2769
2901
|
await handleList({ kadaiDir, all: parsed.all });
|
|
2770
2902
|
} else if (parsed.type === "run") {
|
|
2771
2903
|
await handleRun({ kadaiDir, actionId: parsed.actionId, cwd });
|
|
2904
|
+
} else if (parsed.type === "rerun") {
|
|
2905
|
+
await handleRerun({ kadaiDir, cwd });
|
|
2772
2906
|
} else {
|
|
2773
2907
|
const { handleSync: handleSync2 } = await Promise.resolve().then(() => (init_commands(), exports_commands));
|
|
2774
2908
|
await handleSync2({ kadaiDir });
|
|
@@ -2794,12 +2928,20 @@ if (!kadaiDir) {
|
|
|
2794
2928
|
console.log(" Created .kadai/config.ts");
|
|
2795
2929
|
if (result.sampleCreated)
|
|
2796
2930
|
console.log(" Created .kadai/actions/hello.sh");
|
|
2797
|
-
if (result.skillCreated)
|
|
2798
|
-
console.log(" Created .claude/skills/kadai/SKILL.md");
|
|
2799
2931
|
console.log(`
|
|
2800
2932
|
Done! Run kadai again to get started.`);
|
|
2801
2933
|
process.exit(0);
|
|
2802
2934
|
}
|
|
2935
|
+
{
|
|
2936
|
+
const { dirname: dirname2 } = await import("path");
|
|
2937
|
+
const { ensureClaudeIntegration: ensureClaudeIntegration2 } = await Promise.resolve().then(() => (init_init_wizard(), exports_init_wizard));
|
|
2938
|
+
const projectRoot = dirname2(kadaiDir);
|
|
2939
|
+
const ensured = await ensureClaudeIntegration2(projectRoot);
|
|
2940
|
+
if (ensured.skillCreated)
|
|
2941
|
+
console.log("Created .claude/skills/kadai/SKILL.md");
|
|
2942
|
+
if (ensured.mcpConfigured)
|
|
2943
|
+
console.log("Configured kadai in .mcp.json");
|
|
2944
|
+
}
|
|
2803
2945
|
function createStdinStream() {
|
|
2804
2946
|
if (process.stdin.isTTY) {
|
|
2805
2947
|
return process.stdin;
|
|
@@ -2881,6 +3023,7 @@ await instance.waitUntilExit();
|
|
|
2881
3023
|
if (!selectedAction)
|
|
2882
3024
|
process.exit(0);
|
|
2883
3025
|
var action = selectedAction;
|
|
3026
|
+
await saveLastAction(kadaiDir, action.id);
|
|
2884
3027
|
var config = await loadConfig2(kadaiDir);
|
|
2885
3028
|
var cmd = resolveCommand2(action);
|
|
2886
3029
|
var env = {
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// @bun
|
package/package.json
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kadai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"kadai": "./dist/cli.js"
|
|
7
7
|
},
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./dist/cli.js",
|
|
10
|
+
"./types": "./dist/types.js",
|
|
11
|
+
"./ink": "./dist/exports/ink.js",
|
|
12
|
+
"./ui": "./dist/exports/ui.js",
|
|
13
|
+
"./react": "./dist/exports/react.js",
|
|
14
|
+
"./react/jsx-runtime": "./dist/exports/jsx-runtime.js",
|
|
15
|
+
"./react/jsx-dev-runtime": "./dist/exports/jsx-dev-runtime.js"
|
|
16
|
+
},
|
|
8
17
|
"scripts": {
|
|
9
18
|
"build": "bun build.ts",
|
|
10
19
|
"check": "tsc --noEmit && biome check ./src ./test",
|