yuanflow-cli 0.1.0 → 0.1.2

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.
Files changed (41) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +34 -50
  3. package/bin/yuanflow-cli.js +26 -0
  4. package/bin/yuanflow-skill.cjs +334 -0
  5. package/generated/registry.json +24641 -0
  6. package/lib/skill-installer/agents.cjs +203 -0
  7. package/lib/skill-installer/discover-skills.cjs +79 -0
  8. package/lib/skill-installer/installer.cjs +300 -0
  9. package/lib/skill-installer/publish.cjs +53 -0
  10. package/lib/skill-installer/repo-source.cjs +162 -0
  11. package/package.json +57 -6
  12. package/scripts/generate-registry.js +174 -0
  13. package/skills/yuanflow-skill/SKILL.md +60 -0
  14. package/skills/yuanflow-skill/yuanflow-cli/SKILL.md +207 -0
  15. package/src/agent-protocol.js +169 -0
  16. package/src/cli.js +382 -0
  17. package/src/config.js +31 -0
  18. package/src/registry.js +45 -0
  19. package/src/request.js +97 -0
  20. package/src/shortcuts.js +346 -0
  21. package/cli.js +0 -3
  22. package/src/ycloud/cli.js +0 -29
  23. package/src/ycloud/commands/analysis.js +0 -82
  24. package/src/ycloud/commands/auth.js +0 -191
  25. package/src/ycloud/commands/commands.js +0 -262
  26. package/src/ycloud/commands/compliance.js +0 -146
  27. package/src/ycloud/commands/config.js +0 -103
  28. package/src/ycloud/commands/health.js +0 -35
  29. package/src/ycloud/commands/index.js +0 -381
  30. package/src/ycloud/commands/kb.js +0 -82
  31. package/src/ycloud/commands/schema.js +0 -229
  32. package/src/ycloud/commands/shared.js +0 -30
  33. package/src/ycloud/commands/tool-registry.js +0 -209
  34. package/src/ycloud/commands/tool-runner.js +0 -226
  35. package/src/ycloud/commands/tool.js +0 -178
  36. package/src/ycloud/commands/version.js +0 -84
  37. package/src/ycloud/core/config.js +0 -78
  38. package/src/ycloud/core/http.js +0 -133
  39. package/src/ycloud/core/token.js +0 -30
  40. package/src/ycloud/resources/.gitkeep +0 -1
  41. package/src/ycloud/resources/tool_catalog_full.json +0 -1
