cuga-harness-kit 0.1.0__py3-none-any.whl
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.
- cuga_harness_kit/__init__.py +0 -0
- cuga_harness_kit/cli.py +136 -0
- cuga_harness_kit/render.py +57 -0
- cuga_harness_kit/skills/author-policy/SKILL.md +179 -0
- cuga_harness_kit/skills/build-agent/SKILL.md +76 -0
- cuga_harness_kit/skills/build-cuga-skill/SKILL.md +62 -0
- cuga_harness_kit/skills/build-tool/SKILL.md +45 -0
- cuga_harness_kit/skills/debug-trajectory/SKILL.md +45 -0
- cuga_harness_kit/skills/getting-started/SKILL.md +31 -0
- cuga_harness_kit/skills/install-and-launch/SKILL.md +49 -0
- cuga_harness_kit/skills/knowledge-rag/SKILL.md +67 -0
- cuga_harness_kit-0.1.0.dist-info/METADATA +65 -0
- cuga_harness_kit-0.1.0.dist-info/RECORD +16 -0
- cuga_harness_kit-0.1.0.dist-info/WHEEL +4 -0
- cuga_harness_kit-0.1.0.dist-info/entry_points.txt +2 -0
- cuga_harness_kit-0.1.0.dist-info/licenses/LICENSE +202 -0
|
File without changes
|
cuga_harness_kit/cli.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""`cuga-harness-kit init` — scaffold cuga skill files for Claude Code, Cursor, Codex, and Bob."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from cuga_harness_kit.render import render_agents_section, render_bob, render_mdc
|
|
9
|
+
|
|
10
|
+
SKILLS_DIR = Path(__file__).parent / "skills"
|
|
11
|
+
ALL_TARGETS = ("claude", "cursor", "codex", "bob")
|
|
12
|
+
AGENTS_START = "<!-- cuga-harness-kit:start -->"
|
|
13
|
+
AGENTS_END = "<!-- cuga-harness-kit:end -->"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _skill_dirs() -> list[Path]:
|
|
17
|
+
return sorted(p for p in SKILLS_DIR.iterdir() if (p / "SKILL.md").is_file())
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _detect_cuga_message(cwd: Path) -> str:
|
|
21
|
+
pyproject = cwd / "pyproject.toml"
|
|
22
|
+
looks_like_cuga_checkout = (cwd / "src" / "cuga").is_dir() or (
|
|
23
|
+
pyproject.is_file() and 'name = "cuga"' in pyproject.read_text(errors="ignore")
|
|
24
|
+
)
|
|
25
|
+
if looks_like_cuga_checkout:
|
|
26
|
+
return "Detected an existing cuga source checkout — skills will reference your local `cuga` package."
|
|
27
|
+
return "No existing cuga installation detected — skills will guide you through `pip install cuga` from scratch."
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _write_file(path: Path, content: str, *, force: bool, dry_run: bool, written: list[str], skipped: list[str]) -> None:
|
|
31
|
+
if path.exists():
|
|
32
|
+
if path.read_text() == content:
|
|
33
|
+
skipped.append(f"{path} (already up to date)")
|
|
34
|
+
return
|
|
35
|
+
if not force:
|
|
36
|
+
skipped.append(f"{path} (exists, differs — pass --force to overwrite)")
|
|
37
|
+
return
|
|
38
|
+
if dry_run:
|
|
39
|
+
written.append(f"{path} (dry-run)")
|
|
40
|
+
return
|
|
41
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
42
|
+
path.write_text(content)
|
|
43
|
+
written.append(str(path))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _write_agents_md(cwd: Path, skill_paths: list[Path], *, dry_run: bool, written: list[str], skipped: list[str]) -> None:
|
|
47
|
+
block_body = "\n".join(render_agents_section(p) for p in skill_paths)
|
|
48
|
+
block = f"{AGENTS_START}\n\n{block_body}{AGENTS_END}\n"
|
|
49
|
+
|
|
50
|
+
path = cwd / "AGENTS.md"
|
|
51
|
+
if path.exists():
|
|
52
|
+
existing = path.read_text()
|
|
53
|
+
if AGENTS_START in existing and AGENTS_END in existing:
|
|
54
|
+
pre, _, rest = existing.partition(AGENTS_START)
|
|
55
|
+
_, _, post = rest.partition(AGENTS_END)
|
|
56
|
+
new_content = f"{pre}{block}{post}"
|
|
57
|
+
else:
|
|
58
|
+
new_content = existing.rstrip("\n") + "\n\n" + block
|
|
59
|
+
else:
|
|
60
|
+
new_content = block
|
|
61
|
+
|
|
62
|
+
if path.exists() and path.read_text() == new_content:
|
|
63
|
+
skipped.append(f"{path} (already up to date)")
|
|
64
|
+
return
|
|
65
|
+
if dry_run:
|
|
66
|
+
written.append(f"{path} (dry-run)")
|
|
67
|
+
return
|
|
68
|
+
path.write_text(new_content)
|
|
69
|
+
written.append(str(path))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def init(targets: list[str], *, force: bool, dry_run: bool, cwd: Path | None = None) -> None:
|
|
73
|
+
cwd = cwd or Path.cwd()
|
|
74
|
+
skill_paths = _skill_dirs()
|
|
75
|
+
written: list[str] = []
|
|
76
|
+
skipped: list[str] = []
|
|
77
|
+
|
|
78
|
+
print(_detect_cuga_message(cwd))
|
|
79
|
+
|
|
80
|
+
if "claude" in targets:
|
|
81
|
+
for skill_path in skill_paths:
|
|
82
|
+
src = skill_path / "SKILL.md"
|
|
83
|
+
dest = cwd / ".claude" / "skills" / skill_path.name / "SKILL.md"
|
|
84
|
+
_write_file(dest, src.read_text(), force=force, dry_run=dry_run, written=written, skipped=skipped)
|
|
85
|
+
|
|
86
|
+
if "cursor" in targets:
|
|
87
|
+
for skill_path in skill_paths:
|
|
88
|
+
src = skill_path / "SKILL.md"
|
|
89
|
+
dest = cwd / ".cursor" / "rules" / f"cuga-{skill_path.name}.mdc"
|
|
90
|
+
_write_file(dest, render_mdc(src), force=force, dry_run=dry_run, written=written, skipped=skipped)
|
|
91
|
+
|
|
92
|
+
if "codex" in targets:
|
|
93
|
+
_write_agents_md(cwd, [p / "SKILL.md" for p in skill_paths], dry_run=dry_run, written=written, skipped=skipped)
|
|
94
|
+
|
|
95
|
+
if "bob" in targets:
|
|
96
|
+
for skill_path in skill_paths:
|
|
97
|
+
src = skill_path / "SKILL.md"
|
|
98
|
+
dest = cwd / ".bob" / "rules" / f"cuga-{skill_path.name}.md"
|
|
99
|
+
_write_file(dest, render_bob(src), force=force, dry_run=dry_run, written=written, skipped=skipped)
|
|
100
|
+
|
|
101
|
+
print(f"\n{len(written)} file(s) written, {len(skipped)} skipped.")
|
|
102
|
+
for line in written:
|
|
103
|
+
print(f" wrote: {line}")
|
|
104
|
+
for line in skipped:
|
|
105
|
+
print(f" skipped: {line}")
|
|
106
|
+
print(
|
|
107
|
+
'\nNext step: open this folder in Claude Code, Cursor, Codex, or Bob, and try asking '
|
|
108
|
+
'"help me build a cuga tool" or "how do I launch cuga".'
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def main(argv: list[str] | None = None) -> None:
|
|
113
|
+
parser = argparse.ArgumentParser(prog="cuga-harness-kit")
|
|
114
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
115
|
+
|
|
116
|
+
init_parser = subparsers.add_parser("init", help="scaffold cuga skill files into the current directory")
|
|
117
|
+
init_parser.add_argument(
|
|
118
|
+
"--targets",
|
|
119
|
+
default=",".join(ALL_TARGETS),
|
|
120
|
+
help=f"comma-separated subset of {ALL_TARGETS} (default: all)",
|
|
121
|
+
)
|
|
122
|
+
init_parser.add_argument("--force", action="store_true", help="overwrite files that already exist and differ")
|
|
123
|
+
init_parser.add_argument("--dry-run", action="store_true", help="print what would be written, write nothing")
|
|
124
|
+
|
|
125
|
+
args = parser.parse_args(argv)
|
|
126
|
+
|
|
127
|
+
if args.command == "init":
|
|
128
|
+
targets = [t.strip() for t in args.targets.split(",") if t.strip()]
|
|
129
|
+
unknown = set(targets) - set(ALL_TARGETS)
|
|
130
|
+
if unknown:
|
|
131
|
+
parser.error(f"unknown target(s): {', '.join(sorted(unknown))} (choose from {ALL_TARGETS})")
|
|
132
|
+
init(targets, force=args.force, dry_run=args.dry_run)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
if __name__ == "__main__":
|
|
136
|
+
main()
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Render a canonical Claude SKILL.md into Cursor .mdc, a Codex AGENTS.md section, or a Bob rules file.
|
|
2
|
+
|
|
3
|
+
Claude, Cursor, Codex, and Bob each read assistant guidance in a different
|
|
4
|
+
shape (per-skill directory + frontmatter / single rule file + frontmatter /
|
|
5
|
+
one shared file with no frontmatter / a rules directory with no frontmatter
|
|
6
|
+
and no selective loading). Rather than hand-maintaining four copies of the
|
|
7
|
+
same content, every skill is authored once as SKILL.md and the other shapes
|
|
8
|
+
are derived from it here, at scaffold time.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
import yaml
|
|
17
|
+
|
|
18
|
+
_FRONTMATTER_RE = re.compile(r"\A---\n(.*?)\n---\n(.*)", re.DOTALL)
|
|
19
|
+
_HEADING_RE = re.compile(r"^(#{1,5})(\s)", re.MULTILINE)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def parse_skill_md(skill_md_path: Path) -> tuple[dict, str]:
|
|
23
|
+
"""Split a SKILL.md file into (frontmatter dict, body markdown)."""
|
|
24
|
+
text = skill_md_path.read_text()
|
|
25
|
+
match = _FRONTMATTER_RE.match(text)
|
|
26
|
+
if not match:
|
|
27
|
+
raise ValueError(f"{skill_md_path} has no YAML frontmatter")
|
|
28
|
+
frontmatter = yaml.safe_load(match.group(1)) or {}
|
|
29
|
+
body = match.group(2).strip("\n")
|
|
30
|
+
return frontmatter, body
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def render_mdc(skill_md_path: Path) -> str:
|
|
34
|
+
"""SKILL.md -> Cursor .mdc content: same description, alwaysApply: false, body verbatim."""
|
|
35
|
+
frontmatter, body = parse_skill_md(skill_md_path)
|
|
36
|
+
header = yaml.safe_dump(
|
|
37
|
+
{"description": frontmatter["description"], "alwaysApply": False},
|
|
38
|
+
sort_keys=False,
|
|
39
|
+
allow_unicode=True,
|
|
40
|
+
).strip()
|
|
41
|
+
return f"---\n{header}\n---\n\n{body}\n"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def render_agents_section(skill_md_path: Path) -> str:
|
|
45
|
+
"""SKILL.md -> one '## <name>' section for AGENTS.md, headings demoted one level."""
|
|
46
|
+
frontmatter, body = parse_skill_md(skill_md_path)
|
|
47
|
+
demoted = _HEADING_RE.sub(r"#\1\2", body)
|
|
48
|
+
return f"## {frontmatter['name']}\n\n_{frontmatter['description']}_\n\n{demoted}\n"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def render_bob(skill_md_path: Path) -> str:
|
|
52
|
+
"""SKILL.md -> a Bob rules file: plain markdown, no frontmatter (Bob doesn't support it
|
|
53
|
+
or selective loading — every file under .bob/rules/ is injected into every conversation
|
|
54
|
+
in full), body verbatim.
|
|
55
|
+
"""
|
|
56
|
+
_, body = parse_skill_md(skill_md_path)
|
|
57
|
+
return f"{body}\n"
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cuga-author-policy
|
|
3
|
+
description: Use when the user wants to govern agent behavior with a cuga policy - block/redirect an intent, add step-by-step playbook guidance, require approval before a tool runs, enhance a tool's description, or reshape agent output format.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Authoring a cuga policy
|
|
7
|
+
|
|
8
|
+
A cuga **policy** is a rule the runtime enforces on a `CugaAgent`, unlike a runtime *skill* (`cuga-build-cuga-skill`) which the agent optionally chooses to load. There are 5 policy types.
|
|
9
|
+
|
|
10
|
+
## Step 1 — identify which type
|
|
11
|
+
|
|
12
|
+
| The user wants to... | Type |
|
|
13
|
+
|---|---|
|
|
14
|
+
| Block or redirect a specific kind of request | `intent_guard` |
|
|
15
|
+
| Give the agent step-by-step guidance for a workflow | `playbook` |
|
|
16
|
+
| Require human approval before a tool runs | `tool_approval` |
|
|
17
|
+
| Add usage guidance/examples to a tool's description | `tool_guide` |
|
|
18
|
+
| Reshape the agent's response into JSON/table/markdown/etc. | `output_formatter` |
|
|
19
|
+
|
|
20
|
+
## Step 2 — pick programmatic SDK or markdown file
|
|
21
|
+
|
|
22
|
+
**Programmatic (recommended for code you already control):** attach at agent construction time via `agent.policies`:
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
await agent.policies.add_intent_guard(
|
|
26
|
+
name="Block Delete Operations",
|
|
27
|
+
description="Prevents deletion of critical data",
|
|
28
|
+
keywords=["delete", "remove", "erase"],
|
|
29
|
+
response="Deletion operations are not permitted for security reasons.",
|
|
30
|
+
priority=100, # higher = checked first
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
await agent.policies.add_playbook(
|
|
34
|
+
name="Budget Analysis Workflow",
|
|
35
|
+
description="Multi-step process for analyzing financial budgets",
|
|
36
|
+
natural_language_trigger=["When user asks to analyze their budget"],
|
|
37
|
+
content="# Budget Analysis Workflow\n\n## Step 1: ...",
|
|
38
|
+
priority=50,
|
|
39
|
+
)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Markdown file (for the `manager` web UI, or policies you want checked into a project):** save a file with YAML frontmatter + markdown body to the matching `.cuga/` subfolder, then restart or re-publish so it's picked up:
|
|
43
|
+
|
|
44
|
+
| Type | Save to |
|
|
45
|
+
|---|---|
|
|
46
|
+
| `playbook` | `.cuga/playbooks/playbook_<name>.md` |
|
|
47
|
+
| `intent_guard` | `.cuga/guards/guard_<name>.md` |
|
|
48
|
+
| `tool_guide` | `.cuga/guides/guide_<name>.md` |
|
|
49
|
+
| `tool_approval` | `.cuga/approvals/approval_<name>.md` |
|
|
50
|
+
| `output_formatter` | `.cuga/formatters/formatter_<name>.md` |
|
|
51
|
+
|
|
52
|
+
Shared frontmatter fields across all 5 types: `id`, `name`, `description`, `enabled`, `priority`, `type`, `triggers` (shape varies by type — see below).
|
|
53
|
+
|
|
54
|
+
## Mode: Playbook
|
|
55
|
+
|
|
56
|
+
```yaml
|
|
57
|
+
---
|
|
58
|
+
description: Brief description of what this playbook does
|
|
59
|
+
enabled: true
|
|
60
|
+
id: playbook_<unique_id>
|
|
61
|
+
name: <Playbook Name>
|
|
62
|
+
priority: 50
|
|
63
|
+
triggers:
|
|
64
|
+
natural_language:
|
|
65
|
+
- keyword 1
|
|
66
|
+
- keyword phrase
|
|
67
|
+
target: intent
|
|
68
|
+
threshold: 0.5
|
|
69
|
+
type: playbook
|
|
70
|
+
---
|
|
71
|
+
# <Title>
|
|
72
|
+
## Overview
|
|
73
|
+
## Parameters
|
|
74
|
+
- **parameter_name** (required/optional): description
|
|
75
|
+
## Steps
|
|
76
|
+
### 1. <Step Name> — constraints: MUST / SHOULD / MAY
|
|
77
|
+
## Examples
|
|
78
|
+
## Troubleshooting
|
|
79
|
+
## Best Practices
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Mode: Intent Guard
|
|
83
|
+
|
|
84
|
+
```yaml
|
|
85
|
+
---
|
|
86
|
+
description: Description of what intents this guard blocks
|
|
87
|
+
enabled: true
|
|
88
|
+
id: guard_<unique_id>
|
|
89
|
+
name: <Guard Name>
|
|
90
|
+
priority: 90 # 90-100 recommended: guards should win priority ties
|
|
91
|
+
triggers:
|
|
92
|
+
natural_language:
|
|
93
|
+
- blocked intent 1
|
|
94
|
+
target: intent
|
|
95
|
+
threshold: 0.7 # strict matching for guards
|
|
96
|
+
type: intent_guard
|
|
97
|
+
intent_examples:
|
|
98
|
+
- 5+ example phrases of the blocked intent, for matching
|
|
99
|
+
response:
|
|
100
|
+
response_type: natural_language # natural_language | json | template
|
|
101
|
+
content: |
|
|
102
|
+
Custom message explaining why this is blocked + alternatives.
|
|
103
|
+
allow_override: false # true = user can bypass, false = enforced
|
|
104
|
+
---
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Mode: Tool Guide
|
|
108
|
+
|
|
109
|
+
```yaml
|
|
110
|
+
---
|
|
111
|
+
description: Enhanced guidance for specific tools
|
|
112
|
+
enabled: true
|
|
113
|
+
id: guide_<unique_id>
|
|
114
|
+
name: <Guide Name>
|
|
115
|
+
priority: 50
|
|
116
|
+
triggers:
|
|
117
|
+
tool_match:
|
|
118
|
+
- tool_name_1
|
|
119
|
+
target: tools
|
|
120
|
+
type: tool_guide
|
|
121
|
+
target_tools: [tool_name_1, tool_name_2] # or target_apps: [app_name] for all tools in an app
|
|
122
|
+
guide_content: |
|
|
123
|
+
## When to Use / Best Practices / Common Pitfalls / Parameter Guidelines / Examples / Related Tools
|
|
124
|
+
prepend: false # true = insert before the tool's own description, false = after
|
|
125
|
+
---
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Mode: Tool Approval
|
|
129
|
+
|
|
130
|
+
```yaml
|
|
131
|
+
---
|
|
132
|
+
description: Require approval for sensitive operations
|
|
133
|
+
enabled: true
|
|
134
|
+
id: approval_<unique_id>
|
|
135
|
+
name: <Approval Policy Name>
|
|
136
|
+
priority: 100 # highest priority recommended
|
|
137
|
+
triggers:
|
|
138
|
+
tool_match: [sensitive_tool_1]
|
|
139
|
+
target: tools
|
|
140
|
+
type: tool_approval
|
|
141
|
+
required_tools: [sensitive_tool_1] # or required_apps: [app_name]
|
|
142
|
+
approval_message: |
|
|
143
|
+
Explain why approval is required and what will happen.
|
|
144
|
+
show_code_preview: true
|
|
145
|
+
auto_approve_after: null # null = never auto-approve (recommended); or seconds e.g. 30/60/300
|
|
146
|
+
---
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Mode: Output Formatter
|
|
150
|
+
|
|
151
|
+
```yaml
|
|
152
|
+
---
|
|
153
|
+
description: Format output in a specific structure
|
|
154
|
+
enabled: true
|
|
155
|
+
id: formatter_<unique_id>
|
|
156
|
+
name: <Formatter Name>
|
|
157
|
+
priority: 50
|
|
158
|
+
triggers:
|
|
159
|
+
natural_language: [format keyword 1]
|
|
160
|
+
target: agent_response # must be agent_response for formatters
|
|
161
|
+
threshold: 0.6
|
|
162
|
+
type: output_formatter
|
|
163
|
+
format_type: json # json | markdown | table | csv | custom
|
|
164
|
+
format_config: |
|
|
165
|
+
{"schema": {"type": "object", "properties": {"field1": {"type": "string"}}, "required": ["field1"]}}
|
|
166
|
+
---
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Testing
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
cuga start competition --local
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Trigger the policy with matching keywords/intent and confirm the expected block/guidance/approval/format behavior shows up.
|
|
176
|
+
|
|
177
|
+
## Reference
|
|
178
|
+
|
|
179
|
+
Full worked templates: `docs/starterkit/.cursor/{intent_guard,playbook,tool_guide,tool_approval,output_formatter}.md` in the cuga-agent repo. Policy data models: `src/cuga/backend/cuga_graph/policy/models.py`. SDK docs: https://docs.cuga.dev/docs/sdk/policies/
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cuga-build-agent
|
|
3
|
+
description: Use when the user wants to write Python code that creates or invokes a CugaAgent or CugaSupervisor, e.g. "build an agent with cuga", "how do I call agent.invoke", "set up a multi-agent supervisor".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Building with the CugaAgent SDK
|
|
7
|
+
|
|
8
|
+
## Single agent
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
from cuga import CugaAgent
|
|
12
|
+
from langchain_core.tools import tool
|
|
13
|
+
import asyncio
|
|
14
|
+
|
|
15
|
+
@tool
|
|
16
|
+
def add_numbers(a: int, b: int) -> int:
|
|
17
|
+
"""Add two numbers together"""
|
|
18
|
+
return a + b
|
|
19
|
+
|
|
20
|
+
agent = CugaAgent(tools=[add_numbers])
|
|
21
|
+
|
|
22
|
+
async def main():
|
|
23
|
+
result = await agent.invoke("What is 5 + 3?")
|
|
24
|
+
print(result.answer)
|
|
25
|
+
|
|
26
|
+
asyncio.run(main())
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Key points:
|
|
30
|
+
- Tools are plain LangChain `@tool`-decorated functions (or an OpenAPI/MCP provider — see `cuga-build-tool`).
|
|
31
|
+
- `await agent.invoke(message, thread_id=...)` — `thread_id` isolates conversation state per user/session; omit it for a one-off call.
|
|
32
|
+
- `agent.stream()` gives real-time execution events instead of a single final result.
|
|
33
|
+
- `agent.policies` is the entry point for attaching Intent Guard / Playbook / Tool Approval / Tool Guide / Output Formatter policies programmatically — see `cuga-author-policy`.
|
|
34
|
+
- Knowledge/RAG is on by default (`enable_knowledge=True`) — see `cuga-knowledge-rag`; pass `enable_knowledge=False` to turn it off.
|
|
35
|
+
- The underlying LangGraph graph is reachable for advanced use cases (custom nodes, inspecting state) if the simple API isn't enough.
|
|
36
|
+
|
|
37
|
+
## Multi-agent (CugaSupervisor)
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from cuga import CugaAgent, CugaSupervisor
|
|
41
|
+
from langchain_core.tools import tool
|
|
42
|
+
import asyncio
|
|
43
|
+
|
|
44
|
+
@tool
|
|
45
|
+
def get_customers(limit: int = 10) -> str:
|
|
46
|
+
"""Fetch top customers from CRM."""
|
|
47
|
+
return "Alice ($250k); Bob ($180k)"
|
|
48
|
+
|
|
49
|
+
@tool
|
|
50
|
+
def send_email(to: str, body: str) -> str:
|
|
51
|
+
"""Send an email."""
|
|
52
|
+
return f"Email sent to {to}"
|
|
53
|
+
|
|
54
|
+
async def main():
|
|
55
|
+
crm_agent = CugaAgent(tools=[get_customers])
|
|
56
|
+
crm_agent.description = "CRM and customer data"
|
|
57
|
+
|
|
58
|
+
email_agent = CugaAgent(tools=[send_email])
|
|
59
|
+
email_agent.description = "Sending emails and notifications"
|
|
60
|
+
|
|
61
|
+
supervisor = CugaSupervisor(agents={"crm": crm_agent, "email": email_agent})
|
|
62
|
+
result = await supervisor.invoke("Get our top customer and email them a thank-you")
|
|
63
|
+
print(result.answer)
|
|
64
|
+
|
|
65
|
+
asyncio.run(main())
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- Each sub-agent needs a `.description` — the supervisor uses it to decide who handles what.
|
|
69
|
+
- Mix local `CugaAgent`s with remote agents via A2A: pass an `"agent_name": {"type": "external", "description": "...", "config": {"a2a_protocol": {...}}}` entry in `agents=`.
|
|
70
|
+
- Pass data between sub-agents with `variables=["var_name"]`.
|
|
71
|
+
- `CugaSupervisor.from_yaml("path/to/config.yaml")` loads agents from a config file instead of constructing them in code.
|
|
72
|
+
- Try it live first: `cuga start demo_supervisor` (see `cuga-install-and-launch`).
|
|
73
|
+
|
|
74
|
+
## Reference
|
|
75
|
+
|
|
76
|
+
Full SDK docs: https://docs.cuga.dev/docs/sdk/cuga_agent/ and https://docs.cuga.dev/docs/sdk/cuga_supervisor
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cuga-build-cuga-skill
|
|
3
|
+
description: Use when the user wants a running CugaAgent to load a new capability at runtime via cuga's own skill system, e.g. "add a skill to my agent", "create a .cuga/skills SKILL.md", "how does load_skill work".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Authoring a cuga runtime skill
|
|
7
|
+
|
|
8
|
+
This is different from the IDE-assistant skill you're reading right now. **cuga's own agents** can discover and load `SKILL.md` files at runtime via a `load_skill` tool — this skill teaches you how to author one of those.
|
|
9
|
+
|
|
10
|
+
## Where skills live
|
|
11
|
+
|
|
12
|
+
Controlled by `[skills] root` in `settings.toml` (or `DYNACONF_SKILLS__ROOT`):
|
|
13
|
+
|
|
14
|
+
| `root` value | Directory | Notes |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| `cuga` (default) | `.cuga/skills/` | cuga's own convention, avoids colliding with `.agents/skills/` written by other tools |
|
|
17
|
+
| `agents` | `.agents/skills/` | shared convention used by e.g. `npx skills` |
|
|
18
|
+
| `global_agents` | `~/.config/agents/skills/` | global, not project-scoped |
|
|
19
|
+
| `global_cuga` | `~/.config/cuga/skills/` | legacy global path |
|
|
20
|
+
|
|
21
|
+
Skills are discovered recursively (`**/SKILL.md`) under whichever root is active.
|
|
22
|
+
|
|
23
|
+
## SKILL.md shape (cuga runtime contract)
|
|
24
|
+
|
|
25
|
+
Required frontmatter: **`name`** and **`description`** (shown to the agent in its available-skills list — write a description the agent can use to decide when to load it). Optional **`requirements`**: a pip/npm package or list of packages the skill needs installed. Everything below the frontmatter is arbitrary markdown — the full instruction text handed back by `load_skill` when the agent loads this skill.
|
|
26
|
+
|
|
27
|
+
```markdown
|
|
28
|
+
---
|
|
29
|
+
name: my-skill
|
|
30
|
+
description: Use when the user asks to <do X>. Loads instructions for <Y>.
|
|
31
|
+
requirements: some-pip-package
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
# My Skill
|
|
35
|
+
|
|
36
|
+
Step-by-step instructions, code snippets, examples — whatever the agent
|
|
37
|
+
needs in its context to actually perform the task.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Name validation: no path separators or `..` in `name` — cuga sanitizes/rejects unsafe or Jinja2-template-injected values in `name`/`description` for prompt-injection safety.
|
|
41
|
+
|
|
42
|
+
## Try it
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
cuga start demo_skills
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Runs with `[advanced_features] sandbox_mode = "native"` by default. For sandboxed execution instead: `uv sync --extra opensandbox` then use the `opensandbox` mode, or `uv sync --group sandbox` + `cuga start demo --sandbox` with `[skills]` enabled for Docker/Podman isolation.
|
|
49
|
+
|
|
50
|
+
## Installing a ready-made skill
|
|
51
|
+
|
|
52
|
+
Anthropic publishes ready-made skill folders (e.g. `pptx`) that follow the same `SKILL.md` convention:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx skills add https://github.com/anthropics/skills --skill pptx -a universal
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
This drops it under `.agents/skills/pptx/SKILL.md`. To use cuga's default layout instead, copy/symlink it into `.cuga/skills/`. Add `-g` to install globally. Restart `cuga start demo_skills` (or your app) afterward so skills are rescanned — there's no hot-reload.
|
|
59
|
+
|
|
60
|
+
## Don't confuse this with policies
|
|
61
|
+
|
|
62
|
+
A runtime skill is instructional content the agent chooses to load. A policy (playbook, intent guard, etc. — see `cuga-author-policy`) is a rule the runtime *enforces* on the agent regardless of whether it "chooses" to. Use a skill for optional know-how, a policy for a constraint or workflow that must always apply.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cuga-build-tool
|
|
3
|
+
description: Use when the user wants to give a cuga agent a new capability via a Python function, REST/OpenAPI service, or MCP server, e.g. "add a tool", "connect this API to cuga", "register an MCP server".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Registering tools with cuga
|
|
7
|
+
|
|
8
|
+
cuga supports three tool integration types:
|
|
9
|
+
|
|
10
|
+
| Type | Best for | Configured via | Loading |
|
|
11
|
+
|---|---|---|---|
|
|
12
|
+
| **LangChain** | Python functions, rapid prototyping | direct import, pass to `CugaAgent(tools=[...])` | Runtime |
|
|
13
|
+
| **OpenAPI** | REST APIs, existing services | `mcp_servers.yaml` | Build-time |
|
|
14
|
+
| **MCP** | Custom protocols, complex integrations | `mcp_servers.yaml` | Build-time |
|
|
15
|
+
|
|
16
|
+
## LangChain tool (fastest path)
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from langchain_core.tools import tool
|
|
20
|
+
|
|
21
|
+
@tool
|
|
22
|
+
def lookup_order(order_id: str) -> str:
|
|
23
|
+
"""Look up an order by ID and return its status."""
|
|
24
|
+
...
|
|
25
|
+
|
|
26
|
+
agent = CugaAgent(tools=[lookup_order])
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This is a runtime-loaded tool: no config file, no restart needed, just pass it into the `CugaAgent` constructor. Write a clear docstring — it becomes the tool description the agent's reasoning engine sees.
|
|
30
|
+
|
|
31
|
+
## OpenAPI / MCP (registry-based)
|
|
32
|
+
|
|
33
|
+
For REST APIs or MCP servers, add an entry to `mcp_servers.yaml` (path: `src/cuga/backend/tools_env/registry/config/mcp_servers.yaml` in a cuga-agent checkout). These are build-time: the tool registry (`cuga start registry`, or bundled into any `demo_*`/`manager` service) loads this config at startup, so changes need a restart to take effect.
|
|
34
|
+
|
|
35
|
+
See the registry's own README for the exact config schema and worked examples: `src/cuga/backend/tools_env/registry/README.md`, plus `docs/examples/cuga_with_runtime_tools/README.md` for a full walkthrough combining different tool types with MCP.
|
|
36
|
+
|
|
37
|
+
## Which to reach for
|
|
38
|
+
|
|
39
|
+
- One-off Python function, prototyping, or logic that lives in your app already → LangChain tool.
|
|
40
|
+
- An existing REST API you don't want to hand-wrap → OpenAPI entry in `mcp_servers.yaml`.
|
|
41
|
+
- A third-party or custom MCP server → MCP entry in `mcp_servers.yaml`.
|
|
42
|
+
|
|
43
|
+
## Enhancing how the agent uses a tool
|
|
44
|
+
|
|
45
|
+
Once a tool exists, you can shape *how* the agent uses it without touching its code — that's a `tool_guide` or `tool_approval` policy (require confirmation before a sensitive tool runs, or append extra usage guidance/examples to its description). See `cuga-author-policy`.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cuga-debug-trajectory
|
|
3
|
+
description: Use when a cuga agent run misbehaved (wrong tool called, silently blocked, wrong output format) and the user wants to inspect what actually happened.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Debugging a cuga agent run
|
|
7
|
+
|
|
8
|
+
## Trajectory viewer
|
|
9
|
+
|
|
10
|
+
Every run is logged to a trajectory data directory (`<cuga log dir>/trajectory_data`). Inspect it visually:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
cuga viz
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Launches a web dashboard for browsing execution trajectories: what the reasoning engine decided at each step, which tools it called with what arguments, and what came back. Start here before reading raw logs — it's usually faster to spot the wrong branch/tool call visually than to grep JSON.
|
|
17
|
+
|
|
18
|
+
## Environment sanity check
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
cuga doctor
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Run this first if the agent won't even start, or behaves inconsistently across environments — it validates the environment/dependency setup.
|
|
25
|
+
|
|
26
|
+
## `cuga status`
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
cuga status <service|all>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Confirms whether the service you expect to be running actually is, before chasing a bug that's really just "nothing is listening on that port."
|
|
33
|
+
|
|
34
|
+
## Common failure patterns
|
|
35
|
+
|
|
36
|
+
- **Tool "not found" or never called** — check the tool actually got passed to `CugaAgent(tools=[...])`, or (for OpenAPI/MCP tools) that the registry service (`cuga start registry`, or the registry bundled in a `demo_*`/`manager` service) picked up your `mcp_servers.yaml` change — those are build-time, not hot-reloaded.
|
|
37
|
+
- **Request silently redirected/blocked with no obvious reason** — an `intent_guard` policy may be matching. Check `.cuga/guards/` (or policies added via `agent.policies.add_intent_guard`) for a guard whose `triggers`/`intent_examples` overlap the query, and check `priority` — guards default to high priority (90-100) and are checked before other policies. See `cuga-author-policy`.
|
|
38
|
+
- **Response in the wrong shape** — an `output_formatter` policy may be firing (or not firing when expected) based on its `triggers.natural_language` keywords and `threshold`. Lower/raise the threshold or adjust keywords.
|
|
39
|
+
- **Tool paused waiting on nothing** — a `tool_approval` policy is likely gating that tool; check `.cuga/approvals/` for `required_tools`/`required_apps` matches and `auto_approve_after`.
|
|
40
|
+
- **`thread_id` confusion** — state (conversation history, session-scoped knowledge) is isolated per `thread_id`. If a run seems to have "forgotten" something from a previous call, confirm the same `thread_id` was passed both times.
|
|
41
|
+
- **Multi-agent: wrong sub-agent picked** — the `CugaSupervisor` routes based on each sub-agent's `.description`. Vague or overlapping descriptions between sub-agents cause misrouting — make them distinct and specific.
|
|
42
|
+
|
|
43
|
+
## Policy-level test coverage
|
|
44
|
+
|
|
45
|
+
If you're debugging policy interaction bugs specifically, the policy engine's own integration tests are a good reference for expected behavior: `src/cuga/backend/cuga_graph/policy/tests/` in a cuga-agent checkout (covers intent guard blocking/priority resolution, playbook guidance injection, and more).
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cuga-getting-started
|
|
3
|
+
description: Use when the user wants to install, launch, or build something with the cuga agent framework (pip package `cuga`), or asks "how do I use cuga" / "how do I build an agent with cuga" — routes to the right cuga-harness-kit skill instead of guessing.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Getting started with cuga
|
|
7
|
+
|
|
8
|
+
cuga (`pip install cuga`, [github.com/cuga-project/cuga-agent](https://github.com/cuga-project/cuga-agent)) is an agent orchestration framework: a reasoning engine + pluggable tools (OpenAPI/MCP/LangChain) + a policy system + optional RAG knowledge base + multi-agent supervision, built on LangGraph.
|
|
9
|
+
|
|
10
|
+
This skill is the entry point. Read the description of the skill that matches what the user is doing before improvising:
|
|
11
|
+
|
|
12
|
+
| User is trying to... | Use skill |
|
|
13
|
+
|---|---|
|
|
14
|
+
| Install cuga or start the demo UI | `cuga-install-and-launch` |
|
|
15
|
+
| Write Python code that creates/invokes a `CugaAgent` or `CugaSupervisor` | `cuga-build-agent` |
|
|
16
|
+
| Author a new **runtime** skill the agent itself can load (`.cuga/skills/<name>/SKILL.md`) | `cuga-build-cuga-skill` |
|
|
17
|
+
| Register a Python function, OpenAPI spec, or MCP server as a callable tool | `cuga-build-tool` |
|
|
18
|
+
| Add a policy: block an intent, add a playbook, require approval, enhance a tool description, or reshape output | `cuga-author-policy` |
|
|
19
|
+
| Ingest/search documents (RAG) | `cuga-knowledge-rag` |
|
|
20
|
+
| An agent run misbehaved and you need to inspect why | `cuga-debug-trajectory` |
|
|
21
|
+
|
|
22
|
+
## Two different meanings of "skill"
|
|
23
|
+
|
|
24
|
+
Don't conflate these — they are unrelated mechanisms that happen to share the `SKILL.md` filename convention:
|
|
25
|
+
|
|
26
|
+
1. **This skill** (and its siblings) is an *IDE-assistant* skill — it teaches you, the coding assistant, how to work with cuga.
|
|
27
|
+
2. **cuga's own runtime skills** (`.cuga/skills/<name>/SKILL.md`) are loaded by a running `CugaAgent` via a `load_skill` tool, and are what the *agent itself* can call at runtime. See `cuga-build-cuga-skill` when the user wants one of these.
|
|
28
|
+
|
|
29
|
+
## Ground truth over memory
|
|
30
|
+
|
|
31
|
+
cuga's CLI and SDK surface can change between versions. If something in these skills looks stale (a flag, a command, a class name), verify against the installed package before trusting the skill text: `python -c "import cuga; print(cuga.__file__)"`, `cuga --help`, or the repo at `src/cuga/` if working inside a checkout.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cuga-install-and-launch
|
|
3
|
+
description: Use when the user wants to install cuga, start the demo/web UI, or asks what `cuga start` does or which service/mode to run.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Installing and launching cuga
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install cuga
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
(`cuga` requires Python >= 3.10, < 3.14.) For working inside a checkout of the [cuga-agent](https://github.com/cuga-project/cuga-agent) repo itself instead of the published package:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
git clone https://github.com/cuga-project/cuga-agent.git
|
|
18
|
+
cd cuga-agent
|
|
19
|
+
uv venv --python=3.12 && source .venv/bin/activate
|
|
20
|
+
uv sync
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Set an LLM API key before starting anything, e.g. `echo "OPENAI_API_KEY=..." > .env`.
|
|
24
|
+
|
|
25
|
+
## Launch
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
cuga start <service>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Valid `<service>` values: `demo`, `demo_skills`, `demo_crm`, `demo_docs`, `demo_health`, `demo_knowledge`, `demo_supervisor`, `travel_agent`, `manager`, `registry`, `appworld`.
|
|
32
|
+
|
|
33
|
+
- `demo` / `demo_crm` / `demo_supervisor` / `demo_knowledge` / `demo_docs` — launch the chat web UI at `https://localhost:7860` plus a tool registry service on port 8001, each preset wiring up different sample tools (CRM+email, multi-agent supervisor, RAG knowledge, docs) so you can try the matching capability immediately.
|
|
34
|
+
- `demo_skills` — same UI, with cuga's own runtime skill-loading enabled (see `cuga-build-cuga-skill`).
|
|
35
|
+
- `manager` — a draft/publish UI: edit agent config (tools, MCP servers, LLM, policies) as a draft, try it, then publish a versioned config for production chat.
|
|
36
|
+
- `registry` — just the tool registry service (OpenAPI/MCP config), no chat UI.
|
|
37
|
+
- `appworld` / `travel_agent` — specific benchmark/example scenarios.
|
|
38
|
+
|
|
39
|
+
Other useful commands:
|
|
40
|
+
- `cuga stop <service>` — stop a running service.
|
|
41
|
+
- `cuga status` — show what's currently running.
|
|
42
|
+
- `cuga doctor` — environment/dependency sanity check.
|
|
43
|
+
- `cuga viz` — trajectory viewer dashboard (see `cuga-debug-trajectory`).
|
|
44
|
+
|
|
45
|
+
There is no `cuga policy` or `cuga knowledge` CLI subcommand — policies and knowledge are managed through the Python SDK (`agent.policies.*`, `agent.knowledge.*`, see `cuga-author-policy` / `cuga-knowledge-rag`) or through the `manager` web UI.
|
|
46
|
+
|
|
47
|
+
## Next step
|
|
48
|
+
|
|
49
|
+
Once the UI is up, point the user at `cuga-build-agent` for writing their own Python code against the SDK, or have them just chat with the demo agent directly in the browser.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cuga-knowledge-rag
|
|
3
|
+
description: Use when the user wants a cuga agent to ingest, search, or answer questions from documents (PDF/DOCX/XLSX/PPTX/HTML/Markdown/images) - RAG / knowledge base features.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Knowledge base (RAG)
|
|
7
|
+
|
|
8
|
+
cuga has a built-in knowledge base: local vector store + **Docling** for parsing/normalizing documents before chunking and embedding, so ingestion stays self-contained with no external document service.
|
|
9
|
+
|
|
10
|
+
Knowledge is **enabled by default** (`enable_knowledge=True`); the SDK auto-injects knowledge tools/awareness so the agent knows what's available and how to search it.
|
|
11
|
+
|
|
12
|
+
## Try it
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
cuga start demo_knowledge
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Full walkthrough with sample docs: `docs/examples/knowledge_demo/` in a cuga-agent checkout.
|
|
19
|
+
|
|
20
|
+
## Programmatic use
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from cuga import CugaAgent
|
|
24
|
+
import asyncio
|
|
25
|
+
|
|
26
|
+
agent = CugaAgent(enable_knowledge=True)
|
|
27
|
+
|
|
28
|
+
async def main():
|
|
29
|
+
await agent.knowledge.ingest("/path/to/quarterly_report.pdf")
|
|
30
|
+
|
|
31
|
+
result = await agent.invoke("What does the report say about Q4 revenue?")
|
|
32
|
+
print(result.answer) # agent searches the knowledge base automatically
|
|
33
|
+
|
|
34
|
+
results = await agent.knowledge.search("Q4 revenue figures")
|
|
35
|
+
for r in results:
|
|
36
|
+
print(f"{r['filename']} (page {r['page']}): {r['text'][:100]}")
|
|
37
|
+
|
|
38
|
+
docs = await agent.knowledge.list_documents()
|
|
39
|
+
await agent.aclose()
|
|
40
|
+
|
|
41
|
+
asyncio.run(main())
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Scoping
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
# Session-scoped: temporary, tied to one conversation thread
|
|
48
|
+
await agent.knowledge.ingest("/path/to/file.pdf", scope="session", thread_id="user-session-123")
|
|
49
|
+
results = await agent.knowledge.search("query", scope="session", thread_id="user-session-123")
|
|
50
|
+
|
|
51
|
+
# Agent-scoped (default): permanent, shared across conversations
|
|
52
|
+
await agent.knowledge.ingest("/path/to/file.pdf", scope="agent")
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Use `session` scope for per-conversation uploads that shouldn't leak between users; use `agent` scope for a shared reference corpus.
|
|
56
|
+
|
|
57
|
+
## Disabling
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
agent = CugaAgent(tools=[my_tools], enable_knowledge=False)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Supported types & tuning
|
|
64
|
+
|
|
65
|
+
PDF, DOCX, XLSX, PPTX, HTML, Markdown, images, and more (via Docling). Embedding provider (`fastembed` default/local, `huggingface`, `openai`, `ollama`, `openrouter`) plus model/batch/concurrency are set under `[knowledge.embeddings]` in `settings.toml` or via `--embeddings-*` CLI flags. Switching provider/model invalidates existing vectors (different dimensionality) — the manage UI (`cuga start manager`) surfaces a "re-index recommended" banner when that happens.
|
|
66
|
+
|
|
67
|
+
Full provider matrix: https://docs.cuga.dev/docs/sdk/knowledge/
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cuga-harness-kit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Scaffolds Claude Code / Cursor / Codex skills that teach coding agents to build with the cuga framework
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: pyyaml>=6.0
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# cuga-harness-kit
|
|
12
|
+
|
|
13
|
+
Skills that teach Claude Code, Cursor, Codex, and Bob how to install, launch, and build with [cuga](https://github.com/cuga-project/cuga-agent) — the open-source generalist agent framework.
|
|
14
|
+
|
|
15
|
+
No git clone, no marketplace install. Just:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install cuga-harness-kit
|
|
19
|
+
cuga-harness-kit init
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Run `init` from an empty new project you're starting from scratch, or from inside an existing cuga checkout — either way it writes the same skill set into the current directory:
|
|
23
|
+
|
|
24
|
+
- `.claude/skills/<name>/SKILL.md` — auto-discovered by Claude Code just by opening the folder.
|
|
25
|
+
- `.cursor/rules/cuga-<name>.mdc` — auto-discovered by Cursor.
|
|
26
|
+
- `AGENTS.md` — read wholesale by Codex (and other `AGENTS.md`-aware tools). Re-running `init` only touches the `<!-- cuga-harness-kit:start/end -->` block, so it won't clobber anything else you've written in that file.
|
|
27
|
+
- `.bob/rules/cuga-<name>.md` — loaded by IBM Bob. Unlike the other three, Bob has no frontmatter/description-based selection: every file under `.bob/rules/` is injected into *every* conversation in full, not just when relevant. (Bob also auto-loads `AGENTS.md` by default, so the Codex output above already reaches it too — the `.bob/rules/` files exist for teams that want per-skill files instead of one shared doc.)
|
|
28
|
+
|
|
29
|
+
Then open the folder in your assistant of choice and ask something like *"help me build a cuga tool"* or *"how do I launch cuga"*.
|
|
30
|
+
|
|
31
|
+
## What's included (v1)
|
|
32
|
+
|
|
33
|
+
| Skill | Teaches |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `getting-started` | Entry point — routes to the right skill below. |
|
|
36
|
+
| `install-and-launch` | `pip install cuga`, `cuga start <mode>`. |
|
|
37
|
+
| `build-agent` | `CugaAgent` / `CugaSupervisor` SDK basics. |
|
|
38
|
+
| `build-cuga-skill` | Authoring cuga's own **runtime** skills (`.cuga/skills/<name>/SKILL.md`) — not to be confused with the IDE-assistant skills in this repo. |
|
|
39
|
+
| `build-tool` | Registering a LangChain / OpenAPI / MCP tool. |
|
|
40
|
+
| `author-policy` | Intent guards, playbooks, tool approval/guides, output formatters — one unified skill. |
|
|
41
|
+
| `knowledge-rag` | Document ingestion/search. |
|
|
42
|
+
| `debug-trajectory` | `cuga viz`, `cuga doctor`, common failure patterns. |
|
|
43
|
+
|
|
44
|
+
All 8 are authored once, as Claude-native `SKILL.md` files under `src/cuga_harness_kit/skills/`, and rendered into the Cursor/Codex/Bob shapes at `init` time (`src/cuga_harness_kit/render.py`) — there's a single source of truth per skill, not four copies to keep in sync by hand.
|
|
45
|
+
|
|
46
|
+
## CLI
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
cuga-harness-kit init [--targets claude,cursor,codex,bob] [--force] [--dry-run]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- `--targets` — comma-separated subset of `claude`, `cursor`, `codex`, `bob` (default: all four).
|
|
53
|
+
- `--force` — overwrite `.claude/skills/*`, `.cursor/rules/*`, and `.bob/rules/*` files that already exist with different content (default: skip and report). `AGENTS.md`'s managed block always updates regardless, since it never touches content outside its markers.
|
|
54
|
+
- `--dry-run` — print what would be written without touching disk.
|
|
55
|
+
|
|
56
|
+
## Development
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
uv sync # or: pip install -e '.[dev]' equivalent via [dependency-groups]
|
|
60
|
+
uv run pytest
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Phase 2 (not yet built)
|
|
64
|
+
|
|
65
|
+
Real Claude Code plugin packaging (`.claude-plugin/plugin.json` + marketplace listing for `/plugin install`), slash commands, session hooks, a smarter `update`/diff command, and additional harness targets (Windsurf, `.clinerules`, `GEMINI.md`) — add these only if a real gap shows up.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
cuga_harness_kit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
cuga_harness_kit/cli.py,sha256=UFs9uYiqh-B2T8kDx2kMA5RuJGLZgACYURaQyVs2ykY,5383
|
|
3
|
+
cuga_harness_kit/render.py,sha256=3rSfH0Ap2zTnnSHmjVrnnRhlBJjp69lWudKD6wtYZ5U,2245
|
|
4
|
+
cuga_harness_kit/skills/author-policy/SKILL.md,sha256=AbvTAKFJx9NHQcIiPFYb1ClGHO72hvNYAxPahYPYKxA,5611
|
|
5
|
+
cuga_harness_kit/skills/build-agent/SKILL.md,sha256=ksgfqKq9Wi6hsWlI0AyTqkAeYdobd-1gxMLCg-KAPBI,2934
|
|
6
|
+
cuga_harness_kit/skills/build-cuga-skill/SKILL.md,sha256=La407XvaHT5Za2X8byqfTXDKZN5DWtkJMTGK1wCiPiE,3194
|
|
7
|
+
cuga_harness_kit/skills/build-tool/SKILL.md,sha256=2NXSXAM7W245iIiwNPWI2e1Ok80U6umG_38cMj6aNrs,2347
|
|
8
|
+
cuga_harness_kit/skills/debug-trajectory/SKILL.md,sha256=wezKnjFOErlrBFPg48z-PCGo40k-8TmwicN3bCbAxYg,3029
|
|
9
|
+
cuga_harness_kit/skills/getting-started/SKILL.md,sha256=kG_BBmiL104yhph5wVA2DLsVOSC-iiIudh4DIcGPnQI,2334
|
|
10
|
+
cuga_harness_kit/skills/install-and-launch/SKILL.md,sha256=AOdRV9TqeGL4LyqpcUs-xiFPuDrM_yZPJtdl1AztuF4,2336
|
|
11
|
+
cuga_harness_kit/skills/knowledge-rag/SKILL.md,sha256=qwHzem5uHrvOyQGxNCb5EFq0jZrj6RcutEm2PZXU_zo,2570
|
|
12
|
+
cuga_harness_kit-0.1.0.dist-info/METADATA,sha256=amjmjOV1wH0alg_1lHrh_qns_pXShUeZdg8SWxX0Hpc,3742
|
|
13
|
+
cuga_harness_kit-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
14
|
+
cuga_harness_kit-0.1.0.dist-info/entry_points.txt,sha256=-yB8fCX2mOnZtj-LotP_YIDlIoy8vbXO-9ywg3n6jGk,63
|
|
15
|
+
cuga_harness_kit-0.1.0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
16
|
+
cuga_harness_kit-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|