pactkit-opencode 4.0.0__tar.gz → 4.1.0__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 (32) hide show
  1. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/PKG-INFO +2 -2
  2. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/pyproject.toml +2 -2
  3. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/src/pactkit_opencode/deployer.py +25 -10
  4. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/src/pactkit_opencode.egg-info/PKG-INFO +2 -2
  5. pactkit_opencode-4.1.0/src/pactkit_opencode.egg-info/requires.txt +1 -0
  6. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/tests/test_step8_declaration_search_producer.py +16 -4
  7. pactkit_opencode-4.0.0/src/pactkit_opencode.egg-info/requires.txt +0 -1
  8. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/README.md +0 -0
  9. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/setup.cfg +0 -0
  10. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/src/pactkit_opencode/__init__.py +0 -0
  11. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/src/pactkit_opencode/declarations.py +0 -0
  12. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/src/pactkit_opencode/enforcement.py +0 -0
  13. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/src/pactkit_opencode.egg-info/SOURCES.txt +0 -0
  14. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/src/pactkit_opencode.egg-info/dependency_links.txt +0 -0
  15. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/src/pactkit_opencode.egg-info/entry_points.txt +0 -0
  16. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/src/pactkit_opencode.egg-info/top_level.txt +0 -0
  17. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/tests/test_a1_config_recovery.py +0 -0
  18. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/tests/test_a2_mcp_retired.py +0 -0
  19. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/tests/test_bug035_opencode_dual_layer.py +0 -0
  20. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/tests/test_capability_contract.py +0 -0
  21. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/tests/test_deploy_opencode_parity.py +0 -0
  22. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/tests/test_gate_plugin_deploy.py +0 -0
  23. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/tests/test_host_body_snapshot.py +0 -0
  24. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/tests/test_opencode_enforcement_channel.py +0 -0
  25. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/tests/test_profile_contract.py +0 -0
  26. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/tests/test_step6_self_sufficiency.py +0 -0
  27. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/tests/test_story069_opencode_format.py +0 -0
  28. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/tests/test_story070_opencode_compliance.py +0 -0
  29. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/tests/test_story071_opencode_config_parity.py +0 -0
  30. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/tests/test_story073_command_model_routing.py +0 -0
  31. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/tests/test_story_work_units.py +0 -0
  32. {pactkit_opencode-4.0.0 → pactkit_opencode-4.1.0}/tests/test_wire_format_contract.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pactkit-opencode
3
- Version: 4.0.0
3
+ Version: 4.1.0
4
4
  Summary: PactKit adapter for OpenCode IDE
5
5
  License-Expression: MIT
6
6
  Requires-Python: >=3.10
7
- Requires-Dist: pactkit<4.1.0,>=4.0.0
7
+ Requires-Dist: pactkit<4.2.0,>=4.1.0
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pactkit-opencode"
7
- version = "4.0.0"
7
+ version = "4.1.0"
8
8
  description = "PactKit adapter for OpenCode IDE"
9
9
  requires-python = ">=3.10"
10
10
  license = "MIT"
11
11
  dependencies = [
12
- "pactkit>=4.0.0,<4.1.0",
12
+ "pactkit>=4.1.0,<4.2.0",
13
13
  ]
14
14
 
15
15
  [project.entry-points."pactkit.deployers"]
@@ -133,8 +133,22 @@ class OpenCodeDeployer(DeployerBase):
133
133
  deployment_claim = declarations.DEPLOYMENT_CLAIM
134
134
  input_translation = declarations.INPUT_TRANSLATION
135
135
 
136
- def deploy(self, config=None, target=None):
136
+ def deploy(self, config=None, target=None, project_root=None, no_redetect=False):
137
+ """Deploy PactKit configuration for OpenCode.
138
+
139
+ ``project_root`` is **not** the deploy root: OpenCode installs globally
140
+ under ``~/.config/opencode``, and the two MUST NOT be confused. It
141
+ names the project whose config, overlay and CI file are read/written —
142
+ those used to resolve from the caller's cwd, so ``pactkit -C
143
+ <other-project>`` read the wrong project's policy.
144
+
145
+ ``no_redetect`` is accepted because the Core dispatcher passes it
146
+ alongside ``project_root``; it is a no-op here — this deployer's
147
+ stack probe only reads the filesystem and never writes the stack
148
+ back into the config.
149
+ """
137
150
  opencode_root = Path(target) if target else Path.home() / ".config" / "opencode"
151
+ project_root = Path(project_root) if project_root is not None else Path.cwd()
138
152
 
139
153
  print("🚀 PactKit OpenCode Deployment")
140
154
 
@@ -150,7 +164,7 @@ class OpenCodeDeployer(DeployerBase):
150
164
  # STORY-slim-008 R1+R2: Load config from project-level pactkit.yaml
151
165
  from pactkit.config import find_pactkit_yaml
152
166
 
153
- project_yaml = find_pactkit_yaml()
167
+ project_yaml = find_pactkit_yaml(project_root)
154
168
 
155
169
  if config is not None:
156
170
  cfg = config
@@ -161,7 +175,7 @@ class OpenCodeDeployer(DeployerBase):
161
175
  cfg = load_config(project_yaml)
162
176
  else:
163
177
  cfg = {}
164
- cfg = activate_pactkit_maintainer_overlay(cfg, Path.cwd())
178
+ cfg = activate_pactkit_maintainer_overlay(cfg, project_root)
165
179
 
166
180
  enabled_agents = cfg.get("agents", sorted(VALID_AGENTS))
