@enactprotocol/cli 2.1.5 → 2.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;GAKG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,oBAAoB,EACpB,uBAAuB,EACvB,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE7C,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAK/B,uBAAuB;AACvB,KAAK,UAAU,IAAI;IACjB,+CAA+C;IAC/C,iBAAiB,EAAE,CAAC;IAEpB,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,OAAO,CAAC;SACb,WAAW,CAAC,6DAA6D,CAAC;SAC1E,OAAO,CAAC,OAAO,EAAE,eAAe,EAAE,2BAA2B,CAAC,CAAC;IAElE,yBAAyB;IACzB,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC/B,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC7B,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,uBAAuB,CAAC,OAAO,CAAC,CAAC;IACjC,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC7B,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC/B,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAEhC,8BAA8B;IAC9B,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChC,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC7B,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC/B,uBAAuB,CAAC,OAAO,CAAC,CAAC;IACjC,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAE/B,wCAAwC;IACxC,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChC,uBAAuB,CAAC,OAAO,CAAC,CAAC;IAEjC,4BAA4B;IAC5B,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAEhC,0EAA0E;IAC1E,OAAO,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,EAAE;QAC3B,wEAAwE;QACxE,gEAAgE;QAChE,IACE,GAAG,CAAC,IAAI,KAAK,gBAAgB;YAC7B,GAAG,CAAC,IAAI,KAAK,yBAAyB;YACtC,GAAG,CAAC,IAAI,KAAK,mBAAmB;YAChC,GAAG,CAAC,IAAI,KAAK,kCAAkC;YAC/C,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,YAAY,CAAC,EACnC,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,iEAAiE;QACjE,MAAM,MAAM,GAAG,GAA0C,CAAC;QAC1D,IAAI,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,YAAY,CAAC,IAAI,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;YACtF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;QACxB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACrB,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACnB,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;QACxB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enactprotocol/cli",
3
- "version": "2.1.5",
3
+ "version": "2.1.7",
4
4
  "description": "Command-line interface for Enact - the npm for AI tools",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -34,10 +34,10 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@clack/prompts": "^0.11.0",
37
- "@enactprotocol/api": "2.1.5",
38
- "@enactprotocol/execution": "2.1.5",
39
- "@enactprotocol/secrets": "2.1.5",
40
- "@enactprotocol/shared": "2.1.5",
37
+ "@enactprotocol/api": "2.1.7",
38
+ "@enactprotocol/execution": "2.1.7",
39
+ "@enactprotocol/secrets": "2.1.7",
40
+ "@enactprotocol/shared": "2.1.7",
41
41
  "commander": "^12.1.0",
42
42
  "picocolors": "^1.1.1"
43
43
  },
@@ -17,6 +17,7 @@ export { configureConfigCommand } from "./config";
17
17
  // Registry commands (Phase 8)
18
18
  export { configureSearchCommand } from "./search";
19
19
  export { configureGetCommand } from "./get";
20
+ export { configureLearnCommand } from "./learn";
20
21
  export { configurePublishCommand } from "./publish";
21
22
  export { configureAuthCommand } from "./auth";
22
23
  export { configureCacheCommand } from "./cache";
@@ -80,8 +80,8 @@ Enact tools are containerized, cryptographically-signed executables. Each tool i
80
80
 
81
81
  | Task | Command |
82
82
  |------|---------|
