@enactprotocol/cli 2.1.24 → 2.1.29

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 (57) hide show
  1. package/dist/commands/index.d.ts +2 -0
  2. package/dist/commands/index.d.ts.map +1 -1
  3. package/dist/commands/index.js +4 -0
  4. package/dist/commands/index.js.map +1 -1
  5. package/dist/commands/init/templates/claude.d.ts +1 -1
  6. package/dist/commands/init/templates/claude.d.ts.map +1 -1
  7. package/dist/commands/init/templates/claude.js +268 -28
  8. package/dist/commands/init/templates/claude.js.map +1 -1
  9. package/dist/commands/init/templates/tool-agents.d.ts +1 -1
  10. package/dist/commands/init/templates/tool-agents.d.ts.map +1 -1
  11. package/dist/commands/init/templates/tool-agents.js +90 -15
  12. package/dist/commands/init/templates/tool-agents.js.map +1 -1
  13. package/dist/commands/install/index.d.ts.map +1 -1
  14. package/dist/commands/install/index.js +9 -1
  15. package/dist/commands/install/index.js.map +1 -1
  16. package/dist/commands/learn/index.d.ts.map +1 -1
  17. package/dist/commands/learn/index.js +4 -11
  18. package/dist/commands/learn/index.js.map +1 -1
  19. package/dist/commands/mcp/index.d.ts.map +1 -1
  20. package/dist/commands/mcp/index.js +204 -53
  21. package/dist/commands/mcp/index.js.map +1 -1
  22. package/dist/commands/run/index.d.ts.map +1 -1
  23. package/dist/commands/run/index.js +380 -39
  24. package/dist/commands/run/index.js.map +1 -1
  25. package/dist/commands/validate/index.d.ts +11 -0
  26. package/dist/commands/validate/index.d.ts.map +1 -0
  27. package/dist/commands/validate/index.js +299 -0
  28. package/dist/commands/validate/index.js.map +1 -0
  29. package/dist/index.d.ts +1 -1
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +6 -2
  32. package/dist/index.js.map +1 -1
  33. package/dist/types.d.ts +2 -0
  34. package/dist/types.d.ts.map +1 -1
  35. package/dist/types.js.map +1 -1
  36. package/dist/utils/errors.d.ts +8 -1
  37. package/dist/utils/errors.d.ts.map +1 -1
  38. package/dist/utils/errors.js +13 -2
  39. package/dist/utils/errors.js.map +1 -1
  40. package/package.json +5 -5
  41. package/src/commands/index.ts +5 -0
  42. package/src/commands/init/templates/claude.ts +268 -28
  43. package/src/commands/init/templates/tool-agents.ts +90 -15
  44. package/src/commands/install/index.ts +11 -0
  45. package/src/commands/learn/index.ts +6 -11
  46. package/src/commands/mcp/index.ts +768 -0
  47. package/src/commands/run/README.md +68 -1
  48. package/src/commands/run/index.ts +475 -35
  49. package/src/commands/validate/index.ts +344 -0
  50. package/src/index.ts +8 -1
  51. package/src/types.ts +2 -0
  52. package/src/utils/errors.ts +26 -6
  53. package/tests/commands/init.test.ts +2 -2
  54. package/tests/commands/run.test.ts +260 -0
  55. package/tests/commands/validate.test.ts +81 -0
  56. package/tests/utils/errors.test.ts +36 -0
  57. package/tsconfig.tsbuildinfo +1 -1
@@ -3,18 +3,24 @@
3
3
  */
