pumuki-ast-hooks 5.5.32 → 5.5.36

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
@@ -43,6 +43,7 @@
43
43
  - [Complete Architecture and Workflow](#complete-architecture-and-workflow)
44
44
  - [What is it?](#what-is-it)
45
45
  - [What problems does it solve?](#what-problems-does-it-solve)
46
+ - [Git Flow Automation](#git-flow-automation)
46
47
  - [Features](#features)
47
48
  - [Use Cases](#use-cases)
48
49
  - [Installation](#installation)
@@ -510,6 +511,70 @@ See [HOW_IT_WORKS.md](./docs/HOW_IT_WORKS.md#architecture-detection-by-platform)
510
511
  - **MEDIUM/LOW**: Generate warnings in reports
511
512
  - **Configurable**: Adjust levels according to your project
512
513
 
514
+ ### šŸ”„ Git Flow Automation
515
+
516
+ **Complete Git Flow workflow automation** with automatic enforcement:
517
+
518
+ - **Protected Branch Blocking**: Automatically blocks commits and pushes to `main`, `master`, and `develop` branches
519
+ - **Auto-Create Feature Branch**: Automatically creates feature branch when on `develop`/`main` based on changes
520
+ - **Smart Branch Naming**: Generates branch names based on file types (feature/, fix/, chore/, docs/, etc.)
521
+ - **Feature Cycle**: Execute complete Git Flow with `npm run ast:gitflow`
522
+ - **Release Cycle**: Create release PR from develop to main with `npm run ast:release`
523
+ - **Automatic PR Creation**: Creates Pull Requests using GitHub CLI (requires `gh`)
524
+ - **Optional Auto-Merge**: Automatically merges PRs with `--auto-merge` flag
525
+ - **Branch Cleanup**: Automatically deletes merged branches (local and remote)
526
+ - **Branch Synchronization**: Syncs `develop` and `main` with remote
527
+
528
+ **Feature Development Workflow:**
529
+ ```bash
530
+ # Work on develop/main (changes detected)
531
+
532
+ # Run complete cycle (auto-creates feature branch)
533
+ npm run ast:gitflow -- --auto-merge
534
+ ```
535
+
536
+ **Release Workflow:**
537
+ ```bash
538
+ # Switch to develop branch
539
+ git checkout develop
540
+
541
+ # Create release PR (develop → main)
542
+ npm run ast:release -- --auto-merge
543
+
544
+ # Or with version tag
545
+ npm run ast:release -- --tag 5.5.35 --auto-merge
546
+ ```
547
+
548
+ **Branch Naming Logic:**
549
+ - `fix/` - Files containing "fix", "bug", "error"
550
+ - `test/` - Test files or "spec" files
551
+ - `docs/` - Documentation files (README, CHANGELOG)
552
+ - `refactor/` - Files containing "refactor", "cleanup"
553
+ - `ci/` - CI/CD files (workflow, github actions)
554
+ - `chore/` - Config files, package.json
555
+ - `feature/` - Default for other changes
556
+
557
+ **Feature Cycle Options:**
558
+ ```bash
559
+ npm run ast:gitflow # Basic cycle (auto-creates branch if needed)
560
+ npm run ast:gitflow -- -m "feat: new feature" # Custom commit message
561
+ npm run ast:gitflow -- --auto-merge # Auto-merge PR
562
+ npm run ast:gitflow -- --skip-cleanup # Skip branch cleanup
563
+ npm run ast:gitflow -- --skip-sync # Skip branch sync
564
+ ```
565
+
566
+ **Release Cycle Options:**
567
+ ```bash
568
+ npm run ast:release # Create release PR
569
+ npm run ast:release -- --auto-merge # Auto-merge release PR
570
+ npm run ast:release -- --tag 5.5.35 # Create and push git tag
571
+ npm run ast:release -- --pr-title "Release v5.5.35" # Custom PR title
572
+ ```
573
+
574
+ **Hooks:**
575
+ - `pre-commit`: Blocks commits on protected branches + AST analysis
576
+ - `pre-push`: Blocks push to protected branches + validates naming
577
+
513
578
  ---
514
579
 
515
580
  ## Use Cases
@@ -568,6 +633,21 @@ hook-watch
568
633
  hook-status
569
634
  ```
570
635
 
636
+ ### 6. Git Flow Automation
637
+
638
+ ```bash
639
+ # Complete Git Flow cycle
640
+ npm run ast:gitflow
641
+
642
+ # With options
643
+ npm run ast:gitflow -- -m "feat: new feature" --auto-merge
644
+
645
+ # Using MCP tools (from IDE)
646
+ mcp0_auto_complete_gitflow
647
+ mcp0_sync_branches
648
+ mcp0_cleanup_stale_branches
649
+ ```
650
+
571
651
  ---
572
652
 
573
653
  ## Quick Start
@@ -1023,6 +1103,9 @@ hook-predict # Violation prediction
1023
1103
  hook-playbook # Execute playbook
1024
1104
 
1025
1105
  # Git Flow
1106
+ npm run ast:gitflow # Complete Git Flow cycle
1107
+ npm run ast:gitflow -- -m "msg" # With commit message
1108
+ npm run ast:gitflow -- --auto-merge # Auto-merge PR
1026
1109
  gitflow check # Verify Git Flow
1027
1110
  gitflow status # Current status
1028
1111
  gitflow workflow # View full workflow
@@ -1033,6 +1116,7 @@ gitflow workflow # View full workflow
1033
1116
  ```bash
1034
1117
  npm run audit # Full analysis
1035
1118
  npm run install-hooks # Install hooks
1119
+ npm run ast:gitflow # Complete Git Flow cycle
1036
1120
  npm test # Run tests
1037
1121
  npm run lint # Linter
1038
1122
  npm run typecheck # Type checking
@@ -1065,6 +1149,83 @@ For coding standards, see [CODE_STANDARDS.md](./docs/CODE_STANDARDS.md).
1065
1149
 
1066
1150
  ## šŸ“ Recent Changes
1067
1151
 
1152
+ ### Version 5.5.35 (2026-01-04)
1153
+
1154
+ **✨ New Features:**
1155
+ - **Git Flow Release Cycle**: New `npm run ast:release` command for creating release PRs (develop → main)
1156
+ - **Release Automation**: Automatic PR creation from develop to main with optional auto-merge
1157
+ - **Git Tag Support**: Optional git tag creation with `--tag` flag
1158
+
1159
+ **Technical Details:**
1160
+ - Separation of concerns: `ast:gitflow` for features, `ast:release` for releases
1161
+ - Release cycle steps:
1162
+ 1. Validates branch (must be develop)
1163
+ 2. Syncs develop with origin
1164
+ 3. Syncs main with origin
1165
+ 4. Creates PR: develop → main
1166
+ 5. Optionally auto-merges PR
1167
+ 6. Optionally creates git tag
1168
+ - Follows Git Flow best practices with clear separation between development and release workflows
1169
+
1170
+ ---
1171
+
1172
+ ### Version 5.5.34 (2026-01-04)
1173
+
1174
+ **✨ New Features:**
1175
+ - **Git Flow Auto-Create Branch**: Automatically creates feature branch when on `develop`/`main` based on changes
1176
+ - **Smart Branch Naming**: Generates branch names based on file types (feature/, fix/, chore/, docs/, etc.)
1177
+
1178
+ **Technical Details:**
1179
+ - When running `npm run ast:gitflow` on protected branch, script now:
1180
+ - Analyzes changed files to infer branch type
1181
+ - Generates descriptive branch name with timestamp
1182
+ - Creates and switches to new feature branch automatically
1183
+ - Continues with complete Git Flow cycle
1184
+ - Branch naming logic:
1185
+ - `fix/` - Files containing "fix", "bug", "error"
1186
+ - `test/` - Test files or "spec" files
1187
+ - `docs/` - Documentation files (README, CHANGELOG)
1188
+ - `refactor/` - Files containing "refactor", "cleanup"
1189
+ - `ci/` - CI/CD files (workflow, github actions)
1190
+ - `chore/` - Config files, package.json
1191
+ - `feature/` - Default for other changes
1192
+
1193
+ ---
1194
+
1195
+ ### Version 5.5.33 (2026-01-04)
1196
+
1197
+ **šŸ› Bug Fixes:**
1198
+ - **iOS Security Analyzer**: Fixed false positive in `ios.security.missing_ssl_pinning` rule
1199
+ - Now recognizes SSL pinning implementations using `URLSessionDelegate` + `URLAuthenticationChallenge`
1200
+ - **iOS Enterprise Analyzer**: Fixed same false positive in `ios.networking.missing_ssl_pinning` rule
1201
+
1202
+ **Technical Details:**
1203
+ - Previous implementation only checked for `ServerTrustPolicy` or `pinning` keywords
1204
+ - Files implementing SSL pinning with `URLSessionDelegate` were incorrectly flagged
1205
+ - Added detection for `URLSessionDelegate` + `URLAuthenticationChallenge` pattern
1206
+ - This fixes false positives on files like `SSLPinningDelegate.swift` that properly implement SSL pinning
1207
+
1208
+ ---
1209
+
1210
+ ### Version 5.5.32 (2026-01-04)
1211
+
1212
+ **✨ New Features:**
1213
+ - **Git Flow Automation**: Complete Git Flow cycle with `npm run ast:gitflow`
1214
+ - **Protected Branch Blocking**: Pre-commit hook now blocks commits on `main`, `master`, and `develop`
1215
+ - **Pre-Push Hook**: Automatically installed, blocks push to protected branches and validates naming conventions
1216
+ - **Auto PR Creation**: Creates Pull Requests using GitHub CLI (`gh`)
1217
+ - **Auto-Merge Support**: Optional auto-merge with `--auto-merge` flag
1218
+ - **Branch Cleanup**: Automatically deletes merged branches (local + remote)
1219
+ - **Branch Synchronization**: Syncs `develop` and `main` with remote
1220
+
1221
+ **šŸ”§ Improvements:**
1222
+ - Added `ast-gitflow` binary to package.json
1223
+ - Added `ast:gitflow` npm script
1224
+ - Updated GitEnvironmentService to install pre-push hook
1225
+ - Updated pre-commit hook to validate protected branches
1226
+
1227
+ ---
1228
+
1068
1229
  ### Version 5.5.25 (2026-01-04)
1069
1230
 
1070
1231
  **⚔ Performance Fix:**
@@ -1174,7 +1335,7 @@ Developed by **Pumuki TeamĀ®**
1174
1335
 
1175
1336
  - **Author**: Juan Carlos Merlos AlbarracĆ­n (Senior Software Architect - AI-Driven Development)
1176
1337
  - **Contact**: freelancemerlos@gmail.com
1177
- - **Version**: 5.5.16
1338
+ - **Version**: 5.5.35
1178
1339
  - **Repository**: [GitHub](https://github.com/SwiftEnProfundidad/ast-intelligence-hooks)
1179
1340
 
1180
1341
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki-ast-hooks",
3
- "version": "5.5.32",
3
+ "version": "5.5.36",
4
4
  "description": "Enterprise-grade AST Intelligence System with multi-platform support (iOS, Android, Backend, Frontend) and Feature-First + DDD + Clean Architecture enforcement. Includes dynamic violations API for intelligent querying.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -30,6 +30,8 @@
30
30
  "gitflow:status": "bash bin/gitflow status",
31
31
  "gitflow:workflow": "bash bin/gitflow workflow",
32
32
  "gitflow:reset": "bash bin/gitflow reset",
33
+ "ast:gitflow": "node scripts/hooks-system/bin/gitflow-cycle.js",
34
+ "ast:release": "node scripts/hooks-system/bin/gitflow-release.js",
33
35
  "violations": "node bin/violations",
34
36
  "violations:list": "node bin/violations list",
35
37
  "violations:show": "node bin/violations show",
@@ -42,7 +44,13 @@
42
44
  "build:ts": "tsc --noEmit",
43
45
  "typecheck": "tsc --noEmit",
44
46
  "ast:refresh": "node scripts/hooks-system/bin/update-evidence.sh",
45
- "ast:audit": "node scripts/hooks-system/infrastructure/ast/ast-intelligence.js"
47
+ "ast:audit": "node scripts/hooks-system/infrastructure/ast/ast-intelligence.js",
48
+ "ast:guard:start": "bash scripts/hooks-system/bin/evidence-guard start",
49
+ "ast:guard:stop": "bash scripts/hooks-system/bin/evidence-guard stop",
50
+ "ast:guard:restart": "bash scripts/hooks-system/bin/evidence-guard restart",
51
+ "ast:guard:status": "bash scripts/hooks-system/bin/evidence-guard status",
52
+ "ast:guard:logs": "bash scripts/hooks-system/bin/evidence-guard logs",
53
+ "ast:check-version": "node scripts/hooks-system/bin/check-version.js"
46
54
  },
47
55
  "keywords": [
48
56
  "ast",
@@ -116,7 +116,6 @@ class McpConfigurator {
116
116
  const mcpConfig = {
117
117
  mcpServers: {
118
118
  [serverId]: {
119
- type: 'stdio',
120
119
  command: nodePath,
121
120
  args: [
122
121
  entrypoint
@@ -104,8 +104,8 @@ function getInstalledVersion() {
104
104
 
105
105
  function getLatestVersion() {
106
106
  try {
107
- // Try npm view
108
- const output = execSync('npm view @pumuki/ast-intelligence-hooks version', {
107
+ // Try npm view (correct package name: pumuki-ast-hooks)
108
+ const output = execSync('npm view pumuki-ast-hooks version', {
109
109
  encoding: 'utf-8',
110
110
  stdio: ['ignore', 'pipe', 'ignore'],
111
111
  timeout: 5000
@@ -102,19 +102,91 @@ function prExists(branch) {
102
102
  // MAIN CYCLE STEPS
103
103
  // ════════════════════════════════════════════════════════════════
104
104
 
105
+ function getChangedFiles() {
106
+ try {
107
+ const output = execSilent('git status --porcelain');
108
+ if (!output) return [];
109
+ return output.split('\n')
110
+ .filter(line => line.trim())
111
+ .map(line => {
112
+ const parts = line.trim().split(/\s+/);
113
+ return parts[1] || parts[0];
114
+ });
115
+ } catch (error) {
116
+ return [];
117
+ }
118
+ }
119
+
120
+ function inferBranchType(changedFiles) {
121
+ const fileTypes = changedFiles.join(' ').toLowerCase();
122
+
123
+ if (fileTypes.includes('fix') || fileTypes.includes('bug') || fileTypes.includes('error')) {
124
+ return 'fix';
125
+ }
126
+ if (fileTypes.includes('test') || fileTypes.includes('spec')) {
127
+ return 'test';
128
+ }
129
+ if (fileTypes.includes('doc') || fileTypes.includes('readme') || fileTypes.includes('changelog')) {
130
+ return 'docs';
131
+ }
132
+ if (fileTypes.includes('refactor') || fileTypes.includes('cleanup')) {
133
+ return 'refactor';
134
+ }
135
+ if (fileTypes.includes('ci') || fileTypes.includes('workflow') || fileTypes.includes('github')) {
136
+ return 'ci';
137
+ }
138
+ if (fileTypes.includes('chore') || fileTypes.includes('config') || fileTypes.includes('package')) {
139
+ return 'chore';
140
+ }
141
+ return 'feature';
142
+ }
143
+
144
+ function generateBranchName(changedFiles) {
145
+ const type = inferBranchType(changedFiles);
146
+
147
+ // Extract keywords from file paths
148
+ const keywords = changedFiles
149
+ .map(f => f.replace(/.*\//, '').replace(/\.[^.]+$/, ''))
150
+ .filter(f => f.length > 2)
151
+ .slice(0, 3);
152
+
153
+ let suffix = keywords.join('-') || 'update';
154
+ suffix = suffix.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
155
+
156
+ // Add timestamp to ensure uniqueness
157
+ const timestamp = Date.now().toString(36);
158
+ return `${type}/${suffix}-${timestamp}`;
159
+ }
160
+
105
161
  function step1_validateBranch() {
106
162
  log(COLORS.cyan, '\n═══════════════════════════════════════════════════════════════');
107
163
  log(COLORS.cyan, 'šŸ“ Step 1: Validate Branch');
108
164
  log(COLORS.cyan, '═══════════════════════════════════════════════════════════════\n');
109
165
 
110
- const branch = getCurrentBranch();
166
+ let branch = getCurrentBranch();
111
167
  log(COLORS.blue, `Current branch: ${branch}`);
112
168
 
113
169
  if (isProtectedBranch(branch)) {
114
- log(COLORS.red, `\nāŒ Cannot run Git Flow cycle on protected branch: ${branch}`);
115
- log(COLORS.yellow, '\nCreate a feature branch first:');
116
- log(COLORS.yellow, ' git checkout -b feature/my-feature');
117
- process.exit(1);
170
+ log(COLORS.yellow, `\nāš ļø On protected branch: ${branch}`);
171
+ log(COLORS.cyan, 'šŸ”„ Automatically creating feature branch...');
172
+
173
+ const changedFiles = getChangedFiles();
174
+ if (changedFiles.length === 0) {
175
+ log(COLORS.red, '\nāŒ No changes detected to create a feature branch');
176
+ process.exit(1);
177
+ }
178
+
179
+ const newBranch = generateBranchName(changedFiles);
180
+ log(COLORS.blue, `Creating branch: ${newBranch}`);
181
+
182
+ try {
183
+ exec(`git checkout -b ${newBranch}`);
184
+ branch = newBranch;
185
+ log(COLORS.green, `āœ… Created and switched to: ${newBranch}`);
186
+ } catch (error) {
187
+ log(COLORS.red, `\nāŒ Failed to create branch: ${error.message}`);
188
+ process.exit(1);
189
+ }
118
190
  }
119
191
 
120
192
  if (!isValidFeatureBranch(branch)) {
@@ -0,0 +1,348 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Git Flow Release Cycle - Release automation
4
+ *
5
+ * Executes the release cycle (develop → main):
6
+ * 1. Validates current branch (must be develop)
7
+ * 2. Syncs develop with origin
8
+ * 3. Creates PR from develop to main
9
+ * 4. Optionally merges PR
10
+ * 5. Syncs main with origin
11
+ */
12
+
13
+ const { execSync } = require('child_process');
14
+
15
+ const COLORS = {
16
+ reset: '\x1b[0m',
17
+ red: '\x1b[31m',
18
+ green: '\x1b[32m',
19
+ yellow: '\x1b[33m',
20
+ blue: '\x1b[34m',
21
+ cyan: '\x1b[36m',
22
+ magenta: '\x1b[35m'
23
+ };
24
+
25
+ function log(color, message) {
26
+ console.log(`${color}${message}${COLORS.reset}`);
27
+ }
28
+
29
+ function exec(cmd, options = {}) {
30
+ try {
31
+ return execSync(cmd, {
32
+ encoding: 'utf-8',
33
+ stdio: options.silent ? 'pipe' : 'inherit',
34
+ ...options
35
+ });
36
+ } catch (error) {
37
+ if (options.ignoreError) {
38
+ return null;
39
+ }
40
+ throw error;
41
+ }
42
+ }
43
+
44
+ function execSilent(cmd) {
45
+ try {
46
+ return execSync(cmd, { encoding: 'utf-8', stdio: 'pipe' }).trim();
47
+ } catch (error) {
48
+ return null;
49
+ }
50
+ }
51
+
52
+ function getCurrentBranch() {
53
+ return execSilent('git branch --show-current') || 'unknown';
54
+ }
55
+
56
+ function isProtectedBranch(branch) {
57
+ const protected = ['main', 'master', 'develop'];
58
+ return protected.includes(branch);
59
+ }
60
+
61
+ function hasUncommittedChanges() {
62
+ const status = execSilent('git status --porcelain');
63
+ return status && status.length > 0;
64
+ }
65
+
66
+ function isGitHubCliAvailable() {
67
+ return execSilent('gh auth status') !== null;
68
+ }
69
+
70
+ function prExists(base, head) {
71
+ return execSilent(`gh pr list --base ${base} --head ${head} --json number --jq '. | length'`) !== '0';
72
+ }
73
+
74
+ function getPrUrl(base, head) {
75
+ const output = execSilent(`gh pr list --base ${base} --head ${head} --json url --jq '.[0].url'`);
76
+ return output || null;
77
+ }
78
+
79
+ // ════════════════════════════════════════════════════════════════
80
+ // MAIN CYCLE STEPS
81
+ // ════════════════════════════════════════════════════════════════
82
+
83
+ function step1_validateBranch() {
84
+ log(COLORS.cyan, '\n═══════════════════════════════════════════════════════════════');
85
+ log(COLORS.cyan, 'šŸ“ Step 1: Validate Branch');
86
+ log(COLORS.cyan, '═══════════════════════════════════════════════════════════════\n');
87
+
88
+ const branch = getCurrentBranch();
89
+ log(COLORS.blue, `Current branch: ${branch}`);
90
+
91
+ if (branch !== 'develop') {
92
+ log(COLORS.red, `\nāŒ Release cycle must be run from develop branch`);
93
+ log(COLORS.yellow, `\nCurrent branch: ${branch}`);
94
+ log(COLORS.yellow, 'Switch to develop first:');
95
+ log(COLORS.yellow, ' git checkout develop');
96
+ process.exit(1);
97
+ }
98
+
99
+ if (hasUncommittedChanges()) {
100
+ log(COLORS.red, '\nāŒ Uncommitted changes detected');
101
+ log(COLORS.yellow, 'Commit or stash changes before creating release');
102
+ process.exit(1);
103
+ }
104
+
105
+ log(COLORS.green, 'āœ… Branch validation passed');
106
+ return branch;
107
+ }
108
+
109
+ function step2_syncDevelop() {
110
+ log(COLORS.cyan, '\n═══════════════════════════════════════════════════════════════');
111
+ log(COLORS.cyan, 'šŸ”„ Step 2: Sync Develop');
112
+ log(COLORS.cyan, '═══════════════════════════════════════════════════════════════\n');
113
+
114
+ log(COLORS.blue, 'Fetching from origin...');
115
+ exec('git fetch origin');
116
+
117
+ log(COLORS.blue, 'Pulling latest changes from develop...');
118
+ exec('git pull --ff-only origin develop');
119
+ log(COLORS.green, 'āœ… Develop synced');
120
+ }
121
+
122
+ function step3_syncMain() {
123
+ log(COLORS.cyan, '\n═══════════════════════════════════════════════════════════════');
124
+ log(COLORS.cyan, 'šŸ”„ Step 3: Sync Main');
125
+ log(COLORS.cyan, '═══════════════════════════════════════════════════════════════\n');
126
+
127
+ const current = getCurrentBranch();
128
+
129
+ log(COLORS.blue, 'Fetching from origin...');
130
+ exec('git fetch origin');
131
+
132
+ log(COLORS.blue, 'Pulling latest changes from main...');
133
+ exec('git checkout main');
134
+ exec('git pull --ff-only origin main');
135
+ log(COLORS.green, 'āœ… Main synced');
136
+
137
+ if (current && current !== 'main') {
138
+ exec(`git checkout ${current}`);
139
+ }
140
+ }
141
+
142
+ function step4_createReleasePR(prTitle, prBody) {
143
+ log(COLORS.cyan, '\n═══════════════════════════════════════════════════════════════');
144
+ log(COLORS.cyan, 'šŸ“‹ Step 4: Create Release PR');
145
+ log(COLORS.cyan, '═══════════════════════════════════════════════════════════════\n');
146
+
147
+ if (!isGitHubCliAvailable()) {
148
+ log(COLORS.yellow, 'āš ļø GitHub CLI (gh) not available or not authenticated');
149
+ log(COLORS.yellow, ' Install: https://cli.github.com/');
150
+ log(COLORS.yellow, ' Auth: gh auth login');
151
+ log(COLORS.yellow, '\n Create PR manually at GitHub.');
152
+ return null;
153
+ }
154
+
155
+ const base = 'main';
156
+ const head = 'develop';
157
+
158
+ if (prExists(base, head)) {
159
+ const existingPrUrl = getPrUrl(base, head);
160
+ log(COLORS.green, 'āœ… Release PR already exists');
161
+ log(COLORS.blue, ` PR: ${existingPrUrl}`);
162
+ return existingPrUrl;
163
+ }
164
+
165
+ const title = prTitle || `Release: ${new Date().toISOString().split('T')[0]}`;
166
+ const body = prBody || `## Release Summary\n\nAutomated release from develop to main\n\n## Changes\n${execSilent('git log --oneline origin/main..develop') || '- See commit history'}`;
167
+
168
+ log(COLORS.blue, `Creating release PR: ${title}`);
169
+ log(COLORS.blue, `Base: ${base} ← Head: ${head}`);
170
+
171
+ try {
172
+ const output = execSilent(`gh pr create --base ${base} --head ${head} --title "${title}" --body "${body.replace(/"/g, '\\"')}"`);
173
+ if (output) {
174
+ const urlMatch = output.match(/https:\/\/github\.com\/.*\/pull\/\d+/);
175
+ const prUrl = urlMatch ? urlMatch[0] : output;
176
+ log(COLORS.green, `āœ… Release PR created: ${prUrl}`);
177
+ return prUrl;
178
+ }
179
+ } catch (error) {
180
+ log(COLORS.red, `āŒ Failed to create PR: ${error.message}`);
181
+ }
182
+
183
+ return null;
184
+ }
185
+
186
+ function step5_mergeReleasePR(prUrl, autoMerge) {
187
+ log(COLORS.cyan, '\n═══════════════════════════════════════════════════════════════');
188
+ log(COLORS.cyan, 'šŸ”€ Step 5: Merge Release PR');
189
+ log(COLORS.cyan, '═══════════════════════════════════════════════════════════════\n');
190
+
191
+ if (!prUrl) {
192
+ log(COLORS.yellow, 'āš ļø No PR URL available, skipping merge');
193
+ return false;
194
+ }
195
+
196
+ if (!autoMerge) {
197
+ log(COLORS.yellow, 'āš ļø Auto-merge disabled. Merge PR manually.');
198
+ log(COLORS.blue, ` PR: ${prUrl}`);
199
+ return false;
200
+ }
201
+
202
+ log(COLORS.blue, 'Enabling auto-merge (squash)...');
203
+ try {
204
+ exec(`gh pr merge --auto --squash "${prUrl}"`, { ignoreError: true });
205
+ log(COLORS.green, 'āœ… Auto-merge enabled');
206
+ return true;
207
+ } catch (error) {
208
+ log(COLORS.yellow, 'āš ļø Could not enable auto-merge (may require admin approval)');
209
+ return false;
210
+ }
211
+ }
212
+
213
+ function step6_tagRelease(version) {
214
+ log(COLORS.cyan, '\n═══════════════════════════════════════════════════════════════');
215
+ log(COLORS.cyan, 'šŸ·ļø Step 6: Tag Release');
216
+ log(COLORS.cyan, '═══════════════════════════════════════════════════════════════\n');
217
+
218
+ if (!version) {
219
+ log(COLORS.yellow, 'āš ļø No version specified, skipping tag');
220
+ return;
221
+ }
222
+
223
+ log(COLORS.blue, `Creating tag: v${version}`);
224
+ try {
225
+ exec(`git tag -a v${version} -m "Release v${version}"`);
226
+ exec(`git push origin v${version}`);
227
+ log(COLORS.green, `āœ… Tag v${version} created and pushed`);
228
+ } catch (error) {
229
+ log(COLORS.red, `āŒ Failed to create tag: ${error.message}`);
230
+ }
231
+ }
232
+
233
+ // ════════════════════════════════════════════════════════════════
234
+ // MAIN
235
+ // ════════════════════════════════════════════════════════════════
236
+
237
+ function printUsage() {
238
+ console.log(`
239
+ ${COLORS.cyan}═══════════════════════════════════════════════════════════════
240
+ 🐈 Pumuki Git Flow Release Cycle
241
+ ═══════════════════════════════════════════════════════════════${COLORS.reset}
242
+
243
+ ${COLORS.blue}Usage:${COLORS.reset} npm run ast:release [options]
244
+
245
+ ${COLORS.blue}Options:${COLORS.reset}
246
+ --pr-title <title> Custom PR title
247
+ --pr-body <body> Custom PR body
248
+ --auto-merge Enable auto-merge after PR creation
249
+ --tag <version> Create and push git tag
250
+ --help, -h Show this help
251
+
252
+ ${COLORS.blue}Examples:${COLORS.reset}
253
+ npm run ast:release
254
+ npm run ast:release -- --auto-merge
255
+ npm run ast:release -- --tag 5.5.35 --auto-merge
256
+ npm run ast:release -- --pr-title "Release v5.5.35" --auto-merge
257
+
258
+ ${COLORS.blue}What it does:${COLORS.reset}
259
+ 1. Validates branch (must be develop)
260
+ 2. Syncs develop with origin
261
+ 3. Syncs main with origin
262
+ 4. Creates PR: develop → main
263
+ 5. Optionally auto-merges PR
264
+ 6. Optionally creates git tag
265
+
266
+ ${COLORS.yellow}āš ļø Requires:${COLORS.reset}
267
+ - Git repository
268
+ - GitHub CLI (gh) for PR operations: https://cli.github.com/
269
+ - Must be on develop branch
270
+ - No uncommitted changes
271
+ `);
272
+ }
273
+
274
+ function parseArgs(args) {
275
+ const options = {
276
+ prTitle: null,
277
+ prBody: null,
278
+ autoMerge: false,
279
+ tag: null,
280
+ help: false
281
+ };
282
+
283
+ for (let i = 0; i < args.length; i++) {
284
+ const arg = args[i];
285
+ switch (arg) {
286
+ case '--pr-title':
287
+ options.prTitle = args[++i];
288
+ break;
289
+ case '--pr-body':
290
+ options.prBody = args[++i];
291
+ break;
292
+ case '--auto-merge':
293
+ options.autoMerge = true;
294
+ break;
295
+ case '--tag':
296
+ options.tag = args[++i];
297
+ break;
298
+ case '--help':
299
+ case '-h':
300
+ options.help = true;
301
+ break;
302
+ }
303
+ }
304
+
305
+ return options;
306
+ }
307
+
308
+ function main() {
309
+ const args = process.argv.slice(2);
310
+ const options = parseArgs(args);
311
+
312
+ if (options.help) {
313
+ printUsage();
314
+ process.exit(0);
315
+ }
316
+
317
+ log(COLORS.magenta, '\n🐈 Pumuki Git Flow Release Cycle - Starting...\n');
318
+
319
+ try {
320
+ // Step 1: Validate branch
321
+ step1_validateBranch();
322
+
323
+ // Step 2: Sync develop
324
+ step2_syncDevelop();
325
+
326
+ // Step 3: Sync main
327
+ step3_syncMain();
328
+
329
+ // Step 4: Create release PR
330
+ const prUrl = step4_createReleasePR(options.prTitle, options.prBody);
331
+
332
+ // Step 5: Merge PR (optional)
333
+ step5_mergeReleasePR(prUrl, options.autoMerge);
334
+
335
+ // Step 6: Tag release (optional)
336
+ step6_tagRelease(options.tag);
337
+
338
+ log(COLORS.green, '\n═══════════════════════════════════════════════════════════════');
339
+ log(COLORS.green, 'āœ… Git Flow Release Cycle Complete!');
340
+ log(COLORS.green, '═══════════════════════════════════════════════════════════════\n');
341
+
342
+ } catch (error) {
343
+ log(COLORS.red, `\nāŒ Git Flow Release Cycle failed: ${error.message}`);
344
+ process.exit(1);
345
+ }
346
+ }
347
+
348
+ main();
@@ -5,7 +5,7 @@
5
5
  "platforms": [
6
6
  "backend"
7
7
  ],
8
- "created": "2025-12-31T11:37:15.219Z"
8
+ "created": "2026-01-04T19:10:44.947Z"
9
9
  },
10
10
  "architecture": {
11
11
  "pattern": "FEATURE_FIRST_CLEAN_DDD",
@@ -294,7 +294,13 @@ class iOSEnterpriseAnalyzer {
294
294
  'Network code without custom NetworkError enum');
295
295
  }
296
296
 
297
- if (content.includes('URLSession') && !content.includes('serverTrustPolicy') && !content.includes('pinning')) {
297
+ // Check for SSL pinning implementation
298
+ const hasSSLPinningImplementation =
299
+ content.includes('serverTrustPolicy') ||
300
+ content.includes('pinning') ||
301
+ (content.includes('URLSessionDelegate') && content.includes('URLAuthenticationChallenge'));
302
+
303
+ if (content.includes('URLSession') && !hasSSLPinningImplementation) {
298
304
  this.addFinding('ios.networking.missing_ssl_pinning', 'medium', filePath, 1,
299
305
  'Consider SSL pinning for high-security apps');
300
306
  }
@@ -1544,7 +1544,13 @@ async function runIOSIntelligence(project, findings, platform) {
1544
1544
  );
1545
1545
  }
1546
1546
 
1547
- if (content.includes('URLSession') && content.includes('https') && !content.includes('ServerTrustPolicy') && !content.includes('pinning')) {
1547
+ // Check for SSL pinning implementation
1548
+ const hasSSLPinningImplementation =
1549
+ content.includes('ServerTrustPolicy') ||
1550
+ content.includes('pinning') ||
1551
+ (content.includes('URLSessionDelegate') && content.includes('URLAuthenticationChallenge'));
1552
+
1553
+ if (content.includes('URLSession') && content.includes('https') && !hasSSLPinningImplementation) {
1548
1554
  if (content.includes('production') || content.includes('release')) {
1549
1555
  pushFinding(
1550
1556
  "ios.security.missing_ssl_pinning",