pactkit-opencode 2.8.0__tar.gz → 2.9.1__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.
- {pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/PKG-INFO +2 -2
- pactkit_opencode-2.9.1/README.md +99 -0
- {pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/pyproject.toml +2 -2
- {pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/src/pactkit_opencode/deployer.py +9 -23
- {pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/src/pactkit_opencode.egg-info/PKG-INFO +2 -2
- {pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/src/pactkit_opencode.egg-info/SOURCES.txt +1 -0
- pactkit_opencode-2.9.1/src/pactkit_opencode.egg-info/requires.txt +1 -0
- {pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/tests/test_story073_command_model_routing.py +7 -17
- pactkit_opencode-2.8.0/src/pactkit_opencode.egg-info/requires.txt +0 -1
- {pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/setup.cfg +0 -0
- {pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/src/pactkit_opencode/__init__.py +0 -0
- {pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/src/pactkit_opencode.egg-info/dependency_links.txt +0 -0
- {pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/src/pactkit_opencode.egg-info/entry_points.txt +0 -0
- {pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/src/pactkit_opencode.egg-info/top_level.txt +0 -0
- {pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/tests/test_bug035_opencode_dual_layer.py +0 -0
- {pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/tests/test_deploy_opencode_parity.py +0 -0
- {pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/tests/test_story069_opencode_format.py +0 -0
- {pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/tests/test_story070_opencode_compliance.py +0 -0
- {pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/tests/test_story071_opencode_config_parity.py +0 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# pactkit-opencode
|
|
2
|
+
|
|
3
|
+
> PactKit PDCA workflow framework adapted for [OpenCode](https://opencode.ai).
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/pactkit-opencode/)
|
|
6
|
+
[](https://www.python.org/downloads/)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
|
|
9
|
+
## What is this?
|
|
10
|
+
|
|
11
|
+
**pactkit-opencode** brings the [PactKit](https://pactkit.dev) spec-driven development workflow to OpenCode IDE. It deploys:
|
|
12
|
+
|
|
13
|
+
- **Commands** (`commands/*.md`) — 11 PDCA workflow commands, auto-discovered by OpenCode (invoked via `/project-plan`, `/project-act`, etc.)
|
|
14
|
+
- **Skills** (`skills/*/SKILL.md`) — 10 embedded skills loaded by AI agent on demand
|
|
15
|
+
- **Agents** (`agents/*.md`) — 9 specialized agent definitions
|
|
16
|
+
- **Rules** (`rules/*.md`) — Governance rules loaded per-command
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install pactkit
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
> `pactkit-opencode` is automatically installed as a dependency of `pactkit`.
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Deploy PactKit to OpenCode (~/.config/opencode/)
|
|
30
|
+
pactkit init --format opencode
|
|
31
|
+
|
|
32
|
+
# Update when pactkit is upgraded
|
|
33
|
+
pactkit upgrade --format opencode
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Then in OpenCode, use commands like:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
/project-plan "Add user authentication"
|
|
40
|
+
/project-act STORY-001
|
|
41
|
+
/project-done
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## What Gets Deployed
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
~/.config/opencode/
|
|
48
|
+
├── AGENTS.md # Global constitution
|
|
49
|
+
├── rules/ # Governance rule modules
|
|
50
|
+
├── commands/ # 11 PDCA command playbooks (auto-discovered by OpenCode)
|
|
51
|
+
├── agents/ # 9 agent definitions (mode: subagent)
|
|
52
|
+
├── skills/ # 10 skill packages (AI agent loads on demand)
|
|
53
|
+
└── opencode.json # Global config (model routing, instructions)
|
|
54
|
+
|
|
55
|
+
./ # Project root
|
|
56
|
+
├── .opencode/
|
|
57
|
+
│ ├── pactkit.yaml # Project config
|
|
58
|
+
│ └── AGENTS.local.md # Your custom instructions (never overwritten)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## OpenCode Architecture
|
|
62
|
+
|
|
63
|
+
OpenCode has two separate mechanisms for extending the AI assistant:
|
|
64
|
+
|
|
65
|
+
| Mechanism | Location | Invocation | Purpose |
|
|
66
|
+
|-----------|----------|------------|---------|
|
|
67
|
+
| **Commands** | `commands/*.md` | User types `/command-name` | PDCA workflow entry points |
|
|
68
|
+
| **Skills** | `skills/*/SKILL.md` | AI agent loads via `skill()` | Embedded tools (visualize, board, scaffold) |
|
|
69
|
+
|
|
70
|
+
Commands are auto-discovered from `commands/*.md` — no `opencode.json` configuration needed.
|
|
71
|
+
|
|
72
|
+
## Key Differences from Claude Code
|
|
73
|
+
|
|
74
|
+
| Feature | Claude Code | OpenCode |
|
|
75
|
+
|---------|-------------|----------|
|
|
76
|
+
| Commands | Skills-only (`skills/project-*/SKILL.md`) | Dual: `commands/` + `skills/` |
|
|
77
|
+
| Config dir | `~/.claude/` | `~/.config/opencode/` |
|
|
78
|
+
| Agent format | Native multi-agent | `mode: subagent` |
|
|
79
|
+
| Model routing | Via skill frontmatter | Via `opencode.json` command entries |
|
|
80
|
+
| Rule loading | `@import` directives | Inline embedding per command |
|
|
81
|
+
|
|
82
|
+
## Development
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
git clone https://github.com/pactkit/pactkit-opencode.git
|
|
86
|
+
cd pactkit-opencode
|
|
87
|
+
pip install -e .
|
|
88
|
+
pytest tests/ -v
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## License
|
|
92
|
+
|
|
93
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
94
|
+
|
|
95
|
+
## Related Projects
|
|
96
|
+
|
|
97
|
+
- [PactKit](https://pactkit.dev) — Core framework
|
|
98
|
+
- [pactkit-codex](https://github.com/pactkit/pactkit-codex) — Adapter for OpenAI Codex CLI
|
|
99
|
+
- [OpenCode](https://opencode.ai) — AI-powered coding IDE
|
|
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pactkit-opencode"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.9.1"
|
|
8
8
|
description = "PactKit adapter for OpenCode IDE"
|
|
9
9
|
requires-python = ">=3.10"
|
|
10
10
|
license = "MIT"
|
|
11
11
|
dependencies = [
|
|
12
|
-
"pactkit>=2.
|
|
12
|
+
"pactkit>=2.9.0",
|
|
13
13
|
]
|
|
14
14
|
|
|
15
15
|
[project.entry-points."pactkit.deployers"]
|
|
@@ -297,33 +297,19 @@ class OpenCodeDeployer(DeployerBase):
|
|
|
297
297
|
config["instructions"] = existing
|
|
298
298
|
|
|
299
299
|
# OpenCode auto-discovers commands from commands/*.md directory.
|
|
300
|
-
# opencode.json "command"
|
|
301
|
-
#
|
|
300
|
+
# opencode.json "command" section requires BOTH "model" AND "template" fields.
|
|
301
|
+
# Since we don't control template content (it's inline prompt text, not a file path),
|
|
302
|
+
# we must NOT write model-only entries — they cause OpenCode validation errors.
|
|
303
|
+
# Clean up any PactKit-managed command entries that lack template.
|
|
302
304
|
cmd_config = config.get("command", {})
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
for k in stale_keys:
|
|
307
|
-
del cmd_config[k]
|
|
308
|
-
# Remove legacy "template" fields (was incorrectly treated as file path)
|
|
309
|
-
for cmd_name in list(cmd_config):
|
|
310
|
-
if cmd_name.startswith("project-") and "template" in cmd_config[cmd_name]:
|
|
311
|
-
del cmd_config[cmd_name]["template"]
|
|
312
|
-
# Remove entry entirely if only template was present
|
|
313
|
-
if not cmd_config[cmd_name]:
|
|
314
|
-
del cmd_config[cmd_name]
|
|
315
|
-
|
|
316
|
-
# Apply model routing
|
|
317
|
-
if command_models and providers:
|
|
318
|
-
for cmd_name, model_short in command_models.items():
|
|
319
|
-
model_id = OpenCodeDeployer._resolve_opencode_model_id(model_short, providers)
|
|
320
|
-
if model_id:
|
|
321
|
-
if cmd_name not in cmd_config:
|
|
322
|
-
cmd_config[cmd_name] = {}
|
|
323
|
-
cmd_config[cmd_name]["model"] = model_id
|
|
305
|
+
for cmd_name in list(cmd_config):
|
|
306
|
+
if cmd_name.startswith("project-") and "template" not in cmd_config[cmd_name]:
|
|
307
|
+
del cmd_config[cmd_name]
|
|
324
308
|
|
|
325
309
|
if cmd_config:
|
|
326
310
|
config["command"] = cmd_config
|
|
311
|
+
else:
|
|
312
|
+
config.pop("command", None)
|
|
327
313
|
|
|
328
314
|
content = json.dumps(config, indent=2, ensure_ascii=False) + "\n"
|
|
329
315
|
atomic_write(json_path, content)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pactkit>=2.9.0
|
{pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/tests/test_story073_command_model_routing.py
RENAMED
|
@@ -40,26 +40,16 @@ class TestAC1CommandModel:
|
|
|
40
40
|
deploy(format="opencode", target=str(out))
|
|
41
41
|
return out
|
|
42
42
|
|
|
43
|
-
def
|
|
44
|
-
"""
|
|
43
|
+
def test_no_model_only_entries_in_opencode_json(self, tmp_path):
|
|
44
|
+
"""Model-only command entries must NOT be written — OpenCode requires both model AND template."""
|
|
45
45
|
out = self._deploy_with_providers(tmp_path)
|
|
46
46
|
data = json.loads((out / "opencode.json").read_text())
|
|
47
47
|
cmd_config = data.get("command", {})
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"""project-done model routing is in opencode.json command section."""
|
|
54
|
-
out = self._deploy_with_providers(tmp_path)
|
|
55
|
-
data = json.loads((out / "opencode.json").read_text())
|
|
56
|
-
assert "project-done" in data.get("command", {})
|
|
57
|
-
|
|
58
|
-
def test_check_model_in_opencode_json(self, tmp_path):
|
|
59
|
-
"""project-check model routing is in opencode.json command section."""
|
|
60
|
-
out = self._deploy_with_providers(tmp_path)
|
|
61
|
-
data = json.loads((out / "opencode.json").read_text())
|
|
62
|
-
assert "project-check" in data.get("command", {})
|
|
48
|
+
for cmd_name, cmd_val in cmd_config.items():
|
|
49
|
+
if cmd_name.startswith("project-"):
|
|
50
|
+
assert "template" in cmd_val, (
|
|
51
|
+
f"command.{cmd_name} has model but no template — OpenCode validation will fail"
|
|
52
|
+
)
|
|
63
53
|
|
|
64
54
|
def test_frontmatter_has_no_model(self, tmp_path):
|
|
65
55
|
"""Command frontmatter should NOT contain model: (routing is in opencode.json)."""
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
pactkit>=2.8.0
|
|
File without changes
|
|
File without changes
|
{pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/src/pactkit_opencode.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/src/pactkit_opencode.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/src/pactkit_opencode.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/tests/test_story070_opencode_compliance.py
RENAMED
|
File without changes
|
{pactkit_opencode-2.8.0 → pactkit_opencode-2.9.1}/tests/test_story071_opencode_config_parity.py
RENAMED
|
File without changes
|