@jojonax/codex-copilot 1.0.1 → 1.0.3

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/bin/cli.js CHANGED
@@ -1,31 +1,39 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * codex-copilot CLI - PRD → 自动开发 → PR → Review → 修复合并
4
+ * codex-copilot CLI - PRD → Auto Dev → PR → Review → FixMerge
5
5
  *
6
- * 用法:
7
- * codex-copilot init # 初始化项目(自动检测 PRD,生成任务)
8
- * codex-copilot run # 启动自动化开发循环
9
- * codex-copilot status # 查看当前进度
10
- * codex-copilot reset # 重置状态(重新开始)
6
+ * Usage:
7
+ * codex-copilot init # Initialize project (auto-detect PRD, generate tasks)
8
+ * codex-copilot run # Start automated development loop
9
+ * codex-copilot status # View current progress
10
+ * codex-copilot reset # Reset state (start over)
11
11
  */
12
12
 
13
- import { resolve } from 'path';
14
- import { existsSync } from 'fs';
13
+ import { resolve, dirname } from 'path';
14
+ import { existsSync, readFileSync } from 'fs';
15
+ import { fileURLToPath } from 'url';
15
16
 
16
17
  import { init } from '../src/commands/init.js';
17
18
  import { run } from '../src/commands/run.js';
18
19
  import { status } from '../src/commands/status.js';
19
20
  import { reset } from '../src/commands/reset.js';
20
21
  import { log } from '../src/utils/logger.js';
22
+ import { checkForUpdates } from '../src/utils/update-check.js';
21
23
 
22
24
  const command = process.argv[2];
23
25
  const projectDir = process.cwd();
24
26
 
27
+ // Read version from package.json
28
+ const __filename = fileURLToPath(import.meta.url);
29
+ const __dirname = dirname(__filename);
30
+ const pkg = JSON.parse(readFileSync(resolve(__dirname, '..', 'package.json'), 'utf-8'));
31
+ const version = pkg.version;
32
+
25
33
  // Banner
26
34
  console.log('');
27
35
  console.log(' ╔══════════════════════════════════════════╗');
28
- console.log(' ║ 🤖 Codex-Copilot v1.0 ║');
36
+ console.log(` ║ 🤖 Codex-Copilot v${version.padEnd(18)}║`);
29
37
  console.log(' ║ PRD-Driven Auto Development ║');
30
38
  console.log(' ╚══════════════════════════════════════════╝');
31
39
  console.log('');
@@ -39,7 +47,7 @@ async function main() {
39
47
 
40
48
  case 'run':
41
49
  if (!existsSync(resolve(projectDir, '.codex-copilot/tasks.json'))) {
42
- log.error('尚未初始化,请先运行: codex-copilot init');
50
+ log.error('Not initialized. Run: codex-copilot init');
43
51
  process.exit(1);
44
52
  }
45
53
  await run(projectDir);
@@ -47,7 +55,7 @@ async function main() {
47
55
 
48
56
  case 'status':
49
57
  if (!existsSync(resolve(projectDir, '.codex-copilot/tasks.json'))) {
50
- log.error('尚未初始化,请先运行: codex-copilot init');
58
+ log.error('Not initialized. Run: codex-copilot init');
51
59
  process.exit(1);
52
60
  }
53
61
  await status(projectDir);
@@ -58,26 +66,29 @@ async function main() {
58
66
  break;
59
67
 
60
68
  default:
61
- console.log(' 用法: codex-copilot <command>');
69
+ console.log(' Usage: codex-copilot <command>');
62
70
  console.log('');
63
- console.log(' 命令:');
64
- console.log(' init 初始化项目(自动检测 PRD,生成任务队列)');
65
- console.log(' run 启动自动化开发循环');
66
- console.log(' status 查看当前任务进度');
67
- console.log(' reset 重置状态,重新开始');
71
+ console.log(' Commands:');
72
+ console.log(' init Initialize project (auto-detect PRD, generate task queue)');
73
+ console.log(' run Start automated development loop');
74
+ console.log(' status View current task progress');
75
+ console.log(' reset Reset state and start over');
68
76
  console.log('');
69
- console.log(' 工作流程:');
70
- console.log(' 1. cd 到你的项目目录');
71
- console.log(' 2. codex-copilot init (自动检测 PRD 并拆解任务)');
72
- console.log(' 3. codex-copilot run (开始自动开发循环)');
77
+ console.log(' Workflow:');
78
+ console.log(' 1. cd into your project directory');
79
+ console.log(' 2. codex-copilot init (auto-detect PRD and decompose tasks)');
80
+ console.log(' 3. codex-copilot run (start automated dev loop)');
73
81
  console.log('');
74
82
  break;
75
83
  }
76
84
  } catch (err) {
77
- log.error(`执行失败: ${err.message}`);
85
+ log.error(`Execution failed: ${err.message}`);
78
86
  if (process.env.DEBUG) console.error(err);
79
87
  process.exit(1);
80
88
  }
81
89
  }
