prompt-plus 1.0.4 → 1.1.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 CHANGED
@@ -1,6 +1,8 @@
1
1
  # prompt-plus
2
2
 
3
- AI 提示词模板管理工具 - 生成符合项目规范的提示词
3
+ AI 技能包管理工具 - 基于技能包驱动的开发工作流
4
+
5
+ > ⚠️ **v1.1.0 断档式更新**:不兼容旧版本,请重新安装
4
6
 
5
7
  ## 安装
6
8
 
@@ -11,108 +13,171 @@ npm install -g prompt-plus
11
13
  ## 快速开始
12
14
 
13
15
  ```bash
14
- # 1. 添加官方模板仓库
15
- prompt-plus repo add official https://github.com/LeeSeaside/prompt-plus-templates.git
16
+ # 1. 添加技能包仓库
17
+ pp repo add official https://github.com/your-repo/skills.git
18
+
19
+ # 2. 同步仓库
20
+ pp repo sync
16
21
 
17
- # 2. 同步模板
18
- prompt-plus repo sync
22
+ # 3. 查看可用技能包
23
+ pp skill list
19
24
 
20
- # 3. 查看可用模板
21
- prompt-plus list
25
+ # 4. 安装技能包到当前项目
26
+ pp skill install backend_api
22
27
 
23
- # 4. 使用模板
24
- prompt-plus use backend-api
28
+ # 5. 查看已安装的技能包
29
+ pp skill installed
25
30
  ```
26
31
 
27
- ## 命令
32
+ ## 命令列表
28
33
 
29
- ### 模板操作
34
+ ### 仓库管理
30
35
 
31
36
  ```bash
32
- # 列出所有模板
33
- prompt-plus list
34
- prompt-plus ls
35
-
36
- # 使用模板(交互式选择)
37
- prompt-plus use
37
+ pp repo add <name> <url> # 添加技能包仓库
38
+ pp repo add <name> <url> -b <branch> # 指定分支
39
+ pp repo remove <name> # 移除仓库
40
+ pp repo list # 列出所有仓库
41
+ pp repo sync # 同步所有仓库
42
+ pp repo sync <name> # 同步指定仓库
43
+ ```
38
44
 
39
- # 使用指定模板
40
- prompt-plus use <模板名>
45
+ ### 技能包管理
41
46
 
42
- # 指定输出目录
43
- prompt-plus use <模板名> -o ./my-prompts
47
+ ```bash
48
+ pp skill list # 列出所有可用技能包
49
+ pp skill list -r <repo> # 列出指定仓库的技能包
50
+ pp skill install # 交互式安装技能包
51
+ pp skill install <name> # 安装指定技能包
52
+ pp skill installed # 查看已安装的技能包
44
53
  ```
45
54
 
46
- ### 仓库管理
55
+ ### 工作区管理
47
56
 
48
57
  ```bash
49
- # 查看仓库列表
50
- prompt-plus repo ls
58
+ pp workspace init # 初始化 AI 工作区
59
+ pp ws init # 简写
60
+ ```
51
61
 
52
- # 添加仓库
53
- prompt-plus repo add <名称> <Git地址>
54
- prompt-plus repo add official https://github.com/LeeSeaside/prompt-plus-templates.git
62
+ ---
55
63
 
56
- # 添加私有仓库(指定分支)
57
- prompt-plus repo add company git@github.com:company/prompts.git -b develop
64
+ ## 技能包仓库格式
58
65
 
59
- # 同步仓库
60
- prompt-plus repo sync # 同步所有
61
- prompt-plus repo sync official # 同步指定仓库
66
+ 如果你想创建自己的技能包仓库,请按以下格式组织:
62
67
 
63
- # 移除仓库
64
- prompt-plus repo rm <名称>
68
+ ```
69
+ your-repo/
70
+ └── skills/
71
+ ├── backend_api/
72
+ │ ├── manifest.md # 执行流程和规范(必需)
73
+ │ ├── context.md # 项目配置模板(必需)
74
+ │ ├── input/ # 输入文档目录
75
+ │ │ └── .gitkeep
76
+ │ ├── output/ # 输出文档目录
77
+ │ │ └── .gitkeep
78
+ │ └── tools/ # 提示词工具
79
+ │ ├── init.md # 初始化提示词
80
+ │ └── dev.md # 开发提示词
81
+ ├── frontend_api/
82
+ │ ├── manifest.md
83
+ │ ├── context.md
84
+ │ └── ...
85
+ └── another_skill/
86
+ └── ...
65
87
  ```
66
88
 
67
- ## 自建模板仓库
89
+ ### manifest.md 格式
68
90
 
69
- 创建 Git 仓库,在 `templates/` 目录下放置 Markdown 模板文件:
91
+ ```markdown
92
+ # Skill Manifest: 技能名称
70
93
 
71
- ```
72
- my-templates/
73
- └── templates/
74
- ├── my-template-1.md
75
- └── my-template-2.md
94
+ ## 技能描述
95
+ 简要说明这个技能包的用途。
96
+
97
+ ## 执行协议
98
+ 定义 AI 执行任务的步骤流程。
99
+
100
+ ### 1. Context Check (上下文检查)
101
+ ...
102
+
103
+ ### 2. Input Acquisition (获取需求)
104
+ ...
105
+
106
+ ### 3. Code Generation (代码生成)
107
+ ...
108
+
109
+ ### 4. Documentation Output (文档输出)
110
+ ...
111
+
112
+ ## 约束条件
113
+ - 约束 1
114
+ - 约束 2
76
115
  ```
77
116
 
78
- 模板格式(Markdown + Front Matter):
117
+ ### context.md 格式
79
118
 
80
119
  ```markdown
