decepticon-sdk 1.1.2__tar.gz
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.
- decepticon_sdk-1.1.2/.gitignore +117 -0
- decepticon_sdk-1.1.2/PKG-INFO +51 -0
- decepticon_sdk-1.1.2/README.md +24 -0
- decepticon_sdk-1.1.2/decepticon_sdk/__init__.py +107 -0
- decepticon_sdk-1.1.2/decepticon_sdk/py.typed +0 -0
- decepticon_sdk-1.1.2/decepticon_sdk/scaffold/__init__.py +21 -0
- decepticon_sdk-1.1.2/decepticon_sdk/scaffold/cli.py +114 -0
- decepticon_sdk-1.1.2/decepticon_sdk/scaffold/templates.py +250 -0
- decepticon_sdk-1.1.2/decepticon_sdk/testing/__init__.py +114 -0
- decepticon_sdk-1.1.2/examples/README.md +41 -0
- decepticon_sdk-1.1.2/examples/agent/README.md +23 -0
- decepticon_sdk-1.1.2/examples/agent/dist/.gitignore +1 -0
- decepticon_sdk-1.1.2/examples/agent/dist/decepticon_example_agent-0.0.1-py3-none-any.whl +0 -0
- decepticon_sdk-1.1.2/examples/agent/dist/decepticon_example_agent-0.0.1.tar.gz +0 -0
- decepticon_sdk-1.1.2/examples/agent/pyproject.toml +21 -0
- decepticon_sdk-1.1.2/examples/agent/src/decepticon_example_agent/__init__.py +19 -0
- decepticon_sdk-1.1.2/examples/callback/README.md +23 -0
- decepticon_sdk-1.1.2/examples/callback/dist/.gitignore +1 -0
- decepticon_sdk-1.1.2/examples/callback/dist/decepticon_example_callback-0.0.1-py3-none-any.whl +0 -0
- decepticon_sdk-1.1.2/examples/callback/dist/decepticon_example_callback-0.0.1.tar.gz +0 -0
- decepticon_sdk-1.1.2/examples/callback/pyproject.toml +21 -0
- decepticon_sdk-1.1.2/examples/callback/src/decepticon_example_callback/__init__.py +25 -0
- decepticon_sdk-1.1.2/examples/middleware/README.md +23 -0
- decepticon_sdk-1.1.2/examples/middleware/dist/.gitignore +1 -0
- decepticon_sdk-1.1.2/examples/middleware/dist/decepticon_example_middleware-0.0.1-py3-none-any.whl +0 -0
- decepticon_sdk-1.1.2/examples/middleware/dist/decepticon_example_middleware-0.0.1.tar.gz +0 -0
- decepticon_sdk-1.1.2/examples/middleware/pyproject.toml +21 -0
- decepticon_sdk-1.1.2/examples/middleware/src/decepticon_example_middleware/__init__.py +22 -0
- decepticon_sdk-1.1.2/examples/prompt/README.md +23 -0
- decepticon_sdk-1.1.2/examples/prompt/dist/.gitignore +1 -0
- decepticon_sdk-1.1.2/examples/prompt/dist/decepticon_example_prompt-0.0.1-py3-none-any.whl +0 -0
- decepticon_sdk-1.1.2/examples/prompt/dist/decepticon_example_prompt-0.0.1.tar.gz +0 -0
- decepticon_sdk-1.1.2/examples/prompt/pyproject.toml +21 -0
- decepticon_sdk-1.1.2/examples/prompt/src/decepticon_example_prompt/__init__.py +14 -0
- decepticon_sdk-1.1.2/examples/skill/README.md +23 -0
- decepticon_sdk-1.1.2/examples/skill/dist/.gitignore +1 -0
- decepticon_sdk-1.1.2/examples/skill/dist/decepticon_example_skill-0.0.1-py3-none-any.whl +0 -0
- decepticon_sdk-1.1.2/examples/skill/dist/decepticon_example_skill-0.0.1.tar.gz +0 -0
- decepticon_sdk-1.1.2/examples/skill/pyproject.toml +21 -0
- decepticon_sdk-1.1.2/examples/skill/src/decepticon_example_skill/__init__.py +14 -0
- decepticon_sdk-1.1.2/examples/tool/README.md +23 -0
- decepticon_sdk-1.1.2/examples/tool/dist/.gitignore +1 -0
- decepticon_sdk-1.1.2/examples/tool/dist/decepticon_example_tool-0.0.1-py3-none-any.whl +0 -0
- decepticon_sdk-1.1.2/examples/tool/dist/decepticon_example_tool-0.0.1.tar.gz +0 -0
- decepticon_sdk-1.1.2/examples/tool/pyproject.toml +21 -0
- decepticon_sdk-1.1.2/examples/tool/src/decepticon_example_tool/__init__.py +22 -0
- decepticon_sdk-1.1.2/pyproject.toml +58 -0
- decepticon_sdk-1.1.2/tests/test_compat_shim_identity.py +98 -0
- decepticon_sdk-1.1.2/tests/test_examples.py +71 -0
- decepticon_sdk-1.1.2/tests/test_extra_routes.py +137 -0
- decepticon_sdk-1.1.2/tests/test_framework_import_smoke.py +163 -0
- decepticon_sdk-1.1.2/tests/test_sdk_api_stability.py +76 -0
- decepticon_sdk-1.1.2/tests/test_sdk_surface.py +125 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Environments
|
|
2
|
+
.env
|
|
3
|
+
.venv
|
|
4
|
+
env/
|
|
5
|
+
venv/
|
|
6
|
+
ENV/
|
|
7
|
+
env.bak/
|
|
8
|
+
venv.bak/
|
|
9
|
+
|
|
10
|
+
# Byte-compiled / optimized / DLL files
|
|
11
|
+
__pycache__/
|
|
12
|
+
*.py[cod]
|
|
13
|
+
*$py.class
|
|
14
|
+
|
|
15
|
+
# IDE and Editor
|
|
16
|
+
.vscode/
|
|
17
|
+
.idea/
|
|
18
|
+
*.swp
|
|
19
|
+
*.swo
|
|
20
|
+
|
|
21
|
+
# Python specific
|
|
22
|
+
build/
|
|
23
|
+
develop-eggs/
|
|
24
|
+
dist/
|
|
25
|
+
downloads/
|
|
26
|
+
eggs/
|
|
27
|
+
.eggs/
|
|
28
|
+
parts/
|
|
29
|
+
sdist/
|
|
30
|
+
var/
|
|
31
|
+
wheels/
|
|
32
|
+
share/python-wheels/
|
|
33
|
+
*.egg-info/
|
|
34
|
+
.installed.cfg
|
|
35
|
+
*.egg
|
|
36
|
+
|
|
37
|
+
# Coverage reports
|
|
38
|
+
htmlcov/
|
|
39
|
+
.tox/
|
|
40
|
+
.nox/
|
|
41
|
+
.coverage
|
|
42
|
+
.coverage.*
|
|
43
|
+
.cache
|
|
44
|
+
nosetests.xml
|
|
45
|
+
coverage.xml
|
|
46
|
+
*.cover
|
|
47
|
+
*.py,cover
|
|
48
|
+
.hypothesis/
|
|
49
|
+
.pytest_cache/
|
|
50
|
+
|
|
51
|
+
# Jupyter Notebook
|
|
52
|
+
.ipynb_checkpoints
|
|
53
|
+
|
|
54
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
55
|
+
__pypackages__/
|
|
56
|
+
|
|
57
|
+
# OS generated files
|
|
58
|
+
.DS_Store
|
|
59
|
+
.DS_Store?
|
|
60
|
+
._*
|
|
61
|
+
.Spotlight-V100
|
|
62
|
+
.Trashes
|
|
63
|
+
ehthumbs.db
|
|
64
|
+
Thumbs.db
|
|
65
|
+
|
|
66
|
+
# Node
|
|
67
|
+
node_modules/
|
|
68
|
+
|
|
69
|
+
# etc
|
|
70
|
+
sliver-py/
|
|
71
|
+
/src/
|
|
72
|
+
config/*.local.*
|
|
73
|
+
reference/
|
|
74
|
+
demo.tape
|
|
75
|
+
|
|
76
|
+
# Decepticon runtime data
|
|
77
|
+
.decepticon/
|
|
78
|
+
.dogfood/
|
|
79
|
+
workspace/
|
|
80
|
+
.bg-shell/
|
|
81
|
+
|
|
82
|
+
.langgraph_api/
|
|
83
|
+
.omc/
|
|
84
|
+
.ouroboros/
|
|
85
|
+
.gstack/
|
|
86
|
+
.claude/
|
|
87
|
+
clients/ee/
|
|
88
|
+
|
|
89
|
+
# Project-specific Claude Code instructions (private)
|
|
90
|
+
CLAUDE.md
|
|
91
|
+
references/octogent
|
|
92
|
+
clients/launcher/decepticon
|
|
93
|
+
|
|
94
|
+
# Benchmark
|
|
95
|
+
# Per-run artefacts (timestamped batch + evidence/ + reports/) are local-only.
|
|
96
|
+
# Curated per-challenge results (XBEN-*-24/) and the LEVEL3 index are committed.
|
|
97
|
+
# LangSmith trace dumps live as public share URLs in READMEs — never committed.
|
|
98
|
+
benchmark/results/*
|
|
99
|
+
!benchmark/results/.gitkeep
|
|
100
|
+
!benchmark/results/XBEN-*-24/
|
|
101
|
+
!benchmark/results/README.md
|
|
102
|
+
benchmark/results/XBEN-*-24/evidence/langsmith_trace.json
|
|
103
|
+
benchmark/results/XBEN-*-24/evidence/iter14_pass.json
|
|
104
|
+
benchmark/results/XBEN-*-24/evidence/*_iter*.json
|
|
105
|
+
# Per-run timestamped subdirs inside XBEN-*-24/ are local dev artefacts.
|
|
106
|
+
# The flat root (report.json / report.md / evidence/summary.{json,md}) is the
|
|
107
|
+
# committed PASS record — see benchmark/results/README.md for the policy.
|
|
108
|
+
benchmark/results/XBEN-*-24/2[0-9][0-9][0-9][0-9][0-9][0-9][0-9]_*/
|
|
109
|
+
benchmark/workspaces/
|
|
110
|
+
|
|
111
|
+
# Codex / OMX runtime artefacts (never committed)
|
|
112
|
+
.omx/
|
|
113
|
+
.env.bak.*
|
|
114
|
+
benchmark/results/XBEN-*-24/run-loop*.log
|
|
115
|
+
|
|
116
|
+
# Git worktrees
|
|
117
|
+
.worktrees/
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: decepticon-sdk
|
|
3
|
+
Version: 1.1.2
|
|
4
|
+
Summary: Decepticon plugin author SDK: protocols, fixtures, scaffolding
|
|
5
|
+
Project-URL: Homepage, https://github.com/PurpleAILAB/Decepticon
|
|
6
|
+
Project-URL: Repository, https://github.com/PurpleAILAB/Decepticon
|
|
7
|
+
Author: Decepticon Team
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
Keywords: agents,ai,plugins,red-team,sdk,security
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Security
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Python: >=3.13
|
|
19
|
+
Requires-Dist: decepticon-core
|
|
20
|
+
Requires-Dist: typer[all]>=0.9.0
|
|
21
|
+
Provides-Extra: fixtures
|
|
22
|
+
Requires-Dist: decepticon; extra == 'fixtures'
|
|
23
|
+
Provides-Extra: testing
|
|
24
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'testing'
|
|
25
|
+
Requires-Dist: pytest>=8.0.0; extra == 'testing'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# decepticon-sdk
|
|
29
|
+
|
|
30
|
+
The Decepticon plugin author entrypoint. Re-exports the `decepticon-core`
|
|
31
|
+
contracts, ships pytest fixtures, and provides a scaffolding CLI for
|
|
32
|
+
creating new plugin packages.
|
|
33
|
+
|
|
34
|
+
A complete plugin can be written importing only from `decepticon_sdk`:
|
|
35
|
+
no underscore-prefixed framework internals required.
|
|
36
|
+
|
|
37
|
+
See the umbrella [`README.md`](../../README.md) and the design spec at
|
|
38
|
+
[`docs/superpowers/specs/2026-05-23-core-framework-sdk-split-design.md`](../../docs/superpowers/specs/2026-05-23-core-framework-sdk-split-design.md).
|
|
39
|
+
|
|
40
|
+
## Install
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install decepticon-sdk
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
For test fixtures and scaffolding:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install "decepticon-sdk[testing]" # FakeBackend / FakeLLM
|
|
50
|
+
pip install "decepticon-sdk[fixtures]" # live framework-backed fixtures
|
|
51
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# decepticon-sdk
|
|
2
|
+
|
|
3
|
+
The Decepticon plugin author entrypoint. Re-exports the `decepticon-core`
|
|
4
|
+
contracts, ships pytest fixtures, and provides a scaffolding CLI for
|
|
5
|
+
creating new plugin packages.
|
|
6
|
+
|
|
7
|
+
A complete plugin can be written importing only from `decepticon_sdk`:
|
|
8
|
+
no underscore-prefixed framework internals required.
|
|
9
|
+
|
|
10
|
+
See the umbrella [`README.md`](../../README.md) and the design spec at
|
|
11
|
+
[`docs/superpowers/specs/2026-05-23-core-framework-sdk-split-design.md`](../../docs/superpowers/specs/2026-05-23-core-framework-sdk-split-design.md).
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install decepticon-sdk
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
For test fixtures and scaffolding:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install "decepticon-sdk[testing]" # FakeBackend / FakeLLM
|
|
23
|
+
pip install "decepticon-sdk[fixtures]" # live framework-backed fixtures
|
|
24
|
+
```
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""decepticon-sdk — plugin author entrypoint for the Decepticon framework.
|
|
2
|
+
|
|
3
|
+
A complete Decepticon plugin can be written importing only from this
|
|
4
|
+
package: every protocol, contract, registry, and helper plugin authors
|
|
5
|
+
need is exported here. No underscore-prefixed framework internals are
|
|
6
|
+
required.
|
|
7
|
+
|
|
8
|
+
Phase 3 surface:
|
|
9
|
+
|
|
10
|
+
* ``decepticon_sdk`` (this module) — re-exports the public
|
|
11
|
+
``decepticon-core`` API (types, protocols, contracts, registries,
|
|
12
|
+
plugin loader). Tracks the framework's release version (current
|
|
13
|
+
series: 1.1.x → 1.1.2 with this redesign; shim removal at 2.0.0).
|
|
14
|
+
Public names listed in ``__all__`` are SemVer-stable.
|
|
15
|
+
* ``decepticon_sdk.testing`` — pytest fixtures and fakes
|
|
16
|
+
(``FakeBackend``, ``FakeLLM``, ``FakeSandbox``) for hermetic
|
|
17
|
+
plugin tests that don't need a live framework.
|
|
18
|
+
|
|
19
|
+
Deferred from Phase 3 (follow-up commits):
|
|
20
|
+
|
|
21
|
+
* ``decepticon-sdk plugin new --kind=...`` scaffolding CLI
|
|
22
|
+
* Runnable example plugin per ``kind`` under ``examples/``
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
# Re-export the decepticon-core contracts so plugin authors only need
|
|
28
|
+
# a single import line:
|
|
29
|
+
#
|
|
30
|
+
# from decepticon_sdk import (
|
|
31
|
+
# BackendProtocol, MiddlewareProtocol, ToolContribution,
|
|
32
|
+
# PluginBundle, SubAgentSpec, RoleRegistry, ...
|
|
33
|
+
# )
|
|
34
|
+
from decepticon_core.contracts.contributions import (
|
|
35
|
+
MiddlewareContribution,
|
|
36
|
+
PromptContribution,
|
|
37
|
+
SafetyDeclaration,
|
|
38
|
+
SubAgentContribution,
|
|
39
|
+
ToolContribution,
|
|
40
|
+
)
|
|
41
|
+
from decepticon_core.contracts.slots import (
|
|
42
|
+
SAFETY_CRITICAL_SLOTS,
|
|
43
|
+
SLOTS_PER_ROLE,
|
|
44
|
+
MiddlewareSlot,
|
|
45
|
+
)
|
|
46
|
+
from decepticon_core.plugin_loader import (
|
|
47
|
+
PluginBundle,
|
|
48
|
+
SubAgentSpec,
|
|
49
|
+
is_bundle_enabled,
|
|
50
|
+
)
|
|
51
|
+
from decepticon_core.protocols import (
|
|
52
|
+
AgentProtocol,
|
|
53
|
+
BackendProtocol,
|
|
54
|
+
CallbackProtocol,
|
|
55
|
+
LLMProtocol,
|
|
56
|
+
MiddlewareProtocol,
|
|
57
|
+
SandboxProtocol,
|
|
58
|
+
ToolProtocol,
|
|
59
|
+
)
|
|
60
|
+
from decepticon_core.registry import (
|
|
61
|
+
PluginConflictWarning,
|
|
62
|
+
PluginInfo,
|
|
63
|
+
PluginRegistry,
|
|
64
|
+
RoleRegistry,
|
|
65
|
+
RoleResolution,
|
|
66
|
+
RoleSpec,
|
|
67
|
+
SafetyRegistry,
|
|
68
|
+
SkillSourceRegistry,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
__version__ = "0.0.0"
|
|
72
|
+
|
|
73
|
+
__all__ = [
|
|
74
|
+
# Protocols (decepticon_core.protocols)
|
|
75
|
+
"AgentProtocol",
|
|
76
|
+
"BackendProtocol",
|
|
77
|
+
"CallbackProtocol",
|
|
78
|
+
"LLMProtocol",
|
|
79
|
+
"MiddlewareProtocol",
|
|
80
|
+
"SandboxProtocol",
|
|
81
|
+
"ToolProtocol",
|
|
82
|
+
# Slot enum + constants (decepticon_core.contracts.slots)
|
|
83
|
+
"MiddlewareSlot",
|
|
84
|
+
"SAFETY_CRITICAL_SLOTS",
|
|
85
|
+
"SLOTS_PER_ROLE",
|
|
86
|
+
# Plugin contributions (decepticon_core.contracts.contributions)
|
|
87
|
+
"MiddlewareContribution",
|
|
88
|
+
"PromptContribution",
|
|
89
|
+
"SafetyDeclaration",
|
|
90
|
+
"SubAgentContribution",
|
|
91
|
+
"ToolContribution",
|
|
92
|
+
# Plugin loader (decepticon_core.plugin_loader)
|
|
93
|
+
"PluginBundle",
|
|
94
|
+
"SubAgentSpec",
|
|
95
|
+
"is_bundle_enabled",
|
|
96
|
+
# Registries (decepticon_core.registry)
|
|
97
|
+
"PluginConflictWarning",
|
|
98
|
+
"PluginInfo",
|
|
99
|
+
"PluginRegistry",
|
|
100
|
+
"RoleRegistry",
|
|
101
|
+
"RoleResolution",
|
|
102
|
+
"RoleSpec",
|
|
103
|
+
"SafetyRegistry",
|
|
104
|
+
"SkillSourceRegistry",
|
|
105
|
+
# Version
|
|
106
|
+
"__version__",
|
|
107
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Plugin scaffolding CLI — ``decepticon-sdk plugin new --kind=...``.
|
|
2
|
+
|
|
3
|
+
Phase 3 of the core/framework/sdk split (per
|
|
4
|
+
``docs/superpowers/specs/2026-05-23-core-framework-sdk-split-design.md``)
|
|
5
|
+
ships a typer-based generator so plugin authors run one command and
|
|
6
|
+
get a buildable plugin package: pyproject.toml, src/<name>/__init__.py
|
|
7
|
+
wired to the right entry-point group, and a short README.
|
|
8
|
+
|
|
9
|
+
Six plugin kinds covered: ``tool``, ``middleware``, ``agent``,
|
|
10
|
+
``callback``, ``skill``, ``prompt``.
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
|
|
14
|
+
decepticon-sdk plugin new --kind=middleware --name=my-plugin --path=./my-plugin
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from decepticon_sdk.scaffold.cli import app
|
|
20
|
+
|
|
21
|
+
__all__ = ["app"]
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""``decepticon-sdk plugin new`` — typer-based scaffold CLI.
|
|
2
|
+
|
|
3
|
+
Generates a buildable plugin package for one of the six supported
|
|
4
|
+
plugin kinds. The output directory contains ``pyproject.toml``,
|
|
5
|
+
``README.md``, and ``src/<module>/__init__.py`` wired to the matching
|
|
6
|
+
``decepticon.<group>`` entry-point group.
|
|
7
|
+
|
|
8
|
+
Sample run::
|
|
9
|
+
|
|
10
|
+
decepticon-sdk plugin new --kind=middleware --name=my-plugin --path=./my-plugin
|
|
11
|
+
cd my-plugin
|
|
12
|
+
uv build
|
|
13
|
+
pip install dist/*.whl
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
import typer
|
|
21
|
+
|
|
22
|
+
from decepticon_sdk.scaffold.templates import TEMPLATES, pyproject_for
|
|
23
|
+
|
|
24
|
+
app = typer.Typer(
|
|
25
|
+
name="decepticon-sdk",
|
|
26
|
+
help="Decepticon plugin author SDK — scaffold + utilities.",
|
|
27
|
+
no_args_is_help=True,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
plugin_app = typer.Typer(name="plugin", help="Plugin scaffolding subcommands.")
|
|
31
|
+
app.add_typer(plugin_app)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _normalize_module_name(plugin_name: str) -> str:
|
|
35
|
+
"""Convert ``my-plugin`` (PyPI-style) into ``my_plugin`` (module-style)."""
|
|
36
|
+
return plugin_name.replace("-", "_")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@plugin_app.command("new")
|
|
40
|
+
def plugin_new(
|
|
41
|
+
kind: str = typer.Option(
|
|
42
|
+
...,
|
|
43
|
+
"--kind",
|
|
44
|
+
help=f"Plugin kind. One of: {', '.join(sorted(TEMPLATES))}",
|
|
45
|
+
),
|
|
46
|
+
name: str = typer.Option(
|
|
47
|
+
...,
|
|
48
|
+
"--name",
|
|
49
|
+
help="Plugin name (PyPI-style, e.g. 'my-decepticon-plugin').",
|
|
50
|
+
),
|
|
51
|
+
path: Path = typer.Option(
|
|
52
|
+
...,
|
|
53
|
+
"--path",
|
|
54
|
+
help="Target directory. Created if absent.",
|
|
55
|
+
),
|
|
56
|
+
force: bool = typer.Option(
|
|
57
|
+
False,
|
|
58
|
+
"--force",
|
|
59
|
+
help="Overwrite existing files in --path.",
|
|
60
|
+
),
|
|
61
|
+
) -> None:
|
|
62
|
+
"""Scaffold a new Decepticon plugin package."""
|
|
63
|
+
if kind not in TEMPLATES:
|
|
64
|
+
raise typer.BadParameter(f"unknown --kind={kind!r}; expected one of {sorted(TEMPLATES)}")
|
|
65
|
+
template = TEMPLATES[kind]
|
|
66
|
+
module_name = _normalize_module_name(name)
|
|
67
|
+
|
|
68
|
+
target = path.resolve()
|
|
69
|
+
src_dir = target / "src" / module_name
|
|
70
|
+
pyproject_path = target / "pyproject.toml"
|
|
71
|
+
readme_path = target / "README.md"
|
|
72
|
+
init_path = src_dir / "__init__.py"
|
|
73
|
+
|
|
74
|
+
if not force and pyproject_path.exists():
|
|
75
|
+
raise typer.BadParameter(f"{pyproject_path} already exists; pass --force to overwrite")
|
|
76
|
+
|
|
77
|
+
src_dir.mkdir(parents=True, exist_ok=True)
|
|
78
|
+
|
|
79
|
+
pyproject_path.write_text(
|
|
80
|
+
pyproject_for(
|
|
81
|
+
plugin_name=name,
|
|
82
|
+
module_name=module_name,
|
|
83
|
+
group=template.entry_point_group,
|
|
84
|
+
),
|
|
85
|
+
encoding="utf-8",
|
|
86
|
+
)
|
|
87
|
+
readme_path.write_text(
|
|
88
|
+
template.readme_body.format(
|
|
89
|
+
plugin_name=name,
|
|
90
|
+
kind=kind,
|
|
91
|
+
entry_point_group=template.entry_point_group,
|
|
92
|
+
),
|
|
93
|
+
encoding="utf-8",
|
|
94
|
+
)
|
|
95
|
+
init_path.write_text(
|
|
96
|
+
template.module_body.format(plugin_name=module_name),
|
|
97
|
+
encoding="utf-8",
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
typer.echo(f"created decepticon plugin '{name}' (kind={kind}) at {target}")
|
|
101
|
+
typer.echo(f" pyproject: {pyproject_path.relative_to(target.parent)}")
|
|
102
|
+
typer.echo(f" module: {init_path.relative_to(target.parent)}")
|
|
103
|
+
typer.echo(f" entry-point: {template.entry_point_group}")
|
|
104
|
+
typer.echo("")
|
|
105
|
+
typer.echo("Next steps:")
|
|
106
|
+
typer.echo(
|
|
107
|
+
f" cd {target.relative_to(Path.cwd()) if target.is_relative_to(Path.cwd()) else target}"
|
|
108
|
+
)
|
|
109
|
+
typer.echo(" uv build")
|
|
110
|
+
typer.echo(" pip install dist/*.whl")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
if __name__ == "__main__":
|
|
114
|
+
app()
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"""Inline templates for the ``decepticon-sdk plugin new`` scaffolder.
|
|
2
|
+
|
|
3
|
+
Each plugin ``kind`` maps to a ``ScaffoldTemplate`` that knows the
|
|
4
|
+
entry-point group, the stub module body, and the README copy. Inline
|
|
5
|
+
templates (no Jinja2) keep the SDK install lean — string ``.format()``
|
|
6
|
+
covers everything the scaffolder needs.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class ScaffoldTemplate:
|
|
16
|
+
"""One plugin-kind scaffold spec."""
|
|
17
|
+
|
|
18
|
+
kind: str
|
|
19
|
+
entry_point_group: str
|
|
20
|
+
module_body: str
|
|
21
|
+
readme_body: str
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
_TOOL_BODY = '''"""Custom tool contributed to Decepticon."""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
from decepticon_sdk import ToolProtocol
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class HelloTool:
|
|
32
|
+
"""Minimal ToolProtocol-compliant tool."""
|
|
33
|
+
|
|
34
|
+
name = "{plugin_name}"
|
|
35
|
+
description = "Hello-world tool scaffolded by decepticon-sdk."
|
|
36
|
+
|
|
37
|
+
def invoke(self, input: object, *, config: object | None = None) -> object:
|
|
38
|
+
del config
|
|
39
|
+
return f"hello from {{self.name}}: {{input}}"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_tools(role: str | None = None, **_: object) -> list[ToolProtocol]:
|
|
43
|
+
"""Plugin factory called by the framework's tool loader."""
|
|
44
|
+
del role
|
|
45
|
+
return [HelloTool()]
|
|
46
|
+
'''
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
_MIDDLEWARE_BODY = '''"""Custom middleware contributed to Decepticon."""
|
|
50
|
+
|
|
51
|
+
from __future__ import annotations
|
|
52
|
+
|
|
53
|
+
from decepticon_sdk import MiddlewareProtocol, MiddlewareSlot
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class HelloMiddleware:
|
|
57
|
+
"""Minimal MiddlewareProtocol-compliant middleware."""
|
|
58
|
+
|
|
59
|
+
name = "{plugin_name}"
|
|
60
|
+
slot: MiddlewareSlot | str = MiddlewareSlot.SKILLS
|
|
61
|
+
priority = 150
|
|
62
|
+
|
|
63
|
+
def wrap_model_call(self, state: object, runtime: object, config: object) -> object:
|
|
64
|
+
return state
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def get_middleware(role: str | None = None, **_: object) -> list[MiddlewareProtocol]:
|
|
68
|
+
"""Plugin factory called by the framework's middleware loader."""
|
|
69
|
+
del role
|
|
70
|
+
return [HelloMiddleware()]
|
|
71
|
+
'''
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
_AGENT_BODY = '''"""Custom agent contributed to Decepticon."""
|
|
75
|
+
|
|
76
|
+
from __future__ import annotations
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def get_agent() -> object:
|
|
80
|
+
"""Plugin factory returning a compiled agent (LangGraph CompiledGraph).
|
|
81
|
+
|
|
82
|
+
Replace this stub with your real agent construction — see
|
|
83
|
+
``decepticon.agents.standard.recon.create_recon_agent`` for an
|
|
84
|
+
example of the framework's factory pattern.
|
|
85
|
+
"""
|
|
86
|
+
raise NotImplementedError(
|
|
87
|
+
"{plugin_name}: replace get_agent() with your compiled-graph factory"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# LangGraph platform discovers ``graph`` as the module-level attribute.
|
|
92
|
+
graph = None
|
|
93
|
+
'''
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
_CALLBACK_BODY = '''"""Custom callback handler contributed to Decepticon."""
|
|
97
|
+
|
|
98
|
+
from __future__ import annotations
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class HelloCallback:
|
|
102
|
+
"""Minimal CallbackProtocol-compliant handler."""
|
|
103
|
+
|
|
104
|
+
def on_llm_start(self, *args: object, **kwargs: object) -> None:
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
def on_llm_end(self, *args: object, **kwargs: object) -> None:
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
def on_tool_start(self, *args: object, **kwargs: object) -> None:
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
def on_tool_end(self, *args: object, **kwargs: object) -> None:
|
|
114
|
+
return None
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def get_callbacks(role: str | None = None, **_: object) -> list[HelloCallback]:
|
|
118
|
+
"""Plugin factory called by the framework's callback loader."""
|
|
119
|
+
del role
|
|
120
|
+
return [HelloCallback()]
|
|
121
|
+
'''
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
_SKILL_BODY = '''"""Skill source paths contributed by this plugin.
|
|
125
|
+
|
|
126
|
+
The framework reads ``/skills/<bundle>/<role>/`` paths via the
|
|
127
|
+
``SkillsMiddleware``. Plugins ship skill markdown files as package data
|
|
128
|
+
and register the path prefix here.
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
from __future__ import annotations
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def get_skill_sources(role: str | None = None) -> list[str]:
|
|
135
|
+
"""Plugin factory called by the framework's skill loader."""
|
|
136
|
+
del role
|
|
137
|
+
return ["/skills/{plugin_name}/"]
|
|
138
|
+
'''
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
_PROMPT_BODY = '''"""Prompt fragments contributed to one or more roles."""
|
|
142
|
+
|
|
143
|
+
from __future__ import annotations
|
|
144
|
+
|
|
145
|
+
from decepticon_sdk import PromptContribution
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def get_contribution() -> PromptContribution:
|
|
149
|
+
"""Plugin factory called by the framework's prompt loader."""
|
|
150
|
+
return PromptContribution(
|
|
151
|
+
fragments={{"recon": "<{plugin_name}>...</{plugin_name}>"}},
|
|
152
|
+
mode="append",
|
|
153
|
+
roles=("recon",),
|
|
154
|
+
)
|
|
155
|
+
'''
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
_README_TEMPLATE = """# {plugin_name}
|
|
159
|
+
|
|
160
|
+
A Decepticon plugin ({kind}) scaffolded by ``decepticon-sdk plugin new``.
|
|
161
|
+
|
|
162
|
+
## Build + install
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
uv build
|
|
166
|
+
pip install dist/*.whl
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
After install, the framework's plugin loader discovers this contribution
|
|
170
|
+
via the ``{entry_point_group}`` entry-point group.
|
|
171
|
+
|
|
172
|
+
## Test
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
pip install decepticon-sdk[testing]
|
|
176
|
+
pytest
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Use ``decepticon_sdk.testing.FakeBackend`` / ``FakeLLM`` / ``FakeSandbox``
|
|
180
|
+
to write hermetic tests that don't need a live framework.
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
TEMPLATES: dict[str, ScaffoldTemplate] = {
|
|
185
|
+
"tool": ScaffoldTemplate(
|
|
186
|
+
kind="tool",
|
|
187
|
+
entry_point_group="decepticon.tools",
|
|
188
|
+
module_body=_TOOL_BODY,
|
|
189
|
+
readme_body=_README_TEMPLATE,
|
|
190
|
+
),
|
|
191
|
+
"middleware": ScaffoldTemplate(
|
|
192
|
+
kind="middleware",
|
|
193
|
+
entry_point_group="decepticon.middleware",
|
|
194
|
+
module_body=_MIDDLEWARE_BODY,
|
|
195
|
+
readme_body=_README_TEMPLATE,
|
|
196
|
+
),
|
|
197
|
+
"agent": ScaffoldTemplate(
|
|
198
|
+
kind="agent",
|
|
199
|
+
entry_point_group="decepticon.agents",
|
|
200
|
+
module_body=_AGENT_BODY,
|
|
201
|
+
readme_body=_README_TEMPLATE,
|
|
202
|
+
),
|
|
203
|
+
"callback": ScaffoldTemplate(
|
|
204
|
+
kind="callback",
|
|
205
|
+
entry_point_group="decepticon.callbacks",
|
|
206
|
+
module_body=_CALLBACK_BODY,
|
|
207
|
+
readme_body=_README_TEMPLATE,
|
|
208
|
+
),
|
|
209
|
+
"skill": ScaffoldTemplate(
|
|
210
|
+
kind="skill",
|
|
211
|
+
entry_point_group="decepticon.skills",
|
|
212
|
+
module_body=_SKILL_BODY,
|
|
213
|
+
readme_body=_README_TEMPLATE,
|
|
214
|
+
),
|
|
215
|
+
"prompt": ScaffoldTemplate(
|
|
216
|
+
kind="prompt",
|
|
217
|
+
entry_point_group="decepticon.prompts",
|
|
218
|
+
module_body=_PROMPT_BODY,
|
|
219
|
+
readme_body=_README_TEMPLATE,
|
|
220
|
+
),
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def pyproject_for(*, plugin_name: str, module_name: str, group: str) -> str:
|
|
225
|
+
"""Return a buildable pyproject.toml for the scaffolded plugin."""
|
|
226
|
+
return f"""[project]
|
|
227
|
+
name = "{plugin_name}"
|
|
228
|
+
version = "0.0.1"
|
|
229
|
+
description = "Decepticon plugin scaffolded by decepticon-sdk."
|
|
230
|
+
readme = "README.md"
|
|
231
|
+
license = {{ text = "Apache-2.0" }}
|
|
232
|
+
requires-python = ">=3.13"
|
|
233
|
+
|
|
234
|
+
dependencies = [
|
|
235
|
+
"decepticon-sdk",
|
|
236
|
+
]
|
|
237
|
+
|
|
238
|
+
[project.entry-points."{group}"]
|
|
239
|
+
{module_name} = "{module_name}"
|
|
240
|
+
|
|
241
|
+
[build-system]
|
|
242
|
+
requires = ["hatchling"]
|
|
243
|
+
build-backend = "hatchling.build"
|
|
244
|
+
|
|
245
|
+
[tool.hatch.build.targets.wheel]
|
|
246
|
+
packages = ["src/{module_name}"]
|
|
247
|
+
"""
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
__all__ = ["TEMPLATES", "ScaffoldTemplate", "pyproject_for"]
|