@dollhousemcp/mcp-server 2.0.16 → 2.0.17

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 (40) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md.backup +18 -0
  3. package/dist/elements/base/BaseElementManager.d.ts.map +1 -1
  4. package/dist/elements/base/BaseElementManager.js +17 -1
  5. package/dist/elements/memories/MemoryManager.d.ts.map +1 -1
  6. package/dist/elements/memories/MemoryManager.js +13 -2
  7. package/dist/generated/version.d.ts +2 -2
  8. package/dist/generated/version.js +3 -3
  9. package/dist/handlers/element-crud/createElement.d.ts.map +1 -1
  10. package/dist/handlers/element-crud/createElement.js +6 -2
  11. package/dist/handlers/element-crud/editElement.d.ts.map +1 -1
  12. package/dist/handlers/element-crud/editElement.js +6 -2
  13. package/dist/handlers/element-crud/helpers.d.ts +2 -0
  14. package/dist/handlers/element-crud/helpers.d.ts.map +1 -1
  15. package/dist/handlers/element-crud/helpers.js +21 -2
  16. package/dist/handlers/mcp-aql/IntrospectionResolver.d.ts.map +1 -1
  17. package/dist/handlers/mcp-aql/IntrospectionResolver.js +34 -7
  18. package/dist/handlers/mcp-aql/MCPAQLHandler.d.ts.map +1 -1
  19. package/dist/handlers/mcp-aql/MCPAQLHandler.js +19 -4
  20. package/dist/handlers/mcp-aql/OperationSchema.d.ts.map +1 -1
  21. package/dist/handlers/mcp-aql/OperationSchema.js +3 -2
  22. package/dist/handlers/mcp-aql/evaluatePermission.d.ts.map +1 -1
  23. package/dist/handlers/mcp-aql/evaluatePermission.js +2 -1
  24. package/dist/handlers/mcp-aql/policies/ElementPolicies.d.ts +8 -0
  25. package/dist/handlers/mcp-aql/policies/ElementPolicies.d.ts.map +1 -1
  26. package/dist/handlers/mcp-aql/policies/ElementPolicies.js +26 -1
  27. package/dist/utils/permissionHooks.d.ts +39 -3
  28. package/dist/utils/permissionHooks.d.ts.map +1 -1
  29. package/dist/utils/permissionHooks.js +651 -74
  30. package/dist/web/public/permissions.js +38 -26
  31. package/dist/web/public/setup.js +367 -94
  32. package/dist/web/routes/permissionRoutes.d.ts.map +1 -1
  33. package/dist/web/routes/permissionRoutes.js +32 -13
  34. package/dist/web/routes/setupRoutes.d.ts +1 -0
  35. package/dist/web/routes/setupRoutes.d.ts.map +1 -1
  36. package/dist/web/routes/setupRoutes.js +113 -41
  37. package/package.json +3 -1
  38. package/scripts/pretooluse-vscode.sh +163 -0
  39. package/scripts/pretooluse-windsurf.sh +166 -4
  40. package/server.json +2 -2
@@ -20,13 +20,13 @@
20
20
  { id: 'claude-desktop', rootKey: 'mcpServers' },
21
21
  { id: 'claude-code', rootKey: 'mcpServers', cli: 'claude', hookSupport: 'verified', hookCommand: `bash ${HOOK_BASE_SCRIPT_PATH}`, hookConfigPath: '<code>~/.claude/settings.json</code>' },
22
22
  // These panels are generated from this data by renderGeneratedPanels()
