@jojonax/codex-copilot 1.5.3 → 1.6.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/LICENSE +21 -21
- package/README.md +144 -44
- package/bin/cli.js +189 -182
- package/package.json +39 -39
- package/src/commands/evolve.js +316 -316
- package/src/commands/fix.js +447 -399
- package/src/commands/init.js +298 -298
- package/src/commands/reset.js +61 -61
- package/src/commands/retry.js +190 -153
- package/src/commands/run.js +958 -905
- package/src/commands/skip.js +62 -62
- package/src/commands/status.js +95 -95
- package/src/commands/usage.js +361 -361
- package/src/utils/automator.js +279 -279
- package/src/utils/checkpoint.js +246 -160
- package/src/utils/detect-prd.js +137 -137
- package/src/utils/git.js +388 -133
- package/src/utils/github.js +486 -429
- package/src/utils/json.js +220 -197
- package/src/utils/logger.js +41 -41
- package/src/utils/prompt.js +49 -49
- package/src/utils/provider.js +770 -769
- package/src/utils/self-heal.js +330 -0
- package/src/utils/shell-bootstrap.js +404 -0
- package/src/utils/update-check.js +103 -103
package/src/commands/reset.js
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* codex-copilot reset - Reset state
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { existsSync, unlinkSync } from 'fs';
|
|
6
|
-
import { resolve } from 'path';
|
|
7
|
-
import { log } from '../utils/logger.js';
|
|
8
|
-
import { confirm, closePrompt } from '../utils/prompt.js';
|
|
9
|
-
import { createCheckpoint } from '../utils/checkpoint.js';
|
|
10
|
-
import { readJSON, writeJSON } from '../utils/json.js';
|
|
11
|
-
|
|
12
|
-
export async function reset(projectDir) {
|
|
13
|
-
const statePath = resolve(projectDir, '.codex-copilot/state.json');
|
|
14
|
-
const tasksPath = resolve(projectDir, '.codex-copilot/tasks.json');
|
|
15
|
-
|
|
16
|
-
if (!existsSync(statePath)) {
|
|
17
|
-
log.warn('Project not initialized, nothing to reset');
|
|
18
|
-
closePrompt();
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
log.title('🔄 Resetting Codex-Copilot state');
|
|
23
|
-
log.blank();
|
|
24
|
-
|
|
25
|
-
const checkpoint = createCheckpoint(projectDir);
|
|
26
|
-
const state = checkpoint.load();
|
|
27
|
-
|
|
28
|
-
// Show current checkpoint before reset
|
|
29
|
-
if (state.phase) {
|
|
30
|
-
log.info(`Current checkpoint: Task #${state.current_task} — ${state.phase} → ${state.phase_step}`);
|
|
31
|
-
log.blank();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const resetTasks = await confirm('Also reset all task statuses to pending?', false);
|
|
35
|
-
|
|
36
|
-
// Reset state using checkpoint manager
|
|
37
|
-
checkpoint.reset();
|
|
38
|
-
log.info('Execution state reset');
|
|
39
|
-
|
|
40
|
-
// Reset task statuses
|
|
41
|
-
if (resetTasks && existsSync(tasksPath)) {
|
|
42
|
-
const tasks = readJSON(tasksPath);
|
|
43
|
-
for (const task of tasks.tasks) {
|
|
44
|
-
task.status = 'pending';
|
|
45
|
-
}
|
|
46
|
-
writeJSON(tasksPath, tasks);
|
|
47
|
-
log.info('Task statuses reset');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Clean up temp files
|
|
51
|
-
const promptPath = resolve(projectDir, '.codex-copilot/_current_prompt.md');
|
|
52
|
-
if (existsSync(promptPath)) unlinkSync(promptPath);
|
|
53
|
-
|
|
54
|
-
// Clean up temp state file if exists
|
|
55
|
-
const tempPath = resolve(projectDir, '.codex-copilot/state.json.tmp');
|
|
56
|
-
if (existsSync(tempPath)) unlinkSync(tempPath);
|
|
57
|
-
|
|
58
|
-
log.blank();
|
|
59
|
-
log.info('✅ Reset complete. You can now run: codex-copilot run');
|
|
60
|
-
closePrompt();
|
|
61
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* codex-copilot reset - Reset state
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { existsSync, unlinkSync } from 'fs';
|
|
6
|
+
import { resolve } from 'path';
|
|
7
|
+
import { log } from '../utils/logger.js';
|
|
8
|
+
import { confirm, closePrompt } from '../utils/prompt.js';
|
|
9
|
+
import { createCheckpoint } from '../utils/checkpoint.js';
|
|
10
|
+
import { readJSON, writeJSON } from '../utils/json.js';
|
|
11
|
+
|
|
12
|
+
export async function reset(projectDir) {
|
|
13
|
+
const statePath = resolve(projectDir, '.codex-copilot/state.json');
|
|
14
|
+
const tasksPath = resolve(projectDir, '.codex-copilot/tasks.json');
|
|
15
|
+
|
|
16
|
+
if (!existsSync(statePath)) {
|
|
17
|
+
log.warn('Project not initialized, nothing to reset');
|
|
18
|
+
closePrompt();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
log.title('🔄 Resetting Codex-Copilot state');
|
|
23
|
+
log.blank();
|
|
24
|
+
|
|
25
|
+
const checkpoint = createCheckpoint(projectDir);
|
|
26
|
+
const state = checkpoint.load();
|
|
27
|
+
|
|
28
|
+
// Show current checkpoint before reset
|
|
29
|
+
if (state.phase) {
|
|
30
|
+
log.info(`Current checkpoint: Task #${state.current_task} — ${state.phase} → ${state.phase_step}`);
|
|
31
|
+
log.blank();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const resetTasks = await confirm('Also reset all task statuses to pending?', false);
|
|
35
|
+
|
|
36
|
+
// Reset state using checkpoint manager
|
|
37
|
+
checkpoint.reset();
|
|
38
|
+
log.info('Execution state reset');
|
|
39
|
+
|
|
40
|
+
// Reset task statuses
|
|
41
|
+
if (resetTasks && existsSync(tasksPath)) {
|
|
42
|
+
const tasks = readJSON(tasksPath);
|
|
43
|
+
for (const task of tasks.tasks) {
|
|
44
|
+
task.status = 'pending';
|
|
45
|
+
}
|
|
46
|
+
writeJSON(tasksPath, tasks);
|
|
47
|
+
log.info('Task statuses reset');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Clean up temp files
|
|
51
|
+
const promptPath = resolve(projectDir, '.codex-copilot/_current_prompt.md');
|
|
52
|
+
if (existsSync(promptPath)) unlinkSync(promptPath);
|
|
53
|
+
|
|
54
|
+
// Clean up temp state file if exists
|
|
55
|
+
const tempPath = resolve(projectDir, '.codex-copilot/state.json.tmp');
|
|
56
|
+
if (existsSync(tempPath)) unlinkSync(tempPath);
|
|
57
|
+
|
|
58
|
+
log.blank();
|
|
59
|
+
log.info('✅ Reset complete. You can now run: codex-copilot run');
|
|
60
|
+
closePrompt();
|
|
61
|
+
}
|
package/src/commands/retry.js
CHANGED
|
@@ -1,153 +1,190 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* codex-copilot retry - Reset blocked tasks for re-execution
|
|
3
|
-
*
|
|
4
|
-
* Recovery strategy per block scenario:
|
|
5
|
-
* Dev-blocked: Save partial context, reset to pending
|
|
6
|
-
* Review-blocked: Close PR, delete branch, save review feedback, reset to pending
|
|
7
|
-
* Merge-blocked: Reset only the merge step
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
11
|
-
import { resolve } from 'path';
|
|
12
|
-
import { log, progressBar } from '../utils/logger.js';
|
|
13
|
-
import { git } from '../utils/git.js';
|
|
14
|
-
import { github } from '../utils/github.js';
|
|
15
|
-
import { createCheckpoint } from '../utils/checkpoint.js';
|
|
16
|
-
import { readJSON, writeJSON } from '../utils/json.js';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
log.
|
|
42
|
-
log.
|
|
43
|
-
log.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
log.dim(
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* codex-copilot retry - Reset blocked tasks for re-execution
|
|
3
|
+
*
|
|
4
|
+
* Recovery strategy per block scenario:
|
|
5
|
+
* Dev-blocked: Save partial context, reset to pending
|
|
6
|
+
* Review-blocked: Close PR, delete branch, save review feedback, reset to pending
|
|
7
|
+
* Merge-blocked: Reset only the merge step
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
11
|
+
import { resolve } from 'path';
|
|
12
|
+
import { log, progressBar } from '../utils/logger.js';
|
|
13
|
+
import { git } from '../utils/git.js';
|
|
14
|
+
import { github } from '../utils/github.js';
|
|
15
|
+
import { createCheckpoint } from '../utils/checkpoint.js';
|
|
16
|
+
import { readJSON, writeJSON } from '../utils/json.js';
|
|
17
|
+
import { preFlightCheck } from '../utils/self-heal.js';
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
export async function retry(projectDir) {
|
|
22
|
+
const tasksPath = resolve(projectDir, '.codex-copilot/tasks.json');
|
|
23
|
+
const checkpoint = createCheckpoint(projectDir);
|
|
24
|
+
const retryDir = resolve(projectDir, '.codex-copilot/retry_context');
|
|
25
|
+
|
|
26
|
+
let tasks;
|
|
27
|
+
try {
|
|
28
|
+
tasks = readJSON(tasksPath);
|
|
29
|
+
} catch (err) {
|
|
30
|
+
log.error(`Failed to read tasks: ${err.message}`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const blocked = tasks.tasks.filter(t => t.status === 'blocked');
|
|
35
|
+
|
|
36
|
+
if (blocked.length === 0) {
|
|
37
|
+
log.info('No blocked tasks found \u2014 nothing to retry.');
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
log.title('\ud83d\udd04 Blocked Task Recovery');
|
|
42
|
+
log.blank();
|
|
43
|
+
log.info(`Found ${blocked.length} blocked task(s):`);
|
|
44
|
+
log.blank();
|
|
45
|
+
|
|
46
|
+
// Ensure retry context directory exists
|
|
47
|
+
mkdirSync(retryDir, { recursive: true });
|
|
48
|
+
|
|
49
|
+
// Pre-flight: resolve any git/environment issues before retrying
|
|
50
|
+
log.blank();
|
|
51
|
+
preFlightCheck(projectDir, 'main', {});
|
|
52
|
+
log.blank();
|
|
53
|
+
|
|
54
|
+
for (const task of blocked) {
|
|
55
|
+
const reason = task.block_reason || 'unknown';
|
|
56
|
+
log.info(` #${task.id}: ${task.title}`);
|
|
57
|
+
log.dim(` Reason: ${reason}`);
|
|
58
|
+
|
|
59
|
+
if (reason === 'review_failed') {
|
|
60
|
+
await recoverReviewBlocked(projectDir, task, checkpoint, retryDir);
|
|
61
|
+
} else if (reason === 'merge_failed') {
|
|
62
|
+
recoverMergeBlocked(task, checkpoint);
|
|
63
|
+
} else if (reason === 'git_checkout_failed') {
|
|
64
|
+
recoverGitBlocked(projectDir, task, checkpoint, retryDir);
|
|
65
|
+
} else if (reason === 'ai_timeout') {
|
|
66
|
+
// AI provider timed out — clear checkpoint so it retries from prompt step
|
|
67
|
+
checkpoint.clearTask(task.id);
|
|
68
|
+
log.dim(' Cleared checkpoint — will retry AI execution');
|
|
69
|
+
} else {
|
|
70
|
+
// dev_failed, rate_limited, or unknown
|
|
71
|
+
recoverDevBlocked(projectDir, task, checkpoint, retryDir);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
task.status = 'pending';
|
|
75
|
+
task.retry_count = (task.retry_count || 0) + 1;
|
|
76
|
+
log.info(` \u2705 Task #${task.id} reset to pending (retry #${task.retry_count})`);
|
|
77
|
+
log.blank();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
writeJSON(tasksPath, tasks);
|
|
81
|
+
|
|
82
|
+
log.blank();
|
|
83
|
+
const done = tasks.tasks.filter(t => t.status === 'completed').length;
|
|
84
|
+
const pending = tasks.tasks.filter(t => t.status === 'pending').length;
|
|
85
|
+
progressBar(done, tasks.total, `${done}/${tasks.total} done, ${pending} pending`);
|
|
86
|
+
log.blank();
|
|
87
|
+
log.info('Run `codex-copilot run` to resume with enhanced prompts.');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Dev-blocked: save any partial work context, clear checkpoints
|
|
92
|
+
*/
|
|
93
|
+
function recoverDevBlocked(projectDir, task, checkpoint, retryDir) {
|
|
94
|
+
// Try to capture any partial diff from the branch
|
|
95
|
+
const diffResult = git.execSafe(`git diff HEAD`, projectDir);
|
|
96
|
+
if (diffResult.ok && diffResult.output.trim()) {
|
|
97
|
+
const contextPath = resolve(retryDir, `${task.id}.md`);
|
|
98
|
+
writeFileSync(contextPath, [
|
|
99
|
+
`# Retry Context for Task #${task.id}`,
|
|
100
|
+
'',
|
|
101
|
+
'## Previous Attempt',
|
|
102
|
+
'The AI attempted this task but failed to produce working code.',
|
|
103
|
+
'Try a simpler, more incremental approach.',
|
|
104
|
+
'',
|
|
105
|
+
'## Partial Changes (from failed attempt)',
|
|
106
|
+
'```diff',
|
|
107
|
+
diffResult.output.substring(0, 5000), // Cap at 5KB
|
|
108
|
+
'```',
|
|
109
|
+
].join('\n'));
|
|
110
|
+
log.dim(' Saved partial diff as retry context');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Clear all checkpoints for this task
|
|
114
|
+
checkpoint.clearTask(task.id);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Review-blocked: close PR, delete branch, save review feedback
|
|
119
|
+
*/
|
|
120
|
+
async function recoverReviewBlocked(projectDir, task, checkpoint, retryDir) {
|
|
121
|
+
const state = checkpoint.load();
|
|
122
|
+
const prNumber = state.current_pr;
|
|
123
|
+
|
|
124
|
+
// Save review feedback as retry context
|
|
125
|
+
if (prNumber) {
|
|
126
|
+
const feedback = github.collectReviewFeedback(projectDir, prNumber);
|
|
127
|
+
if (feedback) {
|
|
128
|
+
const contextPath = resolve(retryDir, `${task.id}.md`);
|
|
129
|
+
writeFileSync(contextPath, [
|
|
130
|
+
`# Retry Context for Task #${task.id}`,
|
|
131
|
+
'',
|
|
132
|
+
'## Known Issues from Previous Review',
|
|
133
|
+
'The previous implementation was rejected. Avoid making the same mistakes.',
|
|
134
|
+
'',
|
|
135
|
+
feedback,
|
|
136
|
+
].join('\n'));
|
|
137
|
+
log.dim(' Saved review feedback as retry context');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Close the stale PR
|
|
141
|
+
if (github.closePR(projectDir, prNumber)) {
|
|
142
|
+
log.dim(` Closed stale PR #${prNumber}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Delete the remote feature branch
|
|
147
|
+
if (task.branch) {
|
|
148
|
+
if (github.deleteBranch(projectDir, task.branch)) {
|
|
149
|
+
log.dim(` Deleted remote branch ${task.branch}`);
|
|
150
|
+
}
|
|
151
|
+
// Delete local branch too
|
|
152
|
+
git.execSafe(`git branch -D ${task.branch}`, projectDir);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Clear all checkpoints for this task
|
|
156
|
+
checkpoint.clearTask(task.id);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Merge-blocked: only reset the merge step
|
|
161
|
+
*/
|
|
162
|
+
function recoverMergeBlocked(task, checkpoint) {
|
|
163
|
+
checkpoint.clearStep(task.id, 'merge', 'merged');
|
|
164
|
+
log.dim(' Reset merge step \u2014 will retry merge on next run');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Git-checkout-blocked: resolve index issues and clear develop checkpoint.
|
|
169
|
+
* The pre-flight check already resolved most git issues;
|
|
170
|
+
* here we just reset the checkpoint so the task retries from branch creation.
|
|
171
|
+
*/
|
|
172
|
+
function recoverGitBlocked(projectDir, task, checkpoint, retryDir) {
|
|
173
|
+
// Resolve any lingering index issues
|
|
174
|
+
git.resolveIndex(projectDir);
|
|
175
|
+
git.clearStaleLocks(projectDir);
|
|
176
|
+
|
|
177
|
+
// Save context for retry prompt
|
|
178
|
+
const contextPath = resolve(retryDir, `${task.id}.md`);
|
|
179
|
+
writeFileSync(contextPath, [
|
|
180
|
+
`# Retry Context for Task #${task.id}`,
|
|
181
|
+
'',
|
|
182
|
+
'## Previous Attempt',
|
|
183
|
+
'The task failed due to a git checkout error (corrupted index, stuck merge/rebase, or lock file).',
|
|
184
|
+
'The git state has been automatically repaired. Try again with the same approach.',
|
|
185
|
+
].join('\n'));
|
|
186
|
+
log.dim(' Resolved git state and saved retry context');
|
|
187
|
+
|
|
188
|
+
// Clear all checkpoints — restart from scratch
|
|
189
|
+
checkpoint.clearTask(task.id);
|
|
190
|
+
}
|