arcgentic 0.2.2a3__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 (54) hide show
  1. arcgentic-0.2.2a3/.gitignore +38 -0
  2. arcgentic-0.2.2a3/PKG-INFO +71 -0
  3. arcgentic-0.2.2a3/README.md +48 -0
  4. arcgentic-0.2.2a3/pyproject.toml +73 -0
  5. arcgentic-0.2.2a3/src/arcgentic/__init__.py +5 -0
  6. arcgentic-0.2.2a3/src/arcgentic/adapters/__init__.py +77 -0
  7. arcgentic-0.2.2a3/src/arcgentic/adapters/_local_env.py +125 -0
  8. arcgentic-0.2.2a3/src/arcgentic/adapters/base.py +108 -0
  9. arcgentic-0.2.2a3/src/arcgentic/adapters/claude_code.py +223 -0
  10. arcgentic-0.2.2a3/src/arcgentic/adapters/codex_cli.py +145 -0
  11. arcgentic-0.2.2a3/src/arcgentic/adapters/cursor.py +136 -0
  12. arcgentic-0.2.2a3/src/arcgentic/adapters/inline.py +101 -0
  13. arcgentic-0.2.2a3/src/arcgentic/adapters/vscode_codex.py +143 -0
  14. arcgentic-0.2.2a3/src/arcgentic/audit_check.py +422 -0
  15. arcgentic-0.2.2a3/src/arcgentic/cli.py +396 -0
  16. arcgentic-0.2.2a3/src/arcgentic/hooks/__init__.py +1 -0
  17. arcgentic-0.2.2a3/src/arcgentic/hooks/quality_gate_enforce.py +235 -0
  18. arcgentic-0.2.2a3/src/arcgentic/hooks/round_boundary_lesson_scan.py +79 -0
  19. arcgentic-0.2.2a3/src/arcgentic/skills_impl/__init__.py +1 -0
  20. arcgentic-0.2.2a3/src/arcgentic/skills_impl/codify_lesson.py +118 -0
  21. arcgentic-0.2.2a3/src/arcgentic/skills_impl/cross_session_handoff.py +157 -0
  22. arcgentic-0.2.2a3/src/arcgentic/skills_impl/execute_round.py +695 -0
  23. arcgentic-0.2.2a3/src/arcgentic/skills_impl/plan_round.py +338 -0
  24. arcgentic-0.2.2a3/src/arcgentic/skills_impl/track_refs.py +221 -0
  25. arcgentic-0.2.2a3/src/arcgentic/source_rules.py +134 -0
  26. arcgentic-0.2.2a3/src/arcgentic/utils/__init__.py +2 -0
  27. arcgentic-0.2.2a3/src/arcgentic/utils/pattern_detection.py +229 -0
  28. arcgentic-0.2.2a3/tests/__init__.py +0 -0
  29. arcgentic-0.2.2a3/tests/fixtures/sample_self_audit.md +21 -0
  30. arcgentic-0.2.2a3/tests/integration/__init__.py +0 -0
  31. arcgentic-0.2.2a3/tests/integration/test_end_to_end_round.py +332 -0
  32. arcgentic-0.2.2a3/tests/unit/__init__.py +0 -0
  33. arcgentic-0.2.2a3/tests/unit/adapters/__init__.py +0 -0
  34. arcgentic-0.2.2a3/tests/unit/adapters/test_base.py +163 -0
  35. arcgentic-0.2.2a3/tests/unit/adapters/test_claude_code.py +415 -0
  36. arcgentic-0.2.2a3/tests/unit/adapters/test_codex_cli.py +284 -0
  37. arcgentic-0.2.2a3/tests/unit/adapters/test_cursor.py +229 -0
  38. arcgentic-0.2.2a3/tests/unit/adapters/test_detect.py +278 -0
  39. arcgentic-0.2.2a3/tests/unit/adapters/test_inline.py +156 -0
  40. arcgentic-0.2.2a3/tests/unit/adapters/test_local_env.py +231 -0
  41. arcgentic-0.2.2a3/tests/unit/adapters/test_vscode_codex.py +275 -0
  42. arcgentic-0.2.2a3/tests/unit/hooks/__init__.py +0 -0
  43. arcgentic-0.2.2a3/tests/unit/hooks/test_quality_gate_enforce.py +433 -0
  44. arcgentic-0.2.2a3/tests/unit/hooks/test_round_boundary_lesson_scan.py +47 -0
  45. arcgentic-0.2.2a3/tests/unit/skills_impl/__init__.py +0 -0
  46. arcgentic-0.2.2a3/tests/unit/skills_impl/test_codify_lesson.py +43 -0
  47. arcgentic-0.2.2a3/tests/unit/skills_impl/test_cross_session_handoff.py +67 -0
  48. arcgentic-0.2.2a3/tests/unit/skills_impl/test_execute_round.py +944 -0
  49. arcgentic-0.2.2a3/tests/unit/skills_impl/test_plan_round.py +561 -0
  50. arcgentic-0.2.2a3/tests/unit/skills_impl/test_track_refs.py +72 -0
  51. arcgentic-0.2.2a3/tests/unit/test_audit_check.py +622 -0
  52. arcgentic-0.2.2a3/tests/unit/test_cli.py +397 -0
  53. arcgentic-0.2.2a3/tests/unit/test_pattern_detection.py +58 -0
  54. arcgentic-0.2.2a3/tests/unit/test_source_rules.py +107 -0
