clay-server 5.2.0-beta.5 → 5.2.0-beta.6

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 (151) hide show
  1. package/lib/claude-hook-installer.js +1 -267
  2. package/lib/claude-jsonl-watcher.js +1 -204
  3. package/lib/clay-skill-install.js +1 -33
  4. package/lib/cli-sessions.js +1 -300
  5. package/lib/codex-defaults.js +1 -28
  6. package/lib/context-usage.js +3 -2
  7. package/lib/daemon.js +5 -55
  8. package/lib/default-ai-runtime.js +4 -8
  9. package/lib/email-mcp-server.js +1 -1
  10. package/lib/kiro-defaults.js +1 -22
  11. package/lib/mcp-proxy.js +2 -32
  12. package/lib/mcp-query-bridge.js +1 -1
  13. package/lib/mcp-source-adapters.js +1 -91
  14. package/lib/mcp-source-context.js +1 -42
  15. package/lib/mcp-source-discovery.js +1 -169
  16. package/lib/mcp-source-plugins.js +1 -68
  17. package/lib/mcp-source-restrictions.js +1 -47
  18. package/lib/mcp-tool-policy.js +1 -26
  19. package/lib/os-user-diagnostics.js +2 -4
  20. package/lib/os-users.js +4 -65
  21. package/lib/project-connection.js +8 -7
  22. package/lib/project-http.js +4 -4
  23. package/lib/project-mcp.js +2 -2
  24. package/lib/project-pair-result-delivery.js +2 -2
  25. package/lib/project-pair-usage.js +12 -13
  26. package/lib/project-pending-message-queue.js +3 -1
  27. package/lib/project-session-history-actions.js +5 -3
  28. package/lib/project-session-pair.js +1 -1
  29. package/lib/project-session-runtime-actions.js +2 -1
  30. package/lib/project-session-spawn.js +2 -3
  31. package/lib/project-session-vendor.js +2 -1
  32. package/lib/project-sessions.js +18 -26
  33. package/lib/project-user-message.js +1 -0
  34. package/lib/project-vendor-login.js +6 -36
  35. package/lib/public/app.js +1 -0
  36. package/lib/public/css/messages.css +6 -2
  37. package/lib/public/modules/app-favicon.js +11 -2
  38. package/lib/public/modules/app-header.js +4 -2
  39. package/lib/public/modules/app-loop-ui.js +2 -0
  40. package/lib/public/modules/app-messages.js +36 -5
  41. package/lib/public/modules/app-notifications.js +1 -2
  42. package/lib/public/modules/app-panels.js +61 -123
  43. package/lib/public/modules/app-rate-limit.js +7 -27
  44. package/lib/public/modules/chat-render-runtime.js +3 -0
  45. package/lib/public/modules/mcp-connection-view.js +1 -1
  46. package/lib/public/modules/permission-control.js +5 -4
  47. package/lib/public/modules/scheduler.js +2 -0
  48. package/lib/public/modules/session-create-form.js +10 -10
  49. package/lib/public/modules/settings-defaults.js +27 -28
  50. package/lib/public/modules/tools.js +2 -1
  51. package/lib/public/modules/tui-grab.js +1 -2
  52. package/lib/public/modules/vendor-login-browser.js +5 -3
  53. package/lib/public/modules/vendor-login.js +2 -2
  54. package/lib/public/modules/vendor-priority.js +15 -1
  55. package/lib/public/muse-avatar.svg +4 -0
  56. package/lib/safe-bash-commands.js +3 -45
  57. package/lib/sdk-bridge.js +127 -395
  58. package/lib/sdk-foreground-admission.js +16 -0
  59. package/lib/sdk-mcp-descriptors.js +2 -37
  60. package/lib/sdk-message-processor.js +50 -17
  61. package/lib/sdk-quota-snapshot.js +1 -1
  62. package/lib/sdk-response-recovery.js +1 -155
  63. package/lib/sdk-worker.js +2 -677
  64. package/lib/server.js +1 -1
  65. package/lib/session-fork-history.js +21 -0
  66. package/lib/session-hygiene.js +3 -35
  67. package/lib/session-permission-mode.js +4 -3
  68. package/lib/session-skill-scope.js +11 -0
  69. package/lib/sessions.js +17 -89
  70. package/lib/tool-llm.js +1 -1
  71. package/lib/tui-transcript-index.js +1 -132
  72. package/lib/vendor-login-browser.js +1 -11
  73. package/lib/yoke/acp-agent-profiles.js +5 -3
  74. package/lib/yoke/adapters/antigravity.js +3 -14
  75. package/lib/yoke/adapters/claude-query-contract.js +9 -0
  76. package/lib/yoke/adapters/claude.js +7 -42
  77. package/lib/yoke/adapters/codex-query-contract.js +24 -0
  78. package/lib/yoke/adapters/codex.js +1 -0
  79. package/lib/yoke/adapters/kiro.js +3 -1
  80. package/lib/yoke/adapters/muse-query-contract.js +7 -0
  81. package/lib/yoke/adapters/muse.js +122 -0
  82. package/lib/yoke/auth-events.js +17 -0
  83. package/lib/yoke/claude-hook-installer.js +267 -0
  84. package/lib/yoke/claude-jsonl-watcher.js +204 -0
  85. package/lib/yoke/claude-maintenance.js +55 -0
  86. package/lib/yoke/claude-permission-patterns.js +53 -0
  87. package/lib/yoke/claude-response-recovery.js +155 -0
  88. package/lib/yoke/claude-transcript-hygiene.js +34 -0
  89. package/lib/yoke/clay-skill-install.js +33 -0
  90. package/lib/yoke/cli-sessions.js +300 -0
  91. package/lib/yoke/codex-app-server.js +7 -12
  92. package/lib/yoke/configuration.js +13 -0
  93. package/lib/yoke/discovery.js +70 -0
  94. package/lib/yoke/errors.js +8 -0
  95. package/lib/yoke/host-environment.js +7 -0
  96. package/lib/yoke/index.js +9 -51
  97. package/lib/yoke/interface.js +116 -0
  98. package/lib/yoke/kiro-acp-server.js +5 -5
  99. package/lib/yoke/legacy-sessions.js +89 -0
  100. package/lib/yoke/login-protocol.js +25 -0
  101. package/lib/yoke/maintenance.js +12 -0
  102. package/lib/yoke/mcp-proxy.js +32 -0
  103. package/lib/yoke/mcp-source-adapters.js +91 -0
  104. package/lib/yoke/mcp-source-context.js +42 -0
  105. package/lib/yoke/mcp-source-discovery.js +169 -0
  106. package/lib/yoke/mcp-source-plugins.js +68 -0
  107. package/lib/yoke/mcp-source-restrictions.js +47 -0
  108. package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
  109. package/lib/yoke/model-preferences.js +6 -0
  110. package/lib/yoke/muse-artifacts.js +36 -0
  111. package/lib/yoke/muse-controls.js +50 -0
  112. package/lib/yoke/muse-input.js +64 -0
  113. package/lib/yoke/muse-items.js +157 -0
  114. package/lib/yoke/muse-policy.js +52 -0
  115. package/lib/yoke/muse-query.js +428 -0
  116. package/lib/yoke/muse-runtime.js +211 -0
  117. package/lib/yoke/muse-services.js +79 -0
  118. package/lib/yoke/muse-skills.js +79 -0
  119. package/lib/yoke/muse-turns.js +71 -0
  120. package/lib/yoke/options/acp.js +2 -0
  121. package/lib/yoke/options/antigravity.js +2 -0
  122. package/lib/yoke/options/claude.js +21 -0
  123. package/lib/yoke/options/codex-defaults.js +29 -0
  124. package/lib/yoke/options/codex-settings.js +21 -0
  125. package/lib/yoke/options/codex.js +6 -0
  126. package/lib/yoke/options/kiro-defaults.js +22 -0
  127. package/lib/yoke/options/kiro.js +5 -0
  128. package/lib/yoke/options/muse.js +5 -0
  129. package/lib/yoke/provider-contract.js +5 -0
  130. package/lib/yoke/providers/antigravity-executable.js +8 -0
  131. package/lib/yoke/providers/claude-executable.js +40 -0
  132. package/lib/yoke/providers/claude-model-descriptions.js +14 -0
  133. package/lib/yoke/providers/claude-process.js +97 -0
  134. package/lib/yoke/providers/claude-provision.js +75 -0
  135. package/lib/yoke/providers/codex-errors.js +8 -0
  136. package/lib/yoke/providers/muse-executable.js +17 -0
  137. package/lib/yoke/provisioning.js +6 -0
  138. package/lib/yoke/query-options.js +10 -0
  139. package/lib/yoke/response-recovery.js +41 -0
  140. package/lib/yoke/sdk-mcp-descriptors.js +37 -0
  141. package/lib/yoke/sdk-server-factory.js +7 -0
  142. package/lib/yoke/session-history.js +8 -0
  143. package/lib/yoke/session-tool-transport.js +31 -13
  144. package/lib/yoke/settings.js +23 -0
  145. package/lib/yoke/source-locations.js +4 -0
  146. package/lib/yoke/title-query.js +18 -7
  147. package/lib/yoke/tui-transcript-index.js +132 -0
  148. package/lib/yoke/usage.js +48 -0
  149. package/lib/yoke/user-input.js +13 -0
  150. package/lib/yoke/vendor-registry.js +81 -1
  151. package/package.json +3 -2
