@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/skip.js
CHANGED
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* codex-copilot skip <task_id> - Force-skip a blocked task
|
|
3
|
-
*
|
|
4
|
-
* Marks a blocked/pending task as 'skipped' so that tasks
|
|
5
|
-
* depending on it can proceed. Use when a task is permanently
|
|
6
|
-
* stuck and downstream work should continue regardless.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { resolve } from 'path';
|
|
10
|
-
import { log } from '../utils/logger.js';
|
|
11
|
-
import { readJSON, writeJSON } from '../utils/json.js';
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export async function skip(projectDir, taskId) {
|
|
16
|
-
const tasksPath = resolve(projectDir, '.codex-copilot/tasks.json');
|
|
17
|
-
|
|
18
|
-
let tasks;
|
|
19
|
-
try {
|
|
20
|
-
tasks = readJSON(tasksPath);
|
|
21
|
-
} catch (err) {
|
|
22
|
-
log.error(`Failed to read tasks: ${err.message}`);
|
|
23
|
-
process.exit(1);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const id = parseInt(taskId, 10);
|
|
27
|
-
if (isNaN(id)) {
|
|
28
|
-
log.error('Invalid task ID. Usage: codex-copilot skip <task_id>');
|
|
29
|
-
process.exit(1);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const task = tasks.tasks.find(t => t.id === id);
|
|
33
|
-
if (!task) {
|
|
34
|
-
log.error(`Task #${id} not found`);
|
|
35
|
-
process.exit(1);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (task.status === 'completed') {
|
|
39
|
-
log.info(`Task #${id} is already completed — no action needed`);
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (task.status === 'skipped') {
|
|
44
|
-
log.info(`Task #${id} is already skipped`);
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Find downstream tasks that depend on this one
|
|
49
|
-
const downstream = tasks.tasks.filter(t =>
|
|
50
|
-
t.depends_on && t.depends_on.includes(id)
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
task.status = 'skipped';
|
|
54
|
-
writeJSON(tasksPath, tasks);
|
|
55
|
-
|
|
56
|
-
log.info(`\u2705 Task #${id} marked as skipped: ${task.title}`);
|
|
57
|
-
if (downstream.length > 0) {
|
|
58
|
-
log.info(` ${downstream.length} downstream task(s) unblocked: ${downstream.map(t => `#${t.id}`).join(', ')}`);
|
|
59
|
-
}
|
|
60
|
-
log.blank();
|
|
61
|
-
log.info('Run `codex-copilot run` to continue.');
|
|
62
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* codex-copilot skip <task_id> - Force-skip a blocked task
|
|
3
|
+
*
|
|
4
|
+
* Marks a blocked/pending task as 'skipped' so that tasks
|
|
5
|
+
* depending on it can proceed. Use when a task is permanently
|
|
6
|
+
* stuck and downstream work should continue regardless.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { resolve } from 'path';
|
|
10
|
+
import { log } from '../utils/logger.js';
|
|
11
|
+
import { readJSON, writeJSON } from '../utils/json.js';
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
export async function skip(projectDir, taskId) {
|
|
16
|
+
const tasksPath = resolve(projectDir, '.codex-copilot/tasks.json');
|
|
17
|
+
|
|
18
|
+
let tasks;
|
|
19
|
+
try {
|
|
20
|
+
tasks = readJSON(tasksPath);
|
|
21
|
+
} catch (err) {
|
|
22
|
+
log.error(`Failed to read tasks: ${err.message}`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const id = parseInt(taskId, 10);
|
|
27
|
+
if (isNaN(id)) {
|
|
28
|
+
log.error('Invalid task ID. Usage: codex-copilot skip <task_id>');
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const task = tasks.tasks.find(t => t.id === id);
|
|
33
|
+
if (!task) {
|
|
34
|
+
log.error(`Task #${id} not found`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (task.status === 'completed') {
|
|
39
|
+
log.info(`Task #${id} is already completed — no action needed`);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (task.status === 'skipped') {
|
|
44
|
+
log.info(`Task #${id} is already skipped`);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Find downstream tasks that depend on this one
|
|
49
|
+
const downstream = tasks.tasks.filter(t =>
|
|
50
|
+
t.depends_on && t.depends_on.includes(id)
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
task.status = 'skipped';
|
|
54
|
+
writeJSON(tasksPath, tasks);
|
|
55
|
+
|
|
56
|
+
log.info(`\u2705 Task #${id} marked as skipped: ${task.title}`);
|
|
57
|
+
if (downstream.length > 0) {
|
|
58
|
+
log.info(` ${downstream.length} downstream task(s) unblocked: ${downstream.map(t => `#${t.id}`).join(', ')}`);
|
|
59
|
+
}
|
|
60
|
+
log.blank();
|
|
61
|
+
log.info('Run `codex-copilot run` to continue.');
|
|
62
|
+
}
|
package/src/commands/status.js
CHANGED
|
@@ -1,95 +1,95 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* codex-copilot status - Show current progress
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { existsSync } from 'fs';
|
|
6
|
-
import { resolve } from 'path';
|
|
7
|
-
import { log, progressBar } from '../utils/logger.js';
|
|
8
|
-
import { createCheckpoint } from '../utils/checkpoint.js';
|
|
9
|
-
import { readJSON } from '../utils/json.js';
|
|
10
|
-
|
|
11
|
-
export async function status(projectDir) {
|
|
12
|
-
const tasksPath = resolve(projectDir, '.codex-copilot/tasks.json');
|
|
13
|
-
const checkpoint = createCheckpoint(projectDir);
|
|
14
|
-
|
|
15
|
-
let tasks, state;
|
|
16
|
-
try {
|
|
17
|
-
tasks = readJSON(tasksPath);
|
|
18
|
-
state = checkpoint.load();
|
|
19
|
-
} catch (err) {
|
|
20
|
-
log.error(`Failed to read files: ${err.message}`);
|
|
21
|
-
log.warn('Files may be corrupted. Run: codex-copilot reset');
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
log.title(`📊 Project: ${tasks.project}`);
|
|
26
|
-
log.blank();
|
|
27
|
-
|
|
28
|
-
// Round info
|
|
29
|
-
const roundsPath = resolve(projectDir, '.codex-copilot/rounds.json');
|
|
30
|
-
if (existsSync(roundsPath)) {
|
|
31
|
-
try {
|
|
32
|
-
const rounds = readJSON(roundsPath);
|
|
33
|
-
const currentRound = tasks.round || rounds.current_round || 1;
|
|
34
|
-
const totalCompleted = rounds.rounds.reduce((s, r) => s + r.tasks_completed, 0);
|
|
35
|
-
log.info(`📦 Round: ${currentRound} | Total across all rounds: ${totalCompleted} tasks`);
|
|
36
|
-
if (rounds.rounds.length > 0) {
|
|
37
|
-
for (const r of rounds.rounds) {
|
|
38
|
-
const pct = Math.round((r.tasks_completed / r.tasks_total) * 100);
|
|
39
|
-
log.dim(` Round ${r.round}: ${r.tasks_completed}/${r.tasks_total} (${pct}%)`);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
log.blank();
|
|
43
|
-
} catch { /* ignore parse errors */ }
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Progress bar
|
|
47
|
-
const completed = tasks.tasks.filter(t => t.status === 'completed').length;
|
|
48
|
-
const inProgress = tasks.tasks.filter(t => t.status === 'in_progress' || t.status === 'developed').length;
|
|
49
|
-
const pending = tasks.tasks.filter(t => t.status === 'pending').length;
|
|
50
|
-
const skipped = tasks.tasks.filter(t => t.status === 'skipped').length;
|
|
51
|
-
|
|
52
|
-
progressBar(completed, tasks.total, `${completed}/${tasks.total} done`);
|
|
53
|
-
log.blank();
|
|
54
|
-
|
|
55
|
-
// Stats
|
|
56
|
-
log.info(`✅ Completed: ${completed}`);
|
|
57
|
-
if (inProgress > 0) log.info(`🔄 In progress: ${inProgress}`);
|
|
58
|
-
log.info(`⏳ Pending: ${pending}`);
|
|
59
|
-
if (skipped > 0) log.warn(`⏭ Skipped: ${skipped}`);
|
|
60
|
-
log.blank();
|
|
61
|
-
|
|
62
|
-
// Current checkpoint state
|
|
63
|
-
if (state.phase) {
|
|
64
|
-
const phaseLabels = {
|
|
65
|
-
develop: '🔨 Develop',
|
|
66
|
-
pr: '📦 PR',
|
|
67
|
-
review: '👀 Review',
|
|
68
|
-
merge: '🔀 Merge',
|
|
69
|
-
};
|
|
70
|
-
const phaseLabel = phaseLabels[state.phase] || state.phase;
|
|
71
|
-
log.info(`📍 Checkpoint: Task #${state.current_task} — ${phaseLabel} → ${state.phase_step}`);
|
|
72
|
-
if (state.branch) log.dim(` Branch: ${state.branch}`);
|
|
73
|
-
if (state.current_pr) log.dim(` PR: #${state.current_pr}`);
|
|
74
|
-
if (state.review_round > 0) log.dim(` Review round: ${state.review_round}`);
|
|
75
|
-
if (state.last_updated) log.dim(` Last saved: ${state.last_updated}`);
|
|
76
|
-
} else if (state.current_task > 0) {
|
|
77
|
-
log.info(`Last completed task: #${state.current_task}`);
|
|
78
|
-
}
|
|
79
|
-
log.blank();
|
|
80
|
-
|
|
81
|
-
// Task list
|
|
82
|
-
log.title('Task list:');
|
|
83
|
-
log.blank();
|
|
84
|
-
for (const task of tasks.tasks) {
|
|
85
|
-
const isCurrentTask = state.current_task === task.id && state.phase;
|
|
86
|
-
const icon = task.status === 'completed' ? '✅' :
|
|
87
|
-
isCurrentTask ? '🔄' :
|
|
88
|
-
task.status === 'in_progress' ? '🔄' :
|
|
89
|
-
task.status === 'developed' ? '📦' :
|
|
90
|
-
task.status === 'skipped' ? '⏭ ' : '⬜';
|
|
91
|
-
const suffix = isCurrentTask ? ` ← ${state.phase}:${state.phase_step}` : '';
|
|
92
|
-
console.log(` ${icon} #${task.id} ${task.title} [${task.branch}]${suffix}`);
|
|
93
|
-
}
|
|
94
|
-
log.blank();
|
|
95
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* codex-copilot status - Show current progress
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { existsSync } from 'fs';
|
|
6
|
+
import { resolve } from 'path';
|
|
7
|
+
import { log, progressBar } from '../utils/logger.js';
|
|
8
|
+
import { createCheckpoint } from '../utils/checkpoint.js';
|
|
9
|
+
import { readJSON } from '../utils/json.js';
|
|
10
|
+
|
|
11
|
+
export async function status(projectDir) {
|
|
12
|
+
const tasksPath = resolve(projectDir, '.codex-copilot/tasks.json');
|
|
13
|
+
const checkpoint = createCheckpoint(projectDir);
|
|
14
|
+
|
|
15
|
+
let tasks, state;
|
|
16
|
+
try {
|
|
17
|
+
tasks = readJSON(tasksPath);
|
|
18
|
+
state = checkpoint.load();
|
|
19
|
+
} catch (err) {
|
|
20
|
+
log.error(`Failed to read files: ${err.message}`);
|
|
21
|
+
log.warn('Files may be corrupted. Run: codex-copilot reset');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
log.title(`📊 Project: ${tasks.project}`);
|
|
26
|
+
log.blank();
|
|
27
|
+
|
|
28
|
+
// Round info
|
|
29
|
+
const roundsPath = resolve(projectDir, '.codex-copilot/rounds.json');
|
|
30
|
+
if (existsSync(roundsPath)) {
|
|
31
|
+
try {
|
|
32
|
+
const rounds = readJSON(roundsPath);
|
|
33
|
+
const currentRound = tasks.round || rounds.current_round || 1;
|
|
34
|
+
const totalCompleted = rounds.rounds.reduce((s, r) => s + r.tasks_completed, 0);
|
|
35
|
+
log.info(`📦 Round: ${currentRound} | Total across all rounds: ${totalCompleted} tasks`);
|
|
36
|
+
if (rounds.rounds.length > 0) {
|
|
37
|
+
for (const r of rounds.rounds) {
|
|
38
|
+
const pct = Math.round((r.tasks_completed / r.tasks_total) * 100);
|
|
39
|
+
log.dim(` Round ${r.round}: ${r.tasks_completed}/${r.tasks_total} (${pct}%)`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
log.blank();
|
|
43
|
+
} catch { /* ignore parse errors */ }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Progress bar
|
|
47
|
+
const completed = tasks.tasks.filter(t => t.status === 'completed').length;
|
|
48
|
+
const inProgress = tasks.tasks.filter(t => t.status === 'in_progress' || t.status === 'developed').length;
|
|
49
|
+
const pending = tasks.tasks.filter(t => t.status === 'pending').length;
|
|
50
|
+
const skipped = tasks.tasks.filter(t => t.status === 'skipped').length;
|
|
51
|
+
|
|
52
|
+
progressBar(completed, tasks.total, `${completed}/${tasks.total} done`);
|
|
53
|
+
log.blank();
|
|
54
|
+
|
|
55
|
+
// Stats
|
|
56
|
+
log.info(`✅ Completed: ${completed}`);
|
|
57
|
+
if (inProgress > 0) log.info(`🔄 In progress: ${inProgress}`);
|
|
58
|
+
log.info(`⏳ Pending: ${pending}`);
|
|
59
|
+
if (skipped > 0) log.warn(`⏭ Skipped: ${skipped}`);
|
|
60
|
+
log.blank();
|
|
61
|
+
|
|
62
|
+
// Current checkpoint state
|
|
63
|
+
if (state.phase) {
|
|
64
|
+
const phaseLabels = {
|
|
65
|
+
develop: '🔨 Develop',
|
|
66
|
+
pr: '📦 PR',
|
|
67
|
+
review: '👀 Review',
|
|
68
|
+
merge: '🔀 Merge',
|
|
69
|
+
};
|
|
70
|
+
const phaseLabel = phaseLabels[state.phase] || state.phase;
|
|
71
|
+
log.info(`📍 Checkpoint: Task #${state.current_task} — ${phaseLabel} → ${state.phase_step}`);
|
|
72
|
+
if (state.branch) log.dim(` Branch: ${state.branch}`);
|
|
73
|
+
if (state.current_pr) log.dim(` PR: #${state.current_pr}`);
|
|
74
|
+
if (state.review_round > 0) log.dim(` Review round: ${state.review_round}`);
|
|
75
|
+
if (state.last_updated) log.dim(` Last saved: ${state.last_updated}`);
|
|
76
|
+
} else if (state.current_task > 0) {
|
|
77
|
+
log.info(`Last completed task: #${state.current_task}`);
|
|
78
|
+
}
|
|
79
|
+
log.blank();
|
|
80
|
+
|
|
81
|
+
// Task list
|
|
82
|
+
log.title('Task list:');
|
|
83
|
+
log.blank();
|
|
84
|
+
for (const task of tasks.tasks) {
|
|
85
|
+
const isCurrentTask = state.current_task === task.id && state.phase;
|
|
86
|
+
const icon = task.status === 'completed' ? '✅' :
|
|
87
|
+
isCurrentTask ? '🔄' :
|
|
88
|
+
task.status === 'in_progress' ? '🔄' :
|
|
89
|
+
task.status === 'developed' ? '📦' :
|
|
90
|
+
task.status === 'skipped' ? '⏭ ' : '⬜';
|
|
91
|
+
const suffix = isCurrentTask ? ` ← ${state.phase}:${state.phase_step}` : '';
|
|
92
|
+
console.log(` ${icon} #${task.id} ${task.title} [${task.branch}]${suffix}`);
|
|
93
|
+
}
|
|
94
|
+
log.blank();
|
|
95
|
+
}
|