@ic-reactor/cli 0.4.1 → 0.5.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/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @ic-reactor/cli
2
2
 
3
- > 🔧 Command-line tool for generating IC reactor hooks and declarations.
3
+ > 🔧 Command-line tool for generating type-safe React hooks for ICP canisters.
4
4
 
5
- The `@ic-reactor/cli` helps you generate TypeScript declarations and React hooks from your Candid files. It provides a simple way to keep your frontend types and interactions in sync with your backend canisters.
5
+ The `@ic-reactor/cli` helps you generate TypeScript declarations and React hooks from your Candid files. It uses the shared `@ic-reactor/codegen` pipeline to ensure consistency with the Vite plugin.
6
6
 
7
7
  ## Installation
8
8
 
@@ -15,10 +15,10 @@ pnpm add -D @ic-reactor/cli
15
15
  ### 1. Initialize your project
16
16
 
17
17
  ```bash
18
- npx @ic-reactor/cli init
18
+ npx ic-reactor init
19
19
  ```
20
20
 
21
- This creates an `ic-reactor.json` configuration file in your project root.
21
+ This creates an `ic-reactor.json` configuration file in your project root and optionally sets up a default `ClientManager` at `src/clients.ts`.
22
22
 
23
23
  ### 2. Configure your canisters
24
24
 
@@ -26,35 +26,36 @@ Update `ic-reactor.json` with the paths to your Candid files:
26
26
 
27
27
  ```json
28
28
  {
29
- "outDir": "./src/canisters",
29
+ "outDir": "src/declarations",
30
+ "clientManagerPath": "../../clients",
30
31
  "canisters": {
31
32
  "backend": {
32
- "didFile": "src/declarations/backend.did"
33
+ "didFile": "src/backend/backend.did"
33
34
  }
34
35
  }
35
36
  }
36
37
  ```
37
38
 
38
- ### 3. Sync hooks and declarations
39
+ ### 3. Generate hooks
39
40
 
40
41
  ```bash
41
- npx @ic-reactor/cli sync
42
+ npx ic-reactor generate
42
43
  ```
43
44
 
44
45
  This command will:
45
46
 
46
- 1. Regenerate TypeScript declarations for your canisters.
47
- 2. Create an `index.ts` file for each canister with fully typed hooks.
47
+ 1. Generate TypeScript declarations (`.d.ts`, `.js`, `.did`) for each canister.
48
+ 2. Create an `index.ts` reactor file for each canister with fully typed hooks.
48
49
 
49
50
  ### 4. Use the generated hooks
50
51
 
51
52
  Import the hooks directly from the generated output folder:
52
53
 
53
54
  ```tsx
54
- import { useActorQuery } from "./canisters/backend"
55
+ import { useBackendQuery } from "./declarations/backend"
55
56
 
56
57
  function MyComponent() {
57
- const { data, isPending } = useActorQuery({
58
+ const { data, isPending } = useBackendQuery({
58
59
  functionName: "get_message",
59
60
  })
60
61
 
@@ -69,50 +70,52 @@ function MyComponent() {
69
70
  Initialize the configuration file (`ic-reactor.json`).
70
71
 
71
72
  ```bash
72
- npx @ic-reactor/cli init [options]
73
+ npx ic-reactor init [options]
73
74
 
74
75
  Options:
75
76
  -y, --yes Skip prompts and use defaults
76
77
  -o, --out-dir <path> Output directory for generated hooks
77
78
  ```
78
79
 
79
- ### `sync`
80
+ ### `generate` (alias: `g`)
80
81
 
81
- Regenerate hooks and declarations based on your configuration and DID files.
82
+ Generate hooks and declarations based on your configuration and DID files.
82
83
 
83
84
  ```bash
84
- npx @ic-reactor/cli sync [options]
85
+ npx ic-reactor generate [options]
85
86
 
86
87
  Options:
87
- -c, --canister <name> Sync only a specific canister
88
- ```
89
-
90
- ### `list`
91
-
92
- List all available methods from a canister's Candid definition.
93
-
94
- ```bash
95
- npx @ic-reactor/cli list -c <canister_name>
88
+ -c, --canister <name> Generate only for a specific canister
89
+ --clean Clean output directory before generating
96
90
  ```
97
91
 
98
92
  ## Configuration
99
93
 
100
- The `ic-reactor.json` file supports the following options:
94
+ The `ic-reactor.json` file schema:
101
95
 
102
- | Option | Type | Description |
103
- | :------------------ | :------------------------------- | :----------------------------------------- |
104
- | `outDir` | `string` | Base output directory for generated files. |
105
- | `canisters` | `Record<string, CanisterConfig>` | Map of canister names to configurations. |
106
- | `clientManagerPath` | `string` | Path to a custom `ClientManager` instance. |
107
-
108
- ### Canister Config
96
+ ```typescript
97
+ interface CodegenConfig {
98
+ /** Default output directory (relative to project root) */
99
+ outDir: string
100
+ /** Default import path for the client manager */
101
+ clientManagerPath?: string
102
+ /** Canister configurations */
103
+ canisters: Record<string, CanisterConfig>
104
+ }
109
105
 
