Initial commit
This commit is contained in:
457
agents/meta.command/README.md
Normal file
457
agents/meta.command/README.md
Normal file
@@ -0,0 +1,457 @@
|
||||
# meta.command - Command Creator Meta-Agent
|
||||
|
||||
Creates complete, production-ready command manifests from natural language descriptions.
|
||||
|
||||
## Purpose
|
||||
|
||||
The `meta.command` meta-agent transforms command descriptions into properly structured YAML manifests that can be registered in the Betty Framework Command Registry. It handles all the details of command creation including parameter validation, execution configuration, and documentation.
|
||||
|
||||
## What It Does
|
||||
|
||||
- ✅ Parses natural language command descriptions (Markdown or JSON)
|
||||
- ✅ Generates complete command manifests in YAML format
|
||||
- ✅ Validates command structure and execution types
|
||||
- ✅ Supports all three execution types: agent, skill, workflow
|
||||
- ✅ Creates proper parameter definitions with type validation
|
||||
- ✅ Prepares commands for registration via `command.define` skill
|
||||
- ✅ Supports traceability tracking
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
python3 agents/meta.command/meta_command.py <description_file>
|
||||
```
|
||||
|
||||
### With Traceability
|
||||
|
||||
```bash
|
||||
python3 agents/meta.command/meta_command.py examples/api_validate_command.md \
|
||||
--requirement-id "REQ-2025-042" \
|
||||
--requirement-description "Create command for API validation" \
|
||||
--rationale "Simplify API validation workflow for developers"
|
||||
```
|
||||
|
||||
## Input Format
|
||||
|
||||
### Markdown Format
|
||||
|
||||
Create a description file with the following structure:
|
||||
|
||||
```markdown
|
||||
# Name: /api-validate
|
||||
# Version: 0.1.0
|
||||
# Description: Validate API specifications against standards
|
||||
|
||||
# Execution Type: skill
|
||||
# Target: api.validate
|
||||
|
||||
# Parameters:
|
||||
- spec_file: string (required) - Path to API specification file
|
||||
- format: enum (optional, default=openapi, values=[openapi,asyncapi,grpc]) - API specification format
|
||||
- strict: boolean (optional, default=true) - Enable strict validation mode
|
||||
|
||||
# Execution Context:
|
||||
- format: json
|
||||
- timeout: 300
|
||||
|
||||
# Status: active
|
||||
|
||||
# Tags: api, validation, quality
|
||||
```
|
||||
|
||||
### JSON Format
|
||||
|
||||
Alternatively, use JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "/api-validate",
|
||||
"version": "0.1.0",
|
||||
"description": "Validate API specifications against standards",
|
||||
"execution_type": "skill",
|
||||
"target": "api.validate",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "spec_file",
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"description": "Path to API specification file"
|
||||
},
|
||||
{
|
||||
"name": "format",
|
||||
"type": "enum",
|
||||
"values": ["openapi", "asyncapi", "grpc"],
|
||||
"default": "openapi",
|
||||
"description": "API specification format"
|
||||
}
|
||||
],
|
||||
"execution_context": {
|
||||
"format": "json",
|
||||
"timeout": 300
|
||||
},
|
||||
"status": "active",
|
||||
"tags": ["api", "validation", "quality"]
|
||||
}
|
||||
```
|
||||
|
||||
## Command Execution Types
|
||||
|
||||
### 1. Agent Execution
|
||||
|
||||
Use for complex, context-aware tasks requiring reasoning:
|
||||
|
||||
```markdown
|
||||
# Name: /api-design
|
||||
# Execution Type: agent
|
||||
# Target: api.architect
|
||||
# Description: Design a complete API architecture
|
||||
```
|
||||
|
||||
**When to use:**
|
||||
- Tasks requiring multi-step reasoning
|
||||
- Context-aware decision making
|
||||
- Complex analysis or design work
|
||||
|
||||
### 2. Skill Execution
|
||||
|
||||
Use for atomic, deterministic operations:
|
||||
|
||||
```markdown
|
||||
# Name: /api-validate
|
||||
# Execution Type: skill
|
||||
# Target: api.validate
|
||||
# Description: Validate API specifications
|
||||
```
|
||||
|
||||
**When to use:**
|
||||
- Direct, predictable operations
|
||||
- Fast, single-purpose tasks
|
||||
- Composable building blocks
|
||||
|
||||
### 3. Workflow Execution
|
||||
|
||||
Use for orchestrated multi-step processes:
|
||||
|
||||
```markdown
|
||||
# Name: /api-pipeline
|
||||
# Execution Type: workflow
|
||||
# Target: workflows/api-pipeline.yaml
|
||||
# Description: Execute full API development pipeline
|
||||
```
|
||||
|
||||
**When to use:**
|
||||
- Multi-agent/skill coordination
|
||||
- Sequential or parallel task execution
|
||||
- Complex business processes
|
||||
|
||||
## Parameter Types
|
||||
|
||||
### Supported Types
|
||||
|
||||
| Type | Description | Example |
|
||||
|------|-------------|---------|
|
||||
| `string` | Text values | `"api-spec.yaml"` |
|
||||
| `integer` | Whole numbers | `42` |
|
||||
| `boolean` | true/false | `true` |
|
||||
| `enum` | Fixed set of values | `["openapi", "asyncapi"]` |
|
||||
| `array` | Lists of values | `["tag1", "tag2"]` |
|
||||
| `object` | Structured data | `{"key": "value"}` |
|
||||
|
||||
### Parameter Options
|
||||
|
||||
- `required: true/false` - Whether parameter is mandatory
|
||||
- `default: value` - Default value if not provided
|
||||
- `values: [...]` - Allowed values (for enum type)
|
||||
- `description: "..."` - What the parameter does
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Simple Validation Command
|
||||
|
||||
**Input:** `examples/api-validate-cmd.md`
|
||||
|
||||
```markdown
|
||||
# Name: /api-validate
|
||||
# Description: Validate API specification files
|
||||
# Execution Type: skill
|
||||
# Target: api.validate
|
||||
|
||||
# Parameters:
|
||||
- spec_file: string (required) - Path to specification file
|
||||
- format: enum (optional, default=openapi, values=[openapi,asyncapi]) - Spec format
|
||||
|
||||
# Status: active
|
||||
# Tags: api, validation
|
||||
```
|
||||
|
||||
**Output:** `commands/api-validate.yaml`
|
||||
|
||||
```yaml
|
||||
name: /api-validate
|
||||
version: 0.1.0
|
||||
description: Validate API specification files
|
||||
parameters:
|
||||
- name: spec_file
|
||||
type: string
|
||||
required: true
|
||||
description: Path to specification file
|
||||
- name: format
|
||||
type: enum
|
||||
values:
|
||||
- openapi
|
||||
- asyncapi
|
||||
default: openapi
|
||||
description: Spec format
|
||||
execution:
|
||||
type: skill
|
||||
target: api.validate
|
||||
status: active
|
||||
tags:
|
||||
- api
|
||||
- validation
|
||||
```
|
||||
|
||||
### Example 2: Agent-Based Design Command
|
||||
|
||||
**Input:** `examples/api-design-cmd.md`
|
||||
|
||||
```markdown
|
||||
# Name: /api-design
|
||||
# Description: Design a complete API architecture
|
||||
# Execution Type: agent
|
||||
# Target: api.architect
|
||||
|
||||
# Parameters:
|
||||
- requirements: string (required) - Path to requirements document
|
||||
- style: enum (optional, default=rest, values=[rest,graphql,grpc]) - API style
|
||||
|
||||
# Execution Context:
|
||||
- reasoning_mode: iterative
|
||||
- max_iterations: 10
|
||||
|
||||
# Status: active
|
||||
# Tags: api, design, architecture
|
||||
```
|
||||
|
||||
**Output:** `commands/api-design.yaml`
|
||||
|
||||
```yaml
|
||||
name: /api-design
|
||||
version: 0.1.0
|
||||
description: Design a complete API architecture
|
||||
parameters:
|
||||
- name: requirements
|
||||
type: string
|
||||
required: true
|
||||
description: Path to requirements document
|
||||
- name: style
|
||||
type: enum
|
||||
values:
|
||||
- rest
|
||||
- graphql
|
||||
- grpc
|
||||
default: rest
|
||||
description: API style
|
||||
execution:
|
||||
type: agent
|
||||
target: api.architect
|
||||
context:
|
||||
reasoning_mode: iterative
|
||||
max_iterations: 10
|
||||
status: active
|
||||
tags:
|
||||
- api
|
||||
- design
|
||||
- architecture
|
||||
```
|
||||
|
||||
### Example 3: Workflow Command
|
||||
|
||||
**Input:** `examples/deploy-cmd.md`
|
||||
|
||||
```markdown
|
||||
# Name: /deploy
|
||||
# Description: Deploy application to specified environment
|
||||
# Execution Type: workflow
|
||||
# Target: workflows/deploy-pipeline.yaml
|
||||
|
||||
# Parameters:
|
||||
- environment: enum (required, values=[dev,staging,production]) - Target environment
|
||||
- version: string (required) - Version to deploy
|
||||
- skip_tests: boolean (optional, default=false) - Skip test execution
|
||||
|
||||
# Status: draft
|
||||
# Tags: deployment, devops
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
The meta-agent creates:
|
||||
|
||||
1. **Command Manifest** - Complete YAML file in `commands/` directory
|
||||
2. **Console Output** - Summary of created command
|
||||
3. **Next Steps** - Instructions for registration
|
||||
|
||||
Example console output:
|
||||
|
||||
```
|
||||
🎯 meta.command - Creating command from examples/api-validate-cmd.md
|
||||
|
||||
✨ Command '/api-validate' created successfully!
|
||||
|
||||
📄 Created file:
|
||||
- commands/api-validate.yaml
|
||||
|
||||
✅ Command manifest is ready for registration
|
||||
Name: /api-validate
|
||||
Execution: skill → api.validate
|
||||
Status: active
|
||||
|
||||
📝 Next steps:
|
||||
1. Review the manifest: cat commands/api-validate.yaml
|
||||
2. Register command: python3 skills/command.define/command_define.py commands/api-validate.yaml
|
||||
3. Verify in registry: cat registry/commands.json
|
||||
```
|
||||
|
||||
## Integration with command.define
|
||||
|
||||
After creating a command manifest, register it using the `command.define` skill:
|
||||
|
||||
```bash
|
||||
# Register the command
|
||||
python3 skills/command.define/command_define.py commands/api-validate.yaml
|
||||
|
||||
# Verify registration
|
||||
cat registry/commands.json
|
||||
```
|
||||
|
||||
The `command.define` skill will:
|
||||
- Validate the manifest structure
|
||||
- Check that the execution target exists
|
||||
- Add the command to the Command Registry
|
||||
- Make the command available for use
|
||||
|
||||
## Artifact Flow
|
||||
|
||||
```
|
||||
┌──────────────────────────┐
|
||||
│ Command Description │
|
||||
│ (Markdown or JSON) │
|
||||
└──────────┬───────────────┘
|
||||
│ consumes
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ meta.command │
|
||||
└──────┬───────┘
|
||||
│ produces
|
||||
▼
|
||||
┌──────────────────────────┐
|
||||
│ Command Manifest (YAML) │
|
||||
│ commands/*.yaml │
|
||||
└──────────┬───────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│command.define│
|
||||
│ (skill) │
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────┐
|
||||
│ Commands Registry │
|
||||
│ registry/commands.json │
|
||||
└──────────────────────────┘
|
||||
```
|
||||
|
||||
## Command Naming Conventions
|
||||
|
||||
- ✅ Must start with `/` (e.g., `/api-validate`)
|
||||
- ✅ Use kebab-case for multi-word commands (e.g., `/api-validate-all`)
|
||||
- ✅ Be concise but descriptive
|
||||
- ✅ Avoid generic names like `/run` or `/execute`
|
||||
- ✅ Use domain prefix for related commands (e.g., `/api-*`, `/db-*`)
|
||||
|
||||
## Validation
|
||||
|
||||
The meta-agent validates:
|
||||
|
||||
- ✅ Required fields present (name, description, execution_type, target)
|
||||
- ✅ Valid execution type (agent, skill, workflow)
|
||||
- ✅ Command name starts with `/`
|
||||
- ✅ Parameter types are valid
|
||||
- ✅ Enum parameters have values defined
|
||||
- ✅ Version follows semantic versioning
|
||||
- ✅ Status is valid (draft, active, deprecated, archived)
|
||||
|
||||
## Error Handling
|
||||
|
||||
Common errors and solutions:
|
||||
|
||||
**Missing required fields:**
|
||||
```
|
||||
❌ Error: Missing required fields: execution_type, target
|
||||
```
|
||||
→ Add all required fields to your description
|
||||
|
||||
**Invalid execution type:**
|
||||
```
|
||||
❌ Error: Invalid execution type: service. Must be one of: agent, skill, workflow
|
||||
```
|
||||
→ Use only valid execution types
|
||||
|
||||
**Invalid parameter type:**
|
||||
```
|
||||
❌ Error: Invalid parameter type: float
|
||||
```
|
||||
→ Use supported parameter types
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Clear Descriptions** - Write concise, actionable command descriptions
|
||||
2. **Proper Parameters** - Define all parameters with types and validation
|
||||
3. **Appropriate Execution Type** - Choose the right execution model (agent/skill/workflow)
|
||||
4. **Meaningful Tags** - Add relevant tags for discoverability
|
||||
5. **Version Management** - Start with 0.1.0, increment appropriately
|
||||
6. **Status Lifecycle** - Use draft → active → deprecated → archived
|
||||
|
||||
## Files Generated
|
||||
|
||||
```
|
||||
commands/
|
||||
└── {command-name}.yaml # Command manifest
|
||||
```
|
||||
|
||||
## Integration with Meta-Agents
|
||||
|
||||
The `meta.command` agent works alongside:
|
||||
|
||||
- **meta.skill** - Create skills that commands can execute
|
||||
- **meta.agent** - Create agents that commands can delegate to
|
||||
- **meta.artifact** - Define artifact types for command I/O
|
||||
- **meta.compatibility** - Find compatible agents for command workflows
|
||||
|
||||
## Traceability
|
||||
|
||||
Track command creation with requirement metadata:
|
||||
|
||||
```bash
|
||||
python3 agents/meta.command/meta_command.py examples/api-validate-cmd.md \
|
||||
--requirement-id "REQ-2025-042" \
|
||||
--requirement-description "API validation command" \
|
||||
--issue-id "BETTY-123" \
|
||||
--requested-by "dev-team" \
|
||||
--rationale "Streamline API validation process"
|
||||
```
|
||||
|
||||
View trace:
|
||||
```bash
|
||||
python3 betty/trace_cli.py show command.api_validate
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- **command.define skill** - Register command manifests
|
||||
- **meta.skill** - Create skills for command execution
|
||||
- **meta.agent** - Create agents for command delegation
|
||||
- **Command Registry** - `registry/commands.json`
|
||||
- **Command Infrastructure** - `docs/COMMAND_HOOK_INFRASTRUCTURE.md`
|
||||
211
agents/meta.command/agent.yaml
Normal file
211
agents/meta.command/agent.yaml
Normal file
@@ -0,0 +1,211 @@
|
||||
name: meta.command
|
||||
version: 0.1.0
|
||||
description: |
|
||||
Creates complete command manifests from natural language descriptions.
|
||||
|
||||
This meta-agent transforms command descriptions into production-ready command
|
||||
manifests that can be registered in the Betty Framework Command Registry.
|
||||
|
||||
Command manifests can delegate to:
|
||||
- Agents: For intelligent, context-aware operations
|
||||
- Skills: For direct, atomic operations
|
||||
- Workflows: For orchestrated multi-step processes
|
||||
|
||||
The meta.command agent generates properly structured YAML manifests with:
|
||||
- Command name and metadata
|
||||
- Parameter definitions with types and validation
|
||||
- Execution configuration (agent/skill/workflow)
|
||||
- Documentation and examples
|
||||
|
||||
After creation, commands can be registered using the command.define skill.
|
||||
|
||||
artifact_metadata:
|
||||
consumes:
|
||||
- type: command-description
|
||||
file_pattern: "**/command_description.md"
|
||||
content_type: "text/markdown"
|
||||
description: "Natural language description of command requirements"
|
||||
schema: "schemas/command-description.json"
|
||||
|
||||
produces:
|
||||
- type: command-manifest
|
||||
file_pattern: "commands/*.yaml"
|
||||
content_type: "application/yaml"
|
||||
description: "Complete command manifest ready for registration"
|
||||
schema: "schemas/command-manifest.json"
|
||||
|
||||
- type: command-documentation
|
||||
file_pattern: "commands/*/README.md"
|
||||
content_type: "text/markdown"
|
||||
description: "Command documentation with usage examples"
|
||||
|
||||
status: draft
|
||||
reasoning_mode: iterative
|
||||
capabilities:
|
||||
- Transform natural language specifications into validated command manifests
|
||||
- Recommend appropriate execution targets across agents, skills, and workflows
|
||||
- Produce documentation and registration-ready assets for new commands
|
||||
skills_available:
|
||||
- command.define # Register command in registry
|
||||
- artifact.define # Generate artifact metadata
|
||||
|
||||
permissions:
|
||||
- filesystem:read
|
||||
- filesystem:write
|
||||
|
||||
system_prompt: |
|
||||
You are meta.command, the command creator for Betty Framework.
|
||||
|
||||
Your purpose is to transform natural language command descriptions into complete,
|
||||
production-ready command manifests that follow Betty conventions.
|
||||
|
||||
## Automatic Pattern Detection
|
||||
|
||||
You automatically analyze command descriptions to determine the best pattern:
|
||||
- COMMAND_ONLY: Simple 1-3 step orchestration
|
||||
- SKILL_AND_COMMAND: Complex 10+ step tasks requiring a skill backend
|
||||
- SKILL_ONLY: Reusable building blocks without user-facing command
|
||||
- HYBRID: Commands that orchestrate multiple existing skills
|
||||
|
||||
Analysis factors:
|
||||
- Step count (from numbered/bulleted lists)
|
||||
- Complexity keywords (analyze, optimize, evaluate, complex, etc.)
|
||||
- Autonomy requirements (intelligent, adaptive, sophisticated, etc.)
|
||||
- Reusability indicators (composable, shared, library, etc.)
|
||||
|
||||
When you detect high complexity or autonomy needs, you recommend creating
|
||||
the skill first before the command wrapper.
|
||||
|
||||
## Your Workflow
|
||||
|
||||
1. **Parse Description** - Understand command requirements
|
||||
- Extract command name, purpose, and target audience
|
||||
- Identify required parameters and their types
|
||||
- Determine execution type (agent, skill, or workflow)
|
||||
- Understand execution context needs
|
||||
|
||||
2. **Generate Command Manifest** - Create complete YAML definition
|
||||
- Proper naming (must start with /)
|
||||
- Complete parameter specifications with types, validation, defaults
|
||||
- Execution configuration pointing to correct target
|
||||
- Version and status information
|
||||
- Appropriate tags
|
||||
|
||||
3. **Validate Structure** - Ensure manifest completeness
|
||||
- All required fields present
|
||||
- Valid execution type
|
||||
- Proper parameter type definitions
|
||||
- Target exists (agent/skill/workflow)
|
||||
|
||||
4. **Generate Documentation** - Create usage guide
|
||||
- Command purpose and use cases
|
||||
- Parameter descriptions with examples
|
||||
- Expected outputs
|
||||
- Integration examples
|
||||
|
||||
5. **Ready for Registration** - Prepare for command.define
|
||||
- Validate against schema
|
||||
- Check for naming conflicts
|
||||
- Ensure target availability
|
||||
|
||||
## Command Execution Types
|
||||
|
||||
**agent** - Delegates to an intelligent agent
|
||||
- Use for: Complex, context-aware tasks requiring reasoning
|
||||
- Example: `/api-design` → `api.architect` agent
|
||||
- Benefits: Full agent capabilities, multi-step reasoning
|
||||
- Target format: `agent_name` (e.g., "api.architect")
|
||||
|
||||
**skill** - Calls a skill directly
|
||||
- Use for: Atomic, deterministic operations
|
||||
- Example: `/api-validate` → `api.validate` skill
|
||||
- Benefits: Fast, predictable, composable
|
||||
- Target format: `skill.name` (e.g., "api.validate")
|
||||
|
||||
**workflow** - Executes a workflow
|
||||
- Use for: Orchestrated multi-step processes
|
||||
- Example: `/api-pipeline` → workflow YAML
|
||||
- Benefits: Coordinated agent/skill execution
|
||||
- Target format: Path to workflow file
|
||||
|
||||
## Parameter Types
|
||||
|
||||
Supported parameter types:
|
||||
- `string` - Text values
|
||||
- `integer` - Whole numbers
|
||||
- `boolean` - true/false
|
||||
- `enum` - Fixed set of allowed values
|
||||
- `array` - Lists of values
|
||||
- `object` - Structured data
|
||||
|
||||
Each parameter can have:
|
||||
- `name` - Parameter identifier
|
||||
- `type` - Data type
|
||||
- `required` - Whether mandatory (true/false)
|
||||
- `default` - Default value if not provided
|
||||
- `description` - What the parameter does
|
||||
- `values` - Allowed values (for enum type)
|
||||
|
||||
## Command Naming Conventions
|
||||
|
||||
- Must start with `/` (e.g., `/api-validate`)
|
||||
- Use kebab-case for multi-word commands
|
||||
- Should be concise but descriptive
|
||||
- Avoid generic names like `/run` or `/execute`
|
||||
|
||||
## Command Status
|
||||
|
||||
- `draft` - Under development, not ready for production
|
||||
- `active` - Production-ready and available
|
||||
- `deprecated` - Still works but discouraged
|
||||
- `archived` - No longer available
|
||||
|
||||
## Structure Example
|
||||
|
||||
```yaml
|
||||
name: /api-validate
|
||||
version: 0.1.0
|
||||
description: "Validate API specifications against standards"
|
||||
|
||||
parameters:
|
||||
- name: spec_file
|
||||
type: string
|
||||
required: true
|
||||
description: "Path to API specification file"
|
||||
|
||||
- name: format
|
||||
type: enum
|
||||
values: [openapi, asyncapi, grpc]
|
||||
default: openapi
|
||||
description: "API specification format"
|
||||
|
||||
execution:
|
||||
type: skill
|
||||
target: api.validate
|
||||
context:
|
||||
format: json
|
||||
|
||||
status: active
|
||||
|
||||
tags: [api, validation, quality]
|
||||
```
|
||||
|
||||
## Quality Standards
|
||||
|
||||
- ✅ Follows Betty command conventions
|
||||
- ✅ Proper parameter definitions with validation
|
||||
- ✅ Correct execution type and target
|
||||
- ✅ Clear, actionable descriptions
|
||||
- ✅ Appropriate status and tags
|
||||
- ✅ Ready for command.define registration
|
||||
|
||||
## Integration with command.define
|
||||
|
||||
After generating the command manifest, users should:
|
||||
1. Review the generated YAML file
|
||||
2. Test the command locally
|
||||
3. Register using: `python3 skills/command.define/command_define.py <manifest.yaml>`
|
||||
4. Verify registration in `registry/commands.json`
|
||||
|
||||
Remember: You're creating user-facing commands that make Betty's capabilities
|
||||
accessible. Make commands intuitive, well-documented, and easy to use.
|
||||
761
agents/meta.command/meta_command.py
Executable file
761
agents/meta.command/meta_command.py
Executable file
@@ -0,0 +1,761 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
meta.command - Command Creator Meta-Agent
|
||||
|
||||
Generates command manifests from natural language descriptions.
|
||||
|
||||
Usage:
|
||||
python3 agents/meta.command/meta_command.py <command_description_file>
|
||||
|
||||
Examples:
|
||||
python3 agents/meta.command/meta_command.py examples/api_validate_command.md
|
||||
python3 agents/meta.command/meta_command.py examples/deploy_command.json
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import yaml
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Any, Optional
|
||||
|
||||
# Add parent directory to path
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")))
|
||||
|
||||
from betty.config import (
|
||||
BASE_DIR,
|
||||
COMMANDS_REGISTRY_FILE,
|
||||
)
|
||||
from betty.enums import CommandExecutionType, CommandStatus
|
||||
from betty.logging_utils import setup_logger
|
||||
from betty.traceability import get_tracer, RequirementInfo
|
||||
|
||||
logger = setup_logger(__name__)
|
||||
|
||||
# Import artifact validation from artifact.define skill
|
||||
try:
|
||||
import importlib.util
|
||||
artifact_define_path = Path(__file__).parent.parent.parent / "skills" / "artifact.define" / "artifact_define.py"
|
||||
spec = importlib.util.spec_from_file_location("artifact_define", artifact_define_path)
|
||||
artifact_define_module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(artifact_define_module)
|
||||
|
||||
validate_artifact_type = artifact_define_module.validate_artifact_type
|
||||
KNOWN_ARTIFACT_TYPES = artifact_define_module.KNOWN_ARTIFACT_TYPES
|
||||
ARTIFACT_VALIDATION_AVAILABLE = True
|
||||
except Exception as e:
|
||||
ARTIFACT_VALIDATION_AVAILABLE = False
|
||||
|
||||
|
||||
class CommandCreator:
|
||||
"""Creates command manifests from descriptions"""
|
||||
|
||||
VALID_EXECUTION_TYPES = ["agent", "skill", "workflow"]
|
||||
VALID_STATUSES = ["draft", "active", "deprecated", "archived"]
|
||||
VALID_PARAMETER_TYPES = ["string", "integer", "boolean", "enum", "array", "object"]
|
||||
|
||||
# Keywords for complexity analysis
|
||||
AUTONOMY_KEYWORDS = [
|
||||
"analyze", "optimize", "decide", "evaluate", "assess",
|
||||
"complex", "multi-step", "autonomous", "intelligent",
|
||||
"adaptive", "sophisticated", "advanced", "comprehensive"
|
||||
]
|
||||
|
||||
REUSABILITY_KEYWORDS = [
|
||||
"reusable", "composable", "building block", "library",
|
||||
"utility", "helper", "shared", "common", "core"
|
||||
]
|
||||
|
||||
def __init__(self, base_dir: str = BASE_DIR):
|
||||
"""Initialize command creator"""
|
||||
self.base_dir = Path(base_dir)
|
||||
self.commands_dir = self.base_dir / "commands"
|
||||
|
||||
def parse_description(self, description_path: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Parse command description from Markdown or JSON file
|
||||
|
||||
Args:
|
||||
description_path: Path to description file
|
||||
|
||||
Returns:
|
||||
Dict with command configuration
|
||||
"""
|
||||
path = Path(description_path)
|
||||
|
||||
if not path.exists():
|
||||
raise FileNotFoundError(f"Description file not found: {description_path}")
|
||||
|
||||
# Read file
|
||||
content = path.read_text()
|
||||
|
||||
# Try JSON first
|
||||
if path.suffix == ".json":
|
||||
return json.loads(content)
|
||||
|
||||
# Parse Markdown format
|
||||
cmd_desc = {}
|
||||
|
||||
# Extract fields using regex patterns
|
||||
patterns = {
|
||||
"name": r"#\s*Name:\s*(.+)",
|
||||
"version": r"#\s*Version:\s*(.+)",
|
||||
"description": r"#\s*Description:\s*(.+)",
|
||||
"execution_type": r"#\s*Execution\s*Type:\s*(.+)",
|
||||
"target": r"#\s*Target:\s*(.+)",
|
||||
"status": r"#\s*Status:\s*(.+)",
|
||||
}
|
||||
|
||||
for field, pattern in patterns.items():
|
||||
match = re.search(pattern, content, re.IGNORECASE)
|
||||
if match:
|
||||
value = match.group(1).strip()
|
||||
cmd_desc[field] = value
|
||||
|
||||
# Parse parameters section
|
||||
params_section = re.search(
|
||||
r"#\s*Parameters:\s*\n(.*?)(?=\n#|\Z)",
|
||||
content,
|
||||
re.DOTALL | re.IGNORECASE
|
||||
)
|
||||
|
||||
if params_section:
|
||||
cmd_desc["parameters"] = self._parse_parameters(params_section.group(1))
|
||||
|
||||
# Parse tags
|
||||
tags_match = re.search(r"#\s*Tags:\s*(.+)", content, re.IGNORECASE)
|
||||
if tags_match:
|
||||
tags_str = tags_match.group(1).strip()
|
||||
# Parse comma-separated or bracket-enclosed tags
|
||||
if tags_str.startswith("[") and tags_str.endswith("]"):
|
||||
tags_str = tags_str[1:-1]
|
||||
cmd_desc["tags"] = [t.strip() for t in tags_str.split(",")]
|
||||
|
||||
# Parse execution context
|
||||
context_section = re.search(
|
||||
r"#\s*Execution\s*Context:\s*\n(.*?)(?=\n#|\Z)",
|
||||
content,
|
||||
re.DOTALL | re.IGNORECASE
|
||||
)
|
||||
if context_section:
|
||||
cmd_desc["execution_context"] = self._parse_context(context_section.group(1))
|
||||
|
||||
# Parse artifact metadata sections
|
||||
produces_section = re.search(
|
||||
r"#\s*Produces\s*Artifacts:\s*\n(.*?)(?=\n#|\Z)",
|
||||
content,
|
||||
re.DOTALL | re.IGNORECASE
|
||||
)
|
||||
if produces_section:
|
||||
cmd_desc["artifact_produces"] = self._parse_artifact_list(produces_section.group(1))
|
||||
|
||||
consumes_section = re.search(
|
||||
r"#\s*Consumes\s*Artifacts:\s*\n(.*?)(?=\n#|\Z)",
|
||||
content,
|
||||
re.DOTALL | re.IGNORECASE
|
||||
)
|
||||
if consumes_section:
|
||||
cmd_desc["artifact_consumes"] = self._parse_artifact_list(consumes_section.group(1))
|
||||
|
||||
# Validate required fields
|
||||
required = ["name", "description", "execution_type", "target"]
|
||||
missing = [f for f in required if f not in cmd_desc]
|
||||
if missing:
|
||||
raise ValueError(f"Missing required fields: {', '.join(missing)}")
|
||||
|
||||
# Validate execution type
|
||||
if cmd_desc["execution_type"].lower() not in self.VALID_EXECUTION_TYPES:
|
||||
raise ValueError(
|
||||
f"Invalid execution type: {cmd_desc['execution_type']}. "
|
||||
f"Must be one of: {', '.join(self.VALID_EXECUTION_TYPES)}"
|
||||
)
|
||||
|
||||
# Ensure command name starts with /
|
||||
if not cmd_desc["name"].startswith("/"):
|
||||
cmd_desc["name"] = "/" + cmd_desc["name"]
|
||||
|
||||
# Set defaults
|
||||
if "version" not in cmd_desc:
|
||||
cmd_desc["version"] = "0.1.0"
|
||||
if "status" not in cmd_desc:
|
||||
cmd_desc["status"] = "draft"
|
||||
if "parameters" not in cmd_desc:
|
||||
cmd_desc["parameters"] = []
|
||||
|
||||
return cmd_desc
|
||||
|
||||
def _parse_parameters(self, params_text: str) -> List[Dict[str, Any]]:
|
||||
"""Parse parameters from markdown text"""
|
||||
parameters = []
|
||||
|
||||
# Match parameter blocks
|
||||
# Format: - name: type (required/optional) - description
|
||||
param_pattern = r"-\s+(\w+):\s+(\w+)(?:\s+\(([^)]+)\))?\s+-\s+(.+?)(?=\n-|\n#|\Z)"
|
||||
matches = re.finditer(param_pattern, params_text, re.DOTALL)
|
||||
|
||||
for match in matches:
|
||||
name, param_type, modifiers, description = match.groups()
|
||||
|
||||
param = {
|
||||
"name": name.strip(),
|
||||
"type": param_type.strip(),
|
||||
"description": description.strip()
|
||||
}
|
||||
|
||||
# Parse modifiers (required, optional, default=value)
|
||||
if modifiers:
|
||||
modifiers = modifiers.lower()
|
||||
param["required"] = "required" in modifiers
|
||||
|
||||
# Extract default value
|
||||
default_match = re.search(r"default[=:]\s*([^,\s]+)", modifiers)
|
||||
if default_match:
|
||||
default_val = default_match.group(1)
|
||||
# Convert types
|
||||
if param_type == "integer":
|
||||
default_val = int(default_val)
|
||||
elif param_type == "boolean":
|
||||
default_val = default_val.lower() in ("true", "yes", "1")
|
||||
param["default"] = default_val
|
||||
|
||||
# Extract enum values
|
||||
values_match = re.search(r"values[=:]\s*\[([^\]]+)\]", modifiers)
|
||||
if values_match:
|
||||
param["values"] = [v.strip() for v in values_match.group(1).split(",")]
|
||||
|
||||
parameters.append(param)
|
||||
|
||||
return parameters
|
||||
|
||||
def _parse_context(self, context_text: str) -> Dict[str, Any]:
|
||||
"""Parse execution context from markdown text"""
|
||||
context = {}
|
||||
|
||||
# Simple key: value parsing
|
||||
for line in context_text.split("\n"):
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
|
||||
match = re.match(r"-\s*(\w+):\s*(.+)", line)
|
||||
if match:
|
||||
key, value = match.groups()
|
||||
# Try to parse as JSON for complex values
|
||||
try:
|
||||
context[key] = json.loads(value)
|
||||
except (json.JSONDecodeError, ValueError):
|
||||
context[key] = value.strip()
|
||||
|
||||
return context
|
||||
|
||||
def _parse_artifact_list(self, artifact_text: str) -> List[str]:
|
||||
"""Parse artifact list from markdown text"""
|
||||
artifacts = []
|
||||
|
||||
for line in artifact_text.split("\n"):
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
|
||||
# Match lines starting with - or *
|
||||
match = re.match(r"[-*]\s*`?([a-z0-9-]+)`?", line)
|
||||
if match:
|
||||
artifacts.append(match.group(1))
|
||||
|
||||
return artifacts
|
||||
|
||||
def analyze_complexity(self, cmd_desc: Dict[str, Any], full_content: str = "") -> Dict[str, Any]:
|
||||
"""
|
||||
Analyze command complexity and recommend pattern
|
||||
|
||||
Args:
|
||||
cmd_desc: Parsed command description
|
||||
full_content: Full description file content for analysis
|
||||
|
||||
Returns:
|
||||
Dict with complexity analysis and pattern recommendation
|
||||
"""
|
||||
analysis = {
|
||||
"step_count": 0,
|
||||
"complexity": "low",
|
||||
"autonomy_level": "none",
|
||||
"reusability": "low",
|
||||
"recommended_pattern": "COMMAND_ONLY",
|
||||
"should_create_skill": False,
|
||||
"reasoning": []
|
||||
}
|
||||
|
||||
# Count steps from description
|
||||
# Look for numbered lists, bullet points, or explicit step mentions
|
||||
step_patterns = [
|
||||
r"^\s*\d+\.\s+", # Numbered lists
|
||||
r"^\s*[-*]\s+", # Bullet points
|
||||
r"\bstep\s+\d+\b", # Explicit "step N"
|
||||
]
|
||||
|
||||
lines = full_content.split("\n")
|
||||
step_count = 0
|
||||
for line in lines:
|
||||
for pattern in step_patterns:
|
||||
if re.search(pattern, line, re.IGNORECASE):
|
||||
step_count += 1
|
||||
break
|
||||
|
||||
analysis["step_count"] = step_count
|
||||
|
||||
# Analyze content for keywords
|
||||
content_lower = full_content.lower()
|
||||
desc_lower = cmd_desc.get("description", "").lower()
|
||||
combined = content_lower + " " + desc_lower
|
||||
|
||||
# Check autonomy keywords
|
||||
autonomy_matches = [kw for kw in self.AUTONOMY_KEYWORDS if kw in combined]
|
||||
if len(autonomy_matches) >= 3:
|
||||
analysis["autonomy_level"] = "high"
|
||||
elif len(autonomy_matches) >= 1:
|
||||
analysis["autonomy_level"] = "medium"
|
||||
else:
|
||||
analysis["autonomy_level"] = "low"
|
||||
|
||||
# Check reusability keywords
|
||||
reusability_matches = [kw for kw in self.REUSABILITY_KEYWORDS if kw in combined]
|
||||
if len(reusability_matches) >= 2:
|
||||
analysis["reusability"] = "high"
|
||||
elif len(reusability_matches) >= 1:
|
||||
analysis["reusability"] = "medium"
|
||||
|
||||
# Determine complexity
|
||||
if step_count >= 10:
|
||||
analysis["complexity"] = "high"
|
||||
elif step_count >= 4:
|
||||
analysis["complexity"] = "medium"
|
||||
else:
|
||||
analysis["complexity"] = "low"
|
||||
|
||||
# Estimate lines of logic (rough heuristic)
|
||||
instruction_lines = sum(1 for line in lines if line.strip() and not line.strip().startswith("#"))
|
||||
if instruction_lines > 50:
|
||||
analysis["complexity"] = "high"
|
||||
|
||||
# Decide pattern based on decision tree
|
||||
if step_count >= 10 or analysis["complexity"] == "high":
|
||||
analysis["recommended_pattern"] = "SKILL_AND_COMMAND"
|
||||
analysis["should_create_skill"] = True
|
||||
analysis["reasoning"].append(f"High complexity: {step_count} steps detected")
|
||||
|
||||
elif analysis["autonomy_level"] == "high":
|
||||
analysis["recommended_pattern"] = "SKILL_AND_COMMAND"
|
||||
analysis["should_create_skill"] = True
|
||||
analysis["reasoning"].append(f"High autonomy: matched keywords {autonomy_matches[:3]}")
|
||||
|
||||
elif analysis["reusability"] == "high":
|
||||
if step_count <= 3:
|
||||
analysis["recommended_pattern"] = "SKILL_ONLY"
|
||||
analysis["should_create_skill"] = True
|
||||
analysis["reasoning"].append("High reusability but low complexity: create skill only")
|
||||
else:
|
||||
analysis["recommended_pattern"] = "SKILL_AND_COMMAND"
|
||||
analysis["should_create_skill"] = True
|
||||
analysis["reasoning"].append(f"High reusability with {step_count} steps: create both")
|
||||
|
||||
elif step_count >= 4 and step_count <= 9:
|
||||
# Medium complexity - could go either way
|
||||
if analysis["autonomy_level"] == "medium":
|
||||
analysis["recommended_pattern"] = "SKILL_AND_COMMAND"
|
||||
analysis["should_create_skill"] = True
|
||||
analysis["reasoning"].append(f"Medium complexity ({step_count} steps) with some autonomy needs")
|
||||
else:
|
||||
analysis["recommended_pattern"] = "COMMAND_ONLY"
|
||||
analysis["reasoning"].append(f"Medium complexity ({step_count} steps) but simple logic: inline is fine")
|
||||
|
||||
else:
|
||||
# Low complexity - command only
|
||||
analysis["recommended_pattern"] = "COMMAND_ONLY"
|
||||
analysis["reasoning"].append(f"Low complexity ({step_count} steps): inline orchestration is sufficient")
|
||||
|
||||
# Check if execution type already specifies skill
|
||||
if cmd_desc.get("execution_type") == "skill":
|
||||
analysis["recommended_pattern"] = "SKILL_AND_COMMAND"
|
||||
analysis["should_create_skill"] = True
|
||||
analysis["reasoning"].append("Execution type explicitly set to 'skill'")
|
||||
|
||||
return analysis
|
||||
|
||||
def generate_command_manifest(self, cmd_desc: Dict[str, Any]) -> str:
|
||||
"""
|
||||
Generate command manifest YAML
|
||||
|
||||
Args:
|
||||
cmd_desc: Parsed command description
|
||||
|
||||
Returns:
|
||||
YAML string
|
||||
"""
|
||||
manifest = {
|
||||
"name": cmd_desc["name"],
|
||||
"version": cmd_desc["version"],
|
||||
"description": cmd_desc["description"]
|
||||
}
|
||||
|
||||
# Add parameters if present
|
||||
if cmd_desc.get("parameters"):
|
||||
manifest["parameters"] = cmd_desc["parameters"]
|
||||
|
||||
# Add execution configuration
|
||||
execution = {
|
||||
"type": cmd_desc["execution_type"],
|
||||
"target": cmd_desc["target"]
|
||||
}
|
||||
|
||||
if cmd_desc.get("execution_context"):
|
||||
execution["context"] = cmd_desc["execution_context"]
|
||||
|
||||
manifest["execution"] = execution
|
||||
|
||||
# Add status
|
||||
manifest["status"] = cmd_desc.get("status", "draft")
|
||||
|
||||
# Add tags if present
|
||||
if cmd_desc.get("tags"):
|
||||
manifest["tags"] = cmd_desc["tags"]
|
||||
|
||||
# Add artifact metadata if present
|
||||
if cmd_desc.get("artifact_produces") or cmd_desc.get("artifact_consumes"):
|
||||
artifact_metadata = {}
|
||||
|
||||
if cmd_desc.get("artifact_produces"):
|
||||
artifact_metadata["produces"] = [
|
||||
{"type": art_type} for art_type in cmd_desc["artifact_produces"]
|
||||
]
|
||||
|
||||
if cmd_desc.get("artifact_consumes"):
|
||||
artifact_metadata["consumes"] = [
|
||||
{"type": art_type, "required": True}
|
||||
for art_type in cmd_desc["artifact_consumes"]
|
||||
]
|
||||
|
||||
manifest["artifact_metadata"] = artifact_metadata
|
||||
|
||||
return yaml.dump(manifest, default_flow_style=False, sort_keys=False)
|
||||
|
||||
def validate_artifacts(self, cmd_desc: Dict[str, Any]) -> List[str]:
|
||||
"""
|
||||
Validate that artifact types exist in the known registry.
|
||||
|
||||
Args:
|
||||
cmd_desc: Parsed command description
|
||||
|
||||
Returns:
|
||||
List of warning messages
|
||||
"""
|
||||
warnings = []
|
||||
|
||||
if not ARTIFACT_VALIDATION_AVAILABLE:
|
||||
warnings.append(
|
||||
"Artifact validation skipped: artifact.define skill not available"
|
||||
)
|
||||
return warnings
|
||||
|
||||
# Validate produced artifacts
|
||||
for artifact_type in cmd_desc.get("artifact_produces", []):
|
||||
is_valid, warning = validate_artifact_type(artifact_type)
|
||||
if not is_valid and warning:
|
||||
warnings.append(f"Produces: {warning}")
|
||||
|
||||
# Validate consumed artifacts
|
||||
for artifact_type in cmd_desc.get("artifact_consumes", []):
|
||||
is_valid, warning = validate_artifact_type(artifact_type)
|
||||
if not is_valid and warning:
|
||||
warnings.append(f"Consumes: {warning}")
|
||||
|
||||
return warnings
|
||||
|
||||
def validate_target(self, cmd_desc: Dict[str, Any]) -> List[str]:
|
||||
"""
|
||||
Validate that the target skill or agent exists.
|
||||
|
||||
Args:
|
||||
cmd_desc: Parsed command description
|
||||
|
||||
Returns:
|
||||
List of warning messages
|
||||
"""
|
||||
warnings = []
|
||||
execution_type = cmd_desc.get("execution_type", "").lower()
|
||||
target = cmd_desc.get("target", "")
|
||||
|
||||
if execution_type == "skill":
|
||||
# Check if skill exists in registry or skills directory
|
||||
skill_registry = self.base_dir / "registry" / "skills.json"
|
||||
skill_dir = self.base_dir / "skills" / target.replace(".", "/")
|
||||
|
||||
skill_exists = False
|
||||
if skill_registry.exists():
|
||||
try:
|
||||
with open(skill_registry) as f:
|
||||
registry = json.load(f)
|
||||
if target in registry.get("skills", {}):
|
||||
skill_exists = True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not skill_exists and not skill_dir.exists():
|
||||
warnings.append(
|
||||
f"Target skill '{target}' not found in registry or skills directory. "
|
||||
f"You may need to create it using meta.skill first."
|
||||
)
|
||||
|
||||
elif execution_type == "agent":
|
||||
# Check if agent exists in agents directory
|
||||
agent_dir = self.base_dir / "agents" / target
|
||||
if not agent_dir.exists():
|
||||
warnings.append(
|
||||
f"Target agent '{target}' not found in agents directory. "
|
||||
f"You may need to create it using meta.agent first."
|
||||
)
|
||||
|
||||
return warnings
|
||||
|
||||
def create_command(
|
||||
self,
|
||||
description_path: str,
|
||||
requirement: Optional[RequirementInfo] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Create command manifest from description file
|
||||
|
||||
Args:
|
||||
description_path: Path to description file
|
||||
requirement: Optional requirement information for traceability
|
||||
|
||||
Returns:
|
||||
Dict with creation results
|
||||
"""
|
||||
try:
|
||||
print(f"🎯 meta.command - Creating command from {description_path}\n")
|
||||
|
||||
# Read full content for analysis
|
||||
with open(description_path, 'r') as f:
|
||||
full_content = f.read()
|
||||
|
||||
# Parse description
|
||||
cmd_desc = self.parse_description(description_path)
|
||||
|
||||
# Validate artifacts
|
||||
artifact_warnings = self.validate_artifacts(cmd_desc)
|
||||
if artifact_warnings:
|
||||
print("\n⚠️ Artifact Validation Warnings:")
|
||||
for warning in artifact_warnings:
|
||||
print(f" {warning}")
|
||||
print()
|
||||
|
||||
# Validate target skill/agent
|
||||
target_warnings = self.validate_target(cmd_desc)
|
||||
if target_warnings:
|
||||
print("\n⚠️ Target Validation Warnings:")
|
||||
for warning in target_warnings:
|
||||
print(f" {warning}")
|
||||
print()
|
||||
|
||||
# Analyze complexity and recommend pattern
|
||||
analysis = self.analyze_complexity(cmd_desc, full_content)
|
||||
|
||||
# Display analysis
|
||||
print(f"📊 Complexity Analysis:")
|
||||
print(f" Steps detected: {analysis['step_count']}")
|
||||
print(f" Complexity: {analysis['complexity']}")
|
||||
print(f" Autonomy level: {analysis['autonomy_level']}")
|
||||
print(f" Reusability: {analysis['reusability']}")
|
||||
print(f"\n💡 Recommended Pattern: {analysis['recommended_pattern']}")
|
||||
for reason in analysis['reasoning']:
|
||||
print(f" • {reason}")
|
||||
print()
|
||||
|
||||
# Generate manifest YAML
|
||||
manifest_yaml = self.generate_command_manifest(cmd_desc)
|
||||
|
||||
# Ensure commands directory exists
|
||||
self.commands_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Determine output filename
|
||||
# Remove leading / and replace spaces/special chars with hyphens
|
||||
filename = cmd_desc["name"].lstrip("/").replace(" ", "-").lower()
|
||||
filename = re.sub(r"[^a-z0-9-]", "", filename)
|
||||
manifest_file = self.commands_dir / f"{filename}.yaml"
|
||||
|
||||
# Write manifest file
|
||||
manifest_file.write_text(manifest_yaml)
|
||||
|
||||
print(f"✨ Command '{cmd_desc['name']}' created successfully!\n")
|
||||
print(f"📄 Created file:")
|
||||
print(f" - {manifest_file}\n")
|
||||
print(f"✅ Command manifest is ready for registration")
|
||||
print(f" Name: {cmd_desc['name']}")
|
||||
print(f" Execution: {cmd_desc['execution_type']} → {cmd_desc['target']}")
|
||||
print(f" Status: {cmd_desc.get('status', 'draft')}\n")
|
||||
|
||||
# Display skill creation recommendation if needed
|
||||
if analysis['should_create_skill']:
|
||||
print(f"⚠️ RECOMMENDATION: Create the skill first!")
|
||||
print(f" Pattern: {analysis['recommended_pattern']}")
|
||||
print(f"\n This command delegates to a skill ({cmd_desc['target']}),")
|
||||
print(f" but that skill may not exist yet.\n")
|
||||
print(f" Suggested workflow:")
|
||||
print(f" 1. Create skill: python3 agents/meta.skill/meta_skill.py <skill-description.md>")
|
||||
print(f" - Skill should implement: {cmd_desc['target']}")
|
||||
print(f" - Include all complex logic from the command description")
|
||||
print(f" 2. Test skill: python3 skills/{cmd_desc['target'].replace('.', '/')}/{cmd_desc['target'].replace('.', '_')}.py")
|
||||
print(f" 3. Review this command manifest: cat {manifest_file}")
|
||||
print(f" 4. Register command: python3 skills/command.define/command_define.py {manifest_file}")
|
||||
print(f" 5. Verify in registry: cat registry/commands.json")
|
||||
print(f"\n See docs/SKILL_COMMAND_DECISION_TREE.md for pattern details\n")
|
||||
else:
|
||||
print(f"📝 Next steps:")
|
||||
print(f" 1. Review the manifest: cat {manifest_file}")
|
||||
print(f" 2. Register command: python3 skills/command.define/command_define.py {manifest_file}")
|
||||
print(f" 3. Verify in registry: cat registry/commands.json")
|
||||
|
||||
result = {
|
||||
"ok": True,
|
||||
"status": "success",
|
||||
"command_name": cmd_desc["name"],
|
||||
"manifest_file": str(manifest_file),
|
||||
"complexity_analysis": analysis,
|
||||
"artifact_warnings": artifact_warnings,
|
||||
"target_warnings": target_warnings
|
||||
}
|
||||
|
||||
# Log traceability if requirement provided
|
||||
trace_id = None
|
||||
if requirement:
|
||||
try:
|
||||
tracer = get_tracer()
|
||||
|
||||
# Create component ID from command name
|
||||
component_id = f"command.{filename.replace('-', '_')}"
|
||||
|
||||
trace_id = tracer.log_creation(
|
||||
component_id=component_id,
|
||||
component_name=cmd_desc["name"],
|
||||
component_type="command",
|
||||
component_version=cmd_desc["version"],
|
||||
component_file_path=str(manifest_file),
|
||||
input_source_path=description_path,
|
||||
created_by_tool="meta.command",
|
||||
created_by_version="0.1.0",
|
||||
requirement=requirement,
|
||||
tags=["command", "auto-generated"] + cmd_desc.get("tags", []),
|
||||
project="Betty Framework"
|
||||
)
|
||||
|
||||
# Log validation check
|
||||
validation_details = {
|
||||
"checks_performed": [
|
||||
{"name": "command_structure", "status": "passed"},
|
||||
{"name": "execution_type_validation", "status": "passed",
|
||||
"message": f"Valid execution type: {cmd_desc['execution_type']}"},
|
||||
{"name": "name_validation", "status": "passed",
|
||||
"message": f"Command name follows convention: {cmd_desc['name']}"}
|
||||
]
|
||||
}
|
||||
|
||||
# Check parameters
|
||||
if cmd_desc.get("parameters"):
|
||||
validation_details["checks_performed"].append({
|
||||
"name": "parameters_validation",
|
||||
"status": "passed",
|
||||
"message": f"Validated {len(cmd_desc['parameters'])} parameters"
|
||||
})
|
||||
|
||||
tracer.log_verification(
|
||||
component_id=component_id,
|
||||
check_type="validation",
|
||||
tool="meta.command",
|
||||
result="passed",
|
||||
details=validation_details
|
||||
)
|
||||
|
||||
result["trace_id"] = trace_id
|
||||
result["component_id"] = component_id
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ Warning: Could not log traceability: {e}")
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error creating command: {e}")
|
||||
logger.error(f"Error creating command: {e}", exc_info=True)
|
||||
return {
|
||||
"ok": False,
|
||||
"status": "failed",
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
"""CLI entry point"""
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="meta.command - Create command manifests from descriptions"
|
||||
)
|
||||
parser.add_argument(
|
||||
"description",
|
||||
help="Path to command description file (.md or .json)"
|
||||
)
|
||||
|
||||
# Traceability arguments
|
||||
parser.add_argument(
|
||||
"--requirement-id",
|
||||
help="Requirement identifier (e.g., REQ-2025-001)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--requirement-description",
|
||||
help="What this command accomplishes"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--requirement-source",
|
||||
help="Source document"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--issue-id",
|
||||
help="Issue tracking ID (e.g., JIRA-123)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--requested-by",
|
||||
help="Who requested this"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--rationale",
|
||||
help="Why this is needed"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Create requirement info if provided
|
||||
requirement = None
|
||||
if args.requirement_id and args.requirement_description:
|
||||
requirement = RequirementInfo(
|
||||
id=args.requirement_id,
|
||||
description=args.requirement_description,
|
||||
source=args.requirement_source,
|
||||
issue_id=args.issue_id,
|
||||
requested_by=args.requested_by,
|
||||
rationale=args.rationale
|
||||
)
|
||||
|
||||
creator = CommandCreator()
|
||||
result = creator.create_command(args.description, requirement=requirement)
|
||||
|
||||
# Display traceability info if available
|
||||
if result.get("trace_id"):
|
||||
print(f"\n📝 Traceability: {result['trace_id']}")
|
||||
print(f" View trace: python3 betty/trace_cli.py show {result['component_id']}")
|
||||
|
||||
sys.exit(0 if result.get("ok") else 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user