cmd-fence
CLI Command: fence
API: GET /api/oculus/fences, POST /api/oculus/fences/{id}/execute
Files: oculus/api.py, oculus/fence_index_builder.py
Query and execute code fences directly via MCP or API.
Fence Labeling
Fences can be given labels for easy addressing. Add the label in square brackets after the fence language:
```python[my-label]
# This fence can be addressed as 'my-label' or 'node-slug:my-label'
print("Hello")
```Label syntax examples:
python[fetch]- label is "fetch"python[jenkins-job-config]- label is "jenkins-job-config"yaml[config,execute=true]- label is "config" with additional attributes
Fence Addressing Formats
The oculus_execute_fence tool supports multiple addressing formats:
| Format | Example | Description |
|---|---|---|
| Label | jenkins-job-config |
Direct label lookup (globally unique) |
| slug:label | my-node:fetch |
Node slug + fence label |
| slug:index | my-node:0 |
Node slug + fence position (0-indexed) |
| fence-id | my-node-fence-0 |
Direct fence ID from index |
Usage
Usage
CLI: Smart Fence Discovery
The fence command provides pattern-matching discovery and optional auto-execution:
# List all fences (table format)
oculus fence
# Pattern matching
oculus fence theme # Substring match
oculus fence ^magic # Prefix match (starts with)
oculus fence ball$ # Suffix match (ends with)
oculus fence daily.todo # Dotted path (node.fence)
# Filter by attributes
oculus fence --type python # Filter by fence type
oculus fence --node my-node # Filter by node
oculus fence --tags aws,infra # Filter by tags
# Auto-execute if single match
oculus fence themes -x # Execute if unique match
# Show detailed info (params, location)
oculus fence magic-8-ball -i # Show fence info + params
# JSON output for scripting
oculus fence --json | jq '.fences[].label'CLI: Fence Execution
# Execute by label
oculus exec magic-8-ball --question "Deploy?"
# Execute by slug:index
oculus exec my-node:0
# Execute with JSON params
oculus execute my-fence --params '{"key": "value"}'
# Output format control
oculus exec themes -f yaml # yaml, json, markdown, text, raw
# Force execution (ignore execute bit)
oculus exec my-fence --forceWhen a fence requires parameters and you don't provide them, the CLI prompts interactively:
$ oculus exec magic-8-ball
Fence 'magic-8-ball' requires parameters
Enter required parameters:
question (Your yes/no question): Should I deploy?Params as Config
When executing a fence via MCP/API, the params object is passed to Python fences as the config variable:
# params: {"name": "Alice", "count": 5}
# These are accessible via config:
name = config.get('name', 'default')
count = config.get('count', 1)
result = f"Hello {name}! Count: {count}"Fence Index
The fence index is automatically updated when:
- A node is modified via
poke - A fence's metadata is updated via
oculus_update_fence
Index location: ~/.local/share/oculus/.cache/fence-index/fence-index.json
Params API
Get parameter documentation for a fence tool:
# Get params for a labeled fence
curl "http://localhost:7778/api/oculus/fences/magic-8-ball/params"Returns:
{
"fence_id": "magic-8-ball",
"fence_label": "magic-8-ball",
"slug": "how-to-create-oculus-tool",
"params": [
{
"name": "question",
"type": "string",
"description": "Your yes/no question",
"default": null,
"optional": false
}
],
"required": ["question"],
"optional": [],
"example_call": {
"fence_id": "magic-8-ball",
"params": {"question": "<string>"}
}
}Parameters are extracted from:
- PARAMS: section in fence docstrings
- ## config yaml sections (defaults)
See fence-params for the self-documenting fence pattern.
Test Suite
TableConfig:
array_path: tests
columns:
Test: test
Category: category
Status: status
Details: details
format: markdownβ Fence Execution Error: "'fence-test-data' - Down the rabbit hole we went, but that node doesn't exist! Try 'oculus list' to see what's available."
Unit Tests
| Test | Command | Expected |
|---|---|---|
| Execute by label | oculus_execute_fence({fence_id: "label"}) |
Finds fence by label |
| Execute by slug:label | oculus_execute_fence({fence_id: "node:label"}) |
Finds fence in specific node |
| Execute by slug:index | oculus_execute_fence({fence_id: "node:0"}) |
Finds first fence in node |
| Pass params | oculus_execute_fence({params: {k: v}}) |
Params available as config |
| Force execute | oculus_execute_fence({force: true}) |
Overrides execute=false bit |
| Index rebuild on poke | oculus_poke(...) |
Fence index updated |
Automatic Fence ID Injection β AUTO-FIXED
Status: Implemented in ast_converter.py:205-245
Pattern: gentle-guidance
When rendering markdown, Oculus automatically injects [id=computed-id] into unlabeled fences. This enables any client to address fences by simply parsing the fence info line.
Before (raw file)
```yaml
location: entrance
```After (rendered output)
```yaml[id=adventure-game-data-fence-0]
location: entrance
```How It Works
- During AST-to-markdown conversion, we track current section and fence count
- For unlabeled fences, compute ID using same formula as fence index:
{slug}-{section-slug}-fence-{idx} - Inject
[id=...]into the fence info line - Labeled fences keep their labels unchanged
Client Integration
Any client (neovim, web, CLI) can:
- Find fence at cursor position
- Parse the fence info line using
FenceURLParser - Get
parsed.label(which falls back tometadata.id) - Call execute with that ID
Competent computing says: "Parse the fence info, get the ID." Gentle guidance says: "We inject the ID for you. No line number matching required."
Slots
North
slots:
- oculus-cliSouth
slots:
- fence-test-dataEast
slots:
- context:
- Linking fence params API docs to core fence docs
slug: fence-params
- context:
- Linking fence ID injection docs to gentle guidance pattern
slug: gentle-guidance