23
- { id: 'cursor', rootKey: 'mcpServers', installClient: 'cursor', openClient: 'cursor', configPath: '<code>.cursor/mcp.json</code> in your project, or <code>~/.cursor/mcp.json</code> for all projects', hint: 'Or configure via Settings &gt; MCP Servers in the Cursor UI.', hookSupport: 'manual', hookCommand: `bash ${HOOKS_DIR}/pretooluse-cursor.sh`, hookConfigPath: '<code>.cursor/mcp.json</code> in your project, or <code>~/.cursor/mcp.json</code> for all projects' },
24
- { id: 'vscode', rootKey: 'servers', installClient: 'vscode', configPath: '<code>.vscode/mcp.json</code> in your workspace', hint: 'VS Code uses <code>"servers"</code>, not <code>"mcpServers"</code>.' },
25
- { id: 'codex', rootKey: 'mcpServers', installClient: 'codex', openClient: 'codex', cli: 'codex', toml: true, tomlPath: '<code>~/.codex/config.toml</code> (Codex uses TOML, not JSON)', hookSupport: 'manual', hookCommand: `bash ${HOOKS_DIR}/pretooluse-codex.sh`, hookConfigPath: '<code>~/.codex/config.toml</code>' },
26
- { id: 'gemini', rootKey: 'mcpServers', installClient: 'gemini-cli', openClient: 'gemini-cli', cli: 'gemini', configPath: '<code>~/.gemini/settings.json</code> or <code>.gemini/settings.json</code> in your project', hookSupport: 'manual', hookCommand: `bash ${HOOKS_DIR}/pretooluse-gemini.sh`, hookConfigPath: '<code>~/.gemini/settings.json</code> or <code>.gemini/settings.json</code> in your project' },
27
- { id: 'windsurf', rootKey: 'mcpServers', installClient: 'windsurf', openClient: 'windsurf', configPath: '<code>~/.codeium/windsurf/mcp_config.json</code>', hint: 'Or click the MCPs icon in the Cascade panel &gt; Configure.', hookSupport: 'manual', hookCommand: `bash ${HOOKS_DIR}/pretooluse-windsurf.sh`, hookConfigPath: '<code>~/.codeium/windsurf/mcp_config.json</code>' },
28
- { id: 'cline', rootKey: 'mcpServers', installClient: 'cline', configPath: '<code>cline_mcp_settings.json</code> via Cline\'s top nav &gt; Configure &gt; Advanced MCP Settings' },
29
- { id: 'lmstudio', rootKey: 'mcpServers', openClient: 'lmstudio', configPath: '<code>~/.lmstudio/mcp.json</code> (or open via Program tab &gt; Install &gt; Edit mcp.json)', hint: 'Restart LM Studio after saving.' },
23
+ { id: 'cursor', rootKey: 'mcpServers', installClient: 'cursor', openClient: 'cursor', configPath: '<code>.cursor/mcp.json</code> in your project, or <code>~/.cursor/mcp.json</code> for all projects', hint: 'Or configure via Settings &gt; MCP Servers in the Cursor UI.', hookSupport: 'partial', hookCommand: `bash ${HOOKS_DIR}/pretooluse-cursor.sh`, hookConfigPath: '<code>.cursor/hooks.json</code> in your project, or <code>~/.cursor/hooks.json</code> for all projects' },
24
+ { id: 'vscode', rootKey: 'servers', installClient: 'vscode', configPath: '<code>.vscode/mcp.json</code> in your workspace', hint: 'VS Code uses <code>"servers"</code>, not <code>"mcpServers"</code>.', hookSupport: 'partial', hookCommand: `bash ${HOOKS_DIR}/pretooluse-vscode.sh`, hookConfigPath: '<code>~/.copilot/hooks/dollhouse-permissions.json</code> plus <code>chat.hookFilesLocations</code> in VS Code user settings' },
25
+ { id: 'codex', rootKey: 'mcpServers', installClient: 'codex', openClient: 'codex', cli: 'codex', toml: true, tomlPath: '<code>~/.codex/config.toml</code> (Codex uses TOML, not JSON)', hookSupport: 'partial', hookCommand: `bash ${HOOKS_DIR}/pretooluse-codex.sh`, hookConfigPath: '<code>~/.codex/hooks.json</code> and <code>~/.codex/config.toml</code>' },
26
+ { id: 'gemini', rootKey: 'mcpServers', installClient: 'gemini-cli', openClient: 'gemini-cli', cli: 'gemini', configPath: '<code>~/.gemini/settings.json</code> or <code>.gemini/settings.json</code> in your project', hookSupport: 'partial', hookCommand: `bash ${HOOKS_DIR}/pretooluse-gemini.sh`, hookConfigPath: '<code>~/.gemini/settings.json</code> or <code>.gemini/settings.json</code> in your project' },
27
+ { id: 'windsurf', rootKey: 'mcpServers', installClient: 'windsurf', openClient: 'windsurf', configPath: '<code>~/.codeium/windsurf/mcp_config.json</code>', hint: 'Or click the MCPs icon in the Cascade panel &gt; Configure.', hookSupport: 'partial', hookCommand: `bash ${HOOKS_DIR}/pretooluse-windsurf.sh`, hookConfigPath: '<code>~/.codeium/windsurf/hooks.json</code> or <code>.windsurf/hooks.json</code> in your project' },
28
+ { id: 'cline', rootKey: 'mcpServers', installClient: 'cline', openClient: 'cline', configPath: 'Cline stores MCP servers in <code>cline_mcp_settings.json</code> inside its extension settings. Use Configure Now or open the file directly.' },
29
+ { id: 'lmstudio', rootKey: 'mcpServers', installClient: 'lmstudio', openClient: 'lmstudio', configPath: '<code>~/.lmstudio/mcp.json</code> (or open via Program tab &gt; Install &gt; Edit mcp.json)', hint: 'Restart LM Studio after saving.' },
30
30
  ];
31
31
 
32
32
  const HOOK_BASE_SCRIPT = `#!/bin/bash
@@ -82,6 +82,94 @@ exec bash "$SCRIPT_DIR/pretooluse-dollhouse.sh"`;
82
82
  }
