vibe-state-cli 0.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 (49) hide show
  1. vibe_state_cli-0.1.0/.gitignore +45 -0
  2. vibe_state_cli-0.1.0/LICENSE +21 -0
  3. vibe_state_cli-0.1.0/PKG-INFO +129 -0
  4. vibe_state_cli-0.1.0/README.md +88 -0
  5. vibe_state_cli-0.1.0/pyproject.toml +82 -0
  6. vibe_state_cli-0.1.0/src/vibe_state/__init__.py +3 -0
  7. vibe_state_cli-0.1.0/src/vibe_state/adapters/__init__.py +0 -0
  8. vibe_state_cli-0.1.0/src/vibe_state/adapters/agents_md.py +39 -0
  9. vibe_state_cli-0.1.0/src/vibe_state/adapters/antigravity.py +49 -0
  10. vibe_state_cli-0.1.0/src/vibe_state/adapters/base.py +201 -0
  11. vibe_state_cli-0.1.0/src/vibe_state/adapters/claude.py +68 -0
  12. vibe_state_cli-0.1.0/src/vibe_state/adapters/cline.py +45 -0
  13. vibe_state_cli-0.1.0/src/vibe_state/adapters/copilot.py +58 -0
  14. vibe_state_cli-0.1.0/src/vibe_state/adapters/cursor.py +49 -0
  15. vibe_state_cli-0.1.0/src/vibe_state/adapters/registry.py +60 -0
  16. vibe_state_cli-0.1.0/src/vibe_state/adapters/roo.py +30 -0
  17. vibe_state_cli-0.1.0/src/vibe_state/adapters/windsurf.py +53 -0
  18. vibe_state_cli-0.1.0/src/vibe_state/cli.py +47 -0
  19. vibe_state_cli-0.1.0/src/vibe_state/commands/__init__.py +0 -0
  20. vibe_state_cli-0.1.0/src/vibe_state/commands/_helpers.py +165 -0
  21. vibe_state_cli-0.1.0/src/vibe_state/commands/cmd_adapt.py +133 -0
  22. vibe_state_cli-0.1.0/src/vibe_state/commands/cmd_init.py +141 -0
  23. vibe_state_cli-0.1.0/src/vibe_state/commands/cmd_start.py +121 -0
  24. vibe_state_cli-0.1.0/src/vibe_state/commands/cmd_status.py +59 -0
  25. vibe_state_cli-0.1.0/src/vibe_state/commands/cmd_sync.py +150 -0
  26. vibe_state_cli-0.1.0/src/vibe_state/config.py +108 -0
  27. vibe_state_cli-0.1.0/src/vibe_state/core/__init__.py +0 -0
  28. vibe_state_cli-0.1.0/src/vibe_state/core/compactor.py +218 -0
  29. vibe_state_cli-0.1.0/src/vibe_state/core/git_ops.py +172 -0
  30. vibe_state_cli-0.1.0/src/vibe_state/core/lifecycle.py +85 -0
  31. vibe_state_cli-0.1.0/src/vibe_state/core/scanner.py +106 -0
  32. vibe_state_cli-0.1.0/src/vibe_state/core/state.py +172 -0
  33. vibe_state_cli-0.1.0/src/vibe_state/core/templates.py +83 -0
  34. vibe_state_cli-0.1.0/src/vibe_state/py.typed +0 -0
  35. vibe_state_cli-0.1.0/src/vibe_state/safety.py +56 -0
  36. vibe_state_cli-0.1.0/src/vibe_state/templates/state/architecture.md.j2 +20 -0
  37. vibe_state_cli-0.1.0/src/vibe_state/templates/state/archive.md.j2 +3 -0
  38. vibe_state_cli-0.1.0/src/vibe_state/templates/state/current.md.j2 +9 -0
  39. vibe_state_cli-0.1.0/src/vibe_state/templates/state/experiments.md.j2 +6 -0
  40. vibe_state_cli-0.1.0/src/vibe_state/templates/state/standards.md.j2 +16 -0
  41. vibe_state_cli-0.1.0/src/vibe_state/templates/state/tasks.md.j2 +3 -0
  42. vibe_state_cli-0.1.0/src/vibe_state/templates/vibe.md.j2 +52 -0
  43. vibe_state_cli-0.1.0/src/vibe_state/templates/zh-TW/state/architecture.md.j2 +20 -0
  44. vibe_state_cli-0.1.0/src/vibe_state/templates/zh-TW/state/archive.md.j2 +3 -0
  45. vibe_state_cli-0.1.0/src/vibe_state/templates/zh-TW/state/current.md.j2 +9 -0
  46. vibe_state_cli-0.1.0/src/vibe_state/templates/zh-TW/state/experiments.md.j2 +6 -0
  47. vibe_state_cli-0.1.0/src/vibe_state/templates/zh-TW/state/standards.md.j2 +16 -0
  48. vibe_state_cli-0.1.0/src/vibe_state/templates/zh-TW/state/tasks.md.j2 +3 -0
  49. vibe_state_cli-0.1.0/src/vibe_state/templates/zh-TW/vibe.md.j2 +52 -0
