claude-cli-advanced-starter-pack 1.0.16 → 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 +309 -80
- 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 +209 -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/context-injector.template.js +261 -0
- package/templates/hooks/git-commit-tracker.template.js +267 -0
- package/templates/hooks/happy-mode-detector.template.js +214 -0
- package/templates/hooks/happy-title-generator.template.js +260 -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-budget-loader.template.js +234 -0
- package/templates/hooks/token-usage-monitor.template.js +193 -0
- package/templates/hooks/tool-output-cacher.template.js +219 -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,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Injector Hook
|
|
3
|
+
*
|
|
4
|
+
* Injects prior session context for seamless resumption.
|
|
5
|
+
* Loads checkpoints, recent progress, and active features.
|
|
6
|
+
* Enables continuity across multi-day projects.
|
|
7
|
+
*
|
|
8
|
+
* Event: UserPromptSubmit (runs once per session)
|
|
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 (can be overridden by hooks-config.json)
|
|
17
|
+
const DEFAULT_CONFIG = {
|
|
18
|
+
recent_completed_tasks: 3, // Number of completed tasks to show
|
|
19
|
+
next_pending_tasks: 2, // Number of pending tasks to show
|
|
20
|
+
active_features_limit: 3, // Number of active features to show
|
|
21
|
+
recent_agents_limit: 5, // Number of recent agents to show
|
|
22
|
+
max_checkpoint_age_hours: 48, // Ignore checkpoints older than this
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Paths
|
|
26
|
+
const CONFIG_PATH = path.join(process.cwd(), '.claude', 'config', 'hooks-config.json');
|
|
27
|
+
const CHECKPOINT_PATH = path.join(process.cwd(), '.claude', 'checkpoints', 'latest.json');
|
|
28
|
+
const FEATURE_TRACKING_PATH = path.join(process.cwd(), '.claude', 'config', 'feature-tracking.json');
|
|
29
|
+
const AGENT_LOG_PATH = path.join(process.cwd(), '.claude', 'logs', 'agent-activity.json');
|
|
30
|
+
const SESSION_MARKER = path.join(process.cwd(), '.claude', 'config', '.context-injected');
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Load configuration with defaults
|
|
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.context_injector || {}) };
|
|
40
|
+
}
|
|
41
|
+
} catch (e) {
|
|
42
|
+
// Use defaults on error
|
|
43
|
+
}
|
|
44
|
+
return DEFAULT_CONFIG;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Check if we've already injected context this session
|
|
49
|
+
*/
|
|
50
|
+
function hasInjectedThisSession() {
|
|
51
|
+
try {
|
|
52
|
+
if (fs.existsSync(SESSION_MARKER)) {
|
|
53
|
+
const content = fs.readFileSync(SESSION_MARKER, 'utf8');
|
|
54
|
+
const timestamp = parseInt(content, 10);
|
|
55
|
+
// Session valid for 4 hours
|
|
56
|
+
if (Date.now() - timestamp < 4 * 60 * 60 * 1000) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
} catch (e) {
|
|
61
|
+
// Continue with injection
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Mark session as injected
|
|
68
|
+
*/
|
|
69
|
+
function markSessionInjected() {
|
|
70
|
+
try {
|
|
71
|
+
const dir = path.dirname(SESSION_MARKER);
|
|
72
|
+
if (!fs.existsSync(dir)) {
|
|
73
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
74
|
+
}
|
|
75
|
+
fs.writeFileSync(SESSION_MARKER, Date.now().toString(), 'utf8');
|
|
76
|
+
} catch (e) {
|
|
77
|
+
// Silent failure
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Load latest checkpoint
|
|
83
|
+
*/
|
|
84
|
+
function loadCheckpoint(config) {
|
|
85
|
+
try {
|
|
86
|
+
if (!fs.existsSync(CHECKPOINT_PATH)) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const checkpoint = JSON.parse(fs.readFileSync(CHECKPOINT_PATH, 'utf8'));
|
|
91
|
+
|
|
92
|
+
// Check age
|
|
93
|
+
if (checkpoint.created_at) {
|
|
94
|
+
const age = Date.now() - new Date(checkpoint.created_at).getTime();
|
|
95
|
+
const maxAge = config.max_checkpoint_age_hours * 60 * 60 * 1000;
|
|
96
|
+
if (age > maxAge) {
|
|
97
|
+
return null; // Checkpoint too old
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return checkpoint;
|
|
102
|
+
} catch (e) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Load feature tracking
|
|
109
|
+
*/
|
|
110
|
+
function loadFeatureTracking() {
|
|
111
|
+
try {
|
|
112
|
+
if (fs.existsSync(FEATURE_TRACKING_PATH)) {
|
|
113
|
+
return JSON.parse(fs.readFileSync(FEATURE_TRACKING_PATH, 'utf8'));
|
|
114
|
+
}
|
|
115
|
+
} catch (e) {
|
|
116
|
+
// No feature tracking
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Load recent agent activity
|
|
123
|
+
*/
|
|
124
|
+
function loadAgentActivity(config) {
|
|
125
|
+
try {
|
|
126
|
+
if (fs.existsSync(AGENT_LOG_PATH)) {
|
|
127
|
+
const activity = JSON.parse(fs.readFileSync(AGENT_LOG_PATH, 'utf8'));
|
|
128
|
+
// Return most recent agents
|
|
129
|
+
if (Array.isArray(activity)) {
|
|
130
|
+
return activity.slice(-config.recent_agents_limit);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} catch (e) {
|
|
134
|
+
// No agent activity
|
|
135
|
+
}
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Format task list for display
|
|
141
|
+
*/
|
|
142
|
+
function formatTaskList(tasks, limit, status) {
|
|
143
|
+
if (!tasks || !Array.isArray(tasks)) return '';
|
|
144
|
+
|
|
145
|
+
const filtered = tasks.filter(t => t.status === status).slice(0, limit);
|
|
146
|
+
if (filtered.length === 0) return '';
|
|
147
|
+
|
|
148
|
+
return filtered.map(t => ` - ${t.title || t.name || 'Unknown task'}`).join('\n');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Format feature list for display
|
|
153
|
+
*/
|
|
154
|
+
function formatFeatureList(features, limit) {
|
|
155
|
+
if (!features || !Array.isArray(features)) return '';
|
|
156
|
+
|
|
157
|
+
const active = features.filter(f => f.status === 'in_progress').slice(0, limit);
|
|
158
|
+
if (active.length === 0) return '';
|
|
159
|
+
|
|
160
|
+
return active.map(f => ` - ${f.name}: ${f.progress || 0}%`).join('\n');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Format agent list for display
|
|
165
|
+
*/
|
|
166
|
+
function formatAgentList(agents) {
|
|
167
|
+
if (!agents || agents.length === 0) return '';
|
|
168
|
+
|
|
169
|
+
return agents.map(a => {
|
|
170
|
+
const name = a.name || a.type || 'agent';
|
|
171
|
+
const status = a.success ? 'completed' : 'failed';
|
|
172
|
+
return ` - ${name}: ${status}`;
|
|
173
|
+
}).join('\n');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Build context message
|
|
178
|
+
*/
|
|
179
|
+
function buildContextMessage(checkpoint, features, agents, config) {
|
|
180
|
+
const sections = [];
|
|
181
|
+
|
|
182
|
+
// Add checkpoint progress
|
|
183
|
+
if (checkpoint) {
|
|
184
|
+
const completed = formatTaskList(checkpoint.tasks, config.recent_completed_tasks, 'completed');
|
|
185
|
+
const pending = formatTaskList(checkpoint.tasks, config.next_pending_tasks, 'pending');
|
|
186
|
+
|
|
187
|
+
if (completed) {
|
|
188
|
+
sections.push(`**Recent Progress:**\n${completed}`);
|
|
189
|
+
}
|
|
190
|
+
if (pending) {
|
|
191
|
+
sections.push(`**Next Tasks:**\n${pending}`);
|
|
192
|
+
}
|
|
193
|
+
if (checkpoint.current_phase) {
|
|
194
|
+
sections.push(`**Current Phase:** ${checkpoint.current_phase}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Add active features
|
|
199
|
+
if (features) {
|
|
200
|
+
const featureList = formatFeatureList(features.features || features, config.active_features_limit);
|
|
201
|
+
if (featureList) {
|
|
202
|
+
sections.push(`**Active Features:**\n${featureList}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Add recent agents
|
|
207
|
+
if (agents && agents.length > 0) {
|
|
208
|
+
const agentList = formatAgentList(agents);
|
|
209
|
+
if (agentList) {
|
|
210
|
+
sections.push(`**Recent Agents:**\n${agentList}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return sections.join('\n\n');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Main hook handler
|
|
219
|
+
*/
|
|
220
|
+
module.exports = async function contextInjector(context) {
|
|
221
|
+
// Always continue - never block
|
|
222
|
+
const approve = () => ({ continue: true });
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
// Check if already injected this session
|
|
226
|
+
if (hasInjectedThisSession()) {
|
|
227
|
+
return approve();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Mark session as injected
|
|
231
|
+
markSessionInjected();
|
|
232
|
+
|
|
233
|
+
const config = loadConfig();
|
|
234
|
+
|
|
235
|
+
// Load context sources
|
|
236
|
+
const checkpoint = loadCheckpoint(config);
|
|
237
|
+
const features = loadFeatureTracking();
|
|
238
|
+
const agents = loadAgentActivity(config);
|
|
239
|
+
|
|
240
|
+
// Check if we have any context to inject
|
|
241
|
+
if (!checkpoint && !features && agents.length === 0) {
|
|
242
|
+
console.log('[context-injector] No prior context found');
|
|
243
|
+
return approve();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Build context message
|
|
247
|
+
const message = buildContextMessage(checkpoint, features, agents, config);
|
|
248
|
+
|
|
249
|
+
if (message) {
|
|
250
|
+
console.log('[context-injector] Session context loaded:');
|
|
251
|
+
console.log('---');
|
|
252
|
+
console.log(message);
|
|
253
|
+
console.log('---');
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return approve();
|
|
257
|
+
} catch (error) {
|
|
258
|
+
console.error(`[context-injector] Error: ${error.message}`);
|
|
259
|
+
return approve();
|
|
260
|
+
}
|
|
261
|
+
};
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Git Commit Tracker Hook
|
|
4
|
+
*
|
|
5
|
+
* Tracks git commits during phased development and updates PROGRESS.json.
|
|
6
|
+
* Enables rollback support by maintaining commit history per phase.
|
|
7
|
+
*
|
|
8
|
+
* Event: PostToolUse (Bash)
|
|
9
|
+
* Trigger: Commands containing "git commit"
|
|
10
|
+
*
|
|
11
|
+
* Configuration: Reads from .claude/config/hooks-config.json
|
|
12
|
+
*
|
|
13
|
+
* Features:
|
|
14
|
+
* - Extracts commit hashes from git output
|
|
15
|
+
* - Detects phase/task from commit messages
|
|
16
|
+
* - Updates PROGRESS.json git_tracking section
|
|
17
|
+
* - Maintains rollback-capable commit history
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
|
|
23
|
+
// Default configuration
|
|
24
|
+
const DEFAULT_CONFIG = {
|
|
25
|
+
enabled: true,
|
|
26
|
+
auto_detect_phase: true, // Detect phase from commit message
|
|
27
|
+
update_progress_json: true, // Update PROGRESS.json with commits
|
|
28
|
+
track_file_changes: false, // Include list of modified files
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Paths
|
|
32
|
+
const CONFIG_PATH = path.join(process.cwd(), '.claude', 'config', 'hooks-config.json');
|
|
33
|
+
const STATE_FILE = path.join(process.cwd(), '.claude', 'hooks', 'config', 'phase-dev-state.json');
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Load configuration
|
|
37
|
+
*/
|
|
38
|
+
function loadConfig() {
|
|
39
|
+
try {
|
|
40
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
41
|
+
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
|
42
|
+
return { ...DEFAULT_CONFIG, ...(config.git_tracker || {}) };
|
|
43
|
+
}
|
|
44
|
+
} catch (e) {
|
|
45
|
+
// Use defaults
|
|
46
|
+
}
|
|
47
|
+
return DEFAULT_CONFIG;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Load current phase development state
|
|
52
|
+
*/
|
|
53
|
+
function loadState() {
|
|
54
|
+
try {
|
|
55
|
+
if (fs.existsSync(STATE_FILE)) {
|
|
56
|
+
return JSON.parse(fs.readFileSync(STATE_FILE, 'utf8'));
|
|
57
|
+
}
|
|
58
|
+
} catch (e) {
|
|
59
|
+
// Use defaults
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
active_project: null,
|
|
63
|
+
current_phase: null,
|
|
64
|
+
current_section: null,
|
|
65
|
+
last_commit: null,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Save phase development state
|
|
71
|
+
*/
|
|
72
|
+
function saveState(state) {
|
|
73
|
+
try {
|
|
74
|
+
const dir = path.dirname(STATE_FILE);
|
|
75
|
+
if (!fs.existsSync(dir)) {
|
|
76
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
|
|
79
|
+
} catch (e) {
|
|
80
|
+
console.error(`[git-tracker] Error saving state: ${e.message}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Extract commit hash from git commit output
|
|
86
|
+
*/
|
|
87
|
+
function extractCommitHash(output) {
|
|
88
|
+
if (!output) return null;
|
|
89
|
+
|
|
90
|
+
// Pattern: [branch_name hash] message
|
|
91
|
+
const match = output.match(/\[[\w\/-]+\s+([a-f0-9]{7,40})\]/i);
|
|
92
|
+
if (match) return match[1];
|
|
93
|
+
|
|
94
|
+
// Alternative: HEAD is now at hash message
|
|
95
|
+
const altMatch = output.match(/HEAD is now at ([a-f0-9]{7,40})/i);
|
|
96
|
+
if (altMatch) return altMatch[1];
|
|
97
|
+
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Detect phase/section from commit message
|
|
103
|
+
*/
|
|
104
|
+
function detectPhaseFromMessage(message) {
|
|
105
|
+
if (!message) return null;
|
|
106
|
+
|
|
107
|
+
// Pattern: feat(project): Complete Phase N - ...
|
|
108
|
+
const phaseMatch = message.match(/Phase\s+(\d+)/i);
|
|
109
|
+
if (phaseMatch) {
|
|
110
|
+
return {
|
|
111
|
+
phase_number: parseInt(phaseMatch[1], 10),
|
|
112
|
+
is_phase_complete: message.toLowerCase().includes('complete phase'),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Pattern: P1-T1 style
|
|
117
|
+
const taskMatch = message.match(/P(\d+)-T(\d+)/i);
|
|
118
|
+
if (taskMatch) {
|
|
119
|
+
return {
|
|
120
|
+
phase_number: parseInt(taskMatch[1], 10),
|
|
121
|
+
task_id: `P${taskMatch[1]}-T${taskMatch[2]}`,
|
|
122
|
+
is_phase_complete: false,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Find PROGRESS.json for the active project
|
|
131
|
+
*/
|
|
132
|
+
function findProgressJson(projectSlug) {
|
|
133
|
+
const possiblePaths = [
|
|
134
|
+
path.join(process.cwd(), '.claude', 'docs', projectSlug, 'PROGRESS.json'),
|
|
135
|
+
path.join(process.cwd(), '.claude', 'commands', `phase-dev-${projectSlug}`, 'PROGRESS.json'),
|
|
136
|
+
path.join(process.cwd(), '.claude', 'docs', `phased-dev-${projectSlug}`, 'PROGRESS.json'),
|
|
137
|
+
path.join(process.cwd(), 'PROGRESS.json'),
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
for (const p of possiblePaths) {
|
|
141
|
+
if (fs.existsSync(p)) return p;
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Update PROGRESS.json with new commit information
|
|
148
|
+
*/
|
|
149
|
+
function updateProgressJson(progressPath, commitInfo) {
|
|
150
|
+
try {
|
|
151
|
+
const progress = JSON.parse(fs.readFileSync(progressPath, 'utf8'));
|
|
152
|
+
|
|
153
|
+
// Initialize git_tracking if not exists
|
|
154
|
+
if (!progress.git_tracking) {
|
|
155
|
+
progress.git_tracking = {
|
|
156
|
+
pre_start_commit: null,
|
|
157
|
+
phase_commits: [],
|
|
158
|
+
current_stable_commit: null,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Add new commit entry
|
|
163
|
+
const commitEntry = {
|
|
164
|
+
phase_number: commitInfo.phase_number || progress.current_phase || 1,
|
|
165
|
+
section_id: commitInfo.section_id || `commit-${Date.now()}`,
|
|
166
|
+
commit_hash: commitInfo.hash,
|
|
167
|
+
timestamp: new Date().toISOString(),
|
|
168
|
+
tasks_completed: commitInfo.tasks || [],
|
|
169
|
+
files_modified: commitInfo.files || [],
|
|
170
|
+
test_status: 'pending',
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
progress.git_tracking.phase_commits.push(commitEntry);
|
|
174
|
+
progress.git_tracking.current_stable_commit = commitInfo.hash;
|
|
175
|
+
progress.last_updated = new Date().toISOString();
|
|
176
|
+
|
|
177
|
+
fs.writeFileSync(progressPath, JSON.stringify(progress, null, 2));
|
|
178
|
+
return true;
|
|
179
|
+
} catch (e) {
|
|
180
|
+
console.error(`[git-tracker] Error updating PROGRESS.json: ${e.message}`);
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Main hook handler
|
|
187
|
+
*/
|
|
188
|
+
module.exports = async function gitCommitTracker(context) {
|
|
189
|
+
const approve = () => ({ continue: true });
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
const config = loadConfig();
|
|
193
|
+
|
|
194
|
+
if (!config.enabled) {
|
|
195
|
+
return approve();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Parse hook input
|
|
199
|
+
let input;
|
|
200
|
+
try {
|
|
201
|
+
input = JSON.parse(process.env.CLAUDE_HOOK_INPUT || '{}');
|
|
202
|
+
} catch (e) {
|
|
203
|
+
return approve();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const { tool_name, tool_input, tool_output } = input;
|
|
207
|
+
|
|
208
|
+
// Only process Bash tool with git commit commands
|
|
209
|
+
if (tool_name !== 'Bash') {
|
|
210
|
+
return approve();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const command = tool_input?.command || '';
|
|
214
|
+
if (!command.includes('git commit')) {
|
|
215
|
+
return approve();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Extract commit hash from output
|
|
219
|
+
const commitHash = extractCommitHash(tool_output);
|
|
220
|
+
if (!commitHash) {
|
|
221
|
+
return approve();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Load state
|
|
225
|
+
const state = loadState();
|
|
226
|
+
|
|
227
|
+
// Detect phase from commit message
|
|
228
|
+
const commitMessage = command.match(/-m\s+["']([^"']+)["']/)?.[1] || '';
|
|
229
|
+
const phaseInfo = config.auto_detect_phase ? detectPhaseFromMessage(commitMessage) : null;
|
|
230
|
+
|
|
231
|
+
// Update state with new commit
|
|
232
|
+
state.last_commit = {
|
|
233
|
+
hash: commitHash,
|
|
234
|
+
message: commitMessage,
|
|
235
|
+
timestamp: new Date().toISOString(),
|
|
236
|
+
phase_info: phaseInfo,
|
|
237
|
+
};
|
|
238
|
+
saveState(state);
|
|
239
|
+
|
|
240
|
+
// Update PROGRESS.json if we have an active project
|
|
241
|
+
if (config.update_progress_json && state.active_project) {
|
|
242
|
+
const progressPath = findProgressJson(state.active_project);
|
|
243
|
+
if (progressPath) {
|
|
244
|
+
const updated = updateProgressJson(progressPath, {
|
|
245
|
+
hash: commitHash,
|
|
246
|
+
phase_number: phaseInfo?.phase_number,
|
|
247
|
+
section_id: phaseInfo?.is_phase_complete
|
|
248
|
+
? `phase-${phaseInfo.phase_number}-complete`
|
|
249
|
+
: phaseInfo?.task_id || `commit-${commitHash.substring(0, 7)}`,
|
|
250
|
+
tasks: phaseInfo?.task_id ? [phaseInfo.task_id] : [],
|
|
251
|
+
files: [],
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
if (updated) {
|
|
255
|
+
console.log(`[git-tracker] Commit ${commitHash.substring(0, 7)} tracked in PROGRESS.json`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
} else {
|
|
259
|
+
console.log(`[git-tracker] Commit detected: ${commitHash.substring(0, 7)}`);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return approve();
|
|
263
|
+
} catch (error) {
|
|
264
|
+
console.error(`[git-tracker] Error: ${error.message}`);
|
|
265
|
+
return approve();
|
|
266
|
+
}
|
|
267
|
+
};
|