@kurtel/cli 0.1.5 → 0.1.8

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.
@@ -5,12 +5,12 @@ export async function runCommand(task, opts) {
5
5
  if (!task || task.trim() === "") {
6
6
  console.log(`${c.red(symbols.cross)} Provide a task, e.g. ${c.indigo('kurtel run "fix the flaky auth test"')}`);
7
7
  process.exitCode = 1;
8
- return;
8
+ return undefined;
9
9
  }
10
10
  if (opts.engine && !isEngineName(opts.engine)) {
11
11
  console.log(`${c.red(symbols.cross)} Unknown engine ${c.white(opts.engine)}. Use ${c.indigo("claude-code")} or ${c.indigo("codex")}.`);
12
12
  process.exitCode = 1;
13
- return;
13
+ return undefined;
14
14
  }
15
15
  try {
16
16
  const run = await createRun({
@@ -18,16 +18,20 @@ export async function runCommand(task, opts) {
18
18
  repo: opts.repo,
19
19
  branch: opts.branch,
20
20
  engine: opts.engine,
21
+ model: opts.model,
21
22
  });
22
23
  console.log("");
23
24
  console.log(`${symbols.check} Launched ${c.indigo(run.id)}`);
24
25
  console.log(`${c.gray("task")} ${c.white(run.task)}`);
25
26
  console.log(`${c.gray("engine")} ${c.white(run.engine)}`);
27
+ if (run.model)
28
+ console.log(`${c.gray("model")} ${c.white(run.model)}`);
26
29
  if (run.repo)
27
30
  console.log(`${c.gray("repo")} ${c.white(run.repo)}`);
28
31
  console.log(`${c.gray("status")} ${c.indigo(run.status)}`);
29
32
  console.log("");
30
33
  console.log(`${c.dim("Follow it with")} ${c.indigo(`kurtel logs ${run.id} --follow`)}`);
34
+ return run.id;
31
35
  }
32
36
  catch (e) {
33
37
  if (e instanceof AuthError) {
@@ -37,5 +41,6 @@ export async function runCommand(task, opts) {
37
41
  console.log(`${c.red(symbols.cross)} Failed to launch: ${e instanceof Error ? e.message : String(e)}`);
38
42
  }
39
43
  process.exitCode = 1;
44
+ return undefined;
40
45
  }
41
46
  }
@@ -1,21 +1,29 @@
1
1
  import * as readline from "node:readline";
2
2
  import { c, symbols } from "../ui/colors.js";
3
3
  import { banner, welcomeBox } from "../ui/banner.js";
4
- import { Spinner, sleep } from "../ui/spinner.js";
5
4
  import { loadConfig } from "../lib/config.js";
6
5
  import { agentsCommand } from "../commands/agents.js";
7
6
  import { loginCommand } from "../commands/auth.js";
8
7
  import { configCommand } from "../commands/config.js";
9
- import { previewNotice } from "../commands/_shared.js";
8
+ import { runCommand } from "../commands/run.js";
9
+ import { runsCommand } from "../commands/runs.js";
10
+ import { logsCommand, statusCommand, stopCommand } from "../commands/runtime.js";
10
11
  const SLASH_COMMANDS = [
11
12
  ["/help", "Show this help"],
12
13
  ["/run <task>", "Launch a cloud agent on a task"],
14
+ ["/runs", "List your recent runs"],
15
+ ["/logs [id]", "Stream a run's logs (defaults to the last one)"],
16
+ ["/status [id]", "Show a run's status & result"],
17
+ ["/stop [id]", "Cancel a run and tear down its sandbox"],
13
18
  ["/agents", "List active agents"],
14
19
  ["/login", "Sign in to Kurtel"],
15
20
  ["/config", "Show local configuration"],
16
21
  ["/clear", "Clear the screen"],
17
22
  ["/exit", "Quit the session"],
18
23
  ];
24
+ // Remember the most recently launched run so `/logs` and `/status` can default
25
+ // to it without making the user paste an id.
26
+ let lastRunId = null;
19
27
  function prompt() {
20
28
  return `${c.indigo("kurtel")} ${c.indigo(symbols.arrow)} `;
21
29
  }
@@ -29,14 +37,51 @@ function printHelp() {
29
37
  console.log(c.dim("Anything else you type is treated as a task and launches an agent."));
30
38
  console.log("");
31
39
  }
32
- async function launchFromPrompt(task) {
33
- const id = "agent-" + Math.random().toString(16).slice(2, 6);
34
- const spin = new Spinner("Provisioning sandbox & booting agent…").start();
35
- await sleep(700);
36
- spin.update("Planning approach…");
37
- await sleep(700);
38
- spin.succeed(`Queued ${c.indigo(id)} for: ${c.white(task)}`);
39
- previewNotice("Launching agents");
40
+ let asking = false;
41
+ function ask(rl, query) {
42
+ return new Promise((resolve) => {
43
+ rl.question(query, (answer) => resolve(answer.trim()));
44
+ });
45
+ }
46
+ async function launchFromPrompt(rl, task) {
47
+ const cfg = loadConfig();
48
+ const defBranch = cfg.defaultBranch || "main";
49
+ const defEngine = cfg.engine || "claude-code";
50
+ let repo = "";
51
+ let branch = defBranch;
52
+ let engine = defEngine;
53
+ let model = "";
54
+ asking = true;
55
+ try {
56
+ console.log("");
57
+ repo = await ask(rl, ` ${c.gray("repo")} ${c.dim("(owner/name, empty for none)")} ${c.indigo(symbols.arrow)} `);
58
+ branch =
59
+ (await ask(rl, ` ${c.gray("branch")} ${c.dim(`(default ${defBranch})`)} ${c.indigo(symbols.arrow)} `)) || defBranch;
60
+ engine =
61
+ (await ask(rl, ` ${c.gray("engine")} ${c.dim(`(default ${defEngine})`)} ${c.indigo(symbols.arrow)} `)) || defEngine;
62
+ model = await ask(rl, ` ${c.gray("model")} ${c.dim("(optional)")} ${c.indigo(symbols.arrow)} `);
63
+ }
64
+ finally {
65
+ asking = false;
66
+ }
67
+ const id = await runCommand(task, {
68
+ repo: repo || undefined,
69
+ branch,
70
+ engine,
71
+ model: model || undefined,
72
+ });
73
+ if (id) {
74
+ lastRunId = id;
75
+ console.log(`${c.dim("In here:")} ${c.indigo("/logs")} ${c.dim("to watch ·")} ${c.indigo("/runs")} ${c.dim("to list")}`);
76
+ }
77
+ }
78
+ function resolveId(arg) {
79
+ if (arg)
80
+ return arg;
81
+ if (lastRunId)
82
+ return lastRunId;
83
+ console.log(`${c.red(symbols.cross)} No run id given and no recent run. Try ${c.indigo("/runs")}.`);
84
+ return null;
40
85
  }
41
86
  export async function startSession(model) {
42
87
  console.log(banner());
@@ -53,6 +98,8 @@ export async function startSession(model) {
53
98
  });
54
99
  rl.prompt();
55
100
  rl.on("line", async (input) => {
101
+ if (asking)
102
+ return;
56
103
  const line = input.trim();
57
104
  if (line === "") {
58
105
  rl.prompt();
@@ -75,6 +122,39 @@ export async function startSession(model) {
75
122
  console.clear();
76
123
  console.log(banner());
77
124
  break;
125
+ case "runs":
126
+ case "ls":
127
+ rl.pause();
128
+ await runsCommand();
129
+ rl.resume();
130
+ break;
131
+ case "logs": {
132
+ const id = resolveId(arg);
133
+ if (id) {
134
+ rl.pause();
135
+ await logsCommand(id, { follow: true });
136
+ rl.resume();
137
+ }
138
+ break;
139
+ }
140
+ case "status": {
141
+ const id = resolveId(arg);
142
+ if (id) {
143
+ rl.pause();
144
+ await statusCommand(id);
145
+ rl.resume();
146
+ }
147
+ break;
148
+ }
149
+ case "stop": {
150
+ const id = resolveId(arg);
151
+ if (id) {
152
+ rl.pause();
153
+ await stopCommand(id);
154
+ rl.resume();
155
+ }
156
+ break;
157
+ }
78
158
  case "agents":
79
159
  case "ps":
80
160
  rl.pause();
@@ -92,14 +172,12 @@ export async function startSession(model) {
92
172
  rl.resume();
93
173
  break;
94
174
  case "run":
95
- rl.pause();
96
175
  if (!arg) {
97
176
  console.log(`${c.red(symbols.cross)} Usage: ${c.indigo("/run <task>")}`);
98
177
  }
99
178
  else {
100
- await launchFromPrompt(arg);
179
+ await launchFromPrompt(rl, arg);
101
180
  }
102
- rl.resume();
103
181
  break;
104
182
  default:
105
183
  console.log(`${c.red(symbols.cross)} Unknown command ${c.white("/" + cmd)}. Type ${c.indigo("/help")}.`);
@@ -107,21 +185,16 @@ export async function startSession(model) {
107
185
  rl.prompt();
108
186
  return;
109
187
  }
110
- // Free text -> treat as a task
111
- rl.pause();
112
- await launchFromPrompt(line);
113
- rl.resume();
188
+ await launchFromPrompt(rl, line);
114
189
  rl.prompt();
115
190
  });
116
191
  rl.on("close", () => {
117
192
  console.log(`\n${c.dim("See you soon. ")}${c.indigo("›")}\n`);
118
193
  process.exit(0);
119
194
  });
120
- // Ctrl+C: confirm-style behavior — first clears line, prompt again.
121
195
  rl.on("SIGINT", () => {
122
196
  console.log(`\n${c.dim("(use /exit or Ctrl+D to quit)")}`);
123
197
  rl.prompt();
124
198
  });
125
- // Touch config so a fresh user has it.
126
199
  loadConfig();
127
200
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kurtel/cli",
3
- "version": "0.1.5",
3
+ "version": "0.1.8",
4
4
  "description": "Launch self-improving coding agents in the cloud — the Kurtel CLI.",
5
5
  "type": "module",
6
6
  "bin": {