81
- ---
82
- name: my-template
83
- description: 模板描述
84
- category: backend
85
- outputFileName: my-template-prompt.md
86
- ---
120
+ # Project Context Configuration
87
121
 
88
- # 模板标题
122
+ > ⚠️ 此文件需要初始化
89
123
 
90
- ## 任务
91
- 模板内容...
92
- ```
124
+ ## Tech Stack
125
+ - **Framework**: <!-- 待填充 -->
126
+ - **ORM**: <!-- 待填充 -->
93
127
 
94
- ## 工作流程
128
+ ## Directory Mapping
129
+ - **Controller Path**: <!-- 待填充 -->
130
+ - **Service Path**: <!-- 待填充 -->
95
131
 
132
+ ## Code Style
133
+ - **Naming Convention**: <!-- 待填充 -->
96
134
  ```
97
- 模板 → 正式提示词 → 对接文档/代码
98
135
 
99
- .prompts/
100
- ├── templates/ # 提示词模板
101
- └── generated/ # 正式提示词(AI 生成)
136
+ ### tools/init.md 格式
137
+
138
+ ```markdown
139
+ # 技能名 - 初始化提示词
140
+
141
+ ## 使用方法
142
+ 复制以下内容发送给 AI
102
143
 
103
- docs/
104
- └── api/ # 对接文档(业务相关)
144
+ ---
145
+
146
+ \`\`\`
147
+ 请执行 xxx 技能初始化:
148
+ 1. 扫描项目结构
149
+ 2. 识别技术栈
150
+ 3. 将结果写入 context.md
151
+ \`\`\`
105
152
  ```
106
153
 
107
- ## 本地开发
154
+ ---
108
155
 