83
83
  }`;
84
84
 
85
+ const GEMINI_HOOK_SETTINGS = `{
86
+ "hooks": {
87
+ "BeforeTool": [
88
+ {
89
+ "matcher": ".*",
90
+ "hooks": [
91
+ {
92
+ "type": "command",
93
+ "command": "bash ${HOOKS_DIR}/pretooluse-gemini.sh"
94
+ }
95
+ ]
96
+ }
97
+ ]
98
+ }
99
+ }`;
100
+
101
+ const CODEX_HOOK_SETTINGS = `{
102
+ "hooks": {
103
+ "PreToolUse": [
104
+ {
105
+ "matcher": "Bash",
106
+ "hooks": [
107
+ {
108
+ "type": "command",
109
+ "command": "bash ${HOOKS_DIR}/pretooluse-codex.sh",
110
+ "statusMessage": "Checking Bash permissions"
111
+ }
112
+ ]
113
+ }
114
+ ]
115
+ }
116
+ }`;
117
+
118
+ const CURSOR_HOOK_SETTINGS = `{
119
+ "version": 1,
120
+ "hooks": {
121
+ "preToolUse": [
122
+ {
123
+ "type": "command",
124
+ "command": "bash ${HOOKS_DIR}/pretooluse-cursor.sh",
125
+ "matcher": ".*"
126
+ }
127
+ ]
128
+ }
129
+ }`;
130
+
131
+ const VSCODE_HOOK_SETTINGS = `{
132
+ "hooks": {
133
+ "PreToolUse": [
134
+ {
135
+ "matcher": "*",
136
+ "hooks": [
137
+ {
138
+ "type": "command",
139
+ "command": "bash ${HOOKS_DIR}/pretooluse-vscode.sh"
140
+ }
141
+ ]
142
+ }
143
+ ]
144
+ }
145
+ }`;
146
+
147
+ const VSCODE_HOOK_LOCATIONS_SETTINGS = `{
148
+ "chat.hookFilesLocations": {
149
+ "~/.copilot/hooks": true
150
+ }
151
+ }`;
152
+
153
+ const WINDSURF_HOOK_SETTINGS = `{
154
+ "hooks": {
155
+ "pre_run_command": [
156
+ {
157
+ "type": "command",
158
+ "command": "bash ${HOOKS_DIR}/pretooluse-windsurf.sh"
159
+ }
160
+ ],
161
+ "pre_mcp_tool_use": [
162
+ {
163
+ "type": "command",
164
+ "command": "bash ${HOOKS_DIR}/pretooluse-windsurf.sh"
165
+ }
166
+ ]
167
+ }
168
+ }`;
169
+
170
+ const CODEX_HOOK_FEATURES_TOML = `[features]
171
+ codex_hooks = true`;
172
+
85
173
  /** Build a JSON config block for a given npx command string */
86
174
  function jsonConfig(rootKey, npxCmd) {
87
175
  const parts = npxCmd.split(' ');
@@ -459,6 +547,32 @@ exec bash "$SCRIPT_DIR/pretooluse-dollhouse.sh"`;
459
547
  : `${msg}. Try the manual config below.`;
460
548
  };
461
549
 
550
+ const buildInstallPayload = (client) => {
551
+ const payload = { client };
552
+ if (currentMethod === 'global' && pinnedVersion && pinnedVersion !== 'latest') {
553
+ payload.version = pinnedVersion;
554
+ } else if (currentChannel !== 'latest') {
555
+ payload.channel = currentChannel;
556
+ }
557
+ return payload;
558
+ };
559
+
560
+ const applyInstallSuccessState = (btn, status, data, verified) => {
561
+ btn.textContent = 'Installed';
562
+ btn.classList.remove('is-loading');
563
+ btn.classList.add('is-success');
564
+ if (!status) return;
565
+
566
+ if (data.hookInstall?.supported && !data.hookInstall?.configured && data.hookInstall?.assetsPrepared) {
567
+ status.textContent = 'Configured MCP server. Dollhouse hook assets were also prepared; finish manual permission setup in Permissions & Security.';
568
+ } else {
569
+ status.textContent = verified
570
+ ? 'Verified — config written. Restart the application to activate.'
571
+ : 'Restart the application to activate.';
572
+ }
573
+ status.classList.add('is-success');
574
+ };
575
+
462
576
  /** Handle Configure Now button click */
463
577
  const handleInstallClick = async (btn) => {
464
578
  const client = btn.dataset.installClient;
@@ -476,17 +590,10 @@ exec bash "$SCRIPT_DIR/pretooluse-dollhouse.sh"`;
476
590
  }
477
591
 
478
592
  try {
479
- const payload = { client };
480
- if (currentMethod === 'global' && pinnedVersion && pinnedVersion !== 'latest') {
481
- payload.version = pinnedVersion;
482
- } else if (currentChannel !== 'latest') {
483
- payload.channel = currentChannel;
484
- }
485
-
486
593
  const res = await DollhouseAuth.apiFetch('/api/setup/install', {
487
594
  method: 'POST',
488
595
  headers: { 'Content-Type': 'application/json' },
489
- body: JSON.stringify(payload),
596
+ body: JSON.stringify(buildInstallPayload(client)),
490
597
  });
491
598
 
492
599
  const data = await res.json();
@@ -500,16 +607,7 @@ exec bash "$SCRIPT_DIR/pretooluse-dollhouse.sh"`;
500
607
  await fetchDetection();
501
608
  updateDetectionState();
502
609
  const verified = detectedConfigs[clientToPlatformReverse[client]]?.installed;
503
-
504
- btn.textContent = 'Installed';
505
- btn.classList.remove('is-loading');
506
- btn.classList.add('is-success');
507
- if (status) {
508
- status.textContent = verified
509
- ? 'Verified — config written. Restart the application to activate.'
510
- : 'Restart the application to activate.';
511
- status.classList.add('is-success');
512
- }
610
+ applyInstallSuccessState(btn, status, data, verified);
513
611
 
514
612
  // Show the completion banner after any successful install
515
613
  showCompletionBanner(client);
@@ -572,7 +670,7 @@ exec bash "$SCRIPT_DIR/pretooluse-dollhouse.sh"`;
572
670
  btn.classList.add('is-success');
573
671
 
574
672
  if (status) {
575
- status.textContent = 'Claude Code permissions are enabled. Restart Claude Code if it is already running.';
673
+ status.textContent = data.hookInstall?.message || 'Permissions are enabled. Restart the client if it is already running.';
576
674
  status.classList.add('is-success');
577
675
  }
578
676
  } catch (err) {
@@ -879,58 +977,163 @@ exec bash "$SCRIPT_DIR/pretooluse-dollhouse.sh"`;
879
977
  lmstudio: 'LM Studio',
880
978
  };
