@intentsolutionsio/devops-automation-pack 1.0.0 → 1.0.3

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/README.md CHANGED
@@ -11,6 +11,7 @@ Version 1.0.0 | October 2025
11
11
  The **DevOps Automation Pack** is a collection of 25 professional plugins for Claude Code that automate your DevOps workflow.
12
12
 
13
13
  **Instead of manually:**
14
+
14
15
  - Writing Git commit messages
15
16
  - Creating CI/CD pipelines from scratch
16
17
  - Optimizing Dockerfiles
@@ -18,6 +19,7 @@ The **DevOps Automation Pack** is a collection of 25 professional plugins for Cl
18
19
  - Writing Terraform modules
19
20
 
20
21
  **You get:**
22
+
21
23
  - AI-generated conventional commits
22
24
  - Production-ready pipelines in minutes
23
25
  - Automatic Docker optimization
@@ -33,6 +35,7 @@ The **DevOps Automation Pack** is a collection of 25 professional plugins for Cl
33
35
  ### 25 Professional Plugins
34
36
 
35
37
  **Git Workflow (5 commands)**
38
+
36
39
  - `/commit-smart` (gc) - Generate conventional commits with AI
37
40
  - `/pr-create` (gpr) - Create pull requests with professional templates
38
41
  - `/branch-create` (bc) - Create properly named feature branches
@@ -40,6 +43,7 @@ The **DevOps Automation Pack** is a collection of 25 professional plugins for Cl
40
43
  - `/rebase-interactive` (ri) - Interactive rebase workflow
41
44
 
42
45
  **CI/CD Automation (6 plugins: 1 agent + 5 commands)**
46
+
43
47
  - CI/CD Expert Agent - Pipeline design specialist
44
48
  - `/github-actions-create` (gha) - Generate GitHub Actions workflows
45
49
  - `/gitlab-ci-create` (glci) - Generate GitLab CI pipelines
@@ -48,24 +52,28 @@ The **DevOps Automation Pack** is a collection of 25 professional plugins for Cl
48
52
  - `/deployment-strategy` (ds) - Recommend deployment approach
49
53
 
50
54
  **Docker (4 plugins: 1 agent + 3 commands)**
55
+
51
56
  - Docker Specialist Agent - Container optimization expert
52
57
  - `/dockerfile-generate` (dg) - Create optimized Dockerfiles
53
58
  - `/docker-compose-create` (dcc) - Generate docker-compose.yml
54
59
  - `/docker-optimize` (do) - Reduce image size by 50-80%
55
60
 
56
61
  **Kubernetes (4 plugins: 1 agent + 3 commands)**
62
+
57
63
  - Kubernetes Expert Agent - K8s orchestration specialist
58
64
  - `/k8s-manifest-generate` (km) - Generate production-ready manifests
59
65
  - `/k8s-helm-chart` (kh) - Create Helm charts with best practices
60
66
  - `/k8s-troubleshoot` (kt) - Debug pod failures instantly
61
67
 
62
68
  **Terraform (4 plugins: 1 agent + 3 commands)**
69
+
63
70
  - Terraform Architect Agent - Infrastructure as code expert
64
71
  - `/terraform-module-create` (tm) - Generate reusable modules
65
72
  - `/terraform-plan-analyze` (tpa) - Analyze plans for risks
66
73
  - `/cloudformation-generate` (cfn) - Create CloudFormation templates
67
74
 
68
75
  **Deployment (2 plugins: 1 agent + 1 command)**
76
+
69
77
  - Deployment Specialist Agent - Release management expert
70
78
  - `/monitoring-setup` (ms) - Set up Prometheus + Grafana
71
79
 
@@ -102,13 +110,15 @@ See [`docs/QUICK_START.md`](docs/QUICK_START.md) for detailed walkthrough.
102
110
  ## Who Is This For?
103
111
 
104
112
  ### Perfect For:
105
- - **Junior developers** learning DevOps practices
106
- - **Senior engineers** who want to move faster
107
- - **DevOps teams** standardizing workflows
108
- - **Startups** adopting cloud infrastructure
109
- - **Enterprises** enforcing best practices
113
+
114
+ - **Junior developers** learning DevOps practices
115
+ - **Senior engineers** who want to move faster
116
+ - **DevOps teams** standardizing workflows
117
+ - **Startups** adopting cloud infrastructure
118
+ - **Enterprises** enforcing best practices
110
119
 
111
120
  ### You'll Benefit If You:
121
+
112
122
  - Write Git commits, PRs, or create branches
113
123
  - Build or optimize CI/CD pipelines
114
124
  - Work with Docker containers
@@ -117,6 +127,7 @@ See [`docs/QUICK_START.md`](docs/QUICK_START.md) for detailed walkthrough.
117
127
  - Set up monitoring and alerting
118
128
 
119
129
  ### Required Knowledge:
130
+
120
131
  - Basic Git understanding (commit, push, pull)
121
132
  - Familiarity with your chosen tools (Docker, K8s, etc.)
122
133
  - **No expert-level knowledge needed** - plugins guide you
@@ -144,15 +155,18 @@ See [`docs/USE_CASES.md`](docs/USE_CASES.md) for 7 detailed real-world scenarios
144
155
  ## Documentation
145
156
 
146
157
  ### Getting Started
158
+
147
159
  - **[Installation Guide](docs/INSTALLATION.md)** - Step-by-step setup instructions (5 minutes)
148
160
  - **[Quick Start](docs/QUICK_START.md)** - Your first workflow in 5 minutes
149
161
  - **[Use Cases](docs/USE_CASES.md)** - 7 real-world scenarios with before/after
150
162
 
151
163
  ### Reference
