@sundaeswap/sprinkles 0.7.0 → 0.8.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.
Files changed (95) hide show
  1. package/README.md +178 -181
  2. package/dist/cjs/Sprinkle/__tests__/builtin-actions.test.js +4 -4
  3. package/dist/cjs/Sprinkle/__tests__/builtin-actions.test.js.map +1 -1
  4. package/dist/cjs/Sprinkle/__tests__/cli-adapter.test.js +25 -3
  5. package/dist/cjs/Sprinkle/__tests__/cli-adapter.test.js.map +1 -1
  6. package/dist/cjs/Sprinkle/__tests__/fill-in-struct.test.js +15 -1
  7. package/dist/cjs/Sprinkle/__tests__/fill-in-struct.test.js.map +1 -1
  8. package/dist/cjs/Sprinkle/__tests__/mcp-adapter.test.js +7 -9
  9. package/dist/cjs/Sprinkle/__tests__/mcp-adapter.test.js.map +1 -1
  10. package/dist/cjs/Sprinkle/__tests__/native-script.test.js +390 -0
  11. package/dist/cjs/Sprinkle/__tests__/native-script.test.js.map +1 -0
  12. package/dist/cjs/Sprinkle/__tests__/utility-actions.test.js +367 -0
  13. package/dist/cjs/Sprinkle/__tests__/utility-actions.test.js.map +1 -0
  14. package/dist/cjs/Sprinkle/actions/builtin/addressbook-actions.js +164 -0
  15. package/dist/cjs/Sprinkle/actions/builtin/addressbook-actions.js.map +1 -0
  16. package/dist/cjs/Sprinkle/actions/builtin/index.js +60 -3
  17. package/dist/cjs/Sprinkle/actions/builtin/index.js.map +1 -1
  18. package/dist/cjs/Sprinkle/actions/builtin/native-script.js +139 -0
  19. package/dist/cjs/Sprinkle/actions/builtin/native-script.js.map +1 -0
  20. package/dist/cjs/Sprinkle/actions/builtin/utility-actions.js +218 -0
  21. package/dist/cjs/Sprinkle/actions/builtin/utility-actions.js.map +1 -0
  22. package/dist/cjs/Sprinkle/actions/cli-adapter.js +20 -2
  23. package/dist/cjs/Sprinkle/actions/cli-adapter.js.map +1 -1
  24. package/dist/cjs/Sprinkle/actions/index.js +12 -0
  25. package/dist/cjs/Sprinkle/actions/index.js.map +1 -1
  26. package/dist/cjs/Sprinkle/actions/mcp-adapter.js +146 -4
  27. package/dist/cjs/Sprinkle/actions/mcp-adapter.js.map +1 -1
  28. package/dist/cjs/Sprinkle/index.js +267 -5
  29. package/dist/cjs/Sprinkle/index.js.map +1 -1
  30. package/dist/cjs/Sprinkle/schemas.js +17 -1
  31. package/dist/cjs/Sprinkle/schemas.js.map +1 -1
  32. package/dist/esm/Sprinkle/__tests__/builtin-actions.test.js +4 -4
  33. package/dist/esm/Sprinkle/__tests__/builtin-actions.test.js.map +1 -1
  34. package/dist/esm/Sprinkle/__tests__/cli-adapter.test.js +25 -3
  35. package/dist/esm/Sprinkle/__tests__/cli-adapter.test.js.map +1 -1
  36. package/dist/esm/Sprinkle/__tests__/fill-in-struct.test.js +15 -1
  37. package/dist/esm/Sprinkle/__tests__/fill-in-struct.test.js.map +1 -1
  38. package/dist/esm/Sprinkle/__tests__/mcp-adapter.test.js +7 -9
  39. package/dist/esm/Sprinkle/__tests__/mcp-adapter.test.js.map +1 -1
  40. package/dist/esm/Sprinkle/__tests__/native-script.test.js +388 -0
  41. package/dist/esm/Sprinkle/__tests__/native-script.test.js.map +1 -0
  42. package/dist/esm/Sprinkle/__tests__/utility-actions.test.js +365 -0
  43. package/dist/esm/Sprinkle/__tests__/utility-actions.test.js.map +1 -0
  44. package/dist/esm/Sprinkle/actions/builtin/addressbook-actions.js +159 -0
  45. package/dist/esm/Sprinkle/actions/builtin/addressbook-actions.js.map +1 -0
  46. package/dist/esm/Sprinkle/actions/builtin/index.js +8 -3
  47. package/dist/esm/Sprinkle/actions/builtin/index.js.map +1 -1
  48. package/dist/esm/Sprinkle/actions/builtin/native-script.js +133 -0
  49. package/dist/esm/Sprinkle/actions/builtin/native-script.js.map +1 -0
  50. package/dist/esm/Sprinkle/actions/builtin/utility-actions.js +213 -0
  51. package/dist/esm/Sprinkle/actions/builtin/utility-actions.js.map +1 -0
  52. package/dist/esm/Sprinkle/actions/cli-adapter.js +20 -2
  53. package/dist/esm/Sprinkle/actions/cli-adapter.js.map +1 -1
  54. package/dist/esm/Sprinkle/actions/index.js +1 -1
  55. package/dist/esm/Sprinkle/actions/index.js.map +1 -1
  56. package/dist/esm/Sprinkle/actions/mcp-adapter.js +145 -5
  57. package/dist/esm/Sprinkle/actions/mcp-adapter.js.map +1 -1
  58. package/dist/esm/Sprinkle/index.js +259 -8
  59. package/dist/esm/Sprinkle/index.js.map +1 -1
  60. package/dist/esm/Sprinkle/schemas.js +16 -0
  61. package/dist/esm/Sprinkle/schemas.js.map +1 -1
  62. package/dist/types/Sprinkle/actions/builtin/addressbook-actions.d.ts +50 -0
  63. package/dist/types/Sprinkle/actions/builtin/addressbook-actions.d.ts.map +1 -0
  64. package/dist/types/Sprinkle/actions/builtin/index.d.ts +6 -2
  65. package/dist/types/Sprinkle/actions/builtin/index.d.ts.map +1 -1
  66. package/dist/types/Sprinkle/actions/builtin/native-script.d.ts +27 -0
  67. package/dist/types/Sprinkle/actions/builtin/native-script.d.ts.map +1 -0
  68. package/dist/types/Sprinkle/actions/builtin/utility-actions.d.ts +48 -0
  69. package/dist/types/Sprinkle/actions/builtin/utility-actions.d.ts.map +1 -0
  70. package/dist/types/Sprinkle/actions/cli-adapter.d.ts.map +1 -1
  71. package/dist/types/Sprinkle/actions/index.d.ts +2 -1
  72. package/dist/types/Sprinkle/actions/index.d.ts.map +1 -1
  73. package/dist/types/Sprinkle/actions/mcp-adapter.d.ts +24 -0
  74. package/dist/types/Sprinkle/actions/mcp-adapter.d.ts.map +1 -1
  75. package/dist/types/Sprinkle/index.d.ts +3 -1
  76. package/dist/types/Sprinkle/index.d.ts.map +1 -1
  77. package/dist/types/Sprinkle/schemas.d.ts +72 -0
  78. package/dist/types/Sprinkle/schemas.d.ts.map +1 -1
  79. package/dist/types/tsconfig.build.tsbuildinfo +1 -1
  80. package/package.json +1 -1
  81. package/src/Sprinkle/__tests__/builtin-actions.test.ts +4 -4
  82. package/src/Sprinkle/__tests__/cli-adapter.test.ts +24 -3
  83. package/src/Sprinkle/__tests__/fill-in-struct.test.ts +23 -1
  84. package/src/Sprinkle/__tests__/mcp-adapter.test.ts +7 -5
  85. package/src/Sprinkle/__tests__/native-script.test.ts +341 -0
  86. package/src/Sprinkle/__tests__/utility-actions.test.ts +348 -0
  87. package/src/Sprinkle/actions/builtin/addressbook-actions.ts +168 -0
  88. package/src/Sprinkle/actions/builtin/index.ts +41 -2
  89. package/src/Sprinkle/actions/builtin/native-script.ts +165 -0
  90. package/src/Sprinkle/actions/builtin/utility-actions.ts +285 -0
  91. package/src/Sprinkle/actions/cli-adapter.ts +18 -2
  92. package/src/Sprinkle/actions/index.ts +2 -1
  93. package/src/Sprinkle/actions/mcp-adapter.ts +179 -4
  94. package/src/Sprinkle/index.ts +261 -3
  95. package/src/Sprinkle/schemas.ts +20 -0
