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.
Files changed (53) hide show
  1. decepticon_sdk-1.1.2/.gitignore +117 -0
  2. decepticon_sdk-1.1.2/PKG-INFO +51 -0
  3. decepticon_sdk-1.1.2/README.md +24 -0
  4. decepticon_sdk-1.1.2/decepticon_sdk/__init__.py +107 -0
  5. decepticon_sdk-1.1.2/decepticon_sdk/py.typed +0 -0
  6. decepticon_sdk-1.1.2/decepticon_sdk/scaffold/__init__.py +21 -0
  7. decepticon_sdk-1.1.2/decepticon_sdk/scaffold/cli.py +114 -0
  8. decepticon_sdk-1.1.2/decepticon_sdk/scaffold/templates.py +250 -0
  9. decepticon_sdk-1.1.2/decepticon_sdk/testing/__init__.py +114 -0
  10. decepticon_sdk-1.1.2/examples/README.md +41 -0
  11. decepticon_sdk-1.1.2/examples/agent/README.md +23 -0
  12. decepticon_sdk-1.1.2/examples/agent/dist/.gitignore +1 -0
  13. decepticon_sdk-1.1.2/examples/agent/dist/decepticon_example_agent-0.0.1-py3-none-any.whl +0 -0
  14. decepticon_sdk-1.1.2/examples/agent/dist/decepticon_example_agent-0.0.1.tar.gz +0 -0
  15. decepticon_sdk-1.1.2/examples/agent/pyproject.toml +21 -0
  16. decepticon_sdk-1.1.2/examples/agent/src/decepticon_example_agent/__init__.py +19 -0
  17. decepticon_sdk-1.1.2/examples/callback/README.md +23 -0
  18. decepticon_sdk-1.1.2/examples/callback/dist/.gitignore +1 -0
  19. decepticon_sdk-1.1.2/examples/callback/dist/decepticon_example_callback-0.0.1-py3-none-any.whl +0 -0
  20. decepticon_sdk-1.1.2/examples/callback/dist/decepticon_example_callback-0.0.1.tar.gz +0 -0
  21. decepticon_sdk-1.1.2/examples/callback/pyproject.toml +21 -0
  22. decepticon_sdk-1.1.2/examples/callback/src/decepticon_example_callback/__init__.py +25 -0
  23. decepticon_sdk-1.1.2/examples/middleware/README.md +23 -0
  24. decepticon_sdk-1.1.2/examples/middleware/dist/.gitignore +1 -0
  25. decepticon_sdk-1.1.2/examples/middleware/dist/decepticon_example_middleware-0.0.1-py3-none-any.whl +0 -0
  26. decepticon_sdk-1.1.2/examples/middleware/dist/decepticon_example_middleware-0.0.1.tar.gz +0 -0
  27. decepticon_sdk-1.1.2/examples/middleware/pyproject.toml +21 -0
  28. decepticon_sdk-1.1.2/examples/middleware/src/decepticon_example_middleware/__init__.py +22 -0
  29. decepticon_sdk-1.1.2/examples/prompt/README.md +23 -0
  30. decepticon_sdk-1.1.2/examples/prompt/dist/.gitignore +1 -0
  31. decepticon_sdk-1.1.2/examples/prompt/dist/decepticon_example_prompt-0.0.1-py3-none-any.whl +0 -0
  32. decepticon_sdk-1.1.2/examples/prompt/dist/decepticon_example_prompt-0.0.1.tar.gz +0 -0
  33. decepticon_sdk-1.1.2/examples/prompt/pyproject.toml +21 -0
  34. decepticon_sdk-1.1.2/examples/prompt/src/decepticon_example_prompt/__init__.py +14 -0
  35. decepticon_sdk-1.1.2/examples/skill/README.md +23 -0
  36. decepticon_sdk-1.1.2/examples/skill/dist/.gitignore +1 -0
  37. decepticon_sdk-1.1.2/examples/skill/dist/decepticon_example_skill-0.0.1-py3-none-any.whl +0 -0
  38. decepticon_sdk-1.1.2/examples/skill/dist/decepticon_example_skill-0.0.1.tar.gz +0 -0
  39. decepticon_sdk-1.1.2/examples/skill/pyproject.toml +21 -0
  40. decepticon_sdk-1.1.2/examples/skill/src/decepticon_example_skill/__init__.py +14 -0
  41. decepticon_sdk-1.1.2/examples/tool/README.md +23 -0
  42. decepticon_sdk-1.1.2/examples/tool/dist/.gitignore +1 -0
  43. decepticon_sdk-1.1.2/examples/tool/dist/decepticon_example_tool-0.0.1-py3-none-any.whl +0 -0
  44. decepticon_sdk-1.1.2/examples/tool/dist/decepticon_example_tool-0.0.1.tar.gz +0 -0
  45. decepticon_sdk-1.1.2/examples/tool/pyproject.toml +21 -0
  46. decepticon_sdk-1.1.2/examples/tool/src/decepticon_example_tool/__init__.py +22 -0
  47. decepticon_sdk-1.1.2/pyproject.toml +58 -0
  48. decepticon_sdk-1.1.2/tests/test_compat_shim_identity.py +98 -0
  49. decepticon_sdk-1.1.2/tests/test_examples.py +71 -0
  50. decepticon_sdk-1.1.2/tests/test_extra_routes.py +137 -0
  51. decepticon_sdk-1.1.2/tests/test_framework_import_smoke.py +163 -0
  52. decepticon_sdk-1.1.2/tests/test_sdk_api_stability.py +76 -0
  53. 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"]