@jojonax/codex-copilot 1.0.0 → 1.0.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/README.md +9 -2
- package/bin/cli.js +1 -2
- package/package.json +1 -1
- package/src/commands/init.js +1 -1
- package/src/commands/run.js +25 -31
- package/src/utils/detect-prd.js +1 -1
- package/src/utils/git.js +5 -1
- package/src/utils/github.js +5 -1
package/README.md
CHANGED
|
@@ -12,10 +12,10 @@ PRD → Tasks → CodeX Dev → PR → AI Review → Fix → Merge → Next Task
|
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
14
|
# Install globally
|
|
15
|
-
npm install -g codex-copilot
|
|
15
|
+
npm install -g @jojonax/codex-copilot
|
|
16
16
|
|
|
17
17
|
# Or run directly without installing
|
|
18
|
-
npx codex-copilot
|
|
18
|
+
npx @jojonax/codex-copilot
|
|
19
19
|
|
|
20
20
|
# In your project directory:
|
|
21
21
|
codex-copilot init # Detect PRD, generate task queue
|
|
@@ -122,6 +122,13 @@ CodeX develops feature
|
|
|
122
122
|
- [ ] GitHub Action for fully server-side automation
|
|
123
123
|
- [ ] Support for monorepo / multi-package projects
|
|
124
124
|
|
|
125
|
+
## Contributing
|
|
126
|
+
|
|
127
|
+
1. Fork the repo
|
|
128
|
+
2. Create a feature branch (`git checkout -b feat/awesome`)
|
|
129
|
+
3. Commit your changes (`git commit -m 'feat: add awesome feature'`)
|
|
130
|
+
4. Push and open a PR
|
|
131
|
+
|
|
125
132
|
## License
|
|
126
133
|
|
|
127
134
|
MIT © Jonas Qin
|
package/bin/cli.js
CHANGED
package/package.json
CHANGED
package/src/commands/init.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* 4. 创建 .codex-copilot/ 目录结构
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { mkdirSync, writeFileSync, readFileSync, existsSync
|
|
10
|
+
import { mkdirSync, writeFileSync, readFileSync, existsSync } from 'fs';
|
|
11
11
|
import { resolve, dirname } from 'path';
|
|
12
12
|
import { fileURLToPath } from 'url';
|
|
13
13
|
import { execSync } from 'child_process';
|
package/src/commands/run.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
8
8
|
import { resolve } from 'path';
|
|
9
|
+
import { execSync } from 'child_process';
|
|
9
10
|
import { log, progressBar } from '../utils/logger.js';
|
|
10
11
|
import { git } from '../utils/git.js';
|
|
11
12
|
import { github } from '../utils/github.js';
|
|
@@ -113,22 +114,14 @@ async function developPhase(projectDir, task, baseBranch) {
|
|
|
113
114
|
const devPrompt = buildDevPrompt(task);
|
|
114
115
|
|
|
115
116
|
// 检查是否有 CodeX CLI
|
|
116
|
-
|
|
117
|
-
try {
|
|
118
|
-
const { execSync } = await import('child_process');
|
|
119
|
-
execSync('which codex', { stdio: 'pipe' });
|
|
120
|
-
codexAvailable = true;
|
|
121
|
-
} catch {}
|
|
117
|
+
const codexAvailable = isCodexAvailable();
|
|
122
118
|
|
|
123
119
|
if (codexAvailable) {
|
|
124
120
|
log.info('检测到 CodeX CLI,自动执行...');
|
|
125
121
|
const autoRun = await confirm('自动调用 CodeX 开发?');
|
|
126
122
|
if (autoRun) {
|
|
127
123
|
try {
|
|
128
|
-
const { execSync } = await import('child_process');
|
|
129
|
-
// 将 prompt 写入临时文件
|
|
130
124
|
const promptPath = resolve(projectDir, '.codex-copilot/_current_prompt.md');
|
|
131
|
-
const { writeFileSync } = await import('fs');
|
|
132
125
|
writeFileSync(promptPath, devPrompt);
|
|
133
126
|
execSync(`codex -q --file .codex-copilot/_current_prompt.md`, {
|
|
134
127
|
cwd: projectDir,
|
|
@@ -154,17 +147,12 @@ async function developPhase(projectDir, task, baseBranch) {
|
|
|
154
147
|
|
|
155
148
|
// 同时将 Prompt 保存到文件,方便复制
|
|
156
149
|
const promptPath = resolve(projectDir, '.codex-copilot/_current_prompt.md');
|
|
157
|
-
|
|
158
|
-
ws(promptPath, devPrompt);
|
|
150
|
+
writeFileSync(promptPath, devPrompt);
|
|
159
151
|
log.dim(`Prompt 已保存到 .codex-copilot/_current_prompt.md (可直接复制文件内容)`);
|
|
160
152
|
log.blank();
|
|
161
153
|
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
const { execSync } = await import('child_process');
|
|
165
|
-
execSync(`echo '${devPrompt.replace(/'/g, "\\'")}' | pbcopy`, { stdio: 'pipe' });
|
|
166
|
-
log.info('📋 已复制到剪贴板!直接到 CodeX 中粘贴即可');
|
|
167
|
-
} catch {}
|
|
154
|
+
// 尝试复制到剪贴板(通过 stdin 传入,避免 shell 注入)
|
|
155
|
+
copyToClipboard(devPrompt);
|
|
168
156
|
|
|
169
157
|
await ask('CodeX 开发完成后按 Enter 继续...');
|
|
170
158
|
}
|
|
@@ -208,7 +196,8 @@ async function prPhase(projectDir, task, baseBranch) {
|
|
|
208
196
|
// ──────────────────────────────────────────────
|
|
209
197
|
// 阶段 3: Review 循环
|
|
210
198
|
// ──────────────────────────────────────────────
|
|
211
|
-
async function reviewLoop(projectDir, task, prInfo, { maxRounds, pollInterval, waitTimeout }) {
|
|
199
|
+
async function reviewLoop(projectDir, task, prInfo, { maxRounds: _maxRounds, pollInterval, waitTimeout }) {
|
|
200
|
+
let maxRounds = _maxRounds;
|
|
212
201
|
log.step('阶段 3/4: 等待 Review');
|
|
213
202
|
|
|
214
203
|
for (let round = 1; round <= maxRounds; round++) {
|
|
@@ -320,22 +309,15 @@ ${feedback}
|
|
|
320
309
|
|
|
321
310
|
// 保存到文件并提示用户
|
|
322
311
|
const promptPath = resolve(projectDir, '.codex-copilot/_current_prompt.md');
|
|
323
|
-
const { writeFileSync } = await import('fs');
|
|
324
312
|
writeFileSync(promptPath, fixPrompt);
|
|
325
313
|
|
|
326
314
|
// 尝试 CodeX CLI
|
|
327
|
-
|
|
328
|
-
try {
|
|
329
|
-
const { execSync } = await import('child_process');
|
|
330
|
-
execSync('which codex', { stdio: 'pipe' });
|
|
331
|
-
codexAvailable = true;
|
|
332
|
-
} catch {}
|
|
315
|
+
const codexAvailable = isCodexAvailable();
|
|
333
316
|
|
|
334
317
|
if (codexAvailable) {
|
|
335
318
|
const autoFix = await confirm('自动调用 CodeX 修复?');
|
|
336
319
|
if (autoFix) {
|
|
337
320
|
try {
|
|
338
|
-
const { execSync } = await import('child_process');
|
|
339
321
|
execSync(`codex -q --file .codex-copilot/_current_prompt.md`, {
|
|
340
322
|
cwd: projectDir,
|
|
341
323
|
stdio: 'inherit',
|
|
@@ -353,11 +335,7 @@ ${feedback}
|
|
|
353
335
|
log.dim(`Review 修复 Prompt 已保存到 .codex-copilot/_current_prompt.md`);
|
|
354
336
|
log.dim('请将文件内容粘贴到 CodeX 执行');
|
|
355
337
|
|
|
356
|
-
|
|
357
|
-
const { execSync } = await import('child_process');
|
|
358
|
-
execSync(`cat .codex-copilot/_current_prompt.md | pbcopy`, { cwd: projectDir, stdio: 'pipe' });
|
|
359
|
-
log.info('📋 已复制到剪贴板');
|
|
360
|
-
} catch {}
|
|
338
|
+
copyToClipboard(fixPrompt);
|
|
361
339
|
|
|
362
340
|
await ask('CodeX 修复完成后按 Enter 继续...');
|
|
363
341
|
}
|
|
@@ -411,3 +389,19 @@ ${task.acceptance.map(a => `- ${a}`).join('\n')}
|
|
|
411
389
|
function sleep(ms) {
|
|
412
390
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
413
391
|
}
|
|
392
|
+
|
|
393
|
+
function isCodexAvailable() {
|
|
394
|
+
try {
|
|
395
|
+
execSync('which codex', { stdio: 'pipe' });
|
|
396
|
+
return true;
|
|
397
|
+
} catch {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function copyToClipboard(text) {
|
|
403
|
+
try {
|
|
404
|
+
execSync('pbcopy', { input: text, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
405
|
+
log.info('📋 已复制到剪贴板');
|
|
406
|
+
} catch {}
|
|
407
|
+
}
|
package/src/utils/detect-prd.js
CHANGED
package/src/utils/git.js
CHANGED
|
@@ -71,10 +71,14 @@ export function commitAll(cwd, message) {
|
|
|
71
71
|
log.dim('没有变更需要提交');
|
|
72
72
|
return false;
|
|
73
73
|
}
|
|
74
|
-
exec(`git commit -m
|
|
74
|
+
exec(`git commit -m ${shellEscape(message)}`, cwd);
|
|
75
75
|
return true;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
function shellEscape(str) {
|
|
79
|
+
return `'${str.replace(/'/g, "'\\''")}'`
|
|
80
|
+
}
|
|
81
|
+
|
|
78
82
|
/**
|
|
79
83
|
* 推送分支
|
|
80
84
|
*/
|
package/src/utils/github.js
CHANGED
|
@@ -14,6 +14,10 @@ function ghJSON(cmd, cwd) {
|
|
|
14
14
|
return output ? JSON.parse(output) : null;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
function shellEscape(str) {
|
|
18
|
+
return `'${str.replace(/'/g, "'\\''")}'`
|
|
19
|
+
}
|
|
20
|
+
|
|
17
21
|
/**
|
|
18
22
|
* 检查 gh CLI 是否登录
|
|
19
23
|
*/
|
|
@@ -31,7 +35,7 @@ export function checkGhAuth() {
|
|
|
31
35
|
*/
|
|
32
36
|
export function createPR(cwd, { title, body, base = 'main', head }) {
|
|
33
37
|
const output = gh(
|
|
34
|
-
`pr create --title
|
|
38
|
+
`pr create --title ${shellEscape(title)} --body ${shellEscape(body)} --base ${base} --head ${head}`,
|
|
35
39
|
cwd
|
|
36
40
|
);
|
|
37
41
|
// 从输出中提取 PR URL 和编号
|