@vibescope/mcp-server 0.4.3 → 0.4.4
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/handlers/session.js +106 -0
- package/package.json +1 -1
- package/src/handlers/session.ts +126 -0
package/dist/handlers/session.js
CHANGED
|
@@ -9,11 +9,102 @@
|
|
|
9
9
|
* - get_token_usage
|
|
10
10
|
*/
|
|
11
11
|
import os from 'os';
|
|
12
|
+
import crypto from 'crypto';
|
|
12
13
|
import { parseArgs, createEnumValidator } from '../validators.js';
|
|
13
14
|
import { getApiClient } from '../api-client.js';
|
|
14
15
|
import { getAgentGuidelinesTemplate, getAgentGuidelinesSummary } from '../templates/agent-guidelines.js';
|
|
15
16
|
import { getFallbackHelpContent, getAvailableHelpTopics } from '../templates/help-content.js';
|
|
16
17
|
import { normalizeGitUrl } from '../utils.js';
|
|
18
|
+
/**
|
|
19
|
+
* Simple hash for content change detection.
|
|
20
|
+
*/
|
|
21
|
+
function simpleHash(content) {
|
|
22
|
+
return crypto.createHash('md5').update(content).digest('hex').slice(0, 12);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Build the full CLAUDE.md content from dynamic rules + project instructions.
|
|
26
|
+
* This is what gets persisted to .claude/CLAUDE.md by the agent.
|
|
27
|
+
*/
|
|
28
|
+
function buildPersistContent(agentRules, workflow, project, agentInstructions) {
|
|
29
|
+
const lines = [];
|
|
30
|
+
lines.push('<!-- hash:{{HASH}} -->');
|
|
31
|
+
lines.push('<!-- AUTO-GENERATED by Vibescope MCP. Do not edit manually — changes will be overwritten on next start_work_session. -->');
|
|
32
|
+
lines.push('');
|
|
33
|
+
lines.push('# Vibescope Agent Guidelines');
|
|
34
|
+
lines.push('');
|
|
35
|
+
// Quick start
|
|
36
|
+
lines.push('## Quick Start');
|
|
37
|
+
lines.push('');
|
|
38
|
+
lines.push('```');
|
|
39
|
+
lines.push('start_work_session(git_url: "https://github.com/YOUR/REPO", model: "opus", agent_type: "claude", agent_name: "YOUR_NAME")');
|
|
40
|
+
lines.push('```');
|
|
41
|
+
lines.push('');
|
|
42
|
+
// Mandatory rules
|
|
43
|
+
lines.push('## MANDATORY Workflow Rules (NON-NEGOTIABLE)');
|
|
44
|
+
lines.push('');
|
|
45
|
+
for (const rule of agentRules) {
|
|
46
|
+
lines.push(`- ${rule}`);
|
|
47
|
+
}
|
|
48
|
+
lines.push('');
|
|
49
|
+
// Session ID reminder
|
|
50
|
+
lines.push('### Session ID');
|
|
51
|
+
lines.push('');
|
|
52
|
+
lines.push('Save the `session_id` from `start_work_session` and pass it on EVERY `update_task`, `complete_task`, and `get_next_task` call. Without it, task claiming fails and your name won\'t show on completed tasks.');
|
|
53
|
+
lines.push('');
|
|
54
|
+
// Workflow
|
|
55
|
+
const workflowContinuous = workflow.continuous_work;
|
|
56
|
+
if (workflowContinuous) {
|
|
57
|
+
lines.push('## Continuous Work Loop');
|
|
58
|
+
lines.push('');
|
|
59
|
+
if (workflowContinuous.steps) {
|
|
60
|
+
for (const step of workflowContinuous.steps) {
|
|
61
|
+
lines.push(`1. ${step}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (workflowContinuous.rule) {
|
|
65
|
+
lines.push('');
|
|
66
|
+
lines.push(`**${workflowContinuous.rule}**`);
|
|
67
|
+
}
|
|
68
|
+
lines.push('');
|
|
69
|
+
}
|
|
70
|
+
const workflowValidation = workflow.validation;
|
|
71
|
+
if (workflowValidation?.steps) {
|
|
72
|
+
lines.push('## Validation Workflow');
|
|
73
|
+
lines.push('');
|
|
74
|
+
for (const step of workflowValidation.steps) {
|
|
75
|
+
lines.push(`1. ${step}`);
|
|
76
|
+
}
|
|
77
|
+
lines.push('');
|
|
78
|
+
}
|
|
79
|
+
// Context management
|
|
80
|
+
lines.push('## Context Management');
|
|
81
|
+
lines.push('');
|
|
82
|
+
lines.push('When context grows large or responses slow:');
|
|
83
|
+
lines.push('1. Run `/clear`');
|
|
84
|
+
lines.push('2. Call `start_work_session(...)` again immediately');
|
|
85
|
+
lines.push('3. Continue with `next_task` — do NOT ask permission');
|
|
86
|
+
lines.push('');
|
|
87
|
+
// MCP connection
|
|
88
|
+
lines.push('## MCP Connection Required');
|
|
89
|
+
lines.push('');
|
|
90
|
+
lines.push('**If MCP connection fails, end session immediately.** Never work without task tracking.');
|
|
91
|
+
lines.push('');
|
|
92
|
+
// Project-specific instructions from template
|
|
93
|
+
if (agentInstructions.trim()) {
|
|
94
|
+
lines.push('## Project-Specific Instructions');
|
|
95
|
+
lines.push('');
|
|
96
|
+
lines.push(agentInstructions.trim());
|
|
97
|
+
lines.push('');
|
|
98
|
+
}
|
|
99
|
+
// Help topics
|
|
100
|
+
lines.push('## Help Topics');
|
|
101
|
+
lines.push('');
|
|
102
|
+
lines.push('`get_help(topic)` for: `getting_started`, `tasks`, `validation`, `deployment`, `git`, `blockers`, `milestones`, `fallback`, `session`, `topics`');
|
|
103
|
+
const content = lines.join('\n');
|
|
104
|
+
// Replace hash placeholder with actual hash
|
|
105
|
+
const hash = simpleHash(content.replace('<!-- hash:{{HASH}} -->', ''));
|
|
106
|
+
return content.replace('{{HASH}}', hash);
|
|
107
|
+
}
|
|
17
108
|
// Auto-detect machine hostname for worktree tracking
|
|
18
109
|
const MACHINE_HOSTNAME = os.hostname();
|
|
19
110
|
const VALID_MODES = ['lite', 'full'];
|
|
@@ -370,6 +461,21 @@ export const startWorkSession = async (args, ctx) => {
|
|
|
370
461
|
rule: 'Don\'t block tasks waiting for deployment — investigation is complete when you identify the resolution.',
|
|
371
462
|
};
|
|
372
463
|
result.WORKFLOW = workflow;
|
|
464
|
+
// Build PERSIST_INSTRUCTIONS — tells the agent to write rules to .claude/CLAUDE.md
|
|
465
|
+
// This ensures instructions survive context clears and persist across the whole session.
|
|
466
|
+
// Content comes from: admin templates + project settings (fully customizable from dashboard).
|
|
467
|
+
const agentInstructions = data.project?.agent_instructions || '';
|
|
468
|
+
const persistContent = buildPersistContent(agentRules, workflow, data.project, agentInstructions);
|
|
469
|
+
// Extract hash from the generated content (it's embedded in the first line)
|
|
470
|
+
const hashMatch = persistContent.match(/<!-- hash:(\w+) -->/);
|
|
471
|
+
const contentHash = hashMatch?.[1] || simpleHash(persistContent);
|
|
472
|
+
result.PERSIST_INSTRUCTIONS = {
|
|
473
|
+
action: 'Write the content below to .claude/CLAUDE.md (overwrite existing). This ensures your instructions persist across context clears.',
|
|
474
|
+
path: '.claude/CLAUDE.md',
|
|
475
|
+
content: persistContent,
|
|
476
|
+
hash: contentHash,
|
|
477
|
+
skip_if_hash_matches: `Only write if the file doesn't exist or its content has changed. Check for "<!-- hash:${contentHash} -->" at the top.`,
|
|
478
|
+
};
|
|
373
479
|
// Add next action at end - pending requests take priority over validation, then regular tasks
|
|
374
480
|
if (hasUrgentQuestions) {
|
|
375
481
|
const firstQuestion = data.URGENT_QUESTIONS?.requests?.[0] || data.pending_requests?.[0];
|
package/package.json
CHANGED
package/src/handlers/session.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import os from 'os';
|
|
13
|
+
import crypto from 'crypto';
|
|
13
14
|
import type { Handler, HandlerRegistry, TokenUsage } from './types.js';
|
|
14
15
|
import { parseArgs, createEnumValidator } from '../validators.js';
|
|
15
16
|
import { getApiClient } from '../api-client.js';
|
|
@@ -17,6 +18,114 @@ import { getAgentGuidelinesTemplate, getAgentGuidelinesSummary } from '../templa
|
|
|
17
18
|
import { getFallbackHelpContent, getAvailableHelpTopics } from '../templates/help-content.js';
|
|
18
19
|
import { normalizeGitUrl } from '../utils.js';
|
|
19
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Simple hash for content change detection.
|
|
23
|
+
*/
|
|
24
|
+
function simpleHash(content: string): string {
|
|
25
|
+
return crypto.createHash('md5').update(content).digest('hex').slice(0, 12);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Build the full CLAUDE.md content from dynamic rules + project instructions.
|
|
30
|
+
* This is what gets persisted to .claude/CLAUDE.md by the agent.
|
|
31
|
+
*/
|
|
32
|
+
function buildPersistContent(
|
|
33
|
+
agentRules: string[],
|
|
34
|
+
workflow: Record<string, unknown>,
|
|
35
|
+
project: Record<string, unknown> | undefined,
|
|
36
|
+
agentInstructions: string
|
|
37
|
+
): string {
|
|
38
|
+
const lines: string[] = [];
|
|
39
|
+
|
|
40
|
+
lines.push('<!-- hash:{{HASH}} -->');
|
|
41
|
+
lines.push('<!-- AUTO-GENERATED by Vibescope MCP. Do not edit manually — changes will be overwritten on next start_work_session. -->');
|
|
42
|
+
lines.push('');
|
|
43
|
+
lines.push('# Vibescope Agent Guidelines');
|
|
44
|
+
lines.push('');
|
|
45
|
+
|
|
46
|
+
// Quick start
|
|
47
|
+
lines.push('## Quick Start');
|
|
48
|
+
lines.push('');
|
|
49
|
+
lines.push('```');
|
|
50
|
+
lines.push('start_work_session(git_url: "https://github.com/YOUR/REPO", model: "opus", agent_type: "claude", agent_name: "YOUR_NAME")');
|
|
51
|
+
lines.push('```');
|
|
52
|
+
lines.push('');
|
|
53
|
+
|
|
54
|
+
// Mandatory rules
|
|
55
|
+
lines.push('## MANDATORY Workflow Rules (NON-NEGOTIABLE)');
|
|
56
|
+
lines.push('');
|
|
57
|
+
for (const rule of agentRules) {
|
|
58
|
+
lines.push(`- ${rule}`);
|
|
59
|
+
}
|
|
60
|
+
lines.push('');
|
|
61
|
+
|
|
62
|
+
// Session ID reminder
|
|
63
|
+
lines.push('### Session ID');
|
|
64
|
+
lines.push('');
|
|
65
|
+
lines.push('Save the `session_id` from `start_work_session` and pass it on EVERY `update_task`, `complete_task`, and `get_next_task` call. Without it, task claiming fails and your name won\'t show on completed tasks.');
|
|
66
|
+
lines.push('');
|
|
67
|
+
|
|
68
|
+
// Workflow
|
|
69
|
+
const workflowContinuous = workflow.continuous_work as { steps?: string[]; rule?: string } | undefined;
|
|
70
|
+
if (workflowContinuous) {
|
|
71
|
+
lines.push('## Continuous Work Loop');
|
|
72
|
+
lines.push('');
|
|
73
|
+
if (workflowContinuous.steps) {
|
|
74
|
+
for (const step of workflowContinuous.steps) {
|
|
75
|
+
lines.push(`1. ${step}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (workflowContinuous.rule) {
|
|
79
|
+
lines.push('');
|
|
80
|
+
lines.push(`**${workflowContinuous.rule}**`);
|
|
81
|
+
}
|
|
82
|
+
lines.push('');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const workflowValidation = workflow.validation as { steps?: string[] } | undefined;
|
|
86
|
+
if (workflowValidation?.steps) {
|
|
87
|
+
lines.push('## Validation Workflow');
|
|
88
|
+
lines.push('');
|
|
89
|
+
for (const step of workflowValidation.steps) {
|
|
90
|
+
lines.push(`1. ${step}`);
|
|
91
|
+
}
|
|
92
|
+
lines.push('');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Context management
|
|
96
|
+
lines.push('## Context Management');
|
|
97
|
+
lines.push('');
|
|
98
|
+
lines.push('When context grows large or responses slow:');
|
|
99
|
+
lines.push('1. Run `/clear`');
|
|
100
|
+
lines.push('2. Call `start_work_session(...)` again immediately');
|
|
101
|
+
lines.push('3. Continue with `next_task` — do NOT ask permission');
|
|
102
|
+
lines.push('');
|
|
103
|
+
|
|
104
|
+
// MCP connection
|
|
105
|
+
lines.push('## MCP Connection Required');
|
|
106
|
+
lines.push('');
|
|
107
|
+
lines.push('**If MCP connection fails, end session immediately.** Never work without task tracking.');
|
|
108
|
+
lines.push('');
|
|
109
|
+
|
|
110
|
+
// Project-specific instructions from template
|
|
111
|
+
if (agentInstructions.trim()) {
|
|
112
|
+
lines.push('## Project-Specific Instructions');
|
|
113
|
+
lines.push('');
|
|
114
|
+
lines.push(agentInstructions.trim());
|
|
115
|
+
lines.push('');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Help topics
|
|
119
|
+
lines.push('## Help Topics');
|
|
120
|
+
lines.push('');
|
|
121
|
+
lines.push('`get_help(topic)` for: `getting_started`, `tasks`, `validation`, `deployment`, `git`, `blockers`, `milestones`, `fallback`, `session`, `topics`');
|
|
122
|
+
|
|
123
|
+
const content = lines.join('\n');
|
|
124
|
+
// Replace hash placeholder with actual hash
|
|
125
|
+
const hash = simpleHash(content.replace('<!-- hash:{{HASH}} -->', ''));
|
|
126
|
+
return content.replace('{{HASH}}', hash);
|
|
127
|
+
}
|
|
128
|
+
|
|
20
129
|
// Auto-detect machine hostname for worktree tracking
|
|
21
130
|
const MACHINE_HOSTNAME = os.hostname();
|
|
22
131
|
|
|
@@ -426,6 +535,23 @@ export const startWorkSession: Handler = async (args, ctx) => {
|
|
|
426
535
|
|
|
427
536
|
result.WORKFLOW = workflow;
|
|
428
537
|
|
|
538
|
+
// Build PERSIST_INSTRUCTIONS — tells the agent to write rules to .claude/CLAUDE.md
|
|
539
|
+
// This ensures instructions survive context clears and persist across the whole session.
|
|
540
|
+
// Content comes from: admin templates + project settings (fully customizable from dashboard).
|
|
541
|
+
const agentInstructions = data.project?.agent_instructions || '';
|
|
542
|
+
const persistContent = buildPersistContent(agentRules, workflow, data.project as Record<string, unknown> | undefined, agentInstructions);
|
|
543
|
+
// Extract hash from the generated content (it's embedded in the first line)
|
|
544
|
+
const hashMatch = persistContent.match(/<!-- hash:(\w+) -->/);
|
|
545
|
+
const contentHash = hashMatch?.[1] || simpleHash(persistContent);
|
|
546
|
+
|
|
547
|
+
result.PERSIST_INSTRUCTIONS = {
|
|
548
|
+
action: 'Write the content below to .claude/CLAUDE.md (overwrite existing). This ensures your instructions persist across context clears.',
|
|
549
|
+
path: '.claude/CLAUDE.md',
|
|
550
|
+
content: persistContent,
|
|
551
|
+
hash: contentHash,
|
|
552
|
+
skip_if_hash_matches: `Only write if the file doesn't exist or its content has changed. Check for "<!-- hash:${contentHash} -->" at the top.`,
|
|
553
|
+
};
|
|
554
|
+
|
|
429
555
|
// Add next action at end - pending requests take priority over validation, then regular tasks
|
|
430
556
|
if (hasUrgentQuestions) {
|
|
431
557
|
const firstQuestion = data.URGENT_QUESTIONS?.requests?.[0] || data.pending_requests?.[0];
|