109
- ```bash
110
- npm install
111
- npm run build
112
- npm link
113
- prompt-plus list
156
+ ## 安装后的项目结构
157
+
158
+ ```
159
+ your-project/
160
+ ├── .ai-workspace/
161
+ │ ├── RULES.md # AI 工作流规则
162
+ │ └── skills/
163
+ │ ├── backend_api/
164
+ │ │ ├── manifest.md
165
+ │ │ ├── context.md # 需要初始化
166
+ │ │ ├── input/
167
+ │ │ ├── output/
168
+ │ │ └── tools/
169
+ │ └── frontend_api/
170
+ │ └── ...
171
+ └── (你的项目源码)
114
172
  ```
115
173
 
174
+ ## 使用流程
175
+
176
+ 1. **安装技能包**:`pp skill install backend_api`
177
+ 2. **初始化配置**:查看 `tools/init.md`,复制提示词发送给 AI
178
+ 3. **AI 扫描项目**:AI 会分析项目并填充 `context.md`
179
+ 4. **开始开发**:将需求文档放入 `input/`,使用 `tools/dev.md` 的提示词
180
+
116
181
  ## License
117
182
 
118
183
  MIT
package/dist/cli.js CHANGED
@@ -1,49 +1,101 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
3
36
  Object.defineProperty(exports, "__esModule", { value: true });
4
37
  const commander_1 = require("commander");
38
+ const path = __importStar(require("path"));
39
+ const fs = __importStar(require("fs"));
5
40
  const commands_1 = require("./commands");
41
+ // 读取版本号
42
+ const pkgPath = path.join(__dirname, '..', 'package.json');
43
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
6
44
  const program = new commander_1.Command();
7
45
  program
8
46
  .name('prompt-plus')
9
- .description('AI提示词模板管理工具 - 生成符合项目规范的提示词')
10
- .version('1.0.0');
11
- program
12
- .command('list')
13
- .alias('ls')
14
- .description('列出所有可用的提示词模板')
15
- .option('-r, --repo <name>', '指定仓库名称')
16
- .action(commands_1.listTemplates);
17
- program
18
- .command('use [templateName]')
19
- .description('选择并使用模板生成提示词')
20
- .option('-o, --output <path>', '输出路径', '.prompts')
21
- .option('-r, --repo <name>', '指定仓库名称')
22
- .action(commands_1.useTemplate);
23
- program
24
- .command('init')
25
- .description('初始化配置文件')
26
- .action(commands_1.initConfig);
47
+ .description('AI 技能包管理工具 - 基于技能包驱动的开发工作流')
48
+ .version(pkg.version);
49
+ // 仓库管理
27
50
  program
28
51
  .command('repo')
29
- .description('管理模板仓库')
52
+ .description('管理技能包仓库')
30
53
  .addCommand(new commander_1.Command('add')
31
- .description('添加模板仓库')
54
+ .description('添加技能包仓库')
32
55
  .argument('<name>', '仓库名称')
33
56
  .argument('<url>', '仓库地址')
34
57
  .option('-b, --branch <branch>', '分支名称', 'main')
35
58
  .action(commands_1.addRepo))
36
59
  .addCommand(new commander_1.Command('remove')
37
60
  .alias('rm')
38
- .description('移除模板仓库')
61
+ .description('移除仓库')
39
62
  .argument('<name>', '仓库名称')
40
63
  .action(commands_1.removeRepo))
41
64
  .addCommand(new commander_1.Command('list')
42
65
  .alias('ls')
43
- .description('列出所有模板仓库')
66
+ .description('列出所有仓库')
44
67
  .action(commands_1.listRepos))
45
68
  .addCommand(new commander_1.Command('sync')
46
- .description('同步模板仓库')
69
+ .description('同步仓库')
47
70
  .argument('[name]', '仓库名称(不指定则同步所有)')
48
71
  .action(commands_1.syncRepo));
72
+ // 技能包管理
73
+ program
74
+ .command('skill')
75
+ .description('管理技能包')
76
+ .addCommand(new commander_1.Command('list')
77
+ .alias('ls')
78
+ .description('列出所有可用技能包')
79
+ .option('-r, --repo <name>', '指定仓库名称')
80
+ .action(commands_1.listSkills))
81
+ .addCommand(new commander_1.Command('install')
82
+ .alias('i')
83
+ .description('安装技能包到当前项目')
84
+ .argument('[skillName]', '技能包名称')
85
+ .option('-r, --repo <name>', '指定仓库名称')
86
+ .option('-o, --output <path>', '输出路径', '.ai-workspace')
87
+ .action(commands_1.installSkill))
88
+ .addCommand(new commander_1.Command('installed')
89
+ .description('查看已安装的技能包')
90
+ .option('-o, --output <path>', '工作区路径', '.ai-workspace')
91
+ .action(commands_1.installedSkills));
92
+ // 工作区管理
93
+ program
94
+ .command('workspace')
95
+ .alias('ws')
96
+ .description('管理 AI 工作区')
97
+ .addCommand(new commander_1.Command('init')
98
+ .description('初始化 AI 工作区')
99
+ .option('-o, --output <path>', '输出路径', '.ai-workspace')
100
+ .action(commands_1.initWorkspace));
49
101
  program.parse();
@@ -1,14 +1,19 @@
1
- export declare function listTemplates(options?: {
2
- repo?: string;
3
- }): Promise<void>;
4
- export declare function useTemplate(templateName?: string, options?: {
5
- output?: string;
6
- repo?: string;
7
- }): Promise<void>;
8
- export declare function initConfig(): Promise<void>;
9
1
  export declare function addRepo(name: string, url: string, options?: {
10
2
  branch?: string;
11
3
  }): Promise<void>;
12
4
  export declare function removeRepo(name: string): Promise<void>;
13
5
  export declare function listRepos(): Promise<void>;
14
6
  export declare function syncRepo(name?: string): Promise<void>;
7
+ export declare function listSkills(options?: {
8
+ repo?: string;
9
+ }): Promise<void>;
10
+ export declare function installSkill(skillName?: string, options?: {
11
+ repo?: string;
12
+ output?: string;
13
+ }): Promise<void>;
14
+ export declare function initWorkspace(options?: {
15
+ output?: string;
16
+ }): Promise<void>;
17
+ export declare function installedSkills(options?: {
18
+ output?: string;
19
+ }): Promise<void>;
package/dist/commands.js CHANGED
@@ -33,17 +33,18 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.listTemplates = listTemplates;
37
- exports.useTemplate = useTemplate;
38
- exports.initConfig = initConfig;
39
36
  exports.addRepo = addRepo;
40
37
  exports.removeRepo = removeRepo;
41
38
  exports.listRepos = listRepos;
42
39
  exports.syncRepo = syncRepo;
40
+ exports.listSkills = listSkills;
41
+ exports.installSkill = installSkill;
42
+ exports.initWorkspace = initWorkspace;
43
+ exports.installedSkills = installedSkills;
43
44
  const fs = __importStar(require("fs"));
44
45
  const path = __importStar(require("path"));
45
46
  const child_process_1 = require("child_process");
46
- const templates_1 = require("./templates");
47
+ const skills_1 = require("./skills");
47
48
  // 配置文件路径(全局配置)
48
49
  const getGlobalConfigDir = () => path.join(process.env.HOME || process.env.USERPROFILE || '', '.prompt-plus');
49
50
  const getGlobalConfigPath = () => path.join(getGlobalConfigDir(), 'config.json');
@@ -64,7 +65,7 @@ function getConfig() {
64
65
  return {
65
66
  defaultRepo: '',
66
67
  repos: [],
67
- outputDir: '.prompts',
68
+ outputDir: '.ai-workspace',
68
69
  };
69
70
  }
70
71
  // 保存全局配置
@@ -75,140 +76,7 @@ function saveConfig(config) {
75
76
  }
76
77
  fs.writeFileSync(getGlobalConfigPath(), JSON.stringify(config, null, 2), 'utf-8');
77
78
  }
78
- // 获取所有模板(按仓库分组)
79
- async function getAllTemplatesWithRepo(repoName) {
80
- const config = getConfig();
81
- const templates = [];
82
- // 指定了具体仓库
83
- if (repoName) {
84
- const repo = config.repos.find((r) => r.name === repoName);
85
- if (repo) {
86
- const repoDir = path.join(getReposDir(), repo.name);
87
- if (fs.existsSync(repoDir)) {
88
- const repoTemplates = (0, templates_1.loadTemplatesFromDir)(path.join(repoDir, 'templates'));
89
- return repoTemplates.map((t) => ({ ...t, repoName: repo.name }));
90
- }
91
- }
92
- return [];
93
- }
94
- // 未指定仓库:合并所有已同步仓库模板
95
- for (const repo of config.repos) {
96
- const repoDir = path.join(getReposDir(), repo.name);
97
- if (fs.existsSync(repoDir)) {
98
- const repoTemplates = (0, templates_1.loadTemplatesFromDir)(path.join(repoDir, 'templates'));
99
- templates.push(...repoTemplates.map((t) => ({ ...t, repoName: repo.name })));
100
- }
101
- }
102
- return templates;
103
- }
104
- async function listTemplates(options) {
105
- const chalk = await getChalk();
106
- const templates = await getAllTemplatesWithRepo(options?.repo);
107
- if (templates.length === 0) {
108
- console.log(chalk.yellow('\n⚠️ 没有找到模板'));
109
- console.log(chalk.gray('请先添加并同步模板仓库:'));
110
- console.log(chalk.gray(' prompt-plus repo add official https://github.com/LeeSeaside/prompt-plus-templates.git'));
111
- console.log(chalk.gray(' prompt-plus repo sync\n'));
112
- return;
113
- }
114
- console.log(chalk.cyan('\n📋 可用的提示词模板:\n'));
115
- // 按仓库分组
116
- const repoNames = [...new Set(templates.map((t) => t.repoName))];
117
- for (const repoName of repoNames) {
118
- console.log(chalk.magenta(`📦 ${repoName}`));
119
- const repoTemplates = templates.filter((t) => t.repoName === repoName);
120
- // 按分类分组
121
- const categories = [...new Set(repoTemplates.map((t) => t.category))];
122
- for (const category of categories) {
123
- console.log(chalk.yellow(` [${category}]`));
124
- const categoryTemplates = repoTemplates.filter((t) => t.category === category);
125
- for (const template of categoryTemplates) {
126
- console.log(chalk.white(` • ${template.name}`), chalk.gray(`- ${template.description}`));
127
- }
128
- }
129
- console.log();
130
- }
131
- console.log(chalk.gray('使用 "prompt-plus use <模板名>" 或 "prompt-plus use" 交互式选择\n'));
132
- }
133
- async function useTemplate(templateName, options) {
134
- const chalk = await getChalk();
135
- const inquirer = await getInquirer();
136
- const templates = await getAllTemplatesWithRepo(options?.repo);
137
- let selectedTemplate;
138
- if (templateName) {
139
- selectedTemplate = templates.find((t) => t.name === templateName);
140
- if (!selectedTemplate) {
141
- console.log(chalk.red(`\n❌ 未找到模板: ${templateName}`));
142
- console.log(chalk.gray('使用 "prompt-plus list" 查看可用模板\n'));
143
- return;
144
- }
145
- }
146
- else {
147
- if (templates.length === 0) {
148
- console.log(chalk.yellow('\n⚠️ 没有可用模板'));
149
- console.log(chalk.gray('请先添加并同步模板仓库\n'));
150
- return;
151
- }
152
- const choices = templates.map((t) => ({
153
- name: `[${t.repoName}] ${t.name} - ${t.description}`,
154
- value: t.name,
155
- }));
156
- const answer = await inquirer.prompt([
157
- {
158
- type: 'list',
159
- name: 'template',
160
- message: '请选择要使用的模板:',
161
- choices,
162
- },
163
- ]);
164
- selectedTemplate = templates.find((t) => t.name === answer.template);
165
- }
166
- if (!selectedTemplate) {
167
- console.log(chalk.red('\n❌ 模板选择失败'));
168
- return;
169
- }
170
- const baseDir = options?.output || '.prompts';
171
- const templatesDir = path.join(process.cwd(), baseDir, 'templates');
172
- const generatedDir = path.join(process.cwd(), baseDir, 'generated');
173
- if (!fs.existsSync(templatesDir)) {
174
- fs.mkdirSync(templatesDir, { recursive: true });
175
- }
176
- if (!fs.existsSync(generatedDir)) {
177
- fs.mkdirSync(generatedDir, { recursive: true });
178
- }
179
- const filePath = path.join(templatesDir, selectedTemplate.outputFileName);
180
- fs.writeFileSync(filePath, selectedTemplate.content, 'utf-8');
181
- console.log(chalk.green(`\n✅ 模板已生成: ${filePath}`));
182
- console.log(chalk.cyan('\n📝 使用方法:'));
183
- console.log(chalk.white(' 1. 打开生成的提示词文件'));
184
- console.log(chalk.white(' 2. 复制内容到AI编辑器(Cursor/Trae等)'));
185
- console.log(chalk.white(' 3. AI会分析你的项目并生成具体的开发提示词'));
186
- console.log(chalk.white(` 4. 将AI生成的正式提示词保存到: ${chalk.yellow(baseDir + '/generated/')}`));
187
- console.log(chalk.white(' 5. 使用正式提示词进行实际开发\n'));
188
- console.log(chalk.gray(`📁 目录结构:`));
189
- console.log(chalk.gray(` ${baseDir}/`));
190
- console.log(chalk.gray(` ├── templates/ # 提示词模板`));
191
- console.log(chalk.gray(` └── generated/ # 正式提示词\n`));
192
- }
193
- async function initConfig() {
194
- const chalk = await getChalk();
195
- const configPath = getGlobalConfigPath();
196
- if (fs.existsSync(configPath)) {
197
- console.log(chalk.yellow('\n⚠️ 配置文件已存在'));
198
- console.log(chalk.gray(`路径: ${configPath}\n`));
199
- return;
200
- }
201
- const defaultConfig = {
202
- defaultRepo: '',
203
- repos: [],
204
- outputDir: '.prompts',
205
- };
206
- saveConfig(defaultConfig);
207
- console.log(chalk.green('\n✅ 配置文件已创建'));
208
- console.log(chalk.gray(`路径: ${configPath}`));
209
- console.log(chalk.gray('\n下一步: 添加模板仓库'));
210
- console.log(chalk.gray(' prompt-plus repo add official https://github.com/LeeSeaside/prompt-plus-templates.git\n'));
211
- }
79
+ // ==================== 仓库管理 ====================
212
80
  async function addRepo(name, url, options) {
213
81
  const chalk = await getChalk();
214
82
  const config = getConfig();
@@ -223,7 +91,7 @@ async function addRepo(name, url, options) {
223
91
  });