package/README.md CHANGED
@@ -2,16 +2,41 @@
2
2
 
3
3
  > **Note:** This is an early release (v0.x). The API may change between minor versions as we refine the library based on community feedback.
4
4
 
5
- A TypeScript library for building interactive CLI menus and TUI applications with TypeBox schema validation. Sprinkles provides a declarative way to create nested menus, handle user input, and manage persistent settings with type safety.
5
+ A TypeScript library for building action-centric CLI applications with TypeBox schema validation. Define your actions once, and Sprinkles exposes them through three facades: an interactive **TUI**, a **CLI** with auto-generated flags and help, and an **MCP** server for AI agent integration.
6
+
7
+ ## Architecture
8
+
9
+ Sprinkles follows an **action-centric** design. The core building block is an `IAction` — a typed, self-describing unit of work with input/output schemas:
10
+
11
+ ```
12
+ ┌──────────────┐
13
+ │ Actions │ ← define once
14
+ │ (IAction) │
15
+ └──────┬───────┘
16
+
17
+ ┌────────────┼────────────┐
18
+ ▼ ▼ ▼
19
+ ┌─────────┐ ┌─────────┐ ┌─────────┐
20
+ │ TUI │ │ CLI │ │ MCP │ ← three facades
21
+ │ (menu) │ │ (flags) │ │ (tools) │
22
+ └─────────┘ └─────────┘ └─────────┘
23
+ ```
24
+
25
+ - **TUI** — Interactive menu-driven interface. Users select actions from a menu, and Sprinkles auto-generates prompts from input schemas.
26
+ - **CLI** — Non-interactive command execution. Actions become subcommands with auto-generated `--flags` from input schemas and structured JSON output.
27
+ - **MCP** — Model Context Protocol server. Actions are exposed as tools that AI agents can discover and call with typed JSON input/output.
28
+
29
+ `Sprinkle.run()` detects the mode from `process.argv` and dispatches automatically.
6
30
 
