bpsai-pair 0.2.0__py3-none-any.whl → 0.2.2__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 CHANGED
@@ -2,7 +2,7 @@
2
2
  bpsai_pair package
3
3
  """
4
4
 
5
- __version__ = "0.2.0"
5
+ __version__ = "0.2.2"
6
6
 
7
7
  # Make modules available at package level
8
8
  from . import cli
@@ -1 +1,19 @@
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
1
+ # Default agent pack exclusions
2
+ .git/
3
+ .venv/
4
+ venv/
5
+ env/
6
+ __pycache__/
7
+ node_modules/
8
+ dist/
9
+ build/
10
+ *.log
11
+ *.bak
12
+ *.tgz
13
+ *.tar.gz
14
+ *.zip
15
+ .env
16
+ .env.*
17
+ *.pyc
18
+ .DS_Store
19
+ Thumbs.db
@@ -1,9 +1,8 @@
1
-
2
1
  name: Refresh Project Tree
3
2
 
4
3
  on:
5
4
  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 *
5
+ - cron: '17 3 * * *' # Daily at 03:17 UTC
7
6
  workflow_dispatch: {}
8
7
 
9
8
  permissions:
@@ -14,20 +13,42 @@ jobs:
14
13
  runs-on: ubuntu-latest
15
14
  steps:
16
15
  - uses: actions/checkout@v4
16
+
17
17
  - name: Generate project tree
18
18
  run: |
19
19
  TS=$(date -u +%Y-%m-%dT%H:%M:%SZ)
20
20
  echo "# Project Tree (snapshot)" > context/project_tree.md
21
21
  echo "_Generated: ${TS}_" >> context/project_tree.md
22
- echo >> context/project_tree.md
23
- echo >> context/project_tree.md
22
+ echo "" >> context/project_tree.md
23
+ echo '```' >> context/project_tree.md
24
+
25
+ # Use find to create tree (cross-platform)
26
+ find . \
27
+ -path './.git' -prune -o \
28
+ -path './node_modules' -prune -o \
29
+ -path './dist' -prune -o \
30
+ -path './build' -prune -o \
31
+ -path './.venv' -prune -o \
32
+ -path './__pycache__' -prune -o \
33
+ -path './tools/cli/dist' -prune -o \
34
+ -path './tools/cli/build' -prune -o \
35
+ -path './**/*.egg-info' -prune -o \
36
+ -type f -print -o \
37
+ -type d -print | \
38
+ sed 's|^\./||' | \
39
+ grep -v '^\.$' | \
40
+ sort >> context/project_tree.md
41
+
42
+ echo '```' >> context/project_tree.md
43
+
24
44
  - name: Commit changes if any
25
45
  run: |
26
- git config user.name "github-actions"
27
- git config user.email "actions@github.com"
46
+ git config user.name "github-actions[bot]"
47
+ git config user.email "github-actions[bot]@users.noreply.github.com"
28
48
  git add context/project_tree.md || true
29
49
  if ! git diff --cached --quiet; then
30
- git commit -m "chore(context): refresh project_tree snapshot" && git push;
50
+ git commit -m "chore(context): refresh project_tree snapshot [skip ci]"
51
+ git push
31
52
  else
32
- echo "No changes to commit.";
53
+ echo "No changes to commit."
33
54
  fi
@@ -0,0 +1,29 @@
1
+ # Agents Guide (Root Pointer)
2
+
3
+ Welcome, AI agent! This project uses PairCoder for AI pair programming.
4
+
5
+ ## Where to Start
6
+
7
+ All instructions, context, and project information are maintained in the `/context` directory:
8
+
9
+ 1. **Read first:** `/context/agents.md` - Complete playbook and guidelines
10
+ 2. **Current state:** `/context/development.md` - Roadmap and Context Loop
11
+ 3. **File structure:** `/context/project_tree.md` - Repository layout
12
+ 4. **Component docs:** `/context/directory_notes/` - Directory-specific guidance
13
+
14
+ ## Critical Reminder
15
+
16
+ Always check the Context Loop at the end of `/context/development.md` for:
17
+ - **Overall goal is:** The project's primary objective
18
+ - **Last action was:** What was just completed
19
+ - **Next action will be:** The immediate next step
20
+ - **Blockers/Risks:** Any issues needing attention
21
+
22
+ ## After Making Changes
23
+
24
+ Update the Context Loop using:
25
+ ```bash
26
+ bpsai-pair context-sync --last "What you did" --next "Next step" --blockers "Any issues"
27
+ ```
28
+
29
+ Begin by reading `/context/agents.md` for complete instructions.
@@ -0,0 +1,32 @@
1
+ # Claude Guide (Root Pointer)
2
+
3
+ Hello Claude! This project follows PairCoder conventions for AI pair programming.
4
+
5
+ ## Getting Started
6
+
7
+ Your instructions are organized in the `/context` directory:
8
+
9
+ 1. **Start here:** `/context/agents.md` - Full AI pairing playbook
10
+ 2. **Project state:** `/context/development.md` - Current goals and progress
11
+ 3. **Repository map:** `/context/project_tree.md` - File structure reference
12
+
13
+ ## Working in This Repository
14
+
15
+ Before making any changes:
16
+ - Review the Context Loop in `/context/development.md`
17
+ - Understand the current phase and next actions
18
+ - Check for any blockers or risks
19
+
20
+ After making changes:
21
+ - Update the Context Loop to maintain continuity
22
+ - Use the provided CLI command or edit directly
23
+ - Keep changes focused and well-documented
24
+
25
+ ## PairCoder Principles
26
+
27
+ - Maintain the Context Loop discipline
28
+ - Make small, reversible changes
29
+ - Add tests before implementing features
30
+ - Follow the project's established patterns
31
+
32
+ See `/context/agents.md` for detailed instructions.
@@ -1,4 +1,13 @@
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
1
+ # Agents Guide
2
+
3
+ This project uses a **Context Loop**. Always keep these fields current:
4
+
5
+ - **Overall goal is:** Single-sentence mission
6
+ - **Last action was:** What just completed
7
+ - **Next action will be:** The very next step
8
+ - **Blockers:** Known issues or decisions needed
9
+
10
+ ### 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
11
  ---
3
12
 
4
13
  ## Branch Discipline
@@ -1 +1,11 @@
1
- # Development Log\n\n**Phase:** (set by first feature)\n**Primary Goal:** (set by first feature)\n\n## Context Sync (AUTO-UPDATED)\n\n- **Overall goal is:** (set by feature)\n- **Last action was:** (set by feature)\n- **Next action will be:** (set by feature)\n- **Blockers:** (set by feature)\n
1
+ # Development Log
2
+
3
+ **Phase:** (set by first feature)
4
+ **Primary Goal:** (set by first feature)
5
+
6
+ ## Context Sync (AUTO-UPDATED)
7
+
8
+ - **Overall goal is:** (set by feature)
9
+ - **Last action was:** (set by feature)
10
+ - **Next action will be:** (set by feature)
11
+ - **Blockers:** (set by feature)
@@ -1,6 +1,6 @@
1
1
  # Scripts directory (intentionally minimal)
2
2
 
3
- This project uses the **PairCoder CLI** (Python) — no Bash scripts required.
3
+ This project uses the **PairCoder CLI** (Python) — Bash scripts have been deprecated.
4
4
  Common commands:
5
5
 
6
6
  ```bash
bpsai_pair/ops.py CHANGED
@@ -17,12 +17,12 @@ import json
17
17
 
18
18
  class GitOps:
19
19
  """Git operations helper."""
20
-
20
+
21
21
  @staticmethod
22
22
  def is_repo(path: Path) -> bool:
23
23
  """Check if path is a git repo."""
24
24
  return (path / ".git").exists()
25
-
25
+
26
26
  @staticmethod
27
27
  def is_clean(path: Path) -> bool:
28
28
  """Check if working tree is clean."""
@@ -58,7 +58,7 @@ class GitOps:
58
58
  return True
59
59
  except:
60
60
  return False
61
-
61
+
62
62
  @staticmethod
63
63
  def current_branch(path: Path) -> str:
64
64
  """Get current branch name."""
@@ -69,7 +69,7 @@ class GitOps:
69
69
  text=True
70
70
  )
71
71
  return result.stdout.strip() if result.returncode == 0 else ""
72
-
72
+
73
73
  @staticmethod
74
74
  def create_branch(path: Path, branch: str, from_branch: str = "main") -> bool:
75
75
  """Create and checkout a new branch."""
@@ -81,10 +81,10 @@ class GitOps:
81
81
  )
82
82
  if check.returncode != 0:
83
83
  return False
84
-
84
+
85
85
  # Checkout source branch
86
86
  subprocess.run(["git", "checkout", from_branch], cwd=path, capture_output=True)
87
-
87
+
88
88
  # Pull if upstream exists
89
89
  upstream = subprocess.run(
90
90
  ["git", "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"],
@@ -93,7 +93,7 @@ class GitOps:
93
93
  )
94
94
  if upstream.returncode == 0:
95
95
  subprocess.run(["git", "pull", "--ff-only"], cwd=path, capture_output=True)
96
-
96
+
97
97
  # Create new branch
98
98
  result = subprocess.run(
99
99
  ["git", "checkout", "-b", branch],
@@ -101,13 +101,13 @@ class GitOps:
101
101
  capture_output=True
102
102
  )
103
103
  return result.returncode == 0
104
-
104
+
105
105
  @staticmethod
106
106
  def add_commit(path: Path, files: List[Path], message: str) -> bool:
107
107
  """Add files and commit."""
108
108
  for f in files:
109
109
  subprocess.run(["git", "add", str(f)], cwd=path, capture_output=True)
110
-
110
+
111
111
  result = subprocess.run(
112
112
  ["git", "commit", "-m", message],
113
113
  cwd=path,
@@ -118,19 +118,19 @@ class GitOps:
118
118
 
119
119
  class ProjectTree:
120
120
  """Generate project tree snapshots."""
121
-
121
+
122
122
  @staticmethod
123
123
  def generate(root: Path, excludes: Optional[Set[str]] = None) -> str:
124
124
  """Generate a tree structure of the project."""
125
125
  if excludes is None:
126
126
  excludes = {
127
- '.git', '.venv', 'venv', '__pycache__',
127
+ '.git', '.venv', 'venv', '__pycache__',
128
128
  'node_modules', 'dist', 'build', '.mypy_cache',
129
129
  '.pytest_cache', '.tox', '*.egg-info', '.DS_Store'
130
130
  }
131
-
131
+
132
132
  tree_lines = []
133
-
133
+
134
134
  def should_skip(path: Path) -> bool:
135
135
  name = path.name
136
136
  for pattern in excludes:
@@ -139,20 +139,20 @@ class ProjectTree:
139
139
  if name == pattern:
140
140
  return True
141
141
  return False
142
-
142
+
143
143
  def walk_dir(dir_path: Path, prefix: str = ""):
144
144
  items = sorted(dir_path.iterdir(), key=lambda x: (x.is_file(), x.name))
145
145
  items = [i for i in items if not should_skip(i)]
146
-
146
+
147
147
  for i, item in enumerate(items):
148
148
  is_last = i == len(items) - 1
149
149
  current = "└── " if is_last else "├── "
150
150
  tree_lines.append(f"{prefix}{current}{item.name}")
151
-
151
+
152
152
  if item.is_dir():
153
153
  extension = " " if is_last else "│ "
154
154
  walk_dir(item, prefix + extension)
155
-
155
+
156
156
  tree_lines.append(".")
157
157
  walk_dir(root)
158
158
  return "\n".join(tree_lines)
@@ -160,7 +160,7 @@ class ProjectTree:
160
160
 
161
161
  class ContextPacker:
162
162
  """Package context files for AI agents."""
163
-
163
+
164
164
  @staticmethod
165
165
  def read_ignore_patterns(ignore_file: Path) -> Set[str]:
166
166
  """Read patterns from .agentpackignore file."""
@@ -170,42 +170,41 @@ class ContextPacker:
170
170
  for line in f:
171
171
  line = line.strip()
172
172
  if line and not line.startswith('#'):
173
- patterns.add(line.rstrip('/'))
173
+ patterns.add(line)
174
174
  else:
175
175
  # Default patterns
176
176
  patterns = {
177
177
  '.git', '.venv', '__pycache__', 'node_modules',
178
- 'dist', 'build', '*.log', '*.bak', '*.tgz',
178
+ 'dist', 'build', '*.log', '*.bak', '*.tgz',
179
179
  '*.tar.gz', '*.zip', '.env*'
180
180
  }
181
181
  return patterns
182
-
182
+
183
183
  @staticmethod
184
184
  def should_exclude(path: Path, patterns: Set[str]) -> bool:
185
185
  """Check if path should be excluded based on patterns."""
186
- path_str = str(path).replace('\\', '/')
187
-
186
+ from pathlib import PurePath
187
+
188
+ p = PurePath(path.as_posix())
189
+
188
190
  for pattern in patterns:
189
- # Handle wildcards
190
- if '*' in pattern:
191
- import fnmatch
192
- if fnmatch.fnmatch(path.name, pattern):
191
+ # Handle directory patterns (ending with /)
192
+ if pattern.endswith('/'):
193
+ dir_pattern = pattern.rstrip('/')
194
+ # Check if this is the directory itself or if it's inside the directory
195
+ if path.is_dir() and p.match(dir_pattern):
196
+ return True
197
+ if any(parent.match(dir_pattern) for parent in p.parents):
193
198
  return True
194
- if fnmatch.fnmatch(path_str, pattern):
199
+ # Handle file/general patterns
200
+ else:
201
+ if p.match(pattern):
195
202
  return True
196
- # Handle directory patterns
197
- elif pattern.endswith('/'):
198
- if path.is_dir() and path.name == pattern[:-1]:
203
+ if any(parent.match(pattern) for parent in p.parents):
199
204
  return True
200
- # Exact match
201
- elif path.name == pattern:
202
- return True
203
- # Path contains pattern
204
- elif pattern in path_str.split('/'):
205
- return True
206
-
205
+
207
206
  return False
208
-
207
+
209
208
  @staticmethod
210
209
  def pack(
211
210
  root: Path,
@@ -217,33 +216,33 @@ class ContextPacker:
217
216
  # Default files to include
218
217
  context_files = [
219
218
  root / "context" / "development.md",
220
- root / "context" / "agents.md",
219
+ root / "context" / "agents.md",
221
220
  root / "context" / "project_tree.md",
222
221
  ]
223
-
222
+
224
223
  # Add directory_notes if it exists
225
224
  dir_notes = root / "context" / "directory_notes"
226
225
  if dir_notes.exists():
227
226
  for note in dir_notes.rglob("*.md"):
228
227
  context_files.append(note)
229
-
228
+
230
229
  # Add extra files
231
230
  if extra_files:
232
231
  for extra in extra_files:
233
232
  extra_path = root / extra
234
233
  if extra_path.exists():
235
234
  context_files.append(extra_path)
236
-
235
+
237
236
  # Filter out non-existent files
238
237
  context_files = [f for f in context_files if f.exists()]
239
-
238
+
240
239
  if dry_run:
241
240
  return context_files
242
-
241
+
243
242
  # Read ignore patterns
244
243
  ignore_file = root / ".agentpackignore"
245
244
  patterns = ContextPacker.read_ignore_patterns(ignore_file)
246
-
245
+
247
246
  # Create tarball
248
247
  with tarfile.open(output, "w:gz") as tar:
249
248
  for file_path in context_files:
@@ -251,13 +250,13 @@ class ContextPacker:
251
250
  if not ContextPacker.should_exclude(file_path, patterns):
252
251
  arcname = file_path.relative_to(root)
253
252
  tar.add(file_path, arcname=str(arcname))
254
-
253
+
255
254
  return context_files
256
255
 
257
256
 
258
257
  class FeatureOps:
259
258
  """Operations for feature branch management."""
260
-
259
+
261
260
  @staticmethod
262
261
  def create_feature(
263
262
  root: Path,
@@ -271,17 +270,17 @@ class FeatureOps:
271
270
  # Check if working tree is clean
272
271
  if not force and not GitOps.is_clean(root):
273
272
  raise ValueError("Working tree not clean. Commit or stash changes, or use --force")
274
-
273
+
275
274
  # Create branch
276
275
  branch_name = f"{branch_type}/{name}"
277
276
  if not GitOps.create_branch(root, branch_name):
278
277
  raise ValueError(f"Failed to create branch {branch_name}")
279
-
278
+
280
279
  # Ensure context directory structure
281
280
  context_dir = root / "context"
282
281
  context_dir.mkdir(exist_ok=True)
283
282
  (context_dir / "directory_notes").mkdir(exist_ok=True)
284
-
283
+
285
284
  # Update or create development.md
286
285
  dev_file = context_dir / "development.md"
287
286
  if not dev_file.exists():
@@ -301,7 +300,7 @@ class FeatureOps:
301
300
  else:
302
301
  # Update existing file
303
302
  content = dev_file.read_text()
304
-
303
+
305
304
  # Update Primary Goal
306
305
  if primary_goal:
307
306
  import re
@@ -315,7 +314,7 @@ class FeatureOps:
315
314
  f'Overall goal is: {primary_goal}',
316
315
  content
317
316
  )
318
-
317
+
319
318
  # Update Phase
320
319
  if phase:
321
320
  content = re.sub(
@@ -328,16 +327,16 @@ class FeatureOps:
328
327
  f'Next action will be: {phase}',
329
328
  content
330
329
  )
331
-
330
+
332
331
  # Update Last action
333
332
  content = re.sub(
334
333
  r'Last action was:.*',
335
334
  f'Last action was: Created feature branch {branch_name}',
336
335
  content
337
336
  )
338
-
337
+
339
338
  dev_file.write_text(content)
340
-
339
+
341
340
  # Create agents.md if missing
342
341
  agents_file = context_dir / "agents.md"
343
342
  if not agents_file.exists():
@@ -360,7 +359,7 @@ This project uses a **Context Loop**. Always keep these fields current:
360
359
  Run `bpsai-pair pack --out agent_pack.tgz` and upload to your session.
361
360
  """