110
- | Option | Type | Description |
111
- | :------------------ | :-------- | :-------------------------------------------------- |
112
- | `didFile` | `string` | Path to the `.did` file. |
113
- | `outDir` | `string` | Override output directory for this canister. |
114
- | `useDisplayReactor` | `boolean` | Use `DisplayReactor` instead of standard `Reactor`. |
115
- | `clientManagerPath` | `string` | Override client manager path. |
106
+ interface CanisterConfig {
107
+ /** Canister name (required) */
108
+ name: string
109
+ /** Path to the .did file (required) */
110
+ didFile: string
111
+ /** Override output directory for this canister */
112
+ outDir?: string
113
+ /** Override client manager import path */
114
+ clientManagerPath?: string
115
+ /** Optional fixed canister ID */
116
+ canisterId?: string
117
+ }
118
+ ```
116
119
 
117
120
  ## Requirements
118
121
 
package/dist/index.js CHANGED
@@ -15,9 +15,8 @@ import path from "path";
15
15
  var CONFIG_FILE_NAME = "ic-reactor.json";
16
16
  var DEFAULT_CONFIG = {
17
17
  $schema: "https://raw.githubusercontent.com/B3Pay/ic-reactor/main/packages/cli/schema.json",
18
- outDir: "src/lib/canisters",
19
- canisters: {},
20
- generatedHooks: {}
18
+ outDir: "src/declarations",
19
+ canisters: {}
21
20
  };
22
21
  function findConfigFile(startDir = process.cwd()) {
23
22
  let currentDir = startDir;
@@ -59,6 +58,7 @@ function ensureDir(dirPath) {
59
58
  }
60
59
 
61
60
  // src/commands/init.ts
61
+ import { generateClientFile } from "@ic-reactor/codegen";
62
62
  async function initCommand(options) {
63
63
  console.log();
64
64
  p.intro(pc.cyan("\u{1F527} ic-reactor CLI Setup"));
@@ -82,174 +82,95 @@ async function initCommand(options) {
82
82
  }
83
83
  } else {
84
84
  const outDir = await p.text({
85
- message: "Where should generated hooks be placed?",
86
- placeholder: "src/lib/canisters",
87
- defaultValue: "src/lib/canisters",
88
- validate: (value) => {
89
- if (!value) return "Output directory is required";
90
- return void 0;
91
- }
85
+ message: "Where should generated files be placed?",
86
+ placeholder: "src/declarations",
87
+ defaultValue: "src/declarations"
92
88
  });
93
- if (p.isCancel(outDir)) {
94
- p.cancel("Setup cancelled.");
95
- process.exit(0);
96
- }
97
- const addCanister = await p.confirm({
98
- message: "Would you like to add a canister now?",
99
- initialValue: true
89
+ if (p.isCancel(outDir)) process.exit(0);
90
+ const clientManagerPath = await p.text({
91
+ message: "Relative path for the client manager import?",
92
+ placeholder: "../../clients",
93
+ defaultValue: "../../clients"
100
94
  });
101
- if (p.isCancel(addCanister)) {
102
- p.cancel("Setup cancelled.");
103
- process.exit(0);
104
- }
95
+ if (p.isCancel(clientManagerPath)) process.exit(0);
105
96
  config = {
106
97
  ...DEFAULT_CONFIG,
107
- outDir
98
+ outDir,
99
+ clientManagerPath
108
100
  };
101
+ const addCanister = await p.confirm({
102
+ message: "Would you like to configure a canister now?",
103
+ initialValue: true
104
+ });
105
+ if (p.isCancel(addCanister)) process.exit(0);
109
106
  if (addCanister) {
110
- const canisterInfo = await promptForCanister(projectRoot);
111
- if (canisterInfo) {
112
- config.canisters[canisterInfo.name] = canisterInfo.config;
107
+ const canister = await promptForCanister(projectRoot);
108
+ if (canister) {
109
+ config.canisters[canister.name] = canister;
113
110
  }
114
111
  }
115
112
  }
116
113
  const configPath = path2.join(projectRoot, CONFIG_FILE_NAME);
117
114
  saveConfig(config, configPath);
118
- const fullOutDir = path2.join(projectRoot, config.outDir);
119
- ensureDir(fullOutDir);
120
- const clientManagerPath = path2.join(projectRoot, "src/lib/clients.ts");
121
- if (!fs2.existsSync(clientManagerPath)) {
122
- const createClient = await p.confirm({
123
- message: "Create a sample client manager at src/lib/clients.ts?",
115
+ ensureDir(path2.join(projectRoot, config.outDir));
116
+ const clientManagerFile = path2.join(projectRoot, "src/clients.ts");
117
+ if (!fs2.existsSync(clientManagerFile)) {
118
+ const createHelpers = await p.confirm({
119
+ message: `Create a default client manager at ${pc.green("src/clients.ts")}?`,
124
120
  initialValue: true
125
121
  });
126
- if (!p.isCancel(createClient) && createClient) {
127
- ensureDir(path2.dirname(clientManagerPath));
128
- fs2.writeFileSync(clientManagerPath, getClientManagerTemplate());
129
- p.log.success(`Created ${pc.green("src/lib/clients.ts")}`);
122
+ if (createHelpers === true) {
123
+ ensureDir(path2.dirname(clientManagerFile));
124
+ fs2.writeFileSync(clientManagerFile, generateClientFile());
125
+ p.log.success(`Created ${pc.green("src/clients.ts")}`);
130
126
  }
131
127
  }
132
128
  p.log.success(`Created ${pc.green(CONFIG_FILE_NAME)}`);
133
- p.log.success(`Created ${pc.green(config.outDir)} directory`);
134
129
  console.log();
135
130
  p.note(
136
- `Next steps:
137
-
138
- 1. ${pc.cyan("Add a canister:")}
139
- ${pc.dim("npx @ic-reactor/cli add")}
140
-
141
- 2. ${pc.cyan("List available methods:")}
142
- ${pc.dim("npx @ic-reactor/cli list -c <canister-name>")}
143
-
144
- 3. ${pc.cyan("Add hooks for specific methods:")}
145
- ${pc.dim("npx @ic-reactor/cli add -c <canister> -m <method>")}`,
146
- "Getting Started"
131
+ `To generate hooks, run:
132
+ ${pc.cyan("npx ic-reactor generate")}`,
133
+ "Next Steps"
147
134
  );
148
- p.outro(pc.green("\u2713 ic-reactor initialized successfully!"));
135
+ p.outro(pc.green("\u2713 Setup complete!"));
149
136
  }
150
137
  async function promptForCanister(projectRoot) {
151
138
  const name = await p.text({
152
139
  message: "Canister name",
153
140
  placeholder: "backend",
154
- validate: (value) => {
155
- if (!value) return "Canister name is required";
156
- if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(value)) {
157
- return "Canister name must start with a letter and contain only letters, numbers, hyphens, and underscores";
158
- }
159
- return void 0;
141
+ validate: (val) => {
142
+ if (!val) return "Name is required";
143
+ if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(val)) return "Invalid name format";
160
144
  }
161
145
  });
162
146
  if (p.isCancel(name)) return null;
163
147
  const didFile = await p.text({
164
148
  message: "Path to .did file",
165
- placeholder: "./backend.did",
166
- validate: (value) => {
167
- if (!value) return "DID file path is required";
168
- const fullPath = path2.resolve(projectRoot, value);
169
- if (!fs2.existsSync(fullPath)) {
170
- return `File not found: ${value}`;
171
- }
172
- return void 0;
149
+ placeholder: "./src/backend/backend.did",
150
+ validate: (val) => {
151
+ if (!val) return "Path is required";
152
+ const fullPath = path2.resolve(projectRoot, val);
153
+ if (!fs2.existsSync(fullPath)) return `File not found: ${val}`;
173
154
  }
174
155
  });
175
156
  if (p.isCancel(didFile)) return null;
176
- const clientManagerPath = await p.text({
177
- message: "Import path to your client manager (relative from generated hooks)",
178
- placeholder: "../../clients",
179
- defaultValue: "../../clients"
180
- });
181
- if (p.isCancel(clientManagerPath)) return null;
182
- const useDisplayReactor = await p.confirm({
183
- message: "Use DisplayReactor? (auto-converts bigint \u2192 string, Principal \u2192 string)",
184
- initialValue: true
185
- });
186
- if (p.isCancel(useDisplayReactor)) return null;
187
157
  return {
188
158
  name,
189
- config: {
190
- didFile,
191
- clientManagerPath,
192
- useDisplayReactor
193
- }
159
+ didFile
194
160
  };
195
161
  }
196
- function getClientManagerTemplate() {
197
- return `/**
198
- * IC Client Manager
199
- *
200
- * This file configures the IC agent and client manager for your application.
201
- * Customize the agent options based on your environment.
202
- */
203
-
204
- import { ClientManager } from "@ic-reactor/react"
205
-
206
- /**
207
- * The client manager handles agent lifecycle and authentication.
208
- *
209
- * Configuration options:
210
- * - host: IC network host (defaults to process env or mainnet)
211
- * - identity: Initial identity (optional, can be set later)
212
- * - verifyQuerySignatures: Verify query signatures (recommended for production)
213
- *
214
- * For local development, the agent will automatically detect local replica.
215
- */
216
- export const clientManager = new ClientManager({
217
- // Uncomment for explicit host configuration:
218
- // host: process.env.DFX_NETWORK === "local"
219
- // ? "http://localhost:4943"
220
- // : "https://icp-api.io",
221
- })
222
- `;
223
- }
224
162
 
225
- // src/commands/sync.ts
163
+ // src/commands/generate.ts
226
164
  import * as p2 from "@clack/prompts";
227
- import fs3 from "fs";
228
- import path3 from "path";
229
165
  import pc2 from "picocolors";
230
-
231
- // src/generators/reactor.ts
232
- import {
233
- generateReactorFile as generateReactorFileFromCodegen
234
- } from "@ic-reactor/codegen";
235
- function generateReactorFile(options) {
236
- const { canisterName, canisterConfig, config } = options;
237
- return generateReactorFileFromCodegen({
238
- canisterName,
239
- canisterConfig,
240
- globalClientManagerPath: config.clientManagerPath
241
- });
242
- }
243
-
244
- // src/commands/sync.ts
245
- import { generateDeclarations } from "@ic-reactor/codegen";
246
- async function syncCommand(options) {
166
+ import { runCanisterPipeline } from "@ic-reactor/codegen";
167
+ async function generateCommand(options) {
247
168
  console.log();
248
- p2.intro(pc2.cyan("\u{1F504} Sync Canister Hooks"));
169
+ p2.intro(pc2.cyan("\u{1F504} Generate Hooks"));
249
170
  const configPath = findConfigFile();
250
171
  if (!configPath) {
251
172
  p2.log.error(
252
- `No ${pc2.yellow("reactor.config.json")} found. Run ${pc2.cyan("npx @ic-reactor/cli init")} first.`
173
+ `No ${pc2.yellow("ic-reactor.json")} found. Run ${pc2.cyan("npx ic-reactor init")} first.`
253
174
  );
254
175
  process.exit(1);
255
176
  }
@@ -264,7 +185,7 @@ async function syncCommand(options) {
264
185
  p2.log.error("No canisters configured.");
265
186
  process.exit(1);
266
187
  }
267
- let canistersToSync;
188
+ let canistersToProcess = [];
268
189
  if (options.canister) {
269
190
  if (!config.canisters[options.canister]) {
270
191
  p2.log.error(
@@ -272,176 +193,70 @@ async function syncCommand(options) {
272
193
  );
273
194
  process.exit(1);
274
195
  }
275
- canistersToSync = [options.canister];
196
+ canistersToProcess = [options.canister];
276
197
  } else {
277
- canistersToSync = canisterNames;
198
+ canistersToProcess = canisterNames;
278
199
  }
279
200
  const spinner2 = p2.spinner();
280
- spinner2.start("Syncing hooks...");
281
- let totalUpdated = 0;
282
- const errors = [];
283
- for (const canisterName of canistersToSync) {
284
- const canisterConfig = config.canisters[canisterName];
285
- const canisterOutDir = path3.join(projectRoot, config.outDir, canisterName);
286
- const didFilePath = path3.resolve(projectRoot, canisterConfig.didFile);
287
- spinner2.message(`Regenerating declarations for ${canisterName}...`);
201
+ spinner2.start(
202
+ `Generating hooks for ${canistersToProcess.length} canisters...`
203
+ );
204
+ let successCount = 0;
205
+ let errorCount = 0;
206
+ const errorMessages = [];
207
+ for (const name of canistersToProcess) {
208
+ const canisterConfig = config.canisters[name];
209
+ spinner2.message(`Processing ${pc2.cyan(name)}...`);
288
210
  try {
289
- const bindgenResult = await generateDeclarations({
290
- didFile: didFilePath,
291
- outDir: canisterOutDir,
292
- canisterName
293
- });
294
- if (!bindgenResult.success) {
295
- errors.push(`${canisterName}: ${bindgenResult.error}`);
296
- p2.log.warn(
297
- `Could not regenerate declarations for ${canisterName}: ${bindgenResult.error}`
298
- );
299
- continue;
300
- }
301
- const reactorPath = path3.join(canisterOutDir, "index.ts");
302
- const reactorContent = generateReactorFile({
303
- canisterName,
211
+ const result = await runCanisterPipeline({
304
212
  canisterConfig,
305
- config
213
+ projectRoot,
214
+ globalConfig: config
306
215
  });
307
- fs3.writeFileSync(reactorPath, reactorContent);
308
- totalUpdated++;
309
- } catch (error) {
310
- errors.push(`${canisterName}: ${error.message}`);
311
- p2.log.error(`Failed to sync ${canisterName}: ${error.message}`);
216
+ if (result.success) {
217
+ successCount++;
218
+ } else {
219
+ errorCount++;
220
+ errorMessages.push(`${name}: ${result.error}`);
221
+ }
222
+ } catch (err) {
223
+ errorCount++;
224
+ errorMessages.push(
225
+ `${name}: ${err instanceof Error ? err.message : String(err)}`
226
+ );
312
227
  }
313
228
  }
314
- spinner2.stop("Sync complete!");
315
- if (errors.length > 0) {
229
+ spinner2.stop("Generation complete");
230
+ if (errorMessages.length > 0) {
316
231
  console.log();
317
232
  p2.log.error("Errors encountered:");
318
- for (const error of errors) {
319
- console.log(` ${pc2.red("\u2022")} ${error}`);
233
+ for (const msg of errorMessages) {
234
+ console.log(` ${pc2.red("\u2022")} ${msg}`);
320
235
  }
321
236
  }
322
237
  console.log();
323
- p2.note(`Updated: ${pc2.green(totalUpdated.toString())} canisters`, "Summary");
324
- p2.outro(pc2.green("\u2713 Sync complete!"));
238
+ p2.note(
239
+ `Success: ${pc2.green(successCount.toString())}
240
+ Failed: ${pc2.red(errorCount.toString())}`,
241
+ "Summary"
242
+ );
243
+ if (errorCount > 0) {
244
+ p2.outro(pc2.red("\u2716 Generation failed with errors."));
245
+ process.exit(1);
246
+ } else {
247
+ p2.outro(pc2.green("\u2713 All hooks generated successfully!"));
248
+ }
325
249
  }
326
250
 
327
- // src/commands/list.ts
328
- import * as p3 from "@clack/prompts";
329
- import path4 from "path";
251
+ // src/index.ts
330
252
  import pc3 from "picocolors";
331
- import { parseDIDFile } from "@ic-reactor/codegen";
332
- async function listCommand(options) {
333
- console.log();
334
- p3.intro(pc3.cyan("\u{1F4CB} List Canister Methods"));
335
- const configPath = findConfigFile();
336
- if (!configPath) {
337
- p3.log.error(
338
- `No ${pc3.yellow("reactor.config.json")} found. Run ${pc3.cyan("npx @ic-reactor/cli init")} first.`
339
- );
340
- process.exit(1);
341
- }
342
- const config = loadConfig(configPath);
343
- if (!config) {
344
- p3.log.error(`Failed to load config from ${pc3.yellow(configPath)}`);
345
- process.exit(1);
346
- }
347
- const projectRoot = getProjectRoot();
348
- const canisterNames = Object.keys(config.canisters);
349
- if (canisterNames.length === 0) {
350
- p3.log.error(
351
- `No canisters configured. Add a canister to ${pc3.yellow("reactor.config.json")} first.`
352
- );
353
- process.exit(1);
354
- }
355
- let selectedCanister = options.canister;
356
- if (!selectedCanister) {
357
- if (canisterNames.length === 1) {
358
- selectedCanister = canisterNames[0];
359
- } else {
360
- const result = await p3.select({
361
- message: "Select a canister",
362
- options: canisterNames.map((name) => ({
363
- value: name,
364
- label: name
365
- }))
366
- });
367
- if (p3.isCancel(result)) {
368
- p3.cancel("Cancelled.");
369
- process.exit(0);
370
- }
371
- selectedCanister = result;
372
- }
373
- }
374
- const canisterConfig = config.canisters[selectedCanister];
375
- if (!canisterConfig) {
376
- p3.log.error(`Canister ${pc3.yellow(selectedCanister)} not found in config.`);
377
- process.exit(1);
378
- }
379
- const didFilePath = path4.resolve(projectRoot, canisterConfig.didFile);
380
- try {
381
- const methods = parseDIDFile(didFilePath);
382
- if (methods.length === 0) {
383
- p3.log.warn(`No methods found in ${pc3.yellow(didFilePath)}`);
384
- process.exit(0);
385
- }
386
- const queries = methods.filter((m) => m.type === "query");
387
- const mutations = methods.filter((m) => m.type === "mutation");
388
- const generatedMethods = config.generatedHooks[selectedCanister] ?? [];
389
- if (queries.length > 0) {
390
- console.log();
391
- console.log(pc3.bold(pc3.cyan(" Queries:")));
392
- for (const method of queries) {
393
- const isGenerated = generatedMethods.includes(method.name);
394
- const status = isGenerated ? pc3.green("\u2713") : pc3.dim("\u25CB");
395
- const argsHint = method.hasArgs ? pc3.dim("(args)") : pc3.dim("()");
396
- console.log(` ${status} ${method.name} ${argsHint}`);
397
- }
398
- }
399
- if (mutations.length > 0) {
400
- console.log();
401
- console.log(pc3.bold(pc3.yellow(" Mutations (Updates):")));
402
- for (const method of mutations) {
403
- const isGenerated = generatedMethods.includes(method.name);
404
- const status = isGenerated ? pc3.green("\u2713") : pc3.dim("\u25CB");
405
- const argsHint = method.hasArgs ? pc3.dim("(args)") : pc3.dim("()");
406
- console.log(` ${status} ${method.name} ${argsHint}`);
407
- }
408
- }
409
- console.log();
410
- const generatedCount = generatedMethods.length;
411
- const totalCount = methods.length;
412
- p3.note(
413
- `Total: ${pc3.bold(totalCount.toString())} methods
414
- Generated: ${pc3.green(generatedCount.toString())} / ${totalCount}
415
253
 