7
31
  ## Features
8
32
 
9
- - **Schema-driven UI**: Define your data structures using TypeBox schemas, and Sprinkles automatically generates interactive prompts
10
- - **Nested menu support**: Create hierarchical menu structures with submenus
11
- - **Persistent settings**: Automatic JSON-based settings storage with BigInt support
12
- - **Type-safe**: Full TypeScript support with type inference from schemas
33
+ - **Action-centric**: Define actions with TypeBox input/output schemas; get TUI, CLI, and MCP for free
34
+ - **Schema-driven UI**: Automatic prompt generation from TypeBox schemas
35
+ - **Multi-profile support**: Multiple named profiles with independent settings
36
+ - **Persistent settings**: JSON-based storage with BigInt support and optional encryption
37
+ - **14 built-in actions**: Profile management, settings, wallet operations, and transaction handling
13
38
  - **Cardano integration**: Built-in helpers for Blaze SDK wallet and provider management
14
- - **Transaction dialogs**: Ready-to-use transaction signing and submission flows
39
+ - **Type-safe**: Full TypeScript support with type inference from schemas
15
40
 
16
41
  ## Installation
17
42
 
@@ -19,241 +44,213 @@ A TypeScript library for building interactive CLI menus and TUI applications wit
19
44
  npm install @sundaeswap/sprinkles
