fraim-framework 2.0.34 → 2.0.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/bin/fraim.js +52 -5
- package/dist/registry/scripts/cleanup-branch.js +62 -33
- package/dist/registry/scripts/generate-engagement-emails.js +119 -44
- package/dist/registry/scripts/newsletter-helpers.js +208 -268
- package/dist/registry/scripts/profile-server.js +387 -0
- package/dist/tests/test-chalk-regression.js +18 -2
- package/dist/tests/test-client-scripts-validation.js +133 -0
- package/dist/tests/test-markdown-to-pdf.js +454 -0
- package/dist/tests/test-script-location-independence.js +76 -28
- package/package.json +5 -2
- package/registry/agent-guardrails.md +62 -62
- package/registry/rules/communication.md +121 -121
- package/registry/rules/continuous-learning.md +54 -54
- package/registry/rules/hitl-ppe-record-analysis.md +302 -302
- package/registry/rules/software-development-lifecycle.md +104 -104
- package/registry/scripts/cleanup-branch.ts +341 -0
- package/registry/scripts/code-quality-check.sh +559 -559
- package/registry/scripts/detect-tautological-tests.sh +38 -38
- package/registry/scripts/generate-engagement-emails.ts +830 -0
- package/registry/scripts/markdown-to-pdf.js +395 -0
- package/registry/scripts/newsletter-helpers.ts +777 -0
- package/registry/scripts/profile-server.ts +424 -0
- package/registry/scripts/run-thank-you-workflow.ts +122 -0
- package/registry/scripts/send-newsletter-simple.ts +102 -0
- package/registry/scripts/send-thank-you-emails.ts +57 -0
- package/registry/scripts/validate-openapi-limits.ts +366 -366
- package/registry/scripts/validate-test-coverage.ts +280 -280
- package/registry/scripts/verify-pr-comments.sh +70 -70
- package/registry/templates/bootstrap/ARCHITECTURE-TEMPLATE.md +53 -53
- package/registry/templates/evidence/Implementation-BugEvidence.md +85 -85
- package/registry/templates/evidence/Implementation-FeatureEvidence.md +120 -120
- package/registry/workflows/convert-to-pdf.md +235 -0
- package/registry/workflows/customer-development/insight-analysis.md +156 -156
- package/registry/workflows/customer-development/interview-preparation.md +421 -421
- package/registry/workflows/customer-development/strategic-brainstorming.md +146 -146
- package/registry/workflows/quality-assurance/iterative-improvement-cycle.md +562 -562
- package/registry/workflows/reviewer/review-implementation-vs-feature-spec.md +669 -669
- package/dist/registry/scripts/build-scripts-generator.js +0 -205
- package/dist/registry/scripts/fraim-config.js +0 -61
- package/dist/registry/scripts/generic-issues-api.js +0 -100
- package/dist/registry/scripts/openapi-generator.js +0 -664
- package/dist/registry/scripts/performance/profile-server.js +0 -390
- package/dist/test-utils.js +0 -96
- package/dist/tests/esm-compat.js +0 -11
- package/dist/tests/test-chalk-esm-issue.js +0 -159
- package/dist/tests/test-chalk-real-world.js +0 -265
- package/dist/tests/test-chalk-resolution-issue.js +0 -304
- package/dist/tests/test-fraim-install-chalk-issue.js +0 -254
- package/dist/tests/test-npm-resolution-diagnostic.js +0 -140
|
@@ -1,105 +1,105 @@
|
|
|
1
|
-
# Software Development Lifecycle
|
|
2
|
-
|
|
3
|
-
## INTENT
|
|
4
|
-
To provide a structured, phase-based approach to issue resolution that ensures proper planning, implementation, testing, and cleanup while maintaining coordination with other agents and respecting project governance.
|
|
5
|
-
|
|
6
|
-
## PRINCIPLES
|
|
7
|
-
- **Phase-Based Development**: Clear phases with specific deliverables
|
|
8
|
-
- **Branch Safety**: Never work on master, always use feature branches
|
|
9
|
-
- **Local Development**: Work in isolated clones to prevent conflicts
|
|
10
|
-
- **Coordination**: Use GitHub for agent coordination and status management
|
|
11
|
-
- **Governance**: Respect CODEOWNERS and project policies
|
|
12
|
-
|
|
13
|
-
## CORE WORKFLOW
|
|
14
|
-
Always work on the feature branch for the current issue: `feature/<issue#>-<kebab-title>`. Never push to master.
|
|
15
|
-
|
|
16
|
-
### Development Workflow
|
|
17
|
-
1. **Clone Setup**: Work in your own cloned repository folder. Folder name should be `Ashley Calendar AI - Issue {issue_number}`
|
|
18
|
-
2. **Branch Management**: Create/checkout feature branch for your issue
|
|
19
|
-
3. **Local Development**: Make changes, run tests locally
|
|
20
|
-
4. **Check before commit**: Only commit after approval from the user.
|
|
21
|
-
5. **Remote Coordination**: Use GitHub MCP for issue labels
|
|
22
|
-
|
|
23
|
-
## MANDATORY PRE-COMMIT VALIDATION
|
|
24
|
-
Before ANY commit, run: `git branch` and verify NOT on master
|
|
25
|
-
|
|
26
|
-
## PHASES
|
|
27
|
-
|
|
28
|
-
### Design Phase
|
|
29
|
-
- **Trigger**: Set ISSUE to `phase:design`
|
|
30
|
-
- **Deliverable**: Create RFC document
|
|
31
|
-
- **Status**: Automatically set to `status:wip`
|
|
32
|
-
|
|
33
|
-
### Implementation Phase
|
|
34
|
-
- **Trigger**: Set ISSUE to `phase:impl`
|
|
35
|
-
- **Deliverable**: Working code with tests
|
|
36
|
-
- **Status**: Set to `status:needs-review` when ready
|
|
37
|
-
|
|
38
|
-
## STATUS MANAGEMENT
|
|
39
|
-
- **WIP**: Automatically set when entering new phase
|
|
40
|
-
- **Needs Review**: Set this when work is ready for review
|
|
41
|
-
- **Complete**: Automatically set when PR is approved
|
|
42
|
-
|
|
43
|
-
## EXAMPLES
|
|
44
|
-
|
|
45
|
-
### Good: Proper Phase Management
|
|
46
|
-
```
|
|
47
|
-
Issue #84: "Fix calendar sync timeout"
|
|
48
|
-
1. Set phase:design → Create RFC for retry logic
|
|
49
|
-
2. Set phase:impl → Implement exponential backoff
|
|
50
|
-
3. Set status:needs-review → Ready for code review
|
|
51
|
-
4. PR approved → Set status:complete
|
|
52
|
-
Result: Clear progression through phases
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### Bad: Skipping Phases
|
|
56
|
-
```
|
|
57
|
-
Issue #84: "Fix calendar sync timeout"
|
|
58
|
-
1. Jump straight to coding without design
|
|
59
|
-
2. No RFC created
|
|
60
|
-
3. Implementation lacks proper planning
|
|
61
|
-
Result: Incomplete solution, potential rework
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## PRE-PHASE VALIDATION
|
|
65
|
-
|
|
66
|
-
### Before Starting Any Phase
|
|
67
|
-
Agents MUST complete validation checklist:
|
|
68
|
-
|
|
69
|
-
1. **Read Relevant Rules**:
|
|
70
|
-
- [ ] Read `get_fraim_file({ path: "rules/software-development-lifecycle.md" })`
|
|
71
|
-
- [ ] Read phase-specific workflow via `get_fraim_file` (e.g. `workflows/product-building/design.md`)
|
|
72
|
-
- [ ] Read retrospectives folder thoroughly and understand past learnings
|
|
73
|
-
|
|
74
|
-
2. **Verify Environment**:
|
|
75
|
-
- [ ] Confirm working directory is correct
|
|
76
|
-
- [ ] Verify branch exists and is checked out
|
|
77
|
-
- [ ] Check issue labels are correct
|
|
78
|
-
|
|
79
|
-
3. **Template Validation**:
|
|
80
|
-
- [ ] Locate correct template file
|
|
81
|
-
- [ ] Verify template exists and is readable
|
|
82
|
-
- [ ] Confirm naming convention understanding
|
|
83
|
-
|
|
84
|
-
4. **Technical Understanding**:
|
|
85
|
-
- [ ] Understand relevant technical patterns for the issue
|
|
86
|
-
- [ ] Plan test strategy for core functionality
|
|
87
|
-
|
|
88
|
-
### Validation Failure Response
|
|
89
|
-
If any validation step fails:
|
|
90
|
-
1. Stop work immediately
|
|
91
|
-
2. Read missing documentation
|
|
92
|
-
3. Ask clarifying questions if needed
|
|
93
|
-
4. Resume only after validation complete
|
|
94
|
-
|
|
95
|
-
## CLEANUP
|
|
96
|
-
When user confirms code is correctly merged into master, confirm with the user, then
|
|
97
|
-
- delete remote branch
|
|
98
|
-
- delete local branch
|
|
99
|
-
- remove your local clone folder:
|
|
100
|
-
```
|
|
101
|
-
cd ..
|
|
102
|
-
Remove-Item -Recurse -Force "Ashley Calendar AI - Issue {issue_number}"
|
|
103
|
-
```
|
|
104
|
-
|
|
1
|
+
# Software Development Lifecycle
|
|
2
|
+
|
|
3
|
+
## INTENT
|
|
4
|
+
To provide a structured, phase-based approach to issue resolution that ensures proper planning, implementation, testing, and cleanup while maintaining coordination with other agents and respecting project governance.
|
|
5
|
+
|
|
6
|
+
## PRINCIPLES
|
|
7
|
+
- **Phase-Based Development**: Clear phases with specific deliverables
|
|
8
|
+
- **Branch Safety**: Never work on master, always use feature branches
|
|
9
|
+
- **Local Development**: Work in isolated clones to prevent conflicts
|
|
10
|
+
- **Coordination**: Use GitHub for agent coordination and status management
|
|
11
|
+
- **Governance**: Respect CODEOWNERS and project policies
|
|
12
|
+
|
|
13
|
+
## CORE WORKFLOW
|
|
14
|
+
Always work on the feature branch for the current issue: `feature/<issue#>-<kebab-title>`. Never push to master.
|
|
15
|
+
|
|
16
|
+
### Development Workflow
|
|
17
|
+
1. **Clone Setup**: Work in your own cloned repository folder. Folder name should be `Ashley Calendar AI - Issue {issue_number}`
|
|
18
|
+
2. **Branch Management**: Create/checkout feature branch for your issue
|
|
19
|
+
3. **Local Development**: Make changes, run tests locally
|
|
20
|
+
4. **Check before commit**: Only commit after approval from the user.
|
|
21
|
+
5. **Remote Coordination**: Use GitHub MCP for issue labels
|
|
22
|
+
|
|
23
|
+
## MANDATORY PRE-COMMIT VALIDATION
|
|
24
|
+
Before ANY commit, run: `git branch` and verify NOT on master
|
|
25
|
+
|
|
26
|
+
## PHASES
|
|
27
|
+
|
|
28
|
+
### Design Phase
|
|
29
|
+
- **Trigger**: Set ISSUE to `phase:design`
|
|
30
|
+
- **Deliverable**: Create RFC document
|
|
31
|
+
- **Status**: Automatically set to `status:wip`
|
|
32
|
+
|
|
33
|
+
### Implementation Phase
|
|
34
|
+
- **Trigger**: Set ISSUE to `phase:impl`
|
|
35
|
+
- **Deliverable**: Working code with tests
|
|
36
|
+
- **Status**: Set to `status:needs-review` when ready
|
|
37
|
+
|
|
38
|
+
## STATUS MANAGEMENT
|
|
39
|
+
- **WIP**: Automatically set when entering new phase
|
|
40
|
+
- **Needs Review**: Set this when work is ready for review
|
|
41
|
+
- **Complete**: Automatically set when PR is approved
|
|
42
|
+
|
|
43
|
+
## EXAMPLES
|
|
44
|
+
|
|
45
|
+
### Good: Proper Phase Management
|
|
46
|
+
```
|
|
47
|
+
Issue #84: "Fix calendar sync timeout"
|
|
48
|
+
1. Set phase:design → Create RFC for retry logic
|
|
49
|
+
2. Set phase:impl → Implement exponential backoff
|
|
50
|
+
3. Set status:needs-review → Ready for code review
|
|
51
|
+
4. PR approved → Set status:complete
|
|
52
|
+
Result: Clear progression through phases
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Bad: Skipping Phases
|
|
56
|
+
```
|
|
57
|
+
Issue #84: "Fix calendar sync timeout"
|
|
58
|
+
1. Jump straight to coding without design
|
|
59
|
+
2. No RFC created
|
|
60
|
+
3. Implementation lacks proper planning
|
|
61
|
+
Result: Incomplete solution, potential rework
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## PRE-PHASE VALIDATION
|
|
65
|
+
|
|
66
|
+
### Before Starting Any Phase
|
|
67
|
+
Agents MUST complete validation checklist:
|
|
68
|
+
|
|
69
|
+
1. **Read Relevant Rules**:
|
|
70
|
+
- [ ] Read `get_fraim_file({ path: "rules/software-development-lifecycle.md" })`
|
|
71
|
+
- [ ] Read phase-specific workflow via `get_fraim_file` (e.g. `workflows/product-building/design.md`)
|
|
72
|
+
- [ ] Read retrospectives folder thoroughly and understand past learnings
|
|
73
|
+
|
|
74
|
+
2. **Verify Environment**:
|
|
75
|
+
- [ ] Confirm working directory is correct
|
|
76
|
+
- [ ] Verify branch exists and is checked out
|
|
77
|
+
- [ ] Check issue labels are correct
|
|
78
|
+
|
|
79
|
+
3. **Template Validation**:
|
|
80
|
+
- [ ] Locate correct template file
|
|
81
|
+
- [ ] Verify template exists and is readable
|
|
82
|
+
- [ ] Confirm naming convention understanding
|
|
83
|
+
|
|
84
|
+
4. **Technical Understanding**:
|
|
85
|
+
- [ ] Understand relevant technical patterns for the issue
|
|
86
|
+
- [ ] Plan test strategy for core functionality
|
|
87
|
+
|
|
88
|
+
### Validation Failure Response
|
|
89
|
+
If any validation step fails:
|
|
90
|
+
1. Stop work immediately
|
|
91
|
+
2. Read missing documentation
|
|
92
|
+
3. Ask clarifying questions if needed
|
|
93
|
+
4. Resume only after validation complete
|
|
94
|
+
|
|
95
|
+
## CLEANUP
|
|
96
|
+
When user confirms code is correctly merged into master, confirm with the user, then
|
|
97
|
+
- delete remote branch
|
|
98
|
+
- delete local branch
|
|
99
|
+
- remove your local clone folder:
|
|
100
|
+
```
|
|
101
|
+
cd ..
|
|
102
|
+
Remove-Item -Recurse -Force "Ashley Calendar AI - Issue {issue_number}"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
105
|
Respect CODEOWNERS; don't modify auth/CI without approval.
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import { readFileSync, existsSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import dotenv from 'dotenv';
|
|
7
|
+
|
|
8
|
+
// Load environment variables
|
|
9
|
+
dotenv.config({ override: true });
|
|
10
|
+
|
|
11
|
+
// Self-contained utility functions (no FRAIM internal imports)
|
|
12
|
+
function extractIssueNumber(branchName: string): string {
|
|
13
|
+
const match = branchName.match(/(\d+)/);
|
|
14
|
+
return match ? match[1] : '';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getCurrentGitBranch(): string {
|
|
18
|
+
try {
|
|
19
|
+
return execSync('git branch --show-current', { encoding: 'utf8' }).trim();
|
|
20
|
+
} catch (error) {
|
|
21
|
+
return 'unknown';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface FraimConfig {
|
|
26
|
+
persona: { name: string; displayNamePattern: string; emailSignature: string };
|
|
27
|
+
git: { repoOwner: string; repoName: string };
|
|
28
|
+
database: { identityCollection: string; executiveCollection: string };
|
|
29
|
+
marketing: { websiteUrl?: string; chatUrl?: string };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function loadClientConfig(): FraimConfig {
|
|
33
|
+
const configPath = join(process.cwd(), '.fraim', 'config.json');
|
|
34
|
+
if (!existsSync(configPath)) {
|
|
35
|
+
throw new Error('.fraim/config.json not found. Run fraim init first.');
|
|
36
|
+
}
|
|
37
|
+
return JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getEnvOr(keys: string[], fallback: string): string {
|
|
41
|
+
for (const key of keys) {
|
|
42
|
+
const value = process.env[key];
|
|
43
|
+
if (value && value.length) return value;
|
|
44
|
+
}
|
|
45
|
+
return fallback;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface CleanupOptions {
|
|
49
|
+
branchName?: string;
|
|
50
|
+
force?: boolean;
|
|
51
|
+
skipProjectCleanup?: boolean;
|
|
52
|
+
skipGit?: boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
class BranchCleanup {
|
|
56
|
+
private options: CleanupOptions;
|
|
57
|
+
|
|
58
|
+
constructor(options: CleanupOptions = {}) {
|
|
59
|
+
this.options = {
|
|
60
|
+
branchName: options.branchName,
|
|
61
|
+
force: options.force || false,
|
|
62
|
+
skipProjectCleanup: options.skipProjectCleanup || false,
|
|
63
|
+
skipGit: options.skipGit || false,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private async insertYourCodeHere(branchName: string): Promise<void> {
|
|
68
|
+
const branchPattern = "branch_" + extractIssueNumber(branchName) + "%";
|
|
69
|
+
this.log(`Attempting to fetch and run DB cleanup for pattern: ${branchPattern}`);
|
|
70
|
+
|
|
71
|
+
// Historically this called: npx tsx scripts/database/cleanup-mongo-schemas.ts
|
|
72
|
+
// In FRAIM 2.0, we should fetch it if possible, or skip if it's an internal-only script
|
|
73
|
+
// that doesn't exist in the registry but was incorrectly referenced.
|
|
74
|
+
|
|
75
|
+
// NOTE: In this specific case, cleanup-mongo-schemas.ts is likely an internal script.
|
|
76
|
+
// If it's not in the registry, get_fraim_file will fail.
|
|
77
|
+
// For now, we fix the path to be more agnostic or explicitly documented as a dependency.
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
// NOTE: Users should define their own cleanup script location in .fraim/config.json
|
|
81
|
+
// or the agent should fetch it. For now, we try to run it from a standard location
|
|
82
|
+
// if it exists, but we don't hardcode it as a requirement for the script to run.
|
|
83
|
+
const cleanupScript = process.env.FRAIM_PROJECT_CLEANUP_SCRIPT || (['scripts', 'database', 'cleanup-mongo-schemas.ts'].join('/'));
|
|
84
|
+
this.log(`Checking for project cleanup script: ${cleanupScript}`);
|
|
85
|
+
|
|
86
|
+
await this.executeCommand(
|
|
87
|
+
`npx tsx ${cleanupScript} "${branchPattern}"`,
|
|
88
|
+
'Run project-specific cleanup script'
|
|
89
|
+
);
|
|
90
|
+
} catch (e) {
|
|
91
|
+
this.log('Skipping project-specific cleanup (script not found or failed).', 'info');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
private log(message: string, level: 'info' | 'warn' | 'error' | 'success' = 'info') {
|
|
97
|
+
const timestamp = new Date().toISOString();
|
|
98
|
+
const prefix = {
|
|
99
|
+
info: 'ℹ️ ',
|
|
100
|
+
warn: '⚠️ ',
|
|
101
|
+
error: '❌',
|
|
102
|
+
success: '✅'
|
|
103
|
+
}[level];
|
|
104
|
+
|
|
105
|
+
console.log(`${prefix} [${timestamp}] ${message}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private async executeCommand(command: string, description: string): Promise<string> {
|
|
109
|
+
this.log(`Executing: ${description}`);
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
const result = execSync(command, {
|
|
113
|
+
encoding: 'utf8',
|
|
114
|
+
stdio: 'pipe',
|
|
115
|
+
cwd: process.cwd()
|
|
116
|
+
});
|
|
117
|
+
this.log(`Success: ${description}`, 'success');
|
|
118
|
+
return result;
|
|
119
|
+
} catch (error: any) {
|
|
120
|
+
this.log(`Failed: ${description} - ${error.message}`, 'error');
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private async getCurrentBranch(): Promise<string> {
|
|
126
|
+
try {
|
|
127
|
+
const result = await this.executeCommand('git branch --show-current', 'Get current branch');
|
|
128
|
+
return result.trim();
|
|
129
|
+
} catch (error) {
|
|
130
|
+
this.log('Could not determine current branch', 'warn');
|
|
131
|
+
return 'unknown';
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private async getBranchName(): Promise<string> {
|
|
136
|
+
if (this.options.branchName) {
|
|
137
|
+
return this.options.branchName;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const currentBranch = await this.getCurrentBranch();
|
|
141
|
+
if (currentBranch === 'master' || currentBranch === 'main') {
|
|
142
|
+
throw new Error('Cannot cleanup master/main branch. Please specify a feature branch name.');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return currentBranch;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private async checkBranchExists(branchName: string, remote: boolean = false): Promise<boolean> {
|
|
149
|
+
try {
|
|
150
|
+
const command = remote
|
|
151
|
+
? `git ls-remote --heads origin ${branchName}`
|
|
152
|
+
: `git branch --list ${branchName}`;
|
|
153
|
+
|
|
154
|
+
const result = await this.executeCommand(command, `Check if ${remote ? 'remote' : 'local'} branch exists`);
|
|
155
|
+
return result.trim().length > 0;
|
|
156
|
+
} catch (error) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private async doProjectSpecificCleanup(branchName: string): Promise<void> {
|
|
162
|
+
try {
|
|
163
|
+
if (this.options.skipProjectCleanup) {
|
|
164
|
+
this.log('Skipping project-specific cleanup', 'warn');
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
this.log('Starting project-specific cleanup...');
|
|
169
|
+
|
|
170
|
+
await this.insertYourCodeHere(branchName);
|
|
171
|
+
|
|
172
|
+
this.log('Project-specific cleanup completed successfully', 'success');
|
|
173
|
+
} catch (error: any) {
|
|
174
|
+
this.log(`Project-specific cleanup failed: ${error.message}`, 'error');
|
|
175
|
+
if (!this.options.force) {
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private async cleanupGitBranches(branchName: string): Promise<void> {
|
|
182
|
+
if (this.options.skipGit) {
|
|
183
|
+
this.log('Skipping Git cleanup', 'warn');
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
this.log(`Starting Git branch cleanup for: ${branchName}`);
|
|
188
|
+
|
|
189
|
+
// Check if we're on the branch we want to delete
|
|
190
|
+
const currentBranch = await this.getCurrentBranch();
|
|
191
|
+
if (currentBranch === branchName) {
|
|
192
|
+
this.log(`Currently on branch ${branchName}, switching to master first`, 'warn');
|
|
193
|
+
await this.executeCommand('git checkout master', 'Switch to master branch');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Delete remote branch
|
|
197
|
+
const remoteExists = await this.checkBranchExists(branchName, true);
|
|
198
|
+
if (remoteExists) {
|
|
199
|
+
this.log(`Deleting remote branch: origin/${branchName}`);
|
|
200
|
+
await this.executeCommand(`git push origin --delete ${branchName}`, `Delete remote branch ${branchName}`);
|
|
201
|
+
} else {
|
|
202
|
+
this.log(`Remote branch origin/${branchName} does not exist`, 'info');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Delete local branch
|
|
206
|
+
const localExists = await this.checkBranchExists(branchName, false);
|
|
207
|
+
if (localExists) {
|
|
208
|
+
this.log(`Deleting local branch: ${branchName}`);
|
|
209
|
+
await this.executeCommand(`git branch -D ${branchName}`, `Delete local branch ${branchName}`);
|
|
210
|
+
} else {
|
|
211
|
+
this.log(`Local branch ${branchName} does not exist`, 'info');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Clean up any untracked files
|
|
215
|
+
this.log('Cleaning up untracked files...');
|
|
216
|
+
await this.executeCommand('git clean -fd', 'Remove untracked files and directories');
|
|
217
|
+
|
|
218
|
+
// Reset any uncommitted changes
|
|
219
|
+
this.log('Resetting uncommitted changes...');
|
|
220
|
+
await this.executeCommand('git reset --hard HEAD', 'Reset to HEAD');
|
|
221
|
+
|
|
222
|
+
this.log('Git branch cleanup completed', 'success');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
private async verifyCleanup(branchName: string): Promise<void> {
|
|
227
|
+
this.log('Verifying cleanup...');
|
|
228
|
+
|
|
229
|
+
// Verify branch deletion
|
|
230
|
+
const remoteExists = await this.checkBranchExists(branchName, true);
|
|
231
|
+
const localExists = await this.checkBranchExists(branchName, false);
|
|
232
|
+
|
|
233
|
+
if (remoteExists || localExists) {
|
|
234
|
+
this.log(`Warning: Branch ${branchName} still exists (remote: ${remoteExists}, local: ${localExists})`, 'warn');
|
|
235
|
+
} else {
|
|
236
|
+
this.log(`Branch ${branchName} successfully deleted`, 'success');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Verify we're on master
|
|
240
|
+
const currentBranch = await this.getCurrentBranch();
|
|
241
|
+
if (currentBranch === 'master' || currentBranch === 'main') {
|
|
242
|
+
this.log('Currently on master/main branch', 'success');
|
|
243
|
+
} else {
|
|
244
|
+
this.log(`Warning: Not on master branch, currently on ${currentBranch}`, 'warn');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
this.log('Cleanup verification completed', 'success');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async cleanup(): Promise<void> {
|
|
251
|
+
try {
|
|
252
|
+
this.log('🚀 Starting branch cleanup process...');
|
|
253
|
+
|
|
254
|
+
const branchName = await this.getBranchName();
|
|
255
|
+
this.log(`Target branch: ${branchName}`);
|
|
256
|
+
|
|
257
|
+
// Step 1: Project-specific cleanup
|
|
258
|
+
await this.doProjectSpecificCleanup(branchName);
|
|
259
|
+
|
|
260
|
+
// Step 2: Git branch cleanup
|
|
261
|
+
await this.cleanupGitBranches(branchName);
|
|
262
|
+
|
|
263
|
+
// Step 3: Verification
|
|
264
|
+
await this.verifyCleanup(branchName);
|
|
265
|
+
|
|
266
|
+
this.log('🎉 Branch cleanup completed successfully!', 'success');
|
|
267
|
+
|
|
268
|
+
} catch (error: any) {
|
|
269
|
+
this.log(`Cleanup failed: ${error.message}`, 'error');
|
|
270
|
+
if (!this.options.force) {
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Parse command line arguments
|
|
278
|
+
const args = process.argv.slice(2);
|
|
279
|
+
const options: CleanupOptions = {};
|
|
280
|
+
|
|
281
|
+
// Parse arguments
|
|
282
|
+
for (let i = 0; i < args.length; i++) {
|
|
283
|
+
const arg = args[i];
|
|
284
|
+
|
|
285
|
+
switch (arg) {
|
|
286
|
+
case '--branch':
|
|
287
|
+
case '-b':
|
|
288
|
+
options.branchName = args[++i];
|
|
289
|
+
break;
|
|
290
|
+
case '--force':
|
|
291
|
+
case '-f':
|
|
292
|
+
options.force = true;
|
|
293
|
+
break;
|
|
294
|
+
case '--skip-project-cleanup':
|
|
295
|
+
options.skipProjectCleanup = true;
|
|
296
|
+
break;
|
|
297
|
+
case '--skip-git':
|
|
298
|
+
options.skipGit = true;
|
|
299
|
+
break;
|
|
300
|
+
case '--help':
|
|
301
|
+
case '-h':
|
|
302
|
+
console.log(`
|
|
303
|
+
🧹 Branch Cleanup Script
|
|
304
|
+
|
|
305
|
+
Usage:
|
|
306
|
+
npx tsx scripts/cleanup-branch.ts [options]
|
|
307
|
+
|
|
308
|
+
Options:
|
|
309
|
+
--branch, -b <name> Specific branch name to cleanup (default: current branch)
|
|
310
|
+
--force, -f Continue even if some operations fail
|
|
311
|
+
--skip-project-cleanup Skip project-specific cleanup
|
|
312
|
+
--skip-git Skip Git branch deletion
|
|
313
|
+
--help, -h Show this help message
|
|
314
|
+
|
|
315
|
+
Examples:
|
|
316
|
+
# Cleanup current branch with default settings
|
|
317
|
+
npx tsx <cleanup-script-path>
|
|
318
|
+
|
|
319
|
+
# Cleanup specific branch
|
|
320
|
+
npx tsx <cleanup-script-path> --branch feature/123
|
|
321
|
+
|
|
322
|
+
# Force cleanup even if some operations fail
|
|
323
|
+
npx tsx <cleanup-script-path> --force
|
|
324
|
+
|
|
325
|
+
# Skip project-specific cleanup (only do Git cleanup)
|
|
326
|
+
npx tsx <cleanup-script-path> --skip-project-cleanup
|
|
327
|
+
|
|
328
|
+
This script performs the following cleanup operations:
|
|
329
|
+
1. 🗄️ Project-specific cleanup (calls configured cleanup script)
|
|
330
|
+
2. 🌿 Git branch deletion (remote and local)
|
|
331
|
+
3. ✅ Verification of cleanup completion
|
|
332
|
+
|
|
333
|
+
⚠️ WARNING: This script will permanently delete branches and data!
|
|
334
|
+
`);
|
|
335
|
+
process.exit(0);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Run the cleanup
|
|
340
|
+
const cleanup = new BranchCleanup(options);
|
|
341
|
+
cleanup.cleanup();
|