362
361
  agents_file.write_text(agents_content)
363
-
362
+
364
363
  # Generate project tree
365
364
  tree_file = context_dir / "project_tree.md"
366
365
  tree_content = f"""# Project Tree (snapshot)
@@ -371,29 +370,29 @@ _Generated: {datetime.now(timezone.utc).isoformat()}Z_
371
370
  ```
372
371
  """
373
372
  tree_file.write_text(tree_content)
374
-
373
+
375
374
  # Commit changes
376
375
  GitOps.add_commit(
377
376
  root,
378
377
  [dev_file, agents_file, tree_file],
379
378
  f"feat(context): start {branch_name} — Primary Goal: {primary_goal or 'TBD'}"
380
379
  )
381
-
380
+
382
381
  return True
383
382
 
384
383
 
385
384
  class LocalCI:
386
385
  """Cross-platform local CI runner."""
387
-
386
+
388
387
  @staticmethod
389
388
  def run_python_checks(root: Path) -> dict:
390
389
  """Run Python linting, formatting, and tests."""
391
390
  results = {}
392
-
391
+
393
392
  # Check if Python project
394
393
  if not ((root / "pyproject.toml").exists() or (root / "requirements.txt").exists()):
395
394
  return results
396
-
395
+
397
396
  # Try to run ruff
