@agentskit/cli 0.6.0 → 0.7.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.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { Command } from 'commander';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
- import { ChatReturn, AdapterFactory } from '@agentskit/core';
3
+ import { ToolDefinition, ChatReturn, SkillDefinition, AdapterFactory, EmbedFn } from '@agentskit/core';
4
4
  import { ChildProcess } from 'node:child_process';
5
+ import { RAG } from '@agentskit/rag';
5
6
 
6
7
  declare function createCli(): Command;
7
8
 
@@ -45,6 +46,60 @@ interface AgentsKitConfig {
45
46
  projectName?: string;
46
47
  };
47
48
  };
49
+ /**
50
+ * Plugin specifiers. Each entry is a package name (`@org/plugin`) or
51
+ * a relative/absolute path to a module exporting a `Plugin`.
52
+ */
53
+ plugins?: string[];
54
+ /**
55
+ * Shell-based hooks keyed by event name. See `extensibility/hooks`.
56
+ */
57
+ hooks?: Record<string, Array<{
58
+ run: string;
59
+ matcher?: string;
60
+ timeout?: number;
61
+ }>>;
62
+ /**
63
+ * Retrieval-augmented generation config. Indexes files via
64
+ * `agentskit rag index`. Chat-side auto-retrieval lands in a later phase.
65
+ */
66
+ rag?: {
67
+ enabled?: boolean;
68
+ backend?: 'memory' | 'file';
69
+ dir?: string;
70
+ sources?: string[];
71
+ embedder?: {
72
+ provider?: string;
73
+ model?: string;
74
+ apiKey?: string;
75
+ baseUrl?: string;
76
+ };
77
+ chunkSize?: number;
78
+ topK?: number;
79
+ };
80
+ /**
81
+ * MCP servers to spawn on chat start. Tools list + call bridge
82
+ * them into the runtime tool set as `<serverName>__<toolName>`.
83
+ */
84
+ mcp?: {
85
+ servers?: Record<string, {
86
+ command: string;
87
+ args?: string[];
88
+ env?: Record<string, string>;
89
+ timeout?: number;
90
+ }>;
91
+ };
92
+ /**
93
+ * Tool permission policy. See `extensibility/permissions`.
94
+ */
95
+ permissions?: {
96
+ mode?: 'default' | 'plan' | 'acceptEdits' | 'bypassPermissions';
97
+ rules?: Array<{
98
+ tool: string;
99
+ action: 'allow' | 'ask' | 'deny';
100
+ scope?: 'session' | 'project' | 'global';
101
+ }>;
102
+ };
48
103
  }
