@aiyiran/myclaw 1.0.92 → 1.0.93

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.
@@ -0,0 +1,225 @@
1
+ # 英语单词学习记录
2
+ ## 日期:2026年4月1日
3
+
4
+ ---
5
+
6
+ ## 学习目标
7
+ - 20个英语单词
8
+ - 对象:初中生
9
+ - 要求:记得快、记得好
10
+
11
+ ---
12
+
13
+ ## 单词列表
14
+
15
+ ### 1. espionage [ɪˈspaɪənɪdʒ]
16
+ - 词性:名词
17
+ - 含义:间谍活动
18
+ - 例句:The government has several agents involved in espionage.
19
+ (政府有好几个特工参与间谍活动。)
20
+
21
+ ### 2. mission [ˈmɪʃn]
22
+ - 词性:名词
23
+ - 含义:任务
24
+ - 例句:The mission brief was sent to your computer.
25
+ (任务简报已经发到你的电脑里了。)
26
+
27
+ ### 3. brief [briːf]
28
+ - 词性:名词/形容词
29
+ - 含义:简报(n.)/ 简短的(adj.)
30
+ - 例句:The mission brief was sent to your computer.
31
+ (任务简报已经发到你的电脑里了。)
32
+
33
+ ### 4. agent [ˈeɪdʒənt]
34
+ - 词性:名词
35
+ - 含义:特工、代理人
36
+ - 例句:The secret agent went into the building alone.
37
+ (那名秘密特工独自走进了那栋大楼。)
38
+ - 简化例句:He is an undercover cop.(他是一名卧底警察。)
39
+
40
+ ### 5. undercover [ˌʌndəˈkʌvə(r)]
41
+ - 词性:形容词
42
+ - 含义:卧底的、秘密执行的
43
+ - 例句:He is an undercover cop.(他是一名卧底警察。)
44
+ - 记忆技巧:under(在…下面)+ cover(覆盖)= 在覆盖之下 = 卧底的
45
+
46
+ ### 6. surveillance [səˈveɪləns]
47
+ - 词性:名词
48
+ - 含义:监视、监控
49
+ - 例句:The police kept the building under surveillance.
50
+ (警察对那栋大楼进行了监视。)
51
+ - 记忆技巧:谐音"司马在外"(Sima Fu)= 司马孚在外面监视 = surveillance
52
+
53
+ ### 7. gadget [ˈɡædʒɪt]
54
+ - 词性:名词
55
+ - 含义:小玩意儿、小装置
56
+ - 例句:The spy used a cool gadget to open the door.
57
+ (特工用一个酷炫的小装置打开了门。)
58
+ - 词源:19世纪英国水手的俚语,用来称呼各种不知道叫什么的小零件
59
+
60
+ ### 8. intel [ˈɪntl]
61
+ - 词性:名词
62
+ - 含义:情报(intelligence的缩写)
63
+ - 例句:The agent got some important intel.
64
+ (特工收到了一条重要情报。)
65
+ - 记忆技巧:in + tel(telephone)= 从电话里得到的情报
66
+
67
+ ### 9. extraction [ɪkˈstrækʃn]
68
+ - 词性:名词
69
+ - 含义:撤离、营救
70
+ - 例句:The team made a quick extraction.(小队快速撤离。)
71
+ - 记忆技巧:ex(出来)+ tract(拉)= 拉出来 = 撤离
72
+ - 5个例句:
73
+ 1. The rescue team did a fast extraction.
74
+ 2. The spy was in trouble, so they ordered an extraction.
75
+ 3. The extraction point is near the old building.
76
+ 4. He finished the extraction mission successfully.
77
+ 5. The helicopter came for the extraction.
78
+
79
+ ### 10. code [kəʊd]
80
+ - 词性:名词
81
+ - 含义:密码、代码
82
+ - 例句:The spy used a secret code to send messages.
83
+ (特工用密码发送信息。)
84
+ - 5个例句:
85
+ 1. Don't tell anyone the secret code.
86
+ 2. The password is a four-digit code.
87
+ 3. He broke the code and saved the day.
88
+ 4. What is the secret code?
89
+ 5. She sent a message in code.
90
+
91
+ ### 11. cipher [ˈsaɪfə(r)]
92
+ - 词性:名词
93
+ - 含义:密码、密文(更专业的加密术语)
94
+ - 例句:Can you read this cipher?(你能读懂这个密码吗?)
95
+ - 与code的区别:code是把整个词换成另一个词,cipher是把每个字母换成另一个字母
96
+ - 5个例句:
97
+ 1. This text looks like a cipher.
98
+ 2. She wrote the message in cipher.
99
+ 3. He tried to solve the cipher.
100
+ 4. The cipher was very hard to break.
101
+ 5. Can you read this cipher?
102
+
103
+ ### 12. double agent
104
+ - 词性:名词短语
105
+ - 含义:双面间谍
106
+ - 例句:He is a double agent, working for both sides.
107
+ (他是双面间谍,同时为两边工作。)
108
+
109
+ ### 13. coming of age
110
+ - 词性:短语/习语
111
+ - 含义:长大成人、成年
112
+ - 例句:The movie is about a boy's coming of age.
113
+ (这部电影是关于一个男孩的成长故事。)
114
+ - 说明:coming of age = 来到成年,最早是法律术语,后延伸指心理成熟
115
+ - 5个例句:
116
+ 1. His coming of age was very difficult.
117
+ 2. The book is about coming of age.
118
+ 3. She had a coming of age experience.
119
+ 4. Coming of age is an important time in life.
120
+ 5. This book is a story about a young man's coming of age in a small town.
121
+
122
+ ### 14. adolescence [ˌædəˈlesns]
123
+ - 词性:名词
124
+ - 含义:青春期
125
+ - 例句:Many changes happen during adolescence.
126
+ (青春期会发生很多变化。)
127
+ - 5个例句:
128
+ 1. He spent his adolescence in Paris.
129
+ 2. Adolescence is a time of big changes.
130
+ 3. Many teenagers feel confused during adolescence.
131
+ 4. She wrote a diary during her adolescence.
132
+ 5. Adolescence usually starts around age 12.
133
+
134
+ ### 15. identity [aɪˈdentəti]
135
+ - 词性:名词
136
+ - 含义:身份
137
+ - 例句:He hid his true identity.(他隐藏了自己的真实身份。)
138
+ - 注意:identity和Identity是同一个词,只是大小写不同(首字母大写只是因为出现在句首)
139
+
140
+ ### 16. independence [ˌɪndɪˈpendəns]
141
+ - 词性:名词
142
+ - 含义:独立、自立
143
+ - 例句:The country got its independence in 1949.
144
+ (这个国家在1949年获得了独立。)
145
+ - 记忆技巧:in(不)+ depend(依赖)+ ence = 不依赖别人 = 独立
146
+
147
+ ### 17. peer pressure
148
+ - 词性:名词短语
149
+ - 含义:同伴压力
150
+ - 例句:He started smoking because of peer pressure.
151
+ (他因为同伴压力开始抽烟。)
152
+ - 记忆技巧:peer(同龄人)+ pressure(压力)= 来自同龄人的压力
153
+ - 5个例句:
154
+ 1. She didn't want to cheat, but peer pressure was too strong.
155
+ 2. Peer pressure can make you do bad things.
156
+ 3. He resisted peer pressure and said no.
157
+ 4. Teens often face peer pressure at school.
158
+ 5. Good friends won't give you bad peer pressure.
159
+
160
+ ### 18. belonging [bɪˈlɒŋɪŋ]
161
+ - 词性:名词
162
+ - 含义:归属感、归属
163
+ - 例句:She felt a sense of belonging in her new school.
164
+ (她在新学校找到了归属感。)
165
+
166
+ ### 19. self-discovery
167
+ - 词性:名词
168
+ - 含义:自我发现、认识自我
169
+ - 例句:Her trip to Tibet was a journey of self-discovery.
170
+ (她去西藏的旅行是一次自我发现的旅程。)
171
+ - 记忆技巧:self(自己)+ discovery(发现)= 发现你自己
172
+
173
+ ### 20. relationships
174
+ - 词性:名词
175
+ - 含义:人际关系、关系
176
+ - 例句:Teenagers often have problems with peer relationships.
177
+ (青少年经常在人际关系方面有问题。)
178
+
179
+ ### 21. internal conflict
180
+ - 词性:名词短语
181
+ - 含义:内心矛盾、内心冲突
182
+ - 例句:He had an internal conflict about telling the truth.
183
+ (他内心很矛盾,不知道该不该说实话。)
184
+ - 记忆技巧:internal(内心的)+ conflict(冲突)= 内心冲突
185
+
186
+ ### 22. personal growth
187
+ - 词性:名词短语
188
+ - 含义:个人成长
189
+ - 例句:Reading books helps with personal growth.
190
+ (阅读有助于个人成长。)
191
+
192
+ ---
193
+
194
+ ## 故事串联
195
+ 所有间谍主题词汇串联:
196
+
197
+ **The Mission(任务)**
198
+
199
+ A secret **agent** got a **mission brief** on his computer.
200
+
201
+ The **brief** said: go **undercover** and do some **espionage**.
202
+
203
+ But the bad guys had the whole building under **surveillance**!
204
+
205
+ He used a cool **gadget** to get inside...
206
+
207
+ ---
208
+
209
+ ## 初中生相关词汇整理
210
+
211
+ ### 青春期主题词汇:
212
+ - adolescence 青春期
213
+ - coming of age 长大成人
214
+ - identity 身份
215
+ - independence 独立
216
+ - peer pressure 同伴压力
217
+ - belonging 归属感
218
+ - self-discovery 自我发现
219
+ - relationships 人际关系
220
+ - internal conflict 内心矛盾
221
+ - personal growth 个人成长
222
+
223
+ ---
224
+
225
+ 备注:学习者是初中生,英语水平一般。采用联想记忆、谐音记忆、词源解释等多种方法帮助记忆。
package/build-manifest.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * build-manifest.js - 自动构建 patch-manifest.json
6
6
  * ============================================================================
