@shiplens/cli 1.3.1 → 1.4.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/README.md +38 -62
- package/docs/02_SHIPLENS_CLI_/345/205/250/345/221/275/344/273/244/345/217/202/350/200/203/346/211/213/345/206/214.md +205 -0
- package/lib/api.js +55 -35
- package/lib/assets/skill.js +164 -87
- package/lib/cli.js +55 -59
- package/lib/commands/auth.js +41 -36
- package/lib/commands/context.js +18 -13
- package/lib/commands/dashboards.js +24 -16
- package/lib/commands/doctor.js +34 -43
- package/lib/commands/heatmap.js +9 -14
- package/lib/commands/init.js +54 -62
- package/lib/commands/mcp.js +2 -2
- package/lib/commands/pages.js +23 -19
- package/lib/commands/projects.js +13 -13
- package/lib/commands/query.js +12 -10
- package/lib/commands/sql.js +5 -4
- package/lib/commands/summary.js +7 -7
- package/lib/injector.js +22 -36
- package/lib/mcp-config.js +5 -5
- package/lib/taxonomy.js +25 -22
- package/package.json +5 -4
- package/prompts/README.md +11 -5
- package/prompts/prompts_cli_zh.md +743 -0
- package/prompts/prompts_cli_en.md +0 -671
package/lib/injector.js
CHANGED
|
@@ -95,7 +95,7 @@ function detectProject(dir = process.cwd()) {
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
//
|
|
98
|
+
// 若无 description,尝试从 README.md 提取第一段有效文本
|
|
99
99
|
if (!result.description) {
|
|
100
100
|
const readmeCandidates = ['README.md', 'readme.md', 'README.MD'];
|
|
101
101
|
for (const r of readmeCandidates) {
|
|
@@ -113,7 +113,7 @@ function detectProject(dir = process.cwd()) {
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
// 4
|
|
116
|
+
// 4 级分类自动推断
|
|
117
117
|
const taxonomy = inferTaxonomy({
|
|
118
118
|
name: result.project_name,
|
|
119
119
|
description: result.description,
|
|
@@ -286,7 +286,7 @@ function injectCRA(dir, appId) {
|
|
|
286
286
|
if (content.includes('@shiplens/sdk') || content.includes('initShiplens') || content.includes(appId)) {
|
|
287
287
|
return cand;
|
|
288
288
|
}
|
|
289
|
-
const injection = `\n// Shiplens SDK -
|
|
289
|
+
const injection = `\n// Shiplens SDK - 用户行为数据采集\nconst { initShiplens } = require('@shiplens/sdk');\ninitShiplens({ appId: '${appId}' });\n`;
|
|
290
290
|
fs.writeFileSync(p, content + injection, 'utf8');
|
|
291
291
|
return cand;
|
|
292
292
|
}
|
|
@@ -351,7 +351,7 @@ async function installSDKDependency(dir, pkgManager = 'npm') {
|
|
|
351
351
|
const sources = [
|
|
352
352
|
{
|
|
353
353
|
id: 'npmjs',
|
|
354
|
-
name: 'npm registry',
|
|
354
|
+
name: 'NPM 官方源 (npm registry)',
|
|
355
355
|
getCommand: (pm) => {
|
|
356
356
|
if (pm === 'pnpm') return `pnpm${ext} add @shiplens/sdk`;
|
|
357
357
|
if (pm === 'yarn') return `yarn${ext} add @shiplens/sdk`;
|
|
@@ -361,7 +361,7 @@ async function installSDKDependency(dir, pkgManager = 'npm') {
|
|
|
361
361
|
},
|
|
362
362
|
{
|
|
363
363
|
id: 'npmmirror',
|
|
364
|
-
name: '
|
|
364
|
+
name: '阿里云/淘宝镜像源 (npmmirror)',
|
|
365
365
|
getCommand: (pm) => {
|
|
366
366
|
const mirror = '--registry=https://registry.npmmirror.com';
|
|
367
367
|
if (pm === 'pnpm') return `pnpm${ext} add @shiplens/sdk ${mirror}`;
|
|
@@ -372,7 +372,7 @@ async function installSDKDependency(dir, pkgManager = 'npm') {
|
|
|
372
372
|
},
|
|
373
373
|
{
|
|
374
374
|
id: 'github',
|
|
375
|
-
name: 'GitHub
|
|
375
|
+
name: 'GitHub 源码镜像',
|
|
376
376
|
getCommand: (pm) => {
|
|
377
377
|
const repoUrl = 'https://github.com/Hyperlong/shiplens-sdk.git';
|
|
378
378
|
if (pm === 'pnpm') return `pnpm${ext} add ${repoUrl}`;
|
|
@@ -383,7 +383,7 @@ async function installSDKDependency(dir, pkgManager = 'npm') {
|
|
|
383
383
|
},
|
|
384
384
|
{
|
|
385
385
|
id: 'cdn',
|
|
386
|
-
name: 'Shiplens CDN
|
|
386
|
+
name: 'Shiplens 官方 CDN 镜像',
|
|
387
387
|
getCommand: (pm) => {
|
|
388
388
|
const cdnUrl = 'https://cdn.shiplens.dev/packages/shiplens-sdk.tgz';
|
|
389
389
|
if (pm === 'pnpm') return `pnpm${ext} add ${cdnUrl}`;
|
|
@@ -394,8 +394,8 @@ async function installSDKDependency(dir, pkgManager = 'npm') {
|
|
|
394
394
|
},
|
|
395
395
|
];
|
|
396
396
|
|
|
397
|
-
const TIER1_DELAY_MS = 20 * 1000; //
|
|
398
|
-
const TOTAL_TIMEOUT_MS = 5 * 60 * 1000; //
|
|
397
|
+
const TIER1_DELAY_MS = 20 * 1000; // 20秒阶梯
|
|
398
|
+
const TOTAL_TIMEOUT_MS = 5 * 60 * 1000; // 5分钟总超时
|
|
399
399
|
|
|
400
400
|
return new Promise((resolve) => {
|
|
401
401
|
let isDone = false;
|
|
@@ -473,25 +473,25 @@ async function installSDKDependency(dir, pkgManager = 'npm') {
|
|
|
473
473
|
cleanupAndFinish({
|
|
474
474
|
success: false,
|
|
475
475
|
manager: pkgManager,
|
|
476
|
-
error: '
|
|
476
|
+
error: '4 个下载源均已失败或超时',
|
|
477
477
|
});
|
|
478
478
|
}
|
|
479
479
|
};
|
|
480
480
|
|
|
481
|
-
// t = 0s:
|
|
481
|
+
// t = 0s: 启动 Tier 1 (npmjs)
|
|
482
482
|
runSource(sources[0]);
|
|
483
483
|
|
|
484
|
-
// t = 20s:
|
|
484
|
+
// t = 20s: 若仍未完成,保持 npmjs 并发启动 npmmirror + GitHub + CDN
|
|
485
485
|
tier2Timer = setTimeout(() => {
|
|
486
486
|
startBackupSources();
|
|
487
487
|
}, TIER1_DELAY_MS);
|
|
488
488
|
|
|
489
|
-
// t = 5min:
|
|
489
|
+
// t = 5min: 全部源总超时
|
|
490
490
|
totalTimer = setTimeout(() => {
|
|
491
491
|
cleanupAndFinish({
|
|
492
492
|
success: false,
|
|
493
493
|
manager: pkgManager,
|
|
494
|
-
error: '
|
|
494
|
+
error: '所有下载源在 5 分钟后均已超时',
|
|
495
495
|
});
|
|
496
496
|
}, TOTAL_TIMEOUT_MS);
|
|
497
497
|
});
|
|
@@ -508,14 +508,14 @@ function getGitEmail() {
|
|
|
508
508
|
function injectSkill(dir = process.cwd()) {
|
|
509
509
|
const { SKILL_CONTENT } = require('./assets/skill');
|
|
510
510
|
|
|
511
|
-
// 1.
|
|
511
|
+
// 1. 生成通用 Agent Skill 目录 (.agents/skills/shiplens/SKILL.md)
|
|
512
512
|
const agentSkillDir = path.join(dir, '.agents', 'skills', 'shiplens');
|
|
513
513
|
try {
|
|
514
514
|
fs.mkdirSync(agentSkillDir, { recursive: true });
|
|
515
515
|
fs.writeFileSync(path.join(agentSkillDir, 'SKILL.md'), SKILL_CONTENT, 'utf8');
|
|
516
516
|
} catch (e) {}
|
|
517
517
|
|
|
518
|
-
// 2.
|
|
518
|
+
// 2. 生成 Cursor 专属规则 (.cursor/rules/shiplens.mdc)
|
|
519
519
|
const cursorRulesDir = path.join(dir, '.cursor', 'rules');
|
|
520
520
|
try {
|
|
521
521
|
fs.mkdirSync(cursorRulesDir, { recursive: true });
|
|
@@ -526,7 +526,7 @@ function injectSkill(dir = process.cwd()) {
|
|
|
526
526
|
}
|
|
527
527
|
|
|
528
528
|
function detectExistingApp(dir = process.cwd()) {
|
|
529
|
-
// 1.
|
|
529
|
+
// 1. 检查本地 .shiplens.json 状态机
|
|
530
530
|
const cfgPath = path.join(dir, '.shiplens.json');
|
|
531
531
|
if (fs.existsSync(cfgPath)) {
|
|
532
532
|
try {
|
|
@@ -543,7 +543,7 @@ function detectExistingApp(dir = process.cwd()) {
|
|
|
543
543
|
} catch (e) {}
|
|
544
544
|
}
|
|
545
545
|
|
|
546
|
-
// 2.
|
|
546
|
+
// 2. 检查代码入口中的插桩
|
|
547
547
|
const checkFiles = [
|
|
548
548
|
'src/app/layout.tsx', 'app/layout.tsx',
|
|
549
549
|
'src/app/layout.jsx', 'app/layout.jsx',
|
|
@@ -567,6 +567,7 @@ function detectExistingApp(dir = process.cwd()) {
|
|
|
567
567
|
if (fs.existsSync(p)) {
|
|
568
568
|
try {
|
|
569
569
|
const content = fs.readFileSync(p, 'utf8');
|
|
570
|
+
// 匹配 appId: 'sl_live_...' 或 data-app-id="sl_live_..."
|
|
570
571
|
const matchAppId = content.match(/appId\s*:\s*['"]([^'"]+)['"]/);
|
|
571
572
|
if (matchAppId && matchAppId[1]) {
|
|
572
573
|
return {
|
|
@@ -583,6 +584,7 @@ function detectExistingApp(dir = process.cwd()) {
|
|
|
583
584
|
source_file: rel,
|
|
584
585
|
};
|
|
585
586
|
}
|
|
587
|
+
// 如果包含 @shiplens/sdk 或 cdn.shiplens.dev 但未提取到具体 ID
|
|
586
588
|
if (content.includes('@shiplens/sdk') || content.includes('cdn.shiplens.dev/sdk.js') || content.includes('initShiplens')) {
|
|
587
589
|
return {
|
|
588
590
|
has_existing: true,
|
|
@@ -594,7 +596,7 @@ function detectExistingApp(dir = process.cwd()) {
|
|
|
594
596
|
}
|
|
595
597
|
}
|
|
596
598
|
|
|
597
|
-
// 3.
|
|
599
|
+
// 3. 检查 package.json 依赖
|
|
598
600
|
const pkgPath = path.join(dir, 'package.json');
|
|
599
601
|
if (fs.existsSync(pkgPath)) {
|
|
600
602
|
try {
|
|
@@ -613,22 +615,6 @@ function detectExistingApp(dir = process.cwd()) {
|
|
|
613
615
|
return { has_existing: false };
|
|
614
616
|
}
|
|
615
617
|
|
|
616
|
-
function autoGitCommit(dir = process.cwd(), message = 'feat: Integrate Shiplens SDK and generate configuration & dashboards') {
|
|
617
|
-
try {
|
|
618
|
-
execSync('git rev-parse --is-inside-work-tree', { cwd: dir, stdio: 'ignore' });
|
|
619
|
-
const status = execSync('git status --porcelain', { cwd: dir, encoding: 'utf8' }).trim();
|
|
620
|
-
if (status) {
|
|
621
|
-
execSync('git add .', { cwd: dir, stdio: 'ignore' });
|
|
622
|
-
execSync(`git commit -m "${message}"`, { cwd: dir, stdio: 'ignore' });
|
|
623
|
-
const hash = execSync('git rev-parse --short HEAD', { cwd: dir, encoding: 'utf8' }).trim();
|
|
624
|
-
return { committed: true, hash, message };
|
|
625
|
-
}
|
|
626
|
-
} catch (e) {
|
|
627
|
-
return { committed: false, error: e.message };
|
|
628
|
-
}
|
|
629
|
-
return { committed: false, reason: 'no_changes' };
|
|
630
|
-
}
|
|
631
|
-
|
|
632
618
|
module.exports = {
|
|
633
619
|
FRAMEWORKS,
|
|
634
620
|
detectPackageManager,
|
|
@@ -638,5 +624,5 @@ module.exports = {
|
|
|
638
624
|
injectSkill,
|
|
639
625
|
installSDKDependency,
|
|
640
626
|
getGitEmail,
|
|
641
|
-
autoGitCommit,
|
|
642
627
|
};
|
|
628
|
+
|
package/lib/mcp-config.js
CHANGED
|
@@ -38,7 +38,7 @@ function getMcpServer(client, url) {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
function getMcpConfig(client, url) {
|
|
41
|
-
if (!CLIENTS.includes(client)) throw makeError(
|
|
41
|
+
if (!CLIENTS.includes(client)) throw makeError(`不支持的 MCP 客户端: ${client}`, 'UNSUPPORTED_MCP_CLIENT');
|
|
42
42
|
return { mcpServers: { shiplens: getMcpServer(client, url) } };
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -49,7 +49,7 @@ function readJsonConfig(filePath) {
|
|
|
49
49
|
if (!value || Array.isArray(value) || typeof value !== 'object') throw new Error('root must be object');
|
|
50
50
|
return value;
|
|
51
51
|
} catch (error) {
|
|
52
|
-
throw makeError(
|
|
52
|
+
throw makeError(`无法读取 MCP 配置 ${filePath}:请先修复 JSON 后重试`, 'INVALID_MCP_CONFIG');
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
@@ -70,8 +70,8 @@ function mergeJsonMcpConfig(client, url, filePath) {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
function configureMcpClient(client, url, options = {}) {
|
|
73
|
-
if (!CLIENTS.includes(client)) throw makeError(
|
|
74
|
-
if (!url) throw makeError('MCP
|
|
73
|
+
if (!CLIENTS.includes(client)) throw makeError(`不支持的 MCP 客户端: ${client}`, 'UNSUPPORTED_MCP_CLIENT');
|
|
74
|
+
if (!url) throw makeError('MCP 地址不能为空', 'MCP_URL_REQUIRED');
|
|
75
75
|
|
|
76
76
|
if (client === 'manual') {
|
|
77
77
|
return { written: false, already_configured: false, path: null, config: getMcpConfig('manual', url) };
|
|
@@ -88,7 +88,7 @@ function configureMcpClient(client, url, options = {}) {
|
|
|
88
88
|
try {
|
|
89
89
|
exec(codexCommand, ['mcp', 'add', 'shiplens', '--', process.platform === 'win32' ? 'npx.cmd' : 'npx', '--yes', 'shiplens-cli', 'mcp', 'serve'], { stdio: 'ignore' });
|
|
90
90
|
} catch (error) {
|
|
91
|
-
throw makeError('
|
|
91
|
+
throw makeError('无法调用 Codex MCP 配置命令,请确认 Codex Desktop 已安装并可在终端中运行 codex。', 'CODEX_MCP_CONFIG_FAILED');
|
|
92
92
|
}
|
|
93
93
|
return { written: true, already_configured: false, path: configPath, config: getMcpConfig('codex', url) };
|
|
94
94
|
}
|
package/lib/taxonomy.js
CHANGED
|
@@ -11,6 +11,7 @@ function loadTaxonomy() {
|
|
|
11
11
|
if (fs.existsSync(filePath)) {
|
|
12
12
|
taxonomyCache = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
13
13
|
} else {
|
|
14
|
+
// 容错兜底
|
|
14
15
|
taxonomyCache = { genres: [] };
|
|
15
16
|
}
|
|
16
17
|
} catch (e) {
|
|
@@ -73,15 +74,15 @@ function buildIndex() {
|
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
const CATEGORY_DISPLAY_NAMES = {
|
|
76
|
-
core_features: '
|
|
77
|
-
monetization: '
|
|
78
|
-
social_engagement: '
|
|
79
|
-
technical_platform: '
|
|
80
|
-
content_theme: '
|
|
77
|
+
core_features: '核心功能',
|
|
78
|
+
monetization: '变现模式',
|
|
79
|
+
social_engagement: '社交促活',
|
|
80
|
+
technical_platform: '技术平台',
|
|
81
|
+
content_theme: '内容与合规',
|
|
81
82
|
};
|
|
82
83
|
|
|
83
84
|
/**
|
|
84
|
-
*
|
|
85
|
+
* 根据项目的名称、简介、依赖、目录特征自动推断最契合的 4 级分类与特性标签
|
|
85
86
|
*/
|
|
86
87
|
function inferTaxonomy(projectContext = {}) {
|
|
87
88
|
const { genresMap, subgenresMap, tagsMap } = buildIndex();
|
|
@@ -102,7 +103,7 @@ function inferTaxonomy(projectContext = {}) {
|
|
|
102
103
|
let matchedSubgenreId = 'developer_tools';
|
|
103
104
|
const matchedTags = new Set();
|
|
104
105
|
|
|
105
|
-
// 1. Game
|
|
106
|
+
// 1. 判断是否为 Game
|
|
106
107
|
const isGame =
|
|
107
108
|
depKeys.some((d) => ['phaser', 'pixi.js', 'three', 'kaboom', 'melonjs'].includes(d)) ||
|
|
108
109
|
/\b(game|arcade|rpg|puzzle|casual|roguelike|fps|chess|cards)\b/.test(combinedText);
|
|
@@ -111,7 +112,7 @@ function inferTaxonomy(projectContext = {}) {
|
|
|
111
112
|
matchedGenreId = 'game_casual_puzzle';
|
|
112
113
|
matchedSubgenreId = 'merge_mechanic_puzzle';
|
|
113
114
|
} else {
|
|
114
|
-
// App
|
|
115
|
+
// App 分支推断
|
|
115
116
|
if (/\b(vpn|proxy|shadowsocks|v2ray|wireguard|clash)\b/.test(combinedText)) {
|
|
116
117
|
matchedGenreId = 'utilities';
|
|
117
118
|
matchedSubgenreId = 'net_vpn_proxy';
|
|
@@ -119,6 +120,7 @@ function inferTaxonomy(projectContext = {}) {
|
|
|
119
120
|
matchedGenreId = 'utilities';
|
|
120
121
|
matchedSubgenreId = 'system_cleaner_optimizer';
|
|
121
122
|
} else if (/\b(calc|turnip|price|profit|currency|converter|calculator|finance|financial|stock|crypto|accounting|tax|invoice|budget)\b/.test(combinedText)) {
|
|
123
|
+
// 计算器 / 金融理财 / 价格收益预测
|
|
122
124
|
matchedGenreId = 'finance_fintech';
|
|
123
125
|
if (/\b(stock|trading|invest)\b/.test(combinedText)) {
|
|
124
126
|
matchedSubgenreId = 'stock_trading_investment';
|
|
@@ -148,7 +150,7 @@ function inferTaxonomy(projectContext = {}) {
|
|
|
148
150
|
}
|
|
149
151
|
}
|
|
150
152
|
|
|
151
|
-
//
|
|
153
|
+
// 确保 genre 和 subgenre 真实有效
|
|
152
154
|
if (!genresMap.has(matchedGenreId)) {
|
|
153
155
|
matchedGenreId = genresMap.keys().next().value || 'utilities';
|
|
154
156
|
}
|
|
@@ -159,11 +161,11 @@ function inferTaxonomy(projectContext = {}) {
|
|
|
159
161
|
|
|
160
162
|
const currentSubObj = currentGenreObj.subgenres.get(matchedSubgenreId) || subgenresMap.get(matchedSubgenreId);
|
|
161
163
|
|
|
162
|
-
// 2.
|
|
164
|
+
// 2. 从当前 Sub-genre 的 5 大类别中智能挑选特性标签 (Feature Tags)
|
|
163
165
|
if (currentSubObj && currentSubObj.tag_categories) {
|
|
164
166
|
const cats = currentSubObj.tag_categories;
|
|
165
167
|
|
|
166
|
-
// A. Core Features
|
|
168
|
+
// A. 核心功能 (Core Features) - 选 2~3 个
|
|
167
169
|
const coreList = cats.core_features || [];
|
|
168
170
|
if (coreList.length > 0) {
|
|
169
171
|
let added = 0;
|
|
@@ -182,7 +184,7 @@ function inferTaxonomy(projectContext = {}) {
|
|
|
182
184
|
}
|
|
183
185
|
}
|
|
184
186
|
|
|
185
|
-
// B.
|
|
187
|
+
// B. 变现模式 (Monetization) - 选 1 个
|
|
186
188
|
const monList = cats.monetization || [];
|
|
187
189
|
if (monList.length > 0) {
|
|
188
190
|
let monTag = null;
|
|
@@ -196,7 +198,7 @@ function inferTaxonomy(projectContext = {}) {
|
|
|
196
198
|
if (monTag) matchedTags.add(monTag.tag_id);
|
|
197
199
|
}
|
|
198
200
|
|
|
199
|
-
// C. Technical Platform
|
|
201
|
+
// C. 技术平台 (Technical Platform) - 选 1 个
|
|
200
202
|
const techList = cats.technical_platform || [];
|
|
201
203
|
if (techList.length > 0) {
|
|
202
204
|
let techTag = techList[0];
|
|
@@ -210,14 +212,14 @@ function inferTaxonomy(projectContext = {}) {
|
|
|
210
212
|
if (techTag) matchedTags.add(techTag.tag_id);
|
|
211
213
|
}
|
|
212
214
|
|
|
213
|
-
// D. Social Engagement
|
|
215
|
+
// D. 社交与促活 (Social Engagement) - 选 1 个
|
|
214
216
|
const socList = cats.social_engagement || [];
|
|
215
217
|
if (socList.length > 0) {
|
|
216
218
|
matchedTags.add(socList[0].tag_id);
|
|
217
219
|
}
|
|
218
220
|
}
|
|
219
221
|
|
|
220
|
-
//
|
|
222
|
+
// 最终组装结构化对象
|
|
221
223
|
const tagDetails = [];
|
|
222
224
|
for (const tagId of matchedTags) {
|
|
223
225
|
const info = tagsMap.get(tagId);
|
|
@@ -229,11 +231,12 @@ function inferTaxonomy(projectContext = {}) {
|
|
|
229
231
|
category_name: CATEGORY_DISPLAY_NAMES[info.category] || info.category,
|
|
230
232
|
});
|
|
231
233
|
} else {
|
|
234
|
+
// 兜底虚拟 tag
|
|
232
235
|
tagDetails.push({
|
|
233
236
|
tag_id: tagId,
|
|
234
237
|
name: tagId.replace(/_/g, ' '),
|
|
235
238
|
category: 'core_features',
|
|
236
|
-
category_name: '
|
|
239
|
+
category_name: '核心功能',
|
|
237
240
|
});
|
|
238
241
|
}
|
|
239
242
|
}
|
|
@@ -254,7 +257,7 @@ function inferTaxonomy(projectContext = {}) {
|
|
|
254
257
|
}
|
|
255
258
|
|
|
256
259
|
/**
|
|
257
|
-
*
|
|
260
|
+
* 格式化输出为 4-Level 汇报格式
|
|
258
261
|
*/
|
|
259
262
|
function formatTaxonomySummary(taxonomy) {
|
|
260
263
|
if (!taxonomy || !taxonomy.genre) return '';
|
|
@@ -270,9 +273,9 @@ function formatTaxonomySummary(taxonomy) {
|
|
|
270
273
|
}
|
|
271
274
|
|
|
272
275
|
const lines = [
|
|
273
|
-
` •
|
|
274
|
-
` • Sub-genre
|
|
275
|
-
` • Feature Tags:`,
|
|
276
|
+
` • 一级大类 (Genre): ${genreStr}`,
|
|
277
|
+
` • 二级子类 (Sub-genre): ${subgenreStr}`,
|
|
278
|
+
` • 特性标签 (Feature Tags):`,
|
|
276
279
|
];
|
|
277
280
|
|
|
278
281
|
for (const [catKey, items] of Object.entries(categorized)) {
|
|
@@ -284,7 +287,7 @@ function formatTaxonomySummary(taxonomy) {
|
|
|
284
287
|
}
|
|
285
288
|
|
|
286
289
|
/**
|
|
287
|
-
*
|
|
290
|
+
* 校验或通过 ID 组合反查
|
|
288
291
|
*/
|
|
289
292
|
function resolveTaxonomyFromIDs(genreId, subgenreId, tagIdList = []) {
|
|
290
293
|
const { genresMap, subgenresMap, tagsMap } = buildIndex();
|
|
@@ -324,7 +327,7 @@ function resolveTaxonomyFromIDs(genreId, subgenreId, tagIdList = []) {
|
|
|
324
327
|
tag_id: tid,
|
|
325
328
|
name: tid.replace(/_/g, ' '),
|
|
326
329
|
category: 'core_features',
|
|
327
|
-
category_name: '
|
|
330
|
+
category_name: '核心功能',
|
|
328
331
|
});
|
|
329
332
|
}
|
|
330
333
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shiplens/cli",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Shiplens CLI
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"description": "Shiplens CLI — Web 用户行为数据统计与 AI Agent 智能分析引擎",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"shiplens-cli": "bin/shiplens.js",
|
|
8
|
-
"shiplens": "bin/shiplens.js"
|
|
7
|
+
"shiplens-cli": "./bin/shiplens.js",
|
|
8
|
+
"shiplens": "./bin/shiplens.js"
|
|
9
9
|
},
|
|
10
10
|
"keywords": [
|
|
11
11
|
"shiplens",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"bin",
|
|
34
34
|
"lib",
|
|
35
35
|
"prompts",
|
|
36
|
+
"docs",
|
|
36
37
|
"README.md",
|
|
37
38
|
"LICENSE"
|
|
38
39
|
],
|
package/prompts/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
# Shiplens Prompt Libraries
|
|
2
2
|
|
|
3
3
|
This directory hosts the deterministic CLI scenario-based prompt libraries for Shiplens.
|
|
4
4
|
|
|
@@ -9,13 +9,19 @@ This directory hosts the deterministic CLI scenario-based prompt libraries for S
|
|
|
9
9
|
```text
|
|
10
10
|
prompts/
|
|
11
11
|
├── README.md # Multilingual architecture overview
|
|
12
|
-
├── prompts_cli_en.md # [English
|
|
13
|
-
└──
|
|
12
|
+
├── prompts_cli_en.md # [English] 42 deterministic CLI analysis scenarios
|
|
13
|
+
└── prompts_cli_zh.md # [中文] 42 个确定性 CLI 分析场景
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
18
|
## ⚡ Execution & Dynamic Overrides
|
|
19
19
|
|
|
20
|
-
1. **Deterministic Execution**: Every scenario
|
|
21
|
-
2. **Dynamic Overrides**: Local rules defined in `.shiplens/learnings.md` override default parameters (such as `--range`, `--grain`, or target funnel routes) during AI Agent execution.
|
|
20
|
+
1. **Deterministic Execution**: Every scenario provides explicit, reproducible CLI commands and SQL queries paired with textbook analytical foundations.
|
|
21
|
+
2. **Dynamic Overrides**: Local rules defined in `.shiplens/learnings.md` override default parameters (such as `--range`, `--grain`, or target funnel routes) during AI Agent execution.
|
|
22
|
+
3. **Scenario Outline**: Each file includes a categorized outline at the top for quick scenario discovery. Agent should read the outline first, then jump to the matching scenario.
|
|
23
|
+
|
|
24
|
+
## 🌐 Multilingual Architecture
|
|
25
|
+
|
|
26
|
+
- Scenario IDs, titles, and structure are **1:1 aligned** across languages.
|
|
27
|
+
- Add new languages by creating `prompts_cli_<lang>.md` with the same outline structure and scenario ordering.
|