@@ -0,0 +1,38 @@
1
+ # macOS
2
+ .DS_Store
3
+
4
+ # Editor
5
+ .vscode/
6
+ .idea/
7
+ *.swp
8
+ *.swo
9
+
10
+ # Claude Code agent state (worktree metadata + per-session settings)
11
+ .claude/
12
+
13
+ # Logs
14
+ *.log
15
+
16
+ # Node (for tooling)
17
+ node_modules/
18
+
19
+ # Python (for tooling / tests)
20
+ __pycache__/
21
+ *.pyc
22
+ .pytest_cache/
23
+ .mypy_cache/
24
+ .ruff_cache/
25
+
26
+ # Build artifacts
27
+ dist/
28
+ build/
29
+ *.egg-info/
30
+
31
+ # Dogfood state (gitignore by default; projects opt-in to committing it)
32
+ .agentic-rounds/
33
+
34
+ # Local-only references (huge, gitignored per arcgentic ref-first discipline)
35
+ references/
36
+ # But skill reference docs inside the plugin itself ARE tracked
37
+ !skills/*/references/
38
+ !skills/*/references/**
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.4
2
+ Name: arcgentic
3
+ Version: 0.2.2a3
4
+ Summary: Agentic harness for rigorous round-driven development — Python CLI for the arcgentic Claude Code plugin
5
+ Project-URL: Homepage, https://github.com/Arch1eSUN/Arcgentic
6
+ Project-URL: Repository, https://github.com/Arch1eSUN/Arcgentic
7
+ Author: Arc Studio
8
+ License: MIT
9
+ Keywords: agentic-harness,claude-code,ide-adapter,round-driven-development
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Topic :: Software Development :: Quality Assurance
15
+ Requires-Python: >=3.13
16
+ Requires-Dist: jsonschema>=4.0
17
+ Requires-Dist: pyyaml>=6.0
18
+ Provides-Extra: dev
19
+ Requires-Dist: mypy>=1.14; extra == 'dev'
20
+ Requires-Dist: pytest>=8.0; extra == 'dev'
21
+ Requires-Dist: ruff>=0.15; extra == 'dev'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # arcgentic toolkit
25
+
26
+ Python CLI + adapter layer for the `arcgentic` Claude Code plugin.
27
+
28
+ This is the **toolkit surface** of the arcgentic hybrid monorepo (see
29
+ [Spec Amendment 01](../docs/plans/2026-05-13-arcgentic-v0.2.0-spec-amendment-01-layout.md)
30
+ for why). The plugin surface (`skills/`, `agents/`, `hooks/`, `.githooks/` at repo root)
31
+ provides markdown contracts discoverable by Claude Code; this toolkit provides the
32
+ Python implementation that markdown skills shell out to.
33
+
34
+ ## Install (dev)
35
+
36
+ ```bash
37
+ cd toolkit
38
+ pip install -e ".[dev]"
39
+ arcgentic --help
40
+ ```
41
+
42
+ ## CLI commands
43
+
44
+ ```bash
45
+ arcgentic plan-round-impl --round R1.0 --type substrate-touching --anchor <sha40>
46
+ arcgentic execute-round-impl --round R1.0 --handoff docs/superpowers/plans/R1.0.md
47
+ arcgentic audit-check docs/audits/R1.0.md --strict-extended
48
+ arcgentic quality-gate-enforce --repo-root .
49
+ arcgentic validate-handoff docs/superpowers/plans/R1.0.md
50
+ arcgentic codify-lesson --audit-dir docs/audits
51
+ arcgentic track-refs add references/example --owner-repo owner/repo --round R1 --usage-evidence '{"pattern_only": true}'
52
+ arcgentic cross-session-handoff read
53
+ ```
54
+
55
+ ## Quality gates (run from `toolkit/`)
56
+
57
+ ```bash
58
+ mypy --strict src/ tests/
59
+ pytest --tb=no
60
+ ruff check .
61
+ ```
62
+
63
+ ## Layout
64
+
65
+ - `src/arcgentic/adapters/` — IDE adapter Protocol + implementations
66
+ - `src/arcgentic/skills_impl/` — skill implementation backends
67
+ - `src/arcgentic/audit_check.py` — mechanical audit fact checker
68
+ - `src/arcgentic/source_rules.py` — Moirai-derived source-rule contract validators
69
+ - `src/arcgentic/utils/pattern_detection.py` — repeated audit-pattern clustering for lessons
70
+ - `src/arcgentic/cli.py` — command-line bridge for skills, gates, and validators
71
+ - `tests/unit/`, `tests/integration/` — pytest suites
@@ -0,0 +1,48 @@
1
+ # arcgentic toolkit
2
+
3
+ Python CLI + adapter layer for the `arcgentic` Claude Code plugin.
4
+
5
+ This is the **toolkit surface** of the arcgentic hybrid monorepo (see
6
+ [Spec Amendment 01](../docs/plans/2026-05-13-arcgentic-v0.2.0-spec-amendment-01-layout.md)
7
+ for why). The plugin surface (`skills/`, `agents/`, `hooks/`, `.githooks/` at repo root)
8
+ provides markdown contracts discoverable by Claude Code; this toolkit provides the
9
+ Python implementation that markdown skills shell out to.
10
+
11
+ ## Install (dev)
12
+
13
+ ```bash
14
+ cd toolkit
15
+ pip install -e ".[dev]"
16
+ arcgentic --help
17
+ ```
18
+
19
+ ## CLI commands
20
+
21
+ ```bash
22
+ arcgentic plan-round-impl --round R1.0 --type substrate-touching --anchor <sha40>
23
+ arcgentic execute-round-impl --round R1.0 --handoff docs/superpowers/plans/R1.0.md
24
+ arcgentic audit-check docs/audits/R1.0.md --strict-extended
25
+ arcgentic quality-gate-enforce --repo-root .
26
+ arcgentic validate-handoff docs/superpowers/plans/R1.0.md
27
+ arcgentic codify-lesson --audit-dir docs/audits
28
+ arcgentic track-refs add references/example --owner-repo owner/repo --round R1 --usage-evidence '{"pattern_only": true}'
29
+ arcgentic cross-session-handoff read
30
+ ```
31
+
32
+ ## Quality gates (run from `toolkit/`)
33
+
34
+ ```bash
35
+ mypy --strict src/ tests/
36
+ pytest --tb=no
37
+ ruff check .
38
+ ```
39
+
40
+ ## Layout
41
+
42
+ - `src/arcgentic/adapters/` — IDE adapter Protocol + implementations
43
+ - `src/arcgentic/skills_impl/` — skill implementation backends
44
+ - `src/arcgentic/audit_check.py` — mechanical audit fact checker
45
+ - `src/arcgentic/source_rules.py` — Moirai-derived source-rule contract validators
46
+ - `src/arcgentic/utils/pattern_detection.py` — repeated audit-pattern clustering for lessons
47
+ - `src/arcgentic/cli.py` — command-line bridge for skills, gates, and validators
48
+ - `tests/unit/`, `tests/integration/` — pytest suites
@@ -0,0 +1,73 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "arcgentic"
7
+ version = "0.2.2-alpha.3"
8
+ description = "Agentic harness for rigorous round-driven development — Python CLI for the arcgentic Claude Code plugin"
9
+ readme = "README.md"
10
+ requires-python = ">=3.13"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Arc Studio" }]
13
+ keywords = ["agentic-harness", "round-driven-development", "claude-code", "ide-adapter"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Topic :: Software Development :: Quality Assurance",
20
+ ]
21
+ dependencies = [
22
+ "pyyaml>=6.0",
23
+ "jsonschema>=4.0",
24
+ ]
25
+
26
+ [project.optional-dependencies]
27
+ dev = [
28
+ "mypy>=1.14",
29
+ "pytest>=8.0",
30
+ "ruff>=0.15",
31
+ ]
32
+
33
+ [project.scripts]
34
+ arcgentic = "arcgentic.cli:main"
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/Arch1eSUN/Arcgentic"
38
+ Repository = "https://github.com/Arch1eSUN/Arcgentic"
39
+
40
+ [tool.hatch.build.targets.wheel]
41
+ packages = ["src/arcgentic"]
42
+
43
+ # === Quality gates per spec § 17 ===
44
+
45
+ [tool.mypy]
46
+ strict = true
47
+ python_version = "3.13"
48
+ files = ["src/", "tests/"]
49
+ exclude = ["build/", "dist/"]
50
+
51
+ [tool.pytest.ini_options]
52
+ testpaths = ["tests"]
53
+ python_files = ["test_*.py"]
54
+ addopts = "--strict-markers"
55
+ markers = [
56
+ "integration: slow integration tests (deselect with '-m \"not integration\"')",
57
+ ]
58
+
59
+ [tool.ruff]
60
+ line-length = 100
61
+ target-version = "py313"
62
+ exclude = ["build/", "dist/"]
63
+
64
+ [tool.ruff.lint]
65
+ select = [
66
+ "E", "W", "F", # default
67
+ "I", # isort
68
+ "UP", # pyupgrade
69
+ "B", # bugbear
70
+ "A", # builtins shadowing
71
+ "N", # pep8-naming
72
+ ]
73
+ ignore = []
@@ -0,0 +1,5 @@
1
+ """arcgentic — agentic harness for rigorous round-driven development."""
2
+
3
+ from __future__ import annotations
4
+
5
+ __version__ = "0.2.2-alpha.3"
@@ -0,0 +1,77 @@
1
+ """IDE adapter auto-detection.
2
+
3
+ `detect_adapter()` inspects environment variables, filesystem markers, and
4
+ binary availability to select the appropriate adapter for the current host.
5
+ Falls back to InlineAdapter if no LLM host can be confidently identified.
6
+
7
+ Detection rules (each: env-var checks use Python truthiness — empty-string
8
+ env-var values fall through, which is the intended behavior since empty-string
9
+ markers are semantically equivalent to unset):
10
+ 1. Claude Code — CLAUDE_CODE_SESSION (non-empty) OR ~/.claude/skills dir exists (home-relative)
11
+ 2. Cursor — CURSOR_SESSION (non-empty) OR .cursor/rules dir exists (CWD-relative)
12
+ 3. VSCode+Codex — VSCODE_PID (non-empty) AND `codex` binary on PATH
13
+ 4. Codex CLI — CODEX_SESSION (non-empty) OR `codex` binary on PATH
14
+ 5. Inline — fallback when no LLM host can be identified
15
+
16
+ Public API:
17
+ detect_adapter() -> IDEAdapter
18
+ IDEAdapter (re-export from .base for convenience)
19
+ AgentDispatchResult (re-export from .base)
20
+
21
+ Spec reference: docs/plans/2026-05-13-arcgentic-v0.2.0-spec.md § 3.6
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import os
27
+ from pathlib import Path
28
+ from shutil import which
29
+
30
+ from .base import AgentDispatchResult, IDEAdapter
31
+
32
+ __all__ = ["detect_adapter", "IDEAdapter", "AgentDispatchResult"]
33
+
34
+
35
+ def detect_adapter() -> IDEAdapter:
36
+ """Return the IDEAdapter best matching the current runtime environment.
37
+
38
+ Falls back to InlineAdapter if no LLM host can be detected. Callers should
39
+ NOT assume the returned adapter can dispatch agents (InlineAdapter can't
40
+ actually dispatch — it's a degraded fallback for dry-run / headless use).
41
+ """
42
+ # 1. Claude Code
43
+ if os.environ.get("CLAUDE_CODE_SESSION") or _has_dir("~/.claude/skills"):
44
+ from .claude_code import ClaudeCodeAdapter
45
+ return ClaudeCodeAdapter()
46
+
47
+ # rule 2 — Cursor (.cursor/rules is project-local, CWD-relative;
48
+ # detect_adapter() picks up the marker only when called from project root)
49
+ if os.environ.get("CURSOR_SESSION") or _has_dir(".cursor/rules"):
50
+ from .cursor import CursorAdapter
51
+ return CursorAdapter()
52
+
53
+ # 3. VSCode + Codex (must have BOTH a VSCode env marker AND codex binary)
54
+ if os.environ.get("VSCODE_PID") and which("codex"):
55
+ from .vscode_codex import VSCodeCodexAdapter
56
+ return VSCodeCodexAdapter()
57
+
58
+ # 4. Codex CLI standalone (CODEX_SESSION env, OR codex binary without VSCode)
59
+ if os.environ.get("CODEX_SESSION") or which("codex"):
60
+ from .codex_cli import CodexCLIAdapter
61
+ return CodexCLIAdapter()
62
+
63
+ # 5. Fallback
64
+ from .inline import InlineAdapter
65
+ return InlineAdapter()
66
+
67
+
68
+ def _has_dir(path: str) -> bool:
69
+ """Return True if `path` is a directory (after ~-expansion).
70
+
71
+ Paths starting with `~` are home-relative (expand to $HOME).
72
+ Other paths are interpreted relative to the current working directory.
73
+ Used by detect_adapter() for both home-scoped markers (e.g. ~/.claude/skills,
74
+ Claude Code installation) and project-scoped markers (e.g. .cursor/rules,
75
+ Cursor project rules).
76
+ """
77
+ return Path(path).expanduser().is_dir()
@@ -0,0 +1,125 @@
1
+ """Shared local-environment helpers for IDEAdapter implementations.
2
+
3
+ These functions implement filesystem / shell / git operations that are identical
4
+ across all non-canonical IDE adapters (Cursor / VSCode-Codex / Codex CLI / Inline).
5
+ The canonical ClaudeCodeAdapter inlines these directly; a future cleanup task may
6
+ unify it with this module.
7
+
8
+ These are module-level functions (NOT a class) because they have no state — each
9
+ call is a fresh subprocess or filesystem op. Adapters call them as
10
+ `_local_env.read_file(path)` etc.
11
+
12
+ Spec reference: docs/plans/2026-05-13-arcgentic-v0.2.0-spec.md § 3.3–§ 3.5
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import subprocess
18
+ from pathlib import Path
19
+
20
+
21
+ def read_file(path: str) -> str:
22
+ """Read a file and return its text content (utf-8)."""
23
+ return Path(path).read_text(encoding="utf-8")
24
+
25
+
26
+ def write_file(path: str, content: str) -> None:
27
+ """Write `content` to `path` (utf-8); creates or overwrites."""
28
+ Path(path).write_text(content, encoding="utf-8")
29
+
30
+
31
+ def edit_file(path: str, old: str, new: str) -> None:
32
+ """Replace exactly one occurrence of `old` with `new`.
33
+
34
+ Raises ValueError on zero or multi-match (identical contract to
35
+ ClaudeCodeAdapter.edit_file per spec § 3 IDEAdapter Protocol).
36
+ """
37
+ p = Path(path)
38
+ text = p.read_text(encoding="utf-8")
39
+ count = text.count(old)
40
+ if count == 0:
41
+ raise ValueError(f"edit_file: `old` not found in {path}")
42
+ if count > 1:
43
+ raise ValueError(f"edit_file: `old` appears {count} times in {path} (ambiguous)")
44
+ p.write_text(text.replace(old, new, 1), encoding="utf-8")
45
+
46
+
47
+ def shell(command: str, timeout_seconds: int = 120) -> tuple[str, int]:
48
+ """Run a shell command; return (stdout, exit_code).
49
+
50
+ On TimeoutExpired returns ('', 124) — same POSIX timeout convention as
51
+ ClaudeCodeAdapter.shell.
52
+ """
53
+ try:
54
+ result = subprocess.run(
55
+ command,
56
+ shell=True,
57
+ capture_output=True,
58
+ text=True,
59
+ timeout=timeout_seconds,
60
+ check=False,
61
+ )
62
+ return result.stdout, result.returncode
63
+ except subprocess.TimeoutExpired:
64
+ return "", 124
65
+
66
+
67
+ def _run_git(args: list[str], timeout_seconds: int = 60) -> tuple[str, str, int]:
68
+ """Run `git <args>` via list-form subprocess (no shell=True).
69
+
70
+ Returns (stdout, stderr, exit_code). Used by git_diff_staged / git_commit
71
+ which need stderr for error diagnostics — shell() can't return stderr
72
+ without breaking the IDEAdapter Protocol's tuple[str, int] signature.
73
+
74
+ Underscore-prefix signals this is an internal helper; callers should use
75
+ git_diff_staged() and git_commit() (the public surface).
76
+ """
77
+ try:
78
+ result = subprocess.run(
79
+ ["git", *args],
80
+ capture_output=True,
81
+ text=True,
82
+ timeout=timeout_seconds,
83
+ check=False,
84
+ )
85
+ return result.stdout, result.stderr, result.returncode
86
+ except subprocess.TimeoutExpired:
87
+ return "", f"git {args[0] if args else ''} timed out", 124
88
+
89
+
90
+ def git_diff_staged() -> str:
91
+ """Return the output of `git diff --staged`.
92
+
93
+ Raises RuntimeError if git exits non-zero (e.g., not in a git repo).
94
+ """
95
+ stdout, stderr, code = _run_git(["diff", "--staged"])
96
+ if code != 0:
97
+ raise RuntimeError(f"git diff --staged failed (exit {code}): {stderr.strip()}")
98
+ return stdout
99
+
100
+
101
+ def git_commit(message: str, files: list[str] | None = None) -> str:
102
+ """Stage `files` (if provided) then commit; return the new SHA.
103
+
104
+ If `files` is None, commits whatever is already in the index.
105
+ Does NOT use --no-verify / --no-gpg-sign / --amend per Protocol contract.
106
+ """
107
+ if files is not None:
108
+ for f in files:
109
+ _, stderr, code = _run_git(["add", "--", f])
110
+ if code != 0:
111
+ raise RuntimeError(f"git add {f} failed (exit {code}): {stderr.strip()}")
112
+
113
+ _, stderr, code = _run_git(["commit", "-m", message])
114
+ if code != 0:
115
+ raise RuntimeError(f"git commit failed (exit {code}): {stderr.strip()}")
116
+
117
+ stdout, stderr, code = _run_git(["rev-parse", "HEAD"])
118
+ if code != 0:
119
+ raise RuntimeError(f"git rev-parse HEAD failed (exit {code}): {stderr.strip()}")
120
+ return stdout.strip()
121
+
122
+
123
+ def shquote(s: str) -> str:
124
+ """POSIX single-quote escape for safe shell=True interpolation."""
125
+ return "'" + s.replace("'", "'\\''") + "'"
@@ -0,0 +1,108 @@
1
+ """IDE Adapter Protocol — the abstraction surface arcgentic skills/CLI use to talk
2
+ to whatever AI agent platform is hosting them (Claude Code / Cursor / VSCode-Codex /
3
+ Codex CLI / inline fallback).
4
+
5
+ Adding a new platform = implementing this Protocol; arcgentic skills/CLI then work
6
+ unchanged.
7
+
8
+ Anti-contamination invariant (spec § 1.5): adapter methods MUST NOT inject
9
+ `tools=` or `tool_choice=` at the agent level. Those belong one layer down
10
+ in the LLM-client layer.
11
+
12
+ Spec reference: docs/plans/2026-05-13-arcgentic-v0.2.0-spec.md § 3.1
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from dataclasses import dataclass
18
+ from typing import Literal, Protocol, runtime_checkable
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class AgentDispatchResult:
23
+ """Result of dispatching a sub-agent through an IDE adapter.
24
+
25
+ `output` : the agent's stdout / response text
26
+ `exit_code` : 0 = success; non-zero = failure
27
+ `duration_ms` : wall-clock ms from dispatch to result
28
+ `agent_type` : the agent_name that was dispatched (echoed back for trace)
29
+ `error` : optional error message (None on success)
30
+ """
31
+
32
+ output: str
33
+ exit_code: int
34
+ duration_ms: int
35
+ agent_type: str
36
+ error: str | None = None
37
+
38
+
39
+ @runtime_checkable
40
+ class IDEAdapter(Protocol):
41
+ """Adapter for an AI IDE/agent platform.
42
+
43
+ Each platform (claude-code / cursor / vscode-codex / codex-cli / inline)
44
+ implements this Protocol. arcgentic skills + CLI invoke platform-agnostic
45
+ methods; the adapter translates to platform-specific tool calls.
46
+
47
+ Anti-contamination invariant (spec § 1.5): adapter methods MUST NOT inject
48
+ `tools=` or `tool_choice=` at the agent level. Those belong one layer down
49
+ in the LLM-client layer.
50
+ """
51
+
52
+ platform_name: str # "claude-code" / "cursor" / "vscode-codex" / "codex-cli" / "inline"
53
+
54
+ def dispatch_agent(
55
+ self,
56
+ agent_name: str,
57
+ prompt: str,
58
+ timeout_seconds: int = 600,
59
+ isolation: Literal["worktree"] | None = None,
60
+ ) -> AgentDispatchResult:
61
+ """Dispatch a sub-agent.
62
+
63
+ `agent_name` maps to a markdown file at agents/<name>.md.
64
+ `prompt` is the full self-contained brief; agent has zero session context.
65
+ Returns the agent's response wrapped in AgentDispatchResult.
66
+ """
67
+ ...
68
+
69
+ def invoke_skill(self, skill_name: str, args: str = "") -> str:
70
+ """Invoke an arcgentic skill in-process.
71
+
72
+ `skill_name` maps to a markdown file at skills/<name>/SKILL.md.
73
+ `args` is the optional argument string for the skill.
74
+ Returns the skill's textual output.
75
+ """
76
+ ...
77
+
78
+ def read_file(self, path: str) -> str: ...
79
+
80
+ def write_file(self, path: str, content: str) -> None: ...
81
+
82
+ def edit_file(self, path: str, old: str, new: str) -> None:
83
+ """Replace exactly one occurrence of `old` with `new` in file at `path`.
84
+
85
+ Match is exact-string (no regex). Implementations MUST raise an error if
86
+ `old` is not found, or if `old` appears more than once (ambiguous match).
87
+ For multi-occurrence replacement, callers should invoke `edit_file` multiple
88
+ times with disambiguating context, or use a higher-level batch API.
89
+ """
90
+ ...
91
+
92
+ def shell(self, command: str, timeout_seconds: int = 120) -> tuple[str, int]:
93
+ """Run a shell command; return (output, exit_code)."""
94
+ ...
95
+
96
+ def git_diff_staged(self) -> str: ...
97
+
98
+ def git_commit(self, message: str, files: list[str] | None = None) -> str:
99
+ """Commit staged changes; return the commit SHA.
100
+
101
+ If `files` is provided (non-None), stage those files first via `git add <file>...`
102
+ then commit. If `files` is None, commit whatever is currently in the index without
103
+ staging anything (caller has already staged).
104
+
105
+ Implementations must NOT use `--no-verify`, `--no-gpg-sign`, or `--amend` unless
106
+ the adapter explicitly documents otherwise.
107
+ """
108
+ ...