@link-assistant/hive-mind 1.54.4 → 1.54.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.
- package/CHANGELOG.md +13 -0
- package/README.hi.md +10 -10
- package/README.md +13 -13
- package/README.ru.md +10 -10
- package/README.zh.md +10 -10
- package/package.json +4 -3
- package/src/claude-quiet-config.lib.mjs +146 -0
- package/src/claude.lib.mjs +2 -0
- package/src/config.lib.mjs +3 -2
- package/src/configure-claude.lib.mjs +127 -0
- package/src/configure-claude.mjs +35 -0
- package/pr-1607-body.md +0 -34
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.54.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 2c15727: Migrate Docker images and deployment paths from `konard/sandbox` to the current
|
|
8
|
+
full `konard/box` base image with the `box` user and `/home/box` home directory.
|
|
9
|
+
|
|
10
|
+
## 1.54.5
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- ea79845: Disable noisy Claude Code features for solve runs via merged user settings, subprocess environment variables, and Docker image defaults. Expands the quiet config to also disable fast mode, feedback surveys, mouse tracking, away summaries, Claude attribution (commit/pr), co-authored-by trailer, thinking summaries, and UI animations, sets viewMode to verbose, and caps tool-use concurrency at 4 for deterministic autonomous runs. Keeps Claude's built-in git/PR instructions on (`includeGitInstructions: true`), enables task tracking (`CLAUDE_CODE_ENABLE_TASKS=1`) and turn resume (`CLAUDE_CODE_RESUME_INTERRUPTED_TURN=1`), and makes the bypass-permissions mode audible via `permissions.defaultMode: "bypassPermissions"` + `skipDangerousModePermissionPrompt: true` (complementing the existing `--dangerously-skip-permissions` CLI flag). Adds a reusable `configure-claude` bin with an apply default and a `--verify` check-only mode so users and system administrators can reset or audit Claude Code configuration manually after installing `@link-assistant/hive-mind`. Docker release builds now wait for the npm package version to become available, pass that exact version into Docker as `HIVE_MIND_VERSION`, install `@link-assistant/hive-mind@${HIVE_MIND_VERSION}`, and invoke the published `configure-claude` bin directly instead of copying repo source files into the Docker build.
|
|
15
|
+
|
|
3
16
|
## 1.54.4
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
package/README.hi.md
CHANGED
|
@@ -197,22 +197,22 @@ docker attach hive-mind
|
|
|
197
197
|
|
|
198
198
|
# Extract auth data from a running (or stopped) container to the host:
|
|
199
199
|
mkdir -p ~/.hive-mind
|
|
200
|
-
docker cp hive-mind:/
|
|
201
|
-
docker cp hive-mind:/
|
|
202
|
-
docker cp hive-mind:/
|
|
200
|
+
docker cp hive-mind:/home/box/.claude ~/.hive-mind/claude
|
|
201
|
+
docker cp hive-mind:/home/box/.claude.json ~/.hive-mind/claude.json
|
|
202
|
+
docker cp hive-mind:/home/box/.config/gh ~/.hive-mind/gh
|
|
203
203
|
|
|
204
|
-
# Fix ownership to match the
|
|
205
|
-
|
|
206
|
-
chown -R $
|
|
207
|
-
chown $
|
|
204
|
+
# Fix ownership to match the box user inside the container:
|
|
205
|
+
BOX_UID=$(docker exec hive-mind id -u box)
|
|
206
|
+
chown -R $BOX_UID:$BOX_UID ~/.hive-mind/claude ~/.hive-mind/gh
|
|
207
|
+
chown $BOX_UID:$BOX_UID ~/.hive-mind/claude.json
|
|
208
208
|
|
|
209
209
|
# On subsequent runs, mount the auth data to keep it between restarts:
|
|
210
210
|
docker run -dit \
|
|
211
211
|
--name hive-mind \
|
|
212
212
|
--restart unless-stopped \
|
|
213
|
-
-v /root/.hive-mind/claude:/
|
|
214
|
-
-v /root/.hive-mind/claude.json:/
|
|
215
|
-
-v /root/.hive-mind/gh:/
|
|
213
|
+
-v /root/.hive-mind/claude:/home/box/.claude \
|
|
214
|
+
-v /root/.hive-mind/claude.json:/home/box/.claude.json \
|
|
215
|
+
-v /root/.hive-mind/gh:/home/box/.config/gh \
|
|
216
216
|
konard/hive-mind:latest
|
|
217
217
|
```
|
|
218
218
|
|
package/README.md
CHANGED
|
@@ -203,19 +203,19 @@ docker attach hive-mind
|
|
|
203
203
|
mkdir -p /root/.hive-mind/claude /root/.hive-mind/codex /root/.hive-mind/gh
|
|
204
204
|
touch -a /root/.hive-mind/claude.json
|
|
205
205
|
|
|
206
|
-
# In our Docker images HOME=/
|
|
206
|
+
# In our Docker images HOME=/home/box, so Codex stores its data in /home/box/.codex.
|
|
207
207
|
# Mount the full Codex directory so auth.json, config.toml, and sessions survive restarts.
|
|
208
|
-
docker run -dit --user
|
|
209
|
-
-v /root/.hive-mind/claude:/
|
|
210
|
-
-v /root/.hive-mind/codex:/
|
|
211
|
-
-v /root/.hive-mind/claude.json:/
|
|
212
|
-
-v /root/.hive-mind/gh:/
|
|
213
|
-
konard/hive-mind:latest bash -l -c 'bash /
|
|
214
|
-
|
|
215
|
-
# After the first start, fix ownership to match the
|
|
216
|
-
|
|
217
|
-
chown -R $
|
|
218
|
-
chown $
|
|
208
|
+
docker run -dit --user box --name hive-mind --restart unless-stopped \
|
|
209
|
+
-v /root/.hive-mind/claude:/home/box/.claude \
|
|
210
|
+
-v /root/.hive-mind/codex:/home/box/.codex \
|
|
211
|
+
-v /root/.hive-mind/claude.json:/home/box/.claude.json \
|
|
212
|
+
-v /root/.hive-mind/gh:/home/box/.config/gh \
|
|
213
|
+
konard/hive-mind:latest bash -l -c 'bash /home/box/start-bot.sh'
|
|
214
|
+
|
|
215
|
+
# After the first start, fix ownership to match the box user inside the container:
|
|
216
|
+
BOX_UID=$(docker exec hive-mind id -u box)
|
|
217
|
+
chown -R $BOX_UID:$BOX_UID /root/.hive-mind/claude /root/.hive-mind/codex /root/.hive-mind/gh
|
|
218
|
+
chown $BOX_UID:$BOX_UID /root/.hive-mind/claude.json
|
|
219
219
|
|
|
220
220
|
# Important: mounted ~/.codex data overrides the image-baked Codex config.
|
|
221
221
|
# If the host directory was created before Playwright MCP was added to the image,
|
|
@@ -231,7 +231,7 @@ docker exec -it hive-mind bash -lc 'codex mcp list && codex mcp add playwright -
|
|
|
231
231
|
- ✅ Easy to run multiple instances with different GitHub accounts
|
|
232
232
|
- ✅ Consistent environment across different machines
|
|
233
233
|
|
|
234
|
-
The Docker image itself now registers Playwright MCP for both Claude and Codex during build, and CI verifies those registrations in the built container. If `codex mcp list` is still empty in a running container, the usual cause is not the published image itself but a mounted `/
|
|
234
|
+
The Docker image itself now registers Playwright MCP for both Claude and Codex during build, and CI verifies those registrations in the built container. If `codex mcp list` is still empty in a running container, the usual cause is not the published image itself but a mounted `/home/box/.codex` directory from the host that replaces the image's default Codex configuration.
|
|
235
235
|
|
|
236
236
|
See [docs/DOCKER.md](./docs/DOCKER.md) for advanced Docker usage.
|
|
237
237
|
|
package/README.ru.md
CHANGED
|
@@ -197,22 +197,22 @@ docker attach hive-mind
|
|
|
197
197
|
|
|
198
198
|
# Extract auth data from a running (or stopped) container to the host:
|
|
199
199
|
mkdir -p ~/.hive-mind
|
|
200
|
-
docker cp hive-mind:/
|
|
201
|
-
docker cp hive-mind:/
|
|
202
|
-
docker cp hive-mind:/
|
|
200
|
+
docker cp hive-mind:/home/box/.claude ~/.hive-mind/claude
|
|
201
|
+
docker cp hive-mind:/home/box/.claude.json ~/.hive-mind/claude.json
|
|
202
|
+
docker cp hive-mind:/home/box/.config/gh ~/.hive-mind/gh
|
|
203
203
|
|
|
204
|
-
# Fix ownership to match the
|
|
205
|
-
|
|
206
|
-
chown -R $
|
|
207
|
-
chown $
|
|
204
|
+
# Fix ownership to match the box user inside the container:
|
|
205
|
+
BOX_UID=$(docker exec hive-mind id -u box)
|
|
206
|
+
chown -R $BOX_UID:$BOX_UID ~/.hive-mind/claude ~/.hive-mind/gh
|
|
207
|
+
chown $BOX_UID:$BOX_UID ~/.hive-mind/claude.json
|
|
208
208
|
|
|
209
209
|
# On subsequent runs, mount the auth data to keep it between restarts:
|
|
210
210
|
docker run -dit \
|
|
211
211
|
--name hive-mind \
|
|
212
212
|
--restart unless-stopped \
|
|
213
|
-
-v /root/.hive-mind/claude:/
|
|
214
|
-
-v /root/.hive-mind/claude.json:/
|
|
215
|
-
-v /root/.hive-mind/gh:/
|
|
213
|
+
-v /root/.hive-mind/claude:/home/box/.claude \
|
|
214
|
+
-v /root/.hive-mind/claude.json:/home/box/.claude.json \
|
|
215
|
+
-v /root/.hive-mind/gh:/home/box/.config/gh \
|
|
216
216
|
konard/hive-mind:latest
|
|
217
217
|
```
|
|
218
218
|
|
package/README.zh.md
CHANGED
|
@@ -197,22 +197,22 @@ docker attach hive-mind
|
|
|
197
197
|
|
|
198
198
|
# Extract auth data from a running (or stopped) container to the host:
|
|
199
199
|
mkdir -p ~/.hive-mind
|
|
200
|
-
docker cp hive-mind:/
|
|
201
|
-
docker cp hive-mind:/
|
|
202
|
-
docker cp hive-mind:/
|
|
200
|
+
docker cp hive-mind:/home/box/.claude ~/.hive-mind/claude
|
|
201
|
+
docker cp hive-mind:/home/box/.claude.json ~/.hive-mind/claude.json
|
|
202
|
+
docker cp hive-mind:/home/box/.config/gh ~/.hive-mind/gh
|
|
203
203
|
|
|
204
|
-
# Fix ownership to match the
|
|
205
|
-
|
|
206
|
-
chown -R $
|
|
207
|
-
chown $
|
|
204
|
+
# Fix ownership to match the box user inside the container:
|
|
205
|
+
BOX_UID=$(docker exec hive-mind id -u box)
|
|
206
|
+
chown -R $BOX_UID:$BOX_UID ~/.hive-mind/claude ~/.hive-mind/gh
|
|
207
|
+
chown $BOX_UID:$BOX_UID ~/.hive-mind/claude.json
|
|
208
208
|
|
|
209
209
|
# On subsequent runs, mount the auth data to keep it between restarts:
|
|
210
210
|
docker run -dit \
|
|
211
211
|
--name hive-mind \
|
|
212
212
|
--restart unless-stopped \
|
|
213
|
-
-v /root/.hive-mind/claude:/
|
|
214
|
-
-v /root/.hive-mind/claude.json:/
|
|
215
|
-
-v /root/.hive-mind/gh:/
|
|
213
|
+
-v /root/.hive-mind/claude:/home/box/.claude \
|
|
214
|
+
-v /root/.hive-mind/claude.json:/home/box/.claude.json \
|
|
215
|
+
-v /root/.hive-mind/gh:/home/box/.config/gh \
|
|
216
216
|
konard/hive-mind:latest
|
|
217
217
|
```
|
|
218
218
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@link-assistant/hive-mind",
|
|
3
|
-
"version": "1.54.
|
|
3
|
+
"version": "1.54.6",
|
|
4
4
|
"description": "AI-powered issue solver and hive mind for collaborative problem solving",
|
|
5
5
|
"main": "src/hive.mjs",
|
|
6
6
|
"type": "module",
|
|
@@ -9,11 +9,12 @@
|
|
|
9
9
|
"solve": "./src/solve.mjs",
|
|
10
10
|
"task": "./src/task.mjs",
|
|
11
11
|
"review": "./src/review.mjs",
|
|
12
|
+
"configure-claude": "./src/configure-claude.mjs",
|
|
12
13
|
"start-screen": "./src/start-screen.mjs",
|
|
13
14
|
"hive-telegram-bot": "./src/telegram-bot.mjs"
|
|
14
15
|
},
|
|
15
16
|
"scripts": {
|
|
16
|
-
"test": "node tests/solve-queue.test.mjs && node tests/limits-display.test.mjs && node tests/test-usage-limit.mjs && node tests/test-codex-support.mjs && node tests/test-build-cost-info-string.mjs && node tests/test-claude-code-install-method.mjs && node tests/test-issue-1616-pr-issue-link-preservation.mjs && node tests/test-pre-pr-failure-notifier-1640.mjs && node tests/test-telegram-message-filters.mjs && node tests/test-telegram-bot-command-aliases.mjs && node tests/test-solve-queue-command.mjs && node tests/test-queue-display-1267.mjs && node tests/test-telegram-bot-launcher.mjs",
|
|
17
|
+
"test": "node tests/solve-queue.test.mjs && node tests/limits-display.test.mjs && node tests/test-usage-limit.mjs && node tests/test-codex-support.mjs && node tests/test-build-cost-info-string.mjs && node tests/test-claude-code-install-method.mjs && node tests/test-claude-quiet-config.mjs && node tests/test-configure-claude-bin.mjs && node tests/test-docker-release-order.mjs && node tests/test-docker-box-migration.mjs && node tests/test-issue-1616-pr-issue-link-preservation.mjs && node tests/test-pre-pr-failure-notifier-1640.mjs && node tests/test-telegram-message-filters.mjs && node tests/test-telegram-bot-command-aliases.mjs && node tests/test-solve-queue-command.mjs && node tests/test-queue-display-1267.mjs && node tests/test-telegram-bot-launcher.mjs",
|
|
17
18
|
"test:queue": "node tests/solve-queue.test.mjs",
|
|
18
19
|
"test:limits-display": "node tests/limits-display.test.mjs",
|
|
19
20
|
"test:usage-limit": "node tests/test-usage-limit.mjs",
|
|
@@ -25,7 +26,7 @@
|
|
|
25
26
|
"changeset": "changeset",
|
|
26
27
|
"changeset:version": "changeset version",
|
|
27
28
|
"changeset:publish": "npm run build:pre && changeset publish",
|
|
28
|
-
"build:pre": "chmod +x src/hive.mjs && chmod +x src/solve.mjs",
|
|
29
|
+
"build:pre": "chmod +x src/hive.mjs && chmod +x src/solve.mjs && chmod +x src/configure-claude.mjs",
|
|
29
30
|
"prepare": "husky"
|
|
30
31
|
},
|
|
31
32
|
"repository": {
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
|
|
7
|
+
export const REQUIRED_CLAUDE_QUIET_ENV = Object.freeze({
|
|
8
|
+
CLAUDE_CODE_DISABLE_AUTO_MEMORY: '1',
|
|
9
|
+
CLAUDE_CODE_DISABLE_CRON: '1',
|
|
10
|
+
CLAUDE_CODE_DISABLE_TERMINAL_TITLE: '1',
|
|
11
|
+
CLAUDE_CODE_DISABLE_CLAUDE_MDS: '1',
|
|
12
|
+
CLAUDE_CODE_DISABLE_FAST_MODE: '1',
|
|
13
|
+
CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY: '1',
|
|
14
|
+
CLAUDE_CODE_DISABLE_MOUSE: '1',
|
|
15
|
+
CLAUDE_CODE_ENABLE_AWAY_SUMMARY: '0',
|
|
16
|
+
CLAUDE_CODE_ENABLE_TASKS: '1',
|
|
17
|
+
CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY: '4',
|
|
18
|
+
CLAUDE_CODE_RESUME_INTERRUPTED_TURN: '1',
|
|
19
|
+
DISABLE_FEEDBACK_COMMAND: '1',
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export const REQUIRED_CLAUDE_QUIET_SETTINGS = Object.freeze({
|
|
23
|
+
autoMemoryEnabled: false,
|
|
24
|
+
spinnerTipsEnabled: false,
|
|
25
|
+
awaySummaryEnabled: false,
|
|
26
|
+
feedbackSurveyRate: 0,
|
|
27
|
+
includeCoAuthoredBy: false,
|
|
28
|
+
includeGitInstructions: true,
|
|
29
|
+
prefersReducedMotion: true,
|
|
30
|
+
showThinkingSummaries: false,
|
|
31
|
+
skipDangerousModePermissionPrompt: true,
|
|
32
|
+
viewMode: 'verbose',
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export const REQUIRED_CLAUDE_QUIET_ATTRIBUTION = Object.freeze({
|
|
36
|
+
commit: '',
|
|
37
|
+
pr: '',
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export const REQUIRED_CLAUDE_QUIET_PERMISSIONS = Object.freeze({
|
|
41
|
+
defaultMode: 'bypassPermissions',
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export const buildClaudeQuietEnv = (baseEnv = process.env) => ({
|
|
45
|
+
...baseEnv,
|
|
46
|
+
...REQUIRED_CLAUDE_QUIET_ENV,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
export const formatClaudeQuietConfigSummary = () => {
|
|
50
|
+
const settings = Object.entries(REQUIRED_CLAUDE_QUIET_SETTINGS)
|
|
51
|
+
.map(([key, value]) => `${key}=${JSON.stringify(value)}`)
|
|
52
|
+
.join(', ');
|
|
53
|
+
const attribution = `attribution=${JSON.stringify(REQUIRED_CLAUDE_QUIET_ATTRIBUTION)}`;
|
|
54
|
+
const permissions = `permissions=${JSON.stringify(REQUIRED_CLAUDE_QUIET_PERMISSIONS)}`;
|
|
55
|
+
const env = Object.entries(REQUIRED_CLAUDE_QUIET_ENV)
|
|
56
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
57
|
+
.join(', ');
|
|
58
|
+
return `settings[${settings}, ${attribution}, ${permissions}], env[${env}]`;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const isPlainObject = value => value && typeof value === 'object' && !Array.isArray(value);
|
|
62
|
+
|
|
63
|
+
export const ensureClaudeQuietConfig = async ({ settingsPath, log } = {}) => {
|
|
64
|
+
const resolvedPath = settingsPath || path.join(os.homedir(), '.claude', 'settings.json');
|
|
65
|
+
let settings = {};
|
|
66
|
+
try {
|
|
67
|
+
const content = await fs.readFile(resolvedPath, 'utf-8');
|
|
68
|
+
const parsed = JSON.parse(content);
|
|
69
|
+
settings = isPlainObject(parsed) ? parsed : {};
|
|
70
|
+
} catch (err) {
|
|
71
|
+
if (err.code !== 'ENOENT' && log) {
|
|
72
|
+
await log(`⚠️ Could not read ${resolvedPath}: ${err.message}`, { verbose: true });
|
|
73
|
+
}
|
|
74
|
+
settings = {};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const updatedSettingsKeys = [];
|
|
78
|
+
for (const [key, value] of Object.entries(REQUIRED_CLAUDE_QUIET_SETTINGS)) {
|
|
79
|
+
if (settings[key] !== value) {
|
|
80
|
+
settings[key] = value;
|
|
81
|
+
updatedSettingsKeys.push(key);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const existingAttribution = isPlainObject(settings.attribution) ? settings.attribution : {};
|
|
86
|
+
const updatedAttributionKeys = [];
|
|
87
|
+
for (const [key, value] of Object.entries(REQUIRED_CLAUDE_QUIET_ATTRIBUTION)) {
|
|
88
|
+
if (existingAttribution[key] !== value) {
|
|
89
|
+
existingAttribution[key] = value;
|
|
90
|
+
updatedAttributionKeys.push(key);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
settings.attribution = existingAttribution;
|
|
94
|
+
if (updatedAttributionKeys.length > 0) {
|
|
95
|
+
updatedSettingsKeys.push('attribution');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const existingPermissions = isPlainObject(settings.permissions) ? settings.permissions : {};
|
|
99
|
+
const updatedPermissionsKeys = [];
|
|
100
|
+
for (const [key, value] of Object.entries(REQUIRED_CLAUDE_QUIET_PERMISSIONS)) {
|
|
101
|
+
if (existingPermissions[key] !== value) {
|
|
102
|
+
existingPermissions[key] = value;
|
|
103
|
+
updatedPermissionsKeys.push(key);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
settings.permissions = existingPermissions;
|
|
107
|
+
if (updatedPermissionsKeys.length > 0) {
|
|
108
|
+
updatedSettingsKeys.push('permissions');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const existingEnv = isPlainObject(settings.env) ? settings.env : {};
|
|
112
|
+
const updatedEnvKeys = [];
|
|
113
|
+
for (const [key, value] of Object.entries(REQUIRED_CLAUDE_QUIET_ENV)) {
|
|
114
|
+
if (existingEnv[key] !== value) {
|
|
115
|
+
existingEnv[key] = value;
|
|
116
|
+
updatedEnvKeys.push(key);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
settings.env = existingEnv;
|
|
120
|
+
|
|
121
|
+
const changed = updatedSettingsKeys.length > 0 || updatedEnvKeys.length > 0;
|
|
122
|
+
try {
|
|
123
|
+
if (changed) {
|
|
124
|
+
await fs.mkdir(path.dirname(resolvedPath), { recursive: true });
|
|
125
|
+
await fs.writeFile(resolvedPath, JSON.stringify(settings, null, 2));
|
|
126
|
+
}
|
|
127
|
+
if (log) {
|
|
128
|
+
await log(`🧭 Claude Code quiet config ${changed ? 'updated' : 'verified'} at ${resolvedPath}: ${formatClaudeQuietConfigSummary()}`);
|
|
129
|
+
}
|
|
130
|
+
} catch (err) {
|
|
131
|
+
if (log) await log(`⚠️ Could not write ${resolvedPath}: ${err.message}`, { verbose: true });
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
path: resolvedPath,
|
|
136
|
+
changed,
|
|
137
|
+
updatedSettingsKeys,
|
|
138
|
+
updatedEnvKeys,
|
|
139
|
+
updatedAttributionKeys,
|
|
140
|
+
updatedPermissionsKeys,
|
|
141
|
+
settings: { ...REQUIRED_CLAUDE_QUIET_SETTINGS },
|
|
142
|
+
attribution: { ...REQUIRED_CLAUDE_QUIET_ATTRIBUTION },
|
|
143
|
+
permissions: { ...REQUIRED_CLAUDE_QUIET_PERMISSIONS },
|
|
144
|
+
env: { ...REQUIRED_CLAUDE_QUIET_ENV },
|
|
145
|
+
};
|
|
146
|
+
};
|
package/src/claude.lib.mjs
CHANGED
|
@@ -21,6 +21,7 @@ import { handleClaudeRuntimeSwitch } from './claude.runtime-switch.lib.mjs'; //
|
|
|
21
21
|
import { CLAUDE_MODELS as availableModels } from './models/index.mjs'; // Issue #1221
|
|
22
22
|
import { buildMcpConfigWithoutPlaywright } from './playwright-mcp.lib.mjs';
|
|
23
23
|
import { resolveClaudeSessionToolFlags } from './useless-tools.lib.mjs';
|
|
24
|
+
import { ensureClaudeQuietConfig } from './claude-quiet-config.lib.mjs';
|
|
24
25
|
import { fetchModelInfo } from './model-info.lib.mjs';
|
|
25
26
|
export { availableModels }; // Re-export for backward compatibility
|
|
26
27
|
export { fetchModelInfo };
|
|
@@ -725,6 +726,7 @@ export const executeClaudeCommand = async params => {
|
|
|
725
726
|
await log(`🔄 Resuming from session: ${argv.resume}`);
|
|
726
727
|
claudeArgs = `--resume ${argv.resume} ${claudeArgs}`;
|
|
727
728
|
}
|
|
729
|
+
await ensureClaudeQuietConfig({ log });
|
|
728
730
|
const { mcpConfigPath, disallowedToolsList } = await resolveClaudeSessionToolFlags({ argv, log, fallbackBuildMcpConfigWithoutPlaywright: buildMcpConfigWithoutPlaywright });
|
|
729
731
|
if (mcpConfigPath) claudeArgs += ` --strict-mcp-config --mcp-config "${mcpConfigPath}"`;
|
|
730
732
|
if (disallowedToolsList.length) claudeArgs += ` --disallowedTools ${disallowedToolsList.join(' ')}`;
|
package/src/config.lib.mjs
CHANGED
|
@@ -24,6 +24,7 @@ const getenv = typeof getenvModule === 'function' ? getenvModule : getenvModule.
|
|
|
24
24
|
|
|
25
25
|
// Use semver package for version comparison (see issue #1146)
|
|
26
26
|
import semver from 'semver';
|
|
27
|
+
import { buildClaudeQuietEnv } from './claude-quiet-config.lib.mjs';
|
|
27
28
|
|
|
28
29
|
// Import lino for parsing Links Notation format
|
|
29
30
|
const { lino } = await import('./lino.lib.mjs');
|
|
@@ -419,14 +420,14 @@ export const getClaudeEnv = (options = {}) => {
|
|
|
419
420
|
// Get max output tokens based on model (Issue #1221)
|
|
420
421
|
const maxOutputTokens = options.model ? getMaxOutputTokensForModel(options.model) : claudeCode.maxOutputTokens;
|
|
421
422
|
|
|
422
|
-
const env = {
|
|
423
|
+
const env = buildClaudeQuietEnv({
|
|
423
424
|
...process.env,
|
|
424
425
|
CLAUDE_CODE_MAX_OUTPUT_TOKENS: String(maxOutputTokens),
|
|
425
426
|
// MCP timeout configurations to prevent tool calls from hanging indefinitely
|
|
426
427
|
// See: https://github.com/link-assistant/hive-mind/issues/1066
|
|
427
428
|
MCP_TIMEOUT: String(claudeCode.mcpTimeout),
|
|
428
429
|
MCP_TOOL_TIMEOUT: String(claudeCode.mcpToolTimeout),
|
|
429
|
-
};
|
|
430
|
+
});
|
|
430
431
|
|
|
431
432
|
// Opus 4.7+ always uses adaptive thinking — MAX_THINKING_TOKENS has no effect (Issue #1620)
|
|
432
433
|
// For Opus 4.6 and earlier, MAX_THINKING_TOKENS controls extended thinking (Claude Code >= 2.1.12)
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared runner for the `configure-claude` bin command. Applies or verifies
|
|
5
|
+
* the quiet Claude Code defaults in a target `settings.json` by reusing the
|
|
6
|
+
* canonical maps and idempotent merge helpers from:
|
|
7
|
+
* - src/claude-quiet-config.lib.mjs (quiet env + settings + attribution
|
|
8
|
+
* + permissions)
|
|
9
|
+
* - src/useless-tools.lib.mjs (disallowedTools block-list)
|
|
10
|
+
*
|
|
11
|
+
* See issues #1627 and #1642.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import fs from 'node:fs/promises';
|
|
15
|
+
import os from 'node:os';
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
|
|
18
|
+
import { REQUIRED_CLAUDE_QUIET_ENV, REQUIRED_CLAUDE_QUIET_SETTINGS, REQUIRED_CLAUDE_QUIET_ATTRIBUTION, REQUIRED_CLAUDE_QUIET_PERMISSIONS, ensureClaudeQuietConfig } from './claude-quiet-config.lib.mjs';
|
|
19
|
+
import { buildDisallowedToolsList, ensureDisallowedToolsInSettings } from './useless-tools.lib.mjs';
|
|
20
|
+
|
|
21
|
+
export const resolveSettingsPath = settingsPath => settingsPath || path.join(os.homedir(), '.claude', 'settings.json');
|
|
22
|
+
|
|
23
|
+
export const parseConfigureClaudeArgs = argv => {
|
|
24
|
+
const args = { settingsPath: null, verify: false, help: false };
|
|
25
|
+
for (let i = 0; i < argv.length; i++) {
|
|
26
|
+
const arg = argv[i];
|
|
27
|
+
if (arg === '--settings-path' || arg === '-s') {
|
|
28
|
+
args.settingsPath = argv[++i];
|
|
29
|
+
} else if (arg.startsWith('--settings-path=')) {
|
|
30
|
+
args.settingsPath = arg.slice('--settings-path='.length);
|
|
31
|
+
} else if (arg === '--verify') {
|
|
32
|
+
args.verify = true;
|
|
33
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
34
|
+
args.help = true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return args;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const CONFIGURE_CLAUDE_HELP = `Usage: configure-claude [options]
|
|
41
|
+
|
|
42
|
+
Apply or verify Hive-Mind's quiet, deterministic Claude Code defaults in
|
|
43
|
+
a target ~/.claude/settings.json (env vars, settings, attribution,
|
|
44
|
+
permissions.defaultMode, and the disallowedTools block-list).
|
|
45
|
+
|
|
46
|
+
Options:
|
|
47
|
+
-s, --settings-path <path> Path to settings.json (default: ~/.claude/settings.json)
|
|
48
|
+
--verify Report configuration status without writing; exit 1 if incorrect
|
|
49
|
+
-h, --help Show this help and exit
|
|
50
|
+
|
|
51
|
+
Examples:
|
|
52
|
+
configure-claude # apply defaults to ~/.claude/settings.json
|
|
53
|
+
configure-claude --verify # check only, non-zero exit if drift detected
|
|
54
|
+
configure-claude -s /home/box/.claude/settings.json
|
|
55
|
+
|
|
56
|
+
Reference: https://github.com/link-assistant/hive-mind/issues/1642
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
const isPlainObject = value => value && typeof value === 'object' && !Array.isArray(value);
|
|
60
|
+
|
|
61
|
+
const readSettings = async settingsPath => {
|
|
62
|
+
try {
|
|
63
|
+
const content = await fs.readFile(settingsPath, 'utf-8');
|
|
64
|
+
const parsed = JSON.parse(content);
|
|
65
|
+
return isPlainObject(parsed) ? parsed : {};
|
|
66
|
+
} catch (err) {
|
|
67
|
+
if (err.code === 'ENOENT') return null;
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const verifyConfigureClaude = async ({ settingsPath } = {}) => {
|
|
73
|
+
const resolvedPath = resolveSettingsPath(settingsPath);
|
|
74
|
+
const settings = await readSettings(resolvedPath);
|
|
75
|
+
const missing = {
|
|
76
|
+
file: settings === null,
|
|
77
|
+
settings: [],
|
|
78
|
+
env: [],
|
|
79
|
+
attribution: [],
|
|
80
|
+
permissions: [],
|
|
81
|
+
disallowedTools: [],
|
|
82
|
+
};
|
|
83
|
+
const current = settings || {};
|
|
84
|
+
for (const [key, value] of Object.entries(REQUIRED_CLAUDE_QUIET_SETTINGS)) {
|
|
85
|
+
if (current[key] !== value) missing.settings.push(key);
|
|
86
|
+
}
|
|
87
|
+
const envSection = isPlainObject(current.env) ? current.env : {};
|
|
88
|
+
for (const [key, value] of Object.entries(REQUIRED_CLAUDE_QUIET_ENV)) {
|
|
89
|
+
if (envSection[key] !== value) missing.env.push(key);
|
|
90
|
+
}
|
|
91
|
+
const attributionSection = isPlainObject(current.attribution) ? current.attribution : {};
|
|
92
|
+
for (const [key, value] of Object.entries(REQUIRED_CLAUDE_QUIET_ATTRIBUTION)) {
|
|
93
|
+
if (attributionSection[key] !== value) missing.attribution.push(key);
|
|
94
|
+
}
|
|
95
|
+
const permissionsSection = isPlainObject(current.permissions) ? current.permissions : {};
|
|
96
|
+
for (const [key, value] of Object.entries(REQUIRED_CLAUDE_QUIET_PERMISSIONS)) {
|
|
97
|
+
if (permissionsSection[key] !== value) missing.permissions.push(key);
|
|
98
|
+
}
|
|
99
|
+
const existingDisallowed = Array.isArray(current.disallowedTools) ? current.disallowedTools : [];
|
|
100
|
+
for (const required of buildDisallowedToolsList()) {
|
|
101
|
+
if (!existingDisallowed.includes(required)) missing.disallowedTools.push(required);
|
|
102
|
+
}
|
|
103
|
+
const ok = !missing.file && missing.settings.length === 0 && missing.env.length === 0 && missing.attribution.length === 0 && missing.permissions.length === 0 && missing.disallowedTools.length === 0;
|
|
104
|
+
return { ok, path: resolvedPath, missing };
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export const runConfigureClaude = async ({ settingsPath, log } = {}) => {
|
|
108
|
+
const resolvedPath = resolveSettingsPath(settingsPath);
|
|
109
|
+
const logger = log || (async line => console.log(line));
|
|
110
|
+
const quietResult = await ensureClaudeQuietConfig({ settingsPath: resolvedPath, log: logger });
|
|
111
|
+
const disallowedResult = await ensureDisallowedToolsInSettings({ settingsPath: resolvedPath, log: logger });
|
|
112
|
+
return { quietResult, disallowedResult, path: resolvedPath };
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export const formatVerifyReport = ({ ok, path: resolvedPath, missing }) => {
|
|
116
|
+
if (ok) {
|
|
117
|
+
return `✅ Quiet Claude Code configuration is up to date in ${resolvedPath}`;
|
|
118
|
+
}
|
|
119
|
+
const sections = [];
|
|
120
|
+
if (missing.file) sections.push(' - settings.json missing');
|
|
121
|
+
if (missing.settings.length) sections.push(` - settings: ${missing.settings.join(', ')}`);
|
|
122
|
+
if (missing.env.length) sections.push(` - env: ${missing.env.join(', ')}`);
|
|
123
|
+
if (missing.attribution.length) sections.push(` - attribution: ${missing.attribution.join(', ')}`);
|
|
124
|
+
if (missing.permissions.length) sections.push(` - permissions: ${missing.permissions.join(', ')}`);
|
|
125
|
+
if (missing.disallowedTools.length) sections.push(` - disallowedTools: ${missing.disallowedTools.join(', ')}`);
|
|
126
|
+
return `❌ Quiet Claude Code configuration drift detected in ${resolvedPath}\n${sections.join('\n')}`;
|
|
127
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `configure-claude` — reusable bin that resets Hive-Mind's quiet,
|
|
5
|
+
* deterministic Claude Code defaults (env, settings, attribution,
|
|
6
|
+
* permissions.defaultMode, and the disallowedTools block-list) in a
|
|
7
|
+
* target `settings.json`, or verifies that they are already in place.
|
|
8
|
+
*
|
|
9
|
+
* Users and system administrators can run this manually after installing
|
|
10
|
+
* `@link-assistant/hive-mind` to reset Claude Code configuration. Docker
|
|
11
|
+
* images invoke this published bin after npm release, so the image baseline
|
|
12
|
+
* stays in lock-step with the package users install.
|
|
13
|
+
*
|
|
14
|
+
* See issues #1627 and #1642.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { CONFIGURE_CLAUDE_HELP, formatVerifyReport, parseConfigureClaudeArgs, resolveSettingsPath, runConfigureClaude, verifyConfigureClaude } from './configure-claude.lib.mjs';
|
|
18
|
+
|
|
19
|
+
const args = parseConfigureClaudeArgs(process.argv.slice(2));
|
|
20
|
+
|
|
21
|
+
if (args.help) {
|
|
22
|
+
console.log(CONFIGURE_CLAUDE_HELP);
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const settingsPath = resolveSettingsPath(args.settingsPath);
|
|
27
|
+
|
|
28
|
+
if (args.verify) {
|
|
29
|
+
const report = await verifyConfigureClaude({ settingsPath });
|
|
30
|
+
console.log(formatVerifyReport(report));
|
|
31
|
+
process.exit(report.ok ? 0 : 1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const { quietResult, disallowedResult } = await runConfigureClaude({ settingsPath });
|
|
35
|
+
console.log(`Configured quiet Claude Code defaults and ${disallowedResult.total} disallowedTools in ${quietResult.path}`);
|
package/pr-1607-body.md
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
## Summary
|
|
2
|
-
|
|
3
|
-
Fixes #1606 by documenting and verifying Playwright MCP registration for Codex in addition to Claude Code, including the Docker-specific case where persisted Codex state overrides the image defaults.
|
|
4
|
-
|
|
5
|
-
## Root Cause
|
|
6
|
-
|
|
7
|
-
The reported environment had `@playwright/mcp` installed and registered in Claude, but `codex mcp list` had no configured servers. The immediate cause was missing Codex MCP registration. Local reproduction confirmed that `/workspace/.codex/config.toml` can exist without a Playwright MCP entry and that `codex mcp add playwright ...` fixes the state immediately. In Docker deployments, the most likely explanation is that a host-mounted `/workspace/.codex` directory preserved an older Codex config and replaced the image-baked MCP registration. Existing docs and helper scripts also focused mainly on Claude setup, so the mismatch was easy to miss even though `/version` already reported it correctly.
|
|
8
|
-
|
|
9
|
-
## Changes
|
|
10
|
-
|
|
11
|
-
- added regression coverage for the mixed MCP state where Claude is connected and Codex is not
|
|
12
|
-
- updated Playwright MCP verification and integration scripts to check Codex MCP registration explicitly
|
|
13
|
-
- updated Docker verification to fail if Claude or Codex is missing the Playwright MCP registration
|
|
14
|
-
- updated configuration and Docker docs to include both `claude mcp add ...` and `codex mcp add ...`
|
|
15
|
-
- documented that mounting `/workspace/.codex` can override the image defaults and reintroduce the problem
|
|
16
|
-
- added the investigation record and collected evidence under `docs/case-studies/issue-1606`
|
|
17
|
-
|
|
18
|
-
## Reproduction
|
|
19
|
-
|
|
20
|
-
1. Install `@playwright/mcp` and register it only with Claude.
|
|
21
|
-
2. Run `claude mcp list` and confirm `playwright` is present.
|
|
22
|
-
3. Run `codex mcp list` and observe `No MCP servers configured yet`.
|
|
23
|
-
4. Run `/version` and observe `Playwright MCP: <version> | Claude Code: connected | Codex: not connected`.
|
|
24
|
-
|
|
25
|
-
## Verification
|
|
26
|
-
|
|
27
|
-
- `node tests/test-version-info.mjs`
|
|
28
|
-
- `node tests/test-version-parsing.mjs`
|
|
29
|
-
- `bash scripts/verify-docker-image.sh`
|
|
30
|
-
|
|
31
|
-
## Evidence
|
|
32
|
-
|
|
33
|
-
- case study: `docs/case-studies/issue-1606/README.md`
|
|
34
|
-
- PR: https://github.com/link-assistant/hive-mind/pull/1607
|