claude-dev-env 1.34.1 → 1.36.0
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.
- package/agents/clean-coder.md +109 -1
- package/agents/docs-agent.md +1 -1
- package/agents/project-docs-analyzer.md +0 -1
- package/agents/skill-to-agent-converter.md +0 -1
- package/bin/install.mjs +28 -8
- package/bin/install.test.mjs +9 -1
- package/commands/initialize.md +0 -1
- package/commands/readability-review.md +4 -4
- package/commands/review-plan.md +2 -4
- package/commands/stubcheck.md +1 -2
- package/docs/CODE_RULES.md +3 -0
- package/docs/agents-md-alignment-plan.md +123 -0
- package/hooks/blocking/code_rules_enforcer.py +686 -60
- package/hooks/blocking/es_exe_path_rewriter.py +10 -4
- package/hooks/blocking/test_code_rules_enforcer.py +273 -39
- package/hooks/blocking/test_code_rules_enforcer_annotations.py +97 -0
- package/hooks/blocking/test_code_rules_enforcer_banned_identifier.py +106 -0
- package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +173 -0
- package/hooks/blocking/test_code_rules_enforcer_collection_prefix.py +328 -0
- package/hooks/blocking/test_code_rules_enforcer_config_path.py +0 -20
- package/hooks/blocking/test_code_rules_enforcer_constant_equality.py +33 -11
- package/hooks/blocking/test_code_rules_enforcer_existence_checks.py +0 -18
- package/hooks/blocking/test_code_rules_enforcer_hardcoded_user_path.py +291 -0
- package/hooks/blocking/test_code_rules_enforcer_inline_literal_collections.py +155 -0
- package/hooks/blocking/test_code_rules_enforcer_loop_variable_naming.py +194 -0
- package/hooks/blocking/test_code_rules_enforcer_naming_pattern.py +49 -13
- package/hooks/blocking/test_code_rules_enforcer_skip_decorators.py +0 -26
- package/hooks/blocking/test_code_rules_enforcer_string_magic.py +234 -0
- package/hooks/blocking/test_code_rules_enforcer_sys_path_insert.py +157 -0
- package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +244 -0
- package/hooks/blocking/test_es_exe_path_rewriter.py +81 -3
- package/hooks/blocking/test_windows_rmtree_blocker.py +120 -8
- package/hooks/blocking/windows_rmtree_blocker.py +23 -6
- package/hooks/config/banned_identifiers_constants.py +24 -0
- package/hooks/config/hardcoded_user_path_constants.py +12 -0
- package/hooks/config/hook_log_extractor_constants.py +1 -1
- package/hooks/config/pre_tool_use_stdin.py +48 -0
- package/hooks/config/setup_project_paths_constants.py +4 -0
- package/hooks/config/stuttering_check_config.py +14 -0
- package/hooks/config/stuttering_import_binding_constants.py +11 -0
- package/hooks/config/sys_path_insert_constants.py +4 -0
- package/hooks/config/test_banned_identifiers_constants.py +48 -0
- package/hooks/config/test_hardcoded_user_path_constants.py +78 -0
- package/hooks/config/test_hook_log_extractor_constants.py +3 -3
- package/hooks/config/test_pre_tool_use_stdin.py +80 -0
- package/hooks/config/unused_module_import_constants.py +7 -0
- package/hooks/config/windows_rmtree_blocker_constants.py +3 -0
- package/hooks/diagnostic/hook_log_stop_wrapper.py +7 -4
- package/hooks/git-hooks/config.py +3 -3
- package/hooks/git-hooks/test_gate_utils.py +10 -10
- package/hooks/mypy.ini +2 -0
- package/package.json +1 -1
- package/rules/gh-paginate.md +125 -0
- package/skills/bugteam/CONSTRAINTS.md +12 -6
- package/skills/bugteam/PROMPTS.md +0 -39
- package/skills/bugteam/SKILL.md +93 -125
- package/skills/bugteam/SKILL_EVALS.md +25 -23
- package/skills/bugteam/reference/README.md +2 -0
- package/skills/bugteam/reference/audit-and-teammates.md +2 -2
- package/skills/bugteam/reference/copilot-gap-analysis.md +12 -0
- package/skills/bugteam/reference/teardown-publish-permissions.md +1 -1
- package/skills/bugteam/reference/workflow-path-a-orchestrated-teams.md +113 -0
- package/skills/bugteam/reference/workflow-path-b-task-harness.md +48 -0
- package/skills/bugteam/test_skill_additions.py +13 -4
- package/skills/bugteam/test_team_lifecycle.py +94 -0
- package/skills/findbugs/SKILL.md +3 -3
- package/skills/fixbugs/SKILL.md +4 -4
- package/skills/monitor-open-prs/SKILL.md +32 -2
- package/skills/monitor-open-prs/test_team_lifecycle.py +46 -0
- package/skills/pr-converge/SKILL.md +576 -95
- package/skills/pr-converge/scripts/README.md +145 -0
- package/skills/pr-converge/scripts/caller-window-pid.ps1 +86 -0
- package/skills/pr-converge/scripts/check_pr_mergeability.py +79 -0
- package/skills/pr-converge/scripts/config/pr_converge_constants.py +65 -0
- package/skills/pr-converge/scripts/config/test_pr_converge_constants.py +176 -0
- package/skills/pr-converge/scripts/cursor-agents-continue-caller.cmd +9 -0
- package/skills/pr-converge/scripts/cursor-agents-continue-stop-others.ps1 +16 -0
- package/skills/pr-converge/scripts/cursor-agents-continue.ahk +172 -0
- package/skills/pr-converge/scripts/cursor-agents-continue.cmd +2 -0
- package/skills/pr-converge/scripts/evict_cached_config_modules.py +20 -0
- package/skills/pr-converge/scripts/fetch_bugbot_inline_comments.py +110 -0
- package/skills/pr-converge/scripts/fetch_bugbot_reviews.py +103 -0
- package/skills/pr-converge/scripts/fetch_copilot_inline_comments.py +112 -0
- package/skills/pr-converge/scripts/fetch_copilot_reviews.py +121 -0
- package/skills/pr-converge/scripts/mark_pr_ready.py +54 -0
- package/skills/pr-converge/scripts/open_followup_copilot_pr.py +136 -0
- package/skills/pr-converge/scripts/post-bugbot-run.helpers.ps1 +49 -0
- package/skills/pr-converge/scripts/post-bugbot-run.ps1 +33 -0
- package/skills/pr-converge/scripts/reply_to_inline_comment.py +84 -0
- package/skills/pr-converge/scripts/request_copilot_review.py +71 -0
- package/skills/pr-converge/scripts/resolve_pr_head.py +58 -0
- package/skills/pr-converge/scripts/review_field_helpers.py +43 -0
- package/skills/pr-converge/scripts/test_check_pr_mergeability.py +126 -0
- package/skills/pr-converge/scripts/test_evict_cached_config_modules.py +22 -0
- package/skills/pr-converge/scripts/test_fetch_bugbot_inline_comments.py +342 -0
- package/skills/pr-converge/scripts/test_fetch_bugbot_reviews.py +220 -0
- package/skills/pr-converge/scripts/test_fetch_copilot_inline_comments.py +372 -0
- package/skills/pr-converge/scripts/test_fetch_copilot_reviews.py +280 -0
- package/skills/pr-converge/scripts/test_mark_pr_ready.py +69 -0
- package/skills/pr-converge/scripts/test_open_followup_copilot_pr.py +236 -0
- package/skills/pr-converge/scripts/test_post_bugbot_run.py +195 -0
- package/skills/pr-converge/scripts/test_reply_to_inline_comment.py +159 -0
- package/skills/pr-converge/scripts/test_request_copilot_review.py +101 -0
- package/skills/pr-converge/scripts/test_resolve_pr_head.py +79 -0
- package/skills/pr-converge/scripts/test_review_field_helpers.py +80 -0
- package/skills/pr-converge/scripts/test_trigger_bugbot.py +139 -0
- package/skills/pr-converge/scripts/test_view_pr_context.py +111 -0
- package/skills/pr-converge/scripts/trigger_bugbot.py +77 -0
- package/skills/pr-converge/scripts/view_pr_context.py +47 -0
- package/skills/pr-converge/test_team_lifecycle.py +47 -0
- package/skills/pr-converge/workflows/ahk-auto-continue-loop.md +108 -0
- package/skills/pr-converge/workflows/schedule-wakeup-loop.md +37 -0
- package/skills/qbug/SKILL.md +4 -4
- package/skills/qbug/test_qbug_skill_post_fix_audit.py +2 -2
- package/skills/resume-review/SKILL.md +261 -0
- package/agents/agent-writer.md +0 -157
- package/agents/config-centralizer.md +0 -686
- package/agents/config-extraction-agent.md +0 -225
- package/agents/doc-orchestrator.md +0 -47
- package/agents/docx-agent.md +0 -211
- package/agents/magic-value-eliminator-agent.md +0 -72
- package/agents/mandatory-agent-workflow-agent.md +0 -88
- package/agents/parallel-workflow-coordinator.md +0 -779
- package/agents/pdf-agent.md +0 -302
- package/agents/project-context-loader.md +0 -238
- package/agents/readability-review-agent.md +0 -76
- package/agents/refactoring-specialist.md +0 -69
- package/agents/right-sized-engineer.md +0 -129
- package/agents/session-continuity-manager.md +0 -53
- package/agents/stub-detector-agent.md +0 -140
- package/agents/tdd-test-writer.md +0 -62
- package/agents/test-data-builder.md +0 -68
- package/agents/tooling-builder.md +0 -78
- package/agents/validation-expert.md +0 -71
- package/agents/xlsx-agent.md +0 -169
- package/skills/bugteam/scripts/README.md +0 -58
- package/skills/bugteam/scripts/_claude_permissions_common.py +0 -219
- package/skills/bugteam/scripts/bugteam_code_rules_gate.py +0 -633
- package/skills/bugteam/scripts/bugteam_fix_hookspath.py +0 -260
- package/skills/bugteam/scripts/bugteam_preflight.py +0 -201
- package/skills/bugteam/scripts/config/bugteam_fix_hookspath_constants.py +0 -17
- package/skills/bugteam/scripts/grant_project_claude_permissions.py +0 -109
- package/skills/bugteam/scripts/revoke_project_claude_permissions.py +0 -135
- package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +0 -271
- package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +0 -267
- package/skills/bugteam/scripts/test_bugteam_preflight.py +0 -189
- package/skills/bugteam/scripts/test_claude_permissions_common.py +0 -44
- /package/skills/{bugteam → pr-converge}/scripts/config/__init__.py +0 -0
package/agents/xlsx-agent.md
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: xlsx-agent
|
|
3
|
-
description: Use this agent when working with Excel spreadsheets requiring complex formulas, formatting, financial modeling, or data analysis.
|
|
4
|
-
model: inherit
|
|
5
|
-
color: green
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
You are an Excel spreadsheet specialist agent complementing the xlsx skill.
|
|
9
|
-
|
|
10
|
-
**Complementary Skill:** skills/xlsx/SKILL.md
|
|
11
|
-
**Your Role:** Handle complex spreadsheet projects, delegate simple operations to the skill
|
|
12
|
-
|
|
13
|
-
## Complexity Assessment
|
|
14
|
-
|
|
15
|
-
**Handle as agent (complex) when:**
|
|
16
|
-
- Creating multi-worksheet workbooks with interconnected formulas
|
|
17
|
-
- Financial modeling requiring industry-standard formatting (color-coding, number formats)
|
|
18
|
-
- Formula debugging across multiple files or sheets
|
|
19
|
-
- Projects requiring systematic formula verification via recalc.py
|
|
20
|
-
- Merging/transforming data across multiple Excel files
|
|
21
|
-
- Creating complex reports with charts, pivot tables, and formatting
|
|
22
|
-
- Extracting and analyzing data from multiple spreadsheet sources
|
|
23
|
-
- Projects requiring both openpyxl (formulas) AND pandas (data analysis)
|
|
24
|
-
|
|
25
|
-
**Delegate to skill (simple) when:**
|
|
26
|
-
- Reading single Excel file into DataFrame
|
|
27
|
-
- Simple data extraction from one sheet
|
|
28
|
-
- Basic CSV conversion
|
|
29
|
-
- Straightforward table extraction with pdfplumber
|
|
30
|
-
- Single formula creation or edit
|
|
31
|
-
- Simple metadata extraction
|
|
32
|
-
|
|
33
|
-
## Workflow for Complex Tasks
|
|
34
|
-
|
|
35
|
-
1. **Load methodology** - Invoke Skill tool to load xlsx skill
|
|
36
|
-
2. **Create TodoWrite** - Track phases:
|
|
37
|
-
```
|
|
38
|
-
Excel Project:
|
|
39
|
-
- [ ] Requirements analysis (worksheets, formulas, formatting)
|
|
40
|
-
- [ ] Implementation (openpyxl or pandas based on needs)
|
|
41
|
-
- [ ] Formula verification (recalc.py if formulas used)
|
|
42
|
-
- [ ] Error fixing (if verification fails)
|
|
43
|
-
- [ ] Final validation
|
|
44
|
-
```
|
|
45
|
-
3. **Gather requirements:**
|
|
46
|
-
- What type of workbook? (Financial model, report, data analysis)
|
|
47
|
-
- Formula complexity? (Simple calculations vs. multi-sheet dependencies)
|
|
48
|
-
- Formatting requirements? (Financial standards, custom styles)
|
|
49
|
-
- Data sources? (CSV, database, API, manual entry)
|
|
50
|
-
4. **Choose tools based on skill guidance:**
|
|
51
|
-
- **openpyxl** for formulas, formatting, Excel-specific features
|
|
52
|
-
- **pandas** for data manipulation, bulk operations, analysis
|
|
53
|
-
- Both if needed (data processing → formatted output)
|
|
54
|
-
5. **Implement with skill patterns:**
|
|
55
|
-
- Use frozen dataclasses for configuration
|
|
56
|
-
- Extract constants (financial color codes, number formats)
|
|
57
|
-
- Follow skill's formula construction rules (cell references, no hardcoding)
|
|
58
|
-
- Apply financial modeling standards if applicable
|
|
59
|
-
6. **MANDATORY - Verify formulas:**
|
|
60
|
-
- If ANY formulas created, run: `python recalc.py output.xlsx`
|
|
61
|
-
- Check JSON output for errors
|
|
62
|
-
- If errors found, iterate fixes until clean
|
|
63
|
-
7. **Follow skill quality guidelines:**
|
|
64
|
-
- Zero formula errors (non-negotiable)
|
|
65
|
-
- Document hardcoded values with sources
|
|
66
|
-
- Preserve existing template conventions when updating files
|
|
67
|
-
- Use Excel formulas, not Python-calculated hardcoded values
|
|
68
|
-
|
|
69
|
-
## Critical Skill Rules to Enforce
|
|
70
|
-
|
|
71
|
-
### ALWAYS Use Excel Formulas (Never Hardcode Calculated Values)
|
|
72
|
-
|
|
73
|
-
❌ **WRONG:**
|
|
74
|
-
```python
|
|
75
|
-
# Calculating in Python and hardcoding result
|
|
76
|
-
total = df['Sales'].sum()
|
|
77
|
-
sheet['B10'] = total # Hardcodes 5000 - spreadsheet can't recalculate
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
✅ **CORRECT:**
|
|
81
|
-
```python
|
|
82
|
-
# Let Excel calculate with formulas
|
|
83
|
-
sheet['B10'] = '=SUM(B2:B9)' # Spreadsheet recalculates when source data changes
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
**Why this matters:** Spreadsheets must remain dynamic. Hardcoded values break when source data changes.
|
|
87
|
-
|
|
88
|
-
### Financial Modeling Standards (When Applicable)
|
|
89
|
-
|
|
90
|
-
Only apply when user indicates financial modeling context:
|
|
91
|
-
- **Blue text:** Hardcoded inputs users will change
|
|
92
|
-
- **Black text:** ALL formulas and calculations
|
|
93
|
-
- **Green text:** Links from other worksheets in same workbook
|
|
94
|
-
- **Red text:** External file links
|
|
95
|
-
- **Yellow background:** Key assumptions needing attention
|
|
96
|
-
|
|
97
|
-
### Formula Verification is Mandatory
|
|
98
|
-
|
|
99
|
-
After creating ANY file with formulas:
|
|
100
|
-
```bash
|
|
101
|
-
python recalc.py output.xlsx
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
If errors found (status: "errors_found"), MUST fix before delivering to user.
|
|
105
|
-
|
|
106
|
-
## Delegation Path
|
|
107
|
-
|
|
108
|
-
If assessment shows simple task:
|
|
109
|
-
```
|
|
110
|
-
Use Skill tool with: skills/xlsx
|
|
111
|
-
Exit after delegation
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
## Common Complex Scenarios
|
|
115
|
-
|
|
116
|
-
**Financial Model Creation:**
|
|
117
|
-
1. Define model structure (income statement, balance sheet, cash flow, valuation)
|
|
118
|
-
2. Create worksheet templates with headers
|
|
119
|
-
3. Build formulas with cell references to assumptions
|
|
120
|
-
4. Apply financial color-coding standards
|
|
121
|
-
5. Verify all formulas calculate correctly
|
|
122
|
-
6. Document assumptions and sources
|
|
123
|
-
|
|
124
|
-
**Multi-File Data Consolidation:**
|
|
125
|
-
1. Read multiple Excel files with pandas
|
|
126
|
-
2. Transform and merge data
|
|
127
|
-
3. Create summary workbook with openpyxl
|
|
128
|
-
4. Add formulas linking to consolidated data
|
|
129
|
-
5. Format output with consistent styles
|
|
130
|
-
6. Verify formulas work
|
|
131
|
-
|
|
132
|
-
**Report Generation:**
|
|
133
|
-
1. Extract data from database/API/CSV
|
|
134
|
-
2. Process with pandas (aggregations, calculations)
|
|
135
|
-
3. Create formatted Excel output with openpyxl
|
|
136
|
-
4. Add charts and pivot tables
|
|
137
|
-
5. Apply conditional formatting
|
|
138
|
-
6. Verify all formulas
|
|
139
|
-
|
|
140
|
-
## Edge Cases to Handle
|
|
141
|
-
|
|
142
|
-
**Existing Template Modification:**
|
|
143
|
-
- MUST preserve existing format, style, and conventions
|
|
144
|
-
- Study template carefully before making changes
|
|
145
|
-
- Never impose standardized formatting on established templates
|
|
146
|
-
- Existing patterns ALWAYS override skill guidelines
|
|
147
|
-
|
|
148
|
-
**Formula Errors:**
|
|
149
|
-
- Common: #REF! (invalid references), #DIV/0! (division by zero), #VALUE! (wrong type)
|
|
150
|
-
- Use recalc.py to identify all errors
|
|
151
|
-
- Fix systematically (verify cell references, check ranges, test edge cases)
|
|
152
|
-
- Re-run verification until clean
|
|
153
|
-
|
|
154
|
-
**Mixed Workbook/Pandas Tasks:**
|
|
155
|
-
- Use pandas for data transformation and analysis
|
|
156
|
-
- Use openpyxl for final formatted output with formulas
|
|
157
|
-
- Never mix in same step (data first, then formatting)
|
|
158
|
-
|
|
159
|
-
## Output Deliverables
|
|
160
|
-
|
|
161
|
-
**For Simple Tasks (delegated):**
|
|
162
|
-
- Delegate to skill, provide result
|
|
163
|
-
|
|
164
|
-
**For Complex Tasks:**
|
|
165
|
-
- Working Excel file(s) with zero formula errors
|
|
166
|
-
- Verification output showing all formulas calculated successfully
|
|
167
|
-
- Documentation of any hardcoded values with sources
|
|
168
|
-
- Brief explanation of formula logic for key calculations
|
|
169
|
-
- Instructions for user on how to modify assumptions/inputs
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
# Bugteam utility scripts
|
|
2
|
-
|
|
3
|
-
Scripts in this directory are **executed** by the lead or teammates. They are not loaded into context as instructions (see Anthropic [Skill authoring best practices — Progressive disclosure](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices#progressive-disclosure-patterns)).
|
|
4
|
-
|
|
5
|
-
| Script | Purpose |
|
|
6
|
-
|--------|---------|
|
|
7
|
-
| `bugteam_preflight.py` | Run pytest (when configured) and optional `pre-commit` before `/bugteam`. |
|
|
8
|
-
| `bugteam_fix_hookspath.py` | Auto-remediate a stale local `core.hooksPath` override, set canonical global value, re-run `bugteam_preflight.py`. Invoked by Claude when preflight reports a `core.hooksPath` failure. |
|
|
9
|
-
| `bugteam_code_rules_gate.py` | Run `validate_content` from `code-rules-enforcer.py` on PR-scoped files (`git diff` vs merge-base). Exit `1` if any mandatory rule fails. Invoked **before each audit**; the fixer clears it before the auditor runs. |
|
|
10
|
-
| `grant_project_claude_permissions.py` | Idempotent grant of Edit/Write/Read on `cwd/.claude/**` into `~/.claude/settings.json`. |
|
|
11
|
-
| `revoke_project_claude_permissions.py` | Removes the matching grant entries from `~/.claude/settings.json`. |
|
|
12
|
-
| `test_claude_permissions_common.py` | Pytest module for path normalization and glob-metacharacter guards in `_claude_permissions_common.py`. |
|
|
13
|
-
| `_claude_permissions_common.py` | Shared helpers for the grant/revoke scripts (atomic JSON writes, settings sections). |
|
|
14
|
-
|
|
15
|
-
## `bugteam_preflight.py`
|
|
16
|
-
|
|
17
|
-
From the repository root:
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
python "${CLAUDE_SKILL_DIR}/scripts/bugteam_preflight.py"
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
- Skips pytest when `BUGTEAM_PREFLIGHT_SKIP=1`.
|
|
24
|
-
- Skips pytest when `pytest.ini` / `pyproject.toml` exists but no `test_*.py` / `*_test.py` files are found under the repo root.
|
|
25
|
-
- Pytest exit code `5` (no tests collected) is treated as success.
|
|
26
|
-
- Add `--pre-commit` to run `pre-commit run --all-files` when `.pre-commit-config.yaml` exists.
|
|
27
|
-
|
|
28
|
-
## `bugteam_fix_hookspath.py`
|
|
29
|
-
|
|
30
|
-
From the repository root:
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
python "${CLAUDE_SKILL_DIR}/scripts/bugteam_fix_hookspath.py"
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
- Removes any local-scope `core.hooksPath` value that does not end in `hooks/git-hooks`.
|
|
37
|
-
- Sets `git config --global core.hooksPath ~/.claude/hooks/git-hooks` when the global value is unset or non-canonical.
|
|
38
|
-
- Refuses to run (exit non-zero) when `~/.claude/hooks/git-hooks` does not exist on disk — install via `npx claude-dev-env .` first.
|
|
39
|
-
- Idempotent: a second invocation is a clean no-op.
|
|
40
|
-
- Re-runs `bugteam_preflight.py --no-pytest` and propagates its exit code.
|
|
41
|
-
|
|
42
|
-
The bugteam SKILL invokes this automatically when preflight stderr indicates a `core.hooksPath` failure, so Claude does not surface the error to the user.
|
|
43
|
-
|
|
44
|
-
## `bugteam_code_rules_gate.py`
|
|
45
|
-
|
|
46
|
-
From the repository root (same merge-base rules as the PR head vs base — default `--base origin/main`):
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
python "${CLAUDE_SKILL_DIR}/scripts/bugteam_code_rules_gate.py"
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
Optional explicit files instead of `git diff`:
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
python "${CLAUDE_SKILL_DIR}/scripts/bugteam_code_rules_gate.py" path/to/a.py path/to/b.ts
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
This loads `validate_content` from `hooks/blocking/code-rules-enforcer.py` inside `claude-dev-env` (same logic as the PreToolUse hook). Exit `0` = mandatory checks pass on scanned files; exit `1` = violations printed to stderr.
|
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
"""Shared helpers for grant_project_claude_permissions and revoke_project_claude_permissions.
|
|
2
|
-
|
|
3
|
-
Writes to ~/.claude/settings.json are atomic and permission-preserving: the
|
|
4
|
-
target file's existing POSIX mode is captured, a sibling temp file is
|
|
5
|
-
created via os.open with O_CREAT | O_EXCL and the preserved mode, content
|
|
6
|
-
is written, then os.replace swaps it into place. Output is serialized with
|
|
7
|
-
sort_keys=True for a stable on-disk layout; the first run on a hand-ordered
|
|
8
|
-
settings file produces a one-time re-sort diff, subsequent writes are stable.
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
import json
|
|
12
|
-
import os
|
|
13
|
-
import stat
|
|
14
|
-
import sys
|
|
15
|
-
from pathlib import Path
|
|
16
|
-
from typing import NoReturn
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
TEXT_FILE_ENCODING: str = "utf-8"
|
|
20
|
-
PERMISSION_ALLOW_TOOLS: tuple[str, ...] = ("Edit", "Write", "Read")
|
|
21
|
-
|
|
22
|
-
AUTO_MODE_ENVIRONMENT_ENTRY_TEMPLATE: str = (
|
|
23
|
-
"Trusted local workspace: {project_path}/.claude/** is the user's "
|
|
24
|
-
"project Claude Code config tree; edits inside are routine"
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def exit_with_error(message: str) -> NoReturn:
|
|
29
|
-
print(f"Error: {message}", file=sys.stderr)
|
|
30
|
-
raise SystemExit(1)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def path_contains_glob_metacharacters(candidate_path: str) -> bool:
|
|
34
|
-
glob_metacharacters_in_path: tuple[str, ...] = (
|
|
35
|
-
"*",
|
|
36
|
-
"?",
|
|
37
|
-
"[",
|
|
38
|
-
"]",
|
|
39
|
-
"(",
|
|
40
|
-
")",
|
|
41
|
-
"{",
|
|
42
|
-
"}",
|
|
43
|
-
",",
|
|
44
|
-
)
|
|
45
|
-
return any(
|
|
46
|
-
each_character in candidate_path
|
|
47
|
-
for each_character in glob_metacharacters_in_path
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def get_current_project_path() -> str:
|
|
52
|
-
normalized_project_path = str(Path.cwd()).replace("\\", "/")
|
|
53
|
-
if path_contains_glob_metacharacters(normalized_project_path):
|
|
54
|
-
raise ValueError(
|
|
55
|
-
f"Current directory path contains glob metacharacters and cannot "
|
|
56
|
-
f"be used to build permission rules safely: {normalized_project_path}"
|
|
57
|
-
)
|
|
58
|
-
return normalized_project_path
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def build_permission_rule(tool_name: str, project_path: str) -> str:
|
|
62
|
-
return f"{tool_name}({project_path}/.claude/**)"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def build_permission_rules(
|
|
66
|
-
project_path: str, permission_allow_tools: tuple[str, ...]
|
|
67
|
-
) -> list[str]:
|
|
68
|
-
return [
|
|
69
|
-
build_permission_rule(each_tool, project_path)
|
|
70
|
-
for each_tool in permission_allow_tools
|
|
71
|
-
]
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def load_settings(settings_path: Path) -> dict[str, object]:
|
|
75
|
-
if not settings_path.exists():
|
|
76
|
-
return {}
|
|
77
|
-
parsed_settings: dict[str, object] = {}
|
|
78
|
-
try:
|
|
79
|
-
raw_text = settings_path.read_text(encoding=TEXT_FILE_ENCODING)
|
|
80
|
-
except OSError as read_error:
|
|
81
|
-
exit_with_error(f"Failed to read {settings_path}: {read_error}")
|
|
82
|
-
try:
|
|
83
|
-
parsed_settings = json.loads(raw_text)
|
|
84
|
-
except json.JSONDecodeError as decode_error:
|
|
85
|
-
exit_with_error(
|
|
86
|
-
f"Refusing to modify {settings_path}: existing file is not valid JSON "
|
|
87
|
-
f"({decode_error}). Fix or back up the file manually, then re-run."
|
|
88
|
-
)
|
|
89
|
-
if not isinstance(parsed_settings, dict):
|
|
90
|
-
exit_with_error(
|
|
91
|
-
f"Refusing to modify {settings_path}: existing file's root is "
|
|
92
|
-
f"{type(parsed_settings).__name__}, not a JSON object. Fix or back up "
|
|
93
|
-
f"the file manually, then re-run."
|
|
94
|
-
)
|
|
95
|
-
return parsed_settings
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def serialize_settings_to_json_text(settings: dict[str, object]) -> str:
|
|
99
|
-
json_indent_width_columns: int = len(" ")
|
|
100
|
-
return json.dumps(
|
|
101
|
-
settings,
|
|
102
|
-
indent=json_indent_width_columns,
|
|
103
|
-
sort_keys=True,
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def get_mode_to_preserve(settings_path: Path) -> int:
|
|
108
|
-
default_settings_file_mode: int = 0o600
|
|
109
|
-
try:
|
|
110
|
-
stat_result = os.stat(settings_path)
|
|
111
|
-
except FileNotFoundError:
|
|
112
|
-
return default_settings_file_mode
|
|
113
|
-
except OSError as stat_error:
|
|
114
|
-
exit_with_error(f"Failed to stat {settings_path}: {stat_error}")
|
|
115
|
-
return stat.S_IMODE(stat_result.st_mode)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def write_atomically_with_mode(
|
|
119
|
-
temporary_path: Path, serialized_content: str, file_mode: int
|
|
120
|
-
) -> None:
|
|
121
|
-
file_descriptor = os.open(
|
|
122
|
-
str(temporary_path),
|
|
123
|
-
os.O_WRONLY | os.O_CREAT | os.O_EXCL,
|
|
124
|
-
file_mode,
|
|
125
|
-
)
|
|
126
|
-
with os.fdopen(file_descriptor, "w", encoding=TEXT_FILE_ENCODING) as writer:
|
|
127
|
-
writer.write(serialized_content)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
def save_settings(settings_path: Path, settings: dict[str, object]) -> None:
|
|
131
|
-
atomic_write_temporary_suffix: str = ".tmp"
|
|
132
|
-
settings_path.parent.mkdir(parents=True, exist_ok=True)
|
|
133
|
-
serialized_settings = serialize_settings_to_json_text(settings)
|
|
134
|
-
temporary_path = settings_path.with_suffix(
|
|
135
|
-
settings_path.suffix + atomic_write_temporary_suffix
|
|
136
|
-
)
|
|
137
|
-
mode_to_preserve = get_mode_to_preserve(settings_path)
|
|
138
|
-
try:
|
|
139
|
-
try:
|
|
140
|
-
write_atomically_with_mode(
|
|
141
|
-
temporary_path, serialized_settings, mode_to_preserve
|
|
142
|
-
)
|
|
143
|
-
os.replace(str(temporary_path), str(settings_path))
|
|
144
|
-
except OSError as os_error:
|
|
145
|
-
exit_with_error(
|
|
146
|
-
f"Failed to write settings atomically to {settings_path}: {os_error}"
|
|
147
|
-
)
|
|
148
|
-
finally:
|
|
149
|
-
if temporary_path.exists():
|
|
150
|
-
try:
|
|
151
|
-
temporary_path.unlink()
|
|
152
|
-
except OSError:
|
|
153
|
-
pass
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
def append_if_missing(target_list: list[object], new_value: str) -> bool:
|
|
157
|
-
if new_value in target_list:
|
|
158
|
-
return False
|
|
159
|
-
target_list.append(new_value)
|
|
160
|
-
return True
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
def ensure_dict_section(
|
|
164
|
-
settings: dict[str, object], section_name: str
|
|
165
|
-
) -> dict[str, object]:
|
|
166
|
-
"""Return an existing dict section or create an empty one if absent.
|
|
167
|
-
|
|
168
|
-
A missing key and an explicit JSON null are treated identically: both
|
|
169
|
-
produce a fresh empty dict stored back into settings. Any other non-dict
|
|
170
|
-
value (string, list, number, bool) calls exit_with_error to avoid
|
|
171
|
-
overwriting user data.
|
|
172
|
-
"""
|
|
173
|
-
existing_section = settings.get(section_name)
|
|
174
|
-
if existing_section is None:
|
|
175
|
-
replacement_section: dict[str, object] = {}
|
|
176
|
-
settings[section_name] = replacement_section
|
|
177
|
-
return replacement_section
|
|
178
|
-
if not isinstance(existing_section, dict):
|
|
179
|
-
exit_with_error(
|
|
180
|
-
f"Refusing to modify settings key {section_name!r}: existing value "
|
|
181
|
-
f"is {type(existing_section).__name__}, not a JSON object. Fix or "
|
|
182
|
-
f"remove the key manually, then re-run."
|
|
183
|
-
)
|
|
184
|
-
return existing_section
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
def ensure_list_entry(section: dict[str, object], entry_name: str) -> list[object]:
|
|
188
|
-
"""Return an existing list entry or create an empty one if absent.
|
|
189
|
-
|
|
190
|
-
A missing key and an explicit JSON null are treated identically: both
|
|
191
|
-
produce a fresh empty list stored back into the section. Any other
|
|
192
|
-
non-list value (string, dict, number, bool) calls exit_with_error to
|
|
193
|
-
avoid overwriting user data.
|
|
194
|
-
"""
|
|
195
|
-
existing_entry = section.get(entry_name)
|
|
196
|
-
if existing_entry is None:
|
|
197
|
-
replacement_entry: list[object] = []
|
|
198
|
-
section[entry_name] = replacement_entry
|
|
199
|
-
return replacement_entry
|
|
200
|
-
if not isinstance(existing_entry, list):
|
|
201
|
-
exit_with_error(
|
|
202
|
-
f"Refusing to modify settings entry {entry_name!r}: existing value "
|
|
203
|
-
f"is {type(existing_entry).__name__}, not a JSON array. Fix or "
|
|
204
|
-
f"remove the entry manually, then re-run."
|
|
205
|
-
)
|
|
206
|
-
return existing_entry
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
def prune_empty_list_then_empty_section(
|
|
210
|
-
settings: dict[str, object], section_key: str, list_key: str
|
|
211
|
-
) -> None:
|
|
212
|
-
section = settings.get(section_key)
|
|
213
|
-
if not isinstance(section, dict):
|
|
214
|
-
return
|
|
215
|
-
list_entry = section.get(list_key)
|
|
216
|
-
if isinstance(list_entry, list) and len(list_entry) == 0:
|
|
217
|
-
del section[list_key]
|
|
218
|
-
if len(section) == 0:
|
|
219
|
-
del settings[section_key]
|