claude-cli-advanced-starter-pack 1.1.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/OVERVIEW.md +5 -1
- package/README.md +241 -132
- package/bin/gtask.js +53 -0
- package/package.json +1 -1
- package/src/cli/menu.js +27 -0
- package/src/commands/explore-mcp/mcp-registry.js +99 -0
- package/src/commands/init.js +306 -77
- package/src/commands/install-panel-hook.js +108 -0
- package/src/commands/install-scripts.js +232 -0
- package/src/commands/install-skill.js +220 -0
- package/src/commands/panel.js +297 -0
- package/src/commands/setup-wizard.js +4 -3
- package/src/commands/test-setup.js +4 -5
- package/src/data/releases.json +164 -0
- package/src/panel/queue.js +188 -0
- package/templates/commands/ask-claude.template.md +118 -0
- package/templates/commands/ccasp-panel.template.md +72 -0
- package/templates/commands/ccasp-setup.template.md +470 -79
- package/templates/commands/create-smoke-test.template.md +186 -0
- package/templates/commands/project-impl.template.md +9 -113
- package/templates/commands/refactor-check.template.md +112 -0
- package/templates/commands/refactor-cleanup.template.md +144 -0
- package/templates/commands/refactor-prep.template.md +192 -0
- package/templates/docs/AI_ARCHITECTURE_CONSTITUTION.template.md +198 -0
- package/templates/docs/DETAILED_GOTCHAS.template.md +347 -0
- package/templates/docs/PHASE-DEV-CHECKLIST.template.md +241 -0
- package/templates/docs/PROGRESS_JSON_TEMPLATE.json +117 -0
- package/templates/docs/background-agent.template.md +264 -0
- package/templates/hooks/autonomous-decision-logger.template.js +207 -0
- package/templates/hooks/branch-merge-checker.template.js +272 -0
- package/templates/hooks/git-commit-tracker.template.js +267 -0
- package/templates/hooks/issue-completion-detector.template.js +205 -0
- package/templates/hooks/panel-queue-reader.template.js +83 -0
- package/templates/hooks/phase-validation-gates.template.js +307 -0
- package/templates/hooks/session-id-generator.template.js +236 -0
- package/templates/hooks/token-usage-monitor.template.js +193 -0
- package/templates/patterns/README.md +129 -0
- package/templates/patterns/l1-l2-orchestration.md +189 -0
- package/templates/patterns/multi-phase-orchestration.md +258 -0
- package/templates/patterns/two-tier-query-pipeline.md +192 -0
- package/templates/scripts/README.md +109 -0
- package/templates/scripts/analyze-delegation-log.js +299 -0
- package/templates/scripts/autonomous-decision-logger.js +277 -0
- package/templates/scripts/git-history-analyzer.py +269 -0
- package/templates/scripts/phase-validation-gates.js +307 -0
- package/templates/scripts/poll-deployment-status.js +260 -0
- package/templates/scripts/roadmap-scanner.js +263 -0
- package/templates/scripts/validate-deployment.js +293 -0
- package/templates/skills/agent-creator/skill.json +18 -0
- package/templates/skills/agent-creator/skill.md +335 -0
- package/templates/skills/hook-creator/skill.json +18 -0
- package/templates/skills/hook-creator/skill.md +318 -0
- package/templates/skills/panel/skill.json +18 -0
- package/templates/skills/panel/skill.md +90 -0
- package/templates/skills/rag-agent-creator/skill.json +18 -0
- package/templates/skills/rag-agent-creator/skill.md +307 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Autonomous Decision Logger Hook
|
|
3
|
+
*
|
|
4
|
+
* Creates JSONL audit trail for all agent decisions.
|
|
5
|
+
* Tracks tool calls, file modifications, and agent spawns.
|
|
6
|
+
* Essential for debugging and compliance.
|
|
7
|
+
*
|
|
8
|
+
* Event: PostToolUse
|
|
9
|
+
*
|
|
10
|
+
* Configuration: Reads from .claude/config/hooks-config.json
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
// Default configuration
|
|
17
|
+
const DEFAULT_CONFIG = {
|
|
18
|
+
enabled: true,
|
|
19
|
+
log_tools: ['Edit', 'Write', 'Bash', 'Task'], // Tools to log
|
|
20
|
+
log_all_tools: false, // Override: log everything
|
|
21
|
+
include_output: false, // Include tool output (can be large)
|
|
22
|
+
max_output_chars: 500, // Truncate output to this length
|
|
23
|
+
rotate_size_mb: 10, // Rotate log at this size
|
|
24
|
+
retention_days: 30, // Keep logs for N days
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Paths
|
|
28
|
+
const CONFIG_PATH = path.join(process.cwd(), '.claude', 'config', 'hooks-config.json');
|
|
29
|
+
const LOG_DIR = path.join(process.cwd(), '.claude', 'logs');
|
|
30
|
+
const LOG_FILE = path.join(LOG_DIR, 'decisions.jsonl');
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Load configuration
|
|
34
|
+
*/
|
|
35
|
+
function loadConfig() {
|
|
36
|
+
try {
|
|
37
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
38
|
+
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
|
39
|
+
return { ...DEFAULT_CONFIG, ...(config.decision_logger || {}) };
|
|
40
|
+
}
|
|
41
|
+
} catch (e) {
|
|
42
|
+
// Use defaults
|
|
43
|
+
}
|
|
44
|
+
return DEFAULT_CONFIG;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Ensure log directory exists
|
|
49
|
+
*/
|
|
50
|
+
function ensureLogDir() {
|
|
51
|
+
if (!fs.existsSync(LOG_DIR)) {
|
|
52
|
+
fs.mkdirSync(LOG_DIR, { recursive: true });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Check if log needs rotation
|
|
58
|
+
*/
|
|
59
|
+
function checkRotation(config) {
|
|
60
|
+
try {
|
|
61
|
+
if (fs.existsSync(LOG_FILE)) {
|
|
62
|
+
const stats = fs.statSync(LOG_FILE);
|
|
63
|
+
const sizeMB = stats.size / (1024 * 1024);
|
|
64
|
+
|
|
65
|
+
if (sizeMB >= config.rotate_size_mb) {
|
|
66
|
+
// Rotate: rename current to timestamped backup
|
|
67
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
68
|
+
const backupPath = path.join(LOG_DIR, `decisions-${timestamp}.jsonl`);
|
|
69
|
+
fs.renameSync(LOG_FILE, backupPath);
|
|
70
|
+
console.log(`[decision-logger] Rotated log to ${path.basename(backupPath)}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} catch (e) {
|
|
74
|
+
// Continue without rotation
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Clean old logs
|
|
80
|
+
*/
|
|
81
|
+
function cleanOldLogs(config) {
|
|
82
|
+
try {
|
|
83
|
+
const files = fs.readdirSync(LOG_DIR);
|
|
84
|
+
const cutoff = Date.now() - (config.retention_days * 24 * 60 * 60 * 1000);
|
|
85
|
+
|
|
86
|
+
for (const file of files) {
|
|
87
|
+
if (file.startsWith('decisions-') && file.endsWith('.jsonl')) {
|
|
88
|
+
const filePath = path.join(LOG_DIR, file);
|
|
89
|
+
const stats = fs.statSync(filePath);
|
|
90
|
+
|
|
91
|
+
if (stats.mtimeMs < cutoff) {
|
|
92
|
+
fs.unlinkSync(filePath);
|
|
93
|
+
console.log(`[decision-logger] Cleaned old log: ${file}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
} catch (e) {
|
|
98
|
+
// Continue without cleaning
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Should this tool be logged?
|
|
104
|
+
*/
|
|
105
|
+
function shouldLog(toolName, config) {
|
|
106
|
+
if (config.log_all_tools) return true;
|
|
107
|
+
|
|
108
|
+
return config.log_tools.some(t =>
|
|
109
|
+
toolName === t || toolName.startsWith(t)
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Truncate string to max length
|
|
115
|
+
*/
|
|
116
|
+
function truncate(str, maxLen) {
|
|
117
|
+
if (!str) return str;
|
|
118
|
+
if (str.length <= maxLen) return str;
|
|
119
|
+
return str.substring(0, maxLen) + `... [truncated ${str.length - maxLen} chars]`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Write log entry
|
|
124
|
+
*/
|
|
125
|
+
function writeLog(entry) {
|
|
126
|
+
try {
|
|
127
|
+
ensureLogDir();
|
|
128
|
+
const line = JSON.stringify(entry) + '\n';
|
|
129
|
+
fs.appendFileSync(LOG_FILE, line, 'utf8');
|
|
130
|
+
} catch (e) {
|
|
131
|
+
console.error(`[decision-logger] Failed to write log: ${e.message}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Main hook handler
|
|
137
|
+
*/
|
|
138
|
+
module.exports = async function autonomousDecisionLogger(context) {
|
|
139
|
+
const approve = () => ({ continue: true });
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const config = loadConfig();
|
|
143
|
+
|
|
144
|
+
if (!config.enabled) {
|
|
145
|
+
return approve();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Parse hook input
|
|
149
|
+
let input;
|
|
150
|
+
try {
|
|
151
|
+
input = JSON.parse(process.env.CLAUDE_HOOK_INPUT || '{}');
|
|
152
|
+
} catch (e) {
|
|
153
|
+
return approve();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const { tool_name, tool_input, tool_output } = input;
|
|
157
|
+
|
|
158
|
+
if (!tool_name) {
|
|
159
|
+
return approve();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Check if we should log this tool
|
|
163
|
+
if (!shouldLog(tool_name, config)) {
|
|
164
|
+
return approve();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Check rotation periodically
|
|
168
|
+
if (Math.random() < 0.1) { // 10% chance to check
|
|
169
|
+
checkRotation(config);
|
|
170
|
+
cleanOldLogs(config);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Build log entry
|
|
174
|
+
const entry = {
|
|
175
|
+
timestamp: new Date().toISOString(),
|
|
176
|
+
tool: tool_name,
|
|
177
|
+
input: tool_input,
|
|
178
|
+
success: true, // PostToolUse means it succeeded
|
|
179
|
+
session: process.env.CLAUDE_SESSION_ID || 'unknown',
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// Optionally include output
|
|
183
|
+
if (config.include_output && tool_output) {
|
|
184
|
+
const outputStr = typeof tool_output === 'string'
|
|
185
|
+
? tool_output
|
|
186
|
+
: JSON.stringify(tool_output);
|
|
187
|
+
entry.output = truncate(outputStr, config.max_output_chars);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Add file path for file operations
|
|
191
|
+
if (tool_input) {
|
|
192
|
+
if (tool_input.file_path) {
|
|
193
|
+
entry.file = tool_input.file_path;
|
|
194
|
+
} else if (tool_input.path) {
|
|
195
|
+
entry.file = tool_input.path;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Write log
|
|
200
|
+
writeLog(entry);
|
|
201
|
+
|
|
202
|
+
return approve();
|
|
203
|
+
} catch (error) {
|
|
204
|
+
console.error(`[decision-logger] Error: ${error.message}`);
|
|
205
|
+
return approve();
|
|
206
|
+
}
|
|
207
|
+
};
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Branch Merge Checker Hook
|
|
4
|
+
*
|
|
5
|
+
* Validates main branch sync status before deployments.
|
|
6
|
+
* Ensures clean working directory and proper sync with origin.
|
|
7
|
+
*
|
|
8
|
+
* Event: UserPromptSubmit
|
|
9
|
+
* Trigger: Deployment commands or manual invocation
|
|
10
|
+
*
|
|
11
|
+
* Configuration: Reads from .claude/config/hooks-config.json
|
|
12
|
+
*
|
|
13
|
+
* Features:
|
|
14
|
+
* - Verifies current branch is main/master
|
|
15
|
+
* - Checks sync status with origin
|
|
16
|
+
* - Detects uncommitted changes
|
|
17
|
+
* - Generates actionable recommendations
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const { execSync } = require('child_process');
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
|
|
24
|
+
// Default configuration
|
|
25
|
+
const DEFAULT_CONFIG = {
|
|
26
|
+
enabled: true,
|
|
27
|
+
block_on_dirty: false, // Block deployments if working directory dirty
|
|
28
|
+
block_on_behind: false, // Block deployments if behind origin
|
|
29
|
+
main_branch: null, // Auto-detect if null (main or master)
|
|
30
|
+
check_on_deploy_commands: true, // Auto-check on deploy-related commands
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Paths
|
|
34
|
+
const CONFIG_PATH = path.join(process.cwd(), '.claude', 'config', 'hooks-config.json');
|
|
35
|
+
|
|
36
|
+
// Deploy-related trigger words
|
|
37
|
+
const DEPLOY_TRIGGERS = [
|
|
38
|
+
'deploy',
|
|
39
|
+
'/deploy',
|
|
40
|
+
'railway',
|
|
41
|
+
'cloudflare',
|
|
42
|
+
'wrangler',
|
|
43
|
+
'publish',
|
|
44
|
+
'release',
|
|
45
|
+
'production',
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Load configuration
|
|
50
|
+
*/
|
|
51
|
+
function loadConfig() {
|
|
52
|
+
try {
|
|
53
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
54
|
+
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
|
55
|
+
return { ...DEFAULT_CONFIG, ...(config.branch_checker || {}) };
|
|
56
|
+
}
|
|
57
|
+
} catch (e) {
|
|
58
|
+
// Use defaults
|
|
59
|
+
}
|
|
60
|
+
return DEFAULT_CONFIG;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Execute a git command and return output
|
|
65
|
+
*/
|
|
66
|
+
function git(command) {
|
|
67
|
+
try {
|
|
68
|
+
return execSync(`git ${command}`, {
|
|
69
|
+
cwd: process.cwd(),
|
|
70
|
+
encoding: 'utf8',
|
|
71
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
72
|
+
}).trim();
|
|
73
|
+
} catch (error) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get current branch name
|
|
80
|
+
*/
|
|
81
|
+
function getCurrentBranch() {
|
|
82
|
+
return git('branch --show-current');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Detect main branch (main or master)
|
|
87
|
+
*/
|
|
88
|
+
function getMainBranch(configBranch) {
|
|
89
|
+
if (configBranch) return configBranch;
|
|
90
|
+
return git('rev-parse --verify main') ? 'main' : 'master';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Check if working directory has uncommitted changes
|
|
95
|
+
*/
|
|
96
|
+
function hasUncommittedChanges() {
|
|
97
|
+
try {
|
|
98
|
+
const output = execSync('git status --porcelain', {
|
|
99
|
+
cwd: process.cwd(),
|
|
100
|
+
encoding: 'utf8',
|
|
101
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
102
|
+
});
|
|
103
|
+
return output.trim().length > 0;
|
|
104
|
+
} catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get ahead/behind counts relative to origin
|
|
111
|
+
*/
|
|
112
|
+
function getOriginDelta(mainBranch) {
|
|
113
|
+
const originBranch = `origin/${mainBranch}`;
|
|
114
|
+
|
|
115
|
+
const ahead = git(`rev-list --count ${originBranch}..${mainBranch}`);
|
|
116
|
+
const behind = git(`rev-list --count ${mainBranch}..${originBranch}`);
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
ahead: parseInt(ahead || '0', 10),
|
|
120
|
+
behind: parseInt(behind || '0', 10),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Analyze branch status
|
|
126
|
+
*/
|
|
127
|
+
function analyzeBranches(config) {
|
|
128
|
+
const currentBranch = getCurrentBranch();
|
|
129
|
+
const mainBranch = getMainBranch(config.main_branch);
|
|
130
|
+
const delta = getOriginDelta(mainBranch);
|
|
131
|
+
const hasChanges = hasUncommittedChanges();
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
currentBranch,
|
|
135
|
+
mainBranch,
|
|
136
|
+
isOnMain: currentBranch === mainBranch,
|
|
137
|
+
ahead: delta.ahead,
|
|
138
|
+
behind: delta.behind,
|
|
139
|
+
hasChanges,
|
|
140
|
+
isClean: !hasChanges && delta.ahead === 0 && delta.behind === 0,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Generate status report
|
|
146
|
+
*/
|
|
147
|
+
function generateReport(analysis) {
|
|
148
|
+
const lines = [];
|
|
149
|
+
|
|
150
|
+
lines.push('');
|
|
151
|
+
lines.push('╔══════════════════════════════════════════════════════════════════╗');
|
|
152
|
+
lines.push('║ BRANCH STATUS CHECK ║');
|
|
153
|
+
lines.push('╚══════════════════════════════════════════════════════════════════╝');
|
|
154
|
+
lines.push('');
|
|
155
|
+
|
|
156
|
+
// Branch status
|
|
157
|
+
if (!analysis.isOnMain) {
|
|
158
|
+
lines.push(`⚠️ NOT ON MAIN: Currently on '${analysis.currentBranch}', expected '${analysis.mainBranch}'`);
|
|
159
|
+
} else {
|
|
160
|
+
lines.push(`✅ BRANCH: On ${analysis.mainBranch}`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Sync status
|
|
164
|
+
if (analysis.behind > 0) {
|
|
165
|
+
lines.push(`⚠️ BEHIND: ${analysis.behind} commit(s) behind origin/${analysis.mainBranch}`);
|
|
166
|
+
}
|
|
167
|
+
if (analysis.ahead > 0) {
|
|
168
|
+
lines.push(`📤 AHEAD: ${analysis.ahead} commit(s) ahead of origin/${analysis.mainBranch}`);
|
|
169
|
+
}
|
|
170
|
+
if (analysis.ahead === 0 && analysis.behind === 0) {
|
|
171
|
+
lines.push('✅ SYNC: In sync with origin');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Working directory
|
|
175
|
+
if (analysis.hasChanges) {
|
|
176
|
+
lines.push('⚠️ DIRTY: Uncommitted changes detected');
|
|
177
|
+
} else {
|
|
178
|
+
lines.push('✅ CLEAN: No uncommitted changes');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
lines.push('');
|
|
182
|
+
|
|
183
|
+
// Overall status
|
|
184
|
+
if (analysis.isClean && analysis.isOnMain) {
|
|
185
|
+
lines.push('✅ READY FOR DEPLOYMENT');
|
|
186
|
+
} else {
|
|
187
|
+
lines.push('⚠️ NEEDS ATTENTION BEFORE DEPLOYMENT');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
lines.push('');
|
|
191
|
+
return lines.join('\n');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Check if prompt contains deployment triggers
|
|
196
|
+
*/
|
|
197
|
+
function isDeploymentCommand(prompt) {
|
|
198
|
+
const lowerPrompt = (prompt || '').toLowerCase();
|
|
199
|
+
return DEPLOY_TRIGGERS.some(trigger => lowerPrompt.includes(trigger));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Main hook handler
|
|
204
|
+
*/
|
|
205
|
+
module.exports = async function branchMergeChecker(context) {
|
|
206
|
+
const approve = () => ({ continue: true });
|
|
207
|
+
const block = (reason) => ({ continue: false, message: reason });
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
const config = loadConfig();
|
|
211
|
+
|
|
212
|
+
if (!config.enabled) {
|
|
213
|
+
return approve();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Parse hook input
|
|
217
|
+
let input;
|
|
218
|
+
try {
|
|
219
|
+
input = JSON.parse(process.env.CLAUDE_HOOK_INPUT || '{}');
|
|
220
|
+
} catch (e) {
|
|
221
|
+
return approve();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const userPrompt = input.user_prompt || input.prompt || '';
|
|
225
|
+
|
|
226
|
+
// Check if this is a deployment command
|
|
227
|
+
if (config.check_on_deploy_commands && !isDeploymentCommand(userPrompt)) {
|
|
228
|
+
return approve();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Analyze branch status
|
|
232
|
+
const analysis = analyzeBranches(config);
|
|
233
|
+
|
|
234
|
+
// Generate and output report
|
|
235
|
+
const report = generateReport(analysis);
|
|
236
|
+
console.log(report);
|
|
237
|
+
|
|
238
|
+
// Check blocking conditions
|
|
239
|
+
if (config.block_on_dirty && analysis.hasChanges) {
|
|
240
|
+
return block('Deployment blocked: Uncommitted changes detected. Commit or stash your changes first.');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (config.block_on_behind && analysis.behind > 0) {
|
|
244
|
+
return block(`Deployment blocked: Branch is ${analysis.behind} commit(s) behind origin. Run 'git pull' first.`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return approve();
|
|
248
|
+
} catch (error) {
|
|
249
|
+
console.error(`[branch-checker] Error: ${error.message}`);
|
|
250
|
+
return approve();
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
// Direct execution support
|
|
255
|
+
if (require.main === module) {
|
|
256
|
+
const config = loadConfig();
|
|
257
|
+
const analysis = analyzeBranches(config);
|
|
258
|
+
console.log(generateReport(analysis));
|
|
259
|
+
|
|
260
|
+
if (!analysis.isOnMain) {
|
|
261
|
+
console.log(`Action: git checkout ${analysis.mainBranch}`);
|
|
262
|
+
}
|
|
263
|
+
if (analysis.behind > 0) {
|
|
264
|
+
console.log(`Action: git pull origin ${analysis.mainBranch}`);
|
|
265
|
+
}
|
|
266
|
+
if (analysis.hasChanges) {
|
|
267
|
+
console.log('Action: git status && git add . && git commit -m "..."');
|
|
268
|
+
}
|
|
269
|
+
if (analysis.ahead > 0) {
|
|
270
|
+
console.log(`Action: git push origin ${analysis.mainBranch}`);
|
|
271
|
+
}
|
|
272
|
+
}
|