code-workspace-zhuiyi 0.1.0-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.
Files changed (98) hide show
  1. package/.env.example +3 -0
  2. package/LICENSE +21 -0
  3. package/README.md +168 -0
  4. package/README.zh-CN.md +168 -0
  5. package/artifacts/manifest.json +183 -0
  6. package/artifacts/templates/USER_GUIDE.template.md +1 -0
  7. package/artifacts/templates/agents/WORKSPACE_GUARD.md.template +73 -0
  8. package/artifacts/templates/agents/skills/code-workspace-resolve-branch/SKILL.md +88 -0
  9. package/artifacts/templates/agents/skills/code-workspace-resolve-branch/evals/evals.json +45 -0
  10. package/artifacts/templates/claude/commands/code-workspace/add-projects.md +23 -0
  11. package/artifacts/templates/codex/hooks.json +86 -0
  12. package/artifacts/templates/codex/skills/code-workspace-add-projects/SKILL.md +28 -0
  13. package/artifacts/templates/user-guide/en-US.md +97 -0
  14. package/artifacts/templates/user-guide/zh-CN.md +97 -0
  15. package/assets/i18n-icon.svg +1 -0
  16. package/assets/logo-vector.svg +30 -0
  17. package/assets/request_tip.mp3 +0 -0
  18. package/assets/session_finish.mp3 +0 -0
  19. package/bin/code-workspace.js +11 -0
  20. package/docs/extension-architecture.zh-CN.md +429 -0
  21. package/docs/extensions.md +64 -0
  22. package/docs/extensions.zh-CN.md +64 -0
  23. package/extensions/openspec-workspace/1.0.0/artifacts/claude/SKILL.md +16 -0
  24. package/extensions/openspec-workspace/1.0.0/artifacts/codex/SKILL.md +16 -0
  25. package/extensions/openspec-workspace/1.0.0/init.js +54 -0
  26. package/extensions/openspec-workspace/1.0.0/manifest.json +27 -0
  27. package/extensions/zhuiyi-jira-mcp/0.1.0/artifacts/claude/server.json +14 -0
  28. package/extensions/zhuiyi-jira-mcp/0.1.0/artifacts/codex/config.toml +11 -0
  29. package/extensions/zhuiyi-jira-mcp/0.1.0/init.js +53 -0
  30. package/extensions/zhuiyi-jira-mcp/0.1.0/lib/archive.js +196 -0
  31. package/extensions/zhuiyi-jira-mcp/0.1.0/manifest.json +38 -0
  32. package/extensions/zhuiyi-jira-mcp/0.1.0/release.json +15 -0
  33. package/package.json +58 -0
  34. package/schemas/extension-init-context-v1.json +39 -0
  35. package/schemas/extension-init-result-v1.json +42 -0
  36. package/schemas/extension-manifest-v1.json +55 -0
  37. package/schemas/extension-manifest-v2.json +84 -0
  38. package/schemas/extension-manifest-v3.json +84 -0
  39. package/spec/extension/v1/specification.en-US.md +171 -0
  40. package/spec/extension/v1/specification.zh-CN.md +170 -0
  41. package/src/cli/commands/completion.js +203 -0
  42. package/src/cli/commands/extension.js +193 -0
  43. package/src/cli/commands/help.js +36 -0
  44. package/src/cli/commands/init.js +200 -0
  45. package/src/cli/commands/monitor.js +62 -0
  46. package/src/cli/commands/permissions.js +33 -0
  47. package/src/cli/commands/project-branch-update.js +107 -0
  48. package/src/cli/commands/project-branch.js +433 -0
  49. package/src/cli/commands/project.js +259 -0
  50. package/src/cli/commands/update.js +146 -0
  51. package/src/cli/commands/workspace.js +24 -0
  52. package/src/cli/confirmation.js +22 -0
  53. package/src/cli/parser.js +108 -0
  54. package/src/cli/registry.js +108 -0
  55. package/src/cli/renderer.js +20 -0
  56. package/src/cli/result.js +118 -0
  57. package/src/cli.js +76 -0
  58. package/src/core/assets.js +83 -0
  59. package/src/core/config.js +378 -0
  60. package/src/core/diagnostics.js +20 -0
  61. package/src/core/directory-digest.js +34 -0
  62. package/src/core/doctor.js +115 -0
  63. package/src/core/errors.js +10 -0
  64. package/src/core/extension-artifacts-legacy.js +180 -0
  65. package/src/core/extension-artifacts.js +310 -0
  66. package/src/core/extensions.js +1216 -0
  67. package/src/core/fs.js +32 -0
  68. package/src/core/init-lock-holder.js +30 -0
  69. package/src/core/init-lock.js +159 -0
  70. package/src/core/init.js +160 -0
  71. package/src/core/initializer.js +233 -0
  72. package/src/core/language.js +102 -0
  73. package/src/core/managed-files.js +334 -0
  74. package/src/core/migration.js +68 -0
  75. package/src/core/permissions/claude.js +92 -0
  76. package/src/core/permissions/codex.js +126 -0
  77. package/src/core/permissions/common.js +44 -0
  78. package/src/core/permissions/index.js +163 -0
  79. package/src/core/project-branch-update.js +424 -0
  80. package/src/core/project-configuration.js +55 -0
  81. package/src/core/project.js +674 -0
  82. package/src/core/tools.js +34 -0
  83. package/src/core/transaction.js +132 -0
  84. package/src/core/validation.js +186 -0
  85. package/src/i18n/index.js +11 -0
  86. package/src/i18n/interpolate.js +7 -0
  87. package/src/i18n/locales/en-US.js +10 -0
  88. package/src/i18n/locales/zh-CN.js +11 -0
  89. package/src/i18n/registry.js +42 -0
  90. package/src/index.js +25 -0
  91. package/src/init/plan.js +12 -0
  92. package/src/init/ui.js +61 -0
  93. package/src/init/wizard.js +97 -0
  94. package/src/monitor/i18n/index.js +33 -0
  95. package/src/monitor/i18n/locales/en-US.js +85 -0
  96. package/src/monitor/i18n/locales/zh-CN.js +79 -0
  97. package/src/monitor/index.js +344 -0
  98. package/src/monitor/page.js +118 -0