7
7
  *
8
- * 扫描 agent-list/ 目录,自动生成注入清单。
8
+ * 扫描 agent-list/ 和 skills/ 目录,自动生成统一注入清单。
9
9
  * 已有的 strategy 设置会保留(不会被覆盖)。
10
10
  *
11
11
  * 在 publish.sh 中自动调用,无需手动维护 manifest。
@@ -17,8 +17,9 @@ const path = require('path');
17
17
 
18
18
  const MANIFEST_PATH = path.join(__dirname, 'patch-manifest.json');
19
19
  const AGENT_LIST_DIR = path.join(__dirname, 'agent-list');
20
+ const SKILLS_DIR = path.join(__dirname, 'skills');
20
21
 
21
- // 默认 config patches(硬编码,这些不从目录扫描)
22
+ // 默认 config patches(硬编码,不从目录扫描)
22
23
  const DEFAULT_CONFIG = {
23
24
  strategy: 'overwrite',
24
25
  patches: {
@@ -30,27 +31,24 @@ const DEFAULT_CONFIG = {
30
31
 
31
32
  function buildManifest() {
32
33
  // 读取已有 manifest(保留手动设置的 strategy)
33
- let existing = { agents: [], config: DEFAULT_CONFIG };
34
+ let existing = { agents: [], skills: [], config: DEFAULT_CONFIG };
34
35
  if (fs.existsSync(MANIFEST_PATH)) {
35
36
  try {
36
37
  existing = JSON.parse(fs.readFileSync(MANIFEST_PATH, 'utf8'));
37
38
  } catch {}
38
39
  }
39
40
 
40
- // 建立已有 agent 的 strategy 映射(保留手动覆盖)
41
- const existingStrategies = {};
42
- if (Array.isArray(existing.agents)) {
43
- for (const a of existing.agents) {
44
- if (a.id && a.strategy) {
45
- existingStrategies[a.id] = a.strategy;
46
- }
47
- if (a.id && a.description) {
48
- existingStrategies[a.id + '_desc'] = a.description;
49
- }
41
+ // 建立已有策略映射(保留手动覆盖)
42
+ const saved = {};
43
+ for (const list of [existing.agents || [], existing.skills || []]) {
44
+ for (const item of list) {
45
+ const key = item.id || item.name;
46
+ if (key && item.strategy) saved[key] = item.strategy;
47
+ if (key && item.description) saved[key + '_desc'] = item.description;
50
48
  }
51
49
  }
52
50
 
53
- // 扫描 agent-list/ 目录
51
+ // === 1. 扫描 agent-list/ ===
54
52
  const agents = [];
55
53
  if (fs.existsSync(AGENT_LIST_DIR)) {
56
54
  const dirs = fs.readdirSync(AGENT_LIST_DIR, { withFileTypes: true })
@@ -62,19 +60,40 @@ function buildManifest() {
62
60
  agents.push({
63
61
  id: agentId,
64
62
  workspace: d.name,
65
- strategy: existingStrategies[agentId] || 'auto',
66
- description: existingStrategies[agentId + '_desc'] || agentId,
63
+ strategy: saved[agentId] || 'auto',
64
+ description: saved[agentId + '_desc'] || agentId,
67
65
  });
68
66
  }
69
67
  }
70
68
 
71
- // 保留已有 config(允许手动编辑 config patches)
69
+ // === 2. 扫描 skills/ ===
70
+ const skills = [];
71
+ if (fs.existsSync(SKILLS_DIR)) {
72
+ const dirs = fs.readdirSync(SKILLS_DIR, { withFileTypes: true })
73
+ .filter(d => d.isDirectory())
74
+ .sort((a, b) => a.name.localeCompare(b.name));
75
+
76
+ for (const d of dirs) {
77
+ // 必须有 SKILL.md 才是合法 skill
78
+ const skillMd = path.join(SKILLS_DIR, d.name, 'SKILL.md');
79
+ if (!fs.existsSync(skillMd)) continue;
80
+
81
+ skills.push({
82
+ name: d.name,
83
+ strategy: saved[d.name] || 'auto',
84
+ description: saved[d.name + '_desc'] || d.name,
85
+ });
86
+ }
87
+ }
88
+
89
+ // === 3. 保留 config ===
72
90
  const config = existing.config || DEFAULT_CONFIG;
73
91
 
74
92
  const manifest = {
75
- _doc: 'MyClaw 注入清单 (auto-generated by build-manifest.js)。strategy: auto(不存在才注入) | overwrite(覆盖) | off(关闭)',
93
+ _doc: 'MyClaw 注入清单 (auto-generated)。strategy: auto | overwrite | off',
76
94
  _generated: new Date().toISOString(),
77
95
  agents,
96
+ skills,
78
97
  config,
79
98
  };
80
99
 
@@ -83,9 +102,13 @@ function buildManifest() {
83
102
  console.log('📋 patch-manifest.json 已生成');
84
103
  console.log(' 智能体: ' + agents.length + ' 个');
85
104
  for (const a of agents) {
86
- console.log(' • ' + a.id + ' [' + a.strategy + '] ' + a.description);
105
+ console.log(' • ' + a.id + ' [' + a.strategy + ']');
106
+ }
107
+ console.log(' 技能: ' + skills.length + ' 个');
108
+ for (const s of skills) {
109
+ console.log(' • ' + s.name + ' [' + s.strategy + ']');
87
110
  }
88
- console.log(' Config patches: ' + Object.keys(config.patches || {}).length + ' 项');
111
+ console.log(' 配置: ' + Object.keys(config.patches || {}).length + ' 项 [' + (config.strategy || 'overwrite') + ']');
89
112
  }
90
113
 
91
114
  buildManifest();
package/index.js CHANGED
@@ -545,7 +545,7 @@ function runPatch() {
545
545
  const manifest = loadManifest();
546
546
  if (manifest && manifest.config && manifest.config.patches) {
547
547
  const configStrategy = manifest.config.strategy || 'overwrite';
548
- if (configStrategy !== 'disabled') {
548
+ if (configStrategy !== 'off') {
549
549
  // 将 dot-notation patch 展开为嵌套对象
550
550
  const patches = manifest.config.patches;
551
551
  const nested = {};
@@ -724,6 +724,102 @@ function runUpdate() {
724
724
  console.log('');
725
725
  }
726
726
 
727
+ // ============================================================================
728
+ // 资源管理列表
729
+ // ============================================================================
730
+
731
+ function runList() {
732
+ const fs = require('fs');
733
+ const path = require('path');
734
+ const os = require('os');
735
+ const { loadManifest } = require('./patch-agent');
736
+ const bar = '----------------------------------------';
737
+
738
+ console.log('');
739
+ console.log('[' + colors.blue + 'MyClaw' + colors.nc + '] ' + colors.blue + '注入资源管理' + colors.nc);
740
+ console.log(bar);
741
+
742
+ const manifest = loadManifest();
743
+ if (!manifest) {
744
+ console.log('');
745
+ console.log('[' + colors.yellow + '提示' + colors.nc + '] patch-manifest.json 不存在');
746
+ console.log(' 运行 ' + colors.yellow + 'myclaw patch' + colors.nc + ' 将自动生成');
747
+ console.log('');
748
+ return;
749
+ }
750
+
751
+ const openclawDir = path.join(os.homedir(), '.openclaw');
752
+ const configPath = path.join(openclawDir, 'openclaw.json');
753
+ let registeredIds = new Set();
754
+ try {
755
+ const cfg = JSON.parse(fs.readFileSync(configPath, 'utf8'));
756
+ if (cfg.agents && Array.isArray(cfg.agents.list)) {
757
+ for (const a of cfg.agents.list) {
758
+ if (a && a.id) registeredIds.add(a.id);
759
+ }
760
+ }
761
+ } catch {}
762
+
763
+ // === Agents ===
764
+ console.log('');
765
+ console.log(' ' + colors.blue + '🤖 智能体 (Agents)' + colors.nc);
766
+ if (manifest.agents && manifest.agents.length > 0) {
767
+ for (const a of manifest.agents) {
768
+ const wsPath = path.join(openclawDir, a.workspace || ('workspace-' + a.id));
769
+ const wsExists = fs.existsSync(wsPath);
770
+ const registered = registeredIds.has(a.id);
771
+ const status = wsExists && registered ? colors.green + '✔ 已安装' + colors.nc
772
+ : wsExists ? colors.yellow + '⚠ 文件存在未注册' + colors.nc
773
+ : colors.red + '✗ 未安装' + colors.nc;
774
+ const strat = a.strategy === 'off' ? colors.red + a.strategy + colors.nc : a.strategy;
775
+ console.log(' ' + padRight(a.id, 20) + status + ' [' + strat + ']');
776
+ }
777
+ } else {
778
+ console.log(' (无)');
779
+ }
780
+
781
+ // === Skills ===
782
+ console.log('');
783
+ console.log(' ' + colors.blue + '🧩 技能 (Skills)' + colors.nc);
784
+ if (manifest.skills && manifest.skills.length > 0) {
785
+ const skillsDir = path.join(openclawDir, 'skills');
786
+ for (const s of manifest.skills) {
787
+ const installed = fs.existsSync(path.join(skillsDir, s.name));
788
+ const status = installed ? colors.green + '✔ 已安装' + colors.nc : colors.red + '✗ 未安装' + colors.nc;
789
+ const strat = s.strategy === 'off' ? colors.red + s.strategy + colors.nc : s.strategy;
790
+ console.log(' ' + padRight(s.name, 20) + status + ' [' + strat + ']');
791
+ }
792
+ } else {
793
+ console.log(' (无)');
794
+ }
795
+
796
+ // === Config ===
797
+ console.log('');
798
+ console.log(' ' + colors.blue + '⚙️ 配置 (Config Patches)' + colors.nc);
799
+ if (manifest.config && manifest.config.patches) {
800
+ const strat = manifest.config.strategy || 'overwrite';
801
+ const stratDisplay = strat === 'off' ? colors.red + strat + colors.nc : strat;
802
+ console.log(' 策略: [' + stratDisplay + ']');
803
+ for (const [key, value] of Object.entries(manifest.config.patches)) {
804
+ console.log(' ' + padRight(key, 30) + '→ ' + JSON.stringify(value));
805
+ }
806
+ } else {
807
+ console.log(' (无)');
808
+ }
809
+
810
+ console.log('');
811
+ console.log(bar);
812
+ console.log('策略说明: ' + colors.green + 'auto' + colors.nc + '=不存在才注入 '
813
+ + colors.yellow + 'overwrite' + colors.nc + '=覆盖 '
814
+ + colors.red + 'off' + colors.nc + '=关闭');
815
+ console.log('');
816
+ }
817
+
818
+ function padRight(str, len) {
819
+ if (str.length >= len) return str + ' ';
820
+ return str + ' '.repeat(len - str.length);
821
+ }
822
+
727
823
  // ============================================================================
728
824
  // 帮助信息
729
825
  // ============================================================================
@@ -751,7 +847,9 @@ function showHelp() {
751
847
  console.log(' open 打开浏览器控制台(自动带 token)');
752
848
  console.log(' wsl2 WSL2 一键安装/修复 (仅限 Windows)');
753
849
  console.log(' bat 在桌面生成一键启动脚本 (仅限 Windows)');
754
- console.log(' patch 注入 MyClaw UI 扩展到 WebChat');
850
+ console.log(' list 查看注入资源管理列表(智能体/技能/配置)');
851
+ console.log(' pull 从 ~/.openclaw 拉取最新资源到源目录(开发用)');
852
+ console.log(' patch 注入 MyClaw UI + 技能 + 智能体 + 配置');
755
853
  console.log(' unpatch 回滚 UI 注入(恢复原版)');
756
854
  console.log(' minimax 注入 MiniMax 模型配置 (可选: --key sk-xxx)');
757
855
  console.log(' restart 重启 OpenClaw Gateway');
@@ -810,6 +908,11 @@ if (!command || command === 'help' || command === '--help' || command === '-h')
810
908
  runLaunch();
811
909
  } else if (command === 'bat') {
812
910
  runBat();
911
+ } else if (command === 'list') {
912
+ runList();
913
+ } else if (command === 'pull') {
914
+ const pull = require('./pull');
915
+ pull.run();
813
916
  } else if (command === 'patch') {
814
917
  runPatch();
815
918
  } else if (command === 'unpatch') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiyiran/myclaw",
3
- "version": "1.0.92",
3
+ "version": "1.0.93",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
package/patch-agent.js CHANGED
@@ -6,9 +6,9 @@
6
6
  * 功能:
7
7
  * 1. 读取 patch-manifest.json 中的 agents 列表
8
8
  * 2. 根据 strategy 决定行为:
9
- * - if-absent: 目标 workspace 不存在时才注入(默认)
9
+ * - auto: 目标 workspace 不存在时才注入(默认)
10
10
  * - overwrite: 始终覆盖更新 workspace 文件
11
- * - disabled: 跳过,不注入
11
+ * - off: 跳过,不注入
12
12
  * 3. 将 workspace 复制到 ~/.openclaw/ 下
13
13
  * 4. 在 openclaw.json 的 agents.list 中注册
14
14
  * 5. 创建 agent 目录结构
@@ -110,12 +110,12 @@ function patchAgents() {
110
110
  for (const entry of manifest.agents) {
111
111
  const agentId = entry.id;
112
112
  const workspaceName = entry.workspace || ('workspace-' + agentId);
113
- const strategy = entry.strategy || 'if-absent';
113
+ const strategy = entry.strategy || 'auto';
114
114
  const desc = entry.description || agentId;
115
115
 
116
- // disabled → 跳过
117
- if (strategy === 'disabled') {
118
- console.log('[myclaw-agent] ⏭️ 跳过: ' + agentId + ' (' + desc + ') [disabled]');
116
+ // off → 跳过
117
+ if (strategy === 'off') {
118
+ console.log('[myclaw-agent] ⏭️ 跳过: ' + agentId + ' (' + desc + ') [off]');
119
119
  skipped++;
120
120
  continue;
121
121
  }
@@ -133,9 +133,9 @@ function patchAgents() {
133
133
 
134
134
  const alreadyExists = fs.existsSync(destWorkspace);
135
135
 
136
- // if-absent → 已存在则跳过
137
- if (strategy === 'if-absent' && alreadyExists) {
138
- console.log('[myclaw-agent] ✔️ 已存在: ' + agentId + ' (' + desc + ') [if-absent, 跳过]');
136
+ // auto → 已存在则跳过
137
+ if (strategy === 'auto' && alreadyExists) {
138
+ console.log('[myclaw-agent] ✔️ 已存在: ' + agentId + ' (' + desc + ') [auto, 跳过]');
139
139
  skipped++;
140
140
  continue;
141
141
  }
@@ -1,12 +1,19 @@
1
1
  {
2
- "_doc": "MyClaw 注入清单 (auto-generated by build-manifest.js)。strategy: auto(不存在才注入) | overwrite(覆盖) | off(关闭)",
3
- "_generated": "2026-04-01T11:53:15.453Z",
2
+ "_doc": "MyClaw 注入清单 (auto-generated)。strategy: auto | overwrite | off",
3
+ "_generated": "2026-04-01T12:06:06.605Z",
4
4
  "agents": [
5
5
  {
6
6
  "id": "danci",
7
7
  "workspace": "workspace-danci",
8
- "strategy": "if-absent",
9
- "description": "单词学习智能体"
8
+ "strategy": "auto",
9
+ "description": "danci"
10
+ }
11
+ ],
12
+ "skills": [
13
+ {
14
+ "name": "minimax-inject",
15
+ "strategy": "auto",
16
+ "description": "minimax-inject"
10
17
  }
11
18
  ],
12
19
  "config": {
package/pull.js ADDED
@@ -0,0 +1,197 @@
1
+ /**
2
+ * ============================================================================
3
+ * MyClaw Pull - 从 ~/.openclaw/ 拉取最新资源到 myclaw 源目录
4
+ * ============================================================================
5
+ *
6
+ * 反向同步:将线上运行环境中的智能体、技能拷贝回 myclaw 包内,
7
+ * 确保下次 publish 时打包的是最新版本。
8
+ *
9
+ * 拉取范围:
10
+ * 1. manifest 中已有的 agents → 从 ~/.openclaw/workspace-* 回拷到 agent-list/
11
+ * 2. manifest 中已有的 skills → 从 ~/.openclaw/skills/* 回拷到 skills/
12
+ * 3. 如果发现 ~/.openclaw/ 中有新的 workspace-* 不在 manifest 中,提示是否纳入
13
+ *
14
+ * ============================================================================
15
+ */
16
+
17
+ const fs = require('fs');
18
+ const path = require('path');
19
+ const os = require('os');
20
+
21
+ const OPENCLAW_DIR = path.join(os.homedir(), '.openclaw');
22
+ const AGENT_LIST_DIR = path.join(__dirname, 'agent-list');
23
+ const SKILLS_DIR = path.join(__dirname, 'skills');
24
+ const MANIFEST_PATH = path.join(__dirname, 'patch-manifest.json');
25
+
26
+ // 不应拉取的 workspace(排除列表)
27
+ const WORKSPACE_IGNORE = new Set([
28
+ 'workspace', // 默认 workspace(非 agent)
29
+ ]);
30
+
31
+ /**
32
+ * 递归复制目录(幂等覆盖)
33
+ * 跳过 .openclaw/ 和 node_modules/ 等运行时目录
34
+ */
35
+ function copyDirSync(src, dest, skipDirs = ['.openclaw', 'node_modules', '.git']) {
36
+ if (!fs.existsSync(dest)) {
37
+ fs.mkdirSync(dest, { recursive: true });
38
+ }
39
+
40
+ const entries = fs.readdirSync(src, { withFileTypes: true });
41
+ for (const entry of entries) {
42
+ if (skipDirs.includes(entry.name)) continue;
43
+
44
+ const srcPath = path.join(src, entry.name);
45
+ const destPath = path.join(dest, entry.name);
46
+
47
+ if (entry.isDirectory()) {
48
+ copyDirSync(srcPath, destPath, skipDirs);
49
+ } else {
50
+ fs.copyFileSync(srcPath, destPath);
51
+ }
52
+ }
53
+ }
54
+
55
+ /**
56
+ * 统计目录内文件数量
57
+ */
58
+ function countFiles(dir) {
59
+ if (!fs.existsSync(dir)) return 0;
60
+ let count = 0;
61
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
62
+ for (const e of entries) {
63
+ if (e.isDirectory()) {
64
+ count += countFiles(path.join(dir, e.name));
65
+ } else {
66
+ count++;
67
+ }
68
+ }
69
+ return count;
70
+ }
71
+
72
+ /**
73
+ * 加载 manifest
74
+ */
75
+ function loadManifest() {
76
+ if (!fs.existsSync(MANIFEST_PATH)) return null;
77
+ try {
78
+ return JSON.parse(fs.readFileSync(MANIFEST_PATH, 'utf8'));
79
+ } catch { return null; }
80
+ }
81
+
82
+ /**
83
+ * 扫描 ~/.openclaw/ 下所有 workspace-* 目录
84
+ */
85
+ function scanLiveWorkspaces() {
86
+ if (!fs.existsSync(OPENCLAW_DIR)) return [];
87
+ return fs.readdirSync(OPENCLAW_DIR, { withFileTypes: true })
88
+ .filter(d => d.isDirectory() && d.name.startsWith('workspace-') && !WORKSPACE_IGNORE.has(d.name))
89
+ .map(d => ({
90
+ dirName: d.name,
91
+ id: d.name.replace(/^workspace-/, ''),
92
+ fullPath: path.join(OPENCLAW_DIR, d.name),
93
+ }))
94
+ .filter(ws => !AGENT_LIST_DIR.startsWith(ws.fullPath)); // 避免无限递归(比如拉取源码所在的 workspace)
95
+ }
96
+
97
+ /**
98
+ * 扫描 ~/.openclaw/skills/ 下所有技能目录
99
+ */
100
+ function scanLiveSkills() {
101
+ const skillsDir = path.join(OPENCLAW_DIR, 'skills');
102
+ if (!fs.existsSync(skillsDir)) return [];
103
+ return fs.readdirSync(skillsDir, { withFileTypes: true })
104
+ .filter(d => d.isDirectory())
105
+ .map(d => ({
106
+ name: d.name,
107
+ fullPath: path.join(skillsDir, d.name),
108
+ hasSkillMd: fs.existsSync(path.join(skillsDir, d.name, 'SKILL.md')),
109
+ }));
110
+ }
111
+
112
+ /**
113
+ * 执行拉取
114
+ */
115
+ function run() {
116
+ const bar = '----------------------------------------';
117
+
118
+ console.log('');
119
+ console.log('[\x1b[34mMyClaw\x1b[0m] \x1b[34mPull - 拉取最新资源\x1b[0m');
120
+ console.log(bar);
121
+ console.log('');
122
+
123
+ if (!fs.existsSync(OPENCLAW_DIR)) {
124
+ console.log('[\x1b[31m错误\x1b[0m] ~/.openclaw 目录不存在');
125
+ return;
126
+ }
127
+
128
+ // 确保目标目录存在
129
+ if (!fs.existsSync(AGENT_LIST_DIR)) {
130
+ fs.mkdirSync(AGENT_LIST_DIR, { recursive: true });
131
+ }
132
+
133
+ const manifest = loadManifest();
134
+ const manifestAgentIds = new Set();
135
+ const manifestSkillNames = new Set();
136
+
137
+ if (manifest) {
138
+ if (manifest.agents) manifest.agents.forEach(a => manifestAgentIds.add(a.id));
139
+ if (manifest.skills) manifest.skills.forEach(s => manifestSkillNames.add(s.name));
140
+ }
141
+
142
+ // === 1. 拉取智能体 ===
143
+ console.log('\x1b[34m🤖 智能体 (Agents)\x1b[0m');
144
+
145
+ const liveWorkspaces = scanLiveWorkspaces();
146
+ let agentPulled = 0;
147
+ for (const ws of liveWorkspaces) {
148
+ const destDir = path.join(AGENT_LIST_DIR, ws.dirName);
149
+ const inManifest = manifestAgentIds.has(ws.id);
150
+
151
+ if (inManifest) {
152
+ const fileCount = countFiles(ws.fullPath);
153
+ copyDirSync(ws.fullPath, destDir);
154
+ console.log(' 🔄 已同步: ' + ws.id + ' (' + fileCount + ' 文件)');
155
+ agentPulled++;
156
+ }
157
+ }
158
+
159
+ if (agentPulled === 0) {
160
+ console.log(' (没有需要同步的智能体)');
161
+ }
162
+
163
+ // === 2. 拉取技能 ===
164
+ console.log('');
165
+ console.log('\x1b[34m🧩 技能 (Skills)\x1b[0m');
166
+
167
+ const liveSkills = scanLiveSkills();
168
+ let skillPulled = 0;
169
+
170
+ for (const sk of liveSkills) {
171
+ const inManifest = manifestSkillNames.has(sk.name);
172
+
173
+ if (inManifest) {
174
+ if (!sk.hasSkillMd) {
175
+ console.log(' ⚠️ 跳过无 SKILL.md 的技能: ' + sk.name);
176
+ continue;
177
+ }
178
+ const destDir = path.join(SKILLS_DIR, sk.name);
179
+ const fileCount = countFiles(sk.fullPath);
180
+ copyDirSync(sk.fullPath, destDir);
181
+ console.log(' 🔄 已同步: ' + sk.name + ' (' + fileCount + ' 文件)');
182
+ skillPulled++;
183
+ }
184
+ }
185
+
186
+ if (skillPulled === 0) {
187
+ console.log(' (没有需要同步的技能)');
188
+ }
189
+
190
+ // === 汇总 ===
191
+ console.log('');
192
+ console.log(bar);
193
+ console.log('✅ Pull 完毕: 同步了 ' + agentPulled + ' 个智能体, ' + skillPulled + ' 个技能');
194
+ console.log('');
195
+ }
196
+
197
+ module.exports = { run };