flower-trellis 0.6.0-beta.1 → 0.6.0-beta.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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flower-trellis",
|
|
3
|
-
"version": "0.6.0-beta.
|
|
3
|
+
"version": "0.6.0-beta.2",
|
|
4
4
|
"description": "一键安装/升级 Trellis 并自动融合 skill-garden 强化包(默认 Claude + agents)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -68,9 +68,9 @@
|
|
|
68
68
|
"commit-and-tag-version": "^12.7.3"
|
|
69
69
|
},
|
|
70
70
|
"flowerReleaseNotes": {
|
|
71
|
-
"version": "0.6.0-beta.
|
|
71
|
+
"version": "0.6.0-beta.2",
|
|
72
72
|
"source": "CHANGELOG.md",
|
|
73
|
-
"body": "### 🐛 修复 Bug Fixes\n\n*
|
|
73
|
+
"body": "### 🐛 修复 Bug Fixes\n\n* 修复 Skill-Garden update 平台状态污染 ([787d4c7](https://github.com/SilentFlower/flower-trellis/commit/787d4c797834b4a0bb909e4eb20afe0be281fde1))",
|
|
74
74
|
"truncated": false
|
|
75
75
|
},
|
|
76
76
|
"optionalDependencies": {
|
|
@@ -343,6 +343,46 @@ export function projectSkillGardenContent(options) {
|
|
|
343
343
|
});
|
|
344
344
|
}
|
|
345
345
|
|
|
346
|
+
/**
|
|
347
|
+
* 只有目录当前全部文件都会在本轮删除时,才登记父目录清理。
|
|
348
|
+
*
|
|
349
|
+
* 旧版本可能只登记 check-all agent 文件,没登记 `.gemini` / `.zcode` 父目录。
|
|
350
|
+
* 这里用当前目录完整文件集合做 fail-closed 判断,避免删除包含用户内容的平台目录。
|
|
351
|
+
*
|
|
352
|
+
* @param {string} directory 项目内目录
|
|
353
|
+
* @param {Set<string>} staleTargets 本轮确认淘汰的旧受管文件
|
|
354
|
+
*/
|
|
355
|
+
function addDirectoryRemovalIfOnlyStaleFiles(directory, staleTargets) {
|
|
356
|
+
assertSafePosixRelativePath(directory, "skill-garden 待删目录");
|
|
357
|
+
const absoluteDirectory = path.join(projectRoot, ...directory.split("/"));
|
|
358
|
+
const existingTargets = listExistingFiles(absoluteDirectory)
|
|
359
|
+
.map((file) => `${directory}/${file.path}`);
|
|
360
|
+
if (existingTargets.length === 0) return;
|
|
361
|
+
if (!existingTargets.every((target) => staleTargets.has(target))) return;
|
|
362
|
+
const beforeHash = hashDirectoryIfExists(absoluteDirectory);
|
|
363
|
+
if (beforeHash === null) return;
|
|
364
|
+
if (directoryRemovals.some(({ path: targetPath }) => targetPath === directory)) return;
|
|
365
|
+
directoryRemovals.push({ owner: resolved.id, path: directory, beforeHash });
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* 为旧平台 check-all agent 规划安全父目录清理。
|
|
370
|
+
*
|
|
371
|
+
* @param {Set<string>} staleTargets 本轮确认淘汰的旧受管 check-all agent
|
|
372
|
+
*/
|
|
373
|
+
function addStaleCheckAllDirectoryRemovals(staleTargets) {
|
|
374
|
+
for (const target of [...staleTargets].sort(compareUtf8)) {
|
|
375
|
+
const parent = path.posix.dirname(target);
|
|
376
|
+
if (parent !== "." && parent !== target) {
|
|
377
|
+
addDirectoryRemovalIfOnlyStaleFiles(parent, staleTargets);
|
|
378
|
+
}
|
|
379
|
+
const [root] = target.split("/");
|
|
380
|
+
if (root && root !== parent) {
|
|
381
|
+
addDirectoryRemovalIfOnlyStaleFiles(root, staleTargets);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
346
386
|
/**
|
|
347
387
|
* 把一个来源目录按 copyPath 语义投影到目标目录。
|
|
348
388
|
*
|
|
@@ -591,6 +631,12 @@ export function projectSkillGardenContent(options) {
|
|
|
591
631
|
const selectedPlatforms = new Set(options.platformSelection?.platforms || []);
|
|
592
632
|
const body = fs.readFileSync(path.join(variantDir, CHECK_ALL_AGENT_BODY_REL), "utf8");
|
|
593
633
|
const checkAllSkillRoot = path.join(variantDir, ".agents", "skills", "trellis-check-all");
|
|
634
|
+
const checkAllTargets = new Set(
|
|
635
|
+
catalog.platforms
|
|
636
|
+
.filter(({ checkAll }) => checkAll.eligible)
|
|
637
|
+
.map(({ checkAll }) => checkAll.target),
|
|
638
|
+
);
|
|
639
|
+
const desiredCheckAllTargets = new Set();
|
|
594
640
|
for (const entry of catalog.platforms.filter(({ checkAll }) => checkAll.eligible)) {
|
|
595
641
|
const runtimePlatforms = entry.runtimePlatforms || [entry.id];
|
|
596
642
|
const enabled = selectedPlatforms.size > 0
|
|
@@ -613,8 +659,16 @@ export function projectSkillGardenContent(options) {
|
|
|
613
659
|
"exclusive",
|
|
614
660
|
pluginPackage.skillGarden.variant === "0.6",
|
|
615
661
|
);
|
|
662
|
+
desiredCheckAllTargets.add(entry.checkAll.target);
|
|
616
663
|
installed.add(`agent:trellis-check-all:${entry.id}`);
|
|
617
664
|
}
|
|
665
|
+
const staleCheckAllTargets = new Set();
|
|
666
|
+
for (const entry of previous?.paths || []) {
|
|
667
|
+
if (!checkAllTargets.has(entry.path) || desiredCheckAllTargets.has(entry.path)) continue;
|
|
668
|
+
paths.delete(entry.path);
|
|
669
|
+
staleCheckAllTargets.add(entry.path);
|
|
670
|
+
}
|
|
671
|
+
addStaleCheckAllDirectoryRemovals(staleCheckAllTargets);
|
|
618
672
|
addFile(
|
|
619
673
|
".trellis/agents/check-all.md",
|
|
620
674
|
Buffer.from([
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import "../../plugin/install/patch-planner.js";
|
|
2
|
+
import { getConfiguredPlatforms } from "@mindfoldhq/trellis/dist/configurators/index.js";
|
|
2
3
|
import { PluginApplicationService } from "../../plugin/application-service.js";
|
|
3
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
detectPluginPlatforms,
|
|
6
|
+
listPluginPlatforms,
|
|
7
|
+
} from "../../plugin/install/platform-detector.js";
|
|
4
8
|
import { PLUGIN_RUNTIME_ERROR_CODES } from "../../plugin/runtime-errors.js";
|
|
9
|
+
import { compareUtf8 } from "../../plugin/stable-order.js";
|
|
5
10
|
import { ProjectStore } from "../../plugin/state/project-store.js";
|
|
6
11
|
import { SourceRegistry } from "../../plugin/sources/source-registry.js";
|
|
7
12
|
import {
|
|
@@ -9,13 +14,40 @@ import {
|
|
|
9
14
|
SkillGardenBuiltinProvider,
|
|
10
15
|
} from "./provider.js";
|
|
11
16
|
|
|
17
|
+
const TRELLIS_TO_PLUGIN_PLATFORM = new Map([
|
|
18
|
+
["claude-code", "claude"],
|
|
19
|
+
]);
|
|
20
|
+
|
|
12
21
|
/**
|
|
13
|
-
*
|
|
22
|
+
* 读取 Trellis 已配置平台并转换为 Plugin 平台 ID。
|
|
23
|
+
*
|
|
24
|
+
* @param {string} projectRoot 项目根
|
|
25
|
+
* @returns {string[]} Plugin 平台列表;无法读取或没有命中时返回空数组
|
|
26
|
+
*/
|
|
27
|
+
function configuredTrellisPluginPlatforms(projectRoot) {
|
|
28
|
+
try {
|
|
29
|
+
const supported = new Set(listPluginPlatforms());
|
|
30
|
+
return [...new Set([...getConfiguredPlatforms(projectRoot)]
|
|
31
|
+
.map((platform) => TRELLIS_TO_PLUGIN_PLATFORM.get(platform) || platform)
|
|
32
|
+
.filter((platform) => supported.has(platform)))]
|
|
33
|
+
.sort(compareUtf8);
|
|
34
|
+
} catch {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 选择 Skill-Garden lifecycle 的显式平台参数。
|
|
41
|
+
*
|
|
42
|
+
* 优先使用 Trellis 模板 hash 记录的平台,避免旧 Plugin state 污染把未启用平台继续投影。
|
|
43
|
+
* 缺少 hash 记录的旧项目继续沿用原生检测与 Claude fallback,保持旧 enhancement 兼容行为。
|
|
14
44
|
*
|
|
15
45
|
* @param {string} projectRoot 项目根
|
|
16
|
-
* @returns {string[]}
|
|
46
|
+
* @returns {string[]} 显式平台
|
|
17
47
|
*/
|
|
18
|
-
function
|
|
48
|
+
export function resolveSkillGardenPlatforms(projectRoot) {
|
|
49
|
+
const configured = configuredTrellisPluginPlatforms(projectRoot);
|
|
50
|
+
if (configured.length > 0) return configured;
|
|
19
51
|
try {
|
|
20
52
|
return detectPluginPlatforms(projectRoot).platforms;
|
|
21
53
|
} catch (error) {
|
|
@@ -46,7 +78,7 @@ export function applySkillGardenPlugin(projectRoot, options = {}) {
|
|
|
46
78
|
const service = new PluginApplicationService(projectRoot, { registry, store });
|
|
47
79
|
const declared = store.readPlugins().plugins.some(({ id }) => id === SKILL_GARDEN_PLUGIN_ID);
|
|
48
80
|
const lifecycleOptions = {
|
|
49
|
-
platforms:
|
|
81
|
+
platforms: resolveSkillGardenPlatforms(projectRoot),
|
|
50
82
|
dryRun: Boolean(options.dryRun),
|
|
51
83
|
nonInteractive: true,
|
|
52
84
|
onPreflight: options.onPreflight,
|
package/src/commands/update.js
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
snapshotUpdateBackups,
|
|
15
15
|
} from "../lib/update-backups.js";
|
|
16
16
|
import { ProjectStore } from "../plugin/state/project-store.js";
|
|
17
|
+
import { resolveSkillGardenPlatforms } from "../builtin-plugins/skill-garden/runtime.js";
|
|
17
18
|
import { SKILL_GARDEN_PLUGIN_ID } from "../builtin-plugins/skill-garden/provider.js";
|
|
18
19
|
import { showCommandCompletion } from "../lib/command-completion.js";
|
|
19
20
|
import { reportTelemetry } from "../lib/telemetry.js";
|
|
@@ -56,7 +57,26 @@ function sandboxTrellisUpdateArgs(passthrough) {
|
|
|
56
57
|
return args;
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
/**
|
|
61
|
+
* 把平台列表转换成 Plugin CLI 的重复 `--platform` 参数。
|
|
62
|
+
*
|
|
63
|
+
* @param {string[]} platforms Plugin 平台 ID
|
|
64
|
+
* @returns {string[]} CLI passthrough 参数
|
|
65
|
+
*/
|
|
66
|
+
function pluginPlatformArgs(platforms) {
|
|
67
|
+
return platforms.flatMap((platform) => ["--platform", platform]);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 在 `flower-trellis update` 流程中重放 Plugin Runtime。
|
|
72
|
+
*
|
|
73
|
+
* @param {object} ctx update 命令上下文
|
|
74
|
+
* @param {string} target 目标项目根
|
|
75
|
+
* @param {boolean} dryRun 是否仅预演
|
|
76
|
+
* @param {ReturnType<typeof createUpdateSnapshot>|null} [compensationSnapshot] 失败补偿快照
|
|
77
|
+
* @returns {Promise<void>} Plugin 重放完成
|
|
78
|
+
*/
|
|
79
|
+
export async function replayPlugins(ctx, target, dryRun, compensationSnapshot = null) {
|
|
60
80
|
const onPreflight = compensationSnapshot
|
|
61
81
|
? ({ plan }) => extendUpdateSnapshot(compensationSnapshot, [
|
|
62
82
|
...plan.contentMutations.map(({ target: mutationTarget }) => mutationTarget),
|
|
@@ -72,6 +92,7 @@ async function replayPlugins(ctx, target, dryRun, compensationSnapshot = null) {
|
|
|
72
92
|
passthrough: [
|
|
73
93
|
declared ? "update" : "add",
|
|
74
94
|
SKILL_GARDEN_PLUGIN_ID,
|
|
95
|
+
...pluginPlatformArgs(resolveSkillGardenPlatforms(target)),
|
|
75
96
|
...(dryRun ? ["--dry-run"] : []),
|
|
76
97
|
],
|
|
77
98
|
}, {
|