224
92
  saveConfig(config);
225
93
  console.log(chalk.green(`\n✅ 已添加仓库: ${name}`));
226
- console.log(chalk.gray(`使用 "prompt-plus repo sync ${name}" 同步模板\n`));
94
+ console.log(chalk.gray(`使用 "pp repo sync ${name}" 同步仓库\n`));
227
95
  }
228
96
  async function removeRepo(name) {
229
97
  const chalk = await getChalk();
@@ -245,19 +113,28 @@ async function removeRepo(name) {
245
113
  async function listRepos() {
246
114
  const chalk = await getChalk();
247
115
  const config = getConfig();
248
- console.log(chalk.cyan('\n📦 模板仓库列表:\n'));
116
+ console.log(chalk.cyan('\n📦 技能包仓库列表:\n'));
249
117
  if (config.repos.length === 0) {
250
118
  console.log(chalk.gray(' 暂无仓库,请先添加:'));
251
- console.log(chalk.gray(' prompt-plus repo add official https://github.com/LeeSeaside/prompt-plus-templates.git\n'));
119
+ console.log(chalk.gray(' pp repo add official https://github.com/your-repo/skills.git\n'));
252
120
  return;
253
121
  }
254
122
  for (const repo of config.repos) {
255
- const synced = fs.existsSync(path.join(getReposDir(), repo.name));
123
+ const repoDir = path.join(getReposDir(), repo.name);
124
+ const synced = fs.existsSync(repoDir);
256
125
  const status = synced ? chalk.green('✓ 已同步') : chalk.yellow('未同步');
257
- console.log(chalk.white(` • ${repo.name}`), chalk.gray(`- ${repo.url}`), status);
126
+ // 检查技能包数量
127
+ let skillCount = 0;
128
+ if (synced) {
129
+ const skillsDir = path.join(repoDir, 'skills');
130
+ if (fs.existsSync(skillsDir)) {
131
+ skillCount = (0, skills_1.loadSkillsFromDir)(skillsDir).length;
132
+ }
133
+ }
134
+ console.log(chalk.white(` • ${repo.name}`), chalk.gray(`- ${repo.url}`), status, synced ? chalk.gray(`(${skillCount} 个技能包)`) : '');
258
135
  }
259
- console.log(chalk.gray('\n使用 "prompt-plus repo add <name> <url>" 添加仓库'));
260
- console.log(chalk.gray('使用 "prompt-plus repo sync [name]" 同步仓库\n'));
136
+ console.log(chalk.gray('\n使用 "pp repo add <name> <url>" 添加仓库'));
137
+ console.log(chalk.gray('使用 "pp repo sync [name]" 同步仓库\n'));
261
138
  }
262
139
  async function syncRepo(name) {
263
140
  const chalk = await getChalk();
@@ -273,7 +150,7 @@ async function syncRepo(name) {
273
150
  }
274
151
  else {
275
152
  console.log(chalk.yellow('\n⚠️ 没有配置任何仓库'));
276
- console.log(chalk.gray('使用 "prompt-plus repo add <name> <url>" 添加仓库\n'));
153
+ console.log(chalk.gray('使用 "pp repo add <name> <url>" 添加仓库\n'));
277
154
  }
278
155
  return;
279
156
  }
@@ -289,6 +166,12 @@ async function syncRepo(name) {
289
166
  (0, child_process_1.execSync)(`git clone -b ${repo.branch || 'main'} "${repo.url}" "${repoDir}"`, { stdio: 'pipe' });
290
167
  console.log(chalk.green(`✅ 已克隆: ${repo.name}`));
291
168
  }
169
+ // 显示技能包数量
170
+ const skillsDir = path.join(repoDir, 'skills');
171
+ if (fs.existsSync(skillsDir)) {
172
+ const skills = (0, skills_1.loadSkillsFromDir)(skillsDir);
173
+ console.log(chalk.gray(` 发现 ${skills.length} 个技能包`));
174
+ }
292
175
  }
293
176
  catch (error) {
294
177
  console.log(chalk.red(`❌ 同步失败: ${repo.name}`));
@@ -297,3 +180,236 @@ async function syncRepo(name) {
297
180
  }
298
181
  console.log();
299
182
  }
183
+ // 获取所有技能包(按仓库分组)
184
+ async function getAllSkillsWithRepo(repoName) {
185
+ const config = getConfig();
186
+ const skills = [];
187
+ if (repoName) {
188
+ const repo = config.repos.find((r) => r.name === repoName);
189
+ if (repo) {
190
+ const repoDir = path.join(getReposDir(), repo.name);
191
+ if (fs.existsSync(repoDir)) {
192
+ const repoSkills = (0, skills_1.loadSkillsFromDir)(path.join(repoDir, 'skills'));
193
+ return repoSkills.map((s) => ({ ...s, repoName: repo.name }));
194
+ }
195
+ }
196
+ return [];
197
+ }
198
+ for (const repo of config.repos) {
199
+ const repoDir = path.join(getReposDir(), repo.name);
200
+ if (fs.existsSync(repoDir)) {
201
+ const repoSkills = (0, skills_1.loadSkillsFromDir)(path.join(repoDir, 'skills'));
202
+ skills.push(...repoSkills.map((s) => ({ ...s, repoName: repo.name })));
203
+ }
204
+ }
205
+ return skills;
206
+ }
207
+ // 列出所有技能包
208
+ async function listSkills(options) {
209
+ const chalk = await getChalk();
210
+ const skills = await getAllSkillsWithRepo(options?.repo);
211
+ if (skills.length === 0) {
212
+ console.log(chalk.yellow('\n⚠️ 没有找到技能包'));
213
+ console.log(chalk.gray('请先添加并同步仓库:'));
214
+ console.log(chalk.gray(' pp repo add official <仓库地址>'));
215
+ console.log(chalk.gray(' pp repo sync\n'));
216
+ return;
217
+ }
218
+ console.log(chalk.cyan('\n🎯 可用的技能包:\n'));
219
+ const repoNames = [...new Set(skills.map((s) => s.repoName))];
220
+ for (const repoName of repoNames) {
221
+ console.log(chalk.magenta(`📦 ${repoName}`));
222
+ const repoSkills = skills.filter((s) => s.repoName === repoName);
223
+ for (const skill of repoSkills) {
224
+ const status = [];
225
+ if (skill.hasManifest)
226
+ status.push(chalk.green('✓manifest'));
227
+ if (skill.hasContext)
228
+ status.push(chalk.green('✓context'));
229
+ if (skill.hasTools)
230
+ status.push(chalk.green('✓tools'));
231
+ console.log(chalk.white(` • ${skill.name}`), chalk.gray(`- ${skill.description || '无描述'}`), chalk.gray(`[${status.join(' ')}]`));
232
+ }
233
+ console.log();
234
+ }
235
+ console.log(chalk.gray('使用 "pp skill install <技能名>" 安装技能包到当前项目\n'));
236
+ }
237
+ // 安装技能包到当前项目
238
+ async function installSkill(skillName, options) {
239
+ const chalk = await getChalk();
240
+ const inquirer = await getInquirer();
241
+ const skills = await getAllSkillsWithRepo(options?.repo);
242
+ let selectedSkill;
243
+ if (skillName) {
244
+ selectedSkill = skills.find((s) => s.name === skillName);
245
+ if (!selectedSkill) {
246
+ console.log(chalk.red(`\n❌ 未找到技能包: ${skillName}`));
247
+ console.log(chalk.gray('使用 "pp skill list" 查看可用技能包\n'));
248
+ return;
249
+ }
250
+ }
251
+ else {
252
+ if (skills.length === 0) {
253
+ console.log(chalk.yellow('\n⚠️ 没有可用技能包'));
254
+ console.log(chalk.gray('请先添加并同步仓库\n'));
255
+ return;
256
+ }
257
+ const choices = skills.map((s) => ({
258
+ name: `[${s.repoName}] ${s.name} - ${s.description || '无描述'}`,
259
+ value: s.name,
260
+ }));
261
+ const answer = await inquirer.prompt([
262
+ {
263
+ type: 'list',
264
+ name: 'skill',
265
+ message: '请选择要安装的技能包:',
266
+ choices,
267
+ },
268
+ ]);
269
+ selectedSkill = skills.find((s) => s.name === answer.skill);
270
+ }
271
+ if (!selectedSkill) {
272
+ console.log(chalk.red('\n❌ 技能包选择失败'));
273
+ return;
274
+ }
275
+ const baseDir = options?.output || '.ai-workspace';
276
+ const targetDir = path.join(process.cwd(), baseDir, 'skills', selectedSkill.name);
277
+ // 检查是否已安装
278
+ if (fs.existsSync(targetDir)) {
279
+ const { overwrite } = await inquirer.prompt([
280
+ {
281
+ type: 'confirm',
282
+ name: 'overwrite',
283
+ message: `技能包 "${selectedSkill.name}" 已存在,是否覆盖?`,
284
+ default: false,
285
+ },
286
+ ]);
287
+ if (!overwrite) {
288
+ console.log(chalk.yellow('\n⚠️ 已取消安装\n'));
289
+ return;
290
+ }
291
+ fs.rmSync(targetDir, { recursive: true, force: true });
292
+ }
293
+ // 复制技能包
294
+ (0, skills_1.copySkill)(selectedSkill.path, targetDir);
295
+ // 确保 RULES.md 存在
296
+ const rulesPath = path.join(process.cwd(), baseDir, 'RULES.md');
297
+ if (!fs.existsSync(rulesPath)) {
298
+ await initWorkspace({ output: baseDir });
299
+ }
300
+ console.log(chalk.green(`\n✅ 技能包已安装: ${targetDir}`));
301
+ console.log(chalk.cyan('\n📝 下一步:'));
302
+ console.log(chalk.white(` 1. 查看 ${baseDir}/skills/${selectedSkill.name}/tools/init.md`));
303
+ console.log(chalk.white(' 2. 复制初始化提示词发送给 AI'));
304
+ console.log(chalk.white(' 3. AI 会扫描项目并填充 context.md\n'));
305
+ }
306
+ // 初始化 AI 工作区
307
+ async function initWorkspace(options) {
308
+ const chalk = await getChalk();
309
+ const baseDir = options?.output || '.ai-workspace';
310
+ const workspaceDir = path.join(process.cwd(), baseDir);
311
+ const rulesPath = path.join(workspaceDir, 'RULES.md');
312
+ if (fs.existsSync(rulesPath)) {
313
+ console.log(chalk.yellow('\n⚠️ AI 工作区已存在'));
314
+ console.log(chalk.gray(`路径: ${workspaceDir}\n`));
315
+ return;
316
+ }
317
+ // 创建目录结构
318
+ fs.mkdirSync(path.join(workspaceDir, 'skills'), { recursive: true });
319
+ // 创建 RULES.md
320
+ const rulesContent = `# AI WORKSPACE - SYSTEM PROTOCOL
321
+
322
+ > 此文件定义 AI 助手的强制性工作流规则,适用于所有 AI 编辑器(Cursor、Copilot、Kiro、Claude 等)
323
+
324
+ ## PRIORITY: ABSOLUTE (最高优先级)
325
+
326
+ 你是一个基于"技能包"驱动的开发助手。在处理任何开发任务之前,必须强制执行以下协议。
327
+
328
+ ## SKILL DISCOVERY
329
+
330
+ **动态发现技能**:扫描 \`.ai-workspace/skills/\` 目录,每个子目录即为一个技能包。
331
+
332
+ ## SKILL STRUCTURE
333
+
334
+ 每个技能包的标准结构:
335
+ \`\`\`
336
+ skills/[skill_name]/
337
+ ├── manifest.md # 执行流程和规范(必读)
338
+ ├── context.md # 项目配置(需初始化)
339
+ ├── input/ # 该技能的输入文档
340
+ ├── output/ # 该技能的输出文档
341
+ └── tools/ # 该技能的提示词工具
342
+ \`\`\`
343
+
344
+ ## MANDATORY WORKFLOW
345
+
346
+ ### 1. Skill Discovery (技能发现)
347
+ - 扫描 \`skills/\` 目录,列出所有可用技能
348
+ - 读取每个技能的 \`manifest.md\` 了解其用途
349
+
350
+ ### 2. Skill Selection (技能选择)
351
+ - 根据用户任务类型,选择对应的技能包
352
+ - 如果不确定,询问用户或列出可用技能供选择
353
+
354
+ ### 3. Load Skill (加载技能)
355
+ - 读取 \`skills/[skill]/manifest.md\` 获取执行流程
356
+ - 读取 \`skills/[skill]/context.md\` 获取项目配置
357
+
358
+ ### 4. Context Check (上下文检查)
359
+ - 如果 \`context.md\` 未初始化,**终止任务**
360
+ - 提示用户:"请先执行项目初始化,参考 \`skills/[skill]/tools/init.md\`"
361
+
362
+ ### 5. Input Acquisition (获取输入)
363
+ - 从 \`skills/[skill]/input/\` 读取需求文档
364
+
365
+ ### 6. Execute Task (执行任务)
366
+ - 按照 \`manifest.md\` 定义的流程执行
367
+ - 代码输出到项目源码目录(由 context.md 指定)
368
+
369
+ ### 7. Output Documentation (输出文档)
370
+ - 执行日志输出到 \`skills/[skill]/output/\`
371
+
372
+ ## CONSTRAINTS
373
+
374
+ - 严格遵循所选技能的 manifest.md 流程
375
+ - 代码路径受控于 context.md
376
+ - 禁止跨技能读写 input/output
377
+ - 禁止臆造依赖库或目录
378
+ - 新技能自动可用,无需修改此文件
379
+ `;
380
+ fs.writeFileSync(rulesPath, rulesContent, 'utf-8');
381
+ console.log(chalk.green('\n✅ AI 工作区已创建'));
382
+ console.log(chalk.gray(`路径: ${workspaceDir}`));
383
+ console.log(chalk.cyan('\n📝 下一步:'));
384
+ console.log(chalk.gray(' pp skill list # 查看可用技能包'));
385
+ console.log(chalk.gray(' pp skill install # 安装技能包\n'));
386
+ }
387
+ // 查看已安装的技能包
388
+ async function installedSkills(options) {
389
+ const chalk = await getChalk();
390
+ const baseDir = options?.output || '.ai-workspace';
391
+ const skillsDir = path.join(process.cwd(), baseDir, 'skills');
392
+ if (!fs.existsSync(skillsDir)) {
393
+ console.log(chalk.yellow('\n⚠️ 未找到 AI 工作区'));
394
+ console.log(chalk.gray('使用 "pp workspace init" 初始化工作区\n'));
395
+ return;
396
+ }
397
+ const skills = (0, skills_1.loadSkillsFromDir)(skillsDir);
398
+ if (skills.length === 0) {
399
+ console.log(chalk.yellow('\n⚠️ 未安装任何技能包'));
400
+ console.log(chalk.gray('使用 "pp skill install" 安装技能包\n'));
401
+ return;
402
+ }
403
+ console.log(chalk.cyan('\n🎯 已安装的技能包:\n'));
404
+ for (const skill of skills) {
405
+ const status = [];
406
+ if (skill.hasManifest)
407
+ status.push(chalk.green('✓manifest'));
408
+ if (skill.hasContext)
409
+ status.push(chalk.green('✓context'));
410
+ if (skill.hasTools)
411
+ status.push(chalk.green('✓tools'));
412
+ console.log(chalk.white(` • ${skill.name}`), chalk.gray(`- ${skill.description || '无描述'}`), chalk.gray(`[${status.join(' ')}]`));
413
+ }
414
+ console.log(chalk.gray(`\n📁 位置: ${skillsDir}\n`));
415
+ }
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { loadTemplatesFromDir } from './templates';
2
- export type { Template } from './types';
1
+ export { loadSkillsFromDir, copySkill } from './skills';
2
+ export type { Skill, SkillMeta, RepoConfig, PromptPlusConfig } from './types';
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.loadTemplatesFromDir = void 0;
4
- var templates_1 = require("./templates");
5
- Object.defineProperty(exports, "loadTemplatesFromDir", { enumerable: true, get: function () { return templates_1.loadTemplatesFromDir; } });
3
+ exports.copySkill = exports.loadSkillsFromDir = void 0;
4
+ var skills_1 = require("./skills");
5
+ Object.defineProperty(exports, "loadSkillsFromDir", { enumerable: true, get: function () { return skills_1.loadSkillsFromDir; } });
6
+ Object.defineProperty(exports, "copySkill", { enumerable: true, get: function () { return skills_1.copySkill; } });
@@ -0,0 +1,3 @@
1
+ import { Skill } from './types';
2
+ export declare function loadSkillsFromDir(skillsDir: string): Skill[];
3
+ export declare function copySkill(skillPath: string, targetDir: string): void;
package/dist/skills.js ADDED
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.loadSkillsFromDir = loadSkillsFromDir;
37
+ exports.copySkill = copySkill;
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ // 从 manifest.md 解析技能元数据
41
+ function parseSkillMeta(manifestPath) {
42
+ if (!fs.existsSync(manifestPath)) {
43
+ return null;
44
+ }
45
+ const content = fs.readFileSync(manifestPath, 'utf-8');
46
+ const lines = content.split('\n');
47
+ // 解析标题作为名称
48
+ const titleMatch = content.match(/^#\s+(?:Skill Manifest:\s*)?(.+)/m);
49
+ const name = titleMatch ? titleMatch[1].trim() : '';
50
+ // 解析描述(## 技能描述 或 ## 核心职责 下的内容)
51
+ let description = '';
52
+ const descMatch = content.match(/##\s*(?:技能描述|核心职责)\s*\n([^\n#]+)/);
53
+ if (descMatch) {
54
+ description = descMatch[1].trim();
55
+ }
56
+ // 解析分类(如果有)
57
+ let category = 'general';
58
+ const categoryMatch = content.match(/category:\s*(\w+)/i);
59
+ if (categoryMatch) {
60
+ category = categoryMatch[1];
61
+ }
62
+ return { name, description, category };
63
+ }
64
+ // 从目录加载技能包
65
+ function loadSkillsFromDir(skillsDir) {
66
+ if (!fs.existsSync(skillsDir)) {
67
+ return [];
68
+ }
69
+ const skills = [];
70
+ const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
71
+ for (const entry of entries) {
72
+ if (!entry.isDirectory())
73
+ continue;
74
+ const skillPath = path.join(skillsDir, entry.name);
75
+ const manifestPath = path.join(skillPath, 'manifest.md');
76
+ const contextPath = path.join(skillPath, 'context.md');
77
+ const toolsPath = path.join(skillPath, 'tools');
78
+ const meta = parseSkillMeta(manifestPath);
79
+ skills.push({
80
+ name: entry.name,
81
+ description: meta?.description || '',
82
+ category: meta?.category || 'general',
83
+ path: skillPath,
84
+ hasManifest: fs.existsSync(manifestPath),
85
+ hasContext: fs.existsSync(contextPath),
86
+ hasTools: fs.existsSync(toolsPath),
87
+ });
88
+ }
89
+ return skills;
90
+ }
91
+ // 复制技能包到目标目录
92
+ function copySkill(skillPath, targetDir) {
93
+ if (!fs.existsSync(targetDir)) {
94
+ fs.mkdirSync(targetDir, { recursive: true });
95
+ }
96
+ copyDirRecursive(skillPath, targetDir);
97
+ }
98
+ // 递归复制目录
99
+ function copyDirRecursive(src, dest) {
100
+ if (!fs.existsSync(dest)) {
101
+ fs.mkdirSync(dest, { recursive: true });
102
+ }
103
+ const entries = fs.readdirSync(src, { withFileTypes: true });
104
+ for (const entry of entries) {
105
+ const srcPath = path.join(src, entry.name);
106
+ const destPath = path.join(dest, entry.name);
107
+ if (entry.isDirectory()) {
108
+ copyDirRecursive(srcPath, destPath);
109
+ }
110
+ else {
111
+ fs.copyFileSync(srcPath, destPath);
112
+ }
113
+ }
114
+ }
package/dist/types.d.ts CHANGED
@@ -1,17 +1,24 @@
1
- export interface Template {
1
+ export interface Skill {
2
2
  name: string;
3
3
  description: string;
4
4
  category: string;
5
- content: string;
6
- outputFileName: string;
5
+ path: string;
6
+ hasManifest: boolean;
7
+ hasContext: boolean;
8
+ hasTools: boolean;
7
9
  }
8
10
  export interface RepoConfig {
9
11
  name: string;
10
12
  url: string;
11
- branch?: string;
13
+ branch: string;
12
14
  }
13
15
  export interface PromptPlusConfig {
14
16
  defaultRepo: string;
15
17
  repos: RepoConfig[];
16
18
  outputDir: string;
17
19
  }
20
+ export interface SkillMeta {
21
+ name: string;
22
+ description: string;
23
+ category?: string;
24
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "prompt-plus",
3
- "version": "1.0.4",
4
- "description": "AI提示词模板管理工具 - 生成符合项目规范的提示词",
3
+ "version": "1.1.0",
4
+ "description": "AI技能包管理工具 - 基于技能包驱动的 AI 开发工作流",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "bin": {
@@ -1,2 +0,0 @@
1
- import { Template } from '../types';
2
- export declare function loadTemplatesFromDir(dir: string): Template[];
@@ -1,92 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.loadTemplatesFromDir = loadTemplatesFromDir;
37
- const fs = __importStar(require("fs"));
38
- const path = __importStar(require("path"));
39
- // 解析 Markdown Front Matter
40
- function parseFrontMatter(content) {
41
- const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
42
- if (!match) {
43
- return { meta: {}, body: content };
44
- }
45
- const meta = {};
46
- const yamlContent = match[1];
47
- const body = match[2];
48
- yamlContent.split('\n').forEach((line) => {
49
- const colonIndex = line.indexOf(':');
50
- if (colonIndex > 0) {
51
- const key = line.slice(0, colonIndex).trim();
52
- const value = line.slice(colonIndex + 1).trim();
53
- meta[key] = value;
54
- }
55
- });
56
- return { meta, body };
57
- }
58
- // 从目录加载模板(支持 .md 和 .json)
59
- function loadTemplatesFromDir(dir) {
60
- if (!fs.existsSync(dir)) {
61
- return [];
62
- }
63
- const templates = [];
64
- const files = fs.readdirSync(dir);
65
- for (const file of files) {
66
- const filePath = path.join(dir, file);
67
- try {
68
- if (file.endsWith('.md') && file !== 'README.md') {
69
- const content = fs.readFileSync(filePath, 'utf-8');
70
- const { meta, body } = parseFrontMatter(content);
71
- if (meta.name) {
72
- templates.push({
73
- name: meta.name,
74
- description: meta.description || '',
75
- category: meta.category || 'other',
76
- outputFileName: meta.outputFileName || `${meta.name}-prompt.md`,
77
- content: body.trim(),
78
- });
79
- }
80
- }
81
- else if (file.endsWith('.json')) {
82
- const content = fs.readFileSync(filePath, 'utf-8');
83
- const template = JSON.parse(content);
84
- templates.push(template);
85
- }
86
- }
87
- catch {
88
- // 忽略解析错误
89
- }
90
- }
91
- return templates;
92
- }