20
45
  ```
21
46
 
47
+ Optional peer dependencies:
48
+ - `@blaze-cardano/sdk` + `@blaze-cardano/query` — for wallet and transaction actions
49
+ - `@modelcontextprotocol/sdk` — for MCP mode
50
+
22
51
  ## Quick Start
23
52
 
24
53
  ```typescript
25
- import { Sprinkle, Type } from "sprinkles";
26
-
27
- // Define your settings schema
28
- const AppSettingsSchema = Type.Object({
29
- username: Type.String({ title: "Username" }),
30
- theme: Type.Union([
31
- Type.Literal("dark"),
32
- Type.Literal("light")
33
- ])
54
+ import { Type } from "@sinclair/typebox";
55
+ import type { TSchema } from "@sinclair/typebox";
56
+ import {
57
+ Sprinkle,
58
+ promptAndExecute,
59
+ getBuiltinActions,
60
+ NetworkSchema,
61
+ ProviderSettingsSchema,
62
+ WalletSettingsSchema,
63
+ } from "@sundaeswap/sprinkles";
64
+ import type { AnyAction, IAction, IMenu } from "@sundaeswap/sprinkles";
65
+
66
+ // 1. Define your settings schema
67
+ const AppSettings = Type.Object({
68
+ network: NetworkSchema,
69
+ provider: ProviderSettingsSchema,
70
+ wallet: WalletSettingsSchema,
34
71
  });
35
72
 
36
- // Create a Sprinkle instance
37
- const app = await Sprinkle.New(AppSettingsSchema, "./config");
73
+ // 2. Define a custom action
74
+ const greetAction: IAction<
75
+ { name: string },
76
+ { greeting: string },
77
+ TSchema
78
+ > = {
79
+ name: "greet",
80
+ description: "Say hello to someone.",
81
+ category: "app",
82
+ inputSchema: Type.Object({
83
+ name: Type.String({ description: "Person to greet" }),
84
+ }),
85
+ outputSchema: Type.Object({
86
+ greeting: Type.String(),
87
+ }),
88
+ execute: async (input) => ({
89
+ greeting: `Hello, ${input.name}!`,
90
+ }),
91
+ };
38
92
 
39
- // Define your menu
40
- const mainMenu = {
41
- title: "Main Menu",
93
+ // 3. Define a TUI menu
94
+ const menu: IMenu<typeof AppSettings> = {
95
+ title: "My App",
42
96
  items: [
43
97
  {
44
- title: "Say Hello",
98
+ title: "Greet someone",
45
99
  action: async (sprinkle) => {
46
- console.log(`Hello, ${sprinkle.settings.username}!`);
47
- }
48
- }
49
- ]
100
+ const result = await promptAndExecute(sprinkle, greetAction);
101
+ if (result.success) {
102
+ console.log(result.data.greeting);
103
+ } else if (result.error.code !== "USER_CANCELLED") {
104
+ console.error("Error:", result.error.message);
105
+ }
106
+ },
107
+ },
108
+ ],
50
109
  };
51
110
 
52
- // Show the menu
53
- await app.showMenu(mainMenu);
111
+ // 4. Run — mode is detected automatically
112
+ await Sprinkle.run({
113
+ type: AppSettings,
114
+ storagePath: `${process.env["HOME"]}/.config/my-app`,
115
+ menu,
116
+ actions: [
117
+ greetAction as unknown as AnyAction<typeof AppSettings>,
118
+ ...getBuiltinActions<typeof AppSettings>(),
119
+ ],
120
+ });
54
121
  ```
55
122
 
56
- ## API Reference
57
-
58
- ### Core Classes
123
+ This single entry point gives you:
59
124
 
60
- #### `Sprinkle<S extends TSchema>`
125
+ ```bash
126
+ # TUI mode (interactive)
127
+ my-app
61
128
 
62
- The main class for managing your CLI application.
129
+ # CLI mode
130
+ my-app greet --name Alice
131
+ my-app list-profiles
132
+ my-app get-settings
63
133
 
