@enactprotocol/cli 2.0.9 → 2.0.11
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/commands/init/index.d.ts.map +1 -1
- package/dist/commands/init/index.js +75 -60
- package/dist/commands/init/index.js.map +1 -1
- package/dist/commands/init/templates/agent-agents.md +47 -0
- package/dist/commands/init/templates/claude.md +65 -0
- package/dist/commands/init/templates/tool-agents.md +56 -0
- package/dist/commands/init/templates/tool-enact.md +44 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/src/commands/init/index.ts +86 -61
- package/src/commands/init/templates/agent-agents.md +47 -0
- package/src/commands/init/templates/claude.md +65 -0
- package/src/commands/init/templates/tool-agents.md +56 -0
- package/src/commands/init/templates/tool-enact.md +44 -0
- package/src/index.ts +1 -1
- package/tests/commands/init.test.ts +387 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Create a basic tool template in the current directory.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
8
|
-
import { join } from "node:path";
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
9
10
|
import { getSecret } from "@enactprotocol/secrets";
|
|
10
11
|
import type { Command } from "commander";
|
|
11
12
|
import type { CommandContext, GlobalOptions } from "../../types";
|
|
@@ -22,9 +23,32 @@ const SUPABASE_ANON_KEY =
|
|
|
22
23
|
process.env.SUPABASE_ANON_KEY ??
|
|
23
24
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InNpaWt3a2Znc21vdWlvb2RnaGhvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjQ2MTkzMzksImV4cCI6MjA4MDE5NTMzOX0.kxnx6-IPFhmGx6rzNx36vbyhFMFZKP_jFqaDbKnJ_E0";
|
|
24
25
|
|
|
26
|
+
/** Get the templates directory path */
|
|
27
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
28
|
+
const __dirname = dirname(__filename);
|
|
29
|
+
const TEMPLATES_DIR = join(__dirname, "templates");
|
|
30
|
+
|
|
25
31
|
interface InitOptions extends GlobalOptions {
|
|
26
32
|
name?: string;
|
|
27
33
|
force?: boolean;
|
|
34
|
+
tool?: boolean;
|
|
35
|
+
agent?: boolean;
|
|
36
|
+
claude?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Load a template file and replace placeholders
|
|
41
|
+
*/
|
|
42
|
+
function loadTemplate(templateName: string, replacements: Record<string, string> = {}): string {
|
|
43
|
+
const templatePath = join(TEMPLATES_DIR, templateName);
|
|
44
|
+
let content = readFileSync(templatePath, "utf-8");
|
|
45
|
+
|
|
46
|
+
// Replace all {{PLACEHOLDER}} patterns
|
|
47
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
48
|
+
content = content.replaceAll(`{{${key}}}`, value);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return content;
|
|
28
52
|
}
|
|
29
53
|
|
|
30
54
|
/**
|
|
@@ -105,65 +129,52 @@ async function getCurrentUsername(): Promise<string | null> {
|
|
|
105
129
|
}
|
|
106
130
|
}
|
|
107
131
|
|
|
108
|
-
/**
|
|
109
|
-
* Generate the tool template content
|
|
110
|
-
*/
|
|
111
|
-
function generateToolTemplate(toolName: string): string {
|
|
112
|
-
return `---
|
|
113
|
-
name: ${toolName}
|
|
114
|
-
description: A simple tool that echoes a greeting
|
|
115
|
-
version: 0.1.0
|
|
116
|
-
enact: "2.0"
|
|
117
|
-
|
|
118
|
-
from: alpine:latest
|
|
119
|
-
|
|
120
|
-
inputSchema:
|
|
121
|
-
type: object
|
|
122
|
-
properties:
|
|
123
|
-
name:
|
|
124
|
-
type: string
|
|
125
|
-
description: Name to greet
|
|
126
|
-
default: World
|
|
127
|
-
required: []
|
|
128
|
-
|
|
129
|
-
command: |
|
|
130
|
-
echo "Hello, \${name}!"
|
|
131
|
-
---
|
|
132
|
-
|
|
133
|
-
# ${toolName}
|
|
134
|
-
|
|
135
|
-
A simple greeting tool created with \`enact init\`.
|
|
136
|
-
|
|
137
|
-
## Usage
|
|
138
|
-
|
|
139
|
-
\`\`\`bash
|
|
140
|
-
enact run ./ --args '{"name": "Alice"}'
|
|
141
|
-
\`\`\`
|
|
142
|
-
|
|
143
|
-
## Customization
|
|
144
|
-
|
|
145
|
-
Edit this file to create your own tool:
|
|
146
|
-
|
|
147
|
-
1. Update the \`name\` and \`description\` in the frontmatter
|
|
148
|
-
2. Modify the \`inputSchema\` to define your tool's inputs
|
|
149
|
-
3. Change the \`command\` to run your desired shell commands
|
|
150
|
-
4. Update this documentation section
|
|
151
|
-
|
|
152
|
-
## Learn More
|
|
153
|
-
|
|
154
|
-
- [Enact Documentation](https://enact.dev/docs)
|
|
155
|
-
- [Tool Manifest Reference](https://enact.dev/docs/manifest)
|
|
156
|
-
`;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
132
|
/**
|
|
160
133
|
* Init command handler
|
|
161
134
|
*/
|
|
162
135
|
async function initHandler(options: InitOptions, ctx: CommandContext): Promise<void> {
|
|
163
136
|
const targetDir = ctx.cwd;
|
|
137
|
+
|
|
138
|
+
// Determine mode: --agent, --claude, or --tool (default)
|
|
139
|
+
const isAgentMode = options.agent;
|
|
140
|
+
const isClaudeMode = options.claude;
|
|
141
|
+
// Default to tool mode if no flag specified
|
|
142
|
+
|
|
143
|
+
// Handle --agent mode: create AGENTS.md for projects using Enact tools
|
|
144
|
+
if (isAgentMode) {
|
|
145
|
+
const agentsPath = join(targetDir, "AGENTS.md");
|
|
146
|
+
if (existsSync(agentsPath) && !options.force) {
|
|
147
|
+
warning(`AGENTS.md already exists at: ${agentsPath}`);
|
|
148
|
+
info("Use --force to overwrite");
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
writeFileSync(agentsPath, loadTemplate("agent-agents.md"), "utf-8");
|
|
152
|
+
success(`Created AGENTS.md: ${agentsPath}`);
|
|
153
|
+
info("");
|
|
154
|
+
info("This file helps AI agents understand how to use Enact tools in your project.");
|
|
155
|
+
info("Run 'enact search <query>' to find tools, 'enact install <tool>' to add them.");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Handle --claude mode: create CLAUDE.md
|
|
160
|
+
if (isClaudeMode) {
|
|
161
|
+
const claudePath = join(targetDir, "CLAUDE.md");
|
|
162
|
+
if (existsSync(claudePath) && !options.force) {
|
|
163
|
+
warning(`CLAUDE.md already exists at: ${claudePath}`);
|
|
164
|
+
info("Use --force to overwrite");
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
writeFileSync(claudePath, loadTemplate("claude.md"), "utf-8");
|
|
168
|
+
success(`Created CLAUDE.md: ${claudePath}`);
|
|
169
|
+
info("");
|
|
170
|
+
info("This file helps Claude understand how to use Enact tools in your project.");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Handle --tool mode (default): create enact.md + AGENTS.md for tool development
|
|
164
175
|
const manifestPath = join(targetDir, "enact.md");
|
|
176
|
+
const agentsPath = join(targetDir, "AGENTS.md");
|
|
165
177
|
|
|
166
|
-
// Check if manifest already exists
|
|
167
178
|
if (existsSync(manifestPath) && !options.force) {
|
|
168
179
|
warning(`Tool manifest already exists at: ${manifestPath}`);
|
|
169
180
|
info("Use --force to overwrite");
|
|
@@ -185,17 +196,28 @@ async function initHandler(options: InitOptions, ctx: CommandContext): Promise<v
|
|
|
185
196
|
}
|
|
186
197
|
}
|
|
187
198
|
|
|
188
|
-
//
|
|
189
|
-
const
|
|
199
|
+
// Load templates with placeholder replacement
|
|
200
|
+
const replacements = { TOOL_NAME: toolName };
|
|
201
|
+
const manifestContent = loadTemplate("tool-enact.md", replacements);
|
|
202
|
+
const agentsContent = loadTemplate("tool-agents.md", replacements);
|
|
190
203
|
|
|
191
204
|
// Ensure directory exists
|
|
192
205
|
if (!existsSync(targetDir)) {
|
|
193
206
|
mkdirSync(targetDir, { recursive: true });
|
|
194
207
|
}
|
|
195
208
|
|
|
196
|
-
|
|
209
|
+
// Write enact.md
|
|
210
|
+
writeFileSync(manifestPath, manifestContent, "utf-8");
|
|
211
|
+
success(`Created tool manifest: ${manifestPath}`);
|
|
212
|
+
|
|
213
|
+
// Write AGENTS.md (only if it doesn't exist or --force is used)
|
|
214
|
+
if (!existsSync(agentsPath) || options.force) {
|
|
215
|
+
writeFileSync(agentsPath, agentsContent, "utf-8");
|
|
216
|
+
success(`Created AGENTS.md: ${agentsPath}`);
|
|
217
|
+
} else {
|
|
218
|
+
info("AGENTS.md already exists, skipping (use --force to overwrite)");
|
|
219
|
+
}
|
|
197
220
|
|
|
198
|
-
success(`Created tool template: ${manifestPath}`);
|
|
199
221
|
info("");
|
|
200
222
|
info("Next steps:");
|
|
201
223
|
info(" 1. Edit enact.md to customize your tool");
|
|
@@ -209,9 +231,12 @@ async function initHandler(options: InitOptions, ctx: CommandContext): Promise<v
|
|
|
209
231
|
export function configureInitCommand(program: Command): void {
|
|
210
232
|
program
|
|
211
233
|
.command("init")
|
|
212
|
-
.description("
|
|
213
|
-
.option("-n, --name <name>", "Tool name (default: username/my-tool
|
|
214
|
-
.option("-f, --force", "Overwrite existing
|
|
234
|
+
.description("Initialize Enact in the current directory")
|
|
235
|
+
.option("-n, --name <name>", "Tool name (default: username/my-tool)")
|
|
236
|
+
.option("-f, --force", "Overwrite existing files")
|
|
237
|
+
.option("--tool", "Create a new Enact tool (default)")
|
|
238
|
+
.option("--agent", "Create AGENTS.md for projects that use Enact tools")
|
|
239
|
+
.option("--claude", "Create CLAUDE.md with Claude-specific instructions")
|
|
215
240
|
.option("-v, --verbose", "Show detailed output")
|
|
216
241
|
.action(async (options: InitOptions) => {
|
|
217
242
|
const ctx: CommandContext = {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
This project uses Enact tools — containerized, cryptographically-signed executables.
|
|
4
|
+
|
|
5
|
+
## Running Tools
|
|
6
|
+
```bash
|
|
7
|
+
enact run <tool-name> --args '{"key": "value"}' # Run installed tool
|
|
8
|
+
enact run ./path/to/tool --args '{}' # Run local tool
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Finding & Installing Tools
|
|
12
|
+
```bash
|
|
13
|
+
enact search "pdf extraction" # Search registry
|
|
14
|
+
enact get author/category/tool # View tool info
|
|
15
|
+
enact install author/category/tool # Add to project (.enact/tools.json)
|
|
16
|
+
enact install author/category/tool --global # Add globally
|
|
17
|
+
enact list # List project tools
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Tool Output
|
|
21
|
+
Tools output JSON to stdout. Parse with jq or your language's JSON parser:
|
|
22
|
+
```bash
|
|
23
|
+
enact run tool --args '{}' | jq '.result'
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Creating Local Tools
|
|
27
|
+
Create `tools/<name>/enact.md` with:
|
|
28
|
+
```yaml
|
|
29
|
+
---
|
|
30
|
+
name: my-tool
|
|
31
|
+
description: What it does
|
|
32
|
+
command: echo "Hello ${name}"
|
|
33
|
+
inputSchema:
|
|
34
|
+
type: object
|
|
35
|
+
properties:
|
|
36
|
+
name: { type: string }
|
|
37
|
+
---
|
|
38
|
+
# My Tool
|
|
39
|
+
Documentation here.
|
|
40
|
+
```
|
|
41
|
+
Run with: `enact run ./tools/<name> --args '{"name": "World"}'`
|
|
42
|
+
|
|
43
|
+
## Environment & Secrets
|
|
44
|
+
```bash
|
|
45
|
+
enact env set API_KEY --secret --namespace author/tool # Set secret
|
|
46
|
+
enact env list # List env vars
|
|
47
|
+
```
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This project uses Enact tools — containerized, signed executables you can run via CLI.
|
|
4
|
+
|
|
5
|
+
## Quick Reference
|
|
6
|
+
```bash
|
|
7
|
+
enact run <tool> --args '{"key": "value"}' # Run a tool
|
|
8
|
+
enact search "keyword" # Find tools
|
|
9
|
+
enact install author/tool # Install tool
|
|
10
|
+
enact list # List installed tools
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Running Tools
|
|
14
|
+
Tools take JSON input and return JSON output:
|
|
15
|
+
```bash
|
|
16
|
+
# Run and capture output
|
|
17
|
+
result=$(enact run author/utils/formatter --args '{"code": "const x=1"}')
|
|
18
|
+
|
|
19
|
+
# Parse with jq
|
|
20
|
+
enact run tool --args '{}' | jq '.data'
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Creating Tools
|
|
24
|
+
Create `enact.md` in a directory:
|
|
25
|
+
```yaml
|
|
26
|
+
---
|
|
27
|
+
name: namespace/category/tool
|
|
28
|
+
description: Clear description for search
|
|
29
|
+
version: 1.0.0
|
|
30
|
+
from: python:3.12-slim # Docker image
|
|
31
|
+
build: pip install requests # Install dependencies (cached)
|
|
32
|
+
command: python /work/main.py ${input}
|
|
33
|
+
inputSchema:
|
|
34
|
+
type: object
|
|
35
|
+
properties:
|
|
36
|
+
input: { type: string, description: "The input to process" }
|
|
37
|
+
required: [input]
|
|
38
|
+
---
|
|
39
|
+
# Tool Name
|
|
40
|
+
Usage documentation here.
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Key points:
|
|
44
|
+
- `${param}` is auto-quoted — never add manual quotes
|
|
45
|
+
- `from:` pin image versions (not `:latest`)
|
|
46
|
+
- `build:` steps are cached by Dagger
|
|
47
|
+
- Output JSON to stdout, errors to stderr
|
|
48
|
+
- Non-zero exit = failure
|
|
49
|
+
|
|
50
|
+
## Tool Development Workflow
|
|
51
|
+
```bash
|
|
52
|
+
enact run ./ --args '{"input": "test"}' # Test locally
|
|
53
|
+
enact run ./ --args '{}' --dry-run # Preview command
|
|
54
|
+
enact sign ./ && enact publish ./ # Publish
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Secrets
|
|
58
|
+
Declare in enact.md, set via CLI:
|
|
59
|
+
```yaml
|
|
60
|
+
env:
|
|
61
|
+
API_KEY: # Declared but not set
|
|
62
|
+
```
|
|
63
|
+
```bash
|
|
64
|
+
enact env set API_KEY --secret --namespace author/tool
|
|
65
|
+
```
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Enact tool: containerized, signed executable. Manifest: `enact.md` (YAML frontmatter + Markdown docs).
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
```bash
|
|
7
|
+
enact run ./ --args '{"name": "Test"}' # Run locally
|
|
8
|
+
enact run ./ --args '{}' --dry-run # Preview execution
|
|
9
|
+
enact sign ./ && enact publish ./ # Sign and publish
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## enact.md Structure
|
|
13
|
+
```yaml
|
|
14
|
+
---
|
|
15
|
+
name: {{TOOL_NAME}} # org/category/tool format
|
|
16
|
+
description: What it does
|
|
17
|
+
version: 1.0.0 # semver
|
|
18
|
+
from: python:3.12-slim # pin versions, not :latest
|
|
19
|
+
build: pip install requests # cached by Dagger
|
|
20
|
+
command: python /work/main.py ${input}
|
|
21
|
+
timeout: 30s
|
|
22
|
+
inputSchema:
|
|
23
|
+
type: object
|
|
24
|
+
properties:
|
|
25
|
+
input: { type: string }
|
|
26
|
+
required: [input]
|
|
27
|
+
env:
|
|
28
|
+
API_KEY: # declare secrets (set via: enact env set API_KEY --secret)
|
|
29
|
+
---
|
|
30
|
+
# Tool Name
|
|
31
|
+
Documentation here (usage examples, etc.)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Parameter Substitution
|
|
35
|
+
- `${param}` — auto-quoted (handles spaces, JSON, special chars)
|
|
36
|
+
- `${param:raw}` — unquoted (use carefully)
|
|
37
|
+
- **Never manually quote**: `"${param}"` causes double-quoting
|
|
38
|
+
|
|
39
|
+
## Output
|
|
40
|
+
Always output valid JSON when `outputSchema` is defined:
|
|
41
|
+
```python
|
|
42
|
+
import json, sys
|
|
43
|
+
print(json.dumps({"result": data})) # stdout = tool output
|
|
44
|
+
sys.exit(1) # non-zero = error
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## File Access
|
|
48
|
+
Tool runs in container with `/work` as working directory. Source files copied there.
|
|
49
|
+
|
|
50
|
+
## Adding Dependencies
|
|
51
|
+
- Python: `build: pip install package1 package2`
|
|
52
|
+
- Node: `build: ["npm install", "npm run build"]`
|
|
53
|
+
- System: `build: apt-get update && apt-get install -y libfoo`
|
|
54
|
+
- Compiled: `build: rustc /work/main.rs -o /work/tool`
|
|
55
|
+
|
|
56
|
+
Build steps are cached — first run slow, subsequent runs instant.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: {{TOOL_NAME}}
|
|
3
|
+
description: A simple tool that echoes a greeting
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
enact: "2.0"
|
|
6
|
+
|
|
7
|
+
from: alpine:latest
|
|
8
|
+
|
|
9
|
+
inputSchema:
|
|
10
|
+
type: object
|
|
11
|
+
properties:
|
|
12
|
+
name:
|
|
13
|
+
type: string
|
|
14
|
+
description: Name to greet
|
|
15
|
+
default: World
|
|
16
|
+
required: []
|
|
17
|
+
|
|
18
|
+
command: |
|
|
19
|
+
echo "Hello, ${name}!"
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
# {{TOOL_NAME}}
|
|
23
|
+
|
|
24
|
+
A simple greeting tool created with `enact init`.
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
enact run ./ --args '{"name": "Alice"}'
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Customization
|
|
33
|
+
|
|
34
|
+
Edit this file to create your own tool:
|
|
35
|
+
|
|
36
|
+
1. Update the `name` and `description` in the frontmatter
|
|
37
|
+
2. Modify the `inputSchema` to define your tool's inputs
|
|
38
|
+
3. Change the `command` to run your desired shell commands
|
|
39
|
+
4. Update this documentation section
|
|
40
|
+
|
|
41
|
+
## Learn More
|
|
42
|
+
|
|
43
|
+
- [Enact Documentation](https://enact.dev/docs)
|
|
44
|
+
- [Tool Manifest Reference](https://enact.dev/docs/manifest)
|
package/src/index.ts
CHANGED