@@ -2,9 +2,9 @@
2
2
  // Used by both server-settings.js and project-settings.js
3
3
 
4
4
  export var MODE_OPTIONS = [
5
- { value: "default", label: "Default", desc: "Claude asks for permission before running tools and editing files." },
6
- { value: "plan", label: "Plan", desc: "Claude creates a plan first and asks for approval before making changes." },
7
- { value: "acceptEdits", label: "Auto-accept edits", desc: "File edits are applied automatically. Claude still asks before running commands." },
5
+ { value: "default", label: "Default", desc: "The agent asks for permission before running tools and editing files." },
6
+ { value: "plan", label: "Plan", desc: "The agent creates a plan first and asks for approval before making changes." },
7
+ { value: "acceptEdits", label: "Auto-accept edits", desc: "File edits are applied automatically. The agent still asks before running commands." },
8
8
  ];
9
9
 
10
10
  export var EFFORT_LEVELS = [
@@ -16,29 +16,17 @@ export var EFFORT_LEVELS = [
16
16
 
17
17
  export var THINKING_OPTIONS = [
18
18
  { value: "disabled", label: "Off", desc: "Disable extended thinking." },
19
- { value: "adaptive", label: "Adaptive", desc: "Claude decides when to use extended thinking." },
19
+ { value: "adaptive", label: "Adaptive", desc: "The agent decides when to use extended thinking." },
20
20
  { value: "budget", label: "Budget", desc: "Set a token budget for extended thinking." },
21
21
  ];
22
22
 
23
- export var MODEL_DESCRIPTIONS = {
24
- "default": "Automatically selects the best model for the task.",
25
- "sonnet": "Fast and capable. Great balance of speed and intelligence.",
26
- "haiku": "Fastest model. Best for quick tasks and simple questions.",
27
- "opus": "Most powerful model. Best for complex reasoning and analysis.",
28
- };
29
-
30
23
  export function getModelDesc(model) {
31
- if (!model) return "";
32
- var lower = (model.value || model).toLowerCase();
33
- for (var key in MODEL_DESCRIPTIONS) {
34
- if (lower.indexOf(key) !== -1) return MODEL_DESCRIPTIONS[key];
35
- }
36
- return "";
24
+ return model && typeof model === "object" && typeof model.description === "string" ? model.description : "";
37
25
  }
38
26
 
27
+ // Compatibility name for the retired beta control; model metadata owns support.
39
28
  export function isSonnetModel(model) {
40
- if (!model) return false;
41
- return model.toLowerCase().indexOf("sonnet") !== -1;
29
+ return !!(model && typeof model === "object" && model.supportsBetaContext === true);
42
30
  }
43
31
 
44
32
  // --- Render functions ---
@@ -75,7 +63,7 @@ export function renderModelList(prefix, opts) {
75
63
  nameSpan.textContent = label;
76
64
  item.appendChild(nameSpan);
77
65
 
78
- var desc = getModelDesc(value);
66
+ var desc = getModelDesc(m);
79
67
  if (desc) {
80
68
  var descSpan = document.createElement("span");
81
69
  descSpan.className = "settings-model-desc";
@@ -106,7 +94,9 @@ export function renderModeList(prefix, opts) {
106
94
  var currentMode = opts.currentMode || "default";
107
95
  listEl.innerHTML = "";
108
96
 
109
- for (var i = 0; i < MODE_OPTIONS.length; i++) {
97
+ var modes = (opts.configuration || {}).permissionModes || [];
98
+ if (listEl.parentElement) listEl.parentElement.style.display = modes.length ? "" : "none";
99
+ for (var i = 0; i < modes.length; i++) {
110
100
  (function (opt) {
111
101
  var item = document.createElement("div");
112
102
  item.className = "settings-model-item" + (opt.value === currentMode ? " active" : "");
@@ -129,7 +119,7 @@ export function renderModeList(prefix, opts) {
129
119
  });
130
120
 
131
121
  listEl.appendChild(item);
132
- })(MODE_OPTIONS[i]);
122
+ })(modes[i]);
133
123
  }
134
124
  }
135
125
 
@@ -143,7 +133,12 @@ export function renderEffortBar(prefix, opts) {
143
133
  var currentEffort = opts.currentEffort || "medium";
144
134
  bar.innerHTML = "";
145
135
 
146
- for (var i = 0; i < EFFORT_LEVELS.length; i++) {
136
+ var levels = opts.effortLevels || [];
137
+ var model = (opts.models || []).find(function (item) { return item.value === opts.currentModel; });
138
+ if (model && model.supportsEffort === false) levels = [];
139
+ else if (model && Array.isArray(model.supportedEffortLevels)) levels = model.supportedEffortLevels;
140
+ if (bar.parentElement) bar.parentElement.style.display = levels.length ? "" : "none";
141
+ for (var i = 0; i < levels.length; i++) {
147
142
  (function (lvl) {
148
143
  var btn = document.createElement("button");
149
144
  btn.className = "settings-btn-option" + (lvl.value === currentEffort ? " active" : "");
@@ -156,7 +151,7 @@ export function renderEffortBar(prefix, opts) {
156
151
  btn.classList.add("active");
157
152
  });
158
153
  bar.appendChild(btn);
159
- })(EFFORT_LEVELS[i]);
154
+ })({ value: levels[i], desc: (EFFORT_LEVELS.find(function (item) { return item.value === levels[i]; }) || {}).desc || "" });
160
155
  }
161
156
  }
