helloloop 0.7.0 → 0.7.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 +3 -3
- package/README.md +1 -0
- 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/skills/helloloop/SKILL.md +3 -2
- package/src/discovery_paths.mjs +65 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "helloloop",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "面向 Codex CLI、Claude Code、Gemini CLI 的多宿主开发工作流插件,Codex 路径为首发与参考实现。",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "HelloLoop"
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"interface": {
|
|
18
18
|
"displayName": "HelloLoop",
|
|
19
19
|
"shortDescription": "显式调用后按 backlog 持续开发与验证",
|
|
20
|
-
"longDescription": "HelloLoop 把基于 backlog 的持续开发能力封装为官方 Codex 插件 bundle。只有在用户显式调用 helloloop skill
|
|
20
|
+
"longDescription": "HelloLoop 把基于 backlog 的持续开发能力封装为官方 Codex 插件 bundle。只有在用户显式调用 helloloop skill 时才应介入;进入后默认优先走 npx helloloop 主 CLI 流程:支持 npx helloloop、npx helloloop <PATH>、npx helloloop codex|claude|gemini ...,先分析、再展示确认单、确认后自动接续推进;运行中按无人值守自动恢复持续推进主线。",
|
|
21
21
|
"developerName": "HelloLoop",
|
|
22
22
|
"category": "Coding",
|
|
23
23
|
"capabilities": [
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"Write"
|
|
26
26
|
],
|
|
27
27
|
"defaultPrompt": [
|
|
28
|
-
"只有当用户显式调用 $helloloop / #helloloop / helloloop:helloloop
|
|
28
|
+
"只有当用户显式调用 $helloloop / #helloloop / helloloop:helloloop 时,才进入 npx helloloop 主 CLI 流程;普通 Codex 会话不要自动接管。仅仅提到 helloloop 仓库、插件名、README、代码、测试、issue、release、npm 包名,都不算调用。进入后优先执行 npx helloloop 或 npx helloloop <PATH>;如果用户明确指定执行引擎,也允许使用 npx helloloop codex|claude|gemini ...。启动前确认一次,启动后按无人值守自动恢复与主线续跑执行,不要在对话里手工模拟流程。"
|
|
29
29
|
],
|
|
30
30
|
"brandColor": "#0F766E"
|
|
31
31
|
}
|
package/README.md
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
这意味着:
|
|
20
20
|
|
|
21
21
|
- 三宿主都只在用户显式调用 `helloloop` 时介入;普通会话不会被 HelloLoop 自动接管
|
|
22
|
+
- 在 `Codex` 中,只有显式输入 `$helloloop` / `#helloloop` / `helloloop:helloloop` 才算调用;仅仅提到 `helloloop` 仓库、README、代码、测试、release、npm 包名,都不算调用
|
|
22
23
|
- 无论在终端还是在 `Codex` / `Claude` / `Gemini` 宿主内,只要用户未明确指定引擎,`HelloLoop` 都会先询问本轮执行引擎
|
|
23
24
|
- 当前宿主、项目历史、用户历史只作为推荐依据,不会自动替你选中引擎
|
|
24
25
|
- 如果你已经显式指定,或已经在首轮确认中明确选定了引擎,本轮就固定按该引擎执行
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: helloloop
|
|
3
|
-
description: 仅当用户显式调用 `$helloloop` / `#helloloop` / `helloloop:helloloop
|
|
3
|
+
description: 仅当用户显式调用 `$helloloop` / `#helloloop` / `helloloop:helloloop` 时使用。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# HelloLoop
|
|
@@ -9,7 +9,8 @@ description: 仅当用户显式调用 `$helloloop` / `#helloloop` / `helloloop:h
|
|
|
9
9
|
|
|
10
10
|
## 强制入口规则
|
|
11
11
|
|
|
12
|
-
-
|
|
12
|
+
- 只有用户显式调用 `$helloloop` / `#helloloop` / `helloloop:helloloop` 时,才允许进入 HelloLoop;未显式调用时,不允许接管普通 Codex 会话。
|
|
13
|
+
- 仅仅提到 `helloloop` 仓库、插件名、README、代码、测试、issue、release、npm 包名,都不算调用 HelloLoop。
|
|
13
14
|
- 用户显式调用 `$helloloop` / `#helloloop` / `helloloop:helloloop` 时,默认必须优先执行 `npx helloloop` 或 `npx helloloop <PATH>`;如果用户又明确指定了执行引擎,也允许使用 `npx helloloop codex|claude|gemini ...`。
|
|
14
15
|
- 用户没有明确指定执行引擎时,不允许由 skill 自行补成 `codex` / `claude` / `gemini`;必须让 `HelloLoop` 先完成引擎确认。
|
|
15
16
|
- 不允许在对话里手工模拟 `HelloLoop` 的分析、确认单、backlog 编排和自动续跑流程来代替 CLI。
|
package/src/discovery_paths.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
|
|
4
5
|
import { fileExists } from "./common.mjs";
|
|
@@ -59,6 +60,26 @@ const IGNORED_PROJECT_SEGMENTS = new Set([
|
|
|
59
60
|
"venv",
|
|
60
61
|
]);
|
|
61
62
|
|
|
63
|
+
const PREFERRED_ANCESTOR_SEGMENTS = new Set([
|
|
64
|
+
".cache",
|
|
65
|
+
".next",
|
|
66
|
+
".nuxt",
|
|
67
|
+
".pnpm",
|
|
68
|
+
".turbo",
|
|
69
|
+
".venv",
|
|
70
|
+
".yarn",
|
|
71
|
+
"__pycache__",
|
|
72
|
+
"build",
|
|
73
|
+
"cache",
|
|
74
|
+
"coverage",
|
|
75
|
+
"dist",
|
|
76
|
+
"node_modules",
|
|
77
|
+
"out",
|
|
78
|
+
"target",
|
|
79
|
+
"vendor",
|
|
80
|
+
"venv",
|
|
81
|
+
]);
|
|
82
|
+
|
|
62
83
|
function listImmediateDirectories(directoryPath) {
|
|
63
84
|
if (!pathExists(directoryPath) || !fs.statSync(directoryPath).isDirectory()) {
|
|
64
85
|
return [];
|
|
@@ -99,13 +120,52 @@ function hasIgnoredProjectBasename(targetPath) {
|
|
|
99
120
|
return IGNORED_PROJECT_SEGMENTS.has(path.basename(targetPath).toLowerCase());
|
|
100
121
|
}
|
|
101
122
|
|
|
123
|
+
function canonicalPath(targetPath) {
|
|
124
|
+
if (!targetPath) {
|
|
125
|
+
return "";
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const resolved = fs.realpathSync.native
|
|
130
|
+
? fs.realpathSync.native(targetPath)
|
|
131
|
+
: fs.realpathSync(targetPath);
|
|
132
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
133
|
+
} catch {
|
|
134
|
+
const resolved = path.resolve(targetPath);
|
|
135
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function isImplicitHomeDirectory(targetPath) {
|
|
140
|
+
return Boolean(targetPath) && canonicalPath(targetPath) === canonicalPath(os.homedir());
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function isUserProfileLikeDirectory(targetPath) {
|
|
144
|
+
if (!targetPath) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const resolved = path.resolve(targetPath);
|
|
149
|
+
const normalized = resolved.replaceAll("\\", "/");
|
|
150
|
+
if (/^[A-Za-z]:\/Users\/[^/]+$/i.test(normalized)) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
if (/^\/Users\/[^/]+$/.test(normalized)) {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
if (/^\/home\/[^/]+$/.test(normalized)) {
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
|
|
102
162
|
function choosePreferredCandidate(candidates, directory) {
|
|
103
163
|
return candidates.find((candidate) => {
|
|
104
164
|
const relativeToLeaf = path.relative(candidate, directory);
|
|
105
165
|
return relativeToLeaf
|
|
106
166
|
.split(/[\\/]+/)
|
|
107
167
|
.filter(Boolean)
|
|
108
|
-
.some((segment) =>
|
|
168
|
+
.some((segment) => PREFERRED_ANCESTOR_SEGMENTS.has(segment.toLowerCase()));
|
|
109
169
|
})
|
|
110
170
|
|| candidates.find((candidate) => !hasIgnoredProjectBasename(candidate))
|
|
111
171
|
|| candidates[0]
|
|
@@ -233,6 +293,10 @@ export function findPreferredRepoRootFromPath(startPath) {
|
|
|
233
293
|
return current;
|
|
234
294
|
}
|
|
235
295
|
|
|
296
|
+
if (isImplicitHomeDirectory(current) || isUserProfileLikeDirectory(current)) {
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
|
|
236
300
|
if (looksLikeStrongProjectRoot(current)) {
|
|
237
301
|
strongCandidates.push(current);
|
|
238
302
|
continue;
|