@jdzhang225gmail/fontend-prompt 0.1.1 → 0.2.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 +31 -56
- package/dist/cli.js +1 -1
- package/dist/index.js +0 -176
- package/package.json +3 -5
package/README.md
CHANGED
|
@@ -1,104 +1,79 @@
|
|
|
1
|
-
# 前端提示词 Skills
|
|
1
|
+
# 前端提示词 Skills (CLI)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
这是一个**前端开发辅助 CLI 工具**。它通过**零 Token 消耗**的本地分析,将模糊的需求转化为**专业、结构化、带审批流的前端开发方案**,供 Agent 或开发者直接使用。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## 🌟 核心特性
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
- **MCP Server 模式**: 经典模式,适合 Claude Desktop 等标准 MCP 客户端。
|
|
7
|
+
- **纯 CLI 工具**: 无需常驻服务,即用即走。
|
|
8
|
+
- **节省 Token**: 本地扫描项目结构,无需将整个文件树塞入 Context。
|
|
9
|
+
- **结构化输出**: 生成包含文件变更、审批关口、校验步骤的标准 Prompt。
|
|
11
10
|
|
|
12
11
|
---
|
|
13
12
|
|
|
14
|
-
## 🚀
|
|
13
|
+
## 🚀 安装 (推荐)
|
|
14
|
+
|
|
15
|
+
为了获得极速体验(避免每次 `npx` 下载),请**全局安装**:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g @jdzhang225gmail/fontend-prompt
|
|
19
|
+
```
|
|
15
20
|
|
|
16
|
-
|
|
21
|
+
安装后,即可使用 `fontend-prompt` 命令。
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 📖 使用指南
|
|
17
26
|
|
|
18
27
|
### 1. 扫描项目 (Scan)
|
|
19
28
|
快速了解项目架构、技术栈与关键文件。
|
|
20
29
|
|
|
21
30
|
```bash
|
|
22
|
-
|
|
31
|
+
fontend-prompt scan
|
|
23
32
|
# 或指定目录
|
|
24
|
-
|
|
33
|
+
fontend-prompt scan --depth 2
|
|
25
34
|
```
|
|
26
35
|
|
|
27
36
|
### 2. 优化需求 (Optimize)
|
|
28
|
-
|
|
37
|
+
将你的“一句话需求”转化为详细的开发方案。
|
|
29
38
|
|
|
30
39
|
```bash
|
|
31
|
-
|
|
40
|
+
fontend-prompt optimize "给后台增加一个用户管理页面"
|
|
32
41
|
```
|
|
33
42
|
|
|
34
43
|
### 3. 评分 (Score)
|
|
35
44
|
看看你的 Prompt 写得好不好,缺什么信息。
|
|
36
45
|
|
|
37
46
|
```bash
|
|
38
|
-
|
|
47
|
+
fontend-prompt score "帮我写个页面"
|
|
39
48
|
```
|
|
40
49
|
|
|
41
50
|
---
|
|
42
51
|
|
|
43
52
|
## 🤖 在 Agent 中使用 (Antigravity / Claude Code)
|
|
44
53
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
告诉你的 Agent(或者写在 `.cursorrules` / `.agent/workflows` 中):
|
|
48
|
-
|
|
49
|
-
> "当需要理解项目结构时,请运行命令 `npx @jdzhang225gmail/fontend-prompt scan`。"
|
|
50
|
-
> "当需要规划新功能时,请运行命令 `npx @jdzhang225gmail/fontend-prompt optimize '<需求>'`,并根据输出结果执行。"
|
|
51
|
-
|
|
52
|
-
### 方式二:作为库集成
|
|
54
|
+
为了让 Agent 更流畅地调用本工具,请告诉它直接使用命令行。
|
|
53
55
|
|
|
54
|
-
|
|
56
|
+
**添加到 `.cursorrules` 或 Agent 系统提示词:**
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const tree = await scanProject({ rootDir: "." });
|
|
60
|
-
const plan = optimizeFrontendPrompt({ userPrompt: "..." });
|
|
61
|
-
```
|
|
58
|
+
> "当需要理解项目结构时,请运行命令 `fontend-prompt scan`。"
|
|
59
|
+
> "当需要规划新功能时,请运行命令 `fontend-prompt optimize '<需求>'`,并根据输出结果执行。"
|
|
62
60
|
|
|
63
61
|
---
|
|
64
62
|
|
|
65
|
-
## 📦
|
|
63
|
+
## 📦 本地开发与构建
|
|
66
64
|
|
|
67
|
-
|
|
65
|
+
如果你想二次开发:
|
|
68
66
|
|
|
69
67
|
```bash
|
|
70
68
|
npm install
|
|
71
69
|
npm run build
|
|
72
70
|
|
|
73
|
-
#
|
|
74
|
-
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
---
|
|
78
|
-
|
|
79
|
-
## 🛠️ MCP Server 配置
|
|
80
|
-
|
|
81
|
-
如果你仍希望在 Claude Desktop 中作为常驻服务使用:
|
|
82
|
-
|
|
83
|
-
```json
|
|
84
|
-
{
|
|
85
|
-
"mcpServers": {
|
|
86
|
-
"frontend-helper": {
|
|
87
|
-
"command": "npx",
|
|
88
|
-
"args": ["-y", "@jdzhang225gmail/fontend-prompt"]
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
71
|
+
# 本地测试
|
|
72
|
+
node dist/cli.js --help
|
|
92
73
|
```
|
|
93
74
|
|
|
94
75
|
---
|
|
95
76
|
|
|
96
|
-
## 功能特性
|
|
97
|
-
|
|
98
|
-
- **中文优先**: 所有输出、文档、注释默认均为中文。
|
|
99
|
-
- **Gate 机制**: 在关键节点(设计/计划/验收)生成审批关口,防止 AI 一条路走到黑。
|
|
100
|
-
- **安全扫描**: 自动识别项目目录,但严格限制在 CWD 下访问。
|
|
101
|
-
|
|
102
77
|
## 许可
|
|
103
78
|
|
|
104
79
|
MIT
|
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import * as fs from "fs/promises";
|
|
|
7
7
|
// 读取 package.json 获取版本号
|
|
8
8
|
// const pkg = JSON.parse(await fs.readFile(new URL("../package.json", import.meta.url), "utf-8"));
|
|
9
9
|
// 为了避免 ESM 路径问题,暂时硬编码,或者用 simple import
|
|
10
|
-
const version = "0.
|
|
10
|
+
const version = "0.2.0";
|
|
11
11
|
const cli = cac("fontend-skill");
|
|
12
12
|
cli
|
|
13
13
|
.command("scan [root]", "扫描项目目录结构与关键文件")
|
package/dist/index.js
CHANGED
|
@@ -1,181 +1,5 @@
|
|
|
1
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
-
import { scanProject } from "./skills/scan.js";
|
|
5
|
-
import { buildOptimizedPromptPackage } from "./skills/optimize.js";
|
|
6
|
-
import { scoreFrontendPrompt } from "./skills/score.js";
|
|
7
|
-
import { asNonEmptyString } from "./utils.js";
|
|
8
|
-
// === 导出 Skills (作为库使用) ===
|
|
9
1
|
export * from "./types.js";
|
|
10
2
|
export * from "./utils.js";
|
|
11
3
|
export { scanProject } from "./skills/scan.js";
|
|
12
4
|
export { buildOptimizedPromptPackage as optimizeFrontendPrompt } from "./skills/optimize.js";
|
|
13
5
|
export { scoreFrontendPrompt } from "./skills/score.js";
|
|
14
|
-
// === MCP Server 实现 ===
|
|
15
|
-
// 只有当文件被直接运行时才启动 Server
|
|
16
|
-
// 简单的判断方式:看是否通过 import 导入
|
|
17
|
-
// 在 Node.js 中,如果 entry point 是此文件,则启动
|
|
18
|
-
const isMainModule = import.meta.url.endsWith(process.argv[1]) || (process.argv[1] && import.meta.url.includes(process.argv[1]));
|
|
19
|
-
// 注意:ESM 中判断 isMain 比较麻烦,这里我们简化处理:
|
|
20
|
-
// 如果是通过 `node dist/index.js` 启动,我们假设它是为了跑 server。
|
|
21
|
-
// 如果作为库被 import,通常不会立即执行 await server.connect。
|
|
22
|
-
// 但 MCP SDK 的 connect 是阻塞的吗?StdioServerTransport 是基于 stdio 的。
|
|
23
|
-
// 为了安全起见,我们把 Server 启动逻辑封装在一个函数里,如果用户想跑 Server 可以显示调用,或者我们检测命令行参数。
|
|
24
|
-
// 或者,为了兼容旧的 `npm start`,我们保留默认启动行为,但如果用户只想用库,可能会有副作用。
|
|
25
|
-
// 更好的做法是:把 Server 逻辑拆分到 `src/server.ts`,`index.ts` 只做导出。
|
|
26
|
-
// 但根据计划,我们尽量保持 index.ts 为入口。
|
|
27
|
-
// 让我们依然默认启动 Server,因为这对 MCP 是标准行为。
|
|
28
|
-
// 使用者如果只是 import { scanProject } from '...', 只要不执行到最后的 connect 就行。
|
|
29
|
-
// 但顶层的代码会执行。
|
|
30
|
-
// 修正方案:将 Server 逻辑包裹在 IIFE 或判断中。
|
|
31
|
-
// 更加稳妥的方式:不做判断,假设这个文件就是 Server 入口。
|
|
32
|
-
// 库的使用者应该 import from './skills/scan.js' 而不是 './index.js' 以避免副作用,或者我们把 index.js 变成纯导出,把 server 移到 server.ts。
|
|
33
|
-
// 鉴于我不想改变 `dist/index.js` 的既定路径(README 里写了),我将保持 `index.ts` 为 Server。
|
|
34
|
-
// 并新建 `src/lib.ts` 或直接让用户 import 子路径。
|
|
35
|
-
// 为了完成任务 "Reorganized Structure",我会把 Server 逻辑保留在 index.ts,但极其精简。
|
|
36
|
-
const server = new Server({
|
|
37
|
-
name: "frontend-prompt-workflow-mcp",
|
|
38
|
-
version: "0.1.0",
|
|
39
|
-
}, {
|
|
40
|
-
capabilities: {
|
|
41
|
-
tools: {},
|
|
42
|
-
},
|
|
43
|
-
});
|
|
44
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
45
|
-
return {
|
|
46
|
-
tools: [
|
|
47
|
-
{
|
|
48
|
-
name: "scan_project",
|
|
49
|
-
description: "Scan project directory tree (read-only) and detect claude.md/CLAUDE.md. Intended for initial architecture understanding.",
|
|
50
|
-
inputSchema: {
|
|
51
|
-
type: "object",
|
|
52
|
-
properties: {
|
|
53
|
-
rootDir: { type: "string" },
|
|
54
|
-
maxDepth: { type: "number" },
|
|
55
|
-
maxEntries: { type: "number" },
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
name: "optimize_frontend_prompt",
|
|
61
|
-
description: "Standardize/optimize a raw user question into a frontend-dev prompt package (messages + guardrails + clarifying questions).",
|
|
62
|
-
inputSchema: {
|
|
63
|
-
type: "object",
|
|
64
|
-
properties: {
|
|
65
|
-
userPrompt: { type: "string" },
|
|
66
|
-
projectContext: { type: "string" },
|
|
67
|
-
techStack: { type: "string" },
|
|
68
|
-
framework: { type: "string" },
|
|
69
|
-
language: { type: "string", enum: ["ts", "js"] },
|
|
70
|
-
styling: { type: "string" },
|
|
71
|
-
stateManagement: { type: "string" },
|
|
72
|
-
router: { type: "string" },
|
|
73
|
-
constraints: { type: "array", items: { type: "string" } },
|
|
74
|
-
taskType: {
|
|
75
|
-
type: "string",
|
|
76
|
-
enum: [
|
|
77
|
-
"new_feature",
|
|
78
|
-
"optimize_existing",
|
|
79
|
-
"refactor",
|
|
80
|
-
"bugfix",
|
|
81
|
-
"performance",
|
|
82
|
-
"ui_polish",
|
|
83
|
-
"dependency_upgrade",
|
|
84
|
-
"test_addition",
|
|
85
|
-
],
|
|
86
|
-
},
|
|
87
|
-
outputLanguage: { type: "string", enum: ["zh", "en"] },
|
|
88
|
-
outputFormat: { type: "string", enum: ["step_by_step", "direct", "both"] },
|
|
89
|
-
codeStyle: { type: "string", enum: ["diff", "full_files", "snippets"] },
|
|
90
|
-
mustAskClarifyingQuestions: { type: "boolean" },
|
|
91
|
-
requireApprovalGates: { type: "boolean" },
|
|
92
|
-
},
|
|
93
|
-
required: ["userPrompt"],
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
name: "score_frontend_prompt",
|
|
98
|
-
description: "Score a frontend prompt quality and suggest missing info / improvements.",
|
|
99
|
-
inputSchema: {
|
|
100
|
-
type: "object",
|
|
101
|
-
properties: {
|
|
102
|
-
prompt: { type: "string" },
|
|
103
|
-
},
|
|
104
|
-
required: ["prompt"],
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
|
-
],
|
|
108
|
-
};
|
|
109
|
-
});
|
|
110
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
111
|
-
if (request.params.name === "scan_project") {
|
|
112
|
-
const rawArgs = (request.params.arguments ?? {});
|
|
113
|
-
const rootDir = asNonEmptyString(rawArgs.rootDir);
|
|
114
|
-
const maxDepth = typeof rawArgs.maxDepth === "number" ? rawArgs.maxDepth : undefined;
|
|
115
|
-
const maxEntries = typeof rawArgs.maxEntries === "number" ? rawArgs.maxEntries : undefined;
|
|
116
|
-
const result = await scanProject({ rootDir, maxDepth, maxEntries });
|
|
117
|
-
return {
|
|
118
|
-
content: [
|
|
119
|
-
{
|
|
120
|
-
type: "text",
|
|
121
|
-
text: JSON.stringify(result, null, 2),
|
|
122
|
-
},
|
|
123
|
-
],
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
if (request.params.name === "optimize_frontend_prompt") {
|
|
127
|
-
const rawArgs = (request.params.arguments ?? {});
|
|
128
|
-
const userPrompt = asNonEmptyString(rawArgs.userPrompt);
|
|
129
|
-
if (!userPrompt) {
|
|
130
|
-
throw new Error("optimize_frontend_prompt: missing required argument 'userPrompt'");
|
|
131
|
-
}
|
|
132
|
-
const args = {
|
|
133
|
-
userPrompt,
|
|
134
|
-
projectContext: asNonEmptyString(rawArgs.projectContext),
|
|
135
|
-
techStack: asNonEmptyString(rawArgs.techStack),
|
|
136
|
-
framework: asNonEmptyString(rawArgs.framework),
|
|
137
|
-
language: asNonEmptyString(rawArgs.language) ?? undefined,
|
|
138
|
-
styling: asNonEmptyString(rawArgs.styling),
|
|
139
|
-
stateManagement: asNonEmptyString(rawArgs.stateManagement),
|
|
140
|
-
router: asNonEmptyString(rawArgs.router),
|
|
141
|
-
constraints: Array.isArray(rawArgs.constraints)
|
|
142
|
-
? rawArgs.constraints.map((x) => String(x)).filter((x) => x.trim().length)
|
|
143
|
-
: undefined,
|
|
144
|
-
taskType: asNonEmptyString(rawArgs.taskType) ?? undefined,
|
|
145
|
-
outputLanguage: asNonEmptyString(rawArgs.outputLanguage) ?? undefined,
|
|
146
|
-
outputFormat: asNonEmptyString(rawArgs.outputFormat) ?? undefined,
|
|
147
|
-
codeStyle: asNonEmptyString(rawArgs.codeStyle) ?? undefined,
|
|
148
|
-
mustAskClarifyingQuestions: typeof rawArgs.mustAskClarifyingQuestions === "boolean" ? rawArgs.mustAskClarifyingQuestions : undefined,
|
|
149
|
-
requireApprovalGates: typeof rawArgs.requireApprovalGates === "boolean" ? rawArgs.requireApprovalGates : undefined,
|
|
150
|
-
};
|
|
151
|
-
const result = buildOptimizedPromptPackage(args);
|
|
152
|
-
return {
|
|
153
|
-
content: [
|
|
154
|
-
{
|
|
155
|
-
type: "text",
|
|
156
|
-
text: JSON.stringify(result, null, 2),
|
|
157
|
-
},
|
|
158
|
-
],
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
if (request.params.name === "score_frontend_prompt") {
|
|
162
|
-
const rawArgs = (request.params.arguments ?? {});
|
|
163
|
-
const prompt = asNonEmptyString(rawArgs.prompt);
|
|
164
|
-
if (!prompt) {
|
|
165
|
-
throw new Error("score_frontend_prompt: missing required argument 'prompt'");
|
|
166
|
-
}
|
|
167
|
-
const result = scoreFrontendPrompt({ prompt });
|
|
168
|
-
return {
|
|
169
|
-
content: [
|
|
170
|
-
{
|
|
171
|
-
type: "text",
|
|
172
|
-
text: JSON.stringify(result, null, 2),
|
|
173
|
-
},
|
|
174
|
-
],
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
throw new Error(`Unknown tool: ${request.params.name}`);
|
|
178
|
-
});
|
|
179
|
-
// 启动 Server
|
|
180
|
-
const transport = new StdioServerTransport();
|
|
181
|
-
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jdzhang225gmail/fontend-prompt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"fontend-prompt": "./dist/
|
|
8
|
-
"fontend-skill": "./dist/cli.js"
|
|
7
|
+
"fontend-prompt": "./dist/cli.js"
|
|
9
8
|
},
|
|
10
9
|
"files": [
|
|
11
10
|
"dist",
|
|
@@ -17,11 +16,10 @@
|
|
|
17
16
|
"scripts": {
|
|
18
17
|
"dev": "tsx watch src/index.ts",
|
|
19
18
|
"build": "tsc -p tsconfig.json",
|
|
20
|
-
"start": "node dist/
|
|
19
|
+
"start": "node dist/cli.js",
|
|
21
20
|
"prepublishOnly": "npm run build"
|
|
22
21
|
},
|
|
23
22
|
"dependencies": {
|
|
24
|
-
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
25
23
|
"cac": "^6.7.14",
|
|
26
24
|
"npm": "^11.7.0"
|
|
27
25
|
},
|