398
397
  try:
399
398
  subprocess.run(["ruff", "format", "--check", "."], cwd=root, check=True)
@@ -401,46 +400,46 @@ class LocalCI:
401
400
  results["ruff"] = "passed"
402
401
  except:
403
402
  results["ruff"] = "failed or not installed"
404
-
403
+
405
404
  # Try to run mypy
406
405
  try:
407
406
  subprocess.run(["mypy", "."], cwd=root, check=True)
408
407
  results["mypy"] = "passed"
409
408
  except:
410
409
  results["mypy"] = "failed or not installed"
411
-
410
+
412
411
  # Try to run pytest
413
412
  try:
414
413
  subprocess.run(["pytest", "-q"], cwd=root, check=True)
415
414
  results["pytest"] = "passed"
416
415
  except:
417
416
  results["pytest"] = "failed or not installed"
418
-
417
+
419
418
  return results
420
-
419
+
421
420
  @staticmethod
422
421
  def run_node_checks(root: Path) -> dict:
423
422
  """Run Node.js linting, formatting, and tests."""
424
423
  results = {}
425
-
424
+
426
425
  if not (root / "package.json").exists():
427
426
  return results
428
-
427
+
429
428
  # Try npm commands
430
429
  try:
431
430
  subprocess.run(["npm", "run", "lint"], cwd=root, check=True)
