Skip to content

Commit f6b8f4c

Browse files
Add SKILL.md and execution script.
1 parent dbf4f63 commit f6b8f4c

2 files changed

Lines changed: 129 additions & 0 deletions

File tree

SKILL.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
name: git-api-client
3+
description: Offline-first, Git-native API client for executing and testing API requests stored as Markdown files. Use when you need to interact with APIs, document endpoints, or verify contract compliance without using heavy external tools like Postman.
4+
---
5+
6+
# Git-Native API Client
7+
8+
This skill allows you to manage API requests as repository-native Markdown files. This ensures that API documentation and test cases live alongside the code, are version-controlled, and can be reviewed via standard Pull Request workflows.
9+
10+
## Workflow
11+
12+
1. **Identify or Create Request File**: API requests are stored in `.md` files (e.g., `docs/api/get-users.md`).
13+
2. **Define Request Structure**:
14+
- Use YAML frontmatter for `method`, `url`, and `headers`.
15+
- Place the request body (JSON, text, etc.) below the frontmatter.
16+
- Support for environment variables using `{{VAR_NAME}}` syntax.
17+
3. **Execute Request**: Run the `execute_api_request.py` script to perform the call and see the response.
18+
19+
## File Format Example
20+
21+
```markdown
22+
---
23+
method: POST
24+
url: https://jsonplaceholder.typicode.com/posts
25+
headers:
26+
Content-Type: application/json; charset=UTF-8
27+
---
28+
29+
{
30+
"title": "foo",
31+
"body": "bar",
32+
"userId": 1
33+
}
34+
```
35+
36+
## Tools & Scripts
37+
38+
- **`scripts/execute_api_request.py`**: The primary execution engine.
39+
- **Usage**: `python scripts/execute_api_request.py <path_to_file>`
40+
- **Environment Variables**: The script automatically substitutes `{{ENV_VAR}}` with values from the current shell environment.
41+
42+
## Best Practices
43+
44+
- **Repository Structure**: Store API requests in a dedicated folder like `docs/api/` or `tests/integration/api/`.
45+
- **Security**: Never hardcode secrets in the Markdown files. Use `{{API_TOKEN}}` and ensure the token is set in the environment or a `.env` file (which should be in `.gitignore`).
46+
- **Responses**: When verifying an API, consider appending the response to a sibling `.response.md` file for record-keeping during development.

scripts/execute_api_request.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import sys
2+
import os
3+
import json
4+
import yaml
5+
import subprocess
6+
import re
7+
8+
def execute_request(file_path):
9+
if not os.path.exists(file_path):
10+
print(f"Error: File {file_path} not found.")
11+
return
12+
13+
with open(file_path, 'r') as f:
14+
content = f.read()
15+
16+
# Simple regex to split frontmatter and body
17+
match = re.match(r'^---\s*\n(.*?)\n---\s*\n(.*)', content, re.DOTALL)
18+
if not match:
19+
print("Error: Invalid Markdown format. Missing frontmatter.")
20+
return
21+
22+
frontmatter_raw = match.group(1)
23+
body = match.group(2).strip()
24+
25+
try:
26+
config = yaml.safe_load(frontmatter_raw)
27+
except yaml.YAMLError as e:
28+
print(f"Error: YAML parsing failed: {e}")
29+
return
30+
31+
method = config.get('method', 'GET').upper()
32+
url = config.get('url')
33+
headers = config.get('headers', {})
34+
35+
if not url:
36+
print("Error: 'url' is required in frontmatter.")
37+
return
38+
39+
# Replace environment variables in URL, headers, and body
40+
def replace_env(text):
41+
if not isinstance(text, str): return text
42+
return re.sub(r'\{\{(.*?)\}\}', lambda m: os.environ.get(m.group(1), m.group(0)), text)
43+
44+
url = replace_env(url)
45+
for k, v in headers.items():
46+
headers[k] = replace_env(v)
47+
body = replace_env(body)
48+
49+
# Build curl command
50+
curl_cmd = ['curl', '-s', '-i', '-X', method, url]
51+
52+
for k, v in headers.items():
53+
curl_cmd.extend(['-H', f"{k}: {v}"])
54+
55+
if body and method in ['POST', 'PUT', 'PATCH']:
56+
curl_cmd.extend(['-d', body])
57+
58+
try:
59+
result = subprocess.run(curl_cmd, capture_output=True, text=True, timeout=30)
60+
61+
output = result.stdout
62+
if result.stderr:
63+
output += f"\n--- Stderr ---\n{result.stderr}"
64+
65+
# Truncate if too long
66+
max_lines = 100
67+
lines = output.splitlines()
68+
if len(lines) > max_lines:
69+
print("\n".join(lines[:max_lines]))
70+
print(f"\n... [TRUNCATED {len(lines) - max_lines} LINES] ...")
71+
else:
72+
print(output)
73+
74+
except subprocess.TimeoutExpired:
75+
print("Error: Request timed out.")
76+
except Exception as e:
77+
print(f"Error: Execution failed: {e}")
78+
79+
if __name__ == "__main__":
80+
if len(sys.argv) < 2:
81+
print("Usage: python execute_api_request.py <path_to_markdown_file>")
82+
else:
83+
execute_request(sys.argv[1])

0 commit comments

Comments
 (0)