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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "syncedAt": "2026-08-03T13:49:40.703Z",
2
+ "syncedAt": "2026-08-03T15:09:38.275Z",
3
3
  "syncedFrom": "vendor/skill-garden",
4
4
  "sourceCommit": "99505703777997be615cecb2e950fbd766398bc9",
5
5
  "common": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flower-trellis",
3
- "version": "0.6.0-beta.1",
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.1",
71
+ "version": "0.6.0-beta.2",
72
72
  "source": "CHANGELOG.md",
73
- "body": "### 🐛 修复 Bug Fixes\n\n* 同步简化 untracked 流程与 Push 路由 ([26ce73a](https://github.com/SilentFlower/flower-trellis/commit/26ce73a0a9a5dbcc3b8a623ca08b062a54a1e810))",
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 { detectPluginPlatforms } from "../../plugin/install/platform-detector.js";
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
- * 选择与旧 enhancement fallback 一致的平台参数。
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 compatibilityPlatforms(projectRoot) {
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: compatibilityPlatforms(projectRoot),
81
+ platforms: resolveSkillGardenPlatforms(projectRoot),
50
82
  dryRun: Boolean(options.dryRun),
51
83
  nonInteractive: true,
52
84
  onPreflight: options.onPreflight,
@@ -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
- async function replayPlugins(ctx, target, dryRun, compensationSnapshot = null) {
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
  }, {