82
90
 
91
+ // Check for updates (non-blocking, cached 24h)
92
+ checkForUpdates(version);
93
+
83
94
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jojonax/codex-copilot",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "PRD-driven automated development orchestrator for CodeX / Cursor",
5
5
  "bin": {
6
6
  "codex-copilot": "./bin/cli.js"
@@ -1,15 +1,14 @@
1
1
  /**
2
- * codex-copilot init - 初始化项目
2
+ * codex-copilot init - Initialize project
3
3
  *
4
- * 1. 自动检测 PRD 文档
5
- * 2. 让用户确认/选择 PRD
6
- * 3. 生成 CodeX 可执行的任务拆解 Prompt
7
- * 4. 创建 .codex-copilot/ 目录结构
4
+ * 1. Auto-detect PRD document
5
+ * 2. Let user confirm/select PRD
6
+ * 3. Generate CodeX-executable task decomposition prompt
7
+ * 4. Create .codex-copilot/ directory structure
8
8
  */
9
9
 
10
10
  import { mkdirSync, writeFileSync, readFileSync, existsSync } from 'fs';
11
- import { resolve, dirname } from 'path';
12
- import { fileURLToPath } from 'url';
11
+ import { resolve } from 'path';
13
12
  import { execSync } from 'child_process';
14
13
  import { detectPRD, readPRD } from '../utils/detect-prd.js';
15
14
  import { log } from '../utils/logger.js';
@@ -17,37 +16,35 @@ import { ask, confirm, select, closePrompt } from '../utils/prompt.js';
17
16
  import { git } from '../utils/git.js';
18
17
  import { github } from '../utils/github.js';
19
18
 
20
- const __dirname = dirname(fileURLToPath(import.meta.url));
21
-
22
19
  export async function init(projectDir) {
23
- log.title('📋 初始化 Codex-Copilot');
20
+ log.title('📋 Initializing Codex-Copilot');
24
21
  log.blank();
25
22
 
26
- // ===== 前置检查 =====
27
- log.step('检查环境...');
23
+ // ===== Pre-flight checks =====
24
+ log.step('Checking environment...');
28
25
 
29
- // 检查是否在 git 仓库中
26
+ // Check if in a git repo
30
27
  try {
31
28
  git.currentBranch(projectDir);
32
- log.info('Git 仓库 ✓');
29
+ log.info('Git repository ✓');
33
30
  } catch {
34
- log.error('当前目录不是 Git 仓库,请先 git init');
31
+ log.error('Not a Git repository. Run: git init');
35
32
  closePrompt();
36
33
  process.exit(1);
37
34
  }
38
35
 
39
- // 检查 gh CLI
36
+ // Check gh CLI
40
37
  if (!github.checkGhAuth()) {
41
- log.error('GitHub CLI 未登录,请先运行: gh auth login');
38
+ log.error('GitHub CLI not authenticated. Run: gh auth login');
42
39
  closePrompt();
43
40
  process.exit(1);
44
41
  }
45
42
  log.info('GitHub CLI ✓');
46
43
 
47
- // 检查 GitHub remote
44
+ // Check GitHub remote
48
45
  try {
49
46
  const repo = git.getRepoInfo(projectDir);
50
- log.info(`GitHub 仓库: ${repo.owner}/${repo.repo} ✓`);
47
+ log.info(`GitHub repo: ${repo.owner}/${repo.repo} ✓`);
51
48
  } catch (err) {
52
49
  log.error(err.message);
53
50
  closePrompt();
@@ -56,51 +53,57 @@ export async function init(projectDir) {
56
53
 
57
54
  log.blank();
58
55
 
59
- // ===== 检测 PRD =====
60
- log.step('扫描 PRD 文档...');
56
+ // ===== Detect PRD =====
57
+ log.step('Scanning for PRD documents...');
61
58
  const candidates = detectPRD(projectDir);
62
59
 
63
60
  let prdPath;
64
61
 
65
62
  if (candidates.length === 0) {
66
- log.warn('未自动检测到 PRD 文档');
67
- const manualPath = await ask('请输入 PRD 文件路径(相对或绝对路径):');
63
+ log.warn('No PRD document detected automatically');
64
+ const manualPath = await ask('Enter PRD file path (relative or absolute):');
68
65
  prdPath = resolve(projectDir, manualPath);
66
+ // Prevent path traversal
67
+ if (!prdPath.startsWith(resolve(projectDir)) && !manualPath.startsWith('/')) {
68
+ log.error('PRD path is outside the project directory');
69
+ closePrompt();
70
+ process.exit(1);
71
+ }
69
72
  if (!existsSync(prdPath)) {
70
- log.error(`文件不存在: ${prdPath}`);
73
+ log.error(`File not found: ${prdPath}`);
71
74
  closePrompt();
72
75
  process.exit(1);
73
76
  }
74
77
  } else if (candidates.length === 1) {
75
78
  prdPath = candidates[0].path;
76
- log.info(`找到 PRD: ${candidates[0].relativePath}`);
77
- const ok = await confirm('使用该文件?');
79
+ log.info(`Found PRD: ${candidates[0].relativePath}`);
80
+ const ok = await confirm('Use this file?');
78
81
  if (!ok) {
79
- const manualPath = await ask('请输入 PRD 文件路径:');
82
+ const manualPath = await ask('Enter PRD file path:');
80
83
  prdPath = resolve(projectDir, manualPath);
81
84
  }
82
85
  } else {
83
- log.info(`找到 ${candidates.length} 个候选 PRD 文件:`);
84
- const choice = await select('请选择要使用的 PRD:', candidates.map(c => ({
85
- label: `${c.relativePath} (匹配度: ${c.score})`,
86
+ log.info(`Found ${candidates.length} candidate PRD files:`);
87
+ const choice = await select('Select the PRD to use:', candidates.map(c => ({
88
+ label: `${c.relativePath} (score: ${c.score})`,
86
89
  value: c.path,
87
90
  })));
88
91
  prdPath = choice.value;
89
92
  }
90
93
 
91
- log.info(`使用 PRD: ${prdPath}`);
94
+ log.info(`Using PRD: ${prdPath}`);
92
95
  log.blank();
93
96
 
94
- // ===== 读取 PRD =====
97
+ // ===== Read PRD =====
95
98
  const prdContent = readPRD(prdPath);
96
- log.info(`PRD 大小: ${(prdContent.length / 1024).toFixed(1)} KB`);
99
+ log.info(`PRD size: ${(prdContent.length / 1024).toFixed(1)} KB`);
97
100
 
98
- // ===== 创建 .codex-copilot 目录 =====
99
- log.step('创建 .codex-copilot/ 目录...');
101
+ // ===== Create .codex-copilot directory =====
102
+ log.step('Creating .codex-copilot/ directory...');
100
103
  const copilotDir = resolve(projectDir, '.codex-copilot');
101
104
  mkdirSync(copilotDir, { recursive: true });
102
105
 
103
- // 写入配置文件
106
+ // Write config file
104
107
  const config = {
105
108
  prd_path: prdPath,
106
109
  base_branch: git.currentBranch(projectDir) || 'main',
@@ -111,7 +114,7 @@ export async function init(projectDir) {
111
114
  };
112
115
  writeFileSync(resolve(copilotDir, 'config.json'), JSON.stringify(config, null, 2));
113
116
 
114
- // 写入初始状态
117
+ // Write initial state
115
118
  const state = {
116
119
  current_task: 0,
117
120
  current_pr: null,
@@ -120,28 +123,28 @@ export async function init(projectDir) {
120
123
  };
121
124
  writeFileSync(resolve(copilotDir, 'state.json'), JSON.stringify(state, null, 2));
122
125
 
123
- // 写入 CodeX 指令模板
124
- const instructions = `# Codex-Copilot 开发指令
126
+ // Write CodeX instruction template
127
+ const instructions = `# Codex-Copilot Development Instructions
125
128
 
126
- ## 角色
127
- 你是一个高效的自动开发 Agent,负责按照任务描述完成功能开发。
129
+ ## Role
130
+ You are an efficient automated development agent responsible for completing feature development according to task descriptions.
128
131
 
129
- ## 规则
130
- 1. **严格遵循项目技术栈**:使用项目已有的框架、工具和代码风格
131
- 2. **自测优先**:开发完成前确保代码可以编译/运行
132
- 3. **Git 规范**:提交信息格式 \`feat(task-N): 简要描述\` \`fix(task-N): 简要描述\`
133
- 4. **不要修改无关文件**:只修改当前任务需要的文件
134
- 5. **完成后执行 git commit**:\`git add -A && git commit -m "..."\`
132
+ ## Rules
133
+ 1. **Follow the project tech stack**: Use the existing frameworks, tools, and code style
134
+ 2. **Test first**: Ensure code compiles/runs before finishing
135
+ 3. **Git conventions**: Commit message format \`feat(task-N): brief description\` or \`fix(task-N): brief description\`
136
+ 4. **Don't modify unrelated files**: Only change files required by the current task
137
+ 5. **Commit when done**: \`git add -A && git commit -m "..."\`
135
138
 
136
- ## 修复 Review 时的规则
137
- 1. 逐条阅读 Review 意见
138
- 2. 区分必须修复 vs 建议性意见
139
- 3. 对于不认同的意见,在 commit message 中说明原因
140
- 4. 修复后确保不引入新问题
139
+ ## Rules for fixing reviews
140
+ 1. Read each review comment carefully
141
+ 2. Distinguish between must-fix vs. suggestions
142
+ 3. If you disagree with a comment, explain why in the commit message
143
+ 4. Ensure fixes don't introduce new issues
141
144
  `;
142
145
  writeFileSync(resolve(copilotDir, 'codex-instructions.md'), instructions);
143
146
 
144
- // 添加 .gitignore
147
+ // Add .gitignore entries
145
148
  const gitignorePath = resolve(projectDir, '.gitignore');
146
149
  const gitignoreEntry = '\n# Codex-Copilot state\n.codex-copilot/state.json\n';
147
150
  if (existsSync(gitignorePath)) {
@@ -151,50 +154,49 @@ export async function init(projectDir) {
151
154
  }
152
155
  }
153
156
 
154
- log.info('.codex-copilot/ 目录已创建');
157
+ log.info('.codex-copilot/ directory created');
155
158
  log.blank();
156
159
 
157
- // ===== 生成任务拆解 Prompt =====
158
- log.step('生成任务拆解 Prompt...');
160
+ // ===== Generate task decomposition prompt =====
161
+ log.step('Generating task decomposition prompt...');
159
162
 
160
163
  const parsePrompt = buildParsePrompt(prdContent, copilotDir);
161
164
  const promptPath = resolve(copilotDir, 'parse-prd-prompt.md');
162
165
  writeFileSync(promptPath, parsePrompt);
163
166
 
164
- log.info(`任务拆解 Prompt 已保存到: .codex-copilot/parse-prd-prompt.md`);
167
+ log.info('Task decomposition prompt saved to: .codex-copilot/parse-prd-prompt.md');
165
168
  log.blank();
166
169
 
167
- // ===== 提示用户下一步 =====
168
- log.title('✅ 初始化完成!');
170
+ // ===== Guide user to next steps =====
171
+ log.title('✅ Initialization complete!');
169
172
  log.blank();
170
- log.info('接下来请执行以下步骤:');
173
+ log.info('Next steps:');
171
174
  log.blank();
172
175
  console.log(' ┌───────────────────────────────────────────────────┐');
173
- console.log(' │ 1. 打开 CodeX 桌面版 │');
174
- console.log(' │ 2. 将以下文件内容粘贴给 CodeX 执行: │');
176
+ console.log(' │ 1. Open CodeX Desktop │');
177
+ console.log(' │ 2. Paste the following file content into CodeX: │');
175
178
  console.log(' │ .codex-copilot/parse-prd-prompt.md │');
176
- console.log(' │ 3. CodeX 会生成 .codex-copilot/tasks.json │');
177
- console.log(' │ 4. 确认任务列表后运行: │');
179
+ console.log(' │ 3. CodeX will generate .codex-copilot/tasks.json │');
180
+ console.log(' │ 4. Confirm the task list, then run: │');
178
181
  console.log(' │ codex-copilot run │');
179
182
  console.log(' └───────────────────────────────────────────────────┘');
180
183
  log.blank();
181
184
 
182
- // 询问是否尝试自动拆解(如果 codex CLI 可用)
185
+ // Ask if user wants to auto-decompose (if codex CLI is available)
183
186
  const hasCodexCLI = checkCodexCLI();
184
187
  if (hasCodexCLI) {
185
- log.info('检测到 CodeX CLI 可用!');
186
- const autoparse = await confirm('是否自动调用 CodeX 拆解 PRD');
188
+ log.info('CodeX CLI detected!');
189
+ const autoparse = await confirm('Auto-invoke CodeX to decompose PRD?');
187
190
  if (autoparse) {
188
- log.step('调用 CodeX CLI 拆解 PRD...');
191
+ log.step('Invoking CodeX CLI to decompose PRD...');
189
192
  try {
190
- const { execSync } = await import('child_process');
191
- execSync(`codex -q "${parsePrompt.slice(0, 2000)}"`, {
193
+ execSync(`cat .codex-copilot/parse-prd-prompt.md | codex exec --full-auto -`, {
192
194
  cwd: projectDir,
193
195
  stdio: 'inherit',
194
196
  });
195
- log.info('CodeX 拆解完成!');
197
+ log.info('CodeX decomposition complete!');
196
198
  } catch {
197
- log.warn('CodeX CLI 调用失败,请手动执行');
199
+ log.warn('CodeX CLI invocation failed. Please run manually.');
198
200
  }
199
201
  }
200
202
  }
@@ -204,7 +206,8 @@ export async function init(projectDir) {
204
206
 
205
207
  function checkCodexCLI() {
206
208
  try {
207
- execSync('which codex', { stdio: 'pipe' });
209
+ const cmd = process.platform === 'win32' ? 'where codex' : 'which codex';
210
+ execSync(cmd, { stdio: 'pipe' });
208
211
  return true;
209
212
  } catch {
210
213
  return false;
@@ -212,27 +215,27 @@ function checkCodexCLI() {
212
215
  }
213
216
 
214
217
  function buildParsePrompt(prdContent, copilotDir) {
215
- return `请阅读以下 PRD 文档,将其拆解为独立的开发任务。
218
+ return `Read the following PRD document and break it down into independent development tasks.
216
219
 
217
- ## 要求
218
- 1. 每个任务在一个 PR 中可以完成(粒度适中,不要太大也不要太碎)
219
- 2. 有明确的验收标准
220
- 3. 按依赖关系排序(被依赖的任务排在前面)
221
- 4. 第一个任务通常是"项目初始化 / 基础框架搭建"
220
+ ## Requirements
221
+ 1. Each task should be completable in a single PR (moderate granularity — not too large, not too small)
222
+ 2. Each task must have clear acceptance criteria
223
+ 3. Tasks should be ordered by dependency (dependent tasks first)
224
+ 4. The first task is usually "Project initialization / base framework setup"
222
225
 
223
- ## 输出格式
224
- 将结果输出到 \`.codex-copilot/tasks.json\`,格式如下:
226
+ ## Output Format
227
+ Write the result to \`.codex-copilot/tasks.json\` in the following format:
225
228
 
226
229
  \`\`\`json
227
230
  {
228
- "project": "项目名称",
231
+ "project": "Project Name",
229
232
  "total": 5,
230
233
  "tasks": [
231
234
  {
232
235
  "id": 1,
233
- "title": "任务标题(简短)",
234
- "description": "详细的任务描述,包含需要实现的具体功能和技术细节",
235
- "acceptance": ["验收条件1", "验收条件2"],
236
+ "title": "Task title (brief)",
237
+ "description": "Detailed task description with specific features and technical details",
238
+ "acceptance": ["Acceptance criteria 1", "Acceptance criteria 2"],
236
239
  "branch": "feature/001-task-slug",
237
240
  "status": "pending",
238
241
  "depends_on": []
@@ -250,7 +253,7 @@ function buildParsePrompt(prdContent, copilotDir) {
250
253
  }
251
254
  \`\`\`
252
255
 
253
- ## PRD 文档内容
256
+ ## PRD Document Content
254
257
 
255
258
  ${prdContent}
256
259
  `;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * codex-copilot reset - 重置状态
2
+ * codex-copilot reset - Reset state
3
3
  */
4
4
 
5
5
  import { readFileSync, writeFileSync, existsSync, unlinkSync } from 'fs';
@@ -12,17 +12,17 @@ export async function reset(projectDir) {
12
12
  const tasksPath = resolve(projectDir, '.codex-copilot/tasks.json');
13
13
 
14
14
  if (!existsSync(statePath)) {
15
- log.warn('项目未初始化,无需重置');
15
+ log.warn('Project not initialized, nothing to reset');
16
16
  closePrompt();
17
17
  return;
18
18
  }
19
19
 
20
- log.title('🔄 重置 Codex-Copilot 状态');
20
+ log.title('🔄 Resetting Codex-Copilot state');
21
21
  log.blank();
22
22
 
23
- const resetTasks = await confirm('是否同时重置任务状态 (所有任务标记为 pending)?', false);
23
+ const resetTasks = await confirm('Also reset all task statuses to pending?', false);
24
24
 
25
- // 重置 state
25
+ // Reset state
26
26
  const state = {
27
27
  current_task: 0,
28
28
  current_pr: null,
@@ -30,23 +30,23 @@ export async function reset(projectDir) {
30
30
  status: 'initialized',
31
31
  };
32
32
  writeFileSync(statePath, JSON.stringify(state, null, 2));
33
- log.info('执行状态已重置');
33
+ log.info('Execution state reset');
34
34
 
35
- // 重置任务状态
35
+ // Reset task statuses
36
36
  if (resetTasks && existsSync(tasksPath)) {
37
37
  const tasks = JSON.parse(readFileSync(tasksPath, 'utf-8'));
38
38
  for (const task of tasks.tasks) {
39
39
  task.status = 'pending';
40
40
  }
41
41
  writeFileSync(tasksPath, JSON.stringify(tasks, null, 2));
42
- log.info('任务状态已重置');
42
+ log.info('Task statuses reset');
43
43
  }
44
44
 
45
- // 清理临时文件
45
+ // Clean up temp files
46
46
  const promptPath = resolve(projectDir, '.codex-copilot/_current_prompt.md');
47
47
  if (existsSync(promptPath)) unlinkSync(promptPath);
48
48
 
49
49
  log.blank();
50
- log.info('✅ 重置完成,可以重新运行 codex-copilot run');
50
+ log.info('✅ Reset complete. You can now run: codex-copilot run');
51
51
  closePrompt();
52
52
  }