@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.
@@ -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
+ }