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.
File without changes
@@ -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,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ cuga-harness-kit = cuga_harness_kit.cli:main
@@ -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.