@jojonax/codex-copilot 1.5.5 → 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 -447
- package/src/commands/init.js +298 -298
- package/src/commands/reset.js +61 -61
- package/src/commands/retry.js +190 -190
- package/src/commands/run.js +958 -958
- 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 -246
- package/src/utils/detect-prd.js +137 -137
- package/src/utils/git.js +388 -388
- package/src/utils/github.js +486 -486
- package/src/utils/json.js +220 -220
- 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 -330
- package/src/utils/shell-bootstrap.js +404 -0
- package/src/utils/update-check.js +103 -103
package/src/utils/detect-prd.js
CHANGED
|
@@ -1,137 +1,137 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PRD auto-detection module
|
|
3
|
-
* Automatically finds PRD documents in the project directory
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { readdirSync, readFileSync, statSync } from 'fs';
|
|
7
|
-
import { resolve, extname, relative } from 'path';
|
|
8
|
-
import { log } from './logger.js';
|
|
9
|
-
|
|
10
|
-
// PRD filename patterns (ordered by priority, highest first)
|
|
11
|
-
const PRD_PATTERNS = [
|
|
12
|
-
/prd/i,
|
|
13
|
-
/product.?requirement/i,
|
|
14
|
-
/product.?design/i,
|
|
15
|
-
/需求文档/,
|
|
16
|
-
/产品需求/,
|
|
17
|
-
/requirement/i,
|
|
18
|
-
/spec/i,
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
// Directories to search (ordered by priority)
|
|
22
|
-
const SEARCH_DIRS = [
|
|
23
|
-
'.', // Project root
|
|
24
|
-
'docs',
|
|
25
|
-
'doc',
|
|
26
|
-
'PRD',
|
|
27
|
-
'prd',
|
|
28
|
-
'.docs',
|
|
29
|
-
'documentation',
|
|
30
|
-
'文档',
|
|
31
|
-
];
|
|
32
|
-
|
|
33
|
-
// Directories to ignore
|
|
34
|
-
const IGNORE_DIRS = new Set([
|
|
35
|
-
'node_modules', '.git', '.next', 'dist', 'build', 'vendor',
|
|
36
|
-
'.codex-copilot', '.vscode', '.idea', '__pycache__', 'coverage',
|
|
37
|
-
]);
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Auto-detect PRD files in the project
|
|
41
|
-
* @param {string} projectDir - Project root directory
|
|
42
|
-
* @returns {Array<{path: string, score: number, name: string}>} Candidate PRD files sorted by match score
|
|
43
|
-
*/
|
|
44
|
-
export function detectPRD(projectDir) {
|
|
45
|
-
const candidates = [];
|
|
46
|
-
|
|
47
|
-
for (const dir of SEARCH_DIRS) {
|
|
48
|
-
const searchPath = resolve(projectDir, dir);
|
|
49
|
-
try {
|
|
50
|
-
if (!statSync(searchPath).isDirectory()) continue;
|
|
51
|
-
} catch {
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
scanDir(searchPath, projectDir, candidates, 0);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Sort by match score (descending)
|
|
59
|
-
candidates.sort((a, b) => b.score - a.score);
|
|
60
|
-
|
|
61
|
-
return candidates;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function scanDir(dir, projectDir, candidates, depth) {
|
|
65
|
-
if (depth > 3) return; // Max 3 levels deep
|
|
66
|
-
|
|
67
|
-
let entries;
|
|
68
|
-
try {
|
|
69
|
-
entries = readdirSync(dir, { withFileTypes: true });
|
|
70
|
-
} catch {
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
for (const entry of entries) {
|
|
75
|
-
const fullPath = resolve(dir, entry.name);
|
|
76
|
-
|
|
77
|
-
if (entry.isDirectory()) {
|
|
78
|
-
if (!IGNORE_DIRS.has(entry.name)) {
|
|
79
|
-
scanDir(fullPath, projectDir, candidates, depth + 1);
|
|
80
|
-
}
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Only scan markdown files
|
|
85
|
-
if (extname(entry.name).toLowerCase() !== '.md') continue;
|
|
86
|
-
|
|
87
|
-
// Calculate match score
|
|
88
|
-
const score = scorePRDMatch(entry.name, fullPath);
|
|
89
|
-
if (score > 0) {
|
|
90
|
-
candidates.push({
|
|
91
|
-
path: fullPath,
|
|
92
|
-
relativePath: relative(projectDir, fullPath),
|
|
93
|
-
name: entry.name,
|
|
94
|
-
score,
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function scorePRDMatch(filename, fullPath) {
|
|
101
|
-
let score = 0;
|
|
102
|
-
const lower = filename.toLowerCase();
|
|
103
|
-
|
|
104
|
-
// Filename pattern matching
|
|
105
|
-
for (let i = 0; i < PRD_PATTERNS.length; i++) {
|
|
106
|
-
if (PRD_PATTERNS[i].test(filename)) {
|
|
107
|
-
score += (PRD_PATTERNS.length - i) * 10; // Higher priority patterns get higher scores
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// File size bonus (PRDs are typically longer)
|
|
112
|
-
try {
|
|
113
|
-
const stat = statSync(fullPath);
|
|
114
|
-
if (stat.size > 5000) score += 5; // > 5KB
|
|
115
|
-
if (stat.size > 20000) score += 5; // > 20KB
|
|
116
|
-
} catch {}
|
|
117
|
-
|
|
118
|
-
// Content sampling (read first 2000 chars)
|
|
119
|
-
if (score > 0) {
|
|
120
|
-
try {
|
|
121
|
-
const content = readFileSync(fullPath, 'utf-8').slice(0, 2000);
|
|
122
|
-
const keywords = ['功能', '需求', '用户', '模块', 'feature', 'requirement', 'user story', '技术栈', '架构'];
|
|
123
|
-
for (const kw of keywords) {
|
|
124
|
-
if (content.toLowerCase().includes(kw.toLowerCase())) score += 2;
|
|
125
|
-
}
|
|
126
|
-
} catch {}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return score;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Read PRD file content
|
|
134
|
-
*/
|
|
135
|
-
export function readPRD(prdPath) {
|
|
136
|
-
return readFileSync(prdPath, 'utf-8');
|
|
137
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* PRD auto-detection module
|
|
3
|
+
* Automatically finds PRD documents in the project directory
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readdirSync, readFileSync, statSync } from 'fs';
|
|
7
|
+
import { resolve, extname, relative } from 'path';
|
|
8
|
+
import { log } from './logger.js';
|
|
9
|
+
|
|
10
|
+
// PRD filename patterns (ordered by priority, highest first)
|
|
11
|
+
const PRD_PATTERNS = [
|
|
12
|
+
/prd/i,
|
|
13
|
+
/product.?requirement/i,
|
|
14
|
+
/product.?design/i,
|
|
15
|
+
/需求文档/,
|
|
16
|
+
/产品需求/,
|
|
17
|
+
/requirement/i,
|
|
18
|
+
/spec/i,
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
// Directories to search (ordered by priority)
|
|
22
|
+
const SEARCH_DIRS = [
|
|
23
|
+
'.', // Project root
|
|
24
|
+
'docs',
|
|
25
|
+
'doc',
|
|
26
|
+
'PRD',
|
|
27
|
+
'prd',
|
|
28
|
+
'.docs',
|
|
29
|
+
'documentation',
|
|
30
|
+
'文档',
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
// Directories to ignore
|
|
34
|
+
const IGNORE_DIRS = new Set([
|
|
35
|
+
'node_modules', '.git', '.next', 'dist', 'build', 'vendor',
|
|
36
|
+
'.codex-copilot', '.vscode', '.idea', '__pycache__', 'coverage',
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Auto-detect PRD files in the project
|
|
41
|
+
* @param {string} projectDir - Project root directory
|
|
42
|
+
* @returns {Array<{path: string, score: number, name: string}>} Candidate PRD files sorted by match score
|
|
43
|
+
*/
|
|
44
|
+
export function detectPRD(projectDir) {
|
|
45
|
+
const candidates = [];
|
|
46
|
+
|
|
47
|
+
for (const dir of SEARCH_DIRS) {
|
|
48
|
+
const searchPath = resolve(projectDir, dir);
|
|
49
|
+
try {
|
|
50
|
+
if (!statSync(searchPath).isDirectory()) continue;
|
|
51
|
+
} catch {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
scanDir(searchPath, projectDir, candidates, 0);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Sort by match score (descending)
|
|
59
|
+
candidates.sort((a, b) => b.score - a.score);
|
|
60
|
+
|
|
61
|
+
return candidates;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function scanDir(dir, projectDir, candidates, depth) {
|
|
65
|
+
if (depth > 3) return; // Max 3 levels deep
|
|
66
|
+
|
|
67
|
+
let entries;
|
|
68
|
+
try {
|
|
69
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
70
|
+
} catch {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
for (const entry of entries) {
|
|
75
|
+
const fullPath = resolve(dir, entry.name);
|
|
76
|
+
|
|
77
|
+
if (entry.isDirectory()) {
|
|
78
|
+
if (!IGNORE_DIRS.has(entry.name)) {
|
|
79
|
+
scanDir(fullPath, projectDir, candidates, depth + 1);
|
|
80
|
+
}
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Only scan markdown files
|
|
85
|
+
if (extname(entry.name).toLowerCase() !== '.md') continue;
|
|
86
|
+
|
|
87
|
+
// Calculate match score
|
|
88
|
+
const score = scorePRDMatch(entry.name, fullPath);
|
|
89
|
+
if (score > 0) {
|
|
90
|
+
candidates.push({
|
|
91
|
+
path: fullPath,
|
|
92
|
+
relativePath: relative(projectDir, fullPath),
|
|
93
|
+
name: entry.name,
|
|
94
|
+
score,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function scorePRDMatch(filename, fullPath) {
|
|
101
|
+
let score = 0;
|
|
102
|
+
const lower = filename.toLowerCase();
|
|
103
|
+
|
|
104
|
+
// Filename pattern matching
|
|
105
|
+
for (let i = 0; i < PRD_PATTERNS.length; i++) {
|
|
106
|
+
if (PRD_PATTERNS[i].test(filename)) {
|
|
107
|
+
score += (PRD_PATTERNS.length - i) * 10; // Higher priority patterns get higher scores
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// File size bonus (PRDs are typically longer)
|
|
112
|
+
try {
|
|
113
|
+
const stat = statSync(fullPath);
|
|
114
|
+
if (stat.size > 5000) score += 5; // > 5KB
|
|
115
|
+
if (stat.size > 20000) score += 5; // > 20KB
|
|
116
|
+
} catch {}
|
|
117
|
+
|
|
118
|
+
// Content sampling (read first 2000 chars)
|
|
119
|
+
if (score > 0) {
|
|
120
|
+
try {
|
|
121
|
+
const content = readFileSync(fullPath, 'utf-8').slice(0, 2000);
|
|
122
|
+
const keywords = ['功能', '需求', '用户', '模块', 'feature', 'requirement', 'user story', '技术栈', '架构'];
|
|
123
|
+
for (const kw of keywords) {
|
|
124
|
+
if (content.toLowerCase().includes(kw.toLowerCase())) score += 2;
|
|
125
|
+
}
|
|
126
|
+
} catch {}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return score;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Read PRD file content
|
|
134
|
+
*/
|
|
135
|
+
export function readPRD(prdPath) {
|
|
136
|
+
return readFileSync(prdPath, 'utf-8');
|
|
137
|
+
}
|