64
- ##### Constructor
134
+ # MCP mode (AI agent)
135
+ my-app --mcp
65
136
 
66
- ```typescript
67
- constructor(type: S, storagePath: string)
137
+ # Help
138
+ my-app --help
139
+ my-app greet --help
68
140
  ```
69
141
 
70
- ##### Static Methods
71
-
72
- - `Sprinkle.New<S>(type: S, storagePath: string): Promise<Sprinkle<S>>` - Create and initialize a new Sprinkle instance
73
- - `Sprinkle.GetProvider(network, settings): Provider` - Create a Cardano provider instance
74
- - `Sprinkle.GetWallet(settings, provider): Promise<Wallet>` - Create a Cardano wallet instance
75
- - `Sprinkle.GetBlaze(network, providerSettings, walletSettings): Promise<Blaze>` - Create a Blaze SDK instance
76
- - `Sprinkle.SettingsPath(storagePath: string): string` - Get the settings file path
77
-
78
- ##### Instance Methods
142
+ ## Actions
79
143
 
80
- - `showMenu(menu: IMenu<S>): Promise<void>` - Display a menu and handle user interactions
81
- - `EditStruct<U>(type: U, current: TExact<U>): Promise<TExact<U>>` - Interactive editor for schema-based structures
82
- - `FillInStruct<U>(type: U, def?: TExact<U>): Promise<TExact<U>>` - Interactive form to fill in a structure
83
- - `TxDialog(blaze, tx): Promise<void>` - Display transaction dialog with sign/submit options
84
- - `saveSettings(): void` - Persist current settings to disk
85
- - `LoadSettings(type, storagePath): Promise<void>` - Load settings from disk
144
+ ### Defining Actions
86
145
 
87
- ### Types
88
-
89
- #### `IMenu<S>`
90
-
91
- Defines a menu structure:
146
+ An action is an object implementing `IAction<TInput, TOutput, TSettings>`:
92
147
 
93
148
  ```typescript
94
- interface IMenu<S extends TSchema> {
95
- title: string;
96
- items: TMenuItem<S>[];
97
- }
149
+ const myAction: IAction<
150
+ { url: string; timeout?: number },
151
+ { status: number },
152
+ TSchema
153
+ > = {
154
+ name: "check-health",
155
+ description: "Check if a service is healthy.",
156
+ category: "ops",
157
+ inputSchema: Type.Object({
158
+ url: Type.String({ description: "Service URL" }),
159
+ timeout: Type.Optional(Type.Number({ description: "Timeout in ms", default: 5000 })),
160
+ }),
161
+ outputSchema: Type.Object({
162
+ status: Type.Number(),
163
+ }),
164
+ execute: async (input, context) => {
165
+ // context.settings gives you the current profile's settings
166
+ // context.sprinkle gives you the Sprinkle instance
167
+ const res = await fetch(input.url, { signal: AbortSignal.timeout(input.timeout ?? 5000) });
168
+ return { status: res.status };
169
+ },
170
+ };
98
171
  ```
99
172
 
100
- #### `IMenuAction<S>`
101
-
102
- Defines a menu action:
173
+ ### Built-in Actions
174
+
175
+ Sprinkles includes 14 built-in actions available via `getBuiltinActions()`:
176
+
177
+ | Category | Action | Description |
178
+ |----------|--------|-------------|
179
+ | sprinkles | `list-profiles` | List all profiles |
180
+ | sprinkles | `get-profile` | Get profile metadata and settings |
181
+ | sprinkles | `set-profile` | Switch active profile |
182
+ | sprinkles | `create-profile` | Create a new profile |
183
+ | sprinkles | `delete-profile` | Delete a profile |
184
+ | sprinkles | `get-settings` | Get current settings |
185
+ | sprinkles | `update-settings` | Update settings |
186
+ | wallet | `get-wallet-address` | Get wallet address |
187
+ | wallet | `get-wallet-balance` | Get ADA and token balances |
188
+ | wallet | `get-wallet-utxos` | Get UTxO set |
189
+ | wallet | `sign-transaction` | Sign a transaction (hot wallet only) |
190
+ | wallet | `submit-transaction` | Submit a signed transaction |
191
+ | wallet | `sign-and-submit` | Sign and submit in one step |
192
+ | transaction | `decode-transaction` | Decode transaction CBOR |
193
+
194
+ ### Running Actions Programmatically
103
195
 