152
- - **[Troubleshooting](docs/000-docs/157-DR-FAQS-troubleshooting.md)** - Solutions to 20 common problems
164
+
165
+ - **Troubleshooting** - Solutions to 20 common problems
153
166
  - **Plugin Reference** - Complete command documentation (see each plugin's help)
154
167
 
155
168
  ### Support
169
+
156
170
  - **Email:** mandy@intentsolutions.io
157
171
  - **Response time:** Within 24 hours
158
172
  - **Include:** Error message, command, OS, Claude Code version
@@ -162,12 +176,15 @@ See [`docs/USE_CASES.md`](docs/USE_CASES.md) for 7 detailed real-world scenarios
162
176
  ## Requirements
163
177
 
164
178
  ### Minimum Requirements
179
+
165
180
  - Claude Code version **1.5.0 or higher**
166
181
  - 10 MB free disk space
167
182
  - Git installed (for Git workflow commands)
168
183
 
169
184
  ### Optional Requirements
185
+
170
186
  Depending on which plugins you use:
187
+
171
188
  - **Docker commands:** Docker installed and running
172
189
  - **Kubernetes commands:** kubectl configured
173
190
  - **Terraform commands:** Terraform installed
@@ -182,32 +199,38 @@ Depending on which plugins you use:
182
199
  The pack supports these technologies:
183
200
 
184
201
  **Version Control:**
202
+
185
203
  - Git (GitHub, GitLab, Bitbucket)
186
204
  - Conventional Commits standard
187
205
 
188
206
  **CI/CD Platforms:**
207
+
189
208
  - GitHub Actions
190
209
  - GitLab CI
191
210
  - CircleCI
192
211
  - Jenkins (via pipeline optimization)
193
212
 
194
213
  **Containers:**
214
+
195
215
  - Docker
196
216
  - docker-compose
197
217
  - Multi-stage builds
198
218
  - Alpine/Debian/Ubuntu base images
199
219
 
200
220
  **Orchestration:**
221
+
201
222
  - Kubernetes (1.25+)
202
223
  - Helm 3
203
224
  - kubectl
204
225
 
205
226
  **Infrastructure:**
227
+
206
228
  - Terraform (1.0+)
207
229
  - AWS CloudFormation
208
230
  - Multi-cloud (AWS, GCP, Azure)
209
231
 
210
232
  **Monitoring:**
233
+
211
234
  - Prometheus
212
235
  - Grafana
213
236
  - AlertManager
@@ -219,6 +242,7 @@ The pack supports these technologies:
219
242
  **Launch Price:** $39 (regular $49)
220
243
 
221
244
  **What You Get:**
245
+
222
246
  - All 25 plugins (19 commands + 6 agents)
223
247
  - Lifetime updates for version 1.x
224
248
  - Email support
@@ -226,6 +250,7 @@ The pack supports these technologies:
226
250
  - No subscription, pay once
227
251
 
228
252
  **Money-Back Guarantee:**
253
+
229
254
  - 30-day full refund
230
255
  - No questions asked
231
256
  - Email mandy@intentsolutions.io
@@ -239,6 +264,7 @@ The pack supports these technologies:
239
264
  The pack is tech-agnostic for most commands. It detects your stack and adapts.
240
265
 
241
266
  **Works with:**
267
+
242
268
  - Any Git hosting (GitHub, GitLab, Bitbucket, etc.)
243
269
  - Any programming language
244
270
  - Any cloud provider (AWS, GCP, Azure)
@@ -247,6 +273,7 @@ The pack is tech-agnostic for most commands. It detects your stack and adapts.
247
273
  ### Do I need to be a DevOps expert?
248
274
 
249
275
  **No.** The pack is designed for all skill levels:
276
+
250
277
  - **Beginners:** Step-by-step guidance and explanations
251
278
  - **Intermediate:** Fast workflow automation
252
279
  - **Experts:** Time-saving tools and best practices enforcement
@@ -254,6 +281,7 @@ The pack is tech-agnostic for most commands. It detects your stack and adapts.
254
281
  ### Can I use this for commercial projects?
255
282
 
256
283
  **Yes.** Your license covers:
284
+
257
285
  - Personal projects
258
286
  - Commercial projects
259
287
  - Team usage (one license per developer)
@@ -261,6 +289,7 @@ The pack is tech-agnostic for most commands. It detects your stack and adapts.
261
289
  ### How often is it updated?
262
290
 
263
291
  **Version 1.x updates:** Free for life
292
+
264
293
  - Bug fixes: As needed
265
294
  - New features: Quarterly
266
295
  - Security patches: Immediately
@@ -270,6 +299,7 @@ The pack is tech-agnostic for most commands. It detects your stack and adapts.
270
299
  ### What if I have issues?
271
300
 
272
301
  **Support channels:**
302
+
273
303
  1. **Documentation:** Check `docs/000-docs/157-DR-FAQS-troubleshooting.md` (covers 20 common issues)
274
304
  2. **Email:** mandy@intentsolutions.io (response within 24 hours)
275
305
  3. **Refund:** 30-day money-back guarantee
@@ -279,16 +309,20 @@ The pack is tech-agnostic for most commands. It detects your stack and adapts.
279
309
  ## Next Steps
280
310
 
281
311
  ### 1. Install the Pack
312
+
282
313
  See [`docs/INSTALLATION.md`](docs/INSTALLATION.md) for step-by-step instructions.
283
314
 
284
315
  ### 2. Run Your First Workflow
316
+
285
317
  Follow [`docs/QUICK_START.md`](docs/QUICK_START.md) - takes 5 minutes.
286
318
 
287
319
  ### 3. Explore Use Cases
320
+
288
321
  Read [`docs/USE_CASES.md`](docs/USE_CASES.md) to see how others use it.
289
322
 
290
323
  ### 4. Get Help If Needed
291
- Check [`docs/000-docs/157-DR-FAQS-troubleshooting.md`](docs/000-docs/157-DR-FAQS-troubleshooting.md) or email support.
324
+
325
+ Check `docs/000-docs/157-DR-FAQS-troubleshooting.md` or email support.
292
326
 
293
327
  ---
294
328
 
@@ -297,11 +331,12 @@ Check [`docs/000-docs/157-DR-FAQS-troubleshooting.md`](docs/000-docs/157-DR-FAQS
297
331
  This plugin pack is licensed for individual use. See LICENSE file for details.
298
332
 
299
333
  **Key Points:**
300
- - Use on unlimited personal projects
301
- - Use on unlimited commercial projects
302
- - One license per developer
303
- - No redistribution
304
- - No reselling
334
+
335
+ - Use on unlimited personal projects
336
+ - Use on unlimited commercial projects
337
+ - One license per developer
338
+ - No redistribution
339
+ - No reselling
305
340
 
306
341
  ---
307
342
 
@@ -319,6 +354,7 @@ This plugin pack is licensed for individual use. See LICENSE file for details.
319
354
  ## Version History
320
355
 
321
356
  **v1.0.0** (October 10, 2025)
357
+
322
358
  - Initial release
323
359
  - 25 plugins (19 commands + 6 agents)
324
360
  - Complete documentation
@@ -332,4 +368,4 @@ Start with the [Quick Start Guide](docs/QUICK_START.md) →
332
368
 
333
369
  **Need help?** Email mandy@intentsolutions.io
334
370
 
335
- **Have questions?** See [Troubleshooting](docs/000-docs/157-DR-FAQS-troubleshooting.md)
371
+ **Have questions?** See Troubleshooting →
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentsolutionsio/devops-automation-pack",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "description": "25 professional DevOps plugins for CI/CD, deployment, Docker, Kubernetes, and infrastructure automation. Save 20+ hours of manual work.",
5
5
  "keywords": [
6
6
  "devops",
@@ -1,13 +1,20 @@
1
1
  ---
2
2
  name: generating-conventional-commits
3
- description: |
4
- Execute generates conventional commit messages using AI. It analyzes code changes and suggests a commit message adhering to the conventional commits specification. Use this skill when you need help writing clear, standardized commit messages, especially a... Use when managing version control. Trigger with phrases like 'commit', 'branch', or 'git'.
3
+ description: 'Execute generates conventional commit messages using AI. It analyzes
4
+ code changes and suggests a commit message adhering to the conventional commits
5
+ specification. Use this skill when you need help writing clear, standardized commit
6
+ messages, especially a... Use when managing version control. Trigger with phrases
7
+ like ''commit'', ''branch'', or ''git''.
8
+
9
+ '
5
10
  allowed-tools: Read, Write, Edit, Grep, Glob, Bash(cmd:*)
6
11
  version: 1.0.0
7
12
  author: Jeremy Longshore <jeremy@intentsolutions.io>
8
13
  license: MIT
9
- compatible-with: claude-code, codex, openclaw
10
- tags: [packages, conventional-commits]
14
+ tags:
15
+ - packages
16
+ - conventional-commits
17
+ compatibility: Designed for Claude Code, also compatible with Codex and OpenClaw
11
18
  ---
12
19
  # Devops Automation Pack
13
20
 
@@ -26,6 +33,7 @@ Create well-formatted, informative commit messages that follow the conventional
26
33
  ## When to Use This Skill
27
34
 
28
35
  This skill activates when you need to:
36
+
29
37
  - Create a commit message after making code changes.
30
38
  - Ensure your commit messages follow the conventional commits standard.
31
39
  - Save time writing commit messages manually.
@@ -37,6 +45,7 @@ This skill activates when you need to:
37
45
  User request: "Generate a commit message for these changes."
38
46
 
39
47
  The skill will:
48
+
40
49
  1. Analyze the staged changes related to a new feature.
41
50
  2. Generate a commit message like `feat: Implement user authentication`.
42
51
 
@@ -45,6 +54,7 @@ The skill will:
45
54
  User request: "Create a commit for the bug fix."
46
55
 
47
56
  The skill will:
57
+
48
58
  1. Analyze the staged changes related to a bug fix.
49
59
  2. Generate a commit message like `fix: Resolve issue with incorrect password reset`.
50
60
 
@@ -83,4 +93,4 @@ The skill produces structured output relevant to the task.
83
93
  ## Resources
84
94
 
85
95
  - Project documentation
86
- - Related skills and commands
96
+ - Related skills and commands
@@ -1,4 +1,3 @@
1
1
  # References
2
2
 
3
3
  Bundled resources for devops-automation-pack skill
4
-
@@ -26,16 +26,11 @@ def get_git_diff(staged_only: bool = False) -> str:
26
26
  The git diff output as a string
27
27
  """
28
28
  try:
29
- cmd = ['git', 'diff']
29
+ cmd = ["git", "diff"]
30
30
  if staged_only:
31
- cmd.append('--cached')
31
+ cmd.append("--cached")
32
32
 
33
- result = subprocess.run(
34
- cmd,
35
- capture_output=True,
36
- text=True,
37
- check=False
38
- )
33
+ result = subprocess.run(cmd, capture_output=True, text=True, check=False)
39
34
 
40
35
  if result.returncode != 0:
41
36
  return ""
@@ -56,7 +51,7 @@ def get_diff_from_file(filepath: str) -> str:
56
51
  The diff content as a string
57
52
  """
58
53
  try:
59
- with open(filepath, 'r', encoding='utf-8') as f:
54
+ with open(filepath, "r", encoding="utf-8") as f:
60
55
  return f.read()
61
56
  except FileNotFoundError:
62
57
  print(f"Error: File not found: {filepath}", file=sys.stderr)
@@ -84,55 +79,55 @@ def analyze_diff(diff_content: str) -> Tuple[str, str, str]:
84
79
  has_fix = False
85
80
  files_changed = []
86
81
 
87
- lines = diff_content.split('\n')
82
+ lines = diff_content.split("\n")
88
83
 
89
84
  for line in lines:
90
85
  # Track which files are being modified
91
- if line.startswith('diff --git'):
86
+ if line.startswith("diff --git"):
92
87
  # Extract filename
93
- parts = line.split(' ')
88
+ parts = line.split(" ")
94
89
  if len(parts) >= 4:
95
90
  filepath = parts[3]
96
91
  files_changed.append(filepath)
97
92
 
98
93
  # Detect test files
99
- if 'test' in line.lower() or 'spec' in line.lower():
94
+ if "test" in line.lower() or "spec" in line.lower():
100
95
  has_tests = True
101
96
 
102
97
  # Detect documentation changes
103
- if any(x in line.lower() for x in ['.md', 'readme', 'docs/', 'documentation']):
98
+ if any(x in line.lower() for x in [".md", "readme", "docs/", "documentation"]):
104
99
  has_docs = True
105
100
 
106
101
  # Detect style changes (formatting, whitespace)
107
- if line.startswith('-') and line.lstrip('-').strip():
108
- if len(line.lstrip('-').strip()) < 20: # Short lines likely style
102
+ if line.startswith("-") and line.lstrip("-").strip():
103
+ if len(line.lstrip("-").strip()) < 20: # Short lines likely style
109
104
  has_style = True
110
105
 
111
106
  # Detect feature additions (new functions, classes)
112
- if any(x in line for x in ['def ', 'class ', 'function ', 'const ', 'let ']):
113
- if line.startswith('+'):
107
+ if any(x in line for x in ["def ", "class ", "function ", "const ", "let "]):
108
+ if line.startswith("+"):
114
109
  has_feature = True
115
110
 
116
111
  # Detect bug fixes (removing problematic code)
117
- if 'bug' in line.lower() or 'fix' in line.lower():
112
+ if "bug" in line.lower() or "fix" in line.lower():
118
113
  has_fix = True
119
114
 
120
115
  # Determine commit type
121
116
  if has_fix:
122
- commit_type = 'fix'
117
+ commit_type = "fix"
123
118
  elif has_feature:
124
- commit_type = 'feat'
119
+ commit_type = "feat"
125
120
  elif has_tests:
126
- commit_type = 'test'
121
+ commit_type = "test"
127
122
  elif has_docs:
128
- commit_type = 'docs'
123
+ commit_type = "docs"
129
124
  elif has_style:
130
- commit_type = 'style'
125
+ commit_type = "style"
131
126
  else:
132
- commit_type = 'refactor'
127
+ commit_type = "refactor"
133
128
 
134
129
  # Determine scope from files changed
135
- scope = ''
130
+ scope = ""
136
131
  if files_changed:
137
132
  # Extract directory or module name from first file
138
133
  first_file = files_changed[0]
@@ -141,17 +136,13 @@ def analyze_diff(diff_content: str) -> Tuple[str, str, str]:
141
136
  scope = parts[0]
142
137
 
143
138
  # Create description
144
- description = f"Update code based on diff analysis"
139
+ description = "Update code based on diff analysis"
145
140
 
146
141
  return commit_type, scope, description
147
142
 
148
143
 
149
144
  def generate_message(
150
- commit_type: str,
151
- scope: Optional[str],
152
- subject: str,
153
- body: Optional[str] = None,
154
- footer: Optional[str] = None
145
+ commit_type: str, scope: Optional[str], subject: str, body: Optional[str] = None, footer: Optional[str] = None
155
146
  ) -> str:
156
147
  """
157
148
  Generate a conventional commit message.
@@ -173,7 +164,7 @@ def generate_message(
173
164
  subject_line = f"{commit_type}: {subject}"
174
165
 
175
166
  # Ensure subject starts with lowercase
176
- parts = subject_line.split(': ', 1)
167
+ parts = subject_line.split(": ", 1)
177
168
  if len(parts) == 2:
178
169
  subject_line = f"{parts[0]}: {parts[1][0].lower()}{parts[1][1:] if len(parts[1]) > 1 else ''}"
179
170
 
@@ -210,52 +201,19 @@ Examples:
210
201
 
211
202
  # Output as JSON
212
203
  %(prog)s --staged --format json
213
- """
204
+ """,
214
205
  )
215
206
 
216
207
  input_group = parser.add_mutually_exclusive_group()
217
- input_group.add_argument(
218
- '--staged',
219
- action='store_true',
220
- help='Analyze staged changes (default)'
221
- )
222
- input_group.add_argument(
223
- '--unstaged',
224
- action='store_true',
225
- help='Analyze unstaged changes'
226
- )
227
- input_group.add_argument(
228
- '--file',
229
- type=str,
230
- help='Path to diff file'
231
- )
208
+ input_group.add_argument("--staged", action="store_true", help="Analyze staged changes (default)")
209
+ input_group.add_argument("--unstaged", action="store_true", help="Analyze unstaged changes")
210
+ input_group.add_argument("--file", type=str, help="Path to diff file")
232
211
 
233
- parser.add_argument(
234
- '--subject',
235
- type=str,
236
- help='Custom subject for the commit message'
237
- )
238
- parser.add_argument(
239
- '--body',
240
- type=str,
241
- help='Optional body text'
242
- )
243
- parser.add_argument(
244
- '--footer',
245
- type=str,
246
- help='Optional footer (e.g., Closes #123)'
247
- )
248
- parser.add_argument(
249
- '--format',
250
- choices=['text', 'json'],
251
- default='text',
252
- help='Output format (default: text)'
253
- )
254
- parser.add_argument(
255
- '-v', '--verbose',
256
- action='store_true',
257
- help='Enable verbose output'
258
- )
212
+ parser.add_argument("--subject", type=str, help="Custom subject for the commit message")
213
+ parser.add_argument("--body", type=str, help="Optional body text")
214
+ parser.add_argument("--footer", type=str, help="Optional footer (e.g., Closes #123)")
215
+ parser.add_argument("--format", choices=["text", "json"], default="text", help="Output format (default: text)")
216
+ parser.add_argument("-v", "--verbose", action="store_true", help="Enable verbose output")
259
217
 
260
218
  args = parser.parse_args()
261
219
 
@@ -278,23 +236,17 @@ Examples:
278
236
  subject = args.subject or "update code"
279
237
 
280
238
  # Generate message
281
- message = generate_message(
282
- commit_type,
283
- scope if scope else None,
284
- subject,
285
- args.body,
286
- args.footer
287
- )
239
+ message = generate_message(commit_type, scope if scope else None, subject, args.body, args.footer)
288
240
 
289
241
  # Output result
290
- if args.format == 'json':
242
+ if args.format == "json":
291
243
  output = {
292
- 'type': commit_type,
293
- 'scope': scope or None,
294
- 'subject': subject,
295
- 'body': args.body,
296
- 'footer': args.footer,
297
- 'message': message
244
+ "type": commit_type,
245
+ "scope": scope or None,
246
+ "subject": subject,
247
+ "body": args.body,
248
+ "footer": args.footer,
249
+ "message": message,
298
250
  }
299
251
  print(json.dumps(output, indent=2))
300
252
  else:
@@ -303,5 +255,5 @@ Examples:
303
255
  return 0
304
256
 
305
257
 
306
- if __name__ == '__main__':
258
+ if __name__ == "__main__":
307
259
  sys.exit(main())
@@ -11,7 +11,7 @@ import argparse
11
11
  import sys
12
12
  import subprocess
13
13
  import json
14
- from typing import Tuple, Dict, List, Optional
14
+ from typing import Tuple, Dict, Optional
15
15
 
16
16
 
17
17
  def get_git_diff(staged_only: bool = False, ref: Optional[str] = None) -> str:
@@ -26,18 +26,13 @@ def get_git_diff(staged_only: bool = False, ref: Optional[str] = None) -> str:
26
26
  The git diff output
27
27
  """
28
28
  try:
29
- cmd = ['git', 'diff']
29
+ cmd = ["git", "diff"]
30
30
  if staged_only:
31
- cmd.append('--cached')
31
+ cmd.append("--cached")
32
32
  if ref:
33
33
  cmd.append(ref)
34
34
 
35
- result = subprocess.run(
36
- cmd,
37
- capture_output=True,
38
- text=True,
39
- check=False
40
- )
35
+ result = subprocess.run(cmd, capture_output=True, text=True, check=False)
41
36
 
42
37
  return result.stdout if result.returncode == 0 else ""
43
38
  except FileNotFoundError:
@@ -47,7 +42,7 @@ def get_git_diff(staged_only: bool = False, ref: Optional[str] = None) -> str:
47
42
  def get_diff_from_file(filepath: str) -> str:
48
43
  """Read diff from a file."""
49
44
  try:
50
- with open(filepath, 'r', encoding='utf-8') as f:
45
+ with open(filepath, "r", encoding="utf-8") as f:
51
46
  return f.read()
52
47
  except FileNotFoundError:
53
48
  print(f"Error: File not found: {filepath}", file=sys.stderr)
@@ -68,77 +63,77 @@ def analyze_changes(diff_content: str) -> Dict[str, int]:
68
63
  Dictionary of change type counts
69
64
  """
70
65
  counts = {
71
- 'test_additions': 0,
72
- 'test_removals': 0,
73
- 'doc_changes': 0,
74
- 'style_changes': 0,
75
- 'feature_additions': 0,
76
- 'feature_removals': 0,
77
- 'performance_changes': 0,
78
- 'bug_references': 0,
79
- 'refactor_changes': 0,
80
- 'ci_changes': 0,
81
- 'dependency_changes': 0,
82
- 'total_additions': 0,
83
- 'total_removals': 0,
66
+ "test_additions": 0,
67
+ "test_removals": 0,
68
+ "doc_changes": 0,
69
+ "style_changes": 0,
70
+ "feature_additions": 0,
71
+ "feature_removals": 0,
72
+ "performance_changes": 0,
73
+ "bug_references": 0,
74
+ "refactor_changes": 0,
75
+ "ci_changes": 0,
76
+ "dependency_changes": 0,
77
+ "total_additions": 0,
78
+ "total_removals": 0,
84
79
  }
85
80
 
86
- lines = diff_content.split('\n')
87
- current_file = ''
81
+ lines = diff_content.split("\n")
82
+ current_file = ""
88
83
 
89
84
  for i, line in enumerate(lines):
90
85
  # Track current file
91
- if line.startswith('diff --git'):
92
- parts = line.split(' ')
86
+ if line.startswith("diff --git"):
87
+ parts = line.split(" ")
93
88
  if len(parts) >= 4:
94
89
  current_file = parts[3]
95
90
 
96
91
  # Count additions and removals
97
- if line.startswith('+') and not line.startswith('+++'):
98
- counts['total_additions'] += 1
92
+ if line.startswith("+") and not line.startswith("+++"):
93
+ counts["total_additions"] += 1
99
94
 
100
95
  # Analyze added lines
101
96
  content = line[1:]
102
- if 'test' in current_file.lower() or 'spec' in current_file.lower():
103
- counts['test_additions'] += 1
104
- elif 'package.json' in current_file or 'requirements.txt' in current_file:
105
- counts['dependency_changes'] += 1
106
- elif any(x in content.lower() for x in ['def ', 'class ', 'function ', 'const ', 'async ']):
107
- counts['feature_additions'] += 1
108
- elif any(x in content.lower() for x in ['optimize', 'performance', 'cache', 'lazy']):
109
- counts['performance_changes'] += 1
110
-
111
- elif line.startswith('-') and not line.startswith('---'):
112
- counts['total_removals'] += 1
97
+ if "test" in current_file.lower() or "spec" in current_file.lower():
98
+ counts["test_additions"] += 1
99
+ elif "package.json" in current_file or "requirements.txt" in current_file:
100
+ counts["dependency_changes"] += 1
101
+ elif any(x in content.lower() for x in ["def ", "class ", "function ", "const ", "async "]):
102
+ counts["feature_additions"] += 1
103
+ elif any(x in content.lower() for x in ["optimize", "performance", "cache", "lazy"]):
104
+ counts["performance_changes"] += 1
105
+
106
+ elif line.startswith("-") and not line.startswith("---"):
107
+ counts["total_removals"] += 1
113
108
 
114
109
  content = line[1:]
115
- if 'test' in current_file.lower() or 'spec' in current_file.lower():
116
- counts['test_removals'] += 1
117
- elif any(x in content.lower() for x in ['def ', 'class ', 'function ', 'const ']):
118
- counts['feature_removals'] += 1
110
+ if "test" in current_file.lower() or "spec" in current_file.lower():
111
+ counts["test_removals"] += 1
112
+ elif any(x in content.lower() for x in ["def ", "class ", "function ", "const "]):
113
+ counts["feature_removals"] += 1
119
114
 
120
115
  # Detect specific change patterns
121
- if any(x in line.lower() for x in ['.md', 'readme', 'docs/', 'documentation']):
122
- counts['doc_changes'] += 1
116
+ if any(x in line.lower() for x in [".md", "readme", "docs/", "documentation"]):
117
+ counts["doc_changes"] += 1
123
118
 
124
119
  # Style changes (mostly whitespace/formatting)
125
- if line.startswith('+') or line.startswith('-'):
126
- if len(line.lstrip('+-').strip()) < 10: # Short lines
127
- counts['style_changes'] += 1
120
+ if line.startswith("+") or line.startswith("-"):
121
+ if len(line.lstrip("+-").strip()) < 10: # Short lines
122
+ counts["style_changes"] += 1
128
123
 
129
124
  # Bug/fix references
130
- if any(x in line.lower() for x in ['fix', 'bug', 'issue', 'closes #', 'fixes #']):
131
- counts['bug_references'] += 1
125
+ if any(x in line.lower() for x in ["fix", "bug", "issue", "closes #", "fixes #"]):
126
+ counts["bug_references"] += 1
132
127
 
133
128
  # CI/CD changes
134
- if '.github/workflows' in current_file or '.gitlab-ci' in current_file or 'Jenkinsfile' in current_file:
135
- counts['ci_changes'] += 1
136
- if 'pipeline' in line.lower() or 'workflow' in line.lower():
137
- counts['ci_changes'] += 1
129
+ if ".github/workflows" in current_file or ".gitlab-ci" in current_file or "Jenkinsfile" in current_file:
130
+ counts["ci_changes"] += 1
131
+ if "pipeline" in line.lower() or "workflow" in line.lower():
132
+ counts["ci_changes"] += 1
138
133
 
139
134
  # Refactoring (renaming, restructuring without functional change)
140
- if any(x in line.lower() for x in ['rename', 'reorganize', 'refactor', 'restructure']):
141
- counts['refactor_changes'] += 1
135
+ if any(x in line.lower() for x in ["rename", "reorganize", "refactor", "restructure"]):
136
+ counts["refactor_changes"] += 1
142
137
 
143
138
  return counts
144
139
 
@@ -157,74 +152,74 @@ def suggest_commit_type(diff_content: str) -> Tuple[str, float, str]:
157
152
 
158
153
  # Scoring system: higher score = more confident
159
154
  scores = {
160
- 'feat': 0.0,
161
- 'fix': 0.0,
162
- 'docs': 0.0,
163
- 'style': 0.0,
164
- 'refactor': 0.0,
165
- 'perf': 0.0,
166
- 'test': 0.0,
167
- 'chore': 0.0,
168
- 'ci': 0.0,
169
- 'build': 0.0,
155
+ "feat": 0.0,
156
+ "fix": 0.0,
157
+ "docs": 0.0,
158
+ "style": 0.0,
159
+ "refactor": 0.0,
160
+ "perf": 0.0,
161
+ "test": 0.0,
162
+ "chore": 0.0,
163
+ "ci": 0.0,
164
+ "build": 0.0,
170
165
  }
171
166
 
172
167
  reasoning = []
173
168
 
174
169
  # Test changes
175
- if changes['test_additions'] > 0:
176
- scores['test'] += changes['test_additions'] * 2
170
+ if changes["test_additions"] > 0:
171
+ scores["test"] += changes["test_additions"] * 2
177
172
  reasoning.append(f"Added {changes['test_additions']} test lines")
178
173
 
179
174
  # Documentation changes
180
- if changes['doc_changes'] > 0:
181
- scores['docs'] += changes['doc_changes'] * 1.5
175
+ if changes["doc_changes"] > 0:
176
+ scores["docs"] += changes["doc_changes"] * 1.5
182
177
  reasoning.append(f"Modified {changes['doc_changes']} documentation lines")
183
178
 
184
179
  # Bug fixes
185
- if changes['bug_references'] > 0:
186
- scores['fix'] += changes['bug_references'] * 3
180
+ if changes["bug_references"] > 0:
181
+ scores["fix"] += changes["bug_references"] * 3
187
182
  reasoning.append(f"Found {changes['bug_references']} bug/fix references")
188
183
 
189
184
  # Performance improvements
190
- if changes['performance_changes'] > 0:
191
- scores['perf'] += changes['performance_changes'] * 2
185
+ if changes["performance_changes"] > 0:
186
+ scores["perf"] += changes["performance_changes"] * 2
192
187
  reasoning.append(f"Found {changes['performance_changes']} performance improvements")
193
188
 
194
189
  # CI/CD changes
195
- if changes['ci_changes'] > 0:
196
- scores['ci'] += changes['ci_changes'] * 2
190
+ if changes["ci_changes"] > 0:
191
+ scores["ci"] += changes["ci_changes"] * 2
197
192
  reasoning.append(f"Modified CI/CD files ({changes['ci_changes']} lines)")
198
193
 
199
194
  # Dependency changes
200
- if changes['dependency_changes'] > 0:
201
- scores['build'] += changes['dependency_changes'] * 1.5
195
+ if changes["dependency_changes"] > 0:
196
+ scores["build"] += changes["dependency_changes"] * 1.5
202
197
  reasoning.append(f"Modified dependencies ({changes['dependency_changes']} changes)")
203
198
 
204
199
  # Feature additions
205
- if changes['feature_additions'] > changes['feature_removals']:
206
- feature_ratio = changes['feature_additions'] / max(1, changes['feature_removals'])
207
- scores['feat'] += feature_ratio * 2
200
+ if changes["feature_additions"] > changes["feature_removals"]:
201
+ feature_ratio = changes["feature_additions"] / max(1, changes["feature_removals"])
202
+ scores["feat"] += feature_ratio * 2
208
203
  reasoning.append(f"Added {changes['feature_additions']} feature lines")
209
204
 
210
205
  # Refactoring
211
- if changes['refactor_changes'] > 0:
212
- scores['refactor'] += changes['refactor_changes'] * 1.5
206
+ if changes["refactor_changes"] > 0:
207
+ scores["refactor"] += changes["refactor_changes"] * 1.5
213
208
  reasoning.append(f"Found {changes['refactor_changes']} refactoring indicators")
214
209
 
215
210
  # Style changes
216
- if changes['style_changes'] > changes['total_additions'] * 0.3:
217
- scores['style'] += changes['style_changes']
211
+ if changes["style_changes"] > changes["total_additions"] * 0.3:
212
+ scores["style"] += changes["style_changes"]
218
213
  reasoning.append(f"Mostly style changes ({changes['style_changes']} lines)")
219
214
 
220
215
  # Fallback: determine based on change volume
221
- total_changes = changes['total_additions'] + changes['total_removals']
216
+ total_changes = changes["total_additions"] + changes["total_removals"]
222
217
  if total_changes == 0:
223
- return 'chore', 0.5, "No meaningful changes detected"
218
+ return "chore", 0.5, "No meaningful changes detected"
224
219
 
225
220
  # If nothing scored highly, use refactor as catch-all
226
221
  if max(scores.values()) == 0:
227
- scores['refactor'] = 1.0
222
+ scores["refactor"] = 1.0
228
223
  reasoning.append("General code changes without specific category")
229
224
 
230
225
  # Find the highest scoring type
@@ -237,27 +232,21 @@ def suggest_commit_type(diff_content: str) -> Tuple[str, float, str]:
237
232
  return suggested, confidence, reasoning_str
238
233
 
239
234
 
240
- def format_output(
241
- suggested_type: str,
242
- confidence: float,
243
- reasoning: str,
244
- format_type: str = 'text'
245
- ) -> str:
235
+ def format_output(suggested_type: str, confidence: float, reasoning: str, format_type: str = "text") -> str:
246
236
  """Format the output based on requested format."""
247
- if format_type == 'json':
248
- return json.dumps({
249
- 'type': suggested_type,
250
- 'confidence': round(confidence, 2),
251
- 'confidence_percent': f"{confidence * 100:.1f}%",
252
- 'reasoning': reasoning
253
- }, indent=2)
237
+ if format_type == "json":
238
+ return json.dumps(
239
+ {
240
+ "type": suggested_type,
241
+ "confidence": round(confidence, 2),
242
+ "confidence_percent": f"{confidence * 100:.1f}%",
243
+ "reasoning": reasoning,
244
+ },
245
+ indent=2,
246
+ )
254
247
  else:
255
248
  confidence_percent = f"{confidence * 100:.1f}%"
256
- return (
257
- f"Suggested commit type: {suggested_type}\n"
258
- f"Confidence: {confidence_percent}\n"
259
- f"Reasoning: {reasoning}"
260
- )
249
+ return f"Suggested commit type: {suggested_type}\nConfidence: {confidence_percent}\nReasoning: {reasoning}"
261
250
 
262
251
 
263
252
  def main():
@@ -284,42 +273,17 @@ Examples:
284
273
 
285
274
  # Verbose output with detailed analysis
286
275
  %(prog)s --staged --verbose
287
- """
276
+ """,
288
277
  )
289
278
 
290
279
  input_group = parser.add_mutually_exclusive_group()
291
- input_group.add_argument(
292
- '--staged',
293
- action='store_true',
294
- help='Analyze staged changes (default)'
295
- )
296
- input_group.add_argument(
297
- '--unstaged',
298
- action='store_true',
299
- help='Analyze unstaged changes'
300
- )
301
- input_group.add_argument(
302
- '--file',
303
- type=str,
304
- help='Path to diff file to analyze'
305
- )
280
+ input_group.add_argument("--staged", action="store_true", help="Analyze staged changes (default)")
281
+ input_group.add_argument("--unstaged", action="store_true", help="Analyze unstaged changes")
282
+ input_group.add_argument("--file", type=str, help="Path to diff file to analyze")
306
283
 
307
- parser.add_argument(
308
- '--ref',
309
- type=str,
310
- help='Git reference to compare against (e.g., main, HEAD~1)'
311
- )
312
- parser.add_argument(
313
- '--format',
314
- choices=['text', 'json'],
315
- default='text',
316
- help='Output format (default: text)'
317
- )
318
- parser.add_argument(
319
- '-v', '--verbose',
320
- action='store_true',
321
- help='Enable verbose output with detailed analysis'
322
- )
284
+ parser.add_argument("--ref", type=str, help="Git reference to compare against (e.g., main, HEAD~1)")
285
+ parser.add_argument("--format", choices=["text", "json"], default="text", help="Output format (default: text)")
286
+ parser.add_argument("-v", "--verbose", action="store_true", help="Enable verbose output with detailed analysis")
323
287
 
324
288
  args = parser.parse_args()
325
289
 
@@ -345,5 +309,5 @@ Examples:
345
309
  return 0
346
310
 
347
311
 
348
- if __name__ == '__main__':
312
+ if __name__ == "__main__":
349
313
  sys.exit(main())
@@ -33,13 +33,13 @@ def validate_conventional_commit(message: str) -> Tuple[bool, str]:
33
33
  return False, "Commit message cannot be empty"
34
34
 
35
35
  message = message.strip()
36
- lines = message.split('\n')
36
+ lines = message.split("\n")
37
37
  subject = lines[0]
38
38
 
39
39
  # Validate subject line format
40
40
  # Pattern: type(scope)?: subject or type: subject
41
41
  # Where type is required, scope is optional, and subject is required
42
- pattern = r'^(feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert)(\(.+\))?!?: .{1,}$'
42
+ pattern = r"^(feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert)(\(.+\))?!?: .{1,}$"
43
43
 
44
44
  if not re.match(pattern, subject):
45
45
  return False, (
@@ -52,17 +52,16 @@ def validate_conventional_commit(message: str) -> Tuple[bool, str]:
52
52
  # Validate subject line length (recommended max 50 chars)
53
53
  if len(subject) > 72:
54
54
  return False, (
55
- f"Subject line too long ({len(subject)} chars). "
56
- "Recommended maximum is 50 characters, hard limit is 72"
55
+ f"Subject line too long ({len(subject)} chars). Recommended maximum is 50 characters, hard limit is 72"
57
56
  )
58
57
 
59
58
  # Validate subject doesn't end with period
60
- if subject.endswith('.'):
59
+ if subject.endswith("."):
61
60
  return False, "Subject line should not end with a period"
62
61
 
63
62
  # If there are multiple lines, validate blank line after subject
64
63
  if len(lines) > 1:
65
- if lines[1].strip() != '':
64
+ if lines[1].strip() != "":
66
65
  return False, "Expected blank line between subject and body"
67
66
 
68
67
  return True, ""
@@ -83,34 +82,22 @@ Examples:
83
82
 
84
83
  # Validate with verbose output
85
84
  %(prog)s --message "fix: resolve bug" --verbose
86
- """
85
+ """,
87
86
  )
88
87
 
89
88
  # Create mutually exclusive group for input source
90
89
  input_group = parser.add_mutually_exclusive_group(required=True)
91
- input_group.add_argument(
92
- '-m', '--message',
93
- type=str,
94
- help='Commit message to validate'
95
- )
96
- input_group.add_argument(
97
- '-f', '--file',
98
- type=str,
99
- help='Path to file containing commit message'
100
- )
90
+ input_group.add_argument("-m", "--message", type=str, help="Commit message to validate")
91
+ input_group.add_argument("-f", "--file", type=str, help="Path to file containing commit message")
101
92
 
102
- parser.add_argument(
103
- '-v', '--verbose',
104
- action='store_true',
105
- help='Enable verbose output'
106
- )
93
+ parser.add_argument("-v", "--verbose", action="store_true", help="Enable verbose output")
107
94
 
108
95
  args = parser.parse_args()
109
96
 
110
97
  # Get the commit message
111
98
  if args.file:
112
99
  try:
113
- with open(args.file, 'r', encoding='utf-8') as f:
100
+ with open(args.file, "r", encoding="utf-8") as f:
114
101
  message = f.read()
115
102
  except FileNotFoundError:
116
103
  print(f"Error: File not found: {args.file}", file=sys.stderr)
@@ -129,10 +116,10 @@ Examples:
129
116
  print("✓ Commit message is valid")
130
117
  return 0
131
118
  else:
132
- print(f"✗ Validation failed:", file=sys.stderr)
119
+ print("✗ Validation failed:", file=sys.stderr)
133
120
  print(error_msg, file=sys.stderr)
134
121
  return 1
135
122
 
136
123
 
137
- if __name__ == '__main__':
124
+ if __name__ == "__main__":
138
125
  sys.exit(main())