@@ -0,0 +1,203 @@
1
+ const os = require('node:os');
2
+ const path = require('node:path');
3
+ const fs = require('node:fs');
4
+
5
+ const SUPPORTED_AGENTS = [
6
+ 'codex',
7
+ 'claude-code',
8
+ 'cursor',
9
+ 'trae',
10
+ 'opencode',
11
+ 'github-copilot',
12
+ 'gemini-cli',
13
+ 'kimi-cli',
14
+ 'windsurf',
15
+ 'openclaw',
16
+ 'trae-cn',
17
+ 'qoder',
18
+ 'qwen-code',
19
+ ];
20
+
21
+ function defaultExists(targetPath) {
22
+ return fs.existsSync(targetPath);
23
+ }
24
+
25
+ function getConfigHome(homeDir, env) {
26
+ return (env && env.XDG_CONFIG_HOME) || path.join(homeDir, '.config');
27
+ }
28
+
29
+ function getCanonicalSkillsDir({
30
+ homeDir = os.homedir(),
31
+ cwd = process.cwd(),
32
+ scope = 'global',
33
+ } = {}) {
34
+ const baseDir = scope === 'project' ? cwd : homeDir;
35
+ return path.join(baseDir, '.yuanflow-skill', 'skills');
36
+ }
37
+
38
+ function buildAgentDefinitions({ homeDir = os.homedir(), env = process.env } = {}) {
39
+ const configHome = getConfigHome(homeDir, env);
40
+ const codexHome = (env.CODEX_HOME || '').trim() || path.join(homeDir, '.codex');
41
+ const claudeHome = (env.CLAUDE_CONFIG_DIR || '').trim() || path.join(homeDir, '.claude');
42
+
43
+ return {
44
+ codex: {
45
+ name: 'codex',
46
+ displayName: 'Codex',
47
+ projectSkillsDir: '.agents/skills',
48
+ globalSkillsDir: path.join(codexHome, 'skills'),
49
+ markers: [codexHome],
50
+ },
51
+ 'claude-code': {
52
+ name: 'claude-code',
53
+ displayName: 'Claude Code',
54
+ projectSkillsDir: '.claude/skills',
55
+ globalSkillsDir: path.join(claudeHome, 'skills'),
56
+ markers: [claudeHome],
57
+ },
58
+ cursor: {
59
+ name: 'cursor',
60
+ displayName: 'Cursor',
61
+ projectSkillsDir: '.agents/skills',
62
+ globalSkillsDir: path.join(homeDir, '.cursor', 'skills'),
63
+ markers: [path.join(homeDir, '.cursor')],
64
+ },
65
+ trae: {
66
+ name: 'trae',
67
+ displayName: 'Trae',
68
+ projectSkillsDir: '.trae/skills',
69
+ globalSkillsDir: path.join(homeDir, '.trae', 'skills'),
70
+ markers: [path.join(homeDir, '.trae')],
71
+ },
72
+ opencode: {
73
+ name: 'opencode',
74
+ displayName: 'OpenCode',
75
+ projectSkillsDir: '.agents/skills',
76
+ globalSkillsDir: path.join(configHome, 'opencode', 'skills'),
77
+ markers: [path.join(configHome, 'opencode')],
78
+ },
79
+ 'github-copilot': {
80
+ name: 'github-copilot',
81
+ displayName: 'GitHub Copilot',
82
+ projectSkillsDir: '.agents/skills',
83
+ globalSkillsDir: path.join(homeDir, '.copilot', 'skills'),
84
+ markers: [path.join(homeDir, '.copilot')],
85
+ },
86
+ 'gemini-cli': {
87
+ name: 'gemini-cli',
88
+ displayName: 'Gemini CLI',
89
+ projectSkillsDir: '.agents/skills',
90
+ globalSkillsDir: path.join(homeDir, '.gemini', 'skills'),
91
+ markers: [path.join(homeDir, '.gemini')],
92
+ },
93
+ 'kimi-cli': {
94
+ name: 'kimi-cli',
95
+ displayName: 'Kimi Code CLI',
96
+ projectSkillsDir: '.agents/skills',
97
+ globalSkillsDir: path.join(configHome, 'agents', 'skills'),
98
+ markers: [path.join(homeDir, '.kimi')],
99
+ },
100
+ windsurf: {
101
+ name: 'windsurf',
102
+ displayName: 'Windsurf',
103
+ projectSkillsDir: '.windsurf/skills',
104
+ globalSkillsDir: path.join(homeDir, '.codeium', 'windsurf', 'skills'),
105
+ markers: [path.join(homeDir, '.codeium', 'windsurf')],
106
+ },
107
+ openclaw: {
108
+ name: 'openclaw',
109
+ displayName: 'OpenClaw',
110
+ projectSkillsDir: 'skills',
111
+ globalSkillsDir: path.join(homeDir, '.openclaw', 'skills'),
112
+ markers: [
113
+ path.join(homeDir, '.openclaw'),
114
+ path.join(homeDir, '.clawdbot'),
115
+ path.join(homeDir, '.moltbot'),
116
+ ],
117
+ resolveGlobalSkillsDir(currentHomeDir, exists = defaultExists) {
118
+ if (exists(path.join(currentHomeDir, '.openclaw'))) {
119
+ return path.join(currentHomeDir, '.openclaw', 'skills');
120
+ }
121
+ if (exists(path.join(currentHomeDir, '.clawdbot'))) {
122
+ return path.join(currentHomeDir, '.clawdbot', 'skills');
123
+ }
124
+ if (exists(path.join(currentHomeDir, '.moltbot'))) {
125
+ return path.join(currentHomeDir, '.moltbot', 'skills');
126
+ }
127
+
128
+ return path.join(currentHomeDir, '.openclaw', 'skills');
129
+ },
130
+ },
131
+ 'trae-cn': {
132
+ name: 'trae-cn',
133
+ displayName: 'Trae CN',
134
+ projectSkillsDir: '.trae/skills',
135
+ globalSkillsDir: path.join(homeDir, '.trae-cn', 'skills'),
136
+ markers: [path.join(homeDir, '.trae-cn')],
137
+ },
138
+ qoder: {
139
+ name: 'qoder',
140
+ displayName: 'Qoder',
141
+ projectSkillsDir: '.qoder/skills',
142
+ globalSkillsDir: path.join(homeDir, '.qoder', 'skills'),
143
+ markers: [path.join(homeDir, '.qoder')],
144
+ },
145
+ 'qwen-code': {
146
+ name: 'qwen-code',
147
+ displayName: 'Qwen Code',
148
+ projectSkillsDir: '.qwen/skills',
149
+ globalSkillsDir: path.join(homeDir, '.qwen', 'skills'),
150
+ markers: [path.join(homeDir, '.qwen')],
151
+ },
152
+ };
153
+ }
154
+
155
+ function getSupportedAgents() {
156
+ return [...SUPPORTED_AGENTS];
157
+ }
158
+
159
+ function getAgentConfig(agentName, options = {}) {
160
+ const definitions = buildAgentDefinitions(options);
161
+ const exists = options.exists || defaultExists;
162
+ const agent = definitions[agentName];
163
+
164
+ if (!agent) {
165
+ throw new Error(`Unsupported agent: ${agentName}`);
166
+ }
167
+
168
+ if (typeof agent.resolveGlobalSkillsDir === 'function') {
169
+ return {
170
+ ...agent,
171
+ globalSkillsDir: agent.resolveGlobalSkillsDir(options.homeDir || os.homedir(), exists),
172
+ };
173
+ }
174
+
175
+ return agent;
176
+ }
177
+
178
+ function getTargetBaseDir(agent, { scope = 'global', cwd = process.cwd() } = {}) {
179
+ if (scope === 'project') {
180
+ return path.join(cwd, agent.projectSkillsDir);
181
+ }
182
+
183
+ return agent.globalSkillsDir;
184
+ }
185
+
186
+ function detectInstalledAgents(options = {}) {
187
+ const exists = options.exists || defaultExists;
188
+ const homeDir = options.homeDir || os.homedir();
189
+
190
+ return SUPPORTED_AGENTS.filter((agentName) => {
191
+ const agent = getAgentConfig(agentName, options);
192
+ return agent.markers.some((markerPath) => exists(markerPath.replace(/^~(?=$|[\\/])/, homeDir)));
193
+ });
194
+ }
195
+
196
+ module.exports = {
197
+ SUPPORTED_AGENTS,
198
+ getSupportedAgents,
199
+ getCanonicalSkillsDir,
200
+ getAgentConfig,
201
+ getTargetBaseDir,
202
+ detectInstalledAgents,
203
+ };
@@ -0,0 +1,79 @@
1
+ const fs = require('node:fs');
2
+ const path = require('node:path');
3
+
4
+ function parseFrontmatter(content) {
5
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
6
+
7
+ if (!match) {
8
+ return {};
9
+ }
10
+
11
+ const result = {};
12
+ const lines = match[1].split(/\r?\n/);
13
+
14
+ for (const line of lines) {
15
+ const separatorIndex = line.indexOf(':');
16
+ if (separatorIndex === -1) {
17
+ continue;
18
+ }
19
+
20
+ const key = line.slice(0, separatorIndex).trim();
21
+ const value = line
22
+ .slice(separatorIndex + 1)
23
+ .trim()
24
+ .replace(/^['"]|['"]$/g, '');
25
+
26
+ result[key] = value;
27
+ }
28
+
29
+ return result;
30
+ }
31
+
32
+ function readSkill(skillDir, isRootSkill = false) {
33
+ const skillPath = path.join(skillDir, 'SKILL.md');
34
+ if (!fs.existsSync(skillPath)) {
35
+ return null;
36
+ }
37
+
38
+ const content = fs.readFileSync(skillPath, 'utf8');
39
+ const frontmatter = parseFrontmatter(content);
40
+
41
+ if (!frontmatter.name || !frontmatter.description) {
42
+ throw new Error(`Invalid SKILL.md metadata: ${skillPath}`);
43
+ }
44
+
45
+ return {
46
+ name: frontmatter.name,
47
+ description: frontmatter.description,
48
+ sourceDir: skillDir,
49
+ skillFile: skillPath,
50
+ rootSkill: isRootSkill,
51
+ };
52
+ }
53
+
54
+ function discoverSkills(packageRoot) {
55
+ const skills = [];
56
+ const rootSkill = readSkill(packageRoot, true);
57
+
58
+ if (rootSkill) {
59
+ skills.push(rootSkill);
60
+ }
61
+
62
+ const entries = fs.readdirSync(packageRoot, { withFileTypes: true });
63
+ for (const entry of entries) {
64
+ if (!entry.isDirectory()) {
65
+ continue;
66
+ }
67
+
68
+ const nestedSkill = readSkill(path.join(packageRoot, entry.name), false);
69
+ if (nestedSkill) {
70
+ skills.push(nestedSkill);
71
+ }
72
+ }
73
+
74
+ return skills;
75
+ }
76
+
77
+ module.exports = {
78
+ discoverSkills,
79
+ };
@@ -0,0 +1,300 @@
1
+ const fs = require('node:fs');
2
+ const os = require('node:os');
3
+ const path = require('node:path');
4
+
5
+ const { getCanonicalSkillsDir, getTargetBaseDir } = require('./agents.cjs');
6
+
7
+ const EXCLUDED_NAMES = new Set([
8
+ '.git',
9
+ '.gitignore',
10
+ '__pycache__',
11
+ '.venv',
12
+ 'node_modules',
13
+ 'memory.md',
14
+ '.DS_Store',
15
+ ]);
16
+
17
+ function sanitizeName(name) {
18
+ return name
19
+ .toLowerCase()
20
+ .replace(/[^a-z0-9._-]+/g, '-')
21
+ .replace(/^-+|-+$/g, '');
22
+ }
23
+
24
+ function ensureDir(targetDir) {
25
+ fs.mkdirSync(targetDir, { recursive: true });
26
+ }
27
+
28
+ function removePath(targetPath) {
29
+ fs.rmSync(targetPath, { recursive: true, force: true });
30
+ }
31
+
32
+ function pathExists(targetPath) {
33
+ return fs.existsSync(targetPath);
34
+ }
35
+
36
+ function copyDirectory(sourceDir, targetDir) {
37
+ ensureDir(targetDir);
38
+
39
+ const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
40
+ for (const entry of entries) {
41
+ if (EXCLUDED_NAMES.has(entry.name)) {
42
+ continue;
43
+ }
44
+
45
+ const sourcePath = path.join(sourceDir, entry.name);
46
+ const targetPath = path.join(targetDir, entry.name);
47
+
48
+ if (entry.isDirectory()) {
49
+ copyDirectory(sourcePath, targetPath);
50
+ continue;
51
+ }
52
+
53
+ fs.copyFileSync(sourcePath, targetPath);
54
+ }
55
+ }
56
+
57
+ function copySkill(skill, targetDir) {
58
+ ensureDir(targetDir);
59
+
60
+ if (skill.rootSkill) {
61
+ fs.copyFileSync(skill.skillFile, path.join(targetDir, 'SKILL.md'));
62
+ return;
63
+ }
64
+
65
+ copyDirectory(skill.sourceDir, targetDir);
66
+ }
67
+
68
+ function getBundleSkill(skills) {
69
+ const rootSkill = skills.find((skill) => skill.rootSkill);
70
+ if (rootSkill) {
71
+ return rootSkill;
72
+ }
73
+
74
+ if (skills.length === 0) {
75
+ throw new Error('No skills were provided for installation.');
76
+ }
77
+
78
+ return skills[0];
79
+ }
80
+
81
+ function copySkillBundle(sourceDir, targetDir) {
82
+ copyDirectory(sourceDir, targetDir);
83
+ }
84
+
85
+ function getLegacySkillEntries({ canonicalDir, targetAgents, skills, scope = 'global', cwd = process.cwd() }) {
86
+ const bundleSkill = getBundleSkill(skills);
87
+ const bundleSlug = sanitizeName(bundleSkill.name);
88
+ const legacySkills = skills.filter((skill) => sanitizeName(skill.name) !== bundleSlug);
89
+ const entries = [];
90
+
91
+ for (const skill of legacySkills) {
92
+ const skillSlug = sanitizeName(skill.name);
93
+ const legacyCanonicalDir = path.join(canonicalDir, skillSlug);
94
+
95
+ for (const agent of targetAgents) {
96
+ const agentBaseDir = getTargetBaseDir(agent, { scope, cwd });
97
+ entries.push({
98
+ skillName: skill.name,
99
+ skillSlug,
100
+ canonicalDir: legacyCanonicalDir,
101
+ targetDir: path.join(agentBaseDir, skillSlug),
102
+ });
103
+ }
104
+ }
105
+
106
+ return entries;
107
+ }
108
+
109
+ function createSymlink(targetDir, linkPath) {
110
+ try {
111
+ ensureDir(path.dirname(linkPath));
112
+ removePath(linkPath);
113
+ fs.symlinkSync(targetDir, linkPath, process.platform === 'win32' ? 'junction' : 'dir');
114
+ return true;
115
+ } catch {
116
+ return false;
117
+ }
118
+ }
119
+
120
+ function getInstallPlan({ canonicalDir, targetAgents, skills, scope = 'global', cwd = process.cwd() }) {
121
+ const bundleSkill = getBundleSkill(skills);
122
+ const plan = [];
123
+ const skillSlug = sanitizeName(bundleSkill.name);
124
+ const skillCanonicalDir = path.join(canonicalDir, skillSlug);
125
+
126
+ for (const agent of targetAgents) {
127
+ const agentBaseDir = getTargetBaseDir(agent, { scope, cwd });
128
+ plan.push({
129
+ skillName: bundleSkill.name,
130
+ skillSlug,
131
+ sourceDir: bundleSkill.sourceDir,
132
+ canonicalDir: skillCanonicalDir,
133
+ agentName: agent.name,
134
+ targetDir: path.join(agentBaseDir, skillSlug),
135
+ });
136
+ }
137
+
138
+ return plan;
139
+ }
140
+
141
+ function getUninstallPlan({
142
+ canonicalDir,
143
+ targetAgents,
144
+ bundleName,
145
+ scope = 'global',
146
+ cwd = process.cwd(),
147
+ }) {
148
+ const plan = [];
149
+ const skillSlug = sanitizeName(bundleName);
150
+
151
+ for (const agent of targetAgents) {
152
+ const agentBaseDir = getTargetBaseDir(agent, { scope, cwd });
153
+ plan.push({
154
+ skillName: bundleName,
155
+ skillSlug,
156
+ canonicalDir: path.join(canonicalDir, skillSlug),
157
+ agentName: agent.name,
158
+ targetDir: path.join(agentBaseDir, skillSlug),
159
+ });
160
+ }
161
+
162
+ return plan;
163
+ }
164
+
165
+ function installSkills({
166
+ packageRoot,
167
+ skills,
168
+ targetAgents,
169
+ mode = 'symlink',
170
+ scope = 'global',
171
+ cwd = process.cwd(),
172
+ dryRun = false,
173
+ homeDir = os.homedir(),
174
+ }) {
175
+ const canonicalDir = getCanonicalSkillsDir({
176
+ homeDir,
177
+ cwd,
178
+ scope,
179
+ });
180
+ ensureDir(canonicalDir);
181
+
182
+ const plan = getInstallPlan({
183
+ canonicalDir,
184
+ targetAgents,
185
+ skills,
186
+ scope,
187
+ cwd,
188
+ });
189
+ const legacyEntries = getLegacySkillEntries({
190
+ canonicalDir,
191
+ targetAgents,
192
+ skills,
193
+ scope,
194
+ cwd,
195
+ });
196
+
197
+ const results = [];
198
+ const bundleSkill = getBundleSkill(skills);
199
+
200
+ if (!dryRun) {
201
+ for (const legacyEntry of legacyEntries) {
202
+ removePath(legacyEntry.canonicalDir);
203
+ removePath(legacyEntry.targetDir);
204
+ }
205
+ }
206
+
207
+ for (const entry of plan) {
208
+ const overwriteCanonical = pathExists(entry.canonicalDir);
209
+ const overwriteTarget = pathExists(entry.targetDir);
210
+
211
+ if (!dryRun) {
212
+ removePath(entry.canonicalDir);
213
+ ensureDir(entry.canonicalDir);
214
+ copySkillBundle(bundleSkill.sourceDir, entry.canonicalDir);
215
+ }
216
+
217
+ let installedBy = mode === 'symlink' ? 'symlink' : 'copy';
218
+ if (!dryRun) {
219
+ if (mode === 'symlink') {
220
+ const linked = createSymlink(entry.canonicalDir, entry.targetDir);
221
+ if (!linked) {
222
+ installedBy = 'copy';
223
+ removePath(entry.targetDir);
224
+ copyDirectory(entry.canonicalDir, entry.targetDir);
225
+ }
226
+ } else {
227
+ removePath(entry.targetDir);
228
+ copyDirectory(entry.canonicalDir, entry.targetDir);
229
+ }
230
+ }
231
+
232
+ results.push({
233
+ ...entry,
234
+ installedBy,
235
+ overwriteCanonical,
236
+ overwriteTarget,
237
+ scope,
238
+ packageRoot,
239
+ dryRun,
240
+ });
241
+ }
242
+
243
+ return results;
244
+ }
245
+
246
+ function uninstallSkills({
247
+ skillNames,
248
+ targetAgents,
249
+ scope = 'global',
250
+ cwd = process.cwd(),
251
+ dryRun = false,
252
+ homeDir = os.homedir(),
253
+ bundleName = 'yuanflow-skill',
254
+ }) {
255
+ const canonicalDir = getCanonicalSkillsDir({
256
+ homeDir,
257
+ cwd,
258
+ scope,
259
+ });
260
+
261
+ const plan = getUninstallPlan({
262
+ canonicalDir,
263
+ targetAgents,
264
+ bundleName,
265
+ scope,
266
+ cwd,
267
+ });
268
+
269
+ const results = [];
270
+
271
+ for (const entry of plan) {
272
+ const targetExists = pathExists(entry.targetDir);
273
+ const canonicalExists = pathExists(entry.canonicalDir);
274
+
275
+ if (!dryRun) {
276
+ removePath(entry.targetDir);
277
+ removePath(entry.canonicalDir);
278
+ }
279
+
280
+ results.push({
281
+ ...entry,
282
+ removedTarget: targetExists,
283
+ removedCanonical: canonicalExists,
284
+ dryRun,
285
+ scope,
286
+ });
287
+ }
288
+
289
+ return results;
290
+ }
291
+
292
+ module.exports = {
293
+ sanitizeName,
294
+ getBundleSkill,
295
+ getLegacySkillEntries,
296
+ getInstallPlan,
297
+ getUninstallPlan,
298
+ installSkills,
299
+ uninstallSkills,
300
+ };
@@ -0,0 +1,53 @@
1
+ const fs = require('node:fs');
2
+ const path = require('node:path');
3
+
4
+ function isSemver(version) {
5
+ return /^\d+\.\d+\.\d+(-[0-9A-Za-z-.]+)?$/.test(version || '');
6
+ }
7
+
8
+ function buildReleaseChecklist({
9
+ packageJson,
10
+ packageRoot,
11
+ repoConfig,
12
+ fileExists = (targetPath) => fs.existsSync(targetPath),
13
+ }) {
14
+ const items = [
15
+ {
16
+ label: 'package.json name',
17
+ ok: Boolean(packageJson.name && String(packageJson.name).trim()),
18
+ },
19
+ {
20
+ label: 'package.json version',
21
+ ok: isSemver(packageJson.version),
22
+ },
23
+ {
24
+ label: 'README.md exists',
25
+ ok: fileExists(path.join(packageRoot, 'README.md')),
26
+ },
27
+ {
28
+ label: 'skill repo source configured',
29
+ ok: Boolean(repoConfig && repoConfig.owner && repoConfig.repo && repoConfig.ref),
30
+ },
31
+ {
32
+ label: 'test script exists',
33
+ ok: Boolean(packageJson.scripts && packageJson.scripts.test),
34
+ },
35
+ {
36
+ label: 'pack:check script exists',
37
+ ok: Boolean(packageJson.scripts && packageJson.scripts['pack:check']),
38
+ },
39
+ {
40
+ label: 'postinstall script exists',
41
+ ok: Boolean(packageJson.scripts && packageJson.scripts.postinstall),
42
+ },
43
+ ];
44
+
45
+ return {
46
+ ok: items.every((item) => item.ok),
47
+ items,
48
+ };
49
+ }
50
+
51
+ module.exports = {
52
+ buildReleaseChecklist,
53
+ };