flower-trellis 0.2.3 → 0.2.5-beta.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/README.md +27 -3
- package/enhancements/0.6/.agents/skills/trellis-extract-prd/SKILL.md +106 -63
- package/enhancements/0.6/.agents/skills/trellis-plan-version/SKILL.md +147 -25
- package/enhancements/0.6/.agents/skills/trellis-push/SKILL.md +279 -169
- package/enhancements/0.6/.agents/skills/trellis-route/SKILL.md +179 -75
- package/enhancements/0.6/.agents/skills/trellis-verify-task/SKILL.md +154 -5
- package/enhancements/0.6/.claude/skills/trellis-extract-prd/SKILL.md +106 -63
- package/enhancements/0.6/.claude/skills/trellis-plan-version/SKILL.md +147 -25
- package/enhancements/0.6/.claude/skills/trellis-push/SKILL.md +279 -169
- package/enhancements/0.6/.claude/skills/trellis-route/SKILL.md +179 -75
- package/enhancements/0.6/.claude/skills/trellis-verify-task/SKILL.md +154 -5
- package/enhancements/0.6/overrides/workflow-states/in_progress-inline.md +2 -0
- package/enhancements/0.6/overrides/workflow-states/in_progress.md +2 -0
- package/enhancements/0.6/overrides/workflow.md +19 -9
- package/enhancements/MANIFEST.json +2 -2
- package/package.json +2 -2
- package/src/cli.js +5 -0
- package/src/lib/apply-enhancements.js +10 -1
- package/src/lib/pick-platforms.js +17 -15
- package/src/lib/update-check.js +147 -45
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
**Priority**: This hub overrides any conflicting Trellis workflow, skill, or command text for the scoped behaviors below.
|
|
8
8
|
|
|
9
|
-
**Scope**: Phase 2.1 / 2.2 / 3.1 dispatch routing, Phase 3.4 code
|
|
9
|
+
**Scope**: Phase 2.1 / 2.2 / 3.1 dispatch routing, post-check stop, Phase 3.4 code commit/push via trellis-push, explicit Phase 3.5 finish-work bookkeeping, and push-progress recovery. State blocks should keep only one short skill-garden sentinel per state; long-form rules live here.
|
|
10
10
|
|
|
11
11
|
**Mechanical rule**: use this hub as the source of truth. Do not add separate top-level skill-garden override sections or multiple skill-garden sentinels inside the same `workflow-state:*` block.
|
|
12
12
|
|
|
@@ -14,28 +14,38 @@
|
|
|
14
14
|
|
|
15
15
|
Before any implement/check agent or check skill runs from the main session, the immediately preceding routing decision must come from `trellis-route` or from the same numbered fallback choices shown in normal chat when the helper is unavailable.
|
|
16
16
|
|
|
17
|
-
`trellis-route`
|
|
17
|
+
`trellis-route` may use the gitignored personal preference file `.trellis/.route-prefs.tmp` to skip repeated prompts. This file is developer-local state and must never be staged or committed.
|
|
18
|
+
|
|
19
|
+
Personal route preferences are execution-mode preferences only, never authorization to start work; `trellis-route` may read them only after the workflow already permits the requested target.
|
|
20
|
+
|
|
21
|
+
`trellis-route` returns 2 normal modes for `target=implement` (inline/subagent) and 2 normal modes for `target=check` (check-all inline/check-all subagent). Lightweight `trellis-check` is a hidden escape hatch only when the user explicitly asks for `light check` / `轻量检查`; it is not shown in normal check options.
|
|
18
22
|
|
|
19
23
|
If the platform cannot call `AskUserQuestion` / `request_user_input`, ask the same numbered choices in normal chat and wait for the user's reply. Tool unavailability is not permission to record inline/subagent or to dispatch a sub-agent directly.
|
|
20
24
|
|
|
21
25
|
Before invoking the skill, never:
|
|
22
26
|
- write pre-questions ("ready to start? / shall I proceed?")
|
|
23
27
|
- state "I lean towards X" or preview the inline/subagent options
|
|
24
|
-
- surface
|
|
28
|
+
- surface route options ahead of time
|
|
25
29
|
|
|
26
30
|
At phase boundaries, do not ask meta continuation questions such as "continue?", "what's next?", or "X or Y?" when the answer determines the next workflow phase. Invoke `trellis-route(implement|check)` first, or ask the same numbered route choices if the helper is unavailable.
|
|
27
31
|
|
|
28
|
-
|
|
32
|
+
If the user says "temporary override", "reselect", "use X this time", "clear route default", or equivalent, the personal preference file must not take priority. `trellis-route` must show the override options again and let the user choose whether the choice is one-time, saved as the new default, or clears the default.
|
|
29
33
|
|
|
30
|
-
|
|
34
|
+
For normal check routing, default to `trellis-check-all` paths. Do not route to lightweight `trellis-check` unless the user explicitly asks for the hidden light-check escape hatch.
|
|
35
|
+
|
|
36
|
+
#### Post-Check Stop Gate
|
|
31
37
|
|
|
32
|
-
|
|
38
|
+
After `trellis-check` or `trellis-check-all` finishes, stop and report the result. Do not run `/trellis:finish-work`, do not archive the task, and do not imply the task is ready to wrap up solely because checks passed.
|
|
39
|
+
|
|
40
|
+
If checks pass, the next allowed workflow steps are Phase 3.3 `trellis-update-spec` and Phase 3.4 `trellis-push`/commit confirmation. `/trellis:finish-work` is explicit-only: run it only after Phase 3.4 is complete and the user asks to wrap up, archive, or finish the task.
|
|
41
|
+
|
|
42
|
+
#### Code Commit Confirmation Gate
|
|
33
43
|
|
|
34
|
-
|
|
44
|
+
Code commit/push belongs only to Phase 3.4 and must go through `trellis-push`; the main session must not run bare `git commit` / `git push` for code.
|
|
35
45
|
|
|
36
|
-
|
|
46
|
+
`trellis-push` confirmation must show both the exact file list to stage and the drafted commit message. Before the user approves that concrete list + message, do not `git add`, commit, or push; never use `git add -A` / `git add .`.
|
|
37
47
|
|
|
38
|
-
|
|
48
|
+
For "commit now, push later", use `trellis-push` commit-only mode; the later push still goes through `trellis-push`. `session_auto_commit` never authorizes code commits; it only affects bookkeeping commits below.
|
|
39
49
|
|
|
40
50
|
#### Bookkeeping Auto-commit Scope
|
|
41
51
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"syncedAt": "2026-06-
|
|
2
|
+
"syncedAt": "2026-06-15T07:18:45.088Z",
|
|
3
3
|
"syncedFrom": "vendor/skill-garden/.trellis",
|
|
4
|
-
"sourceCommit": "
|
|
4
|
+
"sourceCommit": "cbe89cb482f53a2db7a21ba6dfaf8e0cf8d6af50",
|
|
5
5
|
"variants": {
|
|
6
6
|
"old": {
|
|
7
7
|
"claudeSkills": [],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flower-trellis",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5-beta.1",
|
|
4
4
|
"description": "一键安装/升级 Trellis 并自动融合 skill-garden 强化包(默认 Claude + agents)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
"node": ">=18.17.0"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
+
"@inquirer/prompts": "^8.5.2",
|
|
15
16
|
"@mindfoldhq/trellis": "0.6.0-beta.8",
|
|
16
17
|
"chalk": "^5.6.2",
|
|
17
18
|
"figlet": "^1.11.0",
|
|
18
|
-
"inquirer": "^9.3.8",
|
|
19
19
|
"node-pty": "^1.1.0"
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
package/src/cli.js
CHANGED
|
@@ -2,6 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { flowerVersion, trellisVersion } from "./lib/versions.js";
|
|
4
4
|
import { selectVariant } from "./lib/variant.js";
|
|
5
|
+
import { readManifest } from "./lib/manifest.js";
|
|
5
6
|
import { runTrellis } from "./lib/trellis-runner.js";
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -21,6 +22,10 @@ function printVersion(cwd) {
|
|
|
21
22
|
const { version } = selectVariant(cwd);
|
|
22
23
|
if (version) console.log(`project .trellis ${version}`);
|
|
23
24
|
}
|
|
25
|
+
// 项目里 flower 上次铺包时戳入的自身版本(来自 .flower-manifest.json);
|
|
26
|
+
// 与首行「当前工具版本」对比即可看出该项目是否需要重新 update。
|
|
27
|
+
const mf = readManifest(cwd);
|
|
28
|
+
if (mf && mf.flowerVersion) console.log(`project flower ${mf.flowerVersion}`);
|
|
24
29
|
} catch {
|
|
25
30
|
// 忽略:版本读取失败不应影响 -v 输出
|
|
26
31
|
}
|
|
@@ -7,6 +7,7 @@ import { copySkills } from "./copy-skills.js";
|
|
|
7
7
|
import { injectWorkflow } from "./workflow-inject.js";
|
|
8
8
|
import { applyCodexTweaks } from "./codex-tweaks.js";
|
|
9
9
|
import { readManifest, writeManifest } from "./manifest.js";
|
|
10
|
+
import { flowerVersion } from "./versions.js";
|
|
10
11
|
import { rmrf } from "./fs-utils.js";
|
|
11
12
|
|
|
12
13
|
/** 清理升级后可能变空的强化目录(深 → 浅)。 */
|
|
@@ -101,7 +102,15 @@ export function applyEnhancements(target, opts = {}) {
|
|
|
101
102
|
pruneEmptyDirs(target);
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
|
-
|
|
105
|
+
// 写 manifest 时同时戳入 flower-trellis 自身版本(flowerVersion),与 version(项目 Trellis
|
|
106
|
+
// 版本)区分开:前者答「上次是哪个 flower 铺的包」,服务后续升级/维护判断。
|
|
107
|
+
writeManifest(target, {
|
|
108
|
+
flowerVersion: flowerVersion(),
|
|
109
|
+
variant,
|
|
110
|
+
version,
|
|
111
|
+
skills: installed,
|
|
112
|
+
paths: newPaths,
|
|
113
|
+
});
|
|
105
114
|
}
|
|
106
115
|
|
|
107
116
|
// workflow 注入:无过滤名(全装)或显式指定 workflow-enhancement/finish-work-enhancement 时执行
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { checkbox } from "@inquirer/prompts";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* 平台多选清单 —— flower 自己的平台选择菜单。
|
|
@@ -39,19 +39,21 @@ const PLATFORMS = [
|
|
|
39
39
|
*/
|
|
40
40
|
export async function pickPlatforms() {
|
|
41
41
|
if (!process.stdin.isTTY) return ["--codex", "--claude"];
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
},
|
|
55
|
-
|
|
42
|
+
// 用 @inquirer/checkbox(现代 @inquirer/core 增量重绘内核)替代经典 inquirer。
|
|
43
|
+
// 为什么换:经典 inquirer 每次重绘都「整块清屏 → 整块重写」,在 WSL2 / ConPTY 终端下
|
|
44
|
+
// 清空与重画之间有一帧空白,上下切换平台时肉眼可见闪屏;新内核按差异增量重绘可消闪。
|
|
45
|
+
// pageSize 取平台总数,一屏展示全部、避免滚动带来的二次重绘;loop:false 保持首尾不循环。
|
|
46
|
+
// 返回值即选中的 value 数组(["--claude","--codex", ...]),对调用方契约不变。
|
|
47
|
+
const tools = await checkbox({
|
|
48
|
+
message:
|
|
49
|
+
"选择要配置的 AI 工具(空格勾选 / 回车确认,默认已勾 Claude Code + Codex):",
|
|
50
|
+
choices: PLATFORMS.map((p) => ({
|
|
51
|
+
name: p.name,
|
|
52
|
+
value: p.value,
|
|
53
|
+
checked: !!p.checked,
|
|
54
|
+
})),
|
|
55
|
+
loop: false,
|
|
56
|
+
pageSize: PLATFORMS.length,
|
|
57
|
+
});
|
|
56
58
|
return tools;
|
|
57
59
|
}
|
package/src/lib/update-check.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
-
import
|
|
3
|
+
import { confirm } from "@inquirer/prompts";
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import { flowerVersion } from "./versions.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* 版本自动检测 —— 在 init / update 启动时尽力而为地比对 npm 上 flower-trellis 自身的
|
|
9
|
-
*
|
|
9
|
+
* 可用版本,发现新版时提示用户并(交互场景下)询问是否立即升级。
|
|
10
10
|
*
|
|
11
11
|
* 设计基调与本项目一致:**绝不阻断主流程**。网络探测带超时,离线/超时/失败一律静默跳过;
|
|
12
|
-
* 仅在「全局直跑 +
|
|
12
|
+
* 仅在「全局直跑 + 有新版」时才打扰用户。稳定版只跟 latest;预发布版同时看 latest / beta。
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
/** npm registry 根地址。 */
|
|
@@ -20,23 +20,26 @@ const PKG = "flower-trellis";
|
|
|
20
20
|
const TIMEOUT_MS = 2500;
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* 取 npm 上 flower-trellis 的
|
|
23
|
+
* 取 npm 上 flower-trellis 的 dist-tags;任何失败(离线/超时/非 200/解析异常)一律
|
|
24
24
|
* 返回 null —— 调用方据此「拿不到就当没这回事」继续主流程。
|
|
25
25
|
*
|
|
26
26
|
* 用 AbortController 给内置 fetch 加超时,finally 清除定时器防句柄泄漏。
|
|
27
|
-
* @returns {Promise<string|null>}
|
|
27
|
+
* @returns {Promise<{latest:string|null,beta:string|null}|null>} 可用 dist-tags,或失败时 null
|
|
28
28
|
*/
|
|
29
|
-
export async function
|
|
29
|
+
export async function fetchPackageDistTags() {
|
|
30
30
|
const ac = new AbortController();
|
|
31
31
|
const timer = setTimeout(() => ac.abort(), TIMEOUT_MS);
|
|
32
32
|
try {
|
|
33
|
-
const res = await fetch(`${REGISTRY}/${PKG}
|
|
33
|
+
const res = await fetch(`${REGISTRY}/${PKG}`, {
|
|
34
34
|
signal: ac.signal,
|
|
35
35
|
headers: { Accept: "application/json" },
|
|
36
36
|
});
|
|
37
37
|
if (!res.ok) return null; // 非 200(404/5xx 等)→ 静默跳过
|
|
38
38
|
const json = await res.json();
|
|
39
|
-
|
|
39
|
+
const tags = json && typeof json === "object" ? json["dist-tags"] : null;
|
|
40
|
+
const latest = typeof tags?.latest === "string" ? tags.latest : null;
|
|
41
|
+
const beta = typeof tags?.beta === "string" ? tags.beta : null;
|
|
42
|
+
return latest || beta ? { latest, beta } : null;
|
|
40
43
|
} catch {
|
|
41
44
|
return null; // AbortError(超时)/ fetch failed(离线)/ JSON 解析失败 → 静默
|
|
42
45
|
} finally {
|
|
@@ -44,28 +47,130 @@ export async function fetchLatestVersion() {
|
|
|
44
47
|
}
|
|
45
48
|
}
|
|
46
49
|
|
|
50
|
+
/**
|
|
51
|
+
* 取 npm 上 flower-trellis 的 latest 版本号。
|
|
52
|
+
*
|
|
53
|
+
* 保留这个导出是为了兼容已有调用方;新逻辑应优先使用 `fetchPackageDistTags()`。
|
|
54
|
+
* @returns {Promise<string|null>} latest 版本号,或失败时 null
|
|
55
|
+
*/
|
|
56
|
+
export async function fetchLatestVersion() {
|
|
57
|
+
const tags = await fetchPackageDistTags();
|
|
58
|
+
return tags?.latest ?? null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 解析项目需要的轻量 semver 版本号。
|
|
63
|
+
*
|
|
64
|
+
* @param {string} version 版本号
|
|
65
|
+
* @returns {{major:number,minor:number,patch:number,prerelease:string[]}|null} 解析结果
|
|
66
|
+
*/
|
|
67
|
+
function parseVersion(version) {
|
|
68
|
+
const match = String(version || "")
|
|
69
|
+
.trim()
|
|
70
|
+
.replace(/^v/, "")
|
|
71
|
+
.match(/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+.*)?$/);
|
|
72
|
+
if (!match) return null;
|
|
73
|
+
return {
|
|
74
|
+
major: Number(match[1]),
|
|
75
|
+
minor: Number(match[2]),
|
|
76
|
+
patch: Number(match[3]),
|
|
77
|
+
prerelease: match[4] ? match[4].split(".") : [],
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 判断版本号是否为 prerelease。
|
|
83
|
+
*
|
|
84
|
+
* @param {string} version 版本号
|
|
85
|
+
* @returns {boolean}
|
|
86
|
+
*/
|
|
87
|
+
export function isPrerelease(version) {
|
|
88
|
+
const parsed = parseVersion(version);
|
|
89
|
+
return Boolean(parsed && parsed.prerelease.length);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 比较 prerelease 标识符数组。
|
|
94
|
+
*
|
|
95
|
+
* 同一 base 下,稳定版高于 prerelease;相同 label(如 beta.1 → beta.2)按数值递增比较。
|
|
96
|
+
* 不同 label 之间不做主观排序,返回 0 以避免把 alpha/rc/beta 的跨线比较误判成升级。
|
|
97
|
+
*
|
|
98
|
+
* @param {string[]} aParts A prerelease 标识符
|
|
99
|
+
* @param {string[]} bParts B prerelease 标识符
|
|
100
|
+
* @returns {-1|0|1}
|
|
101
|
+
*/
|
|
102
|
+
function comparePrerelease(aParts, bParts) {
|
|
103
|
+
if (!aParts.length && !bParts.length) return 0;
|
|
104
|
+
if (!aParts.length) return 1;
|
|
105
|
+
if (!bParts.length) return -1;
|
|
106
|
+
if (aParts[0] !== bParts[0]) return 0;
|
|
107
|
+
|
|
108
|
+
const len = Math.max(aParts.length, bParts.length);
|
|
109
|
+
for (let i = 0; i < len; i++) {
|
|
110
|
+
const a = aParts[i];
|
|
111
|
+
const b = bParts[i];
|
|
112
|
+
if (a === undefined) return -1;
|
|
113
|
+
if (b === undefined) return 1;
|
|
114
|
+
if (a === b) continue;
|
|
115
|
+
|
|
116
|
+
const aNum = /^\d+$/.test(a);
|
|
117
|
+
const bNum = /^\d+$/.test(b);
|
|
118
|
+
if (aNum && bNum) {
|
|
119
|
+
const delta = Number(a) - Number(b);
|
|
120
|
+
if (delta !== 0) return delta > 0 ? 1 : -1;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (aNum !== bNum) return aNum ? -1 : 1;
|
|
124
|
+
return a > b ? 1 : -1;
|
|
125
|
+
}
|
|
126
|
+
return 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
47
129
|
/**
|
|
48
130
|
* 比较两个版本号,返回 1 / 0 / -1(a 比 b 新 / 相同 / 旧)。
|
|
49
131
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* 天然不含预发布优先级排序问题。与 src/lib/variant.js「剥 -beta.x 再比数值」的先例一致。
|
|
132
|
+
* 支持 `major.minor.patch` 与项目 beta 通道使用的 `major.minor.patch-beta.n`。
|
|
133
|
+
* 不引 `semver` 依赖,因为这里仅服务启动时的轻量升级提示,失败/不认识时宁可不提示。
|
|
53
134
|
* @param {string} a 版本号 A
|
|
54
135
|
* @param {string} b 版本号 B
|
|
55
136
|
* @returns {-1|0|1}
|
|
56
137
|
*/
|
|
57
138
|
export function compareVersions(a, b) {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
139
|
+
const av = parseVersion(a);
|
|
140
|
+
const bv = parseVersion(b);
|
|
141
|
+
if (!av || !bv) return 0;
|
|
142
|
+
|
|
143
|
+
for (const key of ["major", "minor", "patch"]) {
|
|
144
|
+
if (av[key] !== bv[key]) return av[key] > bv[key] ? 1 : -1;
|
|
145
|
+
}
|
|
146
|
+
return comparePrerelease(av.prerelease, bv.prerelease);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* 根据当前版本和 npm dist-tags 生成升级推荐。
|
|
151
|
+
*
|
|
152
|
+
* 稳定版只跟随稳定形态的 latest,避免把稳定用户引导到预发布通道;预发布版同时看
|
|
153
|
+
* 稳定形态的 latest / beta,且 latest 高于当前版本时优先推荐 latest,用于 beta 线被
|
|
154
|
+
* 稳定版追上后的回归稳定。
|
|
155
|
+
*
|
|
156
|
+
* @param {string} current 当前安装的 flower-trellis 版本
|
|
157
|
+
* @param {{latest?:string|null,beta?:string|null}|null} tags npm dist-tags
|
|
158
|
+
* @returns {{version:string,tag:"latest"|"beta",command:string}|null} 升级推荐
|
|
159
|
+
*/
|
|
160
|
+
export function getUpdateRecommendation(current, tags) {
|
|
161
|
+
if (!current || !tags) return null;
|
|
162
|
+
const currentIsPrerelease = isPrerelease(current);
|
|
163
|
+
const latest = typeof tags.latest === "string" ? tags.latest : null;
|
|
164
|
+
const beta = typeof tags.beta === "string" ? tags.beta : null;
|
|
165
|
+
const latestIsStable = latest && !isPrerelease(latest);
|
|
166
|
+
|
|
167
|
+
if (latestIsStable && compareVersions(latest, current) === 1) {
|
|
168
|
+
return { version: latest, tag: "latest", command: `npm i -g ${PKG}@latest` };
|
|
169
|
+
}
|
|
170
|
+
if (currentIsPrerelease && beta && compareVersions(beta, current) === 1) {
|
|
171
|
+
return { version: beta, tag: "beta", command: `npm i -g ${PKG}@beta` };
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
69
174
|
}
|
|
70
175
|
|
|
71
176
|
/**
|
|
@@ -92,10 +197,10 @@ export function isRunningViaNpx() {
|
|
|
92
197
|
*
|
|
93
198
|
* 短路条件(任一命中即跳过,什么都不打印):关闭开关(`--no-update-check` 使
|
|
94
199
|
* `ctx.updateCheck===false`,或环境变量 `FLOWER_NO_UPDATE_CHECK` 非空)、经 npx 运行、
|
|
95
|
-
*
|
|
200
|
+
* 网络探测失败、无升级推荐。
|
|
96
201
|
*
|
|
97
202
|
* 发现新版时的行为:
|
|
98
|
-
* - 交互 TTY:打印通知 →
|
|
203
|
+
* - 交互 TTY:打印通知 → confirm 询问是否升级 → 同意则执行推荐的 npm install 命令;
|
|
99
204
|
* 成功后打印「请重新运行」并 **process.exit(0)**(不做 re-exec 自动重跑),失败则降级为
|
|
100
205
|
* 打印手动升级命令并继续主流程;拒绝则继续主流程。
|
|
101
206
|
* - 非交互(`-y`/`--yes` 或非 TTY):仅打印通知 + 升级命令,不弹确认、不阻塞。
|
|
@@ -110,19 +215,20 @@ export async function checkForUpdate(ctx, commandLabel) {
|
|
|
110
215
|
// 2. npx 本就是最新版,跳过(连通知都不打,避免误导)
|
|
111
216
|
if (isRunningViaNpx()) return;
|
|
112
217
|
|
|
113
|
-
// 3. 尽力而为取
|
|
114
|
-
const
|
|
115
|
-
if (!
|
|
218
|
+
// 3. 尽力而为取 dist-tags;拿不到就静默退出
|
|
219
|
+
const tags = await fetchPackageDistTags();
|
|
220
|
+
if (!tags) return;
|
|
116
221
|
|
|
117
|
-
// 4.
|
|
222
|
+
// 4. 根据本地版本通道生成推荐;无推荐时不打扰
|
|
118
223
|
const current = flowerVersion();
|
|
119
|
-
|
|
224
|
+
const recommendation = getUpdateRecommendation(current, tags);
|
|
225
|
+
if (!recommendation) return;
|
|
120
226
|
|
|
121
227
|
// 5. 打印发现新版本通知(粉色品牌色,与 banner 一致)
|
|
122
228
|
console.log(
|
|
123
229
|
"\n🌸 " +
|
|
124
|
-
chalk.hex("#ff6fb5")(`发现 flower-trellis 新版本 ${chalk.bold(
|
|
125
|
-
chalk.gray(`(当前 ${current})`),
|
|
230
|
+
chalk.hex("#ff6fb5")(`发现 flower-trellis 新版本 ${chalk.bold(recommendation.version)}`) +
|
|
231
|
+
chalk.gray(`(当前 ${current}, 通道 ${recommendation.tag})`),
|
|
126
232
|
);
|
|
127
233
|
|
|
128
234
|
// 6. 非交互(-y/--yes 或非 TTY):仅打印升级命令,不弹确认、不阻塞
|
|
@@ -131,38 +237,34 @@ export async function checkForUpdate(ctx, commandLabel) {
|
|
|
131
237
|
ctx.passthrough.includes("--yes") ||
|
|
132
238
|
!process.stdin.isTTY;
|
|
133
239
|
if (nonInteractive) {
|
|
134
|
-
console.log(` ·
|
|
135
|
-
console.log(
|
|
240
|
+
console.log(` · 升级:${recommendation.command}`);
|
|
241
|
+
console.log(` · 升级后请重跑 ft ${commandLabel},让新版强化包重新叠加到现有项目`);
|
|
136
242
|
return;
|
|
137
243
|
}
|
|
138
244
|
|
|
139
|
-
// 7. 交互:询问是否升级
|
|
140
|
-
const
|
|
141
|
-
{
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
message: `是否现在升级到 ${latest}?(升级后需重新运行命令)`,
|
|
145
|
-
default: true,
|
|
146
|
-
},
|
|
147
|
-
]);
|
|
245
|
+
// 7. 交互:询问是否升级(@inquirer/confirm,返回 boolean)
|
|
246
|
+
const doUpgrade = await confirm({
|
|
247
|
+
message: `是否现在升级到 ${recommendation.version}(${recommendation.tag})?(升级后需重新运行命令)`,
|
|
248
|
+
default: true,
|
|
249
|
+
});
|
|
148
250
|
if (!doUpgrade) {
|
|
149
251
|
console.log(" · 已跳过升级");
|
|
150
252
|
return;
|
|
151
253
|
}
|
|
152
254
|
|
|
153
255
|
// 8. 执行全局升级。失败(含 EACCES 权限问题、npm 不存在)不自行提权,降级为打印手动命令
|
|
154
|
-
const res = spawnSync("npm", ["i", "-g", `${PKG}
|
|
256
|
+
const res = spawnSync("npm", ["i", "-g", `${PKG}@${recommendation.tag}`], {
|
|
155
257
|
stdio: "inherit",
|
|
156
258
|
shell: process.platform === "win32", // Windows 上 npm 实为 npm.cmd
|
|
157
259
|
});
|
|
158
260
|
if (res.status === 0) {
|
|
159
|
-
console.log(`\n ✓ 已升级到 ${
|
|
261
|
+
console.log(`\n ✓ 已升级到 ${recommendation.version}(${recommendation.tag})`);
|
|
160
262
|
console.log(` · 请重新运行 ft ${commandLabel} 以使用新版本`);
|
|
161
263
|
console.log(" · 强化包随版本更新,升级后可 ft update 重新叠加到现有项目");
|
|
162
264
|
// 当前进程内存里仍是旧代码,必须退出由用户重跑新版本(不做 re-exec,规避权限/平台/进程态坑)
|
|
163
265
|
process.exit(0);
|
|
164
266
|
} else {
|
|
165
|
-
console.log(` ·
|
|
267
|
+
console.log(` · 自动升级失败,请手动运行:${recommendation.command}`);
|
|
166
268
|
// 升级未成功:以当前版本继续 init/update,不阻断
|
|
167
269
|
}
|
|
168
270
|
}
|