432
431
  results["eslint"] = "passed"
433
432
  except:
434
433
  results["eslint"] = "failed or not configured"
435
-
434
+
436
435
  try:
437
436
  subprocess.run(["npm", "test"], cwd=root, check=True)
438
437
  results["npm test"] = "passed"
439
438
  except:
440
439
  results["npm test"] = "failed or not configured"
441
-
440
+
442
441
  return results
443
-
442
+
444
443
  @staticmethod
445
444
  def run_all(root: Path) -> dict:
446
445
  """Run all applicable CI checks."""
@@ -0,0 +1,89 @@
1
+ Metadata-Version: 2.4
2
+ Name: bpsai-pair
3
+ Version: 0.2.2
4
+ Summary: CLI for AI pair-coding workflow
5
+ Author: BPS AI Software
6
+ Requires-Python: >=3.9
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: typer>=0.12
9
+ Requires-Dist: rich>=13.7
10
+ Requires-Dist: pyyaml>=6.0
11
+
12
+ # bpsai-pair CLI
13
+
14
+ The PairCoder CLI tool for AI pair programming workflows.
15
+
16
+ ## Quick Start
17
+
18
+ ### Install from PyPI
19
+ ```bash
20
+ pip install bpsai-pair
21
+ bpsai-pair --help
22
+ ```
23
+
24
+ ### Development Install
25
+ ```bash
26
+ cd tools/cli
27
+ pip install -e .
28
+ bpsai-pair --help
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ### Initialize scaffolding (uses bundled template)
34
+ ```bash
35
+ bpsai-pair-init
36
+ # or with main CLI:
37
+ bpsai-pair init
38
+ ```
39
+
40
+ ### Create feature branch
41
+ ```bash
42
+ bpsai-pair feature auth-refactor \
43
+ --type refactor \
44
+ --primary "Decouple auth via DI" \
45
+ --phase "Refactor auth module + tests"
46
+ ```
47
+
48
+ ### Pack context for AI
49
+ ```bash
50
+ bpsai-pair pack --out agent_pack.tgz
51
+ bpsai-pair pack --list # Preview files
52
+ bpsai-pair pack --json # JSON output
53
+ ```
54
+
55
+ ### Update context loop
56
+ ```bash
57
+ bpsai-pair context-sync \
58
+ --last "Initialized scaffolding" \
59
+ --next "Set up CI secrets" \
60
+ --blockers "None"
61
+ ```
62
+
63
+ ## Commands
64
+
65
+ - `bpsai-pair init` - Initialize repo with PairCoder structure
66
+ - `bpsai-pair-init` - Quick init with bundled template (no args)
67
+ - `bpsai-pair feature` - Create feature/fix/refactor branch
68
+ - `bpsai-pair pack` - Package context for AI agents
69
+ - `bpsai-pair context-sync` - Update the Context Loop
70
+ - `bpsai-pair status` - Show current state
71
+ - `bpsai-pair validate` - Check repo structure
72
+ - `bpsai-pair ci` - Run local CI checks
73
+
74
+ ## Development
75
+
76
+ Run tests:
77
+ ```bash
78
+ pytest
79
+ ```
80
+
81
+ Build wheel:
82
+ ```bash
83
+ python -m build
84
+ ```
85
+
86
+ ## Template
87
+
88
+ The CLI bundles a cookiecutter template in `bpsai_pair/data/cookiecutter-paircoder/`
89
+ that gets installed with the package and used by `bpsai-pair-init`.
@@ -1,42 +1,42 @@
1
- bpsai_pair/__init__.py,sha256=LpEf96Jx3tRBAzZZ8ZkPU7yWnTduFq_vZYru2jLa1Rs,379
1
+ bpsai_pair/__init__.py,sha256=j8lrbjl_BGMHWus8G47VvaIP9w7kM_aQ7_kdGVY1hzM,379
2
2
  bpsai_pair/__main__.py,sha256=Qd-f8z2Q2vpiEP2x6PBFsJrpACWDVxFKQk820MhFmHo,59
3
3
  bpsai_pair/adapters.py,sha256=oowin5juQ9dTRwpHD8eJGBCahI2BoQZVEAhueDD821Y,323
4
4
  bpsai_pair/cli.py,sha256=qc5Pcr8gUG-xuJtrqqMT-kxlHi6zUyPDIUrOIuE_110,18422
5
5
  bpsai_pair/config.py,sha256=OIBaBkjz_jNsms4zjFxnmEN9gpqRLk1FTPq4ZBNkAUM,8750
6
6
  bpsai_pair/init_bundled_cli.py,sha256=AjtdC7yt4p-FatYt5y65XEwH9CtSFKnRc09cgb0di3I,1906
7
7
  bpsai_pair/jsonio.py,sha256=C_n42gPLRqjpif-AO0vjE3G1ae_v_PT3cywu7J4Xx-M,189
8
- bpsai_pair/ops.py,sha256=wYVhZ5XNfRjBkr3Ztvp6j1bBW-0bOggyAQOCVHmjPuA,14706
8
+ bpsai_pair/ops.py,sha256=BbGEefND2xLCpxtZvKJtyDVd_yWW61g-LtPWXsVTEys,14340
9
9
  bpsai_pair/pyutils.py,sha256=5ub27mF4OIaGDm7CDacXsU_9FUZVaPOnYp2NyavFydo,998
10
10
  bpsai_pair/utils.py,sha256=TzaN27qKsBlRQCYHBcA06ufQERcc1fcyyOY7QlJQv8M,289
11
11
  bpsai_pair/data/cookiecutter-paircoder/cookiecutter.json,sha256=FoaPEPabd5AneVMamzBSw6QNsLZ3DhJRkNtmPOVtSIY,346
12
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/.agentpackignore,sha256=4NkboG3S3zI736evJVm4RTjmzFxx_hvMi6KTBaGnLxU,130
12
+ bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/.agentpackignore,sha256=CIhaDHCwLQMwHBtVEL5VVgM9c_OQ6jKvRU7H8kvSNEI,167
13
13
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/.editorconfig,sha256=Qpd1apKmHcq5gVWI8jpoxAbY_LF_04tkclK06aE2lB0,235
14
14
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/.gitignore,sha256=ylldDXITCRrj038OGp1pBwihuM4SA6vDvThOvilZV0Q,47
15
15
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/.gitleaks.toml,sha256=STXJmj13hqxp9ejb2hImc1h72BPB_1qsgT_b86o0HYA,373
16
16
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/.pre-commit-config.yaml,sha256=o9AmanEQcUJCYOlnKg4SNcA_rviJKbczGiijyp9oSdY,1021
17
+ bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/AGENTS.md,sha256=sChAlA26eE_I57Sfv3d6gAbMsu0UQyYxUUtctIbx3RA,1046
18
+ bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/CLAUDE.md,sha256=5u5fq0HQaRsOwfI_h6GmR7lI7mMt91Bfk4aYOLuIzwU,1012
17
19
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/CODEOWNERS,sha256=vWB4d6NzBm1qlhLs_4uFQFz6UDICn5Ehn0NUqvXlMYo,715
18
20
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/CONTRIBUTING.md,sha256=KdZ4GjoyWMSS_kSb4zI0riALXkGuXmsiT09yH5X3Ac4,1191
19
21
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/SECURITY.md,sha256=L6teX1-0xxToQek2m5lR8iEZlJdzg2TYwpSIIKZDTzE,495
20
22
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/.github/PULL_REQUEST_TEMPLATE.md,sha256=VbjBqMsIcryg3YY8TL8NsGnnzE1Thz43MrBaI3-m5mU,1146
21
23
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/.github/workflows/ci.yml,sha256=VisTKdEHgnL6902gqKczr5FXFZgJlmA0zw2FJBMjb2Q,2766
22
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/.github/workflows/project_tree.yml,sha256=QXS7BCkua_digYmhhSJtWhVcPrEhVJWbRTqIstdK1nw,1146
23
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/context/agents.md,sha256=yUgVqo5BuU5l3oX1hrRCzjLtvItvfArknxQ7ElrEzo4,836
24
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/context/agents.md.bak,sha256=jO9h6OnstcQmcejpO4BBnjX4USZUuSBDZ0U4GJf0tv0,6813
25
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/context/development.md,sha256=wQIGHsI6YDQ_hv1Ps-xYXf9vLmGi73rWbwTGOzt2S60,295
26
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/context/development.md.bak,sha256=Nm0e5ZNsTWawkcwU61m-6MFwseWouakPMWCwxCNElUY,251
24
+ bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/.github/workflows/project_tree.yml,sha256=jCdBPZSVnoMlrZGZRPrfNI_omy2bBneP501iW0yQ4Nc,1702
25
+ bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/context/agents.md,sha256=3sxgyfzfBY7tjJuGh5t1e9f4qFS8Z5iaT70OEKzB2Ig,827
26
+ bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/context/development.md,sha256=-1Woc7zLMw-YYkkE8_E0indISEiS0wv00qkpCEpyQm8,284
27
27
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/context/project_tree.md,sha256=ppNgU2afWoJOnSJbOl9GoJ8EuNT85N005_8LjilEN4w,89
28
28
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/context/directory_notes/.gitkeep,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
29
29
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/prompts/deep_research.yml,sha256=lhHG4JRi9Zdsq5J8TNaw2HeupD5oD7mm5J0XPNg1H9A,958
30
30
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/prompts/implementation.yml,sha256=co2EmK7ojw3lqeNXf99rlU3wxn6mrSuVAZGMlOEZHt4,639
31
31
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/prompts/roadmap.yml,sha256=EIdCo_EZpPZ9GXXg71IDgMooychyarWPpulojrcGIg8,498
32
- bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/scripts/README.md,sha256=gnZc_VUQPU81z7SIh3VqTqh0JPJJecKWmqWh-eBgJpU,358
32
+ bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/scripts/README.md,sha256=2eHMbeH479EQwq5QFz83Mw-cb_x2SeYlKolk5hfELIw,367
33
33
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/src/.gitkeep,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
34
34
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/templates/adr.md,sha256=y_9hvwCgzukpV2pYOjYGk4kD-a6YZZZJqflMjnHvbLE,460
35
35
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/templates/directory_note.md,sha256=FnNMtnD_CpvVzpp5VzQzS4qddFQ6jfyS8q36ff10DrA,346
36
36
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/tests/example_contract/README.md,sha256=x5ZpsFnN9QiMWtXJsYw9n27ik5Yf425N5tbzMKOpVQQ,109
37
37
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/tests/example_integration/README.md,sha256=_G9MzJQq1GR4fVSgGeUFVo2eyzDXJVQn6mqJ5He1Euk,150
38
- bpsai_pair-0.2.0.dist-info/METADATA,sha256=jBUABNC5sNe8WFZ4oR_SPNAOBfsi8ITw3ufXBielzPk,813
39
- bpsai_pair-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
40
- bpsai_pair-0.2.0.dist-info/entry_points.txt,sha256=tK6yOUS1oseLus0bPQYd2cuLJgL1Zr3AGb_YPHVCtCI,101
41
- bpsai_pair-0.2.0.dist-info/top_level.txt,sha256=kwTlUncK6pxJyQpZQdspexSc-kWtPvZkLUy4ji1P6LU,11
42
- bpsai_pair-0.2.0.dist-info/RECORD,,
38
+ bpsai_pair-0.2.2.dist-info/METADATA,sha256=rz1nAlU4uxedrEUfYzndhr_A838a0UCHujUkBysVC_A,1812
39
+ bpsai_pair-0.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
40
+ bpsai_pair-0.2.2.dist-info/entry_points.txt,sha256=tK6yOUS1oseLus0bPQYd2cuLJgL1Zr3AGb_YPHVCtCI,101
41
+ bpsai_pair-0.2.2.dist-info/top_level.txt,sha256=kwTlUncK6pxJyQpZQdspexSc-kWtPvZkLUy4ji1P6LU,11
42
+ bpsai_pair-0.2.2.dist-info/RECORD,,
@@ -1,196 +0,0 @@
1
- # Agents Guide — AI Pair Coding Playbook
2
-
3
- **Purpose:** Make GPT-5 / Claude / CodeX effective partners for {{ cookiecutter.primary_goal }} while improving modularity, maintainability, and testability.
4
-
5
- **Audience:** Engineers, Tech Leads, and AI Agents connected to this repo.
6
-
7
- ---
8
-
9
- ## 0) Ground Rules (READ FIRST)
10
-
11
- * **Single Source of Truth:** This repository and the files in `/context` are canonical for all agent runs.
12
- * **Safety First:** No destructive ops without backups and a PR. Agents must create `*.bak` or use Git branches.
13
- * **Tests Before Changes:** Add/extend tests that fail *before* implementing a fix/refactor.
14
- * **Small, Reviewable Diffs:** Keep changes scoped; open PRs early; link to the relevant roadmap task.
15
- * **Update Context Every Run:** Always persist “Overall/Last/Next” (see §6) after actions.
16
-
17
- ---
18
-
19
- ## 1) Repo Layout & Context Discipline
20
-
21
- > Keep the agent’s attention on code that matters. Avoid token waste and blind spots.
22
-
23
- * **Project Tree (authoritative sketch):**
24
-
25
- ```
26
- .
27
- ├─ /src # Application code
28
- ├─ /tests # Unit/integration tests
29
- ├─ /services # External adapters, API clients
30
- ├─ /infra # IaC / deployment (optional)
31
- ├─ /docs # Human-readable docs
32
- ├─ /context # Agent context files (canonical)
33
- │ ├─ development.md
34
- │ ├─ agents.md
35
- │ ├─ project_tree.md
36
- │ └─ directory_notes/
37
- ├─ /assets/images # Large/media (EXCLUDED from agent context)
38
- └─ ...
39
- ```
40
-
41
- * **Explicit Exclusions:** `/assets/images`, `/dist`, `/build`, `/node_modules`, binaries, large JSON/CSV.
42
- Agents must **assume these exist** and avoid recommending moves/rewrites.
43
-
44
- * **Directory Notes (optional but powerful):**
45
- Add `/context/directory_notes/<dir>.md` with:
46
-
47
- * Purpose, entry points, invariants, dependency direction.
48
- * “Do/Don’t” for this directory.
49
- * Local glossary (domain terms, DTOs).
50
- * Known pitfalls.
51
-
52
- ---
53
-
54
- ## 2) Branching & PR Conventions
55
-
56
- * **Branch names:** `feature/<short-goal>` or `refactor/<module>` or `fix/<ticket-id>`
57
- * **Create branch from:** `main`
58
- * **PR template must include:**
59
-
60
- * Goal link to `<PHASE N TASK>`
61
- * Risk level (Low/Med/High)
62
- * Test plan (automated + manual)
63
- * Rollback plan
64
- * Context update diff (what changed in `/context` files)
65
-
66
- ---
67
-
68
- ## 3) Operating Modes
69
-
70
- ### A) Non-Scripted (Live Collaboration)
71
-
72
- 1. Create branch: `git checkout -b feature/<FEATURE>`
73
- 2. Connect agent (Claude or GPT-5) to repo URL.
74
- 3. Attach context: `/context/development.md`, `/context/agents.md`, relevant `/context/directory_notes/*`.
75
- 4. **Prompt:**
76
-
77
- ```
78
- PHASE 1 GOAL: <PHASE 1 GOAL>
79
- Constraints: preserve public APIs, add tests first, no destructive ops.
80
- Output: stepwise plan + minimal diffs per step.
81
- ```
82
-
83
- ### B) Scripted (One-Pass Implementation)
84
-
85
- 1. Create branch: `git checkout -b feature/<FEATURE>`
86
- 2. Attach **only** relevant context files.
87
- 3. **Prompt:**
88
-
89
- ```
90
- Create a comprehensive script/commit plan to accomplish <PHASE 1 GOAL> in one pass.
91
- Requirements:
92
- - Zero breakage; create backups where needed.
93
- - Generate/modify tests first to capture intended behavior.
94
- - Respect repo conventions, linting, and formatting.
95
- - Produce: (a) ordered commit plan, (b) code patches, (c) updated docs, (d) updated context (see §6).
96
- ```
97
-
98
- ---
99
-
100
- ## 4) Canonical Prompts
101
-
102
- ### 4.1 Deep Research (Kickoff)
103
-
104
- ```
105
- You are a staff-level engineer embedded in this codebase.
106
- Objective: Produce a comprehensive plan to improve modularity, maintainability, and achieve {{ cookiecutter.primary_goal }}.
107
- Deliverables:
108
- - Architecture review (current vs. target), explicit trade-offs.
109
- - Refactoring map (by module), dependency inversion opportunities, interface boundaries.
110
- - Test posture upgrade plan (unit/integration/contract), coverage deltas.
111
- - Risks, complexity hotspots, and rollback strategies.
112
- - 3-phase roadmap with measurable outcomes.
113
- Constraints:
114
- - Respect exclusions listed in agents.md.
115
- - Assume assets exist where excluded.
116
- - Prefer small, reversible changes; maximize seam creation for safe refactors.
117
- ```
118
-
119
- ### 4.2 Roadmap → Files
120
-
121
- ```
122
- Convert the roadmap into:
123
- - /context/development.md (engineering tasks, test plans, risks)
124
- - /context/agents.md (this file; update Operating Modes, Prompts if needed)
125
- Ensure clear Phase 1/2/3 breakdown with milestone checklists and acceptance criteria.
126
- ```
127
-
128
- ### 4.3 Implementation Guardrails (per task)
129
-
130
- ```
131
- For TASK: <task-name>
132
- - Propose minimal diff solution.
133
- - Add/adjust tests first to lock behavior.
134
- - Provide code patches and commands to run tests/lints.
135
- - Call out risks + rollback.
136
- - Update context loop (Overall/Last/Next).
137
- ```
138
-
139
- ---
140
-
141
- ## 5) Testing & Quality Gates
142
-
143
- * **Unit tests:** focus on pure logic and interfaces.
144
- * **Integration tests:** external boundaries (DB, queues, HTTP) via testcontainers/mocks.
145
- * **Contract tests:** for service clients with provider/consumer pacts if applicable.
146
- * **Coverage targets:** raise or maintain ≥ <TARGET>% lines/branches where practical.
147
- * **CI gates:** lint, type-check, build, test, basic security scan (SAST/dep audit).
148
-
149
- ---
150
-
151
- ## 6) Context Loop (Mandatory)
152
-
153
- Agents **must** persist this block at the end of every session in `/context/development.md` (and in any relevant directory note):
154
-
155
- ```
156
- ## Context Sync (AUTO-UPDATED)
157
- Overall goal is: {{ cookiecutter.primary_goal }}
158
- Last action was: <what changed and why> (commit SHA if available)
159
- Next action will be: <smallest valuable step with owner>
160
- Blockers/Risks: <if any>
161
- ```
162
-
163
- ---
164
-
165
- ## 7) Failure Modes & Rollback
166
-
167
- * **If tests fail:** revert last commit or apply backup. Fix tests first, then code.
168
- * **If unintended API change:** restore interface, add regression test.
169
- * **If scope creep:** park in `Phase N Backlog` with rationale.
170
-
171
- ---
172
-
173
- ## 8) Example Exclusion Prompt
174
-
175
- > *Drop into your agent prompt when large dirs are omitted.*
176
-
177
- “Relevant image/media assets exist under `/assets/images` but are intentionally excluded from your context to conserve tokens. **Do not** propose changes that relocate, inline, or re-encode assets. Assume paths referenced in code are valid. Focus your analysis on `/src`, `/services`, and `/tests`. If a change seems to require asset inspection, propose an interface-level abstraction instead.”
178
-
179
- ---
180
-
181
- ## 9) Definitions
182
-
183
- * **Seam:** a point in code where behavior can be changed without editing the code (e.g., via interface, DI, adapter).
184
- * **Backwards compatibility window:** period where both old and new APIs coexist with deprecation notices.
185
-
186
- ---
187
-
188
- ## 10) Tools & Commands (fill per repo)
189
-
190
- * **Install:** `<cmd>`
191
- * **Format/Lint:** `<cmd>`
192
- * **Test (unit):** `<cmd>`
193
- * **Test (integration):** `<cmd>`
194
- * **Type check:** `<cmd>`
195
- * **Local CI bundle:** `<cmd>`
196
-
@@ -1,10 +0,0 @@
1
- # Development Log
2
-
3
- **Phase:** (set by first feature)
4
- **Primary Goal:** (set by first feature)
5
-
6
- ## Context Sync (AUTO-UPDATED)
7
-
8
- - **Overall goal is:** (set by feature)
9
- - **Last action was:** (set by feature)
10
- - **Next action will be:** (set by feature)
@@ -1,29 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: bpsai-pair
3
- Version: 0.2.0
4
- Summary: CLI for AI pair-coding workflow
5
- Author: BPS AI Software
6
- Requires-Python: >=3.9
7
- Description-Content-Type: text/markdown
8
- Requires-Dist: typer>=0.12
9
- Requires-Dist: rich>=13.7
10
- Requires-Dist: pyyaml>=6.0
11
-
12
-
13
- # bpsai-pair CLI
14
-
15
- ## Quick start (local, un-packaged)
16
- ```
17
- python -m tools.cli.bpsai_pair --help
18
- python -m tools.cli.bpsai_pair init tools/cookiecutter-paircoder
19
- python -m tools.cli.bpsai_pair feature auth-di --primary "Decouple auth via DI" --phase "Refactor auth + tests"
20
- python -m tools.cli.bpsai_pair pack --extra README.md
21
- python -m tools.cli.bpsai_pair context-sync --last "initialized scaffolding" --nxt "set up CI secrets" --blockers "none"
22
- ```
23
-
24
- ## Install as a CLI
25
- ```
26
- cd tools/cli
27
- pip install -e .
28
- # now available as: bpsai-pair --help
29
- ```