104
196
  ```typescript
105
- interface IMenuAction<S extends TSchema> {
106
- title: string;
107
- action: (sprinkle: Sprinkle<S>) => Promise<Sprinkle<S> | void>;
108
- }
109
- ```
110
-
111
- #### `TMenuItem<S>`
112
-
113
- A menu item can be either an action or a submenu:
197
+ import { executeAction } from "@sundaeswap/sprinkles";
114
198
 
115
- ```typescript
116
- type TMenuItem<S extends TSchema> = IMenuAction<S> | IMenu<S>;
199
+ const result = await executeAction(myAction, { url: "https://example.com" }, context);
200
+ if (result.success) {
201
+ console.log(result.data.status);
202
+ } else {
203
+ console.error(result.error.code, result.error.message);
204
+ }
117
205
  ```
118
206
 
119
- ### Built-in Schemas
207
+ ## Built-in Schemas
120
208
 
121
- #### `NetworkSchema`
209
+ Sprinkles exports common Cardano schemas for use in your settings:
122
210
 
123
- Cardano network selection:
211
+ ### `NetworkSchema`
124
212
 
125
213
  ```typescript
126
- Type.Union([
127
- Type.Literal("mainnet"),
128
- Type.Literal("preview"),
129
- Type.Literal("preprod")
130
- ])
214
+ Type.Union([Type.Literal("mainnet"), Type.Literal("preview"), Type.Literal("preprod")])
131
215
  ```
132
216
 
133
- #### `ProviderSettingsSchema`
134
-
135
- Cardano provider configuration:
217
+ ### `ProviderSettingsSchema`
136
218
 
137
219
  ```typescript
138
220
  Type.Union([
139
221
  Type.Object({
140
222
  type: Type.Literal("blockfrost"),
141
- projectId: Type.String({ minLength: 1, title: "Blockfrost Project ID" })
223
+ projectId: Type.String({ minLength: 1, title: "Blockfrost Project ID" }),
142
224
  }),
143
225
  Type.Object({
144
226
  type: Type.Literal("maestro"),
145
- apiKey: Type.String({ minLength: 1, title: "Maestro API Key" })
146
- })
227
+ apiKey: Type.String({ minLength: 1, title: "Maestro API Key" }),
228
+ }),
147
229
  ])
148
230
  ```
149
231
 
150
- #### `WalletSettingsSchema`
151
-
152
- Cardano wallet configuration:
232
+ ### `WalletSettingsSchema`
153
233
 
154
234
  ```typescript
155
235
  Type.Union([
156
236
  Type.Object({
157
237
  type: Type.Literal("hot"),
158
- privateKey: Type.String({ minLength: 1, title: "Hot Wallet Private Key" })
238
+ privateKey: Type.String({ minLength: 1, title: "Hot Wallet Private Key", sensitive: true }),
159
239
  }),
160
240
  Type.Object({
161
241
  type: Type.Literal("cold"),
162
- address: Type.String({ minLength: 1, title: "Cold Wallet Address" })
163
- })
242
+ address: Type.String({ minLength: 1, title: "Cold Wallet Address" }),
243
+ }),
164
244
  ])
165
245
  ```
166
246
 
