@falkenslab/agent-kit 0.10.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.
Files changed (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +97 -0
  3. package/assets/knowledge-plugin/.claude-plugin/plugin.json +4 -0
  4. package/assets/knowledge-plugin/commands/ingest.md +7 -0
  5. package/assets/knowledge-plugin/commands/lint.md +5 -0
  6. package/assets/knowledge-plugin/commands/query.md +5 -0
  7. package/assets/knowledge-plugin/skills/knowledge-ingest/SKILL.md +42 -0
  8. package/assets/knowledge-plugin/skills/knowledge-lint/SKILL.md +26 -0
  9. package/assets/knowledge-plugin/skills/knowledge-pages/SKILL.md +137 -0
  10. package/assets/knowledge-plugin/skills/knowledge-query/SKILL.md +14 -0
  11. package/dist/core/agentSpec.d.ts +132 -0
  12. package/dist/core/agentSpec.js +1 -0
  13. package/dist/core/claudeAuth.d.ts +24 -0
  14. package/dist/core/claudeAuth.js +27 -0
  15. package/dist/core/hooks/fileScopeGate.d.ts +33 -0
  16. package/dist/core/hooks/fileScopeGate.js +68 -0
  17. package/dist/core/hooks/humanInput.d.ts +15 -0
  18. package/dist/core/hooks/humanInput.js +52 -0
  19. package/dist/core/hooks/stepGate.d.ts +11 -0
  20. package/dist/core/hooks/stepGate.js +47 -0
  21. package/dist/core/hooks/subagentBashGate.d.ts +28 -0
  22. package/dist/core/hooks/subagentBashGate.js +40 -0
  23. package/dist/core/hooks/subagentForegroundGate.d.ts +18 -0
  24. package/dist/core/hooks/subagentForegroundGate.js +34 -0
  25. package/dist/core/hooks/subagentTypeGate.d.ts +16 -0
  26. package/dist/core/hooks/subagentTypeGate.js +33 -0
  27. package/dist/core/hooks/transcriptLogger.d.ts +14 -0
  28. package/dist/core/hooks/transcriptLogger.js +66 -0
  29. package/dist/core/interaction.d.ts +29 -0
  30. package/dist/core/interaction.js +7 -0
  31. package/dist/core/knowledge.d.ts +15 -0
  32. package/dist/core/knowledge.js +54 -0
  33. package/dist/core/mcpPermissions.d.ts +23 -0
  34. package/dist/core/mcpPermissions.js +47 -0
  35. package/dist/core/promptTemplate.d.ts +6 -0
  36. package/dist/core/promptTemplate.js +20 -0
  37. package/dist/core/runner.d.ts +137 -0
  38. package/dist/core/runner.js +115 -0
  39. package/dist/core/session.d.ts +63 -0
  40. package/dist/core/session.js +222 -0
  41. package/dist/core/toolLabels.d.ts +23 -0
  42. package/dist/core/toolLabels.js +114 -0
  43. package/dist/core/tools/humanApproval.d.ts +14 -0
  44. package/dist/core/tools/humanApproval.js +37 -0
  45. package/dist/core/tools/manualLogin.d.ts +16 -0
  46. package/dist/core/tools/manualLogin.js +27 -0
  47. package/dist/core/tools/saveToSources.d.ts +10 -0
  48. package/dist/core/tools/saveToSources.js +105 -0
  49. package/dist/index.d.ts +31 -0
  50. package/dist/index.js +32 -0
  51. package/dist/tui/chatTui.d.ts +105 -0
  52. package/dist/tui/chatTui.js +265 -0
  53. package/dist/tui/claudeAuth.d.ts +19 -0
  54. package/dist/tui/claudeAuth.js +96 -0
  55. package/dist/tui/consoleRenderer.d.ts +32 -0
  56. package/dist/tui/consoleRenderer.js +72 -0
  57. package/dist/tui/ink/PromptInput.d.ts +40 -0
  58. package/dist/tui/ink/PromptInput.js +333 -0
  59. package/dist/tui/ink/SessionView.d.ts +42 -0
  60. package/dist/tui/ink/SessionView.js +223 -0
  61. package/dist/tui/ink/fileMentions.d.ts +13 -0
  62. package/dist/tui/ink/fileMentions.js +50 -0
  63. package/dist/tui/ink/fullscreen.d.ts +79 -0
  64. package/dist/tui/ink/fullscreen.js +175 -0
  65. package/dist/tui/ink/header.d.ts +17 -0
  66. package/dist/tui/ink/header.js +24 -0
  67. package/dist/tui/ink/inkInteraction.d.ts +22 -0
  68. package/dist/tui/ink/inkInteraction.js +64 -0
  69. package/dist/tui/ink/lineBuffer.d.ts +28 -0
  70. package/dist/tui/ink/lineBuffer.js +87 -0
  71. package/dist/tui/ink/markdown.d.ts +12 -0
  72. package/dist/tui/ink/markdown.js +148 -0
  73. package/dist/tui/ink/progressView.d.ts +27 -0
  74. package/dist/tui/ink/progressView.js +65 -0
  75. package/dist/tui/ink/promptEditing.d.ts +54 -0
  76. package/dist/tui/ink/promptEditing.js +123 -0
  77. package/dist/tui/ink/runChatInk.d.ts +103 -0
  78. package/dist/tui/ink/runChatInk.js +298 -0
  79. package/dist/tui/ink/sessionModel.d.ts +85 -0
  80. package/dist/tui/ink/sessionModel.js +262 -0
  81. package/dist/tui/ink/terminalCursor.d.ts +32 -0
  82. package/dist/tui/ink/terminalCursor.js +68 -0
  83. package/dist/tui/ink/terminalStatus.d.ts +20 -0
  84. package/dist/tui/ink/terminalStatus.js +55 -0
  85. package/dist/tui/ink/toolGroup.d.ts +36 -0
  86. package/dist/tui/ink/toolGroup.js +77 -0
  87. package/dist/tui/ink/wizard.d.ts +62 -0
  88. package/dist/tui/ink/wizard.js +167 -0
  89. package/dist/tui/promptErrors.d.ts +2 -0
  90. package/dist/tui/promptErrors.js +4 -0
  91. package/dist/tui/terminalInteraction.d.ts +18 -0
  92. package/dist/tui/terminalInteraction.js +85 -0
  93. package/dist/tui/ui.d.ts +21 -0
  94. package/dist/tui/ui.js +25 -0
  95. package/package.json +75 -0
@@ -0,0 +1,62 @@
1
+ export type WizardAnswers = Record<string, unknown>;
2
+ /** A value, or one computed from the answers given so far. */
3
+ type FromAnswers<T> = T | ((answers: WizardAnswers) => T);
4
+ interface BaseStep {
5
+ /** Key of this step's answer in the result. */
6
+ name: string;
7
+ message: FromAnswers<string>;
8
+ /** Skips the step (leaving no answer) when it returns false. */
9
+ when?: (answers: WizardAnswers) => boolean;
10
+ }
11
+ export interface SelectStep<T = unknown> extends BaseStep {
12
+ type: "select";
13
+ choices: FromAnswers<{
14
+ name: string;
15
+ value: T;
16
+ }[]>;
17
+ /** Preselects the choice with this value. */
18
+ default?: FromAnswers<T>;
19
+ }
20
+ export interface InputStep extends BaseStep {
21
+ type: "input";
22
+ default?: FromAnswers<string>;
23
+ /** `true` to accept, or the message to show. */
24
+ validate?: (value: string, answers: WizardAnswers) => true | string;
25
+ }
26
+ export interface PasswordStep extends BaseStep {
27
+ type: "password";
28
+ validate?: (value: string, answers: WizardAnswers) => true | string;
29
+ }
30
+ export interface ConfirmStep extends BaseStep {
31
+ type: "confirm";
32
+ default?: FromAnswers<boolean>;
33
+ }
34
+ export type WizardStep = SelectStep | InputStep | PasswordStep | ConfirmStep;
35
+ export interface WizardOptions {
36
+ /** Printed above the first question. */
37
+ title?: string;
38
+ /** Use the plain @inquirer/prompts questions even on a TTY. */
39
+ plain?: boolean;
40
+ }
41
+ /**
42
+ * How many choices a select shows at once, so the question, its choices and a validation
43
+ * line stay shorter than the terminal: Ink clears the whole screen to redraw anything
44
+ * taller (see SessionView.tsx's previewLines()).
45
+ */
46
+ export declare function visibleOptions(choices: number, messageRows: number, terminalRows: number | undefined): number;
47
+ export declare function Wizard({ steps, title, onDone }: {
48
+ steps: WizardStep[];
49
+ title?: string;
50
+ onDone(answers: WizardAnswers | null): void;
51
+ }): import("react").JSX.Element;
52
+ /**
53
+ * Asks `steps` in order and resolves with every answer by step name. A step's message,
54
+ * choices or default can depend on earlier answers, and `when` skips it, so a consumer's
55
+ * whole interactive menu becomes a list of step definitions.
56
+ *
57
+ * Ctrl+C rejects with an error named "ExitPromptError", like @inquirer/prompts, so
58
+ * `isExitPromptError()` keeps working. Without a TTY (or with `plain`) it asks the same
59
+ * steps through @inquirer/prompts.
60
+ */
61
+ export declare function runWizard(steps: WizardStep[], options?: WizardOptions): Promise<WizardAnswers>;
62
+ export {};
@@ -0,0 +1,167 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { stdin, stdout } from "node:process";
3
+ import { useState } from "react";
4
+ import { Box, render, Static, Text, useApp, useInput, useStdout } from "ink";
5
+ import wrapAnsi from "wrap-ansi";
6
+ import { liveWidth } from "./sessionModel.js";
7
+ import { ConfirmInput, PasswordInput, Select, TextInput } from "@inkjs/ui";
8
+ import * as inquirer from "@inquirer/prompts";
9
+ import * as ui from "../ui.js";
10
+ function resolve(value, answers) {
11
+ return typeof value === "function" ? value(answers) : value;
12
+ }
13
+ /** The error @inquirer/prompts throws on Ctrl+C, so `isExitPromptError()` covers both. */
14
+ function exitPromptError() {
15
+ const error = new Error("User force closed the prompt with Ctrl+C");
16
+ error.name = "ExitPromptError";
17
+ return error;
18
+ }
19
+ function StepInput({ step, answers, onAnswer }) {
20
+ const [error, setError] = useState(null);
21
+ const { stdout } = useStdout();
22
+ const width = liveWidth(stdout.columns);
23
+ const message = `${ui.success("?")} ${ui.heading(resolve(step.message, answers))}`;
24
+ function validated(value) {
25
+ const check = step.type === "input" || step.type === "password" ? (step.validate?.(value, answers) ?? true) : true;
26
+ if (check === true)
27
+ onAnswer(value);
28
+ else
29
+ setError(check);
30
+ }
31
+ let field;
32
+ switch (step.type) {
33
+ case "select": {
34
+ const choices = resolve(step.choices, answers);
35
+ const preselected = step.default === undefined ? -1 : choices.findIndex((c) => c.value === resolve(step.default, answers));
36
+ field = (_jsx(Select, { visibleOptionCount: visibleOptions(choices.length, wrapAnsi(message, width, { hard: true }).split(NEWLINE).length, stdout.rows), options: choices.map((choice, index) => ({ label: choice.name, value: String(index) })), defaultValue: preselected >= 0 ? String(preselected) : undefined, onChange: (index) => onAnswer(choices[Number(index)].value) }));
37
+ break;
38
+ }
39
+ case "input":
40
+ field = _jsx(TextInput, { defaultValue: step.default === undefined ? undefined : resolve(step.default, answers), onSubmit: validated });
41
+ break;
42
+ case "password":
43
+ field = _jsx(PasswordInput, { onSubmit: validated });
44
+ break;
45
+ case "confirm": {
46
+ const yes = step.default === undefined ? true : resolve(step.default, answers);
47
+ field = _jsx(ConfirmInput, { defaultChoice: yes ? "confirm" : "cancel", onConfirm: () => onAnswer(true), onCancel: () => onAnswer(false) });
48
+ break;
49
+ }
50
+ }
51
+ return (_jsxs(Box, { flexDirection: "column", width: width, children: [_jsx(Text, { children: message }), field, error ? _jsx(Text, { children: ui.error(error) }) : null] }));
52
+ }
53
+ const NEWLINE = String.fromCharCode(10);
54
+ /**
55
+ * How many choices a select shows at once, so the question, its choices and a validation
56
+ * line stay shorter than the terminal: Ink clears the whole screen to redraw anything
57
+ * taller (see SessionView.tsx's previewLines()).
58
+ */
59
+ export function visibleOptions(choices, messageRows, terminalRows) {
60
+ return Math.max(1, Math.min(choices, (terminalRows || 24) - messageRows - 3));
61
+ }
62
+ function summary(step, value, answers) {
63
+ if (step.type === "password")
64
+ return "*".repeat(String(value).length);
65
+ if (step.type === "confirm")
66
+ return value ? "Yes" : "No";
67
+ if (step.type === "select")
68
+ return resolve(step.choices, answers).find((c) => c.value === value)?.name ?? String(value);
69
+ return String(value);
70
+ }
71
+ export function Wizard({ steps, title, onDone }) {
72
+ const { exit } = useApp();
73
+ const [answers, setAnswers] = useState({});
74
+ const [done, setDone] = useState([]);
75
+ const [index, setIndex] = useState(() => nextIndex(steps, -1, {}));
76
+ useInput((input, key) => {
77
+ if (key.ctrl && input === "c") {
78
+ onDone(null);
79
+ exit();
80
+ }
81
+ });
82
+ function answer(value) {
83
+ const step = steps[index];
84
+ const next = { ...answers, [step.name]: value };
85
+ setAnswers(next);
86
+ setDone([...done, { message: resolve(step.message, answers), text: summary(step, value, answers) }]);
87
+ const following = nextIndex(steps, index, next);
88
+ setIndex(following);
89
+ if (following >= steps.length) {
90
+ onDone(next);
91
+ exit();
92
+ }
93
+ }
94
+ // The title and the answered steps go to <Static>: written once, never redrawn, so a long
95
+ // menu can't grow the live area past the terminal's height.
96
+ const history = [
97
+ ...(title ? [{ id: "title", text: ui.heading(title) }] : []),
98
+ ...done.map(({ message, text }, i) => ({ id: String(i), text: `${ui.success("✔")} ${message} ${ui.agent(text)}` })),
99
+ ];
100
+ const current = steps[index];
101
+ return (_jsxs(_Fragment, { children: [_jsx(Static, { items: history, children: (item) => _jsx(Text, { children: item.text }, item.id) }), current ? _jsx(StepInput, { step: current, answers: answers, onAnswer: answer }, index) : null] }));
102
+ }
103
+ function nextIndex(steps, from, answers) {
104
+ let index = from + 1;
105
+ while (index < steps.length && steps[index].when && !steps[index].when(answers))
106
+ index++;
107
+ return index;
108
+ }
109
+ /** The same steps as plain @inquirer/prompts questions, for hosts without a TTY. */
110
+ async function runPlainWizard(steps, title) {
111
+ if (title)
112
+ console.log(ui.heading(title));
113
+ const answers = {};
114
+ for (const step of steps) {
115
+ if (step.when && !step.when(answers))
116
+ continue;
117
+ const message = resolve(step.message, answers);
118
+ switch (step.type) {
119
+ case "select":
120
+ answers[step.name] = await inquirer.select({
121
+ message,
122
+ choices: resolve(step.choices, answers),
123
+ default: step.default === undefined ? undefined : resolve(step.default, answers),
124
+ });
125
+ break;
126
+ case "input":
127
+ answers[step.name] = await inquirer.input({
128
+ message,
129
+ default: step.default === undefined ? undefined : resolve(step.default, answers),
130
+ validate: step.validate ? (value) => step.validate(value, answers) : undefined,
131
+ });
132
+ break;
133
+ case "password":
134
+ answers[step.name] = await inquirer.password({
135
+ message,
136
+ mask: "*",
137
+ validate: step.validate ? (value) => step.validate(value, answers) : undefined,
138
+ });
139
+ break;
140
+ case "confirm":
141
+ answers[step.name] = await inquirer.confirm({ message, default: step.default === undefined ? true : resolve(step.default, answers) });
142
+ break;
143
+ }
144
+ }
145
+ return answers;
146
+ }
147
+ /**
148
+ * Asks `steps` in order and resolves with every answer by step name. A step's message,
149
+ * choices or default can depend on earlier answers, and `when` skips it, so a consumer's
150
+ * whole interactive menu becomes a list of step definitions.
151
+ *
152
+ * Ctrl+C rejects with an error named "ExitPromptError", like @inquirer/prompts, so
153
+ * `isExitPromptError()` keeps working. Without a TTY (or with `plain`) it asks the same
154
+ * steps through @inquirer/prompts.
155
+ */
156
+ export async function runWizard(steps, options = {}) {
157
+ if (options.plain || !stdin.isTTY || !stdout.isTTY)
158
+ return await runPlainWizard(steps, options.title);
159
+ if (nextIndex(steps, -1, {}) >= steps.length)
160
+ return {};
161
+ let result = null;
162
+ const app = render(_jsx(Wizard, { steps: steps, title: options.title, onDone: (answers) => (result = answers) }), { exitOnCtrlC: false });
163
+ await app.waitUntilExit();
164
+ if (result === null)
165
+ throw exitPromptError();
166
+ return result;
167
+ }
@@ -0,0 +1,2 @@
1
+ /** True for the error @inquirer/prompts throws when the user cancels (Ctrl+C) a prompt. */
2
+ export declare function isExitPromptError(error: unknown): boolean;
@@ -0,0 +1,4 @@
1
+ /** True for the error @inquirer/prompts throws when the user cancels (Ctrl+C) a prompt. */
2
+ export function isExitPromptError(error) {
3
+ return error instanceof Error && error.name === "ExitPromptError";
4
+ }
@@ -0,0 +1,18 @@
1
+ import readline from "node:readline/promises";
2
+ import type { InteractionPort } from "../core/interaction.js";
3
+ export declare function setSharedReadline(rl: readline.Interface | null): void;
4
+ export declare function getSharedReadline(): readline.Interface | null;
5
+ /**
6
+ * Asks `question` on the shared interface, flagging it as in progress meanwhile — so the
7
+ * REPL that owns the interface can tell a human-in-the-loop checkpoint waiting on the
8
+ * keyboard apart from a turn simply streaming (e.g. Esc must not interrupt the turn while
9
+ * an approval question is on screen).
10
+ */
11
+ export declare function askOnSharedReadline(rl: readline.Interface, question: string): Promise<string>;
12
+ export declare function isSharedQuestionActive(): boolean;
13
+ /**
14
+ * The default `InteractionPort`, installed by the package entry point: prints the
15
+ * checkpoint and reads the answer on the terminal, on the chat's own readline when one is
16
+ * registered via `setSharedReadline()`. Without a TTY it prints nothing and never answers.
17
+ */
18
+ export declare const terminalInteractionPort: InteractionPort;
@@ -0,0 +1,85 @@
1
+ import readline from "node:readline/promises";
2
+ import { stdin, stdout } from "node:process";
3
+ const DEFAULT_QUESTION = "Allow this to continue? [Y/n/q] ";
4
+ /**
5
+ * A long-lived chat REPL's readline interface (if any), so that the terminal port reuses
6
+ * it instead of creating its own.
7
+ *
8
+ * Closing a readline.Interface desyncs stdin's raw mode for *any* other interface still
9
+ * alive on the same stream — Interface.close() calls input.setRawMode(false) directly on
10
+ * the shared stream, not something private to that instance (confirmed by reading
11
+ * node:readline/promises). If the port creates and closes its own interface while a chat
12
+ * REPL's own is still open waiting at its prompt, that second interface stops receiving
13
+ * keystroke events — the prompt sits there accepting no input. By registering the REPL's
14
+ * interface here, the port reuses it (creating or closing nothing) whenever it's available.
15
+ */
16
+ let sharedReadline = null;
17
+ export function setSharedReadline(rl) {
18
+ sharedReadline = rl;
19
+ }
20
+ export function getSharedReadline() {
21
+ return sharedReadline;
22
+ }
23
+ let activeQuestions = 0;
24
+ /**
25
+ * Asks `question` on the shared interface, flagging it as in progress meanwhile — so the
26
+ * REPL that owns the interface can tell a human-in-the-loop checkpoint waiting on the
27
+ * keyboard apart from a turn simply streaming (e.g. Esc must not interrupt the turn while
28
+ * an approval question is on screen).
29
+ */
30
+ export async function askOnSharedReadline(rl, question) {
31
+ activeQuestions++;
32
+ try {
33
+ return await rl.question(question);
34
+ }
35
+ finally {
36
+ activeQuestions--;
37
+ }
38
+ }
39
+ export function isSharedQuestionActive() {
40
+ return activeQuestions > 0;
41
+ }
42
+ function never() {
43
+ return new Promise(() => { });
44
+ }
45
+ /**
46
+ * Whether the keyboard channel (and any console output at all) is even attempted is
47
+ * decided from `stdin.isTTY` — confirmed empirically that a non-TTY `rl.question()` still
48
+ * writes its query text to `stdout` even though it can never resolve from real keystrokes,
49
+ * so skipping the whole readline.Interface (not just not reading from it) is what actually
50
+ * keeps a non-interactive host's stdout silent: it depends entirely on the response file.
51
+ */
52
+ async function askOnTerminal(prompt, signal) {
53
+ if (!stdin.isTTY)
54
+ return await never();
55
+ console.log(`\n=== ${prompt.title} ===`);
56
+ for (const line of prompt.lines)
57
+ console.log(line);
58
+ const question = prompt.question ?? DEFAULT_QUESTION;
59
+ const shared = sharedReadline;
60
+ if (shared)
61
+ return await askOnSharedReadline(shared, question);
62
+ const rl = readline.createInterface({ input: stdin, output: stdout });
63
+ const close = () => rl.close();
64
+ signal.addEventListener("abort", close, { once: true });
65
+ try {
66
+ return await rl.question(question);
67
+ }
68
+ finally {
69
+ signal.removeEventListener("abort", close);
70
+ rl.close();
71
+ }
72
+ }
73
+ /**
74
+ * The default `InteractionPort`, installed by the package entry point: prints the
75
+ * checkpoint and reads the answer on the terminal, on the chat's own readline when one is
76
+ * registered via `setSharedReadline()`. Without a TTY it prints nothing and never answers.
77
+ */
78
+ export const terminalInteractionPort = {
79
+ askDecision: askOnTerminal,
80
+ askManualIntervention: askOnTerminal,
81
+ notify(message) {
82
+ if (stdin.isTTY)
83
+ console.log(message);
84
+ },
85
+ };
@@ -0,0 +1,21 @@
1
+ /** Color palette for a consistent console look across whatever CLI is built on this kit. */
2
+ export declare const agent: (s: string) => string;
3
+ export declare const user: (s: string) => string;
4
+ export declare const action: (s: string) => string;
5
+ export declare const heading: (s: string) => string;
6
+ export declare const success: (s: string) => string;
7
+ export declare const warn: (s: string) => string;
8
+ export declare const error: (s: string) => string;
9
+ export declare const dim: (s: string) => string;
10
+ /** The orange accent: spinner, turn summary, mode. */
11
+ export declare const accent: (s: string) => string;
12
+ /** The spinner and what the agent is doing. */
13
+ export declare const working: (s: string) => string;
14
+ /** Inline code and code blocks. */
15
+ export declare const code: (s: string) => string;
16
+ /** The background of the human's lines. */
17
+ export declare const userBar: (s: string) => string;
18
+ export declare const bold: (s: string) => string;
19
+ export declare const italic: (s: string) => string;
20
+ export declare const strike: (s: string) => string;
21
+ export declare const toolBullet: (s: string) => string;
package/dist/tui/ui.js ADDED
@@ -0,0 +1,25 @@
1
+ import pc from "picocolors";
2
+ /** Color palette for a consistent console look across whatever CLI is built on this kit. */
3
+ export const agent = (s) => pc.cyanBright(s);
4
+ export const user = (s) => pc.white(s);
5
+ export const action = (s) => pc.magenta(s);
6
+ export const heading = (s) => pc.bold(s);
7
+ export const success = (s) => pc.green(s);
8
+ export const warn = (s) => pc.yellow(s);
9
+ export const error = (s) => pc.red(s);
10
+ export const dim = (s) => pc.dim(s);
11
+ // The Claude Code-like look of the Ink views (see the feature ink-claude-style), in 24-bit
12
+ // color when the terminal supports color at all, like the rest of this palette.
13
+ const rgb = (r, g, b) => (s) => pc.isColorSupported ? `\x1b[38;2;${r};${g};${b}m${s}\x1b[39m` : s;
14
+ /** The orange accent: spinner, turn summary, mode. */
15
+ export const accent = rgb(215, 119, 87);
16
+ /** The spinner and what the agent is doing. */
17
+ export const working = rgb(137, 180, 250);
18
+ /** Inline code and code blocks. */
19
+ export const code = rgb(177, 185, 249);
20
+ /** The background of the human's lines. */
21
+ export const userBar = (s) => (pc.isColorSupported ? `\x1b[48;2;55;55;55m${s}\x1b[49m` : s);
22
+ export const bold = (s) => pc.bold(s);
23
+ export const italic = (s) => pc.italic(s);
24
+ export const strike = (s) => pc.strikethrough(s);
25
+ export const toolBullet = (s) => pc.green(s);
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@falkenslab/agent-kit",
3
+ "version": "0.10.1",
4
+ "description": "Library for building an agent on top of the Claude Agent SDK",
5
+ "author": "Francisco Vargas Ruiz",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/falkenslab/agent-kit.git"
10
+ },
11
+ "homepage": "https://github.com/falkenslab/agent-kit#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/falkenslab/agent-kit/issues"
14
+ },
15
+ "keywords": [
16
+ "claude",
17
+ "claude-agent-sdk",
18
+ "ai-agent",
19
+ "agent",
20
+ "anthropic",
21
+ "ink",
22
+ "tui",
23
+ "cli",
24
+ "human-in-the-loop",
25
+ "mcp"
26
+ ],
27
+ "type": "module",
28
+ "engines": {
29
+ "node": ">=20"
30
+ },
31
+ "main": "./dist/index.js",
32
+ "types": "./dist/index.d.ts",
33
+ "files": [
34
+ "dist",
35
+ "assets"
36
+ ],
37
+ "scripts": {
38
+ "build": "tsc",
39
+ "typecheck": "tsc --noEmit",
40
+ "test": "tsx --test \"test/**/*.test.ts\" \"test/**/*.test.tsx\"",
41
+ "lint": "eslint .",
42
+ "prepare": "npm run build",
43
+ "prepublishOnly": "npm run typecheck && npm run lint && npm test && npm run build"
44
+ },
45
+ "dependencies": {
46
+ "@anthropic-ai/claude-agent-sdk": "^0.3.234",
47
+ "@inkjs/ui": "^2.0.0",
48
+ "@inquirer/prompts": "^8.5.2",
49
+ "@types/react": "^19.3.0",
50
+ "ink": "^6.8.0",
51
+ "marked": "^18.0.14",
52
+ "picocolors": "^1.1.1",
53
+ "react": "^19.3.0",
54
+ "slice-ansi": "^8.0.0",
55
+ "string-width": "^8.3.0",
56
+ "wrap-ansi": "^9.0.2",
57
+ "zod": "^4.4.3"
58
+ },
59
+ "devDependencies": {
60
+ "@eslint/js": "^10.0.1",
61
+ "@types/node": "^24.0.0",
62
+ "eslint": "^10.9.1",
63
+ "globals": "^17.11.0",
64
+ "ink-testing-library": "^4.0.0",
65
+ "tsx": "^4.19.0",
66
+ "typescript": "^5.7.0",
67
+ "typescript-eslint": "^8.68.0"
68
+ },
69
+ "allowScripts": {
70
+ "esbuild@0.28.2": true
71
+ },
72
+ "publishConfig": {
73
+ "access": "public"
74
+ }
75
+ }