49
104
  interface LoadConfigOptions {
50
105
  cwd?: string;
@@ -68,6 +123,37 @@ interface LoadConfigOptions {
68
123
  */
69
124
  declare function loadConfig(options?: LoadConfigOptions): Promise<AgentsKitConfig | undefined>;
70
125
 
126
+ type PermissionMode = 'default' | 'plan' | 'acceptEdits' | 'bypassPermissions';
127
+ type PermissionAction = 'allow' | 'ask' | 'deny';
128
+ interface PermissionRule {
129
+ /** Exact name, or a `RegExp` / `"re:pattern"` string matching the tool name. */
130
+ tool: string | RegExp;
131
+ action: PermissionAction;
132
+ scope?: 'session' | 'project' | 'global';
133
+ }
134
+ interface PermissionPolicy {
135
+ mode: PermissionMode;
136
+ rules: PermissionRule[];
137
+ }
138
+ declare const defaultPolicy: PermissionPolicy;
139
+ /**
140
+ * Resolve an action for a tool name. Modes override specific rules:
141
+ *
142
+ * - `bypassPermissions`: everything → allow
143
+ * - `plan`: everything → ask (pretend the agent is planning)
144
+ * - `acceptEdits`: fs_write / edit tools → allow, others unchanged
145
+ * - `default`: rules drive it; no matching rule → ask
146
+ */
147
+ declare function evaluatePolicy(policy: PermissionPolicy, toolName: string): PermissionAction;
148
+ /**
149
+ * Apply the policy to a tool definition. Returns the tool with
150
+ * `requiresConfirmation` set per the evaluated action, or `null` when the
151
+ * tool is denied (the caller should skip it entirely).
152
+ */
153
+ declare function applyPolicyToTool(policy: PermissionPolicy, tool: ToolDefinition): ToolDefinition | null;
154
+ /** Filter+annotate a tool list under a policy. Denied tools dropped. */
155
+ declare function applyPolicyToTools(policy: PermissionPolicy, tools: ToolDefinition[]): ToolDefinition[];
156
+
71
157
  type FeedbackKind = 'info' | 'warn' | 'error' | 'success';
72
158
  interface SlashCommandContext {
73
159
  chat: ChatReturn;
@@ -79,6 +165,7 @@ interface SlashCommandContext {
79
165
  baseUrl?: string;
80
166
  tools?: string;
81
167
  skill?: string;
168
+ sessionId?: string;
82
169
  };
83
170
  /** Mutators — each rebuilds the underlying adapter/tool chain. */
84
171
  setProvider: (value: string) => void;
@@ -100,6 +187,106 @@ interface SlashCommand {
100
187
  run: (ctx: SlashCommandContext, argsText: string) => void | Promise<void>;
101
188
  }
102
189
 
190
+ /**
191
+ * Public plugin contract. Every capability shipped by the CLI is a record
192
+ * that a plugin can contribute or override — built-ins use the same shape.
193
+ */
194
+ interface Plugin {
195
+ name: string;
196
+ version?: string;
197
+ slashCommands?: SlashCommand[];
198
+ tools?: ToolDefinition[];
199
+ skills?: SkillDefinition[];
200
+ providers?: Record<string, ProviderFactory>;
201
+ hooks?: HookHandler[];
202
+ mcpServers?: McpServerSpec[];
203
+ init?: (ctx: PluginContext) => void | Promise<void>;
204
+ dispose?: () => void | Promise<void>;
205
+ }
206
+ interface PluginContext {
207
+ /** Working directory the CLI was launched from. */
208
+ cwd: string;
209
+ /** Absolute path to the plugin file (for relative resolution). */
210
+ sourcePath?: string;
211
+ /** Logger. */
212
+ log: (msg: string) => void;
213
+ }
214
+ type ProviderFactory = (config: {
215
+ apiKey?: string;
216
+ model: string;
217
+ baseUrl?: string;
218
+ extra?: Record<string, unknown>;
219
+ }) => unknown;
220
+ type HookEvent = 'SessionStart' | 'SessionEnd' | 'UserPromptSubmit' | 'PreLLM' | 'PostLLM' | 'PreToolUse' | 'PostToolUse' | 'Stop' | 'Error';
221
+ interface HookPayload {
222
+ event: HookEvent;
223
+ [key: string]: unknown;
224
+ }
225
+ type HookResult = {
226
+ decision: 'continue';
227
+ } | {
228
+ decision: 'block';
229
+ reason: string;
230
+ } | {
231
+ decision: 'modify';
232
+ payload: HookPayload;
233
+ };
234
+ interface HookHandler {
235
+ event: HookEvent;
236
+ matcher?: RegExp | ((payload: HookPayload) => boolean);
237
+ run: (payload: HookPayload) => HookResult | Promise<HookResult>;
238
+ }
239
+ interface McpServerSpec {
240
+ name: string;
241
+ command: string;
242
+ args?: string[];
243
+ env?: Record<string, string>;
244
+ timeout?: number;
245
+ }
246
+ /** Plugin factory — a plugin module may export this instead of a Plugin value. */
247
+ type PluginFactory = (ctx: PluginContext) => Plugin | Promise<Plugin>;
248
+ /** Shape of aggregated plugin records after loading. */
249
+ interface PluginBundle {
250
+ plugins: Plugin[];
251
+ slashCommands: SlashCommand[];
252
+ tools: ToolDefinition[];
253
+ skills: SkillDefinition[];
254
+ providers: Record<string, ProviderFactory>;
255
+ hooks: HookHandler[];
256
+ mcpServers: McpServerSpec[];
257
+ }
258
+
259
+ interface LoadPluginsOptions {
260
+ /** Plugin specifiers: absolute paths, relative paths, or package names. */
261
+ specs?: string[];
262
+ /** Extra directories to auto-discover plugin modules from. */
263
+ pluginDirs?: string[];
264
+ /** Working directory; defaults to process.cwd(). */
265
+ cwd?: string;
266
+ /**
267
+ * Auto-discover from `~/.agentskit/plugins`. Defaults to true. Tests pass
268
+ * false so the user's real home dir can't contaminate runs.
269
+ */
270
+ autoDiscoverUserDir?: boolean;
271
+ /** Error logger. Defaults to stderr. */
272
+ onError?: (spec: string, err: unknown) => void;
273
+ /** Info logger. */
274
+ log?: (msg: string) => void;
275
+ }
276
+ /**
277
+ * Load every plugin listed in `specs` + any auto-discovered module in
278
+ * `pluginDirs` (and `~/.agentskit/plugins` by default). Failures on a single
279
+ * plugin are reported but do not abort the rest — a broken third-party
280
+ * plugin should never prevent the CLI from starting.
281
+ */
282
+ declare function loadPlugins(options?: LoadPluginsOptions): Promise<PluginBundle>;
283
+ /**
284
+ * Merge every plugin's records into a single bundle. Later plugins override
285
+ * earlier ones by name (last-write-wins) — users override built-ins without
286
+ * forking.
287
+ */
288
+ declare function mergePluginsIntoBundle(plugins: Plugin[]): PluginBundle;
289
+
103
290
  interface ChatCommandOptions {
104
291
  provider: string;
105
292
  model?: string;
@@ -118,6 +305,14 @@ interface ChatCommandOptions {
118
305
  * built-in by re-registering its name.
119
306
  */
120
307
  slashCommands?: SlashCommand[];
308
+ /** Extra tools contributed by plugins — merged into the resolved tool set. */
309
+ extraTools?: ToolDefinition[];
310
+ /** Extra skills contributed by plugins — merged into the resolved skill set. */
311
+ extraSkills?: SkillDefinition[];
312
+ /** Hook handlers — from plugins + config. Fire on lifecycle events. */
313
+ hookHandlers?: HookHandler[];
314
+ /** Permission policy for tool calls. */
315
+ permissionPolicy?: PermissionPolicy;
121
316
  }
122
317
  declare function ChatApp(options: ChatCommandOptions): react_jsx_runtime.JSX.Element;
123
318
  declare function renderChatHeader(options: ChatCommandOptions): string;
@@ -286,4 +481,227 @@ interface TunnelController {
286
481
  */
287
482
  declare function startTunnel(options: TunnelOptions): Promise<TunnelController>;
288
483
 
289
- export { type AgentsKitConfig, ChatApp, type ChatCommandOptions, type ChatProviderOptions, type CheckResult, type CheckStatus, type DevController, type DevOptions, type DevWatcher, type DoctorOptions, type DoctorReport, type InitCommandOptions, type LoadConfigOptions, type ResolvedChatProvider, type RunCommandOptions, type StarterKind, type TunnelController, type TunnelLike, type TunnelOptions, createCli, loadConfig, renderChatHeader, renderReport, resolveChatProvider, runAgent, runDoctor, startDev, startTunnel, writeStarterProject };
484
+ interface SessionMetadata {
485
+ id: string;
486
+ cwd: string;
487
+ createdAt: string;
488
+ updatedAt: string;
489
+ messageCount: number;
490
+ preview: string;
491
+ provider?: string;
492
+ model?: string;
493
+ /** Optional human-readable label. Set via `/rename` or `renameSession`. */
494
+ label?: string;
495
+ /** Session id this one was forked from, if any. */
496
+ forkedFrom?: string;
497
+ }
498
+ interface SessionRecord {
499
+ metadata: SessionMetadata;
500
+ file: string;
501
+ }
502
+ /** Generate a new session id — ISO-ish timestamp + 6 random hex chars. */
503
+ declare function generateSessionId(): string;
504
+ declare function sessionFilePath(id: string, cwd?: string): string;
505
+ declare function writeSessionMeta(meta: SessionMetadata, cwd?: string): void;
506
+ /** Derive a short preview (first user message, one line, max 80 chars). */
507
+ declare function derivePreview(messages: Array<{
508
+ role: string;
509
+ content: string;
510
+ }>): string;
511
+ declare function listSessions(cwd?: string): SessionRecord[];
512
+ declare function findLatestSession(cwd?: string): SessionRecord | null;
513
+ declare function findSession(id: string, cwd?: string): SessionRecord | null;
514
+ /** Attach or update a human-readable label on an existing session. */
515
+ declare function renameSession(id: string, label: string, cwd?: string): SessionMetadata;
516
+ /**
517
+ * Copy an existing session's message file into a new session so the user
518
+ * can branch a conversation without disturbing the original.
519
+ */
520
+ declare function forkSession(id: string, cwd?: string): ResolvedSession;
521
+ interface ResolveSessionInput {
522
+ explicitPath?: string;
523
+ resumeId?: string | true;
524
+ forceNew?: boolean;
525
+ cwd?: string;
526
+ }
527
+ interface ResolvedSession {
528
+ id: string;
529
+ file: string;
530
+ isNew: boolean;
531
+ }
532
+ /**
533
+ * Decide which session file to read/write:
534
+ * - `explicitPath` wins if given (legacy `--memory-path` flag).
535
+ * - `forceNew` creates a fresh session.
536
+ * - `resumeId === true` → latest in cwd.
537
+ * - `resumeId` string → that id (or prefix).
538
+ * - Otherwise: resume latest if one exists, else create new. Matches
539
+ * Claude Code's "pick up where you left off" default.
540
+ */
541
+ declare function resolveSession(input: ResolveSessionInput): ResolvedSession;
542
+
543
+ interface McpTool {
544
+ name: string;
545
+ description?: string;
546
+ inputSchema?: unknown;
547
+ }
548
+ /**
549
+ * Minimal JSON-RPC-over-stdio client for an MCP server. This is a
550
+ * pragmatic subset of the MCP protocol sufficient for `tools/list` +
551
+ * `tools/call` — full spec support lives in `@modelcontextprotocol/sdk`
552
+ * if/when we depend on it.
553
+ *
554
+ * Transport: newline-delimited JSON (per the MCP stdio transport spec).
555
+ */
556
+ declare class McpClient {
557
+ readonly spec: McpServerSpec;
558
+ private readonly onError;
559
+ private child;
560
+ private buffer;
561
+ private nextId;
562
+ private readonly pending;
563
+ private disposed;
564
+ constructor(spec: McpServerSpec, onError?: (err: unknown) => void);
565
+ start(): Promise<void>;
566
+ listTools(): Promise<McpTool[]>;
567
+ callTool(name: string, args: unknown): Promise<unknown>;
568
+ dispose(): void;
569
+ private request;
570
+ private onStdout;
571
+ }
572
+
573
+ interface McpBridgeResult {
574
+ clients: McpClient[];
575
+ tools: ToolDefinition[];
576
+ }
577
+ /**
578
+ * Start every MCP server in `specs`, call `tools/list`, and produce a
579
+ * `ToolDefinition[]` whose `execute` forwards to the server via
580
+ * `tools/call`. Returns the live clients so the caller can dispose them
581
+ * on session end.
582
+ */
583
+ declare function bridgeMcpServers(specs: McpServerSpec[]): Promise<McpBridgeResult>;
584
+ declare function disposeMcpClients(clients: McpClient[]): void;
585
+
586
+ /**
587
+ * Per-million-token prices in USD. Keeps only a few canonical models —
588
+ * hosts can override at runtime via `registerPricing`. Values are
589
+ * publicly listed prices as of 2026-Q1 and may drift; authoritative
590
+ * cost tracking should come from the provider's invoice.
591
+ */
592
+ interface ModelPricing {
593
+ inputPerM: number;
594
+ outputPerM: number;
595
+ }
596
+ declare function registerPricing(model: string, pricing: ModelPricing): void;
597
+ declare function getPricing(model: string | undefined): ModelPricing | undefined;
598
+ interface TokenUsageLike {
599
+ promptTokens: number;
600
+ completionTokens: number;
601
+ }
602
+ interface ComputedCost {
603
+ model: string;
604
+ inputUsd: number;
605
+ outputUsd: number;
606
+ totalUsd: number;
607
+ }
608
+ /** Returns computed cost, or `undefined` if no pricing is registered for the model. */
609
+ declare function computeCost(model: string | undefined, usage: TokenUsageLike): ComputedCost | undefined;
610
+
611
+ interface OpenAiEmbedderConfig {
612
+ apiKey: string;
613
+ model?: string;
614
+ baseUrl?: string;
615
+ }
616
+ /**
617
+ * Minimal OpenAI-compatible embedder. Works with the official OpenAI API
618
+ * or any gateway that speaks the `/v1/embeddings` shape (OpenRouter,
619
+ * Azure OpenAI, local servers, etc.). One embedding per call — batching
620
+ * is a follow-up when demand arrives.
621
+ */
622
+ declare function createOpenAiEmbedder(config: OpenAiEmbedderConfig): EmbedFn;
623
+
624
+ interface RagConfig {
625
+ enabled?: boolean;
626
+ backend?: 'memory' | 'file';
627
+ dir?: string;
628
+ sources?: string[];
629
+ embedder?: {
630
+ provider?: string;
631
+ model?: string;
632
+ apiKey?: string;
633
+ baseUrl?: string;
634
+ };
635
+ chunkSize?: number;
636
+ topK?: number;
637
+ }
638
+ interface BuildRagOptions {
639
+ config: RagConfig;
640
+ cwd?: string;
641
+ /** Override the embedder resolution (useful for tests). */
642
+ embedder?: EmbedFn;
643
+ }
644
+ interface IndexResult {
645
+ /** Number of input documents ingested. */
646
+ documentCount: number;
647
+ /** Sources globbed + ingested (absolute paths). */
648
+ sources: string[];
649
+ }
650
+ /**
651
+ * Build a live `RAG` instance from a config. Wires the embedder + vector
652
+ * store but does not ingest anything — call `indexSources` for that.
653
+ */
654
+ declare function buildRagFromConfig(options: BuildRagOptions): RAG;
655
+ /**
656
+ * Glob `config.sources` from `cwd`, read each file, and ingest through the
657
+ * provided RAG. Returns a summary of what was indexed.
658
+ */
659
+ declare function indexSources(rag: RAG, config: RagConfig, cwd?: string): Promise<IndexResult>;
660
+
661
+ interface HookDispatchResult {
662
+ /** Final payload after any `modify` handlers. */
663
+ payload: HookPayload;
664
+ /** True when any handler returned `block`. */
665
+ blocked: boolean;
666
+ /** Block reason from the first blocking handler, if any. */
667
+ reason?: string;
668
+ }
669
+ /**
670
+ * Runs every handler registered for an event in order. Handlers can:
671
+ * - `continue`: do nothing, pass payload through
672
+ * - `modify`: replace the payload for subsequent handlers
673
+ * - `block`: stop dispatch and surface a reason to the caller
674
+ *
675
+ * Handlers that throw are reported via `onError` and treated as `continue`.
676
+ */
677
+ declare class HookDispatcher {
678
+ private readonly onError;
679
+ private readonly handlers;
680
+ constructor(handlers?: HookHandler[], onError?: (handler: HookHandler, err: unknown) => void);
681
+ register(handler: HookHandler): void;
682
+ dispatch(event: HookEvent, payload: HookPayload): Promise<HookDispatchResult>;
683
+ private matches;
684
+ }
685
+
686
+ interface ConfigHookEntry {
687
+ /** Command to run. Executed through `sh -c`, so shell syntax is allowed. */
688
+ run: string;
689
+ /** Optional regex string the hook's subject must match to fire. */
690
+ matcher?: string;
691
+ /** Millisecond budget. Default 5000. */
692
+ timeout?: number;
693
+ }
694
+ type ConfigHooksMap = Partial<Record<HookEvent, ConfigHookEntry[]>>;
695
+ /**
696
+ * Normalize `config.hooks` (shell entries) into `HookHandler[]`. The shell
697
+ * command receives the JSON-serialized payload on stdin. It can print a
698
+ * single JSON object on stdout to `modify` the payload, or exit non-zero
699
+ * to `block`.
700
+ *
701
+ * { "decision": "continue" } — default, no output needed
702
+ * { "decision": "block", "reason": "…" } — also signalled by non-zero exit
703
+ * { "decision": "modify", "payload": … } — swaps the payload
704
+ */
705
+ declare function configHooksToHandlers(config: ConfigHooksMap | undefined): HookHandler[];
706
+
707
+ export { type AgentsKitConfig, type BuildRagOptions, ChatApp, type ChatCommandOptions, type ChatProviderOptions, type CheckResult, type CheckStatus, type ComputedCost, type ConfigHookEntry, type ConfigHooksMap, type DevController, type DevOptions, type DevWatcher, type DoctorOptions, type DoctorReport, type HookDispatchResult, HookDispatcher, type HookEvent, type HookHandler, type HookPayload, type HookResult, type IndexResult, type InitCommandOptions, type LoadConfigOptions, type LoadPluginsOptions, type McpBridgeResult, McpClient, type McpServerSpec, type McpTool, type ModelPricing, type OpenAiEmbedderConfig, type PermissionAction, type PermissionMode, type PermissionPolicy, type PermissionRule, type Plugin, type PluginBundle, type PluginContext, type PluginFactory, type ProviderFactory, type RagConfig, type ResolveSessionInput, type ResolvedChatProvider, type ResolvedSession, type RunCommandOptions, type SessionMetadata, type SessionRecord, type StarterKind, type TokenUsageLike, type TunnelController, type TunnelLike, type TunnelOptions, applyPolicyToTool, applyPolicyToTools, bridgeMcpServers, buildRagFromConfig, computeCost, configHooksToHandlers, createCli, createOpenAiEmbedder, defaultPolicy, derivePreview, disposeMcpClients, evaluatePolicy, findLatestSession, findSession, forkSession, generateSessionId, getPricing, indexSources, listSessions, loadConfig, loadPlugins, mergePluginsIntoBundle, registerPricing, renameSession, renderChatHeader, renderReport, resolveChatProvider, resolveSession, runAgent, runDoctor, sessionFilePath, startDev, startTunnel, writeSessionMeta, writeStarterProject };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- export { ChatApp, createCli, loadConfig, renderChatHeader, renderReport, resolveChatProvider, runAgent, runDoctor, startDev, startTunnel, writeStarterProject } from './chunk-V7E4HWTG.js';
2
+ export { ChatApp, HookDispatcher, McpClient, applyPolicyToTool, applyPolicyToTools, bridgeMcpServers, buildRagFromConfig, computeCost, configHooksToHandlers, createCli, createOpenAiEmbedder, defaultPolicy, derivePreview, disposeMcpClients, evaluatePolicy, findLatestSession, findSession, forkSession, generateSessionId, getPricing, indexSources, listSessions, loadConfig, loadPlugins, mergePluginsIntoBundle, registerPricing, renameSession, renderChatHeader, renderReport, resolveChatProvider, resolveSession, runAgent, runDoctor, sessionFilePath, startDev, startTunnel, writeSessionMeta, writeStarterProject } from './chunk-72XFU2X2.js';
3
3
  //# sourceMappingURL=index.js.map
4
4
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentskit/cli",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "CLI for AgentsKit chat and project scaffolding.",
5
5
  "keywords": [
6
6
  "agentskit",
@@ -51,9 +51,10 @@
51
51
  "@agentskit/core": "1.3.0",
52
52
  "@agentskit/ink": "0.7.0",
53
53
  "@agentskit/memory": "0.5.4",
54
+ "@agentskit/rag": "0.1.7",
54
55
  "@agentskit/runtime": "0.4.7",
55
- "@agentskit/skills": "0.4.7",
56
- "@agentskit/tools": "0.6.0"
56
+ "@agentskit/tools": "0.6.0",
57
+ "@agentskit/skills": "0.4.7"
57
58
  },
58
59
  "devDependencies": {
59
60
  "@types/localtunnel": "^2.0.4",