@@ -0,0 +1,84 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://icebearx.ai/code-workspace/extension-manifest-v3.json",
4
+ "title": "Code Workspace Extension Spec v1 manifest schema v3",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schemaVersion", "extensionSpecVersion", "experimental", "id", "name", "version", "entry", "entrySha256", "timeoutMs", "outputs"],
8
+ "properties": {
9
+ "schemaVersion": { "const": 3 },
10
+ "extensionSpecVersion": { "const": 1 },
11
+ "experimental": { "const": true },
12
+ "id": { "$ref": "#/$defs/id" },
13
+ "name": { "type": "string", "minLength": 1, "maxLength": 100 },
14
+ "version": { "type": "string" },
15
+ "entry": { "const": "init.js" },
16
+ "entrySha256": { "$ref": "#/$defs/sha256" },
17
+ "timeoutMs": { "type": "integer", "minimum": 1, "maximum": 300000 },
18
+ "capabilities": {
19
+ "type": "object",
20
+ "additionalProperties": false,
21
+ "properties": {
22
+ "networkHosts": {
23
+ "type": "array",
24
+ "minItems": 1,
25
+ "uniqueItems": true,
26
+ "items": { "$ref": "#/$defs/hostname" }
27
+ }
28
+ }
29
+ },
30
+ "outputs": {
31
+ "type": "array",
32
+ "minItems": 1,
33
+ "items": {
34
+ "type": "object",
35
+ "additionalProperties": false,
36
+ "required": ["id", "kind", "ownership", "target"],
37
+ "properties": {
38
+ "id": { "$ref": "#/$defs/id" },
39
+ "kind": { "enum": ["file", "directory", "text-block", "json-member"] },
40
+ "ownership": { "enum": ["exclusive", "shared"] },
41
+ "target": { "$ref": "#/$defs/relativePath" },
42
+ "selector": { "type": "string", "pattern": "^/(?:[^~/]|~[01])+(?:/(?:[^~/]|~[01])+)*$" },
43
+ "format": { "enum": ["text", "toml"] },
44
+ "tools": {
45
+ "type": "array",
46
+ "minItems": 1,
47
+ "uniqueItems": true,
48
+ "items": { "enum": ["claude", "codex"] }
49
+ }
50
+ },
51
+ "allOf": [
52
+ {
53
+ "if": { "properties": { "kind": { "enum": ["file", "directory"] } } },
54
+ "then": { "properties": { "ownership": { "const": "exclusive" } } }
55
+ },
56
+ {
57
+ "if": { "properties": { "kind": { "enum": ["text-block", "json-member"] } } },
58
+ "then": { "properties": { "ownership": { "const": "shared" } } }
59
+ },
60
+ {
61
+ "if": { "properties": { "kind": { "const": "json-member" } } },
62
+ "then": { "required": ["selector"] },
63
+ "else": { "properties": { "selector": false } }
64
+ },
65
+ {
66
+ "if": { "properties": { "kind": { "const": "text-block" } } },
67
+ "then": { "properties": { "format": { "enum": ["text", "toml"] } } },
68
+ "else": { "properties": { "format": false } }
69
+ }
70
+ ]
71
+ }
72
+ }
73
+ },
74
+ "$defs": {
75
+ "id": { "type": "string", "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$" },
76
+ "sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
77
+ "hostname": { "type": "string", "pattern": "^(?=.{1,253}$)[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$" },
78
+ "relativePath": {
79
+ "type": "string",
80
+ "minLength": 1,
81
+ "pattern": "^(?!/)(?!.*\\\\)(?!.*(?:^|/)\\.{1,2}(?:/|$))(?!.*//).+$"
82
+ }
83
+ }
84
+ }
@@ -0,0 +1,171 @@
1
+ # Code Workspace Extension Spec v1
2
+
3
+ [简体中文](./specification.zh-CN.md) | [English](./specification.en-US.md)
4
+
5
+ > Status: Normative translation
6
+ > Extension Spec version: 1
7
+ > Language: English
8
+ > Interpretation baseline: `specification.zh-CN.md`
9
+ > Published at: `spec/extension/v1/specification.en-US.md`
10
+
11
+ This document and the JSON Schemas it references together constitute Code Workspace Extension Spec v1. Compatibility between an extension and a Host is determined only by `extensionSpecVersion`; it does not depend on the Code Workspace product version or the extension version.
12
+
13
+ ## 0. Document Status and Conformance
14
+
15
+ ### 0.1 Normative Keywords
16
+
17
+ The terms MUST and MUST NOT express conformance requirements. SHOULD means that a requirement is expected unless there is a specific, explainable reason not to follow it. MAY indicates permitted but optional behavior.
18
+
19
+ ### 0.2 Normative Components
20
+
21
+ The normative artifacts of Extension Spec v1 are:
22
+
23
+ - the entry, capability, output, staging, installation, upgrade, verification, and uninstall semantics defined by this document;
24
+ - manifest schema v3: `schemas/extension-manifest-v3.json`;
25
+ - init context schema v1: `schemas/extension-init-context-v1.json`;
26
+ - init result schema v1: `schemas/extension-init-result-v1.json`.
27
+
28
+ The JSON Schemas define document structure. This document defines cross-document and lifecycle semantics. A conflict between them is a specification defect; Hosts and extensions MUST NOT infer compatibility from such a conflict.
29
+
30
+ The Chinese and English specifications MUST express the same conformance requirements and preserve corresponding section structures. If the two languages produce different interpretations, the difference is a specification defect. Until corrected, the Chinese specification is the interpretation baseline, and a translation difference MUST NOT be treated as a compatibility capability.
31
+
32
+ ### 0.3 Conforming Parties
33
+
34
+ A conforming Spec v1 Host MUST implement every Host requirement in this document and MUST execute only extensions that pass the Spec v1 schema and lifecycle validations. A conforming Spec v1 extension MUST declare `extensionSpecVersion: 1`, follow the corresponding schemas, and MUST NOT bypass the Host when managing artifacts in the real Workspace.
35
+
36
+ All JSON, command, and directory-layout examples in this document are non-normative. They illustrate the rules but do not expand or narrow the requirements.
37
+
38
+ ## 1. Versioning and Compatibility
39
+
40
+ Extension Spec versions are discrete positive integers. A Host MUST explicitly list the versions it implements. An extension MUST declare exactly one version.
41
+
42
+ ```text
43
+ compatible ⇔ extension.extensionSpecVersion ∈ host.supportedExtensionSpecVersions
44
+ ```
45
+
46
+ A component `schemaVersion` describes the format of one JSON document. `extensionSpecVersion` describes the complete development and execution contract between a Host and an extension.
47
+
48
+ A product release, a private implementation change, or an extension SemVer change does not automatically create a new Extension Spec. A new specification version is required only when the manifest, context/result, output, or lifecycle contract has a non-backward-compatible, machine-observable change.
49
+
50
+ ## 2. Stable Discovery Envelope
51
+
52
+ Every manifest in every specification version MUST retain the following fields so that an older Host can identify, but not execute, an unknown specification:
53
+
54
+ ```json
55
+ {
56
+ "extensionSpecVersion": 1,
57
+ "id": "example-extension",
58
+ "name": "Example Extension",
59
+ "version": "1.0.0"
60
+ }
61
+ ```
62
+
63
+ For an unknown `extensionSpecVersion`, the Host MUST read only these fields and MUST NOT interpret the entry, capabilities, or outputs. When an extension contains multiple versions, the Host selects the highest extension SemVer implemented against a specification version that the Host supports.
64
+
65
+ ## 3. Manifest
66
+
67
+ A Spec v1 manifest uses manifest schema v3. The manifest is the static declaration of installation permissions and maximum output scope; it is not a post-execution report.
68
+
69
+ ```json
70
+ {
71
+ "schemaVersion": 3,
72
+ "extensionSpecVersion": 1,
73
+ "experimental": true,
74
+ "id": "example-extension",
75
+ "name": "Example Extension",
76
+ "version": "1.0.0",
77
+ "entry": "init.js",
78
+ "entrySha256": "<sha256>",
79
+ "timeoutMs": 30000,
80
+ "capabilities": {
81
+ "networkHosts": ["example.com"]
82
+ },
83
+ "outputs": [
84
+ {
85
+ "id": "runtime",
86
+ "kind": "directory",
87
+ "ownership": "exclusive",
88
+ "target": ".code-workspace/extensions/example-extension/1.0.0"
89
+ }
90
+ ]
91
+ }
92
+ ```
93
+
94
+ The `codeWorkspace` product-version range is not part of Extension Spec and MUST NOT appear in a Spec v1 manifest.
95
+
96
+ Before confirmation, the Host MUST freeze the manifest, entry, and complete extension-version directory digest. The Host MUST verify them again before execution. An extension MUST NOT use a dynamic result to expand the declarations in its manifest.
97
+
98
+ ## 4. Execution Entry
99
+
100
+ The entry MUST be `init.js` inside the extension-version directory. The Host executes it with the current Node.js runtime:
101
+
102
+ ```text
103
+ node init.js --context <context.json> --output <staging-directory> --result <result.json>
104
+ ```
105
+
106
+ The Host MUST pass only the environment-variable allowlist required for execution. Process isolation is not a security sandbox; Spec v1 executes trusted bundled extensions only.
107
+
108
+ The entry MUST exit within the timeout declared by the manifest. A failure, timeout, missing result, or invalid result MUST NOT produce writes to the real Workspace.
109
+
110
+ ## 5. Context and Result
111
+
112
+ The context and result MUST both echo `extensionSpecVersion: 1`. The value MUST match the frozen plan.
113
+
114
+ The context contains only extension identity, non-sensitive Workspace display metadata, and the selected tools. It MUST NOT contain the real Workspace root or credentials.
115
+
116
+ The result contains only the extension identity and a list of `{id, source}` entries. It MUST NOT declare targets, kinds, ownership, selectors, digests, or network capabilities.
117
+
118
+ ## 6. Capability Declarations
119
+
120
+ Spec v1 defines only `capabilities.networkHosts`. The field is used in planning, confirmation, and diagnostics to describe the HTTPS hosts that the extension expects to access.
121
+
122
+ Without operating-system-level enforcement, the Host MUST NOT claim that it can prevent a trusted extension from accessing other network or user resources.
123
+
124
+ ## 7. Output Kinds
125
+
126
+ Spec v1 supports four output kinds:
127
+
128
+ | kind | ownership | Lifecycle semantics |
129
+ |---|---|---|
130
+ | `file` | `exclusive` | The Host exclusively writes, digests, drift-checks, and removes one regular file |
131
+ | `directory` | `exclusive` | The Host exclusively replaces, canonically digests, drift-checks, and recursively removes a directory |
132
+ | `text-block` | `shared` | The Host manages a marked text contribution using the extension id and output id |
133
+ | `json-member` | `shared` | The Host manages one member selected by JSON Pointer while preserving other content |
134
+
135
+ Public output kinds MUST NOT encode private business concepts such as Jira, MCP, npm, archive formats, or individual Agent products.
136
+
137
+ ## 8. Staging Verification
138
+
139
+ An extension MUST generate candidate content only inside the staging directory supplied by the Host. The Host MUST:
140
+
141
+ - normalize result sources and reject path traversal;
142
+ - require the result to return exactly every output id applicable to the current execution;
143
+ - reject unknown, duplicate, missing, overlapping, or extra outputs;
144
+ - reject symbolic links, devices, sockets, FIFOs, and other special files;
145
+ - compute file and directory digests from the actual staging content.
146
+
147
+ A digest reported by the extension MUST NOT be accepted as an installed fact.
148
+
149
+ ## 9. Installation, Upgrade, and Uninstall
150
+
151
+ Each extension uses an independent transaction to commit its exclusive outputs, shared contributions, and installed state. The transaction MUST NOT commit until every postcondition passes.
152
+
153
+ A new installation MUST record:
154
+
155
+ - the installed-record version;
156
+ - `extensionSpecVersion`;
157
+ - the extension version;
158
+ - the manifest and complete extension-package digests;
159
+ - output ownership and the installed facts computed by the Host.
160
+
161
+ Idempotency MUST verify both installed state and the real Workspace. A failed upgrade MUST restore the previous artifacts and previous installed state.
162
+
163
+ Uninstall depends only on installed state. It MUST NOT read the current extension package or execute extension code. Even when the Host no longer supports the execution specification of an installed extension, it MUST safely verify and uninstall the artifacts whenever it can still read the corresponding installed record.
164
+
165
+ ## 10. Specification Evolution
166
+
167
+ A new extension that fully reuses Spec v1 capabilities MUST NOT require changes to Host core or the public schemas.
168
+
169
+ A new public capability can enter a later specification version only when it has cross-extension semantics and a complete Host-managed installation, verification, upgrade, rollback, and uninstall lifecycle. A Host MUST fail safely on an unknown specification version and MUST NOT infer compatibility from numeric ranges.
170
+
171
+ After Spec v1 is published, its machine-observable semantics remain unchanged. Text-only clarifications that do not alter conformance outcomes MAY continue to be published in this directory. Any breaking change MUST use a new `spec/extension/<version>/` directory and a new `extensionSpecVersion`.
@@ -0,0 +1,170 @@
1
+ # Code Workspace Extension Spec v1
2
+
3
+ [简体中文](./specification.zh-CN.md) | [English](./specification.en-US.md)
4
+
5
+ > 状态:规范性(Normative)
6
+ > Extension Spec 版本:1
7
+ > 语言:简体中文(解释基准)
8
+ > 规范发布路径:`spec/extension/v1/specification.zh-CN.md`
9
+
10
+ 本文档与其引用的 JSON Schema 共同构成 Code Workspace Extension Spec v1。扩展与 Host 的兼容性只由 `extensionSpecVersion` 判断,不依赖 Code Workspace 产品版本或扩展版本。
11
+
12
+ ## 0. 文档地位与一致性
13
+
14
+ ### 0.1 规范关键字
15
+
16
+ 本文中的“必须”“不得”表示一致性要求;“应当”表示除非存在明确且可说明的理由,否则需要遵守;“可以”表示允许但不强制。
17
+
18
+ ### 0.2 规范组成
19
+
20
+ Extension Spec v1 的规范性制品包括:
21
+
22
+ - 本文定义的入口、能力、输出、staging、安装、升级、验证和卸载语义;
23
+ - manifest schema v3:`schemas/extension-manifest-v3.json`;
24
+ - init context schema v1:`schemas/extension-init-context-v1.json`;
25
+ - init result schema v1:`schemas/extension-init-result-v1.json`。
26
+
27
+ JSON Schema 规定文档结构,本文规定跨文档和生命周期语义。二者出现冲突时属于规范缺陷,Host 和扩展不得自行猜测兼容性。
28
+
29
+ 中文与英文规范必须表达相同的一致性要求并保持对应章节结构。两种语言产生不同解释时,差异属于规范缺陷;修正前以中文版本为解释基准,不得将翻译差异当作兼容能力。
30
+
31
+ ### 0.3 一致性主体
32
+
33
+ 符合 Spec v1 的 Host 必须实现本文对 Host 的全部要求,并且只执行通过 Spec v1 schema 和生命周期校验的扩展。符合 Spec v1 的扩展必须声明 `extensionSpecVersion: 1`,遵守对应 schema,并且不得绕过 Host 管理真实 Workspace 制品。
34
+
35
+ 本文中的 JSON、命令和目录结构示例均为非规范性示例;它们用于说明规则,不能扩大或缩小规范要求。
36
+
37
+ ## 1. 版本与兼容性
38
+
39
+ Extension Spec 使用离散正整数版本。Host 必须明确列出自己实现的版本集合;扩展必须声明一个版本。
40
+
41
+ ```text
42
+ 兼容 ⇔ extension.extensionSpecVersion ∈ host.supportedExtensionSpecVersions
43
+ ```
44
+
45
+ 组件的 `schemaVersion` 只描述单个 JSON 文档格式;`extensionSpecVersion` 描述 Host 与扩展之间的完整开发和执行契约。
46
+
47
+ 产品发布、扩展私有实现或扩展 SemVer 变化不会自动产生新的 Extension Spec。只有 manifest、context/result、输出或生命周期出现不向后兼容的机器可观察变化时,才发布新的规范版本。
48
+
49
+ ## 2. 稳定发现 Envelope
50
+
51
+ 所有规范版本的 manifest 必须保留以下字段,使旧 Host 可以识别但不执行未知规范:
52
+
53
+ ```json
54
+ {
55
+ "extensionSpecVersion": 1,
56
+ "id": "example-extension",
57
+ "name": "Example Extension",
58
+ "version": "1.0.0"
59
+ }
60
+ ```
61
+
62
+ Host 对未知 `extensionSpecVersion` 只能读取上述字段,不得解释入口、能力或输出。一个扩展包含多个版本时,Host 从自己支持的规范实现中选择最高扩展 SemVer。
63
+
64
+ ## 3. Manifest
65
+
66
+ Spec v1 manifest 使用 schema v3。manifest 是安装权限和最大输出范围的静态声明,不是扩展执行后的报告。
67
+
68
+ ```json
69
+ {
70
+ "schemaVersion": 3,
71
+ "extensionSpecVersion": 1,
72
+ "experimental": true,
73
+ "id": "example-extension",
74
+ "name": "Example Extension",
75
+ "version": "1.0.0",
76
+ "entry": "init.js",
77
+ "entrySha256": "<sha256>",
78
+ "timeoutMs": 30000,
79
+ "capabilities": {
80
+ "networkHosts": ["example.com"]
81
+ },
82
+ "outputs": [
83
+ {
84
+ "id": "runtime",
85
+ "kind": "directory",
86
+ "ownership": "exclusive",
87
+ "target": ".code-workspace/extensions/example-extension/1.0.0"
88
+ }
89
+ ]
90
+ }
91
+ ```
92
+
93
+ `codeWorkspace` 产品版本范围不属于 Extension Spec,不得出现在 Spec v1 manifest 中。
94
+
95
+ Host 在确认前冻结 manifest、入口及完整扩展版本目录摘要,在执行前重新验证。扩展不得通过动态 result 扩大 manifest 声明。
96
+
97
+ ## 4. 执行入口
98
+
99
+ 入口必须是扩展版本目录中的 `init.js`,由 Host 使用当前 Node 运行时执行:
100
+
101
+ ```text
102
+ node init.js --context <context.json> --output <staging-directory> --result <result.json>
103
+ ```
104
+
105
+ Host 只传递运行所需的环境变量白名单。子进程隔离不是安全沙箱;Spec v1 只执行可信内置扩展。
106
+
107
+ 入口必须在 manifest 声明的超时内退出。失败、超时、缺少 result 或非法 result 均不得产生真实 Workspace 写入。
108
+
109
+ ## 5. Context 与 Result
110
+
111
+ context 和 result 必须同时回显 `extensionSpecVersion: 1`。该值必须与冻结计划一致。
112
+
113
+ context 只包含扩展身份、非敏感 Workspace 元数据和工具选择,不包含真实 Workspace 根目录或凭证。
114
+
115
+ result 只包含扩展身份和 `{id, source}` 列表。它不得声明 target、kind、ownership、selector、摘要或网络能力。
116
+
117
+ ## 6. 能力声明
118
+
119
+ Spec v1 只定义 `capabilities.networkHosts`。该字段用于安装计划、确认和诊断,表达扩展预期访问的 HTTPS host。
120
+
121
+ 在没有 OS 级 enforcement 时,Host 不得声称它能阻止可信扩展访问其他网络或用户资源。
122
+
123
+ ## 7. 输出类型
124
+
125
+ Spec v1 支持四种输出:
126
+
127
+ | kind | ownership | 生命周期语义 |
128
+ |---|---|---|
129
+ | `file` | `exclusive` | Host 独占写入、摘要、漂移检查和删除单个普通文件 |
130
+ | `directory` | `exclusive` | Host 独占替换、规范目录摘要、漂移检查和递归删除目录 |
131
+ | `text-block` | `shared` | Host 以扩展 id/output id 标记并管理文本片段 |
132
+ | `json-member` | `shared` | Host 以 JSON Pointer selector 管理单个成员并保留其他内容 |
133
+
134
+ 公共输出不得包含 Jira、MCP、npm、归档或特定 Agent 产品的业务类型。
135
+
136
+ ## 8. Staging 验证
137
+
138
+ 扩展只能在 Host 提供的 staging 目录生成候选内容。Host 必须:
139
+
140
+ - 规范化 result source 并拒绝路径逃逸;
141
+ - 要求 result 恰好返回本次适用的全部 output id;
142
+ - 拒绝未知、重复、缺失、重叠或额外输出;
143
+ - 拒绝符号链接、设备、socket、FIFO 和其他特殊文件;
144
+ - 根据真实 staging 内容计算文件或目录摘要。
145
+
146
+ 扩展报告的摘要不能作为 installed 事实。
147
+
148
+ ## 9. 安装、升级与卸载
149
+
150
+ 每个扩展使用独立事务提交其独占输出、共享 contribution 和 installed 状态。完整后置条件通过前不得提交。
151
+
152
+ 新安装必须记录:
153
+
154
+ - installed record 版本;
155
+ - `extensionSpecVersion`;
156
+ - 扩展版本;
157
+ - manifest 和完整扩展包摘要;
158
+ - 输出所有权以及 Host 计算的 installed 事实。
159
+
160
+ 幂等判断必须同时验证 installed 状态和真实 Workspace。升级失败必须恢复旧制品和旧 installed 状态。
161
+
162
+ 卸载只依据 installed 状态,不读取当前扩展包,也不执行扩展代码。Host 即使不再支持某个扩展的执行规范,只要仍能读取对应 installed record,就必须能够安全验证和卸载其制品。
163
+
164
+ ## 10. 规范演进
165
+
166
+ 新扩展完全复用 Spec v1 能力时,不得修改 Host 核心或公共 schema。
167
+
168
+ 新公共能力只有同时具备跨扩展语义以及安装、验证、升级、回滚和卸载闭环时,才可以进入后续规范版本。Host 对未知规范版本必须安全失败,不得按数字范围猜测兼容。
169
+
170
+ Spec v1 发布后,其机器可观察语义保持不变。仅文字澄清且不改变一致性结果的修订可以继续发布在本目录;任何破坏性变化必须使用新的 `spec/extension/<version>/` 目录和新的 `extensionSpecVersion`。
@@ -0,0 +1,203 @@
1
+ const { WorkspaceError } = require("../../core/errors");
2
+ const { COMMANDS, GLOBAL_OPTIONS } = require("../registry");
3
+ const { success } = require("../result");
4
+
5
+ const SUPPORTED_SHELLS = ["bash", "zsh"];
6
+
7
+ function optionToken(name) {
8
+ return `--${name}`;
9
+ }
10
+
11
+ function aliasToken(alias) {
12
+ return alias.length === 1 ? `-${alias}` : `--${alias}`;
13
+ }
14
+
15
+ function optionTokens(definitions) {
16
+ return Object.entries(definitions).flatMap(([name, definition]) => [
17
+ optionToken(name),
18
+ ...(definition.aliases || []).map(aliasToken),
19
+ ]);
20
+ }
21
+
22
+ function buildCompletionSpec(commands = COMMANDS, globalOptions = GLOBAL_OPTIONS) {
23
+ const children = new Map();
24
+ const addChild = (prefix, child) => {
25
+ const key = prefix.join(" ");
26
+ const values = children.get(key) || [];
27
+ if (!values.includes(child)) values.push(child);
28
+ children.set(key, values);
29
+ };
30
+ for (const command of commands) {
31
+ for (let index = 0; index < command.path.length; index += 1) {
32
+ addChild(command.path.slice(0, index), command.path[index]);
33
+ }
34
+ }
35
+ const globals = optionTokens(globalOptions);
36
+ return {
37
+ globals,
38
+ children: [...children.entries()].map(([key, values]) => ({
39
+ path: key ? key.split(" ") : [],
40
+ values,
41
+ })),
42
+ commands: commands.map((command) => ({
43
+ path: [...command.path],
44
+ options: [...new Set([...globals, ...optionTokens(command.options)])],
45
+ })),
46
+ };
47
+ }
48
+
49
+ function bashWords(values) {
50
+ return values.join(" ").replace(/(["\\$`])/g, "\\$1");
51
+ }
52
+
53
+ function bashPathCondition(path) {
54
+ return path.map((part, index) => `[[ "\${COMP_WORDS[$((command_index + ${index}))]}" == "${part}" ]]`).join(" && ");
55
+ }
56
+
57
+ function renderBashCompletion(spec) {
58
+ const root = spec.children.find((entry) => entry.path.length === 0)?.values || [];
59
+ const childBlocks = spec.children
60
+ .filter((entry) => entry.path.length > 0)
61
+ .map((entry) => [
62
+ ` if (( depth == ${entry.path.length} )) && ${bashPathCondition(entry.path)} && [[ "$current" != -* ]]; then`,
63
+ ` COMPREPLY=( $(compgen -W "${bashWords(entry.values)}" -- "$current") )`,
64
+ " return",
65
+ " fi",
66
+ ].join("\n"))
67
+ .join("\n");
68
+ const optionBlocks = [...spec.commands]
69
+ .sort((left, right) => right.path.length - left.path.length)
70
+ .map((entry) => [
71
+ ` if ${bashPathCondition(entry.path)}; then`,
72
+ ` COMPREPLY=( $(compgen -W "${bashWords(entry.options)}" -- "$current") )`,
73
+ " return",
74
+ " fi",
75
+ ].join("\n"))
76
+ .join("\n");
77
+ return [
78
+ "_code_workspace() {",
79
+ " local current=\"${COMP_WORDS[COMP_CWORD]}\"",
80
+ " local command_index=1",
81
+ " local depth",
82
+ " COMPREPLY=()",
83
+ " while (( command_index < COMP_CWORD )); do",
84
+ " case \"${COMP_WORDS[command_index]}\" in",
85
+ ` ${spec.globals.join("|")}) ((command_index += 1)) ;;`,
86
+ " *) break ;;",
87
+ " esac",
88
+ " done",
89
+ " depth=$((COMP_CWORD - command_index))",
90
+ " if [[ \"${COMP_WORDS[COMP_CWORD-1]}\" == \"--shell\" ]]; then",
91
+ ` COMPREPLY=( $(compgen -W "${SUPPORTED_SHELLS.join(" ")}" -- "$current") )`,
92
+ " return",
93
+ " fi",
94
+ " if (( depth == 0 )); then",
95
+ " if [[ \"$current\" == -* ]]; then",
96
+ ` COMPREPLY=( $(compgen -W "${bashWords(spec.globals)}" -- "$current") )`,
97
+ " else",
98
+ ` COMPREPLY=( $(compgen -W "${bashWords(root)}" -- "$current") )`,
99
+ " fi",
100
+ " return",
101
+ " fi",
102
+ childBlocks,
103
+ " if [[ \"$current\" != -* ]]; then return; fi",
104
+ optionBlocks,
105
+ "}",
106
+ "complete -F _code_workspace code-workspace code-w",
107
+ ].filter(Boolean).join("\n");
108
+ }
109
+
110
+ function zshQuote(value) {
111
+ return `'${value.replace(/'/g, `'\\''`)}'`;
112
+ }
113
+
114
+ function zshArray(values) {
115
+ return values.map(zshQuote).join(" ");
116
+ }
117
+
118
+ function zshPathCondition(path) {
119
+ return path.map((part, index) => `[[ "\${words[$((command_index + ${index}))]}" == ${zshQuote(part)} ]]`).join(" && ");
120
+ }
121
+
122
+ function renderZshCompletion(spec) {
123
+ const root = spec.children.find((entry) => entry.path.length === 0)?.values || [];
124
+ const childBlocks = spec.children
125
+ .filter((entry) => entry.path.length > 0)
126
+ .map((entry) => [
127
+ ` if (( depth == ${entry.path.length} )) && ${zshPathCondition(entry.path)} && [[ "$current" != -* ]]; then`,
128
+ ` candidates=( ${zshArray(entry.values)} )`,
129
+ " compadd -- \"${candidates[@]}\"",
130
+ " return",
131
+ " fi",
132
+ ].join("\n"))
133
+ .join("\n");
134
+ const optionBlocks = [...spec.commands]
135
+ .sort((left, right) => right.path.length - left.path.length)
136
+ .map((entry) => [
137
+ ` if ${zshPathCondition(entry.path)}; then`,
138
+ ` candidates=( ${zshArray(entry.options)} )`,
139
+ " compadd -- \"${candidates[@]}\"",
140
+ " return",
141
+ " fi",
142
+ ].join("\n"))
143
+ .join("\n");
144
+ return [
145
+ "#compdef code-workspace code-w",
146
+ "_code_workspace() {",
147
+ " local current=\"${words[CURRENT]}\"",
148
+ " local command_index=2",
149
+ " local depth",
150
+ " local -a candidates",
151
+ " while (( command_index < CURRENT )); do",
152
+ " case \"${words[command_index]}\" in",
153
+ ` ${spec.globals.join("|")}) ((command_index += 1)) ;;`,
154
+ " *) break ;;",
155
+ " esac",
156
+ " done",
157
+ " depth=$((CURRENT - command_index))",
158
+ " if [[ \"${words[CURRENT-1]}\" == \"--shell\" ]]; then",
159
+ ` candidates=( ${zshArray(SUPPORTED_SHELLS)} )`,
160
+ " compadd -- \"${candidates[@]}\"",
161
+ " return",
162
+ " fi",
163
+ " if (( depth == 0 )); then",
164
+ " if [[ \"$current\" == -* ]]; then",
165
+ ` candidates=( ${zshArray(spec.globals)} )`,
166
+ " else",
167
+ ` candidates=( ${zshArray(root)} )`,
168
+ " fi",
169
+ " compadd -- \"${candidates[@]}\"",
170
+ " return",
171
+ " fi",
172
+ childBlocks,
173
+ " if [[ \"$current\" != -* ]]; then return; fi",
174
+ optionBlocks,
175
+ "}",
176
+ "compdef _code_workspace code-workspace code-w",
177
+ ].filter(Boolean).join("\n");
178
+ }
179
+
180
+ function executeCompletion(invocation) {
181
+ const shell = invocation.options.shell || "zsh";
182
+ const spec = buildCompletionSpec();
183
+ const script = shell === "bash"
184
+ ? renderBashCompletion(spec)
185
+ : shell === "zsh"
186
+ ? renderZshCompletion(spec)
187
+ : null;
188
+ if (!script) {
189
+ throw new WorkspaceError("CLI_COMPLETION_SHELL_UNSUPPORTED", `Unsupported completion shell: ${shell}`, {
190
+ actual: shell,
191
+ supported: SUPPORTED_SHELLS,
192
+ });
193
+ }
194
+ return success("completion", { shell, script }, script);
195
+ }
196
+
197
+ module.exports = {
198
+ SUPPORTED_SHELLS,
199
+ buildCompletionSpec,
200
+ executeCompletion,
201
+ renderBashCompletion,
202
+ renderZshCompletion,
203
+ };