167
- #### `MultisigScript`
168
-
169
- Cardano multisig script schema with support for:
170
- - Signature verification
171
- - AllOf (all signatures required)
172
- - AnyOf (any signature works)
173
- - AtLeast (m-of-n threshold)
174
- - Before/After (time-based conditions)
175
- - Script hash references
176
-
177
- ## Advanced Usage
178
-
179
- ### Creating Nested Menus
180
-
181
- ```typescript
182
- const menu = {
183
- title: "Main Menu",
184
- items: [
185
- {
186
- title: "User Management",
187
- items: [
188
- {
189
- title: "Add User",
190
- action: async (sprinkle) => {
191
- // Add user logic
192
- }
193
- },
194
- {
195
- title: "Remove User",
196
- action: async (sprinkle) => {
197
- // Remove user logic
198
- }
199
- }
200
- ]
201
- }
202
- ]
203
- };
204
- ```
205
-
206
- ### Modifying Settings from Actions
207
-
208
- ```typescript
209
- {
210
- title: "Change Username",
211
- action: async (sprinkle) => {
212
- const newSettings = await sprinkle.EditStruct(
213
- Type.Object({ username: Type.String() }),
214
- { username: sprinkle.settings.username }
215
- );
216
- return new Sprinkle(sprinkle.type, sprinkle.storagePath);
217
- }
218
- }
219
- ```
220
-
221
- ### Working with Cardano Transactions
247
+ ## Examples
222
248
 
223
- ```typescript
224
- const blaze = await Sprinkle.GetBlaze(
225
- "preprod",
226
- providerSettings,
227
- walletSettings
228
- );
229
-
230
- // Build your transaction
231
- const tx = await blaze
232
- .newTransaction()
233
- .payLovelace(recipientAddress, 5_000_000n)
234
- .complete();
235
-
236
- // Show transaction dialog
237
- await app.TxDialog(blaze, tx);
238
- ```
239
-
240
- ## Settings Persistence
241
-
242
- Settings are automatically saved to `{storagePath}/settings.json`. The file format includes:
243
-
244
- ```json
245
- {
246
- "settings": {
247
- "username": "alice",
248
- "count": "42n"
249
- },
250
- "defaults": {
251
- "string": "last_entered_value"
252
- }
253
- }
254
- ```
249
+ See [`examples/`](./examples/) for runnable examples:
255
250
 
256
- Note: BigInt values are serialized with an 'n' suffix and automatically parsed on load.
251
+ - **[`demo-app/`](./examples/demo-app/)** Full-featured testing harness exercising all built-in actions across TUI, CLI, and MCP modes
252
+ - **[`action-example.ts`](./examples/action-example.ts)** — Multi-mode example with a custom action
253
+ - **[`action-in-menu.ts`](./examples/action-in-menu.ts)** — TUI menu integration patterns
257
254
 
258
255
  ## License
259
256
 
@@ -49,13 +49,13 @@ async function makeSprinkle(storagePath, profileId = "default", settings = {
49
49
  return sprinkle;
50
50
  }
51
51
  (0, _bunTest.describe)("getBuiltinActions", () => {
52
- (0, _bunTest.test)("returns an array of 14 actions", () => {
52
+ (0, _bunTest.test)("returns an array of 21 actions", () => {
53
53
  const actions = (0, _index.getBuiltinActions)();
54
- (0, _bunTest.expect)(actions).toHaveLength(14);
54
+ (0, _bunTest.expect)(actions).toHaveLength(21);
55
55
  });
56
56
  (0, _bunTest.test)("all actions have a category", () => {
57
57
  const actions = (0, _index.getBuiltinActions)();
58
- const validCategories = ["sprinkles", "wallet", "transaction"];
58
+ const validCategories = ["sprinkles", "wallet", "transaction", "utility", "addressbook"];
59
59
  for (const action of actions) {
60
60
  (0, _bunTest.expect)(validCategories).toContain(action.category);
61
61
  }
@@ -83,7 +83,7 @@ async function makeSprinkle(storagePath, profileId = "default", settings = {
83
83
  for (const action of actions) {
84
84
  sprinkle.registerAction(action);
85
85
  }
86
- (0, _bunTest.expect)(sprinkle.listActions()).toHaveLength(14);
86
+ (0, _bunTest.expect)(sprinkle.listActions()).toHaveLength(21);
87
87
  });
88
88
  });
89
89