@@ -0,0 +1,45 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ *.egg-info/
7
+ *.egg
8
+ dist/
9
+ build/
10
+ .eggs/
11
+
12
+ # Virtual environments
13
+ .venv/
14
+ venv/
15
+ env/
16
+
17
+ # IDE
18
+ .vscode/
19
+ .idea/
20
+ *.swp
21
+ *.swo
22
+ *~
23
+
24
+ # OS
25
+ .DS_Store
26
+ Thumbs.db
27
+
28
+ # Testing
29
+ .pytest_cache/
30
+ .coverage
31
+ htmlcov/
32
+ .mypy_cache/
33
+
34
+ # Environment
35
+ .env
36
+ .env.local
37
+
38
+ # Distribution
39
+ *.whl
40
+ *.tar.gz
41
+
42
+ # vibe-state-cli development
43
+ # Keep .vibe/ tracked for dogfooding, but ignore backups/snapshots
44
+ .vibe/backups/
45
+ .vibe/snapshots/
@@ -0,0 +1,21 @@
1
+ # MIT License
2
+
3
+ Copyright (c) 2026 vibe-state-cli contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 2.4
2
+ Name: vibe-state-cli
3
+ Version: 0.1.0
4
+ Summary: Model-agnostic AI-human collaboration state management CLI
5
+ Project-URL: Homepage, https://github.com/vibe-state-cli/vibe-state-cli
6
+ Project-URL: Documentation, https://vibe-state-cli.github.io/
7
+ Project-URL: Repository, https://github.com/vibe-state-cli/vibe-state-cli
8
+ Project-URL: Issues, https://github.com/vibe-state-cli/vibe-state-cli/issues
9
+ Author: vibe-state-cli contributors
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: ai,cli,developer-tools,state-management,vibe
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Software Development :: Build Tools
23
+ Classifier: Topic :: Software Development :: Libraries
24
+ Requires-Python: >=3.10
25
+ Requires-Dist: jinja2>=3.1.0
26
+ Requires-Dist: markdown-it-py>=3.0.0
27
+ Requires-Dist: pydantic>=2.0.0
28
+ Requires-Dist: rich>=13.0.0
29
+ Requires-Dist: tomli-w>=1.0.0
30
+ Requires-Dist: tomli>=2.0.0; python_version < '3.11'
31
+ Requires-Dist: typer>=0.9.0
32
+ Provides-Extra: dev
33
+ Requires-Dist: mypy>=1.10.0; extra == 'dev'
34
+ Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
35
+ Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
36
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
37
+ Requires-Dist: ruff>=0.4.0; extra == 'dev'
38
+ Provides-Extra: mcp
39
+ Requires-Dist: mcp>=1.27.0; extra == 'mcp'
40
+ Description-Content-Type: text/markdown
41
+
42
+ # vibe-state-cli
43
+
44
+ [English](README.md) | [繁體中文](docs/zh-TW/README.md)
45
+
46
+ **Model-agnostic AI-human collaboration state management CLI.**
47
+
48
+ Let any AI model — Claude, GPT, Gemini, or local models — instantly sync with your project's context by reading a single `.vibe/` directory.
49
+
50
+ ## Why vibe?
51
+
52
+ | Problem | How vibe solves it |
53
+ |---------|-------------------|
54
+ | AI loses memory every session | `.vibe/state/` persists across sessions and tools |
55
+ | Switching AI tools = starting over | 8 adapters generate each tool's native config from one source |
56
+ | CLAUDE.md grows forever, wastes tokens | `vibe sync --compact` auto-archives, keeps state lean (~684 tokens) |
57
+ | Manual copy-paste of context prompts | `vibe init` scans your project and generates everything |
58
+ | No structured handoff between sessions | `vibe sync` appends git state + C.L.E.A.R. review checklist |
59
+
60
+ Works **100% offline**. No API keys, no telemetry, no network calls.
61
+
62
+ ## Install
63
+
64
+ ```bash
65
+ pipx install vibe-state-cli
66
+ ```
67
+
68
+ ## Quick Start
69
+
70
+ ```bash
71
+ cd my-project
72
+
73
+ vibe init # Initialize .vibe/ (auto-detects language, framework, AI tools)
74
+ vibe start # Daily start — load state, git check, auto-compact
75
+ vibe sync # Daily close — append git status, C.L.E.A.R. review
76
+ vibe sync --compact # Archive completed tasks, compress state files
77
+ vibe sync --close # End project — final sync + retrospective
78
+ vibe status # Check project state (anytime)
79
+ vibe adapt --list # See which AI tool adapters are enabled
80
+ ```
81
+
82
+ ## 5 Commands
83
+
84
+ | Command | When | What it does |
85
+ |---------|------|-------------|
86
+ | `vibe init` | Once | Scan project, generate `.vibe/`, detect AI tools, emit adapter files |
87
+ | `vibe start` | Daily | Load state, validate vs git, auto-compact if needed, Rich summary |
88
+ | `vibe sync` | Daily | Append git status to state, C.L.E.A.R. checklist |
89
+ | `vibe status` | Anytime | Show lifecycle, tasks, file sizes |
90
+ | `vibe adapt` | As needed | `--add`/`--remove`/`--list`/`--sync` adapter files |
91
+
92
+ ### Flags
93
+
94
+ - `vibe init --lang zh-TW` — Traditional Chinese templates
95
+ - `vibe init --force` — Reinitialize or reopen a closed project
96
+ - `vibe sync --compact` — Run memory compaction after sync
97
+ - `vibe sync --close` — Close project with retrospective
98
+ - `vibe adapt --remove cursor --dry-run` — Preview before deleting
99
+ - `vibe adapt --remove cursor --confirm` — Delete with backup
100
+
101
+ ## Supported AI Tools
102
+
103
+ | Tool | Generated Config | Auto-detected by |
104
+ |------|-----------------|------------------|
105
+ | AGENTS.md | `AGENTS.md` | `AGENTS.md` exists |
106
+ | Claude Code | `CLAUDE.md` + `.claude/rules/` | `.claude/` directory |
107
+ | Antigravity / Gemini | `GEMINI.md` | `GEMINI.md` or `.gemini/` |
108
+ | Cursor | `.cursor/rules/*.mdc` | `.cursor/` directory |
109
+ | GitHub Copilot | `.github/copilot-instructions.md` | existing copilot config |
110
+ | Windsurf | `.windsurf/rules/*.md` | `.windsurf/` directory |
111
+ | Cline | `.clinerules/*.md` | `.clinerules/` directory |
112
+ | Roo Code | `.roo/rules/*.md` | `.roo/` directory |
113
+
114
+ Only detected tools get adapter files generated. No bloat.
115
+
116
+ ## Autoresearch Integration
117
+
118
+ Supports [autoresearch](https://github.com/uditgoenka/autoresearch) experiment loops. `vibe sync` auto-detects experiment commits and records results to `state/experiments.md`.
119
+
120
+ ## Safety
121
+
122
+ - `vibe adapt --remove` defaults to **dry-run** — requires `--confirm` to delete
123
+ - Snapshots saved on every emit for diff detection
124
+ - Backups kept (last 3) before any deletion
125
+ - User-modified files trigger warnings before overwrite
126
+
127
+ ## License
128
+
129
+ MIT
@@ -0,0 +1,88 @@
1
+ # vibe-state-cli
2
+
3
+ [English](README.md) | [繁體中文](docs/zh-TW/README.md)
4
+
5
+ **Model-agnostic AI-human collaboration state management CLI.**
6
+
7
+ Let any AI model — Claude, GPT, Gemini, or local models — instantly sync with your project's context by reading a single `.vibe/` directory.
8
+
9
+ ## Why vibe?
10
+
11
+ | Problem | How vibe solves it |
12
+ |---------|-------------------|
13
+ | AI loses memory every session | `.vibe/state/` persists across sessions and tools |
14
+ | Switching AI tools = starting over | 8 adapters generate each tool's native config from one source |
15
+ | CLAUDE.md grows forever, wastes tokens | `vibe sync --compact` auto-archives, keeps state lean (~684 tokens) |
16
+ | Manual copy-paste of context prompts | `vibe init` scans your project and generates everything |
17
+ | No structured handoff between sessions | `vibe sync` appends git state + C.L.E.A.R. review checklist |
18
+
19
+ Works **100% offline**. No API keys, no telemetry, no network calls.
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ pipx install vibe-state-cli
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ```bash
30
+ cd my-project
31
+
32
+ vibe init # Initialize .vibe/ (auto-detects language, framework, AI tools)
33
+ vibe start # Daily start — load state, git check, auto-compact
34
+ vibe sync # Daily close — append git status, C.L.E.A.R. review
35
+ vibe sync --compact # Archive completed tasks, compress state files
36
+ vibe sync --close # End project — final sync + retrospective
37
+ vibe status # Check project state (anytime)
38
+ vibe adapt --list # See which AI tool adapters are enabled
39
+ ```
40
+
41
+ ## 5 Commands
42
+
43
+ | Command | When | What it does |
44
+ |---------|------|-------------|
45
+ | `vibe init` | Once | Scan project, generate `.vibe/`, detect AI tools, emit adapter files |
46
+ | `vibe start` | Daily | Load state, validate vs git, auto-compact if needed, Rich summary |
47
+ | `vibe sync` | Daily | Append git status to state, C.L.E.A.R. checklist |
48
+ | `vibe status` | Anytime | Show lifecycle, tasks, file sizes |
49
+ | `vibe adapt` | As needed | `--add`/`--remove`/`--list`/`--sync` adapter files |
50
+
51
+ ### Flags
52
+
53
+ - `vibe init --lang zh-TW` — Traditional Chinese templates
54
+ - `vibe init --force` — Reinitialize or reopen a closed project
55
+ - `vibe sync --compact` — Run memory compaction after sync
56
+ - `vibe sync --close` — Close project with retrospective
57
+ - `vibe adapt --remove cursor --dry-run` — Preview before deleting
58
+ - `vibe adapt --remove cursor --confirm` — Delete with backup
59
+
60
+ ## Supported AI Tools
61
+
62
+ | Tool | Generated Config | Auto-detected by |
63
+ |------|-----------------|------------------|
64
+ | AGENTS.md | `AGENTS.md` | `AGENTS.md` exists |
65
+ | Claude Code | `CLAUDE.md` + `.claude/rules/` | `.claude/` directory |
66
+ | Antigravity / Gemini | `GEMINI.md` | `GEMINI.md` or `.gemini/` |
67
+ | Cursor | `.cursor/rules/*.mdc` | `.cursor/` directory |
68
+ | GitHub Copilot | `.github/copilot-instructions.md` | existing copilot config |
69
+ | Windsurf | `.windsurf/rules/*.md` | `.windsurf/` directory |
70
+ | Cline | `.clinerules/*.md` | `.clinerules/` directory |
71
+ | Roo Code | `.roo/rules/*.md` | `.roo/` directory |
72
+
73
+ Only detected tools get adapter files generated. No bloat.
74
+
75
+ ## Autoresearch Integration
76
+
77
+ Supports [autoresearch](https://github.com/uditgoenka/autoresearch) experiment loops. `vibe sync` auto-detects experiment commits and records results to `state/experiments.md`.
78
+
79
+ ## Safety
80
+
81
+ - `vibe adapt --remove` defaults to **dry-run** — requires `--confirm` to delete
82
+ - Snapshots saved on every emit for diff detection
83
+ - Backups kept (last 3) before any deletion
84
+ - User-modified files trigger warnings before overwrite
85
+
86
+ ## License
87
+
88
+ MIT
@@ -0,0 +1,82 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "vibe-state-cli"
7
+ version = "0.1.0"
8
+ description = "Model-agnostic AI-human collaboration state management CLI"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ authors = [
13
+ { name = "vibe-state-cli contributors" },
14
+ ]
15
+ keywords = ["ai", "cli", "state-management", "developer-tools", "vibe"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Environment :: Console",
19
+ "Intended Audience :: Developers",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Topic :: Software Development :: Build Tools",
27
+ "Topic :: Software Development :: Libraries",
28
+ ]
29
+ dependencies = [
30
+ "typer>=0.9.0",
31
+ "rich>=13.0.0",
32
+ "jinja2>=3.1.0",
33
+ "markdown-it-py>=3.0.0",
34
+ "pydantic>=2.0.0",
35
+ "tomli>=2.0.0; python_version < '3.11'",
36
+ "tomli-w>=1.0.0",
37
+ ]
38
+
39
+ [project.optional-dependencies]
40
+ dev = [
41
+ "pytest>=8.0.0",
42
+ "pytest-cov>=5.0.0",
43
+ "ruff>=0.4.0",
44
+ "mypy>=1.10.0",
45
+ "pre-commit>=3.0.0",
46
+ ]
47
+ mcp = [
48
+ "mcp>=1.27.0",
49
+ ]
50
+
51
+ [project.scripts]
52
+ vibe = "vibe_state.cli:app"
53
+
54
+ [project.urls]
55
+ Homepage = "https://github.com/vibe-state-cli/vibe-state-cli"
56
+ Documentation = "https://vibe-state-cli.github.io/"
57
+ Repository = "https://github.com/vibe-state-cli/vibe-state-cli"
58
+ Issues = "https://github.com/vibe-state-cli/vibe-state-cli/issues"
59
+
60
+ [tool.hatch.build.targets.sdist]
61
+ include = ["src/vibe_state"]
62
+
63
+ [tool.hatch.build.targets.wheel]
64
+ packages = ["src/vibe_state"]
65
+
66
+ [tool.ruff]
67
+ target-version = "py310"
68
+ line-length = 100
69
+ src = ["src"]
70
+
71
+ [tool.ruff.lint]
72
+ select = ["E", "F", "W", "I", "UP", "B", "SIM"]
73
+
74
+ [tool.mypy]
75
+ python_version = "3.10"
76
+ strict = true
77
+ warn_return_any = true
78
+ warn_unused_configs = true
79
+
80
+ [tool.pytest.ini_options]
81
+ testpaths = ["tests"]
82
+ addopts = "-v --tb=short"
@@ -0,0 +1,3 @@
1
+ """vibe-state-cli: Model-agnostic AI-human collaboration state management."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,39 @@
1
+ """AGENTS.md adapter — Linux Foundation / AAIF universal standard."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from vibe_state.adapters.base import AdapterBase, AdapterContext
8
+ from vibe_state.adapters.registry import register_adapter
9
+
10
+ MAX_SIZE_BYTES = 32 * 1024
11
+
12
+
13
+ @register_adapter
14
+ class AgentsMdAdapter(AdapterBase):
15
+ name = "agents_md"
16
+ REQUIRED_FIELDS: set[str] = set()
17
+
18
+ def detect(self, project_root: Path) -> bool:
19
+ return (project_root / "AGENTS.md").exists()
20
+
21
+ def emit(self, ctx: AdapterContext) -> list[Path]:
22
+ lines = [
23
+ f"# AGENTS.md — {ctx.project_name}",
24
+ "",
25
+ "## Project",
26
+ "",
27
+ ]
28
+ lines += self._build_common_body(ctx)
29
+ content = "\n".join(lines)
30
+ if not self.validate(content):
31
+ self._warn_validation("AGENTS.md (>32KiB)")
32
+ return [self._write_file(ctx.project_root / "AGENTS.md", content)]
33
+
34
+ def clean(self, project_root: Path) -> list[Path]:
35
+ p = project_root / "AGENTS.md"
36
+ return [p] if p.exists() else []
37
+
38
+ def validate(self, output: str) -> bool:
39
+ return len(output.encode("utf-8")) <= MAX_SIZE_BYTES
@@ -0,0 +1,49 @@
1
+ """Antigravity / Gemini CLI adapter — GEMINI.md (plain Markdown, no frontmatter)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from vibe_state.adapters.base import AdapterBase, AdapterContext
8
+ from vibe_state.adapters.registry import register_adapter
9
+
10
+
11
+ @register_adapter
12
+ class AntigravityAdapter(AdapterBase):
13
+ name = "antigravity"
14
+ REQUIRED_FIELDS: set[str] = set() # No frontmatter for GEMINI.md
15
+
16
+ def detect(self, project_root: Path) -> bool:
17
+ return (project_root / "GEMINI.md").exists()
18
+
19
+ def emit(self, ctx: AdapterContext) -> list[Path]:
20
+ lines = [f"# GEMINI.md — {ctx.project_name}", ""]
21
+
22
+ if "agents_md" in ctx.enabled_adapters:
23
+ # Import AGENTS.md to avoid duplication (same pattern as Claude adapter)
24
+ lines += [
25
+ "@AGENTS.md",
26
+ "",
27
+ "## Antigravity-Specific",
28
+ "",
29
+ ]
30
+ else:
31
+ # Self-contained: include full project info
32
+ lines += ["## Project", ""]
33
+ lines += self._build_common_body(ctx)
34
+
35
+ lines += [
36
+ "## Vibe Workflow",
37
+ "",
38
+ "Read `.vibe/VIBE.md` for the full protocol.",
39
+ "**Checkpoint**: After each task, mark `[x]` in `state/tasks.md`"
40
+ " and append to `state/current.md`.",
41
+ "**Reality-First**: When memory conflicts with git, trust git.",
42
+ "",
43
+ ]
44
+ content = "\n".join(lines)
45
+ return [self._write_file(ctx.project_root / "GEMINI.md", content)]
46
+
47
+ def clean(self, project_root: Path) -> list[Path]:
48
+ p = project_root / "GEMINI.md"
49
+ return [p] if p.exists() else []
@@ -0,0 +1,201 @@
1
+ """Base adapter interface for all AI/IDE tool adapters."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from abc import ABC, abstractmethod
6
+ from dataclasses import dataclass, field
7
+ from pathlib import Path
8
+
9
+
10
+ @dataclass
11
+ class AdapterContext:
12
+ """Context passed to adapters for file generation."""
13
+
14
+ project_root: Path
15
+ vibe_dir: Path
16
+ constitution: str # VIBE.md content
17
+ standards: str # state/standards.md content
18
+ architecture: str # state/architecture.md content
19
+ languages: list[str] = field(default_factory=list)
20
+ frameworks: list[str] = field(default_factory=list)
21
+ project_name: str = ""
22
+ enabled_adapters: list[str] = field(default_factory=list)
23
+
24
+
25
+ def _sanitize(value: str) -> str:
26
+ """Strip newlines, #, quotes, and control chars from user-controlled strings."""
27
+ return "".join(c for c in value if c.isprintable() and c not in '\n\r#"\'`')
28
+
29
+
30
+ # Patterns that indicate malicious injection in standards/state files
31
+ _SUSPICIOUS_PATTERNS = [
32
+ "eval(",
33
+ "exec(",
34
+ "system(",
35
+ "import os",
36
+ "import subprocess",
37
+ "__import__",
38
+ "curl ",
39
+ "wget ",
40
+ "rm -rf",
41
+ "ignore all",
42
+ "ignore previous",
43
+ "disregard",
44
+ "override all",
45
+ "new rule",
46
+ "send all",
47
+ "exfiltrate",
48
+ "http://",
49
+ "https://",
50
+ ]
51
+
52
+
53
+ def _is_suspicious_instruction(text: str) -> bool:
54
+ """Detect potentially malicious instructions in state file content."""
55
+ lower = text.lower()
56
+ return any(pattern in lower for pattern in _SUSPICIOUS_PATTERNS)
57
+
58
+
59
+ def build_adapter_context(project_root: Path) -> AdapterContext:
60
+ """Build AdapterContext from .vibe/ config + state files."""
61
+ vibe_dir = project_root / ".vibe"
62
+
63
+ def _read(rel: str) -> str:
64
+ p = vibe_dir / rel
65
+ return p.read_text(encoding="utf-8") if p.exists() else ""
66
+
67
+ from vibe_state.config import load_config
68
+
69
+ config = load_config(vibe_dir)
70
+
71
+ # Parse languages/frameworks from architecture.md
72
+ languages: list[str] = []
73
+ frameworks: list[str] = []
74
+ arch = _read("state/architecture.md")
75
+ for line in arch.splitlines():
76
+ if line.strip().startswith("- Language:"):
77
+ lang_val = line.split(":", 1)[1].strip()
78
+ if lang_val:
79
+ languages.append(lang_val)
80
+ if line.strip().startswith("- Framework:"):
81
+ fw_val = line.split(":", 1)[1].strip()
82
+ if fw_val:
83
+ frameworks.append(fw_val)
84
+
85
+ return AdapterContext(
86
+ project_root=project_root,
87
+ vibe_dir=vibe_dir,
88
+ constitution=_read("VIBE.md"),
89
+ standards=_read("state/standards.md"),
90
+ architecture=_read("state/architecture.md"),
91
+ languages=[_sanitize(lang) for lang in languages],
92
+ frameworks=[_sanitize(fw) for fw in frameworks],
93
+ project_name=_sanitize(project_root.name),
94
+ enabled_adapters=config.adapters.enabled,
95
+ )
96
+
97
+
98
+ class AdapterBase(ABC):
99
+ """Abstract base class for all adapters."""
100
+
101
+ name: str = ""
102
+ REQUIRED_FIELDS: set[str] = set()
103
+
104
+ @abstractmethod
105
+ def detect(self, project_root: Path) -> bool:
106
+ """Detect if this tool's config already exists in the project."""
107
+
108
+ @abstractmethod
109
+ def emit(self, ctx: AdapterContext) -> list[Path]:
110
+ """Generate config files for this tool. Returns list of created file paths."""
111
+
112
+ @abstractmethod
113
+ def clean(self, project_root: Path) -> list[Path]:
114
+ """Return list of files that would be removed for this adapter."""
115
+
116
+ def validate(self, output: str) -> bool:
117
+ """Validate emitted file content. Override for frontmatter checks."""
118
+ return True
119
+
120
+ def _write_file(self, path: Path, content: str) -> Path:
121
+ """Write content to file with integrity marker (markdown only)."""
122
+ import hashlib
123
+
124
+ path.parent.mkdir(parents=True, exist_ok=True)
125
+ # Add integrity marker only to markdown files (not JSON, TOML, etc.)
126
+ if path.suffix in (".md", ".mdc"):
127
+ content_hash = hashlib.sha256(content.encode("utf-8")).hexdigest()[:12]
128
+ content = content.rstrip() + f"\n\n<!-- vibe-state-cli:integrity:{content_hash} -->\n"
129
+ path.write_text(content, encoding="utf-8", newline="\n")
130
+ return path
131
+
132
+ def _build_common_body(
133
+ self, ctx: AdapterContext, *, slim: bool = False
134
+ ) -> list[str]:
135
+ """Build the common project info + standards + security block.
136
+
137
+ Args:
138
+ slim: If True, emit only the session-start directive (for when
139
+ AGENTS.md is also enabled and already carries the full body).
140
+ """
141
+ lines: list[str] = []
142
+
143
+ if slim:
144
+ # Minimal: just point to AGENTS.md and .vibe/ for details
145
+ lines += [
146
+ "See AGENTS.md for project standards and security rules.",
147
+ "",
148
+ ]
149
+ else:
150
+ if ctx.languages:
151
+ lines.append(f"- Languages: {', '.join(ctx.languages)}")
152
+ if ctx.frameworks:
153
+ lines.append(f"- Frameworks: {', '.join(ctx.frameworks)}")
154
+ lines.append("")
155
+
156
+ # Pull standards with injection detection
157
+ has_security = False
158
+ if ctx.standards:
159
+ for line in ctx.standards.splitlines():
160
+ stripped = line.strip()
161
+ if stripped.startswith("- ") and not stripped.startswith("- ("):
162
+ # Block suspicious instructions from propagating
163
+ lower = stripped.lower()
164
+ if _is_suspicious_instruction(lower):
165
+ continue
166
+ lines.append(_sanitize(stripped))
167
+ if "hardcode" in lower or "secret" in lower:
168
+ has_security = True
169
+ lines.append("")
170
+
171
+ # Only add security block if standards didn't already include it
172
+ if not has_security:
173
+ lines += [
174
+ "## Security",
175
+ "- Never hardcode secrets, tokens, or passwords",
176
+ "- Use .env files for environment variables",
177
+ "",
178
+ ]
179
+
180
+ # Always include: session-start directive + boundaries
181
+ lines += [
182
+ "## Session Start — READ THESE FILES",
183
+ "At the beginning of every session, read these files for project context:",
184
+ "- `.vibe/state/current.md` — latest progress and sync history",
185
+ "- `.vibe/state/tasks.md` — active task checklist",
186
+ "- `.vibe/VIBE.md` — project constitution and workflow SOP",
187
+ "",
188
+ "## Boundaries",
189
+ "- Do NOT modify `.vibe/config.toml` or `.vibe/state/.lifecycle` directly",
190
+ "- Do NOT run destructive commands without human confirmation",
191
+ "",
192
+ ]
193
+ return lines
194
+
195
+ def _warn_validation(self, adapter_name: str) -> None:
196
+ """Print a validation warning."""
197
+ from rich.console import Console
198
+
199
+ Console().print(
200
+ f"[yellow]Warning:[/] {adapter_name} frontmatter validation failed"
201
+ )