83
- | Run local tool | \`enact run ./ --input "key=value"\` |
84
83
  | Run with JSON | \`enact run ./ --args '{"key": "value"}'\` |
84
+ | Run from file | \`enact run ./ --input-file inputs.json\` |
85
85
  | Dry run | \`enact run ./ --args '{}' --dry-run\` |
86
86
  | Sign & publish | \`enact sign ./ && enact publish ./\` |
87
87
 
@@ -149,6 +149,20 @@ command: python /work/main.py "\${input}"
149
149
  command: python /work/main.py \${input}
150
150
  \`\`\`
151
151
 
152
+ **Optional params:** Missing optional params become empty strings. Always provide defaults:
153
+ \`\`\`yaml
154
+ inputSchema:
155
+ properties:
156
+ greeting:
157
+ type: string
158
+ default: "Hello" # Recommended for optional params
159
+ \`\`\`
160
+
161
+ Or handle empty in shell:
162
+ \`\`\`yaml
163
+ command: "echo \${greeting:-Hello} \${name}"
164
+ \`\`\`
165
+
152
166
  Modifiers:
153
167
  - \`\${param}\` — auto-quoted (handles spaces, JSON, special chars)
154
168
  - \`\${param:raw}\` — raw, no quoting (use carefully)
@@ -268,9 +282,9 @@ Return JSON: {"issues": [...], "score": 75}
268
282
  ## Troubleshooting
269
283
 
270
284
  \`\`\`bash
271
- enact run ./ --input "x=y" --verbose # Verbose output
272
- enact run ./ --args '{}' --dry-run # Preview command
273
- enact list # List installed tools
285
+ enact run ./ --args '{"x": "y"}' --verbose # Verbose output
286
+ enact run ./ --args '{}' --dry-run # Preview command
287
+ enact list # List installed tools
274
288
  \`\`\`
275
289
  `,
276
290
 
@@ -416,6 +430,30 @@ function loadTemplate(templateName: string, replacements: Record<string, string>
416
430
  return content;
417
431
  }
418
432
 
433
+ /**
434
+ * Create .enact/tools.json for project tool tracking
435
+ */
436
+ function createEnactProjectDir(targetDir: string, force: boolean): boolean {
437
+ const enactDir = join(targetDir, ".enact");
438
+ const toolsJsonPath = join(enactDir, "tools.json");
439
+
440
+ // Check if tools.json already exists
441
+ if (existsSync(toolsJsonPath) && !force) {
442
+ info(".enact/tools.json already exists, skipping");
443
+ return false;
444
+ }
445
+
446
+ // Create .enact directory if it doesn't exist
447
+ if (!existsSync(enactDir)) {
448
+ mkdirSync(enactDir, { recursive: true });
449
+ }
450
+
451
+ // Write empty tools.json
452
+ const toolsJson = { tools: {} };
453
+ writeFileSync(toolsJsonPath, `${JSON.stringify(toolsJson, null, 2)}\n`, "utf-8");
454
+ return true;
455
+ }
456
+
419
457
  /**
420
458
  * Get the current logged-in username
421
459
  */
