helloloop 0.2.0 → 0.2.1
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/hosts/claude/marketplace/.claude-plugin/marketplace.json +3 -1
- package/hosts/claude/marketplace/plugins/helloloop/.claude-plugin/plugin.json +1 -1
- package/hosts/gemini/extension/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/src/cli_support.mjs +24 -4
- package/src/install.mjs +65 -7
package/README.md
CHANGED
|
@@ -112,7 +112,7 @@ pwsh -NoLogo -NoProfile -File .\scripts\install-home-plugin.ps1 -CodexHome <CODE
|
|
|
112
112
|
安装完成后:
|
|
113
113
|
|
|
114
114
|
- Codex 会写入 `<CODEX_HOME>/plugins/helloloop`
|
|
115
|
-
- Claude 会写入 `<CLAUDE_HOME>/marketplaces/helloloop-local
|
|
115
|
+
- Claude 会写入 `<CLAUDE_HOME>/plugins/marketplaces/helloloop-local`,并生成 `<CLAUDE_HOME>/plugins/cache/helloloop-local/helloloop/<VERSION>`
|
|
116
116
|
- Gemini 会写入 `<GEMINI_HOME>/extensions/helloloop`
|
|
117
117
|
|
|
118
118
|
如果你想在安装后做一次全宿主环境检查:
|
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
"owner": {
|
|
4
4
|
"name": "HelloLoop"
|
|
5
5
|
},
|
|
6
|
+
"metadata": {
|
|
7
|
+
"description": "HelloLoop 的 Claude Code 本地 marketplace,提供基于开发文档的分析、确认与自动接续开发工作流。"
|
|
8
|
+
},
|
|
6
9
|
"plugins": [
|
|
7
10
|
{
|
|
8
11
|
"name": "helloloop",
|
|
9
|
-
"displayName": "HelloLoop",
|
|
10
12
|
"description": "基于开发文档分析当前进度、展示确认单,并在确认后继续接续开发的 Claude Code 原生插件。",
|
|
11
13
|
"source": "./plugins/helloloop"
|
|
12
14
|
}
|
package/package.json
CHANGED
package/src/cli_support.mjs
CHANGED
|
@@ -151,16 +151,36 @@ function collectClaudeDoctorChecks(context, options = {}) {
|
|
|
151
151
|
|
|
152
152
|
if (options.claudeHome) {
|
|
153
153
|
const settingsFile = path.join(options.claudeHome, "settings.json");
|
|
154
|
+
const knownMarketplacesFile = path.join(options.claudeHome, "plugins", "known_marketplaces.json");
|
|
155
|
+
const installedPluginsFile = path.join(options.claudeHome, "plugins", "installed_plugins.json");
|
|
154
156
|
const settings = fileExists(settingsFile) ? readJson(settingsFile) : {};
|
|
157
|
+
const installedPlugins = fileExists(installedPluginsFile) ? readJson(installedPluginsFile) : {};
|
|
158
|
+
const installs = Array.isArray(installedPlugins?.plugins?.["helloloop@helloloop-local"])
|
|
159
|
+
? installedPlugins.plugins["helloloop@helloloop-local"]
|
|
160
|
+
: [];
|
|
161
|
+
const installedPluginRoot = installs[0]?.installPath
|
|
162
|
+
? String(installs[0].installPath)
|
|
163
|
+
: path.join(options.claudeHome, "plugins", "cache", "helloloop-local", "helloloop");
|
|
164
|
+
|
|
155
165
|
checks.push({
|
|
156
166
|
name: "claude installed marketplace",
|
|
157
|
-
ok: fileExists(path.join(options.claudeHome, "marketplaces", "helloloop-local", ".claude-plugin", "marketplace.json")),
|
|
158
|
-
detail: path.join(options.claudeHome, "marketplaces", "helloloop-local", ".claude-plugin", "marketplace.json"),
|
|
167
|
+
ok: fileExists(path.join(options.claudeHome, "plugins", "marketplaces", "helloloop-local", ".claude-plugin", "marketplace.json")),
|
|
168
|
+
detail: path.join(options.claudeHome, "plugins", "marketplaces", "helloloop-local", ".claude-plugin", "marketplace.json"),
|
|
169
|
+
});
|
|
170
|
+
checks.push({
|
|
171
|
+
name: "claude marketplace registry",
|
|
172
|
+
ok: fileExists(knownMarketplacesFile),
|
|
173
|
+
detail: knownMarketplacesFile,
|
|
174
|
+
});
|
|
175
|
+
checks.push({
|
|
176
|
+
name: "claude installed plugin index",
|
|
177
|
+
ok: fileExists(installedPluginsFile),
|
|
178
|
+
detail: installedPluginsFile,
|
|
159
179
|
});
|
|
160
180
|
checks.push({
|
|
161
181
|
name: "claude installed plugin",
|
|
162
|
-
ok: fileExists(path.join(
|
|
163
|
-
detail: path.join(
|
|
182
|
+
ok: fileExists(path.join(installedPluginRoot, ".claude-plugin", "plugin.json")),
|
|
183
|
+
detail: path.join(installedPluginRoot, ".claude-plugin", "plugin.json"),
|
|
164
184
|
});
|
|
165
185
|
checks.push({
|
|
166
186
|
name: "claude settings enabled",
|
package/src/install.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import os from "node:os";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
|
|
6
|
-
import { ensureDir, fileExists, readJson, writeJson } from "./common.mjs";
|
|
6
|
+
import { ensureDir, fileExists, nowIso, readJson, writeJson } from "./common.mjs";
|
|
7
7
|
|
|
8
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
9
|
const __dirname = path.dirname(__filename);
|
|
@@ -28,6 +28,8 @@ const codexBundleEntries = runtimeBundleEntries.filter((entry) => ![
|
|
|
28
28
|
].includes(entry));
|
|
29
29
|
|
|
30
30
|
const supportedHosts = ["codex", "claude", "gemini"];
|
|
31
|
+
const CLAUDE_MARKETPLACE_NAME = "helloloop-local";
|
|
32
|
+
const CLAUDE_PLUGIN_KEY = "helloloop@helloloop-local";
|
|
31
33
|
|
|
32
34
|
function resolveHomeDir(homeDir, defaultDirName) {
|
|
33
35
|
return path.resolve(homeDir || path.join(os.homedir(), defaultDirName));
|
|
@@ -118,15 +120,57 @@ function updateClaudeSettings(settingsFile, marketplaceRoot) {
|
|
|
118
120
|
settings.extraKnownMarketplaces = settings.extraKnownMarketplaces || {};
|
|
119
121
|
settings.enabledPlugins = settings.enabledPlugins || {};
|
|
120
122
|
|
|
121
|
-
settings.extraKnownMarketplaces[
|
|
122
|
-
source:
|
|
123
|
-
|
|
123
|
+
settings.extraKnownMarketplaces[CLAUDE_MARKETPLACE_NAME] = {
|
|
124
|
+
source: {
|
|
125
|
+
source: "directory",
|
|
126
|
+
path: marketplaceRoot,
|
|
127
|
+
},
|
|
124
128
|
};
|
|
125
|
-
settings.enabledPlugins[
|
|
129
|
+
settings.enabledPlugins[CLAUDE_PLUGIN_KEY] = true;
|
|
126
130
|
|
|
127
131
|
writeJson(settingsFile, settings);
|
|
128
132
|
}
|
|
129
133
|
|
|
134
|
+
function updateClaudeKnownMarketplaces(knownMarketplacesFile, marketplaceRoot, updatedAt) {
|
|
135
|
+
const knownMarketplaces = fileExists(knownMarketplacesFile)
|
|
136
|
+
? readJson(knownMarketplacesFile)
|
|
137
|
+
: {};
|
|
138
|
+
|
|
139
|
+
knownMarketplaces[CLAUDE_MARKETPLACE_NAME] = {
|
|
140
|
+
source: {
|
|
141
|
+
source: "directory",
|
|
142
|
+
path: marketplaceRoot,
|
|
143
|
+
},
|
|
144
|
+
installLocation: marketplaceRoot,
|
|
145
|
+
lastUpdated: updatedAt,
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
writeJson(knownMarketplacesFile, knownMarketplaces);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function updateClaudeInstalledPlugins(installedPluginsFile, pluginRoot, pluginVersion, updatedAt) {
|
|
152
|
+
const installedPlugins = fileExists(installedPluginsFile)
|
|
153
|
+
? readJson(installedPluginsFile)
|
|
154
|
+
: {
|
|
155
|
+
version: 2,
|
|
156
|
+
plugins: {},
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
installedPlugins.version = 2;
|
|
160
|
+
installedPlugins.plugins = installedPlugins.plugins || {};
|
|
161
|
+
installedPlugins.plugins[CLAUDE_PLUGIN_KEY] = [
|
|
162
|
+
{
|
|
163
|
+
scope: "user",
|
|
164
|
+
installPath: pluginRoot,
|
|
165
|
+
version: pluginVersion,
|
|
166
|
+
installedAt: updatedAt,
|
|
167
|
+
lastUpdated: updatedAt,
|
|
168
|
+
},
|
|
169
|
+
];
|
|
170
|
+
|
|
171
|
+
writeJson(installedPluginsFile, installedPlugins);
|
|
172
|
+
}
|
|
173
|
+
|
|
130
174
|
function installCodexHost(bundleRoot, options) {
|
|
131
175
|
const resolvedCodexHome = resolveHomeDir(options.codexHome, ".codex");
|
|
132
176
|
const targetPluginsRoot = path.join(resolvedCodexHome, "plugins");
|
|
@@ -165,7 +209,13 @@ function installClaudeHost(bundleRoot, options) {
|
|
|
165
209
|
const resolvedClaudeHome = resolveHomeDir(options.claudeHome, ".claude");
|
|
166
210
|
const sourceMarketplaceRoot = path.join(bundleRoot, "hosts", "claude", "marketplace");
|
|
167
211
|
const sourceManifest = path.join(bundleRoot, ".claude-plugin", "plugin.json");
|
|
168
|
-
const
|
|
212
|
+
const pluginVersion = readJson(sourceManifest).version || readJson(path.join(bundleRoot, "package.json")).version;
|
|
213
|
+
const targetPluginsRoot = path.join(resolvedClaudeHome, "plugins");
|
|
214
|
+
const targetMarketplaceRoot = path.join(targetPluginsRoot, "marketplaces", CLAUDE_MARKETPLACE_NAME);
|
|
215
|
+
const targetCachePluginsRoot = path.join(targetPluginsRoot, "cache", CLAUDE_MARKETPLACE_NAME, "helloloop");
|
|
216
|
+
const targetInstalledPluginRoot = path.join(targetCachePluginsRoot, pluginVersion);
|
|
217
|
+
const knownMarketplacesFile = path.join(targetPluginsRoot, "known_marketplaces.json");
|
|
218
|
+
const installedPluginsFile = path.join(targetPluginsRoot, "installed_plugins.json");
|
|
169
219
|
const settingsFile = path.join(resolvedClaudeHome, "settings.json");
|
|
170
220
|
|
|
171
221
|
if (!fileExists(sourceManifest)) {
|
|
@@ -176,16 +226,24 @@ function installClaudeHost(bundleRoot, options) {
|
|
|
176
226
|
}
|
|
177
227
|
|
|
178
228
|
assertPathInside(resolvedClaudeHome, targetMarketplaceRoot, "Claude marketplace 目录");
|
|
229
|
+
assertPathInside(resolvedClaudeHome, targetInstalledPluginRoot, "Claude 插件缓存目录");
|
|
179
230
|
removeTargetIfNeeded(targetMarketplaceRoot, options.force);
|
|
231
|
+
removeTargetIfNeeded(targetCachePluginsRoot, options.force);
|
|
180
232
|
|
|
181
233
|
ensureDir(path.dirname(targetMarketplaceRoot));
|
|
234
|
+
ensureDir(path.dirname(targetInstalledPluginRoot));
|
|
182
235
|
copyDirectory(sourceMarketplaceRoot, targetMarketplaceRoot);
|
|
236
|
+
copyDirectory(path.join(sourceMarketplaceRoot, "plugins", "helloloop"), targetInstalledPluginRoot);
|
|
237
|
+
ensureDir(targetPluginsRoot);
|
|
238
|
+
const updatedAt = nowIso();
|
|
183
239
|
updateClaudeSettings(settingsFile, targetMarketplaceRoot);
|
|
240
|
+
updateClaudeKnownMarketplaces(knownMarketplacesFile, targetMarketplaceRoot, updatedAt);
|
|
241
|
+
updateClaudeInstalledPlugins(installedPluginsFile, targetInstalledPluginRoot, pluginVersion, updatedAt);
|
|
184
242
|
|
|
185
243
|
return {
|
|
186
244
|
host: "claude",
|
|
187
245
|
displayName: "Claude",
|
|
188
|
-
targetRoot:
|
|
246
|
+
targetRoot: targetInstalledPluginRoot,
|
|
189
247
|
marketplaceFile: path.join(targetMarketplaceRoot, ".claude-plugin", "marketplace.json"),
|
|
190
248
|
settingsFile,
|
|
191
249
|
};
|