claude-cli-advanced-starter-pack 1.1.0 → 1.8.1
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 +339 -351
- 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,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
|
+
};
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Issue Completion Detector Hook
|
|
3
|
+
*
|
|
4
|
+
* Detects natural language indicators of task/issue completion.
|
|
5
|
+
* Auto-triggers deployment pipeline when completion phrases are detected.
|
|
6
|
+
* Integrates with GitHub to update issue status.
|
|
7
|
+
*
|
|
8
|
+
* Event: UserPromptSubmit
|
|
9
|
+
*
|
|
10
|
+
* Configuration: Reads from .claude/config/hooks-config.json
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const { execSync } = require('child_process');
|
|
16
|
+
|
|
17
|
+
// Default configuration
|
|
18
|
+
const DEFAULT_CONFIG = {
|
|
19
|
+
enabled: true,
|
|
20
|
+
auto_deploy: false, // Auto-trigger deployment on completion
|
|
21
|
+
update_github: true, // Update GitHub issue status
|
|
22
|
+
completion_phrases: [
|
|
23
|
+
'task complete',
|
|
24
|
+
'task completed',
|
|
25
|
+
'issue resolved',
|
|
26
|
+
'issue fixed',
|
|
27
|
+
'done with this',
|
|
28
|
+
'finished implementing',
|
|
29
|
+
'ready for review',
|
|
30
|
+
'ready to merge',
|
|
31
|
+
'all tests pass',
|
|
32
|
+
'implementation complete',
|
|
33
|
+
],
|
|
34
|
+
deploy_command: '/deploy-full', // Command to trigger on completion
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Paths
|
|
38
|
+
const CONFIG_PATH = path.join(process.cwd(), '.claude', 'config', 'hooks-config.json');
|
|
39
|
+
const STATE_PATH = path.join(process.cwd(), '.claude', 'config', 'completion-detector-state.json');
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Load configuration with defaults
|
|
43
|
+
*/
|
|
44
|
+
function loadConfig() {
|
|
45
|
+
try {
|
|
46
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
47
|
+
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
|
48
|
+
return { ...DEFAULT_CONFIG, ...(config.issue_completion || {}) };
|
|
49
|
+
}
|
|
50
|
+
} catch (e) {
|
|
51
|
+
// Use defaults
|
|
52
|
+
}
|
|
53
|
+
return DEFAULT_CONFIG;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Load state
|
|
58
|
+
*/
|
|
59
|
+
function loadState() {
|
|
60
|
+
try {
|
|
61
|
+
if (fs.existsSync(STATE_PATH)) {
|
|
62
|
+
return JSON.parse(fs.readFileSync(STATE_PATH, 'utf8'));
|
|
63
|
+
}
|
|
64
|
+
} catch (e) {
|
|
65
|
+
// Fresh state
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
last_detection: null,
|
|
69
|
+
completions_detected: 0,
|
|
70
|
+
deployments_triggered: 0,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Save state
|
|
76
|
+
*/
|
|
77
|
+
function saveState(state) {
|
|
78
|
+
try {
|
|
79
|
+
const dir = path.dirname(STATE_PATH);
|
|
80
|
+
if (!fs.existsSync(dir)) {
|
|
81
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
82
|
+
}
|
|
83
|
+
fs.writeFileSync(STATE_PATH, JSON.stringify(state, null, 2), 'utf8');
|
|
84
|
+
} catch (e) {
|
|
85
|
+
// Silent
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Check if message contains completion phrase
|
|
91
|
+
*/
|
|
92
|
+
function detectCompletion(message, config) {
|
|
93
|
+
if (!message) return null;
|
|
94
|
+
|
|
95
|
+
const lowerMessage = message.toLowerCase();
|
|
96
|
+
|
|
97
|
+
for (const phrase of config.completion_phrases) {
|
|
98
|
+
if (lowerMessage.includes(phrase.toLowerCase())) {
|
|
99
|
+
return phrase;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get current issue number from branch
|
|
108
|
+
*/
|
|
109
|
+
function getCurrentIssue() {
|
|
110
|
+
try {
|
|
111
|
+
const branch = execSync('git rev-parse --abbrev-ref HEAD', {
|
|
112
|
+
encoding: 'utf8',
|
|
113
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
114
|
+
}).trim();
|
|
115
|
+
|
|
116
|
+
const match = branch.match(/issue-(\d+)/i) || branch.match(/(\d+)-/);
|
|
117
|
+
if (match) {
|
|
118
|
+
return parseInt(match[1], 10);
|
|
119
|
+
}
|
|
120
|
+
} catch (e) {
|
|
121
|
+
// Not in git repo or error
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Update GitHub issue (if gh CLI available)
|
|
128
|
+
*/
|
|
129
|
+
function updateGitHubIssue(issueNumber, message) {
|
|
130
|
+
try {
|
|
131
|
+
// Add comment about completion
|
|
132
|
+
execSync(`gh issue comment ${issueNumber} --body "Task marked as complete via Claude CLI"`, {
|
|
133
|
+
encoding: 'utf8',
|
|
134
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
135
|
+
timeout: 10000,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
console.log(`[issue-completion-detector] Updated GitHub issue #${issueNumber}`);
|
|
139
|
+
return true;
|
|
140
|
+
} catch (e) {
|
|
141
|
+
console.log(`[issue-completion-detector] Could not update GitHub issue: ${e.message}`);
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Main hook handler
|
|
148
|
+
*/
|
|
149
|
+
module.exports = async function issueCompletionDetector(context) {
|
|
150
|
+
const approve = () => ({ continue: true });
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const config = loadConfig();
|
|
154
|
+
|
|
155
|
+
if (!config.enabled) {
|
|
156
|
+
return approve();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Parse hook input
|
|
160
|
+
let userMessage = '';
|
|
161
|
+
try {
|
|
162
|
+
const input = JSON.parse(process.env.CLAUDE_HOOK_INPUT || '{}');
|
|
163
|
+
userMessage = input.prompt || input.message || '';
|
|
164
|
+
} catch (e) {
|
|
165
|
+
return approve();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Check for completion phrase
|
|
169
|
+
const detectedPhrase = detectCompletion(userMessage, config);
|
|
170
|
+
|
|
171
|
+
if (!detectedPhrase) {
|
|
172
|
+
return approve();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Completion detected!
|
|
176
|
+
console.log(`[issue-completion-detector] Completion detected: "${detectedPhrase}"`);
|
|
177
|
+
|
|
178
|
+
const state = loadState();
|
|
179
|
+
state.last_detection = new Date().toISOString();
|
|
180
|
+
state.completions_detected++;
|
|
181
|
+
|
|
182
|
+
// Get current issue
|
|
183
|
+
const issueNumber = getCurrentIssue();
|
|
184
|
+
|
|
185
|
+
// Update GitHub if configured
|
|
186
|
+
if (config.update_github && issueNumber) {
|
|
187
|
+
updateGitHubIssue(issueNumber, userMessage);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Log deployment suggestion
|
|
191
|
+
if (config.auto_deploy) {
|
|
192
|
+
console.log(`[issue-completion-detector] Auto-deploy enabled. Suggested: ${config.deploy_command}`);
|
|
193
|
+
state.deployments_triggered++;
|
|
194
|
+
} else {
|
|
195
|
+
console.log(`[issue-completion-detector] Consider running: ${config.deploy_command}`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
saveState(state);
|
|
199
|
+
|
|
200
|
+
return approve();
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.error(`[issue-completion-detector] Error: ${error.message}`);
|
|
203
|
+
return approve();
|
|
204
|
+
}
|
|
205
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CCASP Panel Queue Reader Hook
|
|
3
|
+
*
|
|
4
|
+
* Reads commands from the CCASP Panel queue and injects them
|
|
5
|
+
* when user submits an empty prompt (just presses Enter).
|
|
6
|
+
*
|
|
7
|
+
* Install: ccasp install-panel-hook
|
|
8
|
+
* Or copy to: .claude/hooks/panel-queue-reader.js
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
12
|
+
import { join } from 'path';
|
|
13
|
+
import { homedir } from 'os';
|
|
14
|
+
|
|
15
|
+
// Queue file location
|
|
16
|
+
const QUEUE_FILE = join(homedir(), '.claude', 'ccasp-panel', 'command-queue.json');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get the next pending command from queue
|
|
20
|
+
*/
|
|
21
|
+
function getNextPendingCommand() {
|
|
22
|
+
if (!existsSync(QUEUE_FILE)) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const queue = JSON.parse(readFileSync(QUEUE_FILE, 'utf8'));
|
|
28
|
+
return queue.find(entry => entry.status === 'pending') || null;
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Mark a command as processed
|
|
36
|
+
*/
|
|
37
|
+
function markCommandProcessed(id) {
|
|
38
|
+
if (!existsSync(QUEUE_FILE)) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const queue = JSON.parse(readFileSync(QUEUE_FILE, 'utf8'));
|
|
44
|
+
const entry = queue.find(e => e.id === id);
|
|
45
|
+
if (entry) {
|
|
46
|
+
entry.status = 'completed';
|
|
47
|
+
entry.processedAt = Date.now();
|
|
48
|
+
writeFileSync(QUEUE_FILE, JSON.stringify(queue, null, 2), 'utf8');
|
|
49
|
+
}
|
|
50
|
+
} catch {
|
|
51
|
+
// Ignore errors
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Hook handler for UserPromptSubmit
|
|
57
|
+
*/
|
|
58
|
+
export default async function panelQueueReader(params) {
|
|
59
|
+
const { prompt } = params;
|
|
60
|
+
|
|
61
|
+
// Only trigger on empty prompts (user just pressed Enter)
|
|
62
|
+
if (prompt && prompt.trim().length > 0) {
|
|
63
|
+
return { proceed: true };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Check for pending commands
|
|
67
|
+
const pendingCommand = getNextPendingCommand();
|
|
68
|
+
|
|
69
|
+
if (pendingCommand) {
|
|
70
|
+
// Mark as processed immediately
|
|
71
|
+
markCommandProcessed(pendingCommand.id);
|
|
72
|
+
|
|
73
|
+
// Return the command to inject
|
|
74
|
+
return {
|
|
75
|
+
proceed: true,
|
|
76
|
+
updatedPrompt: pendingCommand.command + (pendingCommand.args ? ' ' + pendingCommand.args : ''),
|
|
77
|
+
message: `[CCASP Panel] Executing: ${pendingCommand.label || pendingCommand.command}`
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// No pending commands - let empty prompt through (will show help or similar)
|
|
82
|
+
return { proceed: true };
|
|
83
|
+
}
|