@falkenslab/agent-kit 0.30.1 → 0.32.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/dist/index.js CHANGED
@@ -5,7 +5,7 @@ export { setTheme, getTheme, DEFAULT_THEME } from "./tui/theme.js";
5
5
  export { isExitPromptError } from "./tui/promptErrors.js";
6
6
  export { allowAnyMcpTool } from "./core/mcpPermissions.js";
7
7
  export { createPromptLoader } from "./core/promptTemplate.js";
8
- export { resolveClaudeAuth } from "./core/claudeAuth.js";
8
+ export { claudeExecutable, claudeSignedIn, startClaudeSignIn } from "./core/claudeAuth.js";
9
9
  export { ensureClaudeAuth } from "./tui/claudeAuth.js";
10
10
  export { createFriendlyToolLabel, truncate, truncatePath, withToolLabels, withToolPhrases, withToolResults, quietResults, } from "./core/toolLabels.js";
11
11
  export { buildSessionOptions, createInputQueue, createDeferred, createModeControl, togglePlanMode } from "./core/session.js";
@@ -17,6 +17,8 @@ export interface ChatTuiOptions {
17
17
  formatAction?: (toolName: string, input: unknown) => string;
18
18
  /** Signing in to an extension: whether its link opens in this computer's browser (the default), or how (see `ChatSettings.openBrowser`, #72). */
19
19
  openBrowser?: ChatSettings["openBrowser"];
20
+ /** Claude Code's own commands in the `/` menu: none by default, `"all"`, or the ones named (see `ChatSettings.cliCommands`, #87). */
21
+ cliCommands?: ChatSettings["cliCommands"];
20
22
  /** Printed once before the first prompt, if given. */
21
23
  welcomeMessage?: string;
22
24
  /** Shown before the cursor at each prompt. */
@@ -77,6 +77,7 @@ export async function runChatTui(options, tuiOptions = {}) {
77
77
  toolLabels: tuiOptions.toolLabels,
78
78
  formatAction: tuiOptions.formatAction,
79
79
  ...(tuiOptions.openBrowser !== undefined ? { openBrowser: tuiOptions.openBrowser } : {}),
80
+ ...(tuiOptions.cliCommands !== undefined ? { cliCommands: tuiOptions.cliCommands } : {}),
80
81
  // Nothing here shows them.
81
82
  promptSuggestions: false,
82
83
  // /resume: a numbered list, and a number typed.
@@ -1,21 +1,16 @@
1
- import { type ClaudeAuthConfig } from "../core/claudeAuth.js";
2
1
  /**
3
- * The terminal-facing counterpart to `resolveClaudeAuth()` (core/claudeAuth.ts), which only
4
- * *looks up* a token. This one also gets you one: if `resolveClaudeAuth(config)` comes back
5
- * empty, it prints why, offers to run "claude setup-token" interactively, and sets the
6
- * result on `process.env.CLAUDE_CODE_OAUTH_TOKEN` for this run. Exits the process if the
7
- * user declines or cancels — there's no agent to run without a token.
2
+ * Makes sure the agent can reach Claude before a terminal chat starts: returns at once when
3
+ * `claudeSignedIn()` says so (an API key or the person's own token in the environment, or a
4
+ * Claude Code sign-in). Otherwise it says why, asks whether to sign in, and runs Claude Code's
5
+ * own `claude auth login` (the binary the Agent SDK ships) on this terminal: Anthropic's page in
6
+ * the browser, the code it shows pasted here. The binary keeps the credentials; nothing is
7
+ * returned or stored (ADR-009: Anthropic's terms forbid an agent collecting them). Exits the
8
+ * process when the person declines or the sign-in doesn't complete: there's no agent to run
9
+ * without Claude.
8
10
  *
9
- * Persisting a freshly-generated token across runs is *not* this function's job — unlike an
10
- * earlier version, this kit owns no config-file storage of its own (see git history). When
11
- * the interactive flow does generate one, it's returned so the caller can save it wherever
12
- * it wants (a file, an OS keychain, ...) and pass it back in as `config.claudeCodeOAuthToken`
13
- * next time; returns `undefined` when auth was already resolved (an env var, or the `config`
14
- * passed in) and nothing new was generated.
15
- *
16
- * Only makes sense with a real terminal in front of a human (a `runWizard()` confirm, colored console
17
- * output), which is why it lives here rather than in the non-interactive core.
11
+ * Only makes sense with a terminal in front of a person (a `runWizard()` confirm, colored
12
+ * output); a host without one uses `startClaudeSignIn()` from the core.
18
13
  */
19
- export declare function ensureClaudeAuth(config?: ClaudeAuthConfig, options?: {
14
+ export declare function ensureClaudeAuth(options?: {
20
15
  language?: string;
21
- }): Promise<string | undefined>;
16
+ }): Promise<void>;
@@ -1,78 +1,47 @@
1
1
  import { spawn } from "node:child_process";
2
- import { resolveClaudeAuth } from "../core/claudeAuth.js";
2
+ import { claudeCommand, claudeExecutable, claudeSignedIn } from "../core/claudeAuth.js";
3
3
  import { isExitPromptError } from "./promptErrors.js";
4
4
  import * as ui from "./ui.js";
5
5
  import { runWizard } from "./ink/wizard.js";
6
6
  import { applyLanguage } from "./language.js";
7
7
  import { t } from "../core/messages/index.js";
8
- // The real format of "claude setup-token" tokens ("sk-ant-oat01-...", seen in the
9
- // installed @anthropic-ai/claude-code binary) — used to extract the token from its output.
10
- const TOKEN_PATTERN = /sk-ant-oat\d{2}-[A-Za-z0-9_-]+/;
11
- /**
12
- * Launches "claude setup-token" as an interactive child process: it opens a browser and
13
- * asks the user to authenticate, with instructions it prints itself. stdin/stderr are
14
- * inherited as-is so the interaction (pasting the authorization code, etc.) is identical
15
- * to running the command by hand; stdout is duplicated live and also buffered to extract
16
- * the token it prints at the end ("Your OAuth token (...): sk-ant-oat01-...").
17
- */
18
- function runSetupToken() {
19
- return new Promise((resolve, reject) => {
20
- // On Windows npx is npx.cmd, which only runs through a shell. The command is passed as
21
- // one fixed string there (nothing user-supplied in it): an args array with
22
- // `shell: true` triggers Node's DEP0190 warning, since args are concatenated unescaped.
23
- const stdio = ["inherit", "pipe", "inherit"];
24
- const child = process.platform === "win32"
25
- ? spawn("npx @anthropic-ai/claude-code setup-token", { stdio, shell: true })
26
- : spawn("npx", ["@anthropic-ai/claude-code", "setup-token"], { stdio });
27
- let output = "";
28
- child.stdout.on("data", (chunk) => {
29
- const text = chunk.toString("utf-8");
30
- output += text;
31
- process.stdout.write(text);
32
- });
33
- child.on("error", reject);
34
- child.on("exit", (code) => {
35
- if (code !== 0) {
36
- reject(new Error(`"claude setup-token" exited with code ${code}.`));
37
- return;
38
- }
39
- const match = output.match(TOKEN_PATTERN);
40
- if (!match) {
41
- reject(new Error('"claude setup-token" exited without errors, but no token was found in its ' +
42
- "output (sk-ant-oat...). Copy it by hand into CLAUDE_CODE_OAUTH_TOKEN if you see it above."));
43
- return;
44
- }
45
- resolve(match[0]);
46
- });
8
+ /** Runs Claude Code's own `auth login` on this terminal: it opens the browser, and the person pastes the code here. */
9
+ function runLogin(executable) {
10
+ const command = claudeCommand(executable, ["auth", "login"]);
11
+ return new Promise((resolve) => {
12
+ const child = spawn(command.file, command.args, { stdio: "inherit" });
13
+ child.on("error", () => resolve());
14
+ child.on("close", () => resolve());
47
15
  });
48
16
  }
49
17
  /**
50
- * The terminal-facing counterpart to `resolveClaudeAuth()` (core/claudeAuth.ts), which only
51
- * *looks up* a token. This one also gets you one: if `resolveClaudeAuth(config)` comes back
52
- * empty, it prints why, offers to run "claude setup-token" interactively, and sets the
53
- * result on `process.env.CLAUDE_CODE_OAUTH_TOKEN` for this run. Exits the process if the
54
- * user declines or cancels — there's no agent to run without a token.
55
- *
56
- * Persisting a freshly-generated token across runs is *not* this function's job — unlike an
57
- * earlier version, this kit owns no config-file storage of its own (see git history). When
58
- * the interactive flow does generate one, it's returned so the caller can save it wherever
59
- * it wants (a file, an OS keychain, ...) and pass it back in as `config.claudeCodeOAuthToken`
60
- * next time; returns `undefined` when auth was already resolved (an env var, or the `config`
61
- * passed in) and nothing new was generated.
18
+ * Makes sure the agent can reach Claude before a terminal chat starts: returns at once when
19
+ * `claudeSignedIn()` says so (an API key or the person's own token in the environment, or a
20
+ * Claude Code sign-in). Otherwise it says why, asks whether to sign in, and runs Claude Code's
21
+ * own `claude auth login` (the binary the Agent SDK ships) on this terminal: Anthropic's page in
22
+ * the browser, the code it shows pasted here. The binary keeps the credentials; nothing is
23
+ * returned or stored (ADR-009: Anthropic's terms forbid an agent collecting them). Exits the
24
+ * process when the person declines or the sign-in doesn't complete: there's no agent to run
25
+ * without Claude.
62
26
  *
63
- * Only makes sense with a real terminal in front of a human (a `runWizard()` confirm, colored console
64
- * output), which is why it lives here rather than in the non-interactive core.
27
+ * Only makes sense with a terminal in front of a person (a `runWizard()` confirm, colored
28
+ * output); a host without one uses `startClaudeSignIn()` from the core.
65
29
  */
66
- export async function ensureClaudeAuth(config = {}, options = {}) {
67
- if (resolveClaudeAuth(config))
68
- return undefined;
30
+ export async function ensureClaudeAuth(options = {}) {
31
+ if (await claudeSignedIn())
32
+ return;
69
33
  applyLanguage(options.language);
70
34
  const texts = t().auth;
71
- console.log(ui.warn(`\n${texts.noToken}`));
72
- console.log(ui.dim(texts.noTokenDetail));
35
+ const executable = claudeExecutable();
36
+ if (!executable) {
37
+ console.log(ui.error(`\n${texts.noClaude}`));
38
+ process.exit(1);
39
+ }
40
+ console.log(ui.warn(`\n${texts.notSignedIn}`));
41
+ console.log(ui.dim(texts.notSignedInDetail));
73
42
  try {
74
- const { generate } = await runWizard([{ type: "confirm", name: "generate", message: texts.generateQuestion, default: true }]);
75
- if (!generate) {
43
+ const { signIn } = await runWizard([{ type: "confirm", name: "signIn", message: texts.signInQuestion, default: true }]);
44
+ if (!signIn) {
76
45
  console.log(ui.error(`\n${texts.cantStart}`));
77
46
  process.exit(0);
78
47
  }
@@ -82,10 +51,12 @@ export async function ensureClaudeAuth(config = {}, options = {}) {
82
51
  process.exit(0);
83
52
  throw error;
84
53
  }
85
- console.log(ui.heading(`\n${texts.generatingHeading}`));
54
+ console.log(ui.heading(`\n${texts.signingInHeading}`));
86
55
  console.log(ui.dim(`${texts.browserWillOpen}\n`));
87
- const token = await runSetupToken();
88
- process.env.CLAUDE_CODE_OAUTH_TOKEN = token;
89
- console.log(ui.success(`\n${texts.generated}\n`));
90
- return token;
56
+ await runLogin(executable);
57
+ if (!(await claudeSignedIn())) {
58
+ console.log(ui.error(`\n${texts.notCompleted}`));
59
+ process.exit(1);
60
+ }
61
+ console.log(ui.success(`\n${texts.signedIn}\n`));
91
62
  }
@@ -200,6 +200,7 @@ export async function runChatInk(options, tuiOptions = {}) {
200
200
  toolPhrase: tuiOptions.toolPhrase,
201
201
  formatResult: tuiOptions.formatResult,
202
202
  ...(tuiOptions.openBrowser !== undefined ? { openBrowser: tuiOptions.openBrowser } : {}),
203
+ ...(tuiOptions.cliCommands !== undefined ? { cliCommands: tuiOptions.cliCommands } : {}),
203
204
  promptSuggestions: tuiOptions.promptSuggestions ?? true,
204
205
  // The /resume list in place of the prompt.
205
206
  pick: (title, choices) => input.pick(title, choices),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@falkenslab/agent-kit",
3
- "version": "0.30.1",
3
+ "version": "0.32.0",
4
4
  "description": "Library for building an agent on top of the Claude Agent SDK",
5
5
  "author": "Francisco Vargas Ruiz",
6
6
  "license": "MIT",
@@ -54,6 +54,7 @@
54
54
  "@inquirer/prompts": "^8.5.2",
55
55
  "@types/react": "^19.3.0",
56
56
  "ink": "^6.8.0",
57
+ "isomorphic-git": "^1.44.5",
57
58
  "marked": "^18.0.14",
58
59
  "picocolors": "^1.1.1",
59
60
  "react": "^19.3.0",