416
- ${pc3.green("\u2713")} = hook generated
417
- ${pc3.dim("\u25CB")} = not yet generated`,
418
- selectedCanister
419
- );
420
- if (generatedCount < totalCount) {
421
- console.log();
422
- console.log(
423
- pc3.dim(
424
- ` Run ${pc3.cyan(`npx @ic-reactor/cli add -c ${selectedCanister}`)} to add hooks`
425
- )
426
- );
427
- }
428
- } catch (error) {
429
- p3.log.error(
430
- `Failed to parse DID file: ${pc3.yellow(didFilePath)}
431
- ${error.message}`
432
- );
433
- process.exit(1);
434
- }
435
- console.log();
436
- }
254
+ // package.json
255
+ var version = "0.5.0";
437
256
 
438
257
  // src/index.ts
439
- import pc4 from "picocolors";
440
258
  var program = new Command();
441
- program.name("ic-reactor").description(
442
- pc4.cyan("\u{1F527} Generate shadcn-style React hooks for ICP canisters")
443
- ).version("3.0.0");
444
- program.command("init").description("Initialize ic-reactor configuration in your project").option("-y, --yes", "Skip prompts and use defaults").option("-o, --out-dir <path>", "Output directory for generated hooks").action(initCommand);
445
- program.command("sync").description("Sync hooks with .did file changes").option("-c, --canister <name>", "Canister to sync").action(syncCommand);
446
- program.command("list").description("List available methods from a canister").option("-c, --canister <name>", "Canister to list methods from").action(listCommand);
259
+ program.name("ic-reactor").description(pc3.cyan("\u{1F527} Generate type-safe React hooks for ICP canisters")).version(version);
260
+ program.command("init").description("Initialize ic-reactor configuration in your project").option("-y, --yes", "Skip prompts and use defaults").option("-o, --out-dir <path>", "Output directory for generated files").action(initCommand);
261
+ program.command("generate").alias("g").description("Generate hooks from .did files").option("-c, --canister <name>", "Generate for a specific canister only").option("--clean", "Clean output directory before generating").action(generateCommand);
447
262
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ic-reactor/cli",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "CLI tool to generate shadcn-style React hooks for ICP canisters",
6
6
  "main": "dist/index.js",
@@ -33,7 +33,7 @@
33
33
  "@icp-sdk/core": "^5.0.0",
34
34
  "commander": "^14.0.3",
35
35
  "picocolors": "^1.1.1",
36
- "@ic-reactor/codegen": "0.4.1"
36
+ "@ic-reactor/codegen": "0.5.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "^25.2.3",