@agent-api/cli 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -73,6 +73,17 @@ Launch the first-class TUI from your current directory:
73
73
  agent-tui
74
74
  ```
75
75
 
76
+ Open a specific local workdir and expose local tools to the agent:
77
+
78
+ ```bash
79
+ agent-tui .
80
+ agent-tui ./my-workdir
81
+ agent-tui /absolute/path/to/my-workdir
82
+ ```
83
+
84
+ The workdir argument must point to an existing directory. When provided, the
85
+ workbench automatically turns on local workdir and shell tools in approval mode.
86
+
76
87
  The workbench opens with auth as the first gate. If the active profile is valid,
77
88
  it enters the conversation UI automatically. If not, it shows an in-terminal
78
89
  auth picker for browser session or API key login.
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command, Option } from "commander";
3
+ import { stat } from "node:fs/promises";
4
+ import { resolve } from "node:path";
3
5
  import { render } from "ink";
4
6
  import React from "react";
5
7
  import { conversationSummary, deleteConversation, getConversation, listConversations, runAgent } from "./agent.js";
@@ -15,15 +17,17 @@ program
15
17
  .alias("agentsway")
16
18
  .alias("agent-tui")
17
19
  .description("First-class command line interface for Agent API")
20
+ .argument("[workdir]", "local workdir to open and expose to the agent")
18
21
  .version(cliVersion)
19
22
  .showHelpAfterError()
20
23
  .showSuggestionAfterError();
21
- program.action(async () => {
24
+ program.action(async (workdir) => {
25
+ const launchWorkdir = workdir ? await validateLaunchWorkdir(workdir) : undefined;
22
26
  if (!process.stdin.isTTY) {
23
27
  program.help();
24
28
  return;
25
29
  }
26
- const options = normalizeChatOptions([], {});
30
+ const options = normalizeChatOptions([], launchWorkdir ? { workdir: launchWorkdir } : {});
27
31
  const app = render(React.createElement(ChatApp, { options }));
28
32
  await app.waitUntilExit();
29
33
  });
@@ -306,3 +310,17 @@ function optionalNumber(value, label) {
306
310
  throw new Error(`${label} must be a number`);
307
311
  return parsed;
308
312
  }
313
+ async function validateLaunchWorkdir(path) {
314
+ const resolved = resolve(path);
315
+ let info;
316
+ try {
317
+ info = await stat(resolved);
318
+ }
319
+ catch {
320
+ throw new Error(`Workdir does not exist: ${path}`);
321
+ }
322
+ if (!info.isDirectory()) {
323
+ throw new Error(`Workdir is not a directory: ${path}`);
324
+ }
325
+ return resolved;
326
+ }
@@ -1,5 +1,5 @@
1
1
  export declare const cliName = "agent-api-cli";
2
2
  export declare const cliAuthor = "AgentsWay";
3
- export declare const cliVersion = "0.0.1";
3
+ export declare const cliVersion = "0.0.3";
4
4
  export declare const runtime: import("@agent-api/sdk/local").LocalRuntime;
5
5
  export declare function ensureRuntime(): Promise<import("@agent-api/sdk/local").LocalRuntime>;
@@ -1,7 +1,7 @@
1
1
  import { createLocalRuntime } from "@agent-api/sdk/local";
2
2
  export const cliName = "agent-api-cli";
3
3
  export const cliAuthor = "AgentsWay";
4
- export const cliVersion = "0.0.1";
4
+ export const cliVersion = "0.0.3";
5
5
  export const runtime = createLocalRuntime({
6
6
  appName: cliName,
7
7
  appAuthor: cliAuthor,
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
- import { type AgentRunOptions } from "../agent.js";
2
+ import { listAvailablePresets, type AgentRunOptions } from "../agent.js";
3
3
  export declare function ChatApp({ options }: {
4
4
  options: AgentRunOptions;
5
5
  }): React.JSX.Element;
6
+ export declare function formatPresetList(presets: Awaited<ReturnType<typeof listAvailablePresets>>, currentPreset?: string): string[];
package/dist/tui/chat.js CHANGED
@@ -5,7 +5,7 @@ import { defaultBaseURL, loadWorkbenchPreferences, updateWorkbenchPreferences, }
5
5
  import { conversationSummary, clearPresetToolCatalogCache, deleteConversation, isAvailablePreset, listAvailablePresets, listConversations, resumeAgentAfterLocalApproval, runAgentTurn, } from "../agent.js";
6
6
  import { openWorkdir, } from "../workdir/index.js";
7
7
  import { createLocalShellToolRegistry, createLocalWorkdirToolRegistry } from "@agent-api/sdk/local";
8
- import { activityColor, createInitialWorkbenchState, formatBytes, helpText, parsePendingApprovalCommand, parseWorkbenchCommand, workbenchReducer, workdirText, } from "./workbench.js";
8
+ import { activityColor, createInputHistory, createInitialWorkbenchState, formatBytes, helpText, parsePendingApprovalCommand, parseWorkbenchCommand, workbenchReducer, workdirText, } from "./workbench.js";
9
9
  import { deleteProfile, formatDeviceUserCode, getAuthStatus, loginWithAPIKey, openBrowserURL, refreshActiveProfileIfNeeded, resolveRuntimeProfile, saveBrowserProfile, startBrowserAuthChallenge, waitForBrowserAuthChallenge, } from "../profile.js";
10
10
  import { checkForUpdate, formatUpdateNotice } from "../update.js";
11
11
  export function ChatApp({ options }) {
@@ -254,6 +254,7 @@ function WorkbenchApp({ onLogin, onLogout, onDeleteProfile, onSwitchProfile, opt
254
254
  const activeResponseIDRef = useRef(null);
255
255
  const cancelledResponseIDsRef = useRef(new Set());
256
256
  const updateNoticeShownRef = useRef(false);
257
+ const inputHistoryRef = useRef(createInputHistory());
257
258
  const [draft, setDraft] = useState("");
258
259
  const [spinnerFrame, setSpinnerFrame] = useState(0);
259
260
  const [runPreset, setRunPreset] = useState(options.preset);
@@ -371,6 +372,14 @@ function WorkbenchApp({ onLogin, onLogout, onDeleteProfile, onSwitchProfile, opt
371
372
  app.exit();
372
373
  return;
373
374
  }
375
+ if (key.upArrow) {
376
+ setDraft((current) => inputHistoryRef.current.previous(current));
377
+ return;
378
+ }
379
+ if (key.downArrow) {
380
+ setDraft((current) => inputHistoryRef.current.next(current));
381
+ return;
382
+ }
374
383
  if (state.busy) {
375
384
  if (key.escape) {
376
385
  void abortActiveTurn("Abort requested.");
@@ -378,6 +387,7 @@ function WorkbenchApp({ onLogin, onLogout, onDeleteProfile, onSwitchProfile, opt
378
387
  }
379
388
  if (key.return) {
380
389
  const command = draft.trim();
390
+ inputHistoryRef.current.record(command);
381
391
  setDraft("");
382
392
  if (command === "/abort" || command === "/cancel") {
383
393
  void abortActiveTurn("Abort requested.");
@@ -390,10 +400,12 @@ function WorkbenchApp({ onLogin, onLogout, onDeleteProfile, onSwitchProfile, opt
390
400
  return;
391
401
  }
392
402
  if (key.backspace || key.delete) {
403
+ inputHistoryRef.current.reset();
393
404
  setDraft((current) => current.slice(0, -1));
394
405
  return;
395
406
  }
396
407
  if (input && !key.ctrl && !key.meta) {
408
+ inputHistoryRef.current.reset();
397
409
  setDraft((current) => current + input);
398
410
  }
399
411
  return;
@@ -402,15 +414,18 @@ function WorkbenchApp({ onLogin, onLogout, onDeleteProfile, onSwitchProfile, opt
402
414
  const prompt = draft.trim();
403
415
  if (!prompt)
404
416
  return;
417
+ inputHistoryRef.current.record(prompt);
405
418
  setDraft("");
406
419
  void submit(prompt);
407
420
  return;
408
421
  }
409
422
  if (key.backspace || key.delete) {
423
+ inputHistoryRef.current.reset();
410
424
  setDraft((current) => current.slice(0, -1));
411
425
  return;
412
426
  }
413
427
  if (input && !key.ctrl && !key.meta) {
428
+ inputHistoryRef.current.reset();
414
429
  setDraft((current) => current + input);
415
430
  }
416
431
  });
@@ -699,7 +714,7 @@ function WorkbenchApp({ onLogin, onLogout, onDeleteProfile, onSwitchProfile, opt
699
714
  prefix,
700
715
  "",
701
716
  "Available presets:",
702
- ...formatPresetList(presets),
717
+ ...formatPresetList(presets, runPreset),
703
718
  ].join("\n");
704
719
  }
705
720
  catch (error) {
@@ -1208,12 +1223,13 @@ function effectiveDefaultPreset(preferences, builtInPreset) {
1208
1223
  return preferences.defaultPreset ?? undefined;
1209
1224
  return builtInPreset;
1210
1225
  }
1211
- function formatPresetList(presets) {
1226
+ export function formatPresetList(presets, currentPreset) {
1212
1227
  if (presets.length === 0)
1213
1228
  return ["- none returned by this endpoint"];
1214
1229
  return presets.map((preset) => {
1215
1230
  const description = preset.description ? ` - ${preset.description}` : "";
1216
- return `- ${preset.preset}${description}`;
1231
+ const current = currentPreset && preset.preset === currentPreset;
1232
+ return `${current ? "*" : "-"} ${preset.preset}${current ? " (current)" : ""}${description}`;
1217
1233
  });
1218
1234
  }
1219
1235
  function authStatusText(status) {
@@ -34,6 +34,13 @@ export interface WorkbenchState {
34
34
  accessMode: WorkdirAccessMode;
35
35
  currentConversation: string;
36
36
  }
37
+ export interface InputHistory {
38
+ record(value: string): void;
39
+ previous(currentDraft: string): string;
40
+ next(currentDraft: string): string;
41
+ reset(): void;
42
+ values(): string[];
43
+ }
37
44
  export type WorkbenchAction = {
38
45
  type: "message.add";
39
46
  role: WorkbenchRole;
@@ -145,6 +152,7 @@ export declare function createInitialWorkbenchState(options: {
145
152
  accessMode?: WorkdirAccessMode;
146
153
  conversation?: string;
147
154
  }): WorkbenchState;
155
+ export declare function createInputHistory(limit?: number): InputHistory;
148
156
  export declare function workbenchReducer(state: WorkbenchState, action: WorkbenchAction): WorkbenchState;
149
157
  export declare function parseWorkbenchCommand(input: string): WorkbenchCommand | null;
150
158
  export declare function parsePendingApprovalCommand(input: string): WorkbenchCommand | null;
@@ -16,6 +16,56 @@ export function createInitialWorkbenchState(options) {
16
16
  currentConversation: options.conversation || "default",
17
17
  };
18
18
  }
19
+ export function createInputHistory(limit = 100) {
20
+ const entries = [];
21
+ let cursor = null;
22
+ let draftBeforeBrowse = "";
23
+ return {
24
+ record(value) {
25
+ const trimmed = value.trim();
26
+ if (!trimmed)
27
+ return;
28
+ if (entries.at(-1) !== trimmed) {
29
+ entries.push(trimmed);
30
+ if (entries.length > limit)
31
+ entries.splice(0, entries.length - limit);
32
+ }
33
+ cursor = null;
34
+ draftBeforeBrowse = "";
35
+ },
36
+ previous(currentDraft) {
37
+ if (entries.length === 0)
38
+ return currentDraft;
39
+ if (cursor === null) {
40
+ draftBeforeBrowse = currentDraft;
41
+ cursor = entries.length - 1;
42
+ }
43
+ else {
44
+ cursor = Math.max(0, cursor - 1);
45
+ }
46
+ return entries[cursor] ?? currentDraft;
47
+ },
48
+ next(currentDraft) {
49
+ if (entries.length === 0 || cursor === null)
50
+ return currentDraft;
51
+ if (cursor < entries.length - 1) {
52
+ cursor += 1;
53
+ return entries[cursor] ?? currentDraft;
54
+ }
55
+ cursor = null;
56
+ const restored = draftBeforeBrowse;
57
+ draftBeforeBrowse = "";
58
+ return restored;
59
+ },
60
+ reset() {
61
+ cursor = null;
62
+ draftBeforeBrowse = "";
63
+ },
64
+ values() {
65
+ return [...entries];
66
+ },
67
+ };
68
+ }
19
69
  export function workbenchReducer(state, action) {
20
70
  switch (action.type) {
21
71
  case "message.add":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-api/cli",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "First-class command line interface for Agent API",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/scalebox-dev/agent-tui#readme",
@@ -20,7 +20,7 @@
20
20
  "scripts": {
21
21
  "sync-version": "node scripts/sync-version.mjs",
22
22
  "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
23
- "build": "npm run clean && tsc -p tsconfig.json",
23
+ "build": "npm run clean && tsc -p tsconfig.json && node scripts/prepare-bin.mjs",
24
24
  "dev": "npm run build && node dist/index.js",
25
25
  "dev:link": "node scripts/dev-link.mjs",
26
26
  "start": "node dist/index.js",