bpsai-pair 0.2.0__py3-none-any.whl
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.
Potentially problematic release.
This version of bpsai-pair might be problematic. Click here for more details.
- bpsai_pair/__init__.py +25 -0
- bpsai_pair/__main__.py +4 -0
- bpsai_pair/adapters.py +9 -0
- bpsai_pair/cli.py +514 -0
- bpsai_pair/config.py +310 -0
- bpsai_pair/data/cookiecutter-paircoder/cookiecutter.json +12 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/.agentpackignore +1 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/.editorconfig +17 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/.github/PULL_REQUEST_TEMPLATE.md +47 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/.github/workflows/ci.yml +90 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/.github/workflows/project_tree.yml +33 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/.gitignore +5 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/.gitleaks.toml +17 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/.pre-commit-config.yaml +38 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/CODEOWNERS +9 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/CONTRIBUTING.md +35 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/SECURITY.md +14 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/context/agents.md +6 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/context/agents.md.bak +196 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/context/development.md +1 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/context/development.md.bak +10 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/context/directory_notes/.gitkeep +1 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/context/project_tree.md +7 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/prompts/deep_research.yml +28 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/prompts/implementation.yml +25 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/prompts/roadmap.yml +14 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/scripts/README.md +11 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/src/.gitkeep +1 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/templates/adr.md +19 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/templates/directory_note.md +17 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/tests/example_contract/README.md +3 -0
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/tests/example_integration/README.md +3 -0
- bpsai_pair/init_bundled_cli.py +47 -0
- bpsai_pair/jsonio.py +6 -0
- bpsai_pair/ops.py +451 -0
- bpsai_pair/pyutils.py +26 -0
- bpsai_pair/utils.py +11 -0
- bpsai_pair-0.2.0.dist-info/METADATA +29 -0
- bpsai_pair-0.2.0.dist-info/RECORD +42 -0
- bpsai_pair-0.2.0.dist-info/WHEEL +5 -0
- bpsai_pair-0.2.0.dist-info/entry_points.txt +3 -0
- bpsai_pair-0.2.0.dist-info/top_level.txt +1 -0
bpsai_pair/config.py
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration management for PairCoder.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Optional, Dict, Any
|
|
9
|
+
import yaml
|
|
10
|
+
import json
|
|
11
|
+
from dataclasses import dataclass, asdict, field
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class Config:
|
|
16
|
+
"""PairCoder configuration."""
|
|
17
|
+
|
|
18
|
+
# Project settings
|
|
19
|
+
project_name: str = "My Project"
|
|
20
|
+
primary_goal: str = "Build awesome software"
|
|
21
|
+
coverage_target: int = 80
|
|
22
|
+
|
|
23
|
+
# Branch settings
|
|
24
|
+
default_branch_type: str = "feature"
|
|
25
|
+
main_branch: str = "main"
|
|
26
|
+
|
|
27
|
+
# Context settings
|
|
28
|
+
context_dir: str = "context"
|
|
29
|
+
|
|
30
|
+
# Pack settings
|
|
31
|
+
default_pack_name: str = "agent_pack.tgz"
|
|
32
|
+
pack_excludes: list[str] = field(default_factory=lambda: [
|
|
33
|
+
".git", ".venv", "__pycache__", "node_modules",
|
|
34
|
+
"dist", "build", "*.log", "*.bak"
|
|
35
|
+
])
|
|
36
|
+
|
|
37
|
+
# CI settings
|
|
38
|
+
python_formatter: str = "ruff"
|
|
39
|
+
node_formatter: str = "prettier"
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def load(cls, root: Path) -> "Config":
|
|
43
|
+
"""Load configuration from .paircoder.yml or environment."""
|
|
44
|
+
config_file = root / ".paircoder.yml"
|
|
45
|
+
|
|
46
|
+
data = {}
|
|
47
|
+
if config_file.exists():
|
|
48
|
+
with open(config_file) as f:
|
|
49
|
+
yaml_data = yaml.safe_load(f) or {}
|
|
50
|
+
|
|
51
|
+
# Handle both flat and nested structures
|
|
52
|
+
if "version" in yaml_data:
|
|
53
|
+
# New nested structure
|
|
54
|
+
if "project" in yaml_data:
|
|
55
|
+
project = yaml_data["project"]
|
|
56
|
+
data["project_name"] = project.get("name", "My Project")
|
|
57
|
+
data["primary_goal"] = project.get("primary_goal", "Build awesome software")
|
|
58
|
+
data["coverage_target"] = project.get("coverage_target", 80)
|
|
59
|
+
|
|
60
|
+
if "workflow" in yaml_data:
|
|
61
|
+
workflow = yaml_data["workflow"]
|
|
62
|
+
data["default_branch_type"] = workflow.get("default_branch_type", "feature")
|
|
63
|
+
data["main_branch"] = workflow.get("main_branch", "main")
|
|
64
|
+
data["context_dir"] = workflow.get("context_dir", "context")
|
|
65
|
+
|
|
66
|
+
if "pack" in yaml_data:
|
|
67
|
+
pack = yaml_data["pack"]
|
|
68
|
+
data["default_pack_name"] = pack.get("default_name", "agent_pack.tgz")
|
|
69
|
+
data["pack_excludes"] = pack.get("excludes", [])
|
|
70
|
+
|
|
71
|
+
if "ci" in yaml_data:
|
|
72
|
+
ci = yaml_data["ci"]
|
|
73
|
+
data["python_formatter"] = ci.get("python_formatter", "ruff")
|
|
74
|
+
data["node_formatter"] = ci.get("node_formatter", "prettier")
|
|
75
|
+
else:
|
|
76
|
+
# Old flat structure (backwards compatibility)
|
|
77
|
+
data = yaml_data
|
|
78
|
+
|
|
79
|
+
# Override with environment variables
|
|
80
|
+
env_mappings = {
|
|
81
|
+
"PAIRCODER_MAIN_BRANCH": "main_branch",
|
|
82
|
+
"PAIRCODER_CONTEXT_DIR": "context_dir",
|
|
83
|
+
"PAIRCODER_DEFAULT_BRANCH": "default_branch_type",
|
|
84
|
+
"PAIRCODER_PROJECT_NAME": "project_name",
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for env_var, config_key in env_mappings.items():
|
|
88
|
+
if env_value := os.getenv(env_var):
|
|
89
|
+
data[config_key] = env_value
|
|
90
|
+
|
|
91
|
+
# Create config with collected data
|
|
92
|
+
return cls(**{k: v for k, v in data.items() if k in cls.__annotations__})
|
|
93
|
+
|
|
94
|
+
def save(self, root: Path) -> None:
|
|
95
|
+
"""Save configuration to .paircoder.yml."""
|
|
96
|
+
config_file = root / ".paircoder.yml"
|
|
97
|
+
|
|
98
|
+
data = {
|
|
99
|
+
"version": "0.1.3",
|
|
100
|
+
"project": {
|
|
101
|
+
"name": self.project_name,
|
|
102
|
+
"primary_goal": self.primary_goal,
|
|
103
|
+
"coverage_target": self.coverage_target,
|
|
104
|
+
},
|
|
105
|
+
"workflow": {
|
|
106
|
+
"default_branch_type": self.default_branch_type,
|
|
107
|
+
"main_branch": self.main_branch,
|
|
108
|
+
"context_dir": self.context_dir,
|
|
109
|
+
},
|
|
110
|
+
"pack": {
|
|
111
|
+
"default_name": self.default_pack_name,
|
|
112
|
+
"excludes": self.pack_excludes,
|
|
113
|
+
},
|
|
114
|
+
"ci": {
|
|
115
|
+
"python_formatter": self.python_formatter,
|
|
116
|
+
"node_formatter": self.node_formatter,
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
with open(config_file, 'w') as f:
|
|
121
|
+
yaml.dump(data, f, default_flow_style=False, sort_keys=False)
|
|
122
|
+
|
|
123
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
124
|
+
"""Convert to dictionary."""
|
|
125
|
+
return asdict(self)
|
|
126
|
+
|
|
127
|
+
def to_json(self) -> str:
|
|
128
|
+
"""Convert to JSON string."""
|
|
129
|
+
return json.dumps(self.to_dict(), indent=2)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class ContextTemplate:
|
|
133
|
+
"""Templates for context files."""
|
|
134
|
+
|
|
135
|
+
@staticmethod
|
|
136
|
+
def development_md(config: Config) -> str:
|
|
137
|
+
"""Generate development.md template."""
|
|
138
|
+
return f"""# Development Log
|
|
139
|
+
|
|
140
|
+
**Project:** {config.project_name}
|
|
141
|
+
**Phase:** Phase 1: Initial Setup
|
|
142
|
+
**Primary Goal:** {config.primary_goal}
|
|
143
|
+
|
|
144
|
+
## KPIs & Non-Functional Targets
|
|
145
|
+
|
|
146
|
+
- Test Coverage: ≥ {config.coverage_target}%
|
|
147
|
+
- Documentation: Complete for all public APIs
|
|
148
|
+
- Performance: Response time < 200ms (p95)
|
|
149
|
+
|
|
150
|
+
## Phase 1 — Foundation (Weeks 1–2)
|
|
151
|
+
|
|
152
|
+
**Objectives**
|
|
153
|
+
- Set up project structure and CI/CD
|
|
154
|
+
- Define core architecture and interfaces
|
|
155
|
+
- Establish testing framework
|
|
156
|
+
|
|
157
|
+
**Tasks**
|
|
158
|
+
- [ ] Initialize repository with PairCoder
|
|
159
|
+
- [ ] Set up CI workflows
|
|
160
|
+
- [ ] Create initial project structure
|
|
161
|
+
- [ ] Write architectural decision records
|
|
162
|
+
|
|
163
|
+
**Testing Plan**
|
|
164
|
+
- Unit tests for all business logic
|
|
165
|
+
- Integration tests for external boundaries
|
|
166
|
+
- End-to-end tests for critical user flows
|
|
167
|
+
|
|
168
|
+
**Risks & Rollback**
|
|
169
|
+
- Risk: Incomplete requirements — Mitigation: Regular stakeholder reviews
|
|
170
|
+
- Rollback: Git revert with documented rollback procedures
|
|
171
|
+
|
|
172
|
+
## Context Sync (AUTO-UPDATED)
|
|
173
|
+
|
|
174
|
+
- **Overall goal is:** {config.primary_goal}
|
|
175
|
+
- **Last action was:** Initialized project
|
|
176
|
+
- **Next action will be:** Set up CI/CD pipeline
|
|
177
|
+
- **Blockers:** None
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
@staticmethod
|
|
181
|
+
def agents_md(config: Config) -> str:
|
|
182
|
+
"""Generate agents.md template."""
|
|
183
|
+
return f"""# Agents Guide — AI Pair Coding Playbook
|
|
184
|
+
|
|
185
|
+
**Project:** {config.project_name}
|
|
186
|
+
**Purpose:** {config.primary_goal}
|
|
187
|
+
|
|
188
|
+
## Ground Rules
|
|
189
|
+
|
|
190
|
+
1. **Context is King**: Always refer to `/context/development.md` for current state
|
|
191
|
+
2. **Test First**: Write tests before implementation
|
|
192
|
+
3. **Small Changes**: Keep PRs under 200 lines when possible
|
|
193
|
+
4. **Update Loop**: Run `bpsai-pair context-sync` after every significant change
|
|
194
|
+
|
|
195
|
+
## Project Structure
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
.
|
|
199
|
+
├── {config.context_dir}/ # Project context and memory
|
|
200
|
+
├── src/ # Source code
|
|
201
|
+
├── tests/ # Test suites
|
|
202
|
+
├── docs/ # Documentation
|
|
203
|
+
└── .paircoder.yml # Configuration
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Workflow
|
|
207
|
+
|
|
208
|
+
1. Check status: `bpsai-pair status`
|
|
209
|
+
2. Create feature: `bpsai-pair feature <name> --primary "<goal>" --phase "<phase>"`
|
|
210
|
+
3. Make changes (with tests)
|
|
211
|
+
4. Update context: `bpsai-pair context-sync --last "<what>" --next "<next>"`
|
|
212
|
+
5. Create pack: `bpsai-pair pack`
|
|
213
|
+
6. Share with AI agent
|
|
214
|
+
|
|
215
|
+
## Testing Requirements
|
|
216
|
+
|
|
217
|
+
- Minimum coverage: {config.coverage_target}%
|
|
218
|
+
- All new code must have tests
|
|
219
|
+
- Integration tests for external dependencies
|
|
220
|
+
- Performance tests for critical paths
|
|
221
|
+
|
|
222
|
+
## Code Style
|
|
223
|
+
|
|
224
|
+
- Python: {config.python_formatter} for formatting and linting
|
|
225
|
+
- JavaScript: {config.node_formatter} for formatting
|
|
226
|
+
- Commit messages: Conventional Commits format
|
|
227
|
+
- Branch names: {config.default_branch_type}/<description>
|
|
228
|
+
|
|
229
|
+
## Context Loop Protocol
|
|
230
|
+
|
|
231
|
+
After EVERY meaningful change:
|
|
232
|
+
```bash
|
|
233
|
+
bpsai-pair context-sync \\
|
|
234
|
+
--last "What was just completed" \\
|
|
235
|
+
--next "The immediate next step" \\
|
|
236
|
+
--blockers "Any impediments"
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Excluded from Context
|
|
240
|
+
|
|
241
|
+
The following are excluded from agent packs (see `.agentpackignore`):
|
|
242
|
+
{chr(10).join(f'- {exclude}' for exclude in config.pack_excludes)}
|
|
243
|
+
|
|
244
|
+
## Commands Reference
|
|
245
|
+
|
|
246
|
+
- `bpsai-pair init` - Initialize scaffolding
|
|
247
|
+
- `bpsai-pair feature` - Create feature branch
|
|
248
|
+
- `bpsai-pair pack` - Create context package
|
|
249
|
+
- `bpsai-pair sync` - Update context loop
|
|
250
|
+
- `bpsai-pair status` - Show current state
|
|
251
|
+
- `bpsai-pair validate` - Check structure
|
|
252
|
+
- `bpsai-pair ci` - Run local CI checks
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
@staticmethod
|
|
256
|
+
def gitignore() -> str:
|
|
257
|
+
"""Generate .gitignore template."""
|
|
258
|
+
return """# PairCoder
|
|
259
|
+
.paircoder.yml.local
|
|
260
|
+
agent_pack*.tgz
|
|
261
|
+
*.bak
|
|
262
|
+
|
|
263
|
+
# Python
|
|
264
|
+
__pycache__/
|
|
265
|
+
*.py[cod]
|
|
266
|
+
*$py.class
|
|
267
|
+
*.so
|
|
268
|
+
.Python
|
|
269
|
+
.venv/
|
|
270
|
+
venv/
|
|
271
|
+
ENV/
|
|
272
|
+
env/
|
|
273
|
+
*.egg-info/
|
|
274
|
+
dist/
|
|
275
|
+
build/
|
|
276
|
+
.pytest_cache/
|
|
277
|
+
.mypy_cache/
|
|
278
|
+
.ruff_cache/
|
|
279
|
+
.coverage
|
|
280
|
+
htmlcov/
|
|
281
|
+
|
|
282
|
+
# Node
|
|
283
|
+
node_modules/
|
|
284
|
+
npm-debug.log*
|
|
285
|
+
yarn-debug.log*
|
|
286
|
+
yarn-error.log*
|
|
287
|
+
.npm
|
|
288
|
+
.yarn-integrity
|
|
289
|
+
|
|
290
|
+
# IDE
|
|
291
|
+
.idea/
|
|
292
|
+
.vscode/
|
|
293
|
+
*.swp
|
|
294
|
+
*.swo
|
|
295
|
+
*~
|
|
296
|
+
.DS_Store
|
|
297
|
+
|
|
298
|
+
# Environment
|
|
299
|
+
.env
|
|
300
|
+
.env.local
|
|
301
|
+
.env.*.local
|
|
302
|
+
|
|
303
|
+
# Logs
|
|
304
|
+
*.log
|
|
305
|
+
logs/
|
|
306
|
+
|
|
307
|
+
# OS
|
|
308
|
+
Thumbs.db
|
|
309
|
+
Desktop.ini
|
|
310
|
+
"""
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
{
|
|
3
|
+
"project_name": "Paircoder Project",
|
|
4
|
+
"project_slug": "{{ cookiecutter.project_name | lower | replace(' ', '-') }}",
|
|
5
|
+
"primary_goal": "<PRIMARY GOAL>",
|
|
6
|
+
"coverage_target": "80%",
|
|
7
|
+
"owner_gh_handle": "<OWNER>",
|
|
8
|
+
"architect_gh_handle": "<ARCHITECT>",
|
|
9
|
+
"build_gh_handle": "<BUILD>",
|
|
10
|
+
"qa_gh_handle": "<QA>",
|
|
11
|
+
"sre_gh_handle": "<SRE>"
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Default agent pack exclusions\n.git/\n.venv/\n__pycache__/\nnode_modules/\ndist/\nbuild/\n*.log\n*.bak\n*.tgz\n*.tar.gz\n*.zip\n
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
root = true
|
|
3
|
+
|
|
4
|
+
[*]
|
|
5
|
+
end_of_line = lf
|
|
6
|
+
insert_final_newline = true
|
|
7
|
+
charset = utf-8
|
|
8
|
+
trim_trailing_whitespace = true
|
|
9
|
+
indent_style = space
|
|
10
|
+
indent_size = 2
|
|
11
|
+
|
|
12
|
+
[*.py]
|
|
13
|
+
indent_size = 4
|
|
14
|
+
|
|
15
|
+
[*.md]
|
|
16
|
+
max_line_length = off
|
|
17
|
+
trim_trailing_whitespace = false
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
|
|
2
|
+
## Summary
|
|
3
|
+
- What change does this PR introduce and why?
|
|
4
|
+
- Link to the PRIMARY GOAL or tracked issue: <link>
|
|
5
|
+
|
|
6
|
+
## Risk Level
|
|
7
|
+
- [ ] Low
|
|
8
|
+
- [ ] Medium
|
|
9
|
+
- [ ] High (requires explicit rollback plan & ADR)
|
|
10
|
+
|
|
11
|
+
## Change Type
|
|
12
|
+
- [ ] Feature
|
|
13
|
+
- [ ] Refactor
|
|
14
|
+
- [ ] Fix
|
|
15
|
+
- [ ] Docs/Chore
|
|
16
|
+
|
|
17
|
+
## Scope & Impact
|
|
18
|
+
- Affected modules/dirs: <list>
|
|
19
|
+
- Backwards compatibility: <preserved / changed (explain)>
|
|
20
|
+
- Breaking changes: <none / explain + migration>
|
|
21
|
+
|
|
22
|
+
## Test Plan
|
|
23
|
+
- Unit tests added/updated: <list>
|
|
24
|
+
- Integration/Contract tests: <list>
|
|
25
|
+
- Manual verification steps: <commands + expected results>
|
|
26
|
+
|
|
27
|
+
## Context Update (MANDATORY)
|
|
28
|
+
Paste the diff or summary of updates to `/context/*`:
|
|
29
|
+
```
|
|
30
|
+
Overall goal is: <...>
|
|
31
|
+
Last action was: <...>
|
|
32
|
+
Next action will be: <...>
|
|
33
|
+
Blockers/Risks: <...>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Rollback Plan
|
|
37
|
+
- Strategy: <revert commit SHA / feature flag / backup restore>
|
|
38
|
+
- Data/Schema implications: <if any>
|
|
39
|
+
|
|
40
|
+
## Screenshots / Logs (optional)
|
|
41
|
+
<attach>
|
|
42
|
+
|
|
43
|
+
## Checklist
|
|
44
|
+
- [ ] CI green (lint, type, tests, audit)
|
|
45
|
+
- [ ] Coverage ≥ target or unchanged
|
|
46
|
+
- [ ] ADR updated/added when design or contracts change
|
|
47
|
+
- [ ] No secrets/PII introduced; `.agentpackignore` updated if needed
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
|
|
2
|
+
name: CI
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches: [ main ]
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: [ main ]
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
node:
|
|
15
|
+
name: Node CI
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- name: Detect package.json
|
|
20
|
+
id: detect
|
|
21
|
+
run: |
|
|
22
|
+
if [ -f package.json ]; then echo "has_node=true" >> $GITHUB_OUTPUT; else echo "has_node=false" >> $GITHUB_OUTPUT; fi
|
|
23
|
+
- name: Setup Node
|
|
24
|
+
if: steps.detect.outputs.has_node == true
|
|
25
|
+
uses: actions/setup-node@v4
|
|
26
|
+
with:
|
|
27
|
+
node-version: lts/*
|
|
28
|
+
cache: npm
|
|
29
|
+
- name: Install deps
|
|
30
|
+
if: steps.detect.outputs.has_node == true
|
|
31
|
+
run: npm ci --ignore-scripts
|
|
32
|
+
- name: Format & Lint
|
|
33
|
+
if: steps.detect.outputs.has_node == true
|
|
34
|
+
run: |
|
|
35
|
+
(npm run -s fmt || npx --yes prettier -c .) || true
|
|
36
|
+
(npm run -s lint || npx --yes eslint .)
|
|
37
|
+
- name: Type Check
|
|
38
|
+
if: steps.detect.outputs.has_node == true && hashFiles(tsconfig.json) !=
|
|
39
|
+
run: (npm run -s typecheck || npx --yes tsc -p . --noEmit)
|
|
40
|
+
- name: Test
|
|
41
|
+
if: steps.detect.outputs.has_node == true
|
|
42
|
+
run: |
|
|
43
|
+
npm test --silent || npm run -s test
|
|
44
|
+
- name: Audit (non-blocking)
|
|
45
|
+
if: steps.detect.outputs.has_node == true
|
|
46
|
+
run: npm audit --audit-level=high || true
|
|
47
|
+
|
|
48
|
+
python:
|
|
49
|
+
name: Python CI
|
|
50
|
+
runs-on: ubuntu-latest
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/checkout@v4
|
|
53
|
+
- name: Detect Python project
|
|
54
|
+
id: detect
|
|
55
|
+
run: |
|
|
56
|
+
if [ -f requirements.txt ] || [ -f pyproject.toml ]; then echo "has_py=true" >> $GITHUB_OUTPUT; else echo "has_py=false" >> $GITHUB_OUTPUT; fi
|
|
57
|
+
- name: Setup Python
|
|
58
|
+
if: steps.detect.outputs.has_py == true
|
|
59
|
+
uses: actions/setup-python@v5
|
|
60
|
+
with:
|
|
61
|
+
python-version: 3.11
|
|
62
|
+
cache: pip
|
|
63
|
+
- name: Install deps
|
|
64
|
+
if: steps.detect.outputs.has_py == true
|
|
65
|
+
run: |
|
|
66
|
+
python -m pip install --upgrade pip
|
|
67
|
+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
|
68
|
+
pip install ruff pytest mypy pip-audit
|
|
69
|
+
- name: Format & Lint
|
|
70
|
+
if: steps.detect.outputs.has_py == true
|
|
71
|
+
run: |
|
|
72
|
+
ruff format --check . || true
|
|
73
|
+
ruff check .
|
|
74
|
+
- name: Type Check
|
|
75
|
+
if: steps.detect.outputs.has_py == true
|
|
76
|
+
run: mypy . || true
|
|
77
|
+
- name: Test
|
|
78
|
+
if: steps.detect.outputs.has_py == true
|
|
79
|
+
run: pytest -q
|
|
80
|
+
- name: Audit (non-blocking)
|
|
81
|
+
if: steps.detect.outputs.has_py == true
|
|
82
|
+
run: pip-audit || true
|
|
83
|
+
|
|
84
|
+
gates:
|
|
85
|
+
name: Quality Gates
|
|
86
|
+
runs-on: ubuntu-latest
|
|
87
|
+
needs: [node, python]
|
|
88
|
+
steps:
|
|
89
|
+
- name: All language jobs completed
|
|
90
|
+
run: echo "Downstream jobs succeeded"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
|
|
2
|
+
name: Refresh Project Tree
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
schedule:
|
|
6
|
+
- cron: 17 3 CODEOWNERS context CONTRIBUTING.md docs infra prompts scripts SECURITY.md services src templates tests tools CODEOWNERS context CONTRIBUTING.md docs infra prompts scripts SECURITY.md services src templates tests tools *
|
|
7
|
+
workflow_dispatch: {}
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
refresh:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- name: Generate project tree
|
|
18
|
+
run: |
|
|
19
|
+
TS=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
20
|
+
echo "# Project Tree (snapshot)" > context/project_tree.md
|
|
21
|
+
echo "_Generated: ${TS}_" >> context/project_tree.md
|
|
22
|
+
echo >> context/project_tree.md
|
|
23
|
+
echo >> context/project_tree.md
|
|
24
|
+
- name: Commit changes if any
|
|
25
|
+
run: |
|
|
26
|
+
git config user.name "github-actions"
|
|
27
|
+
git config user.email "actions@github.com"
|
|
28
|
+
git add context/project_tree.md || true
|
|
29
|
+
if ! git diff --cached --quiet; then
|
|
30
|
+
git commit -m "chore(context): refresh project_tree snapshot" && git push;
|
|
31
|
+
else
|
|
32
|
+
echo "No changes to commit.";
|
|
33
|
+
fi
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
title = "paircoder gitleaks config"
|
|
3
|
+
|
|
4
|
+
[allowlist]
|
|
5
|
+
description = "Allowed patterns"
|
|
6
|
+
files = [
|
|
7
|
+
"^context/project_tree.md$",
|
|
8
|
+
"^docs/adr/",
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
[allowlist.regexes]
|
|
12
|
+
description = "Common false positives"
|
|
13
|
+
regexes = [
|
|
14
|
+
"(?i)example(_|-)secret",
|
|
15
|
+
"(?i)dummy(token|key)",
|
|
16
|
+
"\b[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b",
|
|
17
|
+
]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
|
|
2
|
+
repos:
|
|
3
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
4
|
+
rev: v4.6.0
|
|
5
|
+
hooks:
|
|
6
|
+
- id: check-yaml
|
|
7
|
+
- id: check-json
|
|
8
|
+
- id: check-toml
|
|
9
|
+
- id: end-of-file-fixer
|
|
10
|
+
- id: trailing-whitespace
|
|
11
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
12
|
+
rev: v0.5.7
|
|
13
|
+
hooks:
|
|
14
|
+
- id: ruff
|
|
15
|
+
args: ["--fix"]
|
|
16
|
+
- id: ruff-format
|
|
17
|
+
- repo: https://github.com/igorshubovych/markdownlint-cli
|
|
18
|
+
rev: v0.42.0
|
|
19
|
+
hooks:
|
|
20
|
+
- id: markdownlint
|
|
21
|
+
args: ["--fix"]
|
|
22
|
+
- repo: https://github.com/pre-commit/mirrors-prettier
|
|
23
|
+
rev: v3.3.3
|
|
24
|
+
hooks:
|
|
25
|
+
- id: prettier
|
|
26
|
+
additional_dependencies: []
|
|
27
|
+
- repo: https://github.com/pre-commit/mirrors-eslint
|
|
28
|
+
rev: v9.9.1
|
|
29
|
+
hooks:
|
|
30
|
+
- id: eslint
|
|
31
|
+
additional_dependencies: []
|
|
32
|
+
files: \.(js|jsx|ts|tsx)$
|
|
33
|
+
args: ["--max-warnings", "0"]
|
|
34
|
+
- repo: local
|
|
35
|
+
hooks:
|
|
36
|
+
- id: gitleaks-scan
|
|
37
|
+
name: gitleaks scan
|
|
38
|
+
entry: bash -c gitleaks detect --no-banner --redact --config .gitleaks.toml
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
|
|
2
|
+
* @{{cookiecutter.owner_gh_handle}}
|
|
3
|
+
/context/* @{{cookiecutter.owner_gh_handle}} @{{cookiecutter.architect_gh_handle}}
|
|
4
|
+
/scripts/* @{{cookiecutter.owner_gh_handle}} @{{cookiecutter.build_gh_handle}}
|
|
5
|
+
/.github/* @{{cookiecutter.owner_gh_handle}} @{{cookiecutter.build_gh_handle}}
|
|
6
|
+
/prompts/* @{{cookiecutter.owner_gh_handle}} @{{cookiecutter.architect_gh_handle}}
|
|
7
|
+
/tests/** @{{cookiecutter.qa_gh_handle}} @{{cookiecutter.owner_gh_handle}}
|
|
8
|
+
/infra/** @{{cookiecutter.sre_gh_handle}} @{{cookiecutter.owner_gh_handle}}
|
|
9
|
+
/schemas/** @{{cookiecutter.architect_gh_handle}} @{{cookiecutter.owner_gh_handle}}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
# Contributing Guide
|
|
3
|
+
|
|
4
|
+
Thanks for contributing! This repo is optimized for AI pair coding and human review.
|
|
5
|
+
|
|
6
|
+
## Branching & Commits
|
|
7
|
+
- Branch from `main` using: `feature/<short-goal>`, `refactor/<module>`, or `fix/<ticket>`.
|
|
8
|
+
- Use Conventional Commits (feat, fix, refactor, docs, test, chore, build, ci).
|
|
9
|
+
|
|
10
|
+
## Local Dev & CI
|
|
11
|
+
- Run local CI before pushing:
|
|
12
|
+
```bash
|
|
13
|
+
scripts/ci_local.sh
|
|
14
|
+
```
|
|
15
|
+
- Add/adjust tests **before** behavior changes.
|
|
16
|
+
- Keep diffs small and focused; open PRs early.
|
|
17
|
+
|
|
18
|
+
## Context Discipline (MANDATORY)
|
|
19
|
+
- `/context/*` is canonical for agent runs. Update the Context Sync block in `context/development.md` after each change:
|
|
20
|
+
```
|
|
21
|
+
## Context Sync (AUTO-UPDATED)
|
|
22
|
+
Overall goal is: <PRIMARY GOAL>
|
|
23
|
+
Last action was: <what changed and why> (commit SHA)
|
|
24
|
+
Next action will be: <smallest valuable step with owner>
|
|
25
|
+
Blockers/Risks: <if any>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Pull Requests
|
|
29
|
+
- Use the PR template (risk, test plan, rollback, context diff).
|
|
30
|
+
- Public API/infra changes require an ADR under `/docs/adr/`.
|
|
31
|
+
- High-risk PRs require CODEOWNERS sign-off.
|
|
32
|
+
|
|
33
|
+
## Secrets & Data Safety
|
|
34
|
+
- Never commit secrets; provide `.env.example`.
|
|
35
|
+
- Do not include binaries/media in agent packs; maintain `.agentpackignore`.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
# Security Policy
|
|
3
|
+
|
|
4
|
+
## Supported Versions
|
|
5
|
+
We aim to keep `main` secure. Fixes are backported to the latest minor.
|
|
6
|
+
|
|
7
|
+
## Reporting a Vulnerability
|
|
8
|
+
- Email: security@<your-domain> (or open a private GitHub security advisory)
|
|
9
|
+
- Include: affected versions/commit, reproduction, impact, proposed severity.
|
|
10
|
+
- We acknowledge within 2 business days and update until resolution.
|
|
11
|
+
|
|
12
|
+
## Handling Sensitive Data
|
|
13
|
+
- No secrets in repo or agent packs.
|
|
14
|
+
- Use redacted fixtures/synthetic data for tests and context.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Agents Guide\n\nThis project uses a **Context Loop**. Always keep these fields current:\n\n- **Overall goal is:** Single-sentence mission\n- **Last action was:** What just completed\n- **Next action will be:** The very next step\n- **Blockers:** Known issues or decisions needed\n\n### Working Rules for Agents\n- Do not modify or examine ignored directories (see `.agentpackignore`). Assume large assets exist even if excluded.\n- Prefer minimal, reversible changes.\n- After committing code, run `bpsai-pair context-sync` to update the loop.\n- Request a new context pack when the tree or docs change significantly.\n\n### Context Pack\nRun `bpsai-pair pack --out agent_pack.tgz` and upload to your session.\n
|
|
2
|
+
---
|
|
3
|
+
|
|
4
|
+
## Branch Discipline
|
|
5
|
+
- Use `--type feature|fix|refactor` when creating features.
|
|
6
|
+
- Conventional Commits recommended.
|