167
181
  enabled_skills = cfg.get("skills", sorted(VALID_SKILLS))
@@ -239,7 +253,6 @@ class OpenCodeDeployer(DeployerBase):
239
253
 
240
254
  ci_config = cfg.get("ci", {})
241
255
  ci_provider = ci_config.get("provider", "none") if isinstance(ci_config, dict) else "none"
242
- project_root = Path.cwd()
243
256
  _deploy_ci(ci_provider, project_root, cfg)
244
257
 
245
258
  # STORY-slim-139 R2: machine-readable deployment manifest
@@ -264,8 +277,8 @@ class OpenCodeDeployer(DeployerBase):
264
277
  )
265
278
 
266
279
  if target is None:
267
- self._generate_project_agents_md()
268
- self._generate_project_config()
280
+ self._generate_project_agents_md(project_root)
281
+ self._generate_project_config(project_root)
269
282
 
270
283
  # --- OpenCode-specific helpers ---
271
284
 
@@ -291,7 +304,7 @@ class OpenCodeDeployer(DeployerBase):
291
304
  return 1
292
305
 
293
306
  @staticmethod
294
- def _generate_project_config():
307
+ def _generate_project_config(project_root=None):
295
308
  """Write this host's pactkit.yaml when the project has none (Step 8 R-③-1).
296
309
 
297
310
  Before this existed, `.opencode/pactkit.yaml` was **declared** in
@@ -306,15 +319,17 @@ class OpenCodeDeployer(DeployerBase):
306
319
  `format="opencode"` closes both halves: this host produces its own
307
320
  copy, and no other host can produce this one's.
308
321
  """
309
- project_root = Path.cwd()
322
+ # Direct callers may omit the root; the deploy path always passes it.
323
+ project_root = Path(project_root) if project_root is not None else Path.cwd()
310
324
  if project_root.resolve() == Path.home().resolve():
311
325
  return
312
326
  generate_config_if_missing(format="opencode", project_root=project_root)
313
327
 
314
328
  @staticmethod
315
- def _generate_project_agents_md():
329
+ def _generate_project_agents_md(project_root=None):
316
330
  """Generate project-level AGENTS.md if it does not exist (STORY-slim-008 R4)."""
317
- project_root = Path.cwd()
331
+ # Direct callers may omit the root; the deploy path always passes it.
332
+ project_root = Path(project_root) if project_root is not None else Path.cwd()
318
333
  if project_root.resolve() == Path.home().resolve():
319
334
  return
320
335
  agents_md_path = project_root / "AGENTS.md"
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pactkit-opencode
3
- Version: 4.0.0
3
+ Version: 4.1.0
4
4
  Summary: PactKit adapter for OpenCode IDE
5
5
  License-Expression: MIT
6
6
  Requires-Python: >=3.10
7
- Requires-Dist: pactkit<4.1.0,>=4.0.0
7
+ Requires-Dist: pactkit<4.2.0,>=4.1.0
@@ -0,0 +1 @@
1
+ pactkit<4.2.0,>=4.1.0
@@ -130,17 +130,24 @@ class TestTheThreeFactsAgree:
130
130
  """内容来自 core 的 canonical generator,不是本仓的第二份模板。
131
131
 
132
132
  逐字段断言会在「core 改了格式而本仓漂移」时照样通过;比字节不会。
133
+
134
+ **两层各比各的生成器**(STORY-slim-20260928c7b15e04a216 Step 1a):
135
+ 共享根对 `generate_default_yaml`,宿主路径对 `generate_sparse_host_yaml`。
136
+ 只比其中一层,另一层就能悄悄漂移。
133
137
  """
134
138
  from pactkit.cleaners import detect_stacks
135
- from pactkit.config import generate_default_yaml
139
+ from pactkit.config import generate_default_yaml, generate_sparse_host_yaml
136
140
  from pactkit.profiles import get_profile
137
141
 
138
142
  from pactkit_opencode import OpenCodeDeployer
139
143
 
140
144
  OpenCodeDeployer().deploy()
141
145
 
142
- written = (project / get_profile("opencode").pactkit_yaml_path).read_text()
143
- assert written == generate_default_yaml(stack=detect_stacks(project))
146
+ shared = (project / "pactkit.yaml").read_text()
147
+ assert shared == generate_default_yaml(stack=detect_stacks(project))
148
+
149
+ host = (project / get_profile("opencode").pactkit_yaml_path).read_text()
150
+ assert host == generate_sparse_host_yaml()
144
151
 
145
152
  def test_the_config_is_valid_and_carries_the_format_version(self, project):
146
153
  from pactkit.config import VALID_STACKS
@@ -154,7 +161,12 @@ class TestTheThreeFactsAgree:
154
161
  (project / get_profile("opencode").pactkit_yaml_path).read_text()
155
162
  )
156
163
  assert data["schema_version"] == 1
157
- assert data["stack"] in VALID_STACKS
164
+ # 策略在共享根里;宿主层是**覆盖**,不是策略的第二份副本。
165
+ shared = yaml.safe_load((project / "pactkit.yaml").read_text())
166
+ assert shared["stack"] in VALID_STACKS
167
+ assert "stack" not in data, (
168
+ f"宿主层重复了项目策略:{sorted(data)}"
169
+ )
158
170
 
159
171
 
160
172
  class TestTheCrossWriteIsClosed:
@@ -1 +0,0 @@
1
- pactkit<4.1.0,>=4.0.0