162
157
 
@@ -173,7 +168,10 @@ export function renderThinkingBar(prefix, opts) {
173
168
  var budgetInput = document.getElementById(prefix + "-thinking-budget");
174
169
  bar.innerHTML = "";
175
170
 
176
- for (var i = 0; i < THINKING_OPTIONS.length; i++) {
171
+ var thinking = (opts.configuration || {}).thinking;
172
+ if (bar.parentElement) bar.parentElement.style.display = thinking ? "" : "none";
173
+ var thinkingOptions = thinking ? THINKING_OPTIONS.filter(function(opt) { return thinking.options.indexOf(opt.value) >= 0; }) : [];
174
+ for (var i = 0; i < thinkingOptions.length; i++) {
177
175
  (function (opt) {
178
176
  var btn = document.createElement("button");
179
177
  btn.className = "settings-btn-option" + (opt.value === currentThinking ? " active" : "");
@@ -191,10 +189,10 @@ export function renderThinkingBar(prefix, opts) {
191
189
  if (budgetRow) budgetRow.style.display = opt.value === "budget" ? "" : "none";
192
190
  });
193
191
  bar.appendChild(btn);
194
- })(THINKING_OPTIONS[i]);
192
+ })(thinkingOptions[i]);
195
193
  }
196
194
 
