Skip to content

Commit 4148057

Browse files
authored
v0.6.0 release (#11)
* v0.6.0 release * bugfix
1 parent 0550d7f commit 4148057

8 files changed

Lines changed: 114 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
## [Unreleased]
66

7-
## [0.6.0] - 2026-08-14
7+
## [0.6.0] - 2026-08-18
88

99
### Added
1010

@@ -19,6 +19,9 @@
1919
Claude CLI 完成来源注册、安装、启停与回读验证。
2020
- 增加 Claude 插件结构、组件路径、清单字段、自定义路径和 Skill 原生语义的
2121
fail-closed 校验,并覆盖真实子进程和临时配置目录的安装回归测试。
22+
- 增加只读 `asset_reconcile_context` MCP/严格 JSON CLI 接口,为已配置 profile 与保存项目
23+
返回分页、带上下文身份的资产对账结果;外部 `cc-port-advisor` Skill 可据此给出同步建议,
24+
但不能创建计划、审批或执行传输。
2225

2326
### Changed
2427

@@ -39,6 +42,8 @@
3942

4043
- MCP 与非交互 CLI 的可执行写计划必须由桌面端批准;审批绑定完整 scope 与 plan hash,
4144
短时有效、只能消费一次,stale 后不能复用。
45+
- 将前端构建链的锁定传递依赖 `nanoid` 更新到 `3.3.18`,修复自定义生成器在零长度输入下
46+
可能无限循环的高危公告。
4247
- 资源仓库与配置、状态、备份和全部 profile 原生目标必须分离;配置保存、资产计划和
4348
apply 都会重新检查路径边界并 fail closed。
4449
- Claude 插件上传和下载同时校验插件名、清单、组件边界、内容指纹和精确 profile;

desktop/package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

desktop/src/app/i18n/catalog.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ export const en = {
116116
"message.asset.blocker.install_name_private_only": "Only private-repository assets can store a platform install name.",
117117
"message.asset.blocker.install_name_required": "A platform install name is required.",
118118
"message.asset.blocker.kind_not_supported": "The detected platform does not support this resource kind.",
119+
"message.asset.blocker.legacy_write_state": "The legacy resource workspace must be resolved before remote asset writes.",
119120
"message.asset.blocker.local_fingerprint_missing": "The local source cannot be fingerprinted safely.",
120121
"message.asset.blocker.local_instance_exists": "The new local name already exists as another local instance.",
121122
"message.asset.blocker.local_source_missing": "The local source does not exist.",
@@ -125,6 +126,7 @@ export const en = {
125126
"message.asset.blocker.nested_link_unsafe": "Nested links or unreadable entries cannot be uploaded: {detail}",
126127
"message.asset.blocker.managed_plugin_read_only": "Managed plugin installations are read-only.",
127128
"message.asset.blocker.memory_install_name_local_only": "Memory project-slot mappings are local profile configuration and cannot be stored remotely.",
129+
"message.asset.blocker.memory_source_tool_invalid": "Memory resources can only originate from Claude Code.",
128130
"message.asset.blocker.memory_settings_untrusted": "Claude Memory settings are not trusted: {detail}",
129131
"message.asset.blocker.memory_target_mapping_required": "Map this Memory resource to the exact local Claude project slot before downloading.",
130132
"message.asset.blocker.new_name_invalid": "The new resource name must use lowercase letters, digits, and hyphens.",
@@ -136,6 +138,7 @@ export const en = {
136138
"message.asset.blocker.platform_unsupported": "The resource is not supported on this platform.",
137139
"message.asset.blocker.plugin_discovery_incomplete": "Plugin discovery is incomplete; missing fields cannot be inferred.",
138140
"message.asset.blocker.plugin_target_missing": "No portable local target is configured for this plugin installation.",
141+
"message.asset.blocker.portable_tool_identity_missing": "The local source tool identity is unavailable.",
139142
"message.asset.blocker.prompt_marker_unsafe": "The Prompt ownership sidecar must not be a symbolic link.",
140143
"message.asset.blocker.prompt_payload_ambiguous": "A file-based Prompt requires exactly one safe root Markdown payload.",
141144
"message.asset.blocker.project_without_remote": "Projects without a Git remote are observation-only and cannot be uploaded.",
@@ -160,6 +163,17 @@ export const en = {
160163
"message.asset.diff.same": "Local and remote content fingerprints match.",
161164
"message.asset.diff.target_conflict": "Multiple resources resolve to the same local target.",
162165
"message.asset.diff.uncomparable": "The resource cannot be compared safely.",
166+
"message.asset.reconcile.issue": "Asset reconciliation could not complete this check.",
167+
"message.asset.reconcile.manifest_link_unsafe": "Link, reparse-point, or unreadable content cannot be summarized safely.",
168+
"message.asset.reconcile.manifest_local_identity_unavailable": "The local asset cannot be fingerprinted safely, so its files were not read.",
169+
"message.asset.reconcile.manifest_nested_link": "Nested link content was excluded from the manifest.",
170+
"message.asset.reconcile.manifest_secret_withheld": "A secret-like value was detected, so its hash was withheld.",
171+
"message.asset.reconcile.manifest_unavailable": "The asset manifest is unavailable or could not be read safely.",
172+
"message.asset.reconcile.manifest_unreadable": "One or more files could not be read safely.",
173+
"message.asset.reconcile.profile_path_invalid": "A configured profile path is invalid.",
174+
"message.asset.reconcile.profile_path_unavailable": "A configured profile path cannot be scanned safely.",
175+
"message.asset.reconcile.profile_path_unreadable": "A configured profile path cannot be read safely.",
176+
"message.asset.reconcile.remote_warning": "The remote refresh returned a warning.",
163177
"message.asset.legacy.dirty": "The legacy resource workspace has local changes. Resolve them before remote asset writes.",
164178
"message.asset.legacy.diverged": "The legacy resource workspace is {state}. Resolve its local commits before remote asset writes.",
165179
"message.asset.legacy.pending_plan": "Legacy sync plan {operation_id} is still pending. Resolve it before remote asset writes.",
@@ -1202,6 +1216,7 @@ export const zh: Record<I18nKey, string> = {
12021216
"message.asset.blocker.install_name_private_only": "只有私有仓库资产才能保存平台安装名称。",
12031217
"message.asset.blocker.install_name_required": "必须提供平台安装名称。",
12041218
"message.asset.blocker.kind_not_supported": "检测到的平台不支持此资源类型。",
1219+
"message.asset.blocker.legacy_write_state": "必须先解决旧资源工作区状态,才能执行远端资产写入。",
12051220
"message.asset.blocker.local_fingerprint_missing": "无法安全计算本地来源指纹。",
12061221
"message.asset.blocker.local_instance_exists": "新的本地名称已被另一个本地实例使用。",
12071222
"message.asset.blocker.local_source_missing": "本地来源不存在。",
@@ -1211,6 +1226,7 @@ export const zh: Record<I18nKey, string> = {
12111226
"message.asset.blocker.nested_link_unsafe": "不能上传嵌套链接或不可读取条目:{detail}",
12121227
"message.asset.blocker.managed_plugin_read_only": "受管插件安装为只读。",
12131228
"message.asset.blocker.memory_install_name_local_only": "Memory 项目槽位映射属于本机 profile 配置,不能存入远端。",
1229+
"message.asset.blocker.memory_source_tool_invalid": "Memory 资源只能来自 Claude Code。",
12141230
"message.asset.blocker.memory_settings_untrusted": "无法信任 Claude Memory 设置:{detail}",
12151231
"message.asset.blocker.memory_target_mapping_required": "下载前请将此 Memory 资源映射到确切的本地 Claude 项目槽位。",
12161232
"message.asset.blocker.new_name_invalid": "新资源名称只能使用小写字母、数字和连字符。",
@@ -1222,6 +1238,7 @@ export const zh: Record<I18nKey, string> = {
12221238
"message.asset.blocker.platform_unsupported": "该资源不受此平台支持。",
12231239
"message.asset.blocker.plugin_discovery_incomplete": "插件发现信息不完整,不能推断缺失字段。",
12241240
"message.asset.blocker.plugin_target_missing": "该插件安装没有配置可移植的本地目标。",
1241+
"message.asset.blocker.portable_tool_identity_missing": "本地来源的工具身份不可用。",
12251242
"message.asset.blocker.prompt_marker_unsafe": "Prompt 所有权 sidecar 不能是符号链接。",
12261243
"message.asset.blocker.prompt_payload_ambiguous": "文件式 Prompt 必须恰好包含一个安全的根级 Markdown 载荷。",
12271244
"message.asset.blocker.project_without_remote": "没有 Git remote 的项目只能观察,不能上传。",
@@ -1246,6 +1263,17 @@ export const zh: Record<I18nKey, string> = {
12461263
"message.asset.diff.same": "本地与远端内容指纹一致。",
12471264
"message.asset.diff.target_conflict": "多个资源解析到了同一本地目标。",
12481265
"message.asset.diff.uncomparable": "无法安全比较该资源。",
1266+
"message.asset.reconcile.issue": "资产对账未能完成此项检查。",
1267+
"message.asset.reconcile.manifest_link_unsafe": "无法安全汇总链接、重解析点或不可读取的内容。",
1268+
"message.asset.reconcile.manifest_local_identity_unavailable": "无法安全计算本地资产指纹,因此未读取其文件。",
1269+
"message.asset.reconcile.manifest_nested_link": "清单已排除嵌套链接内容。",
1270+
"message.asset.reconcile.manifest_secret_withheld": "检测到疑似秘密,已隐藏其哈希。",
1271+
"message.asset.reconcile.manifest_unavailable": "资产清单不可用或无法安全读取。",
1272+
"message.asset.reconcile.manifest_unreadable": "一个或多个文件无法安全读取。",
1273+
"message.asset.reconcile.profile_path_invalid": "配置的 profile 路径无效。",
1274+
"message.asset.reconcile.profile_path_unavailable": "配置的 profile 路径无法安全扫描。",
1275+
"message.asset.reconcile.profile_path_unreadable": "配置的 profile 路径无法安全读取。",
1276+
"message.asset.reconcile.remote_warning": "远端刷新返回了警告。",
12491277
"message.asset.legacy.dirty": "旧资源工作区存在本地更改,请先处理再执行远端资产写入。",
12501278
"message.asset.legacy.diverged": "旧资源工作区处于 {state} 状态,请先处理本地提交再执行远端资产写入。",
12511279
"message.asset.legacy.pending_plan": "旧同步计划 {operation_id} 仍在等待处理,请先解决再执行远端资产写入。",

docs/releases/v0.6.0.en.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ CC Port `v0.6.0` is a substantial feature update since `v0.5.4`. The installer n
66
standalone `cc-port.exe` CLI/MCP agent, resource repositories move to the tool-neutral Registry v1
77
format, Windows and WSL use independent profiles, Claude Code user instructions and auto memory
88
become portable resources, and Claude Skills, skills-directory Plugins, and Marketplace Plugins
9-
follow separate native workflows.
9+
follow separate native workflows. A new read-only reconciliation interface can also provide
10+
structured synchronization context to the independent `cc-port-advisor` Skill.
1011

1112
## Downloads
1213

@@ -109,6 +110,21 @@ available for recovery.
109110
- A missing, malformed, linked Registry or invalid overlay fails closed for remote-dependent upload
110111
and installation while leaving local discovery available.
111112

113+
### Read-only reconciliation and Advisor
114+
115+
- MCP `asset_reconcile_context` and its strict JSON CLI counterpart rescan configured profiles and
116+
saved projects, then return paginated local/remote comparisons, action eligibility, blockers, and
117+
coverage gaps under one `context_id`. They do not scan the whole disk or treat unavailable profiles
118+
or saved projects as missing resources.
119+
- `local-only` and `remote-only` entries become upload or install candidates only when the service
120+
marks that exact action eligible. `content-different`, `metadata-only`, variants, confirmation items,
121+
and blockers remain review-only; an unknown baseline never establishes which side is newer or
122+
authoritative.
123+
- The external [`cc-port-advisor`](https://github.com/Ling-ye/LingyeAIResources/tree/main/skills/cc-port-advisor)
124+
Skill can read this context and provide advice. It is not bundled with CC Port and cannot create a
125+
plan, approval, or transfer. If the user chooses an action, control returns to the bundled `cc-port`
126+
Skill for a fresh refresh, plan, desktop approval, apply, and verification sequence.
127+
112128
### Clearer desktop information
113129

114130
- Plugin rows, details, and batch plans use human-readable platforms and distributions such as

docs/releases/v0.6.0.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
CC Port `v0.6.0``v0.5.4` 之后的一次较大功能更新:安装包首次包含独立的
66
`cc-port.exe` CLI/MCP agent,资源仓库切换到工具中立的 Registry v1,并增加
77
Windows/WSL 独立 profile、Claude Code 用户指令与 auto memory,以及按 Claude
8-
原生语义区分的 Skill、skills-directory Plugin 和 Marketplace Plugin 工作流。
8+
原生语义区分的 Skill、skills-directory Plugin 和 Marketplace Plugin 工作流。新的
9+
只读资产对账接口还可为独立的 `cc-port-advisor` Skill 提供结构化同步建议上下文。
910

1011
## 下载
1112

@@ -94,6 +95,18 @@ Desktop API sidecar 或 agent EXE;公开 Release 只提供上述两个文件
9495
- Registry 缺失、损坏、为链接或 overlay 无效时,本地扫描仍可使用,但依赖远端清单的上传
9596
和安装会 fail closed。
9697

98+
### 只读资产对账与 Advisor
99+
100+
- MCP `asset_reconcile_context` 与对应的严格 JSON CLI 会重新扫描已配置的 profile 和保存项目,
101+
返回同一 `context_id` 下可分页读取的本地/远端比较、执行资格、阻断项和覆盖缺口;不会扫描
102+
整块磁盘,也不会把不可用的 profile 或保存项目误判为资源缺失。
103+
- `local-only``remote-only` 只有在服务明确标记为可执行时才可成为上传或安装候选;
104+
`content-different``metadata-only`、variants、确认项和阻断项保持人工审阅,不会依据未知
105+
baseline 猜测哪一侧更新或权威。
106+
- 外部 [`cc-port-advisor`](https://github.com/Ling-ye/LingyeAIResources/tree/main/skills/cc-port-advisor)
107+
Skill 可以读取该上下文并给出只读建议。它不随 CC Port 安装包发布,也不创建 plan、审批或
108+
传输;用户决定执行后,仍必须交回内置 `cc-port` Skill,重新刷新、计划、桌面审批、应用并验证。
109+
97110
### 桌面信息更清晰
98111

99112
- Plugin 列表、详情和批量计划现在显示人类可读的平台与分发方式,例如

src/cc_port/services/asset_sync.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5631,7 +5631,13 @@ def _local_target_ancestor_problem(target: Path) -> str:
56315631
probe = probe_local_path(component)
56325632
if probe.health == "missing":
56335633
continue
5634-
if probe.is_link or not probe.ready:
5634+
if probe.is_link:
5635+
link_kind = "symbolic link" if probe.path_kind == "symlink" else probe.path_kind
5636+
return probe.problem or (
5637+
f"The local target ancestor is a {link_kind} and cannot be used safely: "
5638+
f"{component}"
5639+
)
5640+
if not probe.ready:
56355641
return probe.problem or f"The local target ancestor cannot be used safely: {component}"
56365642
return ""
56375643

src/cc_port/services/env_manager.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,21 @@ def _claude_auto_memory_override(
833833
path_problem,
834834
False,
835835
)
836+
wsl_runtime_root = _wsl_unc_runtime_root(candidate, profile=profile)
837+
if wsl_runtime_root is not None:
838+
runtime_probe = probe_local_path(wsl_runtime_root)
839+
if (
840+
runtime_probe.path_kind != "regular"
841+
or not runtime_probe.ready
842+
or runtime_probe.content_path is None
843+
or not runtime_probe.content_path.is_dir()
844+
):
845+
return (
846+
None,
847+
"The configured WSL runtime cannot be accessed; automatic memory "
848+
"migration is blocked.",
849+
False,
850+
)
836851
return candidate, "", True
837852

838853

@@ -917,6 +932,22 @@ def _claude_memory_override_path(
917932
)
918933

919934

935+
def _wsl_unc_runtime_root(path: Path, *, profile: PlatformProfile) -> Path | None:
936+
"""Return the matching WSL UNC share root when it must be reachable from Windows."""
937+
if not _host_is_windows() or profile.environment_kind != "wsl":
938+
return None
939+
normalized = str(path).replace("/", "\\")
940+
match = re.match(
941+
r"^\\\\(wsl\.localhost|wsl\$)\\([^\\]+)(?:\\|$)",
942+
normalized,
943+
flags=re.IGNORECASE,
944+
)
945+
distro = profile.environment_name.strip()
946+
if match is None or not distro or match.group(2).casefold() != distro.casefold():
947+
return None
948+
return Path(f"\\\\{match.group(1)}\\{match.group(2)}")
949+
950+
920951
def _host_is_windows() -> bool:
921952
return os.name == "nt"
922953

@@ -1176,7 +1207,10 @@ def _unsafe_path_component_problem(
11761207
probe = probe_local_path(component)
11771208
if probe.health == "missing":
11781209
continue
1179-
if probe.is_link or not probe.ready:
1210+
if probe.is_link:
1211+
link_kind = "symbolic link" if probe.path_kind == "symlink" else probe.path_kind
1212+
return probe.problem or f"Unsafe {link_kind} path component: {component}"
1213+
if not probe.ready:
11801214
return probe.problem or f"Unsafe path component: {component}"
11811215
return ""
11821216

tests/test_claude_memory_runtime_profiles.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -326,10 +326,11 @@ def test_auto_memory_directory_is_used_as_the_direct_memory_directory(
326326
json.dumps({"autoMemoryDirectory": str(configured_memory)}),
327327
encoding="utf-8",
328328
)
329+
profile_name = "claude-windows" if os.name == "nt" else "claude-wsl-test"
329330
profile = _claude_profile(
330-
name="claude-windows",
331+
name=profile_name,
331332
root=claude_root,
332-
environment_kind="windows",
333+
environment_kind="windows" if os.name == "nt" else "wsl",
333334
)
334335

335336
result = env_manager.discover_environment(
@@ -340,7 +341,7 @@ def test_auto_memory_directory_is_used_as_the_direct_memory_directory(
340341
assert result.tools[0].memory_layout == "direct"
341342
assert result.tools[0].memories_path == configured_memory.resolve()
342343
expected_name = "claude-memory-" + hashlib.sha256(
343-
f"claude-windows\0{os.path.normcase(str(configured_memory.resolve()))}".encode()
344+
f"{profile_name}\0{os.path.normcase(str(configured_memory.resolve()))}".encode()
344345
).hexdigest()[:12]
345346
assert [(resource.kind, resource.name_hint, resource.path) for resource in result.resources] == [
346347
("memory", expected_name, configured_memory.resolve())

0 commit comments

Comments
 (0)