bpsai-pair 0.2.1__py3-none-any.whl → 0.2.3__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.1"
5
+ __version__ = "0.2.3"
6
6
 
7
7
  # Make modules available at package level
8
8
  from . import cli
@@ -1,6 +1,8 @@
1
1
  # Default agent pack exclusions
2
2
  .git/
3
3
  .venv/
4
+ venv/
5
+ env/
4
6
  __pycache__/
5
7
  node_modules/
6
8
  dist/
@@ -10,3 +12,8 @@ build/
10
12
  *.tgz
11
13
  *.tar.gz
12
14
  *.zip
15
+ .env
16
+ .env.*
17
+ *.pyc
18
+ .DS_Store
19
+ Thumbs.db
@@ -0,0 +1,294 @@
1
+ # PairCoder Documentation
2
+
3
+ ## Purpose and Overview
4
+
5
+ PairCoder is an AI-augmented pair programming framework that you can drop into any existing code repository to facilitate collaboration between human developers and AI coding agents (such as GPT-5, Codex, Claude, etc.). The core idea is to provide a standard structure and workflow so that AI assistants can integrate into your development process in a governed, transparent, and productive way. PairCoder introduces a set of conventions and tools for maintaining a project "memory," tracking development progress, and enforcing quality and governance standards.
6
+
7
+ ### Key Features (What You Get)
8
+
9
+ **"Context as Memory"**: A structured `context/` directory holds the project's roadmap, an AI agents guide, and a snapshot of the code tree. This serves as the shared knowledge base that both developers and AI agents refer to and update, ensuring continuity in the pair-programming conversation.
10
+
11
+ **"Disciplined Loop"**: A required practice where after each action (by human or AI), the Context Sync block is updated with what happened last, what's next, and any blockers. This creates a running narrative of the project, keeping the AI aligned with recent changes and upcoming plans.
12
+
13
+ **Governance Files**: Standard open-source/project governance files are provided – `CONTRIBUTING.md` (contribution guidelines), `CODEOWNERS` (to define code reviewers/owners), `SECURITY.md` (security policy), and a Pull Request template. These ensure any AI contributions or human contributions follow the project rules and that appropriate owners review changes.
14
+
15
+ **Quality Gates**: Built-in pre-commit configuration for linting/formatting (Python uses Ruff, Markdown lint; Node uses Prettier/ESLint) and secret scanning via Gitleaks. PairCoder helps set up these tools so that any code (AI-generated or not) meets quality standards before being committed. Continuous Integration (CI) workflows are also included to run tests, linters, and refresh the context tree in an automated fashion.
16
+
17
+ **CLI Tool – bpsai-pair**: A command-line interface to orchestrate the workflow. The CLI has commands for initializing the repo scaffolding, creating new "feature" branches with context updates, packaging context for the AI, and syncing context after each change.
18
+
19
+ **Cookiecutter Template**: The package includes a Cookiecutter template (`tools/cookiecutter-paircoder`) which can scaffold a new project from scratch with PairCoder's structure. This is useful if you want to start a brand new repository already configured with PairCoder, rather than adding it to an existing one.
20
+
21
+ In summary, PairCoder's purpose is to make AI pair-programming systematic and team-friendly. Instead of ad-hoc AI suggestions, it establishes a shared memory and strict process so that AI contributions can be tracked, reviewed, and integrated like any other team member's work. This benefits any developer or team by providing a ready-to-use framework that saves time setting up project structure and ensures that if an AI agent is introduced, it operates within known guidelines.
22
+
23
+ ## Installation Requirements
24
+
25
+ To use PairCoder, you need the following (these are the base requirements from the README):
26
+
27
+ - **Python 3.9+** – The CLI is a Python tool (built on Typer and Rich libraries). It should work on Python 3.9 and above.
28
+
29
+ - **Git** – PairCoder is designed to work inside a Git repository and uses Git commands (for branch management in the feature workflow).
30
+
31
+ - **Virtual Environment (Recommended)**: It's best to use a virtualenv for installing PairCoder to avoid conflicts (especially on systems with PEP 668 that restrict system Python packages).
32
+
33
+ - **Optional: Node.js** – If your project includes JavaScript/TypeScript, having Node will allow the provided CI hook (`ci_local.sh` and `ci.yml`) to run linters and tests for that code. If you don't use Node, those steps will simply be skipped (the script checks if `package.json` exists).
34
+
35
+ - **Optional: Docker** – Not directly used in v0.2.0, but noted for potential future integration tests.
36
+
37
+ - **Gitleaks Binary**: If you plan to use the secret scanning, install the gitleaks CLI tool on your machine (available via package managers or from GitHub). The pre-commit hook and documentation refer to this for scanning secrets.
38
+
39
+ ### Installation Steps
40
+
41
+ Once published on PyPI, you can install PairCoder with pip. For example:
42
+
43
+ ```bash
44
+ pip install bpsai-pair
45
+ ```
46
+
47
+ This will install the CLI tool and its Python dependencies (Typer, Rich for colored output, PyYAML, etc. – as listed in the pyproject). After installation, the command `bpsai-pair` should be available.
48
+
49
+ If you prefer the latest code or a development install, you can clone the repo and do:
50
+
51
+ ```bash
52
+ python3 -m venv .venv && source .venv/bin/activate
53
+ pip install -e tools/cli # install in editable mode from the repo
54
+ ```
55
+
56
+ (as shown in the README). This editable install is mainly for contributing to PairCoder itself; normal users would use the pip release.
57
+
58
+ **Note**: If you encounter an issue where `bpsai-pair` is not found (e.g., on some systems or IDE terminals), ensure your Python scripts path is in your PATH, or invoke via python -m:
59
+
60
+ ```bash
61
+ python -m bpsai_pair.cli --help
62
+ ```
63
+
64
+ This is a fallback mentioned for environments where entry points might not be set up. In a proper installation, this shouldn't be necessary, but it's useful for troubleshooting.
65
+
66
+ ## Using the CLI Commands
67
+
68
+ PairCoder's CLI (`bpsai-pair`) is the primary way to interact with the framework. It is a Typer-based CLI, meaning it has subcommands with `--help` available for each. Here are the main commands and how to use them:
69
+
70
+ ### `bpsai-pair init <template_path>` – Initialize scaffolding
71
+
72
+ This command bootstraps a repository with PairCoder's files. You typically run this once, on an existing repository that you want to augment with AI pairing tools. It copies in all the missing pieces: context directory and files, prompt templates, scripts, config files, CI workflows, etc.
73
+
74
+ **Usage**: In the root of your repo (where `.git` is), run:
75
+ ```bash
76
+ bpsai-pair init tools/cookiecutter-paircoder
77
+ ```
78
+ (if using the source repo path) or simply `bpsai-pair init` (if the package provides a default template internally). The required argument is the path to the template directory; in our packaged scenario, we will have a default template packaged, so this could become optional or automatically resolved.
79
+
80
+ **What it does**:
81
+ - It searches the given `template_dir` for a subfolder named `{{cookiecutter.project_slug}}` and copies everything under that into your repository root.
82
+ - Files are only copied if they do not already exist to avoid overwriting. This means you can safely run init on an existing project; it won't clobber your files (it will fill in what's missing).
83
+ - It also makes any new shell scripts executable for you.
84
+ - After running, you will see new files like `.editorconfig`, `.pre-commit-config.yaml`, etc., appear (see Files and Directories below for full list).
85
+ - The CLI prints a success message "Initialized repo with pair-coding scaffolding (non-destructive). Review diffs and commit."
86
+ - At this point, you should review and commit these scaffold files into your repository.
87
+
88
+ ### `bpsai-pair feature <name>` – Start a feature branch
89
+
90
+ This command creates a new Git branch (named `feature/<name>`) and prepares the context for a new phase of work. It's meant to be used whenever you begin working on a new feature or task, especially if an AI will be involved in that task. It ensures the project context is up to date and ready to guide the development.
91
+
92
+ **Usage**: Run `bpsai-pair feature <feature-name> [--primary "<Primary Goal>"] [--phase "<Phase 1 Goal>"]`. For example:
93
+ ```bash
94
+ bpsai-pair feature login-system --primary "Implement login with DI seam" --phase "Phase 1: Scaffolding & tests"
95
+ ```
96
+
97
+ The `<feature-name>` is a short slug for your feature (it will become part of the branch name). `--primary` is an optional description of the feature's primary goal (if not provided, the context will keep the generic placeholder or previous text), and `--phase` describes the next immediate action or phase. There is also a `--force` flag to bypass the "dirty working tree" check (useful if you have uncommitted changes and still want to branch, but generally it's better to commit or stash before running this).
98
+
99
+ **What it does**:
100
+
101
+ 1. Checks you are at the repo root and that there are no uncommitted changes (unless `--force`)
102
+ 2. Verifies that a main (or master) branch exists to branch off from
103
+ 3. Creates a new branch `feature/<name>` off the latest main and switches to it
104
+ 4. Scaffolds context files for the new feature:
105
+ - Ensures `context/development.md` exists. If not, it creates a base roadmap file with placeholders. If it exists, it will be updated.
106
+ - Inserts the `--primary` goal into the Primary Goal field of `development.md` (replacing the placeholder text)
107
+ - Updates the Overall goal in the Context Sync section to match the primary goal (so the AI knows the overarching objective)
108
+ - Ensures `context/agents.md` exists (creates a stub if not) – this file is meant to contain instructions or a playbook for AI agents; the stub reminds you to fill it in
109
+ - Refreshes `context/project_tree.md` to the current snapshot of the repository's file structure (this gives the AI a current map of the project)
110
+ - Appends or updates the Context Sync block in `development.md`: If one isn't there, it appends a fresh template for it; if it exists, it updates "Last action was:" to "initialized feature branch and context", and sets "Next action will be:" to the `--phase` text (if provided)
111
+ 5. Stages and commits the changes to the context files on the new branch (with a commit message indicating the scaffold)
112
+
113
+ After this command completes, you have a new feature branch ready with all context files up-to-date. The Primary Goal for the feature is recorded, the Context Sync notes that the branch was initialized, and the Next action is set to the first phase/task. The CLI's output also prints what branch was created/switched and any info logs from the script. You can now proceed to actually implement the feature. The idea is that the AI agent (given the updated context) can help with the next steps.
114
+
115
+ ### `bpsai-pair pack [output.tgz] [--extra <paths>]` – Package context for AI
116
+
117
+ This command creates a tarball of the important context and documentation files, which you can then provide to an AI coding assistant (e.g., upload to a chat or agent interface). This ensures the AI works with the latest project information and guidelines without exposing source code or sensitive files.
118
+
119
+ **Usage**: `bpsai-pair pack` by default will create `agent_pack.tgz` in the current directory. You can specify a custom filename: e.g., `bpsai-pair pack my_feature_context.tgz`. You can also add extra files to include with `--extra`. For instance, you might include README.md or a specific design doc:
120
+ ```bash
121
+ bpsai-pair pack --extra README.md docs/Architecture.md
122
+ ```
123
+ You can list multiple `--extra` items.
124
+
125
+ **What it does**: Under the hood, this uses the tar command to archive files. By default, it always includes:
126
+ - `context/development.md` (the roadmap & latest context sync info)
127
+ - `context/agents.md` (the AI playbook/guidelines)
128
+ - `context/project_tree.md` (project structure snapshot)
129
+ - the entire `context/directory_notes/` directory (any per-directory notes you've written)
130
+
131
+ It then adds any `--extra` paths you provided to that list. Before creating the tar, it checks that all those paths actually exist – so you'll get an error if, for example, you typo'd a filename.
132
+
133
+ The packing process also respects an ignore list for safety: there is a file `.agentpackignore` which functions like a `.gitignore` for packaging. Common large or sensitive patterns (like `.git/**`, `node_modules/`, `dist/`, `__pycache__/`, etc.) are excluded automatically. You can customize `.agentpackignore` to exclude or include other patterns as needed. The tarball will thus contain only the files we want the AI to see. The script prints out what it's doing – e.g., "Packing -> agent_pack.tgz" and confirms creation with the file size.
134
+
135
+ After running this, you'll have an archive file. How to use it? This archive can be provided to the AI agent. For example, if using an UX with file upload, you'd attach the `agent_pack.tgz`. The AI can open it and read all the context files to understand the project's state before contributing code.
136
+
137
+ **Note**: The agent pack deliberately omits source code (unless you add some in extras), focusing on context and docs. This encourages the AI to generate code without directly copying existing code, and avoids sending potentially sensitive code unnecessarily. If the AI needs to see a particular source file, you can always add it with `--extra` or share it separately.
138
+
139
+ ### `bpsai-pair context-sync` – Update Context Sync
140
+
141
+ This command updates the Context Sync section of `context/development.md` programmatically. It's a convenience to ensure that whenever you or the AI complete a step, the shared context log is updated consistently without manual editing errors.
142
+
143
+ **Usage**: Provide at least the `--last` and `--nxt` arguments:
144
+ ```bash
145
+ bpsai-pair context-sync --last "Fixed bug in login flow" \
146
+ --nxt "Code review and merge" \
147
+ --blockers "None"
148
+ ```
149
+
150
+ The `--blockers` can be an empty string if no blockers (it defaults to empty). You can also use `--overall` if, say, the overall goal of the project has shifted and you want to change the top-line context (this is less common on a per-action basis).
151
+
152
+ **What it does**: This command simply finds the lines in `context/development.md` that start with "Last action was:", "Next action will be:", etc., and replaces them with your provided text. It requires that `context/development.md` already exists and contains those expected lines; otherwise it errors out (to ensure you don't accidentally run it in a non-PairCoder repo). Each time you run it, it effectively logs the latest state:
153
+ - "Last action was: …" gets the `--last` text
154
+ - "Next action will be: …" gets the `--nxt` text
155
+ - "Blockers/Risks: …" gets the `--blockers` text (or is left empty if none provided)
156
+ - If `--overall` is given, it will also update "Overall goal is: …" at the top of the block (you might use this if you complete a major feature and want to set a new overall project objective)
157
+
158
+ This command prints a confirmation like "Context Sync updated." in green, and you can open the `development.md` to see the changes. The idea is that both humans and AI agents use this regularly: after every meaningful code change, run context-sync (or have the AI run it) to log progress. This keeps everyone on the same page about what just happened and what's next, preventing the AI from repeating work or the humans from forgetting context the AI had.
159
+
160
+ In practice, you might integrate this into your workflow: e.g., as part of a script that the AI runs, or manually as a discipline (the README stresses this as a "required discipline" for using PairCoder effectively). It ensures continuity in pair programming sessions.
161
+
162
+ Each of these CLI commands has a help screen (try `bpsai-pair <command> --help`) which will summarize usage and options, thanks to Typer. For instance, `bpsai-pair --help` will list all commands, and `bpsai-pair feature --help` will show the options like `--primary` and `--phase`. Using these commands in the intended order (init → feature → pack → context-sync, and repeating feature/pack/sync as needed) creates a development loop where the AI is always operating with up-to-date project context.
163
+
164
+ ## Files and Directory Structure Explained
165
+
166
+ When PairCoder is initialized in a repo (via init or by starting a new project with the cookiecutter), it adds a number of files and directories. Here we describe the purpose of each and how you, as a user, interact with them. The repository layout is summarized in the documentation, but here we expand on each item:
167
+
168
+ ### `context/` – Project Context and Memory
169
+
170
+ This folder is central to PairCoder. It contains:
171
+
172
+ **`development.md`** – The "Development Roadmap" Markdown file. This is essentially the journal of the project's progress and plans. It typically starts with a Primary Goal of the project, the project name, owner, last updated date, etc., followed by the Context Sync section. You should treat this as the single source of truth for "what are we doing and why" in the project. At project start, you fill in the Primary Goal (either manually or via the `--primary` flag during feature command). As development proceeds, every time something changes, update the Context Sync (preferably via the CLI). Think of it as a constantly evolving README focused on dynamic progress. This file is also packaged and given to the AI, so it's how the AI knows the overall context and recent history. It's a living document; keep it updated for the best results.
173
+
174
+ **`project_tree.md`** – A snapshot of the repository's directory tree structure. This is auto-generated (via the feature command or a daily CI job) and is not meant to be edited by hand. It shows all files and folders (excluding certain ignored patterns) in a tree format, which gives the AI a bird's-eye view of the project's scope. The top of the file includes a timestamp of when it was generated. The CI workflow will update this daily to catch any new files committed outside the feature scaffolding. As a user, you don't edit this; you only ensure the CI is running or run `bpsai-pair feature` again to refresh it if needed.
175
+
176
+ **`agents.md`** – The "Agents Guide / AI Pair Coding Playbook." This is initially a stub file with a note telling you to fill in the canonical version. The intention is that you provide instructions here for AI agents working on your code. For example, you might include coding style guidelines, architectural principles, definitions of done, or any rules the AI should follow (like "don't touch files in /core without approval" or "use Python style X for logging"). Essentially, this is a place to encode your team's best practices and any specific domain knowledge the AI needs. Before you begin using an AI agent, you should replace the stub with a well-thought guide. Once written, this file is always included in context packages, so the AI will refer to it whenever it's working on tasks.
177
+
178
+ **`directory_notes/`** – A directory intended to hold Markdown notes for individual directories or components in your repo. It starts with just a `.gitkeep` (an empty file to ensure the folder exists). PairCoder provides a template for directory notes (`templates/directory_note.md`) which you can copy into this folder for any submodule or directory that might need explanation. For instance, if you have a `backend/` directory, you could create `context/directory_notes/backend.md` describing what's in there, important design choices, etc. These notes can then be kept up to date and will be packaged for the AI. They help the AI (and new developers) understand each part of the codebase in context. Maintaining these notes is optional but recommended for complex projects.
179
+
180
+ (For optional usage: context could also include an `architecture.md` or other overview docs if you want to place them here. PairCoder doesn't generate those by default beyond what's mentioned.)
181
+
182
+ ### `prompts/` – AI Prompt Templates
183
+
184
+ This directory contains YAML files that define the base prompts or instructions for various phases of the AI's involvement:
185
+
186
+ - **`roadmap.yml`** – Contains a template for prompting the AI to assist in roadmap generation from a proposed plan. It outlines how to ask the AI to break down the Primary Goal into phases or tasks (the "Roadmap" phase of pair programming) and to generate the necessary `/context/development.md` & `/context/agents.md` files based on the accepted plan.
187
+
188
+ - **`deep_research.yml`** – A template for a "deep research" phase prompt. This template is used when you want the AI to do a deep dive (analyzing a repo, summarizing info, answering complex questions).
189
+
190
+ - **`implementation.yml`** – A template prompt for coding or implementing features, guiding the AI on how to produce code within the project's context and constraints.
191
+
192
+ These files come with the framework but are not actively modified by the CLI. They are assets you or the AI agent integration can use. For instance, if you have a script or UI that interacts with an AI model, it might load these prompts to formulate its queries. You can customize them as needed for your project's tone or specificity. By shipping these, PairCoder ensures that all team members and AI agents start with a consistent approach to asking the AI for help at different stages (planning vs. researching vs. coding). As a user, you should review these prompt templates and adjust any placeholders or project-specific info before using them heavily.
193
+
194
+
195
+ ### Governance & Config Files (repo root)
196
+
197
+ - **`CONTRIBUTING.md`** – Guidelines for contributors. PairCoder provides a template that includes instructions to use Conventional Commits for commit messages, to always update context sync, keep diffs small, etc. You should read this and update any project-specific sections (like how to get started, or the PR process if different). Share this with human contributors and parse the relevant pieces into the AI's guidelines so it follows the contributing rules.
198
+
199
+ - **`SECURITY.md`** – Security policy, e.g., how to report vulnerabilities, and a reminder not to include secrets in the repo or context packs. The one included with PairCoder also notes that `.agentpackignore` is set up to avoid packing secrets and that test data should be redacted or synthetic. This file is mainly informational for your repo's users; it's good for both internal use and to provide the agent guidance on your team's security best practices.
200
+
201
+ - **`CODEOWNERS`** – A file that GitHub uses to auto-assign reviewers. This file should list the repository owners or lead developers who should review incoming PRs. You should edit this to match your team's GitHub usernames and desired code ownership (e.g., you might want all AI-generated PRs to ping a certain lead). This helps enforce that a human reviews any AI contributions.
202
+
203
+ - **`.editorconfig`** – Standard EditorConfig file to ensure consistent indentation, charset, end-of-line, etc., across different editors. It's a generic one suitable for most projects. With this in place, developers' IDEs will follow the same basic formatting rules, reducing diffs caused by whitespace.
204
+
205
+ - **`.pre-commit-config.yaml`** – Configuration for pre-commit hooks. PairCoder's config will set up hooks such as:
206
+ - Ruff (Python linter/formatter) to run on Python files
207
+ - Prettier for formatting Markdown, JSON, YAML, etc.
208
+ - Markdownlint for Markdown files
209
+ - A hook to call Gitleaks
210
+
211
+ You should run `pre-commit install` after initializing PairCoder to activate these hooks in your local repo. Then, every commit will trigger them. If the AI introduces code that doesn't pass these, the commit will be blocked until fixed (or you skip the hook). This is excellent for maintaining quality.
212
+
213
+ - **`.gitleaks.toml`** – Configuration for Gitleaks (secret scanner). It contains patterns to detect API keys, credentials, etc., and also a list of allowed patterns or false-positive suppressions. The one provided by PairCoder covers common file paths to ignore (like it doesn't scan `context/` or tests for certain dummy secrets). Keep this updated if you find false positives or need to add custom regexes for your stack. Use it by running `gitleaks detect ...` as per README or let it run in CI.
214
+
215
+ - **`.agentpackignore`** – As discussed, this file lists what to exclude from the context tarball. By default, it covers `.git/`, build artifacts, virtualenvs, caches, etc. You can add anything else you never want to send to the AI (for example, you might exclude `**/secrets.env`).
216
+
217
+ - **`.gitignore`** – The template provides a baseline `.gitignore` (covering Python, Node, etc.). Check it to ensure it doesn't conflict with your project's needs. This prevents committing of venvs, node_modules, pyc files, etc.
218
+
219
+ ### CI Workflows (`.github/workflows/`)
220
+
221
+ - **`ci.yml`** – A GitHub Actions workflow that runs on each push/PR. It will set up appropriate environments and run formatting, linting, typing, and tests for both Node and Python portions of the repo. Ensure your repository has any required test commands or configuration so this passes. As you develop, maintain your CI to cover your real test suites.
222
+
223
+ - **`project_tree.yml`** – A scheduled workflow that executes a small script to regenerate `context/project_tree.md` and commit it if it has changed. This is a "maintenance" workflow to keep the context tree updated without manual intervention. It uses a bot account or GitHub Actions token to push the update. You may need to set the proper permissions (repo write) for this Action. Check that this workflow's schedule (cron) is acceptable (daily might be default). If your repo is private, ensure actions are enabled. This automation ensures that even if no one ran `bpsai-pair feature` recently, your context tree doesn't fall far behind the actual repo structure.
224
+
225
+ ### Templates (`templates/` directory in repo root)
226
+
227
+ - **`adr.md`** – A template for an Architecture Decision Record. The repository includes an example ADR template. You can use this to document any big decisions. ADRs are typically stored in `docs/adr/` (notice the template created a `docs/adr/` folder with a `.gitkeep`). When a significant design choice is made (perhaps with AI input), you write an ADR using this template, assign it a number (e.g., ADR-001), and commit it. This helps future contributors (and AI agents) understand why certain decisions were made. PairCoder doesn't automate ADR creation, but by providing the template and directory, it encourages this best practice.
228
+
229
+ - **`directory_note.md`** – A template for writing a directory note (similar to what would go in `context/directory_notes/`). It's a short template likely just providing a structure (maybe headers for "Purpose of this directory/module," etc.). Use it whenever you want to add documentation for a specific part of the codebase.
230
+
231
+ These templates are for your convenience; the CLI doesn't directly use them (except that `new_feature.sh` will copy `directory_note.md` if needed when scaffolding directory notes, which currently it doesn't automatically for each folder – it just ensures the folder exists). It's up to you to create actual notes or ADR files based on them.
232
+
233
+ ### Tests (`tests/` directory)
234
+
235
+ The scaffold includes a `tests/` folder with subfolders like `example_contract` and `example_integration`, each containing a README. These are placeholders indicating to the coding agent where and how to write tests:
236
+ - Example contract tests refer to Consumer Pact tests
237
+ - Example integration tests for external boundaries
238
+
239
+ **NOTE**: The nested READMEs are stubs and are not functional code. They serve as a placeholder for your team. You should replace or revise these once you add real tests. For example, if choosing to keep and revise them, they may be incorporated to signal to the AI where tests go and what frameworks to use, etc.
240
+
241
+ ### `services/` and `infra/` directories
242
+
243
+ These are present as empty folders with `.gitkeep`. They indicate where you might put microservice code or infrastructure-as-code (Terraform, etc.), depending on your project. They're not used by PairCoder's logic directly, but they're part of the scaffold to nudge a clean project structure. If used, their purpose should be included in your `context/agents.md` file to allow efficient crawling.
244
+
245
+ ### Cookiecutter Config (`tools/cookiecutter-paircoder/`)
246
+
247
+ While not part of the project itself once you've initialized, it's worth noting that PairCoder was developed with a Cookiecutter template. The `cookiecutter.json` file in that directory defines variables like `project_name`, `project_slug`, etc., which are used in the scaffold files. If you start a new project using Cookiecutter (rather than using init on an existing repo), those variables will be substituted. For example, `<PROJECT NAME>` in `development.md` would be replaced with your actual project name.
248
+
249
+ When you run `bpsai-pair init` on an existing repo, it doesn't do string substitution (it just copies files with the placeholders as-is, then feature command stamps the Primary Goal). In the future (v0.2.x on the roadmap), there are plans to integrate template variable substitution into init so that it can ask for your project name and fill it in automatically. For now, just be aware of the placeholders and update them manually (e.g., replace `<PROJECT NAME>` in `development.md` with your repo name after init).
250
+
251
+ ## How to Use PairCoder Day-to-Day
252
+
253
+ With everything set up, here's how a developer might employ PairCoder in their workflow:
254
+
255
+ ### 1. Initialize (one-time)
256
+ Add PairCoder to your repo by running `bpsai-pair init`. Commit the introduced files. Configure CODEOWNERS and other files to your liking. Set up pre-commit hooks (`pre-commit install`) and ensure CI is enabled. Fill in `context/agents.md` with any special instructions for AI. At this point, your project is "PairCoder-enabled."
257
+
258
+ ### 2. Planning & Roadmap
259
+ Use `context/development.md` to outline your high-level roadmap. Possibly break it into phases. The `prompts/roadmap.yml` can be used with an AI (e.g., ask the AI "Using the roadmap prompt, help me plan the phases for achieving the Primary Goal"). This can yield a structured plan that you then place into `development.md` (under the Primary Goal section or below). Essentially, `development.md` can hold more detail than just the Context Sync; it's your space to plan. Keep it updated as plans change.
260
+
261
+ ### 3. Starting a Feature (with AI or without)
262
+ When ready to tackle a piece of work, run `bpsai-pair feature <name> ...`. This creates a feature branch and updates the context. Push this branch to your remote (so others know you're working on it). At this point, you might engage an AI: you package the context (`bpsai-pair pack`) and send it to the AI with a prompt (perhaps using `prompts/implementation.yml`) like "Here's the current project context, please implement feature X following the guidelines." The AI will produce code changes.
263
+
264
+ ### 4. AI Contributions & Context Loop
265
+ If the AI suggests code, you would apply those changes in your repo (maybe via a patch file or manually). After any significant change (AI or human), run:
266
+ ```bash
267
+ bpsai-pair context-sync --last "described last change" --nxt "what's next" --blockers "any issues"
268
+ ```
269
+ to log progress. Commit this change to `development.md`. This way, the next time you or the AI looks at context, it sees what was just done.
270
+
271
+ You might also update `context/agents.md` if, for example, you realize the AI needs new instructions ("don't use library X" or "prefer Y approach") based on what it did.
272
+
273
+ ### 5. Testing & Quality
274
+ Run `pre-commit run --all-files` to ensure the code meets standards. Fix any lint errors or formatting issues (the AI might not adhere to all style guidelines perfectly, so this is where you correct it, or include style guidance in `context/agents.md` for next time). Write tests for the new feature if the AI didn't. This is normal dev work, just within the PairCoder framework.
275
+
276
+ ### 6. Integrate & Repeat
277
+ Once the feature is done, you create a PR (the PR template provided will remind the contributor to ensure context is updated, tests are added, etc.). Codeowners or team leads review it just like a normal PR. Merge it to main. The context (`development.md`) now contains a historical log of that feature's development. If a new cycle starts, you again use `bpsai-pair feature` for the next task.
278
+
279
+ ### 7. Maintenance
280
+ The daily `project_tree.yml` job will keep updating the project tree snapshot. You should also occasionally refine `agents.md` as the AI's role evolves, and keep governance docs up to date (for example, if you adopt a new coding standard, note it in `CONTRIBUTING.md` and possibly in `agents.md` for the AI). Use ADRs to record big decisions that the AI might not be aware of inherently.
281
+
282
+ ### 8. Extensibility
283
+ If desired, you can integrate PairCoder with other tools. The README mentions plans for separate repos like `paircoder-ui`, a project management tool with a UI for enhanced UX when interacting with the AI. The CLI could be called from such similar integrations (and a future version plans to expose a Python API for the CLI commands). For now, you primarily interact via CLI.
284
+
285
+ By following this workflow, any developer on the team (and the AI "developer") has a shared understanding of the project's state and next steps. PairCoder essentially provides the scaffolding and guardrails, but it's up to the team to use them consistently. The benefit is a more structured and trackable collaboration with AI, reducing the chaos and improving transparency.
286
+
287
+ ## Conclusion
288
+
289
+ PairCoder is suitable for any development team looking to incorporate AI assistance into their coding process in a controlled, auditable way. Its combination of a CLI tool and repository scaffolding makes it a comprehensive drop-in framework for AI pair programming. It treats many aspects (context sharing, branch discipline, quality checks, documentation) holistically. Once set up, you can focus on building features – with an AI – while PairCoder quietly maintains the order (updating the context, enforcing rules, etc.).
290
+
291
+ Always remember that the value of PairCoder grows when the context is well-maintained and practices are followed. The more you document in `development.md`, `agents.md`, and directory notes, the more the AI can help effectively. The more you adhere to updating the Context Sync and using the provided tools, the smoother the collaboration. It's a two-way street: PairCoder provides the tools, and the team (human + AI) uses them to create a virtuous cycle of improvement.
292
+
293
+ ## Windows Support
294
+ From v2.0, PairCoder is OS-agnostic. All features are supported equally on Linux, macOS, and Windows.
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."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bpsai-pair
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: CLI for AI pair-coding workflow
5
5
  Author: BPS AI Software
6
6
  Requires-Python: >=3.9
@@ -1,15 +1,15 @@
1
- bpsai_pair/__init__.py,sha256=EfYcTV1HELQ2f1Mr-apUl6brpm6WeWEtPqhKwWHJ1Zs,379
1
+ bpsai_pair/__init__.py,sha256=fsyqsBclEuOREjj8zwNEjj9jqLT9L8Nc0mTYZxzHQ0o,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=rNKGNGVNECXgk-SvoaEgqs2JogTgA3fEwH8t_Sk3sVI,118
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
@@ -26,6 +26,7 @@ bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/context/age
26
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
+ bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/docs/USER_GUIDE.md,sha256=SWzv1BLGFGaE60dVzIF1NqGn_MUBN3lWE8dOHv2mpzM,34262
29
30
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/prompts/deep_research.yml,sha256=lhHG4JRi9Zdsq5J8TNaw2HeupD5oD7mm5J0XPNg1H9A,958
30
31
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/prompts/implementation.yml,sha256=co2EmK7ojw3lqeNXf99rlU3wxn6mrSuVAZGMlOEZHt4,639
31
32
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/prompts/roadmap.yml,sha256=EIdCo_EZpPZ9GXXg71IDgMooychyarWPpulojrcGIg8,498
@@ -35,8 +36,8 @@ bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/templates/a
35
36
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/templates/directory_note.md,sha256=FnNMtnD_CpvVzpp5VzQzS4qddFQ6jfyS8q36ff10DrA,346
36
37
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/tests/example_contract/README.md,sha256=x5ZpsFnN9QiMWtXJsYw9n27ik5Yf425N5tbzMKOpVQQ,109
37
38
  bpsai_pair/data/cookiecutter-paircoder/{{cookiecutter.project_slug}}/tests/example_integration/README.md,sha256=_G9MzJQq1GR4fVSgGeUFVo2eyzDXJVQn6mqJ5He1Euk,150
38
- bpsai_pair-0.2.1.dist-info/METADATA,sha256=sORc65ulVeH16ffSm-Vjnbt8sCLlOVPSYiiD9hHqa7s,1812
39
- bpsai_pair-0.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
40
- bpsai_pair-0.2.1.dist-info/entry_points.txt,sha256=tK6yOUS1oseLus0bPQYd2cuLJgL1Zr3AGb_YPHVCtCI,101
41
- bpsai_pair-0.2.1.dist-info/top_level.txt,sha256=kwTlUncK6pxJyQpZQdspexSc-kWtPvZkLUy4ji1P6LU,11
42
- bpsai_pair-0.2.1.dist-info/RECORD,,
39
+ bpsai_pair-0.2.3.dist-info/METADATA,sha256=Z0awCwvv7fPkAFuM99NhFnAp1MF30ger6ff8EslL0Dk,1812
40
+ bpsai_pair-0.2.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
41
+ bpsai_pair-0.2.3.dist-info/entry_points.txt,sha256=tK6yOUS1oseLus0bPQYd2cuLJgL1Zr3AGb_YPHVCtCI,101
42
+ bpsai_pair-0.2.3.dist-info/top_level.txt,sha256=kwTlUncK6pxJyQpZQdspexSc-kWtPvZkLUy4ji1P6LU,11
43
+ bpsai_pair-0.2.3.dist-info/RECORD,,