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,126 @@
1
+ const fs = require("node:fs");
2
+ const path = require("node:path");
3
+
4
+ const { WorkspaceError } = require("../errors");
5
+ const { atomicWrite } = require("../fs");
6
+ const { assertFingerprint, planDirectoryMutation, readTarget } = require("./common");
7
+
8
+ const START = "# BEGIN workspace-permissions:code-workspace";
9
+ const END = "# END workspace-permissions:code-workspace";
10
+
11
+ function escapeRegExp(value) {
12
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
13
+ }
14
+
15
+ function blockPattern(start = START, end = END) {
16
+ return new RegExp(`${escapeRegExp(start)}[\\s\\S]*?${escapeRegExp(end)}\\n?`, "g");
17
+ }
18
+
19
+ function render(directories) {
20
+ const roots = directories.map((directory) => ` ${JSON.stringify(directory)},`).join("\n");
21
+ return `${START}\nsandbox_mode = "workspace-write"\napproval_policy = "on-request"\n\n[sandbox_workspace_write]\nwritable_roots = [\n${roots}\n]\n${END}\n`;
22
+ }
23
+
24
+ function managedBlock(text) {
25
+ return text.match(blockPattern())?.[0] || null;
26
+ }
27
+
28
+ function parseManagedRoots(text, file) {
29
+ const block = managedBlock(text);
30
+ if (!block) return [];
31
+ const array = block.match(/writable_roots\s*=\s*\[([\s\S]*?)\]/)?.[1] || "";
32
+ const roots = [];
33
+ for (const line of array.split(/\r?\n/)) {
34
+ const value = line.trim().replace(/,$/, "");
35
+ if (!value) continue;
36
+ try {
37
+ const parsed = JSON.parse(value);
38
+ if (typeof parsed !== "string") throw new Error("entry is not a string");
39
+ roots.push(parsed);
40
+ } catch (error) {
41
+ throw new WorkspaceError("CODEX_PERMISSION_CONFIG_INVALID", `Cannot parse Codex writable root in ${file}: ${error.message}`, { file });
42
+ }
43
+ }
44
+ return roots;
45
+ }
46
+
47
+ function unmanagedConflicts(text) {
48
+ const stripped = text.replace(blockPattern(), "");
49
+ const conflicts = [];
50
+ let section = "";
51
+ for (const line of stripped.split(/\r?\n/)) {
52
+ const trimmed = line.trim();
53
+ if (!trimmed || trimmed.startsWith("#")) continue;
54
+ const header = trimmed.match(/^\[([^\]]+)\]$/);
55
+ if (header) {
56
+ section = header[1];
57
+ if (section === "sandbox_workspace_write") conflicts.push("[sandbox_workspace_write]");
58
+ continue;
59
+ }
60
+ if (!section && /^(sandbox_mode|approval_policy)\s*=/.test(trimmed)) conflicts.push(trimmed.split("=")[0].trim());
61
+ }
62
+ return [...new Set(conflicts)];
63
+ }
64
+
65
+ function desiredContent(current, directories) {
66
+ const outside = current.replace(blockPattern(), "").replace(/^\s+/, "");
67
+ const desired = `${render(directories)}${outside ? `\n${outside}` : ""}`;
68
+ return desired.endsWith("\n") ? desired : `${desired}\n`;
69
+ }
70
+
71
+ const codexPermissionAdapter = {
72
+ id: "codex",
73
+ targets(root) {
74
+ return [path.join(root, ".codex", "config.toml")];
75
+ },
76
+ inspect(root) {
77
+ const target = readTarget(this.targets(root)[0]);
78
+ const conflicts = unmanagedConflicts(target.content);
79
+ if (conflicts.length > 0) {
80
+ throw new WorkspaceError("WORKSPACE_PERMISSIONS_CONFLICT", `Unmanaged Codex sandbox configuration conflicts: ${conflicts.join(", ")}`, {
81
+ tool: this.id,
82
+ file: target.file,
83
+ conflicts,
84
+ });
85
+ }
86
+ return { ...target, directories: parseManagedRoots(target.content, target.file) };
87
+ },
88
+ plan({ current, grants, revokes }) {
89
+ const mutation = planDirectoryMutation(current.directories, grants, revokes);
90
+ return {
91
+ tool: this.id,
92
+ target: current.file,
93
+ targets: [current.file],
94
+ fingerprint: current.fingerprint,
95
+ ...mutation,
96
+ desiredContent: mutation.action === "write" ? desiredContent(current.content, mutation.desired) : current.content,
97
+ };
98
+ },
99
+ assertCurrent(plan) {
100
+ assertFingerprint(plan);
101
+ },
102
+ apply(plan, options = {}) {
103
+ (options.atomicWrite || atomicWrite)(plan.target, plan.desiredContent);
104
+ },
105
+ verify(plan) {
106
+ let actual;
107
+ try {
108
+ actual = fs.readFileSync(plan.target, "utf8");
109
+ } catch (error) {
110
+ throw new WorkspaceError("WORKSPACE_PERMISSION_VERIFY_FAILED", `Cannot verify Codex directory authorization: ${error.message}`, {
111
+ tool: this.id,
112
+ file: plan.target,
113
+ remediation: "Inspect .codex/config.toml and re-run code-w permissions apply.",
114
+ });
115
+ }
116
+ if (actual !== plan.desiredContent) {
117
+ throw new WorkspaceError("WORKSPACE_PERMISSION_VERIFY_FAILED", "Codex directory authorization does not match the confirmed plan.", {
118
+ tool: this.id,
119
+ file: plan.target,
120
+ remediation: "Inspect .codex/config.toml and re-run code-w permissions apply.",
121
+ });
122
+ }
123
+ },
124
+ };
125
+
126
+ module.exports = { END, START, blockPattern, codexPermissionAdapter, parseManagedRoots, render, unmanagedConflicts };
@@ -0,0 +1,44 @@
1
+ const fs = require("node:fs");
2
+ const path = require("node:path");
3
+
4
+ const { WorkspaceError } = require("../errors");
5
+ const { sha256 } = require("../fs");
6
+
7
+ function readTarget(file) {
8
+ const exists = fs.existsSync(file);
9
+ const content = exists ? fs.readFileSync(file, "utf8") : "";
10
+ return { file, exists, content, fingerprint: exists ? sha256(content) : null };
11
+ }
12
+
13
+ function assertFingerprint(plan) {
14
+ const actual = readTarget(plan.target);
15
+ if (actual.fingerprint !== plan.fingerprint) {
16
+ throw new WorkspaceError("WORKSPACE_PERMISSION_PLAN_STALE", `Agent permission target changed after planning: ${plan.target}`, {
17
+ tool: plan.tool,
18
+ file: plan.target,
19
+ expectedFingerprint: plan.fingerprint,
20
+ actualFingerprint: actual.fingerprint,
21
+ remediation: "Review the changed Agent settings and retry the command.",
22
+ });
23
+ }
24
+ }
25
+
26
+ function normalizedExistingDirectory(value) {
27
+ return typeof value === "string" && path.isAbsolute(value) ? path.resolve(value) : null;
28
+ }
29
+
30
+ function planDirectoryMutation(currentDirectories, grants, revokes) {
31
+ const currentNormalized = currentDirectories.map(normalizedExistingDirectory);
32
+ const granted = grants.filter((directory) => !currentNormalized.includes(directory));
33
+ const revoked = revokes.filter((directory) => currentNormalized.includes(directory));
34
+ const unchanged = [
35
+ ...grants.filter((directory) => currentNormalized.includes(directory)),
36
+ ...revokes.filter((directory) => !currentNormalized.includes(directory)),
37
+ ];
38
+ const revokedSet = new Set(revoked);
39
+ const desired = currentDirectories.filter((directory, index) => !revokedSet.has(currentNormalized[index]));
40
+ for (const directory of granted) desired.push(directory);
41
+ return { action: granted.length > 0 || revoked.length > 0 ? "write" : "skip", granted, revoked, unchanged, desired };
42
+ }
43
+
44
+ module.exports = { assertFingerprint, normalizedExistingDirectory, planDirectoryMutation, readTarget };
@@ -0,0 +1,163 @@
1
+ const path = require("node:path");
2
+
3
+ const { WorkspaceError } = require("../errors");
4
+ const { createFileTransaction } = require("../transaction");
5
+ const { claudePermissionAdapter } = require("./claude");
6
+ const { codexPermissionAdapter } = require("./codex");
7
+
8
+ const permissionAdapters = new Map([
9
+ [claudePermissionAdapter.id, claudePermissionAdapter],
10
+ [codexPermissionAdapter.id, codexPermissionAdapter],
11
+ ]);
12
+
13
+ function normalizeDirectories(values, label) {
14
+ const normalized = [];
15
+ for (const value of values || []) {
16
+ if (typeof value !== "string" || !value.trim() || !path.isAbsolute(value.trim())) {
17
+ throw new WorkspaceError("WORKSPACE_PERMISSION_DIRECTORY_INVALID", `${label} directories must be non-empty absolute paths.`, {
18
+ label,
19
+ directory: value ?? null,
20
+ });
21
+ }
22
+ const directory = path.resolve(value.trim());
23
+ if (!normalized.includes(directory)) normalized.push(directory);
24
+ }
25
+ return normalized;
26
+ }
27
+
28
+ function resolvePermissionAdapters(tools) {
29
+ return [...new Set(tools || [])].map((tool) => {
30
+ const adapter = permissionAdapters.get(tool);
31
+ if (!adapter) {
32
+ throw new WorkspaceError(
33
+ "WORKSPACE_PERMISSION_TOOL_UNSUPPORTED",
34
+ `Agent tool does not support workspace directory authorization: ${tool}`,
35
+ { tool, remediation: "Select a permission-capable tool or install a Code Workspace release that supports it." }
36
+ );
37
+ }
38
+ return adapter;
39
+ });
40
+ }
41
+
42
+ function permissionTargets(root, tools) {
43
+ return resolvePermissionAdapters(tools).flatMap((adapter) => adapter.targets(root));
44
+ }
45
+
46
+ function planPermissionChanges({ root, tools, grants = [], revokes = [] }, options = {}) {
47
+ const normalizedGrants = normalizeDirectories(grants, "grant");
48
+ const normalizedRevokes = normalizeDirectories(revokes, "revoke");
49
+ const overlap = normalizedGrants.filter((directory) => normalizedRevokes.includes(directory));
50
+ if (overlap.length > 0) {
51
+ throw new WorkspaceError("WORKSPACE_PERMISSION_REQUEST_CONFLICT", `Directories cannot be granted and revoked in one request: ${overlap.join(", ")}`, {
52
+ directories: overlap,
53
+ });
54
+ }
55
+ const adapters = resolvePermissionAdapters(tools);
56
+ if (normalizedGrants.length === 0 && normalizedRevokes.length === 0) {
57
+ const plans = adapters.map((adapter) => {
58
+ const target = adapter.targets(root)[0];
59
+ return { tool: adapter.id, target, targets: [target], action: "skip", granted: [], revoked: [], unchanged: [], passive: true };
60
+ });
61
+ return {
62
+ root: path.resolve(root),
63
+ tools: adapters.map((adapter) => adapter.id),
64
+ requested: { grants: [], revokes: [] },
65
+ action: "skip",
66
+ targets: plans.flatMap((plan) => plan.targets),
67
+ plans,
68
+ };
69
+ }
70
+ const plans = adapters.map((adapter) => {
71
+ const current = adapter.inspect(root, options);
72
+ return adapter.plan({ root, current, grants: normalizedGrants, revokes: normalizedRevokes });
73
+ });
74
+ return {
75
+ root: path.resolve(root),
76
+ tools: adapters.map((adapter) => adapter.id),
77
+ requested: { grants: normalizedGrants, revokes: normalizedRevokes },
78
+ action: plans.some((plan) => plan.action === "write") ? "write" : "skip",
79
+ targets: plans.flatMap((plan) => plan.targets),
80
+ plans,
81
+ };
82
+ }
83
+
84
+ function permissionResult(plan) {
85
+ return {
86
+ action: plan.action,
87
+ requestedTools: plan.tools,
88
+ requested: plan.requested,
89
+ tools: plan.plans.map((toolPlan) => ({
90
+ tool: toolPlan.tool,
91
+ target: path.relative(plan.root, toolPlan.target),
92
+ action: toolPlan.action,
93
+ granted: toolPlan.granted,
94
+ revoked: toolPlan.revoked,
95
+ unchanged: toolPlan.unchanged,
96
+ verified: true,
97
+ })),
98
+ };
99
+ }
100
+
101
+ function applyPermissionPlan(plan, options = {}) {
102
+ const transaction = options.transaction || createFileTransaction(plan.targets);
103
+ const ownsTransaction = !options.transaction;
104
+ try {
105
+ for (const toolPlan of plan.plans) {
106
+ if (toolPlan.passive) continue;
107
+ const adapter = permissionAdapters.get(toolPlan.tool);
108
+ adapter.assertCurrent(toolPlan, options);
109
+ }
110
+ for (const toolPlan of plan.plans) {
111
+ if (toolPlan.action === "skip") continue;
112
+ const adapter = permissionAdapters.get(toolPlan.tool);
113
+ adapter.apply(toolPlan, options);
114
+ options.injectFailure?.(`after-${toolPlan.tool}-permission-write`, toolPlan);
115
+ }
116
+ for (const toolPlan of plan.plans) {
117
+ if (toolPlan.action === "skip") continue;
118
+ const adapter = permissionAdapters.get(toolPlan.tool);
119
+ adapter.verify(toolPlan, options);
120
+ options.injectFailure?.(`after-${toolPlan.tool}-permission-verify`, toolPlan);
121
+ }
122
+ if (ownsTransaction) transaction.commit();
123
+ return permissionResult(plan);
124
+ } catch (error) {
125
+ if (ownsTransaction) transaction.rollback(error);
126
+ throw error;
127
+ }
128
+ }
129
+
130
+ function inspectProjectPermissions({ root, tools, projects }, options = {}) {
131
+ const directories = normalizeDirectories((projects || []).map((project) => project.location), "project");
132
+ return resolvePermissionAdapters(tools).map((adapter) => {
133
+ const current = adapter.inspect(root, options);
134
+ const authorized = new Set(current.directories);
135
+ return {
136
+ tool: adapter.id,
137
+ target: path.relative(path.resolve(root), current.file),
138
+ directories: current.directories,
139
+ missing: directories.filter((directory) => !authorized.has(directory)),
140
+ };
141
+ });
142
+ }
143
+
144
+ function formatPermissionPlan(plan) {
145
+ const lines = [];
146
+ for (const toolPlan of plan.plans) {
147
+ for (const directory of toolPlan.granted) lines.push(` GRANT ${directory} (${toolPlan.tool})`);
148
+ for (const directory of toolPlan.revoked) lines.push(` REVOKE ${directory} (${toolPlan.tool})`);
149
+ }
150
+ if (lines.length === 0) return "No Agent authorization changes are required.";
151
+ return ["Agent authorization changes:", ...lines, "Affected files:", ...plan.plans.filter((entry) => entry.action === "write").map((entry) => ` ${path.relative(plan.root, entry.target)}`)].join("\n");
152
+ }
153
+
154
+ module.exports = {
155
+ applyPermissionPlan,
156
+ formatPermissionPlan,
157
+ inspectProjectPermissions,
158
+ normalizeDirectories,
159
+ permissionAdapters,
160
+ permissionTargets,
161
+ planPermissionChanges,
162
+ resolvePermissionAdapters,
163
+ };