881
979
 
882
- const getPermissionStatusCopy = (platformId, detected) => {
883
- if (platformId === 'claude-code') {
884
- if (detected?.hookInstalled) {
885
- return {
886
- tone: 'info',
887
- titleText: 'Claude Code permission enforcement is enabled.',
888
- messageText: 'No further changes are needed here unless you want to reinstall the hook settings.',
889
- };
890
- }
980
+ const VERIFIED_PERMISSION_PLATFORMS = {
981
+ 'claude-code': {
982
+ label: 'Claude Code',
983
+ statusTag: 'claude code',
984
+ configPath: '<code>~/.claude/settings.json</code>',
985
+ scriptPath: HOOK_BASE_SCRIPT_PATH,
986
+ settingsBlock: CLAUDE_CODE_HOOK_SETTINGS,
987
+ },
988
+ };
891
989
 
892
- if (detected?.installed) {
893
- return {
894
- tone: 'warning',
895
- titleText: 'Claude Code is connected for this client.',
896
- messageText: 'DollhouseMCP is configured as an MCP server. Use Configure Now below to also install the Claude Code permission hook.',
897
- };
898
- }
990
+ const PARTIAL_PERMISSION_PLATFORMS = {
991
+ gemini: {
992
+ label: 'Gemini CLI',
993
+ statusTag: 'allow / deny',
994
+ configPath: '<code>~/.gemini/settings.json</code> or <code>.gemini/settings.json</code> in your project',
995
+ scriptPath: `${HOOKS_DIR}/pretooluse-gemini.sh`,
996
+ settingsBlock: GEMINI_HOOK_SETTINGS,
997
+ limitation: 'Gemini CLI exposes native BeforeTool hooks, but it does not support an ask/confirm response path. Confirmation-style policies currently degrade to deny.',
998
+ },
999
+ cursor: {
1000
+ label: 'Cursor',
1001
+ statusTag: 'native hooks',
1002
+ configPath: '<code>.cursor/hooks.json</code> in your project, or <code>~/.cursor/hooks.json</code> for all projects',
1003
+ scriptPath: `${HOOKS_DIR}/pretooluse-cursor.sh`,
1004
+ settingsBlock: CURSOR_HOOK_SETTINGS,
1005
+ limitation: 'Cursor exposes native hooks, but its permission handling still needs broader runtime verification across allow and ask decisions.',
1006
+ },
1007
+ vscode: {
1008
+ label: 'VS Code',
1009
+ statusTag: 'native hooks',
1010
+ configPath: '<code>~/.copilot/hooks/dollhouse-permissions.json</code> and VS Code user settings',
1011
+ scriptPath: `${HOOKS_DIR}/pretooluse-vscode.sh`,
1012
+ settingsBlock: VSCODE_HOOK_SETTINGS,
1013
+ featureBlock: VSCODE_HOOK_LOCATIONS_SETTINGS,
1014
+ featureHeading: '2. Enable <code>~/.copilot/hooks</code> in VS Code user settings',
1015
+ featureCopyLabel: 'Copy VS Code hookFilesLocations settings',
1016
+ limitation: 'VS Code exposes native PreToolUse hooks, but it ignores matcher values and uses tool names that differ from Claude Code. This adapter normalizes the common built-in tools we know about.',
1017
+ },
1018
+ windsurf: {
1019
+ label: 'Windsurf',
1020
+ statusTag: 'allow / deny',
1021
+ configPath: '<code>~/.codeium/windsurf/hooks.json</code> or <code>.windsurf/hooks.json</code> in your project',
1022
+ scriptPath: `${HOOKS_DIR}/pretooluse-windsurf.sh`,
1023
+ settingsBlock: WINDSURF_HOOK_SETTINGS,
1024
+ limitation: 'Windsurf exposes native pre-run and pre-MCP hooks, but they are binary allow-or-block hooks. Confirmation-style policies currently degrade to block.',
1025
+ },
1026
+ codex: {
1027
+ label: 'Codex',
1028
+ statusTag: 'bash only',
1029
+ configPath: '<code>~/.codex/hooks.json</code> and <code>~/.codex/config.toml</code>',
1030
+ scriptPath: `${HOOKS_DIR}/pretooluse-codex.sh`,
1031
+ settingsBlock: CODEX_HOOK_SETTINGS,
1032
+ featureBlock: CODEX_HOOK_FEATURES_TOML,
1033
+ limitation: 'Codex currently only supports native PreToolUse hooks for Bash, so this turns on Bash permission guardrails only.',
1034
+ },
1035
+ };
899
1036
 
1037
+ const getVerifiedPermissionStatusCopy = (verified, detected) => {
1038
+ if (detected?.hookInstalled) {
900
1039
  return {
901
1040
  tone: 'info',
902
- titleText: 'Claude Code permissions are not configured yet.',
903
- messageText: 'First connect DollhouseMCP using Auto-updating or Pinned version, then use Configure Now below to install the Claude Code permission hook.',
1041
+ titleText: `${verified.label} permission enforcement is enabled.`,
1042
+ messageText: 'No further changes are needed here unless you want to reinstall the hook settings.',
904
1043
  };
905
1044
  }
906
1045
 
907
- const support = PLATFORMS.find((platform) => platform.id === platformId)?.hookSupport || 'unsupported';
908
- if (support === 'manual') {
909
- if (detected?.installed) {
910
- return {
911
- tone: 'warning',
912
- titleText: 'DollhouseMCP is connected for this client.',
913
- messageText: 'DollhouseMCP is configured here, but permission enforcement is separate. Use the manual hook steps below to turn it on for this client.',
914
- };
915
- }
1046
+ if (detected?.installed) {
1047
+ return {
1048
+ tone: 'warning',
1049
+ titleText: `${verified.label} is connected for this client.`,
1050
+ messageText: `DollhouseMCP is configured as an MCP server. Use Configure Now below to also install the ${verified.label} permission hook.`,
1051
+ };
1052
+ }
916
1053
 
1054
+ return {
1055
+ tone: 'info',
1056
+ titleText: `${verified.label} permissions are not configured yet.`,
1057
+ messageText: `First connect DollhouseMCP using Auto-updating or Pinned version, then use Configure Now below to install the ${verified.label} permission hook.`,
1058
+ };
1059
+ };
1060
+
1061
+ const getPartialPermissionStatusCopy = (partial, detected) => {
1062
+ const activationLabel = partial.label === 'Codex' ? 'Bash guardrails' : 'permission hooks';
1063
+ if (detected?.hookInstalled) {
917
1064
  return {
918
1065
  tone: 'info',
919
- titleText: 'Manual permissions setup is available for this client.',
920
- messageText: 'Use the steps below if you want to turn on permission enforcement for this client manually.',
1066
+ titleText: `${partial.label} ${activationLabel} are enabled.`,
1067
+ messageText: partial.limitation,
1068
+ };
1069
+ }
1070
+
1071
+ if (detected?.installed) {
1072
+ return {
1073
+ tone: 'warning',
1074
+ titleText: `${partial.label} is connected for this client.`,
1075
+ messageText: `DollhouseMCP is configured as an MCP server. Use Configure Now below to turn on ${partial.label}'s native ${activationLabel}.`,
921
1076
  };
922
1077
  }
923
1078
 
924
- const platformLabel = PERMISSION_PLATFORM_LABELS[platformId] || 'this client';
925
1079
  return {
926
- tone: detected?.installed ? 'warning' : 'neutral',
927
- titleText: `Permissions & security tools are unavailable for ${platformLabel} right now.`,
928
- messageText: detected?.installed
929
- ? 'DollhouseMCP is connected for this client, but this release does not include a supported permissions setup flow here yet.'
930
- : 'This release does not include a supported permissions setup flow for this client yet.',
1080
+ tone: 'info',
1081
+ titleText: `${partial.label} ${activationLabel} are not configured yet.`,
1082
+ messageText: `First connect DollhouseMCP using Auto-updating or Pinned version, then use Configure Now below to install ${partial.label}'s native ${activationLabel}.`,
931
1083
  };
932
1084
  };
933
1085
 
1086
+ const getManualPermissionStatusCopy = (detected) => {
1087
+ if (detected?.hookAssetsPrepared) {
1088
+ return {
1089
+ tone: 'info',
1090
+ titleText: 'Hook bridge files are already prepared for this client.',
1091
+ messageText: 'Finish the client-specific hook registration below to turn on permission enforcement.',
1092
+ };
1093
+ }
1094
+ if (detected?.installed) {
1095
+ return {
1096
+ tone: 'warning',
1097
+ titleText: 'DollhouseMCP is connected for this client.',
1098
+ messageText: 'DollhouseMCP is configured here, but permission enforcement is separate. Use the manual hook steps below to turn it on for this client.',
1099
+ };
1100
+ }
1101
+
1102
+ return {
1103
+ tone: 'info',
1104
+ titleText: 'Manual permissions setup is available for this client.',
1105
+ messageText: 'Use the steps below if you want to turn on permission enforcement for this client manually.',
1106
+ };
1107
+ };
1108
+
1109
+ const getUnsupportedPermissionStatusCopy = (platformLabel, detected) => ({
1110
+ tone: detected?.installed ? 'warning' : 'neutral',
1111
+ titleText: `Permissions & security tools are unavailable for ${platformLabel} right now.`,
1112
+ messageText: detected?.installed
1113
+ ? 'DollhouseMCP is connected for this client, but this release does not include a supported permissions setup flow here yet.'
1114
+ : 'This release does not include a supported permissions setup flow for this client yet.',
1115
+ });
1116
+
1117
+ const getPermissionStatusCopy = (platformId, detected) => {
1118
+ const verified = VERIFIED_PERMISSION_PLATFORMS[platformId];
1119
+ if (verified) {
1120
+ return getVerifiedPermissionStatusCopy(verified, detected);
1121
+ }
1122
+
1123
+ const partial = PARTIAL_PERMISSION_PLATFORMS[platformId];
1124
+ if (partial) {
1125
+ return getPartialPermissionStatusCopy(partial, detected);
1126
+ }
1127
+
1128
+ const support = PLATFORMS.find((platform) => platform.id === platformId)?.hookSupport || 'unsupported';
1129
+ if (support === 'manual') {
1130
+ return getManualPermissionStatusCopy(detected);
1131
+ }
1132
+
1133
+ const platformLabel = PERMISSION_PLATFORM_LABELS[platformId] || 'this client';
1134
+ return getUnsupportedPermissionStatusCopy(platformLabel, detected);
1135
+ };
1136
+
934
1137
  const updatePermissionStatus = (panel, platformId, detected) => {
935
1138
  const status = panel?.querySelector('.setup-permission-status');
936
1139
  if (!status) return;
@@ -950,6 +1153,7 @@ exec bash "$SCRIPT_DIR/pretooluse-dollhouse.sh"`;
950
1153
  const panel = document.getElementById('setup-panel-' + platformId);
951
1154
  const tabBtn = document.getElementById('setup-tab-' + platformId);
952
1155
  updatePermissionStatus(panel, platformId, detected);
1156
+ updatePermissionInstallButton(panel?.querySelector('.setup-permission-install-btn'), detected);
953
1157
 
954
1158
  if (!detected?.installed) return;
955
1159
 
@@ -958,7 +1162,6 @@ exec bash "$SCRIPT_DIR/pretooluse-dollhouse.sh"`;
958
1162
  updateDetectionNotice(panel?.querySelector('.setup-installed-notice'), matches);
959
1163
  updateDetectionBadge(tabBtn?.querySelector('.setup-tab-badge'), matches);
960
1164
  updateDetectionButton(panel?.querySelector('.setup-install-btn'), matches);
961
- updatePermissionInstallButton(panel?.querySelector('.setup-permission-install-btn'), detected);
962
1165
 
963
1166
  // Refresh the "Current config" code block with the latest detected config
964
1167
  if (detected.currentConfig && panel) {
@@ -1120,41 +1323,111 @@ exec bash "$SCRIPT_DIR/pretooluse-dollhouse.sh"`;
1120
1323
  return html;
1121
1324
  };
1122
1325
 
1123
- const renderPermissionSection = (p) => {
1124
- const hookSupport = p.hookSupport || 'unsupported';
1125
- const configPath = p.hookConfigPath || p.configPath || p.tomlPath || 'this client’s user configuration';
1326
+ const buildPartialAutoHint = (p, partial) => {
1327
+ const base = partial.limitation;
1328
+ if (p.id === 'codex') {
1329
+ return `${base} This automatic path writes the shared hook bridge, updates <code>~/.codex/hooks.json</code>, and enables <code>features.codex_hooks</code> in <code>~/.codex/config.toml</code>.`;
1330
+ }
1331
+ if (p.id === 'vscode') {
1332
+ return `${base} This automatic path writes the shared hook bridge, creates <code>~/.copilot/hooks/dollhouse-permissions.json</code>, and enables <code>~/.copilot/hooks</code> in VS Code's <code>chat.hookFilesLocations</code> setting.`;
1333
+ }
1334
+ return `${base} This automatic path writes the shared hook bridge and updates ${partial.configPath}.`;
1335
+ };
1126
1336
 
1127
- if (hookSupport === 'verified' && p.id === 'claude-code') {
1128
- return `<div class="setup-method setup-security-mode" data-setup-modes="permissions" hidden>
1129
- <h3>Permissions &amp; Security <span class="setup-support-badge setup-support-badge--verified">claude code</span></h3>
1130
- <div class="setup-permission-status" data-state="info">
1131
- <strong class="setup-permission-status-title"></strong>
1132
- <p class="setup-permission-status-msg"></p>
1133
- </div>
1134
- <div class="setup-install-row">
1135
- <button class="setup-btn setup-btn-primary setup-permission-install-btn" type="button" data-permission-install-client="claude-code">Configure Now</button>
1136
- <span class="setup-install-status" data-permission-install-status="claude-code"></span>
1337
+ const buildPartialFeatureHeading = (p, partial) => {
1338
+ if (partial.featureHeading) return partial.featureHeading;
1339
+ if (p.id === 'codex') return '2. Enable Codex hooks in <code>~/.codex/config.toml</code>';
1340
+ return '2. Add the additional client settings';
1341
+ };
1342
+
1343
+ const renderVerifiedPermissionSection = (p, verified) => {
1344
+ const permissionInstallClient = p.installClient || p.id;
1345
+ return `<div class="setup-method setup-security-mode" data-setup-modes="permissions" hidden>
1346
+ <h3>Permissions &amp; Security <span class="setup-support-badge setup-support-badge--verified">${verified.statusTag}</span></h3>
1347
+ <div class="setup-permission-status" data-state="info">
1348
+ <strong class="setup-permission-status-title"></strong>
1349
+ <p class="setup-permission-status-msg"></p>
1350
+ </div>
1351
+ <div class="setup-install-row">
1352
+ <button class="setup-btn setup-btn-primary setup-permission-install-btn" type="button" data-permission-install-client="${permissionInstallClient}">Configure Now</button>
1353
+ <span class="setup-install-status" data-permission-install-status="${permissionInstallClient}"></span>
1354
+ </div>
1355
+ <p class="setup-hint">This writes the shared hook bridge assets and updates ${verified.configPath} automatically.</p>
1356
+ </div>
1357
+ <div class="setup-method setup-security-mode" data-setup-modes="permissions" hidden>
1358
+ <details class="setup-manual-fallback">
1359
+ <summary>Manual fallback</summary>
1360
+ <div class="setup-manual-fallback-body">
1361
+ <h4>1. Save the shared hook bridge once</h4>
1362
+ <p>Save this file as <code>${HOOK_BASE_SCRIPT_PATH}</code>, then make it executable with <code>chmod +x ${HOOK_BASE_SCRIPT_PATH}</code>.</p>
1363
+ <div class="setup-code-block"><button class="setup-copy-btn" type="button" data-copy-text='${escapeAttr(HOOK_BASE_SCRIPT)}' aria-label="Copy shared hook bridge">Copy</button>
1364
+ <pre><code>${escapeHtml(HOOK_BASE_SCRIPT)}</code></pre>
1365
+ </div>
1366
+ <h4>2. Add the ${verified.label} hook settings</h4>
1367
+ <p>Add this block to ${verified.configPath} so ${verified.label} can call the hook bridge before tool use.</p>
1368
+ <div class="setup-code-block"><button class="setup-copy-btn" type="button" data-copy-text='${escapeAttr(verified.settingsBlock)}' aria-label="Copy ${verified.label} hook settings">Copy</button>
1369
+ <pre><code>${escapeHtml(verified.settingsBlock)}</code></pre>
1370
+ </div>
1371
+ <p class="setup-hint">Command hook target: <code>${verified.scriptPath}</code></p>
1137
1372
  </div>
1138
- <p class="setup-hint">This writes the shared hook bridge to <code>${HOOK_BASE_SCRIPT_PATH}</code> and updates ${configPath} automatically.</p>
1373
+ </details>
1374
+ </div>`;
1375
+ };
1376
+
1377
+ const renderPartialPermissionSection = (p, partial) => {
1378
+ const permissionInstallClient = p.installClient || p.id;
1379
+ const autoHint = buildPartialAutoHint(p, partial);
1380
+ const featureHeading = buildPartialFeatureHeading(p, partial);
1381
+ const featureSection = partial.featureBlock
1382
+ ? `<h4>${featureHeading}</h4>
1383
+ <div class="setup-code-block"><button class="setup-copy-btn" type="button" data-copy-text='${escapeAttr(partial.featureBlock)}' aria-label="${escapeAttr(partial.featureCopyLabel || `Copy ${partial.label} settings`)}">Copy</button>
1384
+ <pre><code>${escapeHtml(partial.featureBlock)}</code></pre>
1385
+ </div>`
1386
+ : '';
1387
+ const stepNumber = partial.featureBlock ? '3' : '2';
1388
+
1389
+ return `<div class="setup-method setup-security-mode" data-setup-modes="permissions" hidden>
1390
+ <h3>Permissions &amp; Security <span class="setup-support-badge setup-support-badge--manual">${partial.statusTag}</span></h3>
1391
+ <div class="setup-permission-status" data-state="info">
1392
+ <strong class="setup-permission-status-title"></strong>
1393
+ <p class="setup-permission-status-msg"></p>
1394
+ </div>
1395
+ <div class="setup-install-row">
1396
+ <button class="setup-btn setup-btn-primary setup-permission-install-btn" type="button" data-permission-install-client="${permissionInstallClient}">Configure Now</button>
1397
+ <span class="setup-install-status" data-permission-install-status="${permissionInstallClient}"></span>
1139
1398
  </div>
1140
- <div class="setup-method setup-security-mode" data-setup-modes="permissions" hidden>
1141
- <details class="setup-manual-fallback">
1142
- <summary>Manual fallback</summary>
1143
- <div class="setup-manual-fallback-body">
1144
- <h4>1. Save the shared hook bridge once</h4>
1145
- <p>Save this file as <code>${HOOK_BASE_SCRIPT_PATH}</code>, then make it executable with <code>chmod +x ${HOOK_BASE_SCRIPT_PATH}</code>.</p>
1146
- <div class="setup-code-block"><button class="setup-copy-btn" type="button" data-copy-text='${escapeAttr(HOOK_BASE_SCRIPT)}' aria-label="Copy shared hook bridge">Copy</button>
1147
- <pre><code>${escapeHtml(HOOK_BASE_SCRIPT)}</code></pre>
1148
- </div>
1149
- <h4>2. Add the Claude Code hook settings</h4>
1150
- <p>Add this block to ${configPath} so Claude Code can call the hook bridge before tool use.</p>
1151
- <div class="setup-code-block"><button class="setup-copy-btn" type="button" data-copy-text='${escapeAttr(CLAUDE_CODE_HOOK_SETTINGS)}' aria-label="Copy Claude Code hook settings">Copy</button>
1152
- <pre><code>${escapeHtml(CLAUDE_CODE_HOOK_SETTINGS)}</code></pre>
1153
- </div>
1154
- <p class="setup-hint">Command hook target: <code>${HOOK_BASE_SCRIPT_PATH}</code></p>
1399
+ <p class="setup-hint">${autoHint}</p>
1400
+ </div>
1401
+ <div class="setup-method setup-security-mode" data-setup-modes="permissions" hidden>
1402
+ <details class="setup-manual-fallback">
1403
+ <summary>Manual fallback</summary>
1404
+ <div class="setup-manual-fallback-body">
1405
+ <h4>1. Save the shared hook bridge once</h4>
1406
+ <p>Save this file as <code>${HOOK_BASE_SCRIPT_PATH}</code>, then make it executable with <code>chmod +x ${HOOK_BASE_SCRIPT_PATH}</code>.</p>
1407
+ <div class="setup-code-block"><button class="setup-copy-btn" type="button" data-copy-text='${escapeAttr(HOOK_BASE_SCRIPT)}' aria-label="Copy shared hook bridge">Copy</button>
1408
+ <pre><code>${escapeHtml(HOOK_BASE_SCRIPT)}</code></pre>
1155
1409
  </div>
1156
- </details>
1157
- </div>`;
1410
+ ${featureSection}
1411
+ <h4>${stepNumber}. Add the ${partial.label} hook settings in ${partial.configPath}</h4>
1412
+ <div class="setup-code-block"><button class="setup-copy-btn" type="button" data-copy-text='${escapeAttr(partial.settingsBlock)}' aria-label="Copy ${partial.label} hook settings">Copy</button>
1413
+ <pre><code>${escapeHtml(partial.settingsBlock)}</code></pre>
1414
+ </div>
1415
+ <p class="setup-hint">Command hook target: <code>${partial.scriptPath}</code></p>
1416
+ </div>
1417
+ </details>
1418
+ </div>`;
1419
+ };
1420
+
1421
+ const renderPermissionSection = (p) => {
1422
+ const hookSupport = p.hookSupport || 'unsupported';
1423
+ const configPath = p.hookConfigPath || p.configPath || p.tomlPath || 'this client’s user configuration';
1424
+
1425
+ if (hookSupport === 'verified' && VERIFIED_PERMISSION_PLATFORMS[p.id]) {
1426
+ return renderVerifiedPermissionSection(p, VERIFIED_PERMISSION_PLATFORMS[p.id]);
1427
+ }
1428
+
1429
+ if (hookSupport === 'partial' && PARTIAL_PERMISSION_PLATFORMS[p.id]) {
1430
+ return renderPartialPermissionSection(p, PARTIAL_PERMISSION_PLATFORMS[p.id]);
1158
1431
  }
1159
1432
 
1160
1433
  if (hookSupport === 'manual') {
@@ -1194,7 +1467,7 @@ exec bash "$SCRIPT_DIR/pretooluse-dollhouse.sh"`;
1194
1467
 
1195
1468
  intro.innerHTML = `<div class="setup-permissions-note">
1196
1469
  <strong>Permissions &amp; Security</strong>
1197
- <p>Use this mode to turn on permission enforcement for supported clients. Claude Code is fully guided in this release. Where we have workable manual steps for other clients, they are shown here. Otherwise, the client will be marked as coming soon.</p>
1470
+ <p>Use this mode to turn on permission enforcement for supported clients. Claude Code is fully guided in this release, and Gemini CLI, Cursor, VS Code, Windsurf, plus Codex have native partial support. Where we have workable manual steps for other clients, they are shown here. Otherwise, the client will be marked as coming soon.</p>
1198
1471
  </div>`;
1199
1472
  };
1200
1473
 
@@ -1 +1 @@
1
- {"version":3,"file":"permissionRoutes.d.ts","sourceRoot":"","sources":["../../../src/web/routes/permissionRoutes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAgB,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAE1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAsI7E;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI,CA8HrF"}
1
+ {"version":3,"file":"permissionRoutes.d.ts","sourceRoot":"","sources":["../../../src/web/routes/permissionRoutes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAgB,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAE1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAsJ7E;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI,CAkIrF"}