197
- if (budgetRow) budgetRow.style.display = currentThinking === "budget" ? "" : "none";
195
+ if (budgetRow) budgetRow.style.display = thinking && currentThinking === "budget" ? "" : "none";
198
196
  if (budgetInput) {
199
197
  budgetInput.value = currentBudget;
200
198
  budgetInput.addEventListener("change", function () {
@@ -212,7 +210,8 @@ export function renderBetaCard(prefix, opts) {
212
210
  var model = opts.overrideModel || opts.currentModel || "";
213
211
  var card = document.getElementById(prefix + "-beta-card");
214
212
  if (card) {
215
- card.style.display = isSonnetModel(model) ? "" : "none";
213
+ var descriptor = (opts.models || []).find(function(item) { return item.value === model; });
214
+ card.style.display = isSonnetModel(descriptor) ? "" : "none";
216
215
  }
217
216
 
218
217
  var toggle = document.getElementById(prefix + "-beta-1m");
@@ -176,7 +176,8 @@ function updateToolGroupHeader(group) {
176
176
  }
177
177
 
178
178
  function isPlanFile(filePath) {
179
- return filePath && filePath.indexOf(".claude/plans/") !== -1;
179
+ var info = (store.get('vendorInfo') || {})[store.get('currentVendor')] || {};
180
+ return !!filePath && (info.planPathSegments || []).some(function (segment) { return filePath.indexOf(segment) !== -1; });
180
181
  }
181
182
 
182
183
  export function toolSummary(name, input) {
@@ -635,10 +635,9 @@ export function attachTuiGrab(xterm, containerEl, localId, opts) {
635
635
  if (opts && opts.vendor) {
636
636
  var vendors = store.get('vendorInfo') || {};
637
637
  var info = vendors[opts.vendor];
638
- var legacyTuiVendors = ["claude"];
639
638
  var supportsTui = info
640
639
  ? Array.isArray(info.sessionModes) && info.sessionModes.indexOf("tui") !== -1
641
- : legacyTuiVendors.indexOf(opts.vendor) !== -1;
640
+ : false;
642
641
  if (!supportsTui) return;
643
642
  }
644
643
 
@@ -1,3 +1,4 @@
1
+ import { escapeHtml } from './utils.js';
1
2
  import { store } from './store.js';
2
3
  import { getWs } from './ws-ref.js';
3
4
  import { iconHtml, refreshIcons } from './icons.js';
@@ -33,7 +34,8 @@ export function openLoginBrowser(msg) {
33
34
  }
34
35
  closeLoginBrowser();
35
36
  var isMcp = msg.kind === 'mcp';
36
- var title = isMcp ? 'Sign in to ' + (msg.label || 'MCP server') : (msg.vendor === "codex" ? "Sign in to Codex" : "Sign in to Claude");
37
+ var provider = (store.get('vendorInfo') || {})[msg.vendor] || {};
38
+ var title = 'Sign in to ' + (isMcp ? msg.label || 'MCP server' : provider.displayName || msg.vendor || 'Provider');
37
39
  store.set({ vendorLoginBrowser: {
38
40
  browserId: msg.browserId, vendor: msg.vendor, kind: msg.kind, connectionId: msg.connectionId, slug: msg.slug || store.get('currentSlug'),
39
41
  previousFocus: document.activeElement, decoding: false, pointerY: null, pointerId: null, mouseDown: false, moveTimer: null, pendingMove: null, resizeObserver: null, requestedSize: null, phase: "loading",
@@ -43,7 +45,7 @@ export function openLoginBrowser(msg) {
43
45
  dialog.className = "login-browser";
44
46
  dialog.setAttribute("aria-labelledby", "login-browser-title");
45
47
  dialog.innerHTML = '<header class="login-browser-header">' +
46
- '<img class="login-browser-vendor" src="/' + (msg.vendor === "codex" ? 'codex-avatar.png' : 'claude-code-avatar.png') + '" alt="">' +
48
+ '<img class="login-browser-vendor" src="' + escapeHtml(provider.avatar || '') + '" alt="">' +
47
49
  '<div class="login-browser-heading"><h2 id="login-browser-title"></h2>' +
48
50
  '<span class="login-browser-origin">Opening sign-in page…</span></div>' +
49
51
  '<div class="login-browser-actions">' +
@@ -56,7 +58,7 @@ export function openLoginBrowser(msg) {
56
58
  '<div class="login-browser-loading"><span class="login-browser-spinner" aria-hidden="true"></span><strong>Opening your sign-in page</strong><span>This may take a moment.</span></div>' +
57
59
  '<textarea class="login-browser-keyboard" aria-label="Type into the remote sign-in page" autocomplete="off" autocapitalize="off" spellcheck="false"></textarea></div>' +
58
60
  '<footer class="login-browser-footer"><div class="login-browser-info"><div class="login-browser-status"><span class="login-browser-status-dot" aria-hidden="true"></span><p role="status">Preparing sign-in…</p></div>' +
59
- '<p class="login-browser-privacy">Browser cookies are discarded when this session ends. You stay signed in to ' + (msg.vendor === "codex" ? 'Codex' : 'Claude') + ' after this browser closes.</p></div>' +
61
+ '<p class="login-browser-privacy">Browser cookies are discarded when this session ends. You stay signed in to ' + escapeHtml(provider.displayName || msg.vendor || 'the provider') + ' after this browser closes.</p></div>' +
60
62
  '<button type="button" data-action="terminal" class="login-browser-terminal">' + iconHtml("terminal") + '<span>Use terminal sign-in</span>' + iconHtml("arrow-up-right") + '</button></footer>';
61
63
  dialog.querySelector("h2").textContent = title;
62
64
  document.body.appendChild(dialog);
@@ -31,7 +31,7 @@ function vendorDisplayName(vendor) {
31
31
  var vendors = store.get('vendorInfo') || {};
32
32
  var info = vendors[vendor];
33
33
  if (info && info.displayName) return info.displayName;
34
- return vendor === "codex" ? "Codex" : "Claude Code";
34
+ return vendor || "Provider";
35
35
  }
36
36
 
37
37
  function currentProjectSlug() {
@@ -170,7 +170,7 @@ export function requestVendorLogin(vendor, opts) {
170
170
  vendor: target,
171
171
  auto: !!options.auto,
172
172
  sessionId: options.sessionId || store.get('activeSessionId') || null,
173
- mode: options.mode || ((target === "claude" || target === "codex") ? "browser" : "terminal"),
173
+ mode: options.mode || ((((store.get("vendorInfo") || {})[target] || {}).browserLoginSupported === true) ? "browser" : "terminal"),
174
174
  }));
175
175
  showLoginStartedBanner(target);
176
176
  return true;
@@ -1,4 +1,18 @@
1
- export var VENDOR_ORDER = ["claude", "codex", "grok", "kimi", "copilot", "qwen", "junie", "antigravity", "opencode", "kiro"];
1
+ export var VENDOR_ORDER = ["claude", "codex", "muse", "grok", "kimi", "copilot", "qwen", "junie", "antigravity", "opencode", "kiro"];
2
+
3
+ // Availability comes from the caller's server catalog. Unknown providers keep
4
+ // their catalog order within each availability group.
5
+ export function sortVendorEntries(entries) {
6
+ return entries.map(function (entry, index) { return { entry: entry, index: index }; }).sort(function (a, b) {
7
+ var installed = Number(!!b.entry.installed) - Number(!!a.entry.installed);
8
+ if (installed) return installed;
9
+ var aRank = VENDOR_ORDER.indexOf(a.entry.id);
10
+ var bRank = VENDOR_ORDER.indexOf(b.entry.id);
11
+ if (aRank === -1) aRank = VENDOR_ORDER.length;
12
+ if (bRank === -1) bRank = VENDOR_ORDER.length;
13
+ return aRank - bRank || a.index - b.index;
14
+ }).map(function (item) { return item.entry; });
15
+ }
2
16
  export var EXPERIMENTAL_VENDORS = ["grok", "kimi", "copilot", "qwen", "junie", "antigravity", "opencode"];
3
17
 
4
18
  export function isExperimentalVendor(vendor) {
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Muse Code">
2
+ <rect width="64" height="64" rx="14" fill="#111827"/>
3
+ <path d="M14 45V19h7l11 14 10-14h8v26h-7V30L32 45 21 30v15z" fill="#f8fafc"/>
4
+ </svg>
@@ -115,51 +115,9 @@ function isSafeBashSegment(seg) {
115
115
  // Emit the `Bash(...)` permission patterns for ~/.claude/settings.json. Each
116
116
  // standalone command gets a bare form (zero-arg) and a space-wildcard form
117
117
  // (claude 2.x prefix matching); subcommands and exact commands likewise.
118
- function buildClayBashAllowPatterns() {
119
- var patterns = [];
120
- for (var i = 0; i < STANDALONE.length; i++) {
121
- patterns.push("Bash(" + STANDALONE[i] + ")");
122
- patterns.push("Bash(" + STANDALONE[i] + " *)");
123
- }
124
- var bins = Object.keys(SUBCOMMAND);
125
- for (var b = 0; b < bins.length; b++) {
126
- var subs = SUBCOMMAND[bins[b]];
127
- for (var s = 0; s < subs.length; s++) {
128
- var base = bins[b] + " " + subs[s];
129
- patterns.push("Bash(" + base + ")");
130
- patterns.push("Bash(" + base + " *)");
131
- }
132
- }
133
- for (var e = 0; e < EXACT.length; e++) {
134
- patterns.push("Bash(" + EXACT[e] + ")");
135
- patterns.push("Bash(" + EXACT[e] + " *)");
136
- }
137
- return patterns;
138
- }
139
-
140
- var GENERATED_SET = {};
141
- (function () {
142
- var p = buildClayBashAllowPatterns();
143
- for (var i = 0; i < p.length; i++) GENERATED_SET[p[i]] = true;
144
- })();
145
-
146
- // Recognize a `permissions.allow` entry that Clay owns, so the installer can
147
- // strip stale variants on upgrade (including shapes a previous version wrote)
148
- // WITHOUT clobbering user-authored patterns. Rules:
149
- // - Standalone commands: own every shape of `Bash(ls ...)` (any args), since
150
- // a user pattern for a command we manage is redundant with our broad one.
151
- // - Subcommand binaries (git, npm, ...) and exact commands: only strip the
152
- // precise patterns we generate, so a user's `Bash(git push)` survives.
153
- // Legacy colon shapes are handled separately by the installer.
154
- function isClayManagedBashPattern(p) {
155
- if (typeof p !== "string") return false;
156
- if (p.indexOf("Bash(") !== 0 || p.charAt(p.length - 1) !== ")") return false;
157
- if (GENERATED_SET[p]) return true;
158
- var inner = p.slice(5, -1).trim();
159
- if (!inner) return false;
160
- var word = inner.split(/[\s:]/)[0];
161
- return STANDALONE_SET[word] === true;
162
- }
118
+ var patternCodec = require("./yoke/claude-permission-patterns").attachPatterns({ STANDALONE: STANDALONE, SUBCOMMAND: SUBCOMMAND, EXACT: EXACT });
119
+ function buildClayBashAllowPatterns() { return patternCodec.build(); }
120
+ function isClayManagedBashPattern(pattern) { return patternCodec.isManaged(pattern); }
163
121
 
164
122
  module.exports = {
165
123
  STANDALONE: STANDALONE,