@@ -8,6 +8,50 @@ const json = args.has('--json');
88const limitArg = process . argv . find ( ( arg ) => arg . startsWith ( '--limit=' ) ) ;
99const limit = Number ( limitArg ?. split ( '=' ) [ 1 ] ?? 10 ) ;
1010
11+ const CONTENT_SIGNALS = {
12+ zh : [
13+ [ 'AI 小白' , '不懂代码' , '小白' ] ,
14+ [ 'sample-data' , '示例数据' ] ,
15+ [ '烟测' ] ,
16+ [ '不覆盖原文件' ] ,
17+ [ '不写死' , '不写死密钥' ] ,
18+ [ '真实接线' ] ,
19+ [ '友好' , '不闪退' ] ,
20+ [ '降级' , '3 次失败' ] ,
21+ ] ,
22+ en : [
23+ [ 'AI beginner' , 'non-technical' , 'non-developer' ] ,
24+ [ 'sample-data' , 'sample data' ] ,
25+ [ 'Smoke test' , 'smoke-tested' ] ,
26+ [ 'Never overwrite' , 'never overwrite' ] ,
27+ [ 'No hard-coded' , 'no hard-coded' ] ,
28+ [ 'Real wiring' ] ,
29+ [ 'friendly' , 'no crash' ] ,
30+ [ 'downgrade' , '3 times' ] ,
31+ ] ,
32+ } ;
33+
34+ const STRUCTURE_SIGNALS = {
35+ zh : [
36+ [ '目标:' , '【目标】' ] ,
37+ [ '功能:' , '【核心功能】' ] ,
38+ [ '平台:' , '【平台与技术】' , '【环境】' ] ,
39+ [ '【快速交付】' , '【交付】' ] ,
40+ [ '完成标准' , '验收清单' , '自检清单' ] ,
41+ [ '【约束】' , '【实现纪律 / 安全】' ] ,
42+ [ '最终只报' , '最终汇报' , '使用说明' ] ,
43+ ] ,
44+ en : [
45+ [ 'Goal:' , '[Goal]' ] ,
46+ [ 'Features:' , '[Core Features]' ] ,
47+ [ 'Platform:' , '[Platform & Stack]' ] ,
48+ [ '[Fast Delivery]' , '[Delivery]' , '[Desktop Delivery Contract]' ] ,
49+ [ 'Done criteria' , 'Acceptance checklist' , 'Self-check' ] ,
50+ [ '[Constraints]' , '[Implementation / Safety]' ] ,
51+ [ 'Final report' , 'user guide' , 'guide' ] ,
52+ ] ,
53+ } ;
54+
1155const entry = `
1256import { caseBundles, getCasePrompt } from './src/data/cases/index.ts';
1357import { recipes, getRecipePrompt } from './src/data/recipes.ts';
@@ -38,17 +82,11 @@ export function collectPrompts() {
3882}
3983` ;
4084
41- function estimateTokens ( text ) {
42- const cjk = text . match ( / [ \u3400 - \u9FFF ] / gu) ?. length ?? 0 ;
43- const compactOther = text . replace ( / [ \s \u3400 - \u9FFF ] / gu, '' ) ;
44- return Math . ceil ( cjk * 1.05 + compactOther . length / 4 ) ;
45- }
46-
47- function percentile ( values , p ) {
48- if ( ! values . length ) return 0 ;
49- const sorted = [ ...values ] . sort ( ( a , b ) => a - b ) ;
50- const index = Math . min ( sorted . length - 1 , Math . ceil ( ( p / 100 ) * sorted . length ) - 1 ) ;
51- return sorted [ index ] ;
85+ function signalCount ( text , lang , signals ) {
86+ const normalized = text . toLowerCase ( ) ;
87+ return signals [ lang ] . filter ( ( alternatives ) =>
88+ alternatives . some ( ( phrase ) => normalized . includes ( phrase . toLowerCase ( ) ) )
89+ ) . length ;
5290}
5391
5492function summarize ( rows ) {
@@ -61,16 +99,18 @@ function summarize(rows) {
6199 }
62100
63101 return [ ...groups . entries ( ) ] . map ( ( [ bucket , list ] ) => {
64- const chars = list . map ( ( item ) => item . chars ) ;
65- const tokens = list . map ( ( item ) => item . tokens ) ;
102+ const contentSignals = list . map ( ( item ) => item . contentSignals ) ;
103+ const structureSignals = list . map ( ( item ) => item . structureSignals ) ;
104+ const qualityScore = list . map ( ( item ) => item . qualityScore ) ;
66105 return {
67106 bucket,
68107 count : list . length ,
69- avgChars : Math . round ( chars . reduce ( ( sum , value ) => sum + value , 0 ) / list . length ) ,
70- p95Chars : percentile ( chars , 95 ) ,
71- maxChars : Math . max ( ...chars ) ,
72- avgTokens : Math . round ( tokens . reduce ( ( sum , value ) => sum + value , 0 ) / list . length ) ,
73- maxTokens : Math . max ( ...tokens ) ,
108+ avgContentSignals : Number ( ( contentSignals . reduce ( ( sum , value ) => sum + value , 0 ) / list . length ) . toFixed ( 1 ) ) ,
109+ minContentSignals : Math . min ( ...contentSignals ) ,
110+ avgStructureSignals : Number ( ( structureSignals . reduce ( ( sum , value ) => sum + value , 0 ) / list . length ) . toFixed ( 1 ) ) ,
111+ minStructureSignals : Math . min ( ...structureSignals ) ,
112+ avgQualityScore : Number ( ( qualityScore . reduce ( ( sum , value ) => sum + value , 0 ) / list . length ) . toFixed ( 1 ) ) ,
113+ minQualityScore : Math . min ( ...qualityScore ) ,
74114 } ;
75115 } ) ;
76116}
@@ -101,32 +141,37 @@ const { collectPrompts } = await import(moduleUrl);
101141const rows = collectPrompts ( )
102142 . map ( ( item ) => ( {
103143 ...item ,
104- chars : [ ...item . text ] . length ,
105- bytes : Buffer . byteLength ( item . text , 'utf8' ) ,
106- tokens : estimateTokens ( item . text ) ,
144+ contentSignals : signalCount ( item . text , item . lang , CONTENT_SIGNALS ) ,
145+ structureSignals : signalCount ( item . text , item . lang , STRUCTURE_SIGNALS ) ,
146+ } ) )
147+ . map ( ( item ) => ( {
148+ ...item ,
149+ qualityScore : item . contentSignals + item . structureSignals ,
107150 } ) )
108- . sort ( ( a , b ) => b . tokens - a . tokens ) ;
151+ . sort ( ( a , b ) => a . qualityScore - b . qualityScore || a . contentSignals - b . contentSignals ) ;
109152
110153const result = {
111154 generatedAt : new Date ( ) . toISOString ( ) ,
112155 total : rows . length ,
113156 summary : summarize ( rows ) ,
114- longest : rows . slice ( 0 , Number . isFinite ( limit ) ? limit : 10 ) ,
157+ reviewQueue : rows . slice ( 0 , Number . isFinite ( limit ) ? limit : 10 ) ,
115158} ;
116159
117160if ( json ) {
118161 console . log ( JSON . stringify ( result , null , 2 ) ) ;
119162} else {
120- console . log ( `Prompt stats (${ result . total } final copy prompts)` ) ;
163+ console . log ( `Prompt quality report (${ result . total } final copy prompts)` ) ;
164+ console . log ( 'Focus: content, structure, safety, verification, and beginner usability coverage.' ) ;
121165 console . table ( result . summary ) ;
122- console . log ( `\nTop ${ limit } longest prompts:` ) ;
166+ console . log ( `\nReview queue ( ${ limit } lowest quality-coverage prompts) :` ) ;
123167 console . table (
124- result . longest . map ( ( { group, id, lang, chars , tokens } ) => ( {
168+ result . reviewQueue . map ( ( { group, id, lang, contentSignals , structureSignals , qualityScore } ) => ( {
125169 group,
126170 id,
127171 lang,
128- chars,
129- approxTokens : tokens ,
172+ contentSignals,
173+ structureSignals,
174+ qualityScore,
130175 } ) )
131176 ) ;
132177 console . log ( '\nTip: run npm run prompt:stats -- --json for machine-readable output.' ) ;
0 commit comments