@@ -500,24 +538,64 @@ async function getCurrentUsername(): Promise<string | null> {
500
538
  async function initHandler(options: InitOptions, ctx: CommandContext): Promise<void> {
501
539
  const targetDir = ctx.cwd;
502
540
 
503
- // Determine mode: --agent, --claude, or --tool (default)
504
- const isAgentMode = options.agent;
541
+ // Determine mode: --tool, --claude, or --agent (default)
542
+ const isToolMode = options.tool;
505
543
  const isClaudeMode = options.claude;
506
- // Default to tool mode if no flag specified
544
+ // Default to agent mode if no flag specified
507
545
 
508
- // Handle --agent mode: create AGENTS.md for projects using Enact tools
509
- if (isAgentMode) {
546
+ // Handle --tool mode: create enact.md + AGENTS.md for tool development
547
+ if (isToolMode) {
548
+ const manifestPath = join(targetDir, "enact.md");
510
549
  const agentsPath = join(targetDir, "AGENTS.md");
511
- if (existsSync(agentsPath) && !options.force) {
512
- warning(`AGENTS.md already exists at: ${agentsPath}`);
550
+
551
+ if (existsSync(manifestPath) && !options.force) {
552
+ warning(`Tool manifest already exists at: ${manifestPath}`);
513
553
  info("Use --force to overwrite");
514
554
  return;
515
555
  }
516
- writeFileSync(agentsPath, loadTemplate("agent-agents.md"), "utf-8");
517
- success(`Created AGENTS.md: ${agentsPath}`);
556
+
557
+ // Get username for the tool name
558
+ let toolName = options.name;
559
+
560
+ if (!toolName) {
561
+ const username = await getCurrentUsername();
562
+ if (username) {
563
+ toolName = `${username}/my-tool`;
564
+ info(`Using logged-in username: ${username}`);
565
+ } else {
566
+ toolName = "my-tool";
567
+ info("Not logged in - using generic tool name");
568
+ info("Run 'enact auth login' to use your username in tool names");
569
+ }
570
+ }
571
+
572
+ // Load templates with placeholder replacement
573
+ const replacements = { TOOL_NAME: toolName };
574
+ const manifestContent = loadTemplate("tool-enact.md", replacements);
575
+ const agentsContent = loadTemplate("tool-agents.md", replacements);
576
+
577
+ // Ensure directory exists
578
+ if (!existsSync(targetDir)) {
579
+ mkdirSync(targetDir, { recursive: true });
580
+ }
581
+
582
+ // Write enact.md
583
+ writeFileSync(manifestPath, manifestContent, "utf-8");
584
+ success(`Created tool manifest: ${manifestPath}`);
585
+
586
+ // Write AGENTS.md (only if it doesn't exist or --force is used)
587
+ if (!existsSync(agentsPath) || options.force) {
588
+ writeFileSync(agentsPath, agentsContent, "utf-8");
589
+ success(`Created AGENTS.md: ${agentsPath}`);
590
+ } else {
591
+ info("AGENTS.md already exists, skipping (use --force to overwrite)");
592
+ }
593
+
518
594
  info("");
519
- info("This file helps AI agents understand how to use Enact tools in your project.");
520
- info("Run 'enact search <query>' to find tools, 'enact install <tool>' to add them.");
595
+ info("Next steps:");
596
+ info(" 1. Edit enact.md to customize your tool");
597
+ info(" 2. Run 'enact run ./' to test your tool");
598
+ info(" 3. Run 'enact publish' to share your tool");
521
599
  return;
522
600
  }
523
601
 
@@ -531,63 +609,35 @@ async function initHandler(options: InitOptions, ctx: CommandContext): Promise<v
531
609
  }
532
610
  writeFileSync(claudePath, loadTemplate("claude.md"), "utf-8");
533
611
  success(`Created CLAUDE.md: ${claudePath}`);
612
+
613
+ // Create .enact/tools.json
614
+ if (createEnactProjectDir(targetDir, options.force ?? false)) {
615
+ success("Created .enact/tools.json");
616
+ }
617
+
534
618
  info("");
535
619
  info("This file helps Claude understand how to use Enact tools in your project.");
536
620
  return;
537
621
  }
538
622
 
539
- // Handle --tool mode (default): create enact.md + AGENTS.md for tool development
540
- const manifestPath = join(targetDir, "enact.md");
623
+ // Handle default (agent) mode: create AGENTS.md for projects using Enact tools
541
624
  const agentsPath = join(targetDir, "AGENTS.md");
542
-
543
- if (existsSync(manifestPath) && !options.force) {
544
- warning(`Tool manifest already exists at: ${manifestPath}`);
625
+ if (existsSync(agentsPath) && !options.force) {
626
+ warning(`AGENTS.md already exists at: ${agentsPath}`);
545
627
  info("Use --force to overwrite");
546
628
  return;
547
629
  }
630
+ writeFileSync(agentsPath, loadTemplate("agent-agents.md"), "utf-8");
631
+ success(`Created AGENTS.md: ${agentsPath}`);
548
632
 
549
- // Get username for the tool name
550
- let toolName = options.name;
551
-
552
- if (!toolName) {
553
- const username = await getCurrentUsername();
554
- if (username) {
555
- toolName = `${username}/my-tool`;
556
- info(`Using logged-in username: ${username}`);
557
- } else {
558
- toolName = "my-tool";
559
- info("Not logged in - using generic tool name");
560
- info("Run 'enact auth login' to use your username in tool names");
561
- }
562
- }
563
-
564
- // Load templates with placeholder replacement
565
- const replacements = { TOOL_NAME: toolName };
566
- const manifestContent = loadTemplate("tool-enact.md", replacements);
567
- const agentsContent = loadTemplate("tool-agents.md", replacements);
568
-
569
- // Ensure directory exists
570
- if (!existsSync(targetDir)) {
571
- mkdirSync(targetDir, { recursive: true });
572
- }
573
-
574
- // Write enact.md
575
- writeFileSync(manifestPath, manifestContent, "utf-8");
576
- success(`Created tool manifest: ${manifestPath}`);
577
-
578
- // Write AGENTS.md (only if it doesn't exist or --force is used)
579
- if (!existsSync(agentsPath) || options.force) {
580
- writeFileSync(agentsPath, agentsContent, "utf-8");
581
- success(`Created AGENTS.md: ${agentsPath}`);
582
- } else {
583
- info("AGENTS.md already exists, skipping (use --force to overwrite)");
633
+ // Create .enact/tools.json
634
+ if (createEnactProjectDir(targetDir, options.force ?? false)) {
635
+ success("Created .enact/tools.json");
584
636
  }
585
637
 
586
638
  info("");
587
- info("Next steps:");
588
- info(" 1. Edit enact.md to customize your tool");
589
- info(" 2. Run 'enact run ./' to test your tool");
590
- info(" 3. Run 'enact publish' to share your tool");
639
+ info("This file helps AI agents understand how to use Enact tools in your project.");
640
+ info("Run 'enact search <query>' to find tools, 'enact install <tool>' to add them.");
591
641
  }
592
642
 
593
643
  /**
@@ -599,9 +649,9 @@ export function configureInitCommand(program: Command): void {
599
649
  .description("Initialize Enact in the current directory")
600
650
  .option("-n, --name <name>", "Tool name (default: username/my-tool)")
601
651
  .option("-f, --force", "Overwrite existing files")
602
- .option("--tool", "Create a new Enact tool (default)")
603
- .option("--agent", "Create AGENTS.md for projects that use Enact tools")
604
- .option("--claude", "Create CLAUDE.md with Claude-specific instructions")
652
+ .option("--tool", "Create a new Enact tool (enact.md + AGENTS.md)")
653
+ .option("--agent", "Create AGENTS.md + .enact/tools.json (default)")
654
+ .option("--claude", "Create CLAUDE.md + .enact/tools.json")
605
655
  .option("-v, --verbose", "Show detailed output")
606
656
  .action(async (options: InitOptions) => {
607
657
  const ctx: CommandContext = {
@@ -0,0 +1,107 @@
1
+ /**
2
+ * enact learn command
3
+ *
4
+ * Display the documentation (enact.md) for a tool.
5
+ * Fetches and displays the raw manifest content for easy reading.
6
+ */
7
+
8
+ import { createApiClient, getToolInfo, getToolVersion } from "@enactprotocol/api";
9
+ import { loadConfig } from "@enactprotocol/shared";
10
+ import type { Command } from "commander";
11
+ import type { CommandContext, GlobalOptions } from "../../types";
12
+ import { dim, error, formatError, header, json, newline } from "../../utils";
13
+
14
+ interface LearnOptions extends GlobalOptions {
15
+ ver?: string;
16
+ }
17
+
18
+ /**
19
+ * Learn command handler
20
+ */
21
+ async function learnHandler(
22
+ toolName: string,
23
+ options: LearnOptions,
24
+ _ctx: CommandContext
25
+ ): Promise<void> {
26
+ const config = loadConfig();
27
+ const registryUrl =
28
+ process.env.ENACT_REGISTRY_URL ??
29
+ config.registry?.url ??
30
+ "https://siikwkfgsmouioodghho.supabase.co/functions/v1";
31
+ const authToken = config.registry?.authToken;
32
+ const client = createApiClient({
33
+ baseUrl: registryUrl,
34
+ authToken: authToken,
35
+ });
36
+
37
+ try {
38
+ // Get the version to fetch - either specified or latest
39
+ let version = options.ver;
40
+ if (!version) {
41
+ const toolInfo = await getToolInfo(client, toolName);
42
+ version = toolInfo.latestVersion;
43
+ }
44
+
45
+ // Get the version info which includes rawManifest
46
+ const versionInfo = await getToolVersion(client, toolName, version);
47
+
48
+ if (options.json) {
49
+ json({
50
+ name: toolName,
51
+ version: versionInfo.version,
52
+ documentation: versionInfo.rawManifest ?? null,
53
+ });
54
+ return;
55
+ }
56
+
57
+ if (!versionInfo.rawManifest) {
58
+ error(`No documentation found for ${toolName}@${version}`);
59
+ dim("This tool may not have an enact.md file.");
60
+ process.exit(1);
61
+ }
62
+
63
+ // Display the documentation
64
+ header(`${toolName}@${version}`);
65
+ newline();
66
+ console.log(versionInfo.rawManifest);
67
+ } catch (err) {
68
+ if (err instanceof Error) {
69
+ if (err.message.includes("not_found") || err.message.includes("404")) {
70
+ error(`Tool not found: ${toolName}`);
71
+ dim("Check the tool name or search with: enact search <query>");
72
+ process.exit(1);
73
+ }
74
+ if (err.message.includes("fetch")) {
75
+ error("Unable to connect to registry. Check your internet connection.");
76
+ process.exit(1);
77
+ }
78
+ }
79
+ throw err;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Configure the learn command
85
+ */
86
+ export function configureLearnCommand(program: Command): void {
87
+ program
88
+ .command("learn <tool>")
89
+ .description("Display documentation (enact.md) for a tool")
90
+ .option("--ver <version>", "Show documentation for a specific version")
91
+ .option("--json", "Output as JSON")
92
+ .action(async (toolName: string, options: LearnOptions) => {
93
+ const ctx: CommandContext = {
94
+ cwd: process.cwd(),
95
+ options,
96
+ isCI: Boolean(process.env.CI),
97
+ isInteractive: process.stdout.isTTY ?? false,
98
+ };
99
+
100
+ try {
101
+ await learnHandler(toolName, options, ctx);
102
+ } catch (err) {
103
+ error(formatError(err));
104
+ process.exit(1);
105
+ }
106
+ });
107
+ }
@@ -61,6 +61,7 @@ import {
61
61
 
62
62
  interface RunOptions extends GlobalOptions {
63
63
  args?: string;
64
+ inputFile?: string;
64
65
  input?: string[];
65
66
  timeout?: string;
66
67
  noCache?: boolean;
@@ -70,14 +71,46 @@ interface RunOptions extends GlobalOptions {
70
71
 
71
72
  /**
72
73
  * Parse input arguments from various formats
74
+ *
75
+ * Priority order (later sources override earlier):
76
+ * 1. --input-file (JSON file)
77
+ * 2. --args (inline JSON)
78
+ * 3. --input (key=value pairs)
79
+ *
80
+ * Recommended for agents: Use --args or --input-file with JSON
73
81
  */
74
82
  function parseInputArgs(
75
83
  argsJson: string | undefined,
84
+ inputFile: string | undefined,
76
85
  inputFlags: string[] | undefined
77
86
  ): Record<string, unknown> {
78
87
  const inputs: Record<string, unknown> = {};
79
88
 
80
- // Parse --args JSON
89
+ // Parse --input-file JSON file (loaded first, can be overridden)
90
+ if (inputFile) {
91
+ try {
92
+ const { readFileSync, existsSync } = require("node:fs");
93
+ const { resolve } = require("node:path");
94
+ const filePath = resolve(inputFile);
95
+
96
+ if (!existsSync(filePath)) {
97
+ throw new Error(`Input file not found: ${inputFile}`);
98
+ }
99
+
100
+ const content = readFileSync(filePath, "utf-8");
101
+ const parsed = JSON.parse(content);
102
+ if (typeof parsed === "object" && parsed !== null) {
103
+ Object.assign(inputs, parsed);
104
+ }
105
+ } catch (err) {
106
+ if (err instanceof Error && err.message.startsWith("Input file not found")) {
107
+ throw err;
108
+ }
109
+ throw new Error(`Invalid JSON in input file: ${formatError(err)}`);
110
+ }
111
+ }
112
+
113
+ // Parse --args JSON (overrides file)
81
114
  if (argsJson) {
82
115
  try {
83
116
  const parsed = JSON.parse(argsJson);
@@ -89,7 +122,7 @@ function parseInputArgs(
89
122
  }
90
123
  }
91
124
 
92
- // Parse --input key=value pairs
125
+ // Parse --input key=value pairs (overrides both)
93
126
  if (inputFlags) {
94
127
  for (const input of inputFlags) {
95
128
  const eqIndex = input.indexOf("=");
@@ -501,7 +534,7 @@ async function runHandler(tool: string, options: RunOptions, ctx: CommandContext
501
534
  const manifest = resolution.manifest;
502
535
 
503
536
  // Parse inputs
504
- const inputs = parseInputArgs(options.args, options.input);
537
+ const inputs = parseInputArgs(options.args, options.inputFile, options.input);
505
538
 
506
539
  // Apply defaults from schema
507
540
  const inputsWithDefaults = manifest.inputSchema
@@ -669,8 +702,9 @@ export function configureRunCommand(program: Command): void {
669
702
  .command("run")
670
703
  .description("Execute a tool with its manifest-defined command")
671
704
  .argument("<tool>", "Tool to run (name, path, or '.' for current directory)")
672
- .option("-a, --args <json>", "Input arguments as JSON")
673
- .option("-i, --input <key=value...>", "Input arguments as key=value pairs")
705
+ .option("-a, --args <json>", "Input arguments as JSON string (recommended)")
706
+ .option("-f, --input-file <path>", "Load input arguments from JSON file")
707
+ .option("-i, --input <key=value...>", "Input arguments as key=value pairs (simple values only)")
674
708
  .option("-t, --timeout <duration>", "Execution timeout (e.g., 30s, 5m)")
675
709
  .option("--no-cache", "Disable container caching")
676
710
  .option("--local", "Only resolve from local sources")
package/src/index.ts CHANGED
@@ -19,6 +19,7 @@ import {
19
19
  configureInitCommand,
20
20
  configureInspectCommand,
21
21
  configureInstallCommand,
22
+ configureLearnCommand,
22
23
  configureListCommand,
23
24
  configurePublishCommand,
24
25
  configureReportCommand,
@@ -32,7 +33,7 @@ import {
32
33
  } from "./commands";
33
34
  import { error, formatError } from "./utils";
34
35
 
35
- export const version = "2.1.5";
36
+ export const version = "2.1.7";
36
37
 
37
38
  // Export types for external use
38
39
  export type { GlobalOptions, CommandContext } from "./types";
@@ -47,7 +48,7 @@ async function main() {
47
48
  program
48
49
  .name("enact")
49
50
  .description("Enact - Verified, portable protocol for AI-executable tools")
50
- .version(version);
51
+ .version(version, "-v, --version", "output the version number");
51
52
 
52
53
  // Configure all commands
53
54
  configureSetupCommand(program);
@@ -63,6 +64,7 @@ async function main() {
63
64
  // Registry commands (Phase 8)
64
65
  configureSearchCommand(program);
65
66
  configureGetCommand(program);
67
+ configureLearnCommand(program);
66
68
  configurePublishCommand(program);
67
69
  configureAuthCommand(program);
68
70
  configureCacheCommand(program);