@askmesh/mcp 0.10.4 → 0.10.5
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/dist/index.js +1 -1
- package/dist/tools/askmesh.js +68 -27
- package/package.json +1 -1
- package/statusline.sh +24 -12
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ const TOKEN = process.env.ASKMESH_TOKEN;
|
|
|
11
11
|
const URL = process.env.ASKMESH_URL || 'https://api.askmesh.dev';
|
|
12
12
|
const server = new McpServer({
|
|
13
13
|
name: 'askmesh',
|
|
14
|
-
version: '0.10.
|
|
14
|
+
version: '0.10.5',
|
|
15
15
|
});
|
|
16
16
|
if (!TOKEN) {
|
|
17
17
|
// No token — start in setup-only mode
|
package/dist/tools/askmesh.js
CHANGED
|
@@ -2,6 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { mkdirSync, writeFileSync, readFileSync, appendFileSync, existsSync, unlinkSync, copyFileSync, chmodSync } from 'fs';
|
|
3
3
|
import { join, dirname } from 'path';
|
|
4
4
|
import { homedir } from 'os';
|
|
5
|
+
import { createHash } from 'crypto';
|
|
5
6
|
import { fileURLToPath } from 'url';
|
|
6
7
|
import * as statusCache from '../statusline/cache.js';
|
|
7
8
|
export function registerAskMesh(server, client) {
|
|
@@ -107,23 +108,39 @@ Actions disponibles :
|
|
|
107
108
|
const { requests } = await client.getSentRequests();
|
|
108
109
|
if (requests.length === 0)
|
|
109
110
|
return text('Aucune question envoyée.');
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
111
|
+
// Separate threads with unread replies from the rest
|
|
112
|
+
const withReply = requests.filter((r) => r.status === 'active' || r.status === 'answered');
|
|
113
|
+
const pending = requests.filter((r) => r.status === 'pending' || r.status === 'in_progress');
|
|
114
|
+
const closed = requests.filter((r) => r.status === 'closed');
|
|
115
|
+
const sections = [];
|
|
116
|
+
if (withReply.length > 0) {
|
|
117
|
+
const lines = withReply.map((r) => {
|
|
118
|
+
let line = `🔔 #${r.id} → @${r.toUsername}: "${r.question}"`;
|
|
119
|
+
if (r.answer)
|
|
120
|
+
line += `\n Réponse: ${r.answer}`;
|
|
121
|
+
line += `\n → Utilise action "thread" requestId ${r.id} pour voir le fil complet, ou "close" pour clôturer.`;
|
|
122
|
+
return line;
|
|
123
|
+
});
|
|
124
|
+
sections.push(`Réponses à lire (${withReply.length}):\n\n${lines.join('\n\n')}`);
|
|
125
|
+
}
|
|
126
|
+
if (pending.length > 0) {
|
|
127
|
+
const lines = pending.map((r) => `⏳ #${r.id} → @${r.toUsername}: "${r.question}" [${r.status}]`);
|
|
128
|
+
sections.push(`En attente de réponse (${pending.length}):\n${lines.join('\n')}`);
|
|
129
|
+
}
|
|
130
|
+
if (closed.length > 0) {
|
|
131
|
+
const lines = closed.map((r) => `✅ #${r.id} → @${r.toUsername}: "${r.question}"`);
|
|
132
|
+
sections.push(`Terminés (${closed.length}):\n${lines.join('\n')}`);
|
|
133
|
+
}
|
|
134
|
+
if (sections.length === 0)
|
|
135
|
+
return text('Rien à traiter, tout est à jour.');
|
|
136
|
+
return text(sections.join('\n\n---\n\n'));
|
|
118
137
|
}
|
|
119
138
|
case 'answer': {
|
|
120
139
|
if (!requestId || !message) {
|
|
121
140
|
return text("Paramètres requis : requestId et message");
|
|
122
141
|
}
|
|
123
142
|
const result = await client.answerRequest(requestId, message);
|
|
124
|
-
|
|
125
|
-
await client.closeThread(requestId).catch(() => { });
|
|
126
|
-
return text(`Réponse envoyée et thread #${result.id} clôturé.`);
|
|
143
|
+
return text(`Réponse envoyée au thread #${result.id}. Le demandeur peut consulter la réponse via /ask-inbox.`);
|
|
127
144
|
}
|
|
128
145
|
case 'reply': {
|
|
129
146
|
if (!requestId || !message) {
|
|
@@ -247,7 +264,7 @@ Si l'utilisateur n'a pas encore de compte, dirige-le vers https://askmesh.dev po
|
|
|
247
264
|
return text('Action inconnue.');
|
|
248
265
|
});
|
|
249
266
|
}
|
|
250
|
-
const CURRENT_VERSION = '0.10.
|
|
267
|
+
const CURRENT_VERSION = '0.10.5';
|
|
251
268
|
async function checkUpdateAndSetup() {
|
|
252
269
|
const lines = [];
|
|
253
270
|
lines.push(`Version actuelle : ${CURRENT_VERSION}`);
|
|
@@ -279,6 +296,14 @@ async function checkUpdateAndSetup() {
|
|
|
279
296
|
function text(t) {
|
|
280
297
|
return { content: [{ type: 'text', text: t }] };
|
|
281
298
|
}
|
|
299
|
+
function readEnvToken(envPath) {
|
|
300
|
+
if (!existsSync(envPath))
|
|
301
|
+
return undefined;
|
|
302
|
+
const content = readFileSync(envPath, 'utf-8');
|
|
303
|
+
const match = content.match(/ASKMESH_TOKEN=(.+)/);
|
|
304
|
+
const val = match?.[1]?.trim();
|
|
305
|
+
return val && val !== 'your_token_here' ? val : undefined;
|
|
306
|
+
}
|
|
282
307
|
function setupSkillsAndStatusLine(token, pollInterval, url) {
|
|
283
308
|
const cwd = process.cwd();
|
|
284
309
|
const commandsDir = join(cwd, '.claude', 'commands');
|
|
@@ -379,33 +404,49 @@ function setupSkillsAndStatusLine(token, pollInterval, url) {
|
|
|
379
404
|
gitignoreStatus = '.gitignore : .env ajouté';
|
|
380
405
|
}
|
|
381
406
|
}
|
|
382
|
-
// Auto-configure status line
|
|
383
|
-
// Copy statusline.sh to
|
|
407
|
+
// Auto-configure status line — per-project, not global
|
|
408
|
+
// Copy statusline.sh to ~/.claude/ (stable location), configure per-project with agent hash
|
|
384
409
|
const mcpDir = dirname(fileURLToPath(import.meta.url));
|
|
385
410
|
const sourceScript = join(mcpDir, '..', '..', 'statusline.sh');
|
|
386
|
-
const
|
|
387
|
-
const stableScript = join(
|
|
388
|
-
const
|
|
411
|
+
const globalClaudeDir = join(homedir(), '.claude');
|
|
412
|
+
const stableScript = join(globalClaudeDir, 'askmesh-statusline.sh');
|
|
413
|
+
const projectClaudeDir = join(cwd, '.claude');
|
|
414
|
+
const projectSettingsPath = join(projectClaudeDir, 'settings.local.json');
|
|
389
415
|
let statusLineStatus = '';
|
|
416
|
+
// Compute token hash for this agent
|
|
417
|
+
const envToken = token || readEnvToken(envPath);
|
|
418
|
+
const tokenHash = envToken ? createHash('md5').update(envToken).digest('hex').slice(0, 8) : '';
|
|
419
|
+
const statusLineCommand = tokenHash
|
|
420
|
+
? `${stableScript} ${tokenHash}`
|
|
421
|
+
: stableScript;
|
|
390
422
|
try {
|
|
391
|
-
mkdirSync(
|
|
423
|
+
mkdirSync(globalClaudeDir, { recursive: true });
|
|
424
|
+
mkdirSync(projectClaudeDir, { recursive: true });
|
|
392
425
|
// Copy script to stable location
|
|
393
426
|
if (existsSync(sourceScript)) {
|
|
394
427
|
copyFileSync(sourceScript, stableScript);
|
|
395
428
|
chmodSync(stableScript, 0o755);
|
|
396
429
|
}
|
|
397
430
|
if (existsSync(stableScript)) {
|
|
431
|
+
// Write to project-level settings (not global)
|
|
398
432
|
let settings = {};
|
|
399
|
-
if (existsSync(
|
|
400
|
-
settings = JSON.parse(readFileSync(
|
|
401
|
-
}
|
|
402
|
-
if (settings.statusLine?.command === stableScript) {
|
|
403
|
-
statusLineStatus = 'Status line : déjà configurée';
|
|
433
|
+
if (existsSync(projectSettingsPath)) {
|
|
434
|
+
settings = JSON.parse(readFileSync(projectSettingsPath, 'utf-8'));
|
|
404
435
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
436
|
+
settings.statusLine = { type: 'command', command: statusLineCommand };
|
|
437
|
+
writeFileSync(projectSettingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
438
|
+
statusLineStatus = `Status line : configurée pour ce projet${tokenHash ? ` (agent ${tokenHash})` : ''}`;
|
|
439
|
+
// Clean up global settings if it had the old config
|
|
440
|
+
const globalSettingsPath = join(globalClaudeDir, 'settings.json');
|
|
441
|
+
if (existsSync(globalSettingsPath)) {
|
|
442
|
+
try {
|
|
443
|
+
const globalSettings = JSON.parse(readFileSync(globalSettingsPath, 'utf-8'));
|
|
444
|
+
if (globalSettings.statusLine?.command?.includes('askmesh')) {
|
|
445
|
+
delete globalSettings.statusLine;
|
|
446
|
+
writeFileSync(globalSettingsPath, JSON.stringify(globalSettings, null, 2) + '\n');
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
catch { }
|
|
409
450
|
}
|
|
410
451
|
}
|
|
411
452
|
else {
|
package/package.json
CHANGED
package/statusline.sh
CHANGED
|
@@ -1,25 +1,37 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# AskMesh status line for Claude Code
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
# Usage: statusline.sh [token_hash]
|
|
4
|
+
# If token_hash provided, reads only that agent's cache
|
|
5
|
+
# Otherwise reads the single cache file
|
|
5
6
|
|
|
6
7
|
input=$(cat)
|
|
7
8
|
|
|
8
9
|
tmpdir="${TMPDIR:-/tmp}"
|
|
10
|
+
hash="$1"
|
|
9
11
|
pending=0
|
|
10
12
|
active=0
|
|
11
13
|
replies=0
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
if [ -n "$hash" ]; then
|
|
16
|
+
# Read specific agent cache
|
|
17
|
+
f="$tmpdir/askmesh_status_${hash}.json"
|
|
18
|
+
if [ -f "$f" ]; then
|
|
19
|
+
pending=$(jq -r '.pending // 0' "$f" 2>/dev/null)
|
|
20
|
+
active=$(jq -r '.active // 0' "$f" 2>/dev/null)
|
|
21
|
+
replies=$(jq -r '.unread_replies // 0' "$f" 2>/dev/null)
|
|
22
|
+
fi
|
|
23
|
+
else
|
|
24
|
+
# Fallback: aggregate all agent caches
|
|
25
|
+
for f in "$tmpdir"/askmesh_status_*.json; do
|
|
26
|
+
[ -f "$f" ] || continue
|
|
27
|
+
p=$(jq -r '.pending // 0' "$f" 2>/dev/null)
|
|
28
|
+
a=$(jq -r '.active // 0' "$f" 2>/dev/null)
|
|
29
|
+
r=$(jq -r '.unread_replies // 0' "$f" 2>/dev/null)
|
|
30
|
+
pending=$((pending + p))
|
|
31
|
+
active=$((active + a))
|
|
32
|
+
replies=$((replies + r))
|
|
33
|
+
done
|
|
34
|
+
fi
|
|
23
35
|
|
|
24
36
|
parts=()
|
|
25
37
|
|