4
4
  export const claudeTemplate = `# CLAUDE.md
5
5
 
6
- This project uses Enact tools — containerized, signed executables you can run via CLI.
6
+ This project uses Enact tools — containerized, cryptographically-signed executables you can run via CLI.
7
7
 
8
8
  ## Quick Reference
9
- \`\`\`bash
10
- enact run <tool> --args '{"key": "value"}' # Run a tool
11
- enact search "keyword" # Find tools
12
- enact learn author/tool # View tool documentation
13
- enact install author/tool # Install tool
14
- enact list # List installed tools
15
- \`\`\`
9
+
10
+ | Task | Command |
11
+ |------|---------|
12
+ | Run with JSON | \`enact run <tool> --args '{"key": "value"}'\` |
13
+ | Run from file | \`enact run <tool> --input-file inputs.json\` |
14
+ | Dry run | \`enact run <tool> --args '{}' --dry-run\` |
15
+ | Debug params | \`enact run <tool> --args '{}' --debug\` |
16
+ | Search tools | \`enact search "keyword"\` |
17
+ | View docs | \`enact learn author/tool\` |
18
+ | Install tool | \`enact install author/tool\` |
19
+ | List installed | \`enact list\` |
20
+ | Sign & publish | \`enact sign ./ && enact publish ./\` |
16
21
 
17
22
  ## Running Tools
23
+
18
24
  Tools take JSON input and return JSON output:
19
25
  \`\`\`bash
20
26
  # Run and capture output
@@ -24,47 +30,281 @@ result=$(enact run author/utils/formatter --args '{"code": "const x=1"}')
24
30
  enact run tool --args '{}' | jq '.data'
25
31
  \`\`\`
26
32
 
27
- ## Creating Tools
28
- Create \`SKILL.md\` in a directory:
33
+ ## SKILL.md Structure
34
+
35
+ Each tool is defined by a \`SKILL.md\` file (YAML frontmatter + Markdown docs):
36
+
29
37
  \`\`\`yaml
30
38
  ---
31
39
  name: namespace/category/tool
32
- description: Clear description for search
40
+ description: What the tool does
33
41
  version: 1.0.0
34
- from: python:3.12-slim # Docker image
35
- build: pip install requests # Install dependencies (cached)
36
- command: python /work/main.py \${input}
42
+ enact: "2.0.0"
43
+
44
+ from: python:3.12-slim # Docker image (pin versions, not :latest)
45
+ build: pip install requests # Build steps (cached by Dagger)
46
+ command: python /workspace/main.py \${input}
47
+ timeout: 30s
48
+
37
49
  inputSchema:
38
50
  type: object
39
51
  properties:
40
- input: { type: string, description: "The input to process" }
52
+ input:
53
+ type: string
54
+ description: "Input to process"
41
55
  required: [input]
56
+
57
+ outputSchema:
58
+ type: object
59
+ properties:
60
+ result:
61
+ type: string
62
+
63
+ env:
64
+ API_KEY:
65
+ description: "External API key"
66
+ secret: true # Set via: enact env set API_KEY --secret
42
67
  ---
43
68
  # Tool Name
44
- Usage documentation here.
69
+ Documentation here.
70
+ \`\`\`
71
+
72
+ ## Field Reference
73
+
74
+ | Field | Description |
75
+ |-------|-------------|
76
+ | \`name\` | Hierarchical ID: \`org/category/tool\` |
77
+ | \`description\` | What the tool does |
78
+ | \`version\` | Semver version |
79
+ | \`from\` | Docker image |
80
+ | \`build\` | Build commands (string or array, cached) |
81
+ | \`command\` | Shell command with \`\${param}\` substitution |
82
+ | \`timeout\` | Max execution time (e.g., "30s", "5m") |
83
+ | \`inputSchema\` | JSON Schema for inputs |
84
+ | \`outputSchema\` | JSON Schema for outputs |
85
+ | \`env\` | Environment variables and secrets |
86
+
87
+ ## Parameter Substitution
88
+
89
+ Enact auto-quotes parameters. **Never manually quote:**
90
+
91
+ \`\`\`yaml
92
+ # WRONG - causes double-quoting
93
+ command: python /workspace/main.py "\${input}"
94
+
95
+ # RIGHT - Enact handles quoting
96
+ command: python /workspace/main.py \${input}
45
97
  \`\`\`
46
98
 
47
- Key points:
48
- - \`\${param}\` is auto-quoted — never add manual quotes
49
- - \`from:\` pin image versions (not \`:latest\`)
50
- - \`build:\` steps are cached by Dagger
51
- - Output JSON to stdout, errors to stderr
52
- - Non-zero exit = failure
99
+ **Optional vs Required Parameters:**
53
100
 
54
- ## Tool Development Workflow
101
+ Parameters listed in the \`required\` array must be provided. All other properties are optional.
102
+
103
+ \`\`\`yaml
104
+ inputSchema:
105
+ type: object
106
+ properties:
107
+ url: # Required (in required array)
108
+ type: string
109
+ user_agent: # Optional (not in required array)
110
+ type: string
111
+ default: "Mozilla/5.0" # Provide default for optional params
112
+ timeout:
113
+ type: number
114
+ default: 30
115
+ required: [url] # Only url is required
116
+ \`\`\`
117
+
118
+ **Best practices for optional parameters:**
119
+ 1. Always provide a \`default\` value in the schema when possible
120
+ 2. Optional params without defaults become empty strings in commands
121
+ 3. Use \`--debug\` to see how parameters are resolved: \`enact run ./ --debug\`
122
+
123
+ Handle optional params in shell commands:
124
+ \`\`\`yaml
125
+ command: "curl \${url} -A '\${user_agent:-Mozilla/5.0}' --max-time \${timeout:-30}"
126
+ \`\`\`
127
+
128
+ Modifiers:
129
+ - \`\${param}\` — auto-quoted (handles spaces, JSON, special chars)
130
+ - \`\${param:raw}\` — raw, no quoting (use carefully)
131
+
132
+ ## Output
133
+
134
+ Output valid JSON to stdout when \`outputSchema\` is defined:
135
+
136
+ \`\`\`python
137
+ import json, sys
138
+
139
+ try:
140
+ result = do_work()
141
+ print(json.dumps({"status": "success", "result": result}))
142
+ except Exception as e:
143
+ print(json.dumps({"status": "error", "message": str(e)}))
144
+ sys.exit(1) # non-zero = error
145
+ \`\`\`
146
+
147
+ ## Build Steps by Language
148
+
149
+ **Python:**
150
+ \`\`\`yaml
151
+ from: python:3.12-slim
152
+ build: pip install requests pandas
153
+ \`\`\`
154
+
155
+ **Node.js:**
156
+ \`\`\`yaml
157
+ from: node:20-alpine
158
+ build:
159
+ - npm install
160
+ - npm run build
161
+ \`\`\`
162
+
163
+ **Rust:**
164
+ \`\`\`yaml
165
+ from: rust:1.83-slim
166
+ build: rustc /workspace/main.rs -o /workspace/tool
167
+ command: /workspace/tool \${input}
168
+ \`\`\`
169
+
170
+ **Go:**
171
+ \`\`\`yaml
172
+ from: golang:1.22-alpine
173
+ build: cd /workspace && go build -o tool main.go
174
+ command: /workspace/tool \${input}
175
+ \`\`\`
176
+
177
+ **System packages:**
178
+ \`\`\`yaml
179
+ build: apt-get update && apt-get install -y libfoo-dev
180
+ \`\`\`
181
+
182
+ Build steps are cached — first run slow, subsequent runs instant.
183
+
184
+ ## Base Image Requirements
185
+
186
+ Not all Docker images work as Enact base images. Requirements:
187
+
188
+ **Must have:**
189
+ - \`/bin/sh\` (POSIX shell) - Enact wraps commands with \`sh -c\`
190
+ - Standard filesystem layout
191
+
192
+ **Recommended base images:**
193
+ | Language | Image | Notes |
194
+ |----------|-------|-------|
195
+ | Python | \`python:3.12-slim\` | Best balance of size/features |
196
+ | Node.js | \`node:20-alpine\` | Smallest, may need build tools |
197
+ | Ruby | \`ruby:3.2-slim\` | For Ruby tools |
198
+ | Go | \`golang:1.22-alpine\` | Compile in build step |
199
+ | Rust | \`rust:1.83-slim\` | Compile in build step |
200
+ | General | \`alpine:3.18\` | Minimal, use apk for packages |
201
+ | Debian | \`debian:bookworm-slim\` | More packages, larger |
202
+
203
+ **Images that may NOT work:**
204
+ - Distroless images (no shell)
205
+ - Scratch images (no shell)
206
+ - Some specialized images with custom entrypoints
207
+
208
+ **Debugging image issues:**
55
209
  \`\`\`bash
56
- enact run ./ --args '{"input": "test"}' # Test locally
57
- enact run ./ --args '{}' --dry-run # Preview command
58
- enact sign ./ && enact publish ./ # Publish
210
+ # Test if an image works with Enact
211
+ enact exec . "echo test" # Run simple command in tool's container
212
+ enact run ./ --verbose # See detailed container output
59
213
  \`\`\`
60
214
 
215
+ ## File Access
216
+
217
+ Tools run in a container with \`/workspace\` as the working directory. All source files are copied there.
218
+
61
219
  ## Secrets
62
- Declare in SKILL.md, set via CLI:
220
+
221
+ Declare in \`SKILL.md\`:
63
222
  \`\`\`yaml
64
223
  env:
65
- API_KEY: # Declared but not set
224
+ API_KEY:
225
+ description: "API key for service"
226
+ secret: true
66
227
  \`\`\`
228
+
229
+ Set before running:
67
230
  \`\`\`bash
68
231
  enact env set API_KEY --secret --namespace author/tool
69
232
  \`\`\`
233
+
234
+ Access in code:
235
+ \`\`\`python
236
+ import os
237
+ api_key = os.environ.get('API_KEY')
238
+ \`\`\`
239
+
240
+ ## LLM Instruction Tools
241
+
242
+ Tools without a \`command\` field are interpreted by LLMs:
243
+
244
+ \`\`\`yaml
245
+ ---
246
+ name: myorg/ai/reviewer
247
+ description: AI-powered code review
248
+ inputSchema:
249
+ type: object
250
+ properties:
251
+ code: { type: string }
252
+ required: [code]
253
+ outputSchema:
254
+ type: object
255
+ properties:
256
+ issues: { type: array }
257
+ score: { type: number }
258
+ ---
259
+ # Code Reviewer
260
+
261
+ You are a senior engineer. Review the code for bugs, style, and security.
262
+ Return JSON: {"issues": [...], "score": 75}
263
+ \`\`\`
264
+
265
+ ## Publishing Checklist
266
+
267
+ - [ ] \`name\` follows \`namespace/category/tool\` pattern
268
+ - [ ] \`version\` set (semver)
269
+ - [ ] \`description\` is clear and searchable
270
+ - [ ] \`inputSchema\` / \`outputSchema\` defined
271
+ - [ ] \`from\` uses pinned image version
272
+ - [ ] \`timeout\` set appropriately
273
+ - [ ] Tool tested locally with \`enact run ./\`
274
+
275
+ ## Troubleshooting
276
+
277
+ \`\`\`bash
278
+ enact run ./ --args '{"x": "y"}' --verbose # Verbose output
279
+ enact run ./ --args '{}' --dry-run # Preview command without running
280
+ enact run ./ --args '{}' --debug # Show parameter resolution details
281
+ enact list # List installed tools
282
+ \`\`\`
283
+
284
+ **Common issues:**
285
+
286
+ | Issue | Solution |
287
+ |-------|----------|
288
+ | "Missing required parameter" | Check your \`required\` array in inputSchema |
289
+ | Parameters not substituting | Use \`--debug\` to see what values are being passed |
290
+ | Optional params failing | Add \`default\` values or handle empty strings |
291
+ | Build step fails | Use \`--verbose\` to see build output |
292
+ | Base image not working | Use recommended images (see Base Image Requirements) |
293
+ | Command exit code 2 | Check if command exists in the container |
294
+
295
+ **Debugging build failures:**
296
+ \`\`\`bash
297
+ enact run ./ --args '{}' --verbose # See build output
298
+ enact exec . "which python" # Check if command exists
299
+ enact exec . "ls /workspace" # Check file mounts
300
+ enact validate ./ # Validate SKILL.md structure
301
+ \`\`\`
302
+
303
+ **Multi-step builds:** Use YAML array syntax for clearer error reporting:
304
+ \`\`\`yaml
305
+ build:
306
+ - apk add --no-cache ruby ruby-dev
307
+ - gem install bundler
308
+ - bundle install
309
+ \`\`\`
70
310
  `;
@@ -12,6 +12,7 @@ Enact tools are containerized, cryptographically-signed executables. Each tool i
12
12
  | Run with JSON | \`enact run ./ --args '{"key": "value"}'\` |
13
13
  | Run from file | \`enact run ./ --input-file inputs.json\` |
14
14
  | Dry run | \`enact run ./ --args '{}' --dry-run\` |
15
+ | Debug params | \`enact run ./ --args '{}' --debug\` |
15
16
  | Sign & publish | \`enact sign ./ && enact publish ./\` |
16
17
 
17
18
  ## SKILL.md Structure
@@ -25,7 +26,7 @@ enact: "2.0.0"
25
26
 
26
27
  from: python:3.12-slim # Docker image (pin versions, not :latest)
27
28
  build: pip install requests # Build steps (cached by Dagger)
28
- command: python /work/main.py \${input}
29
+ command: python /workspace/main.py \${input}
29
30
  timeout: 30s
30
31
 
31
32
  inputSchema:
@@ -72,24 +73,39 @@ Enact auto-quotes parameters. **Never manually quote:**
72
73
 
73
74
  \`\`\`yaml
74
75
  # WRONG - causes double-quoting
75
- command: python /work/main.py "\${input}"
76
+ command: python /workspace/main.py "\${input}"
76
77
 
77
78
  # RIGHT - Enact handles quoting
78
- command: python /work/main.py \${input}
79
+ command: python /workspace/main.py \${input}
79
80
  \`\`\`
80
81
 
81
- **Optional params:** Missing optional params become empty strings. Always provide defaults:
82
+ **Optional vs Required Parameters:**
83
+
84
+ Parameters listed in the \`required\` array must be provided. All other properties are optional.
85
+
82
86
  \`\`\`yaml
83
87
  inputSchema:
88
+ type: object
84
89
  properties:
85
- greeting:
90
+ url: # Required (in required array)
86
91
  type: string
87
- default: "Hello" # Recommended for optional params
92
+ user_agent: # Optional (not in required array)
93
+ type: string
94
+ default: "Mozilla/5.0" # Provide default for optional params
95
+ timeout:
96
+ type: number
97
+ default: 30
98
+ required: [url] # Only url is required
88
99
  \`\`\`
89
100
 
90
- Or handle empty in shell:
101
+ **Best practices for optional parameters:**
102
+ 1. Always provide a \`default\` value in the schema when possible
103
+ 2. Optional params without defaults become empty strings in commands
104
+ 3. Use \`--debug\` to see how parameters are resolved: \`enact run ./ --debug\`
105
+
106
+ Handle optional params in shell commands:
91
107
  \`\`\`yaml
92
- command: "echo \${greeting:-Hello} \${name}"
108
+ command: "curl \${url} -A '\${user_agent:-Mozilla/5.0}' --max-time \${timeout:-30}"
93
109
  \`\`\`
94
110
 
95
111
  Modifiers:
@@ -130,15 +146,15 @@ build:
130
146
  **Rust:**
131
147
  \`\`\`yaml
132
148
  from: rust:1.83-slim
133
- build: rustc /work/main.rs -o /work/tool
134
- command: /work/tool \${input}
149
+ build: rustc /workspace/main.rs -o /workspace/tool
150
+ command: /workspace/tool \${input}
135
151
  \`\`\`
136
152
 
137
153
  **Go:**
138
154
  \`\`\`yaml
139
155
  from: golang:1.22-alpine
140
- build: cd /work && go build -o tool main.go
141
- command: /work/tool \${input}
156
+ build: cd /workspace && go build -o tool main.go
157
+ command: /workspace/tool \${input}
142
158
  \`\`\`
143
159
 
144
160
  **System packages:**
@@ -148,9 +164,40 @@ build: apt-get update && apt-get install -y libfoo-dev
148
164
 
149
165
  Build steps are cached — first run slow, subsequent runs instant.
150
166
 
167
+ ## Base Image Requirements
168
+
169
+ Not all Docker images work as Enact base images. Requirements:
170
+
171
+ **Must have:**
172
+ - \`/bin/sh\` (POSIX shell) - Enact wraps commands with \`sh -c\`
173
+ - Standard filesystem layout
174
+
175
+ **Recommended base images:**
176
+ | Language | Image | Notes |
177
+ |----------|-------|-------|
178
+ | Python | \`python:3.12-slim\` | Best balance of size/features |
179
+ | Node.js | \`node:20-alpine\` | Smallest, may need build tools |
180
+ | Ruby | \`ruby:3.2-slim\` | For Ruby tools |
181
+ | Go | \`golang:1.22-alpine\` | Compile in build step |
182
+ | Rust | \`rust:1.83-slim\` | Compile in build step |
183
+ | General | \`alpine:3.18\` | Minimal, use apk for packages |
184
+ | Debian | \`debian:bookworm-slim\` | More packages, larger |
185
+
186
+ **Images that may NOT work:**
187
+ - Distroless images (no shell)
188
+ - Scratch images (no shell)
189
+ - Some specialized images with custom entrypoints
190
+
191
+ **Debugging image issues:**
192
+ \`\`\`bash
193
+ # Test if an image works with Enact
194
+ enact exec . "echo test" # Run simple command in tool's container
195
+ enact run ./ --verbose # See detailed container output
196
+ \`\`\`
197
+
151
198
  ## File Access
152
199
 
153
- Tools run in a container with \`/work\` as the working directory. All source files are copied there.
200
+ Tools run in a container with \`/workspace\` as the working directory. All source files are copied there.
154
201
 
155
202
  ## Secrets
156
203
 
@@ -212,7 +259,35 @@ Return JSON: {"issues": [...], "score": 75}
212
259
 
213
260
  \`\`\`bash
214
261
  enact run ./ --args '{"x": "y"}' --verbose # Verbose output
215
- enact run ./ --args '{}' --dry-run # Preview command
216
- enact list # List installed tools
262
+ enact run ./ --args '{}' --dry-run # Preview command without running
263
+ enact run ./ --args '{}' --debug # Show parameter resolution details
264
+ enact list # List installed tools
265
+ \`\`\`
266
+
267
+ **Common issues:**
268
+
269
+ | Issue | Solution |
270
+ |-------|----------|
271
+ | "Missing required parameter" | Check your \`required\` array in inputSchema |
272
+ | Parameters not substituting | Use \`--debug\` to see what values are being passed |
273
+ | Optional params failing | Add \`default\` values or handle empty strings |
274
+ | Build step fails | Use \`--verbose\` to see build output |
275
+ | Base image not working | Use recommended images (see Base Image Requirements) |
276
+ | Command exit code 2 | Check if command exists in the container |
277
+
278
+ **Debugging build failures:**
279
+ \`\`\`bash
280
+ enact run ./ --args '{}' --verbose # See build output
281
+ enact exec . "which python" # Check if command exists
282
+ enact exec . "ls /workspace" # Check file mounts
283
+ enact validate ./ # Validate SKILL.md structure
284
+ \`\`\`
285
+
286
+ **Multi-step builds:** Use YAML array syntax for clearer error reporting:
287
+ \`\`\`yaml
288
+ build:
289
+ - apk add --no-cache ruby ruby-dev
290
+ - gem install bundler
291
+ - bundle install
217
292
  \`\`\`
218
293
  `;
@@ -22,6 +22,7 @@ import {
22
22
  verifyAllAttestations,
23
23
  } from "@enactprotocol/api";
24
24
  import {
25
+ addMcpTool,
25
26
  addToolToRegistry,
26
27
  getCacheDir,
27
28
  getInstalledVersion,
@@ -438,6 +439,11 @@ async function installFromRegistry(
438
439
  // Update tools.json for the appropriate scope
439
440
  addToolToRegistry(toolName, targetVersion!, scope, isGlobal ? undefined : ctx.cwd);
440
441
 
442
+ // Also add to MCP registry for global installs
443
+ if (isGlobal) {
444
+ addMcpTool(toolName, targetVersion!);
445
+ }
446
+
441
447
  // Output result
442
448
  if (options.json) {
443
449
  json({
@@ -533,6 +539,11 @@ async function installFromPath(
533
539
  // Update tools.json for the appropriate scope
534
540
  addToolToRegistry(manifest.name, version, scope, isGlobal ? undefined : ctx.cwd);
535
541
 
542
+ // Also add to MCP registry for global installs
543
+ if (isGlobal) {
544
+ addMcpTool(manifest.name, version);
545
+ }
546
+
536
547
  if (options.json) {
537
548
  json({
538
549
  installed: true,
@@ -114,17 +114,12 @@ async function learnHandler(
114
114
  config.registry?.url ??
115
115
  "https://siikwkfgsmouioodghho.supabase.co/functions/v1";
116
116
 
117
- // Get auth token - try stored JWT first (for private tools), then fall back to config/env/anon
118
- const { getValidToken } = await import("../auth/index.js");
119
- let authToken: string | undefined = (await getValidToken()) ?? undefined;
120
- if (!authToken) {
121
- authToken = config.registry?.authToken ?? process.env.ENACT_AUTH_TOKEN;
122
- }
123
- // Fall back to anon key for unauthenticated public access
124
- if (!authToken && registryUrl.includes("siikwkfgsmouioodghho.supabase.co")) {
125
- authToken =
126
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InNpaWt3a2Znc21vdWlvb2RnaGhvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjQ2MTkzMzksImV4cCI6MjA4MDE5NTMzOX0.kxnx6-IPFhmGx6rzNx36vbyhFMFZKP_jFqaDbKnJ_E0";
127
- }
117
+ // Anon key for public access (learn doesn't require authentication)
118
+ const SUPABASE_ANON_KEY =
119
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InNpaWt3a2Znc21vdWlvb2RnaGhvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjQ2MTkzMzksImV4cCI6MjA4MDE5NTMzOX0.kxnx6-IPFhmGx6rzNx36vbyhFMFZKP_jFqaDbKnJ_E0";
120
+
121
+ // Use anon key for public tool access - no auth required for learn command
122
+ const authToken = process.env.ENACT_AUTH_TOKEN ?? config.registry?.authToken ?? SUPABASE_ANON_KEY;
128
123
 
129
124
  const